diff --git a/builds/win32/msvc15/engine_static.vcxproj b/builds/win32/msvc15/engine_static.vcxproj index 67bbab0013e..1543ed65a93 100644 --- a/builds/win32/msvc15/engine_static.vcxproj +++ b/builds/win32/msvc15/engine_static.vcxproj @@ -41,6 +41,7 @@ + diff --git a/doc/sql.extensions/README.ddl.txt b/doc/sql.extensions/README.ddl.txt index bb11f8c1bbe..b63c4fb0442 100644 --- a/doc/sql.extensions/README.ddl.txt +++ b/doc/sql.extensions/README.ddl.txt @@ -138,6 +138,7 @@ COMMENT ON name IS {'txt'|NULL}; COMMENT ON COLUMN table_or_view_name.field_name IS {'txt'|NULL}; COMMENT ON {PROCEDURE | [EXTERNAL] FUNCTION} [ .] name.param_name IS {'txt'|NULL}; COMMENT ON [PROCEDURE | FUNCTION] PARAMETER [ .] name.param_name IS {'txt'|NULL}; +COMMENT ON CONSTANT [ .] . name IS {'txt'|NULL}; An empty literal string '' will act as NULL since the internal code (DYN in this case) works this way with blobs. diff --git a/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt index 74ecf7cf888..c3433557a6c 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 diff --git a/src/burp/backup.epp b/src/burp/backup.epp index f3df7267962..c3d36a71b49 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -156,6 +156,7 @@ void write_triggers(); void write_trigger_messages(); void write_types(); void write_user_privileges(); +void write_constants(); void general_on_error(); @@ -505,6 +506,13 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name) write_pub_tables(); } + if (tdgbl->runtimeODS >= DB_VERSION_DDL14) + { + // Write constants + BURP_verbose(USHORT(isc_gbak_writing_constants)); // writing constants + write_constants(); + } + // Finish up put(tdgbl, (UCHAR) rec_end); @@ -4831,6 +4839,60 @@ void write_user_privileges() MISC_release_request_silent(req_handle1); } +void write_constants() +{ + Firebird::IRequest* req_handle1 = nullptr; + + BurpGlobals* tdgbl = BurpGlobals::getSpecific(); + + FOR (REQUEST_HANDLE req_handle1) + CONST IN RDB$CONSTANTS + { + QualifiedMetaString name(CONST.RDB$CONSTANT_NAME); + + put(tdgbl, rec_constants); + PUT_TEXT(att_constant_name, CONST.RDB$CONSTANT_NAME); + + if (!CONST.RDB$PACKAGE_NAME.NULL) + { + PUT_TEXT(att_constant_package, CONST.RDB$PACKAGE_NAME); + name.package = CONST.RDB$PACKAGE_NAME; + } + + PUT_TEXT(att_constant_field_source, CONST.RDB$FIELD_SOURCE); + + if (!CONST.RDB$PRIVATE_FLAG.NULL) + put_int32(att_constant_private_flag, CONST.RDB$PRIVATE_FLAG); + + if (!CONST.RDB$CONSTANT_BLR.NULL) + put_blr_blob(att_constant_blr, CONST.RDB$CONSTANT_BLR); + + if (!CONST.RDB$CONSTANT_SOURCE.NULL) + put_source_blob(att_constant_source, att_constant_source, CONST.RDB$CONSTANT_SOURCE); + + if (!CONST.RDB$SCHEMA_NAME.NULL) + { + PUT_TEXT(att_constant_schema_name, CONST.RDB$SCHEMA_NAME); + name.schema = CONST.RDB$SCHEMA_NAME; + } + + if (!CONST.RDB$DESCRIPTION.NULL) + { + put_source_blob (att_constant_description, att_constant_description, CONST.RDB$DESCRIPTION); + } + + // writing constant %s + BURP_verbose(USHORT(isc_gbak_writing_constant), SafeArg() << name.toQuotedString().data()); + put(tdgbl, att_end); + } + END_FOR; + ON_ERROR + general_on_error(); + END_ERROR; + + MISC_release_request_silent(req_handle1); +} + } // namespace namespace Burp { diff --git a/src/burp/burp.h b/src/burp/burp.h index 71680b43b03..36c37c0ac22 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -124,7 +124,8 @@ enum rec_type { rec_db_creator, // Database creator rec_publication, // Publication rec_pub_table, // Publication table - rec_schema // Schema + rec_schema, // Schema + rec_constants // Constants }; @@ -701,6 +702,16 @@ enum att_type { att_schema_security_class, att_schema_owner_name, att_schema_description, + + // Constants + att_constant_name = SERIES, + att_constant_package, + att_constant_field_source, + att_constant_private_flag, + att_constant_blr, + att_constant_source, + att_constant_schema_name, + att_constant_description, }; diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 1108459d14d..e1affb98319 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -167,6 +167,7 @@ bool get_trigger_old (BurpGlobals* tdgbl, burp_rel*); bool get_type(BurpGlobals* tdgbl); bool get_user_privilege(BurpGlobals* tdgbl); bool get_view(BurpGlobals* tdgbl, burp_rel*); +bool get_constants_table(BurpGlobals* tdgbl); void ignore_array(BurpGlobals* tdgbl, burp_rel*); void ignore_blob(BurpGlobals* tdgbl); rec_type ignore_data(BurpGlobals* tdgbl, burp_rel*); @@ -10534,6 +10535,85 @@ bool get_view(BurpGlobals* tdgbl, burp_rel* relation) return true; } +bool get_constants_table(BurpGlobals* tdgbl) +{ + if (tdgbl->runtimeODS < DB_VERSION_DDL14) // FB6 + return false; + + QualifiedMetaString name; + + att_type attribute; + scan_attr_t scan_next_attr; + + Firebird::ITransaction* local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; + + STORE (TRANSACTION_HANDLE local_trans + REQUEST_HANDLE tdgbl->handles_get_type_req_handle1) + CONST IN RDB$CONSTANTS + { + skip_init(&scan_next_attr); + while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) + { + switch (attribute) + { + case att_constant_name: + CONST.RDB$CONSTANT_NAME.NULL = FALSE; + GET_TEXT(CONST.RDB$CONSTANT_NAME); + name.object = CONST.RDB$CONSTANT_NAME; + break; + + case att_constant_package: + CONST.RDB$PACKAGE_NAME.NULL = FALSE; + GET_TEXT(CONST.RDB$PACKAGE_NAME); + name.package = CONST.RDB$PACKAGE_NAME; + break; + + case att_constant_field_source: + CONST.RDB$FIELD_SOURCE.NULL = FALSE; + GET_TEXT(CONST.RDB$FIELD_SOURCE); + break; + + case att_constant_private_flag: + CONST.RDB$PRIVATE_FLAG.NULL = FALSE; + CONST.RDB$PRIVATE_FLAG = (USHORT) get_int32(tdgbl); + break; + + case att_constant_blr: + CONST.RDB$CONSTANT_BLR.NULL = FALSE; + get_blr_blob(tdgbl, CONST.RDB$CONSTANT_BLR, true); + break; + + case att_constant_source: + CONST.RDB$CONSTANT_SOURCE.NULL = FALSE; + get_source_blob(tdgbl, CONST.RDB$CONSTANT_SOURCE, true); + break; + + case att_constant_schema_name: + CONST.RDB$SCHEMA_NAME.NULL = FALSE; + GET_TEXT(CONST.RDB$SCHEMA_NAME); + name.schema = CONST.RDB$SCHEMA_NAME; + break; + + case att_constant_description: + CONST.RDB$DESCRIPTION.NULL = FALSE; + get_source_blob(tdgbl, CONST.RDB$DESCRIPTION, true); + break; + default: + bad_attribute(scan_next_attr, attribute, USHORT(isc_gbak_constant)); + break; + } + } + + BURP_verbose(USHORT(isc_gbak_restoring_constant), SafeArg() << name.toQuotedString().data()); + } + END_STORE; + ON_ERROR + general_on_error(); + END_ERROR; + + return true; +} + void ignore_array(BurpGlobals* tdgbl, burp_rel* relation) { /************************************** @@ -11367,6 +11447,12 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file flag = true; break; + case rec_constants: + if (!get_constants_table(tdgbl)) + return false; + flag = true; + break; + default: BURP_error(43, true, SafeArg() << record); // msg 43 don't recognize record type %ld diff --git a/src/common/ParserTokens.h b/src/common/ParserTokens.h index 588252b429c..2d460814abe 100644 --- a/src/common/ParserTokens.h +++ b/src/common/ParserTokens.h @@ -138,6 +138,7 @@ PARSER_TOKEN(TOK_CONDITIONAL, "CONDITIONAL", true) PARSER_TOKEN(TOK_CONNECT, "CONNECT", false) PARSER_TOKEN(TOK_CONNECTIONS, "CONNECTIONS", true) PARSER_TOKEN(TOK_CONSISTENCY, "CONSISTENCY", true) +PARSER_TOKEN(TOK_CONSTANT, "CONSTANT", true) PARSER_TOKEN(TOK_CONSTRAINT, "CONSTRAINT", false) PARSER_TOKEN(TOK_CONTAINING, "CONTAINING", true) PARSER_TOKEN(TOK_CONTINUE, "CONTINUE", true) diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 49e78682a34..d5fa4a804f2 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 { @@ -130,16 +131,6 @@ static rel_t relationType(SSHORT relationTypeNull, SSHORT relationType) noexcept static void saveField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const MetaName& fieldName); static dsql_rel* saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& relationName, bool view, bool creating); -static void updateRdbFields(const TypeClause* type, - SSHORT& fieldType, - SSHORT& fieldLength, - SSHORT& fieldSubTypeNull, SSHORT& fieldSubType, - SSHORT& fieldScaleNull, SSHORT& fieldScale, - SSHORT& characterSetIdNull, SSHORT& characterSetId, - SSHORT& characterLengthNull, SSHORT& characterLength, - SSHORT& fieldPrecisionNull, SSHORT& fieldPrecision, - SSHORT& collationIdNull, SSHORT& collationId, - SSHORT& segmentLengthNull, SSHORT& segmentLength); static ISC_STATUS getErrorCodeByObjectType(int obj_type); static constexpr const char* CHECK_CONSTRAINT_EXCEPTION = "check_constraint"; @@ -887,7 +878,7 @@ static dsql_rel* saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, } // Update RDB$FIELDS received by reference. -static void updateRdbFields(const TypeClause* type, +void DdlNode::updateRdbFields(const TypeClause* type, SSHORT& fieldType, SSHORT& fieldLength, SSHORT& fieldSubTypeNull, SSHORT& fieldSubType, @@ -1486,6 +1477,20 @@ DdlNode* CommentOnNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) dsqlScratch->resolveRoutineOrRelation(name, {objType}); break; + case obj_package_constant: + { + QualifiedName constantName(subName, name.schema, name.object); // name is a package + if (constantName.schema.isEmpty()) + constantName.schema = PUBLIC_SCHEMA; + + 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; @@ -1590,6 +1595,10 @@ void CommentOnNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) SCL_check_package(tdbb, name, SCL_alter); break; + case obj_package_constant: + SCL_check_package(tdbb, name.getSchemaAndPackage(), SCL_alter); + break; + default: fb_assert(false); } @@ -1753,6 +1762,12 @@ void CommentOnNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, j status << Arg::Gds(isc_dyn_package_not_found) << Arg::Str(objNameStr); break; + case obj_package_constant: + tableClause = "rdb$constants"; + columnClause = "rdb$constant_name"; + status << Arg::Gds(isc_bad_constant_name) << Arg::Str(objNameStr); + break; + default: fb_assert(false); return; @@ -1776,7 +1791,7 @@ void CommentOnNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, j sql << "and" << subColumnClause << "=" << subName; } - if (objType == obj_procedure || objType == obj_udf) + if (objType == obj_procedure || objType == obj_udf || objType == obj_package_constant) sql << "and rdb$package_name is not distinct from nullif(" << name.package << ", '')"; if (addWhereClause) @@ -11562,11 +11577,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))) { @@ -11582,6 +11599,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; diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 043133893fb..7affd1964ac 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,28 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec } } + // Use context to check conflicts beween . and . + dsql_ctx packageContext(dsqlScratch->getPool()); + { // Consatnts + + const QualifiedName constantName(dsqlName, + dsqlQualifier.schema.hasData() ? dsqlQualifier.schema : (dsqlScratch->package.schema.hasData() ? dsqlScratch->package.schema : PUBLIC_SCHEMA), + dsqlQualifier.object.hasData() ? dsqlQualifier.object : dsqlScratch->package.object); + + if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), 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. @@ -12463,7 +12504,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) @@ -14151,6 +14197,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, @@ -688,9 +701,17 @@ 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 + 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; diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 42f888c9cb0..77d88c812ee 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/Constant.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,761 @@ DATABASE DB = STATIC "ODS.RDB"; namespace { + +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) + { + Constant::drop(tdbb, transaction, m_name); + } + +private: + QualifiedName m_name; +}; + + +template +void dropItem(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& fullName) +{ + MemoryPool& pool = dsqlScratch->getPool(); + jrd_tra* transaction = dsqlScratch->getTransaction(); + + TDropNode dropNode(pool, fullName); + dropNode.dsqlPass(dsqlScratch); + dropNode.execute(tdbb, dsqlScratch, transaction); +} + +template +void dropMissingItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema, + const PackageItemsHolder::ItemsSignatureArray& existsItems, + const CreateAlterPackageNode::ItemsNameArray& newNames) +{ + for (auto i = existsItems.begin(); i != existsItems.end(); ++i) + { + if (!newNames.exist(i->name)) + { + dropItem(tdbb, dsqlScratch, + QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.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, + ISC_STATUS missingCode, ISC_STATUS mismatchCode) +{ + for (auto i = existingItems.begin(); i != existingItems.end(); ++i) + { + FB_SIZE_T pos; + bool found = newItems.find(Signature(pool, i->name), pos); + + if (!found || !newItems[pos].defined) + { + status_exception::raise( + Arg::Gds(missingCode) << i->name << packageAndSchema.toQuotedString()); + } + else if (newItems[pos] != *i) + { + status_exception::raise( + Arg::Gds(mismatchCode) << i->name << packageAndSchema.toQuotedString()); + } + } +} + +} // namespace + + +//---------------------- + +void PackageItemsHolder::drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema) +{ + dropItems(tdbb, dsqlScratch, packageAndSchema, functions); + dropItems(tdbb, dsqlScratch, packageAndSchema, procedures); + dropItems(tdbb, dsqlScratch, packageAndSchema, constants); +} + +void PackageItemsHolder::checkDefineMatch(MemoryPool& pool, const QualifiedName& packageAndSchema, + const PackageItemsHolder& newItems) +{ + checkItemsDefineMatch(pool, packageAndSchema, newItems.functions, functions, + isc_dyn_funcnotdef_package, isc_dyn_funcsignat_package); + checkItemsDefineMatch(pool, packageAndSchema, newItems.procedures, procedures, + isc_dyn_procnotdef_package, isc_dyn_procsignat_package); +} + // Return function and procedure names (in the user charset) and optionally its details for a // given package. - void collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& packageName, - SortedObjectsArray& functions, - SortedObjectsArray& procedures, bool details) +void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, + const QualifiedName& packageName, + bool details, bool collectConstants) +{ + AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS); + AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + FUN IN RDB$FUNCTIONS + WITH FUN.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND + FUN.RDB$PACKAGE_NAME EQ packageName.object.c_str() { - AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS); - AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS); + Signature function(FUN.RDB$FUNCTION_NAME); + function.defined = !FUN.RDB$FUNCTION_BLR.NULL || !FUN.RDB$ENTRYPOINT.NULL; - 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 (!FUN.RDB$DETERMINISTIC_FLAG.NULL && FUN.RDB$DETERMINISTIC_FLAG != 0) + function.flags |= Signature::FLAG_DETERMINISTIC; + + 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 + } - if (!FUN.RDB$DETERMINISTIC_FLAG.NULL && FUN.RDB$DETERMINISTIC_FLAG != 0) - function.flags |= Signature::FLAG_DETERMINISTIC; + functions.add(function); + } + END_FOR - if (details) + requestHandle.reset(tdbb, drq_l_pkg_procs, DYN_REQUESTS); + requestHandle2.reset(tdbb, drq_l_pkg_proc_args, DYN_REQUESTS); + + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) + PRC IN RDB$PROCEDURES + WITH PRC.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND + PRC.RDB$PACKAGE_NAME EQ packageName.object.c_str() + { + Signature procedure(PRC.RDB$PROCEDURE_NAME); + procedure.defined = !PRC.RDB$PROCEDURE_BLR.NULL || !PRC.RDB$ENTRYPOINT.NULL; + + if (details) + { + FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) + PRM IN RDB$PROCEDURE_PARAMETERS CROSS + FLD IN RDB$FIELDS + WITH PRM.RDB$SCHEMA_NAME EQ PRC.RDB$SCHEMA_NAME AND + PRM.RDB$PACKAGE_NAME EQ PRC.RDB$PACKAGE_NAME AND + PRM.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND + FLD.RDB$SCHEMA_NAME EQUIV PRM.RDB$FIELD_SOURCE_SCHEMA_NAME AND + FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE { - FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) - ARG IN RDB$FUNCTION_ARGUMENTS CROSS - FLD IN RDB$FIELDS - WITH ARG.RDB$SCHEMA_NAME EQ FUN.RDB$SCHEMA_NAME AND - ARG.RDB$PACKAGE_NAME EQ FUN.RDB$PACKAGE_NAME AND - ARG.RDB$FUNCTION_NAME EQ FUN.RDB$FUNCTION_NAME AND - FLD.RDB$SCHEMA_NAME EQUIV ARG.RDB$FIELD_SOURCE_SCHEMA_NAME AND - FLD.RDB$FIELD_NAME EQ ARG.RDB$FIELD_SOURCE - { - SignatureParameter parameter(*getDefaultMemoryPool()); - - parameter.number = ARG.RDB$ARGUMENT_POSITION; - parameter.name = ARG.RDB$ARGUMENT_NAME; - parameter.fieldSource = QualifiedName(ARG.RDB$FIELD_SOURCE, ARG.RDB$FIELD_SOURCE_SCHEMA_NAME); - parameter.mechanism = ARG.RDB$ARGUMENT_MECHANISM; - - if (!ARG.RDB$FIELD_NAME.NULL) - parameter.fieldName = QualifiedName(ARG.RDB$FIELD_NAME); - if (!ARG.RDB$RELATION_NAME.NULL) - parameter.relationName = QualifiedName(ARG.RDB$RELATION_NAME, ARG.RDB$RELATION_SCHEMA_NAME); - if (!ARG.RDB$COLLATION_ID.NULL) - parameter.collationId = CollId(ARG.RDB$COLLATION_ID); - if (!ARG.RDB$NULL_FLAG.NULL) - parameter.nullFlag = ARG.RDB$NULL_FLAG; - - if (!FLD.RDB$FIELD_LENGTH.NULL) - parameter.fieldLength = FLD.RDB$FIELD_LENGTH; - if (!FLD.RDB$FIELD_SCALE.NULL) - parameter.fieldScale = FLD.RDB$FIELD_SCALE; - if (!FLD.RDB$FIELD_TYPE.NULL) - parameter.fieldType = FLD.RDB$FIELD_TYPE; - if (!FLD.RDB$FIELD_SUB_TYPE.NULL) - parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE; - if (!FLD.RDB$SEGMENT_LENGTH.NULL) - parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH; - if (!FLD.RDB$NULL_FLAG.NULL) - parameter.fieldNullFlag = FLD.RDB$NULL_FLAG; - if (!FLD.RDB$CHARACTER_LENGTH.NULL) - parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH; - if (!FLD.RDB$COLLATION_ID.NULL) - parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID); - if (!FLD.RDB$CHARACTER_SET_ID.NULL) - parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID); - if (!FLD.RDB$FIELD_PRECISION.NULL) - parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; - - function.parameters.add(parameter); - } - END_FOR + SignatureParameter parameter(*getDefaultMemoryPool()); + parameter.type = PRM.RDB$PARAMETER_TYPE; + parameter.number = PRM.RDB$PARAMETER_NUMBER; + parameter.name = PRM.RDB$PARAMETER_NAME; + parameter.fieldSource = QualifiedName(PRM.RDB$FIELD_SOURCE, PRM.RDB$FIELD_SOURCE_SCHEMA_NAME); + parameter.mechanism = PRM.RDB$PARAMETER_MECHANISM; + + if (!PRM.RDB$FIELD_NAME.NULL) + parameter.fieldName = QualifiedName(PRM.RDB$FIELD_NAME); + if (!PRM.RDB$RELATION_NAME.NULL) + parameter.relationName = QualifiedName(PRM.RDB$RELATION_NAME, PRM.RDB$RELATION_SCHEMA_NAME); + if (!PRM.RDB$COLLATION_ID.NULL) + parameter.collationId = CollId(PRM.RDB$COLLATION_ID); + if (!PRM.RDB$NULL_FLAG.NULL) + parameter.nullFlag = PRM.RDB$NULL_FLAG; + + if (!FLD.RDB$FIELD_LENGTH.NULL) + parameter.fieldLength = FLD.RDB$FIELD_LENGTH; + if (!FLD.RDB$FIELD_SCALE.NULL) + parameter.fieldScale = FLD.RDB$FIELD_SCALE; + if (!FLD.RDB$FIELD_TYPE.NULL) + parameter.fieldType = FLD.RDB$FIELD_TYPE; + if (!FLD.RDB$FIELD_SUB_TYPE.NULL) + parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE; + if (!FLD.RDB$SEGMENT_LENGTH.NULL) + parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH; + if (!FLD.RDB$NULL_FLAG.NULL) + parameter.fieldNullFlag = FLD.RDB$NULL_FLAG; + if (!FLD.RDB$CHARACTER_LENGTH.NULL) + parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH; + if (!FLD.RDB$COLLATION_ID.NULL) + parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID); + if (!FLD.RDB$CHARACTER_SET_ID.NULL) + parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID); + if (!FLD.RDB$FIELD_PRECISION.NULL) + parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; + + procedure.parameters.add(parameter); } + END_FOR + } + + procedures.add(procedure); + } + END_FOR + - 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(); +} + +//---------------------- - requestHandle.reset(tdbb, drq_l_pkg_procs, DYN_REQUESTS); - requestHandle2.reset(tdbb, drq_l_pkg_proc_args, DYN_REQUESTS); - FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) - PRC IN RDB$PROCEDURES - WITH PRC.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND - PRC.RDB$PACKAGE_NAME EQ packageName.object.c_str() +// ----------------------------------- +// 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); + + 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_ref_item_const: + { + QualifiedName fullName; + csb->csb_blr_reader.getMetaName(fullName.object); + csb->csb_blr_reader.getMetaName(fullName.schema); + csb->csb_blr_reader.getMetaName(fullName.package); + + PackageReferenceNode* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, fullName, itemType); + + if (csb->collectingDependencies()) + { + Dependency dependency(obj_package_constant); + dependency.name = FB_NEW_POOL(pool) QualifiedName(pool, fullName); + + csb->addDependency(dependency); + } + + { + csb->qualifyExistingName(tdbb, fullName, obj_package_constant); + auto* constant = MetadataCache::getPerm(tdbb, fullName, CacheFlag::AUTOCREATE); + if (constant) + node->m_constant = csb->csb_resources->constants.registerResource(constant); + } + + if (!node->m_constant) + 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_ref_item_const); + dsqlScratch->appendMetaString(m_fullName.object.c_str()); + dsqlScratch->appendMetaString(m_fullName.schema.c_str()); + dsqlScratch->appendMetaString(m_fullName.package.c_str()); +} + +void PackageReferenceNode::setParameterName(dsql_par* parameter) const +{ + parameter->par_name = parameter->par_alias = m_fullName.object; +} + +void PackageReferenceNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) +{ + jrd_tra* transaction = dsqlScratch->getTransaction(); + thread_db* tdbb = JRD_get_thread_data(); + *desc = Constant::getDesc(tdbb, transaction, m_fullName); +} + +bool PackageReferenceNode::constantExists(thread_db* tdbb, Jrd::jrd_tra* transaction, + const QualifiedName& fullName, bool* isPrivate) +{ + if (fullName.package.isEmpty() || fullName.object.isEmpty()) + return false; + + // Use default schema if one not specified + const char* schemaName = fullName.schema.hasData() ? fullName.schema.c_str() : PUBLIC_SCHEMA; + + static const CachedRequestId requestId; + AutoCacheRequest getConstantRequest(tdbb, requestId); + FOR (REQUEST_HANDLE getConstantRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ schemaName AND + CONST.RDB$PACKAGE_NAME EQ fullName.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ fullName.object.c_str() + { + if (isPrivate) { - Signature procedure(PRC.RDB$PROCEDURE_NAME); - procedure.defined = !PRC.RDB$PROCEDURE_BLR.NULL || !PRC.RDB$ENTRYPOINT.NULL; + *isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + } + + return true; + } + END_FOR + + return false; +} + +void PackageReferenceNode::getDesc(thread_db* tdbb, CompilerScratch*, dsc* desc) +{ + *desc = Constant::getDesc(tdbb, tdbb->getTransaction(), m_fullName); +} + +ValueExprNode* PackageReferenceNode::copy(thread_db* tdbb, NodeCopier& copier) const +{ + MemoryPool& pool = *tdbb->getDefaultPool(); + auto* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, m_fullName, m_itemType); + + auto* constant = MetadataCache::getPerm(tdbb, m_fullName, 0); + node->m_constant = copier.csb->csb_resources->constants.registerResource(constant); + + return node; +} + +ValueExprNode* PackageReferenceNode::pass2(thread_db* tdbb, CompilerScratch* csb) +{ + ValueExprNode::pass2(tdbb, csb); + + if (m_itemType == blr_pkg_ref_item_const) + m_impureOffset = csb->allocImpure(); + + return this; +} + +dsc* PackageReferenceNode::execute(thread_db* tdbb, Request* request) const +{ + impure_value* impure = request->getImpure(m_impureOffset); + switch (m_itemType) + { + case blr_pkg_ref_item_const: + { + const Constant* constant = m_constant(request->getResources()); + constant->checkReload(tdbb); + + EVL_make_value(tdbb, &constant->getValue(), impure); + return &impure->vlu_desc; + } + default: + fb_assert(false); + return nullptr; + } +} + + +// ---------------------------------------- +// CreatePackageConstantNode +// ---------------------------------------- + +string CreatePackageConstantNode::internalPrint(NodePrinter& printer) const +{ + DdlNode::internalPrint(printer); - if (details) + 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()); + } + // DsqlDescMaker::fromField(&node->castDesc, type); + // node->castDesc.dsc_flags = node->source->getDsqlDesc().dsc_flags & DSC_nullable; + + DdlNode::dsqlPass(dsqlScratch); + return nullptr; +} + +void CreatePackageConstantNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) +{ + // SCL_check_create_access(tdbb, obj_package_constant); +} + +void CreatePackageConstantNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) +{ + if (create) + { + if (alter && executeAlter(tdbb, dsqlScratch, transaction)) + { + return; + } + + executeCreate(tdbb, dsqlScratch, transaction); + } + else + executeAlter(tdbb, dsqlScratch, transaction); + + fb_assert(m_id); + MetadataCache::newVersion(tdbb, m_id); +} + +void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) +{ + FbLocalStatus status; + + static const CachedRequestId requestId; + AutoCacheRequest storeConstantRequest(tdbb, requestId); + + // Check uniqueness + if (PackageReferenceNode::constantExists(tdbb, transaction, 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 SCHEME + 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); + + int faults = 0; + while (true) + { + try + { + SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_const_id, "RDB$CONSTANTS"); + id %= (MAX_SSHORT + 1); + if (!id) + continue; + + m_id = id; + + // Store desc and metainfo in the RDB$CONSTANTS relation + STORE (REQUEST_HANDLE storeConstantRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS USING { - FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction) - PRM IN RDB$PROCEDURE_PARAMETERS CROSS - FLD IN RDB$FIELDS - WITH PRM.RDB$SCHEMA_NAME EQ PRC.RDB$SCHEMA_NAME AND - PRM.RDB$PACKAGE_NAME EQ PRC.RDB$PACKAGE_NAME AND - PRM.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND - FLD.RDB$SCHEMA_NAME EQUIV PRM.RDB$FIELD_SOURCE_SCHEMA_NAME AND - FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE - { - SignatureParameter parameter(*getDefaultMemoryPool()); - parameter.type = PRM.RDB$PARAMETER_TYPE; - parameter.number = PRM.RDB$PARAMETER_NUMBER; - parameter.name = PRM.RDB$PARAMETER_NAME; - parameter.fieldSource = QualifiedName(PRM.RDB$FIELD_SOURCE, PRM.RDB$FIELD_SOURCE_SCHEMA_NAME); - parameter.mechanism = PRM.RDB$PARAMETER_MECHANISM; - - if (!PRM.RDB$FIELD_NAME.NULL) - parameter.fieldName = QualifiedName(PRM.RDB$FIELD_NAME); - if (!PRM.RDB$RELATION_NAME.NULL) - parameter.relationName = QualifiedName(PRM.RDB$RELATION_NAME, PRM.RDB$RELATION_SCHEMA_NAME); - if (!PRM.RDB$COLLATION_ID.NULL) - parameter.collationId = CollId(PRM.RDB$COLLATION_ID); - if (!PRM.RDB$NULL_FLAG.NULL) - parameter.nullFlag = PRM.RDB$NULL_FLAG; - - if (!FLD.RDB$FIELD_LENGTH.NULL) - parameter.fieldLength = FLD.RDB$FIELD_LENGTH; - if (!FLD.RDB$FIELD_SCALE.NULL) - parameter.fieldScale = FLD.RDB$FIELD_SCALE; - if (!FLD.RDB$FIELD_TYPE.NULL) - parameter.fieldType = FLD.RDB$FIELD_TYPE; - if (!FLD.RDB$FIELD_SUB_TYPE.NULL) - parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE; - if (!FLD.RDB$SEGMENT_LENGTH.NULL) - parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH; - if (!FLD.RDB$NULL_FLAG.NULL) - parameter.fieldNullFlag = FLD.RDB$NULL_FLAG; - if (!FLD.RDB$CHARACTER_LENGTH.NULL) - parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH; - if (!FLD.RDB$COLLATION_ID.NULL) - parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID); - if (!FLD.RDB$CHARACTER_SET_ID.NULL) - parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID); - if (!FLD.RDB$FIELD_PRECISION.NULL) - parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION; - - procedure.parameters.add(parameter); - } - END_FOR + // 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 + Constant::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); + + // Put the blr + blb* blob = blb::create(tdbb, transaction, &CONST.RDB$CONSTANT_BLR); + blob->BLB_put_segment(tdbb, dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount()); + blob->BLB_close(tdbb); + + // Parent package + CONST.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str()); + + // Schema of the parent package + CONST.RDB$SCHEMA_NAME.NULL = FALSE; + strcpy(CONST.RDB$SCHEMA_NAME, name.schema.c_str()); + + // Type + CONST.RDB$PRIVATE_FLAG.NULL = FALSE; + CONST.RDB$PRIVATE_FLAG = m_isPrivate; + + 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) +{ + FbLocalStatus status; + + static const CachedRequestId eraseRequestId; + AutoCacheRequest eraseDscRequest(tdbb, eraseRequestId); + + static const CachedRequestId modifyRequestId; + AutoCacheRequest modifyConstantRequest(tdbb, modifyRequestId); + bid blobId; + + bool found = false; + + // Get existing filed + // Store constant dsc in RDB$FIELDS + FOR (REQUEST_HANDLE eraseDscRequest TRANSACTION_HANDLE transaction) + FLD IN RDB$FIELDS CROSS CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND + FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND + FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE + { + m_id = CONST.RDB$CONSTANT_ID; + + MetadataCache::oldVersion(tdbb, m_id, CacheFlag::OLD_ALTER); + + dyn_fld origDom, newDom; + DSC_make_descriptor(&origDom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE, + FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE, + CSetId(FLD.RDB$CHARACTER_SET_ID), + CollId(FLD.RDB$COLLATION_ID)); + + origDom.dyn_fld_name = name; + origDom.dyn_charbytelen = FLD.RDB$FIELD_LENGTH; + origDom.dyn_dtype = FLD.RDB$FIELD_TYPE; + origDom.dyn_precision = FLD.RDB$FIELD_PRECISION; + origDom.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE; + origDom.dyn_charlen = FLD.RDB$CHARACTER_LENGTH; + origDom.dyn_collation = FLD.RDB$COLLATION_ID; + origDom.dyn_null_flag = !FLD.RDB$NULL_FLAG.NULL && FLD.RDB$NULL_FLAG != 0; + origDom.dyn_fld_source = QualifiedName(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME); + + // If the original field type is an array, force its blr type to blr_blob + if (FLD.RDB$DIMENSIONS != 0) + origDom.dyn_dtype = blr_blob; + + const bool wasInternalDomain = fb_utils::implicit_domain(origDom.dyn_fld_source.object.c_str()); + + 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 + Constant::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema); + + // And write + blb* blob = blb::create(tdbb, transaction, &blobId); + blob->BLB_put_segment(tdbb, dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount()); + blob->BLB_close(tdbb); + + MODIFY CONST USING + CONST.RDB$CONSTANT_BLR.NULL = FALSE; + CONST.RDB$CONSTANT_BLR = blobId; + END_MODIFY + + found = true; + } + END_FOR + + return found; +} string CreateAlterPackageNode::internalPrint(NodePrinter& printer) const @@ -215,6 +829,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 +859,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 +872,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); } @@ -385,6 +998,7 @@ void CreateAlterPackageNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* DYN_UTIL_check_unique_name(tdbb, name, obj_package_header); + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_s_pkg, DYN_REQUESTS); STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) @@ -430,6 +1044,8 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* { MemoryPool& pool = dsqlScratch->getPool(); Attachment* attachment = transaction->getAttachment(); + + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_pkg, DYN_REQUESTS); bool modified = false; @@ -442,31 +1058,14 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PACKAGE, name, {}); - SortedObjectsArray existingFuncs(pool); - SortedObjectsArray existingProcs(pool); - collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false); + PackageItemsHolder existingItems(pool); + existingItems.collectPackagedItems(tdbb, transaction, name, false, 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); - } - } + dropMissingItems(tdbb, dsqlScratch, name, existingItems.functions, functionNames); + dropMissingItems(tdbb, dsqlScratch, name, existingItems.procedures, procedureNames); - 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); - } - } + // To admit type/value change, all the constants should be recreated + Constant::dropAllFromPackage(tdbb, transaction, name, false); MODIFY PKG PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE; @@ -507,6 +1106,7 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* bool CreateAlterPackageNode::executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_prm_pkg, DYN_REQUESTS); bool modified = false; @@ -547,15 +1147,18 @@ void CreateAlterPackageNode::executeItems(thread_db* tdbb, DsqlCompilerScratch* { switch ((*items)[i].type) { - case Item::FUNCTION: + case PackageItemType::FUNCTION: (*items)[i].function->packageOwner = owner; (*items)[i].function->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true); break; - case Item::PROCEDURE: + case PackageItemType::PROCEDURE: (*items)[i].procedure->packageOwner = owner; (*items)[i].procedure->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true); break; + case PackageItemType::CONSTANT: + (*items)[i].constant->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true); + break; } } } @@ -589,6 +1192,8 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, AutoSavePoint savePoint(tdbb, transaction); bool found = false; + + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_e_pkg, DYN_REQUESTS); FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) @@ -619,25 +1224,9 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, Arg::Gds(isc_dyn_package_not_found) << name.toQuotedString()); } - SortedObjectsArray existingFuncs(pool); - SortedObjectsArray existingProcs(pool); - collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false); - - for (SortedObjectsArray::iterator i = existingFuncs.begin(); - i != existingFuncs.end(); ++i) - { - DropFunctionNode dropNode(pool, QualifiedName(i->name, name.schema, name.object)); - dropNode.dsqlPass(dsqlScratch); - dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); - } - - for (SortedObjectsArray::iterator i = existingProcs.begin(); - i != existingProcs.end(); ++i) - { - DropProcedureNode dropNode(pool, QualifiedName(i->name, name.schema, name.object)); - dropNode.dsqlPass(dsqlScratch); - dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); - } + PackageItemsHolder existingItems(pool); + existingItems.collectPackagedItems(tdbb, transaction, name, false, true); + existingItems.drop(tdbb, dsqlScratch, name); requestHandle.reset(tdbb, drq_e_pkg_prv, DYN_REQUESTS); @@ -690,8 +1279,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 +1292,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 +1308,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 +1323,19 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) break; } + case PackageItemType::CONSTANT: + { + CreatePackageConstantNode* const constant = (*arrays[i])[j].constant; + ddlNode = constant; + + names[i].constants.addName(constant->name); + constant->name.schema = name.schema; + constant->name.package = name.object; + constant->create = true; + constant->makePrivate(); + break; + } default: fb_assert(false); } @@ -796,6 +1378,7 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc // run all statements under savepoint control AutoSavePoint savePoint(tdbb, transaction); + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body, DYN_REQUESTS); bool modified = false; @@ -837,12 +1420,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 +1435,10 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc if (arrays[i] == items) { - existingFuncs.clear(); - existingProcs.clear(); + existingItems.clear(); } - collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, true); + existingItems.collectPackagedItems(tdbb, transaction, name, true, true); for (unsigned j = 0; j < arrays[i]->getCount(); ++j) { @@ -866,90 +1446,59 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc switch (elem.type) { - case CreateAlterPackageNode::Item::FUNCTION: + case PackageItemType::FUNCTION: { CreateAlterFunctionNode* func = elem.function; if (arrays[i] == items) - func->privateScope = !headerFuncs.exist(Signature(func->name.object)); - else if (existingFuncs.exist(Signature(func->name.object))) + func->privateScope = !headerItems.functions.exist(Signature(func->name.object)); + else { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_duplicate_package_item) << - Arg::Str("FUNCTION") << func->name.toQuotedString()); + existingItems.functions.checkDuplicate(func->name); } func->packageOwner = owner; func->preserveDefaults = - existingFuncs.exist(Signature(func->name.object)) && arrays[i] == items; + existingItems.functions.exist(Signature(func->name.object)) && arrays[i] == items; func->executeDdl(tdbb, elem.dsqlScratch, transaction, true); break; } - case CreateAlterPackageNode::Item::PROCEDURE: + case PackageItemType::PROCEDURE: { CreateAlterProcedureNode* proc = elem.procedure; if (arrays[i] == items) - proc->privateScope = !headerProcs.exist(Signature(proc->name.object)); - else if (existingProcs.exist(Signature(proc->name.object))) + proc->privateScope = !headerItems.procedures.exist(Signature(proc->name.object)); + else { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_duplicate_package_item) << - Arg::Str("PROCEDURE") << proc->name.toQuotedString()); + existingItems.procedures.checkDuplicate(proc->name); } proc->packageOwner = owner; proc->preserveDefaults = - existingProcs.exist(Signature(proc->name.object)) && arrays[i] == items; + existingItems.procedures.exist(Signature(proc->name.object)) && arrays[i] == items; proc->executeDdl(tdbb, elem.dsqlScratch, transaction, true); break; } - } - } - } - - SortedObjectsArray newFuncs(pool); - SortedObjectsArray newProcs(pool); - collectPackagedItems(tdbb, transaction, name, newFuncs, newProcs, true); + case PackageItemType::CONSTANT: + { + CreatePackageConstantNode* constant = elem.constant; - for (SortedObjectsArray::iterator i = existingFuncs.begin(); - i != existingFuncs.end(); ++i) - { - FB_SIZE_T pos; - bool found = newFuncs.find(Signature(pool, i->name), pos); + headerItems.constants.checkDuplicate(constant->name); + existingItems.constants.checkDuplicate(constant->name); - if (!found || !newFuncs[pos].defined) - { - status_exception::raise( - Arg::Gds(isc_dyn_funcnotdef_package) << i->name << name.toQuotedString()); - } - else if (newFuncs[pos] != *i) - { - status_exception::raise( - Arg::Gds(isc_dyn_funcsignat_package) << i->name << name.toQuotedString()); + constant->executeDdl(tdbb, elem.dsqlScratch, transaction, true); + break; + } + } } } - for (SortedObjectsArray::iterator i = existingProcs.begin(); - i != existingProcs.end(); ++i) - { - FB_SIZE_T pos; - bool found = newProcs.find(Signature(pool, i->name), pos); + PackageItemsHolder newItems(pool); + newItems.collectPackagedItems(tdbb, transaction, name, true, false); + existingItems.checkDefineMatch(pool, name, newItems); - if (!found || !newProcs[pos].defined) - { - status_exception::raise( - Arg::Gds(isc_dyn_procnotdef_package) << i->name << name.toQuotedString()); - } - else if (newProcs[pos] != *i) - { - status_exception::raise( - Arg::Gds(isc_dyn_procsignat_package) << i->name << name.toQuotedString()); - } - } executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE_BODY, name, {}); @@ -986,6 +1535,8 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra AutoSavePoint savePoint(tdbb, transaction); bool found = false; + + static const CachedRequestId requestId; AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body2, DYN_REQUESTS); FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) @@ -1031,9 +1582,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 +1607,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 +1622,8 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra } END_FOR + Constant::dropAllFromPackage(tdbb, transaction, name, true); + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE_BODY, name, {}); savePoint.release(); // everything is ok diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h index 839e3c8d0f6..5bff32bb07b 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -25,9 +25,191 @@ #include "../dsql/DdlNodes.h" #include "../common/classes/array.h" +#include "../common/classes/objects_array.h" +#include "../include/fb_exception.h" namespace Jrd { +enum class PackageItemType : USHORT +{ + FUNCTION = 0, + PROCEDURE, + CONSTANT, + META_SIZE +}; + +template +class ItemNames : public TArray +{ +public: + ItemNames() : TArray() + {} + + ItemNames(Firebird::MemoryPool& pool) : TArray(pool) + {} + + operator TArray&() + { + return *this; + } + + template + void addName(const QualifiedName& newName) + { + checkDuplicate(newName); + TArray::add(TType(newName.object)); + } + + template + void checkDuplicate(const QualifiedName& newName) + { + if constexpr (std::is_same_v) + { + if (!TArray::exist(newName.object)) + return; // The name is unique + } + else + { + // Cast + if (!TArray::exist(TType(newName.object))) + return; // The name is unique + } + + static_assert(size_t(IValue) >= 0 && size_t(IValue) < size_t(PackageItemType::META_SIZE), "Invalid item type"); + static const std::array names{ + "FUNCTION", + "PROCEDURE", + "CONSTANT", + }; + + // Print just the object name because the full path is present n the parent error message + Firebird::status_exception::raise( + Firebird::Arg::Gds(isc_no_meta_update) << + Firebird::Arg::Gds(isc_dyn_duplicate_package_item) << + Firebird::Arg::Str(names[size_t(IValue)]) << Firebird::Arg::Str(newName.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, bool details, 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_ref_item_const); + + Firebird::string internalPrint(NodePrinter& printer) const final; + + ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) final; + static DmlNode* parse(thread_db* tdbb, Firebird::MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp); + void genBlr(DsqlCompilerScratch* dsqlScratch) final; + + void setParameterName(dsql_par* parameter) const final; + void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) final; + + static bool constantExists(thread_db* tdbb, Jrd::jrd_tra* transaction, + const QualifiedName& name, bool* isPrivate = nullptr); + + // Compute descriptor for value expression. + void getDesc(thread_db*, CompilerScratch*, dsc*) final; + + ValueExprNode* copy(thread_db*, NodeCopier&) const final; + ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) final; + dsc* execute(thread_db*, Request*) const final; + + const char* getName() const + { + return m_fullName.object.c_str(); + } + +private: + CachedResource m_constant; + const QualifiedName m_fullName; + + const UCHAR m_itemType; + ULONG m_impureOffset = 0; +}; + + +class CreatePackageConstantNode final : public DdlNode +{ +public: + CreatePackageConstantNode(Firebird::MemoryPool& pool, const MetaName& name, + dsql_fld* type = nullptr, ValueExprNode* value = nullptr, bool isPrivate = false) + : DdlNode(pool), + name(pool, name), + m_type(type), + m_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; + +private: + NestConst m_type; + NestConst m_expr; + MetaId m_id = 0; + bool m_isPrivate = false; +}; + class CreateAlterPackageNode : public DdlNode { @@ -37,36 +219,45 @@ class CreateAlterPackageNode : public DdlNode static Item create(CreateAlterFunctionNode* function) { Item item; - item.type = FUNCTION; + item.type = PackageItemType::FUNCTION; item.function = function; - item.dsqlScratch = NULL; + item.dsqlScratch = nullptr; return item; } static Item create(CreateAlterProcedureNode* procedure) { Item item; - item.type = PROCEDURE; + item.type = PackageItemType::PROCEDURE; item.procedure = procedure; - item.dsqlScratch = NULL; + item.dsqlScratch = nullptr; return item; } - enum + static Item create(CreatePackageConstantNode* constant) { - FUNCTION, - PROCEDURE - } type; + Item item; + item.type = PackageItemType::CONSTANT; + item.constant = constant; + item.dsqlScratch = nullptr; + return item; + } + + PackageItemType type; union { CreateAlterFunctionNode* function; CreateAlterProcedureNode* procedure; + CreatePackageConstantNode* constant; }; DsqlCompilerScratch* dsqlScratch; }; + + using ItemsNameArray = ItemNames, MetaName>; + public: CreateAlterPackageNode(MemoryPool& pool, const QualifiedName& aName) : DdlNode(pool), @@ -77,6 +268,7 @@ class CreateAlterPackageNode : public DdlNode items(NULL), functionNames(pool), procedureNames(pool), + constantNames(pool), owner(pool) { } @@ -110,8 +302,9 @@ class CreateAlterPackageNode : public DdlNode bool createIfNotExistsOnly = false; Firebird::string source; Firebird::Array* items; - Firebird::SortedArray functionNames; - Firebird::SortedArray procedureNames; + ItemsNameArray functionNames; + ItemsNameArray procedureNames; + ItemsNameArray constantNames; std::optional ssDefiner; private: diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index 976805c3e15..ae3f4fd024e 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -560,6 +560,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 364be68de06..7cfc67c5174 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -723,6 +723,7 @@ using namespace Firebird; %token TRUNCATE %token UNLIST %token WITHIN +%token CONSTANT // precedence declarations for expression evaluation @@ -882,6 +883,7 @@ using namespace Firebird; Jrd::SetBindNode* setBindNode; Jrd::SessionResetNode* sessionResetNode; Jrd::ForRangeNode::Direction forRangeDirection; + Jrd::CreatePackageConstantNode* createPackageConstantNode; } %include types.y @@ -3170,6 +3172,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 @@ -3257,6 +3261,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 @@ -6275,6 +6286,7 @@ ddl_type3 : PARAMETER { $$ = obj_parameter; } | PROCEDURE PARAMETER { $$ = obj_procedure; } | FUNCTION PARAMETER { $$ = obj_udf; } + | CONSTANT { $$ = obj_package_constant; } ; %type ddl_type4 @@ -9792,6 +9804,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 @@ -10112,6 +10129,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..a4985165c6f 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_ref_item_const (unsigned char) 1 + #endif // FIREBIRD_IMPL_BLR_H diff --git a/src/include/firebird/impl/msg/dyn.h b/src/include/firebird/impl/msg/dyn.h index 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/jrd.h b/src/include/firebird/impl/msg/jrd.h index 7ba26ca0ac2..50a6bd6c928 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1000,3 +1000,11 @@ FB_IMPL_MSG(JRD, 997, invalid_unqualified_name_list, -901, "HY", "000", "Invalid FB_IMPL_MSG(JRD, 998, no_user_att_while_restore, -901, "HY", "000", "User attachments are not allowed for the database being restored") FB_IMPL_MSG(JRD, 999, genseq_stepmustbe_nonzero, -833, "42", "000", "Argument STEP must be different than zero for function @1") FB_IMPL_MSG(JRD, 1000, argmustbe_exact_function, -833, "42", "000", "Arguments for @1 function must be exact numeric types") +FB_IMPL_MSG(JRD, 1001, not_defined_constant, -901, "42", "000", "The constant @1 is not defined in the package @2") +FB_IMPL_MSG(JRD, 1002, const_name, -901, "42", "000", "CONSTANT @1") +FB_IMPL_MSG(JRD, 1003, private_constant, -901, "42", "000", "The constant @1 is private") +FB_IMPL_MSG(JRD, 1004, package_alias_help, -901, "42", "000", "Use an alias to resolve the conflict") +FB_IMPL_MSG(JRD, 1005, bad_constant_blr_error, -901, "2F", "000", "Error while parsing BLR value of the constant @1") +FB_IMPL_MSG(JRD, 1006, bad_constant_type_error, -901, "2F", "000", "Error while reading type of the constant @1") +FB_IMPL_MSG(JRD, 1007, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found") +FB_IMPL_MSG(JRD, 1008, bad_constant_type, -901, "2F", "000", "@1 is not suppotred to be a constant type") diff --git a/src/include/firebird/impl/msg/sqlerr.h b/src/include/firebird/impl/msg/sqlerr.h index 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 c176e1dda9c..635f8fdebc1 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5955,6 +5955,14 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_no_user_att_while_restore = 335545318; isc_genseq_stepmustbe_nonzero = 335545319; isc_argmustbe_exact_function = 335545320; + isc_not_defined_constant = 335545321; + isc_const_name = 335545322; + isc_private_constant = 335545323; + isc_package_alias_help = 335545324; + isc_bad_constant_blr_error = 335545325; + isc_bad_constant_type_error = 335545326; + isc_bad_constant_name = 335545327; + isc_bad_constant_type = 335545328; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; @@ -6218,6 +6226,10 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_gbak_invalid_data = 336331094; isc_gbak_inv_bkup_ver2 = 336331096; isc_gbak_db_format_too_old2 = 336331100; + isc_gbak_writing_constants = 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; @@ -6354,6 +6366,9 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_dsql_recreate_schema_failed = 336397338; isc_dsql_alter_schema_failed = 336397339; isc_dsql_create_alter_schema_failed = 336397340; + isc_dsql_create_const_failed = 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..f922d3fef55 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) { @@ -649,6 +650,8 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() } else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_VIEWS, 4)) return parsed.value(); + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_CONSTANTS, 4)) + return parsed.value(); else if (text.length() >= 9 && TOKEN_WIRE_STATISTICS.find(text) == 0 || text == TOKEN_WIRE_STATS) { diff --git a/src/isql/FrontendParser.h b/src/isql/FrontendParser.h index 28a1b9cc3ba..1a029e78352 100644 --- a/src/isql/FrontendParser.h +++ b/src/isql/FrontendParser.h @@ -119,6 +119,7 @@ class FrontendParser struct ShowVersionNode {}; struct ShowViewsNode { std::optional name; }; struct ShowWireStatsNode {}; + struct ShowConstantsNode { std::optional name; }; using AnySetNode = std::variant< SetNode, @@ -184,6 +185,7 @@ class FrontendParser ShowVersionNode, ShowViewsNode, ShowWireStatsNode, + ShowConstantsNode, InvalidNode >; diff --git a/src/isql/show.epp b/src/isql/show.epp index 0b7a0ddcda1..af88daa4dd6 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -118,6 +118,8 @@ static processing_state show_trigger(const std::optional& n static processing_state show_users(); static processing_state show_users12(); static processing_state show_wireStats(); +static processing_state show_constants(const std::optional& name, const char* msg = nullptr); +static processing_state show_constant(const SCHAR* constantName); const char* const spaces = " "; static TEXT Print_buffer[512]; @@ -2571,6 +2573,11 @@ processing_state SHOW_metadata(const FrontendParser::AnyShowNode& node) return show_wireStats(); }, + [](const FrontendParser::ShowConstantsNode& node) + { + return show_constants(node.name); + }, + [](auto& arg) { static_assert(FrontendParser::AlwaysFalseV, @@ -6735,3 +6742,148 @@ static processing_state show_wireStats() return SKIP; } + +static void print_constant_type(const char* fieldName, const char* schemaName) +{ + FOR FLD IN RDB$FIELDS WITH + FLD.RDB$SCHEMA_NAME EQ schemaName AND + FLD.RDB$FIELD_NAME EQ fieldName + { + // Decide if this is a user-created domain + if (!fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) || FLD.RDB$SYSTEM_FLAG == 1) + { + fb_utils::exact_name(FLD.RDB$FIELD_NAME); + isqlGlob.printf("%s) ", FLD.RDB$FIELD_NAME); + } + + const QualifiedMetaString domainName(FLD.RDB$FIELD_NAME, FLD.RDB$SCHEMA_NAME); + if (!ISQL_printNumericType(domainName, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SUB_TYPE, + FLD.RDB$FIELD_PRECISION, FLD.RDB$FIELD_SCALE)) + { + return; + } + + // Use RDB$CHARACTER_LENGTH instead of RDB$FIELD_LENGTH + // FSG 19.Nov.2000 + if ((FLD.RDB$FIELD_TYPE == blr_text || FLD.RDB$FIELD_TYPE == blr_varying) && + !FLD.RDB$CHARACTER_LENGTH.NULL) + { + isqlGlob.printf("(%d)", FLD.RDB$CHARACTER_LENGTH); + } + + // Show international character sets and collations + + SSHORT char_set_id = 0; + if (!FLD.RDB$CHARACTER_SET_ID.NULL) + char_set_id = FLD.RDB$CHARACTER_SET_ID; + + SSHORT collation = 0; + if (!FLD.RDB$COLLATION_ID.NULL) + collation = FLD.RDB$COLLATION_ID; + + if (((FLD.RDB$FIELD_TYPE == blr_text || + FLD.RDB$FIELD_TYPE == blr_varying) && FLD.RDB$FIELD_SUB_TYPE != fb_text_subtype_binary) || + FLD.RDB$FIELD_TYPE == blr_blob && FLD.RDB$FIELD_SUB_TYPE == isc_blob_text) + show_charsets(char_set_id, collation); + + + if (fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) && !FLD.RDB$DEFAULT_SOURCE.NULL) + { + isqlGlob.printf(" "); + SHOW_print_metadata_text_blob(isqlGlob.Out, &FLD.RDB$DEFAULT_SOURCE); + } + } + END_FOR + ON_ERROR + ISQL_errmsg (fbStatus); + END_ERROR; +} + +static processing_state show_constants(const std::optional& name, const char* msg) +{ + bool first = true; + + if (name.has_value()) + { + const QualifiedMetaString& constant = name.value(); + + FOR CONST IN RDB$CONSTANTS WITH + CONST.RDB$SCHEMA_NAME EQ constant.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ constant.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ constant.object.c_str() + SORTED BY CONST.RDB$CONSTANT_NAME + { + bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + const char* type = isPrivate ? "BODY" : "HEADER"; + + if (first && msg) + isqlGlob.printf("%s%s", msg, NEWLINE); + + first = false; + show_constant(constant.object.c_str()); + } + END_FOR + ON_ERROR + ISQL_errmsg(fbStatus); + return ps_ERR; + END_ERROR; + + return SKIP; + } + + // All constants + + FOR CONST IN RDB$CONSTANTS + SORTED BY CONST.RDB$CONSTANT_NAME + bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + { + const char* type = isPrivate ? "BODY" : "HEADER"; + + if (first && msg) + isqlGlob.printf("%s%s", msg, NEWLINE); + + first = false; + fb_utils::exact_name(CONST.RDB$CONSTANT_NAME); + show_constant(CONST.RDB$CONSTANT_NAME); + + } + END_FOR + ON_ERROR + ISQL_errmsg(fbStatus); + return ps_ERR; + END_ERROR; + + return SKIP; +} + +static processing_state show_constant(const SCHAR* constantName) +{ + FOR CONST IN RDB$CONSTANTS + WITH CONST.RDB$CONSTANT_NAME EQ constantName + SORTED BY CONST.RDB$CONSTANT_NAME + { + bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG; + + if (!CONST.RDB$PACKAGE_NAME.NULL) + { + MetaString packageName(CONST.RDB$PACKAGE_NAME); + isqlGlob.printf("%s (%s %s)%-20s", constantName, packageName.c_str(), isPrivate ? "BODY" : "HEADER", " "); + } + else + isqlGlob.printf("%-20s", constantName); + + fb_utils::exact_name(CONST.RDB$FIELD_SOURCE); + fb_utils::exact_name(CONST.RDB$SCHEMA_NAME); + print_constant_type(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME); + + isqlGlob.printf(NEWLINE); + + } + END_FOR + ON_ERROR + ISQL_errmsg(fbStatus); + return ps_ERR; + END_ERROR; + + return SKIP; +} diff --git a/src/isql/tests/FrontendParserTest.cpp b/src/isql/tests/FrontendParserTest.cpp index d4f62903351..96de3e7468d 100644 --- a/src/isql/tests/FrontendParserTest.cpp +++ b/src/isql/tests/FrontendParserTest.cpp @@ -609,6 +609,13 @@ BOOST_AUTO_TEST_CASE(ParseShowTest) "show wire_stat"))); BOOST_TEST(std::holds_alternative(parseShow( "show wire_statistics"))); + + BOOST_TEST(std::holds_alternative(parseShow( + "show const"))); + BOOST_TEST(std::holds_alternative(parseShow( + "show constant"))); + BOOST_TEST(std::holds_alternative(parseShow( + "show constants"))); } diff --git a/src/jrd/Constant.epp b/src/jrd/Constant.epp new file mode 100644 index 00000000000..a60a4a21344 --- /dev/null +++ b/src/jrd/Constant.epp @@ -0,0 +1,534 @@ +/* + * PROGRAM: Firebird CONSTANTS implementation. + * MODULE: Constant.epp + * DESCRIPTION: Routine to cache and reload constants + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Artyom Abakumov + * for Red Soft Corporation. + * + * Copyright (c) 2025 Red Soft Corporation + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + +#include "firebird.h" +#include "../jrd/Constant.h" + +#include "../jrd/tra.h" +#include "../jrd/exe_proto.h" +#include "../jrd/dfw_proto.h" +#include "../common/dsc_proto.h" +#include "../jrd/met_proto.h" +#include "../jrd/met.h" +#include "../jrd/Statement.h" // 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 + +using namespace Firebird; +using namespace Jrd; + +DATABASE DB = FILENAME "ODS.RDB"; + +//---------------------- + +Constant* Constant::lookup(thread_db* tdbb, MetaId id) +{ + return MetadataCache::getVersioned(tdbb, id, CacheFlag::AUTOCREATE); +} + +Constant* Constant::lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags) +{ + return MetadataCache::getVersioned(tdbb, name, flags); +} + +Constant* Constant::create(thread_db* tdbb, MemoryPool& pool, Cached::Constant* perm) +{ + return FB_NEW_POOL(perm->getPool()) Constant(perm); +} + + +ScanResult Constant::scan(thread_db* tdbb, ObjectBase::Flag flags) +{ + Attachment* attachment = tdbb->getAttachment(); + jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); + Database* dbb = tdbb->getDatabase(); + + MemoryPool& pool = getPermanent()->getPool(); + bool found = false; + + //try + { + static const CachedRequestId requestId; + AutoCacheRequest requestConst(tdbb, requestId); + + FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$CONSTANT_ID EQ this->getId() + { + found = true; + + getPermanent()->setName(QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, + (CONST.RDB$PACKAGE_NAME.NULL ? nullptr : CONST.RDB$PACKAGE_NAME))); + + MetaName owner; + TriState ssDefiner; + if (!CONST.RDB$PACKAGE_NAME.NULL) + { + static const CachedRequestId requestId; + AutoCacheRequest requestHandle(tdbb, requestId); + + FOR (REQUEST_HANDLE requestHandle) + PKG IN RDB$PACKAGES + CROSS SCH IN RDB$SCHEMAS WITH + PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND + PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME + { + + owner = PKG.RDB$OWNER_NAME; + + // if (!PKG.RDB$SECURITY_CLASS.NULL) + // { + // getPermanent()->setSecurityName(QualifiedName(PKG.RDB$SECURITY_CLASS, SCH.RDB$SECURITY_CLASS)); + // } + + // SQL SECURITY of function must be the same if it's defined in package + if (!PKG.RDB$SQL_SECURITY.NULL) + ssDefiner = (bool) PKG.RDB$SQL_SECURITY; + } + END_FOR + } + + if (!ssDefiner.isAssigned()) + { + ssDefiner = MET_get_ss_definer(tdbb, CONST.RDB$SCHEMA_NAME); + } + + // if (ssDefiner.asBool()) + // invoker = dbb->getUserId(getPermanent()->owner); + + if ((flags & CacheFlag::MINISCAN) == 0) + makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); + else + this->m_callReload = true; // Call makeValue in reload + } + END_FOR + } + + return found ? (this->m_callReload ? ScanResult::REPEAT : ScanResult::COMPLETE) : ScanResult::MISS; +} + + +std::optional Constant::getIdByName(thread_db* tdbb, const QualifiedName& name) +{ + SET_TDBB(tdbb); + Attachment* attachment = tdbb->getAttachment(); + std::optional id; + + static const CachedRequestId requestId; + AutoCacheRequest request(tdbb, requestId); + + FOR (REQUEST_HANDLE request) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ name.object.c_str() + { + id = CONST.RDB$CONSTANT_ID; + } + END_FOR + + return id; +} + +ScanResult Constant::reload(thread_db* tdbb, ObjectBase::Flag) +{ + Attachment* attachment = tdbb->getAttachment(); + Database* dbb = tdbb->getDatabase(); + jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb); + + static const CachedRequestId requestId; + AutoCacheRequest request(tdbb, requestId); + + bool found = false; + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE metaTransaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$CONSTANT_ID EQ this->getId() + { + + // if (compiling) + // { + // fb_assert(false); + // return ScanResult::REPEAT; + // } + + found = true; + makeValue(tdbb, attachment, CONST.RDB$CONSTANT_BLR); + } + END_FOR + + return found ? ScanResult::COMPLETE : ScanResult::MISS; +} + +int Constant::objectType() +{ + return obj_package_constant; +} + +bool Constant::hash(thread_db* tdbb, Firebird::sha512& digest) +{ + if (m_value.vlu_desc.dsc_dtype == 0) + { + dsc type = getDesc(tdbb, tdbb->getTransaction(), getName()); + digest.process(sizeof(type), &type); + } + else + { + digest.process(sizeof(m_value.vlu_desc), &m_value.vlu_desc); + } + + return true; +} + +void Constant::checkReload(thread_db* tdbb) const +{ + if (m_callReload) + getPermanent()->reload(tdbb, 0); +} + + +class DropConstantSavepoint +{ +public: + DropConstantSavepoint(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) : + m_savePoint(tdbb, transaction), + m_tdbb(tdbb) + { + MetadataCache::getVersioned(tdbb, name, CacheFlag::OLD_DROP); + } + + DropConstantSavepoint(thread_db* tdbb, jrd_tra* transaction) : + m_savePoint(tdbb, transaction), + m_tdbb(tdbb) + { + } + + inline void setFound(const MetaId id) noexcept + { + m_id = id; + m_found = true; + } + + void release() + { + fb_assert(m_found); + if (m_found) + MetadataCache::erase(m_tdbb, m_id); + + m_savePoint.release(); + } + +private: + AutoSavePoint m_savePoint; + thread_db* m_tdbb; + MetaId m_id{}; + bool m_found = false; +}; + +void Constant::drop(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) +{ + // Run all statements under savepoint control + DropConstantSavepoint savePoint(tdbb, transaction, name); + + static const CachedRequestId requestId; + AutoCacheRequest request(tdbb, requestId); + + FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ name.object.c_str() + { + savePoint.setFound(CONST.RDB$CONSTANT_ID); + + ERASE CONST; + } + END_FOR + + savePoint.release(); // everything is ok +} + +void Constant::dropAllFromPackage(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& parent, bool privateFlag) +{ + static const CachedRequestId requestId; + AutoCacheRequest eraseConstantRequest(tdbb, requestId); + FOR (REQUEST_HANDLE eraseConstantRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS + WITH CONST.RDB$SCHEMA_NAME EQ parent.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ parent.object.c_str() AND + CONST.RDB$PRIVATE_FLAG EQ privateFlag + { + DropConstantSavepoint savePoint(tdbb, transaction, QualifiedName(CONST.RDB$CONSTANT_NAME, parent.schema, parent.object)); + savePoint.setFound(CONST.RDB$CONSTANT_ID); + + ERASE CONST; + + savePoint.release(); // everything is ok + } + END_FOR +} + +dsc Constant::getDesc(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name) +{ + dsc desc{}; + bool succeed = false; + + FbLocalStatus status; + static const CachedRequestId requestId; + AutoCacheRequest getConstantDscRequest(tdbb, requestId); + FOR(REQUEST_HANDLE getConstantDscRequest TRANSACTION_HANDLE transaction) + CONST IN RDB$CONSTANTS CROSS FLD IN RDB$FIELDS + WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND + CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND + FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND + FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE + + succeed = DSC_make_descriptor(&desc, + FLD.RDB$FIELD_TYPE, + FLD.RDB$FIELD_SCALE, + FLD.RDB$FIELD_LENGTH, + FLD.RDB$FIELD_SUB_TYPE, + CSetId(FLD.RDB$CHARACTER_SET_ID), + CollId(FLD.RDB$COLLATION_ID)); + END_FOR + + if (!succeed) + (Arg::Gds(isc_bad_constant_type_error) << Arg::Str(name.toQuotedString())).raise(); + + return desc; +} + +static dsc* executeConstantExpressionWithRequest(thread_db* tdbb, CompilerScratch* csb) +{ + 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); +} + +void Constant::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); +} + +void Constant::makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id) +{ + m_value = {}; + + 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 + m_callReload = false; + MET_parse_blob(tdbb, &getName().schema, nullptr, &blob_id, &csb, nullptr, false, false); + + fb_assert(csb != nullptr); + m_callReload = (csb->csb_g_flags & csb_reload); + } + + executeConstantExpression(tdbb, csb, getPermanent()->getPool(), m_value); + } + catch (const Exception& ex) + { + StaticStatusVector temp_status; + ex.stuffException(temp_status); + + const string name = this->getName().toQuotedString(); + (Arg::Gds(isc_bad_constant_blr_error) << Arg::Str(name) + << Arg::StatusVector(temp_status.begin())).raise(); + } + } + catch (const Exception&) + { + attachment->att_database->deletePool(csb_pool); + throw; + } +} diff --git a/src/jrd/Constant.h b/src/jrd/Constant.h new file mode 100644 index 00000000000..7e03b8db56e --- /dev/null +++ b/src/jrd/Constant.h @@ -0,0 +1,173 @@ +/* + * PROGRAM: Firebird CONSTANTS implementation. + * MODULE: Constant.h + * DESCRIPTION: Routine to cache and reload constants + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Artyom Abakumov + * for Red Soft Corporation. + * + * Copyright (c) 2025 Red Soft Corporation + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + */ + + +#ifndef JRD_CONSTANT_H +#define JRD_CONSTANT_H + +#include "firebird.h" +#include "../jrd/CacheVector.h" +#include "../jrd/Resources.h" +#include "../jrd/obj.h" +#include "../jrd/val.h" +#include "../jrd/lck.h" + +namespace Jrd +{ +class DsqlCompilerScratch; +class dsql_fld; + +class ConstantPermanent : public Firebird::PermanentStorage +{ +public: + explicit ConstantPermanent(thread_db* tdbb, MemoryPool& p, MetaId metaId, NoData) + : PermanentStorage(p), + id(metaId), + name(p) + { } + + explicit ConstantPermanent(MemoryPool& p) + : PermanentStorage(p), + id(~0), + name(p) + { } + + MetaId getId() const + { + return id; + } + + static bool destroy(thread_db* tdbb, ConstantPermanent* routine) + { + return false; + } + + void releaseLock(thread_db*) { } + + const QualifiedName& getName() const noexcept { return name; } + void setName(const QualifiedName& value) { name = value; } + + bool hasData() const { return name.hasData(); } + +public: + MetaId id; // routine ID + QualifiedName name; // routine name +}; + +class Constant final : public Firebird::PermanentStorage, public ObjectBase +{ +public: + static Constant* lookup(thread_db* tdbb, MetaId id); + static Constant* lookup(thread_db* tdbb, const QualifiedName& name, ObjectBase::Flag flags); + + // lock requeued by CacheElement + static const enum lck_t LOCKTYPE = LCK_constant_rescan; + +private: + explicit Constant(Cached::Constant* perm) + : Firebird::PermanentStorage(perm->getPool()), + cachedConstant(perm) + { } + +public: + explicit Constant(MemoryPool& p) + : Firebird::PermanentStorage(p) + { } + + static bool destroy(thread_db* tdbb, Constant* routine) + { + return false; + } + + static Constant* create(thread_db* tdbb, MemoryPool& pool, Cached::Constant* perm); + ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags); + static std::optional getIdByName(thread_db* tdbb, const QualifiedName& name); + void checkReload(thread_db* tdbb) const; + + static const char* objectFamily(void*) + { + return "constant"; + } + +public: + const QualifiedName& getName() const noexcept { return getPermanent()->getName(); } + MetaId getId() const noexcept { return getPermanent()->getId(); } + + int getObjectType() const noexcept + { + return objectType(); + } + + SLONG getSclType() const noexcept + { + return obj_package_constant; + } + + ScanResult reload(thread_db* tdbb, ObjectBase::Flag fl); + + static int objectType(); + + bool hash(thread_db* tdbb, Firebird::sha512& digest); + +public: + inline const dsc& getValue() const + { + return m_value.vlu_desc; + } + + static void drop(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& name); + static void dropAllFromPackage(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& parent, bool privateFlag); + static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name); + + static void genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, + ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema); + +private: + void makeValue(thread_db* tdbb, Attachment* attachment, bid blob_id); + + virtual ~Constant() + { + delete m_value.vlu_string; + } + +public: + Cached::Constant* cachedConstant; // entry in the cache + + Cached::Constant* getPermanent() const noexcept + { + return cachedConstant; + } + +private: + impure_value m_value{}; + + // Make sure the constant value will be read in at less at reload state + bool m_callReload = true; +}; + +} // namespace Jrd + +#endif // JRD_CONSTANT_H diff --git a/src/jrd/Resources.cpp b/src/jrd/Resources.cpp index 99962ee616d..2d512087f64 100644 --- a/src/jrd/Resources.cpp +++ b/src/jrd/Resources.cpp @@ -32,6 +32,7 @@ void Resources::transfer(thread_db* tdbb, VersionedObjects* to, bool internal) gotHash += functions.transfer(tdbb, to, internal, digest); gotHash += triggers.transfer(tdbb, to, internal, digest); gotHash += indices.transfer(tdbb, to, internal, digest); + gotHash += constants.transfer(tdbb, to, internal, digest); if (hasHash) { diff --git a/src/jrd/Resources.h b/src/jrd/Resources.h index cb7f2f9276d..6c6eebbdab7 100644 --- a/src/jrd/Resources.h +++ b/src/jrd/Resources.h @@ -42,6 +42,8 @@ class DbTriggers; class CharSetVers; class IndexPermanent; class IndexVersion; +class Constant; +class ConstantPermanent; namespace Cached { @@ -52,6 +54,7 @@ namespace Cached typedef CacheElement Function; typedef CacheElement Triggers; typedef CacheElement Index; + typedef CacheElement Constant; } class Resources; @@ -66,6 +69,7 @@ union VersionedPartPtr CharSetVers* charset; DbTriggers* triggers; IndexVersion* index; + Constant* constant; }; 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 Constant*& VersionedObjects::object(FB_SIZE_T n) { return data[n].constant; } template <> inline Function* VersionedObjects::object(FB_SIZE_T n) const { return data[n].function; } template <> inline jrd_prc* VersionedObjects::object(FB_SIZE_T n) const { return data[n].procedure; } @@ -127,6 +132,7 @@ template <> inline jrd_rel* VersionedObjects::object(FB_SIZE_T n) const template <> inline CharSetVers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].charset; } template <> inline DbTriggers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].triggers; } template <> inline IndexVersion* VersionedObjects::object(FB_SIZE_T n) const { return data[n].index; } +template <> inline Constant* VersionedObjects::object(FB_SIZE_T n) const { return data[n].constant; } template @@ -283,7 +289,8 @@ class Resources final procedures(p, versionCurrentPosition), functions(p, versionCurrentPosition), triggers(p, versionCurrentPosition), - indices(p, versionCurrentPosition) + indices(p, versionCurrentPosition), + constants(p, versionCurrentPosition) { } ~Resources(); @@ -294,6 +301,13 @@ class Resources final RscArray functions; RscArray triggers; RscArray indices; + RscArray constants; + + inline FB_SIZE_T countVersionedObjects() const noexcept + { + return charSets.getCount() + relations.getCount() + procedures.getCount() + + functions.getCount() + triggers.getCount() + constants.getCount(); //? + indices.getCount() ? + } }; // specialization @@ -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 constants; } namespace Rsc { @@ -312,6 +327,7 @@ namespace Rsc typedef CachedResource CSet; typedef CachedResource Trig; typedef CachedResource Idx; + typedef CachedResource Const; }; //namespace Rsc diff --git a/src/jrd/Routine.cpp b/src/jrd/Routine.cpp index e32096996e9..2cd3648c3db 100644 --- a/src/jrd/Routine.cpp +++ b/src/jrd/Routine.cpp @@ -134,6 +134,9 @@ void Routine::setStatement(Statement* value) statement->function = static_cast(this); break; + case obj_package_constant: + break; + default: fb_assert(false); break; diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp index 3b0797099cd..6571238726b 100644 --- a/src/jrd/Statement.cpp +++ b/src/jrd/Statement.cpp @@ -223,9 +223,7 @@ void Statement::loadResources(thread_db* tdbb, Request* req, bool withLock) if (ddl || (!latestVer) || (latestVer->version != frontVersion)) { - const FB_SIZE_T resourceCount = latestVer ? latestVer->getCapacity() : - resources->charSets.getCount() + resources->relations.getCount() + resources->procedures.getCount() + - resources->functions.getCount() + resources->triggers.getCount(); + const FB_SIZE_T resourceCount = latestVer ? latestVer->getCapacity() : resources->countVersionedObjects(); AutoPtr newVer = FB_NEW_RPT(*pool, resourceCount) VersionedObjects(resourceCount); MetadataCache::Version ver(mdc); diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index d34fd07fd99..04b5c5fd25c 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -6910,97 +6910,98 @@ dsc* evlUnicodeVal(thread_db* tdbb, const SysFunction*, const NestValueArray& ar +constexpr 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_SET_CONTEXT, 3, 3, false, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL}, - {"RDB$SYSTEM_PRIVILEGE", 1, 1, true, NULL, makeBooleanResult, evlSystemPrivilege, NULL}, - {"REPLACE", 3, 3, true, setParamsFromList, makeReplace, evlReplace, NULL}, - {"REVERSE", 1, 1, true, NULL, makeReverse, evlReverse, NULL}, - {"RIGHT", 2, 2, true, setParamsSecondInteger, makeLeftRight, evlRight, NULL}, - {"ROUND", 1, 2, true, setParamsRoundTrunc, makeRound, evlRound, NULL}, - {"RPAD", 2, 3, true, setParamsSecondInteger, makePad, evlPad, (void*) funRPad}, - {"RSA_DECRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, true, setParamsRsaEncrypt, makeRsaCrypt, evlRsaDecrypt, NULL}, - {"RSA_ENCRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, true, setParamsRsaEncrypt, makeRsaCrypt, evlRsaEncrypt, NULL}, - {"RSA_PRIVATE", 1, 1, false, setParamsInteger, makeRsaPrivate, evlRsaPrivate, NULL}, - {"RSA_PUBLIC", 1, 1, false, setParamsRsaPublic, makeRsaPublic, evlRsaPublic, NULL}, - {"RSA_SIGN_HASH", RSA_SIGN_ARG_MAX, RSA_SIGN_ARG_MAX, true, setParamsRsaSign, makeRsaSign, evlRsaSign, NULL}, - {"RSA_VERIFY_HASH", RSA_VERIFY_ARG_MAX, RSA_VERIFY_ARG_MAX, true, setParamsRsaVerify, makeBoolResult, evlRsaVerify, NULL}, - {"SIGN", 1, 1, true, setParamsDblDec, makeShortResult, evlSign, NULL}, - {"SIN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSin}, - {"SINH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSinh}, - {"SQRT", 1, 1, true, setParamsDblDec, makeDblDecResult, evlSqrt, NULL}, - {"TAN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTan}, - {"TANH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh}, - {"TOTALORDER", 2, 2, true, setParamsDecFloat, makeShortResult, evlCompare, (void*) funTotalOrd}, - {"TRUNC", 1, 2, true, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL}, - {"UNICODE_CHAR", 1, 1, true, setParamsInteger, makeUnicodeChar, evlUnicodeChar, NULL}, - {"UNICODE_VAL", 1, 1, true, setParamsUnicodeVal, makeLongResult, evlUnicodeVal, NULL}, - {"UUID_TO_CHAR", 1, 1, true, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL}, - {"", 0, 0, false, NULL, NULL, NULL, NULL} + {"ABS", 1, 1, DEFAULT, setParamsDblDec, makeAbs, evlAbs, NULL}, + {"ACOS", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcos}, + {"ACOSH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcosh}, + {"ASCII_CHAR", 1, 1, DEFAULT, setParamsInteger, makeAsciiChar, evlAsciiChar, NULL}, + {"ASCII_VAL", 1, 1, DEFAULT, setParamsAsciiVal, makeShortResult, evlAsciiVal, NULL}, + {"ASIN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsin}, + {"ASINH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsinh}, + {"ATAN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtan}, + {"ATANH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtanh}, + {"ATAN2", 2, 2, DEFAULT, setParamsDouble, makeDoubleResult, evlAtan2, NULL}, + {"BASE64_DECODE", 1, 1, DEFAULT, NULL, makeDecode64, evlDecode64, NULL}, + {"BASE64_ENCODE", 1, 1, DEFAULT, NULL, makeEncode64, evlEncode64, NULL}, + {"BIN_AND", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinAnd}, + {"BIN_NOT", 1, 1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinNot}, + {"BIN_OR", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinOr}, + {"BIN_SHL", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShl}, + {"BIN_SHR", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShr}, + {"BIN_SHL_ROT", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShlRot}, + {"BIN_SHR_ROT", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShrRot}, + {"BIN_XOR", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinXor}, + {"BLOB_APPEND", 2, -1, DETERMINISTIC, setParamsBlobAppend, makeBlobAppend, evlBlobAppend, NULL}, + {"CEIL", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlCeil, NULL}, + {"CEILING", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlCeil, NULL}, + {"CHAR_TO_UUID", 1, 1, DEFAULT, setParamsCharToUuid, makeUuid, evlCharToUuid, NULL}, + {"COMPARE_DECFLOAT", 2, 2, DEFAULT, setParamsDecFloat, makeShortResult, evlCompare, (void*) funCmpDec}, + {"COS", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCos}, + {"COSH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCosh}, + {"COT", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCot}, + {"CRYPT_HASH", 2, 2, DEFAULT, setParamsHash, makeHash, evlHash, NULL}, + {"DATEADD", 3, 3, DEFAULT, setParamsDateAdd, makeDateAdd, evlDateAdd, NULL}, + {"DATEDIFF", 3, 3, DEFAULT, setParamsDateDiff, makeDateDiff, evlDateDiff, NULL}, + {"DECRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, DEFAULT, setParamsEncrypt, makeCrypt, evlDecrypt, NULL}, + {"ENCRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, DEFAULT, setParamsEncrypt, makeCrypt, evlEncrypt, NULL}, + {"EXP", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlExp, NULL}, + {"FIRST_DAY", 2, 2, DEFAULT, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay}, + {"FLOOR", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlFloor, NULL}, + {"GEN_UUID", 0, 0, NONE, NULL, makeUuid, evlGenUuid, NULL}, + {"GREATEST", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue}, + {"HASH", 1, 2, DEFAULT, setParamsHash, makeHash, evlHash, NULL}, + {"HEX_DECODE", 1, 1, DEFAULT, NULL, makeDecodeHex, evlDecodeHex, NULL}, + {"HEX_ENCODE", 1, 1, DEFAULT, NULL, makeEncodeHex, evlEncodeHex, NULL}, + {"LAST_DAY", 2, 2, DEFAULT, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funLastDay}, + {"LEAST", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue}, + {"LEFT", 2, 2, DEFAULT, setParamsSecondInteger, makeLeftRight, evlLeft, NULL}, + {"LN", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLnat}, + {"LOG", 2, 2, DEFAULT, setParamsDblDec, makeDblDecResult, evlLog, NULL}, + {"LOG10", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLog10}, + {"LPAD", 2, 3, DEFAULT, setParamsSecondInteger, makePad, evlPad, (void*) funLPad}, + {"MAKE_DBKEY", 2, 4, DEFAULT, setParamsMakeDbkey, makeDbkeyResult, evlMakeDbkey, NULL}, + {"MAXVALUE", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue}, + {"MINVALUE", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue}, + {"MOD", 2, 2, DEFAULT, setParamsFromList, makeMod, evlMod, NULL}, + {"NORMALIZE_DECFLOAT", 1, 1, DEFAULT, setParamsDecFloat, makeDecFloatResult, evlNormDec, NULL}, + {"OVERLAY", 3, 4, DEFAULT, setParamsOverlay, makeOverlay, evlOverlay, NULL}, + {"PI", 0, 0, DEFAULT, NULL, makePi, evlPi, NULL}, + {"POSITION", 2, 4, DEFAULT, setParamsPosition, makeLongResult, evlPosition, NULL}, + {"POWER", 2, 2, DEFAULT, setParamsDblDec, makeDblDecResult, evlPower, NULL}, + {"QUANTIZE", 2, 2, DEFAULT, setParamsDecFloat, makeDecFloatResult, evlQuantize, NULL}, + {"RAND", 0, 0, NONE, NULL, makeDoubleResult, evlRand, NULL}, + {RDB_GET_CONTEXT, 2, 2, DETERMINISTIC, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL}, + {"RDB$GET_TRANSACTION_CN", 1, 1, NONE, setParamsInt64, makeGetTranCN, evlGetTranCN, NULL}, + {"RDB$ROLE_IN_USE", 1, 1, DETERMINISTIC, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL}, + {RDB_SET_CONTEXT, 3, 3, NONE, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL}, + {"RDB$SYSTEM_PRIVILEGE", 1, 1, DETERMINISTIC, NULL, makeBooleanResult, evlSystemPrivilege, NULL}, + {"REPLACE", 3, 3, DEFAULT, setParamsFromList, makeReplace, evlReplace, NULL}, + {"REVERSE", 1, 1, DEFAULT, NULL, makeReverse, evlReverse, NULL}, + {"RIGHT", 2, 2, DEFAULT, setParamsSecondInteger, makeLeftRight, evlRight, NULL}, + {"ROUND", 1, 2, DEFAULT, setParamsRoundTrunc, makeRound, evlRound, NULL}, + {"RPAD", 2, 3, DEFAULT, setParamsSecondInteger, makePad, evlPad, (void*) funRPad}, + {"RSA_DECRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, DEFAULT, setParamsRsaEncrypt, makeRsaCrypt, evlRsaDecrypt, NULL}, + {"RSA_ENCRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, DEFAULT, setParamsRsaEncrypt, makeRsaCrypt, evlRsaEncrypt, NULL}, + {"RSA_PRIVATE", 1, 1, NONE, setParamsInteger, makeRsaPrivate, evlRsaPrivate, NULL}, + {"RSA_PUBLIC", 1, 1, NONE, setParamsRsaPublic, makeRsaPublic, evlRsaPublic, NULL}, + {"RSA_SIGN_HASH", RSA_SIGN_ARG_MAX, RSA_SIGN_ARG_MAX, DEFAULT, setParamsRsaSign, makeRsaSign, evlRsaSign, NULL}, + {"RSA_VERIFY_HASH", RSA_VERIFY_ARG_MAX, RSA_VERIFY_ARG_MAX, DEFAULT, setParamsRsaVerify, makeBoolResult, evlRsaVerify, NULL}, + {"SIGN", 1, 1, DEFAULT, setParamsDblDec, makeShortResult, evlSign, NULL}, + {"SIN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSin}, + {"SINH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSinh}, + {"SQRT", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlSqrt, NULL}, + {"TAN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTan}, + {"TANH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh}, + {"TOTALORDER", 2, 2, DEFAULT, setParamsDecFloat, makeShortResult, evlCompare, (void*) funTotalOrd}, + {"TRUNC", 1, 2, DEFAULT, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL}, + {"UNICODE_CHAR", 1, 1, DEFAULT, setParamsInteger, makeUnicodeChar, evlUnicodeChar, NULL}, + {"UNICODE_VAL", 1, 1, DEFAULT, setParamsUnicodeVal, makeLongResult, evlUnicodeVal, NULL}, + {"UUID_TO_CHAR", 1, 1, DEFAULT, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL}, + {"", 0, 0, NONE, NULL, NULL, NULL, NULL} }; diff --git a/src/jrd/SysFunction.h b/src/jrd/SysFunction.h index 5a2cbc671b9..65543b710f5 100644 --- a/src/jrd/SysFunction.h +++ b/src/jrd/SysFunction.h @@ -46,6 +46,13 @@ namespace Jrd class SysFunction { public: + enum Flags : UCHAR + { + NONE = 0, + DETERMINISTIC = 1, + CONSTANT = 2 + }; + typedef void (*SetParamsFunc)(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int, dsc**); typedef void (*MakeFunc)(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc*, int, const dsc**); typedef dsc* (*EvlFunc)(Jrd::thread_db*, const SysFunction* function, @@ -54,7 +61,7 @@ class SysFunction const char* name; int minArgCount; int maxArgCount; // -1 for no limit - bool deterministic; + UCHAR flags; SetParamsFunc setParamsFunc; MakeFunc makeFunc; EvlFunc evlFunc; @@ -64,6 +71,16 @@ class SysFunction void checkArgsMismatch(int count) const; + inline bool isDeterministic() const + { + return flags & DETERMINISTIC; + } + + inline bool isConstant() const + { + return flags & CONSTANT; + } + private: const static SysFunction functions[]; }; diff --git a/src/jrd/constants.h b/src/jrd/constants.h index b40a51dbf77..dcb4230693d 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -154,6 +154,7 @@ inline constexpr int GEN_SECCLASS_PREFIX_LEN = 4; inline constexpr const char* PROCEDURES_GENERATOR = "RDB$PROCEDURES"; inline constexpr const char* FUNCTIONS_GENERATOR = "RDB$FUNCTIONS"; +inline constexpr const char* const CONSTANTS_GENERATOR = "RDB$CONSTANTS"; // Automatically created check constraints for unnamed PRIMARY and UNIQUE declarations. inline constexpr const char* IMPLICIT_INTEGRITY_PREFIX = "INTEG_"; diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 14512b641d1..8a9a49d05e3 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -133,6 +133,9 @@ #include "../jrd/shut_proto.h" #include "../jrd/ProtectRelations.h" +#include "../jrd/Constant.h" +#include "../dsql/metd_proto.h" + #ifdef HAVE_UNISTD_H #include #endif @@ -465,6 +468,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); } @@ -820,7 +826,7 @@ namespace private: // Get relations and fields on which this routine depends, either when it's being // created or when it's modified. - static Routine* getDependencies(DeferredWork* work, bool compile, jrd_tra* transaction) + static void getDependencies(DeferredWork* work, bool compile, jrd_tra* transaction) { thread_db* tdbb = JRD_get_thread_data(); Jrd::Database* dbb = tdbb->getDatabase(); @@ -830,14 +836,14 @@ namespace bid blobId; blobId.clear(); - Routine* routine = Self::lookupBlobId(tdbb, work, blobId, compile); + auto* object = Self::lookupBlobId(tdbb, work, blobId, compile); MetadataCache::verify_cache(tdbb); // get any dependencies now by parsing the blr - if (!routine) - return nullptr; + if (!object) + return; const QualifiedName depName(work->dfw_package.isEmpty() ? work->getQualifiedName() : @@ -864,8 +870,9 @@ namespace dbb->deletePool(new_pool); } } - else + else if constexpr (std::is_base_of_v) { + Routine* routine = object; Array dependencies; const auto allParameters = {&routine->getInputFields(), &routine->getOutputFields()}; @@ -904,7 +911,6 @@ namespace } MetadataCache::verify_cache(tdbb); - return routine; } }; @@ -938,6 +944,21 @@ namespace static void checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction); }; + class ConstantManager : public RoutineManager, MetadataCache::getPerm> + { + public: + static const char* getTypeStr() + { + return "constant"; + } + + static Constant* lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, bool compile); + static void validate(thread_db* tdbb, jrd_tra* transaction, DeferredWork* work, + SSHORT validBlr); + static void checkParamDependencies(thread_db* tdbb, DeferredWork* work, jrd_tra* transaction); + }; + // These methods cannot be defined inline, because GPRE generates wrong code. Routine* FunctionManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, @@ -1117,6 +1138,37 @@ namespace ERR_post(status); } } + + Constant* ConstantManager::lookupBlobId(thread_db* tdbb, DeferredWork* work, bid& blobId, + bool compile) + { + Jrd::Attachment* attachment = tdbb->getAttachment(); + CachedRequestId constDpd; + AutoCacheRequest handle(tdbb, constDpd); + Constant* routine = nullptr; + + FOR(REQUEST_HANDLE handle) + X IN RDB$CONSTANTS WITH + X.RDB$SCHEMA_NAME EQ work->dfw_schema.c_str() AND + X.RDB$CONSTANT_NAME EQ work->dfw_name.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), "") + { + if (!X.RDB$CONSTANT_BLR.NULL) + blobId = X.RDB$CONSTANT_BLR; + + routine = MetadataCache::getVersioned(tdbb, + work->getQualifiedName(), compile ? 0 : CacheFlag::MINISCAN); + } + END_FOR + + return routine; + } + + void ConstantManager::validate(thread_db*, jrd_tra*, DeferredWork*, SSHORT) + { } + + void ConstantManager::checkParamDependencies(thread_db*, DeferredWork*, jrd_tra*) + { } } // namespace static inline constexpr deferred_task task_table[] = @@ -1170,6 +1222,9 @@ static inline constexpr deferred_task task_table[] = { dfw_clear_cache, clear_cache }, { dfw_change_repl_state, change_repl_state }, { dfw_set_statistics, set_statistics }, + { dfw_create_package_constant, ConstantManager::createRoutine }, + { dfw_modify_package_constant, ConstantManager::modifyRoutine }, + { dfw_delete_package_constant, ConstantManager::deleteRoutine }, { dfw_null, NULL } }; @@ -2810,6 +2865,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; @@ -2823,6 +2881,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..b2d24f2a4f4 100644 --- a/src/jrd/drq.h +++ b/src/jrd/drq.h @@ -242,6 +242,7 @@ 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_MAX }; diff --git a/src/jrd/fields.h b/src/jrd/fields.h index 49a47fd055a..6c3ea4aa8cb 100644 --- a/src/jrd/fields.h +++ b/src/jrd/fields.h @@ -239,3 +239,8 @@ 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_const_name , nam_const_name , dtype_varying , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata , NULL , false , ODS_13_1) + FIELD(fld_const_id , nam_const_id , dtype_long , sizeof(SLONG) , 0 , NULL , true , ODS_14_0) + FIELD(fld_const_blr , nam_const_blr , dtype_blob , BLOB_SIZE , isc_blob_blr , NULL , true , ODS_13_1) + FIELD(fld_const_source , nam_const_source , dtype_blob , BLOB_SIZE , isc_blob_text , NULL , true , ODS_13_1) diff --git a/src/jrd/ids.h b/src/jrd/ids.h index 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..5c8485872cc 100644 --- a/src/jrd/idx.h +++ b/src/jrd/idx.h @@ -524,6 +524,16 @@ static inline constexpr struct ini_idx_t indices[] = SEGMENT(f_pubtab_tab_schema, idx_metadata), // table schema name SEGMENT(f_pubtab_tab_name, idx_metadata), // table name SEGMENT(f_pubtab_pub_name, idx_metadata) // publication name + }}, + // define index RDB$INDEX_98 for RDB$CONSTANTS unique RDB$SCHEMA_NAME, RDB$PACKAGE_NAME, RDB$CONSTANT_NAME; + INDEX(98, rel_constants, idx_unique, 3, ODS_14_0) + SEGMENT(f_const_package_schema, idx_metadata), // table schema name + SEGMENT(f_const_package, idx_metadata), // package name + SEGMENT(f_const_name, idx_metadata) // constant name + }}, + // define index RDB$INDEX_99 for RDB$CONSTANTS unique RDB$CONSTANT_ID; + INDEX(99, rel_constants, idx_unique, 1, ODS_14_0) + SEGMENT(f_const_id, idx_numeric) // constant id }} }; diff --git a/src/jrd/irq.h b/src/jrd/irq.h index a1611c9e07a..979c817d911 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 @@ -178,7 +179,6 @@ enum irq_type_t irq_index_id_erase, // cleanup index ID irq_get_index_by_name, // find appropriate index irq_l_index_cnstrt, // lookup index for constraint - irq_MAX }; diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index 132170ea5e8..7eb93e3cfe6 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -58,6 +58,7 @@ #include "../common/os/guid.h" #include "../jrd/sbm.h" #include "../jrd/scl.h" +#include "../jrd/Constant.h" #include "../jrd/Routine.h" #include "../jrd/ExtEngineManager.h" #include "../jrd/Attachment.h" diff --git a/src/jrd/lck.cpp b/src/jrd/lck.cpp index a1868ed441e..d9f20aae664 100644 --- a/src/jrd/lck.cpp +++ b/src/jrd/lck.cpp @@ -584,6 +584,7 @@ static lck_owner_t get_owner_type(enum lck_t lock_type) case LCK_idx_rescan: case LCK_prc_rescan: case LCK_fun_rescan: + case LCK_constant_rescan: case LCK_cs_rescan: case LCK_dbwide_triggers: owner_type = LCK_OWNER_database; diff --git a/src/jrd/lck.h b/src/jrd/lck.h index ce927051978..24e74ed468b 100644 --- a/src/jrd/lck.h +++ b/src/jrd/lck.h @@ -67,6 +67,7 @@ enum lck_t : UCHAR { LCK_idx_rescan, // Index rescan lock LCK_prc_rescan, // Procedure rescan lock LCK_fun_rescan, // Function existence lock + LCK_constant_rescan, // Constant existence lock LCK_rel_partners, // Relation partners lock LCK_crypt, // Crypt lock for single crypt thread LCK_crypt_status, // Notifies about changed database encryption status diff --git a/src/jrd/met.epp b/src/jrd/met.epp index a1ef049d924..654316f358a 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/Constant.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_constants.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 constants. * **************************************/ SET_TDBB(tdbb); @@ -806,6 +809,32 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const } END_FOR + + request.reset(tdbb, irq_m_fields10, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE request) + DEP IN RDB$DEPENDENCIES CROSS + CONST IN RDB$CONSTANTS + WITH DEP.RDB$DEPENDED_ON_SCHEMA_NAME EQ schemaName->dsc_address AND + DEP.RDB$DEPENDED_ON_NAME EQ fieldSource->dsc_address AND + DEP.RDB$DEPENDED_ON_TYPE EQ obj_field AND + (DEP.RDB$DEPENDENT_TYPE EQ obj_package_header OR + DEP.RDB$DEPENDENT_TYPE EQ obj_package_body) AND + DEP.RDB$DEPENDENT_SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND + DEP.RDB$DEPENDENT_NAME EQ CONST.RDB$PACKAGE_NAME + { + QualifiedName constantFullName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME); + + const auto id = CONST.RDB$CONSTANT_ID; + + DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_package_constant, + string(constantFullName.object.c_str()), constantFullName.schema, id, constantFullName.package); + DFW_post_work_arg(transaction, dw2, nullptr, nullptr, 0, dfw_arg_check_blr); + + MetadataCache::newVersion(tdbb, id); + } + END_FOR + return calcFieldRelation; } @@ -4269,6 +4298,10 @@ void MET_store_dependencies(thread_db* tdbb, name = *dependency.name; dpdo_name = &name; break; + case obj_package_constant: + name = *dependency.name; + dpdo_name = &name; + break; } MetaName field_name; @@ -4616,6 +4649,14 @@ void MetadataCache::releaseLocks(thread_db* tdbb) charset->releaseLocks(tdbb); } + // Release constants locks + + for (auto constant : mdc_constants) + { + if (constant) + constant->releaseLocks(tdbb); + } + // Release database triggers locks for (unsigned i = 0; i < DB_TRIGGERS_COUNT; ++i) diff --git a/src/jrd/met.h b/src/jrd/met.h index 4663a189928..086f8f501e0 100644 --- a/src/jrd/met.h +++ b/src/jrd/met.h @@ -238,6 +238,7 @@ class MetadataCache : public Firebird::PermanentStorage mdc_procedures(getPool()), mdc_functions(getPool()), mdc_charsets(getPool()), + mdc_constants(getPool()), mdc_cleanup_queue(pool) { memset(mdc_triggers, 0, sizeof(mdc_triggers)); @@ -490,6 +491,16 @@ class MetadataCache : public Firebird::PermanentStorage } }; + template + class Vector + { + public: + static CacheVector& get(MetadataCache* mdc) + { + return mdc->mdc_constants; + } + }; + static MetadataCache* getCache(thread_db* tdbb) noexcept; class GeneratorFinder @@ -595,6 +606,7 @@ class MetadataCache : public Firebird::PermanentStorage CacheVector mdc_procedures; CacheVector mdc_functions; // User defined functions CacheVector mdc_charsets; // intl character set descriptions + CacheVector mdc_constants; // Package constants TriggersSet mdc_triggers[DB_TRIGGERS_COUNT]; // Two numbers are required because commit into cache is not atomic event. // Front value is incremented before commit, back - after commit. diff --git a/src/jrd/names.h b/src/jrd/names.h index 27ceff0f931..ba020049c00 100644 --- a/src/jrd/names.h +++ b/src/jrd/names.h @@ -491,6 +491,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..b2374d6cc82 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: @@ -185,6 +188,8 @@ inline constexpr const char* getDdlSecurityName(ObjectType object_type) noexcept return "SQL$TABLESPACES"; case obj_schemas: return "SQL$SCHEMAS"; + case obj_package_constant: + return "SQL$PACKAGE_CONSTANT"; default: return ""; } @@ -270,6 +275,8 @@ inline const char* getObjectName(ObjectType objType) return "FILTER"; case obj_schema: return "SCHEMA"; + case obj_package_constant: + return "PACKAGE CONSTANT"; default: fb_assert(false); return ""; diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 030025effcb..d2ef9e4de13 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -839,3 +839,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, 0, ODS_14_0) + FIELD(f_const_field, nam_f_source, fld_f_name, 0, ODS_14_0) + FIELD(f_const_private_flag, nam_private_flag, fld_flag_nullable, 0, ODS_14_0) + FIELD(f_const_blr, nam_const_blr, fld_const_blr, 0, ODS_14_0) + FIELD(f_const_source, nam_const_source, fld_source, 1, ODS_14_0) + FIELD(f_const_package_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) + FIELD(f_const_description, nam_description, fld_description, 1, ODS_14_0) +END_RELATION diff --git a/src/jrd/tra.h b/src/jrd/tra.h index e10079bebb4..e6e8867060a 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -538,6 +538,11 @@ enum dfw_t : int { dfw_set_generator, dfw_change_repl_state, + // Package constant + dfw_create_package_constant, + dfw_modify_package_constant, + dfw_delete_package_constant, + // deferred works argument types dfw_arg_proc_name, // procedure name for dfw_delete_prm, mandatory dfw_arg_check_blr, // check if BLR is still compilable diff --git a/src/jrd/trig.h b/src/jrd/trig.h index 53bb6bba270..fc05531a6a1 100644 --- a/src/jrd/trig.h +++ b/src/jrd/trig.h @@ -82,6 +82,7 @@ static inline constexpr Jrd::gen generators[] = { "RDB$BACKUP_HISTORY", 9, "Nbackup technology", ODS_13_0 }, { FUNCTIONS_GENERATOR, 10, "Function ID", ODS_13_0 }, { "RDB$GENERATOR_NAME", 11, "Implicit generator name", ODS_13_0 }, + { CONSTANTS_GENERATOR, 12, "Constant ID", ODS_14_0 }, { nullptr, 0, nullptr, 0 } }; diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 63276ae47ed..88a867d79c5 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -94,6 +94,7 @@ #include "../jrd/trace/TraceJrdHelpers.h" #include "../common/Task.h" #include "../jrd/WorkerAttachment.h" +#include "../jrd/Constant.h" using namespace Jrd; using namespace Firebird; @@ -2346,6 +2347,27 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) DFW_post_work(transaction, dfw_change_repl_state, {}, {}, 1); break; + case rel_constants: + protect_system_table_delupd(tdbb, relation, "DELETE"); + + EVL_field(0, rpb->rpb_record, f_const_name, &desc); + EVL_field(0, rpb->rpb_record, f_const_package_schema, &schemaDesc); + + if (EVL_field(0, rpb->rpb_record, f_const_package, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); + + EVL_field(0, rpb->rpb_record, f_const_id, &desc2); + id = MOV_get_long(tdbb, &desc2, 0); + + { + [[maybe_unused]] + auto constant = Constant::lookup(tdbb, id); + fb_assert(constant); + } + + DFW_post_work(transaction, dfw_delete_package_constant, &desc, &schemaDesc, id, object_name.package); + break; + default: // Shut up compiler warnings break; } @@ -3732,6 +3754,25 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j check_repl_state(tdbb, transaction, org_rpb, new_rpb, f_pub_active_flag); break; + case rel_constants: + if (!check_nullify_source(tdbb, org_rpb, new_rpb, f_const_source)) + protect_system_table_delupd(tdbb, relation, "UPDATE"); + + EVL_field(0, org_rpb->rpb_record, f_const_name, &desc1); + EVL_field(0, org_rpb->rpb_record, f_const_package_schema, &schemaDesc); + + if (EVL_field(0, org_rpb->rpb_record, f_const_package, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); + + protect_system_table_delupd(tdbb, relation, "UPDATE"); + + { // Send dfw + EVL_field(0, org_rpb->rpb_record, f_const_id, &desc2); + const USHORT id = MOV_get_long(tdbb, &desc2, 0); + DFW_post_work(transaction, Jrd::dfw_modify_package_constant, &desc1, &schemaDesc, id, object_name.package); + } + break; + default: break; } @@ -4641,6 +4682,21 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) DFW_post_work(transaction, dfw_change_repl_state, {}, {}, 1); break; + case rel_constants: + protect_system_table_insert(tdbb, request, relation); + + EVL_field(0, rpb->rpb_record, f_const_name, &desc); + EVL_field(0, rpb->rpb_record, f_const_package_schema, &schemaDesc); + + if (EVL_field(0, rpb->rpb_record, f_const_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, "RDB$CONSTANTS"); + + work = DFW_post_work(transaction, dfw_create_package_constant, &desc, &schemaDesc, object_id, object_name.package); + break; + default: // Shut up compiler warnings break; }