diff --git a/builds/win32/msvc15/engine_static.vcxproj b/builds/win32/msvc15/engine_static.vcxproj
index 67bbab0013e..0d1222eb56f 100644
--- a/builds/win32/msvc15/engine_static.vcxproj
+++ b/builds/win32/msvc15/engine_static.vcxproj
@@ -41,6 +41,7 @@
+
@@ -416,6 +417,7 @@
+
diff --git a/doc/sql.extensions/README.ddl.txt b/doc/sql.extensions/README.ddl.txt
index 8d53d670a5d..35b714c1294 100644
--- a/doc/sql.extensions/README.ddl.txt
+++ b/doc/sql.extensions/README.ddl.txt
@@ -138,6 +138,7 @@ COMMENT ON name IS {'txt'|NULL};
COMMENT ON COLUMN table_or_view_name.field_name IS {'txt'|NULL};
COMMENT ON {PROCEDURE | [EXTERNAL] FUNCTION} [ .] name.param_name IS {'txt'|NULL};
COMMENT ON [PROCEDURE | FUNCTION] PARAMETER [ .] name.param_name IS {'txt'|NULL};
+COMMENT ON CONSTANT [ .] . name IS {'txt'|NULL};
An empty literal string '' will act as NULL since the internal code (DYN in this case)
works this way with blobs.
diff --git a/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt
index 74ecf7cf888..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 4ec7cd6d1f2..4a36df8eb69 100644
--- a/src/burp/backup.epp
+++ b/src/burp/backup.epp
@@ -156,6 +156,7 @@ void write_triggers();
void write_trigger_messages();
void write_types();
void write_user_privileges();
+void write_constants();
void general_on_error();
@@ -505,6 +506,13 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name)
write_pub_tables();
}
+ if (tdgbl->runtimeODS >= DB_VERSION_DDL14)
+ {
+ // Write constants
+ BURP_verbose(USHORT(isc_gbak_writing_constants)); // writing constants
+ write_constants();
+ }
+
// Finish up
put(tdgbl, (UCHAR) rec_end);
@@ -4845,6 +4853,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 3fb58a76b9e..0130d4070ab 100644
--- a/src/burp/burp.h
+++ b/src/burp/burp.h
@@ -124,7 +124,8 @@ enum rec_type {
rec_db_creator, // Database creator
rec_publication, // Publication
rec_pub_table, // Publication table
- rec_schema // Schema
+ rec_schema, // Schema
+ rec_constants // Constants
};
@@ -701,6 +702,16 @@ enum att_type {
att_schema_security_class,
att_schema_owner_name,
att_schema_description,
+
+ // Constants
+ att_constant_name = SERIES,
+ att_constant_package,
+ att_constant_field_source,
+ att_constant_private_flag,
+ att_constant_blr,
+ att_constant_source,
+ att_constant_schema_name,
+ att_constant_description,
};
diff --git a/src/burp/restore.epp b/src/burp/restore.epp
index aa36e72a748..5c786c3fe34 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*);
@@ -10555,6 +10556,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)
{
/**************************************
@@ -11398,6 +11478,12 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file
flag = true;
break;
+ case rec_constants:
+ if (!get_constants_table(tdgbl))
+ return false;
+ flag = true;
+ break;
+
default:
BURP_error(43, true, SafeArg() << record);
// msg 43 don't recognize record type %ld
diff --git a/src/common/ParserTokens.h b/src/common/ParserTokens.h
index 3ddad43eb78..8dfd004bc2e 100644
--- a/src/common/ParserTokens.h
+++ b/src/common/ParserTokens.h
@@ -138,6 +138,7 @@ PARSER_TOKEN(TOK_CONDITIONAL, "CONDITIONAL", true)
PARSER_TOKEN(TOK_CONNECT, "CONNECT", false)
PARSER_TOKEN(TOK_CONNECTIONS, "CONNECTIONS", true)
PARSER_TOKEN(TOK_CONSISTENCY, "CONSISTENCY", true)
+PARSER_TOKEN(TOK_CONSTANT, "CONSTANT", true)
PARSER_TOKEN(TOK_CONSTRAINT, "CONSTRAINT", false)
PARSER_TOKEN(TOK_CONTAINING, "CONTAINING", true)
PARSER_TOKEN(TOK_CONTINUE, "CONTINUE", true)
diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp
index 8bdb8c9b184..ae65d2c2ce4 100644
--- a/src/dsql/DdlNodes.epp
+++ b/src/dsql/DdlNodes.epp
@@ -80,6 +80,7 @@
#include "../jrd/ini.h"
#include "../jrd/GarbageCollector.h"
#include "../jrd/ProtectRelations.h"
+#include "../dsql/PackageNodes.h"
namespace Jrd {
@@ -131,16 +132,6 @@ static rel_t relationType(SSHORT relationTypeNull, SSHORT relationType) noexcept
static void saveField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const MetaName& fieldName);
static dsql_rel* saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
const QualifiedName& relationName, bool view, bool creating);
-static void updateRdbFields(const TypeClause* type,
- SSHORT& fieldType,
- SSHORT& fieldLength,
- SSHORT& fieldSubTypeNull, SSHORT& fieldSubType,
- SSHORT& fieldScaleNull, SSHORT& fieldScale,
- SSHORT& characterSetIdNull, SSHORT& characterSetId,
- SSHORT& characterLengthNull, SSHORT& characterLength,
- SSHORT& fieldPrecisionNull, SSHORT& fieldPrecision,
- SSHORT& collationIdNull, SSHORT& collationId,
- SSHORT& segmentLengthNull, SSHORT& segmentLength);
static ISC_STATUS getErrorCodeByObjectType(int obj_type);
static constexpr const char* CHECK_CONSTRAINT_EXCEPTION = "check_constraint";
@@ -898,7 +889,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,
@@ -1506,6 +1497,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;
@@ -1610,6 +1615,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);
}
@@ -1773,6 +1782,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;
@@ -1796,7 +1811,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)
@@ -11608,11 +11623,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)))
{
@@ -11628,6 +11645,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 29eac015e71..0b40e024e3a 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.
@@ -12462,7 +12503,12 @@ void SysFuncCallNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc)
bool SysFuncCallNode::deterministic(thread_db* tdbb) const
{
- return ExprNode::deterministic(tdbb) && function->deterministic;
+ return ExprNode::deterministic(tdbb) && function->isDeterministic();
+}
+
+bool SysFuncCallNode::constant() const
+{
+ return ExprNode::isChildrenConstant() && function->isConstant();
}
void SysFuncCallNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc)
@@ -14149,6 +14195,17 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
if (!node->dsqlVar ||
(node->dsqlVar->type == dsql_var::TYPE_LOCAL && !node->dsqlVar->initialized && !dsqlScratch->mainScratch))
{
+ if (dsqlScratch->package.object.hasData())
+ {
+ thread_db* tdbb = JRD_get_thread_data();
+ QualifiedName constantFullName(dsqlName, dsqlScratch->package.schema, dsqlScratch->package.object);
+ if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), constantFullName))
+ {
+ delete node;
+ return FB_NEW_POOL(dsqlScratch->getPool()) PackageReferenceNode(dsqlScratch->getPool(), constantFullName);
+ }
+ }
+
PASS1_field_unknown(NULL, dsqlName.toQuotedString().c_str(), this);
}
diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h
index 108daf7d984..9b196cdb6f4 100644
--- a/src/dsql/ExprNodes.h
+++ b/src/dsql/ExprNodes.h
@@ -88,6 +88,11 @@ class ArithmeticNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) override;
@@ -236,6 +246,11 @@ class BoolAsValueNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void setDsqlDesc(const dsc& desc)
{
dsqlDesc = desc;
@@ -320,6 +340,11 @@ class CoalesceNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
bool possiblyUnknown() const override
{
return true;
@@ -655,6 +695,11 @@ class DefaultNode final : public DsqlNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override;
ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb) override;
@@ -714,6 +759,11 @@ class DerivedExprNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) override;
@@ -1315,6 +1385,11 @@ class NullNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return true;
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
dsc* execute(thread_db* tdbb, Request* request) const override;
@@ -1834,6 +1909,11 @@ class ScalarNode final : public TypedNode
fb_assert(false);
}
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) override;
@@ -1925,6 +2005,11 @@ class StrCaseNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override;
@@ -2061,6 +2151,11 @@ class SubstringNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override;
@@ -2275,6 +2381,11 @@ class ValueIfNode final : public TypedNode > MetaNameBidPair;
typedef Firebird::GenericMap MetaNameBidMap;
@@ -519,6 +531,7 @@ class ExprNode : public DmlNode
TYPE_WINDOW_CLAUSE,
TYPE_WINDOW_CLAUSE_FRAME,
TYPE_WINDOW_CLAUSE_FRAME_EXTENT,
+ TYPE_REFERENCE,
// Bool types
TYPE_BINARY_BOOL,
@@ -677,6 +690,9 @@ class ExprNode : public DmlNode
}
// Check if expression returns deterministic result
+ // Determinate whether the node is volatile (or not) in the current execution context.
+ // A DBKEY is deterministic (it cannot change for an already fetched row)
+ // but it's not constant (and thus cannot be used as an initializer expression).
virtual bool deterministic(thread_db* tdbb) const;
// Check if expression could return NULL or expression can turn NULL into a true/false.
@@ -688,9 +704,18 @@ class ExprNode : public DmlNode
// Verify if this node is allowed in an unmapped boolean.
virtual bool unmappable(const MapNode* mapNode, StreamType shellStream) const;
+ // Check if expression returns constant result
+ // The result true means the value does not change after recompilation
+ virtual bool constant() const
+ {
+ return false;
+ }
+
// Return all streams referenced by the expression.
virtual void collectStreams(SortedStreamList& streamList) const;
+ bool isChildrenConstant() const;
+
bool containsStream(StreamType stream, bool only = false) const
{
SortedStreamList nodeStreams;
@@ -1342,6 +1367,17 @@ class ValueListNode : public TypedNode
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
+ virtual bool constant() const override
+ {
+ for (auto& child : items)
+ {
+ if (!child->constant())
+ return false;
+ }
+
+ return true;
+ }
+
ValueListNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override
{
ValueListNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ValueListNode(dsqlScratch->getPool(),
diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp
index 42f888c9cb0..3a6e9464774 100644
--- a/src/dsql/PackageNodes.epp
+++ b/src/dsql/PackageNodes.epp
@@ -38,6 +38,14 @@
#include "../jrd/Attachment.h"
#include "../jrd/scl_proto.h"
+#include "../common/dsc_proto.h" // DSC_make_descriptor
+#include "../jrd/Package.h" // Constant
+
+#include "../dsql/metd_proto.h" // METD_get_domain
+#include "../common/classes/VaryStr.h" // METD_get_domain
+#include "../jrd/par_proto.h" // PAR_proto in Nodes.h (dependency hell)
+#include "../jrd/met.h" // Metacache
+#include "../jrd/Statement.h" // Jrd::Statement
using namespace Firebird;
@@ -53,155 +61,780 @@ 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)
+ {
+ static const CachedRequestId requestId;
+ AutoCacheRequest request(tdbb, requestId);
+
+ FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ CONST IN RDB$CONSTANTS
+ WITH CONST.RDB$SCHEMA_NAME EQ m_name.schema.c_str() AND
+ CONST.RDB$PACKAGE_NAME EQ m_name.package.c_str() AND
+ CONST.RDB$CONSTANT_NAME EQ m_name.object.c_str()
+ {
+ ERASE CONST;
+ }
+ END_FOR
+ }
+
+private:
+ QualifiedName m_name;
+};
+
+
+template
+void dropItem(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& fullName)
+{
+ MemoryPool& pool = dsqlScratch->getPool();
+ jrd_tra* transaction = dsqlScratch->getTransaction();
+
+ TDropNode dropNode(pool, fullName);
+ dropNode.dsqlPass(dsqlScratch);
+ dropNode.execute(tdbb, dsqlScratch, transaction);
+}
+
+template
+void dropMissingItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema,
+ const PackageItemsHolder::ItemsSignatureArray& existsItems,
+ const CreateAlterPackageNode::ItemsNameArray& newNames)
+{
+ for (auto i = existsItems.begin(); i != existsItems.end(); ++i)
+ {
+ if (!newNames.exist(i->name))
+ {
+ dropItem(tdbb, dsqlScratch,
+ QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.object));
+ }
+ }
+}
+
+template
+void dropItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema,
+ const PackageItemsHolder::ItemsSignatureArray& items)
+{
+ for (auto i = items.begin(); i != items.end(); ++i)
+ {
+ dropItem(tdbb, dsqlScratch,
+ QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.object));
+ }
+}
+
+void checkItemsDefineMatch(MemoryPool& pool, const QualifiedName& packageAndSchema,
+ const PackageItemsHolder::ItemsSignatureArray& newItems,
+ const PackageItemsHolder::ItemsSignatureArray& existingItems,
+ ISC_STATUS missingCode, ISC_STATUS mismatchCode)
+{
+ for (auto i = existingItems.begin(); i != existingItems.end(); ++i)
+ {
+ FB_SIZE_T pos;
+ bool found = newItems.find(Signature(pool, i->name), pos);
+
+ if (!found || !newItems[pos].defined)
+ {
+ status_exception::raise(
+ Arg::Gds(missingCode) << i->name << packageAndSchema.toQuotedString());
+ }
+ else if (newItems[pos] != *i)
+ {
+ status_exception::raise(
+ Arg::Gds(mismatchCode) << i->name << packageAndSchema.toQuotedString());
+ }
+ }
+}
+
+} // namespace
+
+
+//----------------------
+
+void PackageItemsHolder::drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema)
+{
+ dropItems(tdbb, dsqlScratch, packageAndSchema, functions);
+ dropItems(tdbb, dsqlScratch, packageAndSchema, procedures);
+ dropItems(tdbb, dsqlScratch, packageAndSchema, constants);
+}
+
+void PackageItemsHolder::checkDefineMatch(MemoryPool& pool, const QualifiedName& packageAndSchema,
+ const PackageItemsHolder& newItems)
+{
+ checkItemsDefineMatch(pool, packageAndSchema, newItems.functions, functions,
+ isc_dyn_funcnotdef_package, isc_dyn_funcsignat_package);
+ checkItemsDefineMatch(pool, packageAndSchema, newItems.procedures, procedures,
+ isc_dyn_procnotdef_package, isc_dyn_procsignat_package);
+}
+
// Return function and procedure names (in the user charset) and optionally its details for a
// given package.
- void collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& packageName,
- SortedObjectsArray& functions,
- SortedObjectsArray& procedures, bool details)
+void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transaction,
+ const QualifiedName& packageName,
+ bool details, bool collectConstants)
+{
+ AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS);
+ AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS);
+
+ FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
+ FUN IN RDB$FUNCTIONS
+ WITH FUN.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND
+ FUN.RDB$PACKAGE_NAME EQ packageName.object.c_str()
{
- AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS);
- AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS);
+ Signature function(FUN.RDB$FUNCTION_NAME);
+ function.defined = !FUN.RDB$FUNCTION_BLR.NULL || !FUN.RDB$ENTRYPOINT.NULL;
+
+ if (!FUN.RDB$DETERMINISTIC_FLAG.NULL && FUN.RDB$DETERMINISTIC_FLAG != 0)
+ function.flags |= Signature::FLAG_DETERMINISTIC;
- FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
- FUN IN RDB$FUNCTIONS
- WITH FUN.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND
- FUN.RDB$PACKAGE_NAME EQ packageName.object.c_str()
+ if (details)
{
- Signature function(FUN.RDB$FUNCTION_NAME);
- function.defined = !FUN.RDB$FUNCTION_BLR.NULL || !FUN.RDB$ENTRYPOINT.NULL;
+ FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
+ ARG IN RDB$FUNCTION_ARGUMENTS CROSS
+ FLD IN RDB$FIELDS
+ WITH ARG.RDB$SCHEMA_NAME EQ FUN.RDB$SCHEMA_NAME AND
+ ARG.RDB$PACKAGE_NAME EQ FUN.RDB$PACKAGE_NAME AND
+ ARG.RDB$FUNCTION_NAME EQ FUN.RDB$FUNCTION_NAME AND
+ FLD.RDB$SCHEMA_NAME EQUIV ARG.RDB$FIELD_SOURCE_SCHEMA_NAME AND
+ FLD.RDB$FIELD_NAME EQ ARG.RDB$FIELD_SOURCE
+ {
+ SignatureParameter parameter(*getDefaultMemoryPool());
+
+ parameter.number = ARG.RDB$ARGUMENT_POSITION;
+ parameter.name = ARG.RDB$ARGUMENT_NAME;
+ parameter.fieldSource = QualifiedName(ARG.RDB$FIELD_SOURCE, ARG.RDB$FIELD_SOURCE_SCHEMA_NAME);
+ parameter.mechanism = ARG.RDB$ARGUMENT_MECHANISM;
+
+ if (!ARG.RDB$FIELD_NAME.NULL)
+ parameter.fieldName = QualifiedName(ARG.RDB$FIELD_NAME);
+ if (!ARG.RDB$RELATION_NAME.NULL)
+ parameter.relationName = QualifiedName(ARG.RDB$RELATION_NAME, ARG.RDB$RELATION_SCHEMA_NAME);
+ if (!ARG.RDB$COLLATION_ID.NULL)
+ parameter.collationId = CollId(ARG.RDB$COLLATION_ID);
+ if (!ARG.RDB$NULL_FLAG.NULL)
+ parameter.nullFlag = ARG.RDB$NULL_FLAG;
+
+ if (!FLD.RDB$FIELD_LENGTH.NULL)
+ parameter.fieldLength = FLD.RDB$FIELD_LENGTH;
+ if (!FLD.RDB$FIELD_SCALE.NULL)
+ parameter.fieldScale = FLD.RDB$FIELD_SCALE;
+ if (!FLD.RDB$FIELD_TYPE.NULL)
+ parameter.fieldType = FLD.RDB$FIELD_TYPE;
+ if (!FLD.RDB$FIELD_SUB_TYPE.NULL)
+ parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE;
+ if (!FLD.RDB$SEGMENT_LENGTH.NULL)
+ parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH;
+ if (!FLD.RDB$NULL_FLAG.NULL)
+ parameter.fieldNullFlag = FLD.RDB$NULL_FLAG;
+ if (!FLD.RDB$CHARACTER_LENGTH.NULL)
+ parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH;
+ if (!FLD.RDB$COLLATION_ID.NULL)
+ parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID);
+ if (!FLD.RDB$CHARACTER_SET_ID.NULL)
+ parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID);
+ if (!FLD.RDB$FIELD_PRECISION.NULL)
+ parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION;
+
+ function.parameters.add(parameter);
+ }
+ END_FOR
+ }
- if (!FUN.RDB$DETERMINISTIC_FLAG.NULL && FUN.RDB$DETERMINISTIC_FLAG != 0)
- function.flags |= Signature::FLAG_DETERMINISTIC;
+ functions.add(function);
+ }
+ END_FOR
+
+ 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)
+ 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
+
+
+ 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()
+ {
- functions.add(function);
+ Signature constant(CONST.RDB$CONSTANT_NAME);
+ constants.add(constant);
}
END_FOR
+ }
+}
+
+void PackageItemsHolder::clear()
+{
+ functions.clear();
+ procedures.clear();
+}
+
+//----------------------
+
+
+// -----------------------------------
+// PackageReferenceNode implementation
+// -----------------------------------
+
+static RegisterNode regPackageReferenceNode({blr_package_reference});
+
+PackageReferenceNode::PackageReferenceNode(MemoryPool& pool, const QualifiedName& fullName, const UCHAR itemType)
+ : TypedNode(pool),
+ m_fullName(fullName.object, fullName.schema.hasData() ? fullName.schema : PUBLIC_SCHEMA, fullName.package), m_itemType(itemType)
+{}
+
+string PackageReferenceNode::internalPrint(NodePrinter& printer) const
+{
+ ExprNode::internalPrint(printer);
+
+ NODE_PRINT(printer, m_fullName);
+
+ 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 = fullName;
+
+ csb->addDependency(dependency);
+ }
+
+ {
+ csb->qualifyExistingName(tdbb, fullName, obj_package_constant);
+ auto package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE);
+ if (package)
+ {
+ node->m_package = csb->csb_resources->packages.registerResource(package->getPermanent());
+ }
+ }
+
+ if (!node->m_package)
+ status_exception::raise(Arg::Gds(isc_bad_constant_name) <<
+ Arg::Str(fullName.toQuotedString()));
+
+ return node;
+ }
+ // TODO: rowtype
+ default:
+ fb_assert(false);
+ }
+
+ return nullptr;
+}
+
+
+void PackageReferenceNode::genBlr(DsqlCompilerScratch* dsqlScratch)
+{
+ dsqlScratch->appendUChar(blr_package_reference);
+ dsqlScratch->appendUChar(blr_pkg_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 = ConstantValue::getDesc(tdbb, transaction, m_fullName);
+}
+
+bool PackageReferenceNode::constantExists(thread_db* tdbb, Jrd::jrd_tra* transaction,
+ QualifiedName fullName, bool* isPrivate)
+{
+ if (fullName.package.isEmpty() || fullName.object.isEmpty())
+ return false;
+
+ // Use default schema if one not specified
+ if (fullName.schema.isEmpty())
+ fullName.schema = PUBLIC_SCHEMA;
+
+ auto* package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE);
+ if (package == nullptr)
+ return false;
+
+ ConstantValue* value = package->findConstant(tdbb, fullName);
+
+ if (value == nullptr)
+ return false;
+
+ if (isPrivate)
+ *isPrivate = value->isPrivate;
+
+ return true;
+}
+
+void PackageReferenceNode::getDesc(thread_db* tdbb, CompilerScratch*, dsc* desc)
+{
+ *desc = ConstantValue::getDesc(tdbb, tdbb->getTransaction(), m_fullName);
+}
+
+ValueExprNode* PackageReferenceNode::copy(thread_db* tdbb, NodeCopier& copier) const
+{
+ MemoryPool& pool = *tdbb->getDefaultPool();
+ auto* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, m_fullName, m_itemType);
+
+ node->m_package = copier.csb->csb_resources->packages.registerResource(m_package());
+
+ return node;
+}
+
+ValueExprNode* PackageReferenceNode::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* outputImpure = request->getImpure(m_impureOffset);
+ switch (m_itemType)
+ {
+ case blr_pkg_ref_item_const:
+ {
+ Package* package = m_package(request->getResources());
+ package->checkReload(tdbb);
+
+ EVL_make_value(tdbb, &package->findConstant(tdbb, m_fullName)->makeValue(tdbb), outputImpure);
+ return &outputImpure->vlu_desc;
+ }
+ default:
+ fb_assert(false);
+ return nullptr;
+ }
+}
+
+
+// ----------------------------------------
+// CreatePackageConstantNode
+// ----------------------------------------
+
+string CreatePackageConstantNode::internalPrint(NodePrinter& printer) const
+{
+ DdlNode::internalPrint(printer);
+
+ NODE_PRINT(printer, name);
+ NODE_PRINT(printer, m_type);
+ NODE_PRINT(printer, m_expr);
+ NODE_PRINT(printer, m_isPrivate);
+
+ return "PackageReferenceNode";
+}
+
+DdlNode* CreatePackageConstantNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
+{
+ dsqlScratch->qualifyNewName(name);
+
+ m_expr = m_expr->dsqlPass(dsqlScratch);
- requestHandle.reset(tdbb, drq_l_pkg_procs, DYN_REQUESTS);
- requestHandle2.reset(tdbb, drq_l_pkg_proc_args, DYN_REQUESTS);
+ QualifiedName dummyCollationName;
+ DDL_resolve_intl_type(dsqlScratch, m_type, dummyCollationName);
- FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
- PRC IN RDB$PROCEDURES
- WITH PRC.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND
- PRC.RDB$PACKAGE_NAME EQ packageName.object.c_str()
+ if (!m_expr->constant())
+ {
+ status_exception::raise(Arg::Gds(isc_dyn_non_constant_constant) << name.toQuotedString());
+ }
+
+ DdlNode::dsqlPass(dsqlScratch);
+ return nullptr;
+}
+
+void CreatePackageConstantNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
+{
+ // 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))
{
- Signature procedure(PRC.RDB$PROCEDURE_NAME);
- procedure.defined = !PRC.RDB$PROCEDURE_BLR.NULL || !PRC.RDB$ENTRYPOINT.NULL;
+ return;
+ }
+
+ executeCreate(tdbb, dsqlScratch, transaction);
+ }
+ else
+ executeAlter(tdbb, dsqlScratch, transaction);
+
+ fb_assert(m_id);
+}
+
+void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction)
+{
+ FbLocalStatus status;
- if (details)
+ static const CachedRequestId requestId;
+ AutoCacheRequest storeConstantRequest(tdbb, requestId);
+
+ // Check uniqueness
+ fb_assert(package != nullptr);
+ if (package->findConstant(tdbb, 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, CONSTANTS_GENERATOR);
+ id %= (MAX_SSHORT + 1);
+ if (!id)
+ continue;
+
+ m_id = id;
+
+ // Store desc and metainfo in the RDB$CONSTANTS relation
+ STORE (REQUEST_HANDLE storeConstantRequest TRANSACTION_HANDLE transaction)
+ CONST IN RDB$CONSTANTS USING
{
- 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
+ // Constant name
+ CONST.RDB$CONSTANT_NAME.NULL = FALSE;
+ strcpy(CONST.RDB$CONSTANT_NAME, name.object.c_str());
+
+ // Constant unique id
+ CONST.RDB$CONSTANT_ID = id;
+
+ // Description (filed) name
+ CONST.RDB$FIELD_SOURCE.NULL = FALSE;
+ strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str());
+
+ // Gen value blr
+ ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema);
+
+ // Put the blr
+ 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
+ fb_assert(name.package.hasData());
{
- SignatureParameter parameter(*getDefaultMemoryPool());
- parameter.type = PRM.RDB$PARAMETER_TYPE;
- parameter.number = PRM.RDB$PARAMETER_NUMBER;
- parameter.name = PRM.RDB$PARAMETER_NAME;
- parameter.fieldSource = QualifiedName(PRM.RDB$FIELD_SOURCE, PRM.RDB$FIELD_SOURCE_SCHEMA_NAME);
- parameter.mechanism = PRM.RDB$PARAMETER_MECHANISM;
-
- if (!PRM.RDB$FIELD_NAME.NULL)
- parameter.fieldName = QualifiedName(PRM.RDB$FIELD_NAME);
- if (!PRM.RDB$RELATION_NAME.NULL)
- parameter.relationName = QualifiedName(PRM.RDB$RELATION_NAME, PRM.RDB$RELATION_SCHEMA_NAME);
- if (!PRM.RDB$COLLATION_ID.NULL)
- parameter.collationId = CollId(PRM.RDB$COLLATION_ID);
- if (!PRM.RDB$NULL_FLAG.NULL)
- parameter.nullFlag = PRM.RDB$NULL_FLAG;
-
- if (!FLD.RDB$FIELD_LENGTH.NULL)
- parameter.fieldLength = FLD.RDB$FIELD_LENGTH;
- if (!FLD.RDB$FIELD_SCALE.NULL)
- parameter.fieldScale = FLD.RDB$FIELD_SCALE;
- if (!FLD.RDB$FIELD_TYPE.NULL)
- parameter.fieldType = FLD.RDB$FIELD_TYPE;
- if (!FLD.RDB$FIELD_SUB_TYPE.NULL)
- parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE;
- if (!FLD.RDB$SEGMENT_LENGTH.NULL)
- parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH;
- if (!FLD.RDB$NULL_FLAG.NULL)
- parameter.fieldNullFlag = FLD.RDB$NULL_FLAG;
- if (!FLD.RDB$CHARACTER_LENGTH.NULL)
- parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH;
- if (!FLD.RDB$COLLATION_ID.NULL)
- parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID);
- if (!FLD.RDB$CHARACTER_SET_ID.NULL)
- parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID);
- if (!FLD.RDB$FIELD_PRECISION.NULL)
- parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION;
-
- procedure.parameters.add(parameter);
+ CONST.RDB$PACKAGE_NAME.NULL = FALSE;
+ strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str());
+
+ fb_assert(package);
+ package->addConstant(tdbb, m_id, name, m_isPrivate, m_type); // Do not cache blob id because the blob is not materialized
}
- END_FOR
+
+ // Schema of the parent package
+ CONST.RDB$SCHEMA_NAME.NULL = FALSE;
+ strcpy(CONST.RDB$SCHEMA_NAME, name.schema.c_str());
+
+ // Type
+ CONST.RDB$PRIVATE_FLAG.NULL = FALSE;
+ CONST.RDB$PRIVATE_FLAG = m_isPrivate;
+
+ CONST.RDB$CONSTANT_SOURCE.NULL = TRUE;
}
+ END_STORE
+ break;
+ }
+ catch (const status_exception& ex)
+ {
+ if (ex.value()[1] != isc_unique_key_violation)
+ throw;
+
+ if (++faults > MAX_SSHORT)
+ throw;
- procedures.add(procedure);
+ fb_utils::init_status(tdbb->tdbb_status_vector);
}
- END_FOR
- }
-} // namespace
+ }// While
+}
-//----------------------
+bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction)
+{
+ 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
+ CROSS PACKAGE IN RDB$PACKAGES
+ WITH PACKAGE.RDB$PACKAGE_NAME EQ name.package.c_str() AND
+ PACKAGE.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND
+ CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND
+ FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND
+ FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE
+ {
+ m_id = CONST.RDB$CONSTANT_ID;
+
+ dyn_fld origDom, newDom;
+ DSC_make_descriptor(&origDom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE,
+ FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE,
+ CSetId(FLD.RDB$CHARACTER_SET_ID),
+ CollId(FLD.RDB$COLLATION_ID));
+
+ origDom.dyn_fld_name = name;
+ origDom.dyn_charbytelen = FLD.RDB$FIELD_LENGTH;
+ origDom.dyn_dtype = FLD.RDB$FIELD_TYPE;
+ origDom.dyn_precision = FLD.RDB$FIELD_PRECISION;
+ origDom.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE;
+ origDom.dyn_charlen = FLD.RDB$CHARACTER_LENGTH;
+ origDom.dyn_collation = FLD.RDB$COLLATION_ID;
+ origDom.dyn_null_flag = !FLD.RDB$NULL_FLAG.NULL && FLD.RDB$NULL_FLAG != 0;
+ origDom.dyn_fld_source = QualifiedName(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME);
+
+ // If the original field type is an array, force its blr type to blr_blob
+ if (FLD.RDB$DIMENSIONS != 0)
+ origDom.dyn_dtype = blr_blob;
+
+ const bool wasInternalDomain = fb_utils::implicit_domain(origDom.dyn_fld_source.object.c_str());
+
+ QualifiedName newDomainName;
+
+ // We have the type. Default and type/domain are exclusive for now.
+ if (m_type->typeOfName.object.hasData())
+ {
+ // Case a1: Internal domain -> domain.
+ // Case a2: Domain -> domain.
+
+ newDomainName = m_type->typeOfName;
+
+ if (fb_utils::implicit_domain(newDomainName.object.c_str()))
+ {
+ // msg 224: "Cannot use the internal domain %s as new type for field %s".
+ status_exception::raise(
+ Arg::PrivateDyn(224) << newDomainName.toQuotedString() << m_type->fld_name);
+ }
+
+ // Get the domain information.
+ if (!METD_get_domain(dsqlScratch->getTransaction(), m_type, newDomainName))
+ {
+ // Specified domain or source field does not exist.
+ status_exception::raise(
+ Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
+ Arg::Gds(isc_dsql_command_err) <<
+ Arg::Gds(isc_dsql_domain_not_found) << newDomainName.toQuotedString());
+ }
+
+ QualifiedName dummyCollationName;
+ DDL_resolve_intl_type(dsqlScratch, m_type, dummyCollationName);
+
+ // If the original definition was a base field type, remove the
+ // entries from RDB$FIELDS.
+ if (wasInternalDomain)
+ {
+ // Case a1: Internal domain -> domain.
+ ERASE FLD;
+ }
+ }
+ else
+ {
+ // Case b1: Internal domain -> internal domain.
+ // Case b2: Domain -> internal domain.
+
+ m_type->resolve(dsqlScratch, true);
+
+ if (wasInternalDomain) // Case b1: Internal domain -> internal domain.
+ {
+ MODIFY FLD
+ updateRdbFields(m_type,
+ FLD.RDB$FIELD_TYPE,
+ FLD.RDB$FIELD_LENGTH,
+ FLD.RDB$FIELD_SUB_TYPE.NULL, FLD.RDB$FIELD_SUB_TYPE,
+ FLD.RDB$FIELD_SCALE.NULL, FLD.RDB$FIELD_SCALE,
+ FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID,
+ FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH,
+ FLD.RDB$FIELD_PRECISION.NULL, FLD.RDB$FIELD_PRECISION,
+ FLD.RDB$COLLATION_ID.NULL, FLD.RDB$COLLATION_ID,
+ FLD.RDB$SEGMENT_LENGTH.NULL, FLD.RDB$SEGMENT_LENGTH);
+ END_MODIFY
+
+ newDom.dyn_fld_source = origDom.dyn_fld_source;
+ }
+ else // Case b2: Domain -> internal domain.
+ storeGlobalField(tdbb, transaction, newDomainName, m_type);
+ }
+
+
+ if (newDomainName.object.hasData())
+ newDom.dyn_fld_source = newDomainName;
+
+ AlterDomainNode::getDomainType(tdbb, transaction, newDom);
+
+ if (newDom.dyn_dtype == blr_blob && newDomainName.object.isEmpty())
+ newDom.dyn_sub_type = m_type->subType;
+
+ // Gen the new constant value as blr
+ ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema);
+
+ // And write
+ blb* blob = blb::create(tdbb, transaction, &blobId);
+ blob->BLB_put_segment(tdbb, dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount());
+ blob->BLB_close(tdbb);
+
+ {
+ fb_assert(package);
+ // Do not cache blob id because the blob is not materialized
+ package->addConstant(tdbb, m_id, name, m_isPrivate, m_type);
+ }
+
+ MODIFY CONST USING
+ CONST.RDB$CONSTANT_BLR.NULL = FALSE;
+ CONST.RDB$CONSTANT_BLR = blobId;
+ END_MODIFY
+
+ found = true;
+ }
+ END_FOR
+
+ return found;
+}
string CreateAlterPackageNode::internalPrint(NodePrinter& printer) const
@@ -215,6 +848,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 +878,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 +891,34 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
break;
}
- case CreateAlterPackageNode::Item::PROCEDURE:
+ case PackageItemType::PROCEDURE:
{
CreateAlterProcedureNode* const proc = (*items)[i].procedure;
ddlNode = proc;
- if (procedureNames.exist(proc->name.object))
- {
- status_exception::raise(
- Arg::Gds(isc_no_meta_update) <<
- Arg::Gds(isc_dyn_duplicate_package_item) <<
- Arg::Str("PROCEDURE") << proc->name.object.toQuotedString());
- }
-
- procedureNames.add(proc->name.object);
+ procedureNames.addName(proc->name);
proc->alter = true;
proc->name.schema = name.schema;
proc->name.package = name.object;
break;
}
+ case PackageItemType::CONSTANT:
+ {
+ CreatePackageConstantNode* const constant = (*items)[i].constant;
+ ddlNode = constant;
+ constantNames.addName(constant->name);
+
+ constant->create = true; // Create a new constant
+ constant->alter = true; // Create a new constant
+ constant->name.schema = name.schema;
+ constant->name.package = name.object;
+ constant->makePublic();
+ break;
+ }
default:
+ ddlNode = nullptr; // Warning
fb_assert(false);
}
@@ -363,7 +995,7 @@ void CreateAlterPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlS
dsc schemaDesc, nameDesc;
schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str()));
nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str()));
- DFW_post_work(transaction, dfw_modify_package_header, &nameDesc, &schemaDesc, 0);
+ DFW_post_work(transaction, dfw_modify_package_header, &nameDesc, &schemaDesc, id);
}
else
executeCreate(tdbb, dsqlScratch, transaction);
@@ -385,41 +1017,69 @@ void CreateAlterPackageNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch*
DYN_UTIL_check_unique_name(tdbb, name, obj_package_header);
+ static const CachedRequestId requestId;
AutoCacheRequest requestHandle(tdbb, drq_s_pkg, DYN_REQUESTS);
- STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
- PKG IN RDB$PACKAGES USING
+ Cached::Package* package = nullptr;
+ ULONG faults = 0;
+ while (true)
{
- PKG.RDB$SCHEMA_NAME.NULL = FALSE;
- strcpy(PKG.RDB$SCHEMA_NAME, name.schema.c_str());
+ try
+ {
+ id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_package_id, PACKAGES_GENERATOR);
+ id %= (MAX_SSHORT + 1);
+ if (!id)
+ continue;
+
+ STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
+ PKG IN RDB$PACKAGES USING
+ {
+ PKG.RDB$SCHEMA_NAME.NULL = FALSE;
+ strcpy(PKG.RDB$SCHEMA_NAME, name.schema.c_str());
+
+ PKG.RDB$PACKAGE_NAME.NULL = FALSE;
+ strcpy(PKG.RDB$PACKAGE_NAME, name.object.c_str());
- PKG.RDB$PACKAGE_NAME.NULL = FALSE;
- strcpy(PKG.RDB$PACKAGE_NAME, name.object.c_str());
+ PKG.RDB$SYSTEM_FLAG.NULL = FALSE;
+ PKG.RDB$SYSTEM_FLAG = 0;
- PKG.RDB$SYSTEM_FLAG.NULL = FALSE;
- PKG.RDB$SYSTEM_FLAG = 0;
+ PKG.RDB$OWNER_NAME.NULL = FALSE;
+ strcpy(PKG.RDB$OWNER_NAME, ownerName.c_str());
- PKG.RDB$OWNER_NAME.NULL = FALSE;
- strcpy(PKG.RDB$OWNER_NAME, ownerName.c_str());
+ PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE;
+ attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, source);
- PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE;
- attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, source);
+ if (ssDefiner.has_value())
+ {
+ PKG.RDB$SQL_SECURITY.NULL = FALSE;
+ PKG.RDB$SQL_SECURITY = ssDefiner.value() == SqlSecurity::SS_DEFINER ? FB_TRUE : FB_FALSE;
+ }
+ else
+ PKG.RDB$SQL_SECURITY.NULL = TRUE;
- if (ssDefiner.has_value())
+ PKG.RDB$PACKAGE_ID = id;
+ }
+ END_STORE
+ break;
+ }
+ catch (const status_exception& ex)
{
- PKG.RDB$SQL_SECURITY.NULL = FALSE;
- PKG.RDB$SQL_SECURITY = ssDefiner.value() == SqlSecurity::SS_DEFINER ? FB_TRUE : FB_FALSE;
+ if (ex.value()[1] != isc_unique_key_violation)
+ throw;
+
+ if (++faults > MAX_SSHORT)
+ throw;
+
+ fb_utils::init_status(tdbb->tdbb_status_vector);
}
- else
- PKG.RDB$SQL_SECURITY.NULL = TRUE;
}
- END_STORE
storePrivileges(tdbb, transaction, name, obj_package_header, EXEC_PRIVILEGES);
owner = ownerName;
- executeItems(tdbb, dsqlScratch, transaction);
+ package = MetadataCache::newVersion(tdbb, id);
+ executeItems(tdbb, dsqlScratch, transaction, package->getVersioned(tdbb, CacheFlag::AUTOCREATE));
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE, name, {});
}
@@ -430,6 +1090,8 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch*
{
MemoryPool& pool = dsqlScratch->getPool();
Attachment* attachment = transaction->getAttachment();
+
+ static const CachedRequestId requestId;
AutoCacheRequest requestHandle(tdbb, drq_m_pkg, DYN_REQUESTS);
bool modified = false;
@@ -438,35 +1100,18 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch*
WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
PKG.RDB$PACKAGE_NAME EQ name.object.c_str()
{
+ id = PKG.RDB$PACKAGE_ID;
modified = true;
+ MetadataCache::oldVersion(tdbb, id, CacheFlag::OLD_ALTER);
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PACKAGE, name, {});
- SortedObjectsArray existingFuncs(pool);
- SortedObjectsArray existingProcs(pool);
- collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false);
+ PackageItemsHolder existingItems(pool);
+ existingItems.collectPackagedItems(tdbb, transaction, name, false, true);
- for (SortedObjectsArray::iterator i = existingFuncs.begin();
- i != existingFuncs.end(); ++i)
- {
- if (!functionNames.exist(i->name))
- {
- DropFunctionNode dropNode(pool, QualifiedName(i->name, name.schema, name.object));
- dropNode.dsqlPass(dsqlScratch);
- dropNode.executeDdl(tdbb, dsqlScratch, transaction, true);
- }
- }
-
- for (SortedObjectsArray::iterator i = existingProcs.begin();
- i != existingProcs.end(); ++i)
- {
- if (!procedureNames.exist(i->name))
- {
- DropProcedureNode dropNode(pool, QualifiedName(i->name, name.schema, name.object));
- dropNode.dsqlPass(dsqlScratch);
- dropNode.executeDdl(tdbb, dsqlScratch, transaction, true);
- }
- }
+ dropMissingItems(tdbb, dsqlScratch, name, existingItems.functions, functionNames);
+ dropMissingItems(tdbb, dsqlScratch, name, existingItems.procedures, procedureNames);
+ dropMissingItems(tdbb, dsqlScratch, name, existingItems.constants, constantNames);
MODIFY PKG
PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE;
@@ -490,13 +1135,14 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch*
dsc schemaDesc, nameDesc;
schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str()));
nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str()));
- DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, 0);
+ DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, id);
}
END_FOR
if (modified)
{
- executeItems(tdbb, dsqlScratch, transaction);
+ auto package = MetadataCache::newVersion(tdbb, id);
+ executeItems(tdbb, dsqlScratch, transaction, package->getVersioned(tdbb, CacheFlag::AUTOCREATE));
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_PACKAGE, name, {});
}
@@ -507,6 +1153,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;
@@ -541,21 +1188,25 @@ bool CreateAlterPackageNode::executeAlterIndividualParameters(thread_db* tdbb, D
}
void CreateAlterPackageNode::executeItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
- jrd_tra* transaction)
+ jrd_tra* transaction, Package* package)
{
for (unsigned i = 0; i < items->getCount(); ++i)
{
switch ((*items)[i].type)
{
- case Item::FUNCTION:
+ case PackageItemType::FUNCTION:
(*items)[i].function->packageOwner = owner;
(*items)[i].function->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true);
break;
- case Item::PROCEDURE:
+ case PackageItemType::PROCEDURE:
(*items)[i].procedure->packageOwner = owner;
(*items)[i].procedure->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true);
break;
+ case PackageItemType::CONSTANT:
+ (*items)[i].constant->package = package;
+ (*items)[i].constant->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true);
+ break;
}
}
}
@@ -589,14 +1240,19 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
AutoSavePoint savePoint(tdbb, transaction);
bool found = false;
+
+ static const CachedRequestId requestId;
AutoCacheRequest requestHandle(tdbb, drq_e_pkg, DYN_REQUESTS);
+ MetaId id{};
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
PKG IN RDB$PACKAGES
WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
PKG.RDB$PACKAGE_NAME EQ name.object.c_str()
{
found = true;
+ id = PKG.RDB$PACKAGE_ID;
+ MetadataCache::getVersioned(tdbb, id, CacheFlag::OLD_DROP);
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PACKAGE, name, {});
@@ -608,7 +1264,7 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
dsc schemaDesc, nameDesc;
schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str()));
nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str()));
- DFW_post_work(transaction, dfw_drop_package_header, &nameDesc, &schemaDesc, 0);
+ DFW_post_work(transaction, dfw_drop_package_header, &nameDesc, &schemaDesc, id);
}
END_FOR
@@ -619,25 +1275,9 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
Arg::Gds(isc_dyn_package_not_found) << name.toQuotedString());
}
- SortedObjectsArray existingFuncs(pool);
- SortedObjectsArray existingProcs(pool);
- collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false);
-
- for (SortedObjectsArray::iterator i = existingFuncs.begin();
- i != existingFuncs.end(); ++i)
- {
- DropFunctionNode dropNode(pool, QualifiedName(i->name, name.schema, name.object));
- dropNode.dsqlPass(dsqlScratch);
- dropNode.executeDdl(tdbb, dsqlScratch, transaction, true);
- }
-
- for (SortedObjectsArray::iterator i = existingProcs.begin();
- i != existingProcs.end(); ++i)
- {
- DropProcedureNode dropNode(pool, QualifiedName(i->name, name.schema, name.object));
- dropNode.dsqlPass(dsqlScratch);
- dropNode.executeDdl(tdbb, dsqlScratch, transaction, true);
- }
+ PackageItemsHolder existingItems(pool);
+ existingItems.collectPackagedItems(tdbb, transaction, name, false, true);
+ existingItems.drop(tdbb, dsqlScratch, name);
requestHandle.reset(tdbb, drq_e_pkg_prv, DYN_REQUESTS);
@@ -656,7 +1296,10 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
END_FOR
if (found)
+ {
+ MetadataCache::erase(tdbb, id);
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE, name, {});
+ }
savePoint.release(); // everything is ok
}
@@ -690,8 +1333,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 +1346,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 +1362,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 +1377,23 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
break;
}
+ case PackageItemType::CONSTANT:
+ {
+ CreatePackageConstantNode* const constant = (*arrays[i])[j].constant;
+ ddlNode = constant;
+ names[i].constants.addName(constant->name);
+
+ constant->name.schema = name.schema;
+ constant->name.package = name.object;
+ constant->create = true;
+
+ if (arrays[i] == items)
+ constant->alter = true;
+
+ constant->makePrivate();
+ break;
+ }
default:
fb_assert(false);
}
@@ -796,9 +1436,11 @@ 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;
+ Package* package = nullptr;
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
PKG IN RDB$PACKAGES
WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
@@ -814,6 +1456,7 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
Arg::Gds(isc_dyn_package_body_exists) << name.toQuotedString());
}
+ package = MetadataCache::newVersion(tdbb, PKG.RDB$PACKAGE_ID)->getVersioned(tdbb, CacheFlag::AUTOCREATE);
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PACKAGE_BODY, name, {});
MODIFY PKG
@@ -837,12 +1480,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 +1495,10 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
if (arrays[i] == items)
{
- existingFuncs.clear();
- existingProcs.clear();
+ existingItems.clear();
}
- collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, true);
+ existingItems.collectPackagedItems(tdbb, transaction, name, true, true);
for (unsigned j = 0; j < arrays[i]->getCount(); ++j)
{
@@ -866,91 +1506,67 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
switch (elem.type)
{
- case CreateAlterPackageNode::Item::FUNCTION:
+ case PackageItemType::FUNCTION:
{
CreateAlterFunctionNode* func = elem.function;
if (arrays[i] == items)
- func->privateScope = !headerFuncs.exist(Signature(func->name.object));
- else if (existingFuncs.exist(Signature(func->name.object)))
+ func->privateScope = !headerItems.functions.exist(Signature(func->name.object));
+ else
{
- status_exception::raise(
- Arg::Gds(isc_no_meta_update) <<
- Arg::Gds(isc_dyn_duplicate_package_item) <<
- Arg::Str("FUNCTION") << func->name.toQuotedString());
+ existingItems.functions.checkDuplicate(func->name);
}
func->packageOwner = owner;
func->preserveDefaults =
- existingFuncs.exist(Signature(func->name.object)) && arrays[i] == items;
+ existingItems.functions.exist(Signature(func->name.object)) && arrays[i] == items;
func->executeDdl(tdbb, elem.dsqlScratch, transaction, true);
break;
}
- case CreateAlterPackageNode::Item::PROCEDURE:
+ case PackageItemType::PROCEDURE:
{
CreateAlterProcedureNode* proc = elem.procedure;
if (arrays[i] == items)
- proc->privateScope = !headerProcs.exist(Signature(proc->name.object));
- else if (existingProcs.exist(Signature(proc->name.object)))
+ proc->privateScope = !headerItems.procedures.exist(Signature(proc->name.object));
+ else
{
- status_exception::raise(
- Arg::Gds(isc_no_meta_update) <<
- Arg::Gds(isc_dyn_duplicate_package_item) <<
- Arg::Str("PROCEDURE") << proc->name.toQuotedString());
+ existingItems.procedures.checkDuplicate(proc->name);
}
proc->packageOwner = owner;
proc->preserveDefaults =
- existingProcs.exist(Signature(proc->name.object)) && arrays[i] == items;
+ existingItems.procedures.exist(Signature(proc->name.object)) && arrays[i] == items;
proc->executeDdl(tdbb, elem.dsqlScratch, transaction, true);
break;
}
- }
- }
- }
+ case PackageItemType::CONSTANT:
+ {
+ CreatePackageConstantNode* constant = elem.constant;
- SortedObjectsArray newFuncs(pool);
- SortedObjectsArray newProcs(pool);
- collectPackagedItems(tdbb, transaction, name, newFuncs, newProcs, true);
+ headerItems.constants.checkDuplicate(constant->name);
+ existingItems.constants.checkDuplicate(constant->name);
- for (SortedObjectsArray::iterator i = existingFuncs.begin();
- i != existingFuncs.end(); ++i)
- {
- FB_SIZE_T pos;
- bool found = newFuncs.find(Signature(pool, i->name), pos);
-
- if (!found || !newFuncs[pos].defined)
- {
- status_exception::raise(
- Arg::Gds(isc_dyn_funcnotdef_package) << i->name << name.toQuotedString());
- }
- else if (newFuncs[pos] != *i)
- {
- status_exception::raise(
- Arg::Gds(isc_dyn_funcsignat_package) << i->name << name.toQuotedString());
+ fb_assert(package);
+ constant->package = package;
+ constant->executeDdl(tdbb, elem.dsqlScratch, transaction, true);
+ break;
+ }
+ }
}
}
- for (SortedObjectsArray::iterator i = existingProcs.begin();
- i != existingProcs.end(); ++i)
- {
- FB_SIZE_T pos;
- bool found = newProcs.find(Signature(pool, i->name), pos);
+ PackageItemsHolder newItems(pool);
+ newItems.collectPackagedItems(tdbb, transaction, name, true, false);
+ existingItems.checkDefineMatch(pool, name, newItems);
- if (!found || !newProcs[pos].defined)
- {
- status_exception::raise(
- Arg::Gds(isc_dyn_procnotdef_package) << i->name << name.toQuotedString());
- }
- else if (newProcs[pos] != *i)
- {
- status_exception::raise(
- Arg::Gds(isc_dyn_procsignat_package) << i->name << name.toQuotedString());
- }
+ {
+ dsc schemaDesc, nameDesc;
+ schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str()));
+ nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str()));
+ DFW_post_work(transaction, dfw_create_package, &nameDesc, &schemaDesc, package->getId());
}
-
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE_BODY, name, {});
savePoint.release(); // everything is ok
@@ -986,14 +1602,18 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
AutoSavePoint savePoint(tdbb, transaction);
bool found = false;
+
+ static const CachedRequestId requestId;
AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body2, DYN_REQUESTS);
+ MetaId id{};
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
PKG IN RDB$PACKAGES
WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
PKG.RDB$PACKAGE_NAME EQ name.object.c_str()
{
found = true;
+ id = PKG.RDB$PACKAGE_ID;
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PACKAGE_BODY, name, {});
@@ -1004,7 +1624,7 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
dsc schemaDesc, nameDesc;
schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str()));
nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str()));
- DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, 0);
+ DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, id);
END_MODIFY
}
END_FOR
@@ -1031,9 +1651,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 +1676,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 +1691,21 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
}
END_FOR
+ // Erase body constants
+ static const CachedRequestId eraseRequestId;
+ AutoCacheRequest eraseConstantRequest(tdbb, eraseRequestId);
+ FOR (REQUEST_HANDLE eraseConstantRequest TRANSACTION_HANDLE transaction)
+ CONST IN RDB$CONSTANTS
+ WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ CONST.RDB$PACKAGE_NAME EQ name.object.c_str() AND
+ CONST.RDB$PRIVATE_FLAG EQ true
+ {
+ ERASE CONST;
+ }
+ END_FOR
+
+ MetadataCache::newVersion(tdbb, id);
+
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE_BODY, name, {});
savePoint.release(); // everything is ok
diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h
index 839e3c8d0f6..947f7cb47f5 100644
--- a/src/dsql/PackageNodes.h
+++ b/src/dsql/PackageNodes.h
@@ -25,9 +25,194 @@
#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,
+ 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:
+ ConstantValue* m_prefetchedConstant = nullptr;
+ CachedResource m_package;
+ const QualifiedName m_fullName;
+
+ const UCHAR m_itemType;
+ ULONG m_impureOffset = 0;
+};
+
+
+class CreatePackageConstantNode final : public DdlNode
+{
+public:
+ CreatePackageConstantNode(Firebird::MemoryPool& pool, const MetaName& name,
+ dsql_fld* type = nullptr, ValueExprNode* value = nullptr, bool isPrivate = false)
+ : DdlNode(pool),
+ name(pool, name),
+ m_type(type),
+ m_expr(value),
+ m_isPrivate(isPrivate)
+ { }
+
+ Firebird::string internalPrint(NodePrinter& printer) const;
+ DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) final;
+ void checkPermission(thread_db* tdbb, jrd_tra* transaction) final;
+ void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) final;
+
+ inline void makePublic()
+ {
+ m_isPrivate = false;
+ }
+ inline void makePrivate()
+ {
+ m_isPrivate = true;
+ }
+
+private:
+ dsc* makeConstantValue(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, CompilerScratch*& nodeContext);
+ void executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
+ bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
+
+
+protected:
+ virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector)
+ {
+ statusVector <<
+ Firebird::Arg::Gds(createAlterCode(create, alter,
+ isc_dsql_create_const_failed, isc_dsql_alter_const_failed,
+ isc_dsql_create_alter_const_failed)) <<
+ Firebird::Arg::Str(name.toQuotedString());
+ }
+
+public:
+ QualifiedName name;
+ bool create = false;
+ bool alter = false;
+
+ Package* package = nullptr;
+
+private:
+ NestConst m_type;
+ NestConst m_expr;
+ MetaId m_id = 0;
+ bool m_isPrivate = false;
+};
+
class CreateAlterPackageNode : public DdlNode
{
@@ -37,36 +222,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 +271,7 @@ class CreateAlterPackageNode : public DdlNode
items(NULL),
functionNames(pool),
procedureNames(pool),
+ constantNames(pool),
owner(pool)
{
}
@@ -101,7 +296,7 @@ class CreateAlterPackageNode : public DdlNode
void executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
bool executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
- void executeItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
+ void executeItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, Package* package);
public:
QualifiedName name;
@@ -110,9 +305,11 @@ class CreateAlterPackageNode : public DdlNode
bool createIfNotExistsOnly = false;
Firebird::string source;
Firebird::Array- * items;
- Firebird::SortedArray functionNames;
- Firebird::SortedArray procedureNames;
+ ItemsNameArray functionNames;
+ ItemsNameArray procedureNames;
+ ItemsNameArray constantNames;
std::optional ssDefiner;
+ MetaId id;
private:
MetaName owner;
diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h
index c81cf09dff2..cf7acfdfa94 100644
--- a/src/dsql/dsql.h
+++ b/src/dsql/dsql.h
@@ -561,6 +561,7 @@ inline constexpr USHORT CTX_view_with_check_modify = 0x40; // Context of WITH C
inline constexpr USHORT CTX_cursor = 0x80; // Context is a cursor
inline constexpr USHORT CTX_lateral = 0x100; // Context is a lateral derived table
inline constexpr USHORT CTX_blr_fields = 0x200; // Fields of the context are defined inside BLR
+inline constexpr USHORT CTX_package = 0x400; // The context is related to a package
//! Aggregate/union map block to map virtual fields to their base
//! TMN: NOTE! This datatype should definitely be renamed!
diff --git a/src/dsql/parse.y b/src/dsql/parse.y
index a8b4786d33d..737644e9660 100644
--- a/src/dsql/parse.y
+++ b/src/dsql/parse.y
@@ -726,6 +726,7 @@ using namespace Firebird;
%token UNLIST
%token WITHIN
%token RDB_RESET_CONTEXT
+%token CONSTANT
// precedence declarations for expression evaluation
@@ -886,6 +887,7 @@ using namespace Firebird;
Jrd::SetBindNode* setBindNode;
Jrd::SessionResetNode* sessionResetNode;
Jrd::ForRangeNode::Direction forRangeDirection;
+ Jrd::CreatePackageConstantNode* createPackageConstantNode;
}
%include types.y
@@ -3175,6 +3177,8 @@ package_item
{ $$ = CreateAlterPackageNode::Item::create($2); }
| PROCEDURE procedure_clause_start ';'
{ $$ = CreateAlterPackageNode::Item::create($2); }
+ | CONSTANT package_const_item ';'
+ { $$ = CreateAlterPackageNode::Item::create($2); }
;
%type alter_package_clause
@@ -3262,6 +3266,13 @@ replace_package_body_clause
{ $$ = newNode($1); }
;
+%type package_const_item
+package_const_item
+ : symbol_package_const_name data_type_descriptor '=' value
+ {
+ $$ = newNode(*$1, $2, $4);
+ }
+ ;
%type replace_schema_clause
replace_schema_clause
@@ -6312,6 +6323,7 @@ ddl_type3
: PARAMETER { $$ = obj_parameter; }
| PROCEDURE PARAMETER { $$ = obj_procedure; }
| FUNCTION PARAMETER { $$ = obj_udf; }
+ | CONSTANT { $$ = obj_package_constant; }
;
%type ddl_type4
@@ -9866,6 +9878,11 @@ symbol_window_name
: valid_symbol_name
;
+%type symbol_package_const_name
+symbol_package_const_name
+ : valid_symbol_name
+ ;
+
// symbols
%type schema_opt_qualified_name
@@ -10186,6 +10203,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 d146ea1562e..3473ca517e6 100644
--- a/src/include/firebird/impl/msg/jrd.h
+++ b/src/include/firebird/impl/msg/jrd.h
@@ -1004,3 +1004,11 @@ FB_IMPL_MSG(JRD, 1001, sysf_argmustbe_range_inc0_1, -833, "42", "000", "Argument
FB_IMPL_MSG(JRD, 1002, argmustbe_numeric_function, -833, "42", "000", "Argument for @1 function must be numeric types")
FB_IMPL_MSG(JRD, 1003, percetile_only_one_sort_item, -833, "42", "000", "The PERCENTILE_DISC and PERENTILE_CONT functions support only one sort item in WITHIN GROUP")
FB_IMPL_MSG(JRD, 1004, argmustbe_const_within_group, -833, "42", "000", "Argument for @1 function must be constant within each group")
+FB_IMPL_MSG(JRD, 1005, not_defined_constant, -901, "42", "000", "The constant @1 is not defined in the package @2")
+FB_IMPL_MSG(JRD, 1006, const_name, -901, "42", "000", "CONSTANT @1")
+FB_IMPL_MSG(JRD, 1007, private_constant, -901, "42", "000", "The constant @1 is private")
+FB_IMPL_MSG(JRD, 1008, package_alias_help, -901, "42", "000", "Use an alias to resolve the conflict")
+FB_IMPL_MSG(JRD, 1009, bad_constant_blr, -901, "2F", "000", "Error while parsing BLR value of the constant @1")
+FB_IMPL_MSG(JRD, 1010, bad_constant_desc, -901, "2F", "000", "Error while reading type of the constant with name @1")
+FB_IMPL_MSG(JRD, 1011, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found")
+FB_IMPL_MSG(JRD, 1012, bad_constant_type, -901, "2F", "000", "@1 is not suppotred to be a constant type")
diff --git a/src/include/firebird/impl/msg/sqlerr.h b/src/include/firebird/impl/msg/sqlerr.h
index a2687461bcd..b2af71a0334 100644
--- a/src/include/firebird/impl/msg/sqlerr.h
+++ b/src/include/firebird/impl/msg/sqlerr.h
@@ -289,3 +289,6 @@ FB_IMPL_MSG(SQLERR, 1049, dsql_drop_schema_failed, -901, "42", "000", "DROP SCHE
FB_IMPL_MSG(SQLERR, 1050, dsql_recreate_schema_failed, -901, "42", "000", "RECREATE SCHEMA @1 failed")
FB_IMPL_MSG(SQLERR, 1051, dsql_alter_schema_failed, -901, "42", "000", "ALTER SCHEMA @1 failed")
FB_IMPL_MSG(SQLERR, 1052, dsql_create_alter_schema_failed, -901, "42", "000", "CREATE OR ALTER SCHEMA @1 failed")
+FB_IMPL_MSG(SQLERR, 1053, dsql_create_const_failed, -901, "42", "000", "CREATE CONSTANT @1 failed")
+FB_IMPL_MSG(SQLERR, 1054, dsql_alter_const_failed, -901, "42", "000", "ALTER CONSTANT @1 failed")
+FB_IMPL_MSG(SQLERR, 1055, dsql_create_alter_const_failed, -901, "42", "000", "CREATE OR ALTER CONSTANT @1 failed")
diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas
index 0efaf4e8965..77c0c8a6093 100644
--- a/src/include/gen/Firebird.pas
+++ b/src/include/gen/Firebird.pas
@@ -5959,6 +5959,14 @@ IPerformanceStatsImpl = class(IPerformanceStats)
isc_argmustbe_numeric_function = 335545322;
isc_percetile_only_one_sort_item = 335545323;
isc_argmustbe_const_within_group = 335545324;
+ isc_not_defined_constant = 335545325;
+ isc_const_name = 335545326;
+ isc_private_constant = 335545327;
+ isc_package_alias_help = 335545328;
+ isc_bad_constant_blr = 335545329;
+ isc_bad_constant_desc = 335545330;
+ isc_bad_constant_name = 335545331;
+ isc_bad_constant_type = 335545332;
isc_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930;
isc_gfix_incmp_sw = 335740932;
@@ -6223,6 +6231,10 @@ IPerformanceStatsImpl = class(IPerformanceStats)
isc_gbak_invalid_data = 336331094;
isc_gbak_inv_bkup_ver2 = 336331096;
isc_gbak_db_format_too_old2 = 336331100;
+ isc_gbak_writing_constants = 336331175;
+ isc_gbak_writing_constant = 336331176;
+ isc_gbak_constant = 336331177;
+ isc_gbak_restoring_constant = 336331178;
isc_dsql_too_old_ods = 336397205;
isc_dsql_table_not_found = 336397206;
isc_dsql_view_not_found = 336397207;
@@ -6359,6 +6371,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 4f6b4f9348e..63ab6640bc6 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,
@@ -6743,3 +6750,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/BlobUtil.cpp b/src/jrd/BlobUtil.cpp
index 18ca4a4270c..fd28ba7691c 100644
--- a/src/jrd/BlobUtil.cpp
+++ b/src/jrd/BlobUtil.cpp
@@ -286,6 +286,12 @@ BlobUtilPackage::BlobUtilPackage(Firebird::MemoryPool& pool)
},
{fld_varybinary_max, true}
)
+ },
+ // constants
+ {
+ SystemConstant(pool, "FROM_BEGIN", fld_integer, "0", {blr_literal, blr_short, 0, 0, 0}),
+ SystemConstant(pool, "FROM_CURRENT", fld_integer, "1", {blr_literal, blr_short, 0, 1, 0}),
+ SystemConstant(pool, "FROM_END", fld_integer, "2", {blr_literal, blr_short, 0, 2, 0})
}
)
{
diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp
new file mode 100644
index 00000000000..ef4e432e25e
--- /dev/null
+++ b/src/jrd/Package.epp
@@ -0,0 +1,511 @@
+/*
+ * PROGRAM: Firebird CONSTANTS implementation.
+ * MODULE: Package.epp
+ * DESCRIPTION: Routine to cache and reload package constants
+ *
+ * The contents of this file are subject to the Initial
+ * Developer's Public License Version 1.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
+ *
+ * Software distributed under the License is distributed AS IS,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights
+ * and limitations under the License.
+ *
+ * The Original Code was created by Artyom Abakumov
+ * for Red Soft Corporation.
+ *
+ * Copyright (c) 2025 Red Soft Corporation
+ * and all contributors signed below.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ */
+
+#include "firebird.h"
+#include "../jrd/Package.h"
+
+#include "../jrd/tra.h"
+#include "../jrd/exe_proto.h"
+#include "../jrd/dfw_proto.h"
+#include "../common/dsc_proto.h"
+#include "../jrd/met_proto.h"
+#include "../jrd/met.h"
+#include "../jrd/Statement.h" // Statement
+#include "../jrd/par_proto.h" // PAR_blr
+
+#include "../jrd/cvt_proto.h" // CVT_get_string_ptr
+#include "../jrd/mov_proto.h" // MOV_get_string_ptr
+#include "../common/classes/VaryStr.h"
+#include "../common/classes/alloc.h" // ALLOC_ARGS0
+#include "../dsql/make_proto.h" // DsqlDescMaker
+
+// I do not know why but the macros is undefined in CI
+#ifndef ALLOC_ARGS0
+#define ALLOC_ARGS0
+#endif
+
+using namespace Firebird;
+using namespace Jrd;
+
+DATABASE DB = FILENAME "ODS.RDB";
+
+//----------------------
+
+
+static dsc* executeConstantExpressionWithRequest(thread_db* tdbb, CompilerScratch* csb)
+{
+ Statement* statement = Statement::makeStatement(tdbb, csb, true);
+
+ Request* request = statement->makeRootRequest(tdbb);
+ {
+ Attachment* attachment = tdbb->getAttachment();
+ jrd_tra* transaction = tdbb->getTransaction();
+
+ tdbb->setRequest(request);
+ request->setUsed();
+ request->setAttachment(attachment);
+ attachment->att_requests.add(request);
+
+ TRA_attach_request(transaction, request);
+ }
+
+
+ { // Execute constant expr
+ ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output
+ return EVL_expr(tdbb, request, valueNode);
+ }
+}
+
+static void executeConstantExpression(thread_db* tdbb, CompilerScratch* csb, MemoryPool& pool, impure_value& value)
+{
+ Statement* statement = Statement::makeStatement(tdbb, csb, true);
+
+ Request* request = statement->makeRootRequest(tdbb);
+
+ const dsc* temp = EVL_expr(tdbb, request, static_cast(csb->csb_node));
+
+ EVL_make_value(tdbb, temp, &value, &pool);
+ statement->release(tdbb);
+}
+
+
+// Convert a literalNode-unsupported constant type to a supported one if necessary
+static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const dsc& scalar)
+{
+ // Make the blr with only the LiteralNode
+ {
+ BlrWriter::BlrData& blr = dsqlScratch->getBlrData();
+ blr.clear();
+ dsqlScratch->getDebugData().clear();
+ }
+
+ dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5);
+ // Convert a literalNode-unsupported constant type to a supported one if necessary
+ switch (scalar.dsc_dtype)
+ {
+ case dtype_varying:
+ case dtype_cstring:
+ {
+ // Convert to dtype_text
+ TTypeId ttype;
+ UCHAR* ptr;
+ auto& status = tdbb->getAttachment()->att_dec_status;
+ const USHORT len = CVT_get_string_ptr(&scalar, &ttype, &ptr, nullptr, 0, status);
+
+ dsc text;
+ text.makeText(len, ttype, ptr);
+ LiteralNode::genConstant(dsqlScratch, &text, false);
+ break;
+ }
+ case dtype_real:
+ {
+ double newValue = *(float*) scalar.dsc_address;
+
+ dsc descForDouble{};
+ descForDouble.makeDouble();
+
+ UCHAR* ptr;
+ VaryStr temp;
+ TTypeId ttype;
+ ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp));
+
+ descForDouble.dsc_address = ptr;
+
+ LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len);
+ break;
+ }
+ case dtype_dec64: // dtype_dec64 is not present in LiteralNode::genConstant
+ case dtype_double: // MOV_move stores double in scalar->dsc_address. Convert it to string to use genConstant
+ case dtype_dec128:
+ {
+ dsc descForDouble{};
+ descForDouble.makeDecimal128();
+
+ UCHAR* ptr;
+ VaryStr temp;
+ TTypeId ttype;
+ ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp));
+
+ descForDouble.dsc_address = ptr;
+
+ LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len);
+ break;
+ }
+ case dtype_int128:
+ {
+ dsc descForInt128{};
+ descForInt128.makeInt128(scalar.dsc_scale);
+
+ UCHAR* ptr;
+ VaryStr temp;
+ TTypeId ttype;
+ ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp));
+
+ descForInt128.dsc_address = ptr;
+
+ LiteralNode::genConstant(dsqlScratch, &descForInt128, false, len);
+ break;
+ }
+ case dtype_blob: // Blob ID will be lost
+ status_exception::raise(Arg::Gds(isc_bad_constant_type) << scalar.typeToText());
+ break;
+ default:
+ LiteralNode::genConstant(dsqlScratch, &scalar, false);
+ break;
+ }
+ dsqlScratch->appendUChar(blr_eoc);
+}
+
+bool ConstantValue::hash(thread_db* tdbb, Firebird::sha512& digest) const
+{
+ fb_assert(value.vlu_desc.dsc_dtype != 0);
+ digest.process(sizeof(value.vlu_desc), &value.vlu_desc);
+
+ return true;
+}
+
+dsc ConstantValue::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name)
+{
+ dsc desc{};
+ bool found = false;
+
+ FbLocalStatus status;
+ static const CachedRequestId requestId;
+ AutoCacheRequest getConstantDscRequest(tdbb, requestId);
+ FOR(REQUEST_HANDLE getConstantDscRequest TRANSACTION_HANDLE transaction)
+ CONST IN RDB$CONSTANTS CROSS FLD IN RDB$FIELDS
+ WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND
+ CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND
+ FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND
+ FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE
+ {
+ found = true;
+
+ const bool succeed = DSC_make_descriptor(&desc,
+ FLD.RDB$FIELD_TYPE,
+ FLD.RDB$FIELD_SCALE,
+ FLD.RDB$FIELD_LENGTH,
+ FLD.RDB$FIELD_SUB_TYPE,
+ CSetId(FLD.RDB$CHARACTER_SET_ID),
+ CollId(FLD.RDB$COLLATION_ID));
+
+ if (!succeed)
+ (Arg::Gds(isc_bad_constant_desc) << Arg::Str(name.toQuotedString())).raise();
+ }
+
+ END_FOR
+
+ if (!found)
+ (Arg::Gds(isc_bad_constant_name) << Arg::Str(name.toQuotedString())).raise();
+
+ return desc;
+}
+
+void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
+ ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema)
+{
+ { // Prepare BLR writer
+ BlrWriter::BlrData& blr = dsqlScratch->getBlrData();
+ blr.clear();
+ dsqlScratch->getDebugData().clear();
+ }
+
+ // Gen blr into dsqlScratch
+ AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0));
+ CastNode cast(*tempPool, constExpr, type);
+ {
+ dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5);
+ cast.genBlr(dsqlScratch);
+ dsqlScratch->appendUChar(blr_eoc);
+ }
+
+ Attachment* attachment = tdbb->getAttachment();
+ MemoryPool* csb_pool = attachment->att_database->createPool(ALLOC_ARGS0);
+ ContextPoolHolder context(tdbb, csb_pool);
+
+ CompilerScratch* csb = nullptr;
+ Cleanup cc([&tdbb, requestToRestore = tdbb->getRequest(), &csb]()
+ {
+ delete csb;
+ tdbb->setRequest(requestToRestore);
+ });
+
+ // Parse BLR for constant expression
+ PAR_blr(tdbb, &schema, nullptr,
+ dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount(),
+ nullptr, &csb,
+ nullptr, false, 0);
+
+ // Execute node from BLR
+ auto output = executeConstantExpressionWithRequest(tdbb, csb);
+ if (output != nullptr)
+ genConstantCompatibleBlr(tdbb, dsqlScratch, *output);
+}
+
+inline bid getConstantBid(thread_db* tdbb, const MetaId id)
+{
+ Attachment* attachment = tdbb->getAttachment();
+ jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb);
+
+ static const CachedRequestId requestId;
+ AutoCacheRequest requestConst(tdbb, requestId);
+
+ FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction)
+ CONST IN RDB$CONSTANTS
+ WITH CONST.RDB$CONSTANT_ID EQ id
+ {
+ return CONST.RDB$CONSTANT_BLR;
+ }
+ END_FOR
+
+ return {};
+}
+
+dsc& ConstantValue::makeValue(thread_db* tdbb)
+{
+ if (value.vlu_desc.dsc_address != nullptr)
+ return value.vlu_desc;
+
+ Attachment* attachment = tdbb->getAttachment();
+
+ MemoryPool* csb_pool = nullptr;
+ try
+ {
+ csb_pool = attachment->att_database->createPool(ALLOC_ARGS0);
+ ContextPoolHolder context(tdbb, csb_pool);
+
+ try
+ {
+ CompilerScratch* csb = nullptr;
+ Cleanup cc([csb]() {delete csb;});
+
+ { // blr
+ // Use cached ID or get a real one (is it probably materialized)
+ bid constantBid = blrBlobId.isEmpty() ? getConstantBid(tdbb, id) : blrBlobId;
+
+ MET_parse_blob(tdbb, &name.schema, nullptr, &constantBid, &csb, nullptr, false, false);
+
+ fb_assert(csb != nullptr);
+ }
+
+ executeConstantExpression(tdbb, csb, getPool(), value);
+ }
+ catch (const Exception& ex)
+ {
+ StaticStatusVector temp_status;
+ ex.stuffException(temp_status);
+
+ const string quotedName = name.toQuotedString();
+ (Arg::Gds(isc_bad_constant_blr) << Arg::Str(quotedName)
+ << Arg::StatusVector(temp_status.begin())).raise();
+ }
+ }
+ catch (const Exception&)
+ {
+ attachment->att_database->deletePool(csb_pool);
+ throw;
+ }
+
+ return value.vlu_desc;
+}
+
+ConstantValue& ConstantsCache::add(const MetaId constId, const QualifiedName& constName, const bool isPrivate)
+{
+ const ULONG id = values.getCount();
+
+ auto& value = values.add();
+ value.name = constName;
+ value.id = constId;
+ value.isPrivate = isPrivate;
+
+ idMap.put(constId, id);
+ nameMap.put(constName, id);
+
+ return value;
+}
+
+
+std::optional Package::getIdByName(thread_db* tdbb, const QualifiedName& name)
+{
+ fb_assert(name.package.isEmpty());
+
+ SET_TDBB(tdbb);
+ Attachment* attachment = tdbb->getAttachment();
+ std::optional id;
+
+ static const CachedRequestId requestId;
+ AutoCacheRequest request(tdbb, requestId);
+
+ FOR (REQUEST_HANDLE request)
+ PKG IN RDB$PACKAGES
+ WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ PKG.RDB$PACKAGE_NAME EQ name.object.c_str()
+ {
+ id = PKG.RDB$PACKAGE_ID;
+ }
+ END_FOR
+
+ return id;
+}
+
+ScanResult Package::reload(thread_db* tdbb, ObjectBase::Flag fl)
+{
+ Attachment* attachment = tdbb->getAttachment();
+ jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb);
+ Database* dbb = tdbb->getDatabase();
+
+ MemoryPool& pool = getPermanent()->getPool();
+
+ static const CachedRequestId requestId;
+ AutoCacheRequest requestConst(tdbb, requestId);
+
+ constants.clear();
+ FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction)
+ PKG IN RDB$PACKAGES CROSS CONST IN RDB$CONSTANTS
+ WITH PKG.RDB$PACKAGE_ID EQ getId() AND
+ PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME
+ {
+ addConstant(tdbb, CONST.RDB$CONSTANT_ID,
+ QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME),
+ CONST.RDB$PRIVATE_FLAG,
+ CONST.RDB$CONSTANT_BLR);
+ }
+ END_FOR
+
+ return ScanResult::COMPLETE;
+}
+
+Package* Package::create(thread_db* tdbb, MemoryPool& pool, Cached::Package* perm)
+{
+ return FB_NEW_POOL(perm->getPool()) Package(perm);
+}
+
+ScanResult Package::scan(thread_db* tdbb, ObjectBase::Flag flags)
+{
+ const bool skipMakeValue = (flags & CacheFlag::MINISCAN) != 0;
+
+ Attachment* attachment = tdbb->getAttachment();
+ jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb);
+ Database* dbb = tdbb->getDatabase();
+
+ MemoryPool& pool = getPermanent()->getPool();
+
+ static const CachedRequestId requestId;
+ AutoCacheRequest requestConst(tdbb, requestId);
+
+ FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction)
+ PKG IN RDB$PACKAGES CROSS CONST IN RDB$CONSTANTS
+ WITH PKG.RDB$PACKAGE_ID EQ getPermanent()->id AND
+ PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME
+ {
+ addConstant(tdbb, CONST.RDB$CONSTANT_ID,
+ QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME),
+ CONST.RDB$PRIVATE_FLAG,
+ CONST.RDB$CONSTANT_BLR,
+ skipMakeValue);
+
+ if (skipMakeValue)
+ this->m_callReload = true; // Call makeValue in reload
+ }
+ END_FOR
+
+ return this->m_callReload ? ScanResult::REPEAT : ScanResult::COMPLETE;
+}
+
+void Package::checkReload(thread_db* tdbb)
+{
+ if (m_callReload)
+ {
+ reload(tdbb, 0);
+ m_callReload = false;
+ }
+}
+
+int Package::objectType()
+{
+ return obj_package_header;
+}
+
+bool Package::hash(thread_db* tdbb, Firebird::sha512& digest)
+{
+ for (auto& constant : constants.values)
+ constant.hash(tdbb, digest);
+
+ return true;
+}
+
+ConstantValue& Package::addConstant(thread_db* tdbb, const MetaId constId,
+ const QualifiedName& constName,
+ const bool isPrivate,
+ const TypeClause* type)
+{
+ dsc typeDesc;
+ DsqlDescMaker::fromField(&typeDesc, type);
+
+ auto& value = constants.add(constId, constName, isPrivate);
+ value.blrBlobId = {};
+ value.value.vlu_desc = typeDesc;
+
+ return value;
+}
+
+ConstantValue& Package::addConstant(thread_db* tdbb, const MetaId constId,
+ const QualifiedName& constName,
+ const bool isPrivate,
+ const bid blrBlobId,
+ const bool skipMakeValue)
+{
+ auto& value = constants.add(constId, constName, isPrivate);
+ value.blrBlobId = blrBlobId;
+ if (!skipMakeValue)
+ value.makeValue(tdbb);
+
+ return value;
+}
+
+ConstantValue* Package::findConstant(thread_db* tdbb, const MetaId id)
+{
+ auto it = constants.idMap.get(id);
+
+ if (it != nullptr)
+ return &constants.values[*it];
+
+ return nullptr;
+}
+
+ConstantValue* Package::findConstant(thread_db* tdbb, QualifiedName name)
+{
+ if (name.schema.isEmpty())
+ name.schema = PUBLIC_SCHEMA;
+
+ auto it = constants.nameMap.get(name);
+
+ if (it != nullptr)
+ return &constants.values[*it];
+
+ return nullptr;
+}
diff --git a/src/jrd/Package.h b/src/jrd/Package.h
new file mode 100644
index 00000000000..c4915602287
--- /dev/null
+++ b/src/jrd/Package.h
@@ -0,0 +1,233 @@
+/*
+ * PROGRAM: Firebird CONSTANTS implementation.
+ * MODULE: Package.h
+ * DESCRIPTION: Routine to cache and reload Package constants
+ *
+ * The contents of this file are subject to the Initial
+ * Developer's Public License Version 1.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
+ *
+ * Software distributed under the License is distributed AS IS,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights
+ * and limitations under the License.
+ *
+ * The Original Code was created by Artyom Abakumov
+ * for Red Soft Corporation.
+ *
+ * Copyright (c) 2025 Red Soft Corporation
+ * and all contributors signed below.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ */
+
+
+#ifndef JRD_CONSTANT_H
+#define JRD_CONSTANT_H
+
+#include "firebird.h"
+#include "../jrd/CacheVector.h"
+#include "../jrd/Resources.h"
+#include "../jrd/obj.h"
+#include "../jrd/val.h"
+#include "../jrd/lck.h"
+#include "../common/classes/GenericMap.h"
+
+namespace Jrd
+{
+class DsqlCompilerScratch;
+class dsql_fld;
+
+class ConstantValue final : public Firebird::PermanentStorage
+{
+public:
+ ConstantValue(MemoryPool& pool) :
+ Firebird::PermanentStorage(pool),
+ name(pool)
+ { }
+
+ MetaId id{};
+ QualifiedName name;
+
+ // Keep type to gen hash (when not commited - we cannot read it from system table)
+ // Keep value when scanning and after the first execution
+ impure_value value{};
+
+ // keep only materialized value
+ bid blrBlobId{};
+
+ bool isPrivate = false;
+
+ bool hash(thread_db* tdbb, Firebird::sha512& digest) const;
+
+ static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name);
+ static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const MetaId id);
+
+ static void genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
+ ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema);
+
+ dsc& makeValue(thread_db* tdbb);
+
+ ~ConstantValue()
+ {
+ delete value.vlu_string;
+ }
+};
+
+struct ConstantsCache
+{
+ using ValueId = ULONG;
+
+ ConstantsCache(MemoryPool& pool) :
+ idMap(pool),
+ nameMap(pool),
+ values(pool)
+ { }
+
+ Firebird::NonPooledMap idMap;
+ Firebird::LeftPooledMap nameMap;
+
+ Firebird::ObjectsArray values;
+
+ ConstantValue& add(const MetaId constId, const QualifiedName& constName, const bool isPrivate);
+
+ void clear()
+ {
+ idMap.clear();
+ nameMap.clear();
+ values.clear();
+ }
+};
+
+
+class PackagePermanent : public Firebird::PermanentStorage
+{
+public:
+ explicit PackagePermanent(thread_db* tdbb, MemoryPool& p, MetaId metaId, NoData)
+ : PermanentStorage(p),
+ id(metaId),
+ name(p)
+ { }
+
+ explicit PackagePermanent(MemoryPool& p)
+ : PermanentStorage(p),
+ id(~0),
+ name(p)
+ { }
+
+ MetaId getId() const
+ {
+ return id;
+ }
+
+ static bool destroy(thread_db* tdbb, PackagePermanent* routine)
+ {
+ return false;
+ }
+
+ void releaseLock(thread_db*) { }
+
+ const QualifiedName& getName() const noexcept { return name; }
+ void setName(const QualifiedName& value) { name = value; }
+
+ bool hasData() const { return name.hasData(); }
+
+public:
+ MetaId id; // routine ID
+ QualifiedName name; // routine name
+};
+
+class Package final : public Firebird::PermanentStorage, public ObjectBase
+{
+public:
+ // lock requeued by CacheElement
+ static const enum lck_t LOCKTYPE = LCK_package_rescan;
+
+private:
+ explicit Package(Cached::Package* perm)
+ : Firebird::PermanentStorage(perm->getPool()),
+ constants(perm->getPool()),
+ cachedPackage(perm)
+ { }
+
+public:
+ explicit Package(MemoryPool& p)
+ : Firebird::PermanentStorage(p),
+ constants(p)
+ { }
+
+ // Methods needed by the MetaCache
+ // ----------
+
+ static bool destroy(thread_db* tdbb, Package* routine)
+ {
+ return false;
+ }
+
+ static Package* create(thread_db* tdbb, MemoryPool& pool, Cached::Package* perm);
+ static std::optional getIdByName(thread_db* tdbb, const QualifiedName& name);
+
+ ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags);
+ void checkReload(thread_db* tdbb);
+ ScanResult reload(thread_db* tdbb, ObjectBase::Flag flags);
+
+ static const char* objectFamily(void*)
+ {
+ return "package";
+ }
+
+ MetaId getId() const
+ {
+ return getPermanent()->id;
+ }
+
+ int getObjectType() const noexcept
+ {
+ return objectType();
+ }
+
+ SLONG getSclType() const noexcept
+ {
+ return obj_package_header;
+ }
+
+ static int objectType();
+
+ bool hash(thread_db* tdbb, Firebird::sha512& digest);
+
+ Cached::Package* getPermanent() const noexcept
+ {
+ return cachedPackage;
+ }
+
+ // ----------
+
+ ConstantValue& addConstant(thread_db* tdbb, const MetaId constId,
+ const QualifiedName& constName,
+ const bool isPrivate,
+ const TypeClause* type);
+
+ ConstantValue& addConstant(thread_db* tdbb, const MetaId constId,
+ const QualifiedName& constName,
+ const bool isPrivate,
+ const bid blrBlobId,
+ const bool skipMakeValue = false);
+
+ ConstantValue* findConstant(thread_db* tdbb, const MetaId id);
+ ConstantValue* findConstant(thread_db* tdbb, QualifiedName name);
+
+private:
+ virtual ~Package() = default;
+
+private:
+ ConstantsCache constants;
+ Cached::Package* cachedPackage = nullptr; // entry in the cache
+ bool m_callReload = true;
+};
+
+} // namespace Jrd
+
+#endif // JRD_CONSTANT_H
diff --git a/src/jrd/Resources.cpp b/src/jrd/Resources.cpp
index 99962ee616d..36e0e5a895d 100644
--- a/src/jrd/Resources.cpp
+++ b/src/jrd/Resources.cpp
@@ -32,6 +32,7 @@ void Resources::transfer(thread_db* tdbb, VersionedObjects* to, bool internal)
gotHash += functions.transfer(tdbb, to, internal, digest);
gotHash += triggers.transfer(tdbb, to, internal, digest);
gotHash += indices.transfer(tdbb, to, internal, digest);
+ gotHash += packages.transfer(tdbb, to, internal, digest);
if (hasHash)
{
diff --git a/src/jrd/Resources.h b/src/jrd/Resources.h
index cb7f2f9276d..a181bdb2611 100644
--- a/src/jrd/Resources.h
+++ b/src/jrd/Resources.h
@@ -42,6 +42,8 @@ class DbTriggers;
class CharSetVers;
class IndexPermanent;
class IndexVersion;
+class Package;
+class PackagePermanent;
namespace Cached
{
@@ -52,6 +54,7 @@ namespace Cached
typedef CacheElement Function;
typedef CacheElement Triggers;
typedef CacheElement Index;
+ typedef CacheElement Package;
}
class Resources;
@@ -66,6 +69,7 @@ union VersionedPartPtr
CharSetVers* charset;
DbTriggers* triggers;
IndexVersion* index;
+ Package* package;
};
class VersionedObjects : public pool_alloc_rpt,
@@ -120,6 +124,7 @@ template <> inline jrd_rel*& VersionedObjects::object(FB_SIZE_T n) { re
template <> inline CharSetVers*& VersionedObjects::object(FB_SIZE_T n) { return data[n].charset; }
template <> inline DbTriggers*& VersionedObjects::object(FB_SIZE_T n) { return data[n].triggers; }
template <> inline IndexVersion*& VersionedObjects::object(FB_SIZE_T n) { return data[n].index; }
+template <> inline Package*& VersionedObjects::object(FB_SIZE_T n) { return data[n].package; }
template <> inline Function* VersionedObjects::object(FB_SIZE_T n) const { return data[n].function; }
template <> inline jrd_prc* VersionedObjects::object(FB_SIZE_T n) const { return data[n].procedure; }
@@ -127,6 +132,7 @@ template <> inline jrd_rel* VersionedObjects::object(FB_SIZE_T n) const
template <> inline CharSetVers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].charset; }
template <> inline DbTriggers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].triggers; }
template <> inline IndexVersion* VersionedObjects::object(FB_SIZE_T n) const { return data[n].index; }
+template <> inline Package* VersionedObjects::object(FB_SIZE_T n) const { return data[n].package; }
template
@@ -283,7 +289,8 @@ class Resources final
procedures(p, versionCurrentPosition),
functions(p, versionCurrentPosition),
triggers(p, versionCurrentPosition),
- indices(p, versionCurrentPosition)
+ indices(p, versionCurrentPosition),
+ packages(p, versionCurrentPosition)
{ }
~Resources();
@@ -294,6 +301,13 @@ class Resources final
RscArray functions;
RscArray triggers;
RscArray indices;
+ RscArray packages;
+
+ inline FB_SIZE_T countVersionedObjects() const noexcept
+ {
+ return charSets.getCount() + relations.getCount() + procedures.getCount() +
+ functions.getCount() + triggers.getCount() + packages.getCount();
+ }
};
// specialization
@@ -303,6 +317,7 @@ template <> inline const Resources::RscArray& Resour
template <> inline const Resources::RscArray& Resources::objects() const { return charSets; }
template <> inline const Resources::RscArray& Resources::objects() const { return triggers; }
template <> inline const Resources::RscArray& Resources::objects() const { return indices; }
+template <> inline const Resources::RscArray& Resources::objects() const { return packages; }
namespace Rsc
{
@@ -312,6 +327,7 @@ namespace Rsc
typedef CachedResource CSet;
typedef CachedResource Trig;
typedef CachedResource Idx;
+ typedef CachedResource Package;
}; //namespace Rsc
diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp
index bf5418ee4b0..525950ec9e4 100644
--- a/src/jrd/Statement.cpp
+++ b/src/jrd/Statement.cpp
@@ -223,9 +223,7 @@ void Statement::loadResources(thread_db* tdbb, Request* req, bool withLock)
if (ddl || (!latestVer) || (latestVer->version != frontVersion))
{
- const FB_SIZE_T resourceCount = latestVer ? latestVer->getCapacity() :
- resources->charSets.getCount() + resources->relations.getCount() + resources->procedures.getCount() +
- resources->functions.getCount() + resources->triggers.getCount();
+ const FB_SIZE_T resourceCount = latestVer ? latestVer->getCapacity() : resources->countVersionedObjects();
AutoPtr newVer = FB_NEW_RPT(*pool, resourceCount) VersionedObjects(resourceCount);
MetadataCache::Version ver(mdc);
diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp
index 4c216b68031..63c22df4273 100644
--- a/src/jrd/SysFunction.cpp
+++ b/src/jrd/SysFunction.cpp
@@ -6977,98 +6977,99 @@ dsc* evlUnicodeVal(thread_db* tdbb, const SysFunction*, const NestValueArray& ar
+constexpr static auto DEFAULT = SysFunction::DETERMINISTIC | SysFunction::CONSTANT;
const SysFunction SysFunction::functions[] =
{
- // name, minArgCount, maxArgCount, deterministic, setParamsFunc, makeFunc, evlFunc, misc
-
- {"ABS", 1, 1, true, setParamsDblDec, makeAbs, evlAbs, NULL},
- {"ACOS", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcos},
- {"ACOSH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcosh},
- {"ASCII_CHAR", 1, 1, true, setParamsInteger, makeAsciiChar, evlAsciiChar, NULL},
- {"ASCII_VAL", 1, 1, true, setParamsAsciiVal, makeShortResult, evlAsciiVal, NULL},
- {"ASIN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsin},
- {"ASINH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsinh},
- {"ATAN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtan},
- {"ATANH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtanh},
- {"ATAN2", 2, 2, true, setParamsDouble, makeDoubleResult, evlAtan2, NULL},
- {"BASE64_DECODE", 1, 1, true, NULL, makeDecode64, evlDecode64, NULL},
- {"BASE64_ENCODE", 1, 1, true, NULL, makeEncode64, evlEncode64, NULL},
- {"BIN_AND", 2, -1, true, setParamsBin, makeBin, evlBin, (void*) funBinAnd},
- {"BIN_NOT", 1, 1, true, setParamsBin, makeBin, evlBin, (void*) funBinNot},
- {"BIN_OR", 2, -1, true, setParamsBin, makeBin, evlBin, (void*) funBinOr},
- {"BIN_SHL", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShl},
- {"BIN_SHR", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShr},
- {"BIN_SHL_ROT", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShlRot},
- {"BIN_SHR_ROT", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShrRot},
- {"BIN_XOR", 2, -1, true, setParamsBin, makeBin, evlBin, (void*) funBinXor},
- {"BLOB_APPEND", 2, -1, true, setParamsBlobAppend, makeBlobAppend, evlBlobAppend, NULL},
- {"CEIL", 1, 1, true, setParamsDblDec, makeCeilFloor, evlCeil, NULL},
- {"CEILING", 1, 1, true, setParamsDblDec, makeCeilFloor, evlCeil, NULL},
- {"CHAR_TO_UUID", 1, 1, true, setParamsCharToUuid, makeUuid, evlCharToUuid, NULL},
- {"COMPARE_DECFLOAT", 2, 2, true, setParamsDecFloat, makeShortResult, evlCompare, (void*) funCmpDec},
- {"COS", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCos},
- {"COSH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCosh},
- {"COT", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCot},
- {"CRYPT_HASH", 2, 2, true, setParamsHash, makeHash, evlHash, NULL},
- {"DATEADD", 3, 3, true, setParamsDateAdd, makeDateAdd, evlDateAdd, NULL},
- {"DATEDIFF", 3, 3, true, setParamsDateDiff, makeDateDiff, evlDateDiff, NULL},
- {"DECRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, true, setParamsEncrypt, makeCrypt, evlDecrypt, NULL},
- {"ENCRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, true, setParamsEncrypt, makeCrypt, evlEncrypt, NULL},
- {"EXP", 1, 1, true, setParamsDblDec, makeDblDecResult, evlExp, NULL},
- {"FIRST_DAY", 2, 2, true, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay},
- {"FLOOR", 1, 1, true, setParamsDblDec, makeCeilFloor, evlFloor, NULL},
- {"GEN_UUID", 0, 1, false, NULL, makeUuid, evlGenUuid, NULL},
- {"GREATEST", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue},
- {"HASH", 1, 2, true, setParamsHash, makeHash, evlHash, NULL},
- {"HEX_DECODE", 1, 1, true, NULL, makeDecodeHex, evlDecodeHex, NULL},
- {"HEX_ENCODE", 1, 1, true, NULL, makeEncodeHex, evlEncodeHex, NULL},
- {"LAST_DAY", 2, 2, true, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funLastDay},
- {"LEAST", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue},
- {"LEFT", 2, 2, true, setParamsSecondInteger, makeLeftRight, evlLeft, NULL},
- {"LN", 1, 1, true, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLnat},
- {"LOG", 2, 2, true, setParamsDblDec, makeDblDecResult, evlLog, NULL},
- {"LOG10", 1, 1, true, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLog10},
- {"LPAD", 2, 3, true, setParamsSecondInteger, makePad, evlPad, (void*) funLPad},
- {"MAKE_DBKEY", 2, 4, true, setParamsMakeDbkey, makeDbkeyResult, evlMakeDbkey, NULL},
- {"MAXVALUE", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue},
- {"MINVALUE", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue},
- {"MOD", 2, 2, true, setParamsFromList, makeMod, evlMod, NULL},
- {"NORMALIZE_DECFLOAT", 1, 1, true, setParamsDecFloat, makeDecFloatResult, evlNormDec, NULL},
- {"OVERLAY", 3, 4, true, setParamsOverlay, makeOverlay, evlOverlay, NULL},
- {"PI", 0, 0, true, NULL, makePi, evlPi, NULL},
- {"POSITION", 2, 3, true, setParamsPosition, makeLongResult, evlPosition, NULL},
- {"POWER", 2, 2, true, setParamsDblDec, makeDblDecResult, evlPower, NULL},
- {"QUANTIZE", 2, 2, true, setParamsDecFloat, makeDecFloatResult, evlQuantize, NULL},
- {"RAND", 0, 0, false, NULL, makeDoubleResult, evlRand, NULL},
- {RDB_GET_CONTEXT, 2, 2, true, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL},
- {"RDB$GET_TRANSACTION_CN", 1, 1, false, setParamsInt64, makeGetTranCN, evlGetTranCN, NULL},
- {"RDB$ROLE_IN_USE", 1, 1, true, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL},
- {RDB_RESET_CONTEXT, 1, 1, false, setParamsResetContext, makeLongResult, evlResetContext, NULL},
- {RDB_SET_CONTEXT, 3, 3, false, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL},
- {"RDB$SYSTEM_PRIVILEGE", 1, 1, true, NULL, makeBooleanResult, evlSystemPrivilege, NULL},
- {"REPLACE", 3, 3, true, setParamsFromList, makeReplace, evlReplace, NULL},
- {"REVERSE", 1, 1, true, NULL, makeReverse, evlReverse, NULL},
- {"RIGHT", 2, 2, true, setParamsSecondInteger, makeLeftRight, evlRight, NULL},
- {"ROUND", 1, 2, true, setParamsRoundTrunc, makeRound, evlRound, NULL},
- {"RPAD", 2, 3, true, setParamsSecondInteger, makePad, evlPad, (void*) funRPad},
- {"RSA_DECRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, true, setParamsRsaEncrypt, makeRsaCrypt, evlRsaDecrypt, NULL},
- {"RSA_ENCRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, true, setParamsRsaEncrypt, makeRsaCrypt, evlRsaEncrypt, NULL},
- {"RSA_PRIVATE", 1, 1, false, setParamsInteger, makeRsaPrivate, evlRsaPrivate, NULL},
- {"RSA_PUBLIC", 1, 1, false, setParamsRsaPublic, makeRsaPublic, evlRsaPublic, NULL},
- {"RSA_SIGN_HASH", RSA_SIGN_ARG_MAX, RSA_SIGN_ARG_MAX, true, setParamsRsaSign, makeRsaSign, evlRsaSign, NULL},
- {"RSA_VERIFY_HASH", RSA_VERIFY_ARG_MAX, RSA_VERIFY_ARG_MAX, true, setParamsRsaVerify, makeBoolResult, evlRsaVerify, NULL},
- {"SIGN", 1, 1, true, setParamsDblDec, makeShortResult, evlSign, NULL},
- {"SIN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSin},
- {"SINH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSinh},
- {"SQRT", 1, 1, true, setParamsDblDec, makeDblDecResult, evlSqrt, NULL},
- {"TAN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTan},
- {"TANH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh},
- {"TOTALORDER", 2, 2, true, setParamsDecFloat, makeShortResult, evlCompare, (void*) funTotalOrd},
- {"TRUNC", 1, 2, true, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL},
- {"UNICODE_CHAR", 1, 1, true, setParamsInteger, makeUnicodeChar, evlUnicodeChar, NULL},
- {"UNICODE_VAL", 1, 1, true, setParamsUnicodeVal, makeLongResult, evlUnicodeVal, NULL},
- {"UUID_TO_CHAR", 1, 1, true, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL},
- {"", 0, 0, false, NULL, NULL, NULL, NULL}
+ // name, minArgCount, maxArgCount, flags, setParamsFunc, makeFunc, evlFunc, misc
+
+ {"ABS", 1, 1, DEFAULT, setParamsDblDec, makeAbs, evlAbs, NULL},
+ {"ACOS", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcos},
+ {"ACOSH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcosh},
+ {"ASCII_CHAR", 1, 1, DEFAULT, setParamsInteger, makeAsciiChar, evlAsciiChar, NULL},
+ {"ASCII_VAL", 1, 1, DEFAULT, setParamsAsciiVal, makeShortResult, evlAsciiVal, NULL},
+ {"ASIN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsin},
+ {"ASINH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsinh},
+ {"ATAN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtan},
+ {"ATANH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtanh},
+ {"ATAN2", 2, 2, DEFAULT, setParamsDouble, makeDoubleResult, evlAtan2, NULL},
+ {"BASE64_DECODE", 1, 1, DEFAULT, NULL, makeDecode64, evlDecode64, NULL},
+ {"BASE64_ENCODE", 1, 1, DEFAULT, NULL, makeEncode64, evlEncode64, NULL},
+ {"BIN_AND", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinAnd},
+ {"BIN_NOT", 1, 1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinNot},
+ {"BIN_OR", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinOr},
+ {"BIN_SHL", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShl},
+ {"BIN_SHR", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShr},
+ {"BIN_SHL_ROT", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShlRot},
+ {"BIN_SHR_ROT", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShrRot},
+ {"BIN_XOR", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinXor},
+ {"BLOB_APPEND", 2, -1, DETERMINISTIC, setParamsBlobAppend, makeBlobAppend, evlBlobAppend, NULL},
+ {"CEIL", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlCeil, NULL},
+ {"CEILING", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlCeil, NULL},
+ {"CHAR_TO_UUID", 1, 1, DEFAULT, setParamsCharToUuid, makeUuid, evlCharToUuid, NULL},
+ {"COMPARE_DECFLOAT", 2, 2, DEFAULT, setParamsDecFloat, makeShortResult, evlCompare, (void*) funCmpDec},
+ {"COS", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCos},
+ {"COSH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCosh},
+ {"COT", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCot},
+ {"CRYPT_HASH", 2, 2, DEFAULT, setParamsHash, makeHash, evlHash, NULL},
+ {"DATEADD", 3, 3, DEFAULT, setParamsDateAdd, makeDateAdd, evlDateAdd, NULL},
+ {"DATEDIFF", 3, 3, DEFAULT, setParamsDateDiff, makeDateDiff, evlDateDiff, NULL},
+ {"DECRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, DEFAULT, setParamsEncrypt, makeCrypt, evlDecrypt, NULL},
+ {"ENCRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, DEFAULT, setParamsEncrypt, makeCrypt, evlEncrypt, NULL},
+ {"EXP", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlExp, NULL},
+ {"FIRST_DAY", 2, 2, DEFAULT, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay},
+ {"FLOOR", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlFloor, NULL},
+ {"GEN_UUID", 0, 0, CONSTANT, NULL, makeUuid, evlGenUuid, NULL},
+ {"GREATEST", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue},
+ {"HASH", 1, 2, DEFAULT, setParamsHash, makeHash, evlHash, NULL},
+ {"HEX_DECODE", 1, 1, DEFAULT, NULL, makeDecodeHex, evlDecodeHex, NULL},
+ {"HEX_ENCODE", 1, 1, DEFAULT, NULL, makeEncodeHex, evlEncodeHex, NULL},
+ {"LAST_DAY", 2, 2, DEFAULT, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funLastDay},
+ {"LEAST", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue},
+ {"LEFT", 2, 2, DEFAULT, setParamsSecondInteger, makeLeftRight, evlLeft, NULL},
+ {"LN", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLnat},
+ {"LOG", 2, 2, DEFAULT, setParamsDblDec, makeDblDecResult, evlLog, NULL},
+ {"LOG10", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLog10},
+ {"LPAD", 2, 3, DEFAULT, setParamsSecondInteger, makePad, evlPad, (void*) funLPad},
+ {"MAKE_DBKEY", 2, 4, DEFAULT, setParamsMakeDbkey, makeDbkeyResult, evlMakeDbkey, NULL},
+ {"MAXVALUE", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue},
+ {"MINVALUE", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue},
+ {"MOD", 2, 2, DEFAULT, setParamsFromList, makeMod, evlMod, NULL},
+ {"NORMALIZE_DECFLOAT", 1, 1, DEFAULT, setParamsDecFloat, makeDecFloatResult, evlNormDec, NULL},
+ {"OVERLAY", 3, 4, DEFAULT, setParamsOverlay, makeOverlay, evlOverlay, NULL},
+ {"PI", 0, 0, DEFAULT, NULL, makePi, evlPi, NULL},
+ {"POSITION", 2, 4, DEFAULT, setParamsPosition, makeLongResult, evlPosition, NULL},
+ {"POWER", 2, 2, DEFAULT, setParamsDblDec, makeDblDecResult, evlPower, NULL},
+ {"QUANTIZE", 2, 2, DEFAULT, setParamsDecFloat, makeDecFloatResult, evlQuantize, NULL},
+ {"RAND", 0, 0, 0, NULL, makeDoubleResult, evlRand, NULL},
+ {RDB_GET_CONTEXT, 2, 2, DETERMINISTIC, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL},
+ {"RDB$GET_TRANSACTION_CN", 1, 1, 0, setParamsInt64, makeGetTranCN, evlGetTranCN, NULL},
+ {"RDB$ROLE_IN_USE", 1, 1, DETERMINISTIC, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL},
+ {RDB_RESET_CONTEXT, 1, 1, 0, setParamsResetContext, makeLongResult, evlResetContext, NULL},
+ {RDB_SET_CONTEXT, 3, 3, 0, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL},
+ {"RDB$SYSTEM_PRIVILEGE", 1, 1, DETERMINISTIC, NULL, makeBooleanResult, evlSystemPrivilege, NULL},
+ {"REPLACE", 3, 3, DEFAULT, setParamsFromList, makeReplace, evlReplace, NULL},
+ {"REVERSE", 1, 1, DEFAULT, NULL, makeReverse, evlReverse, NULL},
+ {"RIGHT", 2, 2, DEFAULT, setParamsSecondInteger, makeLeftRight, evlRight, NULL},
+ {"ROUND", 1, 2, DEFAULT, setParamsRoundTrunc, makeRound, evlRound, NULL},
+ {"RPAD", 2, 3, DEFAULT, setParamsSecondInteger, makePad, evlPad, (void*) funRPad},
+ {"RSA_DECRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, DEFAULT, setParamsRsaEncrypt, makeRsaCrypt, evlRsaDecrypt, NULL},
+ {"RSA_ENCRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, DEFAULT, setParamsRsaEncrypt, makeRsaCrypt, evlRsaEncrypt, NULL},
+ {"RSA_PRIVATE", 1, 1, 0, setParamsInteger, makeRsaPrivate, evlRsaPrivate, NULL},
+ {"RSA_PUBLIC", 1, 1, 0, setParamsRsaPublic, makeRsaPublic, evlRsaPublic, NULL},
+ {"RSA_SIGN_HASH", RSA_SIGN_ARG_MAX, RSA_SIGN_ARG_MAX, DEFAULT, setParamsRsaSign, makeRsaSign, evlRsaSign, NULL},
+ {"RSA_VERIFY_HASH", RSA_VERIFY_ARG_MAX, RSA_VERIFY_ARG_MAX, DEFAULT, setParamsRsaVerify, makeBoolResult, evlRsaVerify, NULL},
+ {"SIGN", 1, 1, DEFAULT, setParamsDblDec, makeShortResult, evlSign, NULL},
+ {"SIN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSin},
+ {"SINH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSinh},
+ {"SQRT", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlSqrt, NULL},
+ {"TAN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTan},
+ {"TANH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh},
+ {"TOTALORDER", 2, 2, DEFAULT, setParamsDecFloat, makeShortResult, evlCompare, (void*) funTotalOrd},
+ {"TRUNC", 1, 2, DEFAULT, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL},
+ {"UNICODE_CHAR", 1, 1, DEFAULT, setParamsInteger, makeUnicodeChar, evlUnicodeChar, NULL},
+ {"UNICODE_VAL", 1, 1, DEFAULT, setParamsUnicodeVal, makeLongResult, evlUnicodeVal, NULL},
+ {"UUID_TO_CHAR", 1, 1, DEFAULT, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL},
+ {"", 0, 0, 0, NULL, NULL, NULL, NULL}
};
diff --git a/src/jrd/SysFunction.h b/src/jrd/SysFunction.h
index 5a2cbc671b9..d8a7a21b95f 100644
--- a/src/jrd/SysFunction.h
+++ b/src/jrd/SysFunction.h
@@ -46,6 +46,12 @@ namespace Jrd
class SysFunction
{
public:
+ enum Flags : UCHAR
+ {
+ DETERMINISTIC = 1,
+ CONSTANT = 2
+ };
+
typedef void (*SetParamsFunc)(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int, dsc**);
typedef void (*MakeFunc)(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc*, int, const dsc**);
typedef dsc* (*EvlFunc)(Jrd::thread_db*, const SysFunction* function,
@@ -54,7 +60,7 @@ class SysFunction
const char* name;
int minArgCount;
int maxArgCount; // -1 for no limit
- bool deterministic;
+ UCHAR flags;
SetParamsFunc setParamsFunc;
MakeFunc makeFunc;
EvlFunc evlFunc;
@@ -64,6 +70,16 @@ class SysFunction
void checkArgsMismatch(int count) const;
+ inline bool isDeterministic() const
+ {
+ return flags & DETERMINISTIC;
+ }
+
+ inline bool isConstant() const
+ {
+ return flags & CONSTANT;
+ }
+
private:
const static SysFunction functions[];
};
diff --git a/src/jrd/SystemPackages.h b/src/jrd/SystemPackages.h
index acb47b379b3..2cf1e403183 100644
--- a/src/jrd/SystemPackages.h
+++ b/src/jrd/SystemPackages.h
@@ -180,6 +180,33 @@ namespace Jrd
SystemFunctionReturnType returnType;
};
+ struct SystemConstant
+ {
+ SystemConstant(
+ Firebird::MemoryPool& pool,
+ const char* aName,
+ const USHORT aFieldId,
+ const char* aValueSource = nullptr,
+ const std::initializer_list aValueBlr = {}
+ )
+ : name(aName),
+ fieldId(aFieldId),
+ valueSource(aValueSource),
+ valueBlr(pool, aValueBlr)
+ { }
+
+ SystemConstant(Firebird::MemoryPool& pool, const SystemConstant& other)
+ : valueBlr(pool)
+ {
+ *this = other;
+ }
+
+ const char* name;
+ USHORT fieldId;
+ const char* valueSource = nullptr;
+ Firebird::Array valueBlr;
+ };
+
struct SystemPackage
{
SystemPackage(
@@ -187,18 +214,21 @@ namespace Jrd
const char* aName,
USHORT aOdsVersion,
std::initializer_list aProcedures,
- std::initializer_list aFunctions
+ std::initializer_list aFunctions,
+ std::initializer_list aConstants = {}
)
: name(aName),
odsVersion(aOdsVersion),
procedures(pool, aProcedures),
- functions(pool, aFunctions)
+ functions(pool, aFunctions),
+ constants(pool, aConstants)
{
}
SystemPackage(Firebird::MemoryPool& pool, const SystemPackage& other)
: procedures(pool),
- functions(pool)
+ functions(pool),
+ constants(pool)
{
*this = other;
}
@@ -207,6 +237,7 @@ namespace Jrd
USHORT odsVersion;
Firebird::ObjectsArray procedures;
Firebird::ObjectsArray functions;
+ Firebird::ObjectsArray constants;
static Firebird::ObjectsArray& get();
diff --git a/src/jrd/SystemTriggers.epp b/src/jrd/SystemTriggers.epp
index ac53b45369c..fc57d817a61 100644
--- a/src/jrd/SystemTriggers.epp
+++ b/src/jrd/SystemTriggers.epp
@@ -1602,19 +1602,26 @@ void afterInsertRelation(thread_db* tdbb, Record* record)
}
-void afterInsertProcedure(thread_db* tdbb, Record* record)
+template
+void populateCache(thread_db* tdbb, Record* record)
{
- if (tdbb->getAttachment()->isRWGbak())
- {
- dsc desc;
- bool idSet = EVL_field(nullptr, record, f_prc_id, &desc);
- fb_assert(idSet);
- if (!idSet)
- return;
+ if (!tdbb->getAttachment()->isRWGbak())
+ return;
- MetaId id = MetaId(MOV_get_long(tdbb, &desc, 0));
- MetadataCache::newVersion(tdbb, id);
- }
+ dsc desc;
+ const bool idSet = EVL_field(nullptr, record, Field, &desc);
+ fb_assert(idSet);
+ if (!idSet)
+ return;
+
+ const IdType id = IdType(MOV_get_long(tdbb, &desc, 0));
+ MetadataCache::newVersion(tdbb, id);
+}
+
+
+void afterInsertProcedure(thread_db* tdbb, Record* record)
+{
+ populateCache(tdbb, record);
}
void afterUpdateProcedure(thread_db* tdbb, Record* orgRecord, Record* newRecord)
@@ -1624,17 +1631,7 @@ void afterUpdateProcedure(thread_db* tdbb, Record* orgRecord, Record* newRecord)
void afterInsertFunction(thread_db* tdbb, Record* record)
{
- if (tdbb->getAttachment()->isRWGbak())
- {
- dsc desc;
- bool idSet = EVL_field(nullptr, record, f_fun_id, &desc);
- fb_assert(idSet);
- if (!idSet)
- return;
-
- MetaId id = MetaId(MOV_get_long(tdbb, &desc, 0));
- MetadataCache::newVersion(tdbb, id);
- }
+ populateCache(tdbb, record);
}
void afterUpdateFunction(thread_db* tdbb, Record* orgRecord, Record* newRecord)
@@ -1705,17 +1702,7 @@ void beforeInsertCollation(thread_db* tdbb, Record* record, jrd_rel* relation)
void afterInsertCollation(thread_db* tdbb, Record* record)
{
- if (tdbb->getAttachment()->isRWGbak())
- {
- dsc desc;
- bool idSet = EVL_field(nullptr, record, f_coll_cs_id, &desc);
- fb_assert(idSet);
- if (!idSet)
- return;
-
- CSetId csId = CSetId(MOV_get_long(tdbb, &desc, 0));
- MetadataCache::newVersion(tdbb, csId);
- }
+ populateCache(tdbb, record);
}
} // anonymous
@@ -1818,6 +1805,10 @@ void SystemTriggers::executeAfterInsertTriggers(thread_db* tdbb, jrd_rel* relati
case rel_funs:
afterInsertFunction(tdbb, record);
break;
+
+ case rel_packages:
+ populateCache(tdbb, record);
+ break;
}
}
diff --git a/src/jrd/constants.h b/src/jrd/constants.h
index b40a51dbf77..01a60bcfef0 100644
--- a/src/jrd/constants.h
+++ b/src/jrd/constants.h
@@ -154,6 +154,8 @@ inline constexpr int GEN_SECCLASS_PREFIX_LEN = 4;
inline constexpr const char* PROCEDURES_GENERATOR = "RDB$PROCEDURES";
inline constexpr const char* FUNCTIONS_GENERATOR = "RDB$FUNCTIONS";
+inline constexpr const char* const CONSTANTS_GENERATOR = "RDB$CONSTANTS";
+inline constexpr const char* const PACKAGES_GENERATOR = "RDB$PACKAGES";
// Automatically created check constraints for unnamed PRIMARY and UNIQUE declarations.
inline constexpr const char* IMPLICIT_INTEGRITY_PREFIX = "INTEG_";
diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp
index 8052962ef70..e7f1f3dc52a 100644
--- a/src/jrd/dfw.epp
+++ b/src/jrd/dfw.epp
@@ -133,6 +133,8 @@
#include "../jrd/shut_proto.h"
#include "../jrd/ProtectRelations.h"
+#include "../jrd/Package.h"
+
#ifdef HAVE_UNISTD_H
#include
#endif
@@ -407,6 +409,7 @@ static bool end_backup(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool check_not_null(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool store_view_context_type(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool user_management(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
+static bool create_package(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction);
static bool drop_package_header(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool modify_package_header(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool drop_package_body(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
@@ -466,6 +469,9 @@ static ISC_STATUS getErrorCodeByObjectType(int obj_type)
case obj_package_body:
err_code = isc_package_name;
break;
+ case obj_package_constant:
+ err_code = isc_const_name;
+ break;
default:
fb_assert(false);
}
@@ -485,6 +491,9 @@ static ISC_STATUS getErrorNotFound(int obj_type)
case obj_udf:
err_code = isc_dyn_func_not_found;
break;
+ case obj_package_constant:
+ err_code = isc_dyn_const_not_found;
+ break;
default:
fb_assert(false);
}
@@ -1116,6 +1125,66 @@ namespace
ERR_post(status);
}
}
+
+ static bool modifyConstant(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
+ {
+ fb_assert(!work->dfw_package.isEmpty());
+
+ SET_TDBB(tdbb);
+ const QualifiedName name(work->dfw_name);
+
+ switch (phase)
+ {
+ case 0:
+ return false;
+
+ case 1:
+ return true;
+ case 2:
+ check_dependencies(tdbb, work->getQualifiedName(), nullptr, obj_package_constant, transaction);
+ return true;
+
+ case 3:
+ return true;
+
+ case 4:
+ MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_constant);
+ return false;
+ }
+
+ return false;
+ }
+
+ static bool deleteConstant(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
+ {
+ fb_assert(!work->dfw_package.isEmpty());
+
+ SET_TDBB(tdbb);
+ Cached::Package* routine;
+
+ switch (phase)
+ {
+ case 0:
+ return false;
+
+ case 1:
+ return true;
+
+ case 2:
+ check_dependencies(tdbb, work->getQualifiedName(), NULL, obj_package_constant, transaction);
+ return true;
+
+ case 3:
+ return true;
+
+ case 4:
+ MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_constant);
+ return false;
+ }
+
+ return false;
+ }
+
} // namespace
static inline constexpr deferred_task task_table[] =
@@ -1170,6 +1239,10 @@ static inline constexpr deferred_task task_table[] =
{ dfw_clear_cache, clear_cache },
{ dfw_change_repl_state, change_repl_state },
{ dfw_set_statistics, set_statistics },
+ { dfw_modify_package_constant, modifyConstant },
+ { dfw_delete_package_constant, deleteConstant },
+ { dfw_create_package, create_package },
+
{ dfw_null, NULL }
};
@@ -2213,17 +2286,75 @@ static bool user_management(thread_db* /*tdbb*/, SSHORT phase, DeferredWork* wor
return false;
}
+static bool create_package(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
+{
+ fb_assert(work->dfw_id != 0);
+
+ SET_TDBB(tdbb);
+
+ Cached::Package* routine = nullptr;
+ switch (phase)
+ {
+ case 0:
+ routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN);
+ if (routine)
+ routine->rollback(tdbb);
+
+ return false;
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ return true;
+ case 7:
+ routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN);
+ fb_assert(routine);
+ if (routine)
+ routine->commit(tdbb);
+
+ return false;
+ }
+
+ return false;
+}
+
// Drop dependencies of a package header.
static bool drop_package_header(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
{
+ fb_assert(work->dfw_id != 0);
+
SET_TDBB(tdbb);
+ Cached::Package* routine = nullptr;
switch (phase)
{
+ case 0:
+ routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN);
+ if (routine)
+ routine->rollback(tdbb);
+
+ return false;
+
case 1:
MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_body);
MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_header);
- break;
+ return true;
+
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ return true;
+ case 7:
+ routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::ERASED | CacheFlag::NOSCAN);
+ fb_assert(routine);
+ if (routine)
+ routine->commit(tdbb);
+
+ return false;
}
return false;
@@ -2234,11 +2365,36 @@ static bool modify_package_header(thread_db* tdbb, SSHORT phase, DeferredWork* w
{
SET_TDBB(tdbb);
+ Cached::Package* routine = nullptr;
switch (phase)
{
+ case 0:
+ routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN);
+ if (routine)
+ routine->rollback(tdbb);
+
+ return false;
case 1:
MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_header);
- break;
+ return true;
+ case 2:
+ case 3:
+ routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::MINISCAN);
+ if (!routine)
+ return false;
+
+ return true;
+ case 4:
+ case 5:
+ case 6:
+ return true;
+ case 7:
+ routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN);
+ fb_assert(routine);
+ if (routine)
+ routine->commit(tdbb);
+
+ return false;
}
return false;
@@ -2247,13 +2403,34 @@ static bool modify_package_header(thread_db* tdbb, SSHORT phase, DeferredWork* w
// Drop dependencies of a package body.
static bool drop_package_body(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
{
+ fb_assert(work->dfw_id != 0);
+
SET_TDBB(tdbb);
+ Cached::Package* routine;
switch (phase)
{
+ case 0:
+ routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::NOSCAN);
+ if (routine)
+ routine->rollback(tdbb);
+
+ return false;
case 1:
MET_delete_dependencies(tdbb, work->getQualifiedName(), obj_package_body);
- break;
+ return true;
+
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ return true;
+ case 7:
+ routine = MetadataCache::getPerm(tdbb, work->dfw_id, CacheFlag::ERASED | CacheFlag::NOSCAN);
+ fb_assert(routine);
+ if (routine)
+ routine->commit(tdbb);
}
return false;
@@ -2810,6 +2987,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 +3003,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..24d844a30ec 100644
--- a/src/jrd/drq.h
+++ b/src/jrd/drq.h
@@ -242,6 +242,8 @@ enum drq_type_t
drq_e_pub_tab_all, // erase relation from all publication
drq_l_rel_con, // lookup relation constraint
drq_l_rel_fld_name, // lookup relation field name
+ drq_g_nxt_const_id, // lookup next constant ID
+ drq_g_nxt_package_id, // lookup next package ID
drq_MAX
};
diff --git a/src/jrd/fields.h b/src/jrd/fields.h
index 49a47fd055a..56f18a0d5d0 100644
--- a/src/jrd/fields.h
+++ b/src/jrd/fields.h
@@ -239,3 +239,10 @@
FIELD(fld_text_max , nam_text_max , dtype_varying, MAX_VARY_COLUMN_SIZE / METADATA_BYTES_PER_CHAR * METADATA_BYTES_PER_CHAR, dsc_text_type_metadata, NULL, true, ODS_14_0)
FIELD(fld_tab_type , nam_mon_tab_type , dtype_varying , 32 , dsc_text_type_ascii , NULL , true , ODS_14_0)
+
+ FIELD(fld_pkg_id , nam_pkg_id , dtype_long , sizeof(SLONG) , 0 , NULL , true , ODS_14_0)
+
+ FIELD(fld_const_name , nam_const_name , dtype_varying , MAX_SQL_IDENTIFIER_LEN , dsc_text_type_metadata , NULL , false , ODS_14_0)
+ FIELD(fld_const_id , nam_const_id , dtype_long , sizeof(SLONG) , 0 , NULL , true , ODS_14_0)
+ FIELD(fld_const_blr , nam_const_blr , dtype_blob , BLOB_SIZE , isc_blob_blr , NULL , true , ODS_14_0)
+ FIELD(fld_const_source , nam_const_source , dtype_blob , BLOB_SIZE , isc_blob_text , NULL , true , ODS_14_0)
diff --git a/src/jrd/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..53deb0ac182 100644
--- a/src/jrd/idx.h
+++ b/src/jrd/idx.h
@@ -524,6 +524,20 @@ static inline constexpr struct ini_idx_t indices[] =
SEGMENT(f_pubtab_tab_schema, idx_metadata), // table schema name
SEGMENT(f_pubtab_tab_name, idx_metadata), // table name
SEGMENT(f_pubtab_pub_name, idx_metadata) // publication name
+ }},
+ // define index RDB$INDEX_98 for RDB$CONSTANTS unique RDB$SCHEMA_NAME, RDB$PACKAGE_NAME, RDB$CONSTANT_NAME;
+ INDEX(98, rel_constants, idx_unique, 3, ODS_14_0)
+ SEGMENT(f_const_package_schema, idx_metadata), // 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
+ }},
+ // define index RDB$INDEX_99 for RDB$CONSTANTS unique RDB$CONSTANT_ID;
+ INDEX(100, rel_packages, idx_unique, 1, ODS_14_0)
+ SEGMENT(f_pkg_id, idx_numeric) // constant id
}}
};
diff --git a/src/jrd/ini.epp b/src/jrd/ini.epp
index 27bf34994b2..aacb92e2d06 100644
--- a/src/jrd/ini.epp
+++ b/src/jrd/ini.epp
@@ -1965,8 +1965,11 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR
AutoRequest packageHandle, procedureHandle, procedureParameterHandle;
AutoRequest functionHandle, functionReturnHandle, functionArgumentHandle;
+ AutoRequest constantHandle;
const SLONG procGen = lookupGenerator(PROCEDURES_GENERATOR);
const SLONG funcGen = lookupGenerator(FUNCTIONS_GENERATOR);
+ const SLONG packageGen = lookupGenerator(PACKAGES_GENERATOR);
+ const SLONG constantGen = lookupGenerator(CONSTANTS_GENERATOR);
for (const auto& systemPackage : SystemPackage::get())
{
@@ -1989,6 +1992,8 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR
PKG.RDB$SYSTEM_FLAG = RDB_system;
PKG.RDB$VALID_BODY_FLAG = TRUE;
+
+ PKG.RDB$PACKAGE_ID = DPM_gen_id(tdbb, packageGen, false, 1);
}
END_STORE
@@ -2157,5 +2162,50 @@ static void store_packages(thread_db* tdbb, NonRelationSecurity& security, USHOR
END_STORE
}
}
+
+ for (const auto& constant : systemPackage.constants)
+ {
+ STORE (REQUEST_HANDLE constantHandle TRANSACTION_HANDLE transaction)
+ CONST IN RDB$CONSTANTS
+ {
+ // Constant name
+ PAD(constant.name, CONST.RDB$CONSTANT_NAME);
+
+ // Constant unique id
+ CONST.RDB$CONSTANT_ID = DPM_gen_id(tdbb, constantGen, false, 1);;
+
+ // Description (filed) name
+ PAD(names[gfields[constant.fieldId].gfld_name], CONST.RDB$FIELD_SOURCE);
+
+ // Put the blr
+ {
+ Array blrData(1 + constant.valueBlr.getCount() + 1);
+ blrData.push(blr_version5);
+ blrData.append(constant.valueBlr);
+ blrData.push(blr_eoc);
+ attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, blrData);
+ CONST.RDB$CONSTANT_BLR.NULL = FALSE;
+ }
+
+ // Parent package
+ PAD(systemPackage.name, CONST.RDB$PACKAGE_NAME);
+
+ // Schema of the parent package
+ PAD(SYSTEM_SCHEMA, CONST.RDB$SCHEMA_NAME);
+
+ // Type
+ CONST.RDB$PRIVATE_FLAG.NULL = FALSE;
+ CONST.RDB$PRIVATE_FLAG = false;
+
+ if (constant.valueSource != nullptr)
+ {
+ CONST.RDB$CONSTANT_SOURCE.NULL = FALSE;
+ attachment->storeMetaDataBlob(tdbb, transaction, &CONST.RDB$CONSTANT_SOURCE, constant.valueSource);
+ }
+ else
+ CONST.RDB$CONSTANT_SOURCE.NULL = TRUE;
+ }
+ END_STORE
+ }
}
}
diff --git a/src/jrd/irq.h b/src/jrd/irq.h
index a1611c9e07a..cbb22ea63e6 100644
--- a/src/jrd/irq.h
+++ b/src/jrd/irq.h
@@ -126,6 +126,7 @@ enum irq_type_t
irq_m_fields7, // process a modification of RDB$FIELDS for functions
irq_m_fields8, // process a modification of RDB$FIELDS for functions (TYPE OF COLUMN)
irq_m_fields9, // process a modification of RDB$FIELDS for packaged functions (TYPE OF COLUMN)
+ irq_m_fields10, // process a modification of RDB$FIELDS for packaged constants (TYPE OF COLUMN)
irq_l_relfield, // lookup a relation field
irq_verify_trusted_role, // ensure trusted role exists
diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h
index 220634bdcd4..ae4d3a0d568 100644
--- a/src/jrd/jrd.h
+++ b/src/jrd/jrd.h
@@ -58,6 +58,7 @@
#include "../common/os/guid.h"
#include "../jrd/sbm.h"
#include "../jrd/scl.h"
+#include "../jrd/Package.h"
#include "../jrd/Routine.h"
#include "../jrd/ExtEngineManager.h"
#include "../jrd/Attachment.h"
diff --git a/src/jrd/lck.cpp b/src/jrd/lck.cpp
index a1868ed441e..88117b2e163 100644
--- a/src/jrd/lck.cpp
+++ b/src/jrd/lck.cpp
@@ -584,6 +584,7 @@ static lck_owner_t get_owner_type(enum lck_t lock_type)
case LCK_idx_rescan:
case LCK_prc_rescan:
case LCK_fun_rescan:
+ case LCK_package_rescan:
case LCK_cs_rescan:
case LCK_dbwide_triggers:
owner_type = LCK_OWNER_database;
diff --git a/src/jrd/lck.h b/src/jrd/lck.h
index ce927051978..cfe37762827 100644
--- a/src/jrd/lck.h
+++ b/src/jrd/lck.h
@@ -67,6 +67,7 @@ enum lck_t : UCHAR {
LCK_idx_rescan, // Index rescan lock
LCK_prc_rescan, // Procedure rescan lock
LCK_fun_rescan, // Function existence lock
+ LCK_package_rescan, // Package existence lock
LCK_rel_partners, // Relation partners lock
LCK_crypt, // Crypt lock for single crypt thread
LCK_crypt_status, // Notifies about changed database encryption status
diff --git a/src/jrd/met.epp b/src/jrd/met.epp
index 6b1719a2ad1..533c40cf177 100644
--- a/src/jrd/met.epp
+++ b/src/jrd/met.epp
@@ -98,6 +98,7 @@
#include "../jrd/trace/TraceJrdHelpers.h"
#include "firebird/impl/msg_helper.h"
#include "../jrd/LocalTemporaryTable.h"
+#include "../jrd/Package.h"
#ifdef HAVE_CTYPE_H
@@ -340,6 +341,7 @@ void MetadataCache::cleanup(thread_db* tdbb)
mdc_procedures.cleanup(tdbb);
mdc_functions.cleanup(tdbb);
mdc_charsets.cleanup(tdbb);
+ mdc_packages.cleanup(tdbb);
for (unsigned i = 0; i < DB_TRIGGERS_COUNT; ++i)
{
@@ -569,6 +571,7 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const
* Find all relations affected and schedule a format update.
* Find all procedures and triggers and schedule a BLR validate.
* Find all functions and schedule a BLR validate.
+ * Find all package constants.
*
**************************************/
SET_TDBB(tdbb);
@@ -806,6 +809,41 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const
}
END_FOR
+
+ request.reset(tdbb, irq_m_fields10, IRQ_REQUESTS);
+
+ Package* package = nullptr;
+ FOR(REQUEST_HANDLE request)
+ DEP IN RDB$DEPENDENCIES CROSS
+ CONST IN RDB$CONSTANTS CROSS
+ PKG IN RDB$PACKAGES
+ WITH PKG.RDB$SCHEMA_NAME EQ schemaName->dsc_address AND
+ PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME AND
+ DEP.RDB$DEPENDED_ON_SCHEMA_NAME EQ schemaName->dsc_address AND
+ DEP.RDB$DEPENDED_ON_NAME EQ fieldSource->dsc_address AND
+ DEP.RDB$DEPENDED_ON_TYPE EQ obj_field AND
+ (DEP.RDB$DEPENDENT_TYPE EQ obj_package_header OR
+ DEP.RDB$DEPENDENT_TYPE EQ obj_package_body) AND
+ DEP.RDB$DEPENDENT_SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND
+ DEP.RDB$DEPENDENT_NAME EQ CONST.RDB$PACKAGE_NAME
+ {
+ QualifiedName constantFullName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME);
+
+ const auto id = CONST.RDB$CONSTANT_ID;
+
+ DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_package_constant,
+ string(constantFullName.object.c_str()), constantFullName.schema, id, constantFullName.package);
+
+ if (package == nullptr)
+ {
+ package = MetadataCache::newVersion(tdbb, id)->getVersioned(tdbb, 0);
+ fb_assert(package);
+ }
+
+ package->addConstant(tdbb, id, constantFullName, CONST.RDB$PRIVATE_FLAG, CONST.RDB$CONSTANT_BLR);
+ }
+ END_FOR
+
return calcFieldRelation;
}
@@ -4402,6 +4440,10 @@ void MET_store_dependency(thread_db* tdbb,
name = dependency.name;
dpdo_name = &name;
break;
+ case obj_package_constant:
+ name = dependency.name;
+ dpdo_name = &name;
+ break;
}
MetaName field_name;
@@ -4743,6 +4785,14 @@ void MetadataCache::releaseLocks(thread_db* tdbb)
charset->releaseLocks(tdbb);
}
+ // Release packages locks
+
+ for (auto package : mdc_packages)
+ {
+ if (package)
+ package->releaseLocks(tdbb);
+ }
+
// Release database triggers locks
for (unsigned i = 0; i < DB_TRIGGERS_COUNT; ++i)
diff --git a/src/jrd/met.h b/src/jrd/met.h
index 16055082e57..5111ed2a505 100644
--- a/src/jrd/met.h
+++ b/src/jrd/met.h
@@ -238,6 +238,7 @@ class MetadataCache : public Firebird::PermanentStorage
mdc_procedures(getPool()),
mdc_functions(getPool()),
mdc_charsets(getPool()),
+ mdc_packages(getPool()),
mdc_cleanup_queue(pool)
{
memset(mdc_triggers, 0, sizeof(mdc_triggers));
@@ -500,6 +501,16 @@ class MetadataCache : public Firebird::PermanentStorage
}
};
+ template
+ class Vector
+ {
+ public:
+ static CacheVector& get(MetadataCache* mdc)
+ {
+ return mdc->mdc_packages;
+ }
+ };
+
static MetadataCache* getCache(thread_db* tdbb) noexcept;
class GeneratorFinder
@@ -605,6 +616,7 @@ class MetadataCache : public Firebird::PermanentStorage
CacheVector mdc_procedures;
CacheVector