From 5aa9116984a9df092557737457ddb3a2a83fdf92 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 11 Feb 2026 13:28:42 +0300 Subject: [PATCH 001/119] Implement package constants --- src/burp/OdsDetection.epp | 1 + src/common/ParserTokens.h | 1 + src/dsql/DdlNodes.epp | 24 +- src/dsql/DdlNodes.h | 14 + src/dsql/ExprNodes.cpp | 26 + src/dsql/Nodes.h | 4 +- src/dsql/PackageNodes.epp | 1158 ++++++++++++++++++------ src/dsql/PackageNodes.h | 213 ++++- src/dsql/StmtNodes.cpp | 75 ++ src/dsql/StmtNodes.h | 25 + src/dsql/parse.y | 17 + src/include/firebird/impl/blr.h | 7 + src/include/firebird/impl/msg/dyn.h | 5 +- src/include/firebird/impl/msg/gbak.h | 9 + src/include/firebird/impl/msg/jrd.h | 5 + src/include/firebird/impl/msg/sqlerr.h | 3 + src/include/gen/Firebird.pas | 12 + src/isql/FrontendParser.cpp | 3 + src/isql/FrontendParser.h | 2 + src/isql/show.epp | 150 +++ src/isql/tests/FrontendParserTest.cpp | 7 + src/jrd/Attachment.h | 1 + src/jrd/Constant.epp | 314 +++++++ src/jrd/Constant.h | 123 +++ src/jrd/Resources.h | 12 +- src/jrd/constants.h | 1 + src/jrd/dfw.epp | 61 ++ src/jrd/drq.h | 12 + src/jrd/fields.h | 5 + src/jrd/ids.h | 7 + src/jrd/idx.h | 10 + src/jrd/irq.h | 5 + src/jrd/jrd.h | 1 + src/jrd/lck.cpp | 1 + src/jrd/lck.h | 1 + src/jrd/met.epp | 43 +- src/jrd/met.h | 18 + src/jrd/names.h | 6 + src/jrd/obj.h | 9 +- src/jrd/relations.h | 14 + src/jrd/tra.h | 5 + src/jrd/trig.h | 1 + src/jrd/vio.cpp | 52 +- 43 files changed, 2158 insertions(+), 305 deletions(-) create mode 100644 src/jrd/Constant.epp create mode 100644 src/jrd/Constant.h diff --git a/src/burp/OdsDetection.epp b/src/burp/OdsDetection.epp index 2ca60361336..02cf616d8b7 100644 --- a/src/burp/OdsDetection.epp +++ b/src/burp/OdsDetection.epp @@ -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} }; diff --git a/src/common/ParserTokens.h b/src/common/ParserTokens.h index 588252b429c..2d460814abe 100644 --- a/src/common/ParserTokens.h +++ b/src/common/ParserTokens.h @@ -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) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 56a6ce146ca..e06569a47a1 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -75,6 +75,12 @@ #include "../jrd/ini.h" #include "../jrd/GarbageCollector.h" +#include "../dsql/PackageNodes.h" + +#include +#include +#include + namespace Jrd { // Define range of user relation ids @@ -123,7 +129,7 @@ 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, +void updateRdbFields(const TypeClause* type, SSHORT& fieldType, SSHORT& fieldLength, SSHORT& fieldSubTypeNull, SSHORT& fieldSubType, @@ -835,7 +841,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, @@ -8320,7 +8326,7 @@ void RelationNode::makeVersion(thread_db* tdbb, jrd_tra* transaction, const Qual if (FLD.RDB$CHARACTER_SET_ID.NULL) FLD.RDB$CHARACTER_SET_ID = CS_NONE; - CollId collation((!FLD.RDB$COLLATION_ID.NULL) ? FLD.RDB$COLLATION_ID : + CollId collation((!FLD.RDB$COLLATION_ID.NULL) ? FLD.RDB$COLLATION_ID : (!RFR.RDB$COLLATION_ID.NULL) ? RFR.RDB$COLLATION_ID : COLLATE_NONE); // codepoint collation if (!DSC_make_descriptor(&tfb->tfb_desc, FLD.RDB$FIELD_TYPE, @@ -10889,11 +10895,13 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra ValueExprNode* nameNode = fieldNode; const char* aliasName = NULL; - while (nodeIs(nameNode) || nodeIs(nameNode) || nodeIs(nameNode)) + while (nodeIs(nameNode) || nodeIs(nameNode) || nodeIs(nameNode) || + nodeAs(nameNode)) { DsqlAliasNode* aliasNode; DsqlMapNode* mapNode; DerivedFieldNode* derivedField; + PackageReferenceNode* referenceNode; if ((aliasNode = nodeAs(nameNode))) { @@ -10909,6 +10917,12 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra aliasName = derivedField->name.c_str(); nameNode = derivedField->value; } + else if ((referenceNode = nodeAs(nameNode))) + { + if (!aliasName) + aliasName = referenceNode->getName(); + nameNode = nullptr; + } } const dsql_fld* nameField = NULL; @@ -13054,7 +13068,7 @@ void DropIndexNode::clearFrgn(thread_db* tdbb, MetaId relId, MetaId indexId) // Second part of index deletion post-processing. // // This function is invoked when index is removed from irt page. -// In some cases (failed DB creation) cleanup, done by clearName, +// In some cases (failed DB creation) cleanup, done by clearName, // also should take place. // diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 5d27c6da74e..6b3b8425b0d 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -41,6 +41,19 @@ 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, @@ -49,6 +62,7 @@ enum SqlSecurity }; class LocalDeclarationsNode; +class CompoundStmtNode; class RelationSourceNode; class ValueListNode; class SecDbContext; diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 043133893fb..ec77629cd80 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -67,6 +67,8 @@ #include "../jrd/trace/TraceObjects.h" #include "../jrd/trace/TraceJrdHelpers.h" +#include "../dsql/PackageNodes.h" + using namespace Firebird; using namespace Jrd; @@ -6226,6 +6228,7 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec // AB: Loop through the scope_levels starting by its own. bool done = false; + bool sourceIsFound = false; USHORT currentScopeLevel = dsqlScratch->scopeLevel + 1; for (; currentScopeLevel > 0 && !done; --currentScopeLevel) { @@ -6252,6 +6255,8 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec if (field) { + sourceIsFound = true; + // If there's no name then we have most probable an asterisk that // needs to be exploded. This should be handled by the caller and // when the caller can handle this, list is not nullptr. @@ -6448,6 +6453,16 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec } } + const QualifiedName consatntName(dsqlName, + dsqlQualifier.schema.hasData() ? dsqlQualifier.schema : dsqlScratch->package.schema, + dsqlQualifier.object.hasData() ? dsqlQualifier.object : dsqlScratch->package.object); + if (node == nullptr && !sourceIsFound + && PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), consatntName)) + { + 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. @@ -14151,6 +14166,17 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (!node->dsqlVar || (node->dsqlVar->type == dsql_var::TYPE_LOCAL && !node->dsqlVar->initialized && !dsqlScratch->mainScratch)) { + if (dsqlScratch->package.package.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); } diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 9b4292ac1f7..6648f135213 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -514,6 +514,7 @@ class ExprNode : public DmlNode TYPE_WINDOW_CLAUSE, TYPE_WINDOW_CLAUSE_FRAME, TYPE_WINDOW_CLAUSE_FRAME_EXTENT, + TYPE_REFERENCE, // Bool types TYPE_BINARY_BOOL, @@ -1492,7 +1493,8 @@ class StmtNode : public DmlNode TYPE_UPDATE_OR_INSERT, TYPE_EXT_INIT_PARAMETERS, - TYPE_EXT_TRIGGER + TYPE_EXT_TRIGGER, + TYPE_DECLARE_CONSTANT }; enum WhichTrigger : UCHAR diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 4951ff27f78..0f83468a143 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -38,6 +38,16 @@ #include "../jrd/Attachment.h" #include "../jrd/scl_proto.h" +#include "../common/dsc_proto.h" // DSC_make_descriptor +#include "../dsql/StmtNodes.h" // ConstantExprNode +#include "../jrd/Constant.h" // Constant +#include "../jrd/cvt_proto.h" // CVT_get_string_ptr +#include "../jrd/mov_proto.h" // MOV_get_string_ptr +#include "../dsql/metd_proto.h" // METD_get_domain +#include "../common/classes/VaryStr.h" // METD_get_domain +#include "../jrd/par_proto.h" // PARc_proto in Nodes.h (dependency hell) +#include "../jrd/met.h" // Metacache +#include "../jrd/Statement.h" // Jrd::Statement using namespace Firebird; @@ -53,155 +63,813 @@ DATABASE DB = STATIC "ODS.RDB"; namespace { + +class DropConstantNode +{ +public: + DropConstantNode(Firebird::MemoryPool& pool, const QualifiedName& fullName) : + m_name(pool, fullName) + { } + + void dsqlPass(DsqlCompilerScratch*) + { } + + void execute(thread_db* tdbb, DsqlCompilerScratch*, jrd_tra* transaction) + { + Constant::drop(tdbb, transaction, m_name); + } + +private: + QualifiedName m_name; +}; + + +template +void dropItem(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& fullName) +{ + MemoryPool& pool = dsqlScratch->getPool(); + jrd_tra* transaction = dsqlScratch->getTransaction(); + + TDropNode dropNode(pool, fullName); + dropNode.dsqlPass(dsqlScratch); + dropNode.execute(tdbb, dsqlScratch, transaction); +} + +template +void dropMissingItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema, + const PackageItemsHolder::ItemsSignatureArray& existsItems, + const CreateAlterPackageNode::ItemsNameArray& newNames) +{ + for (auto i = existsItems.begin(); i != existsItems.end(); ++i) + { + if (!newNames.exist(i->name)) + { + dropItem(tdbb, dsqlScratch, QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.package)); + } + } +} + +template +void dropItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema, + const PackageItemsHolder::ItemsSignatureArray& items) +{ + for (auto i = items.begin(); i != items.end(); ++i) + { + dropItem(tdbb, dsqlScratch, QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.package)); + } +} + +void checkItemsDefineMatch(MemoryPool& pool, const QualifiedName& packageAndSchema, + const PackageItemsHolder::ItemsSignatureArray& newItems, + const PackageItemsHolder::ItemsSignatureArray& existingItems, + ISC_STATUS missingCode, ISC_STATUS mismatchCode) +{ + for (auto i = existingItems.begin(); i != existingItems.end(); ++i) + { + FB_SIZE_T pos; + bool found = newItems.find(Signature(pool, i->name), pos); + + if (!found || !newItems[pos].defined) + { + status_exception::raise( + Arg::Gds(missingCode) << i->name << packageAndSchema.toQuotedString()); + } + else if (newItems[pos] != *i) + { + status_exception::raise( + Arg::Gds(mismatchCode) << i->name << packageAndSchema.toQuotedString()); + } + } +} + +} // namespace + + +//---------------------- + +void PackageItemsHolder::drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema) +{ + dropItems(tdbb, dsqlScratch, packageAndSchema, functions); + dropItems(tdbb, dsqlScratch, packageAndSchema, procedures); + dropItems(tdbb, dsqlScratch, packageAndSchema, constants); +} + +void PackageItemsHolder::checkDefineMatch(MemoryPool& pool, const QualifiedName& packageAndSchema, + const PackageItemsHolder& newItems) +{ + checkItemsDefineMatch(pool, packageAndSchema, newItems.functions, functions, + isc_dyn_funcnotdef_package, isc_dyn_funcsignat_package); + checkItemsDefineMatch(pool, packageAndSchema, newItems.procedures, procedures, + isc_dyn_procnotdef_package, isc_dyn_procsignat_package); +} + // Return function and procedure names (in the user charset) and optionally its details for a // given package. - void collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& packageName, - SortedObjectsArray& functions, - SortedObjectsArray& procedures, bool details) +void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, + const QualifiedName& packageName, + bool details, bool collectConstants) +{ + AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS); + AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + FUN IN RDB$FUNCTIONS + WITH FUN.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND + FUN.RDB$PACKAGE_NAME EQ packageName.object.c_str() { - AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS); - AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS); + Signature function(FUN.RDB$FUNCTION_NAME); + function.defined = !FUN.RDB$FUNCTION_BLR.NULL || !FUN.RDB$ENTRYPOINT.NULL; + + if (!FUN.RDB$DETERMINISTIC_FLAG.NULL && FUN.RDB$DETERMINISTIC_FLAG != 0) + function.flags |= Signature::FLAG_DETERMINISTIC; - FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) - FUN IN RDB$FUNCTIONS - WITH FUN.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND - FUN.RDB$PACKAGE_NAME EQ packageName.object.c_str() + if (details) { - Signature function(FUN.RDB$FUNCTION_NAME); - function.defined = !FUN.RDB$FUNCTION_BLR.NULL || !FUN.RDB$ENTRYPOINT.NULL; + FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) + ARG IN RDB$FUNCTION_ARGUMENTS CROSS + FLD IN RDB$FIELDS + WITH ARG.RDB$SCHEMA_NAME EQ FUN.RDB$SCHEMA_NAME AND + ARG.RDB$PACKAGE_NAME EQ FUN.RDB$PACKAGE_NAME AND + ARG.RDB$FUNCTION_NAME EQ FUN.RDB$FUNCTION_NAME AND + FLD.RDB$SCHEMA_NAME EQUIV ARG.RDB$FIELD_SOURCE_SCHEMA_NAME AND + FLD.RDB$FIELD_NAME EQ ARG.RDB$FIELD_SOURCE + { + SignatureParameter parameter(*getDefaultMemoryPool()); + + parameter.number = ARG.RDB$ARGUMENT_POSITION; + parameter.name = ARG.RDB$ARGUMENT_NAME; + parameter.fieldSource = QualifiedName(ARG.RDB$FIELD_SOURCE, ARG.RDB$FIELD_SOURCE_SCHEMA_NAME); + parameter.mechanism = ARG.RDB$ARGUMENT_MECHANISM; + + if (!ARG.RDB$FIELD_NAME.NULL) + parameter.fieldName = QualifiedName(ARG.RDB$FIELD_NAME); + if (!ARG.RDB$RELATION_NAME.NULL) + parameter.relationName = QualifiedName(ARG.RDB$RELATION_NAME, ARG.RDB$RELATION_SCHEMA_NAME); + if (!ARG.RDB$COLLATION_ID.NULL) + parameter.collationId = CollId(ARG.RDB$COLLATION_ID); + if (!ARG.RDB$NULL_FLAG.NULL) + parameter.nullFlag = ARG.RDB$NULL_FLAG; + + if (!FLD.RDB$FIELD_LENGTH.NULL) + parameter.fieldLength = FLD.RDB$FIELD_LENGTH; + if (!FLD.RDB$FIELD_SCALE.NULL) + parameter.fieldScale = FLD.RDB$FIELD_SCALE; + if (!FLD.RDB$FIELD_TYPE.NULL) + parameter.fieldType = FLD.RDB$FIELD_TYPE; + if (!FLD.RDB$FIELD_SUB_TYPE.NULL) + parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE; + if (!FLD.RDB$SEGMENT_LENGTH.NULL) + parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH; + if (!FLD.RDB$NULL_FLAG.NULL) + parameter.fieldNullFlag = FLD.RDB$NULL_FLAG; + if (!FLD.RDB$CHARACTER_LENGTH.NULL) + parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH; + if (!FLD.RDB$COLLATION_ID.NULL) + parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID); + if (!FLD.RDB$CHARACTER_SET_ID.NULL) + parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID); + if (!FLD.RDB$FIELD_PRECISION.NULL) + parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; + + function.parameters.add(parameter); + } + END_FOR + } + + functions.add(function); + } + END_FOR - if (!FUN.RDB$DETERMINISTIC_FLAG.NULL && FUN.RDB$DETERMINISTIC_FLAG != 0) - function.flags |= Signature::FLAG_DETERMINISTIC; + requestHandle.reset(tdbb, drq_l_pkg_procs, DYN_REQUESTS); + requestHandle2.reset(tdbb, drq_l_pkg_proc_args, DYN_REQUESTS); - if (details) + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRC IN RDB$PROCEDURES + WITH PRC.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND + PRC.RDB$PACKAGE_NAME EQ packageName.object.c_str() + { + Signature procedure(PRC.RDB$PROCEDURE_NAME); + procedure.defined = !PRC.RDB$PROCEDURE_BLR.NULL || !PRC.RDB$ENTRYPOINT.NULL; + + if (details) + { + FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) + PRM IN RDB$PROCEDURE_PARAMETERS CROSS + FLD IN RDB$FIELDS + WITH PRM.RDB$SCHEMA_NAME EQ PRC.RDB$SCHEMA_NAME AND + PRM.RDB$PACKAGE_NAME EQ PRC.RDB$PACKAGE_NAME AND + PRM.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND + FLD.RDB$SCHEMA_NAME EQUIV PRM.RDB$FIELD_SOURCE_SCHEMA_NAME AND + FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE { - FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) - ARG IN RDB$FUNCTION_ARGUMENTS CROSS - FLD IN RDB$FIELDS - WITH ARG.RDB$SCHEMA_NAME EQ FUN.RDB$SCHEMA_NAME AND - ARG.RDB$PACKAGE_NAME EQ FUN.RDB$PACKAGE_NAME AND - ARG.RDB$FUNCTION_NAME EQ FUN.RDB$FUNCTION_NAME AND - FLD.RDB$SCHEMA_NAME EQUIV ARG.RDB$FIELD_SOURCE_SCHEMA_NAME AND - FLD.RDB$FIELD_NAME EQ ARG.RDB$FIELD_SOURCE - { - SignatureParameter parameter(*getDefaultMemoryPool()); - - parameter.number = ARG.RDB$ARGUMENT_POSITION; - parameter.name = ARG.RDB$ARGUMENT_NAME; - parameter.fieldSource = QualifiedName(ARG.RDB$FIELD_SOURCE, ARG.RDB$FIELD_SOURCE_SCHEMA_NAME); - parameter.mechanism = ARG.RDB$ARGUMENT_MECHANISM; - - if (!ARG.RDB$FIELD_NAME.NULL) - parameter.fieldName = QualifiedName(ARG.RDB$FIELD_NAME); - if (!ARG.RDB$RELATION_NAME.NULL) - parameter.relationName = QualifiedName(ARG.RDB$RELATION_NAME, ARG.RDB$RELATION_SCHEMA_NAME); - if (!ARG.RDB$COLLATION_ID.NULL) - parameter.collationId = CollId(ARG.RDB$COLLATION_ID); - if (!ARG.RDB$NULL_FLAG.NULL) - parameter.nullFlag = ARG.RDB$NULL_FLAG; - - if (!FLD.RDB$FIELD_LENGTH.NULL) - parameter.fieldLength = FLD.RDB$FIELD_LENGTH; - if (!FLD.RDB$FIELD_SCALE.NULL) - parameter.fieldScale = FLD.RDB$FIELD_SCALE; - if (!FLD.RDB$FIELD_TYPE.NULL) - parameter.fieldType = FLD.RDB$FIELD_TYPE; - if (!FLD.RDB$FIELD_SUB_TYPE.NULL) - parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE; - if (!FLD.RDB$SEGMENT_LENGTH.NULL) - parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH; - if (!FLD.RDB$NULL_FLAG.NULL) - parameter.fieldNullFlag = FLD.RDB$NULL_FLAG; - if (!FLD.RDB$CHARACTER_LENGTH.NULL) - parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH; - if (!FLD.RDB$COLLATION_ID.NULL) - parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID); - if (!FLD.RDB$CHARACTER_SET_ID.NULL) - parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID); - if (!FLD.RDB$FIELD_PRECISION.NULL) - parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; - - function.parameters.add(parameter); - } - END_FOR + SignatureParameter parameter(*getDefaultMemoryPool()); + parameter.type = PRM.RDB$PARAMETER_TYPE; + parameter.number = PRM.RDB$PARAMETER_NUMBER; + parameter.name = PRM.RDB$PARAMETER_NAME; + parameter.fieldSource = QualifiedName(PRM.RDB$FIELD_SOURCE, PRM.RDB$FIELD_SOURCE_SCHEMA_NAME); + parameter.mechanism = PRM.RDB$PARAMETER_MECHANISM; + + if (!PRM.RDB$FIELD_NAME.NULL) + parameter.fieldName = QualifiedName(PRM.RDB$FIELD_NAME); + if (!PRM.RDB$RELATION_NAME.NULL) + parameter.relationName = QualifiedName(PRM.RDB$RELATION_NAME, PRM.RDB$RELATION_SCHEMA_NAME); + if (!PRM.RDB$COLLATION_ID.NULL) + parameter.collationId = CollId(PRM.RDB$COLLATION_ID); + if (!PRM.RDB$NULL_FLAG.NULL) + parameter.nullFlag = PRM.RDB$NULL_FLAG; + + if (!FLD.RDB$FIELD_LENGTH.NULL) + parameter.fieldLength = FLD.RDB$FIELD_LENGTH; + if (!FLD.RDB$FIELD_SCALE.NULL) + parameter.fieldScale = FLD.RDB$FIELD_SCALE; + if (!FLD.RDB$FIELD_TYPE.NULL) + parameter.fieldType = FLD.RDB$FIELD_TYPE; + if (!FLD.RDB$FIELD_SUB_TYPE.NULL) + parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE; + if (!FLD.RDB$SEGMENT_LENGTH.NULL) + parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH; + if (!FLD.RDB$NULL_FLAG.NULL) + parameter.fieldNullFlag = FLD.RDB$NULL_FLAG; + if (!FLD.RDB$CHARACTER_LENGTH.NULL) + parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH; + if (!FLD.RDB$COLLATION_ID.NULL) + parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID); + if (!FLD.RDB$CHARACTER_SET_ID.NULL) + parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID); + if (!FLD.RDB$FIELD_PRECISION.NULL) + parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; + + procedure.parameters.add(parameter); + } + END_FOR + } + + procedures.add(procedure); + } + END_FOR +} + +void PackageItemsHolder::clear() +{ + functions.clear(); + procedures.clear(); +} + +//---------------------- + + +// ----------------------------------- +// PackageReferenceNode implementation +// ----------------------------------- + +static RegisterNode regPackageReferenceNode({blr_package_reference}); + +PackageReferenceNode::PackageReferenceNode(MemoryPool& pool, const QualifiedName& fullName, const UCHAR itemType) + : TypedNode(pool), + m_fullName(fullName), m_itemType(itemType) +{} + +string PackageReferenceNode::internalPrint(NodePrinter& printer) const +{ + ExprNode::internalPrint(printer); + + NODE_PRINT(printer, m_fullName); + + return "PackageReferenceNode"; +} + +ValueExprNode* PackageReferenceNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + MemoryPool& pool = dsqlScratch->getPool(); + + jrd_tra* transaction = dsqlScratch->getTransaction(); + thread_db* tdbb = JRD_get_thread_data(); + + bool isPrivate = false; + if (constantExists(tdbb, transaction, m_fullName, &isPrivate)) + { + // External items have not access to private constants + if (isPrivate) + { + status_exception::raise(Arg::Gds(isc_private_constant) << + Arg::Str(m_fullName.toQuotedString())); + } + } + else + { + status_exception::raise(Arg::Gds(isc_not_defined_constant) << + Arg::Str(m_fullName.toQuotedString())); + } + + auto* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, m_fullName, m_itemType); + return node; +} + + +DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR) +{ + const UCHAR itemType = csb->csb_blr_reader.getByte(); + + switch (itemType) + { + case blr_pkg_ref_item_const: + { + QualifiedName fullName; + csb->csb_blr_reader.getMetaName(fullName.object); + csb->csb_blr_reader.getMetaName(fullName.schema); + csb->csb_blr_reader.getMetaName(fullName.package); + + PackageReferenceNode* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, fullName, itemType); + + if (csb->collectingDependencies()) + { + Dependency dependency(obj_package_constant); + dependency.name = FB_NEW_POOL(pool) QualifiedName(pool, fullName); + csb->addDependency(dependency); + } + node->m_constant = Constant::lookup(tdbb, fullName, 0); + return node; + } + // TODO: rowtype + default: + fb_assert(false); + } + + return nullptr; +} + + +void PackageReferenceNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ + dsqlScratch->appendUChar(blr_package_reference); + dsqlScratch->appendUChar(blr_pkg_ref_item_const); + dsqlScratch->appendMetaString(m_fullName.object.c_str()); + dsqlScratch->appendMetaString(m_fullName.schema.c_str()); + dsqlScratch->appendMetaString(m_fullName.package.c_str()); +} + +void PackageReferenceNode::setParameterName(dsql_par* parameter) const +{ + parameter->par_name = parameter->par_alias = m_fullName.object; +} + +void PackageReferenceNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) +{ + jrd_tra* transaction = dsqlScratch->getTransaction(); + thread_db* tdbb = JRD_get_thread_data(); + *desc = Constant::getDesc(tdbb, transaction, m_fullName); +} + +bool PackageReferenceNode::constantExists(thread_db* tdbb, Jrd::jrd_tra* transaction, + const QualifiedName& fullName, bool* isPrivate) +{ + if (fullName.package.isEmpty() || fullName.object.isEmpty()) + return false; + + AutoCacheRequest getConstantRequest(tdbb, drq_l_pkg_const_flag, DYN_REQUESTS); + FOR (REQUEST_HANDLE getConstantRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ fullName.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ fullName.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ fullName.object.c_str() + + if (isPrivate) + { + *isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + } + + return true; + END_FOR + + return false; +} + +void PackageReferenceNode::getDesc(thread_db* tdbb, CompilerScratch*, dsc* desc) +{ + *desc = Constant::getDesc(tdbb, tdbb->getTransaction(), m_fullName); +} + +ValueExprNode* PackageReferenceNode::copy(thread_db* tdbb, NodeCopier&) const +{ + MemoryPool& pool = *tdbb->getDefaultPool(); + auto* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, m_fullName, m_itemType); + node->m_constant = m_constant; + return node; +} + +ValueExprNode* PackageReferenceNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + ValueExprNode::pass2(tdbb, csb); + + if (m_itemType == blr_pkg_ref_item_const) + m_impureOffset = csb->allocImpure(); + + return this; +} + +dsc* PackageReferenceNode::execute(thread_db* tdbb, Request* request) const +{ + impure_value* impure = request->getImpure(m_impureOffset); + switch (m_itemType) + { + case blr_pkg_ref_item_const: + { + const_cast(m_constant.getObject())->checkReload(tdbb); + EVL_make_value(tdbb, &m_constant->getValue(), impure); + + return &impure->vlu_desc; + } + default: + fb_assert(false); + return nullptr; + } +} + + +// ---------------------------------------- +// CreatePackageConstantNode +// ---------------------------------------- + +string CreatePackageConstantNode::internalPrint(NodePrinter& printer) const +{ + DdlNode::internalPrint(printer); + + NODE_PRINT(printer, name); + NODE_PRINT(printer, m_type); + NODE_PRINT(printer, m_value); + NODE_PRINT(printer, m_isPrivate); + + return "PackageReferenceNode"; +} + +DdlNode* CreatePackageConstantNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + m_value = m_value->dsqlPass(dsqlScratch); + + QualifiedName dummyCollationName; + DDL_resolve_intl_type(dsqlScratch, m_type, dummyCollationName); + // TODO: Is value compatible with type? + //if (!m_value->constant())//TODOCONST:FIXME + { + status_exception::raise(Arg::Gds(isc_dyn_non_deterministic_constant) << name.toQuotedString()); + } + // DsqlDescMaker::fromField(&node->castDesc, type); + // node->castDesc.dsc_flags = node->source->getDsqlDesc().dsc_flags & DSC_nullable; + + DdlNode::dsqlPass(dsqlScratch); + return nullptr; +} + +void CreatePackageConstantNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + // SCL_check_create_access(tdbb, obj_package_constant); +} + +void CreatePackageConstantNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) +{ + if (create) + { + if (alter && executeAlter(tdbb, dsqlScratch, transaction)) + { + return; + } + + executeCreate(tdbb, dsqlScratch, transaction); + } + else + executeAlter(tdbb, dsqlScratch, transaction); + + fb_assert(m_id); + MetadataCache::newVersion(tdbb, m_id); +} + +dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch) +{ + // Prepare variables + Attachment* attachment = tdbb->getAttachment(); + jrd_tra* transaction = tdbb->getTransaction(); + + Firebird::MemoryPool& tempPool = *tdbb->getDatabase()->createPool(ALLOC_ARGS0); + Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); + + // We need to execute the constant expresion and get the descriptor with constant value + // We need a request. To get it, we need to compile the blr. To compile the blr, wrap a ValueExpr in a StmtExpr + + + // Create and setup a proxy stmt node + ConstantExprNode proxyStmt(tempPool); + CastNode* cast = FB_NEW_POOL(tempPool) CastNode(tempPool, m_value, m_type); + proxyStmt.value = cast; + + // Get the blr + blr.clear(); + dsqlScratch->getDebugData().clear(); + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + proxyStmt.genBlr(dsqlScratch); + dsqlScratch->appendUChar(blr_eoc); + + // Compile the blr + Request* getConstantRequest = nullptr; + try + { + getConstantRequest = CMP_compile_request(tdbb, blr.begin(), blr.getCount(), true); + + const StmtNode* initNode = getConstantRequest->getStatement()->topNode; + + // Execute the statement. + // The ConstantExprNode node will prepare the value and will write it into the impure + EXE_start(tdbb, getConstantRequest, transaction); + fb_assert(nodeIs(initNode)); + return static_cast(initNode)->getImpureDsc(getConstantRequest); + } + catch (const Firebird::Exception& ex) + { + tdbb->getDatabase()->deletePool(&tempPool); + ex.stuffException(tdbb->tdbb_status_vector); + ERR_punt(); + return nullptr; + } +} + +void CreatePackageConstantNode::genOutputConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch) +{ + // Compile the constant + dsc* scalar = nullptr; + if ((scalar = makeConstantValue(tdbb, dsqlScratch)) == nullptr) + return; // Something went wrong + + // Make the blr with only the LiteralNode + Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); + blr.clear(); + dsqlScratch->getDebugData().clear(); + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + + // Convert a literalNode-unsupported constant type to a supported one if necessary + switch (scalar->dsc_dtype) + { + case dtype_varying: + case dtype_cstring: + { + // Convert to dtype_text + TTypeId ttype; + UCHAR* ptr; + auto& status = tdbb->getAttachment()->att_dec_status; + const USHORT len = CVT_get_string_ptr(scalar, &ttype, &ptr, nullptr, 0, status); + + dsc text; + text.makeText(len, ttype, ptr); + LiteralNode::genConstant(dsqlScratch, &text, false); + break; + } + case dtype_real: + { + double newValue = *(float*)scalar->dsc_address; + + dsc descForDouble; + descForDouble.makeDouble(); + + UCHAR* ptr; + VaryStr temp; + TTypeId ttype; + ULONG len = MOV_get_string_ptr(tdbb, scalar, &ttype, &ptr, &temp, sizeof(temp)); + + descForDouble.dsc_address = ptr; + + LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); + break; + } + default: + LiteralNode::genConstant(dsqlScratch, scalar, false); + break; + } + dsqlScratch->appendUChar(blr_eoc); +} + +void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) +{ + // DYN_UTIL_check_unique_name(tdbb, transaction, itemName, obj_package_constant); + + FbLocalStatus status; + AutoCacheRequest storeConstantRequest(tdbb, drq_s_pkg_const, DYN_REQUESTS); + + // Check uniqueness + if (PackageReferenceNode::constantExists(tdbb, transaction, name)) + { + status_exception::raise(Arg::Gds(isc_dyn_dup_const) << name.toQuotedString()); + } + + // Store description in the RDB$FIELDS relation + storeGlobalField(tdbb, tdbb->getTransaction(), name, m_type); + + int faults = 0; + while (true) + { + try + { + SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_const_id, "RDB$CONSTANTS"); + id %= (MAX_SSHORT + 1); + if (!id) + continue; + + m_id = id; + + // Store desc and metainfo in the RDB$CONSTANTS relation + STORE (REQUEST_HANDLE storeConstantRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS USING + { + // Constant name + CONST.RDB$CONSTANT_NAME.NULL = FALSE; + strcpy(CONST.RDB$CONSTANT_NAME, name.object.c_str()); + + CONST.RDB$CONSTANT_ID = id; + + // Description (filed) name + CONST.RDB$FIELD_SOURCE.NULL = FALSE; + strcpy(CONST.RDB$FIELD_SOURCE, name.object.c_str()); + + // Gen value blr + genOutputConstantBlr(tdbb, dsqlScratch); + + // Put the blr + blb* blob = blb::create(tdbb, transaction, &CONST.RDB$CONSTANT_BLR); + blob->BLB_put_segment(tdbb, dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount()); + blob->BLB_close(tdbb); + + // Parent package + CONST.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str()); + + // Type + CONST.RDB$PRIVATE_FLAG.NULL = FALSE; + CONST.RDB$PRIVATE_FLAG = m_isPrivate; + + CONST.RDB$CONSTANT_SOURCE.NULL = TRUE; } + END_STORE + break; + } + catch (const status_exception& ex) + { + if (ex.value()[1] != isc_unique_key_violation) + throw; + + if (++faults > MAX_SSHORT) + throw; - functions.add(function); + fb_utils::init_status(tdbb->tdbb_status_vector); } - END_FOR + }// While +} - requestHandle.reset(tdbb, drq_l_pkg_procs, DYN_REQUESTS); - requestHandle2.reset(tdbb, drq_l_pkg_proc_args, DYN_REQUESTS); - FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) - PRC IN RDB$PROCEDURES - WITH PRC.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND - PRC.RDB$PACKAGE_NAME EQ packageName.object.c_str() +bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) +{ + FbLocalStatus status; + AutoCacheRequest eraseDscRequest(tdbb, drq_e_pkg_const_dsc, DYN_REQUESTS); + AutoCacheRequest modifyConstantRequest(tdbb, drq_m_pkg_const, DYN_REQUESTS); + bid blobId; + + bool found = false; + + // Get existing filed + // Store constant dsc in RDB$FIELDS + FOR (REQUEST_HANDLE eraseDscRequest TRANSACTION_HANDLE transaction) + FLD IN RDB$FIELDS CROSS CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND + FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND + FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE + { + m_id = CONST.RDB$CONSTANT_ID; + + MetadataCache::oldVersion(tdbb, m_id, CacheFlag::OLD_ALTER); + + dyn_fld origDom, newDom; + DSC_make_descriptor(&origDom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, + FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, + CSetId(FLD.RDB$CHARACTER_SET_ID), + CollId(FLD.RDB$COLLATION_ID)); + + origDom.dyn_fld_name = name; + origDom.dyn_charbytelen = FLD.RDB$FIELD_LENGTH; + origDom.dyn_dtype = FLD.RDB$FIELD_TYPE; + origDom.dyn_precision = FLD.RDB$FIELD_PRECISION; + origDom.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE; + origDom.dyn_charlen = FLD.RDB$CHARACTER_LENGTH; + origDom.dyn_collation = FLD.RDB$COLLATION_ID; + origDom.dyn_null_flag = !FLD.RDB$NULL_FLAG.NULL && FLD.RDB$NULL_FLAG != 0; + origDom.dyn_fld_source = QualifiedName(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME); + + // If the original field type is an array, force its blr type to blr_blob + if (FLD.RDB$DIMENSIONS != 0) + origDom.dyn_dtype = blr_blob; + + const bool wasInternalDomain = fb_utils::implicit_domain(origDom.dyn_fld_source.object.c_str()); + + // ERASE FLD; + + // Should it be here? + // if (!FLD.RDB$SECURITY_CLASS.NULL) + // deleteSecurityClass(tdbb, transaction, FLD.RDB$SECURITY_CLASS); + + // Should it be here? + // deletePrivilegesByRelName(tdbb, transaction, FLD.RDB$FIELD_NAME, obj_field); + + QualifiedName newDomainName; + + // We have the type. Default and type/domain are exclusive for now. + if (m_type->typeOfName.object.hasData()) { - Signature procedure(PRC.RDB$PROCEDURE_NAME); - procedure.defined = !PRC.RDB$PROCEDURE_BLR.NULL || !PRC.RDB$ENTRYPOINT.NULL; + // Case a1: Internal domain -> domain. + // Case a2: Domain -> domain. - if (details) + newDomainName = m_type->typeOfName; + + if (fb_utils::implicit_domain(newDomainName.object.c_str())) { - FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) - PRM IN RDB$PROCEDURE_PARAMETERS CROSS - FLD IN RDB$FIELDS - WITH PRM.RDB$SCHEMA_NAME EQ PRC.RDB$SCHEMA_NAME AND - PRM.RDB$PACKAGE_NAME EQ PRC.RDB$PACKAGE_NAME AND - PRM.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND - FLD.RDB$SCHEMA_NAME EQUIV PRM.RDB$FIELD_SOURCE_SCHEMA_NAME AND - FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE - { - SignatureParameter parameter(*getDefaultMemoryPool()); - parameter.type = PRM.RDB$PARAMETER_TYPE; - parameter.number = PRM.RDB$PARAMETER_NUMBER; - parameter.name = PRM.RDB$PARAMETER_NAME; - parameter.fieldSource = QualifiedName(PRM.RDB$FIELD_SOURCE, PRM.RDB$FIELD_SOURCE_SCHEMA_NAME); - parameter.mechanism = PRM.RDB$PARAMETER_MECHANISM; - - if (!PRM.RDB$FIELD_NAME.NULL) - parameter.fieldName = QualifiedName(PRM.RDB$FIELD_NAME); - if (!PRM.RDB$RELATION_NAME.NULL) - parameter.relationName = QualifiedName(PRM.RDB$RELATION_NAME, PRM.RDB$RELATION_SCHEMA_NAME); - if (!PRM.RDB$COLLATION_ID.NULL) - parameter.collationId = CollId(PRM.RDB$COLLATION_ID); - if (!PRM.RDB$NULL_FLAG.NULL) - parameter.nullFlag = PRM.RDB$NULL_FLAG; - - if (!FLD.RDB$FIELD_LENGTH.NULL) - parameter.fieldLength = FLD.RDB$FIELD_LENGTH; - if (!FLD.RDB$FIELD_SCALE.NULL) - parameter.fieldScale = FLD.RDB$FIELD_SCALE; - if (!FLD.RDB$FIELD_TYPE.NULL) - parameter.fieldType = FLD.RDB$FIELD_TYPE; - if (!FLD.RDB$FIELD_SUB_TYPE.NULL) - parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE; - if (!FLD.RDB$SEGMENT_LENGTH.NULL) - parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH; - if (!FLD.RDB$NULL_FLAG.NULL) - parameter.fieldNullFlag = FLD.RDB$NULL_FLAG; - if (!FLD.RDB$CHARACTER_LENGTH.NULL) - parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH; - if (!FLD.RDB$COLLATION_ID.NULL) - parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID); - if (!FLD.RDB$CHARACTER_SET_ID.NULL) - parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID); - if (!FLD.RDB$FIELD_PRECISION.NULL) - parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; - - procedure.parameters.add(parameter); - } - END_FOR + // msg 224: "Cannot use the internal domain %s as new type for field %s". + status_exception::raise( + Arg::PrivateDyn(224) << newDomainName.toQuotedString() << m_type->fld_name); + } + + // Get the domain information. + if (!METD_get_domain(dsqlScratch->getTransaction(), m_type, newDomainName)) + { + // Specified domain or source field does not exist. + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_domain_not_found) << newDomainName.toQuotedString()); } - procedures.add(procedure); + QualifiedName dummyCollationName; + DDL_resolve_intl_type(dsqlScratch, m_type, dummyCollationName); + + // If the original definition was a base field type, remove the + // entries from RDB$FIELDS. + if (wasInternalDomain) + { + // Case a1: Internal domain -> domain. + ERASE FLD; + } } - END_FOR - } -} // namespace + else + { + // Case b1: Internal domain -> internal domain. + // Case b2: Domain -> internal domain. + m_type->resolve(dsqlScratch, true); -//---------------------- + if (wasInternalDomain) // Case b1: Internal domain -> internal domain. + { + MODIFY FLD + updateRdbFields(m_type, + FLD.RDB$FIELD_TYPE, + FLD.RDB$FIELD_LENGTH, + FLD.RDB$FIELD_SUB_TYPE.NULL, FLD.RDB$FIELD_SUB_TYPE, + FLD.RDB$FIELD_SCALE.NULL, FLD.RDB$FIELD_SCALE, + FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID, + FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH, + FLD.RDB$FIELD_PRECISION.NULL, FLD.RDB$FIELD_PRECISION, + FLD.RDB$COLLATION_ID.NULL, FLD.RDB$COLLATION_ID, + FLD.RDB$SEGMENT_LENGTH.NULL, FLD.RDB$SEGMENT_LENGTH); + END_MODIFY + + newDom.dyn_fld_source = origDom.dyn_fld_source; + } + else // Case b2: Domain -> internal domain. + storeGlobalField(tdbb, transaction, newDomainName, m_type); + } + + + if (newDomainName.object.hasData()) + newDom.dyn_fld_source = newDomainName; + + AlterDomainNode::getDomainType(tdbb, transaction, newDom); + + if (newDom.dyn_dtype == blr_blob && newDomainName.object.isEmpty()) + newDom.dyn_sub_type = m_type->subType; + + // Gen the new constant value as blr + genOutputConstantBlr(tdbb, dsqlScratch); + + // And write + blb* blob = blb::create(tdbb, transaction, &blobId); + blob->BLB_put_segment(tdbb, dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount()); + blob->BLB_close(tdbb); + + MODIFY CONST USING + CONST.RDB$CONSTANT_BLR.NULL = FALSE; + CONST.RDB$CONSTANT_BLR = blobId; + END_MODIFY + + found = true; + } + END_FOR + + return found; +} string CreateAlterPackageNode::internalPrint(NodePrinter& printer) const @@ -215,6 +883,7 @@ string CreateAlterPackageNode::internalPrint(NodePrinter& printer) const //// FIXME-PRINT: NODE_PRINT(printer, items); NODE_PRINT(printer, functionNames); NODE_PRINT(printer, procedureNames); + NODE_PRINT(printer, constantNames); return "CreateAlterPackageNode"; } @@ -244,20 +913,12 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) switch ((*items)[i].type) { - case CreateAlterPackageNode::Item::FUNCTION: + case PackageItemType::FUNCTION: { CreateAlterFunctionNode* const fun = (*items)[i].function; ddlNode = fun; - if (functionNames.exist(fun->name.object)) - { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_duplicate_package_item) << - Arg::Str("FUNCTION") << fun->name.object.toQuotedString()); - } - - functionNames.add(fun->name.object); + functionNames.addName(fun->name); fun->alter = true; fun->name.schema = name.schema; @@ -265,26 +926,32 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) break; } - case CreateAlterPackageNode::Item::PROCEDURE: + case PackageItemType::PROCEDURE: { CreateAlterProcedureNode* const proc = (*items)[i].procedure; ddlNode = proc; - if (procedureNames.exist(proc->name.object)) - { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_duplicate_package_item) << - Arg::Str("PROCEDURE") << proc->name.object.toQuotedString()); - } - procedureNames.add(proc->name.object); + procedureNames.addName(proc->name); proc->alter = true; proc->name.schema = name.schema; proc->name.package = name.object; break; } + case PackageItemType::CONSTANT: + { + CreatePackageConstantNode* const constant = (*items)[i].constant; + ddlNode = constant; + constantNames.addName(constant->name); + + constant->create = true; // Create a new constant + constant->alter = true; // Create a new constant + constant->name.schema = name.schema; + constant->name.package = name.object; + constant->makePublic(); + break; + } default: fb_assert(false); @@ -442,31 +1109,12 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PACKAGE, name, {}); - SortedObjectsArray existingFuncs(pool); - SortedObjectsArray existingProcs(pool); - collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false); + PackageItemsHolder existingItems(pool); + existingItems.collectPackagedItems(tdbb, transaction, name, false, true); - for (SortedObjectsArray::iterator i = existingFuncs.begin(); - i != existingFuncs.end(); ++i) - { - if (!functionNames.exist(i->name)) - { - DropFunctionNode dropNode(pool, QualifiedName(i->name, name.schema, name.object)); - dropNode.dsqlPass(dsqlScratch); - dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); - } - } - - for (SortedObjectsArray::iterator i = existingProcs.begin(); - i != existingProcs.end(); ++i) - { - if (!procedureNames.exist(i->name)) - { - DropProcedureNode dropNode(pool, QualifiedName(i->name, name.schema, name.object)); - dropNode.dsqlPass(dsqlScratch); - dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); - } - } + dropMissingItems(tdbb, dsqlScratch, name, existingItems.functions, functionNames); + dropMissingItems(tdbb, dsqlScratch, name, existingItems.procedures, procedureNames); + dropMissingItems(tdbb, dsqlScratch, name, existingItems.constants, constantNames); MODIFY PKG PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE; @@ -547,15 +1195,18 @@ void CreateAlterPackageNode::executeItems(thread_db* tdbb, DsqlCompilerScratch* { switch ((*items)[i].type) { - case Item::FUNCTION: + case PackageItemType::FUNCTION: (*items)[i].function->packageOwner = owner; (*items)[i].function->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true); break; - case Item::PROCEDURE: + case PackageItemType::PROCEDURE: (*items)[i].procedure->packageOwner = owner; (*items)[i].procedure->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true); break; + case PackageItemType::CONSTANT: + (*items)[i].constant->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true); + break; } } } @@ -619,25 +1270,9 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, Arg::Gds(isc_dyn_package_not_found) << name.toQuotedString()); } - SortedObjectsArray existingFuncs(pool); - SortedObjectsArray existingProcs(pool); - collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false); - - for (SortedObjectsArray::iterator i = existingFuncs.begin(); - i != existingFuncs.end(); ++i) - { - DropFunctionNode dropNode(pool, QualifiedName(i->name, name.schema, name.object)); - dropNode.dsqlPass(dsqlScratch); - dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); - } - - for (SortedObjectsArray::iterator i = existingProcs.begin(); - i != existingProcs.end(); ++i) - { - DropProcedureNode dropNode(pool, QualifiedName(i->name, name.schema, name.object)); - dropNode.dsqlPass(dsqlScratch); - dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); - } + PackageItemsHolder existingItems(pool); + existingItems.collectPackagedItems(tdbb, transaction, name, false, true); + existingItems.drop(tdbb, dsqlScratch, name); requestHandle.reset(tdbb, drq_e_pkg_prv, DYN_REQUESTS); @@ -690,8 +1325,7 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) // process declaredItems and items Array* arrays[] = {declaredItems, items}; - SortedArray functionNames[FB_NELEM(arrays)]; - SortedArray procedureNames[FB_NELEM(arrays)]; + PackageItemsHolder names[FB_NELEM(arrays)]; for (unsigned i = 0; i < FB_NELEM(arrays); ++i) { @@ -704,20 +1338,11 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) switch ((*arrays[i])[j].type) { - case CreateAlterPackageNode::Item::FUNCTION: + case PackageItemType::FUNCTION: { CreateAlterFunctionNode* const fun = (*arrays[i])[j].function; ddlNode = fun; - - if (functionNames[i].exist(fun->name.object)) - { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_duplicate_package_item) << - Arg::Str("FUNCTION") << fun->name.object.toQuotedString()); - } - - functionNames[i].add(fun->name.object); + names[i].functions.addName(fun->name); fun->name.schema = name.schema; fun->name.package = name.object; @@ -729,20 +1354,11 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) break; } - case CreateAlterPackageNode::Item::PROCEDURE: + case PackageItemType::PROCEDURE: { CreateAlterProcedureNode* const proc = (*arrays[i])[j].procedure; ddlNode = proc; - - if (procedureNames[i].exist(proc->name.object)) - { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_duplicate_package_item) << - Arg::Str("PROCEDURE") << proc->name.object.toQuotedString()); - } - - procedureNames[i].add(proc->name.object); + names[i].procedures.addName(proc->name); proc->name.schema = name.schema; proc->name.package = name.object; @@ -753,7 +1369,19 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) break; } + case PackageItemType::CONSTANT: + { + CreatePackageConstantNode* const constant = (*arrays[i])[j].constant; + ddlNode = constant; + + names[i].constants.addName(constant->name); + constant->name.schema = name.schema; + constant->name.package = name.object; + constant->create = true; + constant->makePrivate(); + break; + } default: fb_assert(false); } @@ -837,12 +1465,10 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc Arg::Gds(isc_dyn_package_not_found) << name.toQuotedString()); } - SortedObjectsArray headerFuncs(pool); - SortedObjectsArray headerProcs(pool); - collectPackagedItems(tdbb, transaction, name, headerFuncs, headerProcs, false); + PackageItemsHolder headerItems(pool); + headerItems.collectPackagedItems(tdbb, transaction, name, false, true); - SortedObjectsArray existingFuncs(pool); - SortedObjectsArray existingProcs(pool); + PackageItemsHolder existingItems(pool); // process declaredItems and items Array* arrays[] = {declaredItems, items}; @@ -854,11 +1480,10 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc if (arrays[i] == items) { - existingFuncs.clear(); - existingProcs.clear(); + existingItems.clear(); } - collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, true); + existingItems.collectPackagedItems(tdbb, transaction, name, true, true); for (unsigned j = 0; j < arrays[i]->getCount(); ++j) { @@ -866,90 +1491,59 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc switch (elem.type) { - case CreateAlterPackageNode::Item::FUNCTION: + case PackageItemType::FUNCTION: { CreateAlterFunctionNode* func = elem.function; if (arrays[i] == items) - func->privateScope = !headerFuncs.exist(Signature(func->name.object)); - else if (existingFuncs.exist(Signature(func->name.object))) + func->privateScope = !headerItems.functions.exist(Signature(func->name.object)); + else { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_duplicate_package_item) << - Arg::Str("FUNCTION") << func->name.toQuotedString()); + existingItems.functions.checkDuplicate(func->name); } func->packageOwner = owner; func->preserveDefaults = - existingFuncs.exist(Signature(func->name.object)) && arrays[i] == items; + existingItems.functions.exist(Signature(func->name.object)) && arrays[i] == items; func->executeDdl(tdbb, elem.dsqlScratch, transaction, true); break; } - case CreateAlterPackageNode::Item::PROCEDURE: + case PackageItemType::PROCEDURE: { CreateAlterProcedureNode* proc = elem.procedure; if (arrays[i] == items) - proc->privateScope = !headerProcs.exist(Signature(proc->name.object)); - else if (existingProcs.exist(Signature(proc->name.object))) + proc->privateScope = !headerItems.procedures.exist(Signature(proc->name.object)); + else { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_duplicate_package_item) << - Arg::Str("PROCEDURE") << proc->name.toQuotedString()); + existingItems.procedures.checkDuplicate(proc->name); } proc->packageOwner = owner; proc->preserveDefaults = - existingProcs.exist(Signature(proc->name.object)) && arrays[i] == items; + existingItems.procedures.exist(Signature(proc->name.object)) && arrays[i] == items; proc->executeDdl(tdbb, elem.dsqlScratch, transaction, true); break; } - } - } - } - - SortedObjectsArray newFuncs(pool); - SortedObjectsArray newProcs(pool); - collectPackagedItems(tdbb, transaction, name, newFuncs, newProcs, true); + case PackageItemType::CONSTANT: + { + CreatePackageConstantNode* constant = elem.constant; - for (SortedObjectsArray::iterator i = existingFuncs.begin(); - i != existingFuncs.end(); ++i) - { - FB_SIZE_T pos; - bool found = newFuncs.find(Signature(pool, i->name), pos); + headerItems.constants.checkDuplicate(constant->name); + existingItems.constants.checkDuplicate(constant->name); - if (!found || !newFuncs[pos].defined) - { - status_exception::raise( - Arg::Gds(isc_dyn_funcnotdef_package) << i->name << name.toQuotedString()); - } - else if (newFuncs[pos] != *i) - { - status_exception::raise( - Arg::Gds(isc_dyn_funcsignat_package) << i->name << name.toQuotedString()); + constant->executeDdl(tdbb, elem.dsqlScratch, transaction, true); + break; + } + } } } - for (SortedObjectsArray::iterator i = existingProcs.begin(); - i != existingProcs.end(); ++i) - { - FB_SIZE_T pos; - bool found = newProcs.find(Signature(pool, i->name), pos); + PackageItemsHolder newItems(pool); + newItems.collectPackagedItems(tdbb, transaction, name, true, false); + existingItems.checkDefineMatch(pool, name, newItems); - if (!found || !newProcs[pos].defined) - { - status_exception::raise( - Arg::Gds(isc_dyn_procnotdef_package) << i->name << name.toQuotedString()); - } - else if (newProcs[pos] != *i) - { - status_exception::raise( - Arg::Gds(isc_dyn_procsignat_package) << i->name << name.toQuotedString()); - } - } executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE_BODY, name, {}); @@ -1031,9 +1625,7 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra { if (!FUN.RDB$PRIVATE_FLAG.NULL && FUN.RDB$PRIVATE_FLAG != 0) { - DropFunctionNode dropNode(pool, QualifiedName(FUN.RDB$FUNCTION_NAME, name.schema, name.object)); - dropNode.dsqlPass(dsqlScratch); - dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); + dropItem(tdbb, dsqlScratch, QualifiedName(FUN.RDB$FUNCTION_NAME, name.schema, name.object)); } else { @@ -1058,9 +1650,7 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra { if (!PRC.RDB$PRIVATE_FLAG.NULL && PRC.RDB$PRIVATE_FLAG != 0) { - DropProcedureNode dropNode(pool, QualifiedName(PRC.RDB$PROCEDURE_NAME, name.schema, name.object)); - dropNode.dsqlPass(dsqlScratch); - dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); + dropItem(tdbb, dsqlScratch, QualifiedName(PRC.RDB$PROCEDURE_NAME, name.schema, name.object)); } else { @@ -1075,6 +1665,8 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra } END_FOR + Constant::dropAllFromPackage(tdbb, transaction, name, true); + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE_BODY, name, {}); savePoint.release(); // everything is ok diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 839e3c8d0f6..f38632283e5 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -25,9 +25,191 @@ #include "../dsql/DdlNodes.h" #include "../common/classes/array.h" +#include "../common/classes/objects_array.h" +#include "../include/fb_exception.h" namespace Jrd { +enum class PackageItemType : USHORT +{ + FUNCTION = 0, + PROCEDURE, + CONSTANT, + META_SIZE +}; + +template +class ItemNames : public TArray +{ +public: + ItemNames() : TArray() + {} + + ItemNames(Firebird::MemoryPool& pool) : TArray(pool) + {} + + operator TArray&() + { + return *this; + } + + template + void addName(const QualifiedName& newName) + { + checkDuplicate(newName); + TArray::add(TType(newName.object)); + } + + template + void checkDuplicate(const QualifiedName& newName) + { + if constexpr (std::is_same_v) + { + if (!TArray::exist(newName.object)) + return; // The name is unique + } + else + { + // Cast + if (!TArray::exist(TType(newName.object))) + return; // The name is unique + } + + static_assert(size_t(IValue) >= 0 && size_t(IValue) < size_t(PackageItemType::META_SIZE), "Invalid item type"); + static const std::array names{ + "FUNCTION", + "PROCEDURE", + "CONSTANT", + }; + + Firebird::status_exception::raise( + Firebird::Arg::Gds(isc_no_meta_update) << + Firebird::Arg::Gds(isc_dyn_duplicate_package_item) << + Firebird::Arg::Str(names[size_t(IValue)]) << Firebird::Arg::Str(newName.toQuotedString())); + } +}; + + +class PackageItemsHolder +{ +public: + using ItemsSignatureArray = ItemNames, Signature>; + +public: + PackageItemsHolder() + { } + + PackageItemsHolder(Firebird::MemoryPool& pool) : + functions(pool), + procedures(pool), + constants(pool) + { } + + void drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema); + void checkDefineMatch(Firebird::MemoryPool& pool, const QualifiedName& packageAndSchema, const PackageItemsHolder& newItems); + void collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& packageAndSchema, bool details, bool collectConstants); + void clear(); + +public: + ItemsSignatureArray functions; + ItemsSignatureArray procedures; + ItemsSignatureArray constants; +}; // PackageItemCollector + +class PackageReferenceNode final : public TypedNode +{ +public: + PackageReferenceNode(Firebird::MemoryPool& pool, const QualifiedName& name, + const UCHAR itemType = blr_pkg_ref_item_const); + + Firebird::string internalPrint(NodePrinter& printer) const final; + + ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) final; + static DmlNode* parse(thread_db* tdbb, Firebird::MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp); + void genBlr(DsqlCompilerScratch* dsqlScratch) final; + + void setParameterName(dsql_par* parameter) const final; + void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) final; + + static bool constantExists(thread_db* tdbb, Jrd::jrd_tra* transaction, + const QualifiedName& name, bool* isPrivate = nullptr); + + // Compute descriptor for value expression. + void getDesc(thread_db*, CompilerScratch*, dsc*) final; + + ValueExprNode* copy(thread_db*, NodeCopier&) const final; + ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) final; + dsc* execute(thread_db*, Request*) const final; + + const char* getName() const + { + return m_fullName.object.c_str(); + } + +private: + NestConst m_constant; + const QualifiedName m_fullName; + + const UCHAR m_itemType; + ULONG m_impureOffset = 0; +}; + + +class CreatePackageConstantNode final : public DdlNode +{ +public: + CreatePackageConstantNode(Firebird::MemoryPool& pool, const MetaName& name, + dsql_fld* type = nullptr, ValueExprNode* value = nullptr, bool isPrivate = false) + : DdlNode(pool), + name(pool, name), + m_type(type), + m_value(value), + m_isPrivate(isPrivate) + { } + + Firebird::string internalPrint(NodePrinter& printer) const; + DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) final; + void checkPermission(thread_db* tdbb, jrd_tra* transaction) final; + void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) final; + + inline void makePublic() + { + m_isPrivate = false; + } + inline void makePrivate() + { + m_isPrivate = true; + } + +private: + dsc* makeConstantValue(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch); + void genOutputConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch); + void executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); + bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); + + +protected: + virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector) + { + statusVector << + Firebird::Arg::Gds(createAlterCode(create, alter, + isc_dsql_create_const_failed, isc_dsql_alter_const_failed, + isc_dsql_create_alter_const_failed)) << + Firebird::Arg::Str(name.toQuotedString()); + } + +public: + QualifiedName name; + bool create = false; + bool alter = false; + +private: + NestConst m_type; + NestConst m_value; + MetaId m_id = 0; + bool m_isPrivate = false; +}; + class CreateAlterPackageNode : public DdlNode { @@ -37,36 +219,45 @@ class CreateAlterPackageNode : public DdlNode static Item create(CreateAlterFunctionNode* function) { Item item; - item.type = FUNCTION; + item.type = PackageItemType::FUNCTION; item.function = function; - item.dsqlScratch = NULL; + item.dsqlScratch = nullptr; return item; } static Item create(CreateAlterProcedureNode* procedure) { Item item; - item.type = PROCEDURE; + item.type = PackageItemType::PROCEDURE; item.procedure = procedure; - item.dsqlScratch = NULL; + item.dsqlScratch = nullptr; return item; } - enum + static Item create(CreatePackageConstantNode* constant) { - FUNCTION, - PROCEDURE - } type; + Item item; + item.type = PackageItemType::CONSTANT; + item.constant = constant; + item.dsqlScratch = nullptr; + return item; + } + + PackageItemType type; union { CreateAlterFunctionNode* function; CreateAlterProcedureNode* procedure; + CreatePackageConstantNode* constant; }; DsqlCompilerScratch* dsqlScratch; }; + + using ItemsNameArray = ItemNames, MetaName>; + public: CreateAlterPackageNode(MemoryPool& pool, const QualifiedName& aName) : DdlNode(pool), @@ -77,6 +268,7 @@ class CreateAlterPackageNode : public DdlNode items(NULL), functionNames(pool), procedureNames(pool), + constantNames(pool), owner(pool) { } @@ -110,8 +302,9 @@ class CreateAlterPackageNode : public DdlNode bool createIfNotExistsOnly = false; Firebird::string source; Firebird::Array* items; - Firebird::SortedArray functionNames; - Firebird::SortedArray procedureNames; + ItemsNameArray functionNames; + ItemsNameArray procedureNames; + ItemsNameArray constantNames; std::optional ssDefiner; private: diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 0316c9b3b2a..89938377562 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -12262,4 +12262,79 @@ static void validateExpressions(thread_db* tdbb, const Array& vali } +// ---------------------------------------- +// ConstantExprNode implementation +// ---------------------------------------- + +static RegisterNode regInitConstantNode({blr_init_constant}); + +DmlNode* ConstantExprNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/) +{ + ConstantExprNode* node = FB_NEW_POOL(pool) ConstantExprNode(pool); + node->value = PAR_parse_value(tdbb, csb); + return node; +} + +ConstantExprNode* ConstantExprNode::dsqlPass(DsqlCompilerScratch* /*dsqlScratch*/) +{ + fb_assert(false); + return this; +} + +string ConstantExprNode::internalPrint(NodePrinter& printer) const +{ + StmtNode::internalPrint(printer); + + NODE_PRINT(printer, value); + + return "ConstantExprNode"; +} + +void ConstantExprNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ + dsqlScratch->appendUChar(blr_init_constant); + GEN_expr(dsqlScratch, value); +} + +ConstantExprNode* ConstantExprNode::copy(thread_db* tdbb, NodeCopier& copier) const +{ + ConstantExprNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) ConstantExprNode(*tdbb->getDefaultPool()); + node->value = value; + return node; +} + +ConstantExprNode* ConstantExprNode::pass1(thread_db* tdbb, CompilerScratch* csb) +{ + ExprNode::doPass1(tdbb, csb, value.getAddress()); + return this; +} + +ConstantExprNode* ConstantExprNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + impureOffset = csb->allocImpure(); + ExprNode::doPass2(tdbb, csb, value.getAddress()); + return this; +} + +const StmtNode* ConstantExprNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const +{ + if (request->req_operation == Request::req_evaluate) + { + dsc* scalar = EVL_expr(tdbb, request, value); + impure_value* impure = request->getImpure(impureOffset); + EVL_make_value(tdbb, scalar, impure); + + request->req_operation = Request::req_return; + } + + return parentStmt; +} + +dsc* ConstantExprNode::getImpureDsc(Request* request) const +{ + impure_value* impure = request->getImpure(impureOffset); + return &impure->vlu_desc; +} + + } // namespace Jrd diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index 51e6afe9b54..7adfecf7248 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -2100,6 +2100,31 @@ class UpdateOrInsertNode final : public TypedNode +{ +public: + explicit ConstantExprNode(MemoryPool& pool) + : TypedNode(pool) + { } + +public: + static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp); + + virtual Firebird::string internalPrint(NodePrinter& printer) const; + virtual ConstantExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); + virtual void genBlr(DsqlCompilerScratch* dsqlScratch); + virtual ConstantExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; + virtual ConstantExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); + virtual ConstantExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); + virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; + + dsc* getImpureDsc(Request* request) const; + +public: + NestConst value; + ULONG impureOffset; // Impure offset from request block. +}; + } // namespace #endif // DSQL_STMT_NODES_H diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 98625c8b02c..af18d954cd2 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -723,6 +723,7 @@ using namespace Firebird; %token TRUNCATE %token UNLIST %token WITHIN +%token CONSTANT // precedence declarations for expression evaluation @@ -882,6 +883,7 @@ using namespace Firebird; Jrd::SetBindNode* setBindNode; Jrd::SessionResetNode* sessionResetNode; Jrd::ForRangeNode::Direction forRangeDirection; + Jrd::CreatePackageConstantNode* createPackageConstantNode; } %include types.y @@ -3140,6 +3142,8 @@ package_item { $$ = CreateAlterPackageNode::Item::create($2); } | PROCEDURE procedure_clause_start ';' { $$ = CreateAlterPackageNode::Item::create($2); } + | CONSTANT package_const_item ';' + { $$ = CreateAlterPackageNode::Item::create($2); } ; %type alter_package_clause @@ -3227,6 +3231,13 @@ replace_package_body_clause { $$ = newNode($1); } ; +%type package_const_item +package_const_item + : symbol_package_const_name data_type_descriptor '=' value + { + $$ = newNode(*$1, $2, $4); + } + ; %type replace_schema_clause replace_schema_clause @@ -9749,6 +9760,11 @@ symbol_window_name : valid_symbol_name ; +%type symbol_package_const_name +symbol_package_const_name + : valid_symbol_name + ; + // symbols %type schema_opt_qualified_name @@ -10069,6 +10085,7 @@ non_reserved_word | SCHEMA | UNLIST | ERROR + | CONSTANT ; %% diff --git a/src/include/firebird/impl/blr.h b/src/include/firebird/impl/blr.h index 4fc7669a807..b63347991fb 100644 --- a/src/include/firebird/impl/blr.h +++ b/src/include/firebird/impl/blr.h @@ -532,4 +532,11 @@ #define blr_within_group_order (unsigned char) 235 +// Package const +#define blr_package_reference (unsigned char) 236 +#define blr_init_constant (unsigned char) 237 + +// Subcodes of blr_package_reference +#define blr_pkg_ref_item_const (unsigned char) 1 + #endif // FIREBIRD_IMPL_BLR_H diff --git a/src/include/firebird/impl/msg/dyn.h b/src/include/firebird/impl/msg/dyn.h index 598b1da1e64..a1b5ea2e5f4 100644 --- a/src/include/firebird/impl/msg/dyn.h +++ b/src/include/firebird/impl/msg/dyn.h @@ -311,4 +311,7 @@ FB_IMPL_MSG(DYN, 318, dyn_cannot_drop_non_emptyschema, -607, "HY", "000", "Canno FB_IMPL_MSG(DYN, 319, dyn_cannot_mod_obj_sys_schema, -607, "28", "000", "Cannot CREATE/ALTER/DROP @1 in SYSTEM schema") FB_IMPL_MSG(DYN, 320, dyn_cannot_create_reserved_schema, -607, "HY", "000", "Schema name @1 is reserved and cannot be created") FB_IMPL_MSG(DYN, 321, dyn_cannot_infer_schema, -901, "42", "000", "Cannot infer schema name as there is no valid schema in the search path") -FB_IMPL_MSG_SYMBOL(DYN, 322, dyn_dup_blob_filter, "Blob filter @1 already exists") +FB_IMPL_MSG_SYMBOL(DYN, 323, dyn_dup_blob_filter, "Blob filter @1 already exists") +FB_IMPL_MSG_SYMBOL(DYN, 334, dyn_const_not_found, "Constant @1 not found") +FB_IMPL_MSG_SYMBOL(DYN, 335, dyn_dup_const, "Constant @1 already exists") +FB_IMPL_MSG_SYMBOL(DYN, 336, dyn_non_deterministic_constant, "The expression to initialize constant \"@1\" must be a constant expression") diff --git a/src/include/firebird/impl/msg/gbak.h b/src/include/firebird/impl/msg/gbak.h index fb5a173b540..09677c985b0 100644 --- a/src/include/firebird/impl/msg/gbak.h +++ b/src/include/firebird/impl/msg/gbak.h @@ -418,3 +418,12 @@ FB_IMPL_MSG_NO_SYMBOL(GBAK, 419, "regular expression to skip schemas was already FB_IMPL_MSG_NO_SYMBOL(GBAK, 420, "regular expression to include schemas was already set") FB_IMPL_MSG_SYMBOL(GBAK, 421, gbak_plugin_schema_migration, "migrating @1 plugin objects to schema @2") FB_IMPL_MSG_SYMBOL(GBAK, 422, gbak_plugin_schema_migration_err, "error migrating @1 plugin objects to schema @2. Plugin objects will be in inconsistent state:") +FB_IMPL_MSG_NO_SYMBOL(GBAK, 527, "Compressor plugin '@1' not found") +FB_IMPL_MSG_NO_SYMBOL(GBAK, 528, " @1COM(PRESSOR) plugin name, compression level and threads count") +FB_IMPL_MSG_NO_SYMBOL(GBAK, 539, "Expected @1 instead of @2") +FB_IMPL_MSG_NO_SYMBOL(GBAK, 540, "compression level @1") +FB_IMPL_MSG_NO_SYMBOL(GBAK, 541, "'@1' compression plugin used") +FB_IMPL_MSG(GBAK, 542, gbak_writing_constants, -901, "00", "000", "writing constants") +FB_IMPL_MSG(GBAK, 543, gbak_writing_constant, -901, "00", "000", "writing constant %s for package %s") +FB_IMPL_MSG(GBAK, 544, gbak_constant, -901, "00", "000", "constant (in RDB$CONSTANTS)") +FB_IMPL_MSG(GBAK, 545, gbak_restoring_constant, -901, "00", "000", "restoring constant %s for package %s") diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index 7ba26ca0ac2..103a1abf83f 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1000,3 +1000,8 @@ FB_IMPL_MSG(JRD, 997, invalid_unqualified_name_list, -901, "HY", "000", "Invalid FB_IMPL_MSG(JRD, 998, no_user_att_while_restore, -901, "HY", "000", "User attachments are not allowed for the database being restored") FB_IMPL_MSG(JRD, 999, genseq_stepmustbe_nonzero, -833, "42", "000", "Argument STEP must be different than zero for function @1") FB_IMPL_MSG(JRD, 1000, argmustbe_exact_function, -833, "42", "000", "Arguments for @1 function must be exact numeric types") +FB_IMPL_MSG(JRD, 1076, not_defined_constant, -901, "42", "000", "The constant @1 is not defined in the package @2") +FB_IMPL_MSG(JRD, 1077, const_name, -901, "42", "000", "CONSTANT @1") +FB_IMPL_MSG(JRD, 1078, private_constant, -901, "42", "000", "The constant @1 is private to the package @2") +FB_IMPL_MSG(JRD, 1079, package_alias_help, -901, "42", "000", "Use an alias to resolve the conflict") +FB_IMPL_MSG(JRD, 1080, bad_constant_BLR, -901, "2F", "000", "Error while parsing constant @1's BLR") diff --git a/src/include/firebird/impl/msg/sqlerr.h b/src/include/firebird/impl/msg/sqlerr.h index a2687461bcd..af8cb1bf2ee 100644 --- a/src/include/firebird/impl/msg/sqlerr.h +++ b/src/include/firebird/impl/msg/sqlerr.h @@ -289,3 +289,6 @@ FB_IMPL_MSG(SQLERR, 1049, dsql_drop_schema_failed, -901, "42", "000", "DROP SCHE FB_IMPL_MSG(SQLERR, 1050, dsql_recreate_schema_failed, -901, "42", "000", "RECREATE SCHEMA @1 failed") FB_IMPL_MSG(SQLERR, 1051, dsql_alter_schema_failed, -901, "42", "000", "ALTER SCHEMA @1 failed") FB_IMPL_MSG(SQLERR, 1052, dsql_create_alter_schema_failed, -901, "42", "000", "CREATE OR ALTER SCHEMA @1 failed") +FB_IMPL_MSG(SQLERR, 1084, dsql_create_const_failed, -901, "42", "000", "CREATE CONSTANT @1 failed") +FB_IMPL_MSG(SQLERR, 1085, dsql_alter_const_failed, -901, "42", "000", "ALTER CONSTANT @1 failed") +FB_IMPL_MSG(SQLERR, 1086, dsql_create_alter_const_failed, -901, "42", "000", "CREATE OR ALTER CONSTANT @1 failed") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index eb0cfeae5a0..a3e8f4a0df2 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5955,6 +5955,11 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_no_user_att_while_restore = 335545318; isc_genseq_stepmustbe_nonzero = 335545319; isc_argmustbe_exact_function = 335545320; + isc_not_defined_constant = 335545396; + isc_const_name = 335545397; + isc_private_constant = 335545398; + isc_package_alias_help = 335545399; + isc_bad_constant_BLR = 335545400; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; @@ -6216,6 +6221,10 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_gbak_invalid_data = 336331094; isc_gbak_inv_bkup_ver2 = 336331096; isc_gbak_db_format_too_old2 = 336331100; + isc_gbak_writing_constants = 336331294; + isc_gbak_writing_constant = 336331295; + isc_gbak_constant = 336331296; + isc_gbak_restoring_constant = 336331297; isc_dsql_too_old_ods = 336397205; isc_dsql_table_not_found = 336397206; isc_dsql_view_not_found = 336397207; @@ -6352,6 +6361,9 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_dsql_recreate_schema_failed = 336397338; isc_dsql_alter_schema_failed = 336397339; isc_dsql_create_alter_schema_failed = 336397340; + isc_dsql_create_const_failed = 336397372; + isc_dsql_alter_const_failed = 336397373; + isc_dsql_create_alter_const_failed = 336397374; isc_gsec_cant_open_db = 336723983; isc_gsec_switches_error = 336723984; isc_gsec_no_op_spec = 336723985; diff --git a/src/isql/FrontendParser.cpp b/src/isql/FrontendParser.cpp index 64218057132..45214320f09 100644 --- a/src/isql/FrontendParser.cpp +++ b/src/isql/FrontendParser.cpp @@ -491,6 +491,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() static constexpr std::string_view TOKEN_VIEWS("VIEWS"); static constexpr std::string_view TOKEN_WIRE_STATISTICS("WIRE_STATISTICS"); static constexpr std::string_view TOKEN_WIRE_STATS("WIRE_STATS"); + static constexpr std::string_view TOKEN_CONSTNATS("CONSTANTS"); switch (const auto showCommandToken = lexer.getToken(); showCommandToken.type) { @@ -649,6 +650,8 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() } else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_VIEWS, 4)) return parsed.value(); + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_CONSTNATS, 4)) + return parsed.value(); else if (text.length() >= 9 && TOKEN_WIRE_STATISTICS.find(text) == 0 || text == TOKEN_WIRE_STATS) { diff --git a/src/isql/FrontendParser.h b/src/isql/FrontendParser.h index 28a1b9cc3ba..1a029e78352 100644 --- a/src/isql/FrontendParser.h +++ b/src/isql/FrontendParser.h @@ -119,6 +119,7 @@ class FrontendParser struct ShowVersionNode {}; struct ShowViewsNode { std::optional name; }; struct ShowWireStatsNode {}; + struct ShowConstantsNode { std::optional name; }; using AnySetNode = std::variant< SetNode, @@ -184,6 +185,7 @@ class FrontendParser ShowVersionNode, ShowViewsNode, ShowWireStatsNode, + ShowConstantsNode, InvalidNode >; diff --git a/src/isql/show.epp b/src/isql/show.epp index 0b7a0ddcda1..2aad99292ca 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -118,6 +118,8 @@ static processing_state show_trigger(const std::optional& n static processing_state show_users(); static processing_state show_users12(); static processing_state show_wireStats(); +static processing_state show_constants(const std::optional& name, const char* msg = nullptr); +static processing_state show_constant(const SCHAR* constantName); const char* const spaces = " "; static TEXT Print_buffer[512]; @@ -2571,6 +2573,11 @@ processing_state SHOW_metadata(const FrontendParser::AnyShowNode& node) return show_wireStats(); }, + [](const FrontendParser::ShowConstantsNode& node) + { + return show_constants(node.name); + }, + [](auto& arg) { static_assert(FrontendParser::AlwaysFalseV, @@ -6735,3 +6742,146 @@ static processing_state show_wireStats() return SKIP; } + + +static void print_constant_type(const char* fieldName, const char* schemaName) +{ + FOR FLD IN RDB$FIELDS WITH + FLD.RDB$SCHEMA_NAME EQ schemaName AND + FLD.RDB$FIELD_NAME EQ fieldName + + // Decide if this is a user-created domain + if (!fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) || FLD.RDB$SYSTEM_FLAG == 1) + { + fb_utils::exact_name(FLD.RDB$FIELD_NAME); + isqlGlob.printf("%s) ", FLD.RDB$FIELD_NAME); + } + + const QualifiedMetaString domainName(FLD.RDB$FIELD_NAME, FLD.RDB$SCHEMA_NAME); + if (!ISQL_printNumericType(domainName, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SUB_TYPE, + FLD.RDB$FIELD_PRECISION, FLD.RDB$FIELD_SCALE)) + { + return; + } + + // Use RDB$CHARACTER_LENGTH instead of RDB$FIELD_LENGTH + // FSG 19.Nov.2000 + if ((FLD.RDB$FIELD_TYPE == blr_text || FLD.RDB$FIELD_TYPE == blr_varying) && + !FLD.RDB$CHARACTER_LENGTH.NULL) + { + isqlGlob.printf("(%d)", FLD.RDB$CHARACTER_LENGTH); + } + + // Show international character sets and collations + + SSHORT char_set_id = 0; + if (!FLD.RDB$CHARACTER_SET_ID.NULL) + char_set_id = FLD.RDB$CHARACTER_SET_ID; + + SSHORT collation = 0; + if (!FLD.RDB$COLLATION_ID.NULL) + collation = FLD.RDB$COLLATION_ID; + + if (((FLD.RDB$FIELD_TYPE == blr_text || + FLD.RDB$FIELD_TYPE == blr_varying) && FLD.RDB$FIELD_SUB_TYPE != fb_text_subtype_binary) || + FLD.RDB$FIELD_TYPE == blr_blob && FLD.RDB$FIELD_SUB_TYPE == isc_blob_text) + show_charsets(char_set_id, collation); + + + if (fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) && !FLD.RDB$DEFAULT_SOURCE.NULL) + { + isqlGlob.printf(" "); + SHOW_print_metadata_text_blob(isqlGlob.Out, &FLD.RDB$DEFAULT_SOURCE); + } + + END_FOR + ON_ERROR + ISQL_errmsg (fbStatus); + END_ERROR; +} + +static processing_state show_constants(const std::optional& name, const char* msg) +{ + bool first = true; + + if (name.has_value()) + { + const QualifiedMetaString& constant = name.value(); + + FOR CONST IN RDB$CONSTANTS WITH + CONST.RDB$SCHEMA_NAME EQ constant.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ constant.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ constant.object.c_str() + SORTED BY CONST.RDB$CONSTANT_NAME + + bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + const char* type = isPrivate ? "BODY" : "HEADER"; + + if (first && msg) + isqlGlob.printf("%s%s", msg, NEWLINE); + + first = false; + show_constant(constant.object.c_str()); + + END_FOR + ON_ERROR + ISQL_errmsg(fbStatus); + return ps_ERR; + END_ERROR; + + return SKIP; + } + + // All constants + + FOR CONST IN RDB$CONSTANTS + SORTED BY CONST.RDB$CONSTANT_NAME + bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + const char* type = isPrivate ? "BODY" : "HEADER"; + + if (first && msg) + isqlGlob.printf("%s%s", msg, NEWLINE); + + first = false; + fb_utils::exact_name(CONST.RDB$CONSTANT_NAME); + show_constant(CONST.RDB$CONSTANT_NAME); + + END_FOR + ON_ERROR + ISQL_errmsg(fbStatus); + return ps_ERR; + END_ERROR; + + return SKIP; +} + +static processing_state show_constant(const SCHAR* constantName) +{ + FOR CONST IN RDB$CONSTANTS + WITH CONST.RDB$CONSTANT_NAME EQ constantName + SORTED BY CONST.RDB$CONSTANT_NAME + + bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + + if (!CONST.RDB$PACKAGE_NAME.NULL) + { + MetaString packageName(CONST.RDB$PACKAGE_NAME); + isqlGlob.printf("%s (%s %s)%-20s", constantName, packageName.c_str(), isPrivate ? "BODY" : "HEADER", " "); + } + else + isqlGlob.printf("%-20s", constantName); + + fb_utils::exact_name(CONST.RDB$FIELD_SOURCE); + fb_utils::exact_name(CONST.RDB$SCHEMA_NAME); + print_constant_type(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME); + + isqlGlob.printf(NEWLINE); + + END_FOR + ON_ERROR + ISQL_errmsg(fbStatus); + return ps_ERR; + END_ERROR; + + return SKIP; +} diff --git a/src/isql/tests/FrontendParserTest.cpp b/src/isql/tests/FrontendParserTest.cpp index d4f62903351..4f3a897e16d 100644 --- a/src/isql/tests/FrontendParserTest.cpp +++ b/src/isql/tests/FrontendParserTest.cpp @@ -609,6 +609,13 @@ BOOST_AUTO_TEST_CASE(ParseShowTest) "show wire_stat"))); BOOST_TEST(std::holds_alternative(parseShow( "show wire_statistics"))); + + BOOST_TEST(std::holds_alternative(parseShow( + "show const"))); + BOOST_TEST(std::holds_alternative(parseShow( + "show constant"))); + BOOST_TEST(std::holds_alternative(parseShow( + "show constants"))); } diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index 7a79bc21a24..83f11e53f3b 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -98,6 +98,7 @@ namespace Jrd class Validation; class Applier; enum InternalRequest : USHORT; + class Constant; struct DSqlCacheItem diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp new file mode 100644 index 00000000000..2f3dad48e2c --- /dev/null +++ b/src/jrd/Constant.epp @@ -0,0 +1,314 @@ +/* + * PROGRAM: Firebird CONSTANTS implementation. + * MODULE: Constant.epp + * DESCRIPTION: Routine to cache and reload constants + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Artyom Abakumov + * for Red Soft Corporation. + * + * Copyright (c) 2025 Red Soft Corporation + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "../jrd/Constant.h" + +#include "../jrd/tra.h" +#include "../jrd/exe_proto.h" +#include "../jrd/dfw_proto.h" +#include "../common/dsc_proto.h" +#include "../jrd/met_proto.h" +#include "../jrd/met.h" +#include "../jrd/Statement.h" // Jrd::Statement + +using namespace Firebird; +using namespace Jrd; + +DATABASE DB = FILENAME "ODS.RDB"; + +//---------------------- + +Constant* Constant::lookup(thread_db* tdbb, MetaId id) +{ + Constant* constant = MetadataCache::get(tdbb)->getConstant(tdbb, id); + return constant; +} + +Constant* Constant::lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags) +{ + return MetadataCache::getVersioned(tdbb, name, flags); +} + +Constant* Constant::create(thread_db* tdbb, MemoryPool& pool, Cached::Constant* perm) +{ + return FB_NEW_POOL(perm->getPool()) Constant(perm); +} + + +ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) +{ + Attachment* attachment = tdbb->getAttachment(); + jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); + Database* dbb = tdbb->getDatabase(); + MetadataCache* mdc = dbb->dbb_mdc; + + MemoryPool& pool = getPermanent()->getPool(); + bool found = false; + + //try + { + AutoCacheRequest request_fun(tdbb, irq_l_constants, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request_fun) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$CONSTANT_ID EQ this->getId() + { + found = true; + + getPermanent()->setName(QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, + (CONST.RDB$PACKAGE_NAME.NULL ? nullptr : CONST.RDB$PACKAGE_NAME))); + // getPermanent()->owner = CONST.RDB$OWNER_NAME; + + TriState ssDefiner; + if (!CONST.RDB$PACKAGE_NAME.NULL) + { + AutoCacheRequest requestHandle(tdbb, irq_l_procedure_pkg_class, IRQ_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle) + PKG IN RDB$PACKAGES + CROSS SCH IN RDB$SCHEMAS + OVER RDB$SCHEMA_NAME + WITH PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME + + if (!PKG.RDB$SECURITY_CLASS.NULL) + { + getPermanent()->setSecurityName(QualifiedName(PKG.RDB$SECURITY_CLASS, SCH.RDB$SECURITY_CLASS)); + } + + // SQL SECURITY of function must be the same if it's defined in package + if (!PKG.RDB$SQL_SECURITY.NULL) + ssDefiner = (bool) PKG.RDB$SQL_SECURITY; + + END_FOR + } + + if (!ssDefiner.isAssigned()) + { + ssDefiner = MET_get_ss_definer(tdbb, CONST.RDB$SCHEMA_NAME); + } + + if (ssDefiner.asBool()) + invoker = dbb->getUserId(getPermanent()->owner); + + makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); + } + END_FOR + } + + return found ? (this->flReload ? ScanResult::REPEAT : ScanResult::COMPLETE) : ScanResult::MISS; +} + + +std::optional Constant::getIdByName(thread_db* tdbb, const QualifiedName& name) +{ + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); + std::optional id; + + AutoCacheRequest request(tdbb, irq_l_const_id, IRQ_REQUESTS); + + FOR (REQUEST_HANDLE request) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ name.object.c_str() + { + id = CONST.RDB$CONSTANT_ID; + } + END_FOR + + return id; +} + +ScanResult Constant::reload(thread_db* tdbb, ObjectBase::Flag) +{ + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); + AutoCacheRequest request(tdbb, irq_l_const_name, IRQ_REQUESTS); + + bool found = false; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE metaTransaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$CONSTANT_ID EQ this->getId() + // FOR (REQUEST_HANDLE request) + // CONST IN RDB$CONSTANTS + // WITH CONST.RDB$SCHEMA_NAME EQ this->getName().schema.c_str() AND + // CONST.RDB$PACKAGE_NAME EQ this->getName().package.c_str() AND + // CONST.RDB$CONSTANT_NAME EQ this->getName().object.c_str() + { + + if (compiling) + return ScanResult::REPEAT; + + found = true; + + MemoryPool* const csb_pool = dbb->createPool(ALLOC_ARGS0); + Jrd::ContextPoolHolder context(tdbb, csb_pool); + + makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); + + // parseBlr() in makeValue could set FLAG_RELOAD again + } + END_FOR + + return found ? ScanResult::COMPLETE : ScanResult::MISS; +} + +int Constant::objectType() +{ + return obj_package_constant; +} + +void Constant::checkReload(thread_db* tdbb) const +{ + if (flReload) + getPermanent()->reload(tdbb, 0); +} + + +void Constant::drop(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name) +{ + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + bool found = false; + + // missing ID in the node - therefore use getVersioned() with appropriate flags + // instead: MetadataCache::oldVersion(tdbb, id); + MetadataCache::getVersioned(tdbb, name, CacheFlag::OLD_DROP); + MetaId id; + + AutoCacheRequest request(tdbb, drq_e_pkg_const_name, DYN_REQUESTS); + FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ name.object.c_str() + { + found = true; + id = CONST.RDB$CONSTANT_ID; + + ERASE CONST; + } + END_FOR + + if (found) + { + MetadataCache::erase(tdbb, id); + } + + savePoint.release(); // everything is ok +} + +void Constant::dropAllFromPackage(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& parent, bool privateFlag) +{ + AutoCacheRequest eraseConstantRequest(tdbb, drq_e_pkg_consts_by_flag, DYN_REQUESTS); + FOR (REQUEST_HANDLE eraseConstantRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ parent.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ parent.object.c_str() AND + CONST.RDB$PRIVATE_FLAG EQ privateFlag + { + drop(tdbb, transaction, QualifiedName(CONST.RDB$CONSTANT_NAME, parent.schema, parent.object)); + ERASE CONST; + } + END_FOR +} + +dsc Constant::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name) +{ + dsc desc; + + FbLocalStatus status; + AutoCacheRequest getConstantDscRequest(tdbb, drq_l_pkg_const_dsc, DYN_REQUESTS); + FOR(REQUEST_HANDLE getConstantDscRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS CROSS FLD IN RDB$FIELDS + WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND + FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE + + const bool succeed = DSC_make_descriptor(&desc, + FLD.RDB$FIELD_TYPE, + FLD.RDB$FIELD_SCALE, + FLD.RDB$FIELD_LENGTH, + FLD.RDB$FIELD_SUB_TYPE, + CSetId(FLD.RDB$CHARACTER_SET_ID), + CollId(FLD.RDB$COLLATION_ID)); + + if (succeed) + { + return desc; + } + END_FOR + // TODO: Error + return desc; +} + + +void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) +{ + MemoryPool* const csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); + Jrd::ContextPoolHolder context(tdbb, csb_pool); + + try + { + AutoPtr csb(FB_NEW_POOL(*csb_pool) CompilerScratch(*csb_pool)); + + try + { + this->parseBlr(tdbb, csb, &blob_id, nullptr); + + auto routineRequest = getStatement()->makeRootRequest(tdbb); + const dsc* temp = EVL_expr(tdbb, routineRequest, static_cast(csb->csb_node)); + + // The m_value has a string that is allocated with the tdbb pool + // The constant cache lives longer than the tdbb pool + // so we clear the value to prevent invalid deletions + m_value = {}; + if (temp) + EVL_make_value(tdbb, temp, &m_value); + else + m_value.vlu_desc.setNull(); + } + catch (const Exception& ex) + { + StaticStatusVector temp_status; + ex.stuffException(temp_status); + + const string name = this->getName().toQuotedString(); + (Arg::Gds(isc_bad_constant_BLR) << Arg::Str(name) << Arg::Str(name) + << Arg::StatusVector(temp_status.begin())).raise(); + } + } + catch (const Exception&) + { + attachment->att_database->deletePool(csb_pool); + throw; + } +} diff --git a/src/jrd/Constant.h b/src/jrd/Constant.h new file mode 100644 index 00000000000..b1e54fb0b6f --- /dev/null +++ b/src/jrd/Constant.h @@ -0,0 +1,123 @@ +/* + * PROGRAM: Firebird CONSTANTS implementation. + * MODULE: Constant.h + * DESCRIPTION: Routine to cache and reload constants + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Artyom Abakumov + * for Red Soft Corporation. + * + * Copyright (c) 2025 Red Soft Corporation + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + + +#ifndef JRD_CONSTANT_H +#define JRD_CONSTANT_H + +#include "../jrd/Routine.h" +#include "firebird.h" +#include "../jrd/obj.h" +#include "../jrd/val.h" +#include "../jrd/lck.h" + +namespace Jrd +{ + +class ValueListNode; + +class Constant : public Routine +{ +public: + static Constant* lookup(thread_db* tdbb, MetaId id); + static Constant* lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags); + +public: + static const enum lck_t LOCKTYPE = LCK_constant_rescan; + +private: + explicit Constant(Cached::Constant* perm) + : Routine(perm->getPool()), + cachedConstant(perm) + { } + +public: + explicit Constant(MemoryPool& p) + : Routine(p) + { } + + static Constant* create(thread_db* tdbb, MemoryPool& pool, Cached::Constant* perm); + ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags); + static std::optional getIdByName(thread_db* tdbb, const QualifiedName& name); + void checkReload(thread_db* tdbb) const override; + + static const char* objectFamily(void*) + { + return "constant"; + } + +public: + int getObjectType() const noexcept final + { + return objectType(); + } + + SLONG getSclType() const noexcept final + { + return obj_package_constant; + } + + ScanResult reload(thread_db* tdbb, ObjectBase::Flag fl); + + static int objectType(); + +public: + void makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id); + + inline const dsc& getValue() const + { + return m_value.vlu_desc; + } + + static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name); + static void drop(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name); + static void dropAllFromPackage(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& parent, bool privateFlag); + // static dsc* makeConstantValue(thread_db* tdbb, UCHAR* blr, ULONG blrLength); + static dsc* readValue(thread_db* tdbb, Request* request, const QualifiedName& name); + // static dsc* readConstantValue2(thread_db* tdbb, Request* request, const MetaName& packageName, const MetaName& itemName); + +private: + virtual ~Constant() override + { + // The string is allocated via tdbb pool and it is dead by now + // delete m_value.vlu_string; + } + +public: + Cached::Constant* cachedConstant; // entry in the cache + + Cached::Constant* getPermanent() const noexcept override + { + return cachedConstant; + } + +private: + impure_value m_value{}; +}; + +} // namespace Jrd + +#endif // JRD_CONSTANT_H diff --git a/src/jrd/Resources.h b/src/jrd/Resources.h index cb7f2f9276d..dac514d70cb 100644 --- a/src/jrd/Resources.h +++ b/src/jrd/Resources.h @@ -42,6 +42,7 @@ class DbTriggers; class CharSetVers; class IndexPermanent; class IndexVersion; +class Constant; namespace Cached { @@ -52,6 +53,7 @@ namespace Cached typedef CacheElement Function; typedef CacheElement Triggers; typedef CacheElement Index; + typedef CacheElement Constant; } class Resources; @@ -66,6 +68,7 @@ union VersionedPartPtr CharSetVers* charset; DbTriggers* triggers; IndexVersion* index; + Constant* constant; }; class VersionedObjects : public pool_alloc_rpt, @@ -120,6 +123,7 @@ template <> inline jrd_rel*& VersionedObjects::object(FB_SIZE_T n) { re template <> inline CharSetVers*& VersionedObjects::object(FB_SIZE_T n) { return data[n].charset; } template <> inline DbTriggers*& VersionedObjects::object(FB_SIZE_T n) { return data[n].triggers; } template <> inline IndexVersion*& VersionedObjects::object(FB_SIZE_T n) { return data[n].index; } +template <> inline Constant*& VersionedObjects::object(FB_SIZE_T n) { return data[n].constant; } template <> inline Function* VersionedObjects::object(FB_SIZE_T n) const { return data[n].function; } template <> inline jrd_prc* VersionedObjects::object(FB_SIZE_T n) const { return data[n].procedure; } @@ -127,6 +131,7 @@ template <> inline jrd_rel* VersionedObjects::object(FB_SIZE_T n) const template <> inline CharSetVers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].charset; } template <> inline DbTriggers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].triggers; } template <> inline IndexVersion* VersionedObjects::object(FB_SIZE_T n) const { return data[n].index; } +template <> inline Constant* VersionedObjects::object(FB_SIZE_T n) const { return data[n].constant; } template @@ -194,7 +199,6 @@ class CachedResource template <> jrd_rel* CachedResource::operator()(thread_db* tdbb) const; - class Resources final { public: @@ -283,7 +287,8 @@ class Resources final procedures(p, versionCurrentPosition), functions(p, versionCurrentPosition), triggers(p, versionCurrentPosition), - indices(p, versionCurrentPosition) + indices(p, versionCurrentPosition), + constants(p, versionCurrentPosition) { } ~Resources(); @@ -294,6 +299,7 @@ class Resources final RscArray functions; RscArray triggers; RscArray indices; + RscArray constants; }; // specialization @@ -303,6 +309,7 @@ template <> inline const Resources::RscArray& Resour template <> inline const Resources::RscArray& Resources::objects() const { return charSets; } template <> inline const Resources::RscArray& Resources::objects() const { return triggers; } template <> inline const Resources::RscArray& Resources::objects() const { return indices; } +template <> inline const Resources::RscArray& Resources::objects() const { return constants; } namespace Rsc { @@ -312,6 +319,7 @@ namespace Rsc typedef CachedResource CSet; typedef CachedResource Trig; typedef CachedResource Idx; + typedef CachedResource Const; // useless? }; //namespace Rsc diff --git a/src/jrd/constants.h b/src/jrd/constants.h index 7ffa745bd5c..32d50f4841f 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -154,6 +154,7 @@ inline constexpr int GEN_SECCLASS_PREFIX_LEN = 4; inline constexpr const char* PROCEDURES_GENERATOR = "RDB$PROCEDURES"; inline constexpr const char* FUNCTIONS_GENERATOR = "RDB$FUNCTIONS"; +inline constexpr const char* const CONSTANTS_GENERATOR = "RDB$CONSTANTS"; // Automatically created check constraints for unnamed PRIMARY and UNIQUE declarations. inline constexpr const char* IMPLICIT_INTEGRITY_PREFIX = "INTEG_"; diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 46a2b8768e6..911685c227a 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -130,6 +130,8 @@ #include "../jrd/shut_proto.h" #include "../jrd/ProtectRelations.h" +#include "../jrd/Constant.h" + #ifdef HAVE_UNISTD_H #include #endif @@ -458,6 +460,9 @@ static ISC_STATUS getErrorCodeByObjectType(int obj_type) case obj_package_body: err_code = isc_package_name; break; + case obj_package_constant: + err_code = isc_const_name; + break; default: fb_assert(false); } @@ -931,6 +936,26 @@ namespace static void checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction); }; + class ConstantManager : public RoutineManager, MetadataCache::getPerm> + { + public: + static const char* getTypeStr() + { + return "constant"; + } + + // static void clearId(Jrd::Attachment* attachment, USHORT id) + // { + // attachment->att_constants[id] = nullptr; + // } + + static Routine* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile); + static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work, + SSHORT validBlr); + static void checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction); + }; + // These methods cannot be defined inline, because GPRE generates wrong code. Routine* FunctionManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, @@ -1110,6 +1135,35 @@ namespace ERR_post(status); } } + + Routine* ConstantManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, + bool compile) + { + Jrd::Attachment* attachment = tdbb->getAttachment(); + AutoCacheRequest handle(tdbb, irq_c_const_dpd, IRQ_REQUESTS); + Routine* routine = nullptr; + + FOR(REQUEST_HANDLE handle) + X IN RDB$CONSTANTS WITH + X.RDB$CONSTANT_NAME EQ work->dfw_name.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), "") + { + if (!X.RDB$CONSTANT_BLR.NULL) + blobId = X.RDB$CONSTANT_BLR; + + routine = MetadataCache::getVersioned(tdbb, + work->getQualifiedName(), compile ? 0 : CacheFlag::MINISCAN); + } + END_FOR + + return routine; + } + + void ConstantManager::validate(thread_db*, jrd_tra*, DeferredWork*, SSHORT) + { } + + void ConstantManager::checkParamDependencies(thread_db*, DeferredWork*, jrd_tra*) + { } } // namespace static inline constexpr deferred_task task_table[] = @@ -1159,6 +1213,9 @@ static inline constexpr deferred_task task_table[] = { dfw_clear_cache, clear_cache }, { dfw_change_repl_state, change_repl_state }, { dfw_set_statistics, set_statistics }, + { dfw_create_package_constant, ConstantManager::createRoutine }, + { dfw_modify_package_constant, ConstantManager::modifyRoutine }, + { dfw_delete_package_constant, ConstantManager::deleteRoutine }, { dfw_null, NULL } }; @@ -2799,6 +2856,9 @@ static bool find_depend_in_dfw(thread_db* tdbb, case obj_udf: dfw_type = dfw_delete_function; break; + case obj_package_constant: + dfw_type = dfw_delete_package_constant; + break; default: fb_assert(false); break; @@ -2812,6 +2872,7 @@ static bool find_depend_in_dfw(thread_db* tdbb, (work->dfw_type == dfw_modify_procedure && dfw_type == dfw_delete_procedure) || (work->dfw_type == dfw_modify_field && dfw_type == dfw_delete_global) || (work->dfw_type == dfw_modify_trigger && dfw_type == dfw_delete_trigger) || + (work->dfw_type == dfw_modify_package_constant && dfw_type == dfw_delete_package_constant) || (work->dfw_type == dfw_modify_function && dfw_type == dfw_delete_function)) && work->dfw_schema == object_name.schema && work->dfw_name == object_name.object.c_str() && work->dfw_package.isEmpty() && diff --git a/src/jrd/drq.h b/src/jrd/drq.h index 1f8e344d3b0..b42da1b085a 100644 --- a/src/jrd/drq.h +++ b/src/jrd/drq.h @@ -243,6 +243,18 @@ enum drq_type_t drq_l_rel_con, // lookup relation constraint drq_l_rel_fld_name, // lookup relation field name + // constants + drq_l_pkg_consts, // get all constants of a package + drq_l_pkg_const_flag, // lookup for a flag of a package constant by its name + drq_l_pkg_const_dsc, // lookup for dsc of a package constant + drq_l_pkg_const_blr, // lookup for blr of a package constant + drq_s_pkg_const, // store a package constant + drq_m_pkg_const, // modify a package constant by its name + drq_e_pkg_const_name, // erase constant by its name + drq_e_pkg_consts_by_flag, // erase all public or private constants of a package + drq_e_pkg_const_dsc, // erase dsc of a package constant + drq_g_nxt_const_id, // lookup next constant ID + drq_MAX }; diff --git a/src/jrd/fields.h b/src/jrd/fields.h index 05e2885456a..06aed285cc5 100644 --- a/src/jrd/fields.h +++ b/src/jrd/fields.h @@ -237,3 +237,8 @@ FIELD(fld_sch_name , nam_sch_name , dtype_text , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata , NULL , true , ODS_14_0) FIELD(fld_text_max , nam_text_max , dtype_varying, MAX_VARY_COLUMN_SIZE / METADATA_BYTES_PER_CHAR * METADATA_BYTES_PER_CHAR, dsc_text_type_metadata, NULL, true, ODS_14_0) + + FIELD(fld_const_name , nam_const_name , dtype_varying , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata , NULL , false , ODS_13_1) + FIELD(fld_const_id , nam_const_id , dtype_long , sizeof(SLONG) , 0 , NULL , true , ODS_14_0) + FIELD(fld_const_blr , nam_const_blr , dtype_blob , BLOB_SIZE , isc_blob_blr , NULL , true , ODS_13_1) + FIELD(fld_const_source , nam_const_source , dtype_blob , BLOB_SIZE , isc_blob_text , NULL , true , ODS_13_1) diff --git a/src/jrd/ids.h b/src/jrd/ids.h index 5d240d311d5..644b789054e 100644 --- a/src/jrd/ids.h +++ b/src/jrd/ids.h @@ -87,3 +87,10 @@ static_assert(f_sec_crt_u_type == 1, "Wrong field id"); static_assert(f_mon_tab_rec_stat_id == 3, "Wrong field id"); static_assert(f_tz_name == 1, "Wrong field id"); + static_assert(f_const_name == 0, "Wrong field id"); + static_assert(f_const_id == 1, "Wrong field id"); + static_assert(f_const_package == 2, "Wrong field id"); + static_assert(f_const_field == 3, "Wrong field id"); + static_assert(f_const_private_flag == 4, "Wrong field id"); + static_assert(f_const_blr == 5, "Wrong field id"); + static_assert(f_const_source == 6, "Wrong field id"); diff --git a/src/jrd/idx.h b/src/jrd/idx.h index 0e7811a7870..5c8485872cc 100644 --- a/src/jrd/idx.h +++ b/src/jrd/idx.h @@ -524,6 +524,16 @@ static inline constexpr struct ini_idx_t indices[] = SEGMENT(f_pubtab_tab_schema, idx_metadata), // table schema name SEGMENT(f_pubtab_tab_name, idx_metadata), // table name SEGMENT(f_pubtab_pub_name, idx_metadata) // publication name + }}, + // define index RDB$INDEX_98 for RDB$CONSTANTS unique RDB$SCHEMA_NAME, RDB$PACKAGE_NAME, RDB$CONSTANT_NAME; + INDEX(98, rel_constants, idx_unique, 3, ODS_14_0) + SEGMENT(f_const_package_schema, idx_metadata), // table schema name + SEGMENT(f_const_package, idx_metadata), // package name + SEGMENT(f_const_name, idx_metadata) // constant name + }}, + // define index RDB$INDEX_99 for RDB$CONSTANTS unique RDB$CONSTANT_ID; + INDEX(99, rel_constants, idx_unique, 1, ODS_14_0) + SEGMENT(f_const_id, idx_numeric) // constant id }} }; diff --git a/src/jrd/irq.h b/src/jrd/irq.h index e4ee2c405df..5c84537a793 100644 --- a/src/jrd/irq.h +++ b/src/jrd/irq.h @@ -128,6 +128,7 @@ enum irq_type_t irq_m_fields7, // process a modification of RDB$FIELDS for functions irq_m_fields8, // process a modification of RDB$FIELDS for functions (TYPE OF COLUMN) irq_m_fields9, // process a modification of RDB$FIELDS for packaged functions (TYPE OF COLUMN) + irq_m_fields10, // process a modification of RDB$FIELDS for packaged constants (TYPE OF COLUMN) irq_l_relfield, // lookup a relation field irq_verify_trusted_role, // ensure trusted role exists @@ -181,6 +182,10 @@ enum irq_type_t irq_index_id_erase, // cleanup index ID irq_get_index_by_name, // find appropriate index irq_l_index_cnstrt, // lookup index for constraint + irq_l_const_name, + irq_l_const_id, + irq_l_constants, + irq_c_const_dpd, // get constant dependencies irq_MAX }; diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 132170ea5e8..7eb93e3cfe6 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -58,6 +58,7 @@ #include "../common/os/guid.h" #include "../jrd/sbm.h" #include "../jrd/scl.h" +#include "../jrd/Constant.h" #include "../jrd/Routine.h" #include "../jrd/ExtEngineManager.h" #include "../jrd/Attachment.h" diff --git a/src/jrd/lck.cpp b/src/jrd/lck.cpp index a1868ed441e..d9f20aae664 100644 --- a/src/jrd/lck.cpp +++ b/src/jrd/lck.cpp @@ -584,6 +584,7 @@ static lck_owner_t get_owner_type(enum lck_t lock_type) case LCK_idx_rescan: case LCK_prc_rescan: case LCK_fun_rescan: + case LCK_constant_rescan: case LCK_cs_rescan: case LCK_dbwide_triggers: owner_type = LCK_OWNER_database; diff --git a/src/jrd/lck.h b/src/jrd/lck.h index ce927051978..24e74ed468b 100644 --- a/src/jrd/lck.h +++ b/src/jrd/lck.h @@ -67,6 +67,7 @@ enum lck_t : UCHAR { LCK_idx_rescan, // Index rescan lock LCK_prc_rescan, // Procedure rescan lock LCK_fun_rescan, // Function existence lock + LCK_constant_rescan, // Constant existence lock LCK_rel_partners, // Relation partners lock LCK_crypt, // Crypt lock for single crypt thread LCK_crypt_status, // Notifies about changed database encryption status diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 1bef423ced5..8cebc68b9aa 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -97,6 +97,7 @@ #include "../jrd/Function.h" #include "../jrd/trace/TraceJrdHelpers.h" #include "firebird/impl/msg_helper.h" +#include "../jrd/Constant.h" #ifdef HAVE_CTYPE_H @@ -300,6 +301,7 @@ void MetadataCache::cleanup(thread_db* tdbb) mdc_procedures.cleanup(tdbb); mdc_functions.cleanup(tdbb); mdc_charsets.cleanup(tdbb); + mdc_constants.cleanup(tdbb); for (unsigned i = 0; i < DB_TRIGGERS_COUNT; ++i) { @@ -766,6 +768,32 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const } END_FOR + + request.reset(tdbb, irq_m_fields10, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) + DEP IN RDB$DEPENDENCIES CROSS + CONST IN RDB$CONSTANTS + WITH DEP.RDB$DEPENDED_ON_SCHEMA_NAME EQ schemaName->dsc_address AND + DEP.RDB$DEPENDED_ON_NAME EQ fieldSource->dsc_address AND + DEP.RDB$DEPENDED_ON_TYPE EQ obj_field AND + (DEP.RDB$DEPENDENT_TYPE EQ obj_package_header OR + DEP.RDB$DEPENDENT_TYPE EQ obj_package_body) AND + DEP.RDB$DEPENDENT_SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND + DEP.RDB$DEPENDENT_NAME EQ CONST.RDB$PACKAGE_NAME + { + QualifiedName constantFullName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME); + + const auto id = CONST.RDB$CONSTANT_ID; + + DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_package_constant, + string(constantFullName.object.c_str()), constantFullName.schema, id, constantFullName.package); + DFW_post_work_arg(transaction, dw2, nullptr, nullptr, 0, dfw_arg_check_blr); + + MetadataCache::newVersion(tdbb, id); + } + END_FOR + return calcFieldRelation; } @@ -4189,6 +4217,11 @@ void MET_store_dependencies(thread_db* tdbb, name = *dependency.name; dpdo_name = &name; break; + case obj_package_constant: + dpdo_name = dependency.name; + name.package = *dependency.subName; + // TOOD: SCHEMA + break; } MetaName field_name; @@ -4214,7 +4247,7 @@ void MET_store_dependencies(thread_db* tdbb, field_name = param->prm_name; } } - else + else if (dpdo_type != obj_package_constant) field_name = *dependency.subName; } @@ -4536,6 +4569,14 @@ void MetadataCache::releaseLocks(thread_db* tdbb) charset->releaseLocks(tdbb); } + // Release constants locks + + for (auto constant : mdc_constants) + { + if (constant) + constant->releaseLocks(tdbb); + } + // Release database triggers locks for (unsigned i = 0; i < DB_TRIGGERS_COUNT; ++i) diff --git a/src/jrd/met.h b/src/jrd/met.h index f9fd832659b..db2eb3acb7d 100644 --- a/src/jrd/met.h +++ b/src/jrd/met.h @@ -238,6 +238,7 @@ class MetadataCache : public Firebird::PermanentStorage mdc_procedures(getPool()), mdc_functions(getPool()), mdc_charsets(getPool()), + mdc_constants(getPool()), mdc_cleanup_queue(pool) { memset(mdc_triggers, 0, sizeof(mdc_triggers)); @@ -277,6 +278,12 @@ class MetadataCache : public Firebird::PermanentStorage return mdc_procedures.getVersioned(tdbb, id, CacheFlag::AUTOCREATE); } + Constant* getConstant(thread_db* tdbb, MetaId id) + { + return mdc_constants.getVersioned(tdbb, id, CacheFlag::AUTOCREATE); + } + + static Cached::CharSet* getCharSet(thread_db* tdbb, CSetId id, ObjectBase::Flag flags); void cleanup(Jrd::thread_db*); @@ -497,6 +504,16 @@ class MetadataCache : public Firebird::PermanentStorage } }; + template + class Vector + { + public: + static CacheVector& get(MetadataCache* mdc) + { + return mdc->mdc_constants; + } + }; + static MetadataCache* getCache(thread_db* tdbb) noexcept; class GeneratorFinder @@ -602,6 +619,7 @@ class MetadataCache : public Firebird::PermanentStorage CacheVector mdc_procedures; CacheVector mdc_functions; // User defined functions CacheVector mdc_charsets; // intl character set descriptions + CacheVector mdc_constants; // Package constants TriggersSet mdc_triggers[DB_TRIGGERS_COUNT]; // Two numbers are required because commit into cache is not atomic event. // Front value is incremented before commit, back - after commit. diff --git a/src/jrd/names.h b/src/jrd/names.h index 661c7974b79..d8d5ebd9559 100644 --- a/src/jrd/names.h +++ b/src/jrd/names.h @@ -490,3 +490,9 @@ NAME("RDB$TABLE_SCHEMA_NAME", nam_tab_sch_name) NAME("RDB$CONST_SCHEMA_NAME_UQ", nam_con_sch_name_uq) NAME("MON$SEARCH_PATH", nam_mon_search_path) NAME("RDB$TEXT_MAX", nam_text_max) + +NAME("RDB$CONSTANTS", nam_constants) +NAME("RDB$CONSTANT_NAME", nam_const_name) +NAME("RDB$CONSTANT_BLR", nam_const_blr) +NAME("RDB$CONSTANT_SOURCE", nam_const_source) +NAME("RDB$CONSTANT_ID", nam_const_id) diff --git a/src/jrd/obj.h b/src/jrd/obj.h index 5246253b90b..ebf75dee5e5 100644 --- a/src/jrd/obj.h +++ b/src/jrd/obj.h @@ -79,7 +79,9 @@ inline constexpr ObjectType obj_index_condition = 37; inline constexpr ObjectType obj_schema = 38; inline constexpr ObjectType obj_schemas = 39; -inline constexpr ObjectType obj_type_MAX = 40; +inline constexpr ObjectType obj_package_constant = 40; + +inline constexpr ObjectType obj_type_MAX = 42; // used in the parser only / no relation with obj_type_MAX (should be greater) inline constexpr ObjectType obj_user_or_role = 100; @@ -106,6 +108,7 @@ inline bool isSchemaBoundObject(ObjectType objectType) noexcept case obj_udf: case obj_collation: case obj_package_header: + case obj_package_constant: return true; default: @@ -185,6 +188,8 @@ inline constexpr const char* getDdlSecurityName(ObjectType object_type) noexcept return "SQL$TABLESPACES"; case obj_schemas: return "SQL$SCHEMAS"; + case obj_package_constant: + return "SQL$PACKAGE_CONSTANT"; default: return ""; } @@ -270,6 +275,8 @@ inline const char* getObjectName(ObjectType objType) return "FILTER"; case obj_schema: return "SCHEMA"; + case obj_package_constant: + return "PACKAGE CONSTANT"; default: fb_assert(false); return ""; diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 7aaaa4a88e8..7df6c5899a9 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -811,3 +811,17 @@ RELATION(nam_schemas, rel_schemas, ODS_14_0, rel_persistent) FIELD(f_sch_sys_flag, nam_sys_flag, fld_flag, 1, ODS_14_0) FIELD(f_sch_desc, nam_description, fld_description, 1, ODS_14_0) END_RELATION + + +// Relation 59 (RDB$CONSTANTS) +RELATION(nam_constants, rel_constants, ODS_13_1, rel_persistent) + FIELD(f_const_name, nam_const_name, fld_f_name, 0, ODS_13_1) + 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_13_1) + FIELD(f_const_field, nam_f_source, fld_f_name, 0, ODS_13_1) + FIELD(f_const_private_flag, nam_private_flag, fld_flag_nullable, 0, ODS_13_1) + FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_13_1) + FIELD(f_const_source, nam_const_source, fld_source, 1, ODS_13_1) + FIELD(f_const_package_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) +END_RELATION + diff --git a/src/jrd/tra.h b/src/jrd/tra.h index 72170f396e6..75d8e3d2695 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -525,6 +525,11 @@ enum dfw_t : int { dfw_set_generator, dfw_change_repl_state, + // Package constant + dfw_create_package_constant, + dfw_modify_package_constant, + dfw_delete_package_constant, + // deferred works argument types dfw_arg_proc_name, // procedure name for dfw_delete_prm, mandatory dfw_arg_check_blr, // check if BLR is still compilable diff --git a/src/jrd/trig.h b/src/jrd/trig.h index 53bb6bba270..fc05531a6a1 100644 --- a/src/jrd/trig.h +++ b/src/jrd/trig.h @@ -82,6 +82,7 @@ static inline constexpr Jrd::gen generators[] = { "RDB$BACKUP_HISTORY", 9, "Nbackup technology", ODS_13_0 }, { FUNCTIONS_GENERATOR, 10, "Function ID", ODS_13_0 }, { "RDB$GENERATOR_NAME", 11, "Implicit generator name", ODS_13_0 }, + { CONSTANTS_GENERATOR, 12, "Constant ID", ODS_14_0 }, { nullptr, 0, nullptr, 0 } }; diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index ab84441f89c..4cd8fd786db 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -94,6 +94,7 @@ #include "../jrd/trace/TraceJrdHelpers.h" #include "../common/Task.h" #include "../jrd/WorkerAttachment.h" +#include "../jrd/Constant.h" using namespace Jrd; using namespace Firebird; @@ -2346,6 +2347,21 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) DFW_post_work(transaction, dfw_change_repl_state, {}, {}, 1); break; + case rel_constants: + protect_system_table_delupd(tdbb, relation, "DELETE"); + EVL_field(0, rpb->rpb_record, f_const_name, &desc); + EVL_field(0, rpb->rpb_record, f_const_package_schema, &schemaDesc); + + if (EVL_field(0, rpb->rpb_record, f_const_package, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); + + EVL_field(0, rpb->rpb_record, f_const_id, &desc2); + id = MOV_get_long(tdbb, &desc2, 0); + + Constant::lookup(tdbb, id); + DFW_post_work(transaction, dfw_delete_package_constant, &desc, &schemaDesc, id, object_name.package); + break; + default: // Shut up compiler warnings break; } @@ -3729,6 +3745,25 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j check_repl_state(tdbb, transaction, org_rpb, new_rpb, f_pub_active_flag); break; + case rel_constants: + if (!check_nullify_source(tdbb, org_rpb, new_rpb, f_const_source)) + protect_system_table_delupd(tdbb, relation, "UPDATE"); + + EVL_field(0, org_rpb->rpb_record, f_const_name, &desc1); + EVL_field(0, org_rpb->rpb_record, f_const_package_schema, &schemaDesc); + + if (EVL_field(0, org_rpb->rpb_record, f_const_package, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); + + protect_system_table_delupd(tdbb, relation, "UPDATE"); + + { // Send dfw + EVL_field(0, org_rpb->rpb_record, f_const_id, &desc2); + const USHORT id = MOV_get_long(tdbb, &desc2, 0); + DFW_post_work(transaction, Jrd::dfw_modify_package_constant, &desc1, &schemaDesc, id, object_name.package); + } + break; + default: break; } @@ -4633,6 +4668,21 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) DFW_post_work(transaction, dfw_change_repl_state, {}, {}, 1); break; + case rel_constants: + protect_system_table_insert(tdbb, request, relation); + + EVL_field(0, rpb->rpb_record, f_const_name, &desc); + EVL_field(0, rpb->rpb_record, f_const_package_schema, &schemaDesc); + + if (EVL_field(0, rpb->rpb_record, f_const_name, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); + + object_id = set_metadata_id(tdbb, rpb->rpb_record, + f_const_id, drq_g_nxt_const_id, "RDB$CONSTANTS"); + + work = DFW_post_work(transaction, dfw_create_package_constant, &desc, &schemaDesc, object_id, object_name.package); + break; + default: // Shut up compiler warnings break; } @@ -4659,7 +4709,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) default: // Shut up compiler warnings break; } - + rpb->rpb_b_page = 0; rpb->rpb_b_line = 0; rpb->rpb_flags = 0; From 251615c2b9ea1f3795be6bdb2d812af3a22b341e Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 11 Feb 2026 13:49:15 +0300 Subject: [PATCH 002/119] Add "is constant" flag to nodes --- src/dsql/ExprNodes.cpp | 24 +++++- src/dsql/ExprNodes.h | 76 +++++++++++++++++ src/dsql/Nodes.h | 8 ++ src/dsql/PackageNodes.epp | 2 +- src/jrd/SysFunction.cpp | 175 +++++++++++++++++++------------------- src/jrd/SysFunction.h | 19 ++++- 6 files changed, 214 insertions(+), 90 deletions(-) diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index ec77629cd80..86cd64a7505 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -456,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; @@ -12478,7 +12495,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) diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index 108daf7d984..b1785c25ce3 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -452,6 +452,11 @@ class CurrentDateNode final : public TypedNode return streams.exist(fieldStream); } + bool constant() const override + { + return false; + } + void collectStreams(SortedStreamList& streamList) const override { if (!streamList.exist(fieldStream)) @@ -914,6 +944,11 @@ class GenIdNode final : public TypedNode return false; } + bool constant() const override + { + return false; + } + void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override; ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override; bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override; @@ -962,6 +997,11 @@ class InternalInfoNode final : public TypedNodeconstant())//TODOCONST:FIXME + if (!m_value->constant()) { status_exception::raise(Arg::Gds(isc_dyn_non_deterministic_constant) << name.toQuotedString()); } diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index d34fd07fd99..2a2ca5fb5a1 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -6910,97 +6910,98 @@ dsc* evlUnicodeVal(thread_db* tdbb, const SysFunction*, const NestValueArray& ar +constexpr auto DEFAULT = SysFunction::DETERMINISTIC | SysFunction::CONSTANT; const SysFunction SysFunction::functions[] = { // name, minArgCount, maxArgCount, deterministic, setParamsFunc, makeFunc, evlFunc, misc - {"ABS", 1, 1, true, setParamsDblDec, makeAbs, evlAbs, NULL}, - {"ACOS", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcos}, - {"ACOSH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcosh}, - {"ASCII_CHAR", 1, 1, true, setParamsInteger, makeAsciiChar, evlAsciiChar, NULL}, - {"ASCII_VAL", 1, 1, true, setParamsAsciiVal, makeShortResult, evlAsciiVal, NULL}, - {"ASIN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsin}, - {"ASINH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsinh}, - {"ATAN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtan}, - {"ATANH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtanh}, - {"ATAN2", 2, 2, true, setParamsDouble, makeDoubleResult, evlAtan2, NULL}, - {"BASE64_DECODE", 1, 1, true, NULL, makeDecode64, evlDecode64, NULL}, - {"BASE64_ENCODE", 1, 1, true, NULL, makeEncode64, evlEncode64, NULL}, - {"BIN_AND", 2, -1, true, setParamsBin, makeBin, evlBin, (void*) funBinAnd}, - {"BIN_NOT", 1, 1, true, setParamsBin, makeBin, evlBin, (void*) funBinNot}, - {"BIN_OR", 2, -1, true, setParamsBin, makeBin, evlBin, (void*) funBinOr}, - {"BIN_SHL", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShl}, - {"BIN_SHR", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShr}, - {"BIN_SHL_ROT", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShlRot}, - {"BIN_SHR_ROT", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShrRot}, - {"BIN_XOR", 2, -1, true, setParamsBin, makeBin, evlBin, (void*) funBinXor}, - {"BLOB_APPEND", 2, -1, true, setParamsBlobAppend, makeBlobAppend, evlBlobAppend, NULL}, - {"CEIL", 1, 1, true, setParamsDblDec, makeCeilFloor, evlCeil, NULL}, - {"CEILING", 1, 1, true, setParamsDblDec, makeCeilFloor, evlCeil, NULL}, - {"CHAR_TO_UUID", 1, 1, true, setParamsCharToUuid, makeUuid, evlCharToUuid, NULL}, - {"COMPARE_DECFLOAT", 2, 2, true, setParamsDecFloat, makeShortResult, evlCompare, (void*) funCmpDec}, - {"COS", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCos}, - {"COSH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCosh}, - {"COT", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCot}, - {"CRYPT_HASH", 2, 2, true, setParamsHash, makeHash, evlHash, NULL}, - {"DATEADD", 3, 3, true, setParamsDateAdd, makeDateAdd, evlDateAdd, NULL}, - {"DATEDIFF", 3, 3, true, setParamsDateDiff, makeDateDiff, evlDateDiff, NULL}, - {"DECRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, true, setParamsEncrypt, makeCrypt, evlDecrypt, NULL}, - {"ENCRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, true, setParamsEncrypt, makeCrypt, evlEncrypt, NULL}, - {"EXP", 1, 1, true, setParamsDblDec, makeDblDecResult, evlExp, NULL}, - {"FIRST_DAY", 2, 2, true, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay}, - {"FLOOR", 1, 1, true, setParamsDblDec, makeCeilFloor, evlFloor, NULL}, - {"GEN_UUID", 0, 1, false, NULL, makeUuid, evlGenUuid, NULL}, - {"GREATEST", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue}, - {"HASH", 1, 2, true, setParamsHash, makeHash, evlHash, NULL}, - {"HEX_DECODE", 1, 1, true, NULL, makeDecodeHex, evlDecodeHex, NULL}, - {"HEX_ENCODE", 1, 1, true, NULL, makeEncodeHex, evlEncodeHex, NULL}, - {"LAST_DAY", 2, 2, true, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funLastDay}, - {"LEAST", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue}, - {"LEFT", 2, 2, true, setParamsSecondInteger, makeLeftRight, evlLeft, NULL}, - {"LN", 1, 1, true, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLnat}, - {"LOG", 2, 2, true, setParamsDblDec, makeDblDecResult, evlLog, NULL}, - {"LOG10", 1, 1, true, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLog10}, - {"LPAD", 2, 3, true, setParamsSecondInteger, makePad, evlPad, (void*) funLPad}, - {"MAKE_DBKEY", 2, 4, true, setParamsMakeDbkey, makeDbkeyResult, evlMakeDbkey, NULL}, - {"MAXVALUE", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue}, - {"MINVALUE", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue}, - {"MOD", 2, 2, true, setParamsFromList, makeMod, evlMod, NULL}, - {"NORMALIZE_DECFLOAT", 1, 1, true, setParamsDecFloat, makeDecFloatResult, evlNormDec, NULL}, - {"OVERLAY", 3, 4, true, setParamsOverlay, makeOverlay, evlOverlay, NULL}, - {"PI", 0, 0, true, NULL, makePi, evlPi, NULL}, - {"POSITION", 2, 3, true, setParamsPosition, makeLongResult, evlPosition, NULL}, - {"POWER", 2, 2, true, setParamsDblDec, makeDblDecResult, evlPower, NULL}, - {"QUANTIZE", 2, 2, true, setParamsDecFloat, makeDecFloatResult, evlQuantize, NULL}, - {"RAND", 0, 0, false, NULL, makeDoubleResult, evlRand, NULL}, - {RDB_GET_CONTEXT, 2, 2, true, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL}, - {"RDB$GET_TRANSACTION_CN", 1, 1, false, setParamsInt64, makeGetTranCN, evlGetTranCN, NULL}, - {"RDB$ROLE_IN_USE", 1, 1, true, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL}, - {RDB_SET_CONTEXT, 3, 3, false, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL}, - {"RDB$SYSTEM_PRIVILEGE", 1, 1, true, NULL, makeBooleanResult, evlSystemPrivilege, NULL}, - {"REPLACE", 3, 3, true, setParamsFromList, makeReplace, evlReplace, NULL}, - {"REVERSE", 1, 1, true, NULL, makeReverse, evlReverse, NULL}, - {"RIGHT", 2, 2, true, setParamsSecondInteger, makeLeftRight, evlRight, NULL}, - {"ROUND", 1, 2, true, setParamsRoundTrunc, makeRound, evlRound, NULL}, - {"RPAD", 2, 3, true, setParamsSecondInteger, makePad, evlPad, (void*) funRPad}, - {"RSA_DECRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, true, setParamsRsaEncrypt, makeRsaCrypt, evlRsaDecrypt, NULL}, - {"RSA_ENCRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, true, setParamsRsaEncrypt, makeRsaCrypt, evlRsaEncrypt, NULL}, - {"RSA_PRIVATE", 1, 1, false, setParamsInteger, makeRsaPrivate, evlRsaPrivate, NULL}, - {"RSA_PUBLIC", 1, 1, false, setParamsRsaPublic, makeRsaPublic, evlRsaPublic, NULL}, - {"RSA_SIGN_HASH", RSA_SIGN_ARG_MAX, RSA_SIGN_ARG_MAX, true, setParamsRsaSign, makeRsaSign, evlRsaSign, NULL}, - {"RSA_VERIFY_HASH", RSA_VERIFY_ARG_MAX, RSA_VERIFY_ARG_MAX, true, setParamsRsaVerify, makeBoolResult, evlRsaVerify, NULL}, - {"SIGN", 1, 1, true, setParamsDblDec, makeShortResult, evlSign, NULL}, - {"SIN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSin}, - {"SINH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSinh}, - {"SQRT", 1, 1, true, setParamsDblDec, makeDblDecResult, evlSqrt, NULL}, - {"TAN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTan}, - {"TANH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh}, - {"TOTALORDER", 2, 2, true, setParamsDecFloat, makeShortResult, evlCompare, (void*) funTotalOrd}, - {"TRUNC", 1, 2, true, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL}, - {"UNICODE_CHAR", 1, 1, true, setParamsInteger, makeUnicodeChar, evlUnicodeChar, NULL}, - {"UNICODE_VAL", 1, 1, true, setParamsUnicodeVal, makeLongResult, evlUnicodeVal, NULL}, - {"UUID_TO_CHAR", 1, 1, true, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL}, - {"", 0, 0, false, NULL, NULL, NULL, NULL} + {"ABS", 1, 1, DEFAULT, setParamsDblDec, makeAbs, evlAbs, NULL}, + {"ACOS", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcos}, + {"ACOSH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcosh}, + {"ASCII_CHAR", 1, 1, DEFAULT, setParamsInteger, makeAsciiChar, evlAsciiChar, NULL}, + {"ASCII_VAL", 1, 1, DEFAULT, setParamsAsciiVal, makeShortResult, evlAsciiVal, NULL}, + {"ASIN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsin}, + {"ASINH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsinh}, + {"ATAN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtan}, + {"ATANH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtanh}, + {"ATAN2", 2, 2, DEFAULT, setParamsDouble, makeDoubleResult, evlAtan2, NULL}, + {"BASE64_DECODE", 1, 1, DEFAULT, NULL, makeDecode64, evlDecode64, NULL}, + {"BASE64_ENCODE", 1, 1, DEFAULT, NULL, makeEncode64, evlEncode64, NULL}, + {"BIN_AND", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinAnd}, + {"BIN_NOT", 1, 1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinNot}, + {"BIN_OR", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinOr}, + {"BIN_SHL", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShl}, + {"BIN_SHR", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShr}, + {"BIN_SHL_ROT", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShlRot}, + {"BIN_SHR_ROT", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShrRot}, + {"BIN_XOR", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinXor}, + {"BLOB_APPEND", 2, -1, DETERMINISTIC, setParamsBlobAppend, makeBlobAppend, evlBlobAppend, NULL}, + {"CEIL", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlCeil, NULL}, + {"CEILING", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlCeil, NULL}, + {"CHAR_TO_UUID", 1, 1, DEFAULT, setParamsCharToUuid, makeUuid, evlCharToUuid, NULL}, + {"COMPARE_DECFLOAT", 2, 2, DEFAULT, setParamsDecFloat, makeShortResult, evlCompare, (void*) funCmpDec}, + {"COS", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCos}, + {"COSH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCosh}, + {"COT", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCot}, + {"CRYPT_HASH", 2, 2, DEFAULT, setParamsHash, makeHash, evlHash, NULL}, + {"DATEADD", 3, 3, DEFAULT, setParamsDateAdd, makeDateAdd, evlDateAdd, NULL}, + {"DATEDIFF", 3, 3, DEFAULT, setParamsDateDiff, makeDateDiff, evlDateDiff, NULL}, + {"DECRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, DEFAULT, setParamsEncrypt, makeCrypt, evlDecrypt, NULL}, + {"ENCRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, DEFAULT, setParamsEncrypt, makeCrypt, evlEncrypt, NULL}, + {"EXP", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlExp, NULL}, + {"FIRST_DAY", 2, 2, DEFAULT, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay}, + {"FLOOR", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlFloor, NULL}, + {"GEN_UUID", 0, 0, NONE, NULL, makeUuid, evlGenUuid, NULL}, + {"GREATEST", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue}, + {"HASH", 1, 2, DEFAULT, setParamsHash, makeHash, evlHash, NULL}, + {"HEX_DECODE", 1, 1, DEFAULT, NULL, makeDecodeHex, evlDecodeHex, NULL}, + {"HEX_ENCODE", 1, 1, DEFAULT, NULL, makeEncodeHex, evlEncodeHex, NULL}, + {"LAST_DAY", 2, 2, DEFAULT, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funLastDay}, + {"LEAST", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue}, + {"LEFT", 2, 2, DEFAULT, setParamsSecondInteger, makeLeftRight, evlLeft, NULL}, + {"LN", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLnat}, + {"LOG", 2, 2, DEFAULT, setParamsDblDec, makeDblDecResult, evlLog, NULL}, + {"LOG10", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLog10}, + {"LPAD", 2, 3, DEFAULT, setParamsSecondInteger, makePad, evlPad, (void*) funLPad}, + {"MAKE_DBKEY", 2, 4, DEFAULT, setParamsMakeDbkey, makeDbkeyResult, evlMakeDbkey, NULL}, + {"MAXVALUE", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue}, + {"MINVALUE", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue}, + {"MOD", 2, 2, DEFAULT, setParamsFromList, makeMod, evlMod, NULL}, + {"NORMALIZE_DECFLOAT", 1, 1, DEFAULT, setParamsDecFloat, makeDecFloatResult, evlNormDec, NULL}, + {"OVERLAY", 3, 4, DEFAULT, setParamsOverlay, makeOverlay, evlOverlay, NULL}, + {"PI", 0, 0, DEFAULT, NULL, makePi, evlPi, NULL}, + {"POSITION", 2, 4, DEFAULT, setParamsPosition, makeLongResult, evlPosition, NULL}, + {"POWER", 2, 2, DEFAULT, setParamsDblDec, makeDblDecResult, evlPower, NULL}, + {"QUANTIZE", 2, 2, DEFAULT, setParamsDecFloat, makeDecFloatResult, evlQuantize, NULL}, + {"RAND", 0, 0, NONE, NULL, makeDoubleResult, evlRand, NULL}, + {RDB_GET_CONTEXT, 2, 2, DETERMINISTIC, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL}, + {"RDB$GET_TRANSACTION_CN", 1, 1, NONE, setParamsInt64, makeGetTranCN, evlGetTranCN, NULL}, + {"RDB$ROLE_IN_USE", 1, 1, DETERMINISTIC, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL}, + {RDB_SET_CONTEXT, 3, 3, NONE, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL}, + {"RDB$SYSTEM_PRIVILEGE", 1, 1, DETERMINISTIC, NULL, makeBooleanResult, evlSystemPrivilege, NULL}, + {"REPLACE", 3, 3, DEFAULT, setParamsFromList, makeReplace, evlReplace, NULL}, + {"REVERSE", 1, 1, DEFAULT, NULL, makeReverse, evlReverse, NULL}, + {"RIGHT", 2, 2, DEFAULT, setParamsSecondInteger, makeLeftRight, evlRight, NULL}, + {"ROUND", 1, 2, DEFAULT, setParamsRoundTrunc, makeRound, evlRound, NULL}, + {"RPAD", 2, 3, DEFAULT, setParamsSecondInteger, makePad, evlPad, (void*) funRPad}, + {"RSA_DECRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, DEFAULT, setParamsRsaEncrypt, makeRsaCrypt, evlRsaDecrypt, NULL}, + {"RSA_ENCRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, DEFAULT, setParamsRsaEncrypt, makeRsaCrypt, evlRsaEncrypt, NULL}, + {"RSA_PRIVATE", 1, 1, NONE, setParamsInteger, makeRsaPrivate, evlRsaPrivate, NULL}, + {"RSA_PUBLIC", 1, 1, NONE, setParamsRsaPublic, makeRsaPublic, evlRsaPublic, NULL}, + {"RSA_SIGN_HASH", RSA_SIGN_ARG_MAX, RSA_SIGN_ARG_MAX, DEFAULT, setParamsRsaSign, makeRsaSign, evlRsaSign, NULL}, + {"RSA_VERIFY_HASH", RSA_VERIFY_ARG_MAX, RSA_VERIFY_ARG_MAX, DEFAULT, setParamsRsaVerify, makeBoolResult, evlRsaVerify, NULL}, + {"SIGN", 1, 1, DEFAULT, setParamsDblDec, makeShortResult, evlSign, NULL}, + {"SIN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSin}, + {"SINH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSinh}, + {"SQRT", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlSqrt, NULL}, + {"TAN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTan}, + {"TANH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh}, + {"TOTALORDER", 2, 2, DEFAULT, setParamsDecFloat, makeShortResult, evlCompare, (void*) funTotalOrd}, + {"TRUNC", 1, 2, DEFAULT, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL}, + {"UNICODE_CHAR", 1, 1, DEFAULT, setParamsInteger, makeUnicodeChar, evlUnicodeChar, NULL}, + {"UNICODE_VAL", 1, 1, DEFAULT, setParamsUnicodeVal, makeLongResult, evlUnicodeVal, NULL}, + {"UUID_TO_CHAR", 1, 1, DEFAULT, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL}, + {"", 0, 0, NONE, NULL, NULL, NULL, NULL} }; diff --git a/src/jrd/SysFunction.h b/src/jrd/SysFunction.h index 5a2cbc671b9..65543b710f5 100644 --- a/src/jrd/SysFunction.h +++ b/src/jrd/SysFunction.h @@ -46,6 +46,13 @@ namespace Jrd class SysFunction { public: + enum Flags : UCHAR + { + NONE = 0, + DETERMINISTIC = 1, + CONSTANT = 2 + }; + typedef void (*SetParamsFunc)(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int, dsc**); typedef void (*MakeFunc)(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc*, int, const dsc**); typedef dsc* (*EvlFunc)(Jrd::thread_db*, const SysFunction* function, @@ -54,7 +61,7 @@ class SysFunction const char* name; int minArgCount; int maxArgCount; // -1 for no limit - bool deterministic; + UCHAR flags; SetParamsFunc setParamsFunc; MakeFunc makeFunc; EvlFunc evlFunc; @@ -64,6 +71,16 @@ class SysFunction void checkArgsMismatch(int count) const; + inline bool isDeterministic() const + { + return flags & DETERMINISTIC; + } + + inline bool isConstant() const + { + return flags & CONSTANT; + } + private: const static SysFunction functions[]; }; From 2d35ecc1271d74b98f75cdada4075a744fea2689 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 11 Feb 2026 14:58:57 +0300 Subject: [PATCH 003/119] Fix blr parsing, generate unique names for constant FIELD_NAME, fix wrong impure cache usage --- src/dsql/PackageNodes.epp | 11 +++++++++-- src/jrd/Constant.epp | 29 ++++++++++++++++++++++++++--- src/jrd/Routine.cpp | 3 +++ src/jrd/irq.h | 1 + 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 019071d78fa..205de25f1d6 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -573,6 +573,7 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS try { getConstantRequest = CMP_compile_request(tdbb, blr.begin(), blr.getCount(), true); + getConstantRequest->setUsed(); const StmtNode* initNode = getConstantRequest->getStatement()->topNode; @@ -658,8 +659,14 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat status_exception::raise(Arg::Gds(isc_dyn_dup_const) << name.toQuotedString()); } + // Generate a new unique field name because constants in different packages may have same name + // but names in RDB$FIELDS should be unique in for a SCHEME + QualifiedName fieldName = name; + fieldName.object = ""; // Store description in the RDB$FIELDS relation - storeGlobalField(tdbb, tdbb->getTransaction(), name, m_type); + storeGlobalField(tdbb, tdbb->getTransaction(), fieldName, m_type); + fb_assert(fieldName.schema == name.schema); + fb_assert(fieldName.package == name.package); int faults = 0; while (true) @@ -685,7 +692,7 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat // Description (filed) name CONST.RDB$FIELD_SOURCE.NULL = FALSE; - strcpy(CONST.RDB$FIELD_SOURCE, name.object.c_str()); + strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); // Gen value blr genOutputConstantBlr(tdbb, dsqlScratch); diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 2f3dad48e2c..bed8b44e178 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -34,6 +34,7 @@ #include "../jrd/met_proto.h" #include "../jrd/met.h" #include "../jrd/Statement.h" // Jrd::Statement +#include "../jrd/par_proto.h" // PAR_blr using namespace Firebird; using namespace Jrd; @@ -86,7 +87,7 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) TriState ssDefiner; if (!CONST.RDB$PACKAGE_NAME.NULL) { - AutoCacheRequest requestHandle(tdbb, irq_l_procedure_pkg_class, IRQ_REQUESTS); + AutoCacheRequest requestHandle(tdbb, irq_l_const_pkg_class, IRQ_REQUESTS); FOR (REQUEST_HANDLE requestHandle) PKG IN RDB$PACKAGES @@ -282,9 +283,31 @@ void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) try { - this->parseBlr(tdbb, csb, &blob_id, nullptr); + Jrd::Attachment* attachment = tdbb->getAttachment(); - auto routineRequest = getStatement()->makeRootRequest(tdbb); + UCharBuffer tmp; + + { + blb* blob = blb::open(tdbb, attachment->getSysTransaction(), &blob_id); + ULONG length = blob->blb_length + 10; + UCHAR* temp = tmp.getBuffer(length); + length = blob->BLB_get_data(tdbb, temp, length); + // blob->BLB_close(tdbb); + tmp.resize(length); + } + + Statement* statement = getStatement(); + flReload = false; + CompilerScratch* csbptr = csb.get(); + PAR_blr(tdbb, &getName().schema, nullptr, tmp.begin(), (ULONG) tmp.getCount(), NULL, &csbptr, &statement, false, 0); + setStatement(statement); + + if (csb->csb_g_flags & csb_reload) + flReload = true; + + setImplemented(true); + + auto routineRequest = statement->makeRootRequest(tdbb); const dsc* temp = EVL_expr(tdbb, routineRequest, static_cast(csb->csb_node)); // The m_value has a string that is allocated with the tdbb pool diff --git a/src/jrd/Routine.cpp b/src/jrd/Routine.cpp index e32096996e9..2cd3648c3db 100644 --- a/src/jrd/Routine.cpp +++ b/src/jrd/Routine.cpp @@ -134,6 +134,9 @@ void Routine::setStatement(Statement* value) statement->function = static_cast(this); break; + case obj_package_constant: + break; + default: fb_assert(false); break; diff --git a/src/jrd/irq.h b/src/jrd/irq.h index 5c84537a793..9ab53327469 100644 --- a/src/jrd/irq.h +++ b/src/jrd/irq.h @@ -186,6 +186,7 @@ enum irq_type_t irq_l_const_id, irq_l_constants, irq_c_const_dpd, // get constant dependencies + irq_l_const_pkg_class, irq_MAX }; From 75db5ab848ef3ed46959aeb04f22062700dde1f3 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 11 Feb 2026 14:59:46 +0300 Subject: [PATCH 004/119] Improve error message for non constant expression --- src/dsql/PackageNodes.epp | 2 +- src/include/firebird/impl/msg/dyn.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 205de25f1d6..0ce9e2824dd 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -511,7 +511,7 @@ DdlNode* CreatePackageConstantNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) // TODO: Is value compatible with type? if (!m_value->constant()) { - status_exception::raise(Arg::Gds(isc_dyn_non_deterministic_constant) << name.toQuotedString()); + status_exception::raise(Arg::Gds(isc_dyn_non_constant_constant) << name.toQuotedString()); } // DsqlDescMaker::fromField(&node->castDesc, type); // node->castDesc.dsc_flags = node->source->getDsqlDesc().dsc_flags & DSC_nullable; diff --git a/src/include/firebird/impl/msg/dyn.h b/src/include/firebird/impl/msg/dyn.h index a1b5ea2e5f4..7c8e8ff50ec 100644 --- a/src/include/firebird/impl/msg/dyn.h +++ b/src/include/firebird/impl/msg/dyn.h @@ -314,4 +314,4 @@ FB_IMPL_MSG(DYN, 321, dyn_cannot_infer_schema, -901, "42", "000", "Cannot infer FB_IMPL_MSG_SYMBOL(DYN, 323, dyn_dup_blob_filter, "Blob filter @1 already exists") FB_IMPL_MSG_SYMBOL(DYN, 334, dyn_const_not_found, "Constant @1 not found") FB_IMPL_MSG_SYMBOL(DYN, 335, dyn_dup_const, "Constant @1 already exists") -FB_IMPL_MSG_SYMBOL(DYN, 336, dyn_non_deterministic_constant, "The expression to initialize constant \"@1\" must be a constant expression") +FB_IMPL_MSG_SYMBOL(DYN, 336, dyn_non_constant_constant, "The constant \"@1\" must be initialized by a constant expression") From e54c9e6858eb03fada9af549797555403be8fce0 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 12 Feb 2026 14:45:50 +0300 Subject: [PATCH 005/119] Add missing SCHEMA set to package constants, fix incorrect cached constant routine --- src/dsql/ExprNodes.cpp | 2 +- src/dsql/PackageNodes.epp | 59 ++++++++++++++++++++++------- src/dsql/PackageNodes.h | 2 +- src/include/firebird/impl/msg/jrd.h | 3 +- src/include/gen/Firebird.pas | 1 + src/jrd/Constant.epp | 23 ++++++----- src/jrd/dfw.epp | 2 +- 7 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 86cd64a7505..195bbc38720 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -14188,7 +14188,7 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (!node->dsqlVar || (node->dsqlVar->type == dsql_var::TYPE_LOCAL && !node->dsqlVar->initialized && !dsqlScratch->mainScratch)) { - if (dsqlScratch->package.package.hasData()) + if (dsqlScratch->package.object.hasData()) { thread_db* tdbb = JRD_get_thread_data(); QualifiedName consatntFullName(dsqlName, dsqlScratch->package.schema, dsqlScratch->package.object); diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 0ce9e2824dd..0bfbb132751 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -71,8 +71,10 @@ public: m_name(pool, fullName) { } - void dsqlPass(DsqlCompilerScratch*) - { } + void dsqlPass(DsqlCompilerScratch* dsqlScratch) + { + dsqlScratch->qualifyExistingName(m_name, obj_package_constant); + } void execute(thread_db* tdbb, DsqlCompilerScratch*, jrd_tra* transaction) { @@ -326,7 +328,7 @@ static RegisterNode regPackageReferenceNode({blr_package_r PackageReferenceNode::PackageReferenceNode(MemoryPool& pool, const QualifiedName& fullName, const UCHAR itemType) : TypedNode(pool), - m_fullName(fullName), m_itemType(itemType) + m_fullName(fullName.object, fullName.schema.hasData() ? fullName.schema : PUBLIC_SCHEMA, fullName.package), m_itemType(itemType) {} string PackageReferenceNode::internalPrint(NodePrinter& printer) const @@ -387,7 +389,26 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler dependency.name = FB_NEW_POOL(pool) QualifiedName(pool, fullName); csb->addDependency(dependency); } - node->m_constant = Constant::lookup(tdbb, fullName, 0); + + { + csb->qualifyExistingName(tdbb, fullName, obj_package_constant); + auto* constant = MetadataCache::getPerm(tdbb, fullName, CacheFlag::AUTOCREATE); + if (constant) + node->m_constant = csb->csb_resources->constants.registerResource(constant); + } + + // std::optional id = Constant::getIdByName(tdbb, fullName); + // if (id.has_value()) + // status_exception::raise(Arg::Gds(isc_bad_constant_name) << + // Arg::Str(fullName.toQuotedString())); + + // node->m_constant = Constant::lookup(tdbb, id.value()); + + // node->m_constant = MetadataCache::getPerm(tdbb, fullName, CacheFlag::AUTOCREATE); + if (!node->m_constant) + status_exception::raise(Arg::Gds(isc_bad_constant_name) << + Arg::Str(fullName.toQuotedString())); + return node; } // TODO: rowtype @@ -426,10 +447,12 @@ bool PackageReferenceNode::constantExists(thread_db* tdbb, Jrd::jrd_tra* transac if (fullName.package.isEmpty() || fullName.object.isEmpty()) return false; + // Use default schema if one not specified + const char* schemaName = fullName.schema.hasData() ? fullName.schema.c_str() : PUBLIC_SCHEMA; AutoCacheRequest getConstantRequest(tdbb, drq_l_pkg_const_flag, DYN_REQUESTS); FOR (REQUEST_HANDLE getConstantRequest TRANSACTION_HANDLE transaction) CONST IN RDB$CONSTANTS - WITH CONST.RDB$SCHEMA_NAME EQ fullName.schema.c_str() AND + WITH CONST.RDB$SCHEMA_NAME EQ schemaName AND CONST.RDB$PACKAGE_NAME EQ fullName.package.c_str() AND CONST.RDB$CONSTANT_NAME EQ fullName.object.c_str() @@ -449,11 +472,14 @@ void PackageReferenceNode::getDesc(thread_db* tdbb, CompilerScratch*, dsc* desc) *desc = Constant::getDesc(tdbb, tdbb->getTransaction(), m_fullName); } -ValueExprNode* PackageReferenceNode::copy(thread_db* tdbb, NodeCopier&) const +ValueExprNode* PackageReferenceNode::copy(thread_db* tdbb, NodeCopier& copier) const { MemoryPool& pool = *tdbb->getDefaultPool(); auto* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, m_fullName, m_itemType); - node->m_constant = m_constant; + + auto* constant = MetadataCache::getPerm(tdbb, m_fullName, 0); + node->m_constant = copier.csb->csb_resources->constants.registerResource(constant); + return node; } @@ -474,9 +500,10 @@ dsc* PackageReferenceNode::execute(thread_db* tdbb, Request* request) const { case blr_pkg_ref_item_const: { - const_cast(m_constant.getObject())->checkReload(tdbb); - EVL_make_value(tdbb, &m_constant->getValue(), impure); + const Constant* constant = m_constant(request->getResources()); + constant->checkReload(tdbb); + EVL_make_value(tdbb, &constant->getValue(), impure); return &impure->vlu_desc; } default: @@ -504,6 +531,8 @@ string CreatePackageConstantNode::internalPrint(NodePrinter& printer) const DdlNode* CreatePackageConstantNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { + dsqlScratch->qualifyNewName(name); + m_value = m_value->dsqlPass(dsqlScratch); QualifiedName dummyCollationName; @@ -549,7 +578,7 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS Attachment* attachment = tdbb->getAttachment(); jrd_tra* transaction = tdbb->getTransaction(); - Firebird::MemoryPool& tempPool = *tdbb->getDatabase()->createPool(ALLOC_ARGS0); + Firebird::AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0)); Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); // We need to execute the constant expresion and get the descriptor with constant value @@ -557,8 +586,8 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS // Create and setup a proxy stmt node - ConstantExprNode proxyStmt(tempPool); - CastNode* cast = FB_NEW_POOL(tempPool) CastNode(tempPool, m_value, m_type); + ConstantExprNode proxyStmt(*tempPool); + CastNode* cast = FB_NEW_POOL(*tempPool) CastNode(*tempPool, m_value, m_type); proxyStmt.value = cast; // Get the blr @@ -585,7 +614,6 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS } catch (const Firebird::Exception& ex) { - tdbb->getDatabase()->deletePool(&tempPool); ex.stuffException(tdbb->tdbb_status_vector); ERR_punt(); return nullptr; @@ -688,6 +716,7 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat CONST.RDB$CONSTANT_NAME.NULL = FALSE; strcpy(CONST.RDB$CONSTANT_NAME, name.object.c_str()); + // Constant unique id CONST.RDB$CONSTANT_ID = id; // Description (filed) name @@ -706,6 +735,10 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat CONST.RDB$PACKAGE_NAME.NULL = FALSE; strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str()); + // Schema of the parent package + CONST.RDB$SCHEMA_NAME.NULL = FALSE; + strcpy(CONST.RDB$SCHEMA_NAME, name.schema.c_str()); + // Type CONST.RDB$PRIVATE_FLAG.NULL = FALSE; CONST.RDB$PRIVATE_FLAG = m_isPrivate; diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index f38632283e5..07940424720 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -147,7 +147,7 @@ class PackageReferenceNode final : public TypedNode m_constant; + CachedResource m_constant; const QualifiedName m_fullName; const UCHAR m_itemType; diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index 103a1abf83f..862fa7b0acb 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1004,4 +1004,5 @@ FB_IMPL_MSG(JRD, 1076, not_defined_constant, -901, "42", "000", "The constant @1 FB_IMPL_MSG(JRD, 1077, const_name, -901, "42", "000", "CONSTANT @1") FB_IMPL_MSG(JRD, 1078, private_constant, -901, "42", "000", "The constant @1 is private to the package @2") FB_IMPL_MSG(JRD, 1079, package_alias_help, -901, "42", "000", "Use an alias to resolve the conflict") -FB_IMPL_MSG(JRD, 1080, bad_constant_BLR, -901, "2F", "000", "Error while parsing constant @1's BLR") +FB_IMPL_MSG(JRD, 1080, bad_constant_BLR, -901, "2F", "000", "Error while parsing BLR value of the constant @1") +FB_IMPL_MSG(JRD, 1081, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index a3e8f4a0df2..c27b0a2331d 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5960,6 +5960,7 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_private_constant = 335545398; isc_package_alias_help = 335545399; isc_bad_constant_BLR = 335545400; + isc_bad_constant_name = 335545401; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index bed8b44e178..b10346c8248 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -72,9 +72,9 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) //try { - AutoCacheRequest request_fun(tdbb, irq_l_constants, IRQ_REQUESTS); + AutoCacheRequest request_const(tdbb, irq_l_constants, IRQ_REQUESTS); - FOR(REQUEST_HANDLE request_fun) + FOR(REQUEST_HANDLE request_const TRANSACTION_HANDLE metaTransaction) CONST IN RDB$CONSTANTS WITH CONST.RDB$CONSTANT_ID EQ this->getId() { @@ -82,8 +82,8 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) getPermanent()->setName(QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, (CONST.RDB$PACKAGE_NAME.NULL ? nullptr : CONST.RDB$PACKAGE_NAME))); - // getPermanent()->owner = CONST.RDB$OWNER_NAME; + MetaName owner; TriState ssDefiner; if (!CONST.RDB$PACKAGE_NAME.NULL) { @@ -92,13 +92,17 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) FOR (REQUEST_HANDLE requestHandle) PKG IN RDB$PACKAGES CROSS SCH IN RDB$SCHEMAS - OVER RDB$SCHEMA_NAME - WITH PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME + OVER RDB$SCHEMA_NAME WITH + PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND + PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME - if (!PKG.RDB$SECURITY_CLASS.NULL) - { - getPermanent()->setSecurityName(QualifiedName(PKG.RDB$SECURITY_CLASS, SCH.RDB$SECURITY_CLASS)); - } + + owner = PKG.RDB$OWNER_NAME; + + // if (!PKG.RDB$SECURITY_CLASS.NULL) + // { + // getPermanent()->setSecurityName(QualifiedName(PKG.RDB$SECURITY_CLASS, SCH.RDB$SECURITY_CLASS)); + // } // SQL SECURITY of function must be the same if it's defined in package if (!PKG.RDB$SQL_SECURITY.NULL) @@ -274,6 +278,7 @@ dsc Constant::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const Qualifie void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) { + Firebird::AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0)); MemoryPool* const csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, csb_pool); diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 911685c227a..395077596ed 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1141,7 +1141,7 @@ namespace { Jrd::Attachment* attachment = tdbb->getAttachment(); AutoCacheRequest handle(tdbb, irq_c_const_dpd, IRQ_REQUESTS); - Routine* routine = nullptr; + Constant* routine = nullptr; FOR(REQUEST_HANDLE handle) X IN RDB$CONSTANTS WITH From 45d62b7e2450a23cddc6885df220ff0ea9fbd022 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 12 Feb 2026 16:15:09 +0300 Subject: [PATCH 006/119] Add missing resource allocation and transfer for constants --- src/jrd/Resources.cpp | 1 + src/jrd/Resources.h | 6 ++++++ src/jrd/Statement.cpp | 4 +--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/jrd/Resources.cpp b/src/jrd/Resources.cpp index 99962ee616d..2d512087f64 100644 --- a/src/jrd/Resources.cpp +++ b/src/jrd/Resources.cpp @@ -32,6 +32,7 @@ void Resources::transfer(thread_db* tdbb, VersionedObjects* to, bool internal) gotHash += functions.transfer(tdbb, to, internal, digest); gotHash += triggers.transfer(tdbb, to, internal, digest); gotHash += indices.transfer(tdbb, to, internal, digest); + gotHash += constants.transfer(tdbb, to, internal, digest); if (hasHash) { diff --git a/src/jrd/Resources.h b/src/jrd/Resources.h index dac514d70cb..2808f25d589 100644 --- a/src/jrd/Resources.h +++ b/src/jrd/Resources.h @@ -300,6 +300,12 @@ class Resources final RscArray triggers; RscArray indices; RscArray constants; + + inline FB_SIZE_T countVersionedObjects() const noexcept + { + return charSets.getCount() + relations.getCount() + procedures.getCount() + + functions.getCount() + triggers.getCount() + constants.getCount(); //? + indices.getCount() ? + } }; // specialization diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index 3b0797099cd..6571238726b 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -223,9 +223,7 @@ void Statement::loadResources(thread_db* tdbb, Request* req, bool withLock) if (ddl || (!latestVer) || (latestVer->version != frontVersion)) { - const FB_SIZE_T resourceCount = latestVer ? latestVer->getCapacity() : - resources->charSets.getCount() + resources->relations.getCount() + resources->procedures.getCount() + - resources->functions.getCount() + resources->triggers.getCount(); + const FB_SIZE_T resourceCount = latestVer ? latestVer->getCapacity() : resources->countVersionedObjects(); AutoPtr newVer = FB_NEW_RPT(*pool, resourceCount) VersionedObjects(resourceCount); MetadataCache::Version ver(mdc); From 06411d74e86ffbe3a7e4371b2f320bf7fac71764 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 12 Feb 2026 17:37:57 +0300 Subject: [PATCH 007/119] Fix invalid constant and package name in store_dependencies --- src/jrd/met.epp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 8cebc68b9aa..3dc243ae3ad 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -4218,9 +4218,8 @@ void MET_store_dependencies(thread_db* tdbb, dpdo_name = &name; break; case obj_package_constant: - dpdo_name = dependency.name; - name.package = *dependency.subName; - // TOOD: SCHEMA + name = *dependency.name; + dpdo_name = &name; break; } @@ -4247,7 +4246,7 @@ void MET_store_dependencies(thread_db* tdbb, field_name = param->prm_name; } } - else if (dpdo_type != obj_package_constant) + else field_name = *dependency.subName; } From 3dc1d3d40cbe1a142329c6f2bd44ccacd3d56413 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 12 Feb 2026 17:38:16 +0300 Subject: [PATCH 008/119] Fix invalid constant drop --- src/include/firebird/impl/msg/jrd.h | 7 +- src/include/gen/Firebird.pas | 5 +- src/jrd/Constant.epp | 104 ++++++++++++++++++++-------- 3 files changed, 81 insertions(+), 35 deletions(-) diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index 862fa7b0acb..468da5eb10b 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1002,7 +1002,8 @@ FB_IMPL_MSG(JRD, 999, genseq_stepmustbe_nonzero, -833, "42", "000", "Argument ST FB_IMPL_MSG(JRD, 1000, argmustbe_exact_function, -833, "42", "000", "Arguments for @1 function must be exact numeric types") FB_IMPL_MSG(JRD, 1076, not_defined_constant, -901, "42", "000", "The constant @1 is not defined in the package @2") FB_IMPL_MSG(JRD, 1077, const_name, -901, "42", "000", "CONSTANT @1") -FB_IMPL_MSG(JRD, 1078, private_constant, -901, "42", "000", "The constant @1 is private to the package @2") +FB_IMPL_MSG(JRD, 1078, private_constant, -901, "42", "000", "The constant @1 is private") FB_IMPL_MSG(JRD, 1079, package_alias_help, -901, "42", "000", "Use an alias to resolve the conflict") -FB_IMPL_MSG(JRD, 1080, bad_constant_BLR, -901, "2F", "000", "Error while parsing BLR value of the constant @1") -FB_IMPL_MSG(JRD, 1081, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found") +FB_IMPL_MSG(JRD, 1080, bad_constant_blr_error, -901, "2F", "000", "Error while parsing BLR value of the constant @1") +FB_IMPL_MSG(JRD, 1081, bad_constant_type_error, -901, "2F", "000", "Error while resiving type of the constant @1") +FB_IMPL_MSG(JRD, 1082, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index c27b0a2331d..9c92fd2a189 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5959,8 +5959,9 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_const_name = 335545397; isc_private_constant = 335545398; isc_package_alias_help = 335545399; - isc_bad_constant_BLR = 335545400; - isc_bad_constant_name = 335545401; + isc_bad_constant_blr_error = 335545400; + isc_bad_constant_type_error = 335545401; + isc_bad_constant_name = 335545402; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index b10346c8248..18e3fd60721 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -172,10 +172,6 @@ ScanResult Constant::reload(thread_db* tdbb, ObjectBase::Flag) return ScanResult::REPEAT; found = true; - - MemoryPool* const csb_pool = dbb->createPool(ALLOC_ARGS0); - Jrd::ContextPoolHolder context(tdbb, csb_pool); - makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); // parseBlr() in makeValue could set FLAG_RELOAD again @@ -197,16 +193,49 @@ void Constant::checkReload(thread_db* tdbb) const } -void Constant::drop(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name) +class DropSavepoint { - // run all statements under savepoint control - AutoSavePoint savePoint(tdbb, transaction); - bool found = false; +public: + DropSavepoint(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name) : + m_savePoint(tdbb, transaction), + m_tdbb(tdbb) + { + MetadataCache::getVersioned(tdbb, name, CacheFlag::OLD_DROP); + } + + DropSavepoint(thread_db* tdbb, Jrd::jrd_tra* transaction) : + m_savePoint(tdbb, transaction), + m_tdbb(tdbb) + { + } - // missing ID in the node - therefore use getVersioned() with appropriate flags - // instead: MetadataCache::oldVersion(tdbb, id); - MetadataCache::getVersioned(tdbb, name, CacheFlag::OLD_DROP); - MetaId id; + inline void setFound(const MetaId id) noexcept + { + m_id = id; + m_found = true; + } + + void release() + { + fb_assert(m_found); + if (m_found) + MetadataCache::erase(m_tdbb, m_id); + + m_savePoint.release(); + } + +private: + AutoSavePoint m_savePoint; + thread_db* m_tdbb; + MetaId m_id{}; + bool m_found = false; + bool m_succeed = false; +}; + +void Constant::drop(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name) +{ + // Run all statements under savepoint control + DropSavepoint savePoint(tdbb, transaction, name); AutoCacheRequest request(tdbb, drq_e_pkg_const_name, DYN_REQUESTS); FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) @@ -215,21 +244,34 @@ void Constant::drop(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedN CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND CONST.RDB$CONSTANT_NAME EQ name.object.c_str() { - found = true; - id = CONST.RDB$CONSTANT_ID; + savePoint.setFound(CONST.RDB$CONSTANT_ID); ERASE CONST; } END_FOR - if (found) - { - MetadataCache::erase(tdbb, id); - } - savePoint.release(); // everything is ok } +// void Constant::drop(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name, const MetaId id) +// { +// // Run all statements under savepoint control +// DropSavepoint savePoint(tdbb, transaction, name); + +// AutoCacheRequest request(tdbb, drq_e_pkg_const_name, DYN_REQUESTS); +// FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) +// CONST IN RDB$CONSTANTS +// WITH CONST.RDB$CONSTANT_ID EQ id +// { +// savePoint.setFound(id); + +// ERASE CONST; +// } +// END_FOR + +// savePoint.release(); // everything is ok +// } + void Constant::dropAllFromPackage(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& parent, bool privateFlag) { AutoCacheRequest eraseConstantRequest(tdbb, drq_e_pkg_consts_by_flag, DYN_REQUESTS); @@ -239,15 +281,20 @@ void Constant::dropAllFromPackage(thread_db* tdbb, Jrd::jrd_tra* transaction, co CONST.RDB$PACKAGE_NAME EQ parent.object.c_str() AND CONST.RDB$PRIVATE_FLAG EQ privateFlag { - drop(tdbb, transaction, QualifiedName(CONST.RDB$CONSTANT_NAME, parent.schema, parent.object)); + DropSavepoint savePoint(tdbb, transaction, QualifiedName(CONST.RDB$CONSTANT_NAME, parent.schema, parent.object)); + savePoint.setFound(CONST.RDB$CONSTANT_ID); + ERASE CONST; + + savePoint.release(); // everything is ok } END_FOR } dsc Constant::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name) { - dsc desc; + dsc desc{}; + bool succeed = false; FbLocalStatus status; AutoCacheRequest getConstantDscRequest(tdbb, drq_l_pkg_const_dsc, DYN_REQUESTS); @@ -258,27 +305,24 @@ dsc Constant::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const Qualifie CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE - const bool succeed = DSC_make_descriptor(&desc, + succeed = DSC_make_descriptor(&desc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, CSetId(FLD.RDB$CHARACTER_SET_ID), CollId(FLD.RDB$COLLATION_ID)); - - if (succeed) - { - return desc; - } END_FOR - // TODO: Error + + if (!succeed) + (Arg::Gds(isc_bad_constant_type_error) << Arg::Str(name.toQuotedString())).raise(); + return desc; } void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) { - Firebird::AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0)); MemoryPool* const csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, csb_pool); @@ -330,7 +374,7 @@ void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) ex.stuffException(temp_status); const string name = this->getName().toQuotedString(); - (Arg::Gds(isc_bad_constant_BLR) << Arg::Str(name) << Arg::Str(name) + (Arg::Gds(isc_bad_constant_blr_error) << Arg::Str(name) << Arg::Str(name) << Arg::StatusVector(temp_status.begin())).raise(); } } From d048560e465dc69c3bd2da45e4216d5d8ff56509 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 13 Feb 2026 12:07:28 +0300 Subject: [PATCH 009/119] Fix constant evaluating in BLR Parse --- src/dsql/PackageNodes.epp | 42 ++++++++++++++++++--------- src/jrd/Constant.epp | 61 +++++++++++++++++---------------------- 2 files changed, 56 insertions(+), 47 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 0bfbb132751..b58db922a69 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -106,7 +106,8 @@ void dropMissingItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const Q { if (!newNames.exist(i->name)) { - dropItem(tdbb, dsqlScratch, QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.package)); + dropItem(tdbb, dsqlScratch, + QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.object)); } } } @@ -117,7 +118,8 @@ void dropItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const Qualifie { for (auto i = items.begin(); i != items.end(); ++i) { - dropItem(tdbb, dsqlScratch, QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.package)); + dropItem(tdbb, dsqlScratch, + QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.object)); } } @@ -387,6 +389,7 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler { Dependency dependency(obj_package_constant); dependency.name = FB_NEW_POOL(pool) QualifiedName(pool, fullName); + // dependency.subName = &dependency.name->object; csb->addDependency(dependency); } @@ -594,23 +597,36 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS blr.clear(); dsqlScratch->getDebugData().clear(); dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); - proxyStmt.genBlr(dsqlScratch); + cast->genBlr(dsqlScratch); dsqlScratch->appendUChar(blr_eoc); - // Compile the blr - Request* getConstantRequest = nullptr; try { - getConstantRequest = CMP_compile_request(tdbb, blr.begin(), blr.getCount(), true); - getConstantRequest->setUsed(); + MemoryPool* csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); + Jrd::ContextPoolHolder context(tdbb, csb_pool); - const StmtNode* initNode = getConstantRequest->getStatement()->topNode; + Statement* statement = nullptr; + CompilerScratch* csb = nullptr; + Cleanup cc([&csb, &statement, &tdbb]() + { + if (statement) + statement->release(tdbb); + + // delete csb; + }); + + { // blr + PAR_blr(tdbb, &name.schema, nullptr, + dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount(), + nullptr, &csb, + nullptr, false, 0); + } + + statement = Statement::makeStatement(tdbb, csb, true); - // Execute the statement. - // The ConstantExprNode node will prepare the value and will write it into the impure - EXE_start(tdbb, getConstantRequest, transaction); - fb_assert(nodeIs(initNode)); - return static_cast(initNode)->getImpureDsc(getConstantRequest); + auto request = statement->makeRootRequest(tdbb); + ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output + return valueNode->execute(tdbb, request); } catch (const Firebird::Exception& ex) { diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 18e3fd60721..4a97da3431d 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -65,7 +65,6 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) Attachment* attachment = tdbb->getAttachment(); jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); Database* dbb = tdbb->getDatabase(); - MetadataCache* mdc = dbb->dbb_mdc; MemoryPool& pool = getPermanent()->getPool(); bool found = false; @@ -323,50 +322,44 @@ dsc Constant::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const Qualifie void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) { - MemoryPool* const csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); - Jrd::ContextPoolHolder context(tdbb, csb_pool); + // The m_value has a string that is allocated with the tdbb pool + // The constant cache lives longer than the tdbb pool + // so we must clear the value to prevent invalid deletions + m_value = {}; + MemoryPool* csb_pool = nullptr; try { - AutoPtr csb(FB_NEW_POOL(*csb_pool) CompilerScratch(*csb_pool)); + csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); + Jrd::ContextPoolHolder context(tdbb, csb_pool); try { - Jrd::Attachment* attachment = tdbb->getAttachment(); + CompilerScratch* csb = nullptr; + Cleanup cc([csb]() {delete csb;}); - UCharBuffer tmp; + { // blr + flReload = false; + MET_parse_blob(tdbb, &getName().schema, nullptr, &blob_id, &csb, nullptr, false, false); - { - blb* blob = blb::open(tdbb, attachment->getSysTransaction(), &blob_id); - ULONG length = blob->blb_length + 10; - UCHAR* temp = tmp.getBuffer(length); - length = blob->BLB_get_data(tdbb, temp, length); - // blob->BLB_close(tdbb); - tmp.resize(length); + fb_assert(csb != nullptr); + if (csb->csb_g_flags & csb_reload) + flReload = true; } - Statement* statement = getStatement(); - flReload = false; - CompilerScratch* csbptr = csb.get(); - PAR_blr(tdbb, &getName().schema, nullptr, tmp.begin(), (ULONG) tmp.getCount(), NULL, &csbptr, &statement, false, 0); - setStatement(statement); - - if (csb->csb_g_flags & csb_reload) - flReload = true; + { // Value + dsc constantDsc{}; + ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output - setImplemented(true); + Statement* statement = nullptr; + //Cleanup cc([&statement, &tdbb]() { if (statement) statement->release(tdbb); }); + statement = Statement::makeValueExpression(tdbb, valueNode, constantDsc, csb, true); - auto routineRequest = statement->makeRootRequest(tdbb); - const dsc* temp = EVL_expr(tdbb, routineRequest, static_cast(csb->csb_node)); - - // The m_value has a string that is allocated with the tdbb pool - // The constant cache lives longer than the tdbb pool - // so we clear the value to prevent invalid deletions - m_value = {}; - if (temp) - EVL_make_value(tdbb, temp, &m_value); - else - m_value.vlu_desc.setNull(); + auto routineRequest = statement->makeRootRequest(tdbb); + const dsc* temp = EVL_expr(tdbb, routineRequest, static_cast(csb->csb_node)); + EVL_make_value(tdbb, &constantDsc, &m_value); + statement->release(tdbb); + } } catch (const Exception& ex) { @@ -374,7 +367,7 @@ void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) ex.stuffException(temp_status); const string name = this->getName().toQuotedString(); - (Arg::Gds(isc_bad_constant_blr_error) << Arg::Str(name) << Arg::Str(name) + (Arg::Gds(isc_bad_constant_blr_error) << Arg::Str(name) << Arg::StatusVector(temp_status.begin())).raise(); } } From 6231dbc968aa16bb9f7102f1907500915a72f546 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 13 Feb 2026 18:16:14 +0300 Subject: [PATCH 010/119] Fix constants collecting and dtype_dec64 parsing --- src/dsql/PackageNodes.epp | 46 ++++++++++++++++++++++++++++++++++++--- src/jrd/vio.cpp | 1 + 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index b58db922a69..950672cfac3 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -311,6 +311,20 @@ void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transact procedures.add(procedure); } END_FOR + + + if (collectConstants) + { + AutoCacheRequest getConstantsRequest(tdbb, drq_l_pkg_consts, DYN_REQUESTS); + FOR(REQUEST_HANDLE getConstantsRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS WITH + CONST.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ packageName.object.c_str() + + Signature constant(CONST.RDB$CONSTANT_NAME); + constants.add(constant); + END_FOR + } } void PackageItemsHolder::clear() @@ -607,11 +621,13 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS Statement* statement = nullptr; CompilerScratch* csb = nullptr; - Cleanup cc([&csb, &statement, &tdbb]() + Request* requestToRestore = tdbb->getRequest(); + Cleanup cc([&csb, &statement, &tdbb, requestToRestore]() { if (statement) statement->release(tdbb); + tdbb->setRequest(requestToRestore); // delete csb; }); @@ -625,8 +641,15 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS statement = Statement::makeStatement(tdbb, csb, true); auto request = statement->makeRootRequest(tdbb); + tdbb->setRequest(request); + request->req_transaction = tdbb->getTransaction(); + ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output - return valueNode->execute(tdbb, request); + auto output = valueNode->execute(tdbb, request); + + request->req_transaction = nullptr; + + return output; } catch (const Firebird::Exception& ex) { @@ -670,7 +693,7 @@ void CreatePackageConstantNode::genOutputConstantBlr(thread_db* tdbb, DsqlCompil { double newValue = *(float*)scalar->dsc_address; - dsc descForDouble; + dsc descForDouble{}; descForDouble.makeDouble(); UCHAR* ptr; @@ -683,6 +706,23 @@ void CreatePackageConstantNode::genOutputConstantBlr(thread_db* tdbb, DsqlCompil LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); break; } + case dtype_dec64: + { + double newValue = *(float*)scalar->dsc_address; + + dsc descForDouble{}; + descForDouble.makeDecimal128(); + + UCHAR* ptr; + VaryStr temp; + TTypeId ttype; + ULONG len = MOV_get_string_ptr(tdbb, scalar, &ttype, &ptr, &temp, sizeof(temp)); + + descForDouble.dsc_address = ptr; + + LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); + break; + } default: LiteralNode::genConstant(dsqlScratch, scalar, false); break; diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 4cd8fd786db..919952fbba6 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -2349,6 +2349,7 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_constants: protect_system_table_delupd(tdbb, relation, "DELETE"); + EVL_field(0, rpb->rpb_record, f_const_name, &desc); EVL_field(0, rpb->rpb_record, f_const_package_schema, &schemaDesc); From 05342aad883c4f334b89ae768f0ad0cde47e779b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 16 Feb 2026 11:06:27 +0300 Subject: [PATCH 011/119] Fix incorrect ambiguity check --- src/dsql/ExprNodes.cpp | 31 ++++++++++++++++++++----------- src/dsql/PackageNodes.epp | 21 ++++++++++++++------- src/dsql/PackageNodes.h | 3 ++- src/dsql/dsql.h | 1 + src/dsql/pass1.cpp | 17 ++++++++++++++++- 5 files changed, 53 insertions(+), 20 deletions(-) diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 195bbc38720..6f48cb4e875 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -6245,7 +6245,7 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec // AB: Loop through the scope_levels starting by its own. bool done = false; - bool sourceIsFound = false; + USHORT currentScopeLevel = dsqlScratch->scopeLevel + 1; for (; currentScopeLevel > 0 && !done; --currentScopeLevel) { @@ -6272,8 +6272,6 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec if (field) { - sourceIsFound = true; - // If there's no name then we have most probable an asterisk that // needs to be exploded. This should be handled by the caller and // when the caller can handle this, list is not nullptr. @@ -6470,14 +6468,25 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec } } - const QualifiedName consatntName(dsqlName, - dsqlQualifier.schema.hasData() ? dsqlQualifier.schema : dsqlScratch->package.schema, - dsqlQualifier.object.hasData() ? dsqlQualifier.object : dsqlScratch->package.object); - if (node == nullptr && !sourceIsFound - && PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), consatntName)) - { - MemoryPool& pool = dsqlScratch->getPool(); - node = FB_NEW_POOL(pool) PackageReferenceNode(pool, consatntName); + dsql_ctx packageContext(dsqlScratch->getPool()); + { // Consatnts + + const QualifiedName consatntName(dsqlName, + dsqlQualifier.schema.hasData() ? dsqlQualifier.schema : (dsqlScratch->package.schema.hasData() ? dsqlScratch->package.schema : PUBLIC_SCHEMA), + 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 diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 950672cfac3..a2872b71c88 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -624,11 +624,11 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS Request* requestToRestore = tdbb->getRequest(); Cleanup cc([&csb, &statement, &tdbb, requestToRestore]() { - if (statement) - statement->release(tdbb); + // if (statement) + // statement->release(tdbb); + delete csb; tdbb->setRequest(requestToRestore); - // delete csb; }); { // blr @@ -640,14 +640,21 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS statement = Statement::makeStatement(tdbb, csb, true); + auto request = statement->makeRootRequest(tdbb); tdbb->setRequest(request); - request->req_transaction = tdbb->getTransaction(); + request->setUsed(); + auto* attachment = tdbb->getAttachment(); + request->setAttachment(attachment); + attachment->att_requests.add(request); + + TRA_attach_request(transaction, request); ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output auto output = valueNode->execute(tdbb, request); - - request->req_transaction = nullptr; + // request->setUnused(); + // statement->release(tdbb); + // statement = nullptr; return output; } @@ -1027,7 +1034,6 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) CreateAlterProcedureNode* const proc = (*items)[i].procedure; ddlNode = proc; - procedureNames.addName(proc->name); proc->alter = true; @@ -1050,6 +1056,7 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } default: + ddlNode = nullptr; // Warning fb_assert(false); } diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 07940424720..756384e2845 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -82,10 +82,11 @@ class ItemNames : public TArray "CONSTANT", }; + // Print just the object name because the full path is present n the parent error message Firebird::status_exception::raise( Firebird::Arg::Gds(isc_no_meta_update) << Firebird::Arg::Gds(isc_dyn_duplicate_package_item) << - Firebird::Arg::Str(names[size_t(IValue)]) << Firebird::Arg::Str(newName.toQuotedString())); + Firebird::Arg::Str(names[size_t(IValue)]) << Firebird::Arg::Str(newName.object.toQuotedString())); } }; diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index b64bc769e94..c5dc59146fd 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -559,6 +559,7 @@ inline constexpr USHORT CTX_view_with_check_modify = 0x40; // Context of WITH C inline constexpr USHORT CTX_cursor = 0x80; // Context is a cursor inline constexpr USHORT CTX_lateral = 0x100; // Context is a lateral derived table inline constexpr USHORT CTX_blr_fields = 0x200; // Fields of the context are defined inside BLR +inline constexpr USHORT CTX_package = 0x400; // The context is related to a package //! Aggregate/union map block to map virtual fields to their base //! TMN: NOTE! This datatype should definitely be renamed! diff --git a/src/dsql/pass1.cpp b/src/dsql/pass1.cpp index cad9b6cc039..01b2ff3a5a5 100644 --- a/src/dsql/pass1.cpp +++ b/src/dsql/pass1.cpp @@ -673,6 +673,7 @@ void PASS1_ambiguity_check(DsqlCompilerScratch* dsqlScratch, string buffers[2]; string* bufferPtr = &buffers[0]; + bool printAliasHelp = false; for (DsqlContextStack::const_iterator stack(ambiguous_contexts); stack.hasData(); ++stack) { @@ -701,6 +702,14 @@ void PASS1_ambiguity_check(DsqlCompilerScratch* dsqlScratch, buffer += "procedure "; buffer += procedure->prc_name.toQuotedString(); } + else if (context->ctx_flags & CTX_package) + { + // Package constant or variable + printAliasHelp = true; + buffer += "package "; + if (context->ctx_alias.hasData()) + buffer += context->getConcatenatedAlias(); + } else { const auto contextAliases = context->getConcatenatedAlias(); @@ -717,9 +726,15 @@ void PASS1_ambiguity_check(DsqlCompilerScratch* dsqlScratch, if (dsqlScratch->clientDialect >= SQL_DIALECT_V6) { - ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << + Arg::StatusVector status; + status.assign(Arg::Gds(isc_sqlerr) << Arg::Num(-204) << Arg::Gds(isc_dsql_ambiguous_field_name) << buffers[0] << buffers[1] << Arg::Gds(isc_random) << name); + + if (printAliasHelp) + status.append(Arg::Gds(isc_package_alias_help)); + + ERR_post(status); } ERRD_post_warning(Arg::Warning(isc_sqlwarn) << Arg::Num(204) << From 459354fd391cf3500ce7d2328bc5e25f19a0a322 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 16 Feb 2026 12:33:16 +0300 Subject: [PATCH 012/119] Update support for complex dtypes --- src/dsql/PackageNodes.epp | 59 +++++++++++++++++++---------- src/dsql/PackageNodes.h | 2 +- src/include/firebird/impl/msg/jrd.h | 3 +- src/include/gen/Firebird.pas | 1 + 4 files changed, 43 insertions(+), 22 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index a2872b71c88..f60e347bd0d 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -589,7 +589,7 @@ void CreatePackageConstantNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds MetadataCache::newVersion(tdbb, m_id); } -dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch) +dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, CompilerScratch*& nodeContext) { // Prepare variables Attachment* attachment = tdbb->getAttachment(); @@ -598,10 +598,6 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS Firebird::AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0)); Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); - // We need to execute the constant expresion and get the descriptor with constant value - // We need a request. To get it, we need to compile the blr. To compile the blr, wrap a ValueExpr in a StmtExpr - - // Create and setup a proxy stmt node ConstantExprNode proxyStmt(*tempPool); CastNode* cast = FB_NEW_POOL(*tempPool) CastNode(*tempPool, m_value, m_type); @@ -620,25 +616,22 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS Jrd::ContextPoolHolder context(tdbb, csb_pool); Statement* statement = nullptr; - CompilerScratch* csb = nullptr; Request* requestToRestore = tdbb->getRequest(); - Cleanup cc([&csb, &statement, &tdbb, requestToRestore]() + Cleanup cc([&tdbb, requestToRestore]() { // if (statement) // statement->release(tdbb); - - delete csb; tdbb->setRequest(requestToRestore); }); { // blr PAR_blr(tdbb, &name.schema, nullptr, dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount(), - nullptr, &csb, + nullptr, &nodeContext, nullptr, false, 0); } - statement = Statement::makeStatement(tdbb, csb, true); + statement = Statement::makeStatement(tdbb, nodeContext, true); auto request = statement->makeRootRequest(tdbb); @@ -650,7 +643,7 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS TRA_attach_request(transaction, request); - ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output + ValueExprNode* valueNode = static_cast(nodeContext->csb_node); // csb_node is the same as MET_parse_blob output auto output = valueNode->execute(tdbb, request); // request->setUnused(); // statement->release(tdbb); @@ -670,7 +663,15 @@ void CreatePackageConstantNode::genOutputConstantBlr(thread_db* tdbb, DsqlCompil { // Compile the constant dsc* scalar = nullptr; - if ((scalar = makeConstantValue(tdbb, dsqlScratch)) == nullptr) + + // Delay csb deletion to keep dsc storage alive + CompilerScratch* csb = nullptr; + Cleanup cc([&csb]() + { + delete csb; + }); + + if ((scalar = makeConstantValue(tdbb, dsqlScratch, csb)) == nullptr) return; // Something went wrong // Make the blr with only the LiteralNode @@ -713,10 +714,10 @@ void CreatePackageConstantNode::genOutputConstantBlr(thread_db* tdbb, DsqlCompil LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); break; } - case dtype_dec64: + case dtype_dec64: // dtype_dec64 is not present in LiteralNode::genConstant + case dtype_double: // MOV_move stores double in scalar->dsc_address. Convert it to string to use genConstant + case dtype_dec128: { - double newValue = *(float*)scalar->dsc_address; - dsc descForDouble{}; descForDouble.makeDecimal128(); @@ -730,6 +731,24 @@ void CreatePackageConstantNode::genOutputConstantBlr(thread_db* tdbb, DsqlCompil LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); break; } + case dtype_int128: + { + dsc descForInt128{}; + descForInt128.makeInt128(scalar->dsc_scale); + + UCHAR* ptr; + VaryStr temp; + TTypeId ttype; + ULONG len = MOV_get_string_ptr(tdbb, scalar, &ttype, &ptr, &temp, sizeof(temp)); + + descForInt128.dsc_address = ptr; + + LiteralNode::genConstant(dsqlScratch, &descForInt128, false, len); + break; + } + case dtype_blob: // Blob ID will be lost + status_exception::raise(Arg::Gds(isc_bad_constant_type) << scalar->typeToText()); + break; default: LiteralNode::genConstant(dsqlScratch, scalar, false); break; @@ -739,8 +758,6 @@ void CreatePackageConstantNode::genOutputConstantBlr(thread_db* tdbb, DsqlCompil void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { - // DYN_UTIL_check_unique_name(tdbb, transaction, itemName, obj_package_constant); - FbLocalStatus status; AutoCacheRequest storeConstantRequest(tdbb, drq_s_pkg_const, DYN_REQUESTS); @@ -1213,11 +1230,13 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PACKAGE, name, {}); PackageItemsHolder existingItems(pool); - existingItems.collectPackagedItems(tdbb, transaction, name, false, true); + existingItems.collectPackagedItems(tdbb, transaction, name, false, false); dropMissingItems(tdbb, dsqlScratch, name, existingItems.functions, functionNames); dropMissingItems(tdbb, dsqlScratch, name, existingItems.procedures, procedureNames); - dropMissingItems(tdbb, dsqlScratch, name, existingItems.constants, constantNames); + + // To admit type/value change, all the constants should be recreated + Constant::dropAllFromPackage(tdbb, transaction, name, false); MODIFY PKG PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE; diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 756384e2845..9c47328f41a 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -183,7 +183,7 @@ class CreatePackageConstantNode final : public DdlNode } private: - dsc* makeConstantValue(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch); + dsc* makeConstantValue(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, CompilerScratch*& nodeContext); void genOutputConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch); void executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index 468da5eb10b..e79f8852622 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1005,5 +1005,6 @@ FB_IMPL_MSG(JRD, 1077, const_name, -901, "42", "000", "CONSTANT @1") FB_IMPL_MSG(JRD, 1078, private_constant, -901, "42", "000", "The constant @1 is private") FB_IMPL_MSG(JRD, 1079, package_alias_help, -901, "42", "000", "Use an alias to resolve the conflict") FB_IMPL_MSG(JRD, 1080, bad_constant_blr_error, -901, "2F", "000", "Error while parsing BLR value of the constant @1") -FB_IMPL_MSG(JRD, 1081, bad_constant_type_error, -901, "2F", "000", "Error while resiving type of the constant @1") +FB_IMPL_MSG(JRD, 1081, bad_constant_type_error, -901, "2F", "000", "Error while reading type of the constant @1") FB_IMPL_MSG(JRD, 1082, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found") +FB_IMPL_MSG(JRD, 1083, bad_constant_type, -901, "2F", "000", "@1 is not suppotred to be a constant type") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 9c92fd2a189..b69890b07d4 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5962,6 +5962,7 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_bad_constant_blr_error = 335545400; isc_bad_constant_type_error = 335545401; isc_bad_constant_name = 335545402; + isc_bad_constant_type = 335545403; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; From 3c2c61e329fc000d97b82797005c56fd7f436003 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 17 Feb 2026 13:57:18 +0300 Subject: [PATCH 013/119] Cleanup --- src/dsql/PackageNodes.epp | 80 ++++++++++++++++----------------- src/dsql/StmtNodes.cpp | 76 ------------------------------- src/dsql/StmtNodes.h | 25 ----------- src/include/firebird/impl/blr.h | 1 - src/jrd/Constant.epp | 16 +++---- 5 files changed, 45 insertions(+), 153 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index f60e347bd0d..208262c3248 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -39,7 +39,6 @@ #include "../jrd/scl_proto.h" #include "../common/dsc_proto.h" // DSC_make_descriptor -#include "../dsql/StmtNodes.h" // ConstantExprNode #include "../jrd/Constant.h" // Constant #include "../jrd/cvt_proto.h" // CVT_get_string_ptr #include "../jrd/mov_proto.h" // MOV_get_string_ptr @@ -596,60 +595,57 @@ dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerS jrd_tra* transaction = tdbb->getTransaction(); Firebird::AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0)); - Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); - // Create and setup a proxy stmt node - ConstantExprNode proxyStmt(*tempPool); - CastNode* cast = FB_NEW_POOL(*tempPool) CastNode(*tempPool, m_value, m_type); - proxyStmt.value = cast; + { // Prepare BLR writer + Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); + blr.clear(); + dsqlScratch->getDebugData().clear(); + } - // Get the blr - blr.clear(); - dsqlScratch->getDebugData().clear(); - dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); - cast->genBlr(dsqlScratch); - dsqlScratch->appendUChar(blr_eoc); + // Gen blr + CastNode cast(*tempPool, m_value, m_type); + { + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + cast.genBlr(dsqlScratch); + dsqlScratch->appendUChar(blr_eoc); + } try { MemoryPool* csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); Jrd::ContextPoolHolder context(tdbb, csb_pool); - Statement* statement = nullptr; - Request* requestToRestore = tdbb->getRequest(); - Cleanup cc([&tdbb, requestToRestore]() + Cleanup cc([&tdbb, requestToRestore = tdbb->getRequest()]() { // if (statement) // statement->release(tdbb); tdbb->setRequest(requestToRestore); }); - { // blr - PAR_blr(tdbb, &name.schema, nullptr, - dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount(), - nullptr, &nodeContext, - nullptr, false, 0); - } - - statement = Statement::makeStatement(tdbb, nodeContext, true); + // Parse BLR + PAR_blr(tdbb, &name.schema, nullptr, + dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount(), + nullptr, &nodeContext, + nullptr, false, 0); + Request* request = nullptr; + { // Make statement and request + Statement* statement = Statement::makeStatement(tdbb, nodeContext, true); - auto request = statement->makeRootRequest(tdbb); - tdbb->setRequest(request); - request->setUsed(); - auto* attachment = tdbb->getAttachment(); - request->setAttachment(attachment); - attachment->att_requests.add(request); + request = statement->makeRootRequest(tdbb); + tdbb->setRequest(request); + request->setUsed(); + request->setAttachment(attachment); + attachment->att_requests.add(request); - TRA_attach_request(transaction, request); - - ValueExprNode* valueNode = static_cast(nodeContext->csb_node); // csb_node is the same as MET_parse_blob output - auto output = valueNode->execute(tdbb, request); - // request->setUnused(); - // statement->release(tdbb); - // statement = nullptr; + TRA_attach_request(transaction, request); + } - return output; + { // Execute constant expr + ValueExprNode* valueNode = static_cast(nodeContext->csb_node); // csb_node is the same as MET_parse_blob output + auto output = valueNode->execute(tdbb, request); + return output; + } } catch (const Firebird::Exception& ex) { @@ -675,11 +671,13 @@ void CreatePackageConstantNode::genOutputConstantBlr(thread_db* tdbb, DsqlCompil return; // Something went wrong // Make the blr with only the LiteralNode - Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); - blr.clear(); - dsqlScratch->getDebugData().clear(); - dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + { + Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); + blr.clear(); + dsqlScratch->getDebugData().clear(); + } + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); // Convert a literalNode-unsupported constant type to a supported one if necessary switch (scalar->dsc_dtype) { diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 89938377562..57d1ea5ddff 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -12261,80 +12261,4 @@ static void validateExpressions(thread_db* tdbb, const Array& vali } } - -// ---------------------------------------- -// ConstantExprNode implementation -// ---------------------------------------- - -static RegisterNode regInitConstantNode({blr_init_constant}); - -DmlNode* ConstantExprNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/) -{ - ConstantExprNode* node = FB_NEW_POOL(pool) ConstantExprNode(pool); - node->value = PAR_parse_value(tdbb, csb); - return node; -} - -ConstantExprNode* ConstantExprNode::dsqlPass(DsqlCompilerScratch* /*dsqlScratch*/) -{ - fb_assert(false); - return this; -} - -string ConstantExprNode::internalPrint(NodePrinter& printer) const -{ - StmtNode::internalPrint(printer); - - NODE_PRINT(printer, value); - - return "ConstantExprNode"; -} - -void ConstantExprNode::genBlr(DsqlCompilerScratch* dsqlScratch) -{ - dsqlScratch->appendUChar(blr_init_constant); - GEN_expr(dsqlScratch, value); -} - -ConstantExprNode* ConstantExprNode::copy(thread_db* tdbb, NodeCopier& copier) const -{ - ConstantExprNode* node = FB_NEW_POOL(*tdbb->getDefaultPool()) ConstantExprNode(*tdbb->getDefaultPool()); - node->value = value; - return node; -} - -ConstantExprNode* ConstantExprNode::pass1(thread_db* tdbb, CompilerScratch* csb) -{ - ExprNode::doPass1(tdbb, csb, value.getAddress()); - return this; -} - -ConstantExprNode* ConstantExprNode::pass2(thread_db* tdbb, CompilerScratch* csb) -{ - impureOffset = csb->allocImpure(); - ExprNode::doPass2(tdbb, csb, value.getAddress()); - return this; -} - -const StmtNode* ConstantExprNode::execute(thread_db* tdbb, Request* request, ExeState* /*exeState*/) const -{ - if (request->req_operation == Request::req_evaluate) - { - dsc* scalar = EVL_expr(tdbb, request, value); - impure_value* impure = request->getImpure(impureOffset); - EVL_make_value(tdbb, scalar, impure); - - request->req_operation = Request::req_return; - } - - return parentStmt; -} - -dsc* ConstantExprNode::getImpureDsc(Request* request) const -{ - impure_value* impure = request->getImpure(impureOffset); - return &impure->vlu_desc; -} - - } // namespace Jrd diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index 7adfecf7248..51e6afe9b54 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -2100,31 +2100,6 @@ class UpdateOrInsertNode final : public TypedNode -{ -public: - explicit ConstantExprNode(MemoryPool& pool) - : TypedNode(pool) - { } - -public: - static DmlNode* parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp); - - virtual Firebird::string internalPrint(NodePrinter& printer) const; - virtual ConstantExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void genBlr(DsqlCompilerScratch* dsqlScratch); - virtual ConstantExprNode* copy(thread_db* tdbb, NodeCopier& copier) const; - virtual ConstantExprNode* pass1(thread_db* tdbb, CompilerScratch* csb); - virtual ConstantExprNode* pass2(thread_db* tdbb, CompilerScratch* csb); - virtual const StmtNode* execute(thread_db* tdbb, Request* request, ExeState* exeState) const; - - dsc* getImpureDsc(Request* request) const; - -public: - NestConst value; - ULONG impureOffset; // Impure offset from request block. -}; - } // namespace #endif // DSQL_STMT_NODES_H diff --git a/src/include/firebird/impl/blr.h b/src/include/firebird/impl/blr.h index b63347991fb..a4985165c6f 100644 --- a/src/include/firebird/impl/blr.h +++ b/src/include/firebird/impl/blr.h @@ -534,7 +534,6 @@ // Package const #define blr_package_reference (unsigned char) 236 -#define blr_init_constant (unsigned char) 237 // Subcodes of blr_package_reference #define blr_pkg_ref_item_const (unsigned char) 1 diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 4a97da3431d..cab1e381c3a 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -98,10 +98,10 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) owner = PKG.RDB$OWNER_NAME; - // if (!PKG.RDB$SECURITY_CLASS.NULL) - // { - // getPermanent()->setSecurityName(QualifiedName(PKG.RDB$SECURITY_CLASS, SCH.RDB$SECURITY_CLASS)); - // } + if (!PKG.RDB$SECURITY_CLASS.NULL) + { + getPermanent()->setSecurityName(QualifiedName(PKG.RDB$SECURITY_CLASS, SCH.RDB$SECURITY_CLASS)); + } // SQL SECURITY of function must be the same if it's defined in package if (!PKG.RDB$SQL_SECURITY.NULL) @@ -348,16 +348,12 @@ void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) } { // Value - dsc constantDsc{}; - ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output - - Statement* statement = nullptr; //Cleanup cc([&statement, &tdbb]() { if (statement) statement->release(tdbb); }); - statement = Statement::makeValueExpression(tdbb, valueNode, constantDsc, csb, true); + Statement* statement = Statement::makeStatement(tdbb, csb, true); auto routineRequest = statement->makeRootRequest(tdbb); const dsc* temp = EVL_expr(tdbb, routineRequest, static_cast(csb->csb_node)); - EVL_make_value(tdbb, &constantDsc, &m_value); + EVL_make_value(tdbb, temp, &m_value); statement->release(tdbb); } } From fd208d136f74f0175f0ee3b99db89f1008876374 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 17 Feb 2026 14:18:49 +0300 Subject: [PATCH 014/119] Rollback unneccecery changes --- src/dsql/DdlNodes.epp | 5 ----- src/dsql/DdlNodes.h | 1 - src/dsql/ExprNodes.cpp | 1 - src/dsql/Nodes.h | 5 ++--- src/dsql/StmtNodes.cpp | 1 + src/isql/show.epp | 1 - src/jrd/Attachment.h | 1 - src/jrd/Resources.h | 1 + src/jrd/met.h | 1 - src/jrd/relations.h | 1 - 10 files changed, 4 insertions(+), 14 deletions(-) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index e06569a47a1..70cf8d25f7d 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -74,13 +74,8 @@ #include "../jrd/mov_proto.h" #include "../jrd/ini.h" #include "../jrd/GarbageCollector.h" - #include "../dsql/PackageNodes.h" -#include -#include -#include - namespace Jrd { // Define range of user relation ids diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 6b3b8425b0d..5839e71847a 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -41,7 +41,6 @@ namespace Jrd { - // Update RDB$FIELDS received by reference. void updateRdbFields(const Jrd::TypeClause* type, SSHORT& fieldType, diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 6f48cb4e875..849c27f5cb8 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -6245,7 +6245,6 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec // AB: Loop through the scope_levels starting by its own. bool done = false; - USHORT currentScopeLevel = dsqlScratch->scopeLevel + 1; for (; currentScopeLevel > 0 && !done; --currentScopeLevel) { diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index a5603b712b8..8d99023a2af 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -1226,7 +1226,7 @@ class RecordSourceNode : public ExprNode return false; } - virtual bool constant() const + virtual bool constant() const override { return false; } @@ -1501,8 +1501,7 @@ class StmtNode : public DmlNode TYPE_UPDATE_OR_INSERT, TYPE_EXT_INIT_PARAMETERS, - TYPE_EXT_TRIGGER, - TYPE_DECLARE_CONSTANT + TYPE_EXT_TRIGGER }; enum WhichTrigger : UCHAR diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 57d1ea5ddff..0316c9b3b2a 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -12261,4 +12261,5 @@ static void validateExpressions(thread_db* tdbb, const Array& vali } } + } // namespace Jrd diff --git a/src/isql/show.epp b/src/isql/show.epp index 2aad99292ca..4e87f26c88a 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -6743,7 +6743,6 @@ static processing_state show_wireStats() return SKIP; } - static void print_constant_type(const char* fieldName, const char* schemaName) { FOR FLD IN RDB$FIELDS WITH diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index 83f11e53f3b..7a79bc21a24 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -98,7 +98,6 @@ namespace Jrd class Validation; class Applier; enum InternalRequest : USHORT; - class Constant; struct DSqlCacheItem diff --git a/src/jrd/Resources.h b/src/jrd/Resources.h index 2808f25d589..a6f6e768e7f 100644 --- a/src/jrd/Resources.h +++ b/src/jrd/Resources.h @@ -199,6 +199,7 @@ class CachedResource template <> jrd_rel* CachedResource::operator()(thread_db* tdbb) const; + class Resources final { public: diff --git a/src/jrd/met.h b/src/jrd/met.h index db2eb3acb7d..c4c3fb0ec89 100644 --- a/src/jrd/met.h +++ b/src/jrd/met.h @@ -283,7 +283,6 @@ class MetadataCache : public Firebird::PermanentStorage return mdc_constants.getVersioned(tdbb, id, CacheFlag::AUTOCREATE); } - static Cached::CharSet* getCharSet(thread_db* tdbb, CSetId id, ObjectBase::Flag flags); void cleanup(Jrd::thread_db*); diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 7df6c5899a9..153d8bef4be 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -812,7 +812,6 @@ RELATION(nam_schemas, rel_schemas, ODS_14_0, rel_persistent) FIELD(f_sch_desc, nam_description, fld_description, 1, ODS_14_0) END_RELATION - // Relation 59 (RDB$CONSTANTS) RELATION(nam_constants, rel_constants, ODS_13_1, rel_persistent) FIELD(f_const_name, nam_const_name, fld_f_name, 0, ODS_13_1) From 8a982ac096d9aa98f1b9dd32e81bee801f95ba41 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 18 Feb 2026 16:19:16 +0300 Subject: [PATCH 015/119] Move all constant related routines to Constant class --- src/dsql/PackageNodes.epp | 202 ++--------------------------------- src/dsql/PackageNodes.h | 5 +- src/jrd/Constant.epp | 215 +++++++++++++++++++++++++++++++------- src/jrd/Constant.h | 16 +-- 4 files changed, 194 insertions(+), 244 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 208262c3248..096a65072cd 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -40,11 +40,10 @@ #include "../common/dsc_proto.h" // DSC_make_descriptor #include "../jrd/Constant.h" // Constant -#include "../jrd/cvt_proto.h" // CVT_get_string_ptr -#include "../jrd/mov_proto.h" // MOV_get_string_ptr + #include "../dsql/metd_proto.h" // METD_get_domain #include "../common/classes/VaryStr.h" // METD_get_domain -#include "../jrd/par_proto.h" // PARc_proto in Nodes.h (dependency hell) +#include "../jrd/par_proto.h" // PAR_proto in Nodes.h (dependency hell) #include "../jrd/met.h" // Metacache #include "../jrd/Statement.h" // Jrd::Statement @@ -402,7 +401,7 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler { Dependency dependency(obj_package_constant); dependency.name = FB_NEW_POOL(pool) QualifiedName(pool, fullName); - // dependency.subName = &dependency.name->object; + csb->addDependency(dependency); } @@ -413,14 +412,6 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler node->m_constant = csb->csb_resources->constants.registerResource(constant); } - // std::optional id = Constant::getIdByName(tdbb, fullName); - // if (id.has_value()) - // status_exception::raise(Arg::Gds(isc_bad_constant_name) << - // Arg::Str(fullName.toQuotedString())); - - // node->m_constant = Constant::lookup(tdbb, id.value()); - - // node->m_constant = MetadataCache::getPerm(tdbb, fullName, CacheFlag::AUTOCREATE); if (!node->m_constant) status_exception::raise(Arg::Gds(isc_bad_constant_name) << Arg::Str(fullName.toQuotedString())); @@ -539,7 +530,7 @@ string CreatePackageConstantNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, name); NODE_PRINT(printer, m_type); - NODE_PRINT(printer, m_value); + NODE_PRINT(printer, m_expr); NODE_PRINT(printer, m_isPrivate); return "PackageReferenceNode"; @@ -549,12 +540,12 @@ DdlNode* CreatePackageConstantNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { dsqlScratch->qualifyNewName(name); - m_value = m_value->dsqlPass(dsqlScratch); + m_expr = m_expr->dsqlPass(dsqlScratch); QualifiedName dummyCollationName; DDL_resolve_intl_type(dsqlScratch, m_type, dummyCollationName); - // TODO: Is value compatible with type? - if (!m_value->constant()) + + if (!m_expr->constant()) { status_exception::raise(Arg::Gds(isc_dyn_non_constant_constant) << name.toQuotedString()); } @@ -588,172 +579,6 @@ void CreatePackageConstantNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds MetadataCache::newVersion(tdbb, m_id); } -dsc* CreatePackageConstantNode::makeConstantValue(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, CompilerScratch*& nodeContext) -{ - // Prepare variables - Attachment* attachment = tdbb->getAttachment(); - jrd_tra* transaction = tdbb->getTransaction(); - - Firebird::AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0)); - - { // Prepare BLR writer - Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); - blr.clear(); - dsqlScratch->getDebugData().clear(); - } - - // Gen blr - CastNode cast(*tempPool, m_value, m_type); - { - dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); - cast.genBlr(dsqlScratch); - dsqlScratch->appendUChar(blr_eoc); - } - - try - { - MemoryPool* csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); - Jrd::ContextPoolHolder context(tdbb, csb_pool); - - Cleanup cc([&tdbb, requestToRestore = tdbb->getRequest()]() - { - // if (statement) - // statement->release(tdbb); - tdbb->setRequest(requestToRestore); - }); - - // Parse BLR - PAR_blr(tdbb, &name.schema, nullptr, - dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount(), - nullptr, &nodeContext, - nullptr, false, 0); - - Request* request = nullptr; - { // Make statement and request - Statement* statement = Statement::makeStatement(tdbb, nodeContext, true); - - request = statement->makeRootRequest(tdbb); - tdbb->setRequest(request); - request->setUsed(); - request->setAttachment(attachment); - attachment->att_requests.add(request); - - TRA_attach_request(transaction, request); - } - - { // Execute constant expr - ValueExprNode* valueNode = static_cast(nodeContext->csb_node); // csb_node is the same as MET_parse_blob output - auto output = valueNode->execute(tdbb, request); - return output; - } - } - catch (const Firebird::Exception& ex) - { - ex.stuffException(tdbb->tdbb_status_vector); - ERR_punt(); - return nullptr; - } -} - -void CreatePackageConstantNode::genOutputConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch) -{ - // Compile the constant - dsc* scalar = nullptr; - - // Delay csb deletion to keep dsc storage alive - CompilerScratch* csb = nullptr; - Cleanup cc([&csb]() - { - delete csb; - }); - - if ((scalar = makeConstantValue(tdbb, dsqlScratch, csb)) == nullptr) - return; // Something went wrong - - // Make the blr with only the LiteralNode - { - Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); - blr.clear(); - dsqlScratch->getDebugData().clear(); - } - - dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); - // Convert a literalNode-unsupported constant type to a supported one if necessary - switch (scalar->dsc_dtype) - { - case dtype_varying: - case dtype_cstring: - { - // Convert to dtype_text - TTypeId ttype; - UCHAR* ptr; - auto& status = tdbb->getAttachment()->att_dec_status; - const USHORT len = CVT_get_string_ptr(scalar, &ttype, &ptr, nullptr, 0, status); - - dsc text; - text.makeText(len, ttype, ptr); - LiteralNode::genConstant(dsqlScratch, &text, false); - break; - } - case dtype_real: - { - double newValue = *(float*)scalar->dsc_address; - - dsc descForDouble{}; - descForDouble.makeDouble(); - - UCHAR* ptr; - VaryStr temp; - TTypeId ttype; - ULONG len = MOV_get_string_ptr(tdbb, scalar, &ttype, &ptr, &temp, sizeof(temp)); - - descForDouble.dsc_address = ptr; - - LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); - break; - } - case dtype_dec64: // dtype_dec64 is not present in LiteralNode::genConstant - case dtype_double: // MOV_move stores double in scalar->dsc_address. Convert it to string to use genConstant - case dtype_dec128: - { - dsc descForDouble{}; - descForDouble.makeDecimal128(); - - UCHAR* ptr; - VaryStr temp; - TTypeId ttype; - ULONG len = MOV_get_string_ptr(tdbb, scalar, &ttype, &ptr, &temp, sizeof(temp)); - - descForDouble.dsc_address = ptr; - - LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); - break; - } - case dtype_int128: - { - dsc descForInt128{}; - descForInt128.makeInt128(scalar->dsc_scale); - - UCHAR* ptr; - VaryStr temp; - TTypeId ttype; - ULONG len = MOV_get_string_ptr(tdbb, scalar, &ttype, &ptr, &temp, sizeof(temp)); - - descForInt128.dsc_address = ptr; - - LiteralNode::genConstant(dsqlScratch, &descForInt128, false, len); - break; - } - case dtype_blob: // Blob ID will be lost - status_exception::raise(Arg::Gds(isc_bad_constant_type) << scalar->typeToText()); - break; - default: - LiteralNode::genConstant(dsqlScratch, scalar, false); - break; - } - dsqlScratch->appendUChar(blr_eoc); -} - void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { FbLocalStatus status; @@ -802,7 +627,7 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); // Gen value blr - genOutputConstantBlr(tdbb, dsqlScratch); + Constant::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); // Put the blr blb* blob = blb::create(tdbb, transaction, &CONST.RDB$CONSTANT_BLR); @@ -885,15 +710,6 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc const bool wasInternalDomain = fb_utils::implicit_domain(origDom.dyn_fld_source.object.c_str()); - // ERASE FLD; - - // Should it be here? - // if (!FLD.RDB$SECURITY_CLASS.NULL) - // deleteSecurityClass(tdbb, transaction, FLD.RDB$SECURITY_CLASS); - - // Should it be here? - // deletePrivilegesByRelName(tdbb, transaction, FLD.RDB$FIELD_NAME, obj_field); - QualifiedName newDomainName; // We have the type. Default and type/domain are exclusive for now. @@ -970,7 +786,7 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc newDom.dyn_sub_type = m_type->subType; // Gen the new constant value as blr - genOutputConstantBlr(tdbb, dsqlScratch); + Constant::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); // And write blb* blob = blb::create(tdbb, transaction, &blobId); diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 9c47328f41a..a58dd3bb45f 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -164,7 +164,7 @@ class CreatePackageConstantNode final : public DdlNode : DdlNode(pool), name(pool, name), m_type(type), - m_value(value), + m_expr(value), m_isPrivate(isPrivate) { } @@ -184,7 +184,6 @@ class CreatePackageConstantNode final : public DdlNode private: dsc* makeConstantValue(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, CompilerScratch*& nodeContext); - void genOutputConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch); void executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); @@ -206,7 +205,7 @@ class CreatePackageConstantNode final : public DdlNode private: NestConst m_type; - NestConst m_value; + NestConst m_expr; MetaId m_id = 0; bool m_isPrivate = false; }; diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index cab1e381c3a..67b5454b522 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -36,6 +36,10 @@ #include "../jrd/Statement.h" // Jrd::Statement #include "../jrd/par_proto.h" // PAR_blr +#include "../jrd/cvt_proto.h" // CVT_get_string_ptr +#include "../jrd/mov_proto.h" // MOV_get_string_ptr +#include "../common/classes/VaryStr.h" + using namespace Firebird; using namespace Jrd; @@ -160,20 +164,16 @@ ScanResult Constant::reload(thread_db* tdbb, ObjectBase::Flag) FOR(REQUEST_HANDLE request TRANSACTION_HANDLE metaTransaction) CONST IN RDB$CONSTANTS WITH CONST.RDB$CONSTANT_ID EQ this->getId() - // FOR (REQUEST_HANDLE request) - // CONST IN RDB$CONSTANTS - // WITH CONST.RDB$SCHEMA_NAME EQ this->getName().schema.c_str() AND - // CONST.RDB$PACKAGE_NAME EQ this->getName().package.c_str() AND - // CONST.RDB$CONSTANT_NAME EQ this->getName().object.c_str() { if (compiling) + { + fb_assert(false); return ScanResult::REPEAT; + } found = true; makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); - - // parseBlr() in makeValue could set FLAG_RELOAD again } END_FOR @@ -252,25 +252,6 @@ void Constant::drop(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedN savePoint.release(); // everything is ok } -// void Constant::drop(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name, const MetaId id) -// { -// // Run all statements under savepoint control -// DropSavepoint savePoint(tdbb, transaction, name); - -// AutoCacheRequest request(tdbb, drq_e_pkg_const_name, DYN_REQUESTS); -// FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) -// CONST IN RDB$CONSTANTS -// WITH CONST.RDB$CONSTANT_ID EQ id -// { -// savePoint.setFound(id); - -// ERASE CONST; -// } -// END_FOR - -// savePoint.release(); // everything is ok -// } - void Constant::dropAllFromPackage(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& parent, bool privateFlag) { AutoCacheRequest eraseConstantRequest(tdbb, drq_e_pkg_consts_by_flag, DYN_REQUESTS); @@ -319,12 +300,175 @@ dsc Constant::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const Qualifie return desc; } +static dsc* executeConstantExpressionFull(thread_db* tdbb, CompilerScratch* csb) +{ + Statement* statement = Statement::makeStatement(tdbb, csb, true); + + Request* request = statement->makeRootRequest(tdbb); + { + Attachment* attachment = tdbb->getAttachment(); + jrd_tra* transaction = tdbb->getTransaction(); + + tdbb->setRequest(request); + request->setUsed(); + request->setAttachment(attachment); + attachment->att_requests.add(request); + + TRA_attach_request(transaction, request); + } + + + { // Execute constant expr + ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output + return EVL_expr(tdbb, request, valueNode); + } +} + +static void executeConstantExpression(thread_db* tdbb, CompilerScratch* csb, MemoryPool& pool, impure_value& value) +{ + Statement* statement = Statement::makeStatement(tdbb, csb, true); + //Cleanup cc([&statement, &tdbb]() { if (statement) statement->release(tdbb); }); + + Request* request = statement->makeRootRequest(tdbb); + + const dsc* temp = EVL_expr(tdbb, request, static_cast(csb->csb_node)); + + EVL_make_value(tdbb, temp, &value, &pool); + statement->release(tdbb); +} + + +static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const dsc& scalar) +{ + // Make the blr with only the LiteralNode + { + Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); + blr.clear(); + dsqlScratch->getDebugData().clear(); + } + + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + // Convert a literalNode-unsupported constant type to a supported one if necessary + switch (scalar.dsc_dtype) + { + case dtype_varying: + case dtype_cstring: + { + // Convert to dtype_text + TTypeId ttype; + UCHAR* ptr; + auto& status = tdbb->getAttachment()->att_dec_status; + const USHORT len = CVT_get_string_ptr(&scalar, &ttype, &ptr, nullptr, 0, status); + + dsc text; + text.makeText(len, ttype, ptr); + LiteralNode::genConstant(dsqlScratch, &text, false); + break; + } + case dtype_real: + { + double newValue = *(float*)scalar.dsc_address; + + dsc descForDouble{}; + descForDouble.makeDouble(); + + UCHAR* ptr; + VaryStr temp; + TTypeId ttype; + ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp)); + + descForDouble.dsc_address = ptr; + + LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); + break; + } + case dtype_dec64: // dtype_dec64 is not present in LiteralNode::genConstant + case dtype_double: // MOV_move stores double in scalar->dsc_address. Convert it to string to use genConstant + case dtype_dec128: + { + dsc descForDouble{}; + descForDouble.makeDecimal128(); + + UCHAR* ptr; + VaryStr temp; + TTypeId ttype; + ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp)); + + descForDouble.dsc_address = ptr; + + LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); + break; + } + case dtype_int128: + { + dsc descForInt128{}; + descForInt128.makeInt128(scalar.dsc_scale); + + UCHAR* ptr; + VaryStr temp; + TTypeId ttype; + ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp)); + + descForInt128.dsc_address = ptr; + + LiteralNode::genConstant(dsqlScratch, &descForInt128, false, len); + break; + } + case dtype_blob: // Blob ID will be lost + status_exception::raise(Arg::Gds(isc_bad_constant_type) << scalar.typeToText()); + break; + default: + LiteralNode::genConstant(dsqlScratch, &scalar, false); + break; + } + dsqlScratch->appendUChar(blr_eoc); +} + +void Constant::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema) +{ + { // Prepare BLR writer + Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); + blr.clear(); + dsqlScratch->getDebugData().clear(); + } + + // Gen blr into dsqlScratch + Firebird::AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0)); + CastNode cast(*tempPool, constExpr, type); + { + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + cast.genBlr(dsqlScratch); + dsqlScratch->appendUChar(blr_eoc); + } + + Attachment* attachment = tdbb->getAttachment(); + MemoryPool* csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); + Jrd::ContextPoolHolder context(tdbb, csb_pool); + + CompilerScratch* csb = nullptr; + Cleanup cc([&tdbb, requestToRestore = tdbb->getRequest(), &csb]() + { + // if (statement) + // statement->release(tdbb); + delete csb; + tdbb->setRequest(requestToRestore); + }); + + // Parse BLR for constant expression + PAR_blr(tdbb, &schema, nullptr, + dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount(), + nullptr, &csb, + nullptr, false, 0); + + // Execute node from BLR + auto output = executeConstantExpressionFull(tdbb, csb); + if (output != nullptr) + genConstantCompatibleBlr(tdbb, dsqlScratch, *output); +} void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) { - // The m_value has a string that is allocated with the tdbb pool - // The constant cache lives longer than the tdbb pool - // so we must clear the value to prevent invalid deletions m_value = {}; MemoryPool* csb_pool = nullptr; @@ -343,19 +487,10 @@ void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) MET_parse_blob(tdbb, &getName().schema, nullptr, &blob_id, &csb, nullptr, false, false); fb_assert(csb != nullptr); - if (csb->csb_g_flags & csb_reload) - flReload = true; + flReload = (csb->csb_g_flags & csb_reload); } - { // Value - //Cleanup cc([&statement, &tdbb]() { if (statement) statement->release(tdbb); }); - Statement* statement = Statement::makeStatement(tdbb, csb, true); - - auto routineRequest = statement->makeRootRequest(tdbb); - const dsc* temp = EVL_expr(tdbb, routineRequest, static_cast(csb->csb_node)); - EVL_make_value(tdbb, temp, &m_value); - statement->release(tdbb); - } + executeConstantExpression(tdbb, csb, getPermanent()->getPool(), m_value); } catch (const Exception& ex) { diff --git a/src/jrd/Constant.h b/src/jrd/Constant.h index b1e54fb0b6f..fc16a46621a 100644 --- a/src/jrd/Constant.h +++ b/src/jrd/Constant.h @@ -36,8 +36,8 @@ namespace Jrd { - -class ValueListNode; +class DsqlCompilerScratch; +class dsql_fld; class Constant : public Routine { @@ -85,21 +85,21 @@ class Constant : public Routine static int objectType(); public: - void makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id); - inline const dsc& getValue() const { return m_value.vlu_desc; } - static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name); static void drop(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name); static void dropAllFromPackage(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& parent, bool privateFlag); - // static dsc* makeConstantValue(thread_db* tdbb, UCHAR* blr, ULONG blrLength); - static dsc* readValue(thread_db* tdbb, Request* request, const QualifiedName& name); - // static dsc* readConstantValue2(thread_db* tdbb, Request* request, const MetaName& packageName, const MetaName& itemName); + static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name); + + static void genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema); private: + void makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id); + virtual ~Constant() override { // The string is allocated via tdbb pool and it is dead by now From c42bd553029b33d725df134c220caab6745a126e Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 24 Feb 2026 22:47:35 +0300 Subject: [PATCH 016/119] Add description for package constants --- doc/sql.extensions/README.packages.txt | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt index 74ecf7cf888..ce46c5f6dc6 100644 --- a/doc/sql.extensions/README.packages.txt +++ b/doc/sql.extensions/README.packages.txt @@ -19,7 +19,8 @@ Syntax: ::= ; | - ; + ; | + ; ::= FUNCTION [( )] RETURNS @@ -27,6 +28,9 @@ Syntax: ::= PROCEDURE [( ) [RETURNS ( )]] + ::= + CONSTANT = + ::= { CREATE [OR ALTER] | ALTER | RECREATE } PACKAGE BODY AS @@ -37,7 +41,8 @@ Syntax: ::= | - + | + ::= FUNCTION [( )] RETURNS @@ -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. @@ -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 . notation. + Constants declared in the package body are private and cannot be accessed from outside the package. + However, they can be referenced directly by within and . + - Facilitate permission management. It's generally a good practice to create routines with a privileged database user and grant From 4a556aa3cf08728c213b23747bbbf68a09f08edc Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 24 Feb 2026 22:47:57 +0300 Subject: [PATCH 017/119] Cleanup --- src/jrd/Constant.epp | 1 - src/jrd/Constant.h | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 67b5454b522..6e990af0022 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -327,7 +327,6 @@ static dsc* executeConstantExpressionFull(thread_db* tdbb, CompilerScratch* csb) static void executeConstantExpression(thread_db* tdbb, CompilerScratch* csb, MemoryPool& pool, impure_value& value) { Statement* statement = Statement::makeStatement(tdbb, csb, true); - //Cleanup cc([&statement, &tdbb]() { if (statement) statement->release(tdbb); }); Request* request = statement->makeRootRequest(tdbb); diff --git a/src/jrd/Constant.h b/src/jrd/Constant.h index fc16a46621a..e5497752020 100644 --- a/src/jrd/Constant.h +++ b/src/jrd/Constant.h @@ -28,8 +28,8 @@ #ifndef JRD_CONSTANT_H #define JRD_CONSTANT_H -#include "../jrd/Routine.h" #include "firebird.h" +#include "../jrd/Routine.h" #include "../jrd/obj.h" #include "../jrd/val.h" #include "../jrd/lck.h" @@ -39,7 +39,7 @@ namespace Jrd class DsqlCompilerScratch; class dsql_fld; -class Constant : public Routine +class Constant final : public Routine { public: static Constant* lookup(thread_db* tdbb, MetaId id); From 44a7050cf06ac311379cc5b936bfc30b78c041b1 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 24 Feb 2026 22:48:22 +0300 Subject: [PATCH 018/119] Delete string in impure --- src/jrd/Constant.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/jrd/Constant.h b/src/jrd/Constant.h index e5497752020..142e49b6d9c 100644 --- a/src/jrd/Constant.h +++ b/src/jrd/Constant.h @@ -102,8 +102,7 @@ class Constant final : public Routine virtual ~Constant() override { - // The string is allocated via tdbb pool and it is dead by now - // delete m_value.vlu_string; + delete m_value.vlu_string; } public: From 3148971c58b85c6225e233e26149887ab07d4525 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 24 Feb 2026 23:28:29 +0300 Subject: [PATCH 019/119] Resolve incorrect merge with LTT --- src/common/classes/alloc.cpp | 6 +- src/dsql/DdlNodes.epp | 134 +++++++-- src/dsql/DdlNodes.h | 18 +- src/dsql/metd.epp | 118 +++++++- src/include/firebird/impl/msg/gbak.h | 18 +- src/include/firebird/impl/msg/jrd.h | 16 +- src/include/firebird/impl/msg/sqlerr.h | 6 +- src/jrd/Attachment.cpp | 21 ++ src/jrd/Relation.h | 2 +- src/jrd/blb.cpp | 10 +- src/jrd/btr.cpp | 6 +- src/jrd/dfw.epp | 373 ++++++++++++++++++++++++- src/jrd/dyn_ut_proto.h | 9 +- src/jrd/dyn_util.epp | 4 +- src/jrd/idx.cpp | 2 - src/jrd/irq.h | 6 +- src/jrd/jrd.cpp | 3 + src/jrd/met.epp | 25 +- src/jrd/met.h | 12 - src/jrd/met_proto.h | 2 +- src/jrd/obj.h | 2 +- src/jrd/tra.cpp | 3 + src/jrd/tra.h | 1 - src/jrd/vio.cpp | 2 +- 24 files changed, 683 insertions(+), 116 deletions(-) diff --git a/src/common/classes/alloc.cpp b/src/common/classes/alloc.cpp index 11b55af864f..b9414ac3bb2 100644 --- a/src/common/classes/alloc.cpp +++ b/src/common/classes/alloc.cpp @@ -2786,9 +2786,9 @@ void MemoryPool::deallocate(void* block) noexcept void MemoryPool::deletePool(MemoryPool* pool) { -// #ifdef DEBUG_LOST_POOLS -// checkPool(pool); -// #endif +#ifdef DEBUG_LOST_POOLS + Jrd::checkPool(pool); +#endif while (pool->finalizers) { diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 89703ad097e..69c69218999 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -70,18 +70,18 @@ #include "../auth/SecureRemotePassword/Message.h" #include "../jrd/Mapping.h" #include "../jrd/extds/ExtDS.h" +#include +#include +#include #include "../jrd/cch_proto.h" #include "../jrd/btr_proto.h" #include "../jrd/tra_proto.h" #include "../jrd/mov_proto.h" #include "../jrd/ini.h" #include "../jrd/GarbageCollector.h" +#include "../jrd/ProtectRelations.h" #include "../dsql/PackageNodes.h" -#include -#include -#include - namespace Jrd { // Define range of user relation ids @@ -131,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); -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"; @@ -8443,7 +8433,7 @@ void RelationNode::makeVersion(thread_db* tdbb, jrd_tra* transaction, const Qual if (FLD.RDB$CHARACTER_SET_ID.NULL) FLD.RDB$CHARACTER_SET_ID = CS_NONE; - CollId collation((!FLD.RDB$COLLATION_ID.NULL) ? FLD.RDB$COLLATION_ID : + CollId collation((!FLD.RDB$COLLATION_ID.NULL) ? FLD.RDB$COLLATION_ID : (!RFR.RDB$COLLATION_ID.NULL) ? RFR.RDB$COLLATION_ID : COLLATE_NONE); // codepoint collation if (!DSC_make_descriptor(&tfb->tfb_desc, FLD.RDB$FIELD_TYPE, @@ -9187,6 +9177,14 @@ void CreateRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + Cached::Relation* relation = nullptr; + bool extFile = false; + + AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true); + if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, name, obj_relation)) return; @@ -9206,8 +9204,28 @@ void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScrat DYN_UTIL_check_unique_name(tdbb, name, obj_relation); - fb_assert(relationType.has_value()); - checkRelationTempScope(tdbb, transaction, name, relationType.value()); + if (tempFlag == REL_temp_ltt) + { + defineLocalTempTable(tdbb, dsqlScratch, transaction); + + dsqlScratch->relation->rel_flags &= ~REL_creating; + + savePoint.release(); // everything is ok + + // Update DSQL cache + METD_drop_relation(transaction, name); + + return; + } + + const rel_t relationType = tempFlag.has_value() ? + (tempRowsFlag.value_or(REL_temp_tran) == REL_temp_tran ? + rel_temp_delete : + rel_temp_preserve + ) : + rel_persistent; + + checkRelationTempScope(tdbb, transaction, name, relationType); try { @@ -9364,6 +9382,9 @@ void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScrat throw; } + // Update DSQL cache + METD_drop_relation(transaction, name); + savePoint.release(); // everything is ok } @@ -9531,6 +9552,13 @@ void AlterRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { + const auto attachment = tdbb->getAttachment(); + + // Check if this is an LTT and set flag before METD_get_relation (called by saveRelation) + // to avoid false "LTT cannot be referenced in persistent metadata" error + AutoSetRestoreFlag autoLttReferences(&dsqlScratch->flags, DsqlCompilerScratch::FLAG_ALLOW_LTT_REFERENCES, + attachment->att_local_temporary_tables.exist(name)); + AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true); // ensures oldVersion is present in cache @@ -10868,7 +10896,42 @@ void DropRelationNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { - Attachment* attachment = tdbb->getAttachment(); + // run all statements under savepoint control + AutoSavePoint savePoint(tdbb, transaction); + + const auto attachment = tdbb->getAttachment(); + + if (const auto lttIt = attachment->att_local_temporary_tables.get(name)) + { + if (view) + { + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_dsql_view_not_found) << name.toQuotedString()); + } + + const auto ltt = *lttIt; + checkLttNotInUse(tdbb, transaction, ltt); + + // Post deferred work to handle the LTT deletion at commit time + DFW_post_work(transaction, dfw_delete_relation, + string(ltt->name.object.c_str()), ltt->name.schema, ltt->relationId); + + // Register undo action with the savepoint before removing + transaction->tra_save_point->createLttAction(LttUndoItem::LTT_UNDO_DROP, name, ltt); + + // Remove from in-memory storage (without deleting - ownership transferred to undo or already deleted) + attachment->att_local_temporary_tables.remove(name); + + savePoint.release(); // everything is ok + + // Update DSQL cache + METD_drop_relation(transaction, name); + + return; + } + Database* dbb = tdbb->getDatabase(); AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true); @@ -10877,6 +10940,9 @@ void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch if (!rel && silent) return; + if (tdbb->getDatabase()->readOnly()) + ERRD_post(Arg::Gds(isc_read_only_database)); + auto* relation = getPermanent(rel); // Check that DROP TABLE is dropping a table and that DROP VIEW is dropping a view. @@ -12694,6 +12760,8 @@ MetaId StoreIndexNode::createExpression(thread_db* tdbb, Cached::Relation* rel, ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* transaction, QualifiedName& idxName, Definition& definition, QualifiedName* referredIndexName) { + fb_assert(idxName.schema == definition.relation.schema); + if (idxName.isEmpty()) DYN_UTIL_generate_index_name(tdbb, transaction, idxName, definition.type); DYN_UTIL_check_unique_name(tdbb, idxName, obj_index); @@ -13327,6 +13395,20 @@ void AlterIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); + // Check if index belongs to a Local Temporary Table + LocalTemporaryTable* ltt = nullptr; + LocalTemporaryTable::Index* lttIndex = nullptr; + + if (MET_get_ltt_index(transaction->getAttachment(), indexName, <t, <tIndex)) + { + alterLocalTempIndex(tdbb, dsqlScratch, transaction, ltt, lttIndex); + savePoint.release(); // everything is ok + return; + } + + if (tdbb->getDatabase()->readOnly()) + ERRD_post(Arg::Gds(isc_read_only_database)); + Cached::Relation* relation = nullptr; bool expressionIndex = false; @@ -13629,6 +13711,20 @@ void DropIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, j // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); + // Check if index belongs to a Local Temporary Table + LocalTemporaryTable* ltt = nullptr; + LocalTemporaryTable::Index* lttIndex = nullptr; + + if (MET_get_ltt_index(transaction->getAttachment(), indexName, <t, <tIndex)) + { + dropLocalTempIndex(tdbb, dsqlScratch, transaction, ltt, lttIndex); + savePoint.release(); // everything is ok + return; + } + + if (tdbb->getDatabase()->readOnly()) + ERRD_post(Arg::Gds(isc_read_only_database)); + ModifyIndexList oneItemList(*MemoryPool::getContextPool()); Cached::Relation* rel = drop(tdbb, dsqlScratch, transaction, oneItemList, true); oneItemList.exec(tdbb, rel, transaction); @@ -13847,7 +13943,7 @@ void DropIndexNode::clearFrgn(thread_db* tdbb, MetaId relId, MetaId indexId) // Second part of index deletion post-processing. // // This function is invoked when index is removed from irt page. -// In some cases (failed DB creation) cleanup, done by clearName, +// In some cases (failed DB creation) cleanup, done by clearName, // also should take place. // diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index a140df0e00d..64c89b6acfb 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -62,7 +62,7 @@ enum SqlSecurity }; class LocalDeclarationsNode; -class CompoundStmtNode; +class LocalTemporaryTable; class RelationSourceNode; class ValueListNode; class SecDbContext; @@ -2093,6 +2093,14 @@ class AlterIndexNode final : public ModifyIndexNode, public DdlNode return DdlNode::dsqlPass(dsqlScratch); } + bool disallowedInReadOnlyDatabase() const override + { + return false; // Deferred to execute() - LTT status unknown at parse time + } + +private: + void alterLocalTempIndex(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + jrd_tra* transaction, LocalTemporaryTable* ltt, LocalTemporaryTable::Index* lttIndex); bool exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) override; protected: @@ -2168,6 +2176,14 @@ class DropIndexNode final : public ModifyIndexNode, public DdlNode return DdlNode::dsqlPass(dsqlScratch); } + bool disallowedInReadOnlyDatabase() const override + { + return false; // Deferred to execute() - LTT status unknown at parse time + } + +private: + void dropLocalTempIndex(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + jrd_tra* transaction, LocalTemporaryTable* ltt, LocalTemporaryTable::Index* lttIndex); bool exec(thread_db* tdbb, Cached::Relation* rel, jrd_tra* transaction) override; protected: diff --git a/src/dsql/metd.epp b/src/dsql/metd.epp index 6886b2809ed..60827137d0e 100644 --- a/src/dsql/metd.epp +++ b/src/dsql/metd.epp @@ -141,6 +141,36 @@ void METD_drop_collation(jrd_tra* transaction, const QualifiedName& name) } +void METD_drop_relation(jrd_tra* transaction, const QualifiedName& name) +{ +/************************************** + * + * M E T D _ d r o p _ r e l a t i o n + * + ************************************** + * + * Functional description + * Drop a relation from our metadata, and + * rely on the next guy who wants it to + * look up the new version. + * + * Dropping will be achieved by marking the relation + * as dropped. Anyone with current access can continue + * accessing it. + * + **************************************/ + thread_db* tdbb = JRD_get_thread_data(); + + dsql_rel* relation; + + if (transaction->tra_cache_rels.get(name, relation)) + { + relation->rel_flags |= REL_dropped; + transaction->tra_cache_rels.remove(name); + } +} + + dsql_intlsym* METD_get_collation(jrd_tra* transaction, const QualifiedName& name, CSetId charset_id) { /************************************** @@ -664,7 +694,7 @@ dsql_rel* METD_get_relation(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat validateTransaction(transaction); - // See if the relation is the one currently being defined in this statement + // see if the relation is the one currently being defined in this statement auto* relation = dsqlScratch->relation; if (relation != NULL && relation->rel_name == name) @@ -674,26 +704,92 @@ dsql_rel* METD_get_relation(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat // check is regular cache still valid - if (dsqlScratch->rels.get(name, relation)) + if (!dsqlScratch->regularCacheValid) { - return relation; + auto* mdc = MetadataCache::get(tdbb); + if (transaction->tra_mdc_version != mdc->getFrontVersion()) + { + transaction->tra_cache_rels.clear(); + transaction->tra_mdc_version = mdc->getBackVersion(); + } + dsqlScratch->regularCacheValid = true; } - // now see if it is in the transaction cache + // see if it is in the regular cache - if (transaction->tra_dsql_rels.get(name, relation)) + if (transaction->tra_cache_rels.get(name, relation)) { return relation; } - // now see if it is in the metadata cache + // Is it LTT? + + const auto lttPtr = tdbb->getAttachment()->att_local_temporary_tables.get(name); + const auto ltt = lttPtr ? *lttPtr : nullptr; + + if (ltt) + { + if ((dsqlScratch->flags & DsqlCompilerScratch::FLAG_DDL) && + !(dsqlScratch->flags & DsqlCompilerScratch::FLAG_ALLOW_LTT_REFERENCES)) + { + // LTTs cannot be referenced in persistent metadata (DDL other than LTT DDL itself) + status_exception::raise(Arg::Gds(isc_dsql_ltt_invalid_reference) + << name.toQuotedString() + << "persistent metadata"); + } + + fb_assert(ltt->relationId); + + relation = FB_NEW_POOL(transaction->getPool()) dsql_rel(transaction->getPool()); + relation->rel_id = ltt->relationId; + relation->rel_name = name; + relation->rel_dbkey_length = 8; + relation->rel_flags = REL_ltt_created; + + dsql_fld** ptr = &relation->rel_fields; + + for (const auto& lttField : ltt->fields) + { + dsql_fld* field = FB_NEW_POOL(transaction->getPool()) dsql_fld(transaction->getPool()); - auto* jrel = MetadataCache::getVersioned(tdbb, name, CacheFlag::AUTOCREATE); - if (!jrel) - return nullptr; + *ptr = field; + ptr = &field->fld_next; + + field->fld_relation = relation; + field->fld_id = lttField.id; + field->fld_name = lttField.name; + field->fieldSource = lttField.source; + + field->dtype = lttField.desc.dsc_dtype; + field->length = lttField.desc.dsc_length; + field->scale = lttField.desc.dsc_scale; + field->subType = lttField.desc.dsc_sub_type; + field->segLength = lttField.segLength; + field->charLength = lttField.charLength; + field->precision = lttField.precision; + + if (lttField.charSetId.has_value()) + field->charSetId = lttField.charSetId.value(); + + if (lttField.collationId.has_value()) + field->collationId = lttField.collationId.value(); + + if (!lttField.notNullFlag) + field->flags |= FLD_nullable; + } + } + else + { + // now see if it is in the metadata cache + + auto* jrel = MetadataCache::getVersioned(tdbb, name, CacheFlag::AUTOCREATE); + if (!jrel) + return nullptr; + + relation = FB_NEW_POOL(transaction->getPool()) dsql_rel(transaction->getPool(), jrel); + } - relation = FB_NEW_POOL(dsqlScratch->getPool()) dsql_rel(dsqlScratch->getPool(), jrel); - dsqlScratch->rels.put(relation->rel_name, relation); + transaction->tra_cache_rels.put(relation->rel_name, relation); return relation; } diff --git a/src/include/firebird/impl/msg/gbak.h b/src/include/firebird/impl/msg/gbak.h index 09677c985b0..907729b7942 100644 --- a/src/include/firebird/impl/msg/gbak.h +++ b/src/include/firebird/impl/msg/gbak.h @@ -418,12 +418,12 @@ FB_IMPL_MSG_NO_SYMBOL(GBAK, 419, "regular expression to skip schemas was already FB_IMPL_MSG_NO_SYMBOL(GBAK, 420, "regular expression to include schemas was already set") FB_IMPL_MSG_SYMBOL(GBAK, 421, gbak_plugin_schema_migration, "migrating @1 plugin objects to schema @2") FB_IMPL_MSG_SYMBOL(GBAK, 422, gbak_plugin_schema_migration_err, "error migrating @1 plugin objects to schema @2. Plugin objects will be in inconsistent state:") -FB_IMPL_MSG_NO_SYMBOL(GBAK, 527, "Compressor plugin '@1' not found") -FB_IMPL_MSG_NO_SYMBOL(GBAK, 528, " @1COM(PRESSOR) plugin name, compression level and threads count") -FB_IMPL_MSG_NO_SYMBOL(GBAK, 539, "Expected @1 instead of @2") -FB_IMPL_MSG_NO_SYMBOL(GBAK, 540, "compression level @1") -FB_IMPL_MSG_NO_SYMBOL(GBAK, 541, "'@1' compression plugin used") -FB_IMPL_MSG(GBAK, 542, gbak_writing_constants, -901, "00", "000", "writing constants") -FB_IMPL_MSG(GBAK, 543, gbak_writing_constant, -901, "00", "000", "writing constant %s for package %s") -FB_IMPL_MSG(GBAK, 544, gbak_constant, -901, "00", "000", "constant (in RDB$CONSTANTS)") -FB_IMPL_MSG(GBAK, 545, gbak_restoring_constant, -901, "00", "000", "restoring constant %s for package %s") +FB_IMPL_MSG_NO_SYMBOL(GBAK, 423, "Compressor plugin '@1' not found") +FB_IMPL_MSG_NO_SYMBOL(GBAK, 424, " @1COM(PRESSOR) plugin name, compression level and threads count") +FB_IMPL_MSG_NO_SYMBOL(GBAK, 435, "Expected @1 instead of @2") +FB_IMPL_MSG_NO_SYMBOL(GBAK, 446, "compression level @1") +FB_IMPL_MSG_NO_SYMBOL(GBAK, 447, "'@1' compression plugin used") +FB_IMPL_MSG(GBAK, 448, gbak_writing_constants, -901, "00", "000", "writing constants") +FB_IMPL_MSG(GBAK, 449, gbak_writing_constant, -901, "00", "000", "writing constant %s for package %s") +FB_IMPL_MSG(GBAK, 450, gbak_constant, -901, "00", "000", "constant (in RDB$CONSTANTS)") +FB_IMPL_MSG(GBAK, 451, gbak_restoring_constant, -901, "00", "000", "restoring constant %s for package %s") diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index e79f8852622..50a6bd6c928 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1000,11 +1000,11 @@ FB_IMPL_MSG(JRD, 997, invalid_unqualified_name_list, -901, "HY", "000", "Invalid FB_IMPL_MSG(JRD, 998, no_user_att_while_restore, -901, "HY", "000", "User attachments are not allowed for the database being restored") FB_IMPL_MSG(JRD, 999, genseq_stepmustbe_nonzero, -833, "42", "000", "Argument STEP must be different than zero for function @1") FB_IMPL_MSG(JRD, 1000, argmustbe_exact_function, -833, "42", "000", "Arguments for @1 function must be exact numeric types") -FB_IMPL_MSG(JRD, 1076, not_defined_constant, -901, "42", "000", "The constant @1 is not defined in the package @2") -FB_IMPL_MSG(JRD, 1077, const_name, -901, "42", "000", "CONSTANT @1") -FB_IMPL_MSG(JRD, 1078, private_constant, -901, "42", "000", "The constant @1 is private") -FB_IMPL_MSG(JRD, 1079, package_alias_help, -901, "42", "000", "Use an alias to resolve the conflict") -FB_IMPL_MSG(JRD, 1080, bad_constant_blr_error, -901, "2F", "000", "Error while parsing BLR value of the constant @1") -FB_IMPL_MSG(JRD, 1081, bad_constant_type_error, -901, "2F", "000", "Error while reading type of the constant @1") -FB_IMPL_MSG(JRD, 1082, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found") -FB_IMPL_MSG(JRD, 1083, bad_constant_type, -901, "2F", "000", "@1 is not suppotred to be a constant type") +FB_IMPL_MSG(JRD, 1001, not_defined_constant, -901, "42", "000", "The constant @1 is not defined in the package @2") +FB_IMPL_MSG(JRD, 1002, const_name, -901, "42", "000", "CONSTANT @1") +FB_IMPL_MSG(JRD, 1003, private_constant, -901, "42", "000", "The constant @1 is private") +FB_IMPL_MSG(JRD, 1004, package_alias_help, -901, "42", "000", "Use an alias to resolve the conflict") +FB_IMPL_MSG(JRD, 1005, bad_constant_blr_error, -901, "2F", "000", "Error while parsing BLR value of the constant @1") +FB_IMPL_MSG(JRD, 1006, bad_constant_type_error, -901, "2F", "000", "Error while reading type of the constant @1") +FB_IMPL_MSG(JRD, 1007, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found") +FB_IMPL_MSG(JRD, 1008, bad_constant_type, -901, "2F", "000", "@1 is not suppotred to be a constant type") diff --git a/src/include/firebird/impl/msg/sqlerr.h b/src/include/firebird/impl/msg/sqlerr.h index af8cb1bf2ee..b2af71a0334 100644 --- a/src/include/firebird/impl/msg/sqlerr.h +++ b/src/include/firebird/impl/msg/sqlerr.h @@ -289,6 +289,6 @@ FB_IMPL_MSG(SQLERR, 1049, dsql_drop_schema_failed, -901, "42", "000", "DROP SCHE FB_IMPL_MSG(SQLERR, 1050, dsql_recreate_schema_failed, -901, "42", "000", "RECREATE SCHEMA @1 failed") FB_IMPL_MSG(SQLERR, 1051, dsql_alter_schema_failed, -901, "42", "000", "ALTER SCHEMA @1 failed") FB_IMPL_MSG(SQLERR, 1052, dsql_create_alter_schema_failed, -901, "42", "000", "CREATE OR ALTER SCHEMA @1 failed") -FB_IMPL_MSG(SQLERR, 1084, dsql_create_const_failed, -901, "42", "000", "CREATE CONSTANT @1 failed") -FB_IMPL_MSG(SQLERR, 1085, dsql_alter_const_failed, -901, "42", "000", "ALTER CONSTANT @1 failed") -FB_IMPL_MSG(SQLERR, 1086, dsql_create_alter_const_failed, -901, "42", "000", "CREATE OR ALTER CONSTANT @1 failed") +FB_IMPL_MSG(SQLERR, 1053, dsql_create_const_failed, -901, "42", "000", "CREATE CONSTANT @1 failed") +FB_IMPL_MSG(SQLERR, 1054, dsql_alter_const_failed, -901, "42", "000", "ALTER CONSTANT @1 failed") +FB_IMPL_MSG(SQLERR, 1055, dsql_create_alter_const_failed, -901, "42", "000", "CREATE OR ALTER CONSTANT @1 failed") diff --git a/src/jrd/Attachment.cpp b/src/jrd/Attachment.cpp index 09c5ded9f67..61578bd07a4 100644 --- a/src/jrd/Attachment.cpp +++ b/src/jrd/Attachment.cpp @@ -374,6 +374,24 @@ void Jrd::Attachment::releaseBatches() delete att_batches.pop(); } +void Jrd::Attachment::releaseLocalTempTables(thread_db* tdbb) +{ + HalfStaticArray tempRelations; + + for (auto& lttEntry : att_local_temporary_tables) + { + const auto ltt = lttEntry.second; + if (ltt->relation) + tempRelations.add(ltt->relation->getPermanent()); + } + + for (const auto tempRelation : tempRelations) + { + if (tempRelation->rel_flags & REL_temp_conn) + tempRelation->delPages(tdbb); + } +} + void Jrd::Attachment::resetSession(thread_db* tdbb, jrd_tra** traHandle) { jrd_tra* oldTran = traHandle ? *traHandle : nullptr; @@ -444,6 +462,9 @@ void Jrd::Attachment::resetSession(thread_db* tdbb, jrd_tra** traHandle) if (att_user->resetRole()) SCL_release_all(att_security_classes); + // reset local temporary tables + releaseLocalTempTables(tdbb); + // reset GTT's att_database->dbb_mdc->releaseGTTs(tdbb); diff --git a/src/jrd/Relation.h b/src/jrd/Relation.h index 27f00f8614d..0acf552c0e3 100644 --- a/src/jrd/Relation.h +++ b/src/jrd/Relation.h @@ -608,7 +608,7 @@ class jrd_rel final : public ObjectBase bool isSystem() const noexcept; bool isTemporary() const noexcept; bool isLTT() const noexcept; - bool isVirtual() const noexcept ; + bool isVirtual() const noexcept; bool isView() const noexcept; bool isReplicating(thread_db* tdbb); diff --git a/src/jrd/blb.cpp b/src/jrd/blb.cpp index 975a0349362..f7e141a1be8 100644 --- a/src/jrd/blb.cpp +++ b/src/jrd/blb.cpp @@ -1449,12 +1449,12 @@ blb* blb::open2(thread_db* tdbb, // know about the relation, the blob id has got to be invalid // anyway. - jrd_rel* relation = MetadataCache::getVersioned(tdbb, blobId.bid_internal.bid_relation_id, 0); - if (!relation) - ERR_post(Arg::Gds(isc_bad_segstr_id)); + blob->blb_relation = MetadataCache::getVersioned(tdbb, blobId.bid_internal.bid_relation_id, 0); + if (!blob->blb_relation) + ERR_post(Arg::Gds(isc_bad_segstr_id)); - blob->blb_pg_space_id = relation->getPages(tdbb)->rel_pg_space_id; - DPM_get_blob(tdbb, blob, relation, blobId.get_permanent_number(), false, 0); + blob->blb_pg_space_id = blob->blb_relation->getPages(tdbb)->rel_pg_space_id; + DPM_get_blob(tdbb, blob, blob->blb_relation, blobId.get_permanent_number(), false, 0); #ifdef CHECK_BLOB_FIELD_ACCESS_FOR_SELECT if (!relation->isSystem() && blob->blb_fld_id < relation->rel_fields->count()) diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index df0fa0d14c8..1767eda9b83 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -2879,8 +2879,10 @@ void BTR_reserve_slot(thread_db* tdbb, IndexCreation& creation, IndexCreateLock& // Leave the root pointer null for the time being. // Index id for temporary index instance of global temporary table is // already assigned, use it. - const bool use_idx_id = (relPages->rel_instance_id != 0); - fb_assert((!use_idx_id) || (idx->idx_id <= dbb->dbb_max_idx)); + const bool use_idx_id = (relPages->rel_instance_id != 0) || + (relation->getPermanent()->rel_flags & REL_temp_ltt); + if (use_idx_id) + fb_assert(idx->idx_id <= dbb->dbb_max_idx); WIN window(relPages->rel_pg_space_id, relPages->rel_index_root); index_root_page* root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 7c5a4f2b343..d936fb0b8a5 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -383,6 +383,8 @@ static bool compute_security(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool create_index(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool modify_ltt_index(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_index(thread_db*, SSHORT, DeferredWork*, jrd_tra*); +static bool create_ltt_index(thread_db*, SSHORT, DeferredWork*, jrd_tra*); +static bool delete_ltt_index(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool commit_relation(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool create_relation(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_relation(thread_db*, SSHORT, DeferredWork*, jrd_tra*); @@ -950,11 +952,6 @@ namespace return "constant"; } - // static void clearId(Jrd::Attachment* attachment, USHORT id) - // { - // attachment->att_constants[id] = nullptr; - // } - static Routine* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile); static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work, SSHORT validBlr); @@ -1174,6 +1171,7 @@ namespace static inline constexpr deferred_task task_table[] = { { dfw_add_shadow, add_shadow }, + { dfw_delete_ltt_index, delete_ltt_index }, { dfw_delete_index, delete_index }, { dfw_commit_relation, commit_relation }, { dfw_delete_rfr, delete_rfr }, @@ -1188,6 +1186,8 @@ static inline constexpr deferred_task task_table[] = { dfw_update_ltt_format, make_ltt_version }, { dfw_compute_security, compute_security }, { dfw_create_index, create_index }, + { dfw_create_ltt_index, create_ltt_index }, + { dfw_modify_ltt_index, modify_ltt_index }, { dfw_grant, grant_privileges }, { dfw_create_trigger, create_trigger }, { dfw_modify_trigger, modify_trigger }, @@ -4691,3 +4691,366 @@ static void check_dependencies(thread_db* tdbb, Arg::Gds(isc_dependency) << Arg::Num(total)); // there are %ld dependencies } } + + +// Create an index for a Local Temporary Table. +static bool create_ltt_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +{ + switch (phase) + { + case 1: + case 2: + return true; + + case 3: + break; + + default: + return false; + } + + const auto attachment = tdbb->getAttachment(); + + // Find the LTT by looking up the index + LocalTemporaryTable* ltt = nullptr; + LocalTemporaryTable::Index* lttIndex = nullptr; + + if (!MET_get_ltt_index(attachment, work->getQualifiedName(), <t, <tIndex)) + return false; // LTT or index no longer exists (rolled back?) + + const auto relation = ltt->relation; + if (!relation) + return false; + + // Skip inactive indexes + if (lttIndex->inactive) + return false; + + // Build the index descriptor + index_desc idx; + memset(&idx, 0, sizeof(idx)); + + idx.idx_id = lttIndex->id; + idx.idx_count = lttIndex->columns.getCount(); + idx.idx_flags = 0; + + if (lttIndex->unique) + idx.idx_flags |= idx_unique; + if (lttIndex->descending) + idx.idx_flags |= idx_descending; + + // Build the index key descriptors from LTT field info + int keyPos = 0; + + for (const auto& colName : lttIndex->columns) + { + // Find the field in the LTT + bool found = false; + + for (const auto& field : ltt->fields) + { + if (field.name == colName) + { + idx.idx_rpt[keyPos].idx_field = field.id; + + // Determine index type based on field type + const auto idxType = DFW_assign_index_type(tdbb, work->getQualifiedName(), + field.desc.dsc_dtype, + DTYPE_IS_TEXT(field.desc.dsc_dtype) ? + TTypeId(field.charSetId.value_or(CS_NONE), field.collationId.value_or(COLLATE_NONE)) : + ttype_none); + idx.idx_rpt[keyPos].idx_itype = idxType; + + found = true; + break; + } + } + + if (!found) + { + ERR_post(Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_idx_seg_err) << work->getQualifiedName().toQuotedString()); + } + + ++keyPos; + } + + { // scope + AutoSetRestoreFlag dbPageSpace(&tdbb->tdbb_flags, TDBB_use_db_page_space, true); + + SelectivityList selectivity(*tdbb->getDefaultPool()); + IDX_create_index(tdbb, IdxCreate::AtOnce, relation, &idx, work->getQualifiedName(), + <tIndex->id, transaction, selectivity); + } + + // Update index on the current instance if it exists + if (relation->getPermanent()->rel_flags & REL_temp_conn) + { + AutoSetRestoreFlag dbPageSpace(&tdbb->tdbb_flags, TDBB_use_db_page_space, false); + + if (relation->getPages(tdbb, 0, false)) + { + SelectivityList selectivity(*tdbb->getDefaultPool()); + idx.idx_root = 0; + IDX_create_index(tdbb, IdxCreate::AtOnce, relation, &idx, work->getQualifiedName(), + <tIndex->id, transaction, selectivity); + } + } + + // Create index description in relation + auto* idp = relation->getPermanent()->rel_indices.ensurePermanent(tdbb, lttIndex->id); + + // create versioned part + auto& attPool = *attachment->att_pool; + AutoPtr idv = FB_NEW_POOL(attPool) IndexVersion(attPool, idp); + idv->setLtt(tdbb, lttIndex); + + idp->storeObject(tdbb, idv, 0); + idv.release(); + + return false; +} + + +// Delete an index for a Local Temporary Table. +static bool delete_ltt_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +{ + switch (phase) + { + case 1: + case 2: + return true; + + case 3: + break; + + default: + return false; + } + + // The index ID is passed in dfw_id, and the relation ID is stored in dfw_ids + const USHORT indexId = work->dfw_id; + const auto& ids = work->dfw_ids; + + if (ids.isEmpty()) + return false; + + const USHORT relationId = ids.front(); + + const auto relation = MetadataCache::getVersioned(tdbb, relationId, 0); + if (!relation) + return false; + + { // scope + AutoSetRestoreFlag noDbPageSpace(&tdbb->tdbb_flags, TDBB_use_db_page_space, true); + + // Get the relation pages + const auto relPages = relation->getPages(tdbb, MAX_TRA_NUMBER, false); + if (!relPages || !relPages->rel_index_root) + return false; + + // Delete the index by its ID + WIN window(relPages->rel_pg_space_id, relPages->rel_index_root); + CCH_FETCH(tdbb, &window, LCK_write, pag_root); + + BTR_delete_index(tdbb, &window, indexId, false); + } + + auto* idp = relation->getPermanent()->eraseIndex(tdbb, indexId); + idp->commit(tdbb); + + return false; +} + + +static bool modify_ltt_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +{ +/************************************** + * + * m o d i f y _ l t t _ i n d e x + * + ************************************** + * + * Functional description + * Recalculate statistics for an index on a Local Temporary Table. + * + **************************************/ + + // Recalculate statistics for an index on a Local Temporary Table. + switch (phase) + { + case 1: + case 2: + return true; + + case 3: + break; + + default: + return false; + } + + const auto attachment = tdbb->getAttachment(); + + // Find the LTT by looking up the index + LocalTemporaryTable* ltt = nullptr; + LocalTemporaryTable::Index* lttIndex = nullptr; + + if (!MET_get_ltt_index(attachment, work->getQualifiedName(), <t, <tIndex)) + return false; // LTT or index no longer exists (rolled back?) + + const auto relation = ltt->relation; + if (!relation) + return false; + + // Skip inactive indexes + if (lttIndex->inactive) + return false; + + // Only recalculate statistics if the table has been instantiated + if (!ltt->relationId) + return false; + + { // scope + AutoSetRestoreFlag dbPageSpace(&tdbb->tdbb_flags, TDBB_use_db_page_space, true); + + // Recalculate index statistics + SelectivityList selectivity(*tdbb->getDefaultPool()); + IDX_statistics(tdbb, relation->getPermanent(), lttIndex->id, selectivity); + + // The selectivity is stored directly in the index root page by IDX_statistics, + // so we don't need to update any system tables (LTT indexes don't have entries in RDB$INDICES) + } + + return false; +} + + +// Make a new format version for a LTT. +static bool make_ltt_version(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +{ + switch (phase) + { + case 1: + case 2: + return true; + + case 3: + break; + + default: + return false; + } + + const auto attachment = tdbb->getAttachment(); + const auto dbb = tdbb->getDatabase(); + auto& attPool = *attachment->att_pool; + + const auto lttPtr = attachment->att_local_temporary_tables.get(work->getQualifiedName()); + if (!lttPtr) + return false; + + const auto ltt = *lttPtr; + + if (!ltt->hasPendingChanges) + return false; + + fb_assert(ltt->pendingFields.hasData()); + + jrd_rel* relation = ltt->relation; + const bool isAlter = (relation != nullptr); + + if (!isAlter) + { + fb_assert(ltt->relationId != 0); + + // CREATE: Create a new relation + AutoPtr permRel = FB_NEW_POOL(attPool) Cached::Relation(tdbb, attPool, ltt->relationId); + permRel->rel_name = ltt->name; + permRel->rel_flags = REL_sql_relation | REL_temp_ltt | + (ltt->relationType == rel_temp_preserve ? REL_temp_conn : REL_temp_tran); + + // Initialize base pages for LTT to act as a connection-wide blueprint for indexes. + permRel->getBasePages()->rel_pg_space_id = dbb->dbb_page_manager.getTempPageSpaceID(tdbb); + DPM_create_relation_pages(tdbb, permRel, permRel->getBasePages()); + + // create versioned part + AutoPtr versRel = FB_NEW_POOL(attPool) jrd_rel(attPool, permRel); + permRel->storeObject(tdbb, versRel, 0); + permRel.release(); + relation = versRel.release(); + + relation->rel_current_fmt = 1; + + // Note: LTTs should NOT be registered in mdc_relations. + // They are stored only in att_local_temporary_tables and accessed via getXxx. + } + else + { + // ALTER: Bump the format version + if (relation->rel_current_fmt == MAX_TABLE_VERSIONS) + RelationNode::raiseTooManyVersionsError(obj_relation, work->getQualifiedName()); + + relation->rel_current_fmt++; + } + + // Update the fields vector - extend if needed for new fields + relation->rel_fields = vec::newVector(attPool, relation->rel_fields, ltt->pendingFields.getCount()); + + TemporaryField* tempFields = nullptr; + + for (FB_SIZE_T i = 0; i < ltt->pendingFields.getCount(); ++i) + { + const auto& lttField = ltt->pendingFields[i]; + auto& relField = (*relation->rel_fields)[i]; + + if (!relField) + relField = FB_NEW_POOL(attPool) jrd_fld(attPool); + + relField->fld_name = lttField.name; + relField->fld_not_null = nullptr; + relField->fld_validation = nullptr; + + if (lttField.notNullFlag) + { + Jrd::ContextPoolHolder context(tdbb, &attPool); + relField->fld_not_null = PAR_validation_blr(tdbb, &relation->getName().schema, relation->getPermanent(), + nonnull_validation_blr, sizeof(nonnull_validation_blr), NULL, NULL, 0); + } + + const auto tempField = FB_NEW_POOL(attPool) TemporaryField(); + tempField->tfb_next = tempFields; + tempFields = tempField; + + tempField->tfb_desc = lttField.desc; + + // For text types, set the text type from charset/collation + if (DTYPE_IS_TEXT(lttField.desc.dsc_dtype)) + { + const auto charSetId = lttField.charSetId.value_or(CS_NONE); + const auto collationId = lttField.collationId.value_or(COLLATE_NONE); + tempField->tfb_desc.setTextType(TTypeId(charSetId, collationId)); + } + + memset(&tempField->tfb_default, 0, sizeof(tempField->tfb_default)); + tempField->tfb_id = lttField.id; + } + + relation->rel_current_format = RelationNode::makeFormat(tdbb, transaction, + relation->getPermanent(), &relation->rel_current_fmt, tempFields); + + // Store the runtime relation in the LTT (for CREATE case) + ltt->relation = relation; + + // Commit the changes: copy pendingFields to fields + ltt->fields = ltt->pendingFields; + ltt->pendingFields.clear(); + ltt->hasPendingChanges = false; + + // Now invalidate DSQL cache at commit time + // METD_drop_relation(transaction, work->getQualifiedName()); + + return false; +} + + diff --git a/src/jrd/dyn_ut_proto.h b/src/jrd/dyn_ut_proto.h index 6a978a8f903..bd2a9b65f4d 100644 --- a/src/jrd/dyn_ut_proto.h +++ b/src/jrd/dyn_ut_proto.h @@ -36,10 +36,11 @@ void DYN_UTIL_generate_index_name(Jrd::thread_db*, Jrd::jrd_tra*, Jrd::Qualified void DYN_UTIL_generate_field_position(Jrd::thread_db*, const Jrd::QualifiedName&, SLONG*); void DYN_UTIL_generate_field_name(Jrd::thread_db*, Jrd::QualifiedName&); void DYN_UTIL_generate_constraint_name(Jrd::thread_db*, Jrd::QualifiedName&); -bool DYN_UTIL_check_unique_name_nothrow(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction, - const Jrd::QualifiedName& object_name, int object_type, USHORT* errorCode = nullptr, bool freeWhenPossible = false); -void DYN_UTIL_check_unique_name(Jrd::thread_db* tdbb, Jrd::jrd_tra* transaction, - const Jrd::QualifiedName& object_name, int object_type); +bool DYN_UTIL_check_unique_name_nothrow(Jrd::thread_db* tdbb, + const Jrd::QualifiedName& object_name, int object_type, USHORT* errorCode = nullptr, + bool freeWhenPossible = false); +void DYN_UTIL_check_unique_name(Jrd::thread_db* tdbb, + const Jrd::QualifiedName& object_name, int object_type); SINT64 DYN_UTIL_gen_unique_id(Jrd::thread_db*, SSHORT, const char*); #endif // JRD_DYN_UT_PROTO_H diff --git a/src/jrd/dyn_util.epp b/src/jrd/dyn_util.epp index 273e6c8c942..5f9b769f0f3 100644 --- a/src/jrd/dyn_util.epp +++ b/src/jrd/dyn_util.epp @@ -83,7 +83,7 @@ static const UCHAR gen_id_blr2[] = }; // Check if an object already exists. If yes, return false. -bool DYN_UTIL_check_unique_name_nothrow(thread_db* tdbb, jrd_tra* transaction, +bool DYN_UTIL_check_unique_name_nothrow(thread_db* tdbb, const QualifiedName& object_name, int object_type, USHORT* errorCode, bool freeWhenPossible) { SET_TDBB(tdbb); @@ -357,7 +357,7 @@ void DYN_UTIL_check_unique_name(thread_db* tdbb, const QualifiedName& object_nam { USHORT errorCode; - if (!DYN_UTIL_check_unique_name_nothrow(tdbb, transaction, object_name, object_type, &errorCode, true)) + if (!DYN_UTIL_check_unique_name_nothrow(tdbb, object_name, object_type, &errorCode, true)) status_exception::raise(Arg::PrivateDyn(errorCode) << object_name.toQuotedString()); } diff --git a/src/jrd/idx.cpp b/src/jrd/idx.cpp index fc938156d3b..16cf61feb33 100644 --- a/src/jrd/idx.cpp +++ b/src/jrd/idx.cpp @@ -2053,5 +2053,3 @@ static idx_e insert_key(thread_db* tdbb, return result; } - - diff --git a/src/jrd/irq.h b/src/jrd/irq.h index 1fda5dfc62b..21b2ca1e602 100644 --- a/src/jrd/irq.h +++ b/src/jrd/irq.h @@ -31,8 +31,6 @@ enum irq_type_t { irq_s_pages, // store PAGES irq_r_pages, // read PAGES - irq_l_field, // lookup field name - irq_c_relation, // create new relation irq_format1, // make a new format for a record irq_format2, // make a new format for a record irq_format3, // make a new format for a record @@ -85,7 +83,6 @@ enum irq_type_t irq_l_exp_index_blr, // lookup expression index BLR irq_l_cond_index, // lookup condition index - irq_l_procedure, // lookup procedure name irq_l_proc_id, // lookup procedure id irq_r_params, // scan procedure parameters @@ -173,8 +170,6 @@ enum irq_type_t irq_grant17, // process grant option (database) irq_grant18, // process grant option (filters) irq_grant19, // process grant option (roles) - irq_l_curr_format, // lookup table's current format - irq_c_relation3, // lookup relation in phase 0 to cleanup irq_linger, // get database linger value irq_dbb_ss_definer, // get database sql security value irq_proc_param_dep, // check procedure parameter dependency @@ -184,6 +179,7 @@ enum irq_type_t irq_index_id_erase, // cleanup index ID irq_get_index_by_name, // find appropriate index irq_l_index_cnstrt, // lookup index for constraint + irq_l_const_name, irq_l_const_id, irq_l_constants, diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 4763e4cd575..98203b7aa71 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -7837,7 +7837,10 @@ void release_attachment(thread_db* tdbb, Jrd::Attachment* attachment, XThreadEns dbb->dbb_extManager->closeAttachment(tdbb, attachment); if (dbb->dbb_config->getServerMode() == MODE_SUPER) + { + attachment->releaseLocalTempTables(tdbb); dbb->dbb_mdc->releaseGTTs(tdbb); + } if (attachment->att_event_session) dbb->eventManager()->deleteSession(attachment->att_event_session); diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 4f7668a8de3..6b5f0632b56 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -2418,8 +2418,6 @@ std::optional jrd_rel::getIdByName(thread_db* tdbb, const QualifiedName& Attachment* attachment = tdbb->getAttachment(); std::optional id; - relation = nullptr; - // We need to look up the relation name in RDB$RELATIONS AUTO_HANDLE(request); @@ -2438,14 +2436,6 @@ std::optional jrd_rel::getIdByName(thread_db* tdbb, const QualifiedName& } -CharSetVers* MetadataCache::lookup_charset(thread_db* tdbb, CSetId id, ObjectBase::Flag flags) -{ - SET_TDBB(tdbb); - - return MetadataCache::get(tdbb)->mdc_charsets.getVersioned(tdbb, id, flags); -} - - DmlNode* MET_parse_blob(thread_db* tdbb, const MetaName* schema, Cached::Relation* relation, @@ -3140,20 +3130,13 @@ ScanResult jrd_rel::scan(thread_db* tdbb, ObjectBase::Flag& flags) fb_assert(rel_perm->rel_flags & REL_virtual); rel_perm->rel_flags |= REL_virtual; break; - case rel_global_temp_preserve: - fb_assert(rel_perm->rel_flags & REL_temp_conn); - rel_perm->rel_flags |= REL_temp_conn; - break; - case rel_global_temp_delete: - fb_assert(rel_perm->rel_flags & REL_temp_tran); - rel_perm->rel_flags |= REL_temp_tran; case rel_temp_preserve: fb_assert((rel_perm->rel_flags & (REL_temp_conn | REL_temp_gtt)) == (REL_temp_conn | REL_temp_gtt)); rel_perm->rel_flags |= REL_temp_conn | REL_temp_gtt; break; case rel_temp_delete: - fb_assert((relation->rel_flags & (REL_temp_tran | REL_temp_gtt)) == (REL_temp_tran | REL_temp_gtt)); - relation->rel_flags |= REL_temp_tran | REL_temp_gtt; + fb_assert((rel_perm->rel_flags & (REL_temp_tran | REL_temp_gtt)) == (REL_temp_tran | REL_temp_gtt)); + rel_perm->rel_flags |= REL_temp_tran | REL_temp_gtt; break; default: fb_assert(false); @@ -4212,7 +4195,9 @@ void MET_store_dependencies(thread_db* tdbb, SET_TDBB(tdbb); - const Trigger* t; + fb_assert(!dep_rel || !(dep_rel->getPermanent()->rel_flags & REL_temp_ltt)); + + const Trigger* t = nullptr; const bool checkTableScope = (dependency_type == obj_computed) || (dependency_type == obj_trigger) && dep_rel && (t = dep_rel->findTrigger(object_name)) && (t->sysTrigger); diff --git a/src/jrd/met.h b/src/jrd/met.h index ac7e3a79074..c54e85bbce8 100644 --- a/src/jrd/met.h +++ b/src/jrd/met.h @@ -268,23 +268,11 @@ class MetadataCache : public Firebird::PermanentStorage return mdc_relations.getCount(); } - Function* getFunction(thread_db* tdbb, MetaId id, ObjectBase::Flag flags) - { - return mdc_functions.getVersioned(tdbb, id, flags); - } - - jrd_prc* getProcedure(thread_db* tdbb, MetaId id) - { - return mdc_procedures.getVersioned(tdbb, id, CacheFlag::AUTOCREATE); - } - Constant* getConstant(thread_db* tdbb, MetaId id) { return mdc_constants.getVersioned(tdbb, id, CacheFlag::AUTOCREATE); } - static Cached::CharSet* getCharSet(thread_db* tdbb, CSetId id, ObjectBase::Flag flags); - void cleanup(Jrd::thread_db*); #ifdef NEVERDEF diff --git a/src/jrd/met_proto.h b/src/jrd/met_proto.h index 6184e2ae2c2..67f21fc19ca 100644 --- a/src/jrd/met_proto.h +++ b/src/jrd/met_proto.h @@ -58,12 +58,12 @@ namespace Jrd class DeferredWork; struct FieldInfo; class ExceptionItem; + class LocalTemporaryTable; class GeneratorItem; class BlobFilter; class RelationPermanent; class Triggers; class TrigArray; - class LocalTemporaryTable; typedef Firebird::HalfStaticArray CharsetVariants; diff --git a/src/jrd/obj.h b/src/jrd/obj.h index ebf75dee5e5..b2374d6cc82 100644 --- a/src/jrd/obj.h +++ b/src/jrd/obj.h @@ -81,7 +81,7 @@ inline constexpr ObjectType obj_schemas = 39; inline constexpr ObjectType obj_package_constant = 40; -inline constexpr ObjectType obj_type_MAX = 42; +inline constexpr ObjectType obj_type_MAX = 41; // used in the parser only / no relation with obj_type_MAX (should be greater) inline constexpr ObjectType obj_user_or_role = 100; diff --git a/src/jrd/tra.cpp b/src/jrd/tra.cpp index 6038d942d20..95acecdc038 100644 --- a/src/jrd/tra.cpp +++ b/src/jrd/tra.cpp @@ -2446,6 +2446,7 @@ void MetadataCache::release_temp_tables(thread_db* tdbb, jrd_tra* transaction) * This is called on full commit/rollback, not on commit retaining. * **************************************/ + // Release GTTs (Global Temporary Tables) with transaction lifetime for (auto* relation : MetadataCache::get(tdbb)->mdc_relations) { if (relation->rel_flags & REL_temp_tran) @@ -2483,6 +2484,8 @@ void MetadataCache::retain_temp_tables(thread_db* tdbb, jrd_tra* transaction, Tr * to preserve the data in ON COMMIT DELETE ROWS tables. * **************************************/ + + // Retain GTTs (Global Temporary Tables) with transaction lifetime for (auto* relation : MetadataCache::get(tdbb)->mdc_relations) { if (relation->rel_flags & REL_temp_tran) diff --git a/src/jrd/tra.h b/src/jrd/tra.h index b0a37776014..e6e8867060a 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -489,7 +489,6 @@ enum dfw_t : int { dfw_commit_relation, dfw_create_relation, dfw_delete_relation, - dfw_update_format, dfw_update_ltt_format, dfw_create_index, dfw_create_ltt_index, diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index ce75efe9f50..45ad3bb0986 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -4718,7 +4718,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) default: // Shut up compiler warnings break; } - + rpb->rpb_b_page = 0; rpb->rpb_b_line = 0; rpb->rpb_flags = 0; From 51fc8d7d0877fb635e165a34e8e971d263d00bee Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 25 Feb 2026 10:52:23 +0300 Subject: [PATCH 020/119] One more cleanup (preparation) before PR --- src/dsql/DdlNodes.epp | 1 + src/dsql/PackageNodes.epp | 2 +- src/dsql/PackageNodes.h | 2 +- src/include/firebird/impl/msg/dyn.h | 7 +++---- src/include/gen/Firebird.pas | 30 ++++++++++++++--------------- src/jrd/Resources.h | 2 +- src/jrd/dfw.epp | 1 + src/jrd/relations.h | 12 ++++++------ 8 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 69c69218999..6a075a029fe 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -11579,6 +11579,7 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra { if (!aliasName) aliasName = referenceNode->getName(); + nameNode = nullptr; } } diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index e2a80c049e4..b11c2cba5c9 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -364,7 +364,7 @@ ValueExprNode* PackageReferenceNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) bool isPrivate = false; if (constantExists(tdbb, transaction, m_fullName, &isPrivate)) { - // External items have not access to private constants + // External objects do not have access to private constants. if (isPrivate) { status_exception::raise(Arg::Gds(isc_private_constant) << diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index a58dd3bb45f..1d4cd176adb 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -115,7 +115,7 @@ class PackageItemsHolder ItemsSignatureArray functions; ItemsSignatureArray procedures; ItemsSignatureArray constants; -}; // PackageItemCollector +}; class PackageReferenceNode final : public TypedNode { diff --git a/src/include/firebird/impl/msg/dyn.h b/src/include/firebird/impl/msg/dyn.h index f2e4314f714..e7f2afe0c8b 100644 --- a/src/include/firebird/impl/msg/dyn.h +++ b/src/include/firebird/impl/msg/dyn.h @@ -313,7 +313,6 @@ FB_IMPL_MSG(DYN, 320, dyn_cannot_create_reserved_schema, -607, "HY", "000", "Sch FB_IMPL_MSG(DYN, 321, dyn_cannot_infer_schema, -901, "42", "000", "Cannot infer schema name as there is no valid schema in the search path") FB_IMPL_MSG_SYMBOL(DYN, 322, dyn_dup_blob_filter, "Blob filter @1 already exists") FB_IMPL_MSG(DYN, 323, dyn_column_name_exists, -612, "42", "000", "Column @1 already exists in table @2") -FB_IMPL_MSG_SYMBOL(DYN, 324, dyn_dup_blob_filter, "Blob filter @1 already exists") -FB_IMPL_MSG_SYMBOL(DYN, 335, dyn_const_not_found, "Constant @1 not found") -FB_IMPL_MSG_SYMBOL(DYN, 336, dyn_dup_const, "Constant @1 already exists") -FB_IMPL_MSG_SYMBOL(DYN, 337, dyn_non_constant_constant, "The constant \"@1\" must be initialized by a constant expression") +FB_IMPL_MSG_SYMBOL(DYN, 334, dyn_const_not_found, "Constant @1 not found") +FB_IMPL_MSG_SYMBOL(DYN, 335, dyn_dup_const, "Constant @1 already exists") +FB_IMPL_MSG_SYMBOL(DYN, 336, dyn_non_constant_constant, "The constant \"@1\" must be initialized by a constant expression") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index c7f8e1d76f5..5b2f4726f49 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5955,14 +5955,14 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_no_user_att_while_restore = 335545318; isc_genseq_stepmustbe_nonzero = 335545319; isc_argmustbe_exact_function = 335545320; - isc_not_defined_constant = 335545396; - isc_const_name = 335545397; - isc_private_constant = 335545398; - isc_package_alias_help = 335545399; - isc_bad_constant_blr_error = 335545400; - isc_bad_constant_type_error = 335545401; - isc_bad_constant_name = 335545402; - isc_bad_constant_type = 335545403; + isc_not_defined_constant = 335545321; + isc_const_name = 335545322; + isc_private_constant = 335545323; + isc_package_alias_help = 335545324; + isc_bad_constant_blr_error = 335545325; + isc_bad_constant_type_error = 335545326; + isc_bad_constant_name = 335545327; + isc_bad_constant_type = 335545328; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; @@ -6226,10 +6226,10 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_gbak_invalid_data = 336331094; isc_gbak_inv_bkup_ver2 = 336331096; isc_gbak_db_format_too_old2 = 336331100; - isc_gbak_writing_constants = 336331294; - isc_gbak_writing_constant = 336331295; - isc_gbak_constant = 336331296; - isc_gbak_restoring_constant = 336331297; + isc_gbak_writing_constants = 336331200; + isc_gbak_writing_constant = 336331201; + isc_gbak_constant = 336331202; + isc_gbak_restoring_constant = 336331203; isc_dsql_too_old_ods = 336397205; isc_dsql_table_not_found = 336397206; isc_dsql_view_not_found = 336397207; @@ -6366,9 +6366,9 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_dsql_recreate_schema_failed = 336397338; isc_dsql_alter_schema_failed = 336397339; isc_dsql_create_alter_schema_failed = 336397340; - isc_dsql_create_const_failed = 336397372; - isc_dsql_alter_const_failed = 336397373; - isc_dsql_create_alter_const_failed = 336397374; + isc_dsql_create_const_failed = 336397341; + isc_dsql_alter_const_failed = 336397342; + isc_dsql_create_alter_const_failed = 336397343; isc_gsec_cant_open_db = 336723983; isc_gsec_switches_error = 336723984; isc_gsec_no_op_spec = 336723985; diff --git a/src/jrd/Resources.h b/src/jrd/Resources.h index a6f6e768e7f..a451beed8ea 100644 --- a/src/jrd/Resources.h +++ b/src/jrd/Resources.h @@ -326,7 +326,7 @@ namespace Rsc typedef CachedResource CSet; typedef CachedResource Trig; typedef CachedResource Idx; - typedef CachedResource Const; // useless? + typedef CachedResource Const; }; //namespace Rsc diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index d936fb0b8a5..e27eea6b8e5 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -402,6 +402,7 @@ static bool modify_field(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_global(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_parameter(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_rfr(thread_db*, SSHORT, DeferredWork*, jrd_tra*); +static bool make_ltt_version(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool add_difference(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool delete_difference(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool begin_backup(thread_db*, SSHORT, DeferredWork*, jrd_tra*); diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 4d3410c315b..d9f73f9638e 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -842,12 +842,12 @@ END_RELATION // Relation 59 (RDB$CONSTANTS) RELATION(nam_constants, rel_constants, ODS_13_1, rel_persistent) - FIELD(f_const_name, nam_const_name, fld_f_name, 0, ODS_13_1) + 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_13_1) - FIELD(f_const_field, nam_f_source, fld_f_name, 0, ODS_13_1) - FIELD(f_const_private_flag, nam_private_flag, fld_flag_nullable, 0, ODS_13_1) - FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_13_1) - FIELD(f_const_source, nam_const_source, fld_source, 1, ODS_13_1) + FIELD(f_const_package, nam_pkg_name, fld_pkg_name, 1, ODS_14_0) + FIELD(f_const_field, nam_f_source, fld_f_name, 0, ODS_14_0) + FIELD(f_const_private_flag, nam_private_flag, fld_flag_nullable, 0, ODS_14_0) + FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_14_0) + FIELD(f_const_source, nam_const_source, fld_source, 1, ODS_14_0) FIELD(f_const_package_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) END_RELATION From bd085c9c842ab4fab74e73c7fdcb16b0aa7c7bb5 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 25 Feb 2026 21:19:00 +0300 Subject: [PATCH 021/119] Add ability to add comment on package constant --- doc/sql.extensions/README.ddl.txt | 1 + src/dsql/DdlNodes.epp | 26 +++++++++++++++++++++++++- src/dsql/parse.y | 1 + src/jrd/relations.h | 1 + 4 files changed, 28 insertions(+), 1 deletion(-) diff --git a/doc/sql.extensions/README.ddl.txt b/doc/sql.extensions/README.ddl.txt index bb11f8c1bbe..b63c4fb0442 100644 --- a/doc/sql.extensions/README.ddl.txt +++ b/doc/sql.extensions/README.ddl.txt @@ -138,6 +138,7 @@ COMMENT ON name IS {'txt'|NULL}; COMMENT ON COLUMN table_or_view_name.field_name IS {'txt'|NULL}; COMMENT ON {PROCEDURE | [EXTERNAL] FUNCTION} [ .] name.param_name IS {'txt'|NULL}; COMMENT ON [PROCEDURE | FUNCTION] PARAMETER [ .] name.param_name IS {'txt'|NULL}; +COMMENT ON CONSTANT [ .] . 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. diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 6a075a029fe..57a5e918805 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -1477,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; + + 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; @@ -1581,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); } @@ -1744,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; @@ -1767,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) diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 6856c4dde5c..7cfc67c5174 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -6286,6 +6286,7 @@ ddl_type3 : PARAMETER { $$ = obj_parameter; } | PROCEDURE PARAMETER { $$ = obj_procedure; } | FUNCTION PARAMETER { $$ = obj_udf; } + | CONSTANT { $$ = obj_package_constant; } ; %type ddl_type4 diff --git a/src/jrd/relations.h b/src/jrd/relations.h index d9f73f9638e..21c20f5bff3 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -850,4 +850,5 @@ RELATION(nam_constants, rel_constants, ODS_13_1, rel_persistent) FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_14_0) FIELD(f_const_source, nam_const_source, fld_source, 1, ODS_14_0) FIELD(f_const_package_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) + FIELD(f_const_comment, nam_description, fld_description, 1, ODS_14_0) END_RELATION From 469505084961bcc2bbaba3b9d547d36b71be308d Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 27 Feb 2026 01:10:40 +0300 Subject: [PATCH 022/119] Remove unneccecery namespace scope --- src/dsql/PackageNodes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 1d4cd176adb..5af9bf13f74 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -148,7 +148,7 @@ class PackageReferenceNode final : public TypedNode m_constant; + CachedResource m_constant; const QualifiedName m_fullName; const UCHAR m_itemType; From e606d59c4487cf10ea34315d17353d3f88cbdb44 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 27 Feb 2026 01:20:33 +0300 Subject: [PATCH 023/119] Make Constant value in reload state when MINISCAN is set --- src/jrd/Constant.epp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 6e990af0022..8d6e8673a9e 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -122,7 +122,10 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) if (ssDefiner.asBool()) invoker = dbb->getUserId(getPermanent()->owner); - makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); + if ((flags & CacheFlag::MINISCAN) == 0) + makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); + else + this->flReload = true; // Call makeValue in reload } END_FOR } From cf7929e291c19d1e645ed4cd9718eaaf081204c1 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 27 Feb 2026 01:22:19 +0300 Subject: [PATCH 024/119] Remove not used variable and omit unneccecery namespace scope --- src/jrd/Constant.epp | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 8d6e8673a9e..8a1c87ed8b9 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -33,7 +33,7 @@ #include "../common/dsc_proto.h" #include "../jrd/met_proto.h" #include "../jrd/met.h" -#include "../jrd/Statement.h" // Jrd::Statement +#include "../jrd/Statement.h" // Statement #include "../jrd/par_proto.h" // PAR_blr #include "../jrd/cvt_proto.h" // CVT_get_string_ptr @@ -198,14 +198,14 @@ void Constant::checkReload(thread_db* tdbb) const class DropSavepoint { public: - DropSavepoint(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name) : + DropSavepoint(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) : m_savePoint(tdbb, transaction), m_tdbb(tdbb) { MetadataCache::getVersioned(tdbb, name, CacheFlag::OLD_DROP); } - DropSavepoint(thread_db* tdbb, Jrd::jrd_tra* transaction) : + DropSavepoint(thread_db* tdbb, jrd_tra* transaction) : m_savePoint(tdbb, transaction), m_tdbb(tdbb) { @@ -231,10 +231,9 @@ private: thread_db* m_tdbb; MetaId m_id{}; bool m_found = false; - bool m_succeed = false; }; -void Constant::drop(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name) +void Constant::drop(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) { // Run all statements under savepoint control DropSavepoint savePoint(tdbb, transaction, name); @@ -255,7 +254,7 @@ void Constant::drop(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedN savePoint.release(); // everything is ok } -void Constant::dropAllFromPackage(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& parent, bool privateFlag) +void Constant::dropAllFromPackage(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& parent, bool privateFlag) { AutoCacheRequest eraseConstantRequest(tdbb, drq_e_pkg_consts_by_flag, DYN_REQUESTS); FOR (REQUEST_HANDLE eraseConstantRequest TRANSACTION_HANDLE transaction) @@ -274,7 +273,7 @@ void Constant::dropAllFromPackage(thread_db* tdbb, Jrd::jrd_tra* transaction, co END_FOR } -dsc Constant::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name) +dsc Constant::getDesc(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) { dsc desc{}; bool succeed = false; @@ -446,7 +445,7 @@ void Constant::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, Attachment* attachment = tdbb->getAttachment(); MemoryPool* csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); - Jrd::ContextPoolHolder context(tdbb, csb_pool); + ContextPoolHolder context(tdbb, csb_pool); CompilerScratch* csb = nullptr; Cleanup cc([&tdbb, requestToRestore = tdbb->getRequest(), &csb]() @@ -477,7 +476,7 @@ void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) try { csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); - Jrd::ContextPoolHolder context(tdbb, csb_pool); + ContextPoolHolder context(tdbb, csb_pool); try { From 12a7b2b6955c9e832e3920218e291147b8fede97 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 27 Feb 2026 10:38:29 +0300 Subject: [PATCH 025/119] Update src/isql/show.epp Co-authored-by: Adriano dos Santos Fernandes <529415+asfernandes@users.noreply.github.com> --- src/isql/show.epp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/isql/show.epp b/src/isql/show.epp index 4e87f26c88a..5237bb2d382 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -6812,7 +6812,7 @@ static processing_state show_constants(const std::optional& CONST.RDB$PACKAGE_NAME EQ constant.package.c_str() AND CONST.RDB$CONSTANT_NAME EQ constant.object.c_str() SORTED BY CONST.RDB$CONSTANT_NAME - + { bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; const char* type = isPrivate ? "BODY" : "HEADER"; From edca2c529793df576c89614007c2794f6ce10c17 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 27 Feb 2026 11:17:29 +0300 Subject: [PATCH 026/119] First part of fixes/corrections related to PR issues --- doc/sql.extensions/README.packages.txt | 3 ++- src/burp/OdsDetection.epp | 2 +- src/dsql/DdlNodes.epp | 2 +- src/dsql/ExprNodes.cpp | 15 +++++++------ src/include/firebird/impl/msg/gbak.h | 13 ++++------- src/include/gen/Firebird.pas | 8 +++---- src/isql/FrontendParser.cpp | 4 ++-- src/isql/show.epp | 11 ++++++---- src/jrd/Constant.epp | 30 ++++++++++++++++---------- src/jrd/Constant.h | 10 ++++++--- src/jrd/SysFunction.cpp | 2 +- src/jrd/dfw.epp | 3 ++- src/jrd/irq.h | 7 ------ src/jrd/relations.h | 4 ++-- 14 files changed, 60 insertions(+), 54 deletions(-) diff --git a/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt index ce46c5f6dc6..c3433557a6c 100644 --- a/doc/sql.extensions/README.packages.txt +++ b/doc/sql.extensions/README.packages.txt @@ -101,9 +101,10 @@ Objectives: 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 . notation. + the [.]. notation. Constants declared in the package body are private and cannot be accessed from outside the package. However, they can be referenced directly by within and . + Header constants can also be used directly with just the name for package body elements. - Facilitate permission management. diff --git a/src/burp/OdsDetection.epp b/src/burp/OdsDetection.epp index 02cf616d8b7..bc77b611ff6 100644 --- a/src/burp/OdsDetection.epp +++ b/src/burp/OdsDetection.epp @@ -46,7 +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 + {"RDB$CONSTANTS", 0, DB_VERSION_DDL14}, // FB6 {0, 0, 0} }; diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 57a5e918805..f735a1b137d 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -1596,7 +1596,7 @@ void CommentOnNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) break; case obj_package_constant: - SCL_check_package(tdbb, QualifiedName(name.package, name.schema), SCL_alter); + SCL_check_package(tdbb, name.getSchemaAndPackage(), SCL_alter); break; default: diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 849c27f5cb8..ebeb4fe2652 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -6467,24 +6467,25 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec } } + // Check conflict beween . and . dsql_ctx packageContext(dsqlScratch->getPool()); { // Consatnts - const QualifiedName consatntName(dsqlName, + const QualifiedName constantName(dsqlName, dsqlQualifier.schema.hasData() ? dsqlQualifier.schema : (dsqlScratch->package.schema.hasData() ? dsqlScratch->package.schema : PUBLIC_SCHEMA), dsqlQualifier.object.hasData() ? dsqlQualifier.object : dsqlScratch->package.object); - if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), consatntName)) + if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), constantName)) { 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_alias.push(QualifiedName(constantName.package, constantName.schema)); packageContext.ctx_flags |= CTX_package; ambiguousCtxStack.push(&packageContext); MemoryPool& pool = dsqlScratch->getPool(); - node = FB_NEW_POOL(pool) PackageReferenceNode(pool, consatntName); + node = FB_NEW_POOL(pool) PackageReferenceNode(pool, constantName); } } @@ -14199,11 +14200,11 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) 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)) + QualifiedName constantFullName(dsqlName, dsqlScratch->package.schema, dsqlScratch->package.object); + if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), constantFullName)) { delete node; - return FB_NEW_POOL(dsqlScratch->getPool()) PackageReferenceNode(dsqlScratch->getPool(), consatntFullName); + return FB_NEW_POOL(dsqlScratch->getPool()) PackageReferenceNode(dsqlScratch->getPool(), constantFullName); } } diff --git a/src/include/firebird/impl/msg/gbak.h b/src/include/firebird/impl/msg/gbak.h index 907729b7942..ca1c4b5981c 100644 --- a/src/include/firebird/impl/msg/gbak.h +++ b/src/include/firebird/impl/msg/gbak.h @@ -418,12 +418,7 @@ FB_IMPL_MSG_NO_SYMBOL(GBAK, 419, "regular expression to skip schemas was already FB_IMPL_MSG_NO_SYMBOL(GBAK, 420, "regular expression to include schemas was already set") FB_IMPL_MSG_SYMBOL(GBAK, 421, gbak_plugin_schema_migration, "migrating @1 plugin objects to schema @2") FB_IMPL_MSG_SYMBOL(GBAK, 422, gbak_plugin_schema_migration_err, "error migrating @1 plugin objects to schema @2. Plugin objects will be in inconsistent state:") -FB_IMPL_MSG_NO_SYMBOL(GBAK, 423, "Compressor plugin '@1' not found") -FB_IMPL_MSG_NO_SYMBOL(GBAK, 424, " @1COM(PRESSOR) plugin name, compression level and threads count") -FB_IMPL_MSG_NO_SYMBOL(GBAK, 435, "Expected @1 instead of @2") -FB_IMPL_MSG_NO_SYMBOL(GBAK, 446, "compression level @1") -FB_IMPL_MSG_NO_SYMBOL(GBAK, 447, "'@1' compression plugin used") -FB_IMPL_MSG(GBAK, 448, gbak_writing_constants, -901, "00", "000", "writing constants") -FB_IMPL_MSG(GBAK, 449, gbak_writing_constant, -901, "00", "000", "writing constant %s for package %s") -FB_IMPL_MSG(GBAK, 450, gbak_constant, -901, "00", "000", "constant (in RDB$CONSTANTS)") -FB_IMPL_MSG(GBAK, 451, gbak_restoring_constant, -901, "00", "000", "restoring constant %s for package %s") +FB_IMPL_MSG(GBAK, 423, gbak_writing_constants, -901, "00", "000", "writing constants") +FB_IMPL_MSG(GBAK, 424, gbak_writing_constant, -901, "00", "000", "writing constant %s for package %s") +FB_IMPL_MSG(GBAK, 425, gbak_constant, -901, "00", "000", "constant (in RDB$CONSTANTS)") +FB_IMPL_MSG(GBAK, 426, gbak_restoring_constant, -901, "00", "000", "restoring constant %s for package %s") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 5b2f4726f49..635f8fdebc1 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -6226,10 +6226,10 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_gbak_invalid_data = 336331094; isc_gbak_inv_bkup_ver2 = 336331096; isc_gbak_db_format_too_old2 = 336331100; - isc_gbak_writing_constants = 336331200; - isc_gbak_writing_constant = 336331201; - isc_gbak_constant = 336331202; - isc_gbak_restoring_constant = 336331203; + isc_gbak_writing_constants = 336331175; + isc_gbak_writing_constant = 336331176; + isc_gbak_constant = 336331177; + isc_gbak_restoring_constant = 336331178; isc_dsql_too_old_ods = 336397205; isc_dsql_table_not_found = 336397206; isc_dsql_view_not_found = 336397207; diff --git a/src/isql/FrontendParser.cpp b/src/isql/FrontendParser.cpp index 45214320f09..f922d3fef55 100644 --- a/src/isql/FrontendParser.cpp +++ b/src/isql/FrontendParser.cpp @@ -491,7 +491,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() static constexpr std::string_view TOKEN_VIEWS("VIEWS"); static constexpr std::string_view TOKEN_WIRE_STATISTICS("WIRE_STATISTICS"); static constexpr std::string_view TOKEN_WIRE_STATS("WIRE_STATS"); - static constexpr std::string_view TOKEN_CONSTNATS("CONSTANTS"); + static constexpr std::string_view TOKEN_CONSTANTS("CONSTANTS"); switch (const auto showCommandToken = lexer.getToken(); showCommandToken.type) { @@ -650,7 +650,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() } else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_VIEWS, 4)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_CONSTNATS, 4)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_CONSTANTS, 4)) return parsed.value(); else if (text.length() >= 9 && TOKEN_WIRE_STATISTICS.find(text) == 0 || text == TOKEN_WIRE_STATS) diff --git a/src/isql/show.epp b/src/isql/show.epp index 5237bb2d382..af88daa4dd6 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -6748,7 +6748,7 @@ static void print_constant_type(const char* fieldName, const char* schemaName) FOR FLD IN RDB$FIELDS WITH FLD.RDB$SCHEMA_NAME EQ schemaName AND FLD.RDB$FIELD_NAME EQ fieldName - + { // Decide if this is a user-created domain if (!fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) || FLD.RDB$SYSTEM_FLAG == 1) { @@ -6792,7 +6792,7 @@ static void print_constant_type(const char* fieldName, const char* schemaName) isqlGlob.printf(" "); SHOW_print_metadata_text_blob(isqlGlob.Out, &FLD.RDB$DEFAULT_SOURCE); } - + } END_FOR ON_ERROR ISQL_errmsg (fbStatus); @@ -6821,7 +6821,7 @@ static processing_state show_constants(const std::optional& first = false; show_constant(constant.object.c_str()); - + } END_FOR ON_ERROR ISQL_errmsg(fbStatus); @@ -6836,6 +6836,7 @@ static processing_state show_constants(const std::optional& FOR CONST IN RDB$CONSTANTS SORTED BY CONST.RDB$CONSTANT_NAME bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + { const char* type = isPrivate ? "BODY" : "HEADER"; if (first && msg) @@ -6845,6 +6846,7 @@ static processing_state show_constants(const std::optional& fb_utils::exact_name(CONST.RDB$CONSTANT_NAME); show_constant(CONST.RDB$CONSTANT_NAME); + } END_FOR ON_ERROR ISQL_errmsg(fbStatus); @@ -6859,7 +6861,7 @@ static processing_state show_constant(const SCHAR* constantName) FOR CONST IN RDB$CONSTANTS WITH CONST.RDB$CONSTANT_NAME EQ constantName SORTED BY CONST.RDB$CONSTANT_NAME - + { bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; if (!CONST.RDB$PACKAGE_NAME.NULL) @@ -6876,6 +6878,7 @@ static processing_state show_constant(const SCHAR* constantName) isqlGlob.printf(NEWLINE); + } END_FOR ON_ERROR ISQL_errmsg(fbStatus); diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 8a1c87ed8b9..07f937184bc 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -75,9 +75,10 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) //try { - AutoCacheRequest request_const(tdbb, irq_l_constants, IRQ_REQUESTS); + static const CachedRequestId requestId; + AutoCacheRequest requestConst(tdbb, requestId); - FOR(REQUEST_HANDLE request_const TRANSACTION_HANDLE metaTransaction) + FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) CONST IN RDB$CONSTANTS WITH CONST.RDB$CONSTANT_ID EQ this->getId() { @@ -90,7 +91,8 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) TriState ssDefiner; if (!CONST.RDB$PACKAGE_NAME.NULL) { - AutoCacheRequest requestHandle(tdbb, irq_l_const_pkg_class, IRQ_REQUESTS); + static const CachedRequestId requestId; + AutoCacheRequest requestHandle(tdbb, requestId); FOR (REQUEST_HANDLE requestHandle) PKG IN RDB$PACKAGES @@ -140,7 +142,8 @@ std::optional Constant::getIdByName(thread_db* tdbb, const QualifiedName Attachment* attachment = tdbb->getAttachment(); std::optional id; - AutoCacheRequest request(tdbb, irq_l_const_id, IRQ_REQUESTS); + static const CachedRequestId requestId; + AutoCacheRequest request(tdbb, requestId); FOR (REQUEST_HANDLE request) CONST IN RDB$CONSTANTS @@ -160,7 +163,9 @@ ScanResult Constant::reload(thread_db* tdbb, ObjectBase::Flag) Attachment* attachment = tdbb->getAttachment(); Database* dbb = tdbb->getDatabase(); jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); - AutoCacheRequest request(tdbb, irq_l_const_name, IRQ_REQUESTS); + + static const CachedRequestId requestId; + AutoCacheRequest request(tdbb, requestId); bool found = false; @@ -195,17 +200,17 @@ void Constant::checkReload(thread_db* tdbb) const } -class DropSavepoint +class DropConstantSavepoint { public: - DropSavepoint(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) : + DropConstantSavepoint(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) : m_savePoint(tdbb, transaction), m_tdbb(tdbb) { MetadataCache::getVersioned(tdbb, name, CacheFlag::OLD_DROP); } - DropSavepoint(thread_db* tdbb, jrd_tra* transaction) : + DropConstantSavepoint(thread_db* tdbb, jrd_tra* transaction) : m_savePoint(tdbb, transaction), m_tdbb(tdbb) { @@ -236,9 +241,11 @@ private: void Constant::drop(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) { // Run all statements under savepoint control - DropSavepoint savePoint(tdbb, transaction, name); + DropConstantSavepoint savePoint(tdbb, transaction, name); + + static const CachedRequestId requestId; + AutoCacheRequest request(tdbb, requestId); - AutoCacheRequest request(tdbb, drq_e_pkg_const_name, DYN_REQUESTS); FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) CONST IN RDB$CONSTANTS WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND @@ -263,7 +270,7 @@ void Constant::dropAllFromPackage(thread_db* tdbb, jrd_tra* transaction, const Q CONST.RDB$PACKAGE_NAME EQ parent.object.c_str() AND CONST.RDB$PRIVATE_FLAG EQ privateFlag { - DropSavepoint savePoint(tdbb, transaction, QualifiedName(CONST.RDB$CONSTANT_NAME, parent.schema, parent.object)); + DropConstantSavepoint savePoint(tdbb, transaction, QualifiedName(CONST.RDB$CONSTANT_NAME, parent.schema, parent.object)); savePoint.setFound(CONST.RDB$CONSTANT_ID); ERASE CONST; @@ -339,6 +346,7 @@ static void executeConstantExpression(thread_db* tdbb, CompilerScratch* csb, Mem } +// Convert a literalNode-unsupported constant type to a supported one if necessary static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const dsc& scalar) { // Make the blr with only the LiteralNode diff --git a/src/jrd/Constant.h b/src/jrd/Constant.h index 142e49b6d9c..3aff104137d 100644 --- a/src/jrd/Constant.h +++ b/src/jrd/Constant.h @@ -51,13 +51,17 @@ class Constant final : public Routine private: explicit Constant(Cached::Constant* perm) : Routine(perm->getPool()), - cachedConstant(perm) - { } + cachedConstant(perm) + { + flReload = true; + } public: explicit Constant(MemoryPool& p) : Routine(p) - { } + { + flReload = true; + } static Constant* create(thread_db* tdbb, MemoryPool& pool, Cached::Constant* perm); ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags); diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 2a2ca5fb5a1..04b5c5fd25c 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -6910,7 +6910,7 @@ dsc* evlUnicodeVal(thread_db* tdbb, const SysFunction*, const NestValueArray& ar -constexpr auto DEFAULT = SysFunction::DETERMINISTIC | SysFunction::CONSTANT; +constexpr static auto DEFAULT = SysFunction::DETERMINISTIC | SysFunction::CONSTANT; const SysFunction SysFunction::functions[] = { // name, minArgCount, maxArgCount, deterministic, setParamsFunc, makeFunc, evlFunc, misc diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index e27eea6b8e5..0431c7c5eed 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1143,7 +1143,8 @@ namespace bool compile) { Jrd::Attachment* attachment = tdbb->getAttachment(); - AutoCacheRequest handle(tdbb, irq_c_const_dpd, IRQ_REQUESTS); + CachedRequestId constDpd; + AutoCacheRequest handle(tdbb, constDpd); Constant* routine = nullptr; FOR(REQUEST_HANDLE handle) diff --git a/src/jrd/irq.h b/src/jrd/irq.h index 21b2ca1e602..979c817d911 100644 --- a/src/jrd/irq.h +++ b/src/jrd/irq.h @@ -179,13 +179,6 @@ enum irq_type_t irq_index_id_erase, // cleanup index ID irq_get_index_by_name, // find appropriate index irq_l_index_cnstrt, // lookup index for constraint - - irq_l_const_name, - irq_l_const_id, - irq_l_constants, - irq_c_const_dpd, // get constant dependencies - irq_l_const_pkg_class, - irq_MAX }; diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 21c20f5bff3..89c88870e47 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -841,10 +841,10 @@ RELATION(nam_mon_local_temp_table_columns, rel_mon_local_temp_table_columns, ODS END_RELATION // Relation 59 (RDB$CONSTANTS) -RELATION(nam_constants, rel_constants, ODS_13_1, rel_persistent) +RELATION(nam_constants, rel_constants, ODS_14_0, 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) + FIELD(f_const_package, nam_pkg_name, fld_pkg_name, 0, ODS_14_0) FIELD(f_const_field, nam_f_source, fld_f_name, 0, ODS_14_0) FIELD(f_const_private_flag, nam_private_flag, fld_flag_nullable, 0, ODS_14_0) FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_14_0) From cfdf4446d15af2e1f6fe1d2b23783c43aa162a2e Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Sat, 28 Feb 2026 00:36:34 +0300 Subject: [PATCH 027/119] Resolve more PR issues --- src/dsql/DdlNodes.epp | 2 +- src/dsql/DdlNodes.h | 12 --- src/dsql/ExprNodes.cpp | 26 +++--- src/dsql/ExprNodes.h | 180 +++++++++++++++++++++++--------------- src/dsql/Nodes.h | 24 +++-- src/dsql/PackageNodes.epp | 35 ++++++-- src/jrd/Constant.epp | 23 +++-- src/jrd/Constant.h | 2 + src/jrd/dfw.epp | 1 + src/jrd/drq.h | 11 --- src/jrd/met.h | 2 +- 11 files changed, 185 insertions(+), 133 deletions(-) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index f735a1b137d..d5fa4a804f2 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -878,7 +878,7 @@ static dsql_rel* saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, } // Update RDB$FIELDS received by reference. -void updateRdbFields(const TypeClause* type, +void DdlNode::updateRdbFields(const TypeClause* type, SSHORT& fieldType, SSHORT& fieldLength, SSHORT& fieldSubTypeNull, SSHORT& fieldSubType, diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 64c89b6acfb..85baa340f4d 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -42,18 +42,6 @@ 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, diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index ebeb4fe2652..7affd1964ac 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -456,33 +456,33 @@ bool ExprNode::unmappable(const MapNode* mapNode, StreamType shellStream) const return true; } -bool ExprNode::constant() const +void ExprNode::collectStreams(SortedStreamList& streamList) const { NodeRefsHolder holder; getChildren(holder, false); for (auto i : holder.refs) { - if (*i == nullptr) - continue; - - if (!(*i)->constant()) - return false; + if (*i) + (*i)->collectStreams(streamList); } - - return true; } -void ExprNode::collectStreams(SortedStreamList& streamList) const +bool ExprNode::isChildrenConstant() const { NodeRefsHolder holder; getChildren(holder, false); for (auto i : holder.refs) { - if (*i) - (*i)->collectStreams(streamList); + if (*i == nullptr) + continue; + + if (!(*i)->constant()) + return false; } + + return true; } bool ExprNode::computable(CompilerScratch* csb, StreamType stream, @@ -6467,7 +6467,7 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec } } - // Check conflict beween . and . + // Use context to check conflicts beween . and . dsql_ctx packageContext(dsqlScratch->getPool()); { // Consatnts @@ -12509,7 +12509,7 @@ bool SysFuncCallNode::deterministic(thread_db* tdbb) const bool SysFuncCallNode::constant() const { - return ExprNode::constant() && function->isConstant(); + return ExprNode::isChildrenConstant() && function->isConstant(); } void SysFuncCallNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index b1785c25ce3..8501bdb9712 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -88,6 +88,11 @@ class ArithmeticNode final : public TypedNode void genBlr(DsqlCompilerScratch* dsqlScratch) override; void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override; + bool constant() const override + { + return isChildrenConstant(); + } + void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override; ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override; ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) override; @@ -236,6 +246,11 @@ class BoolAsValueNode final : public TypedNode void genBlr(DsqlCompilerScratch* dsqlScratch) override; void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override; + bool constant() const override + { + return isChildrenConstant(); + } + void setDsqlDesc(const dsc& desc) { dsqlDesc = desc; @@ -320,6 +340,11 @@ class CoalesceNode final : public TypedNode void genBlr(DsqlCompilerScratch* dsqlScratch) override; void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override; + bool constant() const override + { + return isChildrenConstant(); + } + bool possiblyUnknown() const override { return true; @@ -680,6 +695,11 @@ class DefaultNode final : public DsqlNode void genBlr(DsqlCompilerScratch* dsqlScratch) override; void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override; + bool constant() const override + { + return isChildrenConstant(); + } + bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override; ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb) override; @@ -739,6 +759,11 @@ class DerivedExprNode final : public TypedNode return streams.exist(fieldStream); } - bool constant() const override - { - return false; - } - void collectStreams(SortedStreamList& streamList) const override { if (!streamList.exist(fieldStream)) @@ -944,11 +974,6 @@ class GenIdNode final : public TypedNode return false; } - bool constant() const override - { - return false; - } - void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override; ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override; bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override; @@ -997,11 +1022,6 @@ class InternalInfoNode final : public TypedNode void genBlr(DsqlCompilerScratch* dsqlScratch) override; void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override; + bool constant() const override + { + return isChildrenConstant(); + } + void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override; ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override; ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) override; @@ -1365,6 +1385,11 @@ class NullNode final : public TypedNode void genBlr(DsqlCompilerScratch* dsqlScratch) override; void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override; + bool constant() const override + { + return true; + } + void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override; ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override; dsc* execute(thread_db* tdbb, Request* request) const override; @@ -1728,11 +1753,6 @@ class ParameterNode final : public TypedNode fb_assert(false); } + bool constant() const override + { + return isChildrenConstant(); + } + void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override; ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override; ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) override; @@ -1985,6 +2010,11 @@ class StrCaseNode final : public TypedNode void genBlr(DsqlCompilerScratch* dsqlScratch) override; void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override; + bool constant() const override + { + return isChildrenConstant(); + } + void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override; ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override; bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override; @@ -2078,11 +2113,6 @@ class SubQueryNode final : public TypedNode void genBlr(DsqlCompilerScratch* dsqlScratch) override; void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override; + bool constant() const override + { + return isChildrenConstant(); + } + void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override; ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override; bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override; @@ -2296,11 +2341,6 @@ class UdfCallNode final : public TypedNode > MetaNameBidPair; typedef Firebird::GenericMap MetaNameBidMap; @@ -690,11 +702,16 @@ class ExprNode : public DmlNode virtual bool unmappable(const MapNode* mapNode, StreamType shellStream) const; // Check if expression returns constant result - virtual bool constant() const; + virtual bool constant() const + { + return false; + } // Return all streams referenced by the expression. virtual void collectStreams(SortedStreamList& streamList) const; + bool isChildrenConstant() const; + bool containsStream(StreamType stream, bool only = false) const { SortedStreamList nodeStreams; @@ -1231,11 +1248,6 @@ class RecordSourceNode : public ExprNode return false; } - virtual bool constant() const override - { - return false; - } - void collectStreams(SortedStreamList& streamList) const override { if (!streamList.exist(getStream())) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index b11c2cba5c9..77d88c812ee 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -313,14 +313,17 @@ void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transact if (collectConstants) { - AutoCacheRequest getConstantsRequest(tdbb, drq_l_pkg_consts, DYN_REQUESTS); - FOR(REQUEST_HANDLE getConstantsRequest TRANSACTION_HANDLE transaction) + static const CachedRequestId requestId; + AutoCacheRequest getConstantsRequest(tdbb, requestId); + FOR (REQUEST_HANDLE getConstantsRequest TRANSACTION_HANDLE transaction) CONST IN RDB$CONSTANTS WITH CONST.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND CONST.RDB$PACKAGE_NAME EQ packageName.object.c_str() + { Signature constant(CONST.RDB$CONSTANT_NAME); constants.add(constant); + } END_FOR } } @@ -456,19 +459,22 @@ bool PackageReferenceNode::constantExists(thread_db* tdbb, Jrd::jrd_tra* transac // Use default schema if one not specified const char* schemaName = fullName.schema.hasData() ? fullName.schema.c_str() : PUBLIC_SCHEMA; - AutoCacheRequest getConstantRequest(tdbb, drq_l_pkg_const_flag, DYN_REQUESTS); + + static const CachedRequestId requestId; + AutoCacheRequest getConstantRequest(tdbb, requestId); FOR (REQUEST_HANDLE getConstantRequest TRANSACTION_HANDLE transaction) CONST IN RDB$CONSTANTS WITH CONST.RDB$SCHEMA_NAME EQ schemaName AND CONST.RDB$PACKAGE_NAME EQ fullName.package.c_str() AND CONST.RDB$CONSTANT_NAME EQ fullName.object.c_str() - + { if (isPrivate) { *isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; } return true; + } END_FOR return false; @@ -582,7 +588,9 @@ void CreatePackageConstantNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { FbLocalStatus status; - AutoCacheRequest storeConstantRequest(tdbb, drq_s_pkg_const, DYN_REQUESTS); + + static const CachedRequestId requestId; + AutoCacheRequest storeConstantRequest(tdbb, requestId); // Check uniqueness if (PackageReferenceNode::constantExists(tdbb, transaction, name)) @@ -668,8 +676,12 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { FbLocalStatus status; - AutoCacheRequest eraseDscRequest(tdbb, drq_e_pkg_const_dsc, DYN_REQUESTS); - AutoCacheRequest modifyConstantRequest(tdbb, drq_m_pkg_const, DYN_REQUESTS); + + static const CachedRequestId eraseRequestId; + AutoCacheRequest eraseDscRequest(tdbb, eraseRequestId); + + static const CachedRequestId modifyRequestId; + AutoCacheRequest modifyConstantRequest(tdbb, modifyRequestId); bid blobId; bool found = false; @@ -986,6 +998,7 @@ void CreateAlterPackageNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* DYN_UTIL_check_unique_name(tdbb, name, obj_package_header); + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_s_pkg, DYN_REQUESTS); STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) @@ -1031,6 +1044,8 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* { MemoryPool& pool = dsqlScratch->getPool(); Attachment* attachment = transaction->getAttachment(); + + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_pkg, DYN_REQUESTS); bool modified = false; @@ -1091,6 +1106,7 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* bool CreateAlterPackageNode::executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_prm_pkg, DYN_REQUESTS); bool modified = false; @@ -1176,6 +1192,8 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, AutoSavePoint savePoint(tdbb, transaction); bool found = false; + + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_e_pkg, DYN_REQUESTS); FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) @@ -1360,6 +1378,7 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body, DYN_REQUESTS); bool modified = false; @@ -1516,6 +1535,8 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra AutoSavePoint savePoint(tdbb, transaction); bool found = false; + + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body2, DYN_REQUESTS); FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 07f937184bc..61f6f180d0f 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -96,11 +96,10 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) FOR (REQUEST_HANDLE requestHandle) PKG IN RDB$PACKAGES - CROSS SCH IN RDB$SCHEMAS - OVER RDB$SCHEMA_NAME WITH + CROSS SCH IN RDB$SCHEMAS WITH PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME - + { owner = PKG.RDB$OWNER_NAME; @@ -112,7 +111,7 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) // SQL SECURITY of function must be the same if it's defined in package if (!PKG.RDB$SQL_SECURITY.NULL) ssDefiner = (bool) PKG.RDB$SQL_SECURITY; - + } END_FOR } @@ -263,7 +262,8 @@ void Constant::drop(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& void Constant::dropAllFromPackage(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& parent, bool privateFlag) { - AutoCacheRequest eraseConstantRequest(tdbb, drq_e_pkg_consts_by_flag, DYN_REQUESTS); + static const CachedRequestId requestId; + AutoCacheRequest eraseConstantRequest(tdbb, requestId); FOR (REQUEST_HANDLE eraseConstantRequest TRANSACTION_HANDLE transaction) CONST IN RDB$CONSTANTS WITH CONST.RDB$SCHEMA_NAME EQ parent.schema.c_str() AND @@ -286,7 +286,8 @@ dsc Constant::getDesc(thread_db* tdbb, jrd_tra* transaction, const QualifiedName bool succeed = false; FbLocalStatus status; - AutoCacheRequest getConstantDscRequest(tdbb, drq_l_pkg_const_dsc, DYN_REQUESTS); + static const CachedRequestId requestId; + AutoCacheRequest getConstantDscRequest(tdbb, requestId); FOR(REQUEST_HANDLE getConstantDscRequest TRANSACTION_HANDLE transaction) CONST IN RDB$CONSTANTS CROSS FLD IN RDB$FIELDS WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND @@ -351,7 +352,7 @@ static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlS { // Make the blr with only the LiteralNode { - Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); + BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); blr.clear(); dsqlScratch->getDebugData().clear(); } @@ -376,7 +377,7 @@ static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlS } case dtype_real: { - double newValue = *(float*)scalar.dsc_address; + double newValue = *(float*) scalar.dsc_address; dsc descForDouble{}; descForDouble.makeDouble(); @@ -437,13 +438,13 @@ void Constant::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema) { { // Prepare BLR writer - Firebird::BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); + BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); blr.clear(); dsqlScratch->getDebugData().clear(); } // Gen blr into dsqlScratch - Firebird::AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0)); + AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0)); CastNode cast(*tempPool, constExpr, type); { dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); @@ -458,8 +459,6 @@ void Constant::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, CompilerScratch* csb = nullptr; Cleanup cc([&tdbb, requestToRestore = tdbb->getRequest(), &csb]() { - // if (statement) - // statement->release(tdbb); delete csb; tdbb->setRequest(requestToRestore); }); diff --git a/src/jrd/Constant.h b/src/jrd/Constant.h index 3aff104137d..75a51751ff3 100644 --- a/src/jrd/Constant.h +++ b/src/jrd/Constant.h @@ -53,6 +53,7 @@ class Constant final : public Routine : Routine(perm->getPool()), cachedConstant(perm) { + // Make sure the constant value will be read in at less at reload state flReload = true; } @@ -60,6 +61,7 @@ class Constant final : public Routine explicit Constant(MemoryPool& p) : Routine(p) { + // Make sure the constant value will be read in at less at reload state flReload = true; } diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 0431c7c5eed..75be1d41190 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1149,6 +1149,7 @@ namespace FOR(REQUEST_HANDLE handle) X IN RDB$CONSTANTS WITH + X.RDB$SCHEMA_NAME EQ work->dfw_schema.c_str() AND X.RDB$CONSTANT_NAME EQ work->dfw_name.c_str() AND X.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), "") { diff --git a/src/jrd/drq.h b/src/jrd/drq.h index b42da1b085a..b2d24f2a4f4 100644 --- a/src/jrd/drq.h +++ b/src/jrd/drq.h @@ -242,17 +242,6 @@ enum drq_type_t drq_e_pub_tab_all, // erase relation from all publication drq_l_rel_con, // lookup relation constraint drq_l_rel_fld_name, // lookup relation field name - - // constants - drq_l_pkg_consts, // get all constants of a package - drq_l_pkg_const_flag, // lookup for a flag of a package constant by its name - drq_l_pkg_const_dsc, // lookup for dsc of a package constant - drq_l_pkg_const_blr, // lookup for blr of a package constant - drq_s_pkg_const, // store a package constant - drq_m_pkg_const, // modify a package constant by its name - drq_e_pkg_const_name, // erase constant by its name - drq_e_pkg_consts_by_flag, // erase all public or private constants of a package - drq_e_pkg_const_dsc, // erase dsc of a package constant drq_g_nxt_const_id, // lookup next constant ID drq_MAX diff --git a/src/jrd/met.h b/src/jrd/met.h index c54e85bbce8..2ab956901c2 100644 --- a/src/jrd/met.h +++ b/src/jrd/met.h @@ -268,7 +268,7 @@ class MetadataCache : public Firebird::PermanentStorage return mdc_relations.getCount(); } - Constant* getConstant(thread_db* tdbb, MetaId id) + Constant* getConstant(thread_db* tdbb, const MetaId id) { return mdc_constants.getVersioned(tdbb, id, CacheFlag::AUTOCREATE); } From 816ad9c2fbd86df296225b96d228e3e508dcfc89 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Sat, 28 Feb 2026 00:39:41 +0300 Subject: [PATCH 028/119] Add missing search for FIELD SCHEMA --- src/jrd/Constant.epp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 61f6f180d0f..25eb6110a03 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -293,6 +293,7 @@ dsc Constant::getDesc(thread_db* tdbb, jrd_tra* transaction, const QualifiedName WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND + FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE succeed = DSC_make_descriptor(&desc, From 13b7df993adda59c4ba546a95416a5beef8286cb Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Sat, 28 Feb 2026 00:46:53 +0300 Subject: [PATCH 029/119] Use better name for method --- src/jrd/Constant.epp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 25eb6110a03..54d282adfaf 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -311,7 +311,7 @@ dsc Constant::getDesc(thread_db* tdbb, jrd_tra* transaction, const QualifiedName return desc; } -static dsc* executeConstantExpressionFull(thread_db* tdbb, CompilerScratch* csb) +static dsc* executeConstantExpressionWithRequest(thread_db* tdbb, CompilerScratch* csb) { Statement* statement = Statement::makeStatement(tdbb, csb, true); @@ -471,7 +471,7 @@ void Constant::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, nullptr, false, 0); // Execute node from BLR - auto output = executeConstantExpressionFull(tdbb, csb); + auto output = executeConstantExpressionWithRequest(tdbb, csb); if (output != nullptr) genConstantCompatibleBlr(tdbb, dsqlScratch, *output); } From 46e0e5df89fa015009d903ac0f4788c0d8bd3f37 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Sun, 1 Mar 2026 14:04:17 +0300 Subject: [PATCH 030/119] Fix type in test --- src/isql/tests/FrontendParserTest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/isql/tests/FrontendParserTest.cpp b/src/isql/tests/FrontendParserTest.cpp index 4f3a897e16d..96de3e7468d 100644 --- a/src/isql/tests/FrontendParserTest.cpp +++ b/src/isql/tests/FrontendParserTest.cpp @@ -610,11 +610,11 @@ BOOST_AUTO_TEST_CASE(ParseShowTest) BOOST_TEST(std::holds_alternative(parseShow( "show wire_statistics"))); - BOOST_TEST(std::holds_alternative(parseShow( + BOOST_TEST(std::holds_alternative(parseShow( "show const"))); - BOOST_TEST(std::holds_alternative(parseShow( + BOOST_TEST(std::holds_alternative(parseShow( "show constant"))); - BOOST_TEST(std::holds_alternative(parseShow( + BOOST_TEST(std::holds_alternative(parseShow( "show constants"))); } From 51d9ab690e1cb826eb1274b7643b3d611ea22272 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Sun, 1 Mar 2026 14:32:39 +0300 Subject: [PATCH 031/119] Omit unneccecery getConstant method and add assert for constant existence --- src/jrd/Constant.epp | 3 +-- src/jrd/met.h | 5 ----- src/jrd/vio.cpp | 7 ++++++- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 54d282adfaf..4aa55481eef 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -49,8 +49,7 @@ DATABASE DB = FILENAME "ODS.RDB"; Constant* Constant::lookup(thread_db* tdbb, MetaId id) { - Constant* constant = MetadataCache::get(tdbb)->getConstant(tdbb, id); - return constant; + return MetadataCache::getVersioned(tdbb, id, CacheFlag::AUTOCREATE);; } Constant* Constant::lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags) diff --git a/src/jrd/met.h b/src/jrd/met.h index 2ab956901c2..086f8f501e0 100644 --- a/src/jrd/met.h +++ b/src/jrd/met.h @@ -268,11 +268,6 @@ class MetadataCache : public Firebird::PermanentStorage return mdc_relations.getCount(); } - Constant* getConstant(thread_db* tdbb, const MetaId id) - { - return mdc_constants.getVersioned(tdbb, id, CacheFlag::AUTOCREATE); - } - void cleanup(Jrd::thread_db*); #ifdef NEVERDEF diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 45ad3bb0986..b43d877223d 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -2359,7 +2359,12 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_const_id, &desc2); id = MOV_get_long(tdbb, &desc2, 0); - Constant::lookup(tdbb, id); + { + [[maybe_unused]] + auto constant = Constant::lookup(tdbb, id); + fb_assert(constant); + } + DFW_post_work(transaction, dfw_delete_package_constant, &desc, &schemaDesc, id, object_name.package); break; From 2ccea0fa5e4834e392204fbd1407fc1a0987cb15 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Sun, 1 Mar 2026 14:33:01 +0300 Subject: [PATCH 032/119] Mark RecordKeyNode as non constant --- src/dsql/ExprNodes.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index 8501bdb9712..9b196cdb6f4 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -1817,11 +1817,6 @@ class RecordKeyNode final : public TypedNode Date: Sun, 1 Mar 2026 16:14:37 +0300 Subject: [PATCH 033/119] Add missing code for backup-restore --- src/burp/OdsDetection.epp | 1 - src/burp/backup.epp | 62 ++++++++++++++++++++ src/burp/burp.h | 13 ++++- src/burp/restore.epp | 86 ++++++++++++++++++++++++++++ src/include/firebird/impl/msg/gbak.h | 4 +- src/jrd/Constant.epp | 1 + src/jrd/relations.h | 2 +- src/jrd/vio.cpp | 2 +- 8 files changed, 165 insertions(+), 6 deletions(-) diff --git a/src/burp/OdsDetection.epp b/src/burp/OdsDetection.epp index bc77b611ff6..2ca60361336 100644 --- a/src/burp/OdsDetection.epp +++ b/src/burp/OdsDetection.epp @@ -46,7 +46,6 @@ 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}, // FB6 {0, 0, 0} }; diff --git a/src/burp/backup.epp b/src/burp/backup.epp index f3df7267962..c3d36a71b49 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -156,6 +156,7 @@ void write_triggers(); void write_trigger_messages(); void write_types(); void write_user_privileges(); +void write_constants(); void general_on_error(); @@ -505,6 +506,13 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name) write_pub_tables(); } + if (tdgbl->runtimeODS >= DB_VERSION_DDL14) + { + // Write constants + BURP_verbose(USHORT(isc_gbak_writing_constants)); // writing constants + write_constants(); + } + // Finish up put(tdgbl, (UCHAR) rec_end); @@ -4831,6 +4839,60 @@ void write_user_privileges() MISC_release_request_silent(req_handle1); } +void write_constants() +{ + Firebird::IRequest* req_handle1 = nullptr; + + BurpGlobals* tdgbl = BurpGlobals::getSpecific(); + + FOR (REQUEST_HANDLE req_handle1) + CONST IN RDB$CONSTANTS + { + QualifiedMetaString name(CONST.RDB$CONSTANT_NAME); + + put(tdgbl, rec_constants); + PUT_TEXT(att_constant_name, CONST.RDB$CONSTANT_NAME); + + if (!CONST.RDB$PACKAGE_NAME.NULL) + { + PUT_TEXT(att_constant_package, CONST.RDB$PACKAGE_NAME); + name.package = CONST.RDB$PACKAGE_NAME; + } + + PUT_TEXT(att_constant_field_source, CONST.RDB$FIELD_SOURCE); + + if (!CONST.RDB$PRIVATE_FLAG.NULL) + put_int32(att_constant_private_flag, CONST.RDB$PRIVATE_FLAG); + + if (!CONST.RDB$CONSTANT_BLR.NULL) + put_blr_blob(att_constant_blr, CONST.RDB$CONSTANT_BLR); + + if (!CONST.RDB$CONSTANT_SOURCE.NULL) + put_source_blob(att_constant_source, att_constant_source, CONST.RDB$CONSTANT_SOURCE); + + if (!CONST.RDB$SCHEMA_NAME.NULL) + { + PUT_TEXT(att_constant_schema_name, CONST.RDB$SCHEMA_NAME); + name.schema = CONST.RDB$SCHEMA_NAME; + } + + if (!CONST.RDB$DESCRIPTION.NULL) + { + put_source_blob (att_constant_description, att_constant_description, CONST.RDB$DESCRIPTION); + } + + // writing constant %s + BURP_verbose(USHORT(isc_gbak_writing_constant), SafeArg() << name.toQuotedString().data()); + put(tdgbl, att_end); + } + END_FOR; + ON_ERROR + general_on_error(); + END_ERROR; + + MISC_release_request_silent(req_handle1); +} + } // namespace namespace Burp { diff --git a/src/burp/burp.h b/src/burp/burp.h index 71680b43b03..36c37c0ac22 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -124,7 +124,8 @@ enum rec_type { rec_db_creator, // Database creator rec_publication, // Publication rec_pub_table, // Publication table - rec_schema // Schema + rec_schema, // Schema + rec_constants // Constants }; @@ -701,6 +702,16 @@ enum att_type { att_schema_security_class, att_schema_owner_name, att_schema_description, + + // Constants + att_constant_name = SERIES, + att_constant_package, + att_constant_field_source, + att_constant_private_flag, + att_constant_blr, + att_constant_source, + att_constant_schema_name, + att_constant_description, }; diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 1108459d14d..e1affb98319 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -167,6 +167,7 @@ bool get_trigger_old (BurpGlobals* tdgbl, burp_rel*); bool get_type(BurpGlobals* tdgbl); bool get_user_privilege(BurpGlobals* tdgbl); bool get_view(BurpGlobals* tdgbl, burp_rel*); +bool get_constants_table(BurpGlobals* tdgbl); void ignore_array(BurpGlobals* tdgbl, burp_rel*); void ignore_blob(BurpGlobals* tdgbl); rec_type ignore_data(BurpGlobals* tdgbl, burp_rel*); @@ -10534,6 +10535,85 @@ bool get_view(BurpGlobals* tdgbl, burp_rel* relation) return true; } +bool get_constants_table(BurpGlobals* tdgbl) +{ + if (tdgbl->runtimeODS < DB_VERSION_DDL14) // FB6 + return false; + + QualifiedMetaString name; + + att_type attribute; + scan_attr_t scan_next_attr; + + Firebird::ITransaction* local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; + + STORE (TRANSACTION_HANDLE local_trans + REQUEST_HANDLE tdgbl->handles_get_type_req_handle1) + CONST IN RDB$CONSTANTS + { + skip_init(&scan_next_attr); + while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) + { + switch (attribute) + { + case att_constant_name: + CONST.RDB$CONSTANT_NAME.NULL = FALSE; + GET_TEXT(CONST.RDB$CONSTANT_NAME); + name.object = CONST.RDB$CONSTANT_NAME; + break; + + case att_constant_package: + CONST.RDB$PACKAGE_NAME.NULL = FALSE; + GET_TEXT(CONST.RDB$PACKAGE_NAME); + name.package = CONST.RDB$PACKAGE_NAME; + break; + + case att_constant_field_source: + CONST.RDB$FIELD_SOURCE.NULL = FALSE; + GET_TEXT(CONST.RDB$FIELD_SOURCE); + break; + + case att_constant_private_flag: + CONST.RDB$PRIVATE_FLAG.NULL = FALSE; + CONST.RDB$PRIVATE_FLAG = (USHORT) get_int32(tdgbl); + break; + + case att_constant_blr: + CONST.RDB$CONSTANT_BLR.NULL = FALSE; + get_blr_blob(tdgbl, CONST.RDB$CONSTANT_BLR, true); + break; + + case att_constant_source: + CONST.RDB$CONSTANT_SOURCE.NULL = FALSE; + get_source_blob(tdgbl, CONST.RDB$CONSTANT_SOURCE, true); + break; + + case att_constant_schema_name: + CONST.RDB$SCHEMA_NAME.NULL = FALSE; + GET_TEXT(CONST.RDB$SCHEMA_NAME); + name.schema = CONST.RDB$SCHEMA_NAME; + break; + + case att_constant_description: + CONST.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob(tdgbl, CONST.RDB$DESCRIPTION, true); + break; + default: + bad_attribute(scan_next_attr, attribute, USHORT(isc_gbak_constant)); + break; + } + } + + BURP_verbose(USHORT(isc_gbak_restoring_constant), SafeArg() << name.toQuotedString().data()); + } + END_STORE; + ON_ERROR + general_on_error(); + END_ERROR; + + return true; +} + void ignore_array(BurpGlobals* tdgbl, burp_rel* relation) { /************************************** @@ -11367,6 +11447,12 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file flag = true; break; + case rec_constants: + if (!get_constants_table(tdgbl)) + return false; + flag = true; + break; + default: BURP_error(43, true, SafeArg() << record); // msg 43 don't recognize record type %ld diff --git a/src/include/firebird/impl/msg/gbak.h b/src/include/firebird/impl/msg/gbak.h index ca1c4b5981c..c4c226fa482 100644 --- a/src/include/firebird/impl/msg/gbak.h +++ b/src/include/firebird/impl/msg/gbak.h @@ -419,6 +419,6 @@ FB_IMPL_MSG_NO_SYMBOL(GBAK, 420, "regular expression to include schemas was alre FB_IMPL_MSG_SYMBOL(GBAK, 421, gbak_plugin_schema_migration, "migrating @1 plugin objects to schema @2") FB_IMPL_MSG_SYMBOL(GBAK, 422, gbak_plugin_schema_migration_err, "error migrating @1 plugin objects to schema @2. Plugin objects will be in inconsistent state:") FB_IMPL_MSG(GBAK, 423, gbak_writing_constants, -901, "00", "000", "writing constants") -FB_IMPL_MSG(GBAK, 424, gbak_writing_constant, -901, "00", "000", "writing constant %s for package %s") +FB_IMPL_MSG(GBAK, 424, gbak_writing_constant, -901, "00", "000", "writing constant %s") FB_IMPL_MSG(GBAK, 425, gbak_constant, -901, "00", "000", "constant (in RDB$CONSTANTS)") -FB_IMPL_MSG(GBAK, 426, gbak_restoring_constant, -901, "00", "000", "restoring constant %s for package %s") +FB_IMPL_MSG(GBAK, 426, gbak_restoring_constant, -901, "00", "000", "restoring constant %s") diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 4aa55481eef..4df9d189217 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -39,6 +39,7 @@ #include "../jrd/cvt_proto.h" // CVT_get_string_ptr #include "../jrd/mov_proto.h" // MOV_get_string_ptr #include "../common/classes/VaryStr.h" +#include "../common/classes/alloc.h" // ALLOC_ARGS0 using namespace Firebird; using namespace Jrd; diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 89c88870e47..d2ef9e4de13 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -850,5 +850,5 @@ RELATION(nam_constants, rel_constants, ODS_14_0, rel_persistent) FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_14_0) FIELD(f_const_source, nam_const_source, fld_source, 1, ODS_14_0) FIELD(f_const_package_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) - FIELD(f_const_comment, nam_description, fld_description, 1, ODS_14_0) + FIELD(f_const_description, nam_description, fld_description, 1, ODS_14_0) END_RELATION diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index b43d877223d..88a867d79c5 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -4688,7 +4688,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_const_name, &desc); EVL_field(0, rpb->rpb_record, f_const_package_schema, &schemaDesc); - if (EVL_field(0, rpb->rpb_record, f_const_name, &desc2)) + if (EVL_field(0, rpb->rpb_record, f_const_package, &desc2)) MOV_get_metaname(tdbb, &desc2, object_name.package); object_id = set_metadata_id(tdbb, rpb->rpb_record, From 2f11ba4ee756d9cbab9dad9c5ab192100b01dedc Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Sun, 1 Mar 2026 16:14:51 +0300 Subject: [PATCH 034/119] Add missing source for windows build --- builds/win32/msvc15/engine_static.vcxproj | 1 + 1 file changed, 1 insertion(+) diff --git a/builds/win32/msvc15/engine_static.vcxproj b/builds/win32/msvc15/engine_static.vcxproj index 67bbab0013e..1543ed65a93 100644 --- a/builds/win32/msvc15/engine_static.vcxproj +++ b/builds/win32/msvc15/engine_static.vcxproj @@ -41,6 +41,7 @@ + From 44b74d7d27f5353ad444569b375a7778c60cd69b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 3 Mar 2026 13:08:06 +0300 Subject: [PATCH 035/119] Do not base Constant class on Routine --- src/dsql/PackageNodes.h | 2 +- src/jrd/Constant.epp | 49 +++++++++++++++++--------- src/jrd/Constant.h | 77 ++++++++++++++++++++++++++++++++--------- src/jrd/Resources.h | 9 ++--- src/jrd/dfw.epp | 16 ++++----- src/jrd/met.epp | 1 + 6 files changed, 108 insertions(+), 46 deletions(-) diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 5af9bf13f74..5bff32bb07b 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -148,7 +148,7 @@ class PackageReferenceNode final : public TypedNode m_constant; + CachedResource m_constant; const QualifiedName m_fullName; const UCHAR m_itemType; diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 4df9d189217..a60a4a21344 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -50,7 +50,7 @@ DATABASE DB = FILENAME "ODS.RDB"; Constant* Constant::lookup(thread_db* tdbb, MetaId id) { - return MetadataCache::getVersioned(tdbb, id, CacheFlag::AUTOCREATE);; + return MetadataCache::getVersioned(tdbb, id, CacheFlag::AUTOCREATE); } Constant* Constant::lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags) @@ -103,10 +103,10 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) owner = PKG.RDB$OWNER_NAME; - if (!PKG.RDB$SECURITY_CLASS.NULL) - { - getPermanent()->setSecurityName(QualifiedName(PKG.RDB$SECURITY_CLASS, SCH.RDB$SECURITY_CLASS)); - } + // if (!PKG.RDB$SECURITY_CLASS.NULL) + // { + // getPermanent()->setSecurityName(QualifiedName(PKG.RDB$SECURITY_CLASS, SCH.RDB$SECURITY_CLASS)); + // } // SQL SECURITY of function must be the same if it's defined in package if (!PKG.RDB$SQL_SECURITY.NULL) @@ -120,18 +120,18 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) ssDefiner = MET_get_ss_definer(tdbb, CONST.RDB$SCHEMA_NAME); } - if (ssDefiner.asBool()) - invoker = dbb->getUserId(getPermanent()->owner); + // if (ssDefiner.asBool()) + // invoker = dbb->getUserId(getPermanent()->owner); if ((flags & CacheFlag::MINISCAN) == 0) makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); else - this->flReload = true; // Call makeValue in reload + this->m_callReload = true; // Call makeValue in reload } END_FOR } - return found ? (this->flReload ? ScanResult::REPEAT : ScanResult::COMPLETE) : ScanResult::MISS; + return found ? (this->m_callReload ? ScanResult::REPEAT : ScanResult::COMPLETE) : ScanResult::MISS; } @@ -173,11 +173,11 @@ ScanResult Constant::reload(thread_db* tdbb, ObjectBase::Flag) WITH CONST.RDB$CONSTANT_ID EQ this->getId() { - if (compiling) - { - fb_assert(false); - return ScanResult::REPEAT; - } + // if (compiling) + // { + // fb_assert(false); + // return ScanResult::REPEAT; + // } found = true; makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); @@ -192,9 +192,24 @@ int Constant::objectType() return obj_package_constant; } +bool Constant::hash(thread_db* tdbb, Firebird::sha512& digest) +{ + if (m_value.vlu_desc.dsc_dtype == 0) + { + dsc type = getDesc(tdbb, tdbb->getTransaction(), getName()); + digest.process(sizeof(type), &type); + } + else + { + digest.process(sizeof(m_value.vlu_desc), &m_value.vlu_desc); + } + + return true; +} + void Constant::checkReload(thread_db* tdbb) const { - if (flReload) + if (m_callReload) getPermanent()->reload(tdbb, 0); } @@ -492,11 +507,11 @@ void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) Cleanup cc([csb]() {delete csb;}); { // blr - flReload = false; + m_callReload = false; MET_parse_blob(tdbb, &getName().schema, nullptr, &blob_id, &csb, nullptr, false, false); fb_assert(csb != nullptr); - flReload = (csb->csb_g_flags & csb_reload); + m_callReload = (csb->csb_g_flags & csb_reload); } executeConstantExpression(tdbb, csb, getPermanent()->getPool(), m_value); diff --git a/src/jrd/Constant.h b/src/jrd/Constant.h index 75a51751ff3..7e03b8db56e 100644 --- a/src/jrd/Constant.h +++ b/src/jrd/Constant.h @@ -29,7 +29,8 @@ #define JRD_CONSTANT_H #include "firebird.h" -#include "../jrd/Routine.h" +#include "../jrd/CacheVector.h" +#include "../jrd/Resources.h" #include "../jrd/obj.h" #include "../jrd/val.h" #include "../jrd/lck.h" @@ -39,36 +40,72 @@ namespace Jrd class DsqlCompilerScratch; class dsql_fld; -class Constant final : public Routine +class ConstantPermanent : public Firebird::PermanentStorage +{ +public: + explicit ConstantPermanent(thread_db* tdbb, MemoryPool& p, MetaId metaId, NoData) + : PermanentStorage(p), + id(metaId), + name(p) + { } + + explicit ConstantPermanent(MemoryPool& p) + : PermanentStorage(p), + id(~0), + name(p) + { } + + MetaId getId() const + { + return id; + } + + static bool destroy(thread_db* tdbb, ConstantPermanent* routine) + { + return false; + } + + void releaseLock(thread_db*) { } + + const QualifiedName& getName() const noexcept { return name; } + void setName(const QualifiedName& value) { name = value; } + + bool hasData() const { return name.hasData(); } + +public: + MetaId id; // routine ID + QualifiedName name; // routine name +}; + +class Constant final : public Firebird::PermanentStorage, public ObjectBase { public: static Constant* lookup(thread_db* tdbb, MetaId id); static Constant* lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags); -public: + // lock requeued by CacheElement static const enum lck_t LOCKTYPE = LCK_constant_rescan; private: explicit Constant(Cached::Constant* perm) - : Routine(perm->getPool()), + : Firebird::PermanentStorage(perm->getPool()), cachedConstant(perm) - { - // Make sure the constant value will be read in at less at reload state - flReload = true; - } + { } public: explicit Constant(MemoryPool& p) - : Routine(p) + : Firebird::PermanentStorage(p) + { } + + static bool destroy(thread_db* tdbb, Constant* routine) { - // Make sure the constant value will be read in at less at reload state - flReload = true; + return false; } static Constant* create(thread_db* tdbb, MemoryPool& pool, Cached::Constant* perm); ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags); static std::optional getIdByName(thread_db* tdbb, const QualifiedName& name); - void checkReload(thread_db* tdbb) const override; + void checkReload(thread_db* tdbb) const; static const char* objectFamily(void*) { @@ -76,12 +113,15 @@ class Constant final : public Routine } public: - int getObjectType() const noexcept final + const QualifiedName& getName() const noexcept { return getPermanent()->getName(); } + MetaId getId() const noexcept { return getPermanent()->getId(); } + + int getObjectType() const noexcept { return objectType(); } - SLONG getSclType() const noexcept final + SLONG getSclType() const noexcept { return obj_package_constant; } @@ -90,6 +130,8 @@ class Constant final : public Routine static int objectType(); + bool hash(thread_db* tdbb, Firebird::sha512& digest); + public: inline const dsc& getValue() const { @@ -106,7 +148,7 @@ class Constant final : public Routine private: void makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id); - virtual ~Constant() override + virtual ~Constant() { delete m_value.vlu_string; } @@ -114,13 +156,16 @@ class Constant final : public Routine public: Cached::Constant* cachedConstant; // entry in the cache - Cached::Constant* getPermanent() const noexcept override + Cached::Constant* getPermanent() const noexcept { return cachedConstant; } private: impure_value m_value{}; + + // Make sure the constant value will be read in at less at reload state + bool m_callReload = true; }; } // namespace Jrd diff --git a/src/jrd/Resources.h b/src/jrd/Resources.h index a451beed8ea..6c6eebbdab7 100644 --- a/src/jrd/Resources.h +++ b/src/jrd/Resources.h @@ -43,6 +43,7 @@ class CharSetVers; class IndexPermanent; class IndexVersion; class Constant; +class ConstantPermanent; namespace Cached { @@ -53,7 +54,7 @@ namespace Cached typedef CacheElement Function; typedef CacheElement Triggers; typedef CacheElement Index; - typedef CacheElement Constant; + typedef CacheElement Constant; } class Resources; @@ -300,7 +301,7 @@ class Resources final RscArray functions; RscArray triggers; RscArray indices; - RscArray constants; + RscArray constants; inline FB_SIZE_T countVersionedObjects() const noexcept { @@ -316,7 +317,7 @@ template <> inline const Resources::RscArray& Resour template <> inline const Resources::RscArray& Resources::objects() const { return charSets; } template <> inline const Resources::RscArray& Resources::objects() const { return triggers; } template <> inline const Resources::RscArray& Resources::objects() const { return indices; } -template <> inline const Resources::RscArray& Resources::objects() const { return constants; } +template <> inline const Resources::RscArray& Resources::objects() const { return constants; } namespace Rsc { @@ -326,7 +327,7 @@ namespace Rsc typedef CachedResource CSet; typedef CachedResource Trig; typedef CachedResource Idx; - typedef CachedResource Const; + typedef CachedResource Const; }; //namespace Rsc diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 75be1d41190..8a9a49d05e3 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -826,7 +826,7 @@ namespace private: // Get relations and fields on which this routine depends, either when it's being // created or when it's modified. - static Routine* getDependencies(DeferredWork* work, bool compile, jrd_tra* transaction) + static void getDependencies(DeferredWork* work, bool compile, jrd_tra* transaction) { thread_db* tdbb = JRD_get_thread_data(); Jrd::Database* dbb = tdbb->getDatabase(); @@ -836,14 +836,14 @@ namespace bid blobId; blobId.clear(); - Routine* routine = Self::lookupBlobId(tdbb, work, blobId, compile); + auto* object = Self::lookupBlobId(tdbb, work, blobId, compile); MetadataCache::verify_cache(tdbb); // get any dependencies now by parsing the blr - if (!routine) - return nullptr; + if (!object) + return; const QualifiedName depName(work->dfw_package.isEmpty() ? work->getQualifiedName() : @@ -870,8 +870,9 @@ namespace dbb->deletePool(new_pool); } } - else + else if constexpr (std::is_base_of_v) { + Routine* routine = object; Array dependencies; const auto allParameters = {&routine->getInputFields(), &routine->getOutputFields()}; @@ -910,7 +911,6 @@ namespace } MetadataCache::verify_cache(tdbb); - return routine; } }; @@ -953,7 +953,7 @@ namespace return "constant"; } - static Routine* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile); + static Constant* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile); static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work, SSHORT validBlr); static void checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction); @@ -1139,7 +1139,7 @@ namespace } } - Routine* ConstantManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, + Constant* ConstantManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile) { Jrd::Attachment* attachment = tdbb->getAttachment(); diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 6b5f0632b56..654316f358a 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -571,6 +571,7 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const * Find all relations affected and schedule a format update. * Find all procedures and triggers and schedule a BLR validate. * Find all functions and schedule a BLR validate. + * Find all constants. * **************************************/ SET_TDBB(tdbb); From e7e439e39d571bdf35c932a5a386b6b5b8b61e2a Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 3 Mar 2026 20:33:44 +0300 Subject: [PATCH 036/119] Add missing cache population for constants when restoring --- src/jrd/SystemTriggers.epp | 61 ++++++++++++++++++-------------------- src/jrd/dfw.epp | 3 ++ 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/jrd/SystemTriggers.epp b/src/jrd/SystemTriggers.epp index ac53b45369c..6b8be162869 100644 --- a/src/jrd/SystemTriggers.epp +++ b/src/jrd/SystemTriggers.epp @@ -1602,19 +1602,26 @@ void afterInsertRelation(thread_db* tdbb, Record* record) } -void afterInsertProcedure(thread_db* tdbb, Record* record) +template +void afterInsertCachedObject(thread_db* tdbb, Record* record) { - if (tdbb->getAttachment()->isRWGbak()) - { - dsc desc; - bool idSet = EVL_field(nullptr, record, f_prc_id, &desc); - fb_assert(idSet); - if (!idSet) - return; + if (!tdbb->getAttachment()->isRWGbak()) + return; - MetaId id = MetaId(MOV_get_long(tdbb, &desc, 0)); - MetadataCache::newVersion(tdbb, id); - } + dsc desc; + bool idSet = EVL_field(nullptr, record, Field, &desc); + fb_assert(idSet); + if (!idSet) + return; + + const IdType id = IdType(MOV_get_long(tdbb, &desc, 0)); + MetadataCache::newVersion(tdbb, id); +} + + +void afterInsertProcedure(thread_db* tdbb, Record* record) +{ + afterInsertCachedObject(tdbb, record); } void afterUpdateProcedure(thread_db* tdbb, Record* orgRecord, Record* newRecord) @@ -1624,19 +1631,10 @@ void afterUpdateProcedure(thread_db* tdbb, Record* orgRecord, Record* newRecord) void afterInsertFunction(thread_db* tdbb, Record* record) { - if (tdbb->getAttachment()->isRWGbak()) - { - dsc desc; - bool idSet = EVL_field(nullptr, record, f_fun_id, &desc); - fb_assert(idSet); - if (!idSet) - return; - - MetaId id = MetaId(MOV_get_long(tdbb, &desc, 0)); - MetadataCache::newVersion(tdbb, id); - } + afterInsertCachedObject(tdbb, record); } + void afterUpdateFunction(thread_db* tdbb, Record* orgRecord, Record* newRecord) { afterInsertFunction(tdbb, newRecord); @@ -1705,17 +1703,12 @@ void beforeInsertCollation(thread_db* tdbb, Record* record, jrd_rel* relation) void afterInsertCollation(thread_db* tdbb, Record* record) { - if (tdbb->getAttachment()->isRWGbak()) - { - dsc desc; - bool idSet = EVL_field(nullptr, record, f_coll_cs_id, &desc); - fb_assert(idSet); - if (!idSet) - return; + afterInsertCachedObject(tdbb, record); +} - CSetId csId = CSetId(MOV_get_long(tdbb, &desc, 0)); - MetadataCache::newVersion(tdbb, csId); - } +void afterInsertConstant(thread_db* tdbb, Record* record) +{ + afterInsertCachedObject(tdbb, record); } } // anonymous @@ -1818,6 +1811,10 @@ void SystemTriggers::executeAfterInsertTriggers(thread_db* tdbb, jrd_rel* relati case rel_funs: afterInsertFunction(tdbb, record); break; + + case rel_constants: + afterInsertConstant(tdbb, record); + break; } } diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 8a9a49d05e3..be5fc802f21 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -490,6 +490,9 @@ static ISC_STATUS getErrorNotFound(int obj_type) case obj_udf: err_code = isc_dyn_func_not_found; break; + case obj_package_constant: + err_code = isc_dyn_const_not_found; + break; default: fb_assert(false); } From 25ab03400e86eca6768def8e11036b8591144b6c Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 3 Mar 2026 20:52:27 +0300 Subject: [PATCH 037/119] Cleanup Constant class --- src/jrd/Constant.epp | 36 ------------------------------------ src/jrd/Routine.cpp | 3 --- 2 files changed, 39 deletions(-) diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index a60a4a21344..950fb0cacbb 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -87,42 +87,6 @@ ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) getPermanent()->setName(QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, (CONST.RDB$PACKAGE_NAME.NULL ? nullptr : CONST.RDB$PACKAGE_NAME))); - MetaName owner; - TriState ssDefiner; - if (!CONST.RDB$PACKAGE_NAME.NULL) - { - static const CachedRequestId requestId; - AutoCacheRequest requestHandle(tdbb, requestId); - - FOR (REQUEST_HANDLE requestHandle) - PKG IN RDB$PACKAGES - CROSS SCH IN RDB$SCHEMAS WITH - PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND - PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME - { - - owner = PKG.RDB$OWNER_NAME; - - // if (!PKG.RDB$SECURITY_CLASS.NULL) - // { - // getPermanent()->setSecurityName(QualifiedName(PKG.RDB$SECURITY_CLASS, SCH.RDB$SECURITY_CLASS)); - // } - - // SQL SECURITY of function must be the same if it's defined in package - if (!PKG.RDB$SQL_SECURITY.NULL) - ssDefiner = (bool) PKG.RDB$SQL_SECURITY; - } - END_FOR - } - - if (!ssDefiner.isAssigned()) - { - ssDefiner = MET_get_ss_definer(tdbb, CONST.RDB$SCHEMA_NAME); - } - - // if (ssDefiner.asBool()) - // invoker = dbb->getUserId(getPermanent()->owner); - if ((flags & CacheFlag::MINISCAN) == 0) makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); else diff --git a/src/jrd/Routine.cpp b/src/jrd/Routine.cpp index 2cd3648c3db..e32096996e9 100644 --- a/src/jrd/Routine.cpp +++ b/src/jrd/Routine.cpp @@ -134,9 +134,6 @@ void Routine::setStatement(Statement* value) statement->function = static_cast(this); break; - case obj_package_constant: - break; - default: fb_assert(false); break; From 70ffad857e45b5f22d71f226be8208cd5971aa67 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 3 Mar 2026 20:52:44 +0300 Subject: [PATCH 038/119] Fix constant check for ValueListNode --- src/dsql/Nodes.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 52a1397da01..34c1ac6f9e5 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -1361,6 +1361,17 @@ class ValueListNode : public TypedNode virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc); + virtual bool constant() const override + { + for (auto& child : items) + { + if (!child->constant()) + return false; + } + + return true; + } + ValueListNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override { ValueListNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ValueListNode(dsqlScratch->getPool(), From edeb6f07e0dd6725925aeb50d13f492c7ae8c0c7 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 4 Mar 2026 11:34:07 +0300 Subject: [PATCH 039/119] Remove NONE flag --- src/jrd/SysFunction.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/jrd/SysFunction.h b/src/jrd/SysFunction.h index 65543b710f5..d8a7a21b95f 100644 --- a/src/jrd/SysFunction.h +++ b/src/jrd/SysFunction.h @@ -48,7 +48,6 @@ class SysFunction public: enum Flags : UCHAR { - NONE = 0, DETERMINISTIC = 1, CONSTANT = 2 }; From 65ed53f6aa0201314c49874dff1703e9f44dabe0 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 4 Mar 2026 11:34:12 +0300 Subject: [PATCH 040/119] Fix CI build --- src/jrd/Constant.epp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp index 950fb0cacbb..95b7e55d291 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Constant.epp @@ -41,6 +41,11 @@ #include "../common/classes/VaryStr.h" #include "../common/classes/alloc.h" // ALLOC_ARGS0 +// I do not know why but the macros is undefined in CI +#ifndef ALLOC_ARGS0 +#define ALLOC_ARGS0 +#endif + using namespace Firebird; using namespace Jrd; From 531b987f9b2db68c2cd3bfefbb3755d47da67826 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 4 Mar 2026 11:37:22 +0300 Subject: [PATCH 041/119] Add more description for constant and deterministic methods --- src/dsql/Nodes.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 34c1ac6f9e5..7d8aaea86f6 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -690,6 +690,9 @@ class ExprNode : public DmlNode } // Check if expression returns deterministic result + // Determinate whether the node is volatile (or not) in the current execution context. + // A DBKEY is deterministic (it cannot change for an already fetched row) + // but it's not constant (and thus cannot be used as an initializer expression). virtual bool deterministic(thread_db* tdbb) const; // Check if expression could return NULL or expression can turn NULL into a true/false. @@ -702,6 +705,7 @@ class ExprNode : public DmlNode virtual bool unmappable(const MapNode* mapNode, StreamType shellStream) const; // Check if expression returns constant result + // The result true means the value does not change after recompilation virtual bool constant() const { return false; From 75a29bf27f741de15a3f30bfd890dabdc4d973f2 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 5 Mar 2026 00:36:08 +0300 Subject: [PATCH 042/119] Push forgotten change --- src/jrd/SysFunction.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 04b5c5fd25c..ce175fe8a32 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -6951,7 +6951,7 @@ const SysFunction SysFunction::functions[] = {"EXP", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlExp, NULL}, {"FIRST_DAY", 2, 2, DEFAULT, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay}, {"FLOOR", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlFloor, NULL}, - {"GEN_UUID", 0, 0, NONE, NULL, makeUuid, evlGenUuid, NULL}, + {"GEN_UUID", 0, 0, 0, NULL, makeUuid, evlGenUuid, NULL}, {"GREATEST", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue}, {"HASH", 1, 2, DEFAULT, setParamsHash, makeHash, evlHash, NULL}, {"HEX_DECODE", 1, 1, DEFAULT, NULL, makeDecodeHex, evlDecodeHex, NULL}, @@ -6973,11 +6973,11 @@ const SysFunction SysFunction::functions[] = {"POSITION", 2, 4, DEFAULT, setParamsPosition, makeLongResult, evlPosition, NULL}, {"POWER", 2, 2, DEFAULT, setParamsDblDec, makeDblDecResult, evlPower, NULL}, {"QUANTIZE", 2, 2, DEFAULT, setParamsDecFloat, makeDecFloatResult, evlQuantize, NULL}, - {"RAND", 0, 0, NONE, NULL, makeDoubleResult, evlRand, NULL}, + {"RAND", 0, 0, 0, NULL, makeDoubleResult, evlRand, NULL}, {RDB_GET_CONTEXT, 2, 2, DETERMINISTIC, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL}, - {"RDB$GET_TRANSACTION_CN", 1, 1, NONE, setParamsInt64, makeGetTranCN, evlGetTranCN, NULL}, + {"RDB$GET_TRANSACTION_CN", 1, 1, 0, setParamsInt64, makeGetTranCN, evlGetTranCN, NULL}, {"RDB$ROLE_IN_USE", 1, 1, DETERMINISTIC, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL}, - {RDB_SET_CONTEXT, 3, 3, NONE, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL}, + {RDB_SET_CONTEXT, 3, 3, 0, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL}, {"RDB$SYSTEM_PRIVILEGE", 1, 1, DETERMINISTIC, NULL, makeBooleanResult, evlSystemPrivilege, NULL}, {"REPLACE", 3, 3, DEFAULT, setParamsFromList, makeReplace, evlReplace, NULL}, {"REVERSE", 1, 1, DEFAULT, NULL, makeReverse, evlReverse, NULL}, @@ -6986,8 +6986,8 @@ const SysFunction SysFunction::functions[] = {"RPAD", 2, 3, DEFAULT, setParamsSecondInteger, makePad, evlPad, (void*) funRPad}, {"RSA_DECRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, DEFAULT, setParamsRsaEncrypt, makeRsaCrypt, evlRsaDecrypt, NULL}, {"RSA_ENCRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, DEFAULT, setParamsRsaEncrypt, makeRsaCrypt, evlRsaEncrypt, NULL}, - {"RSA_PRIVATE", 1, 1, NONE, setParamsInteger, makeRsaPrivate, evlRsaPrivate, NULL}, - {"RSA_PUBLIC", 1, 1, NONE, setParamsRsaPublic, makeRsaPublic, evlRsaPublic, NULL}, + {"RSA_PRIVATE", 1, 1, 0, setParamsInteger, makeRsaPrivate, evlRsaPrivate, NULL}, + {"RSA_PUBLIC", 1, 1, 0, setParamsRsaPublic, makeRsaPublic, evlRsaPublic, NULL}, {"RSA_SIGN_HASH", RSA_SIGN_ARG_MAX, RSA_SIGN_ARG_MAX, DEFAULT, setParamsRsaSign, makeRsaSign, evlRsaSign, NULL}, {"RSA_VERIFY_HASH", RSA_VERIFY_ARG_MAX, RSA_VERIFY_ARG_MAX, DEFAULT, setParamsRsaVerify, makeBoolResult, evlRsaVerify, NULL}, {"SIGN", 1, 1, DEFAULT, setParamsDblDec, makeShortResult, evlSign, NULL}, @@ -7001,7 +7001,7 @@ const SysFunction SysFunction::functions[] = {"UNICODE_CHAR", 1, 1, DEFAULT, setParamsInteger, makeUnicodeChar, evlUnicodeChar, NULL}, {"UNICODE_VAL", 1, 1, DEFAULT, setParamsUnicodeVal, makeLongResult, evlUnicodeVal, NULL}, {"UUID_TO_CHAR", 1, 1, DEFAULT, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL}, - {"", 0, 0, NONE, NULL, NULL, NULL, NULL} + {"", 0, 0, 0, NULL, NULL, NULL, NULL} }; From 9100a7918299a62be91cba176eb11aaf9db13386 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 5 Mar 2026 00:36:32 +0300 Subject: [PATCH 043/119] Mark GEN_UUID function as constant --- src/jrd/SysFunction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index ce175fe8a32..26694c904c5 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -6951,7 +6951,7 @@ const SysFunction SysFunction::functions[] = {"EXP", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlExp, NULL}, {"FIRST_DAY", 2, 2, DEFAULT, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay}, {"FLOOR", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlFloor, NULL}, - {"GEN_UUID", 0, 0, 0, NULL, makeUuid, evlGenUuid, NULL}, + {"GEN_UUID", 0, 0, CONSTANT, NULL, makeUuid, evlGenUuid, NULL}, {"GREATEST", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue}, {"HASH", 1, 2, DEFAULT, setParamsHash, makeHash, evlHash, NULL}, {"HEX_DECODE", 1, 1, DEFAULT, NULL, makeDecodeHex, evlDecodeHex, NULL}, From 50f39a0bfa91e3eaf2a308cc7837d997e1af8a89 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 12 Mar 2026 12:31:50 +0300 Subject: [PATCH 044/119] Fix build after merge --- src/jrd/met.epp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 17ed1915583..ce240425673 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -4343,7 +4343,7 @@ void MET_store_dependency(thread_db* tdbb, dpdo_name = &name; break; case obj_package_constant: - name = *dependency.name; + name = dependency.name; dpdo_name = &name; break; } From 81978d38442c86237e4d4d1edf4bd4a669a3848b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 31 Mar 2026 17:32:13 +0300 Subject: [PATCH 045/119] Reimplement package constants with the package cache object - Add package ID field - Add package metacache Package body create/drop handles like a newVersion of the cache object. The versioned part as a dictionary to search constants by name and by ID in the package Known issues: 1) Id for system packages is missing 2) Restore of old packages lead to lock leakage assert 3) Probably incorrect Package::scan result and Package::reload with Package::checkReload handle 4) Probably not needed `if (schema.isEmpty())` checks --- builds/win32/msvc15/engine_static.vcxproj | 3 +- src/dsql/PackageNodes.epp | 236 ++++++---- src/dsql/PackageNodes.h | 10 +- src/include/firebird/impl/msg/jrd.h | 4 +- src/include/gen/Firebird.pas | 4 +- src/jrd/Constant.h | 173 -------- src/jrd/{Constant.epp => Package.epp} | 514 +++++++++++----------- src/jrd/Package.h | 233 ++++++++++ src/jrd/Resources.cpp | 2 +- src/jrd/Resources.h | 22 +- src/jrd/SystemTriggers.epp | 21 +- src/jrd/constants.h | 1 + src/jrd/dfw.epp | 207 +++++++-- src/jrd/drq.h | 1 + src/jrd/fields.h | 8 +- src/jrd/idx.h | 4 + src/jrd/jrd.h | 2 +- src/jrd/lck.cpp | 2 +- src/jrd/lck.h | 2 +- src/jrd/met.epp | 29 +- src/jrd/met.h | 10 +- src/jrd/names.h | 1 + src/jrd/relations.h | 1 + src/jrd/tra.h | 14 +- src/jrd/trig.h | 1 + src/jrd/vio.cpp | 25 +- 26 files changed, 913 insertions(+), 617 deletions(-) delete mode 100644 src/jrd/Constant.h rename src/jrd/{Constant.epp => Package.epp} (60%) create mode 100644 src/jrd/Package.h diff --git a/builds/win32/msvc15/engine_static.vcxproj b/builds/win32/msvc15/engine_static.vcxproj index 1543ed65a93..0d1222eb56f 100644 --- a/builds/win32/msvc15/engine_static.vcxproj +++ b/builds/win32/msvc15/engine_static.vcxproj @@ -41,7 +41,7 @@ - + @@ -417,6 +417,7 @@ + diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 77d88c812ee..3a6e9464774 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -39,7 +39,7 @@ #include "../jrd/scl_proto.h" #include "../common/dsc_proto.h" // DSC_make_descriptor -#include "../jrd/Constant.h" // Constant +#include "../jrd/Package.h" // Constant #include "../dsql/metd_proto.h" // METD_get_domain #include "../common/classes/VaryStr.h" // METD_get_domain @@ -76,7 +76,18 @@ public: void execute(thread_db* tdbb, DsqlCompilerScratch*, jrd_tra* transaction) { - Constant::drop(tdbb, transaction, m_name); + static const CachedRequestId requestId; + AutoCacheRequest request(tdbb, requestId); + + FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ m_name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ m_name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ m_name.object.c_str() + { + ERASE CONST; + } + END_FOR } private: @@ -403,19 +414,21 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler if (csb->collectingDependencies()) { Dependency dependency(obj_package_constant); - dependency.name = FB_NEW_POOL(pool) QualifiedName(pool, fullName); + dependency.name = fullName; csb->addDependency(dependency); } { csb->qualifyExistingName(tdbb, fullName, obj_package_constant); - auto* constant = MetadataCache::getPerm(tdbb, fullName, CacheFlag::AUTOCREATE); - if (constant) - node->m_constant = csb->csb_resources->constants.registerResource(constant); + auto package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE); + if (package) + { + node->m_package = csb->csb_resources->packages.registerResource(package->getPermanent()); + } } - if (!node->m_constant) + if (!node->m_package) status_exception::raise(Arg::Gds(isc_bad_constant_name) << Arg::Str(fullName.toQuotedString())); @@ -448,41 +461,37 @@ void PackageReferenceNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) { jrd_tra* transaction = dsqlScratch->getTransaction(); thread_db* tdbb = JRD_get_thread_data(); - *desc = Constant::getDesc(tdbb, transaction, m_fullName); + *desc = ConstantValue::getDesc(tdbb, transaction, m_fullName); } bool PackageReferenceNode::constantExists(thread_db* tdbb, Jrd::jrd_tra* transaction, - const QualifiedName& fullName, bool* isPrivate) + QualifiedName fullName, bool* isPrivate) { if (fullName.package.isEmpty() || fullName.object.isEmpty()) return false; // Use default schema if one not specified - const char* schemaName = fullName.schema.hasData() ? fullName.schema.c_str() : PUBLIC_SCHEMA; + if (fullName.schema.isEmpty()) + fullName.schema = PUBLIC_SCHEMA; - static const CachedRequestId requestId; - AutoCacheRequest getConstantRequest(tdbb, requestId); - FOR (REQUEST_HANDLE getConstantRequest TRANSACTION_HANDLE transaction) - CONST IN RDB$CONSTANTS - WITH CONST.RDB$SCHEMA_NAME EQ schemaName AND - CONST.RDB$PACKAGE_NAME EQ fullName.package.c_str() AND - CONST.RDB$CONSTANT_NAME EQ fullName.object.c_str() - { - if (isPrivate) - { - *isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; - } + auto* package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE); + if (package == nullptr) + return false; - return true; - } - END_FOR + ConstantValue* value = package->findConstant(tdbb, fullName); - return false; + if (value == nullptr) + return false; + + if (isPrivate) + *isPrivate = value->isPrivate; + + return true; } void PackageReferenceNode::getDesc(thread_db* tdbb, CompilerScratch*, dsc* desc) { - *desc = Constant::getDesc(tdbb, tdbb->getTransaction(), m_fullName); + *desc = ConstantValue::getDesc(tdbb, tdbb->getTransaction(), m_fullName); } ValueExprNode* PackageReferenceNode::copy(thread_db* tdbb, NodeCopier& copier) const @@ -490,8 +499,7 @@ ValueExprNode* PackageReferenceNode::copy(thread_db* tdbb, NodeCopier& copier) c MemoryPool& pool = *tdbb->getDefaultPool(); auto* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, m_fullName, m_itemType); - auto* constant = MetadataCache::getPerm(tdbb, m_fullName, 0); - node->m_constant = copier.csb->csb_resources->constants.registerResource(constant); + node->m_package = copier.csb->csb_resources->packages.registerResource(m_package()); return node; } @@ -508,16 +516,16 @@ ValueExprNode* PackageReferenceNode::pass2(thread_db* tdbb, CompilerScratch* csb dsc* PackageReferenceNode::execute(thread_db* tdbb, Request* request) const { - impure_value* impure = request->getImpure(m_impureOffset); + impure_value* outputImpure = request->getImpure(m_impureOffset); switch (m_itemType) { case blr_pkg_ref_item_const: { - const Constant* constant = m_constant(request->getResources()); - constant->checkReload(tdbb); + Package* package = m_package(request->getResources()); + package->checkReload(tdbb); - EVL_make_value(tdbb, &constant->getValue(), impure); - return &impure->vlu_desc; + EVL_make_value(tdbb, &package->findConstant(tdbb, m_fullName)->makeValue(tdbb), outputImpure); + return &outputImpure->vlu_desc; } default: fb_assert(false); @@ -555,8 +563,6 @@ DdlNode* CreatePackageConstantNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { status_exception::raise(Arg::Gds(isc_dyn_non_constant_constant) << name.toQuotedString()); } - // DsqlDescMaker::fromField(&node->castDesc, type); - // node->castDesc.dsc_flags = node->source->getDsqlDesc().dsc_flags & DSC_nullable; DdlNode::dsqlPass(dsqlScratch); return nullptr; @@ -582,7 +588,6 @@ void CreatePackageConstantNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds executeAlter(tdbb, dsqlScratch, transaction); fb_assert(m_id); - MetadataCache::newVersion(tdbb, m_id); } void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) @@ -593,7 +598,8 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat AutoCacheRequest storeConstantRequest(tdbb, requestId); // Check uniqueness - if (PackageReferenceNode::constantExists(tdbb, transaction, name)) + fb_assert(package != nullptr); + if (package->findConstant(tdbb, name)) { status_exception::raise(Arg::Gds(isc_dyn_dup_const) << name.toQuotedString()); } @@ -612,7 +618,7 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat { try { - SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_const_id, "RDB$CONSTANTS"); + SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_const_id, CONSTANTS_GENERATOR); id %= (MAX_SSHORT + 1); if (!id) continue; @@ -635,7 +641,7 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); // Gen value blr - Constant::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); + ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); // Put the blr blb* blob = blb::create(tdbb, transaction, &CONST.RDB$CONSTANT_BLR); @@ -643,8 +649,14 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat blob->BLB_close(tdbb); // Parent package - CONST.RDB$PACKAGE_NAME.NULL = FALSE; - strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str()); + fb_assert(name.package.hasData()); + { + CONST.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str()); + + fb_assert(package); + package->addConstant(tdbb, m_id, name, m_isPrivate, m_type); // Do not cache blob id because the blob is not materialized + } // Schema of the parent package CONST.RDB$SCHEMA_NAME.NULL = FALSE; @@ -690,7 +702,10 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc // Store constant dsc in RDB$FIELDS FOR (REQUEST_HANDLE eraseDscRequest TRANSACTION_HANDLE transaction) FLD IN RDB$FIELDS CROSS CONST IN RDB$CONSTANTS - WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CROSS PACKAGE IN RDB$PACKAGES + WITH PACKAGE.RDB$PACKAGE_NAME EQ name.package.c_str() AND + PACKAGE.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND @@ -698,8 +713,6 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc { m_id = CONST.RDB$CONSTANT_ID; - MetadataCache::oldVersion(tdbb, m_id, CacheFlag::OLD_ALTER); - dyn_fld origDom, newDom; DSC_make_descriptor(&origDom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, @@ -798,13 +811,19 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc newDom.dyn_sub_type = m_type->subType; // Gen the new constant value as blr - Constant::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); + ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); // And write blb* blob = blb::create(tdbb, transaction, &blobId); blob->BLB_put_segment(tdbb, dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount()); blob->BLB_close(tdbb); + { + fb_assert(package); + // Do not cache blob id because the blob is not materialized + package->addConstant(tdbb, m_id, name, m_isPrivate, m_type); + } + MODIFY CONST USING CONST.RDB$CONSTANT_BLR.NULL = FALSE; CONST.RDB$CONSTANT_BLR = blobId; @@ -976,7 +995,7 @@ void CreateAlterPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlS dsc schemaDesc, nameDesc; schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str())); nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str())); - DFW_post_work(transaction, dfw_modify_package_header, &nameDesc, &schemaDesc, 0); + DFW_post_work(transaction, dfw_modify_package_header, &nameDesc, &schemaDesc, id); } else executeCreate(tdbb, dsqlScratch, transaction); @@ -1001,39 +1020,66 @@ void CreateAlterPackageNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_s_pkg, DYN_REQUESTS); - STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) - PKG IN RDB$PACKAGES USING + Cached::Package* package = nullptr; + ULONG faults = 0; + while (true) { - PKG.RDB$SCHEMA_NAME.NULL = FALSE; - strcpy(PKG.RDB$SCHEMA_NAME, name.schema.c_str()); + try + { + id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_package_id, PACKAGES_GENERATOR); + id %= (MAX_SSHORT + 1); + if (!id) + continue; - PKG.RDB$PACKAGE_NAME.NULL = FALSE; - strcpy(PKG.RDB$PACKAGE_NAME, name.object.c_str()); + STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PKG IN RDB$PACKAGES USING + { + PKG.RDB$SCHEMA_NAME.NULL = FALSE; + strcpy(PKG.RDB$SCHEMA_NAME, name.schema.c_str()); - PKG.RDB$SYSTEM_FLAG.NULL = FALSE; - PKG.RDB$SYSTEM_FLAG = 0; + PKG.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(PKG.RDB$PACKAGE_NAME, name.object.c_str()); - PKG.RDB$OWNER_NAME.NULL = FALSE; - strcpy(PKG.RDB$OWNER_NAME, ownerName.c_str()); + PKG.RDB$SYSTEM_FLAG.NULL = FALSE; + PKG.RDB$SYSTEM_FLAG = 0; - PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE; - attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, source); + PKG.RDB$OWNER_NAME.NULL = FALSE; + strcpy(PKG.RDB$OWNER_NAME, ownerName.c_str()); - if (ssDefiner.has_value()) + PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE; + attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, source); + + if (ssDefiner.has_value()) + { + PKG.RDB$SQL_SECURITY.NULL = FALSE; + PKG.RDB$SQL_SECURITY = ssDefiner.value() == SqlSecurity::SS_DEFINER ? FB_TRUE : FB_FALSE; + } + else + PKG.RDB$SQL_SECURITY.NULL = TRUE; + + PKG.RDB$PACKAGE_ID = id; + } + END_STORE + break; + } + catch (const status_exception& ex) { - PKG.RDB$SQL_SECURITY.NULL = FALSE; - PKG.RDB$SQL_SECURITY = ssDefiner.value() == SqlSecurity::SS_DEFINER ? FB_TRUE : FB_FALSE; + if (ex.value()[1] != isc_unique_key_violation) + throw; + + if (++faults > MAX_SSHORT) + throw; + + fb_utils::init_status(tdbb->tdbb_status_vector); } - else - PKG.RDB$SQL_SECURITY.NULL = TRUE; } - END_STORE storePrivileges(tdbb, transaction, name, obj_package_header, EXEC_PRIVILEGES); owner = ownerName; - executeItems(tdbb, dsqlScratch, transaction); + package = MetadataCache::newVersion(tdbb, id); + executeItems(tdbb, dsqlScratch, transaction, package->getVersioned(tdbb, CacheFlag::AUTOCREATE)); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE, name, {}); } @@ -1054,18 +1100,18 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND PKG.RDB$PACKAGE_NAME EQ name.object.c_str() { + id = PKG.RDB$PACKAGE_ID; modified = true; + MetadataCache::oldVersion(tdbb, id, CacheFlag::OLD_ALTER); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PACKAGE, name, {}); PackageItemsHolder existingItems(pool); - existingItems.collectPackagedItems(tdbb, transaction, name, false, false); + existingItems.collectPackagedItems(tdbb, transaction, name, false, true); dropMissingItems(tdbb, dsqlScratch, name, existingItems.functions, functionNames); dropMissingItems(tdbb, dsqlScratch, name, existingItems.procedures, procedureNames); - - // To admit type/value change, all the constants should be recreated - Constant::dropAllFromPackage(tdbb, transaction, name, false); + dropMissingItems(tdbb, dsqlScratch, name, existingItems.constants, constantNames); MODIFY PKG PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE; @@ -1089,13 +1135,14 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsc schemaDesc, nameDesc; schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str())); nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str())); - DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, 0); + DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, id); } END_FOR if (modified) { - executeItems(tdbb, dsqlScratch, transaction); + auto package = MetadataCache::newVersion(tdbb, id); + executeItems(tdbb, dsqlScratch, transaction, package->getVersioned(tdbb, CacheFlag::AUTOCREATE)); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_PACKAGE, name, {}); } @@ -1141,7 +1188,7 @@ bool CreateAlterPackageNode::executeAlterIndividualParameters(thread_db* tdbb, D } void CreateAlterPackageNode::executeItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction) + jrd_tra* transaction, Package* package) { for (unsigned i = 0; i < items->getCount(); ++i) { @@ -1157,6 +1204,7 @@ void CreateAlterPackageNode::executeItems(thread_db* tdbb, DsqlCompilerScratch* (*items)[i].procedure->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true); break; case PackageItemType::CONSTANT: + (*items)[i].constant->package = package; (*items)[i].constant->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true); break; } @@ -1196,12 +1244,15 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_e_pkg, DYN_REQUESTS); + MetaId id{}; FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) PKG IN RDB$PACKAGES WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND PKG.RDB$PACKAGE_NAME EQ name.object.c_str() { found = true; + id = PKG.RDB$PACKAGE_ID; + MetadataCache::getVersioned(tdbb, id, CacheFlag::OLD_DROP); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PACKAGE, name, {}); @@ -1213,7 +1264,7 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, dsc schemaDesc, nameDesc; schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str())); nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str())); - DFW_post_work(transaction, dfw_drop_package_header, &nameDesc, &schemaDesc, 0); + DFW_post_work(transaction, dfw_drop_package_header, &nameDesc, &schemaDesc, id); } END_FOR @@ -1245,7 +1296,10 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, END_FOR if (found) + { + MetadataCache::erase(tdbb, id); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE, name, {}); + } savePoint.release(); // everything is ok } @@ -1333,6 +1387,10 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) constant->name.schema = name.schema; constant->name.package = name.object; constant->create = true; + + if (arrays[i] == items) + constant->alter = true; + constant->makePrivate(); break; } @@ -1382,6 +1440,7 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body, DYN_REQUESTS); bool modified = false; + Package* package = nullptr; FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) PKG IN RDB$PACKAGES WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND @@ -1397,6 +1456,7 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc Arg::Gds(isc_dyn_package_body_exists) << name.toQuotedString()); } + package = MetadataCache::newVersion(tdbb, PKG.RDB$PACKAGE_ID)->getVersioned(tdbb, CacheFlag::AUTOCREATE); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PACKAGE_BODY, name, {}); MODIFY PKG @@ -1488,6 +1548,8 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc headerItems.constants.checkDuplicate(constant->name); existingItems.constants.checkDuplicate(constant->name); + fb_assert(package); + constant->package = package; constant->executeDdl(tdbb, elem.dsqlScratch, transaction, true); break; } @@ -1499,7 +1561,12 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc newItems.collectPackagedItems(tdbb, transaction, name, true, false); existingItems.checkDefineMatch(pool, name, newItems); - + { + dsc schemaDesc, nameDesc; + schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str())); + nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str())); + DFW_post_work(transaction, dfw_create_package, &nameDesc, &schemaDesc, package->getId()); + } executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE_BODY, name, {}); savePoint.release(); // everything is ok @@ -1539,12 +1606,14 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body2, DYN_REQUESTS); + MetaId id{}; FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) PKG IN RDB$PACKAGES WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND PKG.RDB$PACKAGE_NAME EQ name.object.c_str() { found = true; + id = PKG.RDB$PACKAGE_ID; executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PACKAGE_BODY, name, {}); @@ -1555,7 +1624,7 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra dsc schemaDesc, nameDesc; schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str())); nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str())); - DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, 0); + DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, id); END_MODIFY } END_FOR @@ -1622,7 +1691,20 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra } END_FOR - Constant::dropAllFromPackage(tdbb, transaction, name, true); + // Erase body constants + static const CachedRequestId eraseRequestId; + AutoCacheRequest eraseConstantRequest(tdbb, eraseRequestId); + FOR (REQUEST_HANDLE eraseConstantRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ name.object.c_str() AND + CONST.RDB$PRIVATE_FLAG EQ true + { + ERASE CONST; + } + END_FOR + + MetadataCache::newVersion(tdbb, id); executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE_BODY, name, {}); diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 5bff32bb07b..947f7cb47f5 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -133,7 +133,7 @@ class PackageReferenceNode final : public TypedNode m_constant; + ConstantValue* m_prefetchedConstant = nullptr; + CachedResource m_package; const QualifiedName m_fullName; const UCHAR m_itemType; @@ -203,6 +204,8 @@ class CreatePackageConstantNode final : public DdlNode bool create = false; bool alter = false; + Package* package = nullptr; + private: NestConst m_type; NestConst m_expr; @@ -293,7 +296,7 @@ class CreateAlterPackageNode : public DdlNode void executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); bool executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); - void executeItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); + void executeItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, Package* package); public: QualifiedName name; @@ -306,6 +309,7 @@ class CreateAlterPackageNode : public DdlNode ItemsNameArray procedureNames; ItemsNameArray constantNames; std::optional ssDefiner; + MetaId id; private: MetaName owner; diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index 50a6bd6c928..4a2ae94ba81 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1004,7 +1004,7 @@ FB_IMPL_MSG(JRD, 1001, not_defined_constant, -901, "42", "000", "The constant @1 FB_IMPL_MSG(JRD, 1002, const_name, -901, "42", "000", "CONSTANT @1") FB_IMPL_MSG(JRD, 1003, private_constant, -901, "42", "000", "The constant @1 is private") FB_IMPL_MSG(JRD, 1004, package_alias_help, -901, "42", "000", "Use an alias to resolve the conflict") -FB_IMPL_MSG(JRD, 1005, bad_constant_blr_error, -901, "2F", "000", "Error while parsing BLR value of the constant @1") -FB_IMPL_MSG(JRD, 1006, bad_constant_type_error, -901, "2F", "000", "Error while reading type of the constant @1") +FB_IMPL_MSG(JRD, 1005, bad_constant_blr, -901, "2F", "000", "Error while parsing BLR value of the constant @1") +FB_IMPL_MSG(JRD, 1006, bad_constant_desc, -901, "2F", "000", "Error while reading type of the constant with name @1") FB_IMPL_MSG(JRD, 1007, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found") FB_IMPL_MSG(JRD, 1008, bad_constant_type, -901, "2F", "000", "@1 is not suppotred to be a constant type") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index fdcc5c96f2c..f91e793b5e7 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5959,8 +5959,8 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_const_name = 335545322; isc_private_constant = 335545323; isc_package_alias_help = 335545324; - isc_bad_constant_blr_error = 335545325; - isc_bad_constant_type_error = 335545326; + isc_bad_constant_blr = 335545325; + isc_bad_constant_desc = 335545326; isc_bad_constant_name = 335545327; isc_bad_constant_type = 335545328; isc_gfix_db_name = 335740929; diff --git a/src/jrd/Constant.h b/src/jrd/Constant.h deleted file mode 100644 index 7e03b8db56e..00000000000 --- a/src/jrd/Constant.h +++ /dev/null @@ -1,173 +0,0 @@ -/* - * PROGRAM: Firebird CONSTANTS implementation. - * MODULE: Constant.h - * DESCRIPTION: Routine to cache and reload constants - * - * The contents of this file are subject to the Initial - * Developer's Public License Version 1.0 (the "License"); - * you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. - * - * Software distributed under the License is distributed AS IS, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. - * See the License for the specific language governing rights - * and limitations under the License. - * - * The Original Code was created by Artyom Abakumov - * for Red Soft Corporation. - * - * Copyright (c) 2025 Red Soft Corporation - * and all contributors signed below. - * - * All Rights Reserved. - * Contributor(s): ______________________________________. - */ - - -#ifndef JRD_CONSTANT_H -#define JRD_CONSTANT_H - -#include "firebird.h" -#include "../jrd/CacheVector.h" -#include "../jrd/Resources.h" -#include "../jrd/obj.h" -#include "../jrd/val.h" -#include "../jrd/lck.h" - -namespace Jrd -{ -class DsqlCompilerScratch; -class dsql_fld; - -class ConstantPermanent : public Firebird::PermanentStorage -{ -public: - explicit ConstantPermanent(thread_db* tdbb, MemoryPool& p, MetaId metaId, NoData) - : PermanentStorage(p), - id(metaId), - name(p) - { } - - explicit ConstantPermanent(MemoryPool& p) - : PermanentStorage(p), - id(~0), - name(p) - { } - - MetaId getId() const - { - return id; - } - - static bool destroy(thread_db* tdbb, ConstantPermanent* routine) - { - return false; - } - - void releaseLock(thread_db*) { } - - const QualifiedName& getName() const noexcept { return name; } - void setName(const QualifiedName& value) { name = value; } - - bool hasData() const { return name.hasData(); } - -public: - MetaId id; // routine ID - QualifiedName name; // routine name -}; - -class Constant final : public Firebird::PermanentStorage, public ObjectBase -{ -public: - static Constant* lookup(thread_db* tdbb, MetaId id); - static Constant* lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags); - - // lock requeued by CacheElement - static const enum lck_t LOCKTYPE = LCK_constant_rescan; - -private: - explicit Constant(Cached::Constant* perm) - : Firebird::PermanentStorage(perm->getPool()), - cachedConstant(perm) - { } - -public: - explicit Constant(MemoryPool& p) - : Firebird::PermanentStorage(p) - { } - - static bool destroy(thread_db* tdbb, Constant* routine) - { - return false; - } - - static Constant* create(thread_db* tdbb, MemoryPool& pool, Cached::Constant* perm); - ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags); - static std::optional getIdByName(thread_db* tdbb, const QualifiedName& name); - void checkReload(thread_db* tdbb) const; - - static const char* objectFamily(void*) - { - return "constant"; - } - -public: - const QualifiedName& getName() const noexcept { return getPermanent()->getName(); } - MetaId getId() const noexcept { return getPermanent()->getId(); } - - int getObjectType() const noexcept - { - return objectType(); - } - - SLONG getSclType() const noexcept - { - return obj_package_constant; - } - - ScanResult reload(thread_db* tdbb, ObjectBase::Flag fl); - - static int objectType(); - - bool hash(thread_db* tdbb, Firebird::sha512& digest); - -public: - inline const dsc& getValue() const - { - return m_value.vlu_desc; - } - - static void drop(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name); - static void dropAllFromPackage(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& parent, bool privateFlag); - static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name); - - static void genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema); - -private: - void makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id); - - virtual ~Constant() - { - delete m_value.vlu_string; - } - -public: - Cached::Constant* cachedConstant; // entry in the cache - - Cached::Constant* getPermanent() const noexcept - { - return cachedConstant; - } - -private: - impure_value m_value{}; - - // Make sure the constant value will be read in at less at reload state - bool m_callReload = true; -}; - -} // namespace Jrd - -#endif // JRD_CONSTANT_H diff --git a/src/jrd/Constant.epp b/src/jrd/Package.epp similarity index 60% rename from src/jrd/Constant.epp rename to src/jrd/Package.epp index 95b7e55d291..ef4e432e25e 100644 --- a/src/jrd/Constant.epp +++ b/src/jrd/Package.epp @@ -1,7 +1,7 @@ /* * PROGRAM: Firebird CONSTANTS implementation. - * MODULE: Constant.epp - * DESCRIPTION: Routine to cache and reload constants + * MODULE: Package.epp + * DESCRIPTION: Routine to cache and reload package constants * * The contents of this file are subject to the Initial * Developer's Public License Version 1.0 (the "License"); @@ -25,7 +25,7 @@ */ #include "firebird.h" -#include "../jrd/Constant.h" +#include "../jrd/Package.h" #include "../jrd/tra.h" #include "../jrd/exe_proto.h" @@ -40,6 +40,7 @@ #include "../jrd/mov_proto.h" // MOV_get_string_ptr #include "../common/classes/VaryStr.h" #include "../common/classes/alloc.h" // ALLOC_ARGS0 +#include "../dsql/make_proto.h" // DsqlDescMaker // I do not know why but the macros is undefined in CI #ifndef ALLOC_ARGS0 @@ -53,247 +54,6 @@ DATABASE DB = FILENAME "ODS.RDB"; //---------------------- -Constant* Constant::lookup(thread_db* tdbb, MetaId id) -{ - return MetadataCache::getVersioned(tdbb, id, CacheFlag::AUTOCREATE); -} - -Constant* Constant::lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags) -{ - return MetadataCache::getVersioned(tdbb, name, flags); -} - -Constant* Constant::create(thread_db* tdbb, MemoryPool& pool, Cached::Constant* perm) -{ - return FB_NEW_POOL(perm->getPool()) Constant(perm); -} - - -ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) -{ - Attachment* attachment = tdbb->getAttachment(); - jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); - Database* dbb = tdbb->getDatabase(); - - MemoryPool& pool = getPermanent()->getPool(); - bool found = false; - - //try - { - static const CachedRequestId requestId; - AutoCacheRequest requestConst(tdbb, requestId); - - FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) - CONST IN RDB$CONSTANTS - WITH CONST.RDB$CONSTANT_ID EQ this->getId() - { - found = true; - - getPermanent()->setName(QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, - (CONST.RDB$PACKAGE_NAME.NULL ? nullptr : CONST.RDB$PACKAGE_NAME))); - - if ((flags & CacheFlag::MINISCAN) == 0) - makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); - else - this->m_callReload = true; // Call makeValue in reload - } - END_FOR - } - - return found ? (this->m_callReload ? ScanResult::REPEAT : ScanResult::COMPLETE) : ScanResult::MISS; -} - - -std::optional Constant::getIdByName(thread_db* tdbb, const QualifiedName& name) -{ - SET_TDBB(tdbb); - Attachment* attachment = tdbb->getAttachment(); - std::optional id; - - static const CachedRequestId requestId; - AutoCacheRequest request(tdbb, requestId); - - FOR (REQUEST_HANDLE request) - CONST IN RDB$CONSTANTS - WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND - CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND - CONST.RDB$CONSTANT_NAME EQ name.object.c_str() - { - id = CONST.RDB$CONSTANT_ID; - } - END_FOR - - return id; -} - -ScanResult Constant::reload(thread_db* tdbb, ObjectBase::Flag) -{ - Attachment* attachment = tdbb->getAttachment(); - Database* dbb = tdbb->getDatabase(); - jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); - - static const CachedRequestId requestId; - AutoCacheRequest request(tdbb, requestId); - - bool found = false; - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE metaTransaction) - CONST IN RDB$CONSTANTS - WITH CONST.RDB$CONSTANT_ID EQ this->getId() - { - - // if (compiling) - // { - // fb_assert(false); - // return ScanResult::REPEAT; - // } - - found = true; - makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); - } - END_FOR - - return found ? ScanResult::COMPLETE : ScanResult::MISS; -} - -int Constant::objectType() -{ - return obj_package_constant; -} - -bool Constant::hash(thread_db* tdbb, Firebird::sha512& digest) -{ - if (m_value.vlu_desc.dsc_dtype == 0) - { - dsc type = getDesc(tdbb, tdbb->getTransaction(), getName()); - digest.process(sizeof(type), &type); - } - else - { - digest.process(sizeof(m_value.vlu_desc), &m_value.vlu_desc); - } - - return true; -} - -void Constant::checkReload(thread_db* tdbb) const -{ - if (m_callReload) - getPermanent()->reload(tdbb, 0); -} - - -class DropConstantSavepoint -{ -public: - DropConstantSavepoint(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) : - m_savePoint(tdbb, transaction), - m_tdbb(tdbb) - { - MetadataCache::getVersioned(tdbb, name, CacheFlag::OLD_DROP); - } - - DropConstantSavepoint(thread_db* tdbb, jrd_tra* transaction) : - m_savePoint(tdbb, transaction), - m_tdbb(tdbb) - { - } - - inline void setFound(const MetaId id) noexcept - { - m_id = id; - m_found = true; - } - - void release() - { - fb_assert(m_found); - if (m_found) - MetadataCache::erase(m_tdbb, m_id); - - m_savePoint.release(); - } - -private: - AutoSavePoint m_savePoint; - thread_db* m_tdbb; - MetaId m_id{}; - bool m_found = false; -}; - -void Constant::drop(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) -{ - // Run all statements under savepoint control - DropConstantSavepoint savePoint(tdbb, transaction, name); - - static const CachedRequestId requestId; - AutoCacheRequest request(tdbb, requestId); - - FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - CONST IN RDB$CONSTANTS - WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND - CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND - CONST.RDB$CONSTANT_NAME EQ name.object.c_str() - { - savePoint.setFound(CONST.RDB$CONSTANT_ID); - - ERASE CONST; - } - END_FOR - - savePoint.release(); // everything is ok -} - -void Constant::dropAllFromPackage(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& parent, bool privateFlag) -{ - static const CachedRequestId requestId; - AutoCacheRequest eraseConstantRequest(tdbb, requestId); - FOR (REQUEST_HANDLE eraseConstantRequest TRANSACTION_HANDLE transaction) - CONST IN RDB$CONSTANTS - WITH CONST.RDB$SCHEMA_NAME EQ parent.schema.c_str() AND - CONST.RDB$PACKAGE_NAME EQ parent.object.c_str() AND - CONST.RDB$PRIVATE_FLAG EQ privateFlag - { - DropConstantSavepoint savePoint(tdbb, transaction, QualifiedName(CONST.RDB$CONSTANT_NAME, parent.schema, parent.object)); - savePoint.setFound(CONST.RDB$CONSTANT_ID); - - ERASE CONST; - - savePoint.release(); // everything is ok - } - END_FOR -} - -dsc Constant::getDesc(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) -{ - dsc desc{}; - bool succeed = false; - - FbLocalStatus status; - static const CachedRequestId requestId; - AutoCacheRequest getConstantDscRequest(tdbb, requestId); - FOR(REQUEST_HANDLE getConstantDscRequest TRANSACTION_HANDLE transaction) - CONST IN RDB$CONSTANTS CROSS FLD IN RDB$FIELDS - WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND - CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND - CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND - FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND - FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE - - succeed = DSC_make_descriptor(&desc, - FLD.RDB$FIELD_TYPE, - FLD.RDB$FIELD_SCALE, - FLD.RDB$FIELD_LENGTH, - FLD.RDB$FIELD_SUB_TYPE, - CSetId(FLD.RDB$CHARACTER_SET_ID), - CollId(FLD.RDB$COLLATION_ID)); - END_FOR - - if (!succeed) - (Arg::Gds(isc_bad_constant_type_error) << Arg::Str(name.toQuotedString())).raise(); - - return desc; -} static dsc* executeConstantExpressionWithRequest(thread_db* tdbb, CompilerScratch* csb) { @@ -419,7 +179,53 @@ static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlS dsqlScratch->appendUChar(blr_eoc); } -void Constant::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, +bool ConstantValue::hash(thread_db* tdbb, Firebird::sha512& digest) const +{ + fb_assert(value.vlu_desc.dsc_dtype != 0); + digest.process(sizeof(value.vlu_desc), &value.vlu_desc); + + return true; +} + +dsc ConstantValue::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name) +{ + dsc desc{}; + bool found = false; + + FbLocalStatus status; + static const CachedRequestId requestId; + AutoCacheRequest getConstantDscRequest(tdbb, requestId); + FOR(REQUEST_HANDLE getConstantDscRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS CROSS FLD IN RDB$FIELDS + WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND + FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND + FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE + { + found = true; + + const bool succeed = DSC_make_descriptor(&desc, + FLD.RDB$FIELD_TYPE, + FLD.RDB$FIELD_SCALE, + FLD.RDB$FIELD_LENGTH, + FLD.RDB$FIELD_SUB_TYPE, + CSetId(FLD.RDB$CHARACTER_SET_ID), + CollId(FLD.RDB$COLLATION_ID)); + + if (!succeed) + (Arg::Gds(isc_bad_constant_desc) << Arg::Str(name.toQuotedString())).raise(); + } + + END_FOR + + if (!found) + (Arg::Gds(isc_bad_constant_name) << Arg::Str(name.toQuotedString())).raise(); + + return desc; +} + +void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema) { { // Prepare BLR writer @@ -460,9 +266,31 @@ void Constant::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, genConstantCompatibleBlr(tdbb, dsqlScratch, *output); } -void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) +inline bid getConstantBid(thread_db* tdbb, const MetaId id) +{ + Attachment* attachment = tdbb->getAttachment(); + jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); + + static const CachedRequestId requestId; + AutoCacheRequest requestConst(tdbb, requestId); + + FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$CONSTANT_ID EQ id + { + return CONST.RDB$CONSTANT_BLR; + } + END_FOR + + return {}; +} + +dsc& ConstantValue::makeValue(thread_db* tdbb) { - m_value = {}; + if (value.vlu_desc.dsc_address != nullptr) + return value.vlu_desc; + + Attachment* attachment = tdbb->getAttachment(); MemoryPool* csb_pool = nullptr; try @@ -476,22 +304,23 @@ void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) Cleanup cc([csb]() {delete csb;}); { // blr - m_callReload = false; - MET_parse_blob(tdbb, &getName().schema, nullptr, &blob_id, &csb, nullptr, false, false); + // Use cached ID or get a real one (is it probably materialized) + bid constantBid = blrBlobId.isEmpty() ? getConstantBid(tdbb, id) : blrBlobId; + + MET_parse_blob(tdbb, &name.schema, nullptr, &constantBid, &csb, nullptr, false, false); fb_assert(csb != nullptr); - m_callReload = (csb->csb_g_flags & csb_reload); } - executeConstantExpression(tdbb, csb, getPermanent()->getPool(), m_value); + executeConstantExpression(tdbb, csb, getPool(), value); } catch (const Exception& ex) { StaticStatusVector temp_status; ex.stuffException(temp_status); - const string name = this->getName().toQuotedString(); - (Arg::Gds(isc_bad_constant_blr_error) << Arg::Str(name) + const string quotedName = name.toQuotedString(); + (Arg::Gds(isc_bad_constant_blr) << Arg::Str(quotedName) << Arg::StatusVector(temp_status.begin())).raise(); } } @@ -500,4 +329,183 @@ void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) attachment->att_database->deletePool(csb_pool); throw; } + + return value.vlu_desc; +} + +ConstantValue& ConstantsCache::add(const MetaId constId, const QualifiedName& constName, const bool isPrivate) +{ + const ULONG id = values.getCount(); + + auto& value = values.add(); + value.name = constName; + value.id = constId; + value.isPrivate = isPrivate; + + idMap.put(constId, id); + nameMap.put(constName, id); + + return value; +} + + +std::optional Package::getIdByName(thread_db* tdbb, const QualifiedName& name) +{ + fb_assert(name.package.isEmpty()); + + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); + std::optional id; + + static const CachedRequestId requestId; + AutoCacheRequest request(tdbb, requestId); + + FOR (REQUEST_HANDLE request) + PKG IN RDB$PACKAGES + WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + PKG.RDB$PACKAGE_NAME EQ name.object.c_str() + { + id = PKG.RDB$PACKAGE_ID; + } + END_FOR + + return id; +} + +ScanResult Package::reload(thread_db* tdbb, ObjectBase::Flag fl) +{ + Attachment* attachment = tdbb->getAttachment(); + jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); + Database* dbb = tdbb->getDatabase(); + + MemoryPool& pool = getPermanent()->getPool(); + + static const CachedRequestId requestId; + AutoCacheRequest requestConst(tdbb, requestId); + + constants.clear(); + FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) + PKG IN RDB$PACKAGES CROSS CONST IN RDB$CONSTANTS + WITH PKG.RDB$PACKAGE_ID EQ getId() AND + PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME + { + addConstant(tdbb, CONST.RDB$CONSTANT_ID, + QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME), + CONST.RDB$PRIVATE_FLAG, + CONST.RDB$CONSTANT_BLR); + } + END_FOR + + return ScanResult::COMPLETE; +} + +Package* Package::create(thread_db* tdbb, MemoryPool& pool, Cached::Package* perm) +{ + return FB_NEW_POOL(perm->getPool()) Package(perm); +} + +ScanResult Package::scan(thread_db* tdbb, ObjectBase::Flag flags) +{ + const bool skipMakeValue = (flags & CacheFlag::MINISCAN) != 0; + + Attachment* attachment = tdbb->getAttachment(); + jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); + Database* dbb = tdbb->getDatabase(); + + MemoryPool& pool = getPermanent()->getPool(); + + static const CachedRequestId requestId; + AutoCacheRequest requestConst(tdbb, requestId); + + FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) + PKG IN RDB$PACKAGES CROSS CONST IN RDB$CONSTANTS + WITH PKG.RDB$PACKAGE_ID EQ getPermanent()->id AND + PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME + { + addConstant(tdbb, CONST.RDB$CONSTANT_ID, + QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME), + CONST.RDB$PRIVATE_FLAG, + CONST.RDB$CONSTANT_BLR, + skipMakeValue); + + if (skipMakeValue) + this->m_callReload = true; // Call makeValue in reload + } + END_FOR + + return this->m_callReload ? ScanResult::REPEAT : ScanResult::COMPLETE; +} + +void Package::checkReload(thread_db* tdbb) +{ + if (m_callReload) + { + reload(tdbb, 0); + m_callReload = false; + } +} + +int Package::objectType() +{ + return obj_package_header; +} + +bool Package::hash(thread_db* tdbb, Firebird::sha512& digest) +{ + for (auto& constant : constants.values) + constant.hash(tdbb, digest); + + return true; +} + +ConstantValue& Package::addConstant(thread_db* tdbb, const MetaId constId, + const QualifiedName& constName, + const bool isPrivate, + const TypeClause* type) +{ + dsc typeDesc; + DsqlDescMaker::fromField(&typeDesc, type); + + auto& value = constants.add(constId, constName, isPrivate); + value.blrBlobId = {}; + value.value.vlu_desc = typeDesc; + + return value; +} + +ConstantValue& Package::addConstant(thread_db* tdbb, const MetaId constId, + const QualifiedName& constName, + const bool isPrivate, + const bid blrBlobId, + const bool skipMakeValue) +{ + auto& value = constants.add(constId, constName, isPrivate); + value.blrBlobId = blrBlobId; + if (!skipMakeValue) + value.makeValue(tdbb); + + return value; +} + +ConstantValue* Package::findConstant(thread_db* tdbb, const MetaId id) +{ + auto it = constants.idMap.get(id); + + if (it != nullptr) + return &constants.values[*it]; + + return nullptr; +} + +ConstantValue* Package::findConstant(thread_db* tdbb, QualifiedName name) +{ + if (name.schema.isEmpty()) + name.schema = PUBLIC_SCHEMA; + + auto it = constants.nameMap.get(name); + + if (it != nullptr) + return &constants.values[*it]; + + return nullptr; } diff --git a/src/jrd/Package.h b/src/jrd/Package.h new file mode 100644 index 00000000000..c4915602287 --- /dev/null +++ b/src/jrd/Package.h @@ -0,0 +1,233 @@ +/* + * PROGRAM: Firebird CONSTANTS implementation. + * MODULE: Package.h + * DESCRIPTION: Routine to cache and reload Package constants + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Artyom Abakumov + * for Red Soft Corporation. + * + * Copyright (c) 2025 Red Soft Corporation + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + + +#ifndef JRD_CONSTANT_H +#define JRD_CONSTANT_H + +#include "firebird.h" +#include "../jrd/CacheVector.h" +#include "../jrd/Resources.h" +#include "../jrd/obj.h" +#include "../jrd/val.h" +#include "../jrd/lck.h" +#include "../common/classes/GenericMap.h" + +namespace Jrd +{ +class DsqlCompilerScratch; +class dsql_fld; + +class ConstantValue final : public Firebird::PermanentStorage +{ +public: + ConstantValue(MemoryPool& pool) : + Firebird::PermanentStorage(pool), + name(pool) + { } + + MetaId id{}; + QualifiedName name; + + // Keep type to gen hash (when not commited - we cannot read it from system table) + // Keep value when scanning and after the first execution + impure_value value{}; + + // keep only materialized value + bid blrBlobId{}; + + bool isPrivate = false; + + bool hash(thread_db* tdbb, Firebird::sha512& digest) const; + + static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name); + static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const MetaId id); + + static void genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema); + + dsc& makeValue(thread_db* tdbb); + + ~ConstantValue() + { + delete value.vlu_string; + } +}; + +struct ConstantsCache +{ + using ValueId = ULONG; + + ConstantsCache(MemoryPool& pool) : + idMap(pool), + nameMap(pool), + values(pool) + { } + + Firebird::NonPooledMap idMap; + Firebird::LeftPooledMap nameMap; + + Firebird::ObjectsArray values; + + ConstantValue& add(const MetaId constId, const QualifiedName& constName, const bool isPrivate); + + void clear() + { + idMap.clear(); + nameMap.clear(); + values.clear(); + } +}; + + +class PackagePermanent : public Firebird::PermanentStorage +{ +public: + explicit PackagePermanent(thread_db* tdbb, MemoryPool& p, MetaId metaId, NoData) + : PermanentStorage(p), + id(metaId), + name(p) + { } + + explicit PackagePermanent(MemoryPool& p) + : PermanentStorage(p), + id(~0), + name(p) + { } + + MetaId getId() const + { + return id; + } + + static bool destroy(thread_db* tdbb, PackagePermanent* routine) + { + return false; + } + + void releaseLock(thread_db*) { } + + const QualifiedName& getName() const noexcept { return name; } + void setName(const QualifiedName& value) { name = value; } + + bool hasData() const { return name.hasData(); } + +public: + MetaId id; // routine ID + QualifiedName name; // routine name +}; + +class Package final : public Firebird::PermanentStorage, public ObjectBase +{ +public: + // lock requeued by CacheElement + static const enum lck_t LOCKTYPE = LCK_package_rescan; + +private: + explicit Package(Cached::Package* perm) + : Firebird::PermanentStorage(perm->getPool()), + constants(perm->getPool()), + cachedPackage(perm) + { } + +public: + explicit Package(MemoryPool& p) + : Firebird::PermanentStorage(p), + constants(p) + { } + + // Methods needed by the MetaCache + // ---------- + + static bool destroy(thread_db* tdbb, Package* routine) + { + return false; + } + + static Package* create(thread_db* tdbb, MemoryPool& pool, Cached::Package* perm); + static std::optional getIdByName(thread_db* tdbb, const QualifiedName& name); + + ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags); + void checkReload(thread_db* tdbb); + ScanResult reload(thread_db* tdbb, ObjectBase::Flag flags); + + static const char* objectFamily(void*) + { + return "package"; + } + + MetaId getId() const + { + return getPermanent()->id; + } + + int getObjectType() const noexcept + { + return objectType(); + } + + SLONG getSclType() const noexcept + { + return obj_package_header; + } + + static int objectType(); + + bool hash(thread_db* tdbb, Firebird::sha512& digest); + + Cached::Package* getPermanent() const noexcept + { + return cachedPackage; + } + + // ---------- + + ConstantValue& addConstant(thread_db* tdbb, const MetaId constId, + const QualifiedName& constName, + const bool isPrivate, + const TypeClause* type); + + ConstantValue& addConstant(thread_db* tdbb, const MetaId constId, + const QualifiedName& constName, + const bool isPrivate, + const bid blrBlobId, + const bool skipMakeValue = false); + + ConstantValue* findConstant(thread_db* tdbb, const MetaId id); + ConstantValue* findConstant(thread_db* tdbb, QualifiedName name); + +private: + virtual ~Package() = default; + +private: + ConstantsCache constants; + Cached::Package* cachedPackage = nullptr; // entry in the cache + bool m_callReload = true; +}; + +} // namespace Jrd + +#endif // JRD_CONSTANT_H diff --git a/src/jrd/Resources.cpp b/src/jrd/Resources.cpp index 2d512087f64..36e0e5a895d 100644 --- a/src/jrd/Resources.cpp +++ b/src/jrd/Resources.cpp @@ -32,7 +32,7 @@ void Resources::transfer(thread_db* tdbb, VersionedObjects* to, bool internal) gotHash += functions.transfer(tdbb, to, internal, digest); gotHash += triggers.transfer(tdbb, to, internal, digest); gotHash += indices.transfer(tdbb, to, internal, digest); - gotHash += constants.transfer(tdbb, to, internal, digest); + gotHash += packages.transfer(tdbb, to, internal, digest); if (hasHash) { diff --git a/src/jrd/Resources.h b/src/jrd/Resources.h index 6c6eebbdab7..a181bdb2611 100644 --- a/src/jrd/Resources.h +++ b/src/jrd/Resources.h @@ -42,8 +42,8 @@ class DbTriggers; class CharSetVers; class IndexPermanent; class IndexVersion; -class Constant; -class ConstantPermanent; +class Package; +class PackagePermanent; namespace Cached { @@ -54,7 +54,7 @@ namespace Cached typedef CacheElement Function; typedef CacheElement Triggers; typedef CacheElement Index; - typedef CacheElement Constant; + typedef CacheElement Package; } class Resources; @@ -69,7 +69,7 @@ union VersionedPartPtr CharSetVers* charset; DbTriggers* triggers; IndexVersion* index; - Constant* constant; + Package* package; }; class VersionedObjects : public pool_alloc_rpt, @@ -124,7 +124,7 @@ template <> inline jrd_rel*& VersionedObjects::object(FB_SIZE_T n) { re template <> inline CharSetVers*& VersionedObjects::object(FB_SIZE_T n) { return data[n].charset; } template <> inline DbTriggers*& VersionedObjects::object(FB_SIZE_T n) { return data[n].triggers; } template <> inline IndexVersion*& VersionedObjects::object(FB_SIZE_T n) { return data[n].index; } -template <> inline Constant*& VersionedObjects::object(FB_SIZE_T n) { return data[n].constant; } +template <> inline Package*& VersionedObjects::object(FB_SIZE_T n) { return data[n].package; } template <> inline Function* VersionedObjects::object(FB_SIZE_T n) const { return data[n].function; } template <> inline jrd_prc* VersionedObjects::object(FB_SIZE_T n) const { return data[n].procedure; } @@ -132,7 +132,7 @@ template <> inline jrd_rel* VersionedObjects::object(FB_SIZE_T n) const template <> inline CharSetVers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].charset; } template <> inline DbTriggers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].triggers; } template <> inline IndexVersion* VersionedObjects::object(FB_SIZE_T n) const { return data[n].index; } -template <> inline Constant* VersionedObjects::object(FB_SIZE_T n) const { return data[n].constant; } +template <> inline Package* VersionedObjects::object(FB_SIZE_T n) const { return data[n].package; } template @@ -290,7 +290,7 @@ class Resources final functions(p, versionCurrentPosition), triggers(p, versionCurrentPosition), indices(p, versionCurrentPosition), - constants(p, versionCurrentPosition) + packages(p, versionCurrentPosition) { } ~Resources(); @@ -301,12 +301,12 @@ class Resources final RscArray functions; RscArray triggers; RscArray indices; - RscArray constants; + RscArray packages; inline FB_SIZE_T countVersionedObjects() const noexcept { return charSets.getCount() + relations.getCount() + procedures.getCount() + - functions.getCount() + triggers.getCount() + constants.getCount(); //? + indices.getCount() ? + functions.getCount() + triggers.getCount() + packages.getCount(); } }; @@ -317,7 +317,7 @@ template <> inline const Resources::RscArray& Resour template <> inline const Resources::RscArray& Resources::objects() const { return charSets; } template <> inline const Resources::RscArray& Resources::objects() const { return triggers; } template <> inline const Resources::RscArray& Resources::objects() const { return indices; } -template <> inline const Resources::RscArray& Resources::objects() const { return constants; } +template <> inline const Resources::RscArray& Resources::objects() const { return packages; } namespace Rsc { @@ -327,7 +327,7 @@ namespace Rsc typedef CachedResource CSet; typedef CachedResource Trig; typedef CachedResource Idx; - typedef CachedResource Const; + typedef CachedResource Package; }; //namespace Rsc diff --git a/src/jrd/SystemTriggers.epp b/src/jrd/SystemTriggers.epp index 6b8be162869..15c56efecc1 100644 --- a/src/jrd/SystemTriggers.epp +++ b/src/jrd/SystemTriggers.epp @@ -1602,14 +1602,14 @@ void afterInsertRelation(thread_db* tdbb, Record* record) } -template -void afterInsertCachedObject(thread_db* tdbb, Record* record) +template +void populateCache(thread_db* tdbb, Record* record) { if (!tdbb->getAttachment()->isRWGbak()) return; dsc desc; - bool idSet = EVL_field(nullptr, record, Field, &desc); + const bool idSet = EVL_field(nullptr, record, Field, &desc); fb_assert(idSet); if (!idSet) return; @@ -1621,7 +1621,7 @@ void afterInsertCachedObject(thread_db* tdbb, Record* record) void afterInsertProcedure(thread_db* tdbb, Record* record) { - afterInsertCachedObject(tdbb, record); + populateCache(tdbb, record); } void afterUpdateProcedure(thread_db* tdbb, Record* orgRecord, Record* newRecord) @@ -1631,7 +1631,7 @@ void afterUpdateProcedure(thread_db* tdbb, Record* orgRecord, Record* newRecord) void afterInsertFunction(thread_db* tdbb, Record* record) { - afterInsertCachedObject(tdbb, record); + populateCache(tdbb, record); } @@ -1703,12 +1703,7 @@ void beforeInsertCollation(thread_db* tdbb, Record* record, jrd_rel* relation) void afterInsertCollation(thread_db* tdbb, Record* record) { - afterInsertCachedObject(tdbb, record); -} - -void afterInsertConstant(thread_db* tdbb, Record* record) -{ - afterInsertCachedObject(tdbb, record); + populateCache(tdbb, record); } } // anonymous @@ -1812,8 +1807,8 @@ void SystemTriggers::executeAfterInsertTriggers(thread_db* tdbb, jrd_rel* relati afterInsertFunction(tdbb, record); break; - case rel_constants: - afterInsertConstant(tdbb, record); + case rel_packages: + populateCache(tdbb, record); break; } } diff --git a/src/jrd/constants.h b/src/jrd/constants.h index dcb4230693d..01a60bcfef0 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -155,6 +155,7 @@ inline constexpr int GEN_SECCLASS_PREFIX_LEN = 4; inline constexpr const char* PROCEDURES_GENERATOR = "RDB$PROCEDURES"; inline constexpr const char* FUNCTIONS_GENERATOR = "RDB$FUNCTIONS"; inline constexpr const char* const CONSTANTS_GENERATOR = "RDB$CONSTANTS"; +inline constexpr const char* const PACKAGES_GENERATOR = "RDB$PACKAGES"; // Automatically created check constraints for unnamed PRIMARY and UNIQUE declarations. inline constexpr const char* IMPLICIT_INTEGRITY_PREFIX = "INTEG_"; diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index ba0cd1efe42..ca3ba56be14 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -133,7 +133,7 @@ #include "../jrd/shut_proto.h" #include "../jrd/ProtectRelations.h" -#include "../jrd/Constant.h" +#include "../jrd/Package.h" #include "../dsql/metd_proto.h" #ifdef HAVE_UNISTD_H @@ -410,6 +410,7 @@ static bool end_backup(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool check_not_null(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool store_view_context_type(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool user_management(thread_db*, SSHORT, DeferredWork*, jrd_tra*); +static bool create_package(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction); static bool drop_package_header(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool modify_package_header(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool drop_package_body(thread_db*, SSHORT, DeferredWork*, jrd_tra*); @@ -947,21 +948,6 @@ namespace static void checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction); }; - class ConstantManager : public RoutineManager, MetadataCache::getPerm> - { - public: - static const char* getTypeStr() - { - return "constant"; - } - - static Constant* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile); - static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work, - SSHORT validBlr); - static void checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction); - }; - // These methods cannot be defined inline, because GPRE generates wrong code. Routine* FunctionManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, @@ -1142,36 +1128,65 @@ namespace } } - Constant* ConstantManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, - bool compile) + static bool modifyConstant(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { - Jrd::Attachment* attachment = tdbb->getAttachment(); - CachedRequestId constDpd; - AutoCacheRequest handle(tdbb, constDpd); - Constant* routine = nullptr; + fb_assert(!work->dfw_package.isEmpty()); - FOR(REQUEST_HANDLE handle) - X IN RDB$CONSTANTS WITH - X.RDB$SCHEMA_NAME EQ work->dfw_schema.c_str() AND - X.RDB$CONSTANT_NAME EQ work->dfw_name.c_str() AND - X.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), "") + SET_TDBB(tdbb); + const QualifiedName name(work->dfw_name); + + switch (phase) { - if (!X.RDB$CONSTANT_BLR.NULL) - blobId = X.RDB$CONSTANT_BLR; + case 0: + return false; - routine = MetadataCache::getVersioned(tdbb, - work->getQualifiedName(), compile ? 0 : CacheFlag::MINISCAN); + case 1: + return true; + case 2: + check_dependencies(tdbb, work->getQualifiedName(), nullptr, obj_package_constant, transaction); + return true; + + case 3: + return true; + + case 4: + MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_constant); + return false; } - END_FOR - return routine; + return false; } - void ConstantManager::validate(thread_db*, jrd_tra*, DeferredWork*, SSHORT) - { } + static bool deleteConstant(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) + { + fb_assert(!work->dfw_package.isEmpty()); + + SET_TDBB(tdbb); + Cached::Package* routine; + + switch (phase) + { + case 0: + return false; + + case 1: + return true; + + case 2: + check_dependencies(tdbb, work->getQualifiedName(), NULL, obj_package_constant, transaction); + return true; + + case 3: + return true; + + case 4: + MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_constant); + return false; + } + + return false; + } - void ConstantManager::checkParamDependencies(thread_db*, DeferredWork*, jrd_tra*) - { } } // namespace static inline constexpr deferred_task task_table[] = @@ -1226,9 +1241,10 @@ static inline constexpr deferred_task task_table[] = { dfw_clear_cache, clear_cache }, { dfw_change_repl_state, change_repl_state }, { dfw_set_statistics, set_statistics }, - { dfw_create_package_constant, ConstantManager::createRoutine }, - { dfw_modify_package_constant, ConstantManager::modifyRoutine }, - { dfw_delete_package_constant, ConstantManager::deleteRoutine }, + { dfw_modify_package_constant, modifyConstant }, + { dfw_delete_package_constant, deleteConstant }, + { dfw_create_package, create_package }, + { dfw_null, NULL } }; @@ -2272,17 +2288,75 @@ static bool user_management(thread_db* /*tdbb*/, SSHORT phase, DeferredWork* wor return false; } +static bool create_package(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +{ + fb_assert(work->dfw_id != 0); + + SET_TDBB(tdbb); + + Cached::Package* routine = nullptr; + switch (phase) + { + case 0: + routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN); + if (routine) + routine->rollback(tdbb); + + return false; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + return true; + case 7: + routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN); + fb_assert(routine); + if (routine) + routine->commit(tdbb); + + return false; + } + + return false; +} + // Drop dependencies of a package header. static bool drop_package_header(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { + fb_assert(work->dfw_id != 0); + SET_TDBB(tdbb); + Cached::Package* routine = nullptr; switch (phase) { + case 0: + routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN); + if (routine) + routine->rollback(tdbb); + + return false; + case 1: MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_body); MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_header); - break; + return true; + + case 2: + case 3: + case 4: + case 5: + case 6: + return true; + case 7: + routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::ERASED | CacheFlag::NOSCAN); + fb_assert(routine); + if (routine) + routine->commit(tdbb); + + return false; } return false; @@ -2293,11 +2367,36 @@ static bool modify_package_header(thread_db* tdbb, SSHORT phase, DeferredWork* w { SET_TDBB(tdbb); + Cached::Package* routine = nullptr; switch (phase) { + case 0: + routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN); + if (routine) + routine->rollback(tdbb); + + return false; case 1: MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_header); - break; + return true; + case 2: + case 3: + routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::MINISCAN); + if (!routine) + return false; + + return true; + case 4: + case 5: + case 6: + return true; + case 7: + routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN); + fb_assert(routine); + if (routine) + routine->commit(tdbb); + + return false; } return false; @@ -2306,18 +2405,40 @@ static bool modify_package_header(thread_db* tdbb, SSHORT phase, DeferredWork* w // Drop dependencies of a package body. static bool drop_package_body(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { + fb_assert(work->dfw_id != 0); + SET_TDBB(tdbb); + Cached::Package* routine; switch (phase) { + case 0: + routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN); + if (routine) + routine->rollback(tdbb); + + return false; case 1: MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_body); - break; + return true; + + case 2: + case 3: + case 4: + case 5: + case 6: + return true; + case 7: + routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::ERASED | CacheFlag::NOSCAN); + fb_assert(routine); + if (routine) + routine->commit(tdbb); } return false; } + static bool change_repl_state(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) { /************************************** diff --git a/src/jrd/drq.h b/src/jrd/drq.h index b2d24f2a4f4..24d844a30ec 100644 --- a/src/jrd/drq.h +++ b/src/jrd/drq.h @@ -243,6 +243,7 @@ enum drq_type_t drq_l_rel_con, // lookup relation constraint drq_l_rel_fld_name, // lookup relation field name drq_g_nxt_const_id, // lookup next constant ID + drq_g_nxt_package_id, // lookup next package ID drq_MAX }; diff --git a/src/jrd/fields.h b/src/jrd/fields.h index 6c3ea4aa8cb..56f18a0d5d0 100644 --- a/src/jrd/fields.h +++ b/src/jrd/fields.h @@ -240,7 +240,9 @@ FIELD(fld_tab_type , nam_mon_tab_type , dtype_varying , 32 , dsc_text_type_ascii , NULL , true , ODS_14_0) - FIELD(fld_const_name , nam_const_name , dtype_varying , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata , NULL , false , ODS_13_1) + FIELD(fld_pkg_id , nam_pkg_id , dtype_long , sizeof(SLONG) , 0 , NULL , true , ODS_14_0) + + FIELD(fld_const_name , nam_const_name , dtype_varying , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata , NULL , false , ODS_14_0) FIELD(fld_const_id , nam_const_id , dtype_long , sizeof(SLONG) , 0 , NULL , true , ODS_14_0) - FIELD(fld_const_blr , nam_const_blr , dtype_blob , BLOB_SIZE , isc_blob_blr , NULL , true , ODS_13_1) - FIELD(fld_const_source , nam_const_source , dtype_blob , BLOB_SIZE , isc_blob_text , NULL , true , ODS_13_1) + FIELD(fld_const_blr , nam_const_blr , dtype_blob , BLOB_SIZE , isc_blob_blr , NULL , true , ODS_14_0) + FIELD(fld_const_source , nam_const_source , dtype_blob , BLOB_SIZE , isc_blob_text , NULL , true , ODS_14_0) diff --git a/src/jrd/idx.h b/src/jrd/idx.h index 5c8485872cc..53deb0ac182 100644 --- a/src/jrd/idx.h +++ b/src/jrd/idx.h @@ -534,6 +534,10 @@ static inline constexpr struct ini_idx_t indices[] = // define index RDB$INDEX_99 for RDB$CONSTANTS unique RDB$CONSTANT_ID; INDEX(99, rel_constants, idx_unique, 1, ODS_14_0) SEGMENT(f_const_id, idx_numeric) // constant id + }}, + // define index RDB$INDEX_99 for RDB$CONSTANTS unique RDB$CONSTANT_ID; + INDEX(100, rel_packages, idx_unique, 1, ODS_14_0) + SEGMENT(f_pkg_id, idx_numeric) // constant id }} }; diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index cf5ea9933ff..ae4d3a0d568 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -58,7 +58,7 @@ #include "../common/os/guid.h" #include "../jrd/sbm.h" #include "../jrd/scl.h" -#include "../jrd/Constant.h" +#include "../jrd/Package.h" #include "../jrd/Routine.h" #include "../jrd/ExtEngineManager.h" #include "../jrd/Attachment.h" diff --git a/src/jrd/lck.cpp b/src/jrd/lck.cpp index d9f20aae664..88117b2e163 100644 --- a/src/jrd/lck.cpp +++ b/src/jrd/lck.cpp @@ -584,7 +584,7 @@ static lck_owner_t get_owner_type(enum lck_t lock_type) case LCK_idx_rescan: case LCK_prc_rescan: case LCK_fun_rescan: - case LCK_constant_rescan: + case LCK_package_rescan: case LCK_cs_rescan: case LCK_dbwide_triggers: owner_type = LCK_OWNER_database; diff --git a/src/jrd/lck.h b/src/jrd/lck.h index 24e74ed468b..cfe37762827 100644 --- a/src/jrd/lck.h +++ b/src/jrd/lck.h @@ -67,7 +67,7 @@ enum lck_t : UCHAR { LCK_idx_rescan, // Index rescan lock LCK_prc_rescan, // Procedure rescan lock LCK_fun_rescan, // Function existence lock - LCK_constant_rescan, // Constant existence lock + LCK_package_rescan, // Package existence lock LCK_rel_partners, // Relation partners lock LCK_crypt, // Crypt lock for single crypt thread LCK_crypt_status, // Notifies about changed database encryption status diff --git a/src/jrd/met.epp b/src/jrd/met.epp index ce240425673..a7436af82b2 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -98,7 +98,7 @@ #include "../jrd/trace/TraceJrdHelpers.h" #include "firebird/impl/msg_helper.h" #include "../jrd/LocalTemporaryTable.h" -#include "../jrd/Constant.h" +#include "../jrd/Package.h" #ifdef HAVE_CTYPE_H @@ -341,7 +341,7 @@ void MetadataCache::cleanup(thread_db* tdbb) mdc_procedures.cleanup(tdbb); mdc_functions.cleanup(tdbb); mdc_charsets.cleanup(tdbb); - mdc_constants.cleanup(tdbb); + mdc_packages.cleanup(tdbb); for (unsigned i = 0; i < DB_TRIGGERS_COUNT; ++i) { @@ -812,10 +812,14 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const request.reset(tdbb, irq_m_fields10, IRQ_REQUESTS); + Package* package = nullptr; FOR(REQUEST_HANDLE request) DEP IN RDB$DEPENDENCIES CROSS - CONST IN RDB$CONSTANTS - WITH DEP.RDB$DEPENDED_ON_SCHEMA_NAME EQ schemaName->dsc_address AND + CONST IN RDB$CONSTANTS CROSS + PKG IN RDB$PACKAGES + WITH PKG.RDB$SCHEMA_NAME EQ schemaName->dsc_address AND + PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME AND + DEP.RDB$DEPENDED_ON_SCHEMA_NAME EQ schemaName->dsc_address AND DEP.RDB$DEPENDED_ON_NAME EQ fieldSource->dsc_address AND DEP.RDB$DEPENDED_ON_TYPE EQ obj_field AND (DEP.RDB$DEPENDENT_TYPE EQ obj_package_header OR @@ -829,9 +833,14 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_package_constant, string(constantFullName.object.c_str()), constantFullName.schema, id, constantFullName.package); - DFW_post_work_arg(transaction, dw2, nullptr, nullptr, 0, dfw_arg_check_blr); - MetadataCache::newVersion(tdbb, id); + if (package == nullptr) + { + package = MetadataCache::newVersion(tdbb, id)->getVersioned(tdbb, 0); + fb_assert(package); + } + + package->addConstant(tdbb, id, constantFullName, CONST.RDB$PRIVATE_FLAG, CONST.RDB$CONSTANT_BLR); } END_FOR @@ -4687,12 +4696,12 @@ void MetadataCache::releaseLocks(thread_db* tdbb) charset->releaseLocks(tdbb); } - // Release constants locks + // Release packages locks - for (auto constant : mdc_constants) + for (auto package : mdc_packages) { - if (constant) - constant->releaseLocks(tdbb); + if (package) + package->releaseLocks(tdbb); } // Release database triggers locks diff --git a/src/jrd/met.h b/src/jrd/met.h index b9674ee27a2..5111ed2a505 100644 --- a/src/jrd/met.h +++ b/src/jrd/met.h @@ -238,7 +238,7 @@ class MetadataCache : public Firebird::PermanentStorage mdc_procedures(getPool()), mdc_functions(getPool()), mdc_charsets(getPool()), - mdc_constants(getPool()), + mdc_packages(getPool()), mdc_cleanup_queue(pool) { memset(mdc_triggers, 0, sizeof(mdc_triggers)); @@ -502,12 +502,12 @@ class MetadataCache : public Firebird::PermanentStorage }; template - class Vector + class Vector { public: - static CacheVector& get(MetadataCache* mdc) + static CacheVector& get(MetadataCache* mdc) { - return mdc->mdc_constants; + return mdc->mdc_packages; } }; @@ -616,7 +616,7 @@ class MetadataCache : public Firebird::PermanentStorage CacheVector mdc_procedures; CacheVector mdc_functions; // User defined functions CacheVector mdc_charsets; // intl character set descriptions - CacheVector mdc_constants; // Package constants + CacheVector mdc_packages; // Package Constants TriggersSet mdc_triggers[DB_TRIGGERS_COUNT]; // Two numbers are required because commit into cache is not atomic event. // Front value is incremented before commit, back - after commit. diff --git a/src/jrd/names.h b/src/jrd/names.h index ba020049c00..d04e7145189 100644 --- a/src/jrd/names.h +++ b/src/jrd/names.h @@ -256,6 +256,7 @@ NAME("RDB$ENGINE_NAME", nam_engine_name) NAME("RDB$LINGER", nam_linger) NAME("RDB$PACKAGES", nam_packages) +NAME("RDB$PACKAGE_ID", nam_pkg_id) NAME("RDB$PACKAGE_NAME", nam_pkg_name) NAME("RDB$PACKAGE_HEADER_SOURCE", nam_pkg_header_source) NAME("RDB$PACKAGE_BODY_SOURCE", nam_pkg_body_source) diff --git a/src/jrd/relations.h b/src/jrd/relations.h index d2ef9e4de13..4c5c205fb9c 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -680,6 +680,7 @@ RELATION(nam_packages, rel_packages, ODS_12_0, rel_persistent) FIELD(f_pkg_desc, nam_description, fld_description, 1, ODS_12_0) FIELD(f_pkg_sql_security, nam_sql_security, fld_b_sql_security, 1, ODS_13_0) FIELD(f_pkg_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) + FIELD(f_pkg_id, nam_pkg_id, fld_pkg_id, 1, ODS_14_0) END_RELATION // Relation 43 (SEC$USERS) diff --git a/src/jrd/tra.h b/src/jrd/tra.h index 485be38bd8d..5e44caf20c7 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -543,11 +543,6 @@ enum dfw_t : int { dfw_set_generator, dfw_change_repl_state, - // Package constant - dfw_create_package_constant, - dfw_modify_package_constant, - dfw_delete_package_constant, - // deferred works argument types dfw_arg_proc_name, // procedure name for dfw_delete_prm, mandatory dfw_arg_check_blr, // check if BLR is still compilable @@ -558,7 +553,14 @@ enum dfw_t : int { dfw_set_linger, // set database linger dfw_clear_cache, // clear user mapping cache dfw_set_statistics, // set statistics support - dfw_deps_to_disk // store saved deps to disk + dfw_deps_to_disk, // store saved deps to disk + + // Constant + dfw_modify_package_constant, + dfw_delete_package_constant, + + // Package + dfw_create_package }; } //namespace Jrd diff --git a/src/jrd/trig.h b/src/jrd/trig.h index fc05531a6a1..3cc9163bc9d 100644 --- a/src/jrd/trig.h +++ b/src/jrd/trig.h @@ -83,6 +83,7 @@ static inline constexpr Jrd::gen generators[] = { FUNCTIONS_GENERATOR, 10, "Function ID", ODS_13_0 }, { "RDB$GENERATOR_NAME", 11, "Implicit generator name", ODS_13_0 }, { CONSTANTS_GENERATOR, 12, "Constant ID", ODS_14_0 }, + { PACKAGES_GENERATOR, 13, "Package ID", ODS_14_0 }, { nullptr, 0, nullptr, 0 } }; diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index e591243bace..9c43b796492 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -94,7 +94,7 @@ #include "../jrd/trace/TraceJrdHelpers.h" #include "../common/Task.h" #include "../jrd/WorkerAttachment.h" -#include "../jrd/Constant.h" +#include "../jrd/Package.h" using namespace Jrd; using namespace Firebird; @@ -2360,12 +2360,6 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_const_id, &desc2); id = MOV_get_long(tdbb, &desc2, 0); - { - [[maybe_unused]] - auto constant = Constant::lookup(tdbb, id); - fb_assert(constant); - } - DFW_post_work(transaction, dfw_delete_package_constant, &desc, &schemaDesc, id, object_name.package); break; @@ -3452,6 +3446,12 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j MOV_get_metaname(tdbb, &schemaDesc, object_name.schema); MOV_get_metaname(tdbb, &desc1, object_name.object); SCL_check_package(tdbb, object_name, SCL_alter); + + { // Send dfw + EVL_field(0, org_rpb->rpb_record, f_pkg_id, &desc2); + const USHORT id = MOV_get_long(tdbb, &desc2, 0); + DFW_post_work(transaction, dfw_modify_package_header, &desc1, &schemaDesc, id, object_name.package); + } } } check_class(tdbb, transaction, org_rpb, new_rpb, f_pkg_class); @@ -3770,7 +3770,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j { // Send dfw EVL_field(0, org_rpb->rpb_record, f_const_id, &desc2); const USHORT id = MOV_get_long(tdbb, &desc2, 0); - DFW_post_work(transaction, Jrd::dfw_modify_package_constant, &desc1, &schemaDesc, id, object_name.package); + DFW_post_work(transaction, dfw_modify_package_constant, &desc1, &schemaDesc, id, object_name.package); } break; @@ -4397,6 +4397,11 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) set_owner_name(tdbb, rpb->rpb_record, f_pkg_owner); if (set_security_class(tdbb, rpb->rpb_record, f_pkg_class)) DFW_post_work(transaction, dfw_grant, &desc, &schemaDesc, obj_package_header); + + object_id = set_metadata_id(tdbb, rpb->rpb_record, + f_pkg_id, drq_g_nxt_package_id, PACKAGES_GENERATOR); + + work = DFW_post_work(transaction, dfw_create_package, &desc, &schemaDesc, object_id, object_name.package); break; case rel_procedures: @@ -4694,9 +4699,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) MOV_get_metaname(tdbb, &desc2, object_name.package); object_id = set_metadata_id(tdbb, rpb->rpb_record, - f_const_id, drq_g_nxt_const_id, "RDB$CONSTANTS"); - - work = DFW_post_work(transaction, dfw_create_package_constant, &desc, &schemaDesc, object_id, object_name.package); + f_const_id, drq_g_nxt_const_id, CONSTANTS_GENERATOR); break; default: // Shut up compiler warnings From 6b64151a79a7481974b2e08c6c803f22480e52ed Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 1 Apr 2026 12:01:45 +0300 Subject: [PATCH 046/119] Update Firebird.pas --- src/include/gen/Firebird.pas | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index d192cc8141b..77c0c8a6093 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5955,18 +5955,18 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_no_user_att_while_restore = 335545318; isc_genseq_stepmustbe_nonzero = 335545319; isc_argmustbe_exact_function = 335545320; - isc_not_defined_constant = 335545321; - isc_const_name = 335545322; - isc_private_constant = 335545323; - isc_package_alias_help = 335545324; - isc_bad_constant_blr = 335545325; - isc_bad_constant_desc = 335545326; - isc_bad_constant_name = 335545327; - isc_bad_constant_type = 335545328; isc_sysf_argmustbe_range_inc0_1 = 335545321; isc_argmustbe_numeric_function = 335545322; isc_percetile_only_one_sort_item = 335545323; isc_argmustbe_const_within_group = 335545324; + isc_not_defined_constant = 335545325; + isc_const_name = 335545326; + isc_private_constant = 335545327; + isc_package_alias_help = 335545328; + isc_bad_constant_blr = 335545329; + isc_bad_constant_desc = 335545330; + isc_bad_constant_name = 335545331; + isc_bad_constant_type = 335545332; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; From 241a95a6c1d700d17f8a6d0e7536b0e0985b495b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 1 Apr 2026 12:47:02 +0300 Subject: [PATCH 047/119] Revert unnecessary changes --- src/jrd/SystemTriggers.epp | 1 - src/jrd/dfw.epp | 14 ++++++-------- src/jrd/irq.h | 1 + 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/jrd/SystemTriggers.epp b/src/jrd/SystemTriggers.epp index 15c56efecc1..fc57d817a61 100644 --- a/src/jrd/SystemTriggers.epp +++ b/src/jrd/SystemTriggers.epp @@ -1634,7 +1634,6 @@ void afterInsertFunction(thread_db* tdbb, Record* record) populateCache(tdbb, record); } - void afterUpdateFunction(thread_db* tdbb, Record* orgRecord, Record* newRecord) { afterInsertFunction(tdbb, newRecord); diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 43335e6db0f..e7f1f3dc52a 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -134,7 +134,6 @@ #include "../jrd/ProtectRelations.h" #include "../jrd/Package.h" -#include "../dsql/metd_proto.h" #ifdef HAVE_UNISTD_H #include @@ -830,7 +829,7 @@ namespace private: // Get relations and fields on which this routine depends, either when it's being // created or when it's modified. - static void getDependencies(DeferredWork* work, bool compile, jrd_tra* transaction) + static Routine* getDependencies(DeferredWork* work, bool compile, jrd_tra* transaction) { thread_db* tdbb = JRD_get_thread_data(); Jrd::Database* dbb = tdbb->getDatabase(); @@ -840,14 +839,14 @@ namespace bid blobId; blobId.clear(); - auto* object = Self::lookupBlobId(tdbb, work, blobId, compile); + Routine* routine = Self::lookupBlobId(tdbb, work, blobId, compile); MetadataCache::verify_cache(tdbb); // get any dependencies now by parsing the blr - if (!object) - return; + if (!routine) + return nullptr; const QualifiedName depName(work->dfw_package.isEmpty() ? work->getQualifiedName() : @@ -874,9 +873,8 @@ namespace dbb->deletePool(new_pool); } } - else if constexpr (std::is_base_of_v) + else { - Routine* routine = object; Array dependencies; const auto allParameters = {&routine->getInputFields(), &routine->getOutputFields()}; @@ -914,6 +912,7 @@ namespace } MetadataCache::verify_cache(tdbb); + return routine; } }; @@ -2437,7 +2436,6 @@ static bool drop_package_body(thread_db* tdbb, SSHORT phase, DeferredWork* work, return false; } - static bool change_repl_state(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra*) { /************************************** diff --git a/src/jrd/irq.h b/src/jrd/irq.h index 979c817d911..cbb22ea63e6 100644 --- a/src/jrd/irq.h +++ b/src/jrd/irq.h @@ -179,6 +179,7 @@ enum irq_type_t irq_index_id_erase, // cleanup index ID irq_get_index_by_name, // find appropriate index irq_l_index_cnstrt, // lookup index for constraint + irq_MAX }; From 71872e23908772bad4fffefafa0e0477f251234f Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 1 Apr 2026 12:47:41 +0300 Subject: [PATCH 048/119] Remove second protect_system_table_delupd check for CONSTANTS table --- src/jrd/met.epp | 2 +- src/jrd/vio.cpp | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 7aa7e7c20c5..533c40cf177 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -571,7 +571,7 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const * Find all relations affected and schedule a format update. * Find all procedures and triggers and schedule a BLR validate. * Find all functions and schedule a BLR validate. - * Find all constants. + * Find all package constants. * **************************************/ SET_TDBB(tdbb); diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index e054a1b8344..000276e88f4 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -3758,16 +3758,13 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j break; case rel_constants: - if (!check_nullify_source(tdbb, org_rpb, new_rpb, f_const_source)) - protect_system_table_delupd(tdbb, relation, "UPDATE"); + protect_system_table_delupd(tdbb, relation, "UPDATE"); EVL_field(0, org_rpb->rpb_record, f_const_name, &desc1); EVL_field(0, org_rpb->rpb_record, f_const_package_schema, &schemaDesc); - if (EVL_field(0, org_rpb->rpb_record, f_const_package, &desc2)) - MOV_get_metaname(tdbb, &desc2, object_name.package); - - protect_system_table_delupd(tdbb, relation, "UPDATE"); + EVL_field(0, org_rpb->rpb_record, f_const_package, &desc2); + MOV_get_metaname(tdbb, &desc2, object_name.package); { // Send dfw EVL_field(0, org_rpb->rpb_record, f_const_id, &desc2); From c483a699f8cccf23578313d16d042c6ad0f99852 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 1 Apr 2026 12:48:01 +0300 Subject: [PATCH 049/119] Fix missing package id for system packages --- src/jrd/ini.epp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index 27bf34994b2..93ec0f47ac9 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -1967,6 +1967,7 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR AutoRequest functionHandle, functionReturnHandle, functionArgumentHandle; const SLONG procGen = lookupGenerator(PROCEDURES_GENERATOR); const SLONG funcGen = lookupGenerator(FUNCTIONS_GENERATOR); + const SLONG packageGen = lookupGenerator(PACKAGES_GENERATOR); for (const auto& systemPackage : SystemPackage::get()) { @@ -1989,6 +1990,8 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR PKG.RDB$SYSTEM_FLAG = RDB_system; PKG.RDB$VALID_BODY_FLAG = TRUE; + + PKG.RDB$PACKAGE_ID = DPM_gen_id(tdbb, packageGen, false, 1); } END_STORE From 731d65d3e990739627168b5d13d08a6ba7c3a88f Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 1 Apr 2026 15:17:27 +0300 Subject: [PATCH 050/119] Add ability to add constants to system packages --- src/jrd/SystemPackages.h | 37 ++++++++++++++++++++++++++++--- src/jrd/ini.epp | 47 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/src/jrd/SystemPackages.h b/src/jrd/SystemPackages.h index acb47b379b3..2cf1e403183 100644 --- a/src/jrd/SystemPackages.h +++ b/src/jrd/SystemPackages.h @@ -180,6 +180,33 @@ namespace Jrd SystemFunctionReturnType returnType; }; + struct SystemConstant + { + SystemConstant( + Firebird::MemoryPool& pool, + const char* aName, + const USHORT aFieldId, + const char* aValueSource = nullptr, + const std::initializer_list aValueBlr = {} + ) + : name(aName), + fieldId(aFieldId), + valueSource(aValueSource), + valueBlr(pool, aValueBlr) + { } + + SystemConstant(Firebird::MemoryPool& pool, const SystemConstant& other) + : valueBlr(pool) + { + *this = other; + } + + const char* name; + USHORT fieldId; + const char* valueSource = nullptr; + Firebird::Array valueBlr; + }; + struct SystemPackage { SystemPackage( @@ -187,18 +214,21 @@ namespace Jrd const char* aName, USHORT aOdsVersion, std::initializer_list aProcedures, - std::initializer_list aFunctions + std::initializer_list aFunctions, + std::initializer_list aConstants = {} ) : name(aName), odsVersion(aOdsVersion), procedures(pool, aProcedures), - functions(pool, aFunctions) + functions(pool, aFunctions), + constants(pool, aConstants) { } SystemPackage(Firebird::MemoryPool& pool, const SystemPackage& other) : procedures(pool), - functions(pool) + functions(pool), + constants(pool) { *this = other; } @@ -207,6 +237,7 @@ namespace Jrd USHORT odsVersion; Firebird::ObjectsArray procedures; Firebird::ObjectsArray functions; + Firebird::ObjectsArray constants; static Firebird::ObjectsArray& get(); diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index 93ec0f47ac9..aacb92e2d06 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -1965,9 +1965,11 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR AutoRequest packageHandle, procedureHandle, procedureParameterHandle; AutoRequest functionHandle, functionReturnHandle, functionArgumentHandle; + AutoRequest constantHandle; const SLONG procGen = lookupGenerator(PROCEDURES_GENERATOR); const SLONG funcGen = lookupGenerator(FUNCTIONS_GENERATOR); const SLONG packageGen = lookupGenerator(PACKAGES_GENERATOR); + const SLONG constantGen = lookupGenerator(CONSTANTS_GENERATOR); for (const auto& systemPackage : SystemPackage::get()) { @@ -2160,5 +2162,50 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR END_STORE } } + + for (const auto& constant : systemPackage.constants) + { + STORE (REQUEST_HANDLE constantHandle TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS + { + // Constant name + PAD(constant.name, CONST.RDB$CONSTANT_NAME); + + // Constant unique id + CONST.RDB$CONSTANT_ID = DPM_gen_id(tdbb, constantGen, false, 1);; + + // Description (filed) name + PAD(names[gfields[constant.fieldId].gfld_name], CONST.RDB$FIELD_SOURCE); + + // Put the blr + { + Array blrData(1 + constant.valueBlr.getCount() + 1); + blrData.push(blr_version5); + blrData.append(constant.valueBlr); + blrData.push(blr_eoc); + attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, blrData); + CONST.RDB$CONSTANT_BLR.NULL = FALSE; + } + + // Parent package + PAD(systemPackage.name, CONST.RDB$PACKAGE_NAME); + + // Schema of the parent package + PAD(SYSTEM_SCHEMA, CONST.RDB$SCHEMA_NAME); + + // Type + CONST.RDB$PRIVATE_FLAG.NULL = FALSE; + CONST.RDB$PRIVATE_FLAG = false; + + if (constant.valueSource != nullptr) + { + CONST.RDB$CONSTANT_SOURCE.NULL = FALSE; + attachment->storeMetaDataBlob(tdbb, transaction, &CONST.RDB$CONSTANT_SOURCE, constant.valueSource); + } + else + CONST.RDB$CONSTANT_SOURCE.NULL = TRUE; + } + END_STORE + } } } From 414bd0ded70112910cf3f059ae64682fbde09d23 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 1 Apr 2026 15:17:57 +0300 Subject: [PATCH 051/119] Add constants to system package RDB$BLOB_UTIL as an example Add the following constants: FROM_BEGIN = 0 FROM_CURRENT = 1 FROM_END = 2 --- src/jrd/BlobUtil.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/jrd/BlobUtil.cpp b/src/jrd/BlobUtil.cpp index 18ca4a4270c..fd28ba7691c 100644 --- a/src/jrd/BlobUtil.cpp +++ b/src/jrd/BlobUtil.cpp @@ -286,6 +286,12 @@ BlobUtilPackage::BlobUtilPackage(Firebird::MemoryPool& pool) }, {fld_varybinary_max, true} ) + }, + // constants + { + SystemConstant(pool, "FROM_BEGIN", fld_integer, "0", {blr_literal, blr_short, 0, 0, 0}), + SystemConstant(pool, "FROM_CURRENT", fld_integer, "1", {blr_literal, blr_short, 0, 1, 0}), + SystemConstant(pool, "FROM_END", fld_integer, "2", {blr_literal, blr_short, 0, 2, 0}) } ) { From 082d5e7482fec6787378acc52b0b1e4e1dfd725b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 2 Apr 2026 12:41:23 +0300 Subject: [PATCH 052/119] Add USAGE privilege requirement to use package constant --- src/dsql/DdlNodes.epp | 24 ++++++++++++++++++ src/dsql/PackageNodes.epp | 23 +++++++++++++++++ src/dsql/PackageNodes.h | 1 + src/dsql/parse.y | 15 +++++++++++ src/jrd/Package.epp | 52 +++++++++++++++++++++++++++------------ src/jrd/Package.h | 5 ++-- src/jrd/grant.epp | 1 + 7 files changed, 103 insertions(+), 18 deletions(-) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index ae65d2c2ce4..d13733b84be 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -15656,6 +15656,21 @@ static bool checkObjectExist(thread_db* tdbb, jrd_tra* transaction, const Qualif END_FOR break; } + case obj_package_constant: + { + static const CachedRequestId requestId; + AutoCacheRequest request(tdbb, requestId); + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + X IN RDB$CONSTANTS + WITH X.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + X.RDB$PACKAGE_NAME EQ name.package.c_str() AND + X.RDB$CONSTANT_NAME EQ name.object.c_str() + { + rc = true; + } + END_FOR + break; + } } return rc; @@ -15867,6 +15882,11 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G if (!checkObjectExist(tdbb, transaction, QualifiedName(objName.schema), obj_schema)) status_exception::raise(Arg::Gds(isc_dyn_schema_not_found) << objName.toQuotedString()); + case obj_package_constant: + if (!checkObjectExist(tdbb, transaction, objName, objType)) + status_exception::raise(Arg::PrivateDyn(303) << objName.toQuotedString()); // Package @1 does not exist + break; + default: fb_assert(object == NULL || isDdlObject(objType)); } @@ -16074,6 +16094,7 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G case obj_generator: case obj_package_header: case obj_schema: + case obj_package_constant: { checkGrantorCanGrantObject(tdbb, transaction, currentUser.c_str(), priv, objName, objType); break; @@ -17092,6 +17113,9 @@ static ISC_STATUS getErrorCodeByObjectType(int obj_type) case obj_package_body: err_code = isc_package_name; break; + case obj_package_constant: + err_code = isc_const_name; + break; default: fb_assert(false); } diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 3a6e9464774..1346382c643 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -504,6 +504,29 @@ ValueExprNode* PackageReferenceNode::copy(thread_db* tdbb, NodeCopier& copier) c return node; } +ValueExprNode* PackageReferenceNode::pass1(thread_db* tdbb, CompilerScratch* csb) +{ + ValueExprNode::pass1(tdbb, csb); + + auto& security = m_package()->securityName; + + CMP_post_access(tdbb, csb, security.schema, 0, SCL_usage, obj_schemas, QualifiedName(m_fullName.schema)); + + switch (m_itemType) + { + case blr_pkg_ref_item_const: + { + CMP_post_access(tdbb, csb, security.object, 0, SCL_usage, obj_packages, m_fullName.getSchemaAndPackage()); + break; + } + default: + fb_assert(false); + return nullptr; + } + + return this; +} + ValueExprNode* PackageReferenceNode::pass2(thread_db* tdbb, CompilerScratch* csb) { ValueExprNode::pass2(tdbb, csb); diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 947f7cb47f5..17ae099baf3 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -139,6 +139,7 @@ class PackageReferenceNode final : public TypedNodegrantAdminOption = $7; $node->grantor = $8; } + | usage_privilege(NOTRIAL(&$node->privileges)) ON PACKAGE symbol_package_name + TO non_role_grantee_list(NOTRIAL(&$node->users)) grant_option granted_by + { + $node->object = newNode(obj_package_header, *$4); + $node->grantAdminOption = $7; + $node->grantor = $8; + } /*** | usage_privilege(NOTRIAL(&$node->privileges)) ON DOMAIN symbol_domain_name TO non_role_grantee_list(NOTRIAL(&$node->users)) grant_option granted_by @@ -1336,6 +1343,13 @@ revoke0($node) $node->grantAdminOption = $1; $node->grantor = $8; } + | rev_grant_option usage_privilege(NOTRIAL(&$node->privileges)) ON PACKAGE symbol_package_name + FROM non_role_grantee_list(NOTRIAL(&$node->users)) granted_by + { + $node->object = newNode(obj_package_header, QualifiedName(*$5)); + $node->grantAdminOption = $1; + $node->grantor = $8; + } /*** | rev_grant_option usage_privilege(NOTRIAL(&$node->privileges)) ON DOMAIN symbol_domain_name FROM non_role_grantee_list(NOTRIAL(&$node->users)) granted_by @@ -4807,6 +4821,7 @@ keyword_or_column | WITHIN ; + col_opt : ALTER | ALTER COLUMN diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp index ef4e432e25e..afae4a40cad 100644 --- a/src/jrd/Package.epp +++ b/src/jrd/Package.epp @@ -387,7 +387,8 @@ ScanResult Package::reload(thread_db* tdbb, ObjectBase::Flag fl) FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) PKG IN RDB$PACKAGES CROSS CONST IN RDB$CONSTANTS WITH PKG.RDB$PACKAGE_ID EQ getId() AND - PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME + PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME AND + PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME { addConstant(tdbb, CONST.RDB$CONSTANT_ID, QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME), @@ -414,24 +415,43 @@ ScanResult Package::scan(thread_db* tdbb, ObjectBase::Flag flags) MemoryPool& pool = getPermanent()->getPool(); - static const CachedRequestId requestId; - AutoCacheRequest requestConst(tdbb, requestId); + { // Security + static const CachedRequestId requestId; + AutoCacheRequest requestConst(tdbb, requestId); - FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) - PKG IN RDB$PACKAGES CROSS CONST IN RDB$CONSTANTS - WITH PKG.RDB$PACKAGE_ID EQ getPermanent()->id AND - PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME - { - addConstant(tdbb, CONST.RDB$CONSTANT_ID, - QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME), - CONST.RDB$PRIVATE_FLAG, - CONST.RDB$CONSTANT_BLR, - skipMakeValue); + FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) + PKG IN RDB$PACKAGES CROSS SCH IN RDB$SCHEMAS + WITH PKG.RDB$SCHEMA_NAME EQ SCH.RDB$SCHEMA_NAME AND + PKG.RDB$PACKAGE_ID EQ getId() AND + PKG.RDB$SECURITY_CLASS NOT MISSING + { + getPermanent()->securityName.object = PKG.RDB$SECURITY_CLASS; + getPermanent()->securityName.schema = SCH.RDB$SECURITY_CLASS; + } + END_FOR + } + + { // Constants + static const CachedRequestId requestId; + AutoCacheRequest requestConst(tdbb, requestId); - if (skipMakeValue) - this->m_callReload = true; // Call makeValue in reload + FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) + PKG IN RDB$PACKAGES CROSS CONST IN RDB$CONSTANTS + WITH PKG.RDB$PACKAGE_ID EQ getId() AND + PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME AND + PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME + { + addConstant(tdbb, CONST.RDB$CONSTANT_ID, + QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME), + CONST.RDB$PRIVATE_FLAG, + CONST.RDB$CONSTANT_BLR, + skipMakeValue); + + if (skipMakeValue) + this->m_callReload = true; // Call makeValue in reload + } + END_FOR } - END_FOR return this->m_callReload ? ScanResult::REPEAT : ScanResult::COMPLETE; } diff --git a/src/jrd/Package.h b/src/jrd/Package.h index c4915602287..ecb76078602 100644 --- a/src/jrd/Package.h +++ b/src/jrd/Package.h @@ -136,8 +136,9 @@ class PackagePermanent : public Firebird::PermanentStorage bool hasData() const { return name.hasData(); } public: - MetaId id; // routine ID - QualifiedName name; // routine name + MetaId id; + QualifiedName name; + QualifiedName securityName; }; class Package final : public Firebird::PermanentStorage, public ObjectBase diff --git a/src/jrd/grant.epp b/src/jrd/grant.epp index a92f11ef3f3..1839b7bafdb 100644 --- a/src/jrd/grant.epp +++ b/src/jrd/grant.epp @@ -139,6 +139,7 @@ void GRANT_privileges(thread_db* tdbb, const QualifiedName& name, ObjectType id, case obj_charset: case obj_collation: case obj_schema: + case obj_package_constant: priv |= SCL_usage; break; From fff58ef407ccc08dc7140c96edb2028dee34dc2b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 2 Apr 2026 17:04:12 +0300 Subject: [PATCH 053/119] Add descriptions for constants added in `RDB$BLOB_UTIL` --- doc/sql.extensions/README.blob_util.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/sql.extensions/README.blob_util.md b/doc/sql.extensions/README.blob_util.md index a90fa3c4f02..4c3efb58893 100644 --- a/doc/sql.extensions/README.blob_util.md +++ b/doc/sql.extensions/README.blob_util.md @@ -56,7 +56,8 @@ Return type: `VARBINARY(32767)`. `RDB$BLOB_UTIL.SEEK` is used to set the position for the next `READ_DATA`. It returns the new position. -`MODE` may be 0 (from the start), 1 (from current position) or 2 (from end). +`MODE` may be 0 (from the start), 1 (from current position) or 2 (from end). The corresponding constants +are available in the package: `FROM_BEGIN`, `FROM_CURRENT`, `FROM_END`. When `MODE` is 2, `OFFSET` should be zero or negative. From 0284ee10e78a0f99294e56298202e87e0f8e4ead Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 2 Apr 2026 17:36:59 +0300 Subject: [PATCH 054/119] Correct nullable flag for new fields --- src/jrd/relations.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 4c5c205fb9c..48b2954faea 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -680,7 +680,7 @@ RELATION(nam_packages, rel_packages, ODS_12_0, rel_persistent) FIELD(f_pkg_desc, nam_description, fld_description, 1, ODS_12_0) FIELD(f_pkg_sql_security, nam_sql_security, fld_b_sql_security, 1, ODS_13_0) FIELD(f_pkg_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) - FIELD(f_pkg_id, nam_pkg_id, fld_pkg_id, 1, ODS_14_0) + FIELD(f_pkg_id, nam_pkg_id, fld_pkg_id, 0, ODS_14_0) END_RELATION // Relation 43 (SEC$USERS) @@ -850,6 +850,6 @@ RELATION(nam_constants, rel_constants, ODS_14_0, rel_persistent) FIELD(f_const_private_flag, nam_private_flag, fld_flag_nullable, 0, ODS_14_0) FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_14_0) FIELD(f_const_source, nam_const_source, fld_source, 1, ODS_14_0) - FIELD(f_const_package_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) + FIELD(f_const_package_schema, nam_sch_name, fld_sch_name, 0, ODS_14_0) FIELD(f_const_description, nam_description, fld_description, 1, ODS_14_0) END_RELATION From 84d0d0a50437a7b74d2f6a0ff65246213aa5041d Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 2 Apr 2026 17:37:17 +0300 Subject: [PATCH 055/119] Revert blank change --- src/dsql/parse.y | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 8318e0393e8..b37203db3e0 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -4821,7 +4821,6 @@ keyword_or_column | WITHIN ; - col_opt : ALTER | ALTER COLUMN From b85ee52c754eacc897b0c7b6f2ea6adabdcad46f Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 2 Apr 2026 17:37:53 +0300 Subject: [PATCH 056/119] Adjust comments for meta objects --- src/jrd/constants.h | 4 ++-- src/jrd/drq.h | 4 ++-- src/jrd/idx.h | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/jrd/constants.h b/src/jrd/constants.h index 01a60bcfef0..9a8b7336b0a 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -154,8 +154,8 @@ inline constexpr int GEN_SECCLASS_PREFIX_LEN = 4; inline constexpr const char* PROCEDURES_GENERATOR = "RDB$PROCEDURES"; inline constexpr const char* FUNCTIONS_GENERATOR = "RDB$FUNCTIONS"; -inline constexpr const char* const CONSTANTS_GENERATOR = "RDB$CONSTANTS"; -inline constexpr const char* const PACKAGES_GENERATOR = "RDB$PACKAGES"; +inline constexpr const char* CONSTANTS_GENERATOR = "RDB$CONSTANTS"; +inline constexpr const char* PACKAGES_GENERATOR = "RDB$PACKAGES"; // Automatically created check constraints for unnamed PRIMARY and UNIQUE declarations. inline constexpr const char* IMPLICIT_INTEGRITY_PREFIX = "INTEG_"; diff --git a/src/jrd/drq.h b/src/jrd/drq.h index 24d844a30ec..d474f8627b4 100644 --- a/src/jrd/drq.h +++ b/src/jrd/drq.h @@ -242,8 +242,8 @@ enum drq_type_t drq_e_pub_tab_all, // erase relation from all publication drq_l_rel_con, // lookup relation constraint drq_l_rel_fld_name, // lookup relation field name - drq_g_nxt_const_id, // lookup next constant ID - drq_g_nxt_package_id, // lookup next package ID + drq_g_nxt_const_id, // lookup next constant ID + drq_g_nxt_package_id, // lookup next package ID drq_MAX }; diff --git a/src/jrd/idx.h b/src/jrd/idx.h index 53deb0ac182..1db8b5c68fd 100644 --- a/src/jrd/idx.h +++ b/src/jrd/idx.h @@ -527,17 +527,17 @@ static inline constexpr struct ini_idx_t indices[] = }}, // define index RDB$INDEX_98 for RDB$CONSTANTS unique RDB$SCHEMA_NAME, RDB$PACKAGE_NAME, RDB$CONSTANT_NAME; INDEX(98, rel_constants, idx_unique, 3, ODS_14_0) - SEGMENT(f_const_package_schema, idx_metadata), // table schema name - SEGMENT(f_const_package, idx_metadata), // package name - SEGMENT(f_const_name, idx_metadata) // constant name + SEGMENT(f_const_package_schema, idx_metadata), // package schema name + SEGMENT(f_const_package, idx_metadata), // package name + SEGMENT(f_const_name, idx_metadata) // constant name }}, // define index RDB$INDEX_99 for RDB$CONSTANTS unique RDB$CONSTANT_ID; INDEX(99, rel_constants, idx_unique, 1, ODS_14_0) SEGMENT(f_const_id, idx_numeric) // constant id }}, - // define index RDB$INDEX_99 for RDB$CONSTANTS unique RDB$CONSTANT_ID; + // define index RDB$INDEX_99 for RDB$PACKAGES unique RDB$PACKAGE_ID; INDEX(100, rel_packages, idx_unique, 1, ODS_14_0) - SEGMENT(f_pkg_id, idx_numeric) // constant id + SEGMENT(f_pkg_id, idx_numeric) // constant id }} }; From 37c44383ed8a2117cec6de744152cf086f10dc54 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 2 Apr 2026 18:18:43 +0300 Subject: [PATCH 057/119] Minor corrections --- src/jrd/dfw.epp | 6 +++--- src/jrd/vio.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index e7f1f3dc52a..2b5d79ed2ed 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -409,7 +409,7 @@ static bool end_backup(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool check_not_null(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool store_view_context_type(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool user_management(thread_db*, SSHORT, DeferredWork*, jrd_tra*); -static bool create_package(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction); +static bool createPackage(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction); static bool drop_package_header(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool modify_package_header(thread_db*, SSHORT, DeferredWork*, jrd_tra*); static bool drop_package_body(thread_db*, SSHORT, DeferredWork*, jrd_tra*); @@ -1241,7 +1241,7 @@ static inline constexpr deferred_task task_table[] = { dfw_set_statistics, set_statistics }, { dfw_modify_package_constant, modifyConstant }, { dfw_delete_package_constant, deleteConstant }, - { dfw_create_package, create_package }, + { dfw_create_package, createPackage }, { dfw_null, NULL } }; @@ -2286,7 +2286,7 @@ static bool user_management(thread_db* /*tdbb*/, SSHORT phase, DeferredWork* wor return false; } -static bool create_package(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) +static bool createPackage(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { fb_assert(work->dfw_id != 0); diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 000276e88f4..f186d350b35 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -3452,7 +3452,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j { // Send dfw EVL_field(0, org_rpb->rpb_record, f_pkg_id, &desc2); const USHORT id = MOV_get_long(tdbb, &desc2, 0); - DFW_post_work(transaction, dfw_modify_package_header, &desc1, &schemaDesc, id, object_name.package); + DFW_post_work(transaction, dfw_modify_package_header, &desc1, &schemaDesc, id); } } } @@ -4400,7 +4400,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) object_id = set_metadata_id(tdbb, rpb->rpb_record, f_pkg_id, drq_g_nxt_package_id, PACKAGES_GENERATOR); - work = DFW_post_work(transaction, dfw_create_package, &desc, &schemaDesc, object_id, object_name.package); + DFW_post_work(transaction, dfw_create_package, &desc, &schemaDesc, object_id); break; case rel_procedures: From d3a0b9db119e14427e4063fa3fc3dca7425dd5ca Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Thu, 2 Apr 2026 18:25:26 +0300 Subject: [PATCH 058/119] Do not backup system constants --- src/burp/backup.epp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 4a36df8eb69..3960bd94202 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -4861,6 +4861,7 @@ void write_constants() FOR (REQUEST_HANDLE req_handle1) CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME NE SYSTEM_SCHEMA { QualifiedMetaString name(CONST.RDB$CONSTANT_NAME); From 7b3ef7854e07681d4818bd2f9b1e60e4f037e4de Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 3 Apr 2026 10:30:19 +0300 Subject: [PATCH 059/119] Use qualifyNewName to search for SCHEMAs of constant --- src/dsql/DdlNodes.epp | 3 +-- src/dsql/ExprNodes.cpp | 27 ++++++++++++++++----------- src/dsql/PackageNodes.epp | 14 ++++++-------- src/dsql/PackageNodes.h | 3 ++- src/jrd/Package.epp | 7 ++----- src/jrd/Package.h | 4 ++-- 6 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 1b01e6da8b8..75f7759637e 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -1500,8 +1500,7 @@ DdlNode* CommentOnNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) case obj_package_constant: { QualifiedName constantName(subName, name.schema, name.object); // name is a package - if (constantName.schema.isEmpty()) - constantName.schema = PUBLIC_SCHEMA; + dsqlScratch->qualifyNewName(constantName); // Search for a schema if (!PackageReferenceNode::constantExists(tdbb, transaction, constantName)) { diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 0b40e024e3a..4dc26d20d99 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -6471,21 +6471,26 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec dsql_ctx packageContext(dsqlScratch->getPool()); { // Consatnts - const QualifiedName constantName(dsqlName, - dsqlQualifier.schema.hasData() ? dsqlQualifier.schema : (dsqlScratch->package.schema.hasData() ? dsqlScratch->package.schema : PUBLIC_SCHEMA), + QualifiedName constantName(dsqlName, + dsqlQualifier.schema.hasData() ? dsqlQualifier.schema : dsqlScratch->package.schema, dsqlQualifier.object.hasData() ? dsqlQualifier.object : dsqlScratch->package.object); - if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), constantName)) + if (constantName.package.hasData()) { - packageContext.ctx_relation = nullptr; - packageContext.ctx_procedure = nullptr; - // Alias is a package name, not a constant - packageContext.ctx_alias.push(QualifiedName(constantName.package, constantName.schema)); - packageContext.ctx_flags |= CTX_package; - ambiguousCtxStack.push(&packageContext); + dsqlScratch->qualifyNewName(constantName); - MemoryPool& pool = dsqlScratch->getPool(); - node = FB_NEW_POOL(pool) PackageReferenceNode(pool, constantName); + if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), constantName)) + { + packageContext.ctx_relation = nullptr; + packageContext.ctx_procedure = nullptr; + // Alias is a package name, not a constant + packageContext.ctx_alias.push(QualifiedName(constantName.package, constantName.schema)); + packageContext.ctx_flags |= CTX_package; + ambiguousCtxStack.push(&packageContext); + + MemoryPool& pool = dsqlScratch->getPool(); + node = FB_NEW_POOL(pool) PackageReferenceNode(pool, constantName); + } } } diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 1346382c643..3dea95b3e8a 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -465,20 +465,18 @@ void PackageReferenceNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) } bool PackageReferenceNode::constantExists(thread_db* tdbb, Jrd::jrd_tra* transaction, - QualifiedName fullName, bool* isPrivate) + const QualifiedName& fullName, bool* isPrivate) { + fb_assert(fullName.schema.hasData()); + if (fullName.package.isEmpty() || fullName.object.isEmpty()) return false; - // Use default schema if one not specified - if (fullName.schema.isEmpty()) - fullName.schema = PUBLIC_SCHEMA; - auto* package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE); if (package == nullptr) return false; - ConstantValue* value = package->findConstant(tdbb, fullName); + ConstantValue* value = package->findConstant(fullName); if (value == nullptr) return false; @@ -547,7 +545,7 @@ dsc* PackageReferenceNode::execute(thread_db* tdbb, Request* request) const Package* package = m_package(request->getResources()); package->checkReload(tdbb); - EVL_make_value(tdbb, &package->findConstant(tdbb, m_fullName)->makeValue(tdbb), outputImpure); + EVL_make_value(tdbb, &package->findConstant(m_fullName)->makeValue(tdbb), outputImpure); return &outputImpure->vlu_desc; } default: @@ -622,7 +620,7 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat // Check uniqueness fb_assert(package != nullptr); - if (package->findConstant(tdbb, name)) + if (package->findConstant(name)) { status_exception::raise(Arg::Gds(isc_dyn_dup_const) << name.toQuotedString()); } diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 17ae099baf3..2f34e9c4367 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -132,8 +132,9 @@ class PackageReferenceNode final : public TypedNode Date: Fri, 3 Apr 2026 10:52:50 +0300 Subject: [PATCH 060/119] Store constant BLR using storeBinaryBlob --- src/dsql/PackageNodes.epp | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 3dea95b3e8a..1c50d9d990b 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -613,10 +613,7 @@ void CreatePackageConstantNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { - FbLocalStatus status; - - static const CachedRequestId requestId; - AutoCacheRequest storeConstantRequest(tdbb, requestId); + Attachment* attachment = transaction->getAttachment(); // Check uniqueness fb_assert(package != nullptr); @@ -634,6 +631,9 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat fb_assert(fieldName.schema == name.schema); fb_assert(fieldName.package == name.package); + static const CachedRequestId requestId; + AutoCacheRequest storeConstantRequest(tdbb, requestId); + int faults = 0; while (true) { @@ -665,9 +665,7 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); // Put the blr - blb* blob = blb::create(tdbb, transaction, &CONST.RDB$CONSTANT_BLR); - blob->BLB_put_segment(tdbb, dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount()); - blob->BLB_close(tdbb); + attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData()); // Parent package fb_assert(name.package.hasData()); @@ -708,14 +706,13 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { - FbLocalStatus status; + Attachment* attachment = transaction->getAttachment(); static const CachedRequestId eraseRequestId; AutoCacheRequest eraseDscRequest(tdbb, eraseRequestId); static const CachedRequestId modifyRequestId; AutoCacheRequest modifyConstantRequest(tdbb, modifyRequestId); - bid blobId; bool found = false; @@ -834,10 +831,10 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc // Gen the new constant value as blr ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); - // And write - blb* blob = blb::create(tdbb, transaction, &blobId); - blob->BLB_put_segment(tdbb, dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount()); - blob->BLB_close(tdbb); + MODIFY CONST USING + attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData()); + CONST.RDB$CONSTANT_BLR.NULL = FALSE; + END_MODIFY { fb_assert(package); @@ -845,11 +842,6 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc package->addConstant(tdbb, m_id, name, m_isPrivate, m_type); } - MODIFY CONST USING - CONST.RDB$CONSTANT_BLR.NULL = FALSE; - CONST.RDB$CONSTANT_BLR = blobId; - END_MODIFY - found = true; } END_FOR From 5a4b65751fb202eb5871c1518d40176b173f230b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 3 Apr 2026 11:14:10 +0300 Subject: [PATCH 061/119] Reorder methods in package class to match definition order --- src/jrd/Package.epp | 70 ++++++++++++++++++++++++--------------------- src/jrd/Package.h | 1 - 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp index e460ea0a497..b7d63a7ffd9 100644 --- a/src/jrd/Package.epp +++ b/src/jrd/Package.epp @@ -349,6 +349,15 @@ ConstantValue& ConstantsCache::add(const MetaId constId, const QualifiedName& co } +// ---------------------------- +// Package class Implementation +// ---------------------------- + +Package* Package::create(thread_db* tdbb, MemoryPool& pool, Cached::Package* perm) +{ + return FB_NEW_POOL(perm->getPool()) Package(perm); +} + std::optional Package::getIdByName(thread_db* tdbb, const QualifiedName& name) { fb_assert(name.package.isEmpty()); @@ -372,39 +381,6 @@ std::optional Package::getIdByName(thread_db* tdbb, const QualifiedName& return id; } -ScanResult Package::reload(thread_db* tdbb, ObjectBase::Flag fl) -{ - Attachment* attachment = tdbb->getAttachment(); - jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); - Database* dbb = tdbb->getDatabase(); - - MemoryPool& pool = getPermanent()->getPool(); - - static const CachedRequestId requestId; - AutoCacheRequest requestConst(tdbb, requestId); - - constants.clear(); - FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) - PKG IN RDB$PACKAGES CROSS CONST IN RDB$CONSTANTS - WITH PKG.RDB$PACKAGE_ID EQ getId() AND - PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME AND - PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME - { - addConstant(tdbb, CONST.RDB$CONSTANT_ID, - QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME), - CONST.RDB$PRIVATE_FLAG, - CONST.RDB$CONSTANT_BLR); - } - END_FOR - - return ScanResult::COMPLETE; -} - -Package* Package::create(thread_db* tdbb, MemoryPool& pool, Cached::Package* perm) -{ - return FB_NEW_POOL(perm->getPool()) Package(perm); -} - ScanResult Package::scan(thread_db* tdbb, ObjectBase::Flag flags) { const bool skipMakeValue = (flags & CacheFlag::MINISCAN) != 0; @@ -465,6 +441,34 @@ void Package::checkReload(thread_db* tdbb) } } +ScanResult Package::reload(thread_db* tdbb, ObjectBase::Flag fl) +{ + Attachment* attachment = tdbb->getAttachment(); + jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); + Database* dbb = tdbb->getDatabase(); + + MemoryPool& pool = getPermanent()->getPool(); + + static const CachedRequestId requestId; + AutoCacheRequest requestConst(tdbb, requestId); + + constants.clear(); + FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) + PKG IN RDB$PACKAGES CROSS CONST IN RDB$CONSTANTS + WITH PKG.RDB$PACKAGE_ID EQ getId() AND + PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME AND + PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME + { + addConstant(tdbb, CONST.RDB$CONSTANT_ID, + QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME), + CONST.RDB$PRIVATE_FLAG, + CONST.RDB$CONSTANT_BLR); + } + END_FOR + + return ScanResult::COMPLETE; +} + int Package::objectType() { return obj_package_header; diff --git a/src/jrd/Package.h b/src/jrd/Package.h index 73632c7598d..2a6b89c215b 100644 --- a/src/jrd/Package.h +++ b/src/jrd/Package.h @@ -64,7 +64,6 @@ class ConstantValue final : public Firebird::PermanentStorage bool hash(thread_db* tdbb, Firebird::sha512& digest) const; static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name); - static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const MetaId id); static void genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema); From bafdb3711cf03e48e335efe653ac0699a19f3f2d Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 3 Apr 2026 11:38:29 +0300 Subject: [PATCH 062/119] Fix adding existing constant to cache again on alter --- src/dsql/PackageNodes.epp | 2 +- src/jrd/Package.epp | 24 ++++++++++++++++++++++++ src/jrd/Package.h | 4 ++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 1c50d9d990b..94f8ecc6b0e 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -839,7 +839,7 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc { fb_assert(package); // Do not cache blob id because the blob is not materialized - package->addConstant(tdbb, m_id, name, m_isPrivate, m_type); + package->updateConstant(tdbb, m_id, m_isPrivate, m_type); } found = true; diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp index b7d63a7ffd9..d18387969b3 100644 --- a/src/jrd/Package.epp +++ b/src/jrd/Package.epp @@ -335,6 +335,9 @@ dsc& ConstantValue::makeValue(thread_db* tdbb) ConstantValue& ConstantsCache::add(const MetaId constId, const QualifiedName& constName, const bool isPrivate) { + fb_assert(!idMap.exist(constId)); + fb_assert(!nameMap.exist(constName)); + const ULONG id = values.getCount(); auto& value = values.add(); @@ -511,6 +514,27 @@ ConstantValue& Package::addConstant(thread_db* tdbb, const MetaId constId, return value; } +ConstantValue& Package::updateConstant(thread_db* tdbb, const MetaId constId, + const bool isPrivate, + const TypeClause* type) +{ + dsc typeDesc; + DsqlDescMaker::fromField(&typeDesc, type); + + auto* innerId = constants.idMap.get(constId); + fb_assert(innerId != nullptr); + + auto& constant = constants.values[*innerId]; + constant.blrBlobId = {}; + constant.isPrivate = isPrivate; + + delete constant.value.vlu_string; + constant.value = {}; + constant.value.vlu_desc = typeDesc; + + return constant; +} + ConstantValue* Package::findConstant(const MetaId id) { auto it = constants.idMap.get(id); diff --git a/src/jrd/Package.h b/src/jrd/Package.h index 2a6b89c215b..375f48368e8 100644 --- a/src/jrd/Package.h +++ b/src/jrd/Package.h @@ -216,6 +216,10 @@ class Package final : public Firebird::PermanentStorage, public ObjectBase const bid blrBlobId, const bool skipMakeValue = false); + ConstantValue& updateConstant(thread_db* tdbb, const MetaId constId, + const bool isPrivate, + const TypeClause* type); + ConstantValue* findConstant(const MetaId id); ConstantValue* findConstant(const QualifiedName& name); From e9445e38b9dc1e90e72844b40bd593de0287156e Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 3 Apr 2026 12:14:18 +0300 Subject: [PATCH 063/119] Fix bug where cached::package lock was not released after Restore --- src/jrd/vio.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index f186d350b35..8abbe98214d 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -4400,6 +4400,13 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) object_id = set_metadata_id(tdbb, rpb->rpb_record, f_pkg_id, drq_g_nxt_package_id, PACKAGES_GENERATOR); + // This getVersioned call is necessary for to set the lock during restore + // The lock itself is created by calling `pingLock`, but the `locked` variable is not set to true + // This resulted in the lock not being released during the cleaning stage + // So set the lock by calling `getVersioned` function + if (tdbb->getAttachment()->isGbak()) + MetadataCache::getVersioned(tdbb, object_id, CacheFlag::AUTOCREATE); + DFW_post_work(transaction, dfw_create_package, &desc, &schemaDesc, object_id); break; From a1488be6e6466f0d0356d2bcbb890ddef0e2343d Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 3 Apr 2026 12:15:32 +0300 Subject: [PATCH 064/119] Populate cache for package at after insert trigger only during restore --- src/jrd/SystemTriggers.epp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jrd/SystemTriggers.epp b/src/jrd/SystemTriggers.epp index fc57d817a61..4d71ce25236 100644 --- a/src/jrd/SystemTriggers.epp +++ b/src/jrd/SystemTriggers.epp @@ -1807,7 +1807,8 @@ void SystemTriggers::executeAfterInsertTriggers(thread_db* tdbb, jrd_rel* relati break; case rel_packages: - populateCache(tdbb, record); + if (tdbb->getAttachment()->isGbak()) + populateCache(tdbb, record); break; } } From 9edf966af10190d32882ee45885f3ed2e6a64c0e Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 3 Apr 2026 12:47:23 +0300 Subject: [PATCH 065/119] Mark package field as nullable in RDB$CONSTANTS table in case of global constants --- src/jrd/relations.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 48b2954faea..39241215760 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -845,7 +845,7 @@ END_RELATION RELATION(nam_constants, rel_constants, ODS_14_0, 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, 0, ODS_14_0) + FIELD(f_const_package, nam_pkg_name, fld_pkg_name, 1, ODS_14_0) FIELD(f_const_field, nam_f_source, fld_f_name, 0, ODS_14_0) FIELD(f_const_private_flag, nam_private_flag, fld_flag_nullable, 0, ODS_14_0) FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_14_0) From 10140bb1e690cc6ce972774dec85c1032ed121c2 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 3 Apr 2026 12:49:10 +0300 Subject: [PATCH 066/119] Final cleanup: Correct comments, remove PACKAGE_NAME.NULL checks, add grant/revoke for constants in README --- doc/sql.extensions/README.packages.txt | 4 ++++ src/burp/backup.epp | 7 ++----- src/dsql/Nodes.h | 2 +- src/dsql/PackageNodes.epp | 16 +++++++--------- src/dsql/PackageNodes.h | 9 +++------ src/include/firebird/impl/blr.h | 2 +- src/isql/show.epp | 11 +++-------- src/jrd/dfw.epp | 1 - src/jrd/obj.h | 2 -- src/jrd/vio.cpp | 4 ++-- 10 files changed, 23 insertions(+), 35 deletions(-) diff --git a/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt index c3433557a6c..2d36a5c8f60 100644 --- a/doc/sql.extensions/README.packages.txt +++ b/doc/sql.extensions/README.packages.txt @@ -119,6 +119,10 @@ Objectives: GRANT SELECT ON TABLE secret TO PACKAGE pk_secret; GRANT EXECUTE ON PACKAGE pk_secret TO ROLE role_secret; + To use package constants in a select expression, a USAGE permission is requeued: + GRANT USAGE ON PACKAGE to [] [] []; + REVOKE USAGE ON PACKAGE FROM []; + - Introduce private scope to routines making them available only for internal usage in the defining package. diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 3960bd94202..25db9caf086 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -4868,11 +4868,8 @@ void write_constants() put(tdgbl, rec_constants); PUT_TEXT(att_constant_name, CONST.RDB$CONSTANT_NAME); - if (!CONST.RDB$PACKAGE_NAME.NULL) - { - PUT_TEXT(att_constant_package, CONST.RDB$PACKAGE_NAME); - name.package = CONST.RDB$PACKAGE_NAME; - } + PUT_TEXT(att_constant_package, CONST.RDB$PACKAGE_NAME); + name.package = CONST.RDB$PACKAGE_NAME; PUT_TEXT(att_constant_field_source, CONST.RDB$FIELD_SOURCE); diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index ab9c6d8bb83..1be1dc97ad8 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -691,7 +691,7 @@ class ExprNode : public DmlNode // Check if expression returns deterministic result // Determinate whether the node is volatile (or not) in the current execution context. - // A DBKEY is deterministic (it cannot change for an already fetched row) + // For example, a DBKEY is deterministic (it cannot change for an already fetched row) // but it's not constant (and thus cannot be used as an initializer expression). virtual bool deterministic(thread_db* tdbb) const; diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 94f8ecc6b0e..610d09dedcb 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -378,7 +378,7 @@ ValueExprNode* PackageReferenceNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) bool isPrivate = false; if (constantExists(tdbb, transaction, m_fullName, &isPrivate)) { - // External objects do not have access to private constants. + // External objects do not have access to private constants if (isPrivate) { status_exception::raise(Arg::Gds(isc_private_constant) << @@ -402,7 +402,7 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler switch (itemType) { - case blr_pkg_ref_item_const: + case blr_pkg_reference_to_constant: { QualifiedName fullName; csb->csb_blr_reader.getMetaName(fullName.object); @@ -446,7 +446,7 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler void PackageReferenceNode::genBlr(DsqlCompilerScratch* dsqlScratch) { dsqlScratch->appendUChar(blr_package_reference); - dsqlScratch->appendUChar(blr_pkg_ref_item_const); + dsqlScratch->appendUChar(blr_pkg_reference_to_constant); dsqlScratch->appendMetaString(m_fullName.object.c_str()); dsqlScratch->appendMetaString(m_fullName.schema.c_str()); dsqlScratch->appendMetaString(m_fullName.package.c_str()); @@ -512,7 +512,7 @@ ValueExprNode* PackageReferenceNode::pass1(thread_db* tdbb, CompilerScratch* csb switch (m_itemType) { - case blr_pkg_ref_item_const: + case blr_pkg_reference_to_constant: { CMP_post_access(tdbb, csb, security.object, 0, SCL_usage, obj_packages, m_fullName.getSchemaAndPackage()); break; @@ -529,7 +529,7 @@ ValueExprNode* PackageReferenceNode::pass2(thread_db* tdbb, CompilerScratch* csb { ValueExprNode::pass2(tdbb, csb); - if (m_itemType == blr_pkg_ref_item_const) + if (m_itemType == blr_pkg_reference_to_constant) m_impureOffset = csb->allocImpure(); return this; @@ -540,7 +540,7 @@ dsc* PackageReferenceNode::execute(thread_db* tdbb, Request* request) const impure_value* outputImpure = request->getImpure(m_impureOffset); switch (m_itemType) { - case blr_pkg_ref_item_const: + case blr_pkg_reference_to_constant: { Package* package = m_package(request->getResources()); package->checkReload(tdbb); @@ -590,9 +590,7 @@ DdlNode* CreatePackageConstantNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) } void CreatePackageConstantNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) -{ - // SCL_check_create_access(tdbb, obj_package_constant); -} +{ } void CreatePackageConstantNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 2f34e9c4367..f4a918a0f26 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -82,7 +82,7 @@ class ItemNames : public TArray "CONSTANT", }; - // Print just the object name because the full path is present n the parent error message + // Print just the object name because the full path is present in the parent error message Firebird::status_exception::raise( Firebird::Arg::Gds(isc_no_meta_update) << Firebird::Arg::Gds(isc_dyn_duplicate_package_item) << @@ -121,7 +121,7 @@ class PackageReferenceNode final : public TypedNode, MetaName>; public: diff --git a/src/include/firebird/impl/blr.h b/src/include/firebird/impl/blr.h index a4985165c6f..84c351fe4f9 100644 --- a/src/include/firebird/impl/blr.h +++ b/src/include/firebird/impl/blr.h @@ -536,6 +536,6 @@ #define blr_package_reference (unsigned char) 236 // Subcodes of blr_package_reference -#define blr_pkg_ref_item_const (unsigned char) 1 +#define blr_pkg_reference_to_constant (unsigned char) 1 #endif // FIREBIRD_IMPL_BLR_H diff --git a/src/isql/show.epp b/src/isql/show.epp index 63ab6640bc6..6ecab644e50 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -6870,15 +6870,10 @@ static processing_state show_constant(const SCHAR* constantName) WITH CONST.RDB$CONSTANT_NAME EQ constantName SORTED BY CONST.RDB$CONSTANT_NAME { - bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + const bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; - if (!CONST.RDB$PACKAGE_NAME.NULL) - { - MetaString packageName(CONST.RDB$PACKAGE_NAME); - isqlGlob.printf("%s (%s %s)%-20s", constantName, packageName.c_str(), isPrivate ? "BODY" : "HEADER", " "); - } - else - isqlGlob.printf("%-20s", constantName); + MetaString packageName(CONST.RDB$PACKAGE_NAME); + isqlGlob.printf("%s (%s %s)%-20s", constantName, packageName.c_str(), isPrivate ? "BODY" : "HEADER", " "); fb_utils::exact_name(CONST.RDB$FIELD_SOURCE); fb_utils::exact_name(CONST.RDB$SCHEMA_NAME); diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 2b5d79ed2ed..cc9ad05197f 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1160,7 +1160,6 @@ namespace fb_assert(!work->dfw_package.isEmpty()); SET_TDBB(tdbb); - Cached::Package* routine; switch (phase) { diff --git a/src/jrd/obj.h b/src/jrd/obj.h index b2374d6cc82..9c351fef616 100644 --- a/src/jrd/obj.h +++ b/src/jrd/obj.h @@ -188,8 +188,6 @@ inline constexpr const char* getDdlSecurityName(ObjectType object_type) noexcept return "SQL$TABLESPACES"; case obj_schemas: return "SQL$SCHEMAS"; - case obj_package_constant: - return "SQL$PACKAGE_CONSTANT"; default: return ""; } diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 8abbe98214d..b213b010978 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -2356,8 +2356,8 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_const_name, &desc); EVL_field(0, rpb->rpb_record, f_const_package_schema, &schemaDesc); - if (EVL_field(0, rpb->rpb_record, f_const_package, &desc2)) - MOV_get_metaname(tdbb, &desc2, object_name.package); + EVL_field(0, rpb->rpb_record, f_const_package, &desc2); + MOV_get_metaname(tdbb, &desc2, object_name.package); EVL_field(0, rpb->rpb_record, f_const_id, &desc2); id = MOV_get_long(tdbb, &desc2, 0); From dba49c13afae07251fff4b2ccb87e33585d7b676 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 3 Apr 2026 18:32:24 +0300 Subject: [PATCH 067/119] Minor corrections --- doc/sql.extensions/README.packages.txt | 4 ++-- src/jrd/idx.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt index 2d36a5c8f60..dafe0356614 100644 --- a/doc/sql.extensions/README.packages.txt +++ b/doc/sql.extensions/README.packages.txt @@ -120,8 +120,8 @@ Objectives: GRANT EXECUTE ON PACKAGE pk_secret TO ROLE role_secret; To use package constants in a select expression, a USAGE permission is requeued: - GRANT USAGE ON PACKAGE to [] [] []; - REVOKE USAGE ON PACKAGE FROM []; + GRANT USAGE ON PACKAGE [.] to [] [] []; + REVOKE USAGE ON PACKAGE [.] FROM []; - Introduce private scope to routines making them available only for internal usage in the defining package. diff --git a/src/jrd/idx.h b/src/jrd/idx.h index 1db8b5c68fd..4ce7dfabf0b 100644 --- a/src/jrd/idx.h +++ b/src/jrd/idx.h @@ -535,7 +535,7 @@ static inline constexpr struct ini_idx_t indices[] = INDEX(99, rel_constants, idx_unique, 1, ODS_14_0) SEGMENT(f_const_id, idx_numeric) // constant id }}, - // define index RDB$INDEX_99 for RDB$PACKAGES unique RDB$PACKAGE_ID; + // define index RDB$INDEX_100 for RDB$PACKAGES unique RDB$PACKAGE_ID; INDEX(100, rel_packages, idx_unique, 1, ODS_14_0) SEGMENT(f_pkg_id, idx_numeric) // constant id }} From 1dcc3df0c7948686d835b8f200e47f7ed9cd4c3a Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Fri, 3 Apr 2026 18:48:36 +0300 Subject: [PATCH 068/119] Fix missing error when showing non existing constant(s) --- src/include/firebird/impl/msg/isql.h | 2 + src/isql/show.epp | 103 ++++++++++++++------------- 2 files changed, 56 insertions(+), 49 deletions(-) diff --git a/src/include/firebird/impl/msg/isql.h b/src/include/firebird/impl/msg/isql.h index fc4a9619737..f10508fc47b 100644 --- a/src/include/firebird/impl/msg/isql.h +++ b/src/include/firebird/impl/msg/isql.h @@ -208,3 +208,5 @@ FB_IMPL_MSG_SYMBOL(ISQL, 208, HLP_SETAUTOTERM, " SET AUTOTERM -- to FB_IMPL_MSG_SYMBOL(ISQL, 209, HLP_SETWIRESTATS, " SET WIRE_stats -- toggle display of wire (network) statistics") FB_IMPL_MSG_SYMBOL(ISQL, 210, USAGE_SEARCH_PATH, " -(se)arch_path set schema search path") FB_IMPL_MSG_SYMBOL(ISQL, 211, MSG_SCHEMAS, "Schemas:") +FB_IMPL_MSG_SYMBOL(ISQL, 212, NO_CONSTANTS, "There are no constants in this database") +FB_IMPL_MSG_SYMBOL(ISQL, 213, NO_CONSTANT, "There are no constant @1 in this database") diff --git a/src/isql/show.epp b/src/isql/show.epp index 6ecab644e50..1763d620dac 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -119,7 +119,6 @@ static processing_state show_users(); static processing_state show_users12(); static processing_state show_wireStats(); static processing_state show_constants(const std::optional& name, const char* msg = nullptr); -static processing_state show_constant(const SCHAR* constantName); const char* const spaces = " "; static TEXT Print_buffer[512]; @@ -2573,9 +2572,21 @@ processing_state SHOW_metadata(const FrontendParser::AnyShowNode& node) return show_wireStats(); }, - [](const FrontendParser::ShowConstantsNode& node) + [&](const FrontendParser::ShowConstantsNode& node) { - return show_constants(node.name); + const processing_state ret = show_constants(node.name); + if (ret == OBJECT_NOT_FOUND) + { + if (node.name.has_value()) + { + key = NO_CONSTANT; + notFoundName = node.name.value(); + } + else + key = NO_CONSTANTS; + } + + return ret; }, [](auto& arg) @@ -6761,7 +6772,7 @@ static void print_constant_type(const char* fieldName, const char* schemaName) if (!fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) || FLD.RDB$SYSTEM_FLAG == 1) { fb_utils::exact_name(FLD.RDB$FIELD_NAME); - isqlGlob.printf("%s) ", FLD.RDB$FIELD_NAME); + isqlGlob.printf("(%s) ", FLD.RDB$FIELD_NAME); } const QualifiedMetaString domainName(FLD.RDB$FIELD_NAME, FLD.RDB$SCHEMA_NAME); @@ -6813,6 +6824,8 @@ static processing_state show_constants(const std::optional& if (name.has_value()) { + // Seach for a constant with name + const QualifiedMetaString& constant = name.value(); FOR CONST IN RDB$CONSTANTS WITH @@ -6821,72 +6834,64 @@ static processing_state show_constants(const std::optional& CONST.RDB$CONSTANT_NAME EQ constant.object.c_str() SORTED BY CONST.RDB$CONSTANT_NAME { - bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + const bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; const char* type = isPrivate ? "BODY" : "HEADER"; if (first && msg) isqlGlob.printf("%s%s", msg, NEWLINE); first = false; - show_constant(constant.object.c_str()); + + fb_utils::exact_name(CONST.RDB$CONSTANT_NAME); + fb_utils::exact_name(CONST.RDB$PACKAGE_NAME); + isqlGlob.printf("%s (%s %s)%-20s", CONST.RDB$CONSTANT_NAME, CONST.RDB$PACKAGE_NAME, type, " "); + + fb_utils::exact_name(CONST.RDB$FIELD_SOURCE); + fb_utils::exact_name(CONST.RDB$SCHEMA_NAME); + print_constant_type(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME); + + isqlGlob.printf(NEWLINE); } END_FOR ON_ERROR ISQL_errmsg(fbStatus); return ps_ERR; END_ERROR; - - return SKIP; } - - // All constants - - FOR CONST IN RDB$CONSTANTS - SORTED BY CONST.RDB$CONSTANT_NAME - bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + else { - const char* type = isPrivate ? "BODY" : "HEADER"; - - if (first && msg) - isqlGlob.printf("%s%s", msg, NEWLINE); + // All constants - first = false; - fb_utils::exact_name(CONST.RDB$CONSTANT_NAME); - show_constant(CONST.RDB$CONSTANT_NAME); - - } - END_FOR - ON_ERROR - ISQL_errmsg(fbStatus); - return ps_ERR; - END_ERROR; - - return SKIP; -} + FOR CONST IN RDB$CONSTANTS + SORTED BY CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME, CONST.RDB$CONSTANT_NAME + { + const bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + const char* type = isPrivate ? "BODY" : "HEADER"; -static processing_state show_constant(const SCHAR* constantName) -{ - FOR CONST IN RDB$CONSTANTS - WITH CONST.RDB$CONSTANT_NAME EQ constantName - SORTED BY CONST.RDB$CONSTANT_NAME - { - const bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + if (first && msg) + isqlGlob.printf("%s%s", msg, NEWLINE); - MetaString packageName(CONST.RDB$PACKAGE_NAME); - isqlGlob.printf("%s (%s %s)%-20s", constantName, packageName.c_str(), isPrivate ? "BODY" : "HEADER", " "); + first = false; - fb_utils::exact_name(CONST.RDB$FIELD_SOURCE); - fb_utils::exact_name(CONST.RDB$SCHEMA_NAME); - print_constant_type(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME); + fb_utils::exact_name(CONST.RDB$CONSTANT_NAME); + fb_utils::exact_name(CONST.RDB$PACKAGE_NAME); + isqlGlob.printf("%s (%s %s)%-20s", CONST.RDB$CONSTANT_NAME, CONST.RDB$PACKAGE_NAME, type, " "); - isqlGlob.printf(NEWLINE); + fb_utils::exact_name(CONST.RDB$FIELD_SOURCE); + fb_utils::exact_name(CONST.RDB$SCHEMA_NAME); + print_constant_type(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME); + isqlGlob.printf(NEWLINE); + } + END_FOR + ON_ERROR + ISQL_errmsg(fbStatus); + return ps_ERR; + END_ERROR; } - END_FOR - ON_ERROR - ISQL_errmsg(fbStatus); - return ps_ERR; - END_ERROR; + + if (first) + return OBJECT_NOT_FOUND; return SKIP; } From 4ddaad4f87b1f3d0cc6f99a9b7eb356772aad63e Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 6 Apr 2026 09:53:52 +0300 Subject: [PATCH 069/119] Add missing gpre call for Package.epp on windows and update vcxproj.filters --- builds/win32/msvc15/engine_static.vcxproj | 2 +- builds/win32/msvc15/engine_static.vcxproj.filters | 5 ++++- builds/win32/preprocess.bat | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/builds/win32/msvc15/engine_static.vcxproj b/builds/win32/msvc15/engine_static.vcxproj index 0d1222eb56f..3bd5bfff341 100644 --- a/builds/win32/msvc15/engine_static.vcxproj +++ b/builds/win32/msvc15/engine_static.vcxproj @@ -417,8 +417,8 @@ - + diff --git a/builds/win32/msvc15/engine_static.vcxproj.filters b/builds/win32/msvc15/engine_static.vcxproj.filters index c5e0ec0fcee..887599cb878 100644 --- a/builds/win32/msvc15/engine_static.vcxproj.filters +++ b/builds/win32/msvc15/engine_static.vcxproj.filters @@ -1199,6 +1199,9 @@ JRD files\GPRE files + + JRD files\GPRE files + Services @@ -1206,4 +1209,4 @@ DSQL - \ No newline at end of file + diff --git a/builds/win32/preprocess.bat b/builds/win32/preprocess.bat index 049959f8ac4..59b712b4753 100644 --- a/builds/win32/preprocess.bat +++ b/builds/win32/preprocess.bat @@ -78,7 +78,7 @@ goto :EOF @for %%i in (metd) do @call :PREPROCESS dsql %%i -gds_cxx @for %%i in (DdlNodes, PackageNodes) do @call :PREPROCESS dsql %%i -gds_cxx @for %%i in (gpre_meta) do @call :PREPROCESS gpre/std %%i -@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function, SystemTriggers) do @call :PREPROCESS jrd %%i -gds_cxx +@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function, SystemTriggers, Package) do @call :PREPROCESS jrd %%i -gds_cxx @for %%i in (extract, isql, show) do @call :PREPROCESS isql %%i -ocxx @for %%i in (dba) do @call :PREPROCESS utilities/gstat %%i @for %%i in (stats) do @call :PREPROCESS utilities %%i From 8149e0944d0efa09a99acc34b45410a010e80b31 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 6 Apr 2026 10:42:08 +0300 Subject: [PATCH 070/119] Add missing gpre call for Package.epp at boot state --- builds/win32/preprocess.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builds/win32/preprocess.bat b/builds/win32/preprocess.bat index 59b712b4753..8d223f78d02 100644 --- a/builds/win32/preprocess.bat +++ b/builds/win32/preprocess.bat @@ -65,7 +65,7 @@ goto :EOF @for %%i in (alice_meta) do @call :PREPROCESS alice %%i @for %%i in (metd, DdlNodes, PackageNodes) do @call :PREPROCESS dsql %%i -gds_cxx @for %%i in (gpre_meta) do @call :PREPROCESS gpre/std %%i -@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function, SystemTriggers) do @call :PREPROCESS jrd %%i -gds_cxx +@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function, SystemTriggers, Package) do @call :PREPROCESS jrd %%i -gds_cxx @for %%i in (stats) do @call :PREPROCESS utilities %%i @goto :EOF From a195eb7b9ca6755d652506c0f995e5a087d39906 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 7 Apr 2026 10:48:32 +0300 Subject: [PATCH 071/119] Fix resolving package and schema for constant name in SHOW command --- src/isql/FrontendParser.cpp | 18 ++++++++++++++++-- src/isql/show.epp | 5 ++++- src/isql/tests/FrontendParserTest.cpp | 6 ++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/isql/FrontendParser.cpp b/src/isql/FrontendParser.cpp index f922d3fef55..7c6b4052df4 100644 --- a/src/isql/FrontendParser.cpp +++ b/src/isql/FrontendParser.cpp @@ -650,14 +650,28 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() } else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_VIEWS, 4)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_CONSTANTS, 4)) - return parsed.value(); else if (text.length() >= 9 && TOKEN_WIRE_STATISTICS.find(text) == 0 || text == TOKEN_WIRE_STATS) { if (parseEof()) return ShowWireStatsNode(); } + else if (text.length() >= 4 && TOKEN_CONSTANTS.find(text) == 0) + { + ShowConstantsNode node; + node.name = parseQualifiedName(true); + if (node.name->package.isEmpty()) + { + // It is expecting to get package = and object = + // But it is getting package = and object = + // Fix the name + node.name->package = node.name->schema; + node.name->schema = ""; + } + + if (parseEof()) + return node; + } break; } diff --git a/src/isql/show.epp b/src/isql/show.epp index 1763d620dac..88211d5f4c9 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -6825,15 +6825,18 @@ static processing_state show_constants(const std::optional& if (name.has_value()) { // Seach for a constant with name + // If SCHEMA is not specifeid, search in all schemas const QualifiedMetaString& constant = name.value(); FOR CONST IN RDB$CONSTANTS WITH - CONST.RDB$SCHEMA_NAME EQ constant.schema.c_str() AND CONST.RDB$PACKAGE_NAME EQ constant.package.c_str() AND CONST.RDB$CONSTANT_NAME EQ constant.object.c_str() SORTED BY CONST.RDB$CONSTANT_NAME { + if (constant.schema.hasData() && constant.schema != CONST.RDB$SCHEMA_NAME) + continue; + const bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; const char* type = isPrivate ? "BODY" : "HEADER"; diff --git a/src/isql/tests/FrontendParserTest.cpp b/src/isql/tests/FrontendParserTest.cpp index 96de3e7468d..e01b7027588 100644 --- a/src/isql/tests/FrontendParserTest.cpp +++ b/src/isql/tests/FrontendParserTest.cpp @@ -616,6 +616,12 @@ BOOST_AUTO_TEST_CASE(ParseShowTest) "show constant"))); BOOST_TEST(std::holds_alternative(parseShow( "show constants"))); + + BOOST_TEST((std::get(parseShow( + "show constants MY_PACKAGE.C1")).name == QualifiedMetaString("C1", "", "MY_PACKAGE"))); + + BOOST_TEST((std::get(parseShow( + "show constants SYSTEM.MY_PACKAGE.C1")).name == QualifiedMetaString("C1", "SYSTEM", "MY_PACKAGE"))); } From f3d7c5235be95da098dea0aa0f96fbe3fc305f5f Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 8 Apr 2026 11:39:54 +0300 Subject: [PATCH 072/119] One more cleanup --- src/dsql/PackageNodes.epp | 46 +++++++++++++----------------------- src/dsql/PackageNodes.h | 4 +++- src/include/gen/Firebird.pas | 3 ++- src/jrd/SystemTriggers.epp | 3 +-- 4 files changed, 23 insertions(+), 33 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 610d09dedcb..d267fb87ef5 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -135,12 +135,12 @@ void dropItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const Qualifie void checkItemsDefineMatch(MemoryPool& pool, const QualifiedName& packageAndSchema, const PackageItemsHolder::ItemsSignatureArray& newItems, const PackageItemsHolder::ItemsSignatureArray& existingItems, - ISC_STATUS missingCode, ISC_STATUS mismatchCode) + const ISC_STATUS missingCode, const ISC_STATUS mismatchCode) { for (auto i = existingItems.begin(); i != existingItems.end(); ++i) { FB_SIZE_T pos; - bool found = newItems.find(Signature(pool, i->name), pos); + const bool found = newItems.find(Signature(pool, i->name), pos); if (!found || !newItems[pos].defined) { @@ -176,11 +176,11 @@ void PackageItemsHolder::checkDefineMatch(MemoryPool& pool, const QualifiedName& isc_dyn_procnotdef_package, isc_dyn_procsignat_package); } - // Return function and procedure names (in the user charset) and optionally its details for a - // given package. +// Return function and procedure names (in the user charset) and optionally its details for a +// given package. void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& packageName, - bool details, bool collectConstants) + const bool details, const bool collectConstants) { AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS); AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS); @@ -202,10 +202,10 @@ void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transact ARG IN RDB$FUNCTION_ARGUMENTS CROSS FLD IN RDB$FIELDS WITH ARG.RDB$SCHEMA_NAME EQ FUN.RDB$SCHEMA_NAME AND - ARG.RDB$PACKAGE_NAME EQ FUN.RDB$PACKAGE_NAME AND - ARG.RDB$FUNCTION_NAME EQ FUN.RDB$FUNCTION_NAME AND - FLD.RDB$SCHEMA_NAME EQUIV ARG.RDB$FIELD_SOURCE_SCHEMA_NAME AND - FLD.RDB$FIELD_NAME EQ ARG.RDB$FIELD_SOURCE + ARG.RDB$PACKAGE_NAME EQ FUN.RDB$PACKAGE_NAME AND + ARG.RDB$FUNCTION_NAME EQ FUN.RDB$FUNCTION_NAME AND + FLD.RDB$SCHEMA_NAME EQUIV ARG.RDB$FIELD_SOURCE_SCHEMA_NAME AND + FLD.RDB$FIELD_NAME EQ ARG.RDB$FIELD_SOURCE { SignatureParameter parameter(*getDefaultMemoryPool()); @@ -259,7 +259,7 @@ void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transact FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) PRC IN RDB$PROCEDURES WITH PRC.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND - PRC.RDB$PACKAGE_NAME EQ packageName.object.c_str() + PRC.RDB$PACKAGE_NAME EQ packageName.object.c_str() { Signature procedure(PRC.RDB$PROCEDURE_NAME); procedure.defined = !PRC.RDB$PROCEDURE_BLR.NULL || !PRC.RDB$ENTRYPOINT.NULL; @@ -270,10 +270,10 @@ void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transact PRM IN RDB$PROCEDURE_PARAMETERS CROSS FLD IN RDB$FIELDS WITH PRM.RDB$SCHEMA_NAME EQ PRC.RDB$SCHEMA_NAME AND - PRM.RDB$PACKAGE_NAME EQ PRC.RDB$PACKAGE_NAME AND - PRM.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND - FLD.RDB$SCHEMA_NAME EQUIV PRM.RDB$FIELD_SOURCE_SCHEMA_NAME AND - FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE + PRM.RDB$PACKAGE_NAME EQ PRC.RDB$PACKAGE_NAME AND + PRM.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND + FLD.RDB$SCHEMA_NAME EQUIV PRM.RDB$FIELD_SOURCE_SCHEMA_NAME AND + FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE { SignatureParameter parameter(*getDefaultMemoryPool()); parameter.type = PRM.RDB$PARAMETER_TYPE; @@ -321,7 +321,6 @@ void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transact } END_FOR - if (collectConstants) { static const CachedRequestId requestId; @@ -331,7 +330,6 @@ void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transact CONST.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND CONST.RDB$PACKAGE_NAME EQ packageName.object.c_str() { - Signature constant(CONST.RDB$CONSTANT_NAME); constants.add(constant); } @@ -395,7 +393,6 @@ ValueExprNode* PackageReferenceNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) return node; } - DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR) { const UCHAR itemType = csb->csb_blr_reader.getByte(); @@ -419,8 +416,7 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler csb->addDependency(dependency); } - { - csb->qualifyExistingName(tdbb, fullName, obj_package_constant); + { // Package cache auto package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE); if (package) { @@ -442,7 +438,6 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler return nullptr; } - void PackageReferenceNode::genBlr(DsqlCompilerScratch* dsqlScratch) { dsqlScratch->appendUChar(blr_package_reference); @@ -476,7 +471,7 @@ bool PackageReferenceNode::constantExists(thread_db* tdbb, Jrd::jrd_tra* transac if (package == nullptr) return false; - ConstantValue* value = package->findConstant(fullName); + const ConstantValue* value = package->findConstant(fullName); if (value == nullptr) return false; @@ -621,7 +616,7 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat } // Generate a new unique field name because constants in different packages may have same name - // but names in RDB$FIELDS should be unique in for a SCHEME + // but names in RDB$FIELDS should be unique in for a SCHEMA QualifiedName fieldName = name; fieldName.object = ""; // Store description in the RDB$FIELDS relation @@ -701,7 +696,6 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat }// While } - bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { Attachment* attachment = transaction->getAttachment(); @@ -1164,7 +1158,6 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* bool CreateAlterPackageNode::executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { - static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_prm_pkg, DYN_REQUESTS); bool modified = false; @@ -1251,8 +1244,6 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, AutoSavePoint savePoint(tdbb, transaction); bool found = false; - - static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_e_pkg, DYN_REQUESTS); MetaId id{}; @@ -1447,7 +1438,6 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); - static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body, DYN_REQUESTS); bool modified = false; @@ -1613,8 +1603,6 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra AutoSavePoint savePoint(tdbb, transaction); bool found = false; - - static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body2, DYN_REQUESTS); MetaId id{}; diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index f4a918a0f26..372beb76314 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -108,7 +108,8 @@ class PackageItemsHolder void drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema); void checkDefineMatch(Firebird::MemoryPool& pool, const QualifiedName& packageAndSchema, const PackageItemsHolder& newItems); - void collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& packageAndSchema, bool details, bool collectConstants); + void collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, + const QualifiedName& packageAndSchema, const bool details, const bool collectConstants); void clear(); public: @@ -179,6 +180,7 @@ class CreatePackageConstantNode final : public DdlNode { m_isPrivate = false; } + inline void makePrivate() { m_isPrivate = true; diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 77c0c8a6093..78005b6dd9a 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5959,7 +5959,7 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_argmustbe_numeric_function = 335545322; isc_percetile_only_one_sort_item = 335545323; isc_argmustbe_const_within_group = 335545324; - isc_not_defined_constant = 335545325; + isc_update_overwrite = 335545325; isc_const_name = 335545326; isc_private_constant = 335545327; isc_package_alias_help = 335545328; @@ -5967,6 +5967,7 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_bad_constant_desc = 335545330; isc_bad_constant_name = 335545331; isc_bad_constant_type = 335545332; + isc_not_defined_constant = 335545333; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; diff --git a/src/jrd/SystemTriggers.epp b/src/jrd/SystemTriggers.epp index c8bb222677a..ddc6fbed78a 100644 --- a/src/jrd/SystemTriggers.epp +++ b/src/jrd/SystemTriggers.epp @@ -1807,8 +1807,7 @@ void SystemTriggers::executeAfterInsertTriggers(thread_db* tdbb, jrd_rel* relati break; case rel_packages: - if (tdbb->getAttachment()->isGbak()) - populateCache(tdbb, record); + populateCache(tdbb, record); break; } } From 2b27364675adc5d35377b93298bd5cb4522219a1 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 14 Apr 2026 21:19:42 +0300 Subject: [PATCH 073/119] Update doc/sql.extensions/README.packages.txt Co-authored-by: Dmitry Yemanov --- doc/sql.extensions/README.packages.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt index dafe0356614..20f51cad077 100644 --- a/doc/sql.extensions/README.packages.txt +++ b/doc/sql.extensions/README.packages.txt @@ -119,7 +119,7 @@ Objectives: GRANT SELECT ON TABLE secret TO PACKAGE pk_secret; GRANT EXECUTE ON PACKAGE pk_secret TO ROLE role_secret; - To use package constants in a select expression, a USAGE permission is requeued: + To use package constants in a select expression, a USAGE permission is required: GRANT USAGE ON PACKAGE [.] to [] [] []; REVOKE USAGE ON PACKAGE [.] FROM []; From db5031a672c497db70bcae4677f3af31b2e40772 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 14 Apr 2026 21:19:58 +0300 Subject: [PATCH 074/119] Update doc/sql.extensions/README.packages.txt Co-authored-by: Dmitry Yemanov --- doc/sql.extensions/README.packages.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt index 20f51cad077..73607d92904 100644 --- a/doc/sql.extensions/README.packages.txt +++ b/doc/sql.extensions/README.packages.txt @@ -104,7 +104,7 @@ Objectives: the [.]. notation. Constants declared in the package body are private and cannot be accessed from outside the package. However, they can be referenced directly by within and . - Header constants can also be used directly with just the name for package body elements. + Header constants can also be referenced directly by their name inside package body elements. - Facilitate permission management. From 8261ce6f3e88364090bd2c4dd36cf27daddfae5f Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 14 Apr 2026 21:24:37 +0300 Subject: [PATCH 075/119] Update src/dsql/Nodes.h Co-authored-by: Dmitry Yemanov --- src/dsql/Nodes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 1be1dc97ad8..6666c03d89e 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -705,7 +705,7 @@ class ExprNode : public DmlNode virtual bool unmappable(const MapNode* mapNode, StreamType shellStream) const; // Check if expression returns constant result - // The result true means the value does not change after recompilation + // Check if expression returns a constant result (the one which does not change after recompilation) virtual bool constant() const { return false; From a660ffa3ac604cf3f3319829732b8025a0d584b8 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 14 Apr 2026 21:25:21 +0300 Subject: [PATCH 076/119] Update src/include/firebird/impl/msg/jrd.h Co-authored-by: Dmitry Yemanov --- src/include/firebird/impl/msg/jrd.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index 27f3322f4cd..14f90075a86 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1011,5 +1011,5 @@ FB_IMPL_MSG(JRD, 1008, package_alias_help, -901, "42", "000", "Use an alias to r FB_IMPL_MSG(JRD, 1009, bad_constant_blr, -901, "2F", "000", "Error while parsing BLR value of the constant @1") FB_IMPL_MSG(JRD, 1010, bad_constant_desc, -901, "2F", "000", "Error while reading type of the constant with name @1") FB_IMPL_MSG(JRD, 1011, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found") -FB_IMPL_MSG(JRD, 1012, bad_constant_type, -901, "2F", "000", "@1 is not suppotred to be a constant type") +FB_IMPL_MSG(JRD, 1012, bad_constant_type, -901, "2F", "000", "@1 is not supported to be a constant type") FB_IMPL_MSG(JRD, 1013, not_defined_constant, -901, "42", "000", "The constant @1 is not defined in the package @2") From 04200076b8d8ab57d9e92a18df1d3345e28f9f8b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 14 Apr 2026 21:44:36 +0300 Subject: [PATCH 077/119] Resolve MR issues --- src/dsql/DdlNodes.epp | 2 +- src/dsql/ExprNodes.cpp | 2 - src/dsql/Nodes.h | 2 +- src/dsql/PackageNodes.epp | 2 +- src/dsql/PackageNodes.h | 2 +- src/include/firebird/impl/msg/dyn.h | 6 +-- src/jrd/Package.epp | 66 ++++++++++++----------------- src/jrd/Package.h | 2 +- src/jrd/dfw.epp | 2 +- 9 files changed, 37 insertions(+), 49 deletions(-) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 6b286b9f7bd..fc4160c82fc 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -16001,7 +16001,7 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G case obj_package_constant: if (!checkObjectExist(tdbb, transaction, objName, objType)) - status_exception::raise(Arg::PrivateDyn(303) << objName.toQuotedString()); // Package @1 does not exist + status_exception::raise(Arg::Gds(isc_dyn_constant_not_found) << objName.toQuotedString()); break; default: diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 4dc26d20d99..7d815bde648 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -6481,8 +6481,6 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), constantName)) { - packageContext.ctx_relation = nullptr; - packageContext.ctx_procedure = nullptr; // Alias is a package name, not a constant packageContext.ctx_alias.push(QualifiedName(constantName.package, constantName.schema)); packageContext.ctx_flags |= CTX_package; diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 6666c03d89e..255d064bb17 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -531,7 +531,7 @@ class ExprNode : public DmlNode TYPE_WINDOW_CLAUSE, TYPE_WINDOW_CLAUSE_FRAME, TYPE_WINDOW_CLAUSE_FRAME_EXTENT, - TYPE_REFERENCE, + TYPE_PACKAGE_REFERENCE, // Bool types TYPE_BINARY_BOOL, diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index d267fb87ef5..f8274ef2090 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -353,7 +353,7 @@ void PackageItemsHolder::clear() static RegisterNode regPackageReferenceNode({blr_package_reference}); PackageReferenceNode::PackageReferenceNode(MemoryPool& pool, const QualifiedName& fullName, const UCHAR itemType) - : TypedNode(pool), + : TypedNode(pool), m_fullName(fullName.object, fullName.schema.hasData() ? fullName.schema : PUBLIC_SCHEMA, fullName.package), m_itemType(itemType) {} diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 372beb76314..e71ae5055c1 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -118,7 +118,7 @@ class PackageItemsHolder ItemsSignatureArray constants; }; -class PackageReferenceNode final : public TypedNode +class PackageReferenceNode final : public TypedNode { public: PackageReferenceNode(Firebird::MemoryPool& pool, const QualifiedName& name, diff --git a/src/include/firebird/impl/msg/dyn.h b/src/include/firebird/impl/msg/dyn.h index e7f2afe0c8b..575243112a4 100644 --- a/src/include/firebird/impl/msg/dyn.h +++ b/src/include/firebird/impl/msg/dyn.h @@ -313,6 +313,6 @@ FB_IMPL_MSG(DYN, 320, dyn_cannot_create_reserved_schema, -607, "HY", "000", "Sch FB_IMPL_MSG(DYN, 321, dyn_cannot_infer_schema, -901, "42", "000", "Cannot infer schema name as there is no valid schema in the search path") FB_IMPL_MSG_SYMBOL(DYN, 322, dyn_dup_blob_filter, "Blob filter @1 already exists") FB_IMPL_MSG(DYN, 323, dyn_column_name_exists, -612, "42", "000", "Column @1 already exists in table @2") -FB_IMPL_MSG_SYMBOL(DYN, 334, dyn_const_not_found, "Constant @1 not found") -FB_IMPL_MSG_SYMBOL(DYN, 335, dyn_dup_const, "Constant @1 already exists") -FB_IMPL_MSG_SYMBOL(DYN, 336, dyn_non_constant_constant, "The constant \"@1\" must be initialized by a constant expression") +FB_IMPL_MSG_SYMBOL(DYN, 324, dyn_constant_not_found, "Constant @1 not found") +FB_IMPL_MSG_SYMBOL(DYN, 325, dyn_dup_const, "Constant @1 already exists") +FB_IMPL_MSG_SYMBOL(DYN, 326, dyn_non_constant_constant, "The constant \"@1\" must be initialized by a constant expression") diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp index d18387969b3..33515fae671 100644 --- a/src/jrd/Package.epp +++ b/src/jrd/Package.epp @@ -58,31 +58,25 @@ DATABASE DB = FILENAME "ODS.RDB"; static dsc* executeConstantExpressionWithRequest(thread_db* tdbb, CompilerScratch* csb) { Statement* statement = Statement::makeStatement(tdbb, csb, true); - Request* request = statement->makeRootRequest(tdbb); - { - Attachment* attachment = tdbb->getAttachment(); - jrd_tra* transaction = tdbb->getTransaction(); - tdbb->setRequest(request); - request->setUsed(); - request->setAttachment(attachment); - attachment->att_requests.add(request); + Attachment* attachment = tdbb->getAttachment(); + jrd_tra* transaction = tdbb->getTransaction(); - TRA_attach_request(transaction, request); - } + tdbb->setRequest(request); + request->setUsed(); + request->setAttachment(attachment); + attachment->att_requests.add(request); + TRA_attach_request(transaction, request); - { // Execute constant expr - ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output - return EVL_expr(tdbb, request, valueNode); - } + ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output + return EVL_expr(tdbb, request, valueNode); } static void executeConstantExpression(thread_db* tdbb, CompilerScratch* csb, MemoryPool& pool, impure_value& value) { Statement* statement = Statement::makeStatement(tdbb, csb, true); - Request* request = statement->makeRootRequest(tdbb); const dsc* temp = EVL_expr(tdbb, request, static_cast(csb->csb_node)); @@ -96,11 +90,9 @@ static void executeConstantExpression(thread_db* tdbb, CompilerScratch* csb, Mem static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const dsc& scalar) { // Make the blr with only the LiteralNode - { - BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); - blr.clear(); - dsqlScratch->getDebugData().clear(); - } + BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); + blr.clear(); + dsqlScratch->getDebugData().clear(); dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); // Convert a literalNode-unsupported constant type to a supported one if necessary @@ -120,6 +112,7 @@ static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlS LiteralNode::genConstant(dsqlScratch, &text, false); break; } + case dtype_real: { double newValue = *(float*) scalar.dsc_address; @@ -137,6 +130,7 @@ static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlS LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); break; } + case dtype_dec64: // dtype_dec64 is not present in LiteralNode::genConstant case dtype_double: // MOV_move stores double in scalar->dsc_address. Convert it to string to use genConstant case dtype_dec128: @@ -154,6 +148,7 @@ static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlS LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); break; } + case dtype_int128: { dsc descForInt128{}; @@ -169,9 +164,11 @@ static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlS LiteralNode::genConstant(dsqlScratch, &descForInt128, false, len); break; } + case dtype_blob: // Blob ID will be lost status_exception::raise(Arg::Gds(isc_bad_constant_type) << scalar.typeToText()); break; + default: LiteralNode::genConstant(dsqlScratch, &scalar, false); break; @@ -228,20 +225,17 @@ dsc ConstantValue::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const Qua void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema) { - { // Prepare BLR writer - BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); - blr.clear(); - dsqlScratch->getDebugData().clear(); - } + BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); + blr.clear(); + dsqlScratch->getDebugData().clear(); // Gen blr into dsqlScratch AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0)); CastNode cast(*tempPool, constExpr, type); - { - dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); - cast.genBlr(dsqlScratch); - dsqlScratch->appendUChar(blr_eoc); - } + dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); + cast.genBlr(dsqlScratch); + dsqlScratch->appendUChar(blr_eoc); + Attachment* attachment = tdbb->getAttachment(); MemoryPool* csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); @@ -303,14 +297,10 @@ dsc& ConstantValue::makeValue(thread_db* tdbb) CompilerScratch* csb = nullptr; Cleanup cc([csb]() {delete csb;}); - { // blr - // Use cached ID or get a real one (is it probably materialized) - bid constantBid = blrBlobId.isEmpty() ? getConstantBid(tdbb, id) : blrBlobId; - - MET_parse_blob(tdbb, &name.schema, nullptr, &constantBid, &csb, nullptr, false, false); - - fb_assert(csb != nullptr); - } + // Use cached ID or get a real one (is it probably materialized) + bid constantBid = blrBlobId.isEmpty() ? getConstantBid(tdbb, id) : blrBlobId; + MET_parse_blob(tdbb, &name.schema, nullptr, &constantBid, &csb, nullptr, false, false); + fb_assert(csb != nullptr); executeConstantExpression(tdbb, csb, getPool(), value); } diff --git a/src/jrd/Package.h b/src/jrd/Package.h index 375f48368e8..10c51d2fb01 100644 --- a/src/jrd/Package.h +++ b/src/jrd/Package.h @@ -1,7 +1,7 @@ /* * PROGRAM: Firebird CONSTANTS implementation. * MODULE: Package.h - * DESCRIPTION: Routine to cache and reload Package constants + * DESCRIPTION: Routines to cache and reload package items * * The contents of this file are subject to the Initial * Developer's Public License Version 1.0 (the "License"); diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 8f499f156d5..ed5ffa6b5d5 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -492,7 +492,7 @@ static ISC_STATUS getErrorNotFound(int obj_type) err_code = isc_dyn_func_not_found; break; case obj_package_constant: - err_code = isc_dyn_const_not_found; + err_code = isc_dyn_constant_not_found; break; default: fb_assert(false); From 79c9ecce5f023d792dc6b9e47bf5c4d4bd62030f Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 14 Apr 2026 22:05:35 +0300 Subject: [PATCH 078/119] Fix build after merge --- src/jrd/Package.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/jrd/Package.h b/src/jrd/Package.h index 10c51d2fb01..06215e4cc1f 100644 --- a/src/jrd/Package.h +++ b/src/jrd/Package.h @@ -128,6 +128,7 @@ class PackagePermanent : public Firebird::PermanentStorage } void releaseLock(thread_db*) { } + void reloadAst(thread_db* tdbb, bool erase) { } const QualifiedName& getName() const noexcept { return name; } void setName(const QualifiedName& value) { name = value; } From 3f89c174129fab94e626c5adaa6830161a4c879b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 14 Apr 2026 22:49:44 +0300 Subject: [PATCH 079/119] Move check_dependencies to phase 1 in deleteConstant DFW --- src/jrd/dfw.epp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index e3f5914fe79..29d90d116e9 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1167,12 +1167,10 @@ namespace return false; case 1: - return true; - - case 2: check_dependencies(tdbb, work->getQualifiedName(), NULL, obj_package_constant, transaction); return true; + case 2: case 3: return true; From 46912b076a2a2a39b01ae25d8ebd73ec49e955f8 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 15 Apr 2026 08:54:04 +0300 Subject: [PATCH 080/119] Remove CONSTANT_ID --- src/dsql/PackageNodes.epp | 100 +++++++++++++------------------------- src/dsql/PackageNodes.h | 1 - src/jrd/Package.epp | 40 ++++++--------- src/jrd/Package.h | 16 +++--- src/jrd/constants.h | 1 - src/jrd/drq.h | 1 - src/jrd/fields.h | 1 - src/jrd/ids.h | 11 ++--- src/jrd/idx.h | 6 +-- src/jrd/ini.epp | 18 +++---- src/jrd/met.epp | 8 +-- src/jrd/names.h | 1 - src/jrd/relations.h | 1 - src/jrd/trig.h | 1 - src/jrd/vio.cpp | 16 ++---- 15 files changed, 73 insertions(+), 149 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index f8274ef2090..fdf273a8d5e 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -600,8 +600,6 @@ void CreatePackageConstantNode::execute(thread_db* tdbb, DsqlCompilerScratch* ds } else executeAlter(tdbb, dsqlScratch, transaction); - - fb_assert(m_id); } void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) @@ -627,73 +625,45 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat static const CachedRequestId requestId; AutoCacheRequest storeConstantRequest(tdbb, requestId); - int faults = 0; - while (true) + // Store desc and metainfo in the RDB$CONSTANTS relation + STORE (REQUEST_HANDLE storeConstantRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS USING { - try - { - SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_const_id, CONSTANTS_GENERATOR); - id %= (MAX_SSHORT + 1); - if (!id) - continue; - - m_id = id; - - // Store desc and metainfo in the RDB$CONSTANTS relation - STORE (REQUEST_HANDLE storeConstantRequest TRANSACTION_HANDLE transaction) - CONST IN RDB$CONSTANTS USING - { - // Constant name - CONST.RDB$CONSTANT_NAME.NULL = FALSE; - strcpy(CONST.RDB$CONSTANT_NAME, name.object.c_str()); + // Constant name + CONST.RDB$CONSTANT_NAME.NULL = FALSE; + strcpy(CONST.RDB$CONSTANT_NAME, name.object.c_str()); - // Constant unique id - CONST.RDB$CONSTANT_ID = id; + // Description (filed) name + CONST.RDB$FIELD_SOURCE.NULL = FALSE; + strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); - // Description (filed) name - CONST.RDB$FIELD_SOURCE.NULL = FALSE; - strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); - - // Gen value blr - ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); - - // Put the blr - attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData()); - - // Parent package - fb_assert(name.package.hasData()); - { - CONST.RDB$PACKAGE_NAME.NULL = FALSE; - strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str()); - - fb_assert(package); - package->addConstant(tdbb, m_id, name, m_isPrivate, m_type); // Do not cache blob id because the blob is not materialized - } + // Gen value blr + ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); - // Schema of the parent package - CONST.RDB$SCHEMA_NAME.NULL = FALSE; - strcpy(CONST.RDB$SCHEMA_NAME, name.schema.c_str()); + // Put the blr + attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData()); - // Type - CONST.RDB$PRIVATE_FLAG.NULL = FALSE; - CONST.RDB$PRIVATE_FLAG = m_isPrivate; + // Parent package + fb_assert(name.package.hasData()); + { + CONST.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str()); - CONST.RDB$CONSTANT_SOURCE.NULL = TRUE; - } - END_STORE - break; + fb_assert(package); + package->addConstant(tdbb, name, m_isPrivate, m_type); // Do not cache blob id because the blob is not materialized } - catch (const status_exception& ex) - { - if (ex.value()[1] != isc_unique_key_violation) - throw; - if (++faults > MAX_SSHORT) - throw; + // Schema of the parent package + CONST.RDB$SCHEMA_NAME.NULL = FALSE; + strcpy(CONST.RDB$SCHEMA_NAME, name.schema.c_str()); - fb_utils::init_status(tdbb->tdbb_status_vector); - } - }// While + // Type + CONST.RDB$PRIVATE_FLAG.NULL = FALSE; + CONST.RDB$PRIVATE_FLAG = m_isPrivate; + + CONST.RDB$CONSTANT_SOURCE.NULL = TRUE; + } + END_STORE } bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) @@ -721,8 +691,6 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE { - m_id = CONST.RDB$CONSTANT_ID; - dyn_fld origDom, newDom; DSC_make_descriptor(&origDom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, @@ -828,11 +796,9 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc CONST.RDB$CONSTANT_BLR.NULL = FALSE; END_MODIFY - { - fb_assert(package); - // Do not cache blob id because the blob is not materialized - package->updateConstant(tdbb, m_id, m_isPrivate, m_type); - } + fb_assert(package); + // Do not cache blob id because the blob is not materialized + package->updateConstant(tdbb, name, m_isPrivate, m_type); found = true; } diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index e71ae5055c1..c436a3b8d84 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -211,7 +211,6 @@ class CreatePackageConstantNode final : public DdlNode private: NestConst m_type; NestConst m_expr; - MetaId m_id = 0; bool m_isPrivate = false; }; diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp index 33515fae671..206d6a13d5e 100644 --- a/src/jrd/Package.epp +++ b/src/jrd/Package.epp @@ -236,7 +236,6 @@ void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScr cast.genBlr(dsqlScratch); dsqlScratch->appendUChar(blr_eoc); - Attachment* attachment = tdbb->getAttachment(); MemoryPool* csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); ContextPoolHolder context(tdbb, csb_pool); @@ -260,7 +259,7 @@ void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScr genConstantCompatibleBlr(tdbb, dsqlScratch, *output); } -inline bid getConstantBid(thread_db* tdbb, const MetaId id) +inline bid getConstantBid(thread_db* tdbb, const QualifiedName& name) { Attachment* attachment = tdbb->getAttachment(); jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); @@ -270,7 +269,9 @@ inline bid getConstantBid(thread_db* tdbb, const MetaId id) FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) CONST IN RDB$CONSTANTS - WITH CONST.RDB$CONSTANT_ID EQ id + WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ name.object.c_str() { return CONST.RDB$CONSTANT_BLR; } @@ -298,7 +299,7 @@ dsc& ConstantValue::makeValue(thread_db* tdbb) Cleanup cc([csb]() {delete csb;}); // Use cached ID or get a real one (is it probably materialized) - bid constantBid = blrBlobId.isEmpty() ? getConstantBid(tdbb, id) : blrBlobId; + bid constantBid = blrBlobId.isEmpty() ? getConstantBid(tdbb, name) : blrBlobId; MET_parse_blob(tdbb, &name.schema, nullptr, &constantBid, &csb, nullptr, false, false); fb_assert(csb != nullptr); @@ -323,19 +324,16 @@ dsc& ConstantValue::makeValue(thread_db* tdbb) return value.vlu_desc; } -ConstantValue& ConstantsCache::add(const MetaId constId, const QualifiedName& constName, const bool isPrivate) +ConstantValue& ConstantsCache::add(const QualifiedName& constName, const bool isPrivate) { - fb_assert(!idMap.exist(constId)); fb_assert(!nameMap.exist(constName)); const ULONG id = values.getCount(); auto& value = values.add(); value.name = constName; - value.id = constId; value.isPrivate = isPrivate; - idMap.put(constId, id); nameMap.put(constName, id); return value; @@ -410,7 +408,7 @@ ScanResult Package::scan(thread_db* tdbb, ObjectBase::Flag flags) PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME AND PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME { - addConstant(tdbb, CONST.RDB$CONSTANT_ID, + addConstant(tdbb, QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME), CONST.RDB$PRIVATE_FLAG, CONST.RDB$CONSTANT_BLR, @@ -452,7 +450,7 @@ ScanResult Package::reload(thread_db* tdbb, ObjectBase::Flag fl) PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME AND PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME { - addConstant(tdbb, CONST.RDB$CONSTANT_ID, + addConstant(tdbb, QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME), CONST.RDB$PRIVATE_FLAG, CONST.RDB$CONSTANT_BLR); @@ -475,7 +473,7 @@ bool Package::hash(thread_db* tdbb, Firebird::sha512& digest) return true; } -ConstantValue& Package::addConstant(thread_db* tdbb, const MetaId constId, +ConstantValue& Package::addConstant(thread_db* tdbb, const QualifiedName& constName, const bool isPrivate, const TypeClause* type) @@ -483,20 +481,20 @@ ConstantValue& Package::addConstant(thread_db* tdbb, const MetaId constId, dsc typeDesc; DsqlDescMaker::fromField(&typeDesc, type); - auto& value = constants.add(constId, constName, isPrivate); + auto& value = constants.add(constName, isPrivate); value.blrBlobId = {}; value.value.vlu_desc = typeDesc; return value; } -ConstantValue& Package::addConstant(thread_db* tdbb, const MetaId constId, +ConstantValue& Package::addConstant(thread_db* tdbb, const QualifiedName& constName, const bool isPrivate, const bid blrBlobId, const bool skipMakeValue) { - auto& value = constants.add(constId, constName, isPrivate); + auto& value = constants.add(constName, isPrivate); value.blrBlobId = blrBlobId; if (!skipMakeValue) value.makeValue(tdbb); @@ -504,14 +502,14 @@ ConstantValue& Package::addConstant(thread_db* tdbb, const MetaId constId, return value; } -ConstantValue& Package::updateConstant(thread_db* tdbb, const MetaId constId, +ConstantValue& Package::updateConstant(thread_db* tdbb, const QualifiedName& name, const bool isPrivate, const TypeClause* type) { dsc typeDesc; DsqlDescMaker::fromField(&typeDesc, type); - auto* innerId = constants.idMap.get(constId); + auto* innerId = constants.nameMap.get(name); fb_assert(innerId != nullptr); auto& constant = constants.values[*innerId]; @@ -525,16 +523,6 @@ ConstantValue& Package::updateConstant(thread_db* tdbb, const MetaId constId, return constant; } -ConstantValue* Package::findConstant(const MetaId id) -{ - auto it = constants.idMap.get(id); - - if (it != nullptr) - return &constants.values[*it]; - - return nullptr; -} - ConstantValue* Package::findConstant(const QualifiedName& name) { auto it = constants.nameMap.get(name); diff --git a/src/jrd/Package.h b/src/jrd/Package.h index 06215e4cc1f..86c29f9ce90 100644 --- a/src/jrd/Package.h +++ b/src/jrd/Package.h @@ -49,7 +49,6 @@ class ConstantValue final : public Firebird::PermanentStorage name(pool) { } - MetaId id{}; QualifiedName name; // Keep type to gen hash (when not commited - we cannot read it from system table) @@ -81,21 +80,18 @@ struct ConstantsCache using ValueId = ULONG; ConstantsCache(MemoryPool& pool) : - idMap(pool), nameMap(pool), values(pool) { } - Firebird::NonPooledMap idMap; Firebird::LeftPooledMap nameMap; Firebird::ObjectsArray values; - ConstantValue& add(const MetaId constId, const QualifiedName& constName, const bool isPrivate); + ConstantValue& add(const QualifiedName& constName, const bool isPrivate); void clear() { - idMap.clear(); nameMap.clear(); values.clear(); } @@ -105,7 +101,7 @@ struct ConstantsCache class PackagePermanent : public Firebird::PermanentStorage { public: - explicit PackagePermanent(thread_db* tdbb, MemoryPool& p, MetaId metaId, NoData) + explicit PackagePermanent(thread_db* tdbb, MemoryPool& p, const MetaId metaId, NoData) : PermanentStorage(p), id(metaId), name(p) @@ -206,22 +202,22 @@ class Package final : public Firebird::PermanentStorage, public ObjectBase // ---------- - ConstantValue& addConstant(thread_db* tdbb, const MetaId constId, + ConstantValue& addConstant(thread_db* tdbb, const QualifiedName& constName, const bool isPrivate, const TypeClause* type); - ConstantValue& addConstant(thread_db* tdbb, const MetaId constId, + ConstantValue& addConstant(thread_db* tdbb, const QualifiedName& constName, const bool isPrivate, const bid blrBlobId, const bool skipMakeValue = false); - ConstantValue& updateConstant(thread_db* tdbb, const MetaId constId, + ConstantValue& updateConstant(thread_db* tdbb, + const QualifiedName& constName, const bool isPrivate, const TypeClause* type); - ConstantValue* findConstant(const MetaId id); ConstantValue* findConstant(const QualifiedName& name); private: diff --git a/src/jrd/constants.h b/src/jrd/constants.h index 9a8b7336b0a..c3897a5e98e 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -154,7 +154,6 @@ inline constexpr int GEN_SECCLASS_PREFIX_LEN = 4; inline constexpr const char* PROCEDURES_GENERATOR = "RDB$PROCEDURES"; inline constexpr const char* FUNCTIONS_GENERATOR = "RDB$FUNCTIONS"; -inline constexpr const char* CONSTANTS_GENERATOR = "RDB$CONSTANTS"; inline constexpr const char* PACKAGES_GENERATOR = "RDB$PACKAGES"; // Automatically created check constraints for unnamed PRIMARY and UNIQUE declarations. diff --git a/src/jrd/drq.h b/src/jrd/drq.h index d474f8627b4..2219d66dc30 100644 --- a/src/jrd/drq.h +++ b/src/jrd/drq.h @@ -242,7 +242,6 @@ enum drq_type_t drq_e_pub_tab_all, // erase relation from all publication drq_l_rel_con, // lookup relation constraint drq_l_rel_fld_name, // lookup relation field name - drq_g_nxt_const_id, // lookup next constant ID drq_g_nxt_package_id, // lookup next package ID drq_MAX diff --git a/src/jrd/fields.h b/src/jrd/fields.h index 56f18a0d5d0..f0fd2d66ebb 100644 --- a/src/jrd/fields.h +++ b/src/jrd/fields.h @@ -243,6 +243,5 @@ FIELD(fld_pkg_id , nam_pkg_id , dtype_long , sizeof(SLONG) , 0 , NULL , true , ODS_14_0) FIELD(fld_const_name , nam_const_name , dtype_varying , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata , NULL , false , ODS_14_0) - FIELD(fld_const_id , nam_const_id , dtype_long , sizeof(SLONG) , 0 , NULL , true , ODS_14_0) FIELD(fld_const_blr , nam_const_blr , dtype_blob , BLOB_SIZE , isc_blob_blr , NULL , true , ODS_14_0) FIELD(fld_const_source , nam_const_source , dtype_blob , BLOB_SIZE , isc_blob_text , NULL , true , ODS_14_0) diff --git a/src/jrd/ids.h b/src/jrd/ids.h index 3e974c03e4a..7e8f3e8823d 100644 --- a/src/jrd/ids.h +++ b/src/jrd/ids.h @@ -89,9 +89,8 @@ static_assert(f_tz_name == 1, "Wrong field id"); static_assert(f_mon_ltt_type == 4, "Wrong field id"); static_assert(f_const_name == 0, "Wrong field id"); - static_assert(f_const_id == 1, "Wrong field id"); - static_assert(f_const_package == 2, "Wrong field id"); - static_assert(f_const_field == 3, "Wrong field id"); - static_assert(f_const_private_flag == 4, "Wrong field id"); - static_assert(f_const_blr == 5, "Wrong field id"); - static_assert(f_const_source == 6, "Wrong field id"); + static_assert(f_const_package == 1, "Wrong field id"); + static_assert(f_const_field == 2, "Wrong field id"); + static_assert(f_const_private_flag == 3, "Wrong field id"); + static_assert(f_const_blr == 4, "Wrong field id"); + static_assert(f_const_source == 5, "Wrong field id"); diff --git a/src/jrd/idx.h b/src/jrd/idx.h index 4ce7dfabf0b..9a1359420b8 100644 --- a/src/jrd/idx.h +++ b/src/jrd/idx.h @@ -531,12 +531,8 @@ static inline constexpr struct ini_idx_t indices[] = SEGMENT(f_const_package, idx_metadata), // package name SEGMENT(f_const_name, idx_metadata) // constant name }}, - // define index RDB$INDEX_99 for RDB$CONSTANTS unique RDB$CONSTANT_ID; - INDEX(99, rel_constants, idx_unique, 1, ODS_14_0) - SEGMENT(f_const_id, idx_numeric) // constant id - }}, // define index RDB$INDEX_100 for RDB$PACKAGES unique RDB$PACKAGE_ID; - INDEX(100, rel_packages, idx_unique, 1, ODS_14_0) + INDEX(99, rel_packages, idx_unique, 1, ODS_14_0) SEGMENT(f_pkg_id, idx_numeric) // constant id }} }; diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index 834487ae845..c69c586a90f 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -1970,7 +1970,6 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR const SLONG procGen = lookupGenerator(PROCEDURES_GENERATOR); const SLONG funcGen = lookupGenerator(FUNCTIONS_GENERATOR); const SLONG packageGen = lookupGenerator(PACKAGES_GENERATOR); - const SLONG constantGen = lookupGenerator(CONSTANTS_GENERATOR); for (const auto& systemPackage : SystemPackage::get()) { @@ -2172,21 +2171,16 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR // Constant name PAD(constant.name, CONST.RDB$CONSTANT_NAME); - // Constant unique id - CONST.RDB$CONSTANT_ID = DPM_gen_id(tdbb, constantGen, false, 1);; - // Description (filed) name PAD(names[gfields[constant.fieldId].gfld_name], CONST.RDB$FIELD_SOURCE); // Put the blr - { - Array blrData(1 + constant.valueBlr.getCount() + 1); - blrData.push(blr_version5); - blrData.append(constant.valueBlr); - blrData.push(blr_eoc); - attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, blrData); - CONST.RDB$CONSTANT_BLR.NULL = FALSE; - } + Array blrData(1 + constant.valueBlr.getCount() + 1); + blrData.push(blr_version5); + blrData.append(constant.valueBlr); + blrData.push(blr_eoc); + attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, blrData); + CONST.RDB$CONSTANT_BLR.NULL = FALSE; // Parent package PAD(systemPackage.name, CONST.RDB$PACKAGE_NAME); diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 99e34a2e1b6..f5c9768c7b7 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -829,18 +829,18 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const { QualifiedName constantFullName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME); - const auto id = CONST.RDB$CONSTANT_ID; - DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_package_constant, - string(constantFullName.object.c_str()), constantFullName.schema, id, constantFullName.package); + string(constantFullName.object.c_str()), constantFullName.schema, 0, constantFullName.package); if (package == nullptr) { + const auto id = PKG.RDB$PACKAGE_ID; + package = MetadataCache::newVersion(tdbb, id)->getVersioned(tdbb, 0); fb_assert(package); } - package->addConstant(tdbb, id, constantFullName, CONST.RDB$PRIVATE_FLAG, CONST.RDB$CONSTANT_BLR); + package->addConstant(tdbb, constantFullName, CONST.RDB$PRIVATE_FLAG, CONST.RDB$CONSTANT_BLR); } END_FOR diff --git a/src/jrd/names.h b/src/jrd/names.h index d04e7145189..a3e6720736f 100644 --- a/src/jrd/names.h +++ b/src/jrd/names.h @@ -496,7 +496,6 @@ NAME("RDB$CONSTANTS", nam_constants) NAME("RDB$CONSTANT_NAME", nam_const_name) NAME("RDB$CONSTANT_BLR", nam_const_blr) NAME("RDB$CONSTANT_SOURCE", nam_const_source) -NAME("RDB$CONSTANT_ID", nam_const_id) NAME("MON$TABLE_TYPE", nam_mon_tab_type) NAME("MON$LOCAL_TEMPORARY_TABLES", nam_mon_local_temp_tables) diff --git a/src/jrd/relations.h b/src/jrd/relations.h index ee784f53630..c3703693a7c 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -845,7 +845,6 @@ END_RELATION // Relation 59 (RDB$CONSTANTS) RELATION(nam_constants, rel_constants, ODS_14_0, 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) FIELD(f_const_field, nam_f_source, fld_f_name, 0, ODS_14_0) FIELD(f_const_private_flag, nam_private_flag, fld_flag_nullable, 0, ODS_14_0) diff --git a/src/jrd/trig.h b/src/jrd/trig.h index 3cc9163bc9d..d0bd76c7237 100644 --- a/src/jrd/trig.h +++ b/src/jrd/trig.h @@ -82,7 +82,6 @@ static inline constexpr Jrd::gen generators[] = { "RDB$BACKUP_HISTORY", 9, "Nbackup technology", ODS_13_0 }, { FUNCTIONS_GENERATOR, 10, "Function ID", ODS_13_0 }, { "RDB$GENERATOR_NAME", 11, "Implicit generator name", ODS_13_0 }, - { CONSTANTS_GENERATOR, 12, "Constant ID", ODS_14_0 }, { PACKAGES_GENERATOR, 13, "Package ID", ODS_14_0 }, { nullptr, 0, nullptr, 0 } }; diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 01afdbba51d..4daf9cfb526 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -2359,10 +2359,8 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_const_package, &desc2); MOV_get_metaname(tdbb, &desc2, object_name.package); - EVL_field(0, rpb->rpb_record, f_const_id, &desc2); - id = MOV_get_long(tdbb, &desc2, 0); - DFW_post_work(transaction, dfw_delete_package_constant, &desc, &schemaDesc, id, object_name.package); + DFW_post_work(transaction, dfw_delete_package_constant, &desc, &schemaDesc, 0, object_name.package); break; default: // Shut up compiler warnings @@ -3766,11 +3764,7 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j EVL_field(0, org_rpb->rpb_record, f_const_package, &desc2); MOV_get_metaname(tdbb, &desc2, object_name.package); - { // Send dfw - EVL_field(0, org_rpb->rpb_record, f_const_id, &desc2); - const USHORT id = MOV_get_long(tdbb, &desc2, 0); - DFW_post_work(transaction, dfw_modify_package_constant, &desc1, &schemaDesc, id, object_name.package); - } + DFW_post_work(transaction, dfw_modify_package_constant, &desc1, &schemaDesc, 0, object_name.package); break; default: @@ -4701,11 +4695,9 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_const_name, &desc); EVL_field(0, rpb->rpb_record, f_const_package_schema, &schemaDesc); - if (EVL_field(0, rpb->rpb_record, f_const_package, &desc2)) - MOV_get_metaname(tdbb, &desc2, object_name.package); + EVL_field(0, rpb->rpb_record, f_const_package, &desc2); + MOV_get_metaname(tdbb, &desc2, object_name.package); - object_id = set_metadata_id(tdbb, rpb->rpb_record, - f_const_id, drq_g_nxt_const_id, CONSTANTS_GENERATOR); break; default: // Shut up compiler warnings From 39ed54f95d99be0abd9f975ebc8476b2f0bb9805 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 15 Apr 2026 09:31:01 +0300 Subject: [PATCH 081/119] Apply same rules for constant change as for domain --- src/dsql/PackageNodes.epp | 2 ++ src/jrd/dfw.epp | 4 ---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index fdf273a8d5e..fc991a66e6a 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -788,6 +788,8 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc if (newDom.dyn_dtype == blr_blob && newDomainName.object.isEmpty()) newDom.dyn_sub_type = m_type->subType; + AlterDomainNode::checkUpdate(origDom, newDom); + // Gen the new constant value as blr ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 29d90d116e9..d1d103d9e1c 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1139,11 +1139,7 @@ namespace return false; case 1: - return true; case 2: - check_dependencies(tdbb, work->getQualifiedName(), nullptr, obj_package_constant, transaction); - return true; - case 3: return true; From abce28fc51f6cd66ef9830cf3f16432aa1c9c529 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Wed, 15 Apr 2026 09:39:28 +0300 Subject: [PATCH 082/119] Mark constant reference as constant expression --- src/dsql/PackageNodes.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index c436a3b8d84..ff9563c5c0f 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -126,6 +126,11 @@ class PackageReferenceNode final : public TypedNode Date: Wed, 15 Apr 2026 11:55:39 +0300 Subject: [PATCH 083/119] Add missing dependencies for constant expression --- src/dsql/PackageNodes.epp | 4 ++-- src/jrd/Package.epp | 34 +++++++++++++------------------ src/jrd/Package.h | 42 +++++++++++++++++++++++++++------------ 3 files changed, 45 insertions(+), 35 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index fc991a66e6a..864fbf23f40 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -638,7 +638,7 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); // Gen value blr - ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); + ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name); // Put the blr attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData()); @@ -791,7 +791,7 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc AlterDomainNode::checkUpdate(origDom, newDom); // Gen the new constant value as blr - ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); + ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name); MODIFY CONST USING attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData()); diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp index 206d6a13d5e..330cd3f7949 100644 --- a/src/jrd/Package.epp +++ b/src/jrd/Package.epp @@ -178,8 +178,8 @@ static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlS bool ConstantValue::hash(thread_db* tdbb, Firebird::sha512& digest) const { - fb_assert(value.vlu_desc.dsc_dtype != 0); - digest.process(sizeof(value.vlu_desc), &value.vlu_desc); + fb_assert(m_value.vlu_desc.dsc_dtype != 0); + digest.process(sizeof(m_value.vlu_desc), &m_value.vlu_desc); return true; } @@ -223,7 +223,7 @@ dsc ConstantValue::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const Qua } void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema) + ValueExprNode* constExpr, dsql_fld* type, const QualifiedName& name) { BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); blr.clear(); @@ -247,11 +247,10 @@ void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScr tdbb->setRequest(requestToRestore); }); - // Parse BLR for constant expression - PAR_blr(tdbb, &schema, nullptr, + // Parse BLR for constant expression and store dependencies + MET_get_dependencies(tdbb, nullptr, dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount(), - nullptr, &csb, - nullptr, false, 0); + nullptr, nullptr, nullptr, &csb, name, obj_package_constant, 0, dsqlScratch->getTransaction()); // Execute node from BLR auto output = executeConstantExpressionWithRequest(tdbb, csb); @@ -282,8 +281,8 @@ inline bid getConstantBid(thread_db* tdbb, const QualifiedName& name) dsc& ConstantValue::makeValue(thread_db* tdbb) { - if (value.vlu_desc.dsc_address != nullptr) - return value.vlu_desc; + if (m_value.vlu_desc.dsc_address != nullptr) + return m_value.vlu_desc; Attachment* attachment = tdbb->getAttachment(); @@ -299,11 +298,11 @@ dsc& ConstantValue::makeValue(thread_db* tdbb) Cleanup cc([csb]() {delete csb;}); // Use cached ID or get a real one (is it probably materialized) - bid constantBid = blrBlobId.isEmpty() ? getConstantBid(tdbb, name) : blrBlobId; + bid constantBid = m_blrBlobId.isEmpty() ? getConstantBid(tdbb, name) : m_blrBlobId; MET_parse_blob(tdbb, &name.schema, nullptr, &constantBid, &csb, nullptr, false, false); fb_assert(csb != nullptr); - executeConstantExpression(tdbb, csb, getPool(), value); + executeConstantExpression(tdbb, csb, getPool(), m_value); } catch (const Exception& ex) { @@ -321,7 +320,7 @@ dsc& ConstantValue::makeValue(thread_db* tdbb) throw; } - return value.vlu_desc; + return m_value.vlu_desc; } ConstantValue& ConstantsCache::add(const QualifiedName& constName, const bool isPrivate) @@ -482,8 +481,7 @@ ConstantValue& Package::addConstant(thread_db* tdbb, DsqlDescMaker::fromField(&typeDesc, type); auto& value = constants.add(constName, isPrivate); - value.blrBlobId = {}; - value.value.vlu_desc = typeDesc; + value.updateValue(typeDesc); return value; } @@ -495,7 +493,7 @@ ConstantValue& Package::addConstant(thread_db* tdbb, const bool skipMakeValue) { auto& value = constants.add(constName, isPrivate); - value.blrBlobId = blrBlobId; + value.updateValue(blrBlobId); if (!skipMakeValue) value.makeValue(tdbb); @@ -513,12 +511,8 @@ ConstantValue& Package::updateConstant(thread_db* tdbb, const QualifiedName& nam fb_assert(innerId != nullptr); auto& constant = constants.values[*innerId]; - constant.blrBlobId = {}; constant.isPrivate = isPrivate; - - delete constant.value.vlu_string; - constant.value = {}; - constant.value.vlu_desc = typeDesc; + constant.updateValue(typeDesc); return constant; } diff --git a/src/jrd/Package.h b/src/jrd/Package.h index 86c29f9ce90..b6a1de9f24d 100644 --- a/src/jrd/Package.h +++ b/src/jrd/Package.h @@ -44,35 +44,51 @@ class dsql_fld; class ConstantValue final : public Firebird::PermanentStorage { public: + QualifiedName name; + bool isPrivate = false; + ConstantValue(MemoryPool& pool) : Firebird::PermanentStorage(pool), name(pool) { } - QualifiedName name; - - // Keep type to gen hash (when not commited - we cannot read it from system table) - // Keep value when scanning and after the first execution - impure_value value{}; - - // keep only materialized value - bid blrBlobId{}; - - bool isPrivate = false; + ~ConstantValue() + { + delete m_value.vlu_string; + } bool hash(thread_db* tdbb, Firebird::sha512& digest) const; static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name); static void genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema); + ValueExprNode* constExpr, dsql_fld* type, const QualifiedName& name); dsc& makeValue(thread_db* tdbb); - ~ConstantValue() + bid getBlobId(); + + void updateValue(const dsc typeDesc) + { + m_blrBlobId = {}; + + delete m_value.vlu_string; + m_value = {}; + m_value.vlu_desc = typeDesc; + } + + void updateValue(const bid blobId) { - delete value.vlu_string; + m_blrBlobId = blobId; } + +private: + // Keep type to gen hash (when not commited - we cannot read it from system table) + // Keep value when scanning and after the first execution + impure_value m_value{}; + + // keep only materialized value + bid m_blrBlobId{}; }; struct ConstantsCache From 53bf352adf27e9a1310dbfc00494eb670669101b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 20 Apr 2026 15:29:35 +0300 Subject: [PATCH 084/119] Move dependency store to dfw Previously, I evaluated constant expressions and stored only the value. But this strategy resulted in lost dependencies. Now I store the full expression and evaluate it on Cache Reload or at Runtime. The calculated value will be cached. Also, fix dependency tracking: RDB$DEPENDENCIES only stores the object and schema. However, a constant name was passed as a dependency, which meant the package name wasn't tracked. --- src/dsql/PackageNodes.epp | 6 +- src/jrd/Package.epp | 232 ++++++++++++-------------------------- src/jrd/Package.h | 12 +- src/jrd/dfw.epp | 79 ++++++++----- src/jrd/met.epp | 3 +- src/jrd/tra.h | 1 + src/jrd/vio.cpp | 1 + 7 files changed, 142 insertions(+), 192 deletions(-) diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 864fbf23f40..6e539db6b1f 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -540,7 +540,7 @@ dsc* PackageReferenceNode::execute(thread_db* tdbb, Request* request) const Package* package = m_package(request->getResources()); package->checkReload(tdbb); - EVL_make_value(tdbb, &package->findConstant(m_fullName)->makeValue(tdbb), outputImpure); + EVL_make_value(tdbb, package->findConstant(m_fullName)->makeValue(tdbb, request), outputImpure); return &outputImpure->vlu_desc; } default: @@ -638,7 +638,7 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); // Gen value blr - ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name); + ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); // Put the blr attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData()); @@ -791,7 +791,7 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc AlterDomainNode::checkUpdate(origDom, newDom); // Gen the new constant value as blr - ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name); + ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); MODIFY CONST USING attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData()); diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp index 330cd3f7949..7928a617493 100644 --- a/src/jrd/Package.epp +++ b/src/jrd/Package.epp @@ -55,126 +55,6 @@ DATABASE DB = FILENAME "ODS.RDB"; //---------------------- -static dsc* executeConstantExpressionWithRequest(thread_db* tdbb, CompilerScratch* csb) -{ - Statement* statement = Statement::makeStatement(tdbb, csb, true); - Request* request = statement->makeRootRequest(tdbb); - - Attachment* attachment = tdbb->getAttachment(); - jrd_tra* transaction = tdbb->getTransaction(); - - tdbb->setRequest(request); - request->setUsed(); - request->setAttachment(attachment); - attachment->att_requests.add(request); - - TRA_attach_request(transaction, request); - - ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output - return EVL_expr(tdbb, request, valueNode); -} - -static void executeConstantExpression(thread_db* tdbb, CompilerScratch* csb, MemoryPool& pool, impure_value& value) -{ - Statement* statement = Statement::makeStatement(tdbb, csb, true); - Request* request = statement->makeRootRequest(tdbb); - - const dsc* temp = EVL_expr(tdbb, request, static_cast(csb->csb_node)); - - EVL_make_value(tdbb, temp, &value, &pool); - statement->release(tdbb); -} - - -// Convert a literalNode-unsupported constant type to a supported one if necessary -static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const dsc& scalar) -{ - // Make the blr with only the LiteralNode - BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); - blr.clear(); - dsqlScratch->getDebugData().clear(); - - dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); - // Convert a literalNode-unsupported constant type to a supported one if necessary - switch (scalar.dsc_dtype) - { - case dtype_varying: - case dtype_cstring: - { - // Convert to dtype_text - TTypeId ttype; - UCHAR* ptr; - auto& status = tdbb->getAttachment()->att_dec_status; - const USHORT len = CVT_get_string_ptr(&scalar, &ttype, &ptr, nullptr, 0, status); - - dsc text; - text.makeText(len, ttype, ptr); - LiteralNode::genConstant(dsqlScratch, &text, false); - break; - } - - case dtype_real: - { - double newValue = *(float*) scalar.dsc_address; - - dsc descForDouble{}; - descForDouble.makeDouble(); - - UCHAR* ptr; - VaryStr temp; - TTypeId ttype; - ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp)); - - descForDouble.dsc_address = ptr; - - LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); - break; - } - - case dtype_dec64: // dtype_dec64 is not present in LiteralNode::genConstant - case dtype_double: // MOV_move stores double in scalar->dsc_address. Convert it to string to use genConstant - case dtype_dec128: - { - dsc descForDouble{}; - descForDouble.makeDecimal128(); - - UCHAR* ptr; - VaryStr temp; - TTypeId ttype; - ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp)); - - descForDouble.dsc_address = ptr; - - LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len); - break; - } - - case dtype_int128: - { - dsc descForInt128{}; - descForInt128.makeInt128(scalar.dsc_scale); - - UCHAR* ptr; - VaryStr temp; - TTypeId ttype; - ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp)); - - descForInt128.dsc_address = ptr; - - LiteralNode::genConstant(dsqlScratch, &descForInt128, false, len); - break; - } - - case dtype_blob: // Blob ID will be lost - status_exception::raise(Arg::Gds(isc_bad_constant_type) << scalar.typeToText()); - break; - - default: - LiteralNode::genConstant(dsqlScratch, &scalar, false); - break; - } - dsqlScratch->appendUChar(blr_eoc); -} bool ConstantValue::hash(thread_db* tdbb, Firebird::sha512& digest) const { @@ -223,7 +103,7 @@ dsc ConstantValue::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const Qua } void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - ValueExprNode* constExpr, dsql_fld* type, const QualifiedName& name) + ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema) { BlrWriter::BlrData& blr = dsqlScratch->getBlrData(); blr.clear(); @@ -241,21 +121,18 @@ void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScr ContextPoolHolder context(tdbb, csb_pool); CompilerScratch* csb = nullptr; - Cleanup cc([&tdbb, requestToRestore = tdbb->getRequest(), &csb]() + Cleanup cc([&csb]() { delete csb; - tdbb->setRequest(requestToRestore); }); - // Parse BLR for constant expression and store dependencies - MET_get_dependencies(tdbb, nullptr, + // Parse BLR for constant expression + // It would be cool to calculate the result value and store it as a LiteralNode + // but it is necessary to track dependencies. So keep the full expression as is + PAR_blr(tdbb, &schema, nullptr, dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount(), - nullptr, nullptr, nullptr, &csb, name, obj_package_constant, 0, dsqlScratch->getTransaction()); - - // Execute node from BLR - auto output = executeConstantExpressionWithRequest(tdbb, csb); - if (output != nullptr) - genConstantCompatibleBlr(tdbb, dsqlScratch, *output); + nullptr, &csb, + nullptr, false, 0); } inline bid getConstantBid(thread_db* tdbb, const QualifiedName& name) @@ -279,48 +156,89 @@ inline bid getConstantBid(thread_db* tdbb, const QualifiedName& name) return {}; } -dsc& ConstantValue::makeValue(thread_db* tdbb) +bid ConstantValue::getBlobId(thread_db* tdbb) { - if (m_value.vlu_desc.dsc_address != nullptr) - return m_value.vlu_desc; + return m_blrBlobId.isEmpty() ? getConstantBid(tdbb, name) : m_blrBlobId; +} - Attachment* attachment = tdbb->getAttachment(); +dsc* ConstantValue::makeValue(thread_db* tdbb, Request* request) +{ + { + ReadLockGuard guard(m_makeValueLock, FB_FUNCTION); + if (m_value.vlu_desc.dsc_address != nullptr) + return &m_value.vlu_desc; + } + + WriteLockGuard guard(m_makeValueLock, FB_FUNCTION); + if (m_value.vlu_desc.dsc_address != nullptr) // Extra check in case of two callers waiting + return &m_value.vlu_desc; + Attachment* attachment = tdbb->getAttachment(); MemoryPool* csb_pool = nullptr; + + Cleanup poolCleanup([&]() + { + attachment->att_database->deletePool(csb_pool); + }); + + csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); + try { - csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); ContextPoolHolder context(tdbb, csb_pool); - try + CompilerScratch* csb = nullptr; + Cleanup cc([&]() { - CompilerScratch* csb = nullptr; - Cleanup cc([csb]() {delete csb;}); + delete csb; + }); - // Use cached ID or get a real one (is it probably materialized) - bid constantBid = m_blrBlobId.isEmpty() ? getConstantBid(tdbb, name) : m_blrBlobId; - MET_parse_blob(tdbb, &name.schema, nullptr, &constantBid, &csb, nullptr, false, false); - fb_assert(csb != nullptr); + // Use cached ID or get a real one (is it probably materialized) + bid constantBid = getBlobId(tdbb); - executeConstantExpression(tdbb, csb, getPool(), m_value); - } - catch (const Exception& ex) + MET_parse_blob(tdbb, &name.schema, nullptr, &constantBid, &csb, nullptr, false, false); + fb_assert(csb->csb_node != nullptr); + + if (request == nullptr) { - StaticStatusVector temp_status; - ex.stuffException(temp_status); + // Came from Reload + Statement* handmadeStmt = nullptr; + handmadeStmt = Statement::makeStatement(tdbb, csb, true); + request = handmadeStmt->makeRootRequest(tdbb); + + Cleanup requestRestore([&, requestToRestore = tdbb->getRequest()]() + { + tdbb->setRequest(requestToRestore); + }); + + tdbb->setRequest(request); + request->setUsed(); + request->setAttachment(attachment); + attachment->att_requests.add(request); + + TRA_attach_request(tdbb->getTransaction(), request); - const string quotedName = name.toQuotedString(); - (Arg::Gds(isc_bad_constant_blr) << Arg::Str(quotedName) - << Arg::StatusVector(temp_status.begin())).raise(); + const dsc* temp = EVL_expr(tdbb, request, static_cast(csb->csb_node)); + EVL_make_value(tdbb, temp, &m_value, &getPool()); + } + else + { + const dsc* temp = EVL_expr(tdbb, request, static_cast(csb->csb_node)); + EVL_make_value(tdbb, temp, &m_value, &getPool()); } } - catch (const Exception&) + catch (const Exception& ex) { - attachment->att_database->deletePool(csb_pool); - throw; + StaticStatusVector temp_status; + ex.stuffException(temp_status); + + const string quotedName = name.toQuotedString(); + (Arg::Gds(isc_bad_constant_blr) << Arg::Str(quotedName) + << Arg::StatusVector(temp_status.begin())).raise(); } + csb_pool = nullptr; // Do not delete - return m_value.vlu_desc; + return &m_value.vlu_desc; } ConstantValue& ConstantsCache::add(const QualifiedName& constName, const bool isPrivate) @@ -414,7 +332,7 @@ ScanResult Package::scan(thread_db* tdbb, ObjectBase::Flag flags) skipMakeValue); if (skipMakeValue) - this->m_callReload = true; // Call makeValue in reload + this->m_callReload = true; // Value is necessary for to calculate hash } END_FOR } @@ -495,7 +413,7 @@ ConstantValue& Package::addConstant(thread_db* tdbb, auto& value = constants.add(constName, isPrivate); value.updateValue(blrBlobId); if (!skipMakeValue) - value.makeValue(tdbb); + value.makeValue(tdbb, nullptr); return value; } diff --git a/src/jrd/Package.h b/src/jrd/Package.h index b6a1de9f24d..42fd1504d00 100644 --- a/src/jrd/Package.h +++ b/src/jrd/Package.h @@ -62,11 +62,8 @@ class ConstantValue final : public Firebird::PermanentStorage static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name); static void genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - ValueExprNode* constExpr, dsql_fld* type, const QualifiedName& name); + ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema); - dsc& makeValue(thread_db* tdbb); - - bid getBlobId(); void updateValue(const dsc typeDesc) { @@ -82,7 +79,14 @@ class ConstantValue final : public Firebird::PermanentStorage m_blrBlobId = blobId; } + bid getBlobId(thread_db* tdbb); + + dsc* makeValue(thread_db* tdbb, Request* request); + private: + // Lock in case of makeing value during the execute state + Firebird::RWLock m_makeValueLock{}; + // Keep type to gen hash (when not commited - we cannot read it from system table) // Keep value when scanning and after the first execution impure_value m_value{}; diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index d1d103d9e1c..0de6046e8eb 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1126,27 +1126,60 @@ namespace } } - static bool modifyConstant(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) + static bool createOrAlterConstant(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) { fb_assert(!work->dfw_package.isEmpty()); SET_TDBB(tdbb); - const QualifiedName name(work->dfw_name); switch (phase) { - case 0: - return false; + case 0: + return false; - case 1: - case 2: - case 3: - return true; + case 1: + case 2: + case 3: + return true; - case 4: - MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_constant); + case 4: + { + const QualifiedName constantName = work->getQualifiedName(); + const QualifiedName packageName = constantName.getSchemaAndPackage(); + + auto* package = MetadataCache::getVersioned(tdbb, packageName, + CacheFlag::AUTOCREATE); + + fb_assert(package); + if (package == nullptr) return false; - } + + auto constant = package->findConstant(constantName); + fb_assert(constant); + if (constant == nullptr) + return false; + + bid blobId = constant->getBlobId(tdbb); + + Jrd::Database* dbb = tdbb->getDatabase(); + MemoryPool* new_pool = dbb->createPool(); + // block is used to ensure verify_cache() + // works in not deleted context + { + Jrd::ContextPoolHolder context(tdbb, new_pool); + // Store constant dependencies as a package dependencies + MET_get_dependencies(tdbb, nullptr, nullptr, 0, nullptr, &blobId, + nullptr, + nullptr, + packageName, obj_package_header, + 0, transaction); + + dbb->deletePool(new_pool); + } + + return false; + } // case + } // return false; } @@ -1156,23 +1189,16 @@ namespace fb_assert(!work->dfw_package.isEmpty()); SET_TDBB(tdbb); - + //! Put Package as a dependency because RDB$DEPENDENCIES only tracks object and schema + const QualifiedName packageDependency(work->dfw_package, work->dfw_schema); switch (phase) { - case 0: - return false; - - case 1: - check_dependencies(tdbb, work->getQualifiedName(), NULL, obj_package_constant, transaction); - return true; - - case 2: - case 3: - return true; + case 0: + return false; - case 4: - MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_constant); - return false; + case 1: + check_dependencies(tdbb, work->getQualifiedName(), nullptr, obj_package_constant, transaction); + return false; } return false; @@ -1232,7 +1258,8 @@ static inline constexpr deferred_task task_table[] = { dfw_clear_cache, clear_cache }, { dfw_change_repl_state, change_repl_state }, { dfw_set_statistics, set_statistics }, - { dfw_modify_package_constant, modifyConstant }, + { dfw_create_package_constant, createOrAlterConstant }, + { dfw_modify_package_constant, createOrAlterConstant }, { dfw_delete_package_constant, deleteConstant }, { dfw_create_package, createPackage }, diff --git a/src/jrd/met.epp b/src/jrd/met.epp index f5c9768c7b7..deb084aed95 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -4441,8 +4441,7 @@ void MET_store_dependency(thread_db* tdbb, dpdo_name = &name; break; case obj_package_constant: - name = dependency.name; - dpdo_name = &name; + dpdo_name = &dependency.name; break; } diff --git a/src/jrd/tra.h b/src/jrd/tra.h index c78d687c7d5..08646cb1bb3 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -557,6 +557,7 @@ enum dfw_t : int { dfw_deps_to_disk, // store saved deps to disk // Constant + dfw_create_package_constant, dfw_modify_package_constant, dfw_delete_package_constant, diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 4daf9cfb526..f3e95d4b9c9 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -4698,6 +4698,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_const_package, &desc2); MOV_get_metaname(tdbb, &desc2, object_name.package); + DFW_post_work(transaction, dfw_create_package_constant, &desc, &schemaDesc, 0, object_name.package); break; default: // Shut up compiler warnings From 16647ca824437ec61ca0f541d628e0cbb39df1b8 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 27 Apr 2026 09:42:05 +0300 Subject: [PATCH 085/119] Update src/dsql/PackageNodes.h as suggested Co-authored-by: Dmitry Yemanov --- src/dsql/PackageNodes.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index ff9563c5c0f..ce4179a9699 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -45,7 +45,7 @@ class ItemNames : public TArray ItemNames() : TArray() {} - ItemNames(Firebird::MemoryPool& pool) : TArray(pool) + explicit ItemNames(Firebird::MemoryPool& pool) : TArray(pool) {} operator TArray&() From c481a7269d8391f0c39bb491c6d465dd5af3ed4c Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 14 Apr 2026 20:51:46 +0000 Subject: [PATCH 086/119] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 7df68d6bd0c..d189411cf8d 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:1895 + FORMAL BUILD NUMBER:1900 */ -#define PRODUCT_VER_STRING "6.0.0.1895" -#define FILE_VER_STRING "WI-T6.0.0.1895" -#define LICENSE_VER_STRING "WI-T6.0.0.1895" -#define FILE_VER_NUMBER 6, 0, 0, 1895 +#define PRODUCT_VER_STRING "6.0.0.1900" +#define FILE_VER_STRING "WI-T6.0.0.1900" +#define LICENSE_VER_STRING "WI-T6.0.0.1900" +#define FILE_VER_NUMBER 6, 0, 0, 1900 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "1895" +#define FB_BUILD_NO "1900" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 4989ce5f804..ec02f196886 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=1895 +BuildNum=1900 NowAt=`pwd` cd `dirname $0` From 0174fa595863ec09898bfaba81c81d60fd591818 Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Tue, 14 Apr 2026 15:43:23 +0300 Subject: [PATCH 087/119] Use Windows native implementation of condition variables. --- src/common/classes/condition.h | 66 +++++----------------------------- src/common/classes/locks.h | 2 ++ 2 files changed, 10 insertions(+), 58 deletions(-) diff --git a/src/common/classes/condition.h b/src/common/classes/condition.h index bd943dedfa5..d1c989733ad 100644 --- a/src/common/classes/condition.h +++ b/src/common/classes/condition.h @@ -43,50 +43,19 @@ class MemoryPool; class Condition { private: - AtomicCounter waiters; - - enum - { - SIGNAL = 0, - BROADCAST, - MAX_EVENTS - }; - - HANDLE events[MAX_EVENTS]; + CONDITION_VARIABLE m_condvar; void init() { - events[SIGNAL] = CreateEvent(NULL, // no security - FALSE, // auto-reset event - FALSE, // non-signaled initially - NULL); // unnamed - - if (!events[SIGNAL]) - system_call_failed::raise("CreateEvent(SIGNAL)"); - - // Create a manual-reset event. - events[BROADCAST] = CreateEvent(NULL, // no security - TRUE, // manual-reset - FALSE, // non-signaled initially - NULL); // unnamed - - if (!events[BROADCAST]) - { - CloseHandle(events[SIGNAL]); - system_call_failed::raise("CreateEvent(BROADCAST)"); - } + InitializeConditionVariable(&m_condvar); } public: - Condition() { init(); } + Condition() { init(); } explicit Condition(MemoryPool&) { init(); } ~Condition() { - if (events[SIGNAL] && !CloseHandle(events[SIGNAL])) - system_call_failed::raise("CloseHandle(SIGNAL)"); - if (events[BROADCAST] && !CloseHandle(events[BROADCAST])) - system_call_failed::raise("CloseHandle(BROADCAST)"); } // Forbid copying @@ -95,41 +64,22 @@ class Condition void wait(Mutex& m) { - ++waiters; - - m.leave(); - - if (WaitForMultipleObjects((DWORD) MAX_EVENTS, events, FALSE, INFINITE) == WAIT_FAILED) - system_call_failed::raise("WaitForMultipleObjects"); - - if (--waiters == 0) - { - if (!ResetEvent(events[BROADCAST])) - system_call_failed::raise("ResetEvent(BROADCAST)"); - } - - m.enter("Condition::wait"); + if (!SleepConditionVariableCS(&m_condvar, &m.spinlock, INFINITE)) + system_call_failed::raise("SleepConditionVariableCS"); } void notifyOne() { - if (waiters.value() > 0) - { - if (!SetEvent(events[SIGNAL])) - system_call_failed::raise("SetEvent(SIGNAL)"); - } + WakeConditionVariable(&m_condvar); } void notifyAll() { - if (waiters.value() > 0) - { - if (!SetEvent(events[BROADCAST])) - system_call_failed::raise("SetEvent"); - } + WakeAllConditionVariable(&m_condvar); } }; + } // namespace Firebird #else // WIN_NT diff --git a/src/common/classes/locks.h b/src/common/classes/locks.h index 2c44ca01df3..f1f56e8c82e 100644 --- a/src/common/classes/locks.h +++ b/src/common/classes/locks.h @@ -56,6 +56,8 @@ class Exception; // Needed for catch class Mutex : public Reasons { +friend class Condition; + protected: CRITICAL_SECTION spinlock; #ifdef DEV_BUILD From 610c4b806ceb1ccbb2a94779f63eab32ca5594a1 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 15 Apr 2026 20:44:08 +0000 Subject: [PATCH 088/119] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index d189411cf8d..b388c7d4257 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:1900 + FORMAL BUILD NUMBER:1902 */ -#define PRODUCT_VER_STRING "6.0.0.1900" -#define FILE_VER_STRING "WI-T6.0.0.1900" -#define LICENSE_VER_STRING "WI-T6.0.0.1900" -#define FILE_VER_NUMBER 6, 0, 0, 1900 +#define PRODUCT_VER_STRING "6.0.0.1902" +#define FILE_VER_STRING "WI-T6.0.0.1902" +#define LICENSE_VER_STRING "WI-T6.0.0.1902" +#define FILE_VER_NUMBER 6, 0, 0, 1902 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "1900" +#define FB_BUILD_NO "1902" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index ec02f196886..80f7c31a47b 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=1900 +BuildNum=1902 NowAt=`pwd` cd `dirname $0` From 650c963fc18dd677241c3d10165bbed7558207b4 Mon Sep 17 00:00:00 2001 From: Adriano dos Santos Fernandes <529415+asfernandes@users.noreply.github.com> Date: Thu, 16 Apr 2026 08:53:35 -0300 Subject: [PATCH 089/119] Add optional IN AUTONOMOUS TRANSACTION clause to USING statement (#8987) --- doc/sql.extensions/README.using_statement.md | 34 +++-- src/dsql/StmtNodes.cpp | 137 +++++++++++++++++-- src/dsql/StmtNodes.h | 17 ++- src/dsql/parse.y | 19 ++- src/include/firebird/impl/msg/dsql.h | 2 +- src/include/gen/Firebird.pas | 2 +- 6 files changed, 178 insertions(+), 33 deletions(-) diff --git a/doc/sql.extensions/README.using_statement.md b/doc/sql.extensions/README.using_statement.md index 6dcb395c202..15631cb178f 100644 --- a/doc/sql.extensions/README.using_statement.md +++ b/doc/sql.extensions/README.using_statement.md @@ -10,24 +10,29 @@ input parameter in multiple places), the developer is currently forced to explic more tediously, all output fields. The `USING` statement simplifies this workflow. It provides the ability to declare parameters, sub-routines and -variables while allowing the engine to infer outputs automatically from the contained SQL command. +variables, optionally execute the `DO` command in an autonomous transaction, and still allow the engine to infer +outputs automatically from the contained SQL command. ## Syntax ```sql USING [ ( ) ] [ ] + [ IN AUTONOMOUS TRANSACTION ] DO ``` -**Note:** At least one of `` or `` must be present. A `USING DO ...` statement -without parameters and without local declarations is invalid. +**Note:** At least one of ``, `` or `IN AUTONOMOUS TRANSACTION` must be +present. A `USING DO ...` statement without parameters, without local declarations and without `IN AUTONOMOUS +TRANSACTION` is invalid. ### Components * **``**: A strictly typed list of parameters. These can be bound to values using the `?` placeholder. * **``**: Standard PSQL local declarations (variables, sub-functions and sub-procedures). +* **`IN AUTONOMOUS TRANSACTION`**: Executes the `DO` command in a separate autonomous transaction, reusing the same + semantics already supported by PSQL. * **``**: The DSQL statement to execute. Supported statements include: * `SELECT` * `INSERT` (with or without `RETURNING`) @@ -121,12 +126,21 @@ begin end ``` +### 5. Autonomous Transaction + +```sql +using in autonomous transaction +do insert into audit_log (log_time, message) + values (current_timestamp, 'Entry written in autonomous transaction'); +``` + ## Comparison -| Feature | Standard DSQL | `EXECUTE BLOCK` | `USING` | -| :---------------------- | :------------------------------ | :-------------------------------------------- | :-------------------------------------------------------- | -| **Local Declarations** | No | Yes | Yes | -| **Input Declarations** | Implicit (Positional) | Explicit | Hybrid (implicit and explicit) | -| **Output Declarations** | Inferred | Explicit (`RETURNS`) | Inferred | -| **Verbosity** | Low | High | Medium | -| **Use Case** | Simple queries | Complex logic, loops, no result set inference | Reusing params, variables, sub-routines, standard queries | +| Feature | Standard DSQL | `EXECUTE BLOCK` | `USING` | +| :------------------------- | :------------------------------ | :-------------------------------------------- | :-------------------------------------------------------- | +| **Local Declarations** | No | Yes | Yes | +| **Input Declarations** | Implicit (Positional) | Explicit | Hybrid (implicit and explicit) | +| **Output Declarations** | Inferred | Explicit (`RETURNS`) | Inferred | +| **Verbosity** | Low | High | Medium | +| **Use Case** | Simple queries | Complex logic, loops, no result set inference | Reusing params, variables, sub-routines, standard queries | +| **Autonomous transaction** | No | Yes (without `SUSPEND`) | Yes | diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index dd11eab52df..6b4fca8a07e 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -2410,6 +2410,9 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch) GEN_port(dsqlScratch, dsqlScratch->recordKeyMessage); std::optional tableNumber; + const bool useInternalAutoTrans = + (dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) && + dsqlReturning && !dsqlScratch->isPsql() && dsqlCursorName.isEmpty(); const bool skipLocked = dsqlRse && dsqlRse->hasSkipLocked(); @@ -2430,6 +2433,13 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch) } } + if (useInternalAutoTrans) + { + dsqlScratch->appendUChar(blr_auto_trans); + dsqlScratch->appendUChar(0); + dsqlScratch->appendUChar(blr_begin); + } + if (dsqlRse) { dsqlScratch->appendUChar(blr_for); @@ -2460,6 +2470,9 @@ void EraseNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (!dsqlScratch->isPsql() && dsqlCursorName.isEmpty()) { + if (useInternalAutoTrans) + dsqlScratch->appendUChar(blr_end); + dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, tableNumber.value()); if (!skipLocked) @@ -4713,16 +4726,12 @@ DmlNode* InAutonomousTransactionNode::parse(thread_db* tdbb, MemoryPool& pool, C InAutonomousTransactionNode* InAutonomousTransactionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - const bool autoTrans = dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK; - dsqlScratch->flags |= DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK; + AutoSetRestoreFlag autoTrans(&dsqlScratch->flags, DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK, true); InAutonomousTransactionNode* node = FB_NEW_POOL(dsqlScratch->getPool()) InAutonomousTransactionNode( dsqlScratch->getPool()); node->action = action->dsqlPass(dsqlScratch); - if (!autoTrans) - dsqlScratch->flags &= ~DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK; - return node; } @@ -4737,9 +4746,33 @@ string InAutonomousTransactionNode::internalPrint(NodePrinter& printer) const void InAutonomousTransactionNode::genBlr(DsqlCompilerScratch* dsqlScratch) { - dsqlScratch->appendUChar(blr_auto_trans); - dsqlScratch->appendUChar(0); // to extend syntax in the future - action->genBlr(dsqlScratch); + bool useInternalAutoTrans = false; + + if (dsqlScratch && !dsqlScratch->isPsql()) + { + if (const auto modifyNode = nodeAs(action)) + useInternalAutoTrans = modifyNode->dsqlReturning && modifyNode->dsqlCursorName.isEmpty(); + else if (const auto eraseNode = nodeAs(action)) + useInternalAutoTrans = eraseNode->dsqlReturning && eraseNode->dsqlCursorName.isEmpty(); + else if (const auto storeNode = nodeAs(action)) + useInternalAutoTrans = storeNode->dsqlReturning != nullptr; + else if (const auto updateOrInsertNode = nodeAs(action)) + useInternalAutoTrans = updateOrInsertNode->returning != nullptr; + else if (const auto mergeNode = nodeAs(action)) + useInternalAutoTrans = mergeNode->returning != nullptr; + } + + if (useInternalAutoTrans) + { + AutoSetRestoreFlag autoTrans(&dsqlScratch->flags, DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK, true); + action->genBlr(dsqlScratch); + } + else + { + dsqlScratch->appendUChar(blr_auto_trans); + dsqlScratch->appendUChar(0); // to extend syntax in the future + action->genBlr(dsqlScratch); + } } InAutonomousTransactionNode* InAutonomousTransactionNode::pass1(thread_db* tdbb, CompilerScratch* csb) @@ -7246,6 +7279,9 @@ string MergeNode::internalPrint(NodePrinter& printer) const void MergeNode::genBlr(DsqlCompilerScratch* dsqlScratch) { std::optional tableNumber; + const bool useInternalAutoTrans = + (dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) && + returning && !dsqlScratch->isPsql(); if (returning && !dsqlScratch->isPsql()) { @@ -7255,6 +7291,13 @@ void MergeNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlGenReturningLocalTableDecl(dsqlScratch, tableNumber.value()); } + if (useInternalAutoTrans) + { + dsqlScratch->appendUChar(blr_auto_trans); + dsqlScratch->appendUChar(0); + dsqlScratch->appendUChar(blr_begin); + } + // Put src info for blr_for. if (hasLineColumn) dsqlScratch->putDebugSrcInfo(line, column); @@ -7493,6 +7536,9 @@ void MergeNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlScratch->appendUChar(blr_end); } + if (useInternalAutoTrans) + dsqlScratch->appendUChar(blr_end); + if (returning && !dsqlScratch->isPsql()) { dsqlGenReturningLocalTableCursor(dsqlScratch, returning, tableNumber.value()); @@ -7949,6 +7995,15 @@ string ModifyNode::internalPrint(NodePrinter& printer) const void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch) { + const bool useInternalAutoTrans = + (dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) && + dsqlReturning && !dsqlScratch->isPsql() && dsqlCursorName.isEmpty() && + dsqlReturningLocalTableNumber.has_value() && + !(dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT); + const bool deferUpdateOrInsertLocalTableDecl = + (dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT) && + (dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK); + if (dsqlScratch->recordKeyMessage) { GEN_port(dsqlScratch, dsqlScratch->recordKeyMessage); @@ -7957,7 +8012,10 @@ void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (dsqlReturning && !dsqlScratch->isPsql()) { if (dsqlCursorName.isEmpty()) - dsqlGenReturningLocalTableDecl(dsqlScratch, dsqlReturningLocalTableNumber.value()); + { + if (!deferUpdateOrInsertLocalTableDecl) + dsqlGenReturningLocalTableDecl(dsqlScratch, dsqlReturningLocalTableNumber.value()); + } else { dsqlScratch->appendUChar(blr_send); @@ -7965,6 +8023,13 @@ void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch) } } + if (useInternalAutoTrans) + { + dsqlScratch->appendUChar(blr_auto_trans); + dsqlScratch->appendUChar(0); + dsqlScratch->appendUChar(blr_begin); + } + if (dsqlRse) { dsqlScratch->appendUChar(blr_for); @@ -8002,6 +8067,9 @@ void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch) !(dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT) && dsqlCursorName.isEmpty()) { + if (useInternalAutoTrans) + dsqlScratch->appendUChar(blr_end); + dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, dsqlReturningLocalTableNumber.value()); } } @@ -9002,6 +9070,11 @@ string StoreNode::internalPrint(NodePrinter& printer) const void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch) { + const bool useInternalAutoTrans = + (dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) && + dsqlReturning && !dsqlScratch->isPsql() && dsqlReturningLocalTableNumber.has_value() && + !(dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT); + if (dsqlReturning && !dsqlScratch->isPsql()) { if (dsqlRse) @@ -9013,6 +9086,13 @@ void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch) } } + if (useInternalAutoTrans) + { + dsqlScratch->appendUChar(blr_auto_trans); + dsqlScratch->appendUChar(0); + dsqlScratch->appendUChar(blr_begin); + } + if (dsqlRse) { dsqlScratch->appendUChar(blr_for); @@ -9035,10 +9115,18 @@ void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch) if (dsqlReturningLocalTableNumber.has_value()) { + const bool deferUpdateOrInsertCursor = + (dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT) && + (dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK); + if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT) dsqlScratch->appendUChar(blr_end); // close blr_if (blr_eql, blr_internal_info) - dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, dsqlReturningLocalTableNumber.value()); + if (useInternalAutoTrans) + dsqlScratch->appendUChar(blr_end); + + if (!deferUpdateOrInsertCursor) + dsqlGenReturningLocalTableCursor(dsqlScratch, dsqlReturning, dsqlReturningLocalTableNumber.value()); } } else if (overrideClause.has_value()) @@ -10745,6 +10833,19 @@ string UpdateOrInsertNode::internalPrint(NodePrinter& printer) const void UpdateOrInsertNode::genBlr(DsqlCompilerScratch* dsqlScratch) { + const bool useInternalAutoTrans = + (dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) && + storeNode->dsqlReturningLocalTableNumber.has_value() && !dsqlScratch->isPsql(); + + if (useInternalAutoTrans) + { + dsqlGenReturningLocalTableDecl(dsqlScratch, storeNode->dsqlReturningLocalTableNumber.value()); + + dsqlScratch->appendUChar(blr_auto_trans); + dsqlScratch->appendUChar(0); + dsqlScratch->appendUChar(blr_begin); + } + dsqlScratch->appendUChar(blr_begin); for (auto& varAssign : varAssignments) @@ -10777,6 +10878,13 @@ void UpdateOrInsertNode::genBlr(DsqlCompilerScratch* dsqlScratch) dsqlScratch->appendUChar(blr_end); // blr_if dsqlScratch->appendUChar(blr_end); + + if (useInternalAutoTrans) + { + dsqlScratch->appendUChar(blr_end); + dsqlGenReturningLocalTableCursor(dsqlScratch, storeNode->dsqlReturning, + storeNode->dsqlReturningLocalTableNumber.value()); + } } @@ -10959,14 +11067,15 @@ void UserSavepointNode::execute(thread_db* tdbb, DsqlRequest* request, jrd_tra** StmtNode* UsingNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - // USING without parameters and subroutines is useless - if (parameters.isEmpty() && !localDeclList) + // USING without parameters, subroutines or autonomous transaction is useless + if (parameters.isEmpty() && !localDeclList && !inAutonomousTransaction) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << - Arg::Gds(isc_dsql_using_requires_params_subroutines)); + Arg::Gds(isc_dsql_using_statement_must_contain_clause)); } dsqlScratch->flags |= DsqlCompilerScratch::FLAG_USING_STATEMENT; + dsqlScratch->reserveInitialVarNumbers(parameters.getCount()); const auto statement = dsqlScratch->getDsqlStatement(); unsigned index = 0; @@ -10974,6 +11083,7 @@ StmtNode* UsingNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) const auto node = FB_NEW_POOL(dsqlScratch->getPool()) UsingNode(dsqlScratch->getPool()); node->parameters = parameters; + node->inAutonomousTransaction = inAutonomousTransaction; for (auto newParam : node->parameters) { @@ -11036,6 +11146,7 @@ string UsingNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, parameters); NODE_PRINT(printer, localDeclList); NODE_PRINT(printer, body); + NODE_PRINT(printer, inAutonomousTransaction); return "UsingNode"; } diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index 901224784ff..2ddd8281d6d 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -1544,6 +1544,14 @@ class SavepointEncloseNode final : public TypedNode(pool), + statement(stmt) + { + } + +public: Firebird::string internalPrint(NodePrinter& printer) const override; SavepointEncloseNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override; void genBlr(DsqlCompilerScratch* dsqlScratch) override; @@ -1553,13 +1561,7 @@ class SavepointEncloseNode final : public TypedNode(pool), - statement(stmt) - { - } - +public: NestConst statement; }; @@ -2116,6 +2118,7 @@ class UsingNode final : public TypedNode Firebird::Array> parameters; NestConst localDeclList; NestConst body; + bool inAutonomousTransaction = false; }; diff --git a/src/dsql/parse.y b/src/dsql/parse.y index b37203db3e0..d19ff49d372 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -4213,16 +4213,33 @@ using { $$ = newNode(); } block_input_params(NOTRIAL(&$2->parameters)) local_declarations_opt + using_autonomous_opt DO using_dml_statement { const auto node = $2; node->localDeclList = $4; - node->body = $6; + node->inAutonomousTransaction = $5; + + if ($5) + { + const auto autoNode = newNode(); + autoNode->action = $7; + node->body = autoNode; + } + else + node->body = $7; + $$ = node; } ; +%type using_autonomous_opt +using_autonomous_opt + : /* nothing */ { $$ = false; } + | IN AUTONOMOUS TRANSACTION { $$ = true; } + ; + %type using_dml_statement using_dml_statement : call { $$ = $1; } diff --git a/src/include/firebird/impl/msg/dsql.h b/src/include/firebird/impl/msg/dsql.h index 22acfc895ef..94c249bdbb4 100644 --- a/src/include/firebird/impl/msg/dsql.h +++ b/src/include/firebird/impl/msg/dsql.h @@ -39,4 +39,4 @@ FB_IMPL_MSG(DSQL, 39, dsql_wrong_param_num, -313, "07", "001", "Wrong number of FB_IMPL_MSG(DSQL, 40, dsql_invalid_drop_ss_clause, -817, "42", "000", "Invalid DROP SQL SECURITY clause") FB_IMPL_MSG(DSQL, 41, upd_ins_cannot_default, -313, "42", "000", "UPDATE OR INSERT value for field @1, part of the implicit or explicit MATCHING clause, cannot be DEFAULT") FB_IMPL_MSG(DSQL, 42, dsql_ltt_invalid_reference, -607, "42", "000", "LOCAL TEMPORARY TABLE @1 cannot be referenced in @2") -FB_IMPL_MSG(DSQL, 43, dsql_using_requires_params_subroutines, -104, "42", "000", "USING requires parameters or subroutines") +FB_IMPL_MSG(DSQL, 43, dsql_using_statement_must_contain_clause, -104, "42", "000", "USING statement must contain at least one clause") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 06f4012f565..95d58b4e1a9 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -6039,7 +6039,7 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_dsql_invalid_drop_ss_clause = 336003112; isc_upd_ins_cannot_default = 336003113; isc_dsql_ltt_invalid_reference = 336003114; - isc_dsql_using_requires_params_subroutines = 336003115; + isc_dsql_using_statement_must_contain_clause = 336003115; isc_dyn_filter_not_found = 336068645; isc_dyn_func_not_found = 336068649; isc_dyn_index_not_found = 336068656; From 234f4f556c2dcf583953c707823bb41bfb69796d Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Thu, 16 Apr 2026 13:34:16 +0300 Subject: [PATCH 090/119] Misc --- src/jrd/Relation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/Relation.cpp b/src/jrd/Relation.cpp index c577c924abe..7dd43088392 100644 --- a/src/jrd/Relation.cpp +++ b/src/jrd/Relation.cpp @@ -350,7 +350,7 @@ RelationPages* RelationPermanent::getPagesInternal(thread_db* tdbb, TraNumber tr fb_assert(rel); IndexDescList indices; - BTR_all(tdbb, getPermanent(rel), indices, &rel_pages_base); + BTR_all(tdbb, rel->getPermanent(), indices, &rel_pages_base); for (auto& idx : indices) { From 01200526fed138ecd0db758d79347966a0596fa1 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Thu, 16 Apr 2026 20:07:24 +0300 Subject: [PATCH 091/119] This should fix #8986: Race condition between VIO_update_in_place and the garbage collector; update RDB$DATABASE in regular autonomous transaction --- src/dsql/DdlNodes.epp | 36 ++++++++++++++++++++++++++---------- src/jrd/SystemTriggers.epp | 16 ++++++++++------ 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 90162c30e8c..f8191b0c781 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -6611,7 +6611,7 @@ bool RelationNode::checkDeletedId(thread_db* tdbb, MetaId& relId) AutoCacheRequest request(tdbb, requestCacheId); Attachment* attachment = tdbb->getAttachment(); - FOR (REQUEST_HANDLE request) + FOR (REQUEST_HANDLE request TRANSACTION_HANDLE attachment->getMetaTransaction(tdbb)) P IN RDB$PAGES WITH P.RDB$RELATION_ID EQ relId { @@ -6650,10 +6650,10 @@ MetaId RelationNode::generateRelId(thread_db* tdbb, MetaName name) Attachment* attachment = tdbb->getAttachment(); MetaId relId = 0; - static const CachedRequestId idDb; - AutoCacheRequest requestDb(tdbb, idDb); + AUTO_HANDLE(requestDb); + auto* metaTransaction = attachment->getMetaTransaction(tdbb); - FOR(REQUEST_HANDLE requestDb) + FOR(REQUEST_HANDLE requestDb TRANSACTION_HANDLE metaTransaction) D IN RDB$DATABASE { MetaId startId = D.RDB$RELATION_ID; @@ -6669,10 +6669,9 @@ MetaId RelationNode::generateRelId(thread_db* tdbb, MetaName name) relId = startId; enum {MISS, VALID, FREE} found = FREE; - static const CachedRequestId idRels; - AutoCacheRequest requestRels(tdbb, idRels); + AUTO_HANDLE(requestRels); - FOR(REQUEST_HANDLE requestRels) + FOR(REQUEST_HANDLE requestRels TRANSACTION_HANDLE metaTransaction) R IN RDB$RELATIONS WITH R.RDB$RELATION_ID GE startId AND R.RDB$RELATION_ID LT stopId @@ -6692,9 +6691,26 @@ MetaId RelationNode::generateRelId(thread_db* tdbb, MetaName name) if (found == VALID) { - MODIFY D USING - D.RDB$RELATION_ID = relId + 1; - END_MODIFY + AUTO_HANDLE(requestMod); + + jrd_tra* previous = tdbb->getTransaction(); + jrd_tra* traMod = TRA_start(tdbb, 0, DEFAULT_LOCK_TIMEOUT); + Cleanup rollback([&]() + { + if (traMod) + TRA_rollback(tdbb, traMod, false, true); + tdbb->setTransaction(previous); + }); + + FOR(REQUEST_HANDLE requestMod TRANSACTION_HANDLE traMod) + DBMOD IN RDB$DATABASE + MODIFY DBMOD USING + DBMOD.RDB$RELATION_ID = relId + 1; + END_MODIFY + END_FOR + + TRA_commit(tdbb, traMod, false); + traMod = nullptr; // !!!!!! printf(" %d\n", relId); fflush(stdout); return relId; diff --git a/src/jrd/SystemTriggers.epp b/src/jrd/SystemTriggers.epp index ddc6fbed78a..c0a4bbb1031 100644 --- a/src/jrd/SystemTriggers.epp +++ b/src/jrd/SystemTriggers.epp @@ -1590,14 +1590,18 @@ void afterInsertRelation(thread_db* tdbb, Record* record) // Do not access external file info from cache - it may be not usable yet // On contrary rel_flags (and therefore check for view) are OK - if (relation && !EVL_field(nullptr, record, f_rel_ext_file, &desc) && - !relation->isView()) + fb_assert(relation); + if (relation) { - // create the table - DPM_create_relation(tdbb, relation); - } + if (!EVL_field(nullptr, record, f_rel_ext_file, &desc) && + !relation->isView()) + { + // create the table + DPM_create_relation(tdbb, relation); + } - relation->rel_flags |= REL_get_dependencies; + relation->rel_flags |= REL_get_dependencies; + } } } From 31e2946f2b10508041f7ac32b3f3b2e9eff34a3d Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Thu, 16 Apr 2026 20:44:46 +0000 Subject: [PATCH 092/119] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index b388c7d4257..2465ae68315 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:1902 + FORMAL BUILD NUMBER:1905 */ -#define PRODUCT_VER_STRING "6.0.0.1902" -#define FILE_VER_STRING "WI-T6.0.0.1902" -#define LICENSE_VER_STRING "WI-T6.0.0.1902" -#define FILE_VER_NUMBER 6, 0, 0, 1902 +#define PRODUCT_VER_STRING "6.0.0.1905" +#define FILE_VER_STRING "WI-T6.0.0.1905" +#define LICENSE_VER_STRING "WI-T6.0.0.1905" +#define FILE_VER_NUMBER 6, 0, 0, 1905 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "1902" +#define FB_BUILD_NO "1905" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 80f7c31a47b..f1b9670ec03 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=1902 +BuildNum=1905 NowAt=`pwd` cd `dirname $0` From 8eea06aebf7a0d16852c2c0688cb9a3d8e533416 Mon Sep 17 00:00:00 2001 From: "aleksey.mochalov" Date: Thu, 16 Apr 2026 12:17:20 +0300 Subject: [PATCH 093/119] Fix EDS connection use after free Raise EDS rollback exception before the connection can be released by deleteTransaction(). Cleanup is still performed by catching the error and rethrow the original exception. --- src/jrd/extds/ExtDS.cpp | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/src/jrd/extds/ExtDS.cpp b/src/jrd/extds/ExtDS.cpp index 414e8da4c69..2c0a5fad905 100644 --- a/src/jrd/extds/ExtDS.cpp +++ b/src/jrd/extds/ExtDS.cpp @@ -1639,15 +1639,30 @@ void Transaction::rollback(thread_db* tdbb, bool retain) doRollback(&status, tdbb, retain); Connection& conn = m_connection; - if (!retain) + const bool hasErrors = status->getState() & IStatus::STATE_ERRORS; + + const auto cleanup = [&]() { - detachFromJrdTran(); - m_connection.deleteTransaction(tdbb, this); - } + if (!retain) + { + detachFromJrdTran(); + m_connection.deleteTransaction(tdbb, this); + } + }; - if (status->getState() & IStatus::STATE_ERRORS) { - conn.raise(&status, tdbb, "transaction rollback"); + try + { + if (hasErrors) { + conn.raise(&status, tdbb, "transaction rollback"); + } + } + catch (const Exception&) + { + cleanup(); + throw; } + + cleanup(); } Transaction* Transaction::getTransaction(thread_db* tdbb, Connection* conn, TraScope tra_scope) From c549f4bea26ff80aa8b53be10a8de2b9e23f451d Mon Sep 17 00:00:00 2001 From: "aleksey.mochalov" Date: Mon, 20 Apr 2026 14:51:21 +0300 Subject: [PATCH 094/119] fix gbak restore buffer sizing by reserving space for the next record marker --- src/burp/restore.epp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/burp/restore.epp b/src/burp/restore.epp index ae52a139f44..9600f3bb633 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -14005,7 +14005,10 @@ bool RestoreRelationTask::fileReader(Item& item) const FB_SSIZE_T attLen = 1 + (tdgbl->gbl_sw_transportable ? 12 : 6); const FB_SSIZE_T overhead = tdgbl->gbl_sw_compress ? (len / 127 + 1) : 0; - if (static_cast(len) + attLen + overhead > space) + // Reserve space for the after payload rec_* marker: rec_data or rec_relation_end. + const FB_SSIZE_T nextRecordMarker = 1; + + if ((static_cast(len) + attLen + overhead + nextRecordMarker) > space) { if (ioBuf) { From f1c3e53f68d250849df57d059d361afe124bfeff Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Mon, 20 Apr 2026 21:09:43 +0300 Subject: [PATCH 095/119] First attempt to #8995: Excessive predicate evaluation in multi-level LEFT JOINs. I tried it in 2022 but found regressions. Now I cannot reproduce them locally after the fix, so I'm willing to analyze the complete QA results. --- src/jrd/optimizer/Optimizer.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 74a02f8d973..105b80a00ad 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -679,20 +679,21 @@ RecordSource* Optimizer::compile(RseNode* subRse, BoolExprNodeStack* parentStack Optimizer subOpt(tdbb, csb, subRse, subFirstRows); const auto rsb = subOpt.compile(parentStack); - if (parentStack && subOpt.isInnerJoin()) + if (parentStack) { - // If any parent conjunct was utilized, update our copy of its flags. - // Currently used for inner joins only, although could also be applied - // to conjuncts utilized for outer streams of outer joins. + // If any parent conjunct was utilized, update our copy of its flags for (auto subIter = subOpt.getParentConjuncts(); subIter.hasData(); ++subIter) { - for (auto selfIter = getConjuncts(); selfIter.hasData(); ++selfIter) + if (subIter & CONJUNCT_USED) { - if (*selfIter == *subIter) + for (auto selfIter = getConjuncts(); selfIter.hasData(); ++selfIter) { - selfIter |= subIter.getFlags(); - break; + if (*selfIter == *subIter) + { + selfIter |= subIter.getFlags(); + break; + } } } } From a0ec40b6779bc57b8441013e3aae0d55bdb79690 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Mon, 20 Apr 2026 20:43:07 +0000 Subject: [PATCH 096/119] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 2465ae68315..18cf28c31c3 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:1905 + FORMAL BUILD NUMBER:1910 */ -#define PRODUCT_VER_STRING "6.0.0.1905" -#define FILE_VER_STRING "WI-T6.0.0.1905" -#define LICENSE_VER_STRING "WI-T6.0.0.1905" -#define FILE_VER_NUMBER 6, 0, 0, 1905 +#define PRODUCT_VER_STRING "6.0.0.1910" +#define FILE_VER_STRING "WI-T6.0.0.1910" +#define LICENSE_VER_STRING "WI-T6.0.0.1910" +#define FILE_VER_NUMBER 6, 0, 0, 1910 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "1905" +#define FB_BUILD_NO "1910" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index f1b9670ec03..8cecbb059c3 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=1905 +BuildNum=1910 NowAt=`pwd` cd `dirname $0` From 0ab5acdbf2d7a80f2ca9287b9927a73545310994 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Tue, 21 Apr 2026 09:42:25 +0300 Subject: [PATCH 097/119] Fix some cases with outer joins, predicates pushed into the inner substream must be re-evaluated after the join --- src/jrd/optimizer/Optimizer.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/jrd/optimizer/Optimizer.cpp b/src/jrd/optimizer/Optimizer.cpp index 105b80a00ad..96b4c033989 100644 --- a/src/jrd/optimizer/Optimizer.cpp +++ b/src/jrd/optimizer/Optimizer.cpp @@ -679,9 +679,10 @@ RecordSource* Optimizer::compile(RseNode* subRse, BoolExprNodeStack* parentStack Optimizer subOpt(tdbb, csb, subRse, subFirstRows); const auto rsb = subOpt.compile(parentStack); - if (parentStack) + if (parentStack && !subRse->isFullJoin()) { - // If any parent conjunct was utilized, update our copy of its flags + // If any parent conjunct was utilized, update our copy of its flags. + // Except those utilized for inner streams of outer joins, they must be re-checked. for (auto subIter = subOpt.getParentConjuncts(); subIter.hasData(); ++subIter) { @@ -691,7 +692,22 @@ RecordSource* Optimizer::compile(RseNode* subRse, BoolExprNodeStack* parentStack { if (*selfIter == *subIter) { - selfIter |= subIter.getFlags(); + if (subRse->isInnerJoin()) + { + selfIter |= subIter.getFlags(); + } + else if (subRse->isOuterJoin()) + { + const auto innerSubRse = subRse->rse_relations[1]; + StreamList innerSubStreams; + innerSubRse->computeRseStreams(innerSubStreams); + + if (!subIter->containsAnyStream(innerSubStreams)) + { + selfIter |= subIter.getFlags(); + } + } + break; } } From f8e1775d9fb1ece3c1ccd33dc2c5343c50b1f4e6 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Tue, 21 Apr 2026 15:52:30 +0300 Subject: [PATCH 098/119] Completed support of GTT in shared metadata cache --- src/jrd/CacheVector.cpp | 16 ++++++++++- src/jrd/CacheVector.h | 22 +++++++++------ src/jrd/CharSetContainer.h | 2 +- src/jrd/Relation.cpp | 32 +++++++++++++++++++-- src/jrd/Relation.h | 17 ++++++++--- src/jrd/Routine.h | 2 +- src/jrd/btr.cpp | 55 +++++++++++++++++++++--------------- src/jrd/btr_proto.h | 6 ++-- src/jrd/dfw.epp | 14 +++------ src/jrd/idx.cpp | 58 +++++++++++++++++++++++++++++++++++--- src/jrd/idx_proto.h | 6 ++-- 11 files changed, 171 insertions(+), 59 deletions(-) diff --git a/src/jrd/CacheVector.cpp b/src/jrd/CacheVector.cpp index 8b9b7be388f..274d3c4e441 100644 --- a/src/jrd/CacheVector.cpp +++ b/src/jrd/CacheVector.cpp @@ -131,6 +131,9 @@ int ElementBase::blockingAst(void* ast_object) AsyncContextHolder tdbb(dbb, FB_FUNCTION); + TraNumber tran = LCK_read_data(tdbb, cacheElement->lock); + fb_assert(tran); + LCK_downgrade(tdbb, cacheElement->lock); const bool erase = (cacheElement->lock->lck_physical < LCK_SR); if (!erase) @@ -139,7 +142,7 @@ int ElementBase::blockingAst(void* ast_object) cacheElement->locked = false; } - cacheElement->reset(tdbb, erase); + cacheElement->reset(tdbb, tran, erase); } } catch (const Exception&) @@ -164,6 +167,17 @@ void ElementBase::pingLock(thread_db* tdbb, ObjectBase::Flag flags, MetaId id, c if (!locked) setLock(tdbb, id, family); + auto tra = tdbb->getTransaction(); + fb_assert(tra); + if (tra) + { + auto tran = tra->tra_number; + if (!tran) // called from system transaction + return; + + LCK_write_data(tdbb, lock, tran); + } + if (!LCK_lock(tdbb, lock, (flags & CacheFlag::ERASED) ? LCK_EX : LCK_PW, LCK_WAIT)) { Firebird::fatal_exception::raiseFmt("Unable to obtain WRITE rescan lock for %s %d", diff --git a/src/jrd/CacheVector.h b/src/jrd/CacheVector.h index ab0aa503832..f00add5077b 100644 --- a/src/jrd/CacheVector.h +++ b/src/jrd/CacheVector.h @@ -69,7 +69,7 @@ class ElementBase { } private: - virtual void reset(thread_db* tdbb, bool erase) = 0; + virtual void reset(thread_db* tdbb, TraNumber tran, bool erase) = 0; static int blockingAst(void* ast_object); public: @@ -785,14 +785,15 @@ class CacheElement : public ElementBase, public P return list || hasLock(); } - StoreResult storeObject(thread_db* tdbb, Versioned* obj, ObjectBase::Flag fl) + StoreResult storeObject(thread_db* tdbb, Versioned* obj, ObjectBase::Flag fl, TraNumber cur = 0) { TraNumber oldest = TransactionNumber::oldestActive(tdbb); TraNumber oldResetAt = resetAt.load(atomics::memory_order_acquire); if (oldResetAt && oldResetAt < oldest) setNewResetAt(oldResetAt, ListEntry::gc(tdbb, &list, oldest)); - TraNumber cur = TransactionNumber::current(tdbb); + if (!cur) + cur = TransactionNumber::current(tdbb); ListEntry* newEntry = FB_NEW_POOL(*getDefaultMemoryPool()) ListEntry(obj, cur, fl); if (!ListEntry::add(tdbb, list, newEntry)) { @@ -820,7 +821,7 @@ class CacheElement : public ElementBase, public P } if (!(fl & CacheFlag::NOCOMMIT)) - commit(tdbb); + commit(tdbb, cur); return rc; } @@ -848,12 +849,15 @@ class CacheElement : public ElementBase, public P return nullptr; } - void commit(thread_db* tdbb) + void commit(thread_db* tdbb, TraNumber cur = 0) { HazardPtr> current(list); if (current) { - auto flags = current->commit(tdbb, TransactionNumber::current(tdbb), TransactionNumber::next(tdbb)); + if (!cur) + cur = TransactionNumber::current(tdbb); + + auto flags = current->commit(tdbb, cur, TransactionNumber::next(tdbb)); if (flags & CacheFlag::NOCOMMIT) // Committed newly created version in cache pingLock(tdbb, flags, this->getId(), Versioned::objectFamily(this)); @@ -878,10 +882,10 @@ class CacheElement : public ElementBase, public P private: // called by AST handler - void reset(thread_db* tdbb, bool erase) override + void reset(thread_db* tdbb, TraNumber tran, bool erase) override { - storeObject(tdbb, nullptr, erase ? CacheFlag::ERASED : 0); - Permanent::reloadAst(tdbb, erase); + storeObject(tdbb, nullptr, erase ? CacheFlag::ERASED : 0, tran); + Permanent::reloadAst(tdbb, tran, erase); } public: diff --git a/src/jrd/CharSetContainer.h b/src/jrd/CharSetContainer.h index b3391b02608..ba0e17a10e4 100644 --- a/src/jrd/CharSetContainer.h +++ b/src/jrd/CharSetContainer.h @@ -56,7 +56,7 @@ class CharSetContainer : public Firebird::PermanentStorage static bool destroy(thread_db* tdbb, CharSetContainer* container); static CharSetContainer* create(thread_db* tdbb, MetaId id); void releaseLock(thread_db* tdbb) { } - void reloadAst(thread_db* tdbb, bool erase) { } + void reloadAst(thread_db* tdbb, TraNumber tran, bool erase) { } Firebird::CharSet* getCharSet() { diff --git a/src/jrd/Relation.cpp b/src/jrd/Relation.cpp index 7dd43088392..42bba3981f1 100644 --- a/src/jrd/Relation.cpp +++ b/src/jrd/Relation.cpp @@ -349,10 +349,11 @@ RelationPages* RelationPermanent::getPagesInternal(thread_db* tdbb, TraNumber tr jrd_rel* rel = MetadataCache::getVersioned(tdbb, getId(), CacheFlag::AUTOCREATE); fb_assert(rel); - IndexDescList indices; - BTR_all(tdbb, rel->getPermanent(), indices, &rel_pages_base); + WIN window(rel_pages_base.rel_pg_space_id, -1); + index_desc idx; + idx.idx_id = idx_invalid; - for (auto& idx : indices) + while (BTR_next_index(tdbb, rel->getPermanent(), idxTran, &idx, &window, &rel_pages_base)) { auto* idp = this->lookupIndex(tdbb, idx.idx_id, CacheFlag::AUTOCREATE); QualifiedName idx_name; @@ -386,6 +387,24 @@ RelationPages* RelationPermanent::getPagesInternal(thread_db* tdbb, TraNumber tr return pages; } +RelationPages* RelationPermanent::getAttPages(thread_db* tdbb, RelationPages::InstanceId inst_id) +{ + fb_assert(!(rel_flags & REL_temp_tran)); + + MutexLockGuard g(rel_pages_mutex, FB_FUNCTION); + + if (!rel_pages_inst) + return nullptr; + + FB_SIZE_T pos; + if (!rel_pages_inst->find(inst_id, pos)) + return nullptr; + + RelationPages* pages = (*rel_pages_inst)[pos]; + fb_assert(pages->rel_instance_id == inst_id); + return pages; +} + bool RelationPermanent::delPages(thread_db* tdbb, TraNumber tran, RelationPages* aPages) { RelationPages* pages = aPages ? aPages : getPages(tdbb, tran, false); @@ -1063,6 +1082,13 @@ void IndexPermanent::releaseStatements(thread_db* tdbb) } } +void IndexPermanent::reloadAst(thread_db* tdbb, TraNumber tran, bool erase) +{ + // we need to special handle temp tables with ON PRESERVE ROWS only + if (erase && (idp_relation->rel_flags & REL_temp_conn)) + IDX_mark_temp(tdbb, idp_relation, idp_id, nullptr, tran); +} + /// jrd_rel diff --git a/src/jrd/Relation.h b/src/jrd/Relation.h index a628bcfa4e7..7f135a22949 100644 --- a/src/jrd/Relation.h +++ b/src/jrd/Relation.h @@ -207,7 +207,7 @@ class DbTriggersHeader : public Firebird::PermanentStorage static bool destroy(thread_db* tdbb, DbTriggersHeader* trigs); void releaseLock(thread_db* tdbb) { } - void reloadAst(thread_db* tdbb, bool erase) { } + void reloadAst(thread_db* tdbb, TraNumber tran, bool erase) { } private: MetaId type; @@ -482,7 +482,7 @@ class IndexPermanent : public Firebird::PermanentStorage } void releaseLock(thread_db* tdbb) { } - void reloadAst(thread_db* tdbb, bool erase) { } + void reloadAst(thread_db* tdbb, TraNumber tran, bool erase); RelationPermanent* getRelation() noexcept { @@ -837,7 +837,7 @@ class RelationPermanent : public Firebird::PermanentStorage ~RelationPermanent(); static bool destroy(thread_db* tdbb, RelationPermanent* rel); - void reloadAst(thread_db* tdbb, bool erase) + void reloadAst(thread_db* tdbb, TraNumber tran, bool erase) { if (erase) dropTempPages(tdbb); @@ -905,6 +905,7 @@ class RelationPermanent : public Firebird::PermanentStorage }; RelationPages* getPages(thread_db* tdbb, TraNumber tran = MAX_TRA_NUMBER, bool allocPages = true); + RelationPages* getAttPages(thread_db* tdbb, RelationPages::InstanceId inst_id); bool delPages(thread_db* tdbb, TraNumber tran = MAX_TRA_NUMBER, RelationPages* aPages = NULL); void freePages(thread_db* tdbb); void retainPages(thread_db* tdbb, TraNumber oldNumber, TraNumber newNumber); @@ -1030,7 +1031,7 @@ class RelationPermanent : public Firebird::PermanentStorage }; -// specialization +// specialization for LTT template <> template <> inline FB_UINT64 CacheElement::makeId(MetaId id, RelationPermanent* rel) @@ -1039,6 +1040,14 @@ inline FB_UINT64 CacheElement::makeId template <> +inline FB_UINT64 CacheElement::makeId(MetaId id, NoData) +{ + return id < 128 ? NO_METALOCK : id; +} + + inline bool jrd_rel::hasData() const { return rel_perm->rel_name.hasData(); diff --git a/src/jrd/Routine.h b/src/jrd/Routine.h index c0eee9dd6f6..716284632fc 100644 --- a/src/jrd/Routine.h +++ b/src/jrd/Routine.h @@ -64,7 +64,7 @@ namespace Jrd } static bool destroy(thread_db* tdbb, RoutinePermanent* routine); - void reloadAst(thread_db* tdbb, bool erase) { } + void reloadAst(thread_db* tdbb, TraNumber tran, bool erase) { } void releaseLock(thread_db* tdbb) { } const QualifiedName& getName() const noexcept { return name; } diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 1efc404c7f0..5188369d560 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -203,7 +203,7 @@ namespace return TipCache::traState(tdbb, descTrans, checkPresence, creating); } - inline int transactionState(thread_db* tdbb, TraNumber descTrans, Cached::Relation* rel, MetaId idxId, bool creating) + inline int transactionState(thread_db* tdbb, TraNumber descTrans, RelationPermanent* rel, MetaId idxId, bool creating) { auto checkPresence = [tdbb, rel, idxId]()->bool { @@ -313,9 +313,9 @@ void dumpIndexRoot(...) { } } static bool checkIrtRepeat(thread_db* tdbb, const index_root_page::irt_repeat* irt_desc, - Cached::Relation* relation, WIN* window, MetaId indexId); + RelationPermanent* relation, WIN* window, MetaId indexId); static ModifyIrtRepeatValue modifyIrtRepeat(thread_db* tdbb, index_root_page::irt_repeat* irt_desc, - Cached::Relation* relation, WIN* window, MetaId indexId, bool fromActivate = false); + RelationPermanent* relation, WIN* window, MetaId indexId, bool fromActivate = false); index_root_page* BTR_fetch_root_for_update(const char* from, thread_db* tdbb, WIN* window) { @@ -1144,9 +1144,9 @@ bool BTR_delete_index(thread_db* tdbb, WIN* window, MetaId id, bool withCleanup) } -static void checkTransactionNumber(const index_root_page::irt_repeat* irt_desc, jrd_tra* tra, const char* msg) +static void checkTransactionNumber(const index_root_page::irt_repeat* irt_desc, TraNumber tran, const char* msg) { - if (irt_desc->getTransaction() != tra->tra_number) + if (irt_desc->getTransaction() != tran) { fb_assert(false); fatal_exception::raiseFmt("Index root transaction number doesn't match when %s", msg); @@ -1161,7 +1161,8 @@ static void checkTransactionNumber(const index_root_page::irt_repeat* irt_desc, } -void BTR_mark_index_for_delete(thread_db* tdbb, Cached::Relation* rel, MetaId id, WIN* window, index_root_page* root) +void BTR_mark_index_for_delete(thread_db* tdbb, RelationPermanent* rel, MetaId id, WIN* window, index_root_page* root, + TraNumber tran) { /*********************************************************** * @@ -1177,15 +1178,26 @@ void BTR_mark_index_for_delete(thread_db* tdbb, Cached::Relation* rel, MetaId id const Database* dbb = tdbb->getDatabase(); CHECK_DBB(dbb); + Cleanup releaseWindow([&] () + { + CCH_RELEASE(tdbb, window); + }); + // Get index descriptor. If index doesn't exist, just leave. if (id < root->irt_count) { auto* irt_desc = root->irt_rpt + id; - jrd_tra* tra = tdbb->getTransaction(); - fb_assert(tra); + if (!tran) + { + jrd_tra* tra = tdbb->getTransaction(); + fb_assert(tra); + if (tra) + tran = tra->tra_number; + } + fb_assert(tran); - if (tra) + if (tran) { bool marked = false; @@ -1217,7 +1229,7 @@ void BTR_mark_index_for_delete(thread_db* tdbb, Cached::Relation* rel, MetaId id break; case irt_commit: - if (tra->tra_number == irt_desc->getTransaction()) + if (tran == irt_desc->getTransaction()) break; // already marked in current transaction [[fallthrough]]; @@ -1226,22 +1238,20 @@ void BTR_mark_index_for_delete(thread_db* tdbb, Cached::Relation* rel, MetaId id badState(irt_desc, "not irt_rollback/irt_normal", msg); case irt_rollback: // created not long ago - checkTransactionNumber(irt_desc, tra, msg); + checkTransactionNumber(irt_desc, tran, msg); if (!marked) CCH_MARK(tdbb, window); - irt_desc->setKill(tra->tra_number); + irt_desc->setKill(tran); break; case irt_normal: if (!marked) CCH_MARK(tdbb, window); - irt_desc->setCommit(tra->tra_number); + irt_desc->setCommit(tran); break; } } } - - CCH_RELEASE(tdbb, window); } @@ -1311,7 +1321,7 @@ bool BTR_activate_index(thread_db* tdbb, Cached::Relation* relation, MetaId id) badState(irt_desc, "irt_in_progress/irt_rollback/irt_normal", msg); case irt_commit: // removed not long ago - checkTransactionNumber(irt_desc, tra, msg); + checkTransactionNumber(irt_desc, tra->tra_number, msg); if (!marked) CCH_MARK(tdbb, &window); irt_desc->setNormal(); @@ -1319,7 +1329,7 @@ bool BTR_activate_index(thread_db* tdbb, Cached::Relation* relation, MetaId id) break; case irt_kill: - checkTransactionNumber(irt_desc, tra, msg); + checkTransactionNumber(irt_desc, tra->tra_number, msg); if (!marked) CCH_MARK(tdbb, &window); irt_desc->setRollback(tra->tra_number); @@ -2505,7 +2515,7 @@ void BTR_make_null_key(thread_db* tdbb, const index_desc* idx, temporary_key* ke // if yes - we release index root window static bool checkIrtRepeat(thread_db* tdbb, const index_root_page::irt_repeat* irt_desc, - Cached::Relation* relation, WIN* window, MetaId indexId) + RelationPermanent* relation, WIN* window, MetaId indexId) { const TraNumber irtTrans = irt_desc->getTransaction(); const TraNumber oldestActive = tdbb->getDatabase()->dbb_oldest_active; @@ -2564,7 +2574,7 @@ static bool checkIrtRepeat(thread_db* tdbb, const index_root_page::irt_repeat* i // if yes - modifies it up to index deletion static ModifyIrtRepeatValue modifyIrtRepeat(thread_db* tdbb, index_root_page::irt_repeat* irt_desc, - Cached::Relation* relation, WIN* window, MetaId indexId, bool fromActivate) + RelationPermanent* relation, WIN* window, MetaId indexId, bool fromActivate) { const TraNumber irtTrans = irt_desc->getTransaction(); const TraNumber oldestActive = tdbb->getDatabase()->dbb_oldest_active; @@ -2654,7 +2664,8 @@ static ModifyIrtRepeatValue modifyIrtRepeat(thread_db* tdbb, index_root_page::ir } -bool BTR_next_index(thread_db* tdbb, Cached::Relation* relation, jrd_tra* transaction, index_desc* idx, WIN* window) +bool BTR_next_index(thread_db* tdbb, Cached::Relation* relation, jrd_tra* transaction, index_desc* idx, + WIN* window, RelationPages* relPages) { /************************************** * @@ -2683,8 +2694,8 @@ bool BTR_next_index(thread_db* tdbb, Cached::Relation* relation, jrd_tra* transa root = (const index_root_page*) window->win_buffer; else { - RelationPages* const relPages = transaction ? - relation->getPages(tdbb, transaction->tra_number) : relation->getPages(tdbb); + if (!relPages) + relPages = transaction ? relation->getPages(tdbb, transaction->tra_number) : relation->getPages(tdbb); if (!(root = fetch_root(tdbb, window, relation, relPages))) return false; diff --git a/src/jrd/btr_proto.h b/src/jrd/btr_proto.h index 32b6e5d42f7..81adc915a97 100644 --- a/src/jrd/btr_proto.h +++ b/src/jrd/btr_proto.h @@ -52,8 +52,10 @@ bool BTR_make_bounds(Jrd::thread_db*, const Jrd::IndexRetrieval*, Jrd::IndexScan Jrd::idx_e BTR_make_key(Jrd::thread_db*, USHORT, const Jrd::ValueExprNode* const*, const SSHORT*, const Jrd::index_desc*, Jrd::temporary_key*, USHORT, bool*); void BTR_make_null_key(Jrd::thread_db*, const Jrd::index_desc*, Jrd::temporary_key*); -void BTR_mark_index_for_delete(Jrd::thread_db*, Jrd::Cached::Relation*, MetaId, Jrd::win*, Ods::index_root_page*); -bool BTR_next_index(Jrd::thread_db*, Jrd::Cached::Relation*, Jrd::jrd_tra*, Jrd::index_desc*, Jrd::win*); +void BTR_mark_index_for_delete(Jrd::thread_db*, Jrd::RelationPermanent*, MetaId, Jrd::win*, Ods::index_root_page*, + TraNumber tran); +bool BTR_next_index(Jrd::thread_db*, Jrd::Cached::Relation*, Jrd::jrd_tra*, Jrd::index_desc*, Jrd::win*, + Jrd::RelationPages* = nullptr); void BTR_remove(Jrd::thread_db*, Jrd::win*, Jrd::index_insertion*); void BTR_reserve_slot(Jrd::thread_db*, Jrd::IndexCreation&, Jrd::IndexCreateLock&); void BTR_selectivity(Jrd::thread_db*, Jrd::Cached::Relation*, MetaId, Jrd::SelectivityList&); diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 0de6046e8eb..7320dd616dc 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -3669,22 +3669,16 @@ static bool delete_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ if (!relation) break; - RelationPages* relPages = relation->getPages(tdbb, MAX_TRA_NUMBER, false); - if (!relPages) - break; - - // we need to special handle temp tables with ON PRESERVE ROWS only - const bool isTempIndex = (relation->rel_flags & REL_temp_conn) && - (relPages->rel_instance_id != 0); - if (isTempIndex) - return true; - fb_assert(work->dfw_ids.getCount() == 1); auto* index = relation->lookup_index(tdbb, work->dfw_ids[0], CacheFlag::NOERASED); fb_assert(index); if (!index) break; + // we need to special handle temp tables with ON PRESERVE ROWS only + if (relation->rel_flags & REL_temp_conn) + IDX_mark_temp(tdbb, relation, index->getId(), tdbb->getAttachment(), transaction->tra_number); + if (index->getForeignKey().hasData()) { if (relation->rel_foreign_refs) // may be missing in a case of self-reference diff --git a/src/jrd/idx.cpp b/src/jrd/idx.cpp index 4fc9464ec36..be6537ba390 100644 --- a/src/jrd/idx.cpp +++ b/src/jrd/idx.cpp @@ -990,10 +990,60 @@ void IDX_mark_index(thread_db* tdbb, Cached::Relation* relation, MetaId id) WIN window(relPages->rel_pg_space_id, relPages->rel_index_root); index_root_page* root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); - // loop through pagespaces and mark for delete %%%%%% - // if ((relation->rel_flags & REL_temp_conn) && (relPages->rel_instance_id != 0)) + BTR_mark_index_for_delete(tdbb, relation, id, &window, root, 0); +} + +void IDX_mark_temp(thread_db* tdbb, RelationPermanent* relation, MetaId id, Attachment* current, TraNumber tran) +{ +/************************************** + * + * I D X _ m a r k _ t e m p + * + ************************************** + * + * Functional description + * Mark GTT (preserve rows) index + * in all pagespaces + * + **************************************/ + Database* dbb = tdbb->getDatabase(); + AttachmentsRefHolder attachments; + + { + Sync guard(&dbb->dbb_sync, "JRD_shutdown_attachments"); + if (!dbb->dbb_sync.ourExclusiveLock()) + guard.lock(SYNC_SHARED); + + for (Attachment* attachment = dbb->dbb_attachments; + attachment; + attachment = attachment->att_next) + { + if (attachment != current) + { + fb_assert(attachment->getStable()); + attachments.add(attachment->getStable()); + } + } + } - BTR_mark_index_for_delete(tdbb, relation, id, &window, root); + for (AttachmentsRefHolder::Iterator iter(attachments); *iter; ++iter) + { + StableAttachmentPart* const sAtt = *iter; + + AttSyncLockGuard guard(*(sAtt->getSync(true)), FB_FUNCTION); + Attachment* attachment = sAtt->getHandle(); + + if (attachment) + { + auto* pages = relation->getAttPages(tdbb, attachment->att_attachment_id); + if (pages && pages->rel_index_root) + { + WIN window(pages->rel_pg_space_id, pages->rel_index_root); + auto* root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); + BTR_mark_index_for_delete(tdbb, relation, id, &window, root, tran); + } + } + } } @@ -1055,7 +1105,7 @@ void IDX_mark_indices(thread_db* tdbb, Cached::Relation* relation) for (USHORT i = 0; i < root->irt_count; i++) { - BTR_mark_index_for_delete(tdbb, relation, i, &window, root); + BTR_mark_index_for_delete(tdbb, relation, i, &window, root, 0); root = BTR_fetch_root_for_update(FB_FUNCTION, tdbb, &window); } diff --git a/src/jrd/idx_proto.h b/src/jrd/idx_proto.h index 2337b6af572..1e166f5cb5a 100644 --- a/src/jrd/idx_proto.h +++ b/src/jrd/idx_proto.h @@ -41,9 +41,11 @@ namespace Jrd bool IDX_activate_index(Jrd::thread_db*, Jrd::Cached::Relation*, MetaId); void IDX_check_access(Jrd::thread_db*, Jrd::CompilerScratch*, Jrd::Cached::Relation*, Jrd::Cached::Relation*); bool IDX_check_master_types (Jrd::thread_db*, Jrd::index_desc&, Jrd::Cached::Relation*, int&); -void IDX_create_index(Jrd::thread_db*, Jrd::IdxCreate createMethod, Jrd::jrd_rel*, Jrd::index_desc*, const Jrd::QualifiedName&, - USHORT*, Jrd::jrd_tra*, Jrd::SelectivityList&); +void IDX_create_index(Jrd::thread_db*, Jrd::IdxCreate createMethod, Jrd::jrd_rel*, Jrd::index_desc*, + const Jrd::QualifiedName&, USHORT*, Jrd::jrd_tra*, Jrd::SelectivityList&); void IDX_mark_index(Jrd::thread_db*, Jrd::Cached::Relation*, MetaId); +void IDX_mark_temp(Jrd::thread_db* tdbb, Jrd::RelationPermanent* relation, MetaId id, Jrd::Attachment* current, + TraNumber tran); void IDX_delete_indices(Jrd::thread_db*, Jrd::RelationPermanent*, Jrd::RelationPages*, bool); void IDX_mark_indices(Jrd::thread_db*, Jrd::Cached::Relation*); void IDX_erase(Jrd::thread_db*, Jrd::record_param*, Jrd::jrd_tra*); From 4281d2dcee24620098c2f53182e71181c29cc849 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Tue, 21 Apr 2026 18:56:25 +0300 Subject: [PATCH 099/119] Rolled back my old commit due to caused regressions --- src/jrd/extds/ExtDS.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/jrd/extds/ExtDS.cpp b/src/jrd/extds/ExtDS.cpp index 2c0a5fad905..d824bcbad58 100644 --- a/src/jrd/extds/ExtDS.cpp +++ b/src/jrd/extds/ExtDS.cpp @@ -515,7 +515,8 @@ void Provider::releaseConnection(thread_db* tdbb, Connection& conn, bool inPool) } } } - status->init(); + // commented out till final solution because it caused regressions + // status->init(); // ???????????? } if (inPool) From 542045b99ea754472a5d91d1a0f6396083915b35 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Tue, 21 Apr 2026 21:36:31 +0300 Subject: [PATCH 100/119] Minor enhancement: avoid 2 parallel shutdown threads with short linger delay --- src/jrd/Database.cpp | 3 ++- src/jrd/jrd.cpp | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/jrd/Database.cpp b/src/jrd/Database.cpp index 1635ee0450e..938f8d4d9fd 100644 --- a/src/jrd/Database.cpp +++ b/src/jrd/Database.cpp @@ -629,7 +629,8 @@ namespace Jrd void Database::Linger::handler() { - JRD_shutdown_database(dbb, SHUT_DBB_RELEASE_POOLS); + if (active) + JRD_shutdown_database(dbb, SHUT_DBB_RELEASE_POOLS); } void Database::Linger::reset() diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index 9b6eb182361..b885fd4b021 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -8123,6 +8123,10 @@ bool JRD_shutdown_database(Database* dbb, const unsigned flags) // Since that moment dbb becomes not reusable dbb->dbb_init_fini->destroy(); + // Reset linger in order to avoid second parallel shutdown in case this one takes really long time + if (dbb->dbb_linger_timer) + dbb->dbb_linger_timer->reset(); + fb_assert(!dbb->locked()); WorkerAttachment::shutdownDbb(dbb); From e78ac9a9edbb7ed2c69ea28eaefe819515b83329 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Tue, 21 Apr 2026 20:50:46 +0000 Subject: [PATCH 101/119] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 18cf28c31c3..be28daeb147 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:1910 + FORMAL BUILD NUMBER:1914 */ -#define PRODUCT_VER_STRING "6.0.0.1910" -#define FILE_VER_STRING "WI-T6.0.0.1910" -#define LICENSE_VER_STRING "WI-T6.0.0.1910" -#define FILE_VER_NUMBER 6, 0, 0, 1910 +#define PRODUCT_VER_STRING "6.0.0.1914" +#define FILE_VER_STRING "WI-T6.0.0.1914" +#define LICENSE_VER_STRING "WI-T6.0.0.1914" +#define FILE_VER_NUMBER 6, 0, 0, 1914 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "1910" +#define FB_BUILD_NO "1914" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 8cecbb059c3..0b62aa39c51 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=1910 +BuildNum=1914 NowAt=`pwd` cd `dirname $0` From 98d54f4f62b36c3f5e5a7cb816f135a27c82b5ff Mon Sep 17 00:00:00 2001 From: Vlad Khorsun Date: Wed, 22 Apr 2026 13:43:16 +0300 Subject: [PATCH 102/119] Warnings --- src/common/sha2/sha2.h | 4 ++-- src/jrd/Monitoring.h | 2 +- src/jrd/Relation.cpp | 2 +- src/jrd/dfw.epp | 2 -- src/jrd/replication/Applier.cpp | 1 - src/jrd/trace/TraceDSQLHelpers.h | 2 +- src/jrd/trace/TraceObjects.cpp | 4 ++-- 7 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/common/sha2/sha2.h b/src/common/sha2/sha2.h index 93f0cb61e24..1ce7688c6d7 100644 --- a/src/common/sha2/sha2.h +++ b/src/common/sha2/sha2.h @@ -152,12 +152,12 @@ class sha2_base { SHA_TRAITS::sha_init(&m_ctx); } - void process(size_t length, const void* bytes) noexcept + void process(unsigned int length, const void* bytes) noexcept { SHA_TRAITS::sha_update(&m_ctx, static_cast(bytes), length); } - void process(size_t length, const unsigned char* message) noexcept + void process(unsigned int length, const unsigned char* message) noexcept { SHA_TRAITS::sha_update(&m_ctx, message, length); } diff --git a/src/jrd/Monitoring.h b/src/jrd/Monitoring.h index f86e779edfb..6b1a2777a6f 100644 --- a/src/jrd/Monitoring.h +++ b/src/jrd/Monitoring.h @@ -167,7 +167,7 @@ class SnapshotData void storeString(int field_id, const std::string_view value) { if (!value.empty()) - storeField(field_id, VALUE_STRING, value.length(), value.data()); + storeField(field_id, VALUE_STRING, static_cast(value.length()), value.data()); } void storeBoolean(int field_id, bool value) diff --git a/src/jrd/Relation.cpp b/src/jrd/Relation.cpp index 42bba3981f1..783978fdfe8 100644 --- a/src/jrd/Relation.cpp +++ b/src/jrd/Relation.cpp @@ -899,7 +899,7 @@ void GCLock::downgrade(thread_db* tdbb) checkGuard(newFlags); } while (!gcFlags.compare_exchange_weak(oldFlags, newFlags, std::memory_order_release, std::memory_order_acquire)); - if ((!gcRel->isLTT()) && (newFlags & GC_counterMask == 0) && (newFlags & GC_blocking)) + if ((!gcRel->isLTT()) && ((newFlags & GC_counterMask) == 0) && (newFlags & GC_blocking)) { fb_assert(newFlags & GC_locked); fb_assert(gcLck->lck_id); diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 7320dd616dc..d17bc9cd4ed 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -3786,9 +3786,7 @@ static bool delete_relation(thread_db* tdbb, SSHORT phase, DeferredWork* work, j * **************************************/ AutoRequest request; - jrd_rel* relation; USHORT view_count; - bool adjusted; SET_TDBB(tdbb); Jrd::Attachment* attachment = tdbb->getAttachment(); diff --git a/src/jrd/replication/Applier.cpp b/src/jrd/replication/Applier.cpp index 2a974ac2e65..f7d069ab37b 100644 --- a/src/jrd/replication/Applier.cpp +++ b/src/jrd/replication/Applier.cpp @@ -1145,7 +1145,6 @@ bool Applier::lookupRecord(thread_db* tdbb, bool haveIdx = false; if (idxName && idxName->object.hasData()) { - SLONG foundRelId; auto* idv = relation->getPermanent()->lookup_index(tdbb, *idxName, CacheFlag::AUTOCREATE); if (idv) diff --git a/src/jrd/trace/TraceDSQLHelpers.h b/src/jrd/trace/TraceDSQLHelpers.h index 1941edcac42..711af02059f 100644 --- a/src/jrd/trace/TraceDSQLHelpers.h +++ b/src/jrd/trace/TraceDSQLHelpers.h @@ -111,7 +111,7 @@ class TraceDSQLPrepare { static constexpr std::string_view empty_string = ""; m_string = empty_string.data(); - m_string_len = empty_string.length(); + m_string_len = static_cast(empty_string.length()); } bool m_need_trace; diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index b91d579523d..6b7ed937c5f 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -680,9 +680,9 @@ TraceRuntimeStats::TraceRuntimeStats(Attachment* attachment, m_tableCounters.reset(&baseline->getTableCounters(), getTableName); - m_info.pin_count = m_tableCounters.getObjectCount(); - m_legacyCounts.resize(m_info.pin_count); + m_legacyCounts.resize(m_tableCounters.getObjectCount()); m_info.pin_tables = m_legacyCounts.begin(); + m_info.pin_count = m_legacyCounts.getCount(); for (MetaId i = 0; i < m_info.pin_count; i++) { From a09289d400ba4d33a1beff5fce0991245eb8366d Mon Sep 17 00:00:00 2001 From: TreeHunter <60896014+TreeHunter9@users.noreply.github.com> Date: Tue, 21 Apr 2026 22:04:29 +0300 Subject: [PATCH 103/119] fix(udr): Add cleanup of UDR objects from `Engine` on attachment disconnect (#8992) * fix(udr): Add cleanup of UDR objects from `Engine` on attachment disconnect * Revert "fix(udr): Add cleanup of UDR objects from `Engine` on attachment disconnect" This reverts commit 80581c5419d7b32eb0f5dbadbf814ae0a25cf84e. * fix(udr): Add cleanup of UDR objects from Engine on attachment disconnect --------- Co-authored-by: Artyom Ivanov --- src/plugins/udr_engine/UdrEngine.cpp | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/plugins/udr_engine/UdrEngine.cpp b/src/plugins/udr_engine/UdrEngine.cpp index cb783d62f44..4c7d10a8cc6 100644 --- a/src/plugins/udr_engine/UdrEngine.cpp +++ b/src/plugins/udr_engine/UdrEngine.cpp @@ -100,8 +100,8 @@ class Engine final : public StdPlugin& sharedObjs, const PathName& moduleName); - template void deleteChildren( - GenericMap > >& children); + template + void sharedObjectCleanup(SharedObjType* sharedObj, SortedArray& sharedObjs); template T* findNode(ThrowStatusWrapper* status, const GenericMap > >& nodes, const string& entryPoint); @@ -285,7 +285,7 @@ class SharedFunction final : public DisposeIfacedeleteChildren(children); + engine->sharedObjectCleanup(this, engine->functions); } public: @@ -347,7 +347,7 @@ class SharedProcedure final : public DisposeIfacedeleteChildren(children); + engine->sharedObjectCleanup(this, engine->procedures); } public: @@ -408,7 +408,7 @@ class SharedTrigger final : public DisposeIfacedeleteChildren(children); + engine->sharedObjectCleanup(this, engine->triggers); } public: @@ -606,16 +606,17 @@ template ObjType* } -template void Engine::deleteChildren( - GenericMap > >& children) +template +void Engine::sharedObjectCleanup(SharedObjType* sharedObj, SortedArray& sharedObjs) { - // No need to lock childrenMutex as if there are more threads simultaneously accessing - // these children in this moment there will be a memory corruption anyway. + MutexLockGuard guard(childrenMutex, FB_FUNCTION); + + for (auto child : sharedObj->children) + child.second->dispose(); - typedef typename GenericMap > >::Accessor ChildrenAccessor; - ChildrenAccessor accessor(&children); - for (bool found = accessor.getFirst(); found; found = accessor.getNext()) - accessor.current()->second->dispose(); + FB_SIZE_T pos; + if (sharedObjs.find(sharedObj, pos)) + sharedObjs.remove(pos); } From 415d9c6d0bd9641ab1eb4806c816748376c9e25a Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Wed, 22 Apr 2026 16:18:37 +0300 Subject: [PATCH 104/119] Use constant as pointed by Adriano --- src/jrd/Relation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/Relation.h b/src/jrd/Relation.h index 7f135a22949..007122ab098 100644 --- a/src/jrd/Relation.h +++ b/src/jrd/Relation.h @@ -1044,7 +1044,7 @@ inline FB_UINT64 CacheElement::makeId template <> inline FB_UINT64 CacheElement::makeId(MetaId id, NoData) { - return id < 128 ? NO_METALOCK : id; + return id < USER_DEF_REL_INIT_ID ? NO_METALOCK : id; } From ccd9433303e644632e28b58ed2cfb1a3c2564028 Mon Sep 17 00:00:00 2001 From: AlexPeshkoff Date: Wed, 22 Apr 2026 20:43:59 +0300 Subject: [PATCH 105/119] Fixed use of Request in Applier --- src/jrd/replication/Applier.cpp | 19 +++++++++++++++++++ src/jrd/replication/Applier.h | 11 ++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/jrd/replication/Applier.cpp b/src/jrd/replication/Applier.cpp index f7d069ab37b..9ab632cf1ee 100644 --- a/src/jrd/replication/Applier.cpp +++ b/src/jrd/replication/Applier.cpp @@ -226,6 +226,24 @@ namespace } // namespace +Applier::Applier(Firebird::MemoryPool& pool, + const Firebird::PathName& database, + Request* request, bool cascade) + : PermanentStorage(pool), + m_txnMap(pool), m_database(pool, database), + m_request(request), m_enableCascade(cascade), + m_constraintIndexMap(pool) +{ + bool setUsed = m_request->setUsed(); + fb_assert(setUsed); +} + +Applier::~Applier() +{ + if (m_request) + m_request->setUnused(); +} + Applier* Applier::create(thread_db* tdbb) { const auto dbb = tdbb->getDatabase(); @@ -279,6 +297,7 @@ void Applier::shutdown(thread_db* tdbb) if (!(dbb->dbb_flags & DBB_bugcheck)) { cleanupTransactions(tdbb); + m_request->setUnused(); CMP_release(tdbb, m_request); } m_request = nullptr; // already deleted by pool diff --git a/src/jrd/replication/Applier.h b/src/jrd/replication/Applier.h index f6609268750..b8c6abac2ff 100644 --- a/src/jrd/replication/Applier.h +++ b/src/jrd/replication/Applier.h @@ -124,17 +124,14 @@ namespace Jrd jrd_tra* const m_transaction; }; */ - public: + private: Applier(Firebird::MemoryPool& pool, const Firebird::PathName& database, - Request* request, bool cascade) - : PermanentStorage(pool), - m_txnMap(pool), m_database(pool, database), - m_request(request), m_enableCascade(cascade), - m_constraintIndexMap(pool) - {} + Request* request, bool cascade); + public: static Applier* create(thread_db* tdbb); + ~Applier(); void process(thread_db* tdbb, ULONG length, const UCHAR* data); void cleanupTransactions(thread_db* tdbb); From 61eaae52fdcd9fe3167edc7e118c6d79a1839269 Mon Sep 17 00:00:00 2001 From: GitHub Action Date: Wed, 22 Apr 2026 20:53:22 +0000 Subject: [PATCH 106/119] increment build number --- src/jrd/build_no.h | 12 ++++++------ src/misc/writeBuildNum.sh | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index be28daeb147..77a098cea0d 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:1914 + FORMAL BUILD NUMBER:1918 */ -#define PRODUCT_VER_STRING "6.0.0.1914" -#define FILE_VER_STRING "WI-T6.0.0.1914" -#define LICENSE_VER_STRING "WI-T6.0.0.1914" -#define FILE_VER_NUMBER 6, 0, 0, 1914 +#define PRODUCT_VER_STRING "6.0.0.1918" +#define FILE_VER_STRING "WI-T6.0.0.1918" +#define LICENSE_VER_STRING "WI-T6.0.0.1918" +#define FILE_VER_NUMBER 6, 0, 0, 1918 #define FB_MAJOR_VER "6" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "1914" +#define FB_BUILD_NO "1918" #define FB_BUILD_TYPE "T" #define FB_BUILD_SUFFIX "Firebird 6.0 Initial" diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index 0b62aa39c51..3dddfd24617 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=6 MinorVer=0 RevNo=0 -BuildNum=1914 +BuildNum=1918 NowAt=`pwd` cd `dirname $0` From bcfb106570d4eb035197e7288e0bdcb2fc1065ef Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 27 Apr 2026 10:00:27 +0300 Subject: [PATCH 107/119] Fix build after merge --- src/jrd/Package.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/Package.h b/src/jrd/Package.h index 42fd1504d00..e271f35a4b5 100644 --- a/src/jrd/Package.h +++ b/src/jrd/Package.h @@ -144,7 +144,7 @@ class PackagePermanent : public Firebird::PermanentStorage } void releaseLock(thread_db*) { } - void reloadAst(thread_db* tdbb, bool erase) { } + void reloadAst(thread_db* tdbb, TraNumber tran, bool erase) { } const QualifiedName& getName() const noexcept { return name; } void setName(const QualifiedName& value) { name = value; } From e735ba6d0862a0b358183cf75d11bd9b0cffa49b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 27 Apr 2026 10:24:33 +0300 Subject: [PATCH 108/119] Add suggested changes --- src/dsql/ExprNodes.cpp | 5 +++-- src/dsql/PackageNodes.epp | 2 +- src/dsql/PackageNodes.h | 2 +- src/jrd/Package.epp | 18 ++++++------------ 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 7d815bde648..382669c0217 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -6487,7 +6487,7 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec ambiguousCtxStack.push(&packageContext); MemoryPool& pool = dsqlScratch->getPool(); - node = FB_NEW_POOL(pool) PackageReferenceNode(pool, constantName); + node = FB_NEW_POOL(pool) PackageReferenceNode(pool, constantName, blr_pkg_reference_to_constant); } } } @@ -14205,7 +14205,8 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), constantFullName)) { delete node; - return FB_NEW_POOL(dsqlScratch->getPool()) PackageReferenceNode(dsqlScratch->getPool(), constantFullName); + return FB_NEW_POOL(dsqlScratch->getPool()) PackageReferenceNode(dsqlScratch->getPool(), + constantFullName, blr_pkg_reference_to_constant); } } diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 6e539db6b1f..1f1e8e5057c 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -354,7 +354,7 @@ static RegisterNode regPackageReferenceNode({blr_package_r PackageReferenceNode::PackageReferenceNode(MemoryPool& pool, const QualifiedName& fullName, const UCHAR itemType) : TypedNode(pool), - m_fullName(fullName.object, fullName.schema.hasData() ? fullName.schema : PUBLIC_SCHEMA, fullName.package), m_itemType(itemType) + m_fullName(fullName), m_itemType(itemType) {} string PackageReferenceNode::internalPrint(NodePrinter& printer) const diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index ce4179a9699..adf2b924c28 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -122,7 +122,7 @@ class PackageReferenceNode final : public TypedNodeatt_database->createPool(ALLOC_ARGS0); ContextPoolHolder context(tdbb, csb_pool); - CompilerScratch* csb = nullptr; - Cleanup cc([&csb]() - { - delete csb; - }); + AutoPtr autoCsb(FB_NEW_POOL(*csb_pool) CompilerScratch(*csb_pool)); + CompilerScratch* csb = autoCsb; // Parse BLR for constant expression // It would be cool to calculate the result value and store it as a LiteralNode @@ -206,12 +203,9 @@ dsc* ConstantValue::makeValue(thread_db* tdbb, Request* request) handmadeStmt = Statement::makeStatement(tdbb, csb, true); request = handmadeStmt->makeRootRequest(tdbb); - Cleanup requestRestore([&, requestToRestore = tdbb->getRequest()]() - { - tdbb->setRequest(requestToRestore); - }); + AutoSetRestore2 autoSetRequest( + tdbb, &thread_db::getRequest, &thread_db::setRequest, request); - tdbb->setRequest(request); request->setUsed(); request->setAttachment(attachment); attachment->att_requests.add(request); @@ -233,8 +227,8 @@ dsc* ConstantValue::makeValue(thread_db* tdbb, Request* request) ex.stuffException(temp_status); const string quotedName = name.toQuotedString(); - (Arg::Gds(isc_bad_constant_blr) << Arg::Str(quotedName) - << Arg::StatusVector(temp_status.begin())).raise(); + (Arg::StatusVector(temp_status.begin()) << + Arg::Gds(isc_bad_constant_blr) << quotedName).raise(); } csb_pool = nullptr; // Do not delete From e69448e68a6891fd451d0ad77c578aa232ff2788 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 27 Apr 2026 10:52:30 +0300 Subject: [PATCH 109/119] Fix gap in generator IDs --- src/jrd/trig.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jrd/trig.h b/src/jrd/trig.h index d0bd76c7237..e664fc0afc5 100644 --- a/src/jrd/trig.h +++ b/src/jrd/trig.h @@ -82,7 +82,7 @@ static inline constexpr Jrd::gen generators[] = { "RDB$BACKUP_HISTORY", 9, "Nbackup technology", ODS_13_0 }, { FUNCTIONS_GENERATOR, 10, "Function ID", ODS_13_0 }, { "RDB$GENERATOR_NAME", 11, "Implicit generator name", ODS_13_0 }, - { PACKAGES_GENERATOR, 13, "Package ID", ODS_14_0 }, + { PACKAGES_GENERATOR, 12, "Package ID", ODS_14_0 }, { nullptr, 0, nullptr, 0 } }; From 10d73569d4eefb2f62c3e9bcbb536b5a71bd7133 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 27 Apr 2026 11:16:44 +0300 Subject: [PATCH 110/119] Remove not needed cache warmup --- src/jrd/vio.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index f3e95d4b9c9..1f856dcedcb 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -4394,13 +4394,6 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) object_id = set_metadata_id(tdbb, rpb->rpb_record, f_pkg_id, drq_g_nxt_package_id, PACKAGES_GENERATOR); - // This getVersioned call is necessary for to set the lock during restore - // The lock itself is created by calling `pingLock`, but the `locked` variable is not set to true - // This resulted in the lock not being released during the cleaning stage - // So set the lock by calling `getVersioned` function - if (tdbb->getAttachment()->isGbak()) - MetadataCache::getVersioned(tdbb, object_id, CacheFlag::AUTOCREATE); - DFW_post_work(transaction, dfw_create_package, &desc, &schemaDesc, object_id); break; From 97ce41718d888943e0d0db1e8f2bc1310fce0605 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 27 Apr 2026 17:29:00 +0300 Subject: [PATCH 111/119] Add RDB$SYSTEM_FLAG to RDB$CONSTANTS --- src/burp/backup.epp | 2 +- src/burp/burp.h | 1 + src/burp/restore.epp | 3 +++ src/jrd/ini.epp | 3 +++ src/jrd/relations.h | 1 + 5 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 25db9caf086..da2c1461ea6 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -4861,7 +4861,7 @@ void write_constants() FOR (REQUEST_HANDLE req_handle1) CONST IN RDB$CONSTANTS - WITH CONST.RDB$SCHEMA_NAME NE SYSTEM_SCHEMA + WITH CONST.RDB$SYSTEM_FLAG NE 1 { QualifiedMetaString name(CONST.RDB$CONSTANT_NAME); diff --git a/src/burp/burp.h b/src/burp/burp.h index 0130d4070ab..c75f8dc2b3a 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -711,6 +711,7 @@ enum att_type { att_constant_blr, att_constant_source, att_constant_schema_name, + att_constant_sysflag, att_constant_description, }; diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 9600f3bb633..36f291bad07 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -10573,6 +10573,9 @@ bool get_constants_table(BurpGlobals* tdgbl) REQUEST_HANDLE tdgbl->handles_get_type_req_handle1) CONST IN RDB$CONSTANTS { + CONST.RDB$SYSTEM_FLAG = 0; + CONST.RDB$SYSTEM_FLAG.NULL = FALSE; + skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index c69c586a90f..4bedca271fd 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -2174,6 +2174,9 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR // Description (filed) name PAD(names[gfields[constant.fieldId].gfld_name], CONST.RDB$FIELD_SOURCE); + CONST.RDB$SYSTEM_FLAG = RDB_system; + CONST.RDB$SYSTEM_FLAG.NULL = FALSE; + // Put the blr Array blrData(1 + constant.valueBlr.getCount() + 1); blrData.push(blr_version5); diff --git a/src/jrd/relations.h b/src/jrd/relations.h index c3703693a7c..dbe61ebd505 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -851,5 +851,6 @@ RELATION(nam_constants, rel_constants, ODS_14_0, rel_persistent) FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_14_0) FIELD(f_const_source, nam_const_source, fld_source, 1, ODS_14_0) FIELD(f_const_package_schema, nam_sch_name, fld_sch_name, 0, ODS_14_0) + FIELD(f_const_sys_flag, nam_sys_flag, fld_flag, 0, ODS_14_0) FIELD(f_const_description, nam_description, fld_description, 1, ODS_14_0) END_RELATION From 6aabe943b715b89cbc5b2609a3a5a46096b3a645 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Mon, 27 Apr 2026 17:31:59 +0300 Subject: [PATCH 112/119] Add missing SYSTEM_FLAG set in RDB$CONSTANT STORE --- src/burp/backup.epp | 2 +- src/dsql/PackageNodes.epp | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/burp/backup.epp b/src/burp/backup.epp index da2c1461ea6..7dde3b13e16 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -4861,7 +4861,7 @@ void write_constants() FOR (REQUEST_HANDLE req_handle1) CONST IN RDB$CONSTANTS - WITH CONST.RDB$SYSTEM_FLAG NE 1 + WITH CONST.RDB$SYSTEM_FLAG NE 1 { QualifiedMetaString name(CONST.RDB$CONSTANT_NAME); diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 1f1e8e5057c..8f8428010c7 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -637,6 +637,9 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat CONST.RDB$FIELD_SOURCE.NULL = FALSE; strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); + CONST.RDB$SYSTEM_FLAG = 0; + CONST.RDB$SYSTEM_FLAG.NULL = FALSE; + // Gen value blr ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); From 970b39d216667483078966a1209ddfac2be76a23 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 28 Apr 2026 14:30:24 +0300 Subject: [PATCH 113/119] Revert "Add missing SYSTEM_FLAG set in RDB$CONSTANT STORE" This reverts commit 6aabe943b715b89cbc5b2609a3a5a46096b3a645. --- src/burp/backup.epp | 2 +- src/dsql/PackageNodes.epp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 7dde3b13e16..da2c1461ea6 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -4861,7 +4861,7 @@ void write_constants() FOR (REQUEST_HANDLE req_handle1) CONST IN RDB$CONSTANTS - WITH CONST.RDB$SYSTEM_FLAG NE 1 + WITH CONST.RDB$SYSTEM_FLAG NE 1 { QualifiedMetaString name(CONST.RDB$CONSTANT_NAME); diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 8f8428010c7..1f1e8e5057c 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -637,9 +637,6 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat CONST.RDB$FIELD_SOURCE.NULL = FALSE; strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); - CONST.RDB$SYSTEM_FLAG = 0; - CONST.RDB$SYSTEM_FLAG.NULL = FALSE; - // Gen value blr ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); From e7b1fd498a5e99b88d89d9d42b6239a8a758d90b Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 28 Apr 2026 14:30:27 +0300 Subject: [PATCH 114/119] Revert "Add RDB$SYSTEM_FLAG to RDB$CONSTANTS" This reverts commit 97ce41718d888943e0d0db1e8f2bc1310fce0605. --- src/burp/backup.epp | 2 +- src/burp/burp.h | 1 - src/burp/restore.epp | 3 --- src/jrd/ini.epp | 3 --- src/jrd/relations.h | 1 - 5 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/burp/backup.epp b/src/burp/backup.epp index da2c1461ea6..25db9caf086 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -4861,7 +4861,7 @@ void write_constants() FOR (REQUEST_HANDLE req_handle1) CONST IN RDB$CONSTANTS - WITH CONST.RDB$SYSTEM_FLAG NE 1 + WITH CONST.RDB$SCHEMA_NAME NE SYSTEM_SCHEMA { QualifiedMetaString name(CONST.RDB$CONSTANT_NAME); diff --git a/src/burp/burp.h b/src/burp/burp.h index c75f8dc2b3a..0130d4070ab 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -711,7 +711,6 @@ enum att_type { att_constant_blr, att_constant_source, att_constant_schema_name, - att_constant_sysflag, att_constant_description, }; diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 36f291bad07..9600f3bb633 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -10573,9 +10573,6 @@ bool get_constants_table(BurpGlobals* tdgbl) REQUEST_HANDLE tdgbl->handles_get_type_req_handle1) CONST IN RDB$CONSTANTS { - CONST.RDB$SYSTEM_FLAG = 0; - CONST.RDB$SYSTEM_FLAG.NULL = FALSE; - skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index 4bedca271fd..c69c586a90f 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -2174,9 +2174,6 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR // Description (filed) name PAD(names[gfields[constant.fieldId].gfld_name], CONST.RDB$FIELD_SOURCE); - CONST.RDB$SYSTEM_FLAG = RDB_system; - CONST.RDB$SYSTEM_FLAG.NULL = FALSE; - // Put the blr Array blrData(1 + constant.valueBlr.getCount() + 1); blrData.push(blr_version5); diff --git a/src/jrd/relations.h b/src/jrd/relations.h index dbe61ebd505..c3703693a7c 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -851,6 +851,5 @@ RELATION(nam_constants, rel_constants, ODS_14_0, rel_persistent) FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_14_0) FIELD(f_const_source, nam_const_source, fld_source, 1, ODS_14_0) FIELD(f_const_package_schema, nam_sch_name, fld_sch_name, 0, ODS_14_0) - FIELD(f_const_sys_flag, nam_sys_flag, fld_flag, 0, ODS_14_0) FIELD(f_const_description, nam_description, fld_description, 1, ODS_14_0) END_RELATION From 5cf588953b3f5480000c621818aea1f3e631af10 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 28 Apr 2026 14:43:55 +0300 Subject: [PATCH 115/119] Use RDB$SYSTEM_FLAG in RDB$PACKAGES to determinate user-created constants --- src/burp/backup.epp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 25db9caf086..c28d2ef1645 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -4860,8 +4860,11 @@ void write_constants() BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) - CONST IN RDB$CONSTANTS - WITH CONST.RDB$SCHEMA_NAME NE SYSTEM_SCHEMA + CONST IN RDB$CONSTANTS CROSS + PKG IN RDB$PACKAGES WITH + CONST.RDB$SCHEMA_NAME EQ PKG.RDB$SCHEMA_NAME AND + CONST.RDB$PACKAGE_NAME EQ PKG.RDB$PACKAGE_NAME AND + PKG.RDB$SYSTEM_FLAG NE 1 { QualifiedMetaString name(CONST.RDB$CONSTANT_NAME); From fb25692e3bfcb9c86402c3296b47defffdb3f246 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 5 May 2026 09:52:41 +0300 Subject: [PATCH 116/119] Resolve MR issues --- doc/sql.extensions/README.packages.txt | 2 +- src/burp/backup.epp | 13 +++-------- src/burp/restore.epp | 8 +++---- src/dsql/ExprNodes.h | 5 ----- src/dsql/Nodes.h | 30 ++++++++++++++++++++++++++ src/dsql/PackageNodes.epp | 14 ++++++------ src/dsql/PackageNodes.h | 10 ++++----- src/include/firebird/impl/msg/dyn.h | 2 +- src/include/firebird/impl/msg/jrd.h | 2 +- src/jrd/Package.epp | 14 ++++-------- src/jrd/Package.h | 6 +++--- src/jrd/SysFunction.cpp | 2 +- src/jrd/SystemPackages.h | 6 +++--- 13 files changed, 63 insertions(+), 51 deletions(-) diff --git a/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt index 73607d92904..f547a4adabd 100644 --- a/doc/sql.extensions/README.packages.txt +++ b/doc/sql.extensions/README.packages.txt @@ -119,7 +119,7 @@ Objectives: GRANT SELECT ON TABLE secret TO PACKAGE pk_secret; GRANT EXECUTE ON PACKAGE pk_secret TO ROLE role_secret; - To use package constants in a select expression, a USAGE permission is required: + To use package constants in an expression, a USAGE permission is required: GRANT USAGE ON PACKAGE [.] to [] [] []; REVOKE USAGE ON PACKAGE [.] FROM []; diff --git a/src/burp/backup.epp b/src/burp/backup.epp index c28d2ef1645..58fce7fa6cf 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -2364,7 +2364,7 @@ void write_character_sets() * each user defined character set. * **************************************/ - Firebird::IRequest* req_handle1 = nullptr; + IRequest* req_handle1 = nullptr; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); @@ -4866,14 +4866,11 @@ void write_constants() CONST.RDB$PACKAGE_NAME EQ PKG.RDB$PACKAGE_NAME AND PKG.RDB$SYSTEM_FLAG NE 1 { - QualifiedMetaString name(CONST.RDB$CONSTANT_NAME); + QualifiedMetaString name(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME); put(tdgbl, rec_constants); PUT_TEXT(att_constant_name, CONST.RDB$CONSTANT_NAME); - PUT_TEXT(att_constant_package, CONST.RDB$PACKAGE_NAME); - name.package = CONST.RDB$PACKAGE_NAME; - PUT_TEXT(att_constant_field_source, CONST.RDB$FIELD_SOURCE); if (!CONST.RDB$PRIVATE_FLAG.NULL) @@ -4885,11 +4882,7 @@ void write_constants() if (!CONST.RDB$CONSTANT_SOURCE.NULL) put_source_blob(att_constant_source, att_constant_source, CONST.RDB$CONSTANT_SOURCE); - if (!CONST.RDB$SCHEMA_NAME.NULL) - { - PUT_TEXT(att_constant_schema_name, CONST.RDB$SCHEMA_NAME); - name.schema = CONST.RDB$SCHEMA_NAME; - } + PUT_TEXT(att_constant_schema_name, CONST.RDB$SCHEMA_NAME); if (!CONST.RDB$DESCRIPTION.NULL) { diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 9600f3bb633..7c57f9f568b 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -167,7 +167,7 @@ bool get_trigger_old (BurpGlobals* tdgbl, burp_rel*); bool get_type(BurpGlobals* tdgbl); bool get_user_privilege(BurpGlobals* tdgbl); bool get_view(BurpGlobals* tdgbl, burp_rel*); -bool get_constants_table(BurpGlobals* tdgbl); +bool get_constants(BurpGlobals* tdgbl); void ignore_array(BurpGlobals* tdgbl, burp_rel*); void ignore_blob(BurpGlobals* tdgbl); rec_type ignore_data(BurpGlobals* tdgbl, burp_rel*); @@ -4535,7 +4535,7 @@ bool get_function(BurpGlobals* tdgbl) bool existFlag = false; - Firebird::ITransaction* local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; + ITransaction* local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; if (tdgbl->runtimeODS >= DB_VERSION_DDL12) { @@ -10557,7 +10557,7 @@ bool get_view(BurpGlobals* tdgbl, burp_rel* relation) return true; } -bool get_constants_table(BurpGlobals* tdgbl) +bool get_constants(BurpGlobals* tdgbl) { if (tdgbl->runtimeODS < DB_VERSION_DDL14) // FB6 return false; @@ -11480,7 +11480,7 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file break; case rec_constants: - if (!get_constants_table(tdgbl)) + if (!get_constants(tdgbl)) return false; flag = true; break; diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index 9b196cdb6f4..d360859fbce 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -582,11 +582,6 @@ class CurrentSchemaNode final : public TypedNode regPackageReferenceNode({blr_package_r PackageReferenceNode::PackageReferenceNode(MemoryPool& pool, const QualifiedName& fullName, const UCHAR itemType) : TypedNode(pool), - m_fullName(fullName), m_itemType(itemType) + m_fullName(fullName), m_itemType(itemType) {} string PackageReferenceNode::internalPrint(NodePrinter& printer) const @@ -645,13 +646,12 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat // Parent package fb_assert(name.package.hasData()); - { - CONST.RDB$PACKAGE_NAME.NULL = FALSE; - strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str()); - fb_assert(package); - package->addConstant(tdbb, name, m_isPrivate, m_type); // Do not cache blob id because the blob is not materialized - } + CONST.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str()); + + fb_assert(package); + package->addConstant(tdbb, name, m_isPrivate, m_type); // Do not cache blob id because the blob is not materialized // Schema of the parent package CONST.RDB$SCHEMA_NAME.NULL = FALSE; diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index adf2b924c28..83d47d16d28 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -176,10 +176,10 @@ class CreatePackageConstantNode final : public DdlNode m_isPrivate(isPrivate) { } - Firebird::string internalPrint(NodePrinter& printer) const; - DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) final; - void checkPermission(thread_db* tdbb, jrd_tra* transaction) final; - void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) final; + Firebird::string internalPrint(NodePrinter& printer) const override; + DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override; + void checkPermission(thread_db* tdbb, jrd_tra* transaction) override; + void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) override; inline void makePublic() { @@ -197,7 +197,7 @@ class CreatePackageConstantNode final : public DdlNode bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction); protected: - virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector) + virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector) override { statusVector << Firebird::Arg::Gds(createAlterCode(create, alter, diff --git a/src/include/firebird/impl/msg/dyn.h b/src/include/firebird/impl/msg/dyn.h index 575243112a4..ffdfd00049c 100644 --- a/src/include/firebird/impl/msg/dyn.h +++ b/src/include/firebird/impl/msg/dyn.h @@ -315,4 +315,4 @@ FB_IMPL_MSG_SYMBOL(DYN, 322, dyn_dup_blob_filter, "Blob filter @1 already exists FB_IMPL_MSG(DYN, 323, dyn_column_name_exists, -612, "42", "000", "Column @1 already exists in table @2") FB_IMPL_MSG_SYMBOL(DYN, 324, dyn_constant_not_found, "Constant @1 not found") FB_IMPL_MSG_SYMBOL(DYN, 325, dyn_dup_const, "Constant @1 already exists") -FB_IMPL_MSG_SYMBOL(DYN, 326, dyn_non_constant_constant, "The constant \"@1\" must be initialized by a constant expression") +FB_IMPL_MSG_SYMBOL(DYN, 326, dyn_non_constant_constant, "The constant @1 must be initialized by a constant expression") diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index 8bad893d222..660e8cfc62a 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1012,5 +1012,5 @@ FB_IMPL_MSG(JRD, 1009, bad_constant_blr, -901, "2F", "000", "Error while parsing FB_IMPL_MSG(JRD, 1010, bad_constant_desc, -901, "2F", "000", "Error while reading type of the constant with name @1") FB_IMPL_MSG(JRD, 1011, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found") FB_IMPL_MSG(JRD, 1012, bad_constant_type, -901, "2F", "000", "@1 is not supported to be a constant type") -FB_IMPL_MSG(JRD, 1013, not_defined_constant, -901, "42", "000", "The constant @1 is not defined in the package @2") +FB_IMPL_MSG(JRD, 1013, not_defined_constant, -901, "42", "000", "The constant @1 is not defined") FB_IMPL_MSG(JRD, 1014, const_name, -901, "42", "000", "CONSTANT @1") diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp index 964624d10dc..ce628ba2ce6 100644 --- a/src/jrd/Package.epp +++ b/src/jrd/Package.epp @@ -39,14 +39,8 @@ #include "../jrd/cvt_proto.h" // CVT_get_string_ptr #include "../jrd/mov_proto.h" // MOV_get_string_ptr #include "../common/classes/VaryStr.h" -#include "../common/classes/alloc.h" // ALLOC_ARGS0 #include "../dsql/make_proto.h" // DsqlDescMaker -// I do not know why but the macros is undefined in CI -#ifndef ALLOC_ARGS0 -#define ALLOC_ARGS0 -#endif - using namespace Firebird; using namespace Jrd; @@ -110,14 +104,14 @@ void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScr dsqlScratch->getDebugData().clear(); // Gen blr into dsqlScratch - AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0)); + AutoMemoryPool tempPool(MemoryPool::createPool(&dsqlScratch->getPool())); CastNode cast(*tempPool, constExpr, type); dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5); cast.genBlr(dsqlScratch); dsqlScratch->appendUChar(blr_eoc); Attachment* attachment = tdbb->getAttachment(); - MemoryPool* csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); + MemoryPool* csb_pool = attachment->att_database->createPool(); ContextPoolHolder context(tdbb, csb_pool); AutoPtr autoCsb(FB_NEW_POOL(*csb_pool) CompilerScratch(*csb_pool)); @@ -132,7 +126,7 @@ void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScr nullptr, false, 0); } -inline bid getConstantBid(thread_db* tdbb, const QualifiedName& name) +inline static bid getConstantBid(thread_db* tdbb, const QualifiedName& name) { Attachment* attachment = tdbb->getAttachment(); jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); @@ -178,7 +172,7 @@ dsc* ConstantValue::makeValue(thread_db* tdbb, Request* request) attachment->att_database->deletePool(csb_pool); }); - csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); + csb_pool = attachment->att_database->createPool(); try { diff --git a/src/jrd/Package.h b/src/jrd/Package.h index e271f35a4b5..2be4b2c89b2 100644 --- a/src/jrd/Package.h +++ b/src/jrd/Package.h @@ -25,8 +25,8 @@ */ -#ifndef JRD_CONSTANT_H -#define JRD_CONSTANT_H +#ifndef JRD_PACKAGE_H +#define JRD_PACKAGE_H #include "firebird.h" #include "../jrd/CacheVector.h" @@ -251,4 +251,4 @@ class Package final : public Firebird::PermanentStorage, public ObjectBase } // namespace Jrd -#endif // JRD_CONSTANT_H +#endif // JRD_PACKAGE_H diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 63c22df4273..417a0953d13 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -7018,7 +7018,7 @@ const SysFunction SysFunction::functions[] = {"EXP", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlExp, NULL}, {"FIRST_DAY", 2, 2, DEFAULT, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay}, {"FLOOR", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlFloor, NULL}, - {"GEN_UUID", 0, 0, CONSTANT, NULL, makeUuid, evlGenUuid, NULL}, + {"GEN_UUID", 0, 0, 0, NULL, makeUuid, evlGenUuid, NULL}, {"GREATEST", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue}, {"HASH", 1, 2, DEFAULT, setParamsHash, makeHash, evlHash, NULL}, {"HEX_DECODE", 1, 1, DEFAULT, NULL, makeDecodeHex, evlDecodeHex, NULL}, diff --git a/src/jrd/SystemPackages.h b/src/jrd/SystemPackages.h index 2cf1e403183..0c3bcbc4673 100644 --- a/src/jrd/SystemPackages.h +++ b/src/jrd/SystemPackages.h @@ -190,9 +190,9 @@ namespace Jrd const std::initializer_list aValueBlr = {} ) : name(aName), - fieldId(aFieldId), - valueSource(aValueSource), - valueBlr(pool, aValueBlr) + fieldId(aFieldId), + valueSource(aValueSource), + valueBlr(pool, aValueBlr) { } SystemConstant(Firebird::MemoryPool& pool, const SystemConstant& other) From d65d7caa4e2a5c9cdd16d3287ee4f512deee6bed Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 5 May 2026 09:56:59 +0300 Subject: [PATCH 117/119] Apply suggestions from code review. Thanks to @asfernandes Co-authored-by: Adriano dos Santos Fernandes <529415+asfernandes@users.noreply.github.com> --- src/burp/backup.epp | 4 ++-- src/burp/restore.epp | 5 +++-- src/dsql/DdlNodes.epp | 1 + src/dsql/PackageNodes.epp | 13 +++++++++---- src/jrd/vio.cpp | 1 - 5 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 58fce7fa6cf..2aace1759cc 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -4893,10 +4893,10 @@ void write_constants() BURP_verbose(USHORT(isc_gbak_writing_constant), SafeArg() << name.toQuotedString().data()); put(tdgbl, att_end); } - END_FOR; + END_FOR ON_ERROR general_on_error(); - END_ERROR; + END_ERROR MISC_release_request_silent(req_handle1); } diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 7c57f9f568b..cbea6656739 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -10620,6 +10620,7 @@ bool get_constants(BurpGlobals* tdgbl) CONST.RDB$DESCRIPTION.NULL = FALSE; get_source_blob(tdgbl, CONST.RDB$DESCRIPTION, true); break; + default: bad_attribute(scan_next_attr, attribute, USHORT(isc_gbak_constant)); break; @@ -10628,10 +10629,10 @@ bool get_constants(BurpGlobals* tdgbl) BURP_verbose(USHORT(isc_gbak_restoring_constant), SafeArg() << name.toQuotedString().data()); } - END_STORE; + END_STORE ON_ERROR general_on_error(); - END_ERROR; + END_ERROR return true; } diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index c96aabcf650..af79a21bccc 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -15784,6 +15784,7 @@ static bool checkObjectExist(thread_db* tdbb, jrd_tra* transaction, const Qualif END_FOR break; } + case obj_package_constant: { static const CachedRequestId requestId; diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index c8e5ad5624c..2ec3e2185f4 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -400,8 +400,8 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler switch (itemType) { - case blr_pkg_reference_to_constant: - { + case blr_pkg_reference_to_constant: + { QualifiedName fullName; csb->csb_blr_reader.getMetaName(fullName.object); csb->csb_blr_reader.getMetaName(fullName.schema); @@ -426,8 +426,10 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler } if (!node->m_package) + { status_exception::raise(Arg::Gds(isc_bad_constant_name) << Arg::Str(fullName.toQuotedString())); + } return node; } @@ -681,7 +683,8 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc // Get existing filed // Store constant dsc in RDB$FIELDS FOR (REQUEST_HANDLE eraseDscRequest TRANSACTION_HANDLE transaction) - FLD IN RDB$FIELDS CROSS CONST IN RDB$CONSTANTS + FLD IN RDB$FIELDS + CROSS CONST IN RDB$CONSTANTS CROSS PACKAGE IN RDB$PACKAGES WITH PACKAGE.RDB$PACKAGE_NAME EQ name.package.c_str() AND PACKAGE.RDB$SCHEMA_NAME EQ name.schema.c_str() AND @@ -779,7 +782,6 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc storeGlobalField(tdbb, transaction, newDomainName, m_type); } - if (newDomainName.object.hasData()) newDom.dyn_fld_source = newDomainName; @@ -876,6 +878,7 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) proc->name.package = name.object; break; } + case PackageItemType::CONSTANT: { CreatePackageConstantNode* const constant = (*items)[i].constant; @@ -1347,6 +1350,7 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) break; } + case PackageItemType::CONSTANT: { CreatePackageConstantNode* const constant = (*arrays[i])[j].constant; @@ -1510,6 +1514,7 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc proc->executeDdl(tdbb, elem.dsqlScratch, transaction, true); break; } + case PackageItemType::CONSTANT: { CreatePackageConstantNode* constant = elem.constant; diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index cbe0138503e..94f31269eef 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -2359,7 +2359,6 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_const_package, &desc2); MOV_get_metaname(tdbb, &desc2, object_name.package); - DFW_post_work(transaction, dfw_delete_package_constant, &desc, &schemaDesc, 0, object_name.package); break; From f345518001e3847a15386194a5dac87a7dc10de6 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 5 May 2026 10:51:31 +0300 Subject: [PATCH 118/119] Remove unneccecery NULL = FALSE --- src/burp/backup.epp | 4 +- src/burp/restore.epp | 9 +- src/dsql/PackageNodes.epp | 220 +++++++++++++++++------------------- src/isql/FrontendParser.cpp | 2 +- src/jrd/ini.epp | 2 - 5 files changed, 111 insertions(+), 126 deletions(-) diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 2aace1759cc..e0188591d55 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -2364,7 +2364,7 @@ void write_character_sets() * each user defined character set. * **************************************/ - IRequest* req_handle1 = nullptr; + Firebird::IRequest* req_handle1 = nullptr; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); @@ -4855,7 +4855,7 @@ void write_user_privileges() void write_constants() { - Firebird::IRequest* req_handle1 = nullptr; + IRequest* req_handle1 = nullptr; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); diff --git a/src/burp/restore.epp b/src/burp/restore.epp index cbea6656739..3b6d19749e5 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -4535,7 +4535,7 @@ bool get_function(BurpGlobals* tdgbl) bool existFlag = false; - ITransaction* local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; + Firebird::ITransaction* local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; if (tdgbl->runtimeODS >= DB_VERSION_DDL12) { @@ -10567,7 +10567,7 @@ bool get_constants(BurpGlobals* tdgbl) att_type attribute; scan_attr_t scan_next_attr; - Firebird::ITransaction* local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; + ITransaction* local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_type_req_handle1) @@ -10579,7 +10579,6 @@ bool get_constants(BurpGlobals* tdgbl) switch (attribute) { case att_constant_name: - CONST.RDB$CONSTANT_NAME.NULL = FALSE; GET_TEXT(CONST.RDB$CONSTANT_NAME); name.object = CONST.RDB$CONSTANT_NAME; break; @@ -10591,17 +10590,14 @@ bool get_constants(BurpGlobals* tdgbl) break; case att_constant_field_source: - CONST.RDB$FIELD_SOURCE.NULL = FALSE; GET_TEXT(CONST.RDB$FIELD_SOURCE); break; case att_constant_private_flag: - CONST.RDB$PRIVATE_FLAG.NULL = FALSE; CONST.RDB$PRIVATE_FLAG = (USHORT) get_int32(tdgbl); break; case att_constant_blr: - CONST.RDB$CONSTANT_BLR.NULL = FALSE; get_blr_blob(tdgbl, CONST.RDB$CONSTANT_BLR, true); break; @@ -10611,7 +10607,6 @@ bool get_constants(BurpGlobals* tdgbl) break; case att_constant_schema_name: - CONST.RDB$SCHEMA_NAME.NULL = FALSE; GET_TEXT(CONST.RDB$SCHEMA_NAME); name.schema = CONST.RDB$SCHEMA_NAME; break; diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 2ec3e2185f4..e6fdf21dec6 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -61,99 +61,98 @@ DATABASE DB = STATIC "ODS.RDB"; namespace { - -class DropConstantNode -{ -public: - DropConstantNode(Firebird::MemoryPool& pool, const QualifiedName& fullName) : - m_name(pool, fullName) - { } - - void dsqlPass(DsqlCompilerScratch* dsqlScratch) + class DropConstantNode { - dsqlScratch->qualifyExistingName(m_name, obj_package_constant); - } + public: + DropConstantNode(Firebird::MemoryPool& pool, const QualifiedName& fullName) : + m_name(pool, fullName) + { } - void execute(thread_db* tdbb, DsqlCompilerScratch*, jrd_tra* transaction) - { - static const CachedRequestId requestId; - AutoCacheRequest request(tdbb, requestId); + void dsqlPass(DsqlCompilerScratch* dsqlScratch) + { + dsqlScratch->qualifyExistingName(m_name, obj_package_constant); + } - FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - CONST IN RDB$CONSTANTS - WITH CONST.RDB$SCHEMA_NAME EQ m_name.schema.c_str() AND - CONST.RDB$PACKAGE_NAME EQ m_name.package.c_str() AND - CONST.RDB$CONSTANT_NAME EQ m_name.object.c_str() + void execute(thread_db* tdbb, DsqlCompilerScratch*, jrd_tra* transaction) { - ERASE CONST; + static const CachedRequestId requestId; + AutoCacheRequest request(tdbb, requestId); + + FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ m_name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ m_name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ m_name.object.c_str() + { + ERASE CONST; + } + END_FOR } - END_FOR - } -private: - QualifiedName m_name; -}; + private: + QualifiedName m_name; + }; -template -void dropItem(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& fullName) -{ - MemoryPool& pool = dsqlScratch->getPool(); - jrd_tra* transaction = dsqlScratch->getTransaction(); + template + void dropItem(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& fullName) + { + MemoryPool& pool = dsqlScratch->getPool(); + jrd_tra* transaction = dsqlScratch->getTransaction(); - TDropNode dropNode(pool, fullName); - dropNode.dsqlPass(dsqlScratch); - dropNode.execute(tdbb, dsqlScratch, transaction); -} + TDropNode dropNode(pool, fullName); + dropNode.dsqlPass(dsqlScratch); + dropNode.execute(tdbb, dsqlScratch, transaction); + } -template -void dropMissingItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema, - const PackageItemsHolder::ItemsSignatureArray& existsItems, - const CreateAlterPackageNode::ItemsNameArray& newNames) -{ - for (auto i = existsItems.begin(); i != existsItems.end(); ++i) + template + void dropMissingItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema, + const PackageItemsHolder::ItemsSignatureArray& existsItems, + const CreateAlterPackageNode::ItemsNameArray& newNames) { - if (!newNames.exist(i->name)) + for (auto i = existsItems.begin(); i != existsItems.end(); ++i) { - dropItem(tdbb, dsqlScratch, - QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.object)); + if (!newNames.exist(i->name)) + { + dropItem(tdbb, dsqlScratch, + QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.object)); + } } } -} -template -void dropItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema, - const PackageItemsHolder::ItemsSignatureArray& items) -{ - for (auto i = items.begin(); i != items.end(); ++i) + template + void dropItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema, + const PackageItemsHolder::ItemsSignatureArray& items) { - dropItem(tdbb, dsqlScratch, - QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.object)); + for (auto i = items.begin(); i != items.end(); ++i) + { + dropItem(tdbb, dsqlScratch, + QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.object)); + } } -} -void checkItemsDefineMatch(MemoryPool& pool, const QualifiedName& packageAndSchema, - const PackageItemsHolder::ItemsSignatureArray& newItems, - const PackageItemsHolder::ItemsSignatureArray& existingItems, - const ISC_STATUS missingCode, const ISC_STATUS mismatchCode) -{ - for (auto i = existingItems.begin(); i != existingItems.end(); ++i) + void checkItemsDefineMatch(MemoryPool& pool, const QualifiedName& packageAndSchema, + const PackageItemsHolder::ItemsSignatureArray& newItems, + const PackageItemsHolder::ItemsSignatureArray& existingItems, + const ISC_STATUS missingCode, const ISC_STATUS mismatchCode) { - FB_SIZE_T pos; - const bool found = newItems.find(Signature(pool, i->name), pos); - - if (!found || !newItems[pos].defined) + for (auto i = existingItems.begin(); i != existingItems.end(); ++i) { - status_exception::raise( - Arg::Gds(missingCode) << i->name << packageAndSchema.toQuotedString()); - } - else if (newItems[pos] != *i) - { - status_exception::raise( - Arg::Gds(mismatchCode) << i->name << packageAndSchema.toQuotedString()); + FB_SIZE_T pos; + const bool found = newItems.find(Signature(pool, i->name), pos); + + if (!found || !newItems[pos].defined) + { + status_exception::raise( + Arg::Gds(missingCode) << i->name << packageAndSchema.toQuotedString()); + } + else if (newItems[pos] != *i) + { + status_exception::raise( + Arg::Gds(mismatchCode) << i->name << packageAndSchema.toQuotedString()); + } } } -} } // namespace @@ -402,37 +401,37 @@ DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, Compiler { case blr_pkg_reference_to_constant: { - QualifiedName fullName; - csb->csb_blr_reader.getMetaName(fullName.object); - csb->csb_blr_reader.getMetaName(fullName.schema); - csb->csb_blr_reader.getMetaName(fullName.package); + QualifiedName fullName; + csb->csb_blr_reader.getMetaName(fullName.object); + csb->csb_blr_reader.getMetaName(fullName.schema); + csb->csb_blr_reader.getMetaName(fullName.package); - PackageReferenceNode* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, fullName, itemType); + PackageReferenceNode* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, fullName, itemType); - if (csb->collectingDependencies()) - { - Dependency dependency(obj_package_constant); - dependency.name = fullName; + if (csb->collectingDependencies()) + { + Dependency dependency(obj_package_constant); + dependency.name = fullName; - csb->addDependency(dependency); - } + csb->addDependency(dependency); + } + + { // Package cache + auto package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE); + if (package) + { + node->m_package = csb->csb_resources->packages.registerResource(package->getPermanent()); + } + } - { // Package cache - auto package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE); - if (package) + if (!node->m_package) { - node->m_package = csb->csb_resources->packages.registerResource(package->getPermanent()); + status_exception::raise(Arg::Gds(isc_bad_constant_name) << + Arg::Str(fullName.toQuotedString())); } - } - if (!node->m_package) - { - status_exception::raise(Arg::Gds(isc_bad_constant_name) << - Arg::Str(fullName.toQuotedString())); + return node; } - - return node; - } // TODO: rowtype default: fb_assert(false); @@ -510,11 +509,11 @@ ValueExprNode* PackageReferenceNode::pass1(thread_db* tdbb, CompilerScratch* csb switch (m_itemType) { - case blr_pkg_reference_to_constant: - { - CMP_post_access(tdbb, csb, security.object, 0, SCL_usage, obj_packages, m_fullName.getSchemaAndPackage()); - break; - } + case blr_pkg_reference_to_constant: + { + CMP_post_access(tdbb, csb, security.object, 0, SCL_usage, obj_packages, m_fullName.getSchemaAndPackage()); + break; + } default: fb_assert(false); return nullptr; @@ -538,14 +537,14 @@ dsc* PackageReferenceNode::execute(thread_db* tdbb, Request* request) const impure_value* outputImpure = request->getImpure(m_impureOffset); switch (m_itemType) { - case blr_pkg_reference_to_constant: - { - Package* package = m_package(request->getResources()); - package->checkReload(tdbb); + case blr_pkg_reference_to_constant: + { + Package* package = m_package(request->getResources()); + package->checkReload(tdbb); - EVL_make_value(tdbb, package->findConstant(m_fullName)->makeValue(tdbb, request), outputImpure); - return &outputImpure->vlu_desc; - } + EVL_make_value(tdbb, package->findConstant(m_fullName)->makeValue(tdbb, request), outputImpure); + return &outputImpure->vlu_desc; + } default: fb_assert(false); return nullptr; @@ -583,8 +582,7 @@ DdlNode* CreatePackageConstantNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) status_exception::raise(Arg::Gds(isc_dyn_non_constant_constant) << name.toQuotedString()); } - DdlNode::dsqlPass(dsqlScratch); - return nullptr; + return DdlNode::dsqlPass(dsqlScratch); } void CreatePackageConstantNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) @@ -633,11 +631,9 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat CONST IN RDB$CONSTANTS USING { // Constant name - CONST.RDB$CONSTANT_NAME.NULL = FALSE; strcpy(CONST.RDB$CONSTANT_NAME, name.object.c_str()); // Description (filed) name - CONST.RDB$FIELD_SOURCE.NULL = FALSE; strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); // Gen value blr @@ -649,18 +645,14 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat // Parent package fb_assert(name.package.hasData()); - CONST.RDB$PACKAGE_NAME.NULL = FALSE; strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str()); fb_assert(package); package->addConstant(tdbb, name, m_isPrivate, m_type); // Do not cache blob id because the blob is not materialized // Schema of the parent package - CONST.RDB$SCHEMA_NAME.NULL = FALSE; strcpy(CONST.RDB$SCHEMA_NAME, name.schema.c_str()); - // Type - CONST.RDB$PRIVATE_FLAG.NULL = FALSE; CONST.RDB$PRIVATE_FLAG = m_isPrivate; CONST.RDB$CONSTANT_SOURCE.NULL = TRUE; diff --git a/src/isql/FrontendParser.cpp b/src/isql/FrontendParser.cpp index 7c6b4052df4..5e398f43062 100644 --- a/src/isql/FrontendParser.cpp +++ b/src/isql/FrontendParser.cpp @@ -662,7 +662,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() node.name = parseQualifiedName(true); if (node.name->package.isEmpty()) { - // It is expecting to get package = and object = + // It is expecting to get package = and object = // But it is getting package = and object = // Fix the name node.name->package = node.name->schema; diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index c69c586a90f..2b7c9cfb945 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -2180,7 +2180,6 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR blrData.append(constant.valueBlr); blrData.push(blr_eoc); attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, blrData); - CONST.RDB$CONSTANT_BLR.NULL = FALSE; // Parent package PAD(systemPackage.name, CONST.RDB$PACKAGE_NAME); @@ -2189,7 +2188,6 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR PAD(SYSTEM_SCHEMA, CONST.RDB$SCHEMA_NAME); // Type - CONST.RDB$PRIVATE_FLAG.NULL = FALSE; CONST.RDB$PRIVATE_FLAG = false; if (constant.valueSource != nullptr) From 26fd59b58a56912dff6fc87101f790d276ea43f4 Mon Sep 17 00:00:00 2001 From: Artyom Abakumov Date: Tue, 5 May 2026 12:29:39 +0300 Subject: [PATCH 119/119] Add RDB$FIELD_SOURCE_SCHEMA_NAME and fix domain handling --- src/burp/backup.epp | 1 + src/burp/burp.h | 1 + src/burp/restore.epp | 4 ++ src/dsql/PackageNodes.epp | 37 ++++++++++------ src/dsql/PackageNodes.h | 92 +++++++++++++++++++-------------------- src/isql/show.epp | 8 ++-- src/jrd/Package.epp | 2 +- src/jrd/dfw.epp | 74 +++++++++++++++---------------- src/jrd/ids.h | 9 ++-- src/jrd/ini.epp | 1 + src/jrd/relations.h | 1 + 11 files changed, 125 insertions(+), 105 deletions(-) diff --git a/src/burp/backup.epp b/src/burp/backup.epp index e0188591d55..ca9c0b6b235 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -4872,6 +4872,7 @@ void write_constants() PUT_TEXT(att_constant_name, CONST.RDB$CONSTANT_NAME); PUT_TEXT(att_constant_package, CONST.RDB$PACKAGE_NAME); PUT_TEXT(att_constant_field_source, CONST.RDB$FIELD_SOURCE); + PUT_TEXT(att_constant_field_source_schema, CONST.RDB$FIELD_SOURCE_SCHEMA_NAME); if (!CONST.RDB$PRIVATE_FLAG.NULL) put_int32(att_constant_private_flag, CONST.RDB$PRIVATE_FLAG); diff --git a/src/burp/burp.h b/src/burp/burp.h index 0130d4070ab..b481e96f0c4 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -707,6 +707,7 @@ enum att_type { att_constant_name = SERIES, att_constant_package, att_constant_field_source, + att_constant_field_source_schema, att_constant_private_flag, att_constant_blr, att_constant_source, diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 3b6d19749e5..a786a396b6c 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -10593,6 +10593,10 @@ bool get_constants(BurpGlobals* tdgbl) GET_TEXT(CONST.RDB$FIELD_SOURCE); break; + case att_constant_field_source_schema: + GET_TEXT(CONST.RDB$FIELD_SOURCE_SCHEMA_NAME); + break; + case att_constant_private_flag: CONST.RDB$PRIVATE_FLAG = (USHORT) get_int32(tdgbl); break; diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index e6fdf21dec6..55db5f5ce57 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -614,15 +614,6 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat status_exception::raise(Arg::Gds(isc_dyn_dup_const) << name.toQuotedString()); } - // Generate a new unique field name because constants in different packages may have same name - // but names in RDB$FIELDS should be unique in for a SCHEMA - QualifiedName fieldName = name; - fieldName.object = ""; - // Store description in the RDB$FIELDS relation - storeGlobalField(tdbb, tdbb->getTransaction(), fieldName, m_type); - fb_assert(fieldName.schema == name.schema); - fb_assert(fieldName.package == name.package); - static const CachedRequestId requestId; AutoCacheRequest storeConstantRequest(tdbb, requestId); @@ -634,7 +625,22 @@ void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScrat strcpy(CONST.RDB$CONSTANT_NAME, name.object.c_str()); // Description (filed) name - strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); + if (m_type->typeOfName.object.hasData()) + { + strcpy(CONST.RDB$FIELD_SOURCE_SCHEMA_NAME, m_type->typeOfName.schema.c_str()); + strcpy(CONST.RDB$FIELD_SOURCE, m_type->typeOfName.object.c_str()); + } + else + { + // Generate a new unique field name because constants in different packages may have same name + // but names in RDB$FIELDS should be unique in for a SCHEMA + QualifiedName fieldName({}, name.schema); + storeGlobalField(tdbb, transaction, fieldName, m_type); + fb_assert(fieldName.schema == name.schema); + + strcpy(CONST.RDB$FIELD_SOURCE_SCHEMA_NAME, fieldName.schema.c_str()); + strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str()); + } // Gen value blr ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); @@ -683,7 +689,7 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND - FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND + FLD.RDB$SCHEMA_NAME EQ CONST.RDB$FIELD_SOURCE_SCHEMA_NAME AND FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE { dyn_fld origDom, newDom; @@ -700,7 +706,7 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc origDom.dyn_charlen = FLD.RDB$CHARACTER_LENGTH; origDom.dyn_collation = FLD.RDB$COLLATION_ID; origDom.dyn_null_flag = !FLD.RDB$NULL_FLAG.NULL && FLD.RDB$NULL_FLAG != 0; - origDom.dyn_fld_source = QualifiedName(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME); + origDom.dyn_fld_source = QualifiedName(CONST.RDB$FIELD_SOURCE, CONST.RDB$FIELD_SOURCE_SCHEMA_NAME); // If the original field type is an array, force its blr type to blr_blob if (FLD.RDB$DIMENSIONS != 0) @@ -751,6 +757,7 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc // Case b1: Internal domain -> internal domain. // Case b2: Domain -> internal domain. + newDomainName.schema = name.schema; m_type->resolve(dsqlScratch, true); if (wasInternalDomain) // Case b1: Internal domain -> internal domain. @@ -788,8 +795,12 @@ bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratc ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); MODIFY CONST USING + if (newDomainName.object.hasData()) + { + strcpy(CONST.RDB$FIELD_SOURCE_SCHEMA_NAME, newDomainName.schema.c_str()); + strcpy(CONST.RDB$FIELD_SOURCE, newDomainName.object.c_str()); + } attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData()); - CONST.RDB$CONSTANT_BLR.NULL = FALSE; END_MODIFY fb_assert(package); diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 83d47d16d28..b1b09e2baeb 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -38,62 +38,60 @@ enum class PackageItemType : USHORT META_SIZE }; -template -class ItemNames : public TArray +class PackageItemsHolder { public: - ItemNames() : TArray() - {} - - explicit ItemNames(Firebird::MemoryPool& pool) : TArray(pool) - {} - - operator TArray&() + template + class ItemNames : public TArray { - return *this; - } + public: + ItemNames() : TArray() + {} - template - void addName(const QualifiedName& newName) - { - checkDuplicate(newName); - TArray::add(TType(newName.object)); - } + explicit ItemNames(Firebird::MemoryPool& pool) : TArray(pool) + {} - template - void checkDuplicate(const QualifiedName& newName) - { - if constexpr (std::is_same_v) + operator TArray&() { - if (!TArray::exist(newName.object)) - return; // The name is unique + return *this; } - else + + template + void addName(const QualifiedName& newName) { - // Cast - if (!TArray::exist(TType(newName.object))) - return; // The name is unique + checkDuplicate(newName); + TArray::add(TType(newName.object)); } - static_assert(size_t(IValue) >= 0 && size_t(IValue) < size_t(PackageItemType::META_SIZE), "Invalid item type"); - static const std::array names{ - "FUNCTION", - "PROCEDURE", - "CONSTANT", - }; - - // Print just the object name because the full path is present in the parent error message - Firebird::status_exception::raise( - Firebird::Arg::Gds(isc_no_meta_update) << - Firebird::Arg::Gds(isc_dyn_duplicate_package_item) << - Firebird::Arg::Str(names[size_t(IValue)]) << Firebird::Arg::Str(newName.object.toQuotedString())); - } -}; - - -class PackageItemsHolder -{ -public: + template + void checkDuplicate(const QualifiedName& newName) + { + if constexpr (std::is_same_v) + { + if (!TArray::exist(newName.object)) + return; // The name is unique + } + else + { + // Cast + if (!TArray::exist(TType(newName.object))) + return; // The name is unique + } + + static_assert(size_t(IValue) >= 0 && size_t(IValue) < size_t(PackageItemType::META_SIZE), "Invalid item type"); + static const std::array names{ + "FUNCTION", + "PROCEDURE", + "CONSTANT", + }; + + // Print just the object name because the full path is present in the parent error message + Firebird::status_exception::raise( + Firebird::Arg::Gds(isc_no_meta_update) << + Firebird::Arg::Gds(isc_dyn_duplicate_package_item) << + Firebird::Arg::Str(names[size_t(IValue)]) << Firebird::Arg::Str(newName.object.toQuotedString())); + } + }; using ItemsSignatureArray = ItemNames, Signature>; public: @@ -264,7 +262,7 @@ class CreateAlterPackageNode : public DdlNode DsqlCompilerScratch* dsqlScratch; }; - using ItemsNameArray = ItemNames, MetaName>; + using ItemsNameArray = PackageItemsHolder::ItemNames, MetaName>; public: CreateAlterPackageNode(MemoryPool& pool, const QualifiedName& aName) diff --git a/src/isql/show.epp b/src/isql/show.epp index 919c53485cf..47047d1e5ad 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -6852,8 +6852,8 @@ static processing_state show_constants(const std::optional& isqlGlob.printf("%s (%s %s)%-20s", CONST.RDB$CONSTANT_NAME, CONST.RDB$PACKAGE_NAME, type, " "); fb_utils::exact_name(CONST.RDB$FIELD_SOURCE); - fb_utils::exact_name(CONST.RDB$SCHEMA_NAME); - print_constant_type(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME); + fb_utils::exact_name(CONST.RDB$FIELD_SOURCE_SCHEMA_NAME); + print_constant_type(CONST.RDB$FIELD_SOURCE, CONST.RDB$FIELD_SOURCE_SCHEMA_NAME); isqlGlob.printf(NEWLINE); } @@ -6883,8 +6883,8 @@ static processing_state show_constants(const std::optional& isqlGlob.printf("%s (%s %s)%-20s", CONST.RDB$CONSTANT_NAME, CONST.RDB$PACKAGE_NAME, type, " "); fb_utils::exact_name(CONST.RDB$FIELD_SOURCE); - fb_utils::exact_name(CONST.RDB$SCHEMA_NAME); - print_constant_type(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME); + fb_utils::exact_name(CONST.RDB$FIELD_SOURCE_SCHEMA_NAME); + print_constant_type(CONST.RDB$FIELD_SOURCE, CONST.RDB$FIELD_SOURCE_SCHEMA_NAME); isqlGlob.printf(NEWLINE); } diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp index ce628ba2ce6..a3066081d93 100644 --- a/src/jrd/Package.epp +++ b/src/jrd/Package.epp @@ -71,7 +71,7 @@ dsc ConstantValue::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const Qua WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND - FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND + FLD.RDB$SCHEMA_NAME EQ CONST.RDB$FIELD_SOURCE_SCHEMA_NAME AND FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE { found = true; diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 21ea7aad941..1eb3dc4f9d7 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1107,52 +1107,52 @@ namespace switch (phase) { - case 0: - return false; + case 0: + return false; - case 1: - case 2: - case 3: - return true; + case 1: + case 2: + case 3: + return true; - case 4: - { - const QualifiedName constantName = work->getQualifiedName(); - const QualifiedName packageName = constantName.getSchemaAndPackage(); + case 4: + { + const QualifiedName constantName = work->getQualifiedName(); + const QualifiedName packageName = constantName.getSchemaAndPackage(); - auto* package = MetadataCache::getVersioned(tdbb, packageName, - CacheFlag::AUTOCREATE); + auto* package = MetadataCache::getVersioned(tdbb, packageName, + CacheFlag::AUTOCREATE); - fb_assert(package); - if (package == nullptr) - return false; + fb_assert(package); + if (package == nullptr) + return false; - auto constant = package->findConstant(constantName); - fb_assert(constant); - if (constant == nullptr) - return false; + auto constant = package->findConstant(constantName); + fb_assert(constant); + if (constant == nullptr) + return false; - bid blobId = constant->getBlobId(tdbb); + bid blobId = constant->getBlobId(tdbb); - Jrd::Database* dbb = tdbb->getDatabase(); - MemoryPool* new_pool = dbb->createPool(); - // block is used to ensure verify_cache() - // works in not deleted context - { - Jrd::ContextPoolHolder context(tdbb, new_pool); - // Store constant dependencies as a package dependencies - MET_get_dependencies(tdbb, nullptr, nullptr, 0, nullptr, &blobId, - nullptr, - nullptr, - packageName, obj_package_header, - 0, transaction); + Jrd::Database* dbb = tdbb->getDatabase(); + MemoryPool* new_pool = dbb->createPool(); + // block is used to ensure verify_cache() + // works in not deleted context + { + Jrd::ContextPoolHolder context(tdbb, new_pool); + // Store constant dependencies as a package dependencies + MET_get_dependencies(tdbb, nullptr, nullptr, 0, nullptr, &blobId, + nullptr, + nullptr, + packageName, obj_package_header, + 0, transaction); - dbb->deletePool(new_pool); - } + dbb->deletePool(new_pool); + } - return false; - } // case - } // + return false; + } // case + } // switch return false; } diff --git a/src/jrd/ids.h b/src/jrd/ids.h index 7e8f3e8823d..58cb9e4667b 100644 --- a/src/jrd/ids.h +++ b/src/jrd/ids.h @@ -91,6 +91,9 @@ static_assert(f_const_name == 0, "Wrong field id"); static_assert(f_const_package == 1, "Wrong field id"); static_assert(f_const_field == 2, "Wrong field id"); - static_assert(f_const_private_flag == 3, "Wrong field id"); - static_assert(f_const_blr == 4, "Wrong field id"); - static_assert(f_const_source == 5, "Wrong field id"); + static_assert(f_const_field_source_schema == 3, "Wrong field id"); + static_assert(f_const_private_flag == 4, "Wrong field id"); + static_assert(f_const_blr == 5, "Wrong field id"); + static_assert(f_const_source == 6, "Wrong field id"); + static_assert(f_const_package_schema == 7, "Wrong field id"); + static_assert(f_const_description == 8, "Wrong field id"); diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index 2b7c9cfb945..77e4c66d1d3 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -2172,6 +2172,7 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR PAD(constant.name, CONST.RDB$CONSTANT_NAME); // Description (filed) name + PAD(SYSTEM_SCHEMA, CONST.RDB$FIELD_SOURCE_SCHEMA_NAME); PAD(names[gfields[constant.fieldId].gfld_name], CONST.RDB$FIELD_SOURCE); // Put the blr diff --git a/src/jrd/relations.h b/src/jrd/relations.h index c3703693a7c..f5f5235d263 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -847,6 +847,7 @@ RELATION(nam_constants, rel_constants, ODS_14_0, rel_persistent) FIELD(f_const_name, nam_const_name, fld_f_name, 0, ODS_14_0) FIELD(f_const_package, nam_pkg_name, fld_pkg_name, 1, ODS_14_0) FIELD(f_const_field, nam_f_source, fld_f_name, 0, ODS_14_0) + FIELD(f_const_field_source_schema, nam_field_source_sch_name, fld_sch_name, 0, ODS_14_0) FIELD(f_const_private_flag, nam_private_flag, fld_flag_nullable, 0, ODS_14_0) FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_14_0) FIELD(f_const_source, nam_const_source, fld_source, 1, ODS_14_0)