Skip to content

Package constants#8916

Open
Noremos wants to merge 31 commits intoFirebirdSQL:masterfrom
Noremos:metacache_package_constants
Open

Package constants#8916
Noremos wants to merge 31 commits intoFirebirdSQL:masterfrom
Noremos:metacache_package_constants

Conversation

@Noremos
Copy link
Contributor

@Noremos Noremos commented Feb 25, 2026

This PR adds a new database object - Package Constant (#1036). See README.packages.txt for more information.
Usage examples:

set autoterm;
CREATE PACKAGE MY_PACKAGE
AS
BEGIN
    CONSTANT PUBLIC_CONST INTEGER = 10;
    FUNCTION MY_SECRET_PRINT() RETURNS INT;
END;

CREATE PACKAGE BODY MY_PACKAGE
AS
BEGIN
    CONSTANT SECRET_1 INTEGER = 15;
    CONSTANT SECRET_2 INTEGER = 30;

    FUNCTION MY_SECRET_PRINT() RETURNS INT
    AS
    BEGIN
        RETURN SECRET_1 * SECRET_2;
    END
END;
commit;

select MY_PACKAGE.PUBLIC_CONST from rdb$database ;
select MY_PACKAGE.MY_SECRET_PRINT() from rdb$database;

The implementation has three key points:

  1. The package constants code is flexible enough to implement package variables, global constants, and additional package elements.
  2. All nodes now have an 'is constant' flag, similar to the 'is deterministic' flag. This is necessary to determine whether an expression can initialize a constant.
  3. In the CreateAlterPackageNode node, a lot of code had to be copied and pasted, and adding constants led to complete chaos. Therefore, I decided to slightly refactor this node. All functionality remains the same; only code duplication was removed.

@sim1984
Copy link
Contributor

sim1984 commented Feb 25, 2026

Are comments supported for constants? I didn't see this in the documentation.
Let me clarify what I mean:

COMMENT ON CONSTANT [<schema>.]<package>.<const_name> IS 'Text'

Comments are simply supported for packaged procedures and functions. It would make sense to do the same for constants (and any package objects, for that matter).

COMMENT ON PROCEDURE [<schema>.]<package>.<proc_name> IS 'Text';
COMMENT ON FUNCTION [<schema>.]<package>.<func_name> IS 'Text';

@Noremos
Copy link
Contributor Author

Noremos commented Feb 25, 2026

Are comments supported for constants?

No, I didn't plan to implement comments, but I think it's possible. I will take a look

@Noremos
Copy link
Contributor Author

Noremos commented Feb 25, 2026

Are comments supported for constants? I didn't see this in the documentation. Let me clarify what I mean:

COMMENT ON CONSTANT [<schema>.]<package>.<const_name> IS 'Text'

Ok, it wasn't that hard, I implemented the feature as suggested.

Examples:

SQL> comment on constant PUBLIC.MY_PACKAGE.PUBLIC_CONST is 'my description';
SQL> comment on constant MY_PACKAGE.SECRET_1 is 'The Magic Value!'; 

Copy link
Member

@AlexPeshkoff AlexPeshkoff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally OK, just minor correction please

if (ssDefiner.asBool())
invoker = dbb->getUserId(getPermanent()->owner);

makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not 100% sure - is it OK to invoke makeValue() when MINISCAN is set? Certainly, constants should not have as complex dependencies as procedures/functions, but anyway - may be better leave it for reload in such a case?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to:

if ((flags & CacheFlag::MINISCAN) == 0)
	makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR);
else
	this->flReload = true; // Call makeValue in reload

@asfernandes asfernandes self-requested a review February 27, 2026 01:08
A package constant is a value initialized by a constant expression.
A constant expression is defined by a simple rule: its value does not change after recompilation.
Constants declared in the package specification are publicly visible and can be referenced using
the <package>.<constant_name> notation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add syntax about optional schema.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added

Constants declared in the package specification are publicly visible and can be referenced using
the <package>.<constant_name> notation.
Constants declared in the package body are private and cannot be accessed from outside the package.
However, they can be referenced directly by <constant_name> within <procedure_impl> and <function_impl>.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This appears that package header constant cannot be referenced in its own body without the qualified package name.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I corrected the desription

{"RDB$PACKAGES", 0, DB_VERSION_DDL12}, // FB3
{"RDB$PUBLICATIONS", 0, DB_VERSION_DDL13}, // FB4
{"RDB$SCHEMAS", 0, DB_VERSION_DDL14}, // FB6
{"RDB$CONSTANTS", 0, DB_VERSION_DDL14},// RDB6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FB6

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not needed at all, as DB_VERSION_DDL14 is already detected.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

{
QualifiedName constantName(subName, name.schema, name.object); // name is a package
if (constantName.schema.isEmpty())
constantName.schema = PUBLIC_SCHEMA;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was not necessary in any other command in DdlNodes.
Looks incorrect here.

Copy link
Contributor Author

@Noremos Noremos Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actuality, as I can see, something similar (but more more complicated) is happening for procedures and functions in resolveRoutineOrRelation(...). Without the check, scheme will remain NULL.

break;

case obj_package_constant:
SCL_check_package(tdbb, QualifiedName(name.package, name.schema), SCL_alter);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why it's not name.getSchemaAndPackage() like for procedures and functions?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have not known about this function. Thanks for the tip!

END_RELATION

// Relation 59 (RDB$CONSTANTS)
RELATION(nam_constants, rel_constants, ODS_13_1, rel_persistent)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
RELATION(nam_constants, rel_constants, ODS_13_1, rel_persistent)
RELATION(nam_constants, rel_constants, ODS_14_0, rel_persistent)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

RELATION(nam_constants, rel_constants, ODS_13_1, rel_persistent)
FIELD(f_const_name, nam_const_name, fld_f_name, 0, ODS_14_0)
FIELD(f_const_id, nam_const_id, fld_const_id, 0, ODS_14_0)
FIELD(f_const_package, nam_pkg_name, fld_pkg_name, 1, ODS_14_0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are some fields using 1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad. Changed to 0.




constexpr auto DEFAULT = SysFunction::DETERMINISTIC | SysFunction::CONSTANT;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use static

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

class SysFunction
{
public:
enum Flags : UCHAR
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum class, please.

SysFunction::NONE is completely misleading.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

enum class is problematic for flags. You have to redefine bit operations or cast the type each time. IMHO, simple enum inside a class is a good choice in such case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And could you clarify what exactly is confusing about SysFunction::NONE?

#include "../jrd/Attachment.h"
#include "../jrd/scl_proto.h"

#include "../common/dsc_proto.h" // DSC_make_descriptor
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I left the review of this file for last, and given the amount of things I saw, didn't read it.
I think the amout of changes in this file and your initial comments about it seems not correctly.
The file was maintanable and it required no extra efforts or such things in what I'm doing now (LTTs in packages).
Your approach seems incorrect for me and is making things more complicate.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was prepared for the changes to this file to be criticized. But if things remain as is, technical debt will accumulate, and over time, everything will become unmaintainable. If @AlexPeshkoff and @dyemanov also think this changes is outside the scope of the PR, I will revert the changes to this file.

Copy link
Member

@dyemanov dyemanov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the first part of the review, PackageNodes.epp will be reviewed separately.

break;

case obj_package_constant:
SCL_check_package(tdbb, QualifiedName(name.package, name.schema), SCL_alter);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SCL_check_package(tdbb, QualifiedName(name.package, name.schema), SCL_alter);
SCL_check_package(tdbb, name.getSchemaAndPackage(), SCL_alter);


bool constant() const override
{
return true;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be false for RecordKeyNode.

{"RDB$PACKAGES", 0, DB_VERSION_DDL12}, // FB3
{"RDB$PUBLICATIONS", 0, DB_VERSION_DDL13}, // FB4
{"RDB$SCHEMAS", 0, DB_VERSION_DDL14}, // FB6
{"RDB$CONSTANTS", 0, DB_VERSION_DDL14},// RDB6
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not needed at all, as DB_VERSION_DDL14 is already detected.

src/dsql/Nodes.h Outdated
virtual bool unmappable(const MapNode* mapNode, StreamType shellStream) const;

// Check if expression returns constant result
virtual bool constant() const;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd define it as returning false here and override only in nodes where true should be returned. No need to pollute the codebase with the default implementation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It actually depends on meaning of "constant result" here. For me it looks like complete duplication of deterministic(). If I'm wrong, what's the difference?

dsql_ctx packageContext(dsqlScratch->getPool());
{ // Consatnts

const QualifiedName consatntName(dsqlName,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constantName

case obj_schemas:
return "SQL$SCHEMAS";
case obj_package_constant:
return "SQL$PACKAGE_CONSTANT";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Constants belong to a package, AFAIU it cannot have it's own privileges, so there're no need in dedicated security classes. Or is it reserved for global constants, if they ever appear in the future?

LCK_idx_rescan, // Index rescan lock
LCK_prc_rescan, // Procedure rescan lock
LCK_fun_rescan, // Function existence lock
LCK_constant_rescan, // Constant existence lock
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it also a preparation for global constants? Because otherwise it's the package (not the constant) that should be cached/locked. Packaged constants are never altered/dropped individually, AFAIU.

class DsqlCompilerScratch;
class dsql_fld;

class Constant final : public Routine
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe it should be refactored, so that Routine and Constant have the same base class?

EVL_field(0, rpb->rpb_record, f_const_id, &desc2);
id = MOV_get_long(tdbb, &desc2, 0);

Constant::lookup(tdbb, id);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't use the result. Is it expected to throw if a constant is not found? Maybe some other method -- e.g. checkExistance() -- is worth adding for readability?

void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;

bool constant() const override
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may agree with Adriano from the safety POV (new nodes are never introduced as constant by mistake), but I don't like polluting the every class with the common code.

And this means that deterministic() should also be changed -- not something I object but I'd prefer to see a clearer code.

@asfernandes
Copy link
Member

No code for backup and restore the new table?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants