Skip to content
Open
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
5aa9116
Implement package constants
Feb 11, 2026
251615c
Add "is constant" flag to nodes
Feb 11, 2026
2d35ecc
Fix blr parsing, generate unique names for constant FIELD_NAME, fix w…
Feb 11, 2026
75db5ab
Improve error message for non constant expression
Feb 11, 2026
e54c9e6
Add missing SCHEMA set to package constants, fix incorrect cached con…
Feb 12, 2026
45d62b7
Add missing resource allocation and transfer for constants
Feb 12, 2026
06411d7
Fix invalid constant and package name in store_dependencies
Feb 12, 2026
3dc1d3d
Fix invalid constant drop
Feb 12, 2026
d048560
Fix constant evaluating in BLR Parse
Feb 13, 2026
6231dbc
Fix constants collecting and dtype_dec64 parsing
Feb 13, 2026
05342aa
Fix incorrect ambiguity check
Feb 16, 2026
459354f
Update support for complex dtypes
Feb 16, 2026
3c2c61e
Cleanup
Feb 17, 2026
fd208d1
Rollback unneccecery changes
Feb 17, 2026
8a982ac
Move all constant related routines to Constant class
Feb 18, 2026
c42bd55
Add description for package constants
Feb 24, 2026
4a556aa
Cleanup
Feb 24, 2026
44a7050
Delete string in impure
Feb 24, 2026
c2caa1b
Merge remote-tracking branch 'upstream/master' into metacache_package…
Feb 24, 2026
4294bc4
Merge remote-tracking branch 'origin/master' into metacache_package_c…
Feb 24, 2026
3148971
Resolve incorrect merge with LTT
Feb 24, 2026
51fc8d7
One more cleanup (preparation) before PR
Feb 25, 2026
bd085c9
Add ability to add comment on package constant
Feb 25, 2026
4695050
Remove unneccecery namespace scope
Feb 26, 2026
e606d59
Make Constant value in reload state when MINISCAN is set
Feb 26, 2026
cf7929e
Remove not used variable and omit unneccecery namespace scope
Feb 26, 2026
12a7b2b
Update src/isql/show.epp
Noremos Feb 27, 2026
edca2c5
First part of fixes/corrections related to PR issues
Feb 27, 2026
cfdf444
Resolve more PR issues
Feb 27, 2026
816ad9c
Add missing search for FIELD SCHEMA
Feb 27, 2026
13b7df9
Use better name for method
Feb 27, 2026
46e0e5d
Fix type in test
Mar 1, 2026
51d9ab6
Omit unneccecery getConstant method and add assert for constant exist…
Mar 1, 2026
2ccea0f
Mark RecordKeyNode as non constant
Mar 1, 2026
8d85f05
Add missing code for backup-restore
Mar 1, 2026
2f11ba4
Add missing source for windows build
Mar 1, 2026
44b74d7
Do not base Constant class on Routine
Mar 3, 2026
e7e439e
Add missing cache population for constants when restoring
Mar 3, 2026
25ab034
Cleanup Constant class
Mar 3, 2026
70ffad8
Fix constant check for ValueListNode
Mar 3, 2026
edeb6f0
Remove NONE flag
Mar 4, 2026
65ed53f
Fix CI build
Mar 4, 2026
531b987
Add more description for constant and deterministic methods
Mar 4, 2026
75a29bf
Push forgotten change
Mar 4, 2026
9100a79
Mark GEN_UUID function as constant
Mar 4, 2026
bfe4542
Merge remote-tracking branch 'origin/master' into metacache_package_c…
Mar 10, 2026
50f39a0
Fix build after merge
Mar 12, 2026
81978d3
Reimplement package constants with the package cache object
Mar 31, 2026
41693fc
Merge remote-tracking branch 'origin/master' into metacache_package_c…
Mar 31, 2026
6b64151
Update Firebird.pas
Apr 1, 2026
241a95a
Revert unnecessary changes
Apr 1, 2026
71872e2
Remove second protect_system_table_delupd check for CONSTANTS table
Apr 1, 2026
c483a69
Fix missing package id for system packages
Apr 1, 2026
731d65d
Add ability to add constants to system packages
Apr 1, 2026
414bd0d
Add constants to system package RDB$BLOB_UTIL as an example
Apr 1, 2026
082d5e7
Add USAGE privilege requirement to use package constant
Apr 2, 2026
d273bc0
Merge remote-tracking branch 'origin/master' into metacache_package_c…
Apr 2, 2026
fff58ef
Add descriptions for constants added in `RDB$BLOB_UTIL`
Apr 2, 2026
0284ee1
Correct nullable flag for new fields
Apr 2, 2026
84d0d0a
Revert blank change
Apr 2, 2026
b85ee52
Adjust comments for meta objects
Apr 2, 2026
37c4438
Minor corrections
Apr 2, 2026
d3a0b9d
Do not backup system constants
Apr 2, 2026
7b3ef78
Use qualifyNewName to search for SCHEMAs of constant
Apr 3, 2026
93caf2e
Store constant BLR using storeBinaryBlob
Apr 3, 2026
5a4b657
Reorder methods in package class to match definition order
Apr 3, 2026
bafdb37
Fix adding existing constant to cache again on alter
Apr 3, 2026
e9445e3
Fix bug where cached::package lock was not released after Restore
Apr 3, 2026
a1488be
Populate cache for package at after insert trigger only during restore
Apr 3, 2026
9edf966
Mark package field as nullable in RDB$CONSTANTS table in case of glob…
Apr 3, 2026
10140bb
Final cleanup: Correct comments, remove PACKAGE_NAME.NULL checks, add…
Apr 3, 2026
dba49c1
Minor corrections
Apr 3, 2026
1dcc3df
Fix missing error when showing non existing constant(s)
Apr 3, 2026
4ddaad4
Add missing gpre call for Package.epp on windows and update vcxproj.f…
Apr 6, 2026
8149e09
Add missing gpre call for Package.epp at boot state
Apr 6, 2026
a195eb7
Fix resolving package and schema for constant name in SHOW command
Apr 7, 2026
f664a74
Merge remote-tracking branch 'origin/master' into metacache_package_c…
Apr 7, 2026
f3d7c52
One more cleanup
Apr 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/sql.extensions/README.ddl.txt
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ COMMENT ON <basic_type> name IS {'txt'|NULL};
COMMENT ON COLUMN table_or_view_name.field_name IS {'txt'|NULL};
COMMENT ON {PROCEDURE | [EXTERNAL] FUNCTION} [<package_name> .] name.param_name IS {'txt'|NULL};
COMMENT ON [PROCEDURE | FUNCTION] PARAMETER [<package_name> .] name.param_name IS {'txt'|NULL};
COMMENT ON CONSTANT [<schema_name> .] <package_name> . name IS {'txt'|NULL};

An empty literal string '' will act as NULL since the internal code (DYN in this case)
works this way with blobs.
Expand Down
20 changes: 16 additions & 4 deletions doc/sql.extensions/README.packages.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@ Syntax:

<package_item> ::=
<function_decl> ; |
<procedure_decl> ;
<procedure_decl> ; |
<constant_decl> ;

<function_decl> ::=
FUNCTION <name> [( <parameters> )] RETURNS <type>

<procedure_decl> ::=
PROCEDURE <name> [( <parameters> ) [RETURNS ( <parameters> )]]

<constant_decl> ::=
CONSTANT <name> <type> = <constant expression>

<package_body> ::=
{ CREATE [OR ALTER] | ALTER | RECREATE } PACKAGE BODY <name>
AS
Expand All @@ -37,7 +41,8 @@ Syntax:

<package_body_item> ::=
<function_impl> |
<procedure_impl>
<procedure_impl> |
<constant_decl>

<function_impl> ::=
FUNCTION <name> [( <parameters> )] RETURNS <type>
Expand Down Expand Up @@ -75,7 +80,7 @@ Objectives:
1) The grouping is not represented in the database metadata.
2) They all participate in a flat namespace and all routines are callable by everyone (not
talking about security permissions here).

- Facilitate dependency tracking between its internal routines and between other packaged and
unpackaged routines.

Expand All @@ -90,9 +95,16 @@ Objectives:
tables that the package body depends on that object. If you want to, for example, drop that
object, you first need to remove who depends on it. As who depends on it is a package body,
you can just drop it even if some other database object depends on this package. When the body
is dropped, the header remains, allowing you to create its body again after changing it based
is dropped, the header remains, allowing you to create its body again after changing it based
on the object removal.

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.
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>.

- Facilitate permission management.

It's generally a good practice to create routines with a privileged database user and grant
Expand Down
1 change: 1 addition & 0 deletions src/burp/OdsDetection.epp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ namespace
{"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
{0, 0, 0}
};

Expand Down
1 change: 1 addition & 0 deletions src/common/ParserTokens.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ PARSER_TOKEN(TOK_CONDITIONAL, "CONDITIONAL", true)
PARSER_TOKEN(TOK_CONNECT, "CONNECT", false)
PARSER_TOKEN(TOK_CONNECTIONS, "CONNECTIONS", true)
PARSER_TOKEN(TOK_CONSISTENCY, "CONSISTENCY", true)
PARSER_TOKEN(TOK_CONSTANT, "CONSTANT", true)
PARSER_TOKEN(TOK_CONSTRAINT, "CONSTRAINT", false)
PARSER_TOKEN(TOK_CONTAINING, "CONTAINING", true)
PARSER_TOKEN(TOK_CONTINUE, "CONTINUE", true)
Expand Down
50 changes: 37 additions & 13 deletions src/dsql/DdlNodes.epp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
#include "../jrd/ini.h"
#include "../jrd/GarbageCollector.h"
#include "../jrd/ProtectRelations.h"
#include "../dsql/PackageNodes.h"

namespace Jrd {

Expand Down Expand Up @@ -130,16 +131,6 @@ static rel_t relationType(SSHORT relationTypeNull, SSHORT relationType) noexcept
static void saveField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const MetaName& fieldName);
static dsql_rel* saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
const QualifiedName& relationName, bool view, bool creating);
static void updateRdbFields(const TypeClause* type,
SSHORT& fieldType,
SSHORT& fieldLength,
SSHORT& fieldSubTypeNull, SSHORT& fieldSubType,
SSHORT& fieldScaleNull, SSHORT& fieldScale,
SSHORT& characterSetIdNull, SSHORT& characterSetId,
SSHORT& characterLengthNull, SSHORT& characterLength,
SSHORT& fieldPrecisionNull, SSHORT& fieldPrecision,
SSHORT& collationIdNull, SSHORT& collationId,
SSHORT& segmentLengthNull, SSHORT& segmentLength);
static ISC_STATUS getErrorCodeByObjectType(int obj_type);

static constexpr const char* CHECK_CONSTRAINT_EXCEPTION = "check_constraint";
Expand Down Expand Up @@ -887,7 +878,7 @@ static dsql_rel* saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
}

// Update RDB$FIELDS received by reference.
static void updateRdbFields(const TypeClause* type,
void updateRdbFields(const TypeClause* type,
SSHORT& fieldType,
SSHORT& fieldLength,
SSHORT& fieldSubTypeNull, SSHORT& fieldSubType,
Expand Down Expand Up @@ -1486,6 +1477,20 @@ DdlNode* CommentOnNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
dsqlScratch->resolveRoutineOrRelation(name, {objType});
break;

case obj_package_constant:
{
QualifiedName constantName(subName, name.schema, name.object); // name is a package
if (constantName.schema.isEmpty())
constantName.schema = PUBLIC_SCHEMA;
Copy link
Copy Markdown
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
Copy Markdown
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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I figured it out. I should have used dsqlScratch->qualifyNewName(constantName);
Fixed


if (!PackageReferenceNode::constantExists(tdbb, transaction, constantName))
{
status_exception::raise(Arg::Gds(isc_bad_constant_name) << constantName.toQuotedString());
}
name = constantName;
break;
}

default:
dsqlScratch->qualifyExistingName(name, objType);
break;
Expand Down Expand Up @@ -1590,6 +1595,10 @@ void CommentOnNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
SCL_check_package(tdbb, name, SCL_alter);
break;

case obj_package_constant:
SCL_check_package(tdbb, QualifiedName(name.package, name.schema), SCL_alter);
break;

default:
fb_assert(false);
}
Expand Down Expand Up @@ -1753,6 +1762,12 @@ void CommentOnNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, j
status << Arg::Gds(isc_dyn_package_not_found) << Arg::Str(objNameStr);
break;

case obj_package_constant:
tableClause = "rdb$constants";
columnClause = "rdb$constant_name";
status << Arg::Gds(isc_bad_constant_name) << Arg::Str(objNameStr);
break;

default:
fb_assert(false);
return;
Expand All @@ -1776,7 +1791,7 @@ void CommentOnNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, j
sql << "and" << subColumnClause << "=" << subName;
}

if (objType == obj_procedure || objType == obj_udf)
if (objType == obj_procedure || objType == obj_udf || objType == obj_package_constant)
sql << "and rdb$package_name is not distinct from nullif(" << name.package << ", '')";

if (addWhereClause)
Expand Down Expand Up @@ -11562,11 +11577,13 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
ValueExprNode* nameNode = fieldNode;
const char* aliasName = NULL;

while (nodeIs<DsqlAliasNode>(nameNode) || nodeIs<DerivedFieldNode>(nameNode) || nodeIs<DsqlMapNode>(nameNode))
while (nodeIs<DsqlAliasNode>(nameNode) || nodeIs<DerivedFieldNode>(nameNode) || nodeIs<DsqlMapNode>(nameNode) ||
nodeAs<PackageReferenceNode>(nameNode))
{
DsqlAliasNode* aliasNode;
DsqlMapNode* mapNode;
DerivedFieldNode* derivedField;
PackageReferenceNode* referenceNode;

if ((aliasNode = nodeAs<DsqlAliasNode>(nameNode)))
{
Expand All @@ -11582,6 +11599,13 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
aliasName = derivedField->name.c_str();
nameNode = derivedField->value;
}
else if ((referenceNode = nodeAs<PackageReferenceNode>(nameNode)))
{
if (!aliasName)
aliasName = referenceNode->getName();

nameNode = nullptr;
}
}

const dsql_fld* nameField = NULL;
Expand Down
12 changes: 12 additions & 0 deletions src/dsql/DdlNodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@

namespace Jrd {

// Update RDB$FIELDS received by reference.
void updateRdbFields(const Jrd::TypeClause* type,
SSHORT& fieldType,
SSHORT& fieldLength,
SSHORT& fieldSubTypeNull, SSHORT& fieldSubType,
SSHORT& fieldScaleNull, SSHORT& fieldScale,
SSHORT& characterSetIdNull, SSHORT& characterSetId,
SSHORT& characterLengthNull, SSHORT& characterLength,
SSHORT& fieldPrecisionNull, SSHORT& fieldPrecision,
SSHORT& collationIdNull, SSHORT& collationId,
SSHORT& segmentLengthNull, SSHORT& segmentLength);

enum SqlSecurity
{
SS_INVOKER,
Expand Down
58 changes: 57 additions & 1 deletion src/dsql/ExprNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
#include "../jrd/trace/TraceObjects.h"
#include "../jrd/trace/TraceJrdHelpers.h"

#include "../dsql/PackageNodes.h"

using namespace Firebird;
using namespace Jrd;

Expand Down Expand Up @@ -454,6 +456,23 @@ bool ExprNode::unmappable(const MapNode* mapNode, StreamType shellStream) const
return true;
}

bool ExprNode::constant() const
{
NodeRefsHolder holder;
getChildren(holder, false);

for (auto i : holder.refs)
{
if (*i == nullptr)
continue;

if (!(*i)->constant())
return false;
}

return true;
}

void ExprNode::collectStreams(SortedStreamList& streamList) const
{
NodeRefsHolder holder;
Expand Down Expand Up @@ -6448,6 +6467,27 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec
}
}

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

const QualifiedName consatntName(dsqlName,
dsqlQualifier.schema.hasData() ? dsqlQualifier.schema : (dsqlScratch->package.schema.hasData() ? dsqlScratch->package.schema : PUBLIC_SCHEMA),
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Don't think should use PUBLIC_SCHEMA here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Without it, schema will be NULL. Or I am doing something wrong

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I figured it out. I should have used dsqlScratch->qualifyNewName(constantName);
Fixed

dsqlQualifier.object.hasData() ? dsqlQualifier.object : dsqlScratch->package.object);

if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), consatntName))
{
packageContext.ctx_relation = nullptr;
packageContext.ctx_procedure = nullptr;
// Alias is a package name, not a constant
packageContext.ctx_alias.push(QualifiedName(consatntName.package, consatntName.schema));
packageContext.ctx_flags |= CTX_package;
ambiguousCtxStack.push(&packageContext);

MemoryPool& pool = dsqlScratch->getPool();
node = FB_NEW_POOL(pool) PackageReferenceNode(pool, consatntName);
}
}

// CVC: We can't return blindly if this is a check constraint, because there's
// the possibility of an invalid field that wasn't found. The multiple places that
// call this function pass1_field() don't expect a NULL pointer, hence will crash.
Expand Down Expand Up @@ -12463,7 +12503,12 @@ void SysFuncCallNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc)

bool SysFuncCallNode::deterministic(thread_db* tdbb) const
{
return ExprNode::deterministic(tdbb) && function->deterministic;
return ExprNode::deterministic(tdbb) && function->isDeterministic();
}

bool SysFuncCallNode::constant() const
{
return ExprNode::constant() && function->isConstant();
}

void SysFuncCallNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc)
Expand Down Expand Up @@ -14151,6 +14196,17 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
if (!node->dsqlVar ||
(node->dsqlVar->type == dsql_var::TYPE_LOCAL && !node->dsqlVar->initialized && !dsqlScratch->mainScratch))
{
if (dsqlScratch->package.object.hasData())
{
thread_db* tdbb = JRD_get_thread_data();
QualifiedName consatntFullName(dsqlName, dsqlScratch->package.schema, dsqlScratch->package.object);
if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), consatntFullName))
{
delete node;
return FB_NEW_POOL(dsqlScratch->getPool()) PackageReferenceNode(dsqlScratch->getPool(), consatntFullName);
}
}

PASS1_field_unknown(NULL, dsqlName.toQuotedString().c_str(), this);
}

Expand Down
Loading
Loading