diff --git a/builds/win32/msvc15/engine_static.vcxproj b/builds/win32/msvc15/engine_static.vcxproj
index 67bbab0013e..3bd5bfff341 100644
--- a/builds/win32/msvc15/engine_static.vcxproj
+++ b/builds/win32/msvc15/engine_static.vcxproj
@@ -41,6 +41,7 @@
+
@@ -417,6 +418,7 @@
+
diff --git a/builds/win32/msvc15/engine_static.vcxproj.filters b/builds/win32/msvc15/engine_static.vcxproj.filters
index c5e0ec0fcee..887599cb878 100644
--- a/builds/win32/msvc15/engine_static.vcxproj.filters
+++ b/builds/win32/msvc15/engine_static.vcxproj.filters
@@ -1199,6 +1199,9 @@
JRD files\GPRE files
+
+ JRD files\GPRE files
+
Services
@@ -1206,4 +1209,4 @@
DSQL
-
\ No newline at end of file
+
diff --git a/builds/win32/preprocess.bat b/builds/win32/preprocess.bat
index 049959f8ac4..8d223f78d02 100644
--- a/builds/win32/preprocess.bat
+++ b/builds/win32/preprocess.bat
@@ -65,7 +65,7 @@ goto :EOF
@for %%i in (alice_meta) do @call :PREPROCESS alice %%i
@for %%i in (metd, DdlNodes, PackageNodes) do @call :PREPROCESS dsql %%i -gds_cxx
@for %%i in (gpre_meta) do @call :PREPROCESS gpre/std %%i
-@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function, SystemTriggers) do @call :PREPROCESS jrd %%i -gds_cxx
+@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function, SystemTriggers, Package) do @call :PREPROCESS jrd %%i -gds_cxx
@for %%i in (stats) do @call :PREPROCESS utilities %%i
@goto :EOF
@@ -78,7 +78,7 @@ goto :EOF
@for %%i in (metd) do @call :PREPROCESS dsql %%i -gds_cxx
@for %%i in (DdlNodes, PackageNodes) do @call :PREPROCESS dsql %%i -gds_cxx
@for %%i in (gpre_meta) do @call :PREPROCESS gpre/std %%i
-@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function, SystemTriggers) do @call :PREPROCESS jrd %%i -gds_cxx
+@for %%i in (dfw, dpm, dyn_util, fun, grant, ini, met, scl, Function, SystemTriggers, Package) do @call :PREPROCESS jrd %%i -gds_cxx
@for %%i in (extract, isql, show) do @call :PREPROCESS isql %%i -ocxx
@for %%i in (dba) do @call :PREPROCESS utilities/gstat %%i
@for %%i in (stats) do @call :PREPROCESS utilities %%i
diff --git a/doc/sql.extensions/README.blob_util.md b/doc/sql.extensions/README.blob_util.md
index a90fa3c4f02..4c3efb58893 100644
--- a/doc/sql.extensions/README.blob_util.md
+++ b/doc/sql.extensions/README.blob_util.md
@@ -56,7 +56,8 @@ Return type: `VARBINARY(32767)`.
`RDB$BLOB_UTIL.SEEK` is used to set the position for the next `READ_DATA`. It returns the new position.
-`MODE` may be 0 (from the start), 1 (from current position) or 2 (from end).
+`MODE` may be 0 (from the start), 1 (from current position) or 2 (from end). The corresponding constants
+are available in the package: `FROM_BEGIN`, `FROM_CURRENT`, `FROM_END`.
When `MODE` is 2, `OFFSET` should be zero or negative.
diff --git a/doc/sql.extensions/README.ddl.txt b/doc/sql.extensions/README.ddl.txt
index 8d53d670a5d..35b714c1294 100644
--- a/doc/sql.extensions/README.ddl.txt
+++ b/doc/sql.extensions/README.ddl.txt
@@ -138,6 +138,7 @@ COMMENT ON name IS {'txt'|NULL};
COMMENT ON COLUMN table_or_view_name.field_name IS {'txt'|NULL};
COMMENT ON {PROCEDURE | [EXTERNAL] FUNCTION} [ .] name.param_name IS {'txt'|NULL};
COMMENT ON [PROCEDURE | FUNCTION] PARAMETER [ .] name.param_name IS {'txt'|NULL};
+COMMENT ON CONSTANT [ .] . name IS {'txt'|NULL};
An empty literal string '' will act as NULL since the internal code (DYN in this case)
works this way with blobs.
diff --git a/doc/sql.extensions/README.packages.txt b/doc/sql.extensions/README.packages.txt
index 74ecf7cf888..dafe0356614 100644
--- a/doc/sql.extensions/README.packages.txt
+++ b/doc/sql.extensions/README.packages.txt
@@ -19,7 +19,8 @@ Syntax:
::=
; |
- ;
+ ; |
+ ;
::=
FUNCTION [( )] RETURNS
@@ -27,6 +28,9 @@ Syntax:
::=
PROCEDURE [( ) [RETURNS ( )]]
+ ::=
+ CONSTANT =
+
::=
{ CREATE [OR ALTER] | ALTER | RECREATE } PACKAGE BODY
AS
@@ -37,7 +41,8 @@ Syntax:
::=
|
-
+ |
+
::=
FUNCTION [( )] RETURNS
@@ -75,7 +80,7 @@ Objectives:
1) The grouping is not represented in the database metadata.
2) They all participate in a flat namespace and all routines are callable by everyone (not
talking about security permissions here).
-
+
- Facilitate dependency tracking between its internal routines and between other packaged and
unpackaged routines.
@@ -90,9 +95,17 @@ Objectives:
tables that the package body depends on that object. If you want to, for example, drop that
object, you first need to remove who depends on it. As who depends on it is a package body,
you can just drop it even if some other database object depends on this package. When the body
- is dropped, the header remains, allowing you to create its body again after changing it based
+ is dropped, the header remains, allowing you to create its body again after changing it based
on the object removal.
+ A package constant is a value initialized by a constant expression.
+ A constant expression is defined by a simple rule: its value does not change after recompilation.
+ Constants declared in the package specification are publicly visible and can be referenced using
+ the [.]. notation.
+ Constants declared in the package body are private and cannot be accessed from outside the package.
+ However, they can be referenced directly by within and .
+ Header constants can also be used directly with just the name for package body elements.
+
- Facilitate permission management.
It's generally a good practice to create routines with a privileged database user and grant
@@ -106,6 +119,10 @@ Objectives:
GRANT SELECT ON TABLE secret TO PACKAGE pk_secret;
GRANT EXECUTE ON PACKAGE pk_secret TO ROLE role_secret;
+ To use package constants in a select expression, a USAGE permission is requeued:
+ GRANT USAGE ON PACKAGE [.] to [] [] [];
+ REVOKE USAGE ON PACKAGE [.] FROM [];
+
- Introduce private scope to routines making them available only for internal usage in the
defining package.
diff --git a/src/burp/backup.epp b/src/burp/backup.epp
index 4ec7cd6d1f2..25db9caf086 100644
--- a/src/burp/backup.epp
+++ b/src/burp/backup.epp
@@ -156,6 +156,7 @@ void write_triggers();
void write_trigger_messages();
void write_types();
void write_user_privileges();
+void write_constants();
void general_on_error();
@@ -505,6 +506,13 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name)
write_pub_tables();
}
+ if (tdgbl->runtimeODS >= DB_VERSION_DDL14)
+ {
+ // Write constants
+ BURP_verbose(USHORT(isc_gbak_writing_constants)); // writing constants
+ write_constants();
+ }
+
// Finish up
put(tdgbl, (UCHAR) rec_end);
@@ -4845,6 +4853,58 @@ void write_user_privileges()
MISC_release_request_silent(req_handle1);
}
+void write_constants()
+{
+ Firebird::IRequest* req_handle1 = nullptr;
+
+ BurpGlobals* tdgbl = BurpGlobals::getSpecific();
+
+ FOR (REQUEST_HANDLE req_handle1)
+ CONST IN RDB$CONSTANTS
+ WITH CONST.RDB$SCHEMA_NAME NE SYSTEM_SCHEMA
+ {
+ QualifiedMetaString name(CONST.RDB$CONSTANT_NAME);
+
+ put(tdgbl, rec_constants);
+ PUT_TEXT(att_constant_name, CONST.RDB$CONSTANT_NAME);
+
+ PUT_TEXT(att_constant_package, CONST.RDB$PACKAGE_NAME);
+ name.package = CONST.RDB$PACKAGE_NAME;
+
+ PUT_TEXT(att_constant_field_source, CONST.RDB$FIELD_SOURCE);
+
+ if (!CONST.RDB$PRIVATE_FLAG.NULL)
+ put_int32(att_constant_private_flag, CONST.RDB$PRIVATE_FLAG);
+
+ if (!CONST.RDB$CONSTANT_BLR.NULL)
+ put_blr_blob(att_constant_blr, CONST.RDB$CONSTANT_BLR);
+
+ if (!CONST.RDB$CONSTANT_SOURCE.NULL)
+ put_source_blob(att_constant_source, att_constant_source, CONST.RDB$CONSTANT_SOURCE);
+
+ if (!CONST.RDB$SCHEMA_NAME.NULL)
+ {
+ PUT_TEXT(att_constant_schema_name, CONST.RDB$SCHEMA_NAME);
+ name.schema = CONST.RDB$SCHEMA_NAME;
+ }
+
+ if (!CONST.RDB$DESCRIPTION.NULL)
+ {
+ put_source_blob (att_constant_description, att_constant_description, CONST.RDB$DESCRIPTION);
+ }
+
+ // writing constant %s
+ BURP_verbose(USHORT(isc_gbak_writing_constant), SafeArg() << name.toQuotedString().data());
+ put(tdgbl, att_end);
+ }
+ END_FOR;
+ ON_ERROR
+ general_on_error();
+ END_ERROR;
+
+ MISC_release_request_silent(req_handle1);
+}
+
} // namespace
namespace Burp {
diff --git a/src/burp/burp.h b/src/burp/burp.h
index 3fb58a76b9e..0130d4070ab 100644
--- a/src/burp/burp.h
+++ b/src/burp/burp.h
@@ -124,7 +124,8 @@ enum rec_type {
rec_db_creator, // Database creator
rec_publication, // Publication
rec_pub_table, // Publication table
- rec_schema // Schema
+ rec_schema, // Schema
+ rec_constants // Constants
};
@@ -701,6 +702,16 @@ enum att_type {
att_schema_security_class,
att_schema_owner_name,
att_schema_description,
+
+ // Constants
+ att_constant_name = SERIES,
+ att_constant_package,
+ att_constant_field_source,
+ att_constant_private_flag,
+ att_constant_blr,
+ att_constant_source,
+ att_constant_schema_name,
+ att_constant_description,
};
diff --git a/src/burp/restore.epp b/src/burp/restore.epp
index 5d5a31765fb..ae52a139f44 100644
--- a/src/burp/restore.epp
+++ b/src/burp/restore.epp
@@ -167,6 +167,7 @@ bool get_trigger_old (BurpGlobals* tdgbl, burp_rel*);
bool get_type(BurpGlobals* tdgbl);
bool get_user_privilege(BurpGlobals* tdgbl);
bool get_view(BurpGlobals* tdgbl, burp_rel*);
+bool get_constants_table(BurpGlobals* tdgbl);
void ignore_array(BurpGlobals* tdgbl, burp_rel*);
void ignore_blob(BurpGlobals* tdgbl);
rec_type ignore_data(BurpGlobals* tdgbl, burp_rel*);
@@ -10556,6 +10557,85 @@ bool get_view(BurpGlobals* tdgbl, burp_rel* relation)
return true;
}
+bool get_constants_table(BurpGlobals* tdgbl)
+{
+ if (tdgbl->runtimeODS < DB_VERSION_DDL14) // FB6
+ return false;
+
+ QualifiedMetaString name;
+
+ att_type attribute;
+ scan_attr_t scan_next_attr;
+
+ Firebird::ITransaction* local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
+
+ STORE (TRANSACTION_HANDLE local_trans
+ REQUEST_HANDLE tdgbl->handles_get_type_req_handle1)
+ CONST IN RDB$CONSTANTS
+ {
+ skip_init(&scan_next_attr);
+ while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
+ {
+ switch (attribute)
+ {
+ case att_constant_name:
+ CONST.RDB$CONSTANT_NAME.NULL = FALSE;
+ GET_TEXT(CONST.RDB$CONSTANT_NAME);
+ name.object = CONST.RDB$CONSTANT_NAME;
+ break;
+
+ case att_constant_package:
+ CONST.RDB$PACKAGE_NAME.NULL = FALSE;
+ GET_TEXT(CONST.RDB$PACKAGE_NAME);
+ name.package = CONST.RDB$PACKAGE_NAME;
+ break;
+
+ case att_constant_field_source:
+ CONST.RDB$FIELD_SOURCE.NULL = FALSE;
+ GET_TEXT(CONST.RDB$FIELD_SOURCE);
+ break;
+
+ case att_constant_private_flag:
+ CONST.RDB$PRIVATE_FLAG.NULL = FALSE;
+ CONST.RDB$PRIVATE_FLAG = (USHORT) get_int32(tdgbl);
+ break;
+
+ case att_constant_blr:
+ CONST.RDB$CONSTANT_BLR.NULL = FALSE;
+ get_blr_blob(tdgbl, CONST.RDB$CONSTANT_BLR, true);
+ break;
+
+ case att_constant_source:
+ CONST.RDB$CONSTANT_SOURCE.NULL = FALSE;
+ get_source_blob(tdgbl, CONST.RDB$CONSTANT_SOURCE, true);
+ break;
+
+ case att_constant_schema_name:
+ CONST.RDB$SCHEMA_NAME.NULL = FALSE;
+ GET_TEXT(CONST.RDB$SCHEMA_NAME);
+ name.schema = CONST.RDB$SCHEMA_NAME;
+ break;
+
+ case att_constant_description:
+ CONST.RDB$DESCRIPTION.NULL = FALSE;
+ get_source_blob(tdgbl, CONST.RDB$DESCRIPTION, true);
+ break;
+ default:
+ bad_attribute(scan_next_attr, attribute, USHORT(isc_gbak_constant));
+ break;
+ }
+ }
+
+ BURP_verbose(USHORT(isc_gbak_restoring_constant), SafeArg() << name.toQuotedString().data());
+ }
+ END_STORE;
+ ON_ERROR
+ general_on_error();
+ END_ERROR;
+
+ return true;
+}
+
void ignore_array(BurpGlobals* tdgbl, burp_rel* relation)
{
/**************************************
@@ -11399,6 +11479,12 @@ bool restore(BurpGlobals* tdgbl, Firebird::IProvider* provider, const TEXT* file
flag = true;
break;
+ case rec_constants:
+ if (!get_constants_table(tdgbl))
+ return false;
+ flag = true;
+ break;
+
default:
BURP_error(43, true, SafeArg() << record);
// msg 43 don't recognize record type %ld
diff --git a/src/common/ParserTokens.h b/src/common/ParserTokens.h
index 3ddad43eb78..8dfd004bc2e 100644
--- a/src/common/ParserTokens.h
+++ b/src/common/ParserTokens.h
@@ -138,6 +138,7 @@ PARSER_TOKEN(TOK_CONDITIONAL, "CONDITIONAL", true)
PARSER_TOKEN(TOK_CONNECT, "CONNECT", false)
PARSER_TOKEN(TOK_CONNECTIONS, "CONNECTIONS", true)
PARSER_TOKEN(TOK_CONSISTENCY, "CONSISTENCY", true)
+PARSER_TOKEN(TOK_CONSTANT, "CONSTANT", true)
PARSER_TOKEN(TOK_CONSTRAINT, "CONSTRAINT", false)
PARSER_TOKEN(TOK_CONTAINING, "CONTAINING", true)
PARSER_TOKEN(TOK_CONTINUE, "CONTINUE", true)
diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp
index d0189ad4294..6b286b9f7bd 100644
--- a/src/dsql/DdlNodes.epp
+++ b/src/dsql/DdlNodes.epp
@@ -80,6 +80,7 @@
#include "../jrd/ini.h"
#include "../jrd/GarbageCollector.h"
#include "../jrd/ProtectRelations.h"
+#include "../dsql/PackageNodes.h"
namespace Jrd {
@@ -131,16 +132,6 @@ static rel_t relationType(SSHORT relationTypeNull, SSHORT relationType) noexcept
static void saveField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const MetaName& fieldName);
static dsql_rel* saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
const QualifiedName& relationName, bool view, bool creating);
-static void updateRdbFields(const TypeClause* type,
- SSHORT& fieldType,
- SSHORT& fieldLength,
- SSHORT& fieldSubTypeNull, SSHORT& fieldSubType,
- SSHORT& fieldScaleNull, SSHORT& fieldScale,
- SSHORT& characterSetIdNull, SSHORT& characterSetId,
- SSHORT& characterLengthNull, SSHORT& characterLength,
- SSHORT& fieldPrecisionNull, SSHORT& fieldPrecision,
- SSHORT& collationIdNull, SSHORT& collationId,
- SSHORT& segmentLengthNull, SSHORT& segmentLength);
static ISC_STATUS getErrorCodeByObjectType(int obj_type);
static constexpr const char* CHECK_CONSTRAINT_EXCEPTION = "check_constraint";
@@ -900,7 +891,7 @@ static dsql_rel* saveRelation(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
}
// Update RDB$FIELDS received by reference.
-static void updateRdbFields(const TypeClause* type,
+void DdlNode::updateRdbFields(const TypeClause* type,
SSHORT& fieldType,
SSHORT& fieldLength,
SSHORT& fieldSubTypeNull, SSHORT& fieldSubType,
@@ -1508,6 +1499,19 @@ DdlNode* CommentOnNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
dsqlScratch->resolveRoutineOrRelation(name, {objType});
break;
+ case obj_package_constant:
+ {
+ QualifiedName constantName(subName, name.schema, name.object); // name is a package
+ dsqlScratch->qualifyNewName(constantName); // Search for a schema
+
+ if (!PackageReferenceNode::constantExists(tdbb, transaction, constantName))
+ {
+ status_exception::raise(Arg::Gds(isc_bad_constant_name) << constantName.toQuotedString());
+ }
+ name = constantName;
+ break;
+ }
+
default:
dsqlScratch->qualifyExistingName(name, objType);
break;
@@ -1612,6 +1616,10 @@ void CommentOnNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
SCL_check_package(tdbb, name, SCL_alter);
break;
+ case obj_package_constant:
+ SCL_check_package(tdbb, name.getSchemaAndPackage(), SCL_alter);
+ break;
+
default:
fb_assert(false);
}
@@ -1775,6 +1783,12 @@ void CommentOnNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, j
status << Arg::Gds(isc_dyn_package_not_found) << Arg::Str(objNameStr);
break;
+ case obj_package_constant:
+ tableClause = "rdb$constants";
+ columnClause = "rdb$constant_name";
+ status << Arg::Gds(isc_bad_constant_name) << Arg::Str(objNameStr);
+ break;
+
default:
fb_assert(false);
return;
@@ -1798,7 +1812,7 @@ void CommentOnNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, j
sql << "and" << subColumnClause << "=" << subName;
}
- if (objType == obj_procedure || objType == obj_udf)
+ if (objType == obj_procedure || objType == obj_udf || objType == obj_package_constant)
sql << "and rdb$package_name is not distinct from nullif(" << name.package << ", '')";
if (addWhereClause)
@@ -11762,11 +11776,13 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
ValueExprNode* nameNode = fieldNode;
const char* aliasName = NULL;
- while (nodeIs(nameNode) || nodeIs(nameNode) || nodeIs(nameNode))
+ while (nodeIs(nameNode) || nodeIs(nameNode) || nodeIs(nameNode) ||
+ nodeAs(nameNode))
{
DsqlAliasNode* aliasNode;
DsqlMapNode* mapNode;
DerivedFieldNode* derivedField;
+ PackageReferenceNode* referenceNode;
if ((aliasNode = nodeAs(nameNode)))
{
@@ -11782,6 +11798,13 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
aliasName = derivedField->name.c_str();
nameNode = derivedField->value;
}
+ else if ((referenceNode = nodeAs(nameNode)))
+ {
+ if (!aliasName)
+ aliasName = referenceNode->getName();
+
+ nameNode = nullptr;
+ }
}
const dsql_fld* nameField = NULL;
@@ -15750,6 +15773,21 @@ static bool checkObjectExist(thread_db* tdbb, jrd_tra* transaction, const Qualif
END_FOR
break;
}
+ case obj_package_constant:
+ {
+ static const CachedRequestId requestId;
+ AutoCacheRequest request(tdbb, requestId);
+ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ X IN RDB$CONSTANTS
+ WITH X.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ X.RDB$PACKAGE_NAME EQ name.package.c_str() AND
+ X.RDB$CONSTANT_NAME EQ name.object.c_str()
+ {
+ rc = true;
+ }
+ END_FOR
+ break;
+ }
}
return rc;
@@ -15961,6 +15999,11 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G
if (!checkObjectExist(tdbb, transaction, QualifiedName(objName.schema), obj_schema))
status_exception::raise(Arg::Gds(isc_dyn_schema_not_found) << objName.toQuotedString());
+ case obj_package_constant:
+ if (!checkObjectExist(tdbb, transaction, objName, objType))
+ status_exception::raise(Arg::PrivateDyn(303) << objName.toQuotedString()); // Package @1 does not exist
+ break;
+
default:
fb_assert(object == NULL || isDdlObject(objType));
}
@@ -16168,6 +16211,7 @@ void GrantRevokeNode::grantRevoke(thread_db* tdbb, jrd_tra* transaction, const G
case obj_generator:
case obj_package_header:
case obj_schema:
+ case obj_package_constant:
{
checkGrantorCanGrantObject(tdbb, transaction, currentUser.c_str(), priv, objName, objType);
break;
@@ -17186,6 +17230,9 @@ static ISC_STATUS getErrorCodeByObjectType(int obj_type)
case obj_package_body:
err_code = isc_package_name;
break;
+ case obj_package_constant:
+ err_code = isc_const_name;
+ break;
default:
fb_assert(false);
}
diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp
index 29eac015e71..4dc26d20d99 100644
--- a/src/dsql/ExprNodes.cpp
+++ b/src/dsql/ExprNodes.cpp
@@ -67,6 +67,8 @@
#include "../jrd/trace/TraceObjects.h"
#include "../jrd/trace/TraceJrdHelpers.h"
+#include "../dsql/PackageNodes.h"
+
using namespace Firebird;
using namespace Jrd;
@@ -466,6 +468,23 @@ void ExprNode::collectStreams(SortedStreamList& streamList) const
}
}
+bool ExprNode::isChildrenConstant() const
+{
+ NodeRefsHolder holder;
+ getChildren(holder, false);
+
+ for (auto i : holder.refs)
+ {
+ if (*i == nullptr)
+ continue;
+
+ if (!(*i)->constant())
+ return false;
+ }
+
+ return true;
+}
+
bool ExprNode::computable(CompilerScratch* csb, StreamType stream,
bool allowOnlyCurrentStream, ValueExprNode* /*value*/)
{
@@ -6448,6 +6467,33 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec
}
}
+ // Use context to check conflicts beween . and .
+ dsql_ctx packageContext(dsqlScratch->getPool());
+ { // Consatnts
+
+ QualifiedName constantName(dsqlName,
+ dsqlQualifier.schema.hasData() ? dsqlQualifier.schema : dsqlScratch->package.schema,
+ dsqlQualifier.object.hasData() ? dsqlQualifier.object : dsqlScratch->package.object);
+
+ if (constantName.package.hasData())
+ {
+ dsqlScratch->qualifyNewName(constantName);
+
+ if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), constantName))
+ {
+ packageContext.ctx_relation = nullptr;
+ packageContext.ctx_procedure = nullptr;
+ // Alias is a package name, not a constant
+ packageContext.ctx_alias.push(QualifiedName(constantName.package, constantName.schema));
+ packageContext.ctx_flags |= CTX_package;
+ ambiguousCtxStack.push(&packageContext);
+
+ MemoryPool& pool = dsqlScratch->getPool();
+ node = FB_NEW_POOL(pool) PackageReferenceNode(pool, constantName);
+ }
+ }
+ }
+
// CVC: We can't return blindly if this is a check constraint, because there's
// the possibility of an invalid field that wasn't found. The multiple places that
// call this function pass1_field() don't expect a NULL pointer, hence will crash.
@@ -12462,7 +12508,12 @@ void SysFuncCallNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc)
bool SysFuncCallNode::deterministic(thread_db* tdbb) const
{
- return ExprNode::deterministic(tdbb) && function->deterministic;
+ return ExprNode::deterministic(tdbb) && function->isDeterministic();
+}
+
+bool SysFuncCallNode::constant() const
+{
+ return ExprNode::isChildrenConstant() && function->isConstant();
}
void SysFuncCallNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc)
@@ -14149,6 +14200,17 @@ ValueExprNode* VariableNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
if (!node->dsqlVar ||
(node->dsqlVar->type == dsql_var::TYPE_LOCAL && !node->dsqlVar->initialized && !dsqlScratch->mainScratch))
{
+ if (dsqlScratch->package.object.hasData())
+ {
+ thread_db* tdbb = JRD_get_thread_data();
+ QualifiedName constantFullName(dsqlName, dsqlScratch->package.schema, dsqlScratch->package.object);
+ if (PackageReferenceNode::constantExists(tdbb, dsqlScratch->getTransaction(), constantFullName))
+ {
+ delete node;
+ return FB_NEW_POOL(dsqlScratch->getPool()) PackageReferenceNode(dsqlScratch->getPool(), constantFullName);
+ }
+ }
+
PASS1_field_unknown(NULL, dsqlName.toQuotedString().c_str(), this);
}
diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h
index 108daf7d984..9b196cdb6f4 100644
--- a/src/dsql/ExprNodes.h
+++ b/src/dsql/ExprNodes.h
@@ -88,6 +88,11 @@ class ArithmeticNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) override;
@@ -236,6 +246,11 @@ class BoolAsValueNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void setDsqlDesc(const dsc& desc)
{
dsqlDesc = desc;
@@ -320,6 +340,11 @@ class CoalesceNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
bool possiblyUnknown() const override
{
return true;
@@ -655,6 +695,11 @@ class DefaultNode final : public DsqlNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override;
ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb) override;
@@ -714,6 +759,11 @@ class DerivedExprNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) override;
@@ -1315,6 +1385,11 @@ class NullNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return true;
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
dsc* execute(thread_db* tdbb, Request* request) const override;
@@ -1834,6 +1909,11 @@ class ScalarNode final : public TypedNode
fb_assert(false);
}
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) override;
@@ -1925,6 +2005,11 @@ class StrCaseNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override;
@@ -2061,6 +2151,11 @@ class SubstringNode final : public TypedNode
void genBlr(DsqlCompilerScratch* dsqlScratch) override;
void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) override;
+ bool constant() const override
+ {
+ return isChildrenConstant();
+ }
+
void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) override;
ValueExprNode* copy(thread_db* tdbb, NodeCopier& copier) const override;
bool dsqlMatch(DsqlCompilerScratch* dsqlScratch, const ExprNode* other, bool ignoreMapCast) const override;
@@ -2275,6 +2381,11 @@ class ValueIfNode final : public TypedNode > MetaNameBidPair;
typedef Firebird::GenericMap MetaNameBidMap;
@@ -519,6 +531,7 @@ class ExprNode : public DmlNode
TYPE_WINDOW_CLAUSE,
TYPE_WINDOW_CLAUSE_FRAME,
TYPE_WINDOW_CLAUSE_FRAME_EXTENT,
+ TYPE_REFERENCE,
// Bool types
TYPE_BINARY_BOOL,
@@ -677,6 +690,9 @@ class ExprNode : public DmlNode
}
// Check if expression returns deterministic result
+ // Determinate whether the node is volatile (or not) in the current execution context.
+ // For example, a DBKEY is deterministic (it cannot change for an already fetched row)
+ // but it's not constant (and thus cannot be used as an initializer expression).
virtual bool deterministic(thread_db* tdbb) const;
// Check if expression could return NULL or expression can turn NULL into a true/false.
@@ -688,9 +704,18 @@ class ExprNode : public DmlNode
// Verify if this node is allowed in an unmapped boolean.
virtual bool unmappable(const MapNode* mapNode, StreamType shellStream) const;
+ // Check if expression returns constant result
+ // The result true means the value does not change after recompilation
+ virtual bool constant() const
+ {
+ return false;
+ }
+
// Return all streams referenced by the expression.
virtual void collectStreams(SortedStreamList& streamList) const;
+ bool isChildrenConstant() const;
+
bool containsStream(StreamType stream, bool only = false) const
{
SortedStreamList nodeStreams;
@@ -1342,6 +1367,17 @@ class ValueListNode : public TypedNode
virtual void getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc);
+ virtual bool constant() const override
+ {
+ for (auto& child : items)
+ {
+ if (!child->constant())
+ return false;
+ }
+
+ return true;
+ }
+
ValueListNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override
{
ValueListNode* node = FB_NEW_POOL(dsqlScratch->getPool()) ValueListNode(dsqlScratch->getPool(),
diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp
index 42f888c9cb0..d267fb87ef5 100644
--- a/src/dsql/PackageNodes.epp
+++ b/src/dsql/PackageNodes.epp
@@ -38,6 +38,14 @@
#include "../jrd/Attachment.h"
#include "../jrd/scl_proto.h"
+#include "../common/dsc_proto.h" // DSC_make_descriptor
+#include "../jrd/Package.h" // Constant
+
+#include "../dsql/metd_proto.h" // METD_get_domain
+#include "../common/classes/VaryStr.h" // METD_get_domain
+#include "../jrd/par_proto.h" // PAR_proto in Nodes.h (dependency hell)
+#include "../jrd/met.h" // Metacache
+#include "../jrd/Statement.h" // Jrd::Statement
using namespace Firebird;
@@ -53,155 +61,785 @@ DATABASE DB = STATIC "ODS.RDB";
namespace
{
- // Return function and procedure names (in the user charset) and optionally its details for a
- // given package.
- void collectPackagedItems(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& packageName,
- SortedObjectsArray& functions,
- SortedObjectsArray& procedures, bool details)
+
+class DropConstantNode
+{
+public:
+ DropConstantNode(Firebird::MemoryPool& pool, const QualifiedName& fullName) :
+ m_name(pool, fullName)
+ { }
+
+ void dsqlPass(DsqlCompilerScratch* dsqlScratch)
+ {
+ dsqlScratch->qualifyExistingName(m_name, obj_package_constant);
+ }
+
+ void execute(thread_db* tdbb, DsqlCompilerScratch*, jrd_tra* transaction)
+ {
+ static const CachedRequestId requestId;
+ AutoCacheRequest request(tdbb, requestId);
+
+ FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
+ CONST IN RDB$CONSTANTS
+ WITH CONST.RDB$SCHEMA_NAME EQ m_name.schema.c_str() AND
+ CONST.RDB$PACKAGE_NAME EQ m_name.package.c_str() AND
+ CONST.RDB$CONSTANT_NAME EQ m_name.object.c_str()
+ {
+ ERASE CONST;
+ }
+ END_FOR
+ }
+
+private:
+ QualifiedName m_name;
+};
+
+
+template
+void dropItem(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& fullName)
+{
+ MemoryPool& pool = dsqlScratch->getPool();
+ jrd_tra* transaction = dsqlScratch->getTransaction();
+
+ TDropNode dropNode(pool, fullName);
+ dropNode.dsqlPass(dsqlScratch);
+ dropNode.execute(tdbb, dsqlScratch, transaction);
+}
+
+template
+void dropMissingItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema,
+ const PackageItemsHolder::ItemsSignatureArray& existsItems,
+ const CreateAlterPackageNode::ItemsNameArray& newNames)
+{
+ for (auto i = existsItems.begin(); i != existsItems.end(); ++i)
+ {
+ if (!newNames.exist(i->name))
+ {
+ dropItem(tdbb, dsqlScratch,
+ QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.object));
+ }
+ }
+}
+
+template
+void dropItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema,
+ const PackageItemsHolder::ItemsSignatureArray& items)
+{
+ for (auto i = items.begin(); i != items.end(); ++i)
+ {
+ dropItem(tdbb, dsqlScratch,
+ QualifiedName(i->name, packageAndSchema.schema, packageAndSchema.object));
+ }
+}
+
+void checkItemsDefineMatch(MemoryPool& pool, const QualifiedName& packageAndSchema,
+ const PackageItemsHolder::ItemsSignatureArray& newItems,
+ const PackageItemsHolder::ItemsSignatureArray& existingItems,
+ const ISC_STATUS missingCode, const ISC_STATUS mismatchCode)
+{
+ for (auto i = existingItems.begin(); i != existingItems.end(); ++i)
+ {
+ FB_SIZE_T pos;
+ const bool found = newItems.find(Signature(pool, i->name), pos);
+
+ if (!found || !newItems[pos].defined)
+ {
+ status_exception::raise(
+ Arg::Gds(missingCode) << i->name << packageAndSchema.toQuotedString());
+ }
+ else if (newItems[pos] != *i)
+ {
+ status_exception::raise(
+ Arg::Gds(mismatchCode) << i->name << packageAndSchema.toQuotedString());
+ }
+ }
+}
+
+} // namespace
+
+
+//----------------------
+
+void PackageItemsHolder::drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema)
+{
+ dropItems(tdbb, dsqlScratch, packageAndSchema, functions);
+ dropItems(tdbb, dsqlScratch, packageAndSchema, procedures);
+ dropItems(tdbb, dsqlScratch, packageAndSchema, constants);
+}
+
+void PackageItemsHolder::checkDefineMatch(MemoryPool& pool, const QualifiedName& packageAndSchema,
+ const PackageItemsHolder& newItems)
+{
+ checkItemsDefineMatch(pool, packageAndSchema, newItems.functions, functions,
+ isc_dyn_funcnotdef_package, isc_dyn_funcsignat_package);
+ checkItemsDefineMatch(pool, packageAndSchema, newItems.procedures, procedures,
+ isc_dyn_procnotdef_package, isc_dyn_procsignat_package);
+}
+
+// Return function and procedure names (in the user charset) and optionally its details for a
+// given package.
+void PackageItemsHolder::collectPackagedItems(thread_db* tdbb, jrd_tra* transaction,
+ const QualifiedName& packageName,
+ const bool details, const bool collectConstants)
+{
+ AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS);
+ AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS);
+
+ FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
+ FUN IN RDB$FUNCTIONS
+ WITH FUN.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND
+ FUN.RDB$PACKAGE_NAME EQ packageName.object.c_str()
{
- AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS);
- AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS);
+ Signature function(FUN.RDB$FUNCTION_NAME);
+ function.defined = !FUN.RDB$FUNCTION_BLR.NULL || !FUN.RDB$ENTRYPOINT.NULL;
+
+ if (!FUN.RDB$DETERMINISTIC_FLAG.NULL && FUN.RDB$DETERMINISTIC_FLAG != 0)
+ function.flags |= Signature::FLAG_DETERMINISTIC;
- FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
- FUN IN RDB$FUNCTIONS
- WITH FUN.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND
- FUN.RDB$PACKAGE_NAME EQ packageName.object.c_str()
+ if (details)
{
- Signature function(FUN.RDB$FUNCTION_NAME);
- function.defined = !FUN.RDB$FUNCTION_BLR.NULL || !FUN.RDB$ENTRYPOINT.NULL;
+ FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
+ ARG IN RDB$FUNCTION_ARGUMENTS CROSS
+ FLD IN RDB$FIELDS
+ WITH ARG.RDB$SCHEMA_NAME EQ FUN.RDB$SCHEMA_NAME AND
+ ARG.RDB$PACKAGE_NAME EQ FUN.RDB$PACKAGE_NAME AND
+ ARG.RDB$FUNCTION_NAME EQ FUN.RDB$FUNCTION_NAME AND
+ FLD.RDB$SCHEMA_NAME EQUIV ARG.RDB$FIELD_SOURCE_SCHEMA_NAME AND
+ FLD.RDB$FIELD_NAME EQ ARG.RDB$FIELD_SOURCE
+ {
+ SignatureParameter parameter(*getDefaultMemoryPool());
+
+ parameter.number = ARG.RDB$ARGUMENT_POSITION;
+ parameter.name = ARG.RDB$ARGUMENT_NAME;
+ parameter.fieldSource = QualifiedName(ARG.RDB$FIELD_SOURCE, ARG.RDB$FIELD_SOURCE_SCHEMA_NAME);
+ parameter.mechanism = ARG.RDB$ARGUMENT_MECHANISM;
+
+ if (!ARG.RDB$FIELD_NAME.NULL)
+ parameter.fieldName = QualifiedName(ARG.RDB$FIELD_NAME);
+ if (!ARG.RDB$RELATION_NAME.NULL)
+ parameter.relationName = QualifiedName(ARG.RDB$RELATION_NAME, ARG.RDB$RELATION_SCHEMA_NAME);
+ if (!ARG.RDB$COLLATION_ID.NULL)
+ parameter.collationId = CollId(ARG.RDB$COLLATION_ID);
+ if (!ARG.RDB$NULL_FLAG.NULL)
+ parameter.nullFlag = ARG.RDB$NULL_FLAG;
+
+ if (!FLD.RDB$FIELD_LENGTH.NULL)
+ parameter.fieldLength = FLD.RDB$FIELD_LENGTH;
+ if (!FLD.RDB$FIELD_SCALE.NULL)
+ parameter.fieldScale = FLD.RDB$FIELD_SCALE;
+ if (!FLD.RDB$FIELD_TYPE.NULL)
+ parameter.fieldType = FLD.RDB$FIELD_TYPE;
+ if (!FLD.RDB$FIELD_SUB_TYPE.NULL)
+ parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE;
+ if (!FLD.RDB$SEGMENT_LENGTH.NULL)
+ parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH;
+ if (!FLD.RDB$NULL_FLAG.NULL)
+ parameter.fieldNullFlag = FLD.RDB$NULL_FLAG;
+ if (!FLD.RDB$CHARACTER_LENGTH.NULL)
+ parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH;
+ if (!FLD.RDB$COLLATION_ID.NULL)
+ parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID);
+ if (!FLD.RDB$CHARACTER_SET_ID.NULL)
+ parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID);
+ if (!FLD.RDB$FIELD_PRECISION.NULL)
+ parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION;
+
+ function.parameters.add(parameter);
+ }
+ END_FOR
+ }
+
+ functions.add(function);
+ }
+ END_FOR
+
+ requestHandle.reset(tdbb, drq_l_pkg_procs, DYN_REQUESTS);
+ requestHandle2.reset(tdbb, drq_l_pkg_proc_args, DYN_REQUESTS);
- if (!FUN.RDB$DETERMINISTIC_FLAG.NULL && FUN.RDB$DETERMINISTIC_FLAG != 0)
- function.flags |= Signature::FLAG_DETERMINISTIC;
+ FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
+ PRC IN RDB$PROCEDURES
+ WITH PRC.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND
+ PRC.RDB$PACKAGE_NAME EQ packageName.object.c_str()
+ {
+ Signature procedure(PRC.RDB$PROCEDURE_NAME);
+ procedure.defined = !PRC.RDB$PROCEDURE_BLR.NULL || !PRC.RDB$ENTRYPOINT.NULL;
- if (details)
+ if (details)
+ {
+ FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
+ PRM IN RDB$PROCEDURE_PARAMETERS CROSS
+ FLD IN RDB$FIELDS
+ WITH PRM.RDB$SCHEMA_NAME EQ PRC.RDB$SCHEMA_NAME AND
+ PRM.RDB$PACKAGE_NAME EQ PRC.RDB$PACKAGE_NAME AND
+ PRM.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND
+ FLD.RDB$SCHEMA_NAME EQUIV PRM.RDB$FIELD_SOURCE_SCHEMA_NAME AND
+ FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE
{
- FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
- ARG IN RDB$FUNCTION_ARGUMENTS CROSS
- FLD IN RDB$FIELDS
- WITH ARG.RDB$SCHEMA_NAME EQ FUN.RDB$SCHEMA_NAME AND
- ARG.RDB$PACKAGE_NAME EQ FUN.RDB$PACKAGE_NAME AND
- ARG.RDB$FUNCTION_NAME EQ FUN.RDB$FUNCTION_NAME AND
- FLD.RDB$SCHEMA_NAME EQUIV ARG.RDB$FIELD_SOURCE_SCHEMA_NAME AND
- FLD.RDB$FIELD_NAME EQ ARG.RDB$FIELD_SOURCE
- {
- SignatureParameter parameter(*getDefaultMemoryPool());
-
- parameter.number = ARG.RDB$ARGUMENT_POSITION;
- parameter.name = ARG.RDB$ARGUMENT_NAME;
- parameter.fieldSource = QualifiedName(ARG.RDB$FIELD_SOURCE, ARG.RDB$FIELD_SOURCE_SCHEMA_NAME);
- parameter.mechanism = ARG.RDB$ARGUMENT_MECHANISM;
-
- if (!ARG.RDB$FIELD_NAME.NULL)
- parameter.fieldName = QualifiedName(ARG.RDB$FIELD_NAME);
- if (!ARG.RDB$RELATION_NAME.NULL)
- parameter.relationName = QualifiedName(ARG.RDB$RELATION_NAME, ARG.RDB$RELATION_SCHEMA_NAME);
- if (!ARG.RDB$COLLATION_ID.NULL)
- parameter.collationId = CollId(ARG.RDB$COLLATION_ID);
- if (!ARG.RDB$NULL_FLAG.NULL)
- parameter.nullFlag = ARG.RDB$NULL_FLAG;
-
- if (!FLD.RDB$FIELD_LENGTH.NULL)
- parameter.fieldLength = FLD.RDB$FIELD_LENGTH;
- if (!FLD.RDB$FIELD_SCALE.NULL)
- parameter.fieldScale = FLD.RDB$FIELD_SCALE;
- if (!FLD.RDB$FIELD_TYPE.NULL)
- parameter.fieldType = FLD.RDB$FIELD_TYPE;
- if (!FLD.RDB$FIELD_SUB_TYPE.NULL)
- parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE;
- if (!FLD.RDB$SEGMENT_LENGTH.NULL)
- parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH;
- if (!FLD.RDB$NULL_FLAG.NULL)
- parameter.fieldNullFlag = FLD.RDB$NULL_FLAG;
- if (!FLD.RDB$CHARACTER_LENGTH.NULL)
- parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH;
- if (!FLD.RDB$COLLATION_ID.NULL)
- parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID);
- if (!FLD.RDB$CHARACTER_SET_ID.NULL)
- parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID);
- if (!FLD.RDB$FIELD_PRECISION.NULL)
- parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION;
-
- function.parameters.add(parameter);
- }
- END_FOR
+ SignatureParameter parameter(*getDefaultMemoryPool());
+ parameter.type = PRM.RDB$PARAMETER_TYPE;
+ parameter.number = PRM.RDB$PARAMETER_NUMBER;
+ parameter.name = PRM.RDB$PARAMETER_NAME;
+ parameter.fieldSource = QualifiedName(PRM.RDB$FIELD_SOURCE, PRM.RDB$FIELD_SOURCE_SCHEMA_NAME);
+ parameter.mechanism = PRM.RDB$PARAMETER_MECHANISM;
+
+ if (!PRM.RDB$FIELD_NAME.NULL)
+ parameter.fieldName = QualifiedName(PRM.RDB$FIELD_NAME);
+ if (!PRM.RDB$RELATION_NAME.NULL)
+ parameter.relationName = QualifiedName(PRM.RDB$RELATION_NAME, PRM.RDB$RELATION_SCHEMA_NAME);
+ if (!PRM.RDB$COLLATION_ID.NULL)
+ parameter.collationId = CollId(PRM.RDB$COLLATION_ID);
+ if (!PRM.RDB$NULL_FLAG.NULL)
+ parameter.nullFlag = PRM.RDB$NULL_FLAG;
+
+ if (!FLD.RDB$FIELD_LENGTH.NULL)
+ parameter.fieldLength = FLD.RDB$FIELD_LENGTH;
+ if (!FLD.RDB$FIELD_SCALE.NULL)
+ parameter.fieldScale = FLD.RDB$FIELD_SCALE;
+ if (!FLD.RDB$FIELD_TYPE.NULL)
+ parameter.fieldType = FLD.RDB$FIELD_TYPE;
+ if (!FLD.RDB$FIELD_SUB_TYPE.NULL)
+ parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE;
+ if (!FLD.RDB$SEGMENT_LENGTH.NULL)
+ parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH;
+ if (!FLD.RDB$NULL_FLAG.NULL)
+ parameter.fieldNullFlag = FLD.RDB$NULL_FLAG;
+ if (!FLD.RDB$CHARACTER_LENGTH.NULL)
+ parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH;
+ if (!FLD.RDB$COLLATION_ID.NULL)
+ parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID);
+ if (!FLD.RDB$CHARACTER_SET_ID.NULL)
+ parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID);
+ if (!FLD.RDB$FIELD_PRECISION.NULL)
+ parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION;
+
+ procedure.parameters.add(parameter);
}
+ END_FOR
+ }
+
+ procedures.add(procedure);
+ }
+ END_FOR
- functions.add(function);
+ if (collectConstants)
+ {
+ static const CachedRequestId requestId;
+ AutoCacheRequest getConstantsRequest(tdbb, requestId);
+ FOR (REQUEST_HANDLE getConstantsRequest TRANSACTION_HANDLE transaction)
+ CONST IN RDB$CONSTANTS WITH
+ CONST.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND
+ CONST.RDB$PACKAGE_NAME EQ packageName.object.c_str()
+ {
+ Signature constant(CONST.RDB$CONSTANT_NAME);
+ constants.add(constant);
}
END_FOR
+ }
+}
+
+void PackageItemsHolder::clear()
+{
+ functions.clear();
+ procedures.clear();
+}
+
+//----------------------
+
+
+// -----------------------------------
+// PackageReferenceNode implementation
+// -----------------------------------
+
+static RegisterNode regPackageReferenceNode({blr_package_reference});
+
+PackageReferenceNode::PackageReferenceNode(MemoryPool& pool, const QualifiedName& fullName, const UCHAR itemType)
+ : TypedNode(pool),
+ m_fullName(fullName.object, fullName.schema.hasData() ? fullName.schema : PUBLIC_SCHEMA, fullName.package), m_itemType(itemType)
+{}
+
+string PackageReferenceNode::internalPrint(NodePrinter& printer) const
+{
+ ExprNode::internalPrint(printer);
+
+ NODE_PRINT(printer, m_fullName);
- requestHandle.reset(tdbb, drq_l_pkg_procs, DYN_REQUESTS);
- requestHandle2.reset(tdbb, drq_l_pkg_proc_args, DYN_REQUESTS);
+ return "PackageReferenceNode";
+}
+
+ValueExprNode* PackageReferenceNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
+{
+ MemoryPool& pool = dsqlScratch->getPool();
+
+ jrd_tra* transaction = dsqlScratch->getTransaction();
+ thread_db* tdbb = JRD_get_thread_data();
+
+ bool isPrivate = false;
+ if (constantExists(tdbb, transaction, m_fullName, &isPrivate))
+ {
+ // External objects do not have access to private constants
+ if (isPrivate)
+ {
+ status_exception::raise(Arg::Gds(isc_private_constant) <<
+ Arg::Str(m_fullName.toQuotedString()));
+ }
+ }
+ else
+ {
+ status_exception::raise(Arg::Gds(isc_not_defined_constant) <<
+ Arg::Str(m_fullName.toQuotedString()));
+ }
+
+ auto* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, m_fullName, m_itemType);
+ return node;
+}
+
+DmlNode* PackageReferenceNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR)
+{
+ const UCHAR itemType = csb->csb_blr_reader.getByte();
+
+ switch (itemType)
+ {
+ case blr_pkg_reference_to_constant:
+ {
+ QualifiedName fullName;
+ csb->csb_blr_reader.getMetaName(fullName.object);
+ csb->csb_blr_reader.getMetaName(fullName.schema);
+ csb->csb_blr_reader.getMetaName(fullName.package);
+
+ PackageReferenceNode* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, fullName, itemType);
- FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
- PRC IN RDB$PROCEDURES
- WITH PRC.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND
- PRC.RDB$PACKAGE_NAME EQ packageName.object.c_str()
+ if (csb->collectingDependencies())
{
- Signature procedure(PRC.RDB$PROCEDURE_NAME);
- procedure.defined = !PRC.RDB$PROCEDURE_BLR.NULL || !PRC.RDB$ENTRYPOINT.NULL;
+ Dependency dependency(obj_package_constant);
+ dependency.name = fullName;
- if (details)
+ csb->addDependency(dependency);
+ }
+
+ { // Package cache
+ auto package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE);
+ if (package)
{
- FOR (REQUEST_HANDLE requestHandle2 TRANSACTION_HANDLE transaction)
- PRM IN RDB$PROCEDURE_PARAMETERS CROSS
- FLD IN RDB$FIELDS
- WITH PRM.RDB$SCHEMA_NAME EQ PRC.RDB$SCHEMA_NAME AND
- PRM.RDB$PACKAGE_NAME EQ PRC.RDB$PACKAGE_NAME AND
- PRM.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND
- FLD.RDB$SCHEMA_NAME EQUIV PRM.RDB$FIELD_SOURCE_SCHEMA_NAME AND
- FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE
+ node->m_package = csb->csb_resources->packages.registerResource(package->getPermanent());
+ }
+ }
+
+ if (!node->m_package)
+ status_exception::raise(Arg::Gds(isc_bad_constant_name) <<
+ Arg::Str(fullName.toQuotedString()));
+
+ return node;
+ }
+ // TODO: rowtype
+ default:
+ fb_assert(false);
+ }
+
+ return nullptr;
+}
+
+void PackageReferenceNode::genBlr(DsqlCompilerScratch* dsqlScratch)
+{
+ dsqlScratch->appendUChar(blr_package_reference);
+ dsqlScratch->appendUChar(blr_pkg_reference_to_constant);
+ dsqlScratch->appendMetaString(m_fullName.object.c_str());
+ dsqlScratch->appendMetaString(m_fullName.schema.c_str());
+ dsqlScratch->appendMetaString(m_fullName.package.c_str());
+}
+
+void PackageReferenceNode::setParameterName(dsql_par* parameter) const
+{
+ parameter->par_name = parameter->par_alias = m_fullName.object;
+}
+
+void PackageReferenceNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc)
+{
+ jrd_tra* transaction = dsqlScratch->getTransaction();
+ thread_db* tdbb = JRD_get_thread_data();
+ *desc = ConstantValue::getDesc(tdbb, transaction, m_fullName);
+}
+
+bool PackageReferenceNode::constantExists(thread_db* tdbb, Jrd::jrd_tra* transaction,
+ const QualifiedName& fullName, bool* isPrivate)
+{
+ fb_assert(fullName.schema.hasData());
+
+ if (fullName.package.isEmpty() || fullName.object.isEmpty())
+ return false;
+
+ auto* package = MetadataCache::getVersioned(tdbb, fullName.getSchemaAndPackage(), CacheFlag::AUTOCREATE);
+ if (package == nullptr)
+ return false;
+
+ const ConstantValue* value = package->findConstant(fullName);
+
+ if (value == nullptr)
+ return false;
+
+ if (isPrivate)
+ *isPrivate = value->isPrivate;
+
+ return true;
+}
+
+void PackageReferenceNode::getDesc(thread_db* tdbb, CompilerScratch*, dsc* desc)
+{
+ *desc = ConstantValue::getDesc(tdbb, tdbb->getTransaction(), m_fullName);
+}
+
+ValueExprNode* PackageReferenceNode::copy(thread_db* tdbb, NodeCopier& copier) const
+{
+ MemoryPool& pool = *tdbb->getDefaultPool();
+ auto* node = FB_NEW_POOL(pool) PackageReferenceNode(pool, m_fullName, m_itemType);
+
+ node->m_package = copier.csb->csb_resources->packages.registerResource(m_package());
+
+ return node;
+}
+
+ValueExprNode* PackageReferenceNode::pass1(thread_db* tdbb, CompilerScratch* csb)
+{
+ ValueExprNode::pass1(tdbb, csb);
+
+ auto& security = m_package()->securityName;
+
+ CMP_post_access(tdbb, csb, security.schema, 0, SCL_usage, obj_schemas, QualifiedName(m_fullName.schema));
+
+ switch (m_itemType)
+ {
+ case blr_pkg_reference_to_constant:
+ {
+ CMP_post_access(tdbb, csb, security.object, 0, SCL_usage, obj_packages, m_fullName.getSchemaAndPackage());
+ break;
+ }
+ default:
+ fb_assert(false);
+ return nullptr;
+ }
+
+ return this;
+}
+
+ValueExprNode* PackageReferenceNode::pass2(thread_db* tdbb, CompilerScratch* csb)
+{
+ ValueExprNode::pass2(tdbb, csb);
+
+ if (m_itemType == blr_pkg_reference_to_constant)
+ m_impureOffset = csb->allocImpure();
+
+ return this;
+}
+
+dsc* PackageReferenceNode::execute(thread_db* tdbb, Request* request) const
+{
+ impure_value* outputImpure = request->getImpure(m_impureOffset);
+ switch (m_itemType)
+ {
+ case blr_pkg_reference_to_constant:
+ {
+ Package* package = m_package(request->getResources());
+ package->checkReload(tdbb);
+
+ EVL_make_value(tdbb, &package->findConstant(m_fullName)->makeValue(tdbb), outputImpure);
+ return &outputImpure->vlu_desc;
+ }
+ default:
+ fb_assert(false);
+ return nullptr;
+ }
+}
+
+
+// ----------------------------------------
+// CreatePackageConstantNode
+// ----------------------------------------
+
+string CreatePackageConstantNode::internalPrint(NodePrinter& printer) const
+{
+ DdlNode::internalPrint(printer);
+
+ NODE_PRINT(printer, name);
+ NODE_PRINT(printer, m_type);
+ NODE_PRINT(printer, m_expr);
+ NODE_PRINT(printer, m_isPrivate);
+
+ return "PackageReferenceNode";
+}
+
+DdlNode* CreatePackageConstantNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
+{
+ dsqlScratch->qualifyNewName(name);
+
+ m_expr = m_expr->dsqlPass(dsqlScratch);
+
+ QualifiedName dummyCollationName;
+ DDL_resolve_intl_type(dsqlScratch, m_type, dummyCollationName);
+
+ if (!m_expr->constant())
+ {
+ status_exception::raise(Arg::Gds(isc_dyn_non_constant_constant) << name.toQuotedString());
+ }
+
+ DdlNode::dsqlPass(dsqlScratch);
+ return nullptr;
+}
+
+void CreatePackageConstantNode::checkPermission(thread_db* tdbb, jrd_tra* transaction)
+{ }
+
+void CreatePackageConstantNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction)
+{
+ if (create)
+ {
+ if (alter && executeAlter(tdbb, dsqlScratch, transaction))
+ {
+ return;
+ }
+
+ executeCreate(tdbb, dsqlScratch, transaction);
+ }
+ else
+ executeAlter(tdbb, dsqlScratch, transaction);
+
+ fb_assert(m_id);
+}
+
+void CreatePackageConstantNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction)
+{
+ Attachment* attachment = transaction->getAttachment();
+
+ // Check uniqueness
+ fb_assert(package != nullptr);
+ if (package->findConstant(name))
+ {
+ status_exception::raise(Arg::Gds(isc_dyn_dup_const) << name.toQuotedString());
+ }
+
+ // Generate a new unique field name because constants in different packages may have same name
+ // but names in RDB$FIELDS should be unique in for a SCHEMA
+ QualifiedName fieldName = name;
+ fieldName.object = "";
+ // Store description in the RDB$FIELDS relation
+ storeGlobalField(tdbb, tdbb->getTransaction(), fieldName, m_type);
+ fb_assert(fieldName.schema == name.schema);
+ fb_assert(fieldName.package == name.package);
+
+ static const CachedRequestId requestId;
+ AutoCacheRequest storeConstantRequest(tdbb, requestId);
+
+ int faults = 0;
+ while (true)
+ {
+ try
+ {
+ SINT64 id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_const_id, CONSTANTS_GENERATOR);
+ id %= (MAX_SSHORT + 1);
+ if (!id)
+ continue;
+
+ m_id = id;
+
+ // Store desc and metainfo in the RDB$CONSTANTS relation
+ STORE (REQUEST_HANDLE storeConstantRequest TRANSACTION_HANDLE transaction)
+ CONST IN RDB$CONSTANTS USING
+ {
+ // Constant name
+ CONST.RDB$CONSTANT_NAME.NULL = FALSE;
+ strcpy(CONST.RDB$CONSTANT_NAME, name.object.c_str());
+
+ // Constant unique id
+ CONST.RDB$CONSTANT_ID = id;
+
+ // Description (filed) name
+ CONST.RDB$FIELD_SOURCE.NULL = FALSE;
+ strcpy(CONST.RDB$FIELD_SOURCE, fieldName.object.c_str());
+
+ // Gen value blr
+ ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema);
+
+ // Put the blr
+ attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData());
+
+ // Parent package
+ fb_assert(name.package.hasData());
{
- SignatureParameter parameter(*getDefaultMemoryPool());
- parameter.type = PRM.RDB$PARAMETER_TYPE;
- parameter.number = PRM.RDB$PARAMETER_NUMBER;
- parameter.name = PRM.RDB$PARAMETER_NAME;
- parameter.fieldSource = QualifiedName(PRM.RDB$FIELD_SOURCE, PRM.RDB$FIELD_SOURCE_SCHEMA_NAME);
- parameter.mechanism = PRM.RDB$PARAMETER_MECHANISM;
-
- if (!PRM.RDB$FIELD_NAME.NULL)
- parameter.fieldName = QualifiedName(PRM.RDB$FIELD_NAME);
- if (!PRM.RDB$RELATION_NAME.NULL)
- parameter.relationName = QualifiedName(PRM.RDB$RELATION_NAME, PRM.RDB$RELATION_SCHEMA_NAME);
- if (!PRM.RDB$COLLATION_ID.NULL)
- parameter.collationId = CollId(PRM.RDB$COLLATION_ID);
- if (!PRM.RDB$NULL_FLAG.NULL)
- parameter.nullFlag = PRM.RDB$NULL_FLAG;
-
- if (!FLD.RDB$FIELD_LENGTH.NULL)
- parameter.fieldLength = FLD.RDB$FIELD_LENGTH;
- if (!FLD.RDB$FIELD_SCALE.NULL)
- parameter.fieldScale = FLD.RDB$FIELD_SCALE;
- if (!FLD.RDB$FIELD_TYPE.NULL)
- parameter.fieldType = FLD.RDB$FIELD_TYPE;
- if (!FLD.RDB$FIELD_SUB_TYPE.NULL)
- parameter.fieldSubType = FLD.RDB$FIELD_SUB_TYPE;
- if (!FLD.RDB$SEGMENT_LENGTH.NULL)
- parameter.fieldSegmentLength = FLD.RDB$SEGMENT_LENGTH;
- if (!FLD.RDB$NULL_FLAG.NULL)
- parameter.fieldNullFlag = FLD.RDB$NULL_FLAG;
- if (!FLD.RDB$CHARACTER_LENGTH.NULL)
- parameter.fieldCharLength = FLD.RDB$CHARACTER_LENGTH;
- if (!FLD.RDB$COLLATION_ID.NULL)
- parameter.fieldCollationId = CollId(FLD.RDB$COLLATION_ID);
- if (!FLD.RDB$CHARACTER_SET_ID.NULL)
- parameter.fieldCharSetId = CSetId(FLD.RDB$CHARACTER_SET_ID);
- if (!FLD.RDB$FIELD_PRECISION.NULL)
- parameter.fieldPrecision = FLD.RDB$FIELD_PRECISION;
-
- procedure.parameters.add(parameter);
+ CONST.RDB$PACKAGE_NAME.NULL = FALSE;
+ strcpy(CONST.RDB$PACKAGE_NAME, name.package.c_str());
+
+ fb_assert(package);
+ package->addConstant(tdbb, m_id, name, m_isPrivate, m_type); // Do not cache blob id because the blob is not materialized
}
- END_FOR
+
+ // Schema of the parent package
+ CONST.RDB$SCHEMA_NAME.NULL = FALSE;
+ strcpy(CONST.RDB$SCHEMA_NAME, name.schema.c_str());
+
+ // Type
+ CONST.RDB$PRIVATE_FLAG.NULL = FALSE;
+ CONST.RDB$PRIVATE_FLAG = m_isPrivate;
+
+ CONST.RDB$CONSTANT_SOURCE.NULL = TRUE;
}
+ END_STORE
+ break;
+ }
+ catch (const status_exception& ex)
+ {
+ if (ex.value()[1] != isc_unique_key_violation)
+ throw;
+
+ if (++faults > MAX_SSHORT)
+ throw;
- procedures.add(procedure);
+ fb_utils::init_status(tdbb->tdbb_status_vector);
}
- END_FOR
- }
-} // namespace
+ }// While
+}
+
+bool CreatePackageConstantNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction)
+{
+ Attachment* attachment = transaction->getAttachment();
+ static const CachedRequestId eraseRequestId;
+ AutoCacheRequest eraseDscRequest(tdbb, eraseRequestId);
-//----------------------
+ static const CachedRequestId modifyRequestId;
+ AutoCacheRequest modifyConstantRequest(tdbb, modifyRequestId);
+
+ bool found = false;
+
+ // Get existing filed
+ // Store constant dsc in RDB$FIELDS
+ FOR (REQUEST_HANDLE eraseDscRequest TRANSACTION_HANDLE transaction)
+ FLD IN RDB$FIELDS CROSS CONST IN RDB$CONSTANTS
+ CROSS PACKAGE IN RDB$PACKAGES
+ WITH PACKAGE.RDB$PACKAGE_NAME EQ name.package.c_str() AND
+ PACKAGE.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND
+ CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND
+ FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND
+ FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE
+ {
+ m_id = CONST.RDB$CONSTANT_ID;
+
+ dyn_fld origDom, newDom;
+ DSC_make_descriptor(&origDom.dyn_dsc, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE,
+ FLD.RDB$FIELD_LENGTH, FLD.RDB$FIELD_SUB_TYPE,
+ CSetId(FLD.RDB$CHARACTER_SET_ID),
+ CollId(FLD.RDB$COLLATION_ID));
+
+ origDom.dyn_fld_name = name;
+ origDom.dyn_charbytelen = FLD.RDB$FIELD_LENGTH;
+ origDom.dyn_dtype = FLD.RDB$FIELD_TYPE;
+ origDom.dyn_precision = FLD.RDB$FIELD_PRECISION;
+ origDom.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE;
+ origDom.dyn_charlen = FLD.RDB$CHARACTER_LENGTH;
+ origDom.dyn_collation = FLD.RDB$COLLATION_ID;
+ origDom.dyn_null_flag = !FLD.RDB$NULL_FLAG.NULL && FLD.RDB$NULL_FLAG != 0;
+ origDom.dyn_fld_source = QualifiedName(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME);
+
+ // If the original field type is an array, force its blr type to blr_blob
+ if (FLD.RDB$DIMENSIONS != 0)
+ origDom.dyn_dtype = blr_blob;
+
+ const bool wasInternalDomain = fb_utils::implicit_domain(origDom.dyn_fld_source.object.c_str());
+
+ QualifiedName newDomainName;
+
+ // We have the type. Default and type/domain are exclusive for now.
+ if (m_type->typeOfName.object.hasData())
+ {
+ // Case a1: Internal domain -> domain.
+ // Case a2: Domain -> domain.
+
+ newDomainName = m_type->typeOfName;
+
+ if (fb_utils::implicit_domain(newDomainName.object.c_str()))
+ {
+ // msg 224: "Cannot use the internal domain %s as new type for field %s".
+ status_exception::raise(
+ Arg::PrivateDyn(224) << newDomainName.toQuotedString() << m_type->fld_name);
+ }
+
+ // Get the domain information.
+ if (!METD_get_domain(dsqlScratch->getTransaction(), m_type, newDomainName))
+ {
+ // Specified domain or source field does not exist.
+ status_exception::raise(
+ Arg::Gds(isc_sqlerr) << Arg::Num(-607) <<
+ Arg::Gds(isc_dsql_command_err) <<
+ Arg::Gds(isc_dsql_domain_not_found) << newDomainName.toQuotedString());
+ }
+
+ QualifiedName dummyCollationName;
+ DDL_resolve_intl_type(dsqlScratch, m_type, dummyCollationName);
+
+ // If the original definition was a base field type, remove the
+ // entries from RDB$FIELDS.
+ if (wasInternalDomain)
+ {
+ // Case a1: Internal domain -> domain.
+ ERASE FLD;
+ }
+ }
+ else
+ {
+ // Case b1: Internal domain -> internal domain.
+ // Case b2: Domain -> internal domain.
+
+ m_type->resolve(dsqlScratch, true);
+
+ if (wasInternalDomain) // Case b1: Internal domain -> internal domain.
+ {
+ MODIFY FLD
+ updateRdbFields(m_type,
+ FLD.RDB$FIELD_TYPE,
+ FLD.RDB$FIELD_LENGTH,
+ FLD.RDB$FIELD_SUB_TYPE.NULL, FLD.RDB$FIELD_SUB_TYPE,
+ FLD.RDB$FIELD_SCALE.NULL, FLD.RDB$FIELD_SCALE,
+ FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID,
+ FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH,
+ FLD.RDB$FIELD_PRECISION.NULL, FLD.RDB$FIELD_PRECISION,
+ FLD.RDB$COLLATION_ID.NULL, FLD.RDB$COLLATION_ID,
+ FLD.RDB$SEGMENT_LENGTH.NULL, FLD.RDB$SEGMENT_LENGTH);
+ END_MODIFY
+
+ newDom.dyn_fld_source = origDom.dyn_fld_source;
+ }
+ else // Case b2: Domain -> internal domain.
+ storeGlobalField(tdbb, transaction, newDomainName, m_type);
+ }
+
+
+ if (newDomainName.object.hasData())
+ newDom.dyn_fld_source = newDomainName;
+
+ AlterDomainNode::getDomainType(tdbb, transaction, newDom);
+
+ if (newDom.dyn_dtype == blr_blob && newDomainName.object.isEmpty())
+ newDom.dyn_sub_type = m_type->subType;
+
+ // Gen the new constant value as blr
+ ConstantValue::genConstantBlr(tdbb, dsqlScratch, m_expr, m_type, name.schema);
+
+ MODIFY CONST USING
+ attachment->storeBinaryBlob(tdbb, transaction, &CONST.RDB$CONSTANT_BLR, dsqlScratch->getBlrData());
+ CONST.RDB$CONSTANT_BLR.NULL = FALSE;
+ END_MODIFY
+
+ {
+ fb_assert(package);
+ // Do not cache blob id because the blob is not materialized
+ package->updateConstant(tdbb, m_id, m_isPrivate, m_type);
+ }
+
+ found = true;
+ }
+ END_FOR
+
+ return found;
+}
string CreateAlterPackageNode::internalPrint(NodePrinter& printer) const
@@ -215,6 +853,7 @@ string CreateAlterPackageNode::internalPrint(NodePrinter& printer) const
//// FIXME-PRINT: NODE_PRINT(printer, items);
NODE_PRINT(printer, functionNames);
NODE_PRINT(printer, procedureNames);
+ NODE_PRINT(printer, constantNames);
return "CreateAlterPackageNode";
}
@@ -244,20 +883,12 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
switch ((*items)[i].type)
{
- case CreateAlterPackageNode::Item::FUNCTION:
+ case PackageItemType::FUNCTION:
{
CreateAlterFunctionNode* const fun = (*items)[i].function;
ddlNode = fun;
- if (functionNames.exist(fun->name.object))
- {
- status_exception::raise(
- Arg::Gds(isc_no_meta_update) <<
- Arg::Gds(isc_dyn_duplicate_package_item) <<
- Arg::Str("FUNCTION") << fun->name.object.toQuotedString());
- }
-
- functionNames.add(fun->name.object);
+ functionNames.addName(fun->name);
fun->alter = true;
fun->name.schema = name.schema;
@@ -265,28 +896,34 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
break;
}
- case CreateAlterPackageNode::Item::PROCEDURE:
+ case PackageItemType::PROCEDURE:
{
CreateAlterProcedureNode* const proc = (*items)[i].procedure;
ddlNode = proc;
- if (procedureNames.exist(proc->name.object))
- {
- status_exception::raise(
- Arg::Gds(isc_no_meta_update) <<
- Arg::Gds(isc_dyn_duplicate_package_item) <<
- Arg::Str("PROCEDURE") << proc->name.object.toQuotedString());
- }
-
- procedureNames.add(proc->name.object);
+ procedureNames.addName(proc->name);
proc->alter = true;
proc->name.schema = name.schema;
proc->name.package = name.object;
break;
}
+ case PackageItemType::CONSTANT:
+ {
+ CreatePackageConstantNode* const constant = (*items)[i].constant;
+ ddlNode = constant;
+ constantNames.addName(constant->name);
+
+ constant->create = true; // Create a new constant
+ constant->alter = true; // Create a new constant
+ constant->name.schema = name.schema;
+ constant->name.package = name.object;
+ constant->makePublic();
+ break;
+ }
default:
+ ddlNode = nullptr; // Warning
fb_assert(false);
}
@@ -363,7 +1000,7 @@ void CreateAlterPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlS
dsc schemaDesc, nameDesc;
schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str()));
nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str()));
- DFW_post_work(transaction, dfw_modify_package_header, &nameDesc, &schemaDesc, 0);
+ DFW_post_work(transaction, dfw_modify_package_header, &nameDesc, &schemaDesc, id);
}
else
executeCreate(tdbb, dsqlScratch, transaction);
@@ -385,41 +1022,69 @@ void CreateAlterPackageNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch*
DYN_UTIL_check_unique_name(tdbb, name, obj_package_header);
+ static const CachedRequestId requestId;
AutoCacheRequest requestHandle(tdbb, drq_s_pkg, DYN_REQUESTS);
- STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
- PKG IN RDB$PACKAGES USING
+ Cached::Package* package = nullptr;
+ ULONG faults = 0;
+ while (true)
{
- PKG.RDB$SCHEMA_NAME.NULL = FALSE;
- strcpy(PKG.RDB$SCHEMA_NAME, name.schema.c_str());
+ try
+ {
+ id = DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_package_id, PACKAGES_GENERATOR);
+ id %= (MAX_SSHORT + 1);
+ if (!id)
+ continue;
+
+ STORE (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
+ PKG IN RDB$PACKAGES USING
+ {
+ PKG.RDB$SCHEMA_NAME.NULL = FALSE;
+ strcpy(PKG.RDB$SCHEMA_NAME, name.schema.c_str());
+
+ PKG.RDB$PACKAGE_NAME.NULL = FALSE;
+ strcpy(PKG.RDB$PACKAGE_NAME, name.object.c_str());
- PKG.RDB$PACKAGE_NAME.NULL = FALSE;
- strcpy(PKG.RDB$PACKAGE_NAME, name.object.c_str());
+ PKG.RDB$SYSTEM_FLAG.NULL = FALSE;
+ PKG.RDB$SYSTEM_FLAG = 0;
- PKG.RDB$SYSTEM_FLAG.NULL = FALSE;
- PKG.RDB$SYSTEM_FLAG = 0;
+ PKG.RDB$OWNER_NAME.NULL = FALSE;
+ strcpy(PKG.RDB$OWNER_NAME, ownerName.c_str());
- PKG.RDB$OWNER_NAME.NULL = FALSE;
- strcpy(PKG.RDB$OWNER_NAME, ownerName.c_str());
+ PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE;
+ attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, source);
- PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE;
- attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, source);
+ if (ssDefiner.has_value())
+ {
+ PKG.RDB$SQL_SECURITY.NULL = FALSE;
+ PKG.RDB$SQL_SECURITY = ssDefiner.value() == SqlSecurity::SS_DEFINER ? FB_TRUE : FB_FALSE;
+ }
+ else
+ PKG.RDB$SQL_SECURITY.NULL = TRUE;
- if (ssDefiner.has_value())
+ PKG.RDB$PACKAGE_ID = id;
+ }
+ END_STORE
+ break;
+ }
+ catch (const status_exception& ex)
{
- PKG.RDB$SQL_SECURITY.NULL = FALSE;
- PKG.RDB$SQL_SECURITY = ssDefiner.value() == SqlSecurity::SS_DEFINER ? FB_TRUE : FB_FALSE;
+ if (ex.value()[1] != isc_unique_key_violation)
+ throw;
+
+ if (++faults > MAX_SSHORT)
+ throw;
+
+ fb_utils::init_status(tdbb->tdbb_status_vector);
}
- else
- PKG.RDB$SQL_SECURITY.NULL = TRUE;
}
- END_STORE
storePrivileges(tdbb, transaction, name, obj_package_header, EXEC_PRIVILEGES);
owner = ownerName;
- executeItems(tdbb, dsqlScratch, transaction);
+ package = MetadataCache::newVersion(tdbb, id);
+ executeItems(tdbb, dsqlScratch, transaction, package->getVersioned(tdbb, CacheFlag::AUTOCREATE));
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE, name, {});
}
@@ -430,6 +1095,8 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch*
{
MemoryPool& pool = dsqlScratch->getPool();
Attachment* attachment = transaction->getAttachment();
+
+ static const CachedRequestId requestId;
AutoCacheRequest requestHandle(tdbb, drq_m_pkg, DYN_REQUESTS);
bool modified = false;
@@ -438,35 +1105,18 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch*
WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
PKG.RDB$PACKAGE_NAME EQ name.object.c_str()
{
+ id = PKG.RDB$PACKAGE_ID;
modified = true;
+ MetadataCache::oldVersion(tdbb, id, CacheFlag::OLD_ALTER);
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_PACKAGE, name, {});
- SortedObjectsArray existingFuncs(pool);
- SortedObjectsArray existingProcs(pool);
- collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false);
-
- for (SortedObjectsArray::iterator i = existingFuncs.begin();
- i != existingFuncs.end(); ++i)
- {
- if (!functionNames.exist(i->name))
- {
- DropFunctionNode dropNode(pool, QualifiedName(i->name, name.schema, name.object));
- dropNode.dsqlPass(dsqlScratch);
- dropNode.executeDdl(tdbb, dsqlScratch, transaction, true);
- }
- }
+ PackageItemsHolder existingItems(pool);
+ existingItems.collectPackagedItems(tdbb, transaction, name, false, true);
- for (SortedObjectsArray::iterator i = existingProcs.begin();
- i != existingProcs.end(); ++i)
- {
- if (!procedureNames.exist(i->name))
- {
- DropProcedureNode dropNode(pool, QualifiedName(i->name, name.schema, name.object));
- dropNode.dsqlPass(dsqlScratch);
- dropNode.executeDdl(tdbb, dsqlScratch, transaction, true);
- }
- }
+ dropMissingItems(tdbb, dsqlScratch, name, existingItems.functions, functionNames);
+ dropMissingItems(tdbb, dsqlScratch, name, existingItems.procedures, procedureNames);
+ dropMissingItems(tdbb, dsqlScratch, name, existingItems.constants, constantNames);
MODIFY PKG
PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE;
@@ -490,13 +1140,14 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch*
dsc schemaDesc, nameDesc;
schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str()));
nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str()));
- DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, 0);
+ DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, id);
}
END_FOR
if (modified)
{
- executeItems(tdbb, dsqlScratch, transaction);
+ auto package = MetadataCache::newVersion(tdbb, id);
+ executeItems(tdbb, dsqlScratch, transaction, package->getVersioned(tdbb, CacheFlag::AUTOCREATE));
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_PACKAGE, name, {});
}
@@ -541,21 +1192,25 @@ bool CreateAlterPackageNode::executeAlterIndividualParameters(thread_db* tdbb, D
}
void CreateAlterPackageNode::executeItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
- jrd_tra* transaction)
+ jrd_tra* transaction, Package* package)
{
for (unsigned i = 0; i < items->getCount(); ++i)
{
switch ((*items)[i].type)
{
- case Item::FUNCTION:
+ case PackageItemType::FUNCTION:
(*items)[i].function->packageOwner = owner;
(*items)[i].function->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true);
break;
- case Item::PROCEDURE:
+ case PackageItemType::PROCEDURE:
(*items)[i].procedure->packageOwner = owner;
(*items)[i].procedure->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true);
break;
+ case PackageItemType::CONSTANT:
+ (*items)[i].constant->package = package;
+ (*items)[i].constant->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true);
+ break;
}
}
}
@@ -591,12 +1246,15 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
bool found = false;
AutoCacheRequest requestHandle(tdbb, drq_e_pkg, DYN_REQUESTS);
+ MetaId id{};
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
PKG IN RDB$PACKAGES
WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
PKG.RDB$PACKAGE_NAME EQ name.object.c_str()
{
found = true;
+ id = PKG.RDB$PACKAGE_ID;
+ MetadataCache::getVersioned(tdbb, id, CacheFlag::OLD_DROP);
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PACKAGE, name, {});
@@ -608,7 +1266,7 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
dsc schemaDesc, nameDesc;
schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str()));
nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str()));
- DFW_post_work(transaction, dfw_drop_package_header, &nameDesc, &schemaDesc, 0);
+ DFW_post_work(transaction, dfw_drop_package_header, &nameDesc, &schemaDesc, id);
}
END_FOR
@@ -619,25 +1277,9 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
Arg::Gds(isc_dyn_package_not_found) << name.toQuotedString());
}
- SortedObjectsArray existingFuncs(pool);
- SortedObjectsArray existingProcs(pool);
- collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false);
-
- for (SortedObjectsArray::iterator i = existingFuncs.begin();
- i != existingFuncs.end(); ++i)
- {
- DropFunctionNode dropNode(pool, QualifiedName(i->name, name.schema, name.object));
- dropNode.dsqlPass(dsqlScratch);
- dropNode.executeDdl(tdbb, dsqlScratch, transaction, true);
- }
-
- for (SortedObjectsArray::iterator i = existingProcs.begin();
- i != existingProcs.end(); ++i)
- {
- DropProcedureNode dropNode(pool, QualifiedName(i->name, name.schema, name.object));
- dropNode.dsqlPass(dsqlScratch);
- dropNode.executeDdl(tdbb, dsqlScratch, transaction, true);
- }
+ PackageItemsHolder existingItems(pool);
+ existingItems.collectPackagedItems(tdbb, transaction, name, false, true);
+ existingItems.drop(tdbb, dsqlScratch, name);
requestHandle.reset(tdbb, drq_e_pkg_prv, DYN_REQUESTS);
@@ -656,7 +1298,10 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
END_FOR
if (found)
+ {
+ MetadataCache::erase(tdbb, id);
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE, name, {});
+ }
savePoint.release(); // everything is ok
}
@@ -690,8 +1335,7 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
// process declaredItems and items
Array* arrays[] = {declaredItems, items};
- SortedArray functionNames[FB_NELEM(arrays)];
- SortedArray procedureNames[FB_NELEM(arrays)];
+ PackageItemsHolder names[FB_NELEM(arrays)];
for (unsigned i = 0; i < FB_NELEM(arrays); ++i)
{
@@ -704,20 +1348,11 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
switch ((*arrays[i])[j].type)
{
- case CreateAlterPackageNode::Item::FUNCTION:
+ case PackageItemType::FUNCTION:
{
CreateAlterFunctionNode* const fun = (*arrays[i])[j].function;
ddlNode = fun;
-
- if (functionNames[i].exist(fun->name.object))
- {
- status_exception::raise(
- Arg::Gds(isc_no_meta_update) <<
- Arg::Gds(isc_dyn_duplicate_package_item) <<
- Arg::Str("FUNCTION") << fun->name.object.toQuotedString());
- }
-
- functionNames[i].add(fun->name.object);
+ names[i].functions.addName(fun->name);
fun->name.schema = name.schema;
fun->name.package = name.object;
@@ -729,20 +1364,11 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
break;
}
- case CreateAlterPackageNode::Item::PROCEDURE:
+ case PackageItemType::PROCEDURE:
{
CreateAlterProcedureNode* const proc = (*arrays[i])[j].procedure;
ddlNode = proc;
-
- if (procedureNames[i].exist(proc->name.object))
- {
- status_exception::raise(
- Arg::Gds(isc_no_meta_update) <<
- Arg::Gds(isc_dyn_duplicate_package_item) <<
- Arg::Str("PROCEDURE") << proc->name.object.toQuotedString());
- }
-
- procedureNames[i].add(proc->name.object);
+ names[i].procedures.addName(proc->name);
proc->name.schema = name.schema;
proc->name.package = name.object;
@@ -753,7 +1379,23 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch)
break;
}
+ case PackageItemType::CONSTANT:
+ {
+ CreatePackageConstantNode* const constant = (*arrays[i])[j].constant;
+ ddlNode = constant;
+
+ names[i].constants.addName(constant->name);
+
+ constant->name.schema = name.schema;
+ constant->name.package = name.object;
+ constant->create = true;
+ if (arrays[i] == items)
+ constant->alter = true;
+
+ constant->makePrivate();
+ break;
+ }
default:
fb_assert(false);
}
@@ -799,6 +1441,7 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body, DYN_REQUESTS);
bool modified = false;
+ Package* package = nullptr;
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
PKG IN RDB$PACKAGES
WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
@@ -814,6 +1457,7 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
Arg::Gds(isc_dyn_package_body_exists) << name.toQuotedString());
}
+ package = MetadataCache::newVersion(tdbb, PKG.RDB$PACKAGE_ID)->getVersioned(tdbb, CacheFlag::AUTOCREATE);
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_PACKAGE_BODY, name, {});
MODIFY PKG
@@ -837,12 +1481,10 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
Arg::Gds(isc_dyn_package_not_found) << name.toQuotedString());
}
- SortedObjectsArray headerFuncs(pool);
- SortedObjectsArray headerProcs(pool);
- collectPackagedItems(tdbb, transaction, name, headerFuncs, headerProcs, false);
+ PackageItemsHolder headerItems(pool);
+ headerItems.collectPackagedItems(tdbb, transaction, name, false, true);
- SortedObjectsArray existingFuncs(pool);
- SortedObjectsArray existingProcs(pool);
+ PackageItemsHolder existingItems(pool);
// process declaredItems and items
Array* arrays[] = {declaredItems, items};
@@ -854,11 +1496,10 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
if (arrays[i] == items)
{
- existingFuncs.clear();
- existingProcs.clear();
+ existingItems.clear();
}
- collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, true);
+ existingItems.collectPackagedItems(tdbb, transaction, name, true, true);
for (unsigned j = 0; j < arrays[i]->getCount(); ++j)
{
@@ -866,91 +1507,67 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc
switch (elem.type)
{
- case CreateAlterPackageNode::Item::FUNCTION:
+ case PackageItemType::FUNCTION:
{
CreateAlterFunctionNode* func = elem.function;
if (arrays[i] == items)
- func->privateScope = !headerFuncs.exist(Signature(func->name.object));
- else if (existingFuncs.exist(Signature(func->name.object)))
+ func->privateScope = !headerItems.functions.exist(Signature(func->name.object));
+ else
{
- status_exception::raise(
- Arg::Gds(isc_no_meta_update) <<
- Arg::Gds(isc_dyn_duplicate_package_item) <<
- Arg::Str("FUNCTION") << func->name.toQuotedString());
+ existingItems.functions.checkDuplicate(func->name);
}
func->packageOwner = owner;
func->preserveDefaults =
- existingFuncs.exist(Signature(func->name.object)) && arrays[i] == items;
+ existingItems.functions.exist(Signature(func->name.object)) && arrays[i] == items;
func->executeDdl(tdbb, elem.dsqlScratch, transaction, true);
break;
}
- case CreateAlterPackageNode::Item::PROCEDURE:
+ case PackageItemType::PROCEDURE:
{
CreateAlterProcedureNode* proc = elem.procedure;
if (arrays[i] == items)
- proc->privateScope = !headerProcs.exist(Signature(proc->name.object));
- else if (existingProcs.exist(Signature(proc->name.object)))
+ proc->privateScope = !headerItems.procedures.exist(Signature(proc->name.object));
+ else
{
- status_exception::raise(
- Arg::Gds(isc_no_meta_update) <<
- Arg::Gds(isc_dyn_duplicate_package_item) <<
- Arg::Str("PROCEDURE") << proc->name.toQuotedString());
+ existingItems.procedures.checkDuplicate(proc->name);
}
proc->packageOwner = owner;
proc->preserveDefaults =
- existingProcs.exist(Signature(proc->name.object)) && arrays[i] == items;
+ existingItems.procedures.exist(Signature(proc->name.object)) && arrays[i] == items;
proc->executeDdl(tdbb, elem.dsqlScratch, transaction, true);
break;
}
- }
- }
- }
+ case PackageItemType::CONSTANT:
+ {
+ CreatePackageConstantNode* constant = elem.constant;
- SortedObjectsArray newFuncs(pool);
- SortedObjectsArray newProcs(pool);
- collectPackagedItems(tdbb, transaction, name, newFuncs, newProcs, true);
+ headerItems.constants.checkDuplicate(constant->name);
+ existingItems.constants.checkDuplicate(constant->name);
- for (SortedObjectsArray::iterator i = existingFuncs.begin();
- i != existingFuncs.end(); ++i)
- {
- FB_SIZE_T pos;
- bool found = newFuncs.find(Signature(pool, i->name), pos);
-
- if (!found || !newFuncs[pos].defined)
- {
- status_exception::raise(
- Arg::Gds(isc_dyn_funcnotdef_package) << i->name << name.toQuotedString());
- }
- else if (newFuncs[pos] != *i)
- {
- status_exception::raise(
- Arg::Gds(isc_dyn_funcsignat_package) << i->name << name.toQuotedString());
+ fb_assert(package);
+ constant->package = package;
+ constant->executeDdl(tdbb, elem.dsqlScratch, transaction, true);
+ break;
+ }
+ }
}
}
- for (SortedObjectsArray::iterator i = existingProcs.begin();
- i != existingProcs.end(); ++i)
- {
- FB_SIZE_T pos;
- bool found = newProcs.find(Signature(pool, i->name), pos);
+ PackageItemsHolder newItems(pool);
+ newItems.collectPackagedItems(tdbb, transaction, name, true, false);
+ existingItems.checkDefineMatch(pool, name, newItems);
- if (!found || !newProcs[pos].defined)
- {
- status_exception::raise(
- Arg::Gds(isc_dyn_procnotdef_package) << i->name << name.toQuotedString());
- }
- else if (newProcs[pos] != *i)
- {
- status_exception::raise(
- Arg::Gds(isc_dyn_procsignat_package) << i->name << name.toQuotedString());
- }
+ {
+ dsc schemaDesc, nameDesc;
+ schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str()));
+ nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str()));
+ DFW_post_work(transaction, dfw_create_package, &nameDesc, &schemaDesc, package->getId());
}
-
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_PACKAGE_BODY, name, {});
savePoint.release(); // everything is ok
@@ -988,12 +1605,14 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
bool found = false;
AutoCacheRequest requestHandle(tdbb, drq_m_pkg_body2, DYN_REQUESTS);
+ MetaId id{};
FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction)
PKG IN RDB$PACKAGES
WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
PKG.RDB$PACKAGE_NAME EQ name.object.c_str()
{
found = true;
+ id = PKG.RDB$PACKAGE_ID;
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_PACKAGE_BODY, name, {});
@@ -1004,7 +1623,7 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
dsc schemaDesc, nameDesc;
schemaDesc.makeText(name.schema.length(), ttype_metadata, (UCHAR*) const_cast(name.schema.c_str()));
nameDesc.makeText(name.object.length(), ttype_metadata, (UCHAR*) const_cast(name.object.c_str()));
- DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, 0);
+ DFW_post_work(transaction, dfw_drop_package_body, &nameDesc, &schemaDesc, id);
END_MODIFY
}
END_FOR
@@ -1031,9 +1650,7 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
{
if (!FUN.RDB$PRIVATE_FLAG.NULL && FUN.RDB$PRIVATE_FLAG != 0)
{
- DropFunctionNode dropNode(pool, QualifiedName(FUN.RDB$FUNCTION_NAME, name.schema, name.object));
- dropNode.dsqlPass(dsqlScratch);
- dropNode.executeDdl(tdbb, dsqlScratch, transaction, true);
+ dropItem(tdbb, dsqlScratch, QualifiedName(FUN.RDB$FUNCTION_NAME, name.schema, name.object));
}
else
{
@@ -1058,9 +1675,7 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
{
if (!PRC.RDB$PRIVATE_FLAG.NULL && PRC.RDB$PRIVATE_FLAG != 0)
{
- DropProcedureNode dropNode(pool, QualifiedName(PRC.RDB$PROCEDURE_NAME, name.schema, name.object));
- dropNode.dsqlPass(dsqlScratch);
- dropNode.executeDdl(tdbb, dsqlScratch, transaction, true);
+ dropItem(tdbb, dsqlScratch, QualifiedName(PRC.RDB$PROCEDURE_NAME, name.schema, name.object));
}
else
{
@@ -1075,6 +1690,21 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra
}
END_FOR
+ // Erase body constants
+ static const CachedRequestId eraseRequestId;
+ AutoCacheRequest eraseConstantRequest(tdbb, eraseRequestId);
+ FOR (REQUEST_HANDLE eraseConstantRequest TRANSACTION_HANDLE transaction)
+ CONST IN RDB$CONSTANTS
+ WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ CONST.RDB$PACKAGE_NAME EQ name.object.c_str() AND
+ CONST.RDB$PRIVATE_FLAG EQ true
+ {
+ ERASE CONST;
+ }
+ END_FOR
+
+ MetadataCache::newVersion(tdbb, id);
+
executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_PACKAGE_BODY, name, {});
savePoint.release(); // everything is ok
diff --git a/src/dsql/PackageNodes.h b/src/dsql/PackageNodes.h
index 839e3c8d0f6..372beb76314 100644
--- a/src/dsql/PackageNodes.h
+++ b/src/dsql/PackageNodes.h
@@ -25,9 +25,196 @@
#include "../dsql/DdlNodes.h"
#include "../common/classes/array.h"
+#include "../common/classes/objects_array.h"
+#include "../include/fb_exception.h"
namespace Jrd {
+enum class PackageItemType : USHORT
+{
+ FUNCTION = 0,
+ PROCEDURE,
+ CONSTANT,
+ META_SIZE
+};
+
+template
+class ItemNames : public TArray
+{
+public:
+ ItemNames() : TArray()
+ {}
+
+ ItemNames(Firebird::MemoryPool& pool) : TArray(pool)
+ {}
+
+ operator TArray&()
+ {
+ return *this;
+ }
+
+ template
+ void addName(const QualifiedName& newName)
+ {
+ checkDuplicate(newName);
+ TArray::add(TType(newName.object));
+ }
+
+ template
+ void checkDuplicate(const QualifiedName& newName)
+ {
+ if constexpr (std::is_same_v)
+ {
+ if (!TArray::exist(newName.object))
+ return; // The name is unique
+ }
+ else
+ {
+ // Cast
+ if (!TArray::exist(TType(newName.object)))
+ return; // The name is unique
+ }
+
+ static_assert(size_t(IValue) >= 0 && size_t(IValue) < size_t(PackageItemType::META_SIZE), "Invalid item type");
+ static const std::array names{
+ "FUNCTION",
+ "PROCEDURE",
+ "CONSTANT",
+ };
+
+ // Print just the object name because the full path is present in the parent error message
+ Firebird::status_exception::raise(
+ Firebird::Arg::Gds(isc_no_meta_update) <<
+ Firebird::Arg::Gds(isc_dyn_duplicate_package_item) <<
+ Firebird::Arg::Str(names[size_t(IValue)]) << Firebird::Arg::Str(newName.object.toQuotedString()));
+ }
+};
+
+
+class PackageItemsHolder
+{
+public:
+ using ItemsSignatureArray = ItemNames, Signature>;
+
+public:
+ PackageItemsHolder()
+ { }
+
+ PackageItemsHolder(Firebird::MemoryPool& pool) :
+ functions(pool),
+ procedures(pool),
+ constants(pool)
+ { }
+
+ void drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const QualifiedName& packageAndSchema);
+ void checkDefineMatch(Firebird::MemoryPool& pool, const QualifiedName& packageAndSchema, const PackageItemsHolder& newItems);
+ void collectPackagedItems(thread_db* tdbb, jrd_tra* transaction,
+ const QualifiedName& packageAndSchema, const bool details, const bool collectConstants);
+ void clear();
+
+public:
+ ItemsSignatureArray functions;
+ ItemsSignatureArray procedures;
+ ItemsSignatureArray constants;
+};
+
+class PackageReferenceNode final : public TypedNode
+{
+public:
+ PackageReferenceNode(Firebird::MemoryPool& pool, const QualifiedName& name,
+ const UCHAR itemType = blr_pkg_reference_to_constant);
+
+ Firebird::string internalPrint(NodePrinter& printer) const final;
+
+ ValueExprNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) final;
+ static DmlNode* parse(thread_db* tdbb, Firebird::MemoryPool& pool, CompilerScratch* csb, const UCHAR blrOp);
+ void genBlr(DsqlCompilerScratch* dsqlScratch) final;
+
+ void setParameterName(dsql_par* parameter) const final;
+ void make(DsqlCompilerScratch* dsqlScratch, dsc* desc) final;
+
+ // Search for a package constant by its fully qualified name
+ static bool constantExists(thread_db* tdbb, Jrd::jrd_tra* transaction,
+ const QualifiedName& name, bool* isPrivate = nullptr);
+
+ void getDesc(thread_db*, CompilerScratch*, dsc*) final;
+
+ ValueExprNode* copy(thread_db*, NodeCopier&) const final;
+ ValueExprNode* pass1(thread_db* tdbb, CompilerScratch* csb) final;
+ ValueExprNode* pass2(thread_db* tdbb, CompilerScratch* csb) final;
+ dsc* execute(thread_db*, Request*) const final;
+
+ const char* getName() const
+ {
+ return m_fullName.object.c_str();
+ }
+
+private:
+ ConstantValue* m_prefetchedConstant = nullptr;
+ CachedResource m_package;
+ const QualifiedName m_fullName;
+
+ const UCHAR m_itemType;
+ ULONG m_impureOffset = 0;
+};
+
+
+class CreatePackageConstantNode final : public DdlNode
+{
+public:
+ CreatePackageConstantNode(Firebird::MemoryPool& pool, const MetaName& name,
+ dsql_fld* type = nullptr, ValueExprNode* value = nullptr, bool isPrivate = false)
+ : DdlNode(pool),
+ name(pool, name),
+ m_type(type),
+ m_expr(value),
+ m_isPrivate(isPrivate)
+ { }
+
+ Firebird::string internalPrint(NodePrinter& printer) const;
+ DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) final;
+ void checkPermission(thread_db* tdbb, jrd_tra* transaction) final;
+ void execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) final;
+
+ inline void makePublic()
+ {
+ m_isPrivate = false;
+ }
+
+ inline void makePrivate()
+ {
+ m_isPrivate = true;
+ }
+
+private:
+ dsc* makeConstantValue(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, CompilerScratch*& nodeContext);
+ void executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
+ bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
+
+protected:
+ virtual void putErrorPrefix(Firebird::Arg::StatusVector& statusVector)
+ {
+ statusVector <<
+ Firebird::Arg::Gds(createAlterCode(create, alter,
+ isc_dsql_create_const_failed, isc_dsql_alter_const_failed,
+ isc_dsql_create_alter_const_failed)) <<
+ Firebird::Arg::Str(name.toQuotedString());
+ }
+
+public:
+ QualifiedName name;
+ bool create = false;
+ bool alter = false;
+
+ Package* package = nullptr;
+
+private:
+ NestConst m_type;
+ NestConst m_expr;
+ MetaId m_id = 0;
+ bool m_isPrivate = false;
+};
+
class CreateAlterPackageNode : public DdlNode
{
@@ -37,36 +224,44 @@ class CreateAlterPackageNode : public DdlNode
static Item create(CreateAlterFunctionNode* function)
{
Item item;
- item.type = FUNCTION;
+ item.type = PackageItemType::FUNCTION;
item.function = function;
- item.dsqlScratch = NULL;
+ item.dsqlScratch = nullptr;
return item;
}
static Item create(CreateAlterProcedureNode* procedure)
{
Item item;
- item.type = PROCEDURE;
+ item.type = PackageItemType::PROCEDURE;
item.procedure = procedure;
- item.dsqlScratch = NULL;
+ item.dsqlScratch = nullptr;
return item;
}
- enum
+ static Item create(CreatePackageConstantNode* constant)
{
- FUNCTION,
- PROCEDURE
- } type;
+ Item item;
+ item.type = PackageItemType::CONSTANT;
+ item.constant = constant;
+ item.dsqlScratch = nullptr;
+ return item;
+ }
+
+ PackageItemType type;
union
{
CreateAlterFunctionNode* function;
CreateAlterProcedureNode* procedure;
+ CreatePackageConstantNode* constant;
};
DsqlCompilerScratch* dsqlScratch;
};
+ using ItemsNameArray = ItemNames, MetaName>;
+
public:
CreateAlterPackageNode(MemoryPool& pool, const QualifiedName& aName)
: DdlNode(pool),
@@ -77,6 +272,7 @@ class CreateAlterPackageNode : public DdlNode
items(NULL),
functionNames(pool),
procedureNames(pool),
+ constantNames(pool),
owner(pool)
{
}
@@ -101,7 +297,7 @@ class CreateAlterPackageNode : public DdlNode
void executeCreate(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
bool executeAlter(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
bool executeAlterIndividualParameters(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
- void executeItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction);
+ void executeItems(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, Package* package);
public:
QualifiedName name;
@@ -110,9 +306,11 @@ class CreateAlterPackageNode : public DdlNode
bool createIfNotExistsOnly = false;
Firebird::string source;
Firebird::Array- * items;
- Firebird::SortedArray functionNames;
- Firebird::SortedArray procedureNames;
+ ItemsNameArray functionNames;
+ ItemsNameArray procedureNames;
+ ItemsNameArray constantNames;
std::optional ssDefiner;
+ MetaId id;
private:
MetaName owner;
diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h
index c81cf09dff2..cf7acfdfa94 100644
--- a/src/dsql/dsql.h
+++ b/src/dsql/dsql.h
@@ -561,6 +561,7 @@ inline constexpr USHORT CTX_view_with_check_modify = 0x40; // Context of WITH C
inline constexpr USHORT CTX_cursor = 0x80; // Context is a cursor
inline constexpr USHORT CTX_lateral = 0x100; // Context is a lateral derived table
inline constexpr USHORT CTX_blr_fields = 0x200; // Fields of the context are defined inside BLR
+inline constexpr USHORT CTX_package = 0x400; // The context is related to a package
//! Aggregate/union map block to map virtual fields to their base
//! TMN: NOTE! This datatype should definitely be renamed!
diff --git a/src/dsql/parse.y b/src/dsql/parse.y
index a8b4786d33d..b37203db3e0 100644
--- a/src/dsql/parse.y
+++ b/src/dsql/parse.y
@@ -726,6 +726,7 @@ using namespace Firebird;
%token UNLIST
%token WITHIN
%token RDB_RESET_CONTEXT
+%token CONSTANT
// precedence declarations for expression evaluation
@@ -886,6 +887,7 @@ using namespace Firebird;
Jrd::SetBindNode* setBindNode;
Jrd::SessionResetNode* sessionResetNode;
Jrd::ForRangeNode::Direction forRangeDirection;
+ Jrd::CreatePackageConstantNode* createPackageConstantNode;
}
%include types.y
@@ -1040,6 +1042,13 @@ grant0($node)
$node->grantAdminOption = $7;
$node->grantor = $8;
}
+ | usage_privilege(NOTRIAL(&$node->privileges)) ON PACKAGE symbol_package_name
+ TO non_role_grantee_list(NOTRIAL(&$node->users)) grant_option granted_by
+ {
+ $node->object = newNode(obj_package_header, *$4);
+ $node->grantAdminOption = $7;
+ $node->grantor = $8;
+ }
/***
| usage_privilege(NOTRIAL(&$node->privileges)) ON DOMAIN symbol_domain_name
TO non_role_grantee_list(NOTRIAL(&$node->users)) grant_option granted_by
@@ -1334,6 +1343,13 @@ revoke0($node)
$node->grantAdminOption = $1;
$node->grantor = $8;
}
+ | rev_grant_option usage_privilege(NOTRIAL(&$node->privileges)) ON PACKAGE symbol_package_name
+ FROM non_role_grantee_list(NOTRIAL(&$node->users)) granted_by
+ {
+ $node->object = newNode(obj_package_header, QualifiedName(*$5));
+ $node->grantAdminOption = $1;
+ $node->grantor = $8;
+ }
/***
| rev_grant_option usage_privilege(NOTRIAL(&$node->privileges)) ON DOMAIN symbol_domain_name
FROM non_role_grantee_list(NOTRIAL(&$node->users)) granted_by
@@ -3175,6 +3191,8 @@ package_item
{ $$ = CreateAlterPackageNode::Item::create($2); }
| PROCEDURE procedure_clause_start ';'
{ $$ = CreateAlterPackageNode::Item::create($2); }
+ | CONSTANT package_const_item ';'
+ { $$ = CreateAlterPackageNode::Item::create($2); }
;
%type alter_package_clause
@@ -3262,6 +3280,13 @@ replace_package_body_clause
{ $$ = newNode($1); }
;
+%type package_const_item
+package_const_item
+ : symbol_package_const_name data_type_descriptor '=' value
+ {
+ $$ = newNode(*$1, $2, $4);
+ }
+ ;
%type replace_schema_clause
replace_schema_clause
@@ -6312,6 +6337,7 @@ ddl_type3
: PARAMETER { $$ = obj_parameter; }
| PROCEDURE PARAMETER { $$ = obj_procedure; }
| FUNCTION PARAMETER { $$ = obj_udf; }
+ | CONSTANT { $$ = obj_package_constant; }
;
%type ddl_type4
@@ -9866,6 +9892,11 @@ symbol_window_name
: valid_symbol_name
;
+%type symbol_package_const_name
+symbol_package_const_name
+ : valid_symbol_name
+ ;
+
// symbols
%type schema_opt_qualified_name
@@ -10186,6 +10217,7 @@ non_reserved_word
| SCHEMA
| UNLIST
| ERROR
+ | CONSTANT
;
%%
diff --git a/src/dsql/pass1.cpp b/src/dsql/pass1.cpp
index cad9b6cc039..01b2ff3a5a5 100644
--- a/src/dsql/pass1.cpp
+++ b/src/dsql/pass1.cpp
@@ -673,6 +673,7 @@ void PASS1_ambiguity_check(DsqlCompilerScratch* dsqlScratch,
string buffers[2];
string* bufferPtr = &buffers[0];
+ bool printAliasHelp = false;
for (DsqlContextStack::const_iterator stack(ambiguous_contexts); stack.hasData(); ++stack)
{
@@ -701,6 +702,14 @@ void PASS1_ambiguity_check(DsqlCompilerScratch* dsqlScratch,
buffer += "procedure ";
buffer += procedure->prc_name.toQuotedString();
}
+ else if (context->ctx_flags & CTX_package)
+ {
+ // Package constant or variable
+ printAliasHelp = true;
+ buffer += "package ";
+ if (context->ctx_alias.hasData())
+ buffer += context->getConcatenatedAlias();
+ }
else
{
const auto contextAliases = context->getConcatenatedAlias();
@@ -717,9 +726,15 @@ void PASS1_ambiguity_check(DsqlCompilerScratch* dsqlScratch,
if (dsqlScratch->clientDialect >= SQL_DIALECT_V6)
{
- ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
+ Arg::StatusVector status;
+ status.assign(Arg::Gds(isc_sqlerr) << Arg::Num(-204) <<
Arg::Gds(isc_dsql_ambiguous_field_name) << buffers[0] << buffers[1] <<
Arg::Gds(isc_random) << name);
+
+ if (printAliasHelp)
+ status.append(Arg::Gds(isc_package_alias_help));
+
+ ERR_post(status);
}
ERRD_post_warning(Arg::Warning(isc_sqlwarn) << Arg::Num(204) <<
diff --git a/src/include/firebird/impl/blr.h b/src/include/firebird/impl/blr.h
index 4fc7669a807..84c351fe4f9 100644
--- a/src/include/firebird/impl/blr.h
+++ b/src/include/firebird/impl/blr.h
@@ -532,4 +532,10 @@
#define blr_within_group_order (unsigned char) 235
+// Package const
+#define blr_package_reference (unsigned char) 236
+
+// Subcodes of blr_package_reference
+#define blr_pkg_reference_to_constant (unsigned char) 1
+
#endif // FIREBIRD_IMPL_BLR_H
diff --git a/src/include/firebird/impl/msg/dyn.h b/src/include/firebird/impl/msg/dyn.h
index 480663c8fdb..e7f2afe0c8b 100644
--- a/src/include/firebird/impl/msg/dyn.h
+++ b/src/include/firebird/impl/msg/dyn.h
@@ -313,3 +313,6 @@ FB_IMPL_MSG(DYN, 320, dyn_cannot_create_reserved_schema, -607, "HY", "000", "Sch
FB_IMPL_MSG(DYN, 321, dyn_cannot_infer_schema, -901, "42", "000", "Cannot infer schema name as there is no valid schema in the search path")
FB_IMPL_MSG_SYMBOL(DYN, 322, dyn_dup_blob_filter, "Blob filter @1 already exists")
FB_IMPL_MSG(DYN, 323, dyn_column_name_exists, -612, "42", "000", "Column @1 already exists in table @2")
+FB_IMPL_MSG_SYMBOL(DYN, 334, dyn_const_not_found, "Constant @1 not found")
+FB_IMPL_MSG_SYMBOL(DYN, 335, dyn_dup_const, "Constant @1 already exists")
+FB_IMPL_MSG_SYMBOL(DYN, 336, dyn_non_constant_constant, "The constant \"@1\" must be initialized by a constant expression")
diff --git a/src/include/firebird/impl/msg/gbak.h b/src/include/firebird/impl/msg/gbak.h
index fb5a173b540..c4c226fa482 100644
--- a/src/include/firebird/impl/msg/gbak.h
+++ b/src/include/firebird/impl/msg/gbak.h
@@ -418,3 +418,7 @@ FB_IMPL_MSG_NO_SYMBOL(GBAK, 419, "regular expression to skip schemas was already
FB_IMPL_MSG_NO_SYMBOL(GBAK, 420, "regular expression to include schemas was already set")
FB_IMPL_MSG_SYMBOL(GBAK, 421, gbak_plugin_schema_migration, "migrating @1 plugin objects to schema @2")
FB_IMPL_MSG_SYMBOL(GBAK, 422, gbak_plugin_schema_migration_err, "error migrating @1 plugin objects to schema @2. Plugin objects will be in inconsistent state:")
+FB_IMPL_MSG(GBAK, 423, gbak_writing_constants, -901, "00", "000", "writing constants")
+FB_IMPL_MSG(GBAK, 424, gbak_writing_constant, -901, "00", "000", "writing constant %s")
+FB_IMPL_MSG(GBAK, 425, gbak_constant, -901, "00", "000", "constant (in RDB$CONSTANTS)")
+FB_IMPL_MSG(GBAK, 426, gbak_restoring_constant, -901, "00", "000", "restoring constant %s")
diff --git a/src/include/firebird/impl/msg/isql.h b/src/include/firebird/impl/msg/isql.h
index fc4a9619737..f10508fc47b 100644
--- a/src/include/firebird/impl/msg/isql.h
+++ b/src/include/firebird/impl/msg/isql.h
@@ -208,3 +208,5 @@ FB_IMPL_MSG_SYMBOL(ISQL, 208, HLP_SETAUTOTERM, " SET AUTOTERM -- to
FB_IMPL_MSG_SYMBOL(ISQL, 209, HLP_SETWIRESTATS, " SET WIRE_stats -- toggle display of wire (network) statistics")
FB_IMPL_MSG_SYMBOL(ISQL, 210, USAGE_SEARCH_PATH, " -(se)arch_path set schema search path")
FB_IMPL_MSG_SYMBOL(ISQL, 211, MSG_SCHEMAS, "Schemas:")
+FB_IMPL_MSG_SYMBOL(ISQL, 212, NO_CONSTANTS, "There are no constants in this database")
+FB_IMPL_MSG_SYMBOL(ISQL, 213, NO_CONSTANT, "There are no constant @1 in this database")
diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h
index 4bcafc9d62a..27f3322f4cd 100644
--- a/src/include/firebird/impl/msg/jrd.h
+++ b/src/include/firebird/impl/msg/jrd.h
@@ -1005,3 +1005,11 @@ FB_IMPL_MSG(JRD, 1002, argmustbe_numeric_function, -833, "42", "000", "Argument
FB_IMPL_MSG(JRD, 1003, percetile_only_one_sort_item, -833, "42", "000", "The PERCENTILE_DISC and PERENTILE_CONT functions support only one sort item in WITHIN GROUP")
FB_IMPL_MSG(JRD, 1004, argmustbe_const_within_group, -833, "42", "000", "Argument for @1 function must be constant within each group")
FB_IMPL_MSG(JRD, 1005, update_overwrite, -901, "27", "000", "UPDATE will overwrite changes made by the trigger or by the another UPDATE in the same cursor")
+FB_IMPL_MSG(JRD, 1006, const_name, -901, "42", "000", "CONSTANT @1")
+FB_IMPL_MSG(JRD, 1007, private_constant, -901, "42", "000", "The constant @1 is private")
+FB_IMPL_MSG(JRD, 1008, package_alias_help, -901, "42", "000", "Use an alias to resolve the conflict")
+FB_IMPL_MSG(JRD, 1009, bad_constant_blr, -901, "2F", "000", "Error while parsing BLR value of the constant @1")
+FB_IMPL_MSG(JRD, 1010, bad_constant_desc, -901, "2F", "000", "Error while reading type of the constant with name @1")
+FB_IMPL_MSG(JRD, 1011, bad_constant_name, -901, "2F", "000", "Constant @1 cannot be found")
+FB_IMPL_MSG(JRD, 1012, bad_constant_type, -901, "2F", "000", "@1 is not suppotred to be a constant type")
+FB_IMPL_MSG(JRD, 1013, not_defined_constant, -901, "42", "000", "The constant @1 is not defined in the package @2")
diff --git a/src/include/firebird/impl/msg/sqlerr.h b/src/include/firebird/impl/msg/sqlerr.h
index a2687461bcd..b2af71a0334 100644
--- a/src/include/firebird/impl/msg/sqlerr.h
+++ b/src/include/firebird/impl/msg/sqlerr.h
@@ -289,3 +289,6 @@ FB_IMPL_MSG(SQLERR, 1049, dsql_drop_schema_failed, -901, "42", "000", "DROP SCHE
FB_IMPL_MSG(SQLERR, 1050, dsql_recreate_schema_failed, -901, "42", "000", "RECREATE SCHEMA @1 failed")
FB_IMPL_MSG(SQLERR, 1051, dsql_alter_schema_failed, -901, "42", "000", "ALTER SCHEMA @1 failed")
FB_IMPL_MSG(SQLERR, 1052, dsql_create_alter_schema_failed, -901, "42", "000", "CREATE OR ALTER SCHEMA @1 failed")
+FB_IMPL_MSG(SQLERR, 1053, dsql_create_const_failed, -901, "42", "000", "CREATE CONSTANT @1 failed")
+FB_IMPL_MSG(SQLERR, 1054, dsql_alter_const_failed, -901, "42", "000", "ALTER CONSTANT @1 failed")
+FB_IMPL_MSG(SQLERR, 1055, dsql_create_alter_const_failed, -901, "42", "000", "CREATE OR ALTER CONSTANT @1 failed")
diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas
index 0efaf4e8965..78005b6dd9a 100644
--- a/src/include/gen/Firebird.pas
+++ b/src/include/gen/Firebird.pas
@@ -5959,6 +5959,15 @@ IPerformanceStatsImpl = class(IPerformanceStats)
isc_argmustbe_numeric_function = 335545322;
isc_percetile_only_one_sort_item = 335545323;
isc_argmustbe_const_within_group = 335545324;
+ isc_update_overwrite = 335545325;
+ isc_const_name = 335545326;
+ isc_private_constant = 335545327;
+ isc_package_alias_help = 335545328;
+ isc_bad_constant_blr = 335545329;
+ isc_bad_constant_desc = 335545330;
+ isc_bad_constant_name = 335545331;
+ isc_bad_constant_type = 335545332;
+ isc_not_defined_constant = 335545333;
isc_gfix_db_name = 335740929;
isc_gfix_invalid_sw = 335740930;
isc_gfix_incmp_sw = 335740932;
@@ -6223,6 +6232,10 @@ IPerformanceStatsImpl = class(IPerformanceStats)
isc_gbak_invalid_data = 336331094;
isc_gbak_inv_bkup_ver2 = 336331096;
isc_gbak_db_format_too_old2 = 336331100;
+ isc_gbak_writing_constants = 336331175;
+ isc_gbak_writing_constant = 336331176;
+ isc_gbak_constant = 336331177;
+ isc_gbak_restoring_constant = 336331178;
isc_dsql_too_old_ods = 336397205;
isc_dsql_table_not_found = 336397206;
isc_dsql_view_not_found = 336397207;
@@ -6359,6 +6372,9 @@ IPerformanceStatsImpl = class(IPerformanceStats)
isc_dsql_recreate_schema_failed = 336397338;
isc_dsql_alter_schema_failed = 336397339;
isc_dsql_create_alter_schema_failed = 336397340;
+ isc_dsql_create_const_failed = 336397341;
+ isc_dsql_alter_const_failed = 336397342;
+ isc_dsql_create_alter_const_failed = 336397343;
isc_gsec_cant_open_db = 336723983;
isc_gsec_switches_error = 336723984;
isc_gsec_no_op_spec = 336723985;
diff --git a/src/isql/FrontendParser.cpp b/src/isql/FrontendParser.cpp
index 64218057132..7c6b4052df4 100644
--- a/src/isql/FrontendParser.cpp
+++ b/src/isql/FrontendParser.cpp
@@ -491,6 +491,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow()
static constexpr std::string_view TOKEN_VIEWS("VIEWS");
static constexpr std::string_view TOKEN_WIRE_STATISTICS("WIRE_STATISTICS");
static constexpr std::string_view TOKEN_WIRE_STATS("WIRE_STATS");
+ static constexpr std::string_view TOKEN_CONSTANTS("CONSTANTS");
switch (const auto showCommandToken = lexer.getToken(); showCommandToken.type)
{
@@ -655,6 +656,22 @@ FrontendParser::AnyShowNode FrontendParser::parseShow()
if (parseEof())
return ShowWireStatsNode();
}
+ else if (text.length() >= 4 && TOKEN_CONSTANTS.find(text) == 0)
+ {
+ ShowConstantsNode node;
+ node.name = parseQualifiedName(true);
+ if (node.name->package.isEmpty())
+ {
+ // It is expecting to get package = and object =
+ // But it is getting package = and object =
+ // Fix the name
+ node.name->package = node.name->schema;
+ node.name->schema = "";
+ }
+
+ if (parseEof())
+ return node;
+ }
break;
}
diff --git a/src/isql/FrontendParser.h b/src/isql/FrontendParser.h
index 28a1b9cc3ba..1a029e78352 100644
--- a/src/isql/FrontendParser.h
+++ b/src/isql/FrontendParser.h
@@ -119,6 +119,7 @@ class FrontendParser
struct ShowVersionNode {};
struct ShowViewsNode { std::optional name; };
struct ShowWireStatsNode {};
+ struct ShowConstantsNode { std::optional name; };
using AnySetNode = std::variant<
SetNode,
@@ -184,6 +185,7 @@ class FrontendParser
ShowVersionNode,
ShowViewsNode,
ShowWireStatsNode,
+ ShowConstantsNode,
InvalidNode
>;
diff --git a/src/isql/show.epp b/src/isql/show.epp
index e905cc4de9b..919c53485cf 100644
--- a/src/isql/show.epp
+++ b/src/isql/show.epp
@@ -118,6 +118,7 @@ static processing_state show_trigger(const std::optional& n
static processing_state show_users();
static processing_state show_users12();
static processing_state show_wireStats();
+static processing_state show_constants(const std::optional& name, const char* msg = nullptr);
const char* const spaces = " ";
static TEXT Print_buffer[512];
@@ -2571,6 +2572,23 @@ processing_state SHOW_metadata(const FrontendParser::AnyShowNode& node)
return show_wireStats();
},
+ [&](const FrontendParser::ShowConstantsNode& node)
+ {
+ const processing_state ret = show_constants(node.name);
+ if (ret == OBJECT_NOT_FOUND)
+ {
+ if (node.name.has_value())
+ {
+ key = NO_CONSTANT;
+ notFoundName = node.name.value();
+ }
+ else
+ key = NO_CONSTANTS;
+ }
+
+ return ret;
+ },
+
[](auto& arg)
{
static_assert(FrontendParser::AlwaysFalseV,
@@ -6745,3 +6763,140 @@ static processing_state show_wireStats()
return SKIP;
}
+
+static void print_constant_type(const char* fieldName, const char* schemaName)
+{
+ FOR FLD IN RDB$FIELDS WITH
+ FLD.RDB$SCHEMA_NAME EQ schemaName AND
+ FLD.RDB$FIELD_NAME EQ fieldName
+ {
+ // Decide if this is a user-created domain
+ if (!fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) || FLD.RDB$SYSTEM_FLAG == 1)
+ {
+ fb_utils::exact_name(FLD.RDB$FIELD_NAME);
+ isqlGlob.printf("(%s) ", FLD.RDB$FIELD_NAME);
+ }
+
+ const QualifiedMetaString domainName(FLD.RDB$FIELD_NAME, FLD.RDB$SCHEMA_NAME);
+ if (!ISQL_printNumericType(domainName, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SUB_TYPE,
+ FLD.RDB$FIELD_PRECISION, FLD.RDB$FIELD_SCALE))
+ {
+ return;
+ }
+
+ // Use RDB$CHARACTER_LENGTH instead of RDB$FIELD_LENGTH
+ // FSG 19.Nov.2000
+ if ((FLD.RDB$FIELD_TYPE == blr_text || FLD.RDB$FIELD_TYPE == blr_varying) &&
+ !FLD.RDB$CHARACTER_LENGTH.NULL)
+ {
+ isqlGlob.printf("(%d)", FLD.RDB$CHARACTER_LENGTH);
+ }
+
+ // Show international character sets and collations
+
+ SSHORT char_set_id = 0;
+ if (!FLD.RDB$CHARACTER_SET_ID.NULL)
+ char_set_id = FLD.RDB$CHARACTER_SET_ID;
+
+ SSHORT collation = 0;
+ if (!FLD.RDB$COLLATION_ID.NULL)
+ collation = FLD.RDB$COLLATION_ID;
+
+ if (((FLD.RDB$FIELD_TYPE == blr_text ||
+ FLD.RDB$FIELD_TYPE == blr_varying) && FLD.RDB$FIELD_SUB_TYPE != fb_text_subtype_binary) ||
+ FLD.RDB$FIELD_TYPE == blr_blob && FLD.RDB$FIELD_SUB_TYPE == isc_blob_text)
+ show_charsets(char_set_id, collation);
+
+
+ if (fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) && !FLD.RDB$DEFAULT_SOURCE.NULL)
+ {
+ isqlGlob.printf(" ");
+ SHOW_print_metadata_text_blob(isqlGlob.Out, &FLD.RDB$DEFAULT_SOURCE);
+ }
+ }
+ END_FOR
+ ON_ERROR
+ ISQL_errmsg (fbStatus);
+ END_ERROR;
+}
+
+static processing_state show_constants(const std::optional& name, const char* msg)
+{
+ bool first = true;
+
+ if (name.has_value())
+ {
+ // Seach for a constant with name
+ // If SCHEMA is not specifeid, search in all schemas
+
+ const QualifiedMetaString& constant = name.value();
+
+ FOR CONST IN RDB$CONSTANTS WITH
+ CONST.RDB$PACKAGE_NAME EQ constant.package.c_str() AND
+ CONST.RDB$CONSTANT_NAME EQ constant.object.c_str()
+ SORTED BY CONST.RDB$CONSTANT_NAME
+ {
+ if (constant.schema.hasData() && constant.schema != CONST.RDB$SCHEMA_NAME)
+ continue;
+
+ const bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG;
+ const char* type = isPrivate ? "BODY" : "HEADER";
+
+ if (first && msg)
+ isqlGlob.printf("%s%s", msg, NEWLINE);
+
+ first = false;
+
+ fb_utils::exact_name(CONST.RDB$CONSTANT_NAME);
+ fb_utils::exact_name(CONST.RDB$PACKAGE_NAME);
+ isqlGlob.printf("%s (%s %s)%-20s", CONST.RDB$CONSTANT_NAME, CONST.RDB$PACKAGE_NAME, type, " ");
+
+ fb_utils::exact_name(CONST.RDB$FIELD_SOURCE);
+ fb_utils::exact_name(CONST.RDB$SCHEMA_NAME);
+ print_constant_type(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME);
+
+ isqlGlob.printf(NEWLINE);
+ }
+ END_FOR
+ ON_ERROR
+ ISQL_errmsg(fbStatus);
+ return ps_ERR;
+ END_ERROR;
+ }
+ else
+ {
+ // All constants
+
+ FOR CONST IN RDB$CONSTANTS
+ SORTED BY CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME, CONST.RDB$CONSTANT_NAME
+ {
+ const bool isPrivate = !CONST.RDB$PRIVATE_FLAG.NULL && CONST.RDB$PRIVATE_FLAG;
+ const char* type = isPrivate ? "BODY" : "HEADER";
+
+ if (first && msg)
+ isqlGlob.printf("%s%s", msg, NEWLINE);
+
+ first = false;
+
+ fb_utils::exact_name(CONST.RDB$CONSTANT_NAME);
+ fb_utils::exact_name(CONST.RDB$PACKAGE_NAME);
+ isqlGlob.printf("%s (%s %s)%-20s", CONST.RDB$CONSTANT_NAME, CONST.RDB$PACKAGE_NAME, type, " ");
+
+ fb_utils::exact_name(CONST.RDB$FIELD_SOURCE);
+ fb_utils::exact_name(CONST.RDB$SCHEMA_NAME);
+ print_constant_type(CONST.RDB$FIELD_SOURCE, CONST.RDB$SCHEMA_NAME);
+
+ isqlGlob.printf(NEWLINE);
+ }
+ END_FOR
+ ON_ERROR
+ ISQL_errmsg(fbStatus);
+ return ps_ERR;
+ END_ERROR;
+ }
+
+ if (first)
+ return OBJECT_NOT_FOUND;
+
+ return SKIP;
+}
diff --git a/src/isql/tests/FrontendParserTest.cpp b/src/isql/tests/FrontendParserTest.cpp
index d4f62903351..e01b7027588 100644
--- a/src/isql/tests/FrontendParserTest.cpp
+++ b/src/isql/tests/FrontendParserTest.cpp
@@ -609,6 +609,19 @@ BOOST_AUTO_TEST_CASE(ParseShowTest)
"show wire_stat")));
BOOST_TEST(std::holds_alternative(parseShow(
"show wire_statistics")));
+
+ BOOST_TEST(std::holds_alternative(parseShow(
+ "show const")));
+ BOOST_TEST(std::holds_alternative(parseShow(
+ "show constant")));
+ BOOST_TEST(std::holds_alternative(parseShow(
+ "show constants")));
+
+ BOOST_TEST((std::get(parseShow(
+ "show constants MY_PACKAGE.C1")).name == QualifiedMetaString("C1", "", "MY_PACKAGE")));
+
+ BOOST_TEST((std::get(parseShow(
+ "show constants SYSTEM.MY_PACKAGE.C1")).name == QualifiedMetaString("C1", "SYSTEM", "MY_PACKAGE")));
}
diff --git a/src/jrd/BlobUtil.cpp b/src/jrd/BlobUtil.cpp
index 18ca4a4270c..fd28ba7691c 100644
--- a/src/jrd/BlobUtil.cpp
+++ b/src/jrd/BlobUtil.cpp
@@ -286,6 +286,12 @@ BlobUtilPackage::BlobUtilPackage(Firebird::MemoryPool& pool)
},
{fld_varybinary_max, true}
)
+ },
+ // constants
+ {
+ SystemConstant(pool, "FROM_BEGIN", fld_integer, "0", {blr_literal, blr_short, 0, 0, 0}),
+ SystemConstant(pool, "FROM_CURRENT", fld_integer, "1", {blr_literal, blr_short, 0, 1, 0}),
+ SystemConstant(pool, "FROM_END", fld_integer, "2", {blr_literal, blr_short, 0, 2, 0})
}
)
{
diff --git a/src/jrd/Package.epp b/src/jrd/Package.epp
new file mode 100644
index 00000000000..d18387969b3
--- /dev/null
+++ b/src/jrd/Package.epp
@@ -0,0 +1,556 @@
+/*
+ * PROGRAM: Firebird CONSTANTS implementation.
+ * MODULE: Package.epp
+ * DESCRIPTION: Routine to cache and reload package constants
+ *
+ * The contents of this file are subject to the Initial
+ * Developer's Public License Version 1.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
+ *
+ * Software distributed under the License is distributed AS IS,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights
+ * and limitations under the License.
+ *
+ * The Original Code was created by Artyom Abakumov
+ * for Red Soft Corporation.
+ *
+ * Copyright (c) 2025 Red Soft Corporation
+ * and all contributors signed below.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ */
+
+#include "firebird.h"
+#include "../jrd/Package.h"
+
+#include "../jrd/tra.h"
+#include "../jrd/exe_proto.h"
+#include "../jrd/dfw_proto.h"
+#include "../common/dsc_proto.h"
+#include "../jrd/met_proto.h"
+#include "../jrd/met.h"
+#include "../jrd/Statement.h" // Statement
+#include "../jrd/par_proto.h" // PAR_blr
+
+#include "../jrd/cvt_proto.h" // CVT_get_string_ptr
+#include "../jrd/mov_proto.h" // MOV_get_string_ptr
+#include "../common/classes/VaryStr.h"
+#include "../common/classes/alloc.h" // ALLOC_ARGS0
+#include "../dsql/make_proto.h" // DsqlDescMaker
+
+// I do not know why but the macros is undefined in CI
+#ifndef ALLOC_ARGS0
+#define ALLOC_ARGS0
+#endif
+
+using namespace Firebird;
+using namespace Jrd;
+
+DATABASE DB = FILENAME "ODS.RDB";
+
+//----------------------
+
+
+static dsc* executeConstantExpressionWithRequest(thread_db* tdbb, CompilerScratch* csb)
+{
+ Statement* statement = Statement::makeStatement(tdbb, csb, true);
+
+ Request* request = statement->makeRootRequest(tdbb);
+ {
+ Attachment* attachment = tdbb->getAttachment();
+ jrd_tra* transaction = tdbb->getTransaction();
+
+ tdbb->setRequest(request);
+ request->setUsed();
+ request->setAttachment(attachment);
+ attachment->att_requests.add(request);
+
+ TRA_attach_request(transaction, request);
+ }
+
+
+ { // Execute constant expr
+ ValueExprNode* valueNode = static_cast(csb->csb_node); // csb_node is the same as MET_parse_blob output
+ return EVL_expr(tdbb, request, valueNode);
+ }
+}
+
+static void executeConstantExpression(thread_db* tdbb, CompilerScratch* csb, MemoryPool& pool, impure_value& value)
+{
+ Statement* statement = Statement::makeStatement(tdbb, csb, true);
+
+ Request* request = statement->makeRootRequest(tdbb);
+
+ const dsc* temp = EVL_expr(tdbb, request, static_cast(csb->csb_node));
+
+ EVL_make_value(tdbb, temp, &value, &pool);
+ statement->release(tdbb);
+}
+
+
+// Convert a literalNode-unsupported constant type to a supported one if necessary
+static void genConstantCompatibleBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, const dsc& scalar)
+{
+ // Make the blr with only the LiteralNode
+ {
+ BlrWriter::BlrData& blr = dsqlScratch->getBlrData();
+ blr.clear();
+ dsqlScratch->getDebugData().clear();
+ }
+
+ dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5);
+ // Convert a literalNode-unsupported constant type to a supported one if necessary
+ switch (scalar.dsc_dtype)
+ {
+ case dtype_varying:
+ case dtype_cstring:
+ {
+ // Convert to dtype_text
+ TTypeId ttype;
+ UCHAR* ptr;
+ auto& status = tdbb->getAttachment()->att_dec_status;
+ const USHORT len = CVT_get_string_ptr(&scalar, &ttype, &ptr, nullptr, 0, status);
+
+ dsc text;
+ text.makeText(len, ttype, ptr);
+ LiteralNode::genConstant(dsqlScratch, &text, false);
+ break;
+ }
+ case dtype_real:
+ {
+ double newValue = *(float*) scalar.dsc_address;
+
+ dsc descForDouble{};
+ descForDouble.makeDouble();
+
+ UCHAR* ptr;
+ VaryStr temp;
+ TTypeId ttype;
+ ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp));
+
+ descForDouble.dsc_address = ptr;
+
+ LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len);
+ break;
+ }
+ case dtype_dec64: // dtype_dec64 is not present in LiteralNode::genConstant
+ case dtype_double: // MOV_move stores double in scalar->dsc_address. Convert it to string to use genConstant
+ case dtype_dec128:
+ {
+ dsc descForDouble{};
+ descForDouble.makeDecimal128();
+
+ UCHAR* ptr;
+ VaryStr temp;
+ TTypeId ttype;
+ ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp));
+
+ descForDouble.dsc_address = ptr;
+
+ LiteralNode::genConstant(dsqlScratch, &descForDouble, false, len);
+ break;
+ }
+ case dtype_int128:
+ {
+ dsc descForInt128{};
+ descForInt128.makeInt128(scalar.dsc_scale);
+
+ UCHAR* ptr;
+ VaryStr temp;
+ TTypeId ttype;
+ ULONG len = MOV_get_string_ptr(tdbb, &scalar, &ttype, &ptr, &temp, sizeof(temp));
+
+ descForInt128.dsc_address = ptr;
+
+ LiteralNode::genConstant(dsqlScratch, &descForInt128, false, len);
+ break;
+ }
+ case dtype_blob: // Blob ID will be lost
+ status_exception::raise(Arg::Gds(isc_bad_constant_type) << scalar.typeToText());
+ break;
+ default:
+ LiteralNode::genConstant(dsqlScratch, &scalar, false);
+ break;
+ }
+ dsqlScratch->appendUChar(blr_eoc);
+}
+
+bool ConstantValue::hash(thread_db* tdbb, Firebird::sha512& digest) const
+{
+ fb_assert(value.vlu_desc.dsc_dtype != 0);
+ digest.process(sizeof(value.vlu_desc), &value.vlu_desc);
+
+ return true;
+}
+
+dsc ConstantValue::getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name)
+{
+ dsc desc{};
+ bool found = false;
+
+ FbLocalStatus status;
+ static const CachedRequestId requestId;
+ AutoCacheRequest getConstantDscRequest(tdbb, requestId);
+ FOR(REQUEST_HANDLE getConstantDscRequest TRANSACTION_HANDLE transaction)
+ CONST IN RDB$CONSTANTS CROSS FLD IN RDB$FIELDS
+ WITH CONST.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ CONST.RDB$PACKAGE_NAME EQ name.package.c_str() AND
+ CONST.RDB$CONSTANT_NAME EQ name.object.c_str() AND
+ FLD.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME AND
+ FLD.RDB$FIELD_NAME EQ CONST.RDB$FIELD_SOURCE
+ {
+ found = true;
+
+ const bool succeed = DSC_make_descriptor(&desc,
+ FLD.RDB$FIELD_TYPE,
+ FLD.RDB$FIELD_SCALE,
+ FLD.RDB$FIELD_LENGTH,
+ FLD.RDB$FIELD_SUB_TYPE,
+ CSetId(FLD.RDB$CHARACTER_SET_ID),
+ CollId(FLD.RDB$COLLATION_ID));
+
+ if (!succeed)
+ (Arg::Gds(isc_bad_constant_desc) << Arg::Str(name.toQuotedString())).raise();
+ }
+
+ END_FOR
+
+ if (!found)
+ (Arg::Gds(isc_bad_constant_name) << Arg::Str(name.toQuotedString())).raise();
+
+ return desc;
+}
+
+void ConstantValue::genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
+ ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema)
+{
+ { // Prepare BLR writer
+ BlrWriter::BlrData& blr = dsqlScratch->getBlrData();
+ blr.clear();
+ dsqlScratch->getDebugData().clear();
+ }
+
+ // Gen blr into dsqlScratch
+ AutoMemoryPool tempPool(MemoryPool::createPool(ALLOC_ARGS0));
+ CastNode cast(*tempPool, constExpr, type);
+ {
+ dsqlScratch->appendUChar(dsqlScratch->isVersion4() ? blr_version4 : blr_version5);
+ cast.genBlr(dsqlScratch);
+ dsqlScratch->appendUChar(blr_eoc);
+ }
+
+ Attachment* attachment = tdbb->getAttachment();
+ MemoryPool* csb_pool = attachment->att_database->createPool(ALLOC_ARGS0);
+ ContextPoolHolder context(tdbb, csb_pool);
+
+ CompilerScratch* csb = nullptr;
+ Cleanup cc([&tdbb, requestToRestore = tdbb->getRequest(), &csb]()
+ {
+ delete csb;
+ tdbb->setRequest(requestToRestore);
+ });
+
+ // Parse BLR for constant expression
+ PAR_blr(tdbb, &schema, nullptr,
+ dsqlScratch->getBlrData().begin(), dsqlScratch->getBlrData().getCount(),
+ nullptr, &csb,
+ nullptr, false, 0);
+
+ // Execute node from BLR
+ auto output = executeConstantExpressionWithRequest(tdbb, csb);
+ if (output != nullptr)
+ genConstantCompatibleBlr(tdbb, dsqlScratch, *output);
+}
+
+inline bid getConstantBid(thread_db* tdbb, const MetaId id)
+{
+ Attachment* attachment = tdbb->getAttachment();
+ jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb);
+
+ static const CachedRequestId requestId;
+ AutoCacheRequest requestConst(tdbb, requestId);
+
+ FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction)
+ CONST IN RDB$CONSTANTS
+ WITH CONST.RDB$CONSTANT_ID EQ id
+ {
+ return CONST.RDB$CONSTANT_BLR;
+ }
+ END_FOR
+
+ return {};
+}
+
+dsc& ConstantValue::makeValue(thread_db* tdbb)
+{
+ if (value.vlu_desc.dsc_address != nullptr)
+ return value.vlu_desc;
+
+ Attachment* attachment = tdbb->getAttachment();
+
+ MemoryPool* csb_pool = nullptr;
+ try
+ {
+ csb_pool = attachment->att_database->createPool(ALLOC_ARGS0);
+ ContextPoolHolder context(tdbb, csb_pool);
+
+ try
+ {
+ CompilerScratch* csb = nullptr;
+ Cleanup cc([csb]() {delete csb;});
+
+ { // blr
+ // Use cached ID or get a real one (is it probably materialized)
+ bid constantBid = blrBlobId.isEmpty() ? getConstantBid(tdbb, id) : blrBlobId;
+
+ MET_parse_blob(tdbb, &name.schema, nullptr, &constantBid, &csb, nullptr, false, false);
+
+ fb_assert(csb != nullptr);
+ }
+
+ executeConstantExpression(tdbb, csb, getPool(), value);
+ }
+ catch (const Exception& ex)
+ {
+ StaticStatusVector temp_status;
+ ex.stuffException(temp_status);
+
+ const string quotedName = name.toQuotedString();
+ (Arg::Gds(isc_bad_constant_blr) << Arg::Str(quotedName)
+ << Arg::StatusVector(temp_status.begin())).raise();
+ }
+ }
+ catch (const Exception&)
+ {
+ attachment->att_database->deletePool(csb_pool);
+ throw;
+ }
+
+ return value.vlu_desc;
+}
+
+ConstantValue& ConstantsCache::add(const MetaId constId, const QualifiedName& constName, const bool isPrivate)
+{
+ fb_assert(!idMap.exist(constId));
+ fb_assert(!nameMap.exist(constName));
+
+ const ULONG id = values.getCount();
+
+ auto& value = values.add();
+ value.name = constName;
+ value.id = constId;
+ value.isPrivate = isPrivate;
+
+ idMap.put(constId, id);
+ nameMap.put(constName, id);
+
+ return value;
+}
+
+
+// ----------------------------
+// Package class Implementation
+// ----------------------------
+
+Package* Package::create(thread_db* tdbb, MemoryPool& pool, Cached::Package* perm)
+{
+ return FB_NEW_POOL(perm->getPool()) Package(perm);
+}
+
+std::optional Package::getIdByName(thread_db* tdbb, const QualifiedName& name)
+{
+ fb_assert(name.package.isEmpty());
+
+ SET_TDBB(tdbb);
+ Attachment* attachment = tdbb->getAttachment();
+ std::optional id;
+
+ static const CachedRequestId requestId;
+ AutoCacheRequest request(tdbb, requestId);
+
+ FOR (REQUEST_HANDLE request)
+ PKG IN RDB$PACKAGES
+ WITH PKG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND
+ PKG.RDB$PACKAGE_NAME EQ name.object.c_str()
+ {
+ id = PKG.RDB$PACKAGE_ID;
+ }
+ END_FOR
+
+ return id;
+}
+
+ScanResult Package::scan(thread_db* tdbb, ObjectBase::Flag flags)
+{
+ const bool skipMakeValue = (flags & CacheFlag::MINISCAN) != 0;
+
+ Attachment* attachment = tdbb->getAttachment();
+ jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb);
+ Database* dbb = tdbb->getDatabase();
+
+ MemoryPool& pool = getPermanent()->getPool();
+
+ { // Security
+ static const CachedRequestId requestId;
+ AutoCacheRequest requestConst(tdbb, requestId);
+
+ FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction)
+ PKG IN RDB$PACKAGES CROSS SCH IN RDB$SCHEMAS
+ WITH PKG.RDB$SCHEMA_NAME EQ SCH.RDB$SCHEMA_NAME AND
+ PKG.RDB$PACKAGE_ID EQ getId() AND
+ PKG.RDB$SECURITY_CLASS NOT MISSING
+ {
+ getPermanent()->securityName.object = PKG.RDB$SECURITY_CLASS;
+ getPermanent()->securityName.schema = SCH.RDB$SECURITY_CLASS;
+ }
+ END_FOR
+ }
+
+ { // Constants
+ static const CachedRequestId requestId;
+ AutoCacheRequest requestConst(tdbb, requestId);
+
+ FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction)
+ PKG IN RDB$PACKAGES CROSS CONST IN RDB$CONSTANTS
+ WITH PKG.RDB$PACKAGE_ID EQ getId() AND
+ PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME AND
+ PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME
+ {
+ addConstant(tdbb, CONST.RDB$CONSTANT_ID,
+ QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME),
+ CONST.RDB$PRIVATE_FLAG,
+ CONST.RDB$CONSTANT_BLR,
+ skipMakeValue);
+
+ if (skipMakeValue)
+ this->m_callReload = true; // Call makeValue in reload
+ }
+ END_FOR
+ }
+
+ return this->m_callReload ? ScanResult::REPEAT : ScanResult::COMPLETE;
+}
+
+void Package::checkReload(thread_db* tdbb)
+{
+ if (m_callReload)
+ {
+ reload(tdbb, 0);
+ m_callReload = false;
+ }
+}
+
+ScanResult Package::reload(thread_db* tdbb, ObjectBase::Flag fl)
+{
+ Attachment* attachment = tdbb->getAttachment();
+ jrd_tra* metaTransaction = attachment->getMetaTransaction(tdbb);
+ Database* dbb = tdbb->getDatabase();
+
+ MemoryPool& pool = getPermanent()->getPool();
+
+ static const CachedRequestId requestId;
+ AutoCacheRequest requestConst(tdbb, requestId);
+
+ constants.clear();
+ FOR(REQUEST_HANDLE requestConst TRANSACTION_HANDLE metaTransaction)
+ PKG IN RDB$PACKAGES CROSS CONST IN RDB$CONSTANTS
+ WITH PKG.RDB$PACKAGE_ID EQ getId() AND
+ PKG.RDB$PACKAGE_NAME EQ CONST.RDB$PACKAGE_NAME AND
+ PKG.RDB$SCHEMA_NAME EQ CONST.RDB$SCHEMA_NAME
+ {
+ addConstant(tdbb, CONST.RDB$CONSTANT_ID,
+ QualifiedName(CONST.RDB$CONSTANT_NAME, CONST.RDB$SCHEMA_NAME, CONST.RDB$PACKAGE_NAME),
+ CONST.RDB$PRIVATE_FLAG,
+ CONST.RDB$CONSTANT_BLR);
+ }
+ END_FOR
+
+ return ScanResult::COMPLETE;
+}
+
+int Package::objectType()
+{
+ return obj_package_header;
+}
+
+bool Package::hash(thread_db* tdbb, Firebird::sha512& digest)
+{
+ for (auto& constant : constants.values)
+ constant.hash(tdbb, digest);
+
+ return true;
+}
+
+ConstantValue& Package::addConstant(thread_db* tdbb, const MetaId constId,
+ const QualifiedName& constName,
+ const bool isPrivate,
+ const TypeClause* type)
+{
+ dsc typeDesc;
+ DsqlDescMaker::fromField(&typeDesc, type);
+
+ auto& value = constants.add(constId, constName, isPrivate);
+ value.blrBlobId = {};
+ value.value.vlu_desc = typeDesc;
+
+ return value;
+}
+
+ConstantValue& Package::addConstant(thread_db* tdbb, const MetaId constId,
+ const QualifiedName& constName,
+ const bool isPrivate,
+ const bid blrBlobId,
+ const bool skipMakeValue)
+{
+ auto& value = constants.add(constId, constName, isPrivate);
+ value.blrBlobId = blrBlobId;
+ if (!skipMakeValue)
+ value.makeValue(tdbb);
+
+ return value;
+}
+
+ConstantValue& Package::updateConstant(thread_db* tdbb, const MetaId constId,
+ const bool isPrivate,
+ const TypeClause* type)
+{
+ dsc typeDesc;
+ DsqlDescMaker::fromField(&typeDesc, type);
+
+ auto* innerId = constants.idMap.get(constId);
+ fb_assert(innerId != nullptr);
+
+ auto& constant = constants.values[*innerId];
+ constant.blrBlobId = {};
+ constant.isPrivate = isPrivate;
+
+ delete constant.value.vlu_string;
+ constant.value = {};
+ constant.value.vlu_desc = typeDesc;
+
+ return constant;
+}
+
+ConstantValue* Package::findConstant(const MetaId id)
+{
+ auto it = constants.idMap.get(id);
+
+ if (it != nullptr)
+ return &constants.values[*it];
+
+ return nullptr;
+}
+
+ConstantValue* Package::findConstant(const QualifiedName& name)
+{
+ auto it = constants.nameMap.get(name);
+
+ if (it != nullptr)
+ return &constants.values[*it];
+
+ return nullptr;
+}
diff --git a/src/jrd/Package.h b/src/jrd/Package.h
new file mode 100644
index 00000000000..375f48368e8
--- /dev/null
+++ b/src/jrd/Package.h
@@ -0,0 +1,237 @@
+/*
+ * PROGRAM: Firebird CONSTANTS implementation.
+ * MODULE: Package.h
+ * DESCRIPTION: Routine to cache and reload Package constants
+ *
+ * The contents of this file are subject to the Initial
+ * Developer's Public License Version 1.0 (the "License");
+ * you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl.
+ *
+ * Software distributed under the License is distributed AS IS,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied.
+ * See the License for the specific language governing rights
+ * and limitations under the License.
+ *
+ * The Original Code was created by Artyom Abakumov
+ * for Red Soft Corporation.
+ *
+ * Copyright (c) 2025 Red Soft Corporation
+ * and all contributors signed below.
+ *
+ * All Rights Reserved.
+ * Contributor(s): ______________________________________.
+ */
+
+
+#ifndef JRD_CONSTANT_H
+#define JRD_CONSTANT_H
+
+#include "firebird.h"
+#include "../jrd/CacheVector.h"
+#include "../jrd/Resources.h"
+#include "../jrd/obj.h"
+#include "../jrd/val.h"
+#include "../jrd/lck.h"
+#include "../common/classes/GenericMap.h"
+
+namespace Jrd
+{
+class DsqlCompilerScratch;
+class dsql_fld;
+
+class ConstantValue final : public Firebird::PermanentStorage
+{
+public:
+ ConstantValue(MemoryPool& pool) :
+ Firebird::PermanentStorage(pool),
+ name(pool)
+ { }
+
+ MetaId id{};
+ QualifiedName name;
+
+ // Keep type to gen hash (when not commited - we cannot read it from system table)
+ // Keep value when scanning and after the first execution
+ impure_value value{};
+
+ // keep only materialized value
+ bid blrBlobId{};
+
+ bool isPrivate = false;
+
+ bool hash(thread_db* tdbb, Firebird::sha512& digest) const;
+
+ static dsc getDesc(thread_db* tdbb, Jrd::jrd_tra* transaction, const QualifiedName& name);
+
+ static void genConstantBlr(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch,
+ ValueExprNode* constExpr, dsql_fld* type, const MetaName& schema);
+
+ dsc& makeValue(thread_db* tdbb);
+
+ ~ConstantValue()
+ {
+ delete value.vlu_string;
+ }
+};
+
+struct ConstantsCache
+{
+ using ValueId = ULONG;
+
+ ConstantsCache(MemoryPool& pool) :
+ idMap(pool),
+ nameMap(pool),
+ values(pool)
+ { }
+
+ Firebird::NonPooledMap idMap;
+ Firebird::LeftPooledMap nameMap;
+
+ Firebird::ObjectsArray values;
+
+ ConstantValue& add(const MetaId constId, const QualifiedName& constName, const bool isPrivate);
+
+ void clear()
+ {
+ idMap.clear();
+ nameMap.clear();
+ values.clear();
+ }
+};
+
+
+class PackagePermanent : public Firebird::PermanentStorage
+{
+public:
+ explicit PackagePermanent(thread_db* tdbb, MemoryPool& p, MetaId metaId, NoData)
+ : PermanentStorage(p),
+ id(metaId),
+ name(p)
+ { }
+
+ explicit PackagePermanent(MemoryPool& p)
+ : PermanentStorage(p),
+ id(~0),
+ name(p)
+ { }
+
+ MetaId getId() const
+ {
+ return id;
+ }
+
+ static bool destroy(thread_db* tdbb, PackagePermanent* routine)
+ {
+ return false;
+ }
+
+ void releaseLock(thread_db*) { }
+
+ const QualifiedName& getName() const noexcept { return name; }
+ void setName(const QualifiedName& value) { name = value; }
+
+ bool hasData() const { return name.hasData(); }
+
+public:
+ MetaId id;
+ QualifiedName name;
+ QualifiedName securityName;
+};
+
+class Package final : public Firebird::PermanentStorage, public ObjectBase
+{
+public:
+ // lock requeued by CacheElement
+ static const enum lck_t LOCKTYPE = LCK_package_rescan;
+
+private:
+ explicit Package(Cached::Package* perm)
+ : Firebird::PermanentStorage(perm->getPool()),
+ constants(perm->getPool()),
+ cachedPackage(perm)
+ { }
+
+public:
+ explicit Package(MemoryPool& p)
+ : Firebird::PermanentStorage(p),
+ constants(p)
+ { }
+
+ // Methods needed by the MetaCache
+ // ----------
+
+ static bool destroy(thread_db* tdbb, Package* routine)
+ {
+ return false;
+ }
+
+ static Package* create(thread_db* tdbb, MemoryPool& pool, Cached::Package* perm);
+ static std::optional getIdByName(thread_db* tdbb, const QualifiedName& name);
+
+ ScanResult scan(thread_db* tdbb, ObjectBase::Flag flags);
+ void checkReload(thread_db* tdbb);
+ ScanResult reload(thread_db* tdbb, ObjectBase::Flag flags);
+
+ static const char* objectFamily(void*)
+ {
+ return "package";
+ }
+
+ MetaId getId() const
+ {
+ return getPermanent()->id;
+ }
+
+ int getObjectType() const noexcept
+ {
+ return objectType();
+ }
+
+ SLONG getSclType() const noexcept
+ {
+ return obj_package_header;
+ }
+
+ static int objectType();
+
+ bool hash(thread_db* tdbb, Firebird::sha512& digest);
+
+ Cached::Package* getPermanent() const noexcept
+ {
+ return cachedPackage;
+ }
+
+ // ----------
+
+ ConstantValue& addConstant(thread_db* tdbb, const MetaId constId,
+ const QualifiedName& constName,
+ const bool isPrivate,
+ const TypeClause* type);
+
+ ConstantValue& addConstant(thread_db* tdbb, const MetaId constId,
+ const QualifiedName& constName,
+ const bool isPrivate,
+ const bid blrBlobId,
+ const bool skipMakeValue = false);
+
+ ConstantValue& updateConstant(thread_db* tdbb, const MetaId constId,
+ const bool isPrivate,
+ const TypeClause* type);
+
+ ConstantValue* findConstant(const MetaId id);
+ ConstantValue* findConstant(const QualifiedName& name);
+
+private:
+ virtual ~Package() = default;
+
+private:
+ ConstantsCache constants;
+ Cached::Package* cachedPackage = nullptr; // entry in the cache
+ bool m_callReload = true;
+};
+
+} // namespace Jrd
+
+#endif // JRD_CONSTANT_H
diff --git a/src/jrd/Resources.cpp b/src/jrd/Resources.cpp
index 99962ee616d..36e0e5a895d 100644
--- a/src/jrd/Resources.cpp
+++ b/src/jrd/Resources.cpp
@@ -32,6 +32,7 @@ void Resources::transfer(thread_db* tdbb, VersionedObjects* to, bool internal)
gotHash += functions.transfer(tdbb, to, internal, digest);
gotHash += triggers.transfer(tdbb, to, internal, digest);
gotHash += indices.transfer(tdbb, to, internal, digest);
+ gotHash += packages.transfer(tdbb, to, internal, digest);
if (hasHash)
{
diff --git a/src/jrd/Resources.h b/src/jrd/Resources.h
index cb7f2f9276d..a181bdb2611 100644
--- a/src/jrd/Resources.h
+++ b/src/jrd/Resources.h
@@ -42,6 +42,8 @@ class DbTriggers;
class CharSetVers;
class IndexPermanent;
class IndexVersion;
+class Package;
+class PackagePermanent;
namespace Cached
{
@@ -52,6 +54,7 @@ namespace Cached
typedef CacheElement Function;
typedef CacheElement Triggers;
typedef CacheElement Index;
+ typedef CacheElement Package;
}
class Resources;
@@ -66,6 +69,7 @@ union VersionedPartPtr
CharSetVers* charset;
DbTriggers* triggers;
IndexVersion* index;
+ Package* package;
};
class VersionedObjects : public pool_alloc_rpt,
@@ -120,6 +124,7 @@ template <> inline jrd_rel*& VersionedObjects::object(FB_SIZE_T n) { re
template <> inline CharSetVers*& VersionedObjects::object(FB_SIZE_T n) { return data[n].charset; }
template <> inline DbTriggers*& VersionedObjects::object(FB_SIZE_T n) { return data[n].triggers; }
template <> inline IndexVersion*& VersionedObjects::object(FB_SIZE_T n) { return data[n].index; }
+template <> inline Package*& VersionedObjects::object(FB_SIZE_T n) { return data[n].package; }
template <> inline Function* VersionedObjects::object(FB_SIZE_T n) const { return data[n].function; }
template <> inline jrd_prc* VersionedObjects::object(FB_SIZE_T n) const { return data[n].procedure; }
@@ -127,6 +132,7 @@ template <> inline jrd_rel* VersionedObjects::object(FB_SIZE_T n) const
template <> inline CharSetVers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].charset; }
template <> inline DbTriggers* VersionedObjects::object(FB_SIZE_T n) const { return data[n].triggers; }
template <> inline IndexVersion* VersionedObjects::object(FB_SIZE_T n) const { return data[n].index; }
+template <> inline Package* VersionedObjects::object(FB_SIZE_T n) const { return data[n].package; }
template
@@ -283,7 +289,8 @@ class Resources final
procedures(p, versionCurrentPosition),
functions(p, versionCurrentPosition),
triggers(p, versionCurrentPosition),
- indices(p, versionCurrentPosition)
+ indices(p, versionCurrentPosition),
+ packages(p, versionCurrentPosition)
{ }
~Resources();
@@ -294,6 +301,13 @@ class Resources final
RscArray functions;
RscArray triggers;
RscArray indices;
+ RscArray packages;
+
+ inline FB_SIZE_T countVersionedObjects() const noexcept
+ {
+ return charSets.getCount() + relations.getCount() + procedures.getCount() +
+ functions.getCount() + triggers.getCount() + packages.getCount();
+ }
};
// specialization
@@ -303,6 +317,7 @@ template <> inline const Resources::RscArray& Resour
template <> inline const Resources::RscArray& Resources::objects() const { return charSets; }
template <> inline const Resources::RscArray& Resources::objects() const { return triggers; }
template <> inline const Resources::RscArray& Resources::objects() const { return indices; }
+template <> inline const Resources::RscArray& Resources::objects() const { return packages; }
namespace Rsc
{
@@ -312,6 +327,7 @@ namespace Rsc
typedef CachedResource CSet;
typedef CachedResource Trig;
typedef CachedResource Idx;
+ typedef CachedResource Package;
}; //namespace Rsc
diff --git a/src/jrd/Statement.cpp b/src/jrd/Statement.cpp
index bf5418ee4b0..525950ec9e4 100644
--- a/src/jrd/Statement.cpp
+++ b/src/jrd/Statement.cpp
@@ -223,9 +223,7 @@ void Statement::loadResources(thread_db* tdbb, Request* req, bool withLock)
if (ddl || (!latestVer) || (latestVer->version != frontVersion))
{
- const FB_SIZE_T resourceCount = latestVer ? latestVer->getCapacity() :
- resources->charSets.getCount() + resources->relations.getCount() + resources->procedures.getCount() +
- resources->functions.getCount() + resources->triggers.getCount();
+ const FB_SIZE_T resourceCount = latestVer ? latestVer->getCapacity() : resources->countVersionedObjects();
AutoPtr newVer = FB_NEW_RPT(*pool, resourceCount) VersionedObjects(resourceCount);
MetadataCache::Version ver(mdc);
diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp
index 4c216b68031..63c22df4273 100644
--- a/src/jrd/SysFunction.cpp
+++ b/src/jrd/SysFunction.cpp
@@ -6977,98 +6977,99 @@ dsc* evlUnicodeVal(thread_db* tdbb, const SysFunction*, const NestValueArray& ar
+constexpr static auto DEFAULT = SysFunction::DETERMINISTIC | SysFunction::CONSTANT;
const SysFunction SysFunction::functions[] =
{
- // name, minArgCount, maxArgCount, deterministic, setParamsFunc, makeFunc, evlFunc, misc
-
- {"ABS", 1, 1, true, setParamsDblDec, makeAbs, evlAbs, NULL},
- {"ACOS", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcos},
- {"ACOSH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcosh},
- {"ASCII_CHAR", 1, 1, true, setParamsInteger, makeAsciiChar, evlAsciiChar, NULL},
- {"ASCII_VAL", 1, 1, true, setParamsAsciiVal, makeShortResult, evlAsciiVal, NULL},
- {"ASIN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsin},
- {"ASINH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsinh},
- {"ATAN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtan},
- {"ATANH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtanh},
- {"ATAN2", 2, 2, true, setParamsDouble, makeDoubleResult, evlAtan2, NULL},
- {"BASE64_DECODE", 1, 1, true, NULL, makeDecode64, evlDecode64, NULL},
- {"BASE64_ENCODE", 1, 1, true, NULL, makeEncode64, evlEncode64, NULL},
- {"BIN_AND", 2, -1, true, setParamsBin, makeBin, evlBin, (void*) funBinAnd},
- {"BIN_NOT", 1, 1, true, setParamsBin, makeBin, evlBin, (void*) funBinNot},
- {"BIN_OR", 2, -1, true, setParamsBin, makeBin, evlBin, (void*) funBinOr},
- {"BIN_SHL", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShl},
- {"BIN_SHR", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShr},
- {"BIN_SHL_ROT", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShlRot},
- {"BIN_SHR_ROT", 2, 2, true, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShrRot},
- {"BIN_XOR", 2, -1, true, setParamsBin, makeBin, evlBin, (void*) funBinXor},
- {"BLOB_APPEND", 2, -1, true, setParamsBlobAppend, makeBlobAppend, evlBlobAppend, NULL},
- {"CEIL", 1, 1, true, setParamsDblDec, makeCeilFloor, evlCeil, NULL},
- {"CEILING", 1, 1, true, setParamsDblDec, makeCeilFloor, evlCeil, NULL},
- {"CHAR_TO_UUID", 1, 1, true, setParamsCharToUuid, makeUuid, evlCharToUuid, NULL},
- {"COMPARE_DECFLOAT", 2, 2, true, setParamsDecFloat, makeShortResult, evlCompare, (void*) funCmpDec},
- {"COS", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCos},
- {"COSH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCosh},
- {"COT", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCot},
- {"CRYPT_HASH", 2, 2, true, setParamsHash, makeHash, evlHash, NULL},
- {"DATEADD", 3, 3, true, setParamsDateAdd, makeDateAdd, evlDateAdd, NULL},
- {"DATEDIFF", 3, 3, true, setParamsDateDiff, makeDateDiff, evlDateDiff, NULL},
- {"DECRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, true, setParamsEncrypt, makeCrypt, evlDecrypt, NULL},
- {"ENCRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, true, setParamsEncrypt, makeCrypt, evlEncrypt, NULL},
- {"EXP", 1, 1, true, setParamsDblDec, makeDblDecResult, evlExp, NULL},
- {"FIRST_DAY", 2, 2, true, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay},
- {"FLOOR", 1, 1, true, setParamsDblDec, makeCeilFloor, evlFloor, NULL},
- {"GEN_UUID", 0, 1, false, NULL, makeUuid, evlGenUuid, NULL},
- {"GREATEST", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue},
- {"HASH", 1, 2, true, setParamsHash, makeHash, evlHash, NULL},
- {"HEX_DECODE", 1, 1, true, NULL, makeDecodeHex, evlDecodeHex, NULL},
- {"HEX_ENCODE", 1, 1, true, NULL, makeEncodeHex, evlEncodeHex, NULL},
- {"LAST_DAY", 2, 2, true, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funLastDay},
- {"LEAST", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue},
- {"LEFT", 2, 2, true, setParamsSecondInteger, makeLeftRight, evlLeft, NULL},
- {"LN", 1, 1, true, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLnat},
- {"LOG", 2, 2, true, setParamsDblDec, makeDblDecResult, evlLog, NULL},
- {"LOG10", 1, 1, true, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLog10},
- {"LPAD", 2, 3, true, setParamsSecondInteger, makePad, evlPad, (void*) funLPad},
- {"MAKE_DBKEY", 2, 4, true, setParamsMakeDbkey, makeDbkeyResult, evlMakeDbkey, NULL},
- {"MAXVALUE", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue},
- {"MINVALUE", 1, -1, true, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue},
- {"MOD", 2, 2, true, setParamsFromList, makeMod, evlMod, NULL},
- {"NORMALIZE_DECFLOAT", 1, 1, true, setParamsDecFloat, makeDecFloatResult, evlNormDec, NULL},
- {"OVERLAY", 3, 4, true, setParamsOverlay, makeOverlay, evlOverlay, NULL},
- {"PI", 0, 0, true, NULL, makePi, evlPi, NULL},
- {"POSITION", 2, 3, true, setParamsPosition, makeLongResult, evlPosition, NULL},
- {"POWER", 2, 2, true, setParamsDblDec, makeDblDecResult, evlPower, NULL},
- {"QUANTIZE", 2, 2, true, setParamsDecFloat, makeDecFloatResult, evlQuantize, NULL},
- {"RAND", 0, 0, false, NULL, makeDoubleResult, evlRand, NULL},
- {RDB_GET_CONTEXT, 2, 2, true, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL},
- {"RDB$GET_TRANSACTION_CN", 1, 1, false, setParamsInt64, makeGetTranCN, evlGetTranCN, NULL},
- {"RDB$ROLE_IN_USE", 1, 1, true, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL},
- {RDB_RESET_CONTEXT, 1, 1, false, setParamsResetContext, makeLongResult, evlResetContext, NULL},
- {RDB_SET_CONTEXT, 3, 3, false, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL},
- {"RDB$SYSTEM_PRIVILEGE", 1, 1, true, NULL, makeBooleanResult, evlSystemPrivilege, NULL},
- {"REPLACE", 3, 3, true, setParamsFromList, makeReplace, evlReplace, NULL},
- {"REVERSE", 1, 1, true, NULL, makeReverse, evlReverse, NULL},
- {"RIGHT", 2, 2, true, setParamsSecondInteger, makeLeftRight, evlRight, NULL},
- {"ROUND", 1, 2, true, setParamsRoundTrunc, makeRound, evlRound, NULL},
- {"RPAD", 2, 3, true, setParamsSecondInteger, makePad, evlPad, (void*) funRPad},
- {"RSA_DECRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, true, setParamsRsaEncrypt, makeRsaCrypt, evlRsaDecrypt, NULL},
- {"RSA_ENCRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, true, setParamsRsaEncrypt, makeRsaCrypt, evlRsaEncrypt, NULL},
- {"RSA_PRIVATE", 1, 1, false, setParamsInteger, makeRsaPrivate, evlRsaPrivate, NULL},
- {"RSA_PUBLIC", 1, 1, false, setParamsRsaPublic, makeRsaPublic, evlRsaPublic, NULL},
- {"RSA_SIGN_HASH", RSA_SIGN_ARG_MAX, RSA_SIGN_ARG_MAX, true, setParamsRsaSign, makeRsaSign, evlRsaSign, NULL},
- {"RSA_VERIFY_HASH", RSA_VERIFY_ARG_MAX, RSA_VERIFY_ARG_MAX, true, setParamsRsaVerify, makeBoolResult, evlRsaVerify, NULL},
- {"SIGN", 1, 1, true, setParamsDblDec, makeShortResult, evlSign, NULL},
- {"SIN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSin},
- {"SINH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSinh},
- {"SQRT", 1, 1, true, setParamsDblDec, makeDblDecResult, evlSqrt, NULL},
- {"TAN", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTan},
- {"TANH", 1, 1, true, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh},
- {"TOTALORDER", 2, 2, true, setParamsDecFloat, makeShortResult, evlCompare, (void*) funTotalOrd},
- {"TRUNC", 1, 2, true, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL},
- {"UNICODE_CHAR", 1, 1, true, setParamsInteger, makeUnicodeChar, evlUnicodeChar, NULL},
- {"UNICODE_VAL", 1, 1, true, setParamsUnicodeVal, makeLongResult, evlUnicodeVal, NULL},
- {"UUID_TO_CHAR", 1, 1, true, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL},
- {"", 0, 0, false, NULL, NULL, NULL, NULL}
+ // name, minArgCount, maxArgCount, flags, setParamsFunc, makeFunc, evlFunc, misc
+
+ {"ABS", 1, 1, DEFAULT, setParamsDblDec, makeAbs, evlAbs, NULL},
+ {"ACOS", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcos},
+ {"ACOSH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcosh},
+ {"ASCII_CHAR", 1, 1, DEFAULT, setParamsInteger, makeAsciiChar, evlAsciiChar, NULL},
+ {"ASCII_VAL", 1, 1, DEFAULT, setParamsAsciiVal, makeShortResult, evlAsciiVal, NULL},
+ {"ASIN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsin},
+ {"ASINH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAsinh},
+ {"ATAN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtan},
+ {"ATANH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAtanh},
+ {"ATAN2", 2, 2, DEFAULT, setParamsDouble, makeDoubleResult, evlAtan2, NULL},
+ {"BASE64_DECODE", 1, 1, DEFAULT, NULL, makeDecode64, evlDecode64, NULL},
+ {"BASE64_ENCODE", 1, 1, DEFAULT, NULL, makeEncode64, evlEncode64, NULL},
+ {"BIN_AND", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinAnd},
+ {"BIN_NOT", 1, 1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinNot},
+ {"BIN_OR", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinOr},
+ {"BIN_SHL", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShl},
+ {"BIN_SHR", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShr},
+ {"BIN_SHL_ROT", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShlRot},
+ {"BIN_SHR_ROT", 2, 2, DEFAULT, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShrRot},
+ {"BIN_XOR", 2, -1, DEFAULT, setParamsBin, makeBin, evlBin, (void*) funBinXor},
+ {"BLOB_APPEND", 2, -1, DETERMINISTIC, setParamsBlobAppend, makeBlobAppend, evlBlobAppend, NULL},
+ {"CEIL", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlCeil, NULL},
+ {"CEILING", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlCeil, NULL},
+ {"CHAR_TO_UUID", 1, 1, DEFAULT, setParamsCharToUuid, makeUuid, evlCharToUuid, NULL},
+ {"COMPARE_DECFLOAT", 2, 2, DEFAULT, setParamsDecFloat, makeShortResult, evlCompare, (void*) funCmpDec},
+ {"COS", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCos},
+ {"COSH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCosh},
+ {"COT", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCot},
+ {"CRYPT_HASH", 2, 2, DEFAULT, setParamsHash, makeHash, evlHash, NULL},
+ {"DATEADD", 3, 3, DEFAULT, setParamsDateAdd, makeDateAdd, evlDateAdd, NULL},
+ {"DATEDIFF", 3, 3, DEFAULT, setParamsDateDiff, makeDateDiff, evlDateDiff, NULL},
+ {"DECRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, DEFAULT, setParamsEncrypt, makeCrypt, evlDecrypt, NULL},
+ {"ENCRYPT", CRYPT_ARG_MAX, CRYPT_ARG_MAX, DEFAULT, setParamsEncrypt, makeCrypt, evlEncrypt, NULL},
+ {"EXP", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlExp, NULL},
+ {"FIRST_DAY", 2, 2, DEFAULT, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funFirstDay},
+ {"FLOOR", 1, 1, DEFAULT, setParamsDblDec, makeCeilFloor, evlFloor, NULL},
+ {"GEN_UUID", 0, 0, CONSTANT, NULL, makeUuid, evlGenUuid, NULL},
+ {"GREATEST", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue},
+ {"HASH", 1, 2, DEFAULT, setParamsHash, makeHash, evlHash, NULL},
+ {"HEX_DECODE", 1, 1, DEFAULT, NULL, makeDecodeHex, evlDecodeHex, NULL},
+ {"HEX_ENCODE", 1, 1, DEFAULT, NULL, makeEncodeHex, evlEncodeHex, NULL},
+ {"LAST_DAY", 2, 2, DEFAULT, setParamsFirstLastDay, makeFirstLastDayResult, evlFirstLastDay, (void*) funLastDay},
+ {"LEAST", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue},
+ {"LEFT", 2, 2, DEFAULT, setParamsSecondInteger, makeLeftRight, evlLeft, NULL},
+ {"LN", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLnat},
+ {"LOG", 2, 2, DEFAULT, setParamsDblDec, makeDblDecResult, evlLog, NULL},
+ {"LOG10", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLog10},
+ {"LPAD", 2, 3, DEFAULT, setParamsSecondInteger, makePad, evlPad, (void*) funLPad},
+ {"MAKE_DBKEY", 2, 4, DEFAULT, setParamsMakeDbkey, makeDbkeyResult, evlMakeDbkey, NULL},
+ {"MAXVALUE", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue},
+ {"MINVALUE", 1, -1, DEFAULT, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue},
+ {"MOD", 2, 2, DEFAULT, setParamsFromList, makeMod, evlMod, NULL},
+ {"NORMALIZE_DECFLOAT", 1, 1, DEFAULT, setParamsDecFloat, makeDecFloatResult, evlNormDec, NULL},
+ {"OVERLAY", 3, 4, DEFAULT, setParamsOverlay, makeOverlay, evlOverlay, NULL},
+ {"PI", 0, 0, DEFAULT, NULL, makePi, evlPi, NULL},
+ {"POSITION", 2, 4, DEFAULT, setParamsPosition, makeLongResult, evlPosition, NULL},
+ {"POWER", 2, 2, DEFAULT, setParamsDblDec, makeDblDecResult, evlPower, NULL},
+ {"QUANTIZE", 2, 2, DEFAULT, setParamsDecFloat, makeDecFloatResult, evlQuantize, NULL},
+ {"RAND", 0, 0, 0, NULL, makeDoubleResult, evlRand, NULL},
+ {RDB_GET_CONTEXT, 2, 2, DETERMINISTIC, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL},
+ {"RDB$GET_TRANSACTION_CN", 1, 1, 0, setParamsInt64, makeGetTranCN, evlGetTranCN, NULL},
+ {"RDB$ROLE_IN_USE", 1, 1, DETERMINISTIC, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL},
+ {RDB_RESET_CONTEXT, 1, 1, 0, setParamsResetContext, makeLongResult, evlResetContext, NULL},
+ {RDB_SET_CONTEXT, 3, 3, 0, setParamsGetSetContext, makeGetSetContext, evlSetContext, NULL},
+ {"RDB$SYSTEM_PRIVILEGE", 1, 1, DETERMINISTIC, NULL, makeBooleanResult, evlSystemPrivilege, NULL},
+ {"REPLACE", 3, 3, DEFAULT, setParamsFromList, makeReplace, evlReplace, NULL},
+ {"REVERSE", 1, 1, DEFAULT, NULL, makeReverse, evlReverse, NULL},
+ {"RIGHT", 2, 2, DEFAULT, setParamsSecondInteger, makeLeftRight, evlRight, NULL},
+ {"ROUND", 1, 2, DEFAULT, setParamsRoundTrunc, makeRound, evlRound, NULL},
+ {"RPAD", 2, 3, DEFAULT, setParamsSecondInteger, makePad, evlPad, (void*) funRPad},
+ {"RSA_DECRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, DEFAULT, setParamsRsaEncrypt, makeRsaCrypt, evlRsaDecrypt, NULL},
+ {"RSA_ENCRYPT", RSA_CRYPT_ARG_MAX, RSA_CRYPT_ARG_MAX, DEFAULT, setParamsRsaEncrypt, makeRsaCrypt, evlRsaEncrypt, NULL},
+ {"RSA_PRIVATE", 1, 1, 0, setParamsInteger, makeRsaPrivate, evlRsaPrivate, NULL},
+ {"RSA_PUBLIC", 1, 1, 0, setParamsRsaPublic, makeRsaPublic, evlRsaPublic, NULL},
+ {"RSA_SIGN_HASH", RSA_SIGN_ARG_MAX, RSA_SIGN_ARG_MAX, DEFAULT, setParamsRsaSign, makeRsaSign, evlRsaSign, NULL},
+ {"RSA_VERIFY_HASH", RSA_VERIFY_ARG_MAX, RSA_VERIFY_ARG_MAX, DEFAULT, setParamsRsaVerify, makeBoolResult, evlRsaVerify, NULL},
+ {"SIGN", 1, 1, DEFAULT, setParamsDblDec, makeShortResult, evlSign, NULL},
+ {"SIN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSin},
+ {"SINH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSinh},
+ {"SQRT", 1, 1, DEFAULT, setParamsDblDec, makeDblDecResult, evlSqrt, NULL},
+ {"TAN", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTan},
+ {"TANH", 1, 1, DEFAULT, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh},
+ {"TOTALORDER", 2, 2, DEFAULT, setParamsDecFloat, makeShortResult, evlCompare, (void*) funTotalOrd},
+ {"TRUNC", 1, 2, DEFAULT, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL},
+ {"UNICODE_CHAR", 1, 1, DEFAULT, setParamsInteger, makeUnicodeChar, evlUnicodeChar, NULL},
+ {"UNICODE_VAL", 1, 1, DEFAULT, setParamsUnicodeVal, makeLongResult, evlUnicodeVal, NULL},
+ {"UUID_TO_CHAR", 1, 1, DEFAULT, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL},
+ {"", 0, 0, 0, NULL, NULL, NULL, NULL}
};
diff --git a/src/jrd/SysFunction.h b/src/jrd/SysFunction.h
index 5a2cbc671b9..d8a7a21b95f 100644
--- a/src/jrd/SysFunction.h
+++ b/src/jrd/SysFunction.h
@@ -46,6 +46,12 @@ namespace Jrd
class SysFunction
{
public:
+ enum Flags : UCHAR
+ {
+ DETERMINISTIC = 1,
+ CONSTANT = 2
+ };
+
typedef void (*SetParamsFunc)(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int, dsc**);
typedef void (*MakeFunc)(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc*, int, const dsc**);
typedef dsc* (*EvlFunc)(Jrd::thread_db*, const SysFunction* function,
@@ -54,7 +60,7 @@ class SysFunction
const char* name;
int minArgCount;
int maxArgCount; // -1 for no limit
- bool deterministic;
+ UCHAR flags;
SetParamsFunc setParamsFunc;
MakeFunc makeFunc;
EvlFunc evlFunc;
@@ -64,6 +70,16 @@ class SysFunction
void checkArgsMismatch(int count) const;
+ inline bool isDeterministic() const
+ {
+ return flags & DETERMINISTIC;
+ }
+
+ inline bool isConstant() const
+ {
+ return flags & CONSTANT;
+ }
+
private:
const static SysFunction functions[];
};
diff --git a/src/jrd/SystemPackages.h b/src/jrd/SystemPackages.h
index acb47b379b3..2cf1e403183 100644
--- a/src/jrd/SystemPackages.h
+++ b/src/jrd/SystemPackages.h
@@ -180,6 +180,33 @@ namespace Jrd
SystemFunctionReturnType returnType;
};
+ struct SystemConstant
+ {
+ SystemConstant(
+ Firebird::MemoryPool& pool,
+ const char* aName,
+ const USHORT aFieldId,
+ const char* aValueSource = nullptr,
+ const std::initializer_list aValueBlr = {}
+ )
+ : name(aName),
+ fieldId(aFieldId),
+ valueSource(aValueSource),
+ valueBlr(pool, aValueBlr)
+ { }
+
+ SystemConstant(Firebird::MemoryPool& pool, const SystemConstant& other)
+ : valueBlr(pool)
+ {
+ *this = other;
+ }
+
+ const char* name;
+ USHORT fieldId;
+ const char* valueSource = nullptr;
+ Firebird::Array valueBlr;
+ };
+
struct SystemPackage
{
SystemPackage(
@@ -187,18 +214,21 @@ namespace Jrd
const char* aName,
USHORT aOdsVersion,
std::initializer_list aProcedures,
- std::initializer_list aFunctions
+ std::initializer_list aFunctions,
+ std::initializer_list aConstants = {}
)
: name(aName),
odsVersion(aOdsVersion),
procedures(pool, aProcedures),
- functions(pool, aFunctions)
+ functions(pool, aFunctions),
+ constants(pool, aConstants)
{
}
SystemPackage(Firebird::MemoryPool& pool, const SystemPackage& other)
: procedures(pool),
- functions(pool)
+ functions(pool),
+ constants(pool)
{
*this = other;
}
@@ -207,6 +237,7 @@ namespace Jrd
USHORT odsVersion;
Firebird::ObjectsArray procedures;
Firebird::ObjectsArray functions;
+ Firebird::ObjectsArray constants;
static Firebird::ObjectsArray& get();
diff --git a/src/jrd/SystemTriggers.epp b/src/jrd/SystemTriggers.epp
index e20773be845..ddc6fbed78a 100644
--- a/src/jrd/SystemTriggers.epp
+++ b/src/jrd/SystemTriggers.epp
@@ -1602,19 +1602,26 @@ void afterInsertRelation(thread_db* tdbb, Record* record)
}
-void afterInsertProcedure(thread_db* tdbb, Record* record)
+template
+void populateCache(thread_db* tdbb, Record* record)
{
- if (tdbb->getAttachment()->isRWGbak())
- {
- dsc desc;
- bool idSet = EVL_field(nullptr, record, f_prc_id, &desc);
- fb_assert(idSet);
- if (!idSet)
- return;
+ if (!tdbb->getAttachment()->isRWGbak())
+ return;
- MetaId id = MetaId(MOV_get_long(tdbb, &desc, 0));
- MetadataCache::newVersion(tdbb, id);
- }
+ dsc desc;
+ const bool idSet = EVL_field(nullptr, record, Field, &desc);
+ fb_assert(idSet);
+ if (!idSet)
+ return;
+
+ const IdType id = IdType(MOV_get_long(tdbb, &desc, 0));
+ MetadataCache::newVersion(tdbb, id);
+}
+
+
+void afterInsertProcedure(thread_db* tdbb, Record* record)
+{
+ populateCache(tdbb, record);
}
void afterUpdateProcedure(thread_db* tdbb, Record* orgRecord, Record* newRecord)
@@ -1624,17 +1631,7 @@ void afterUpdateProcedure(thread_db* tdbb, Record* orgRecord, Record* newRecord)
void afterInsertFunction(thread_db* tdbb, Record* record)
{
- if (tdbb->getAttachment()->isRWGbak())
- {
- dsc desc;
- bool idSet = EVL_field(nullptr, record, f_fun_id, &desc);
- fb_assert(idSet);
- if (!idSet)
- return;
-
- MetaId id = MetaId(MOV_get_long(tdbb, &desc, 0));
- MetadataCache::newVersion(tdbb, id);
- }
+ populateCache(tdbb, record);
}
void afterUpdateFunction(thread_db* tdbb, Record* orgRecord, Record* newRecord)
@@ -1705,17 +1702,7 @@ void beforeInsertCollation(thread_db* tdbb, Record* record, jrd_rel* relation)
void afterInsertCollation(thread_db* tdbb, Record* record)
{
- if (tdbb->getAttachment()->isRWGbak())
- {
- dsc desc;
- bool idSet = EVL_field(nullptr, record, f_coll_cs_id, &desc);
- fb_assert(idSet);
- if (!idSet)
- return;
-
- CSetId csId = CSetId(MOV_get_long(tdbb, &desc, 0));
- MetadataCache::newVersion