Skip to content

Package constants#8916

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

Package constants#8916
Noremos wants to merge 36 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

@asfernandes asfernandes self-requested a review February 27, 2026 01:08
{
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.

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.

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?

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 false. The logic moved to the isChildrenConstant() method. Child methods will call it when needed.

Copy link
Contributor

Choose a reason for hiding this comment

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

@Noremos I repeat my question: what is the difference between constant and deterministic methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

When I started implementing constants, I asked @dyemanov if I could use the deterministic() method to check the constant status of an expression. Dmitry responded that this would be incorrect. So, please wait for Dmitry's reply.

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it is reserved for global constants, but to be honest, I haven't looked into the security code so I'm not sure if it's really necessary for global constants

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.

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 added this because it is necessary to Routine class

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?

src/jrd/vio.cpp Outdated
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?

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'm relying on the rel_funs case, and it simply calls Function::lookup(tdbb, id, 0); So I assume the case where the constant isn't found is impossible. I hope an assert will be enough. Or should I add an exception?

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 should clarify. I think the main purpose of the lookup method isn't to check for existence, but to populate the cache, since it has an auto-create flag. But I could be wrong.

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?

@Noremos
Copy link
Contributor Author

Noremos commented Mar 1, 2026

No code for backup and restore the new table?

It seems like the changes were lost during some merge or migration. I added the changes again. Thanks for noticing

@Noremos
Copy link
Contributor Author

Noremos commented Mar 1, 2026

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

I'll try. I hope I can make as few changes as possible.

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