diff --git a/builds/win32/msvc15/engine_static.vcxproj b/builds/win32/msvc15/engine_static.vcxproj index 67bbab0013e..3bd5bfff341 100644 --- a/builds/win32/msvc15/engine_static.vcxproj +++ b/builds/win32/msvc15/engine_static.vcxproj @@ -41,6 +41,7 @@ + @@ -417,6 +418,7 @@ + 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..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 @@ -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 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. diff --git a/doc/sql.extensions/README.ddl.txt b/doc/sql.extensions/README.ddl.txt index 8d53d670a5d..35b714c1294 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/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt index 74ecf7cf888..dafe0356614 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,17 @@ 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 . + Header constants can also be used directly with just the name for package body elements. + - Facilitate permission management. It's generally a good practice to create routines with a privileged database user and grant @@ -106,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 4ec7cd6d1f2..25db9caf086 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); @@ -4845,6 +4853,58 @@ 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 + WITH CONST.RDB$SCHEMA_NAME NE SYSTEM_SCHEMA + { + QualifiedMetaString name(CONST.RDB$CONSTANT_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) + 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 3fb58a76b9e..0130d4070ab 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 5d5a31765fb..ae52a139f44 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*); @@ -10556,6 +10557,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) { /************************************** @@ -11399,6 +11479,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/common/ParserTokens.h b/src/common/ParserTokens.h index 3ddad43eb78..8dfd004bc2e 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 d0189ad4294..6b286b9f7bd 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -80,6 +80,7 @@ #include "../jrd/ini.h" #include "../jrd/GarbageCollector.h" #include "../jrd/ProtectRelations.h" +#include "../dsql/PackageNodes.h" namespace Jrd { @@ -131,16 +132,6 @@ static rel_t relationType(SSHORT relationTypeNull, SSHORT relationType) noexcept static void saveField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const MetaName& fieldName); static dsql_rel* saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& relationName, bool view, bool creating); -static void updateRdbFields(const TypeClause* type, - SSHORT& fieldType, - SSHORT& fieldLength, - SSHORT& fieldSubTypeNull, SSHORT& fieldSubType, - SSHORT& fieldScaleNull, SSHORT& fieldScale, - SSHORT& characterSetIdNull, SSHORT& characterSetId, - SSHORT& characterLengthNull, SSHORT& characterLength, - SSHORT& fieldPrecisionNull, SSHORT& fieldPrecision, - SSHORT& collationIdNull, SSHORT& collationId, - SSHORT& segmentLengthNull, SSHORT& segmentLength); static ISC_STATUS getErrorCodeByObjectType(int obj_type); static constexpr const char* CHECK_CONSTRAINT_EXCEPTION = "check_constraint"; @@ -900,7 +891,7 @@ static dsql_rel* saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, } // Update RDB$FIELDS received by reference. -static void updateRdbFields(const TypeClause* type, +void DdlNode::updateRdbFields(const TypeClause* type, SSHORT& fieldType, SSHORT& fieldLength, SSHORT& fieldSubTypeNull, SSHORT& fieldSubType, @@ -1508,6 +1499,19 @@ 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 + dsqlScratch->qualifyNewName(constantName); // Search for a 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; @@ -1612,6 +1616,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, name.getSchemaAndPackage(), SCL_alter); + break; + default: fb_assert(false); } @@ -1775,6 +1783,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; @@ -1798,7 +1812,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) @@ -11762,11 +11776,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))) { @@ -11782,6 +11798,13 @@ 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; @@ -15750,6 +15773,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; @@ -15961,6 +15999,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)); } @@ -16168,6 +16211,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; @@ -17186,6 +17230,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/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 29eac015e71..4dc26d20d99 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; @@ -466,6 +468,23 @@ void ExprNode::collectStreams(SortedStreamList& streamList) const } } +bool ExprNode::isChildrenConstant() const +{ + NodeRefsHolder holder; + getChildren(holder, false); + + for (auto i : holder.refs) + { + if (*i == nullptr) + continue; + + if (!(*i)->constant()) + return false; + } + + return true; +} + bool ExprNode::computable(CompilerScratch* csb, StreamType stream, bool allowOnlyCurrentStream, ValueExprNode* /*value*/) { @@ -6448,6 +6467,33 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec } } + // Use context to check conflicts beween . and . + dsql_ctx packageContext(dsqlScratch->getPool()); + { // Consatnts + + QualifiedName constantName(dsqlName, + dsqlQualifier.schema.hasData() ? dsqlQualifier.schema : dsqlScratch->package.schema, + dsqlQualifier.object.hasData() ? dsqlQualifier.object : dsqlScratch->package.object); + + if (constantName.package.hasData()) + { + dsqlScratch->qualifyNewName(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); + } + } + } + // 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. @@ -12462,7 +12508,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::isChildrenConstant() && function->isConstant(); } void SysFuncCallNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) @@ -14149,6 +14200,17 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (!node->dsqlVar || (node->dsqlVar->type == dsql_var::TYPE_LOCAL && !node->dsqlVar->initialized && !dsqlScratch->mainScratch)) { + if (dsqlScratch->package.object.hasData()) + { + thread_db* tdbb = JRD_get_thread_data(); + QualifiedName 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(), constantFullName); + } + } + PASS1_field_unknown(NULL, dsqlName.toQuotedString().c_str(), this); } diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index 108daf7d984..9b196cdb6f4 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; @@ -655,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; @@ -714,6 +759,11 @@ class DerivedExprNode 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; @@ -1315,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; @@ -1834,6 +1909,11 @@ class ScalarNode 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; @@ -1925,6 +2005,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; @@ -2061,6 +2151,11 @@ class SubstringNode 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; @@ -2275,6 +2381,11 @@ class ValueIfNode final : public TypedNode > MetaNameBidPair; typedef Firebird::GenericMap MetaNameBidMap; @@ -519,6 +531,7 @@ class ExprNode : public DmlNode TYPE_WINDOW_CLAUSE, TYPE_WINDOW_CLAUSE_FRAME, TYPE_WINDOW_CLAUSE_FRAME_EXTENT, + TYPE_REFERENCE, // Bool types TYPE_BINARY_BOOL, @@ -677,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. + // 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; // Check if expression could return NULL or expression can turn NULL into a true/false. @@ -688,9 +704,18 @@ class ExprNode : public DmlNode // Verify if this node is allowed in an unmapped boolean. 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; + } + // 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; @@ -1342,6 +1367,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(), diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 42f888c9cb0..d267fb87ef5 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -38,6 +38,14 @@ #include "../jrd/Attachment.h" #include "../jrd/scl_proto.h" +#include "../common/dsc_proto.h" // DSC_make_descriptor +#include "../jrd/Package.h" // Constant + +#include "../dsql/metd_proto.h" // METD_get_domain +#include "../common/classes/VaryStr.h" // METD_get_domain +#include "../jrd/par_proto.h" // PAR_proto in Nodes.h (dependency hell) +#include "../jrd/met.h" // Metacache +#include "../jrd/Statement.h" // Jrd::Statement using namespace Firebird; @@ -53,155 +61,785 @@ DATABASE DB = STATIC "ODS.RDB"; namespace { - // 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) + +class DropConstantNode +{ +public: + DropConstantNode(Firebird::MemoryPool& pool, const QualifiedName& fullName) : + m_name(pool, fullName) + { } + + void dsqlPass(DsqlCompilerScratch* dsqlScratch) + { + dsqlScratch->qualifyExistingName(m_name, obj_package_constant); + } + + void execute(thread_db* tdbb, DsqlCompilerScratch*, jrd_tra* transaction) + { + 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: + 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.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) + { + 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) + { + 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 + + +//---------------------- + +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 PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, + const QualifiedName& packageName, + 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); + + 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 + + requestHandle.reset(tdbb, drq_l_pkg_procs, DYN_REQUESTS); + requestHandle2.reset(tdbb, drq_l_pkg_proc_args, DYN_REQUESTS); - if (!FUN.RDB$DETERMINISTIC_FLAG.NULL && FUN.RDB$DETERMINISTIC_FLAG != 0) - function.flags |= Signature::FLAG_DETERMINISTIC; + 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) + 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 - functions.add(function); + if (collectConstants) + { + 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 + } +} + +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.object, fullName.schema.hasData() ? fullName.schema : PUBLIC_SCHEMA, fullName.package), m_itemType(itemType) +{} + +string PackageReferenceNode::internalPrint(NodePrinter& printer) const +{ + ExprNode::internalPrint(printer); + + NODE_PRINT(printer, m_fullName); - requestHandle.reset(tdbb, drq_l_pkg_procs, DYN_REQUESTS); - requestHandle2.reset(tdbb, drq_l_pkg_proc_args, DYN_REQUESTS); + 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 objects do not have 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_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); + + PackageReferenceNode* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, fullName, itemType); - 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() + if (csb->collectingDependencies()) { - Signature procedure(PRC.RDB$PROCEDURE_NAME); - procedure.defined = !PRC.RDB$PROCEDURE_BLR.NULL || !PRC.RDB$ENTRYPOINT.NULL; + Dependency dependency(obj_package_constant); + dependency.name = fullName; - if (details) + csb->addDependency(dependency); + } + + { // Package cache + auto package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE); + if (package) { - 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 + node->m_package = csb->csb_resources->packages.registerResource(package->getPermanent()); + } + } + + if (!node->m_package) + status_exception::raise(Arg::Gds(isc_bad_constant_name) << + Arg::Str(fullName.toQuotedString())); + + return node; + } + // TODO: rowtype + default: + fb_assert(false); + } + + return nullptr; +} + +void PackageReferenceNode::genBlr(DsqlCompilerScratch* dsqlScratch) +{ + dsqlScratch->appendUChar(blr_package_reference); + 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()); +} + +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 = ConstantValue::getDesc(tdbb, transaction, m_fullName); +} + +bool PackageReferenceNode::constantExists(thread_db* tdbb, Jrd::jrd_tra* transaction, + const QualifiedName& fullName, bool* isPrivate) +{ + fb_assert(fullName.schema.hasData()); + + if (fullName.package.isEmpty() || fullName.object.isEmpty()) + return false; + + auto* package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE); + if (package == nullptr) + return false; + + const ConstantValue* value = package->findConstant(fullName); + + if (value == nullptr) + return false; + + if (isPrivate) + *isPrivate = value->isPrivate; + + return true; +} + +void PackageReferenceNode::getDesc(thread_db* tdbb, CompilerScratch*, dsc* desc) +{ + *desc = ConstantValue::getDesc(tdbb, tdbb->getTransaction(), m_fullName); +} + +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_package = copier.csb->csb_resources->packages.registerResource(m_package()); + + 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_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; + } + + return this; +} + +ValueExprNode* PackageReferenceNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + ValueExprNode::pass2(tdbb, csb); + + if (m_itemType == blr_pkg_reference_to_constant) + m_impureOffset = csb->allocImpure(); + + return this; +} + +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); + + EVL_make_value(tdbb, &package->findConstant(m_fullName)->makeValue(tdbb), outputImpure); + return &outputImpure->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_expr); + NODE_PRINT(printer, m_isPrivate); + + return "PackageReferenceNode"; +} + +DdlNode* CreatePackageConstantNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) +{ + dsqlScratch->qualifyNewName(name); + + m_expr = m_expr->dsqlPass(dsqlScratch); + + QualifiedName dummyCollationName; + DDL_resolve_intl_type(dsqlScratch, m_type, dummyCollationName); + + if (!m_expr->constant()) + { + status_exception::raise(Arg::Gds(isc_dyn_non_constant_constant) << name.toQuotedString()); + } + + DdlNode::dsqlPass(dsqlScratch); + return nullptr; +} + +void CreatePackageConstantNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ } + +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); +} + +void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) +{ + Attachment* attachment = transaction->getAttachment(); + + // Check uniqueness + fb_assert(package != nullptr); + if (package->findConstant(name)) + { + 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); + + int faults = 0; + while (true) + { + 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 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()); + + // 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()); { - 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); + 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 } - END_FOR + + // 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; } + END_STORE + break; + } + catch (const status_exception& ex) + { + if (ex.value()[1] != isc_unique_key_violation) + throw; + + if (++faults > MAX_SSHORT) + throw; - procedures.add(procedure); + fb_utils::init_status(tdbb->tdbb_status_vector); } - END_FOR - } -} // namespace + }// While +} + +bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) +{ + Attachment* attachment = transaction->getAttachment(); + static const CachedRequestId eraseRequestId; + AutoCacheRequest eraseDscRequest(tdbb, eraseRequestId); -//---------------------- + static const CachedRequestId modifyRequestId; + AutoCacheRequest modifyConstantRequest(tdbb, modifyRequestId); + + 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 + 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 + 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, + 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()); + + QualifiedName newDomainName; + + // We have the type. Default and type/domain are exclusive for now. + if (m_type->typeOfName.object.hasData()) + { + // Case a1: Internal domain -> domain. + // Case a2: Domain -> domain. + + newDomainName = m_type->typeOfName; + + if (fb_utils::implicit_domain(newDomainName.object.c_str())) + { + // 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()); + } + + 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; + } + } + 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 + ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); + + MODIFY CONST USING + attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData()); + 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); + } + + found = true; + } + END_FOR + + return found; +} string CreateAlterPackageNode::internalPrint(NodePrinter& printer) const @@ -215,6 +853,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 +883,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,28 +896,34 @@ 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: + ddlNode = nullptr; // Warning fb_assert(false); } @@ -363,7 +1000,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); @@ -385,41 +1022,69 @@ 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) - 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; + + 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$PACKAGE_NAME.NULL = FALSE; + strcpy(PKG.RDB$PACKAGE_NAME, name.object.c_str()); - PKG.RDB$PACKAGE_NAME.NULL = FALSE; - strcpy(PKG.RDB$PACKAGE_NAME, name.object.c_str()); + PKG.RDB$SYSTEM_FLAG.NULL = FALSE; + PKG.RDB$SYSTEM_FLAG = 0; - PKG.RDB$SYSTEM_FLAG.NULL = FALSE; - PKG.RDB$SYSTEM_FLAG = 0; + PKG.RDB$OWNER_NAME.NULL = FALSE; + strcpy(PKG.RDB$OWNER_NAME, ownerName.c_str()); - PKG.RDB$OWNER_NAME.NULL = FALSE; - strcpy(PKG.RDB$OWNER_NAME, ownerName.c_str()); + PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE; + attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, source); - 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; - if (ssDefiner.has_value()) + 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, {}); } @@ -430,6 +1095,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; @@ -438,35 +1105,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, {}); - SortedObjectsArray existingFuncs(pool); - SortedObjectsArray existingProcs(pool); - collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false); - - 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); - } - } + PackageItemsHolder existingItems(pool); + existingItems.collectPackagedItems(tdbb, transaction, name, false, 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; @@ -490,13 +1140,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, {}); } @@ -541,21 +1192,25 @@ 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) { 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->package = package; + (*items)[i].constant->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true); + break; } } } @@ -591,12 +1246,15 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, bool found = false; 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, {}); @@ -608,7 +1266,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 @@ -619,25 +1277,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); @@ -656,7 +1298,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 } @@ -690,8 +1335,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 +1348,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 +1364,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 +1379,23 @@ 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; + if (arrays[i] == items) + constant->alter = true; + + constant->makePrivate(); + break; + } default: fb_assert(false); } @@ -799,6 +1441,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 @@ -814,6 +1457,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 @@ -837,12 +1481,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 +1496,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,91 +1507,67 @@ 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; } - } - } - } + case PackageItemType::CONSTANT: + { + CreatePackageConstantNode* constant = elem.constant; - SortedObjectsArray newFuncs(pool); - SortedObjectsArray newProcs(pool); - collectPackagedItems(tdbb, transaction, name, newFuncs, newProcs, true); + headerItems.constants.checkDuplicate(constant->name); + existingItems.constants.checkDuplicate(constant->name); - for (SortedObjectsArray::iterator i = existingFuncs.begin(); - i != existingFuncs.end(); ++i) - { - FB_SIZE_T pos; - bool found = newFuncs.find(Signature(pool, i->name), pos); - - 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()); + fb_assert(package); + constant->package = package; + 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()); - } + { + 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 @@ -988,12 +1605,14 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra bool found = false; 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, {}); @@ -1004,7 +1623,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 @@ -1031,9 +1650,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 +1675,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 +1690,21 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra } END_FOR + // 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, {}); savePoint.release(); // everything is ok diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 839e3c8d0f6..372beb76314 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -25,9 +25,196 @@ #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", + }; + + // 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: + 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, const bool details, const bool collectConstants); + void clear(); + +public: + ItemsSignatureArray functions; + ItemsSignatureArray procedures; + ItemsSignatureArray constants; +}; + +class PackageReferenceNode final : public TypedNode +{ +public: + PackageReferenceNode(Firebird::MemoryPool& pool, const QualifiedName& name, + const UCHAR itemType = blr_pkg_reference_to_constant); + + 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; + + // Search for a package constant by its fully qualified name + static bool constantExists(thread_db* tdbb, Jrd::jrd_tra* transaction, + const QualifiedName& name, bool* isPrivate = nullptr); + + void getDesc(thread_db*, CompilerScratch*, dsc*) final; + + ValueExprNode* copy(thread_db*, NodeCopier&) const final; + ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb) 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: + ConstantValue* m_prefetchedConstant = nullptr; + CachedResource m_package; + 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_expr(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, CompilerScratch*& nodeContext); + 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; + + Package* package = nullptr; + +private: + NestConst m_type; + NestConst m_expr; + MetaId m_id = 0; + bool m_isPrivate = false; +}; + class CreateAlterPackageNode : public DdlNode { @@ -37,36 +224,44 @@ 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 +272,7 @@ class CreateAlterPackageNode : public DdlNode items(NULL), functionNames(pool), procedureNames(pool), + constantNames(pool), owner(pool) { } @@ -101,7 +297,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; @@ -110,9 +306,11 @@ 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; + MetaId id; private: MetaName owner; diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index c81cf09dff2..cf7acfdfa94 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -561,6 +561,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/parse.y b/src/dsql/parse.y index a8b4786d33d..b37203db3e0 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -726,6 +726,7 @@ using namespace Firebird; %token UNLIST %token WITHIN %token RDB_RESET_CONTEXT +%token CONSTANT // precedence declarations for expression evaluation @@ -886,6 +887,7 @@ using namespace Firebird; Jrd::SetBindNode* setBindNode; Jrd::SessionResetNode* sessionResetNode; Jrd::ForRangeNode::Direction forRangeDirection; + Jrd::CreatePackageConstantNode* createPackageConstantNode; } %include types.y @@ -1040,6 +1042,13 @@ grant0($node) $node->grantAdminOption = $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 @@ -1334,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 @@ -3175,6 +3191,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 @@ -3262,6 +3280,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 @@ -6312,6 +6337,7 @@ ddl_type3 : PARAMETER { $$ = obj_parameter; } | PROCEDURE PARAMETER { $$ = obj_procedure; } | FUNCTION PARAMETER { $$ = obj_udf; } + | CONSTANT { $$ = obj_package_constant; } ; %type ddl_type4 @@ -9866,6 +9892,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 @@ -10186,6 +10217,7 @@ non_reserved_word | SCHEMA | UNLIST | ERROR + | CONSTANT ; %% 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) << diff --git a/src/include/firebird/impl/blr.h b/src/include/firebird/impl/blr.h index 4fc7669a807..84c351fe4f9 100644 --- a/src/include/firebird/impl/blr.h +++ b/src/include/firebird/impl/blr.h @@ -532,4 +532,10 @@ #define blr_within_group_order (unsigned char) 235 +// Package const +#define blr_package_reference (unsigned char) 236 + +// Subcodes of blr_package_reference +#define blr_pkg_reference_to_constant (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 480663c8fdb..e7f2afe0c8b 100644 --- a/src/include/firebird/impl/msg/dyn.h +++ b/src/include/firebird/impl/msg/dyn.h @@ -313,3 +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") diff --git a/src/include/firebird/impl/msg/gbak.h b/src/include/firebird/impl/msg/gbak.h index fb5a173b540..c4c226fa482 100644 --- a/src/include/firebird/impl/msg/gbak.h +++ b/src/include/firebird/impl/msg/gbak.h @@ -418,3 +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(GBAK, 423, gbak_writing_constants, -901, "00", "000", "writing constants") +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") 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/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index 4bcafc9d62a..27f3322f4cd 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1005,3 +1005,11 @@ FB_IMPL_MSG(JRD, 1002, argmustbe_numeric_function, -833, "42", "000", "Argument FB_IMPL_MSG(JRD, 1003, percetile_only_one_sort_item, -833, "42", "000", "The PERCENTILE_DISC and PERENTILE_CONT functions support only one sort item in WITHIN GROUP") FB_IMPL_MSG(JRD, 1004, argmustbe_const_within_group, -833, "42", "000", "Argument for @1 function must be constant within each group") FB_IMPL_MSG(JRD, 1005, update_overwrite, -901, "27", "000", "UPDATE will overwrite changes made by the trigger or by the another UPDATE in the same cursor") +FB_IMPL_MSG(JRD, 1006, const_name, -901, "42", "000", "CONSTANT @1") +FB_IMPL_MSG(JRD, 1007, private_constant, -901, "42", "000", "The constant @1 is private") +FB_IMPL_MSG(JRD, 1008, package_alias_help, -901, "42", "000", "Use an alias to resolve the conflict") +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, 1013, not_defined_constant, -901, "42", "000", "The constant @1 is not defined in the package @2") diff --git a/src/include/firebird/impl/msg/sqlerr.h b/src/include/firebird/impl/msg/sqlerr.h index a2687461bcd..b2af71a0334 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, 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/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index 0efaf4e8965..78005b6dd9a 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5959,6 +5959,15 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_argmustbe_numeric_function = 335545322; isc_percetile_only_one_sort_item = 335545323; isc_argmustbe_const_within_group = 335545324; + isc_update_overwrite = 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_not_defined_constant = 335545333; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; @@ -6223,6 +6232,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 = 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; @@ -6359,6 +6372,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 = 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/isql/FrontendParser.cpp b/src/isql/FrontendParser.cpp index 64218057132..7c6b4052df4 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_CONSTANTS("CONSTANTS"); switch (const auto showCommandToken = lexer.getToken(); showCommandToken.type) { @@ -655,6 +656,22 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() 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/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 e905cc4de9b..919c53485cf 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -118,6 +118,7 @@ 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); const char* const spaces = " "; static TEXT Print_buffer[512]; @@ -2571,6 +2572,23 @@ processing_state SHOW_metadata(const FrontendParser::AnyShowNode& node) return show_wireStats(); }, + [&](const FrontendParser::ShowConstantsNode& node) + { + 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) { static_assert(FrontendParser::AlwaysFalseV, @@ -6745,3 +6763,140 @@ 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()) + { + // 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$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"; + + if (first && msg) + isqlGlob.printf("%s%s", msg, NEWLINE); + + first = false; + + 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; + } + else + { + // All constants + + 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"; + + if (first && msg) + isqlGlob.printf("%s%s", msg, NEWLINE); + + first = false; + + 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; + } + + if (first) + return OBJECT_NOT_FOUND; + + return SKIP; +} diff --git a/src/isql/tests/FrontendParserTest.cpp b/src/isql/tests/FrontendParserTest.cpp index d4f62903351..e01b7027588 100644 --- a/src/isql/tests/FrontendParserTest.cpp +++ b/src/isql/tests/FrontendParserTest.cpp @@ -609,6 +609,19 @@ 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"))); + + 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"))); } 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}) } ) { diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp new file mode 100644 index 00000000000..d18387969b3 --- /dev/null +++ b/src/jrd/Package.epp @@ -0,0 +1,556 @@ +/* + * PROGRAM: Firebird CONSTANTS implementation. + * 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"); + * 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/Package.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" // 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" +#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; + +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); + } + + + { // 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); + + 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 +{ + 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 + 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); + } + + Attachment* attachment = tdbb->getAttachment(); + MemoryPool* csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); + ContextPoolHolder context(tdbb, csb_pool); + + CompilerScratch* csb = nullptr; + Cleanup cc([&tdbb, requestToRestore = tdbb->getRequest(), &csb]() + { + 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 = executeConstantExpressionWithRequest(tdbb, csb); + if (output != nullptr) + genConstantCompatibleBlr(tdbb, dsqlScratch, *output); +} + +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) +{ + if (value.vlu_desc.dsc_address != nullptr) + return value.vlu_desc; + + Attachment* attachment = tdbb->getAttachment(); + + MemoryPool* csb_pool = nullptr; + try + { + csb_pool = attachment->att_database->createPool(ALLOC_ARGS0); + ContextPoolHolder context(tdbb, csb_pool); + + try + { + 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); + } + + executeConstantExpression(tdbb, csb, getPool(), value); + } + catch (const Exception& ex) + { + 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(); + } + } + catch (const Exception&) + { + attachment->att_database->deletePool(csb_pool); + throw; + } + + return value.vlu_desc; +} + +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(); + value.name = constName; + value.id = constId; + value.isPrivate = isPrivate; + + idMap.put(constId, id); + nameMap.put(constName, id); + + return value; +} + + +// ---------------------------- +// 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()); + + 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::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(); + + { // Security + static const CachedRequestId requestId; + AutoCacheRequest requestConst(tdbb, requestId); + + 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); + + 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 + } + + return this->m_callReload ? ScanResult::REPEAT : ScanResult::COMPLETE; +} + +void Package::checkReload(thread_db* tdbb) +{ + if (m_callReload) + { + reload(tdbb, 0); + m_callReload = false; + } +} + +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; +} + +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::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); + + if (it != nullptr) + return &constants.values[*it]; + + return nullptr; +} + +ConstantValue* Package::findConstant(const QualifiedName& name) +{ + 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..375f48368e8 --- /dev/null +++ b/src/jrd/Package.h @@ -0,0 +1,237 @@ +/* + * 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 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; + QualifiedName name; + QualifiedName securityName; +}; + +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& updateConstant(thread_db* tdbb, const MetaId constId, + const bool isPrivate, + const TypeClause* type); + + ConstantValue* findConstant(const MetaId id); + ConstantValue* findConstant(const 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 99962ee616d..36e0e5a895d 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 += packages.transfer(tdbb, to, internal, digest); if (hasHash) { diff --git a/src/jrd/Resources.h b/src/jrd/Resources.h index cb7f2f9276d..a181bdb2611 100644 --- a/src/jrd/Resources.h +++ b/src/jrd/Resources.h @@ -42,6 +42,8 @@ class DbTriggers; class CharSetVers; class IndexPermanent; class IndexVersion; +class Package; +class PackagePermanent; namespace Cached { @@ -52,6 +54,7 @@ namespace Cached typedef CacheElement Function; typedef CacheElement Triggers; typedef CacheElement Index; + typedef CacheElement Package; } class Resources; @@ -66,6 +69,7 @@ union VersionedPartPtr CharSetVers* charset; DbTriggers* triggers; IndexVersion* index; + Package* package; }; class VersionedObjects : public pool_alloc_rpt, @@ -120,6 +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 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; } @@ -127,6 +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 Package* VersionedObjects::object(FB_SIZE_T n) const { return data[n].package; } template @@ -283,7 +289,8 @@ class Resources final procedures(p, versionCurrentPosition), functions(p, versionCurrentPosition), triggers(p, versionCurrentPosition), - indices(p, versionCurrentPosition) + indices(p, versionCurrentPosition), + packages(p, versionCurrentPosition) { } ~Resources(); @@ -294,6 +301,13 @@ class Resources final RscArray functions; RscArray triggers; RscArray indices; + RscArray packages; + + inline FB_SIZE_T countVersionedObjects() const noexcept + { + return charSets.getCount() + relations.getCount() + procedures.getCount() + + functions.getCount() + triggers.getCount() + packages.getCount(); + } }; // specialization @@ -303,6 +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 packages; } namespace Rsc { @@ -312,6 +327,7 @@ namespace Rsc typedef CachedResource CSet; typedef CachedResource Trig; typedef CachedResource Idx; + typedef CachedResource Package; }; //namespace Rsc diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index bf5418ee4b0..525950ec9e4 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); diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 4c216b68031..63c22df4273 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -6977,98 +6977,99 @@ dsc* evlUnicodeVal(thread_db* tdbb, const SysFunction*, const NestValueArray& ar +constexpr static 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_RESET_CONTEXT, 1, 1, false, setParamsResetContext, makeLongResult, evlResetContext, 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} + // name, minArgCount, maxArgCount, flags, setParamsFunc, makeFunc, evlFunc, misc + + {"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, 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}, + {"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, 0, NULL, makeDoubleResult, evlRand, NULL}, + {RDB_GET_CONTEXT, 2, 2, DETERMINISTIC, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL}, + {"RDB$GET_TRANSACTION_CN", 1, 1, 0, setParamsInt64, makeGetTranCN, evlGetTranCN, NULL}, + {"RDB$ROLE_IN_USE", 1, 1, DETERMINISTIC, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL}, + {RDB_RESET_CONTEXT, 1, 1, 0, setParamsResetContext, makeLongResult, evlResetContext, 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}, + {"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, 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}, + {"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, 0, NULL, NULL, NULL, NULL} }; diff --git a/src/jrd/SysFunction.h b/src/jrd/SysFunction.h index 5a2cbc671b9..d8a7a21b95f 100644 --- a/src/jrd/SysFunction.h +++ b/src/jrd/SysFunction.h @@ -46,6 +46,12 @@ namespace Jrd class SysFunction { public: + enum Flags : UCHAR + { + 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 +60,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 +70,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[]; }; 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/SystemTriggers.epp b/src/jrd/SystemTriggers.epp index e20773be845..ddc6fbed78a 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 populateCache(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; + const 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) +{ + populateCache(tdbb, record); } void afterUpdateProcedure(thread_db* tdbb, Record* orgRecord, Record* newRecord) @@ -1624,17 +1631,7 @@ 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); - } + populateCache(tdbb, record); } void afterUpdateFunction(thread_db* tdbb, Record* orgRecord, Record* newRecord) @@ -1705,17 +1702,7 @@ 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; - - CSetId csId = CSetId(MOV_get_long(tdbb, &desc, 0)); - MetadataCache::newVersion(tdbb, csId); - } + populateCache(tdbb, record); } } // anonymous @@ -1818,6 +1805,10 @@ void SystemTriggers::executeAfterInsertTriggers(thread_db* tdbb, jrd_rel* relati case rel_funs: afterInsertFunction(tdbb, record); break; + + case rel_packages: + populateCache(tdbb, record); + break; } } diff --git a/src/jrd/constants.h b/src/jrd/constants.h index b40a51dbf77..9a8b7336b0a 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -154,6 +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* 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/dfw.epp b/src/jrd/dfw.epp index 430b2a98145..8f499f156d5 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -133,6 +133,8 @@ #include "../jrd/shut_proto.h" #include "../jrd/ProtectRelations.h" +#include "../jrd/Package.h" + #ifdef HAVE_UNISTD_H #include #endif @@ -407,6 +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 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*); @@ -466,6 +469,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); } @@ -485,6 +491,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); } @@ -1116,6 +1125,65 @@ namespace ERR_post(status); } } + + static bool modifyConstant(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 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; + } + + return false; + } + + static bool deleteConstant(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction) + { + fb_assert(!work->dfw_package.isEmpty()); + + SET_TDBB(tdbb); + + 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; + } + } // namespace static inline constexpr deferred_task task_table[] = @@ -1170,6 +1238,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_modify_package_constant, modifyConstant }, + { dfw_delete_package_constant, deleteConstant }, + { dfw_create_package, createPackage }, + { dfw_null, NULL } }; @@ -2218,17 +2290,75 @@ static bool user_management(thread_db* /*tdbb*/, SSHORT phase, DeferredWork* wor return false; } +static bool createPackage(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; @@ -2239,11 +2369,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; @@ -2252,13 +2407,34 @@ 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; @@ -2815,6 +2991,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; @@ -2828,6 +3007,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..d474f8627b4 100644 --- a/src/jrd/drq.h +++ b/src/jrd/drq.h @@ -242,6 +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_MAX }; diff --git a/src/jrd/fields.h b/src/jrd/fields.h index 49a47fd055a..56f18a0d5d0 100644 --- a/src/jrd/fields.h +++ b/src/jrd/fields.h @@ -239,3 +239,10 @@ 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_tab_type , nam_mon_tab_type , dtype_varying , 32 , dsc_text_type_ascii , NULL , true , ODS_14_0) + + 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/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; diff --git a/src/jrd/ids.h b/src/jrd/ids.h index 6eabeb76241..3e974c03e4a 100644 --- a/src/jrd/ids.h +++ b/src/jrd/ids.h @@ -88,3 +88,10 @@ static_assert(f_mon_tab_rec_stat_id == 3, "Wrong field id"); 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"); diff --git a/src/jrd/idx.h b/src/jrd/idx.h index 0e7811a7870..4ce7dfabf0b 100644 --- a/src/jrd/idx.h +++ b/src/jrd/idx.h @@ -524,6 +524,20 @@ 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), // 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_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 }} }; diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp index 9b0ce4b3935..834487ae845 100644 --- a/src/jrd/ini.epp +++ b/src/jrd/ini.epp @@ -1966,8 +1966,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()) { @@ -1990,6 +1993,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 @@ -2158,5 +2163,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 + } } } diff --git a/src/jrd/irq.h b/src/jrd/irq.h index a1611c9e07a..cbb22ea63e6 100644 --- a/src/jrd/irq.h +++ b/src/jrd/irq.h @@ -126,6 +126,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 diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 220634bdcd4..ae4d3a0d568 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/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 a1868ed441e..88117b2e163 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_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 ce927051978..cfe37762827 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_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 145135e851b..084612ed759 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -98,6 +98,7 @@ #include "../jrd/trace/TraceJrdHelpers.h" #include "firebird/impl/msg_helper.h" #include "../jrd/LocalTemporaryTable.h" +#include "../jrd/Package.h" #ifdef HAVE_CTYPE_H @@ -340,6 +341,7 @@ void MetadataCache::cleanup(thread_db* tdbb) mdc_procedures.cleanup(tdbb); mdc_functions.cleanup(tdbb); mdc_charsets.cleanup(tdbb); + mdc_packages.cleanup(tdbb); for (unsigned i = 0; i < DB_TRIGGERS_COUNT; ++i) { @@ -569,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 package constants. * **************************************/ SET_TDBB(tdbb); @@ -806,6 +809,41 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const } END_FOR + + request.reset(tdbb, irq_m_fields10, IRQ_REQUESTS); + + Package* package = nullptr; + FOR(REQUEST_HANDLE request) + DEP IN RDB$DEPENDENCIES CROSS + 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 + 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); + + 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 + return calcFieldRelation; } @@ -4402,6 +4440,10 @@ void MET_store_dependency(thread_db* tdbb, name = dependency.name; dpdo_name = &name; break; + case obj_package_constant: + name = dependency.name; + dpdo_name = &name; + break; } MetaName field_name; @@ -4743,6 +4785,14 @@ void MetadataCache::releaseLocks(thread_db* tdbb) charset->releaseLocks(tdbb); } + // Release packages locks + + for (auto package : mdc_packages) + { + if (package) + package->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 16055082e57..5111ed2a505 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_packages(getPool()), mdc_cleanup_queue(pool) { memset(mdc_triggers, 0, sizeof(mdc_triggers)); @@ -500,6 +501,16 @@ class MetadataCache : public Firebird::PermanentStorage } }; + template + class Vector + { + public: + static CacheVector& get(MetadataCache* mdc) + { + return mdc->mdc_packages; + } + }; + static MetadataCache* getCache(thread_db* tdbb) noexcept; class GeneratorFinder @@ -605,6 +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_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 27ceff0f931..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) @@ -491,6 +492,12 @@ 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) + NAME("MON$TABLE_TYPE", nam_mon_tab_type) NAME("MON$LOCAL_TEMPORARY_TABLES", nam_mon_local_temp_tables) NAME("MON$TABLE_ID", nam_mon_tab_id) diff --git a/src/jrd/obj.h b/src/jrd/obj.h index 5246253b90b..9c351fef616 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 = 41; // 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: @@ -270,6 +273,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 9bae656cb5c..ee784f53630 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -681,6 +681,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, 0, ODS_14_0) END_RELATION // Relation 43 (SEC$USERS) @@ -840,3 +841,16 @@ RELATION(nam_mon_local_temp_table_columns, rel_mon_local_temp_table_columns, ODS FIELD(f_mon_lttc_charset_id, nam_mon_charset_id, fld_charset_id, 0, ODS_14_0) FIELD(f_mon_lttc_collate_id, nam_mon_collate_id, fld_collate_id, 0, ODS_14_0) 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) + 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_description, nam_description, fld_description, 1, ODS_14_0) +END_RELATION diff --git a/src/jrd/tra.h b/src/jrd/tra.h index 68872e68716..fb1e05a9557 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -553,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 53bb6bba270..3cc9163bc9d 100644 --- a/src/jrd/trig.h +++ b/src/jrd/trig.h @@ -82,6 +82,8 @@ 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 fb959b29e3d..01afdbba51d 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/Package.h" using namespace Jrd; using namespace Firebird; @@ -2349,6 +2350,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); + + 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); + break; + default: // Shut up compiler warnings break; } @@ -3432,6 +3448,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); + } } } check_class(tdbb, transaction, org_rpb, new_rpb, f_pkg_class); @@ -3735,6 +3757,22 @@ 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: + 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); + + 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); + } + break; + default: break; } @@ -4358,6 +4396,18 @@ 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); + + // 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; case rel_procedures: @@ -4645,6 +4695,19 @@ 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_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 break; }