diff --git a/doc/sql.extensions/README.declared_local_temporary_tables.md b/doc/sql.extensions/README.declared_local_temporary_tables.md new file mode 100644 index 00000000000..ac19876c3a8 --- /dev/null +++ b/doc/sql.extensions/README.declared_local_temporary_tables.md @@ -0,0 +1,176 @@ +# Declared Local Temporary Tables in Packages (FB 6.0) + +Firebird 6.0 supports declaring local temporary tables in SQL packages. + +These tables are declared in package metadata. Internally, they are stored as package-owned persistent temporary table +metadata, identified through `RDB$PACKAGE_NAME`. Their data remains temporary: transaction-local for +`ON COMMIT DELETE ROWS` and connection-local for `ON COMMIT PRESERVE ROWS`. + +## Syntax + +Declared local temporary tables can be used in package declarations. + +```sql +CREATE PACKAGE +AS +BEGIN + [{ ;}...] +END + + ::= + | + | + + + ::= + DECLARE LOCAL TEMPORARY TABLE + ( + [, ...] + ) + [ON COMMIT {DELETE | PRESERVE} ROWS] + [{[UNIQUE] [ASC | DESC] INDEX ()}...] +``` + +They can also appear in the declaration section of `CREATE PACKAGE BODY`. + +```sql +CREATE PACKAGE BODY +AS +BEGIN + [{ ;}...] +END + + ::= + | + | + +``` + +## Semantics + +- `ON COMMIT DELETE ROWS` (default): rows are transaction-local and cleared at transaction end. +- `ON COMMIT PRESERVE ROWS`: rows are connection-local and preserved across transactions in the same attachment. + +The table definition is part of package metadata and is persistent like other package members; table data remains +temporary. + +## Visibility and Name Resolution + +Visibility depends on where the table is declared: + +- Tables declared in `CREATE PACKAGE` (header) are public package members. +- Tables declared in `CREATE PACKAGE BODY` are private to that package body. +- Unqualified references to a matching declared table name inside package routines resolve to the package table. + +External SQL access rules: + +- Header tables can be accessed externally as `[{schema_name}.package_name.table_name` (for example, `pkg.t_pub`). +- Body tables cannot be accessed externally and are only valid inside routines of the same package body. + +Index DDL rules: + +- Packaged declared local temporary table indexes must be declared inline in `DECLARE LOCAL TEMPORARY TABLE`. +- Standalone index DDL commands are not allowed for packaged tables: + `CREATE INDEX`, `ALTER INDEX`, `DROP INDEX`, `SET STATISTICS INDEX`. + +## Name Isolation + +Declared local temporary table names are isolated by package context. + +- Different packages may declare tables with the same name. +- A package table name may also match a regular table name in the same schema. + +Resolution inside package routines prefers the package-local declaration. + +## Dependencies and DDL lifecycle + +Declared local temporary tables participate in package dependency handling. + +- Package routines depending on declared table columns are tracked as package dependencies. +- `DROP PACKAGE` removes package body/header dependencies and package members (routines and declared tables) in package + scope. +- `ALTER PACKAGE` and `CREATE OR ALTER PACKAGE` recreate packaged declared local temporary tables that belong to the + package header. +- `RECREATE PACKAGE BODY` and `CREATE OR ALTER PACKAGE BODY` recreate packaged declared local temporary tables that + belong to the package body. + +## System metadata changes + +Packaged declared local temporary tables add package ownership and visibility information to system metadata. Tools +that inspect metadata should use these columns when present. + +| Table | Column | Meaning | +|----------------------|--------------------|-----------------------------------------------------------------| +| `RDB$RELATIONS` | `RDB$PACKAGE_NAME` | Owning package of the declared temporary table | +| `RDB$RELATIONS` | `RDB$PRIVATE_FLAG` | `PUBLIC` (`0`) for header tables, `PRIVATE` (`1`) for body tables | +| `RDB$RELATION_FIELDS`| `RDB$PACKAGE_NAME` | Owning package of the declared temporary table columns | +| `RDB$INDICES` | `RDB$PACKAGE_NAME` | Owning package of inline indexes declared for packaged tables | +| `RDB$INDEX_SEGMENTS` | `RDB$PACKAGE_NAME` | Owning package of the packaged table index segments | +| `MON$TABLE_STATS` | `MON$PACKAGE_NAME` | Owning package reported in runtime table statistics | + +## Example + +```sql +set term !; + +recreate package pkg +as +begin + declare local temporary table t_pub( + id integer + ) on commit preserve rows + index idx_t_pub_id (id); + + procedure p1(n integer); + procedure p2 returns (n integer); +end! + +create package body pkg +as +begin + declare local temporary table t_priv( + id integer + ) on commit preserve rows + unique index uq_t_priv_id (id); + + procedure p1(n integer) + as + begin + insert into t_pub(id) values (:n); + insert into t_priv(id) values (:n); + end + + procedure p2 returns (n integer) + as + begin + for select id from t_pub into :n do + suspend; + end +end! + +set term ;! + +-- use package routines +execute procedure pkg.p1(10); +select * from pkg.p2; + +-- header-declared table: allowed +select * from pkg.t_pub; + +-- body-declared table: not allowed +-- select * from pkg.t_priv; + +-- not allowed for packaged tables: +-- create index idx_cmd on pkg.t_pub(id); +-- alter index idx_t_pub_id active; +-- drop index idx_t_pub_id; +``` + +## Notes + +- This feature is distinct from SQL-created local temporary tables (`CREATE LOCAL TEMPORARY TABLE ...`), which are + attachment-private DDL objects. +- Packaged declared temporary tables are not attachment-private created LTTs. They use persistent temporary-table + metadata associated with the package through `RDB$PACKAGE_NAME`. +- Declared package temporary tables are package DDL items and follow package compilation, visibility, and dependency + rules. diff --git a/src/auth/SecurityDatabase/LegacyServer.cpp b/src/auth/SecurityDatabase/LegacyServer.cpp index 66f1340c7d9..6771b06481b 100644 --- a/src/auth/SecurityDatabase/LegacyServer.cpp +++ b/src/auth/SecurityDatabase/LegacyServer.cpp @@ -66,6 +66,7 @@ const UCHAR PWD_REQUEST[] = blr_rse, 1, blr_relation3, 14, 'P', 'L', 'G', '$', 'L', 'E', 'G', 'A', 'C', 'Y', '_', 'S', 'E', 'C', // PLG_LEGACY_SEC_SCHEMA + 0, 9, 'P', 'L', 'G', '$', 'U', 'S', 'E', 'R', 'S', 0, 0, diff --git a/src/burp/backup.epp b/src/burp/backup.epp index 4ec7cd6d1f2..af13dc96a4b 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -424,6 +424,9 @@ int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name) if (relation->rel_name.schema.hasData()) put_text(att_relation_schema_name, relation->rel_name.schema); + if (relation->rel_name.package.hasData()) + put_text(att_relation_package_name, relation->rel_name.package); + put_text(att_relation_name, relation->rel_name.object); put(tdgbl, att_end); @@ -673,6 +676,7 @@ burp_fld* get_fields( burp_rel* relation) X.RDB$FIELD_SOURCE_SCHEMA_NAME EQUIV Y.RDB$SCHEMA_NAME AND X.RDB$FIELD_SOURCE = Y.RDB$FIELD_NAME AND X.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND X.RDB$RELATION_NAME EQ relation->rel_name.object.c_str() { field = (burp_fld*) BURP_alloc_zero(sizeof(burp_fld)); @@ -1659,6 +1663,7 @@ void put_index( burp_rel* relation) FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle1) X IN RDB$INDICES WITH X.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND X.RDB$RELATION_NAME EQ relation->rel_name.object.c_str() AND X.RDB$INDEX_NAME NOT STARTING TEMP_DEPEND { @@ -1668,8 +1673,10 @@ void put_index( burp_rel* relation) RFR IN RDB$RELATION_FIELDS WITH I_S.RDB$FIELD_NAME = RFR.RDB$FIELD_NAME AND I_S.RDB$SCHEMA_NAME EQUIV RFR.RDB$SCHEMA_NAME AND + I_S.RDB$PACKAGE_NAME EQUIV RFR.RDB$PACKAGE_NAME AND I_S.RDB$INDEX_NAME = X.RDB$INDEX_NAME AND RFR.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND + RFR.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND RFR.RDB$RELATION_NAME = relation->rel_name.object.c_str() { count++; @@ -1687,7 +1694,8 @@ void put_index( burp_rel* relation) put(tdgbl, rec_index); PUT_TEXT(att_index_name, X.RDB$INDEX_NAME); - BURP_verbose(151, QualifiedMetaString(X.RDB$INDEX_NAME, relation->rel_name.schema).toQuotedString()); + BURP_verbose(151, QualifiedMetaString( + X.RDB$INDEX_NAME, relation->rel_name.schema, relation->rel_name.package).toQuotedString()); // msg 151 writing index %s put_int32 (att_segment_count, X.RDB$SEGMENT_COUNT); @@ -1697,6 +1705,7 @@ void put_index( burp_rel* relation) FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle5) Y IN RDB$INDEX_SEGMENTS WITH Y.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND + Y.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND Y.RDB$INDEX_NAME EQ X.RDB$INDEX_NAME SORTED BY Y.RDB$FIELD_POSITION { @@ -4049,6 +4058,7 @@ void write_rel_constraints() X IN RDB$RELATION_CONSTRAINTS CROSS REL IN RDB$RELATIONS WITH REL.RDB$SCHEMA_NAME EQUIV X.RDB$SCHEMA_NAME AND + REL.RDB$PACKAGE_NAME MISSING AND REL.RDB$RELATION_NAME EQ X.RDB$RELATION_NAME AND (REL.RDB$SYSTEM_FLAG MISSING OR REL.RDB$SYSTEM_FLAG NE 1) { @@ -4124,6 +4134,15 @@ void write_relations() name.schema = X.RDB$SCHEMA_NAME; } + if (!X.RDB$PACKAGE_NAME.NULL) + { + PUT_TEXT(att_relation_package_name, X.RDB$PACKAGE_NAME); + name.package = X.RDB$PACKAGE_NAME; + } + + if (!X.RDB$PRIVATE_FLAG.NULL) + put_int32(att_relation_private_flag, X.RDB$PRIVATE_FLAG); + PUT_TEXT(att_relation_name, X.RDB$RELATION_NAME); name.object = X.RDB$RELATION_NAME; diff --git a/src/burp/burp.h b/src/burp/burp.h index 3fb58a76b9e..55cf5aec258 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -212,6 +212,13 @@ Version 11: FB4.0. Version 12: FB6.0. Schemas. + + Packaged tables: + RDB$RELATIONS.RDB$PACKAGE_NAME, + RDB$RELATIONS.RDB$PRIVATE_FLAG, + RDB$RELATION_FIELDSS.RDB$PACKAGE_NAME, + RDB$INDICES.RDB$PACKAGE_NAME and + RDB$INDEX_SEGMENTS.RDB$PACKAGE_NAME. */ inline constexpr int ATT_BACKUP_FORMAT = 12; @@ -291,6 +298,8 @@ enum att_type { att_relation_sql_security_deprecated, // can be removed later att_relation_sql_security, att_relation_schema_name, + att_relation_package_name, + att_relation_private_flag, // Field attributes (used for both global and local fields) diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 5d5a31765fb..6163cb698c8 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -490,6 +490,7 @@ int RESTORE_restore (const TEXT* file_name, const TEXT* database_name) CROSS IDS IN RDB$INDICES WITH CNST.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND CNST.RDB$SCHEMA_NAME EQUIV IDS.RDB$SCHEMA_NAME AND + IDS.RDB$PACKAGE_NAME MISSING AND CNST.RDB$INDEX_NAME EQ IDS.RDB$INDEX_NAME AND IDS.RDB$INDEX_INACTIVE EQ DEFERRED_ACTIVE { @@ -3862,6 +3863,7 @@ burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation, USHORT id) { strcpy(X.RDB$RELATION_NAME, relation->rel_name.object.c_str()); X.RDB$SCHEMA_NAME.NULL = TRUE; + X.RDB$PACKAGE_NAME.NULL = TRUE; X.RDB$FIELD_POSITION = 0; X.RDB$VIEW_CONTEXT.NULL = TRUE; X.RDB$BASE_FIELD.NULL = TRUE; @@ -3894,6 +3896,12 @@ burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation, USHORT id) X.RDB$SCHEMA_NAME.NULL = FALSE; } + if (relation->rel_name.package.hasData()) + { + strcpy(X.RDB$PACKAGE_NAME, relation->rel_name.package.c_str()); + X.RDB$PACKAGE_NAME.NULL = FALSE; + } + skip_init(&scan_next_attr); while (get_attribute(&attribute, tdgbl) != att_end) { @@ -6746,6 +6754,7 @@ bool get_index(BurpGlobals* tdgbl, const burp_rel* relation) X.RDB$INDEX_INACTIVE = TRUE; X.RDB$SCHEMA_NAME.NULL = TRUE; + X.RDB$PACKAGE_NAME.NULL = TRUE; X.RDB$INDEX_TYPE.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$FOREIGN_KEY_SCHEMA_NAME.NULL = TRUE; @@ -6764,6 +6773,12 @@ bool get_index(BurpGlobals* tdgbl, const burp_rel* relation) X.RDB$SCHEMA_NAME.NULL = FALSE; } + if (relation->rel_name.package.hasData()) + { + strcpy(X.RDB$PACKAGE_NAME, relation->rel_name.package.c_str()); + X.RDB$PACKAGE_NAME.NULL = FALSE; + } + skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { @@ -6772,7 +6787,8 @@ bool get_index(BurpGlobals* tdgbl, const burp_rel* relation) case att_index_name: GET_TEXT(X.RDB$INDEX_NAME); strcpy(index_name, X.RDB$INDEX_NAME); - BURP_verbose(122, QualifiedMetaString(X.RDB$INDEX_NAME, relation->rel_name.schema).toQuotedString()); + BURP_verbose(122, QualifiedMetaString( + X.RDB$INDEX_NAME, relation->rel_name.schema, relation->rel_name.package).toQuotedString()); break; case att_segment_count: @@ -6817,6 +6833,10 @@ bool get_index(BurpGlobals* tdgbl, const burp_rel* relation) Y.RDB$SCHEMA_NAME.NULL = X.RDB$SCHEMA_NAME.NULL; if (!X.RDB$SCHEMA_NAME.NULL) strcpy(Y.RDB$SCHEMA_NAME, X.RDB$SCHEMA_NAME); + + Y.RDB$PACKAGE_NAME.NULL = X.RDB$PACKAGE_NAME.NULL; + if (!X.RDB$PACKAGE_NAME.NULL) + strcpy(Y.RDB$PACKAGE_NAME, X.RDB$PACKAGE_NAME); } END_STORE ON_ERROR @@ -6898,8 +6918,10 @@ bool get_index(BurpGlobals* tdgbl, const burp_rel* relation) RFR IN RDB$RELATION_FIELDS CROSS IDS IN RDB$INDEX_SEGMENTS WITH RFR.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND + RFR.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND RFR.RDB$RELATION_NAME = relation->rel_name.object.c_str() AND IDS.RDB$SCHEMA_NAME EQUIV RFR.RDB$SCHEMA_NAME AND + IDS.RDB$PACKAGE_NAME EQUIV RFR.RDB$PACKAGE_NAME AND IDS.RDB$FIELD_NAME = RFR.RDB$FIELD_NAME AND IDS.RDB$INDEX_NAME = index_name { @@ -6915,6 +6937,7 @@ bool get_index(BurpGlobals* tdgbl, const burp_rel* relation) FOR (REQUEST_HANDLE tdgbl->handles_get_index_req_handle4) IDS IN RDB$INDEX_SEGMENTS WITH IDS.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND + IDS.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND IDS.RDB$INDEX_NAME = index_name { ERASE IDS; @@ -8051,6 +8074,8 @@ bool get_relation(BurpGlobals* tdgbl, Coordinator* coord, RestoreRelationTask* t ext_desc_null = true; FB_BOOLEAN sql_security = FB_FALSE; bool sql_security_null = true; + USHORT private_flag = 0; + bool private_flag_null = true; BASED_ON RDB$RELATIONS.RDB$SECURITY_CLASS sec_class; sec_class[0] = '\0'; @@ -8099,6 +8124,11 @@ bool get_relation(BurpGlobals* tdgbl, Coordinator* coord, RestoreRelationTask* t relation->rel_name.schema = temp; break; + case att_relation_package_name: + GET_TEXT(temp); + relation->rel_name.package = temp; + break; + case att_relation_name: { GET_TEXT(temp); @@ -8182,6 +8212,11 @@ bool get_relation(BurpGlobals* tdgbl, Coordinator* coord, RestoreRelationTask* t sql_security = get_boolean(tdgbl, attribute == att_relation_sql_security_deprecated); break; + case att_relation_private_flag: + private_flag_null = false; + private_flag = (USHORT) get_int32(tdgbl); + break; + default: bad_attribute(scan_next_attr, attribute, 111); // msg 111 table @@ -8214,6 +8249,8 @@ bool get_relation(BurpGlobals* tdgbl, Coordinator* coord, RestoreRelationTask* t X.RDB$EXTERNAL_FILE.NULL = ext_file_name_null; X.RDB$RELATION_TYPE.NULL = FALSE; X.RDB$SQL_SECURITY.NULL = sql_security_null; + X.RDB$PACKAGE_NAME.NULL = TRUE; + X.RDB$PRIVATE_FLAG.NULL = private_flag_null; X.RDB$SYSTEM_FLAG = (USHORT) sys_flag; X.RDB$FLAGS = (USHORT) rel_flags; @@ -8228,12 +8265,19 @@ bool get_relation(BurpGlobals* tdgbl, Coordinator* coord, RestoreRelationTask* t X.RDB$SCHEMA_NAME.NULL = FALSE; } + if (relation->rel_name.package.hasData()) + { + strcpy(X.RDB$PACKAGE_NAME, relation->rel_name.package.c_str()); + X.RDB$PACKAGE_NAME.NULL = FALSE; + } + strcpy(X.RDB$SECURITY_CLASS, sec_class); strcpy(X.RDB$RELATION_NAME, relation->rel_name.object.c_str()); strcpy(X.RDB$EXTERNAL_FILE, ext_file_name); X.RDB$RELATION_TYPE = (USHORT) type; X.RDB$SQL_SECURITY = (FB_BOOLEAN) sql_security; + X.RDB$PRIVATE_FLAG = private_flag; } END_STORE ON_ERROR @@ -8468,6 +8512,11 @@ bool get_relation_data(BurpGlobals* tdgbl, Coordinator* coord, RestoreRelationTa name.schema = temp; break; + case att_relation_package_name: + GET_TEXT(temp); + name.package = temp; + break; + case att_relation_name: GET_TEXT(temp); name.object = temp; @@ -11585,6 +11634,10 @@ USHORT get_view_base_relation_count(BurpGlobals* tdgbl, Firebird::IRequest* req_handle1 = nullptr; USHORT result = 0; + fb_assert(current_view_name.package.isEmpty()); + + if (current_view_name.package.hasData()) + return result; FOR (REQUEST_HANDLE req_handle1) V IN RDB$VIEW_RELATIONS @@ -11593,6 +11646,7 @@ USHORT get_view_base_relation_count(BurpGlobals* tdgbl, V.RDB$SCHEMA_NAME EQUIV NULLIF(current_view_name.schema.c_str(), '') AND V.RDB$VIEW_NAME EQ current_view_name.object.c_str() AND R.RDB$SCHEMA_NAME EQUIV V.RDB$RELATION_SCHEMA_NAME AND + R.RDB$PACKAGE_NAME EQUIV V.RDB$PACKAGE_NAME AND R.RDB$RELATION_NAME EQ V.RDB$RELATION_NAME { if (R.RDB$VIEW_BLR.NULL) @@ -11602,6 +11656,8 @@ USHORT get_view_base_relation_count(BurpGlobals* tdgbl, } else { + fb_assert(V.RDB$PACKAGE_NAME.NULL); + // Call recursive for VIEWS that are referenced in VIEWS result += get_view_base_relation_count(tdgbl, QualifiedMetaString(V.RDB$RELATION_NAME, V.RDB$RELATION_SCHEMA_NAME), @@ -12001,6 +12057,7 @@ void update_ownership(BurpGlobals* tdgbl) FOR (REQUEST_HANDLE req_handle2) X IN RDB$RELATIONS WITH X.RDB$SCHEMA_NAME EQUIV NULLIF(relation->rel_name.schema.c_str(), '') AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND X.RDB$RELATION_NAME EQ relation->rel_name.object.c_str() { MODIFY X @@ -13674,6 +13731,7 @@ void WriteRelationMeta::prepareRequest(BurpGlobals* tdgbl) { add_byte(blr, blr_relation3); add_string(blr, m_relation->rel_name.schema.c_str()); + add_string(blr, m_relation->rel_name.package.c_str()); add_string(blr, m_relation->rel_name.object.c_str()); add_string(blr, ""); } diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index d0189ad4294..734172e63b8 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -238,8 +238,10 @@ static void checkForeignKeyTempScope(thread_db* tdbb, jrd_tra* transaction, RLC_M.RDB$SCHEMA_NAME EQ masterIndexName.schema.c_str() AND RLC_M.RDB$INDEX_NAME EQ masterIndexName.object.c_str() AND REL_C.RDB$SCHEMA_NAME EQ childRelName.schema.c_str() AND + REL_C.RDB$PACKAGE_NAME MISSING AND REL_C.RDB$RELATION_NAME EQ childRelName.object.c_str() AND REL_M.RDB$SCHEMA_NAME EQ RLC_M.RDB$SCHEMA_NAME AND + REL_M.RDB$PACKAGE_NAME MISSING AND REL_M.RDB$RELATION_NAME EQ RLC_M.RDB$RELATION_NAME { fb_assert(masterRelName.object.isEmpty()); @@ -308,10 +310,13 @@ static void checkRelationTempScope(thread_db* tdbb, jrd_tra* transaction, RLC_C.RDB$SCHEMA_NAME EQ childRelName.schema.c_str() AND RLC_C.RDB$RELATION_NAME EQ childRelName.object.c_str() AND IND_C.RDB$SCHEMA_NAME EQ RLC_C.RDB$SCHEMA_NAME AND + IND_C.RDB$PACKAGE_NAME MISSING AND IND_C.RDB$INDEX_NAME EQ RLC_C.RDB$INDEX_NAME AND IND_M.RDB$SCHEMA_NAME EQ IND_C.RDB$FOREIGN_KEY_SCHEMA_NAME AND + IND_M.RDB$PACKAGE_NAME MISSING AND IND_M.RDB$INDEX_NAME EQ IND_C.RDB$FOREIGN_KEY AND IND_M.RDB$SCHEMA_NAME EQ REL_M.RDB$SCHEMA_NAME AND + REL_M.RDB$PACKAGE_NAME MISSING AND IND_M.RDB$RELATION_NAME EQ REL_M.RDB$RELATION_NAME { fb_assert(masterRelName.object.isEmpty()); @@ -367,15 +372,18 @@ static void checkViewDependency(thread_db* tdbb, jrd_tra* transaction, Y IN RDB$RELATION_FIELDS CROSS Z IN RDB$VIEW_RELATIONS WITH X.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(relationName.package.c_str(), '') AND X.RDB$RELATION_NAME EQ relationName.object.c_str() AND X.RDB$FIELD_NAME EQ fieldName.c_str() AND X.RDB$FIELD_NAME EQ Y.RDB$BASE_FIELD AND X.RDB$FIELD_SOURCE_SCHEMA_NAME EQ Y.RDB$FIELD_SOURCE_SCHEMA_NAME AND X.RDB$FIELD_SOURCE EQ Y.RDB$FIELD_SOURCE AND Y.RDB$SCHEMA_NAME EQ Z.RDB$SCHEMA_NAME AND + Y.RDB$PACKAGE_NAME MISSING AND Y.RDB$RELATION_NAME EQ Z.RDB$VIEW_NAME AND X.RDB$SCHEMA_NAME EQ Z.RDB$RELATION_SCHEMA_NAME AND X.RDB$RELATION_NAME EQ Z.RDB$RELATION_NAME AND + X.RDB$PACKAGE_NAME EQUIV Z.RDB$PACKAGE_NAME AND Y.RDB$VIEW_CONTEXT EQ Z.RDB$VIEW_CONTEXT { const QualifiedName viewName(Z.RDB$VIEW_NAME, Z.RDB$SCHEMA_NAME); @@ -583,6 +591,7 @@ static bool fieldExists(thread_db* tdbb, jrd_tra* transaction, const QualifiedNa FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) FLD IN RDB$RELATION_FIELDS WITH FLD.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND + FLD.RDB$PACKAGE_NAME EQUIV NULLIF(relationName.package.c_str(), '') AND FLD.RDB$RELATION_NAME EQ relationName.object.c_str() AND FLD.RDB$FIELD_NAME EQ fieldName.c_str() { @@ -627,17 +636,20 @@ static QualifiedName getIndexRelationName(thread_db* tdbb, jrd_tra* transaction, // Check LTT indexes first const auto attachment = transaction->getAttachment(); - for (const auto& lttEntry : attachment->att_local_temporary_tables) + if (indexName.package.isEmpty()) { - const auto ltt = lttEntry.second; + for (const auto& lttEntry : attachment->att_local_temporary_tables) + { + const auto ltt = lttEntry.second; - if (ltt->name.schema != indexName.schema) - continue; + if (ltt->name.schema != indexName.schema) + continue; - for (const auto& index : ltt->indexes) - { - if (index.name == indexName) - return ltt->name; + for (const auto& index : ltt->indexes) + { + if (index.name == indexName) + return ltt->name; + } } } @@ -646,10 +658,11 @@ static QualifiedName getIndexRelationName(thread_db* tdbb, jrd_tra* transaction, FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(indexName.package.c_str(), '') AND IDX.RDB$INDEX_NAME EQ indexName.object.c_str() { systemIndex = IDX.RDB$SYSTEM_FLAG == 1; - return QualifiedName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME); + return QualifiedName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME, IDX.RDB$PACKAGE_NAME); } END_FOR @@ -767,6 +780,7 @@ static void modifyLocalFieldPosition(thread_db* tdbb, jrd_tra* transaction, FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) FLD IN RDB$RELATION_FIELDS WITH FLD.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND + FLD.RDB$PACKAGE_NAME EQUIV NULLIF(relationName.package.c_str(), '') AND FLD.RDB$RELATION_NAME EQ relationName.object.c_str() SORTED BY ASCENDING FLD.RDB$FIELD_POSITION { @@ -813,6 +827,7 @@ static void modifyLocalFieldPosition(thread_db* tdbb, jrd_tra* transaction, FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) FLD IN RDB$RELATION_FIELDS WITH FLD.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND + FLD.RDB$PACKAGE_NAME EQUIV NULLIF(relationName.package.c_str(), '') AND FLD.RDB$RELATION_NAME EQ relationName.object.c_str() AND FLD.RDB$FIELD_POSITION >= MIN(newPosition, existingPosition) AND FLD.RDB$FIELD_POSITION <= MAX(newPosition, existingPosition) @@ -1503,6 +1518,16 @@ DdlNode* CommentOnNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) fb_assert(name.schema.isEmpty()); break; + case obj_relation: + { + // Allow relation resolution to reach execute(), which rejects created LTTs with + // the COMMENT ON-specific error instead of the generic persistent-metadata one. + AutoSetRestoreFlag autoLttReferences(&dsqlScratch->flags, + DsqlCompilerScratch::FLAG_ALLOW_CREATED_LTT_REFERENCE, true); + dsqlScratch->resolveRoutineOrRelation(name, {objType}); + break; + } + case obj_procedure: case obj_udf: dsqlScratch->resolveRoutineOrRelation(name, {objType}); @@ -1798,7 +1823,7 @@ void CommentOnNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, j sql << "and" << subColumnClause << "=" << subName; } - if (objType == obj_procedure || objType == obj_udf) + if (objType == obj_relation || objType == obj_procedure || objType == obj_udf) sql << "and rdb$package_name is not distinct from nullif(" << name.package << ", '')"; if (addWhereClause) @@ -1831,7 +1856,7 @@ string CreateAlterFunctionNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, compiled); NODE_PRINT(printer, invalid); NODE_PRINT(printer, packageOwner); - NODE_PRINT(printer, privateScope); + NODE_PRINT(printer, packagePrivate); NODE_PRINT(printer, udfReturnPos); return "CreateAlterFunctionNode"; @@ -2013,7 +2038,7 @@ bool CreateAlterFunctionNode::executeCreate(thread_db* tdbb, DsqlCompilerScratch strcpy(FUN.RDB$PACKAGE_NAME, name.package.c_str()); FUN.RDB$PRIVATE_FLAG.NULL = FALSE; - FUN.RDB$PRIVATE_FLAG = privateScope; + FUN.RDB$PRIVATE_FLAG = packagePrivate; FUN.RDB$OWNER_NAME.NULL = FALSE; strcpy(FUN.RDB$OWNER_NAME, packageOwner.c_str()); @@ -2241,7 +2266,7 @@ bool CreateAlterFunctionNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* if (name.package.hasData()) { FUN.RDB$PRIVATE_FLAG.NULL = FALSE; - FUN.RDB$PRIVATE_FLAG = privateScope; + FUN.RDB$PRIVATE_FLAG = packagePrivate; } else FUN.RDB$PRIVATE_FLAG.NULL = TRUE; @@ -2904,7 +2929,7 @@ string CreateAlterProcedureNode::internalPrint(NodePrinter& printer) const NODE_PRINT(printer, compiled); NODE_PRINT(printer, invalid); NODE_PRINT(printer, packageOwner); - NODE_PRINT(printer, privateScope); + NODE_PRINT(printer, packagePrivate); return "CreateAlterProcedureNode"; } @@ -3086,7 +3111,7 @@ bool CreateAlterProcedureNode::executeCreate(thread_db* tdbb, DsqlCompilerScratc strcpy(P.RDB$PACKAGE_NAME, name.package.c_str()); P.RDB$PRIVATE_FLAG.NULL = FALSE; - P.RDB$PRIVATE_FLAG = privateScope; + P.RDB$PRIVATE_FLAG = packagePrivate; strcpy(P.RDB$OWNER_NAME, packageOwner.c_str()); } @@ -3182,7 +3207,7 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch if (name.package.hasData()) { P.RDB$PRIVATE_FLAG.NULL = FALSE; - P.RDB$PRIVATE_FLAG = privateScope; + P.RDB$PRIVATE_FLAG = packagePrivate; } else P.RDB$PRIVATE_FLAG.NULL = TRUE; @@ -3291,6 +3316,7 @@ bool CreateAlterProcedureNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch VRL.RDB$PACKAGE_NAME EQUIV PRM.RDB$PACKAGE_NAME AND VRL.RDB$CONTEXT_TYPE EQ VCT_PROCEDURE AND RFR.RDB$SCHEMA_NAME EQ VRL.RDB$SCHEMA_NAME AND + RFR.RDB$PACKAGE_NAME MISSING AND RFR.RDB$RELATION_NAME EQ VRL.RDB$VIEW_NAME AND RFR.RDB$VIEW_CONTEXT EQ VRL.RDB$VIEW_CONTEXT AND RFR.RDB$BASE_FIELD = PRM.RDB$PARAMETER_NAME @@ -4290,6 +4316,8 @@ void DropTriggerNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, requestHandle.reset(tdbb, drq_e_trg_prv3, DYN_REQUESTS); + fb_assert(relationName.package.isEmpty()); + FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) FIRST 1 V IN RDB$VIEW_RELATIONS CROSS F IN RDB$RELATION_FIELDS @@ -4298,6 +4326,7 @@ void DropTriggerNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, V.RDB$VIEW_NAME EQ relationName.object.c_str() AND F.RDB$SCHEMA_NAME EQ V.RDB$SCHEMA_NAME AND F.RDB$RELATION_NAME EQ V.RDB$VIEW_NAME AND + F.RDB$PACKAGE_NAME MISSING AND T.RDB$SCHEMA_NAME EQ F.RDB$SCHEMA_NAME AND T.RDB$RELATION_NAME EQ F.RDB$RELATION_NAME { @@ -4312,7 +4341,8 @@ void DropTriggerNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, FOR (REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) F IN RDB$RELATION_FIELDS WITH F.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND - F.RDB$RELATION_NAME EQ relationName.object.c_str() + F.RDB$RELATION_NAME EQ relationName.object.c_str() AND + F.RDB$PACKAGE_NAME MISSING { MODIFY F USING F.RDB$UPDATE_FLAG = FALSE; @@ -4638,7 +4668,7 @@ void DropCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc status_exception::raise( Arg::Gds(isc_dyn_coll_used_table) << name.toQuotedString() << - QualifiedName(RF.RDB$RELATION_NAME, RF.RDB$SCHEMA_NAME).toQuotedString() << + QualifiedName(RF.RDB$RELATION_NAME, RF.RDB$SCHEMA_NAME, RF.RDB$PACKAGE_NAME).toQuotedString() << RF.RDB$FIELD_NAME); } END_FOR @@ -5362,8 +5392,10 @@ void AlterDomainNode::modifyLocalFieldIndex(thread_db* tdbb, jrd_tra* transactio IDX IN RDB$INDICES CROSS SEG IN RDB$INDEX_SEGMENTS WITH SEG.RDB$SCHEMA_NAME EQ IDX.RDB$SCHEMA_NAME AND + SEG.RDB$PACKAGE_NAME EQUIV IDX.RDB$PACKAGE_NAME AND SEG.RDB$INDEX_NAME EQ IDX.RDB$INDEX_NAME AND IDX.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(relationName.package.c_str(), '') AND IDX.RDB$RELATION_NAME EQ relationName.object.c_str() AND SEG.RDB$FIELD_NAME EQ fieldName.c_str() { @@ -5684,8 +5716,10 @@ void AlterDomainNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, RFL.RDB$FIELD_SOURCE EQ name.object.c_str() AND (RFL.RDB$NULL_FLAG MISSING OR RFL.RDB$NULL_FLAG EQ 0) AND IND.RDB$SCHEMA_NAME EQ RFL.RDB$SCHEMA_NAME AND + IND.RDB$PACKAGE_NAME EQUIV RFL.RDB$PACKAGE_NAME AND IND.RDB$RELATION_NAME EQ RFL.RDB$RELATION_NAME AND INDSEG.RDB$SCHEMA_NAME EQ IND.RDB$SCHEMA_NAME AND + INDSEG.RDB$PACKAGE_NAME EQUIV IND.RDB$PACKAGE_NAME AND INDSEG.RDB$INDEX_NAME EQ IND.RDB$INDEX_NAME AND INDSEG.RDB$FIELD_NAME EQ RFL.RDB$FIELD_NAME AND RELCON.RDB$SCHEMA_NAME EQ INDSEG.RDB$SCHEMA_NAME AND @@ -5694,7 +5728,7 @@ void AlterDomainNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, { status_exception::raise( Arg::Gds(isc_domain_primary_key_notnull) << - QualifiedName(RFL.RDB$RELATION_NAME, RFL.RDB$SCHEMA_NAME).toQuotedString()); + QualifiedName(RFL.RDB$RELATION_NAME, RFL.RDB$SCHEMA_NAME, RFL.RDB$PACKAGE_NAME).toQuotedString()); } END_FOR } @@ -5783,7 +5817,7 @@ void AlterDomainNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, DOM.RDB$FIELD_SOURCE EQ name.object.c_str() { modifyLocalFieldIndex(tdbb, transaction, - QualifiedName(DOM.RDB$RELATION_NAME, DOM.RDB$SCHEMA_NAME), + QualifiedName(DOM.RDB$RELATION_NAME, DOM.RDB$SCHEMA_NAME, DOM.RDB$PACKAGE_NAME), DOM.RDB$FIELD_NAME, DOM.RDB$FIELD_NAME, indexList); } END_FOR @@ -5879,7 +5913,7 @@ void AlterDomainNode::rename(thread_db* tdbb, jrd_tra* transaction, SSHORT dimen END_MODIFY modifyLocalFieldIndex(tdbb, transaction, - QualifiedName(RFLD.RDB$RELATION_NAME, RFLD.RDB$SCHEMA_NAME), + QualifiedName(RFLD.RDB$RELATION_NAME, RFLD.RDB$SCHEMA_NAME, RFLD.RDB$PACKAGE_NAME), RFLD.RDB$FIELD_NAME, RFLD.RDB$FIELD_NAME, indexList); } END_FOR @@ -5976,7 +6010,7 @@ void DropDomainNode::check(thread_db* tdbb, jrd_tra* transaction) // msg 43: "Domain %s is used in table %s (local name %s) and can not be dropped" status_exception::raise( Arg::PrivateDyn(43) << name.toQuotedString() << - QualifiedName(Y.RDB$RELATION_NAME, Y.RDB$SCHEMA_NAME).toQuotedString() << + QualifiedName(Y.RDB$RELATION_NAME, Y.RDB$SCHEMA_NAME, Y.RDB$PACKAGE_NAME).toQuotedString() << MetaName(Y.RDB$FIELD_NAME).toQuotedString()); } END_FOR @@ -6702,37 +6736,6 @@ MetaId RelationNode::generateRelId(thread_db* tdbb, MetaName name) Arg::Gds(isc_imp_exc)); } -// Calculate the dbkey length to include each of the base relations. - -USHORT RelationNode::calcDbKeyLength(thread_db* tdbb) -{ - const USHORT MIN_RELATION_ID = USER_DEF_REL_INIT_ID; - Attachment* attachment = tdbb->getAttachment(); - - USHORT dbKeyLength = 0; - - static const CachedRequestId reqId; - AutoCacheRequest handle(tdbb, reqId); - - FOR(REQUEST_HANDLE handle) - Z IN RDB$VIEW_RELATIONS CROSS - R IN RDB$RELATIONS - WITH Z.RDB$RELATION_NAME EQ R.RDB$RELATION_NAME AND - Z.RDB$RELATION_SCHEMA_NAME EQ R.RDB$SCHEMA_NAME AND - Z.RDB$VIEW_NAME EQ name.object.c_str() AND - Z.RDB$SCHEMA_NAME EQ name.schema.c_str() AND - (Z.RDB$CONTEXT_TYPE = VCT_TABLE OR - Z.RDB$CONTEXT_TYPE = VCT_VIEW) - { - dbKeyLength += R.RDB$DBKEY_LENGTH; - } - END_FOR - - fb_assert(dbKeyLength); - - return dbKeyLength; -} - void RelationNode::FieldDefinition::modify(thread_db* tdbb, jrd_tra* transaction) { AutoRequest request; @@ -6740,6 +6743,7 @@ void RelationNode::FieldDefinition::modify(thread_db* tdbb, jrd_tra* transaction FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) RFR IN RDB$RELATION_FIELDS WITH RFR.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND + RFR.RDB$PACKAGE_NAME EQUIV NULLIF(relationName.package.c_str(), '') AND RFR.RDB$RELATION_NAME EQ relationName.object.c_str() AND RFR.RDB$FIELD_NAME EQ name.c_str() { @@ -6816,6 +6820,15 @@ void RelationNode::FieldDefinition::store(thread_db* tdbb, jrd_tra* transaction) { strcpy(RFR.RDB$FIELD_NAME, name.c_str()); strcpy(RFR.RDB$SCHEMA_NAME, relationName.schema.c_str()); + + if (relationName.package.hasData()) + { + RFR.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(RFR.RDB$PACKAGE_NAME, relationName.package.c_str()); + } + else + RFR.RDB$PACKAGE_NAME.NULL = TRUE; + strcpy(RFR.RDB$RELATION_NAME, relationName.object.c_str()); strcpy(RFR.RDB$FIELD_SOURCE_SCHEMA_NAME, fieldSource.schema.c_str()); strcpy(RFR.RDB$FIELD_SOURCE, fieldSource.object.c_str()); @@ -6956,6 +6969,20 @@ DdlNode* RelationNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) case Clause::TYPE_ALTER_PUBLICATION: break; + case Clause::TYPE_ADD_PACKAGE_LTT_INDEX: + { + auto addPackageLttIndexClause = static_cast(clause.getObject()); + + if (!addPackageLttIndexClause->indexNode->relation) + { + addPackageLttIndexClause->indexNode->relation = FB_NEW_POOL(dsqlScratch->getPool()) + RelationSourceNode(dsqlScratch->getPool(), name); + } + + addPackageLttIndexClause->indexNode->dsqlPass(dsqlScratch); + break; + } + default: fb_assert(false); break; @@ -7016,10 +7043,12 @@ bool RelationNode::deleteLocalField(thread_db* tdbb, jrd_tra* transaction, Z IN RDB$VIEW_RELATIONS WITH X.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND X.RDB$RELATION_NAME EQ relationName.object.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(relationName.package.c_str(), '') AND X.RDB$FIELD_NAME EQ fieldName.c_str() AND X.RDB$FIELD_NAME EQ Y.RDB$BASE_FIELD AND X.RDB$FIELD_SOURCE_SCHEMA_NAME EQ Y.RDB$FIELD_SOURCE_SCHEMA_NAME AND X.RDB$FIELD_SOURCE EQ Y.RDB$FIELD_SOURCE AND + Y.RDB$PACKAGE_NAME MISSING AND Y.RDB$SCHEMA_NAME EQ Z.RDB$SCHEMA_NAME AND Y.RDB$RELATION_NAME EQ Z.RDB$VIEW_NAME AND X.RDB$SCHEMA_NAME EQ Z.RDB$RELATION_SCHEMA_NAME AND @@ -7037,6 +7066,8 @@ bool RelationNode::deleteLocalField(thread_db* tdbb, jrd_tra* transaction, } END_FOR + if (relationName.package.isEmpty()) + { // FIXME: indent // If the column to be dropped is being used as a foreign key // and the column was not part of any compound foreign key, // then we can drop the column. But we have to drop the foreign key @@ -7049,11 +7080,13 @@ bool RelationNode::deleteLocalField(thread_db* tdbb, jrd_tra* transaction, IDX_SEG IN RDB$INDEX_SEGMENTS CROSS REL_CONST IN RDB$RELATION_CONSTRAINTS WITH IDX.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND + IDX.RDB$PACKAGE_NAME MISSING AND IDX.RDB$RELATION_NAME EQ relationName.object.c_str() AND REL_CONST.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND REL_CONST.RDB$RELATION_NAME EQ relationName.object.c_str() AND IDX_SEG.RDB$FIELD_NAME EQ fieldName.c_str() AND IDX.RDB$SCHEMA_NAME EQ IDX_SEG.RDB$SCHEMA_NAME AND + IDX_SEG.RDB$PACKAGE_NAME MISSING AND IDX.RDB$INDEX_NAME EQ IDX_SEG.RDB$INDEX_NAME AND IDX.RDB$SCHEMA_NAME EQ REL_CONST.RDB$SCHEMA_NAME AND IDX.RDB$INDEX_NAME EQ REL_CONST.RDB$INDEX_NAME AND @@ -7087,8 +7120,10 @@ bool RelationNode::deleteLocalField(thread_db* tdbb, jrd_tra* transaction, IDX IN RDB$INDICES CROSS IDX_SEG IN RDB$INDEX_SEGMENTS WITH IDX.RDB$SCHEMA_NAME EQ IDX_SEG.RDB$SCHEMA_NAME AND + IDX.RDB$PACKAGE_NAME EQUIV IDX_SEG.RDB$PACKAGE_NAME AND IDX.RDB$INDEX_NAME EQ IDX_SEG.RDB$INDEX_NAME AND IDX.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND + IDX.RDB$PACKAGE_NAME MISSING AND IDX.RDB$RELATION_NAME EQ relationName.object.c_str() AND IDX_SEG.RDB$FIELD_NAME EQ fieldName.c_str() AND (IDX.RDB$INDEX_INACTIVE MISSING OR @@ -7108,6 +7143,7 @@ bool RelationNode::deleteLocalField(thread_db* tdbb, jrd_tra* transaction, MetaName(IDX.RDB$INDEX_NAME).toQuotedString()); } END_FOR + } // FIXME: indent end // Delete the automatically created generator for Identity columns. @@ -7119,6 +7155,7 @@ bool RelationNode::deleteLocalField(thread_db* tdbb, jrd_tra* transaction, RFR IN RDB$RELATION_FIELDS WITH RFR.RDB$FIELD_NAME EQ fieldName.c_str() AND RFR.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND + RFR.RDB$PACKAGE_NAME EQUIV NULLIF(relationName.package.c_str(), '') AND RFR.RDB$RELATION_NAME EQ relationName.object.c_str() { executePreChangeHandler(); @@ -7171,6 +7208,34 @@ bool RelationNode::deleteLocalField(thread_db* tdbb, jrd_tra* transaction, return found; } +void RelationNode::validateLttClauses(const Array>& clauses) +{ + for (const auto clause : clauses) + { + switch (clause->type) + { + case Clause::TYPE_ADD_COLUMN: + validateLttColumnClause(static_cast(clause.getObject())); + break; + + case Clause::TYPE_ADD_CONSTRAINT: + status_exception::raise( + Arg::Gds(isc_sqlerr) << Arg::Num(-607) << + Arg::Gds(isc_dsql_command_err) << + Arg::Gds(isc_random) << + "Only NOT NULL constraints without names are supported on LOCAL TEMPORARY TABLEs"); + break; + + case Clause::TYPE_ADD_PACKAGE_LTT_INDEX: + break; + + default: + fb_assert(false); + break; + } + } +} + // Validate column clause for local temporary table limitations. // Throws exception if unsupported features are used. void RelationNode::validateLttColumnClause(const AddColumnClause* addColumnClause) @@ -7312,7 +7377,7 @@ void RelationNode::defineField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch field->resolve(dsqlScratch); - if (tempFlag != REL_temp_ltt) + if (!isCreatedLtt()) { // Generate a domain. @@ -7390,7 +7455,7 @@ void RelationNode::defineField(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch if (field->typeOfName.object.isEmpty() || field->collate.object.hasData()) fieldDefinition.collationId = field->collationId; - if (tempFlag != REL_temp_ltt) + if (!isCreatedLtt()) { fieldDefinition.store(tdbb, transaction); @@ -7623,6 +7688,9 @@ void RelationNode::makeConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScra void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, MetaName& constraintName, Constraint& constraint) { + fb_assert(name.package.isEmpty() || + (constraintName.isEmpty() && constraint.type == Constraint::TYPE_NOT_NULL)); + if (constraintName.isEmpty()) { QualifiedName qualifiedConstraintName(constraintName, name.schema); @@ -7688,7 +7756,7 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc definition.refRelation = constraint.refRelation; definition.refColumns = constraint.refColumns; - QualifiedName qualifiedIndexName(constraint.index->name, name.schema); + QualifiedName qualifiedIndexName(constraint.index->name, name.schema, name.package); indexList.push(CreateIndexNode::store(tdbb, indexList.getPool(), transaction, qualifiedIndexName, definition, &referredIndexName)); constraint.index->name = qualifiedIndexName.object; @@ -7747,8 +7815,10 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc RFR IN RDB$RELATION_FIELDS CROSS FLX IN RDB$FIELDS WITH IDS.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + IDS.RDB$PACKAGE_NAME MISSING AND IDS.RDB$INDEX_NAME EQ constraint.index->name.c_str() AND RFR.RDB$SCHEMA_NAME EQ IDS.RDB$SCHEMA_NAME AND + RFR.RDB$PACKAGE_NAME MISSING AND RFR.RDB$RELATION_NAME EQ name.object.c_str() AND RFR.RDB$FIELD_NAME EQ IDS.RDB$FIELD_NAME AND FLX.RDB$SCHEMA_NAME EQ RFR.RDB$FIELD_SOURCE_SCHEMA_NAME AND @@ -7776,6 +7846,7 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDS IN RDB$INDEX_SEGMENTS WITH IDS.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + IDS.RDB$PACKAGE_NAME MISSING AND IDS.RDB$INDEX_NAME EQ constraint.index->name.c_str() { ++allCount; @@ -7837,6 +7908,7 @@ void RelationNode::defineConstraint(thread_db* tdbb, DsqlCompilerScratch* dsqlSc IDS IN RDB$INDEX_SEGMENTS OVER RDB$SCHEMA_NAME, RDB$INDEX_NAME WITH CRT.RDB$SCHEMA_NAME EQ name.schema.c_str() AND CRT.RDB$RELATION_NAME EQ name.object.c_str() AND + IDS.RDB$PACKAGE_NAME MISSING AND CRT.RDB$CONSTRAINT_NAME NE constraintName.c_str() AND (CRT.RDB$CONSTRAINT_TYPE EQ PRIMARY_KEY OR CRT.RDB$CONSTRAINT_TYPE EQ UNIQUE_CNSTRT) @@ -8155,6 +8227,7 @@ void RelationNode::defineDeleteCascadeTrigger(DsqlCompilerScratch* dsqlScratch, { blrWriter.appendUChar(blr_relation3); blrWriter.appendMetaString(name.schema.c_str()); + blrWriter.appendMetaString(""); blrWriter.appendMetaString(name.object.c_str()); blrWriter.appendMetaString(""); } @@ -8246,6 +8319,7 @@ void RelationNode::generateUnnamedTriggerBeginning(Constraint& constraint, bool { blrWriter.appendUChar(blr_relation3); blrWriter.appendMetaString(name.schema.c_str()); + blrWriter.appendMetaString(""); blrWriter.appendMetaString(name.object.c_str()); blrWriter.appendMetaString(""); } @@ -8414,7 +8488,8 @@ void RelationNode::makeVersion(thread_db* tdbb, jrd_tra* transaction, const Qual FOR(REQUEST_HANDLE request_fmt1 TRANSACTION_HANDLE transaction) REL IN RDB$RELATIONS WITH REL.RDB$SCHEMA_NAME EQ relName.schema.c_str() AND - REL.RDB$RELATION_NAME EQ relName.object.c_str() + REL.RDB$PACKAGE_NAME EQUIV NULLIF(relName.package.c_str(), '') AND + REL.RDB$RELATION_NAME EQ relName.object.c_str() { relation = MetadataCache::getVersioned(tdbb, REL.RDB$RELATION_ID, CacheFlag::AUTOCREATE); @@ -8445,9 +8520,10 @@ void RelationNode::makeVersion(thread_db* tdbb, jrd_tra* transaction, const Qual RFR IN RDB$RELATION_FIELDS CROSS FLD IN RDB$FIELDS WITH RFR.RDB$SCHEMA_NAME EQ relName.schema.c_str() AND - RFR.RDB$RELATION_NAME EQ relName.object.c_str() AND - FLD.RDB$SCHEMA_NAME EQ RFR.RDB$FIELD_SOURCE_SCHEMA_NAME AND - FLD.RDB$FIELD_NAME EQ RFR.RDB$FIELD_SOURCE + RFR.RDB$PACKAGE_NAME EQUIV NULLIF(relName.package.c_str(), '') AND + RFR.RDB$RELATION_NAME EQ relName.object.c_str() AND + FLD.RDB$SCHEMA_NAME EQ RFR.RDB$FIELD_SOURCE_SCHEMA_NAME AND + FLD.RDB$FIELD_NAME EQ RFR.RDB$FIELD_SOURCE { // Update RFR to reflect new fields id @@ -8788,6 +8864,7 @@ void RelationNode::makeVersion(thread_db* tdbb, jrd_tra* transaction, const Qual // update the dbkey length to include each of the base relations REL.RDB$DBKEY_LENGTH = 0; + fb_assert(relName.package.isEmpty()); AutoRequest handle; FOR(REQUEST_HANDLE handle) @@ -8796,6 +8873,7 @@ void RelationNode::makeVersion(thread_db* tdbb, jrd_tra* transaction, const Qual WITH Z.RDB$SCHEMA_NAME = relName.schema.c_str() AND Z.RDB$VIEW_NAME = relName.object.c_str() AND R.RDB$SCHEMA_NAME EQ Z.RDB$RELATION_SCHEMA_NAME AND + R.RDB$PACKAGE_NAME EQUIV Z.RDB$PACKAGE_NAME AND R.RDB$RELATION_NAME EQ Z.RDB$RELATION_NAME { REL.RDB$DBKEY_LENGTH += R.RDB$DBKEY_LENGTH; @@ -9390,9 +9468,13 @@ void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScrat if (tempFlag != REL_temp_ltt) executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_TABLE, name, {}); - DYN_UTIL_check_unique_name(tdbb, name, obj_relation); + if (name.package.isEmpty()) + DYN_UTIL_check_unique_name(tdbb, name, obj_relation); if (tempFlag == REL_temp_ltt) + validateLttClauses(clauses); + + if (isCreatedLtt()) { dsqlScratch->flags |= DsqlCompilerScratch::FLAG_ACTUAL_LTT_DDL; defineLocalTempTable(tdbb, dsqlScratch, transaction); @@ -9431,6 +9513,21 @@ void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScrat { strcpy(REL.RDB$SCHEMA_NAME, name.schema.c_str()); strcpy(REL.RDB$RELATION_NAME, name.object.c_str()); + + if (name.package.hasData()) + { + REL.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(REL.RDB$PACKAGE_NAME, name.package.c_str()); + + REL.RDB$PRIVATE_FLAG.NULL = FALSE; + REL.RDB$PRIVATE_FLAG = packagePrivate ? 1 : 0; + } + else + { + REL.RDB$PACKAGE_NAME.NULL = TRUE; + REL.RDB$PRIVATE_FLAG.NULL = TRUE; + } + REL.RDB$SYSTEM_FLAG = 0; REL.RDB$FLAGS = REL_sql; REL.RDB$RELATION_TYPE = relationType; @@ -9496,7 +9593,8 @@ void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScrat // access rights - storePrivileges(tdbb, transaction, name, obj_relation, ALL_PRIVILEGES); + if (name.package.isEmpty()) + storePrivileges(tdbb, transaction, name, obj_relation, ALL_PRIVILEGES); // constraints @@ -9519,6 +9617,40 @@ void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScrat static_cast(i->getObject()), constraints); break; + case Clause::TYPE_ADD_PACKAGE_LTT_INDEX: + { + auto addPackageLttIndexClause = static_cast(i->getObject()); + const auto indexNode = addPackageLttIndexClause->indexNode; + + if (!indexNode->relation) + { + addPackageLttIndexClause->indexNode->relation = FB_NEW_POOL(dsqlScratch->getPool()) + RelationSourceNode(dsqlScratch->getPool(), name); + } + + CreateIndexNode::Definition definition; + definition.type = isc_dyn_def_idx; + definition.relation = indexNode->relation->dsqlName; + definition.unique = indexNode->unique; + definition.descending = indexNode->descending; + definition.inactive = !indexNode->active; + + fb_assert(indexNode->columns); + fb_assert(!indexNode->computed); + fb_assert(!indexNode->partial); + + for (const auto indexColumn : indexNode->columns->items) + { + MetaName& column = definition.columns.add(); + column = nodeAs(indexColumn)->dsqlName; + } + + indexList.push(CreateIndexNode::store(tdbb, indexList.getPool(), transaction, + addPackageLttIndexClause->indexNode->name, definition)); + + break; + } + default: fb_assert(false); break; @@ -9547,7 +9679,8 @@ void CreateRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScrat dsqlScratch->relation->rel_flags &= ~REL_creating; - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_TABLE, name, {}); + if (tempFlag != REL_temp_ltt) + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_TABLE, name, {}); } catch (const Exception&) { @@ -9597,7 +9730,7 @@ const ObjectsArray* CreateRelationNode::findPkColumns() void CreateRelationNode::defineLocalTempTable(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { - AutoSetRestoreFlag autoLttReferences(&dsqlScratch->flags, DsqlCompilerScratch::FLAG_ALLOW_LTT_REFERENCES, true); + AutoSetRestoreFlag autoLttReferences(&dsqlScratch->flags, DsqlCompilerScratch::FLAG_ALLOW_CREATED_LTT_REFERENCE, true); const auto attachment = tdbb->getAttachment(); auto& attPool = *attachment->att_pool; @@ -9647,8 +9780,6 @@ void CreateRelationNode::defineLocalTempTable(thread_db* tdbb, DsqlCompilerScrat name.toQuotedString()); } - validateLttColumnClause(addColumnClause); - field->resolve(dsqlScratch); const bool notNullFlag = std::any_of( @@ -9679,13 +9810,7 @@ void CreateRelationNode::defineLocalTempTable(thread_db* tdbb, DsqlCompilerScrat lttField.desc.dsc_sub_type = field->subType; } else if (clause->type == Clause::TYPE_ADD_CONSTRAINT) - { - status_exception::raise( - Arg::Gds(isc_sqlerr) << Arg::Num(-607) << - Arg::Gds(isc_dsql_command_err) << - Arg::Gds(isc_random) << - "Only NOT NULL constraints without names are supported on local temporary tables"); - } + fb_assert(false); } attachment->att_local_temporary_tables.put(name, ltt); @@ -9748,7 +9873,7 @@ void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc // Check if this is an LTT and set flag before METD_get_relation (called by saveRelation) // to avoid false "LTT cannot be referenced in persistent metadata" error - AutoSetRestoreFlag autoLttReferences(&dsqlScratch->flags, DsqlCompilerScratch::FLAG_ALLOW_LTT_REFERENCES, + AutoSetRestoreFlag autoLttReferences(&dsqlScratch->flags, DsqlCompilerScratch::FLAG_ALLOW_CREATED_LTT_REFERENCE, attachment->att_local_temporary_tables.exist(name)); AutoSetRestoreFlag dfwFlags(&tdbb->tdbb_flags, TDBB_use_db_page_space, true); @@ -9831,6 +9956,7 @@ void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) RFL IN RDB$RELATION_FIELDS WITH RFL.RDB$SCHEMA_NAME = relation->rel_name.schema.c_str() AND + RFL.RDB$PACKAGE_NAME EQUIV NULLIF(relation->rel_name.package.c_str(), '') AND RFL.RDB$RELATION_NAME = relation->rel_name.object.c_str() AND RFL.RDB$FIELD_NAME = addColumnClause->field->fld_name.c_str() { @@ -9867,6 +9993,7 @@ void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc RFL IN RDB$RELATION_FIELDS WITH RFL.RDB$FIELD_NAME EQ clause->fromName.c_str() AND RFL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + RFL.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND RFL.RDB$RELATION_NAME EQ name.object.c_str() { found = true; @@ -9932,6 +10059,7 @@ void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc RFL IN RDB$RELATION_FIELDS WITH RFL.RDB$FIELD_NAME EQ clause->name.c_str() AND RFL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + RFL.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND RFL.RDB$RELATION_NAME EQ name.object.c_str() { found = true; @@ -10085,6 +10213,7 @@ void AlterRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) REL IN RDB$RELATIONS WITH REL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + REL.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND REL.RDB$RELATION_NAME EQ name.object.c_str() { MODIFY REL @@ -10274,9 +10403,11 @@ void AlterRelationNode::modifyField(thread_db* tdbb, DsqlCompilerScratch* dsqlSc REL IN RDB$RELATIONS CROSS FLD IN RDB$FIELDS WITH RFR.RDB$SCHEMA_NAME = name.schema.c_str() AND + RFR.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND RFR.RDB$RELATION_NAME = name.object.c_str() AND RFR.RDB$FIELD_NAME = field->fld_name.c_str() AND REL.RDB$SCHEMA_NAME = RFR.RDB$SCHEMA_NAME AND + REL.RDB$PACKAGE_NAME EQUIV RFR.RDB$PACKAGE_NAME AND REL.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME AND FLD.RDB$SCHEMA_NAME = RFR.RDB$FIELD_SOURCE_SCHEMA_NAME AND FLD.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE @@ -10663,9 +10794,10 @@ void AlterRelationNode::modifyField(thread_db* tdbb, DsqlCompilerScratch* dsqlSc VRL IN RDB$VIEW_RELATIONS WITH VRL.RDB$RELATION_SCHEMA_NAME EQ name.schema.c_str() AND VRL.RDB$RELATION_NAME EQ name.object.c_str() AND - VRL.RDB$PACKAGE_NAME MISSING AND + VRL.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND VRL.RDB$CONTEXT_TYPE EQ VCT_TABLE AND RFR2.RDB$SCHEMA_NAME EQ VRL.RDB$SCHEMA_NAME AND + RFR2.RDB$PACKAGE_NAME MISSING AND RFR2.RDB$RELATION_NAME EQ VRL.RDB$VIEW_NAME AND RFR2.RDB$VIEW_CONTEXT EQ VRL.RDB$VIEW_CONTEXT AND RFR2.RDB$BASE_FIELD = field->fld_name.c_str() @@ -10706,7 +10838,7 @@ void AlterRelationNode::modifyField(thread_db* tdbb, DsqlCompilerScratch* dsqlSc void AlterRelationNode::alterLocalTempTable(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { - fb_assert(dsqlScratch->flags & DsqlCompilerScratch::FLAG_ALLOW_LTT_REFERENCES); + fb_assert(dsqlScratch->flags & DsqlCompilerScratch::FLAG_ALLOW_CREATED_LTT_REFERENCE); const auto attachment = tdbb->getAttachment(); auto& attPool = *attachment->att_pool; @@ -11185,9 +11317,11 @@ void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) R IN RDB$RELATIONS WITH R.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + R.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND R.RDB$RELATION_NAME EQ name.object.c_str() { - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, ddlTriggerAction, name, {}); + if (name.package.isEmpty()) + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, ddlTriggerAction, name, {}); found = true; } END_FOR @@ -11198,40 +11332,71 @@ void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch status_exception::raise(Arg::PrivateDyn(61)); } - request.reset(tdbb, drq_e_rel_con2, DYN_REQUESTS); - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - CRT IN RDB$RELATION_CONSTRAINTS - WITH CRT.RDB$SCHEMA_NAME EQ name.schema.c_str() AND - CRT.RDB$RELATION_NAME EQ name.object.c_str() AND - (CRT.RDB$CONSTRAINT_TYPE EQ PRIMARY_KEY OR - CRT.RDB$CONSTRAINT_TYPE EQ UNIQUE_CNSTRT OR - CRT.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY) - SORTED BY ASCENDING CRT.RDB$CONSTRAINT_TYPE + if (name.package.isEmpty()) { - ERASE CRT; + request.reset(tdbb, drq_e_rel_con2, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + CRT IN RDB$RELATION_CONSTRAINTS + WITH CRT.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + CRT.RDB$RELATION_NAME EQ name.object.c_str() AND + (CRT.RDB$CONSTRAINT_TYPE EQ PRIMARY_KEY OR + CRT.RDB$CONSTRAINT_TYPE EQ UNIQUE_CNSTRT OR + CRT.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY) + SORTED BY ASCENDING CRT.RDB$CONSTRAINT_TYPE + { + ERASE CRT; + } + END_FOR } - END_FOR MemoryPool& p = *MemoryPool::getContextPool(); ModifyIndexList indexList(p); request.reset(tdbb, drq_e_rel_idxs, DYN_REQUESTS); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND IDX.RDB$RELATION_NAME EQ name.object.c_str() AND IDX.RDB$INDEX_INACTIVE NE MET_index_deferred_drop { - auto* node = FB_NEW_POOL(p) DropIndexNode(p, QualifiedName(IDX.RDB$INDEX_NAME, name.schema)); + auto* node = FB_NEW_POOL(p) DropIndexNode(p, QualifiedName(IDX.RDB$INDEX_NAME, name.schema, name.package)); node->drop(tdbb, nullptr, transaction, indexList); } END_FOR indexList.exec(tdbb, transaction); + request.reset(tdbb, drq_e_rel_flds, DYN_REQUESTS); + + FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) + RFR IN RDB$RELATION_FIELDS + WITH RFR.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + RFR.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND + RFR.RDB$RELATION_NAME EQ name.object.c_str() + { + if (!RFR.RDB$GENERATOR_NAME.NULL) + { + DropSequenceNode::deleteIdentity(tdbb, transaction, + QualifiedName(RFR.RDB$GENERATOR_NAME, name.schema)); + } + + ERASE RFR; + + if (!RFR.RDB$SECURITY_CLASS.NULL && + !strncmp(RFR.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN)) + { + deleteSecurityClass(tdbb, transaction, RFR.RDB$SECURITY_CLASS); + } + + deleteGlobalField(tdbb, transaction, QualifiedName(RFR.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE_SCHEMA_NAME)); + } + END_FOR + + if (name.package.isEmpty()) + { // FIXME: indent request.reset(tdbb, drq_e_trg_msgs2, DYN_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) @@ -11260,31 +11425,6 @@ void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch } END_FOR - request.reset(tdbb, drq_e_rel_flds, DYN_REQUESTS); - - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) - RFR IN RDB$RELATION_FIELDS - WITH RFR.RDB$SCHEMA_NAME EQ name.schema.c_str() AND - RFR.RDB$RELATION_NAME EQ name.object.c_str() - { - if (!RFR.RDB$GENERATOR_NAME.NULL) - { - DropSequenceNode::deleteIdentity(tdbb, transaction, - QualifiedName(RFR.RDB$GENERATOR_NAME, name.schema)); - } - - ERASE RFR; - - if (!RFR.RDB$SECURITY_CLASS.NULL && - !strncmp(RFR.RDB$SECURITY_CLASS, SQL_SECCLASS_PREFIX, SQL_SECCLASS_PREFIX_LEN)) - { - deleteSecurityClass(tdbb, transaction, RFR.RDB$SECURITY_CLASS); - } - - deleteGlobalField(tdbb, transaction, QualifiedName(RFR.RDB$FIELD_SOURCE, RFR.RDB$FIELD_SOURCE_SCHEMA_NAME)); - } - END_FOR - request.reset(tdbb, drq_e_view_rels, DYN_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) @@ -11295,12 +11435,14 @@ void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch ERASE VR; } END_FOR + } // FIXME: indent end request.reset(tdbb, drq_e_relation, DYN_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) R IN RDB$RELATIONS WITH R.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + R.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND R.RDB$RELATION_NAME EQ name.object.c_str() { ERASE R; @@ -11313,6 +11455,8 @@ void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch } END_FOR + if (name.package.isEmpty()) + { // FIXME: indent // Triggers must be deleted after check constraints QualifiedName triggerName; @@ -11369,6 +11513,7 @@ void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch ERASE PTAB; } END_FOR + } // FIXME: indent end bool rolledBack = false; @@ -11439,10 +11584,11 @@ void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND IDX.RDB$RELATION_NAME EQ name.object.c_str() { - name.object = IDX.RDB$INDEX_NAME; - DropIndexNode::deleteSegmentRecords(tdbb, transaction, name); + const QualifiedName indexName(IDX.RDB$INDEX_NAME, IDX.RDB$SCHEMA_NAME, IDX.RDB$PACKAGE_NAME); + DropIndexNode::deleteSegmentRecords(tdbb, transaction, indexName); ERASE IDX; } END_FOR @@ -11459,7 +11605,8 @@ void DropRelationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch throw; } - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, {}); + if (name.package.isEmpty()) + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, {}); // Update DSQL cache METD_drop_relation(transaction, name); @@ -11514,6 +11661,8 @@ void CreateAlterViewNode::checkPermission(thread_db* tdbb, jrd_tra* transaction) void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction) { + fb_assert(name.package.isEmpty()); + if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, name, obj_relation)) return; @@ -11683,6 +11832,11 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra VRL.RDB$PACKAGE_NAME.NULL = FALSE; strcpy(VRL.RDB$PACKAGE_NAME, procedure->prc_name.package.c_str()); } + else if (relation && relation->rel_name.package.hasData()) + { + VRL.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(VRL.RDB$PACKAGE_NAME, relation->rel_name.package.c_str()); + } else VRL.RDB$PACKAGE_NAME.NULL = TRUE; } @@ -11693,14 +11847,13 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra // Check privileges on base tables and views. request.reset(tdbb, drq_l_view_rels, DYN_REQUESTS); - FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) VRL IN RDB$VIEW_RELATIONS CROSS PREL IN RDB$RELATIONS - WITH VRL.RDB$PACKAGE_NAME MISSING AND - VRL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + WITH VRL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND VRL.RDB$VIEW_NAME EQ name.object.c_str() AND PREL.RDB$SCHEMA_NAME EQ VRL.RDB$RELATION_SCHEMA_NAME AND + PREL.RDB$PACKAGE_NAME EQUIV VRL.RDB$PACKAGE_NAME AND PREL.RDB$RELATION_NAME EQ VRL.RDB$RELATION_NAME { // CVC: This never matches so it causes unnecessary calls to verify, @@ -11713,7 +11866,9 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra SCL_check_schema(tdbb, PREL.RDB$SCHEMA_NAME, SCL_usage); - if (const auto priv = SCL_get_mask(tdbb, QualifiedName(PREL.RDB$RELATION_NAME, PREL.RDB$SCHEMA_NAME), ""); + const QualifiedName relationName(PREL.RDB$RELATION_NAME, PREL.RDB$SCHEMA_NAME, PREL.RDB$PACKAGE_NAME); + + if (const auto priv = SCL_get_mask(tdbb, relationName, ""); !(priv & SCL_select) ) { @@ -11723,7 +11878,7 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra // Remember, a view may be based on a view. "TABLE/VIEW" << // Non-Translatable // We want to print the name of the base table or view. - QualifiedName(PREL.RDB$RELATION_NAME, PREL.RDB$SCHEMA_NAME).toQuotedString()); + relationName.toQuotedString()); } } } @@ -11909,6 +12064,7 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra FLD IN RDB$FIELDS WITH RFL.RDB$FIELD_NAME EQ fieldStr AND RFL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + RFL.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND RFL.RDB$RELATION_NAME EQ name.object.c_str() AND RFL.RDB$BASE_FIELD MISSING AND FLD.RDB$SCHEMA_NAME EQ RFL.RDB$FIELD_SOURCE_SCHEMA_NAME AND @@ -11972,6 +12128,7 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra FLD IN RDB$FIELDS WITH RFL.RDB$FIELD_NAME EQ fieldStr AND RFL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + RFL.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND RFL.RDB$RELATION_NAME EQ name.object.c_str() AND RFL.RDB$BASE_FIELD MISSING AND FLD.RDB$SCHEMA_NAME EQ RFL.RDB$FIELD_SOURCE_SCHEMA_NAME AND @@ -12387,11 +12544,13 @@ Cached::Relation* ModifyIndexNode::getRelByIndex(thread_db* tdbb, const Qualifie IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ index.schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(index.package.c_str(), '') AND IDX.RDB$INDEX_NAME EQ index.object.c_str() { fb_assert(!relation); relation = MetadataCache::getPerm(tdbb, - QualifiedName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME), CacheFlag::AUTOCREATE); + QualifiedName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME, IDX.RDB$PACKAGE_NAME), + CacheFlag::AUTOCREATE); } END_FOR @@ -12484,7 +12643,9 @@ MetaId StoreIndexNode::create(thread_db* tdbb, jrd_tra* transaction) IDX IN RDB$INDICES CROSS REL IN RDB$RELATIONS OVER RDB$SCHEMA_NAME, RDB$RELATION_NAME WITH IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND - IDX.RDB$INDEX_NAME EQ indexName.object.c_str() + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(indexName.package.c_str(), '') AND + IDX.RDB$INDEX_NAME EQ indexName.object.c_str() AND + REL.RDB$PACKAGE_NAME EQUIV IDX.RDB$PACKAGE_NAME { active = IDX.RDB$INDEX_INACTIVE.NULL || !IDX.RDB$INDEX_INACTIVE; @@ -12522,17 +12683,20 @@ MetaId StoreIndexNode::create(thread_db* tdbb, jrd_tra* transaction) if (!IDX.RDB$FOREIGN_KEY.NULL) idx.idx_flags |= idx_foreign; - AutoCacheRequest rc_request(tdbb, irq_c_index_rc, IRQ_REQUESTS); - - FOR(REQUEST_HANDLE rc_request TRANSACTION_HANDLE transaction) - RC IN RDB$RELATION_CONSTRAINTS WITH - RC.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND - RC.RDB$INDEX_NAME EQ indexName.object.c_str() AND - RC.RDB$CONSTRAINT_TYPE = PRIMARY_KEY + if (indexName.package.isEmpty()) { - idx.idx_flags |= idx_primary; + AutoCacheRequest rc_request(tdbb, irq_c_index_rc, IRQ_REQUESTS); + + FOR(REQUEST_HANDLE rc_request TRANSACTION_HANDLE transaction) + RC IN RDB$RELATION_CONSTRAINTS WITH + RC.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND + RC.RDB$INDEX_NAME EQ indexName.object.c_str() AND + RC.RDB$CONSTRAINT_TYPE = PRIMARY_KEY + { + idx.idx_flags |= idx_primary; + } + END_FOR } - END_FOR idx.idx_condition_node = nullptr; idx.idx_condition_statement = nullptr; @@ -12578,8 +12742,10 @@ MetaId StoreIndexNode::create(thread_db* tdbb, jrd_tra* transaction) RFR IN RDB$RELATION_FIELDS CROSS FLD IN RDB$FIELDS WITH SEG.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND + SEG.RDB$PACKAGE_NAME EQUIV NULLIF(indexName.package.c_str(), '') AND SEG.RDB$INDEX_NAME EQ indexName.object.c_str() AND RFR.RDB$SCHEMA_NAME EQ SEG.RDB$SCHEMA_NAME AND + RFR.RDB$PACKAGE_NAME EQUIV SEG.RDB$PACKAGE_NAME AND RFR.RDB$RELATION_NAME EQ indexRelation->getName().object.c_str() AND RFR.RDB$FIELD_NAME EQ SEG.RDB$FIELD_NAME AND FLD.RDB$SCHEMA_NAME EQ RFR.RDB$FIELD_SOURCE_SCHEMA_NAME AND @@ -12791,7 +12957,9 @@ MetaId StoreIndexNode::createExpression(thread_db* tdbb, jrd_tra* transaction) CROSS REL IN RDB$RELATIONS OVER RDB$SCHEMA_NAME, RDB$RELATION_NAME WITH IDX.RDB$EXPRESSION_BLR NOT MISSING AND IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND - IDX.RDB$INDEX_NAME EQ indexName.object.c_str() + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(indexName.package.c_str(), '') AND + IDX.RDB$INDEX_NAME EQ indexName.object.c_str() AND + REL.RDB$PACKAGE_NAME EQUIV IDX.RDB$PACKAGE_NAME { active = IDX.RDB$INDEX_INACTIVE.NULL || !IDX.RDB$INDEX_INACTIVE; @@ -12939,6 +13107,7 @@ ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* QualifiedName& idxName, Definition& definition, QualifiedName* referredIndexName) { fb_assert(idxName.schema == definition.relation.schema); + fb_assert(idxName.package == definition.relation.package); if (idxName.isEmpty()) DYN_UTIL_generate_index_name(tdbb, transaction, idxName, definition.type); @@ -12963,6 +13132,15 @@ ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* IDX.RDB$RELATION_NAME.NULL = FALSE; strcpy(IDX.RDB$SCHEMA_NAME, idxName.schema.c_str()); strcpy(IDX.RDB$INDEX_NAME, idxName.object.c_str()); + + if (definition.relation.package.hasData()) + { + IDX.RDB$PACKAGE_NAME.NULL = FALSE; + strcpy(IDX.RDB$PACKAGE_NAME, definition.relation.package.c_str()); + } + else + IDX.RDB$PACKAGE_NAME.NULL = TRUE; + strcpy(IDX.RDB$RELATION_NAME, definition.relation.object.c_str()); IDX.RDB$SYSTEM_FLAG = 0; @@ -12973,6 +13151,7 @@ ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) VREL IN RDB$RELATIONS WITH VREL.RDB$SCHEMA_NAME EQ IDX.RDB$SCHEMA_NAME AND + VREL.RDB$PACKAGE_NAME EQUIV NULLIF(definition.relation.package.c_str(), '') AND VREL.RDB$RELATION_NAME EQ IDX.RDB$RELATION_NAME { if (!VREL.RDB$VIEW_BLR.NULL) @@ -13020,11 +13199,12 @@ ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) F IN RDB$RELATION_FIELDS CROSS GF IN RDB$FIELDS - WITH F.RDB$FIELD_NAME EQ definition.columns[i].c_str() AND + WITH F.RDB$SCHEMA_NAME EQ IDX.RDB$SCHEMA_NAME AND + F.RDB$PACKAGE_NAME EQUIV NULLIF(definition.relation.package.c_str(), '') AND + F.RDB$RELATION_NAME EQ IDX.RDB$RELATION_NAME AND + F.RDB$FIELD_NAME EQ definition.columns[i].c_str() AND GF.RDB$SCHEMA_NAME EQ F.RDB$FIELD_SOURCE_SCHEMA_NAME AND - GF.RDB$FIELD_NAME EQ F.RDB$FIELD_SOURCE AND - IDX.RDB$SCHEMA_NAME EQ F.RDB$SCHEMA_NAME AND - IDX.RDB$RELATION_NAME EQ F.RDB$RELATION_NAME + GF.RDB$FIELD_NAME EQ F.RDB$FIELD_SOURCE { ULONG length = 0; @@ -13120,6 +13300,11 @@ ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* X IN RDB$INDEX_SEGMENTS { strcpy(X.RDB$SCHEMA_NAME, IDX.RDB$SCHEMA_NAME); + + X.RDB$PACKAGE_NAME.NULL = IDX.RDB$PACKAGE_NAME.NULL; + if (!IDX.RDB$PACKAGE_NAME.NULL) + strcpy(X.RDB$PACKAGE_NAME, IDX.RDB$PACKAGE_NAME); + strcpy(X.RDB$INDEX_NAME, IDX.RDB$INDEX_NAME); strcpy(X.RDB$FIELD_NAME, segment->c_str()); X.RDB$FIELD_POSITION = position++; @@ -13135,6 +13320,8 @@ ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* if (definition.refColumns.hasData()) { + fb_assert(definition.refRelation.package.isEmpty()); + // If referring columns count <> referred columns return error. if (definition.columns.getCount() != definition.refColumns.getCount()) @@ -13159,13 +13346,15 @@ ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* ISEG IN RDB$INDEX_SEGMENTS OVER RDB$SCHEMA_NAME, RDB$INDEX_NAME WITH IND.RDB$SCHEMA_NAME EQ definition.refRelation.schema.c_str() AND IND.RDB$RELATION_NAME EQ definition.refRelation.object.c_str() AND + IND.RDB$PACKAGE_NAME MISSING AND IND.RDB$UNIQUE_FLAG NOT MISSING AND (RC.RDB$CONSTRAINT_TYPE = PRIMARY_KEY OR - RC.RDB$CONSTRAINT_TYPE = UNIQUE_CNSTRT) + RC.RDB$CONSTRAINT_TYPE = UNIQUE_CNSTRT) AND + ISEG.RDB$PACKAGE_NAME MISSING SORTED BY IND.RDB$INDEX_NAME, DESCENDING ISEG.RDB$FIELD_POSITION { - if (refIndexName != QualifiedName(IND.RDB$INDEX_NAME, IND.RDB$SCHEMA_NAME)) + if (refIndexName != QualifiedName(IND.RDB$INDEX_NAME, IND.RDB$SCHEMA_NAME, IND.RDB$PACKAGE_NAME)) { if (listIndex >= 0) found = false; @@ -13212,6 +13401,7 @@ ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* FOR(REQUEST_HANDLE request3 TRANSACTION_HANDLE transaction) X IN RDB$RELATIONS WITH X.RDB$SCHEMA_NAME EQ definition.refRelation.schema.c_str() AND + X.RDB$PACKAGE_NAME MISSING AND X.RDB$RELATION_NAME EQ definition.refRelation.object.c_str() { found = true; @@ -13240,12 +13430,15 @@ ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* } else if (definition.refRelation.object.hasData()) { + fb_assert(definition.refRelation.package.isEmpty()); + request2.reset(tdbb, drq_l_primary, DYN_REQUESTS); FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) IND IN RDB$INDICES CROSS RC IN RDB$RELATION_CONSTRAINTS OVER RDB$SCHEMA_NAME, RDB$INDEX_NAME WITH IND.RDB$SCHEMA_NAME EQ definition.refRelation.schema.c_str() AND + IND.RDB$PACKAGE_NAME MISSING AND IND.RDB$RELATION_NAME EQ definition.refRelation.object.c_str() AND RC.RDB$CONSTRAINT_TYPE EQ PRIMARY_KEY { @@ -13292,6 +13485,7 @@ ModifyIndexNode* CreateIndexNode::store(thread_db* tdbb, MemoryPool& p, jrd_tra* FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ idxName.schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(idxName.package.c_str(), '') AND IDX.RDB$INDEX_NAME EQ idxName.object.c_str() { MODIFY IDX @@ -13362,7 +13556,8 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, if (createIfNotExistsOnly && !DYN_UTIL_check_unique_name_nothrow(tdbb, name, obj_index)) return; - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_INDEX, name, {}); + if (relation->dsqlName.package.isEmpty()) + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_CREATE_INDEX, name, {}); Definition definition; definition.type = isc_dyn_def_idx; @@ -13387,10 +13582,10 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, string computedSource; BlrDebugWriter::BlrData computedValue; - defineComputed(dsqlScratch, relation, NULL, computed, computedSource, computedValue); + defineComputed(dsqlScratch, relation, NULL, + computed, computedSource, computedValue); - attachment->storeMetaDataBlob(tdbb, transaction, &definition.expressionSource, - computedSource); + attachment->storeMetaDataBlob(tdbb, transaction, &definition.expressionSource, computedSource); attachment->storeBinaryBlob(tdbb, transaction, &definition.expressionBlr, computedValue); } @@ -13409,7 +13604,8 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, &definition.conditionBlr, partialValue); } - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_INDEX, name, {}); + if (relation->dsqlName.package.isEmpty()) + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_CREATE_INDEX, name, {}); savePoint.release(); // everything is ok @@ -13420,9 +13616,9 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, // Define an index on a Local Temporary Table. void CreateIndexNode::defineLocalTempIndex(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction, LocalTemporaryTable* ltt) + jrd_tra* transaction, LocalTemporaryTable* ltt) const { - AutoSetRestoreFlag autoLttReferences(&dsqlScratch->flags, DsqlCompilerScratch::FLAG_ALLOW_LTT_REFERENCES, true); + AutoSetRestoreFlag autoLttReferences(&dsqlScratch->flags, DsqlCompilerScratch::FLAG_ALLOW_CREATED_LTT_REFERENCE, true); const auto attachment = transaction->tra_attachment; auto& attPool = *attachment->att_pool; @@ -13535,6 +13731,9 @@ DdlNode* CreateIndexNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (name.schema.isEmpty()) name.schema = relation->dsqlName.schema; + if (name.package.isEmpty()) + name.package = relation->dsqlName.package; + if (name.schema != relation->dsqlName.schema) { status_exception::raise( @@ -13543,6 +13742,8 @@ DdlNode* CreateIndexNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) relation->dsqlName.schema.toQuotedString()); } + fb_assert(name.package == relation->dsqlName.package); + dsqlScratch->ddlSchema = name.schema; return DdlNode::dsqlPass(dsqlScratch); @@ -13606,7 +13807,8 @@ void AlterIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, } } - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_INDEX, indexName, {}); + if (indexName.package.isEmpty()) + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_INDEX, indexName, {}); savePoint.release(); // system table modified OK } @@ -13621,16 +13823,18 @@ bool AlterIndexNode::step1(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jr FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND - IDX.RDB$INDEX_NAME EQ indexName.object.c_str() + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(indexName.package.c_str(), '') AND + IDX.RDB$INDEX_NAME EQ indexName.object.c_str() { found = true; currentStatus = MetadataCache::getIndexStatus(IDX.RDB$INDEX_INACTIVE.NULL, IDX.RDB$INDEX_INACTIVE); if (currentStatus != desiredStatus) { - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_INDEX, indexName, {}); + if (indexName.package.isEmpty()) + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_INDEX, indexName, {}); - auto relName = QualifiedName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME); + auto relName = QualifiedName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME, IDX.RDB$PACKAGE_NAME); indexRelation = MetadataCache::getPerm(tdbb, relName, CacheFlag::AUTOCREATE); fb_assert(indexRelation); @@ -13684,6 +13888,7 @@ void AlterIndexNode::step2(thread_db* tdbb, jrd_tra* transaction) FOR(REQUEST_HANDLE rq1 TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(indexName.package.c_str(), '') AND IDX.RDB$INDEX_NAME EQ indexName.object.c_str() { MODIFY IDX @@ -13797,11 +14002,13 @@ void SetStatisticsNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(indexName.package.c_str(), '') AND IDX.RDB$INDEX_NAME EQ indexName.object.c_str() { found = true; - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_INDEX, indexName, {}); + if (indexName.package.isEmpty()) + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_ALTER_INDEX, indexName, {}); MODIFY IDX // For V4 index selectivity can be set only to -1. @@ -13812,7 +14019,10 @@ void SetStatisticsNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratc END_FOR if (found) - executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_INDEX, indexName, {}); + { + if (indexName.package.isEmpty()) + executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_ALTER_INDEX, indexName, {}); + } else { // msg 48: "Index not found" @@ -13853,6 +14063,7 @@ bool DropIndexNode::deleteSegmentRecords(thread_db* tdbb, jrd_tra* transaction, FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDXSEG IN RDB$INDEX_SEGMENTS WITH IDXSEG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + IDXSEG.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND IDXSEG.RDB$INDEX_NAME EQ name.object.c_str() { found = true; @@ -13925,16 +14136,19 @@ static MetaName makeTempDependName(MetaId relId, MetaId indexId) void DropIndexNode::drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_tra* transaction, ModifyIndexList& list, bool dropSegments) { - AUTO_HANDLE(req1); - - FOR (REQUEST_HANDLE req1 TRANSACTION_HANDLE transaction) - RLC IN RDB$RELATION_CONSTRAINTS - WITH RLC.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND - RLC.RDB$INDEX_NAME EQ indexName.object.c_str() + if (indexName.package.isEmpty()) { - ERR_post(Arg::Gds(isc_integ_index_del)); + AUTO_HANDLE(req1); + + FOR (REQUEST_HANDLE req1 TRANSACTION_HANDLE transaction) + RLC IN RDB$RELATION_CONSTRAINTS + WITH RLC.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND + RLC.RDB$INDEX_NAME EQ indexName.object.c_str() + { + ERR_post(Arg::Gds(isc_integ_index_del)); + } + END_FOR } - END_FOR AutoCacheRequest request(tdbb, drq_e_indices, DYN_REQUESTS); bool addToTheList = false; @@ -13943,15 +14157,16 @@ void DropIndexNode::drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_ FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ indexName.schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(indexName.package.c_str(), '') AND IDX.RDB$INDEX_NAME EQ indexName.object.c_str() AND IDX.RDB$INDEX_INACTIVE NE MET_index_deferred_drop { - auto relName = QualifiedName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME); + auto relName = QualifiedName(IDX.RDB$RELATION_NAME, indexName.schema, indexName.package); indexRelation = MetadataCache::getPerm(tdbb, relName, CacheFlag::AUTOCREATE); fb_assert(indexRelation); - if (dsqlScratch) + if (dsqlScratch && indexName.package.isEmpty()) executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_BEFORE, DDL_TRIGGER_DROP_INDEX, indexName, {}); if (indexRelation && @@ -13988,33 +14203,37 @@ void DropIndexNode::drop(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd_ if (indexRelation) { - if (dsqlScratch) + if (dsqlScratch && indexName.package.isEmpty()) executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, DDL_TRIGGER_DROP_INDEX, indexName, {}); + if (addToTheList) list.push(this); - AUTO_HANDLE(handle2); - FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction) - DEP IN RDB$DEPENDENCIES - WITH DEP.RDB$DEPENDENT_SCHEMA_NAME EQUIV NULLIF(indexName.schema.c_str(), '') AND - DEP.RDB$DEPENDENT_NAME EQ indexName.object.c_str() AND - (DEP.RDB$DEPENDENT_TYPE EQ obj_index_expression OR - DEP.RDB$DEPENDENT_TYPE EQ obj_index_condition) + if (indexName.package.isEmpty()) { - if (addToTheList) + AUTO_HANDLE(handle2); + FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction) + DEP IN RDB$DEPENDENCIES + WITH DEP.RDB$DEPENDENT_SCHEMA_NAME EQUIV NULLIF(indexName.schema.c_str(), '') AND + DEP.RDB$DEPENDENT_NAME EQ indexName.object.c_str() AND + (DEP.RDB$DEPENDENT_TYPE EQ obj_index_expression OR + DEP.RDB$DEPENDENT_TYPE EQ obj_index_condition) { - MODIFY DEP USING + if (addToTheList) { - strcpy(DEP.RDB$DEPENDENT_NAME, tempName.c_str()); + MODIFY DEP USING + { + strcpy(DEP.RDB$DEPENDENT_NAME, tempName.c_str()); + } + END_MODIFY + } + else + { + ERASE DEP; } - END_MODIFY - } - else - { - ERASE DEP; } + END_FOR } - END_FOR } else if (!silent) { @@ -14080,6 +14299,7 @@ void DropIndexNode::clearFrgn(thread_db* tdbb, MetaId relId, MetaId indexId) OVER RDB$RELATION_NAME, RDB$SCHEMA_NAME WITH IND.RDB$INDEX_ID EQ indexId + 1 AND REL.RDB$RELATION_ID EQ relId + AND REL.RDB$PACKAGE_NAME EQUIV IND.RDB$PACKAGE_NAME AND IND.RDB$INDEX_INACTIVE NOT MISSING AND IND.RDB$INDEX_INACTIVE EQ MET_index_deferred_drop { @@ -14148,8 +14368,9 @@ void DropIndexNode::clearId(thread_db* tdbb, MetaId relId, MetaId indexId) OVER RDB$RELATION_NAME, RDB$SCHEMA_NAME WITH IND.RDB$INDEX_ID EQ indexId + 1 AND REL.RDB$RELATION_ID EQ relId + AND REL.RDB$PACKAGE_NAME EQUIV IND.RDB$PACKAGE_NAME { - indexName = QualifiedName(IND.RDB$INDEX_NAME, IND.RDB$SCHEMA_NAME); + indexName = QualifiedName(IND.RDB$INDEX_NAME, IND.RDB$SCHEMA_NAME, IND.RDB$PACKAGE_NAME); fb_assert (!indexName.isEmpty()); if (!IND.RDB$FOREIGN_KEY.NULL) @@ -15715,6 +15936,7 @@ static bool checkObjectExist(thread_db* tdbb, jrd_tra* transaction, const Qualif FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) X IN RDB$RELATIONS WITH X.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND X.RDB$RELATION_NAME EQ name.object.c_str() { rc = (type != obj_view) || !X.RDB$VIEW_BLR.NULL; @@ -15764,6 +15986,7 @@ static bool checkFieldExist(thread_db* tdbb, jrd_tra* transaction, const Qualifi FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) X IN RDB$RELATION_FIELDS WITH X.RDB$SCHEMA_NAME EQ relation.schema.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(relation.package.c_str(), '') AND X.RDB$RELATION_NAME EQ relation.object.c_str() AND X.RDB$FIELD_NAME EQ field.c_str() { @@ -16310,6 +16533,8 @@ void GrantRevokeNode::checkGrantorCanGrantRelation(thread_db* tdbb, jrd_tra* tra const char* grantor, const char* privilege, const QualifiedName& relationName, const MetaName& fieldName, bool topLevel) { + fb_assert(relationName.package.isEmpty()); + const Attachment* attachment = tdbb->getAttachment(); // Verify that the input relation exists. @@ -16493,6 +16718,7 @@ void GrantRevokeNode::checkGrantorCanGrantRelation(thread_db* tdbb, jrd_tra* tra CROSS G_VIEW IN RDB$VIEW_RELATIONS WITH G_FLD.RDB$SCHEMA_NAME = relationName.schema.c_str() AND G_FLD.RDB$RELATION_NAME = relationName.object.c_str() AND + G_FLD.RDB$PACKAGE_NAME MISSING AND G_FLD.RDB$BASE_FIELD NOT MISSING AND G_VIEW.RDB$SCHEMA_NAME EQ G_FLD.RDB$SCHEMA_NAME AND G_VIEW.RDB$VIEW_NAME EQ G_FLD.RDB$RELATION_NAME AND @@ -16503,14 +16729,16 @@ void GrantRevokeNode::checkGrantorCanGrantRelation(thread_db* tdbb, jrd_tra* tra if (fieldName == G_FLD.RDB$FIELD_NAME) { checkGrantorCanGrantRelation(tdbb, transaction, grantor, privilege, - QualifiedName(G_VIEW.RDB$RELATION_NAME, G_VIEW.RDB$RELATION_SCHEMA_NAME), + QualifiedName(G_VIEW.RDB$RELATION_NAME, G_VIEW.RDB$RELATION_SCHEMA_NAME, + G_VIEW.RDB$PACKAGE_NAME), G_FLD.RDB$BASE_FIELD, false); } } else { checkGrantorCanGrantRelation(tdbb, transaction, grantor, privilege, - QualifiedName(G_VIEW.RDB$RELATION_NAME, G_VIEW.RDB$RELATION_SCHEMA_NAME), + QualifiedName(G_VIEW.RDB$RELATION_NAME, G_VIEW.RDB$RELATION_SCHEMA_NAME, + G_VIEW.RDB$PACKAGE_NAME), G_FLD.RDB$BASE_FIELD, false); } } diff --git a/src/dsql/DdlNodes.h b/src/dsql/DdlNodes.h index 2d28700012b..36a67742822 100644 --- a/src/dsql/DdlNodes.h +++ b/src/dsql/DdlNodes.h @@ -52,6 +52,7 @@ enum SqlSecurity class LocalDeclarationsNode; class LocalTemporaryTable; class RelationSourceNode; +class CreateIndexNode; class ValueListNode; class SecDbContext; class ModifyIndexList; @@ -418,7 +419,7 @@ class CreateAlterFunctionNode final : public DdlNode compiled(false), invalid(false), packageOwner(pool), - privateScope(false), + packagePrivate(false), preserveDefaults(false), udfReturnPos(0) { @@ -473,7 +474,7 @@ class CreateAlterFunctionNode final : public DdlNode bool compiled; bool invalid; MetaName packageOwner; - bool privateScope; + bool packagePrivate; bool preserveDefaults; SLONG udfReturnPos; std::optional ssDefiner; @@ -572,7 +573,7 @@ class CreateAlterProcedureNode final : public DdlNode compiled(false), invalid(false), packageOwner(pool), - privateScope(false), + packagePrivate(false), preserveDefaults(false) { } @@ -620,7 +621,7 @@ class CreateAlterProcedureNode final : public DdlNode bool compiled; bool invalid; MetaName packageOwner; - bool privateScope; + bool packagePrivate; bool preserveDefaults; std::optional ssDefiner; @@ -1462,7 +1463,8 @@ class RelationNode : public DdlNode TYPE_DROP_COLUMN, TYPE_DROP_CONSTRAINT, TYPE_ALTER_SQL_SECURITY, - TYPE_ALTER_PUBLICATION + TYPE_ALTER_PUBLICATION, + TYPE_ADD_PACKAGE_LTT_INDEX }; explicit Clause(MemoryPool& p, Type aType) noexcept @@ -1490,6 +1492,17 @@ class RelationNode : public DdlNode unsigned deleteAction; }; + struct AddPackageLttIndexClause : public Clause + { + explicit AddPackageLttIndexClause(MemoryPool& p, CreateIndexNode* aIndexNode) + : Clause(p, TYPE_ADD_PACKAGE_LTT_INDEX), + indexNode(aIndexNode) + { + } + + NestConst indexNode; + }; + struct AddConstraintClause : public Clause { enum ConstraintType @@ -1659,8 +1672,6 @@ class RelationNode : public DdlNode static bool checkDeletedId(thread_db* tdbb, MetaId& relId); static bool checkIdRange(thread_db* tdbb, MetaId& relId, const MetaId existingRelationId); - USHORT calcDbKeyLength(thread_db* tdbb); - static bool deleteLocalField(thread_db* tdbb, jrd_tra* transaction, const QualifiedName& relationName, const MetaName& fieldName, bool silent, std::function preChangeHandler = {}); @@ -1674,6 +1685,7 @@ class RelationNode : public DdlNode DdlNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) override; protected: + static void validateLttClauses(const Firebird::Array>& clauses); static void validateLttColumnClause(const AddColumnClause* addColumnClause); Firebird::string internalPrint(NodePrinter& printer) const override @@ -1731,6 +1743,11 @@ class RelationNode : public DdlNode static void getArrayDesc(thread_db* tdbb, const TEXT* field_name, Ods::InternalArrayDesc* desc); public: + bool isCreatedLtt() const + { + return tempFlag == REL_temp_ltt && name.package.isEmpty(); + } + NestConst dsqlNode; QualifiedName name; Firebird::Array > clauses; @@ -1773,7 +1790,7 @@ class CreateRelationNode final : public RelationNode bool disallowedInReadOnlyDatabase() const override { - return tempFlag != REL_temp_ltt; + return !isCreatedLtt(); } protected: @@ -1789,6 +1806,7 @@ class CreateRelationNode final : public RelationNode public: const Firebird::string* externalFile; bool createIfNotExistsOnly = false; + bool packagePrivate = false; }; @@ -2046,9 +2064,8 @@ class CreateIndexNode final : public DdlNode return false; // Deferred to execute() - LTT status unknown at parse time } -private: void defineLocalTempIndex(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, - jrd_tra* transaction, LocalTemporaryTable* ltt); + jrd_tra* transaction, LocalTemporaryTable* ltt) const; protected: void putErrorPrefix(Firebird::Arg::StatusVector& statusVector) override diff --git a/src/dsql/DsqlCompilerScratch.cpp b/src/dsql/DsqlCompilerScratch.cpp index e1dc329de0e..798cb91a253 100644 --- a/src/dsql/DsqlCompilerScratch.cpp +++ b/src/dsql/DsqlCompilerScratch.cpp @@ -141,7 +141,7 @@ std::variant DsqlCompilerScratc } } - // search packaged routine in the same package: name, same_package.name + // search packaged routine or declared LTT in the same package: name, same_package.name if (notFound() && package.object.hasData() && name.package.isEmpty() && @@ -153,6 +153,9 @@ std::variant DsqlCompilerScratc if (searchProcedures) setObject(METD_get_procedure(getTransaction(), this, routineName)); + if (searchRelations) + setObject(METD_get_relation(getTransaction(), this, routineName)); + if (searchFunctions) setObject(METD_get_function(getTransaction(), this, routineName)); } @@ -174,7 +177,7 @@ std::variant DsqlCompilerScratc setObject(METD_get_function(getTransaction(), this, qualifiedName)); } - // search packaged routine: name1%package.name2, name1.name2.name3 + // search packaged routine or declared LTT: name1%package.name2, name1.name2.name3 if (notFound() && name.package.hasData()) { @@ -184,11 +187,14 @@ std::variant DsqlCompilerScratc if (searchProcedures) setObject(METD_get_procedure(getTransaction(), this, qualifiedName)); + if (searchRelations) + setObject(METD_get_relation(getTransaction(), this, qualifiedName)); + if (searchFunctions) setObject(METD_get_function(getTransaction(), this, qualifiedName)); } - // search packaged routine: name1.name2 + // search packaged routine or LTT: name1.name2 if (notFound() && !name.isUnambiguous() && name.schema.hasData() && @@ -200,6 +206,9 @@ std::variant DsqlCompilerScratc if (searchProcedures) setObject(METD_get_procedure(getTransaction(), this, qualifiedName)); + if (searchRelations) + setObject(METD_get_relation(getTransaction(), this, qualifiedName)); + if (searchFunctions) setObject(METD_get_function(getTransaction(), this, qualifiedName)); } diff --git a/src/dsql/DsqlCompilerScratch.h b/src/dsql/DsqlCompilerScratch.h index bf0c0ac1fd2..c276003affe 100644 --- a/src/dsql/DsqlCompilerScratch.h +++ b/src/dsql/DsqlCompilerScratch.h @@ -76,7 +76,7 @@ class DsqlCompilerScratch : public BlrDebugWriter static const unsigned FLAG_FETCH = 0x4000; static const unsigned FLAG_VIEW_WITH_CHECK = 0x8000; static const unsigned FLAG_EXEC_BLOCK = 0x010000; - static const unsigned FLAG_ALLOW_LTT_REFERENCES = 0x020000; + static const unsigned FLAG_ALLOW_CREATED_LTT_REFERENCE = 0x020000; static const unsigned FLAG_USING_STATEMENT = 0x040000; static const unsigned FLAG_ACTUAL_LTT_DDL = 0x080000; diff --git a/src/dsql/PackageNodes.epp b/src/dsql/PackageNodes.epp index 42f888c9cb0..838b11d799f 100644 --- a/src/dsql/PackageNodes.epp +++ b/src/dsql/PackageNodes.epp @@ -53,11 +53,21 @@ DATABASE DB = STATIC "ODS.RDB"; namespace { + template + [[noreturn]] void raiseDuplicatePackageItem(const char* type, const T& name) + { + status_exception::raise( + Arg::Gds(isc_no_meta_update) << + Arg::Gds(isc_dyn_duplicate_package_item) << + Arg::Str(type) << name.toQuotedString()); + } + // 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) + SortedObjectsArray& procedures, + SortedObjectsArray& tables, bool details) { AutoCacheRequest requestHandle(tdbb, drq_l_pkg_funcs, DYN_REQUESTS); AutoCacheRequest requestHandle2(tdbb, drq_l_pkg_func_args, DYN_REQUESTS); @@ -197,6 +207,19 @@ namespace procedures.add(procedure); } END_FOR + + static const CachedRequestId requestTableCacheId; + AutoCacheRequest requestTable(tdbb, requestTableCacheId); + + FOR (REQUEST_HANDLE requestTable TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS + WITH REL.RDB$SCHEMA_NAME EQ packageName.schema.c_str() AND + REL.RDB$PACKAGE_NAME EQ packageName.object.c_str() + { + const MetaName tableName(REL.RDB$RELATION_NAME); + tables.add(tableName); + } + END_FOR } } // namespace @@ -250,12 +273,7 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) 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()); - } + raiseDuplicatePackageItem("FUNCTION", fun->name.object); functionNames.add(fun->name.object); @@ -270,13 +288,8 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) 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()); - } + if (procedureNames.exist(proc->name.object) || tableNames.exist(proc->name.object)) + raiseDuplicatePackageItem("PROCEDURE", proc->name.object); procedureNames.add(proc->name.object); @@ -286,6 +299,21 @@ DdlNode* CreateAlterPackageNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) break; } + case CreateAlterPackageNode::Item::TABLE: + { + CreateRelationNode* const table = (*items)[i].table; + ddlNode = table; + + if (tableNames.exist(table->name.object) || procedureNames.exist(table->name.object)) + raiseDuplicatePackageItem("TABLE", table->name.object); + + tableNames.add(table->name.object); + + table->name.schema = name.schema; + table->name.package = name.object; + break; + } + default: fb_assert(false); } @@ -444,30 +472,37 @@ bool CreateAlterPackageNode::executeAlter(thread_db* tdbb, DsqlCompilerScratch* SortedObjectsArray existingFuncs(pool); SortedObjectsArray existingProcs(pool); - collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false); + SortedObjectsArray existingTables(pool); + collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, existingTables, false); - for (SortedObjectsArray::iterator i = existingFuncs.begin(); - i != existingFuncs.end(); ++i) + for (const auto& signature : existingFuncs) { - if (!functionNames.exist(i->name)) + if (!functionNames.exist(signature.name)) { - DropFunctionNode dropNode(pool, QualifiedName(i->name, name.schema, name.object)); + DropFunctionNode dropNode(pool, QualifiedName(signature.name, name.schema, name.object)); dropNode.dsqlPass(dsqlScratch); dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); } } - for (SortedObjectsArray::iterator i = existingProcs.begin(); - i != existingProcs.end(); ++i) + for (const auto& signature : existingProcs) { - if (!procedureNames.exist(i->name)) + if (!procedureNames.exist(signature.name)) { - DropProcedureNode dropNode(pool, QualifiedName(i->name, name.schema, name.object)); + DropProcedureNode dropNode(pool, QualifiedName(signature.name, name.schema, name.object)); dropNode.dsqlPass(dsqlScratch); dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); } } + // Tables are always recreated + for (const auto& existingName : existingTables) + { + DropRelationNode dropNode(pool, QualifiedName(existingName, name.schema, name.object)); + dropNode.dsqlPass(dsqlScratch); + dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); + } + MODIFY PKG PKG.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE; attachment->storeMetaDataBlob(tdbb, transaction, &PKG.RDB$PACKAGE_HEADER_SOURCE, @@ -556,6 +591,10 @@ void CreateAlterPackageNode::executeItems(thread_db* tdbb, DsqlCompilerScratch* (*items)[i].procedure->packageOwner = owner; (*items)[i].procedure->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true); break; + + case Item::TABLE: + (*items)[i].table->executeDdl(tdbb, (*items)[i].dsqlScratch, transaction, true); + break; } } } @@ -608,6 +647,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_body, &nameDesc, &schemaDesc, 0); DFW_post_work(transaction, dfw_drop_package_header, &nameDesc, &schemaDesc, 0); } END_FOR @@ -621,20 +661,26 @@ void DropPackageNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, SortedObjectsArray existingFuncs(pool); SortedObjectsArray existingProcs(pool); - collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, false); + SortedObjectsArray existingTables(pool); + collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, existingTables, false); - for (SortedObjectsArray::iterator i = existingFuncs.begin(); - i != existingFuncs.end(); ++i) + for (const auto& signature : existingFuncs) { - DropFunctionNode dropNode(pool, QualifiedName(i->name, name.schema, name.object)); + DropFunctionNode dropNode(pool, QualifiedName(signature.name, name.schema, name.object)); dropNode.dsqlPass(dsqlScratch); dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); } - for (SortedObjectsArray::iterator i = existingProcs.begin(); - i != existingProcs.end(); ++i) + for (const auto& signature : existingProcs) { - DropProcedureNode dropNode(pool, QualifiedName(i->name, name.schema, name.object)); + DropProcedureNode dropNode(pool, QualifiedName(signature.name, name.schema, name.object)); + dropNode.dsqlPass(dsqlScratch); + dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); + } + + for (const auto& existingName : existingTables) + { + DropRelationNode dropNode(pool, QualifiedName(existingName, name.schema, name.object)); dropNode.dsqlPass(dsqlScratch); dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); } @@ -692,6 +738,7 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) Array* arrays[] = {declaredItems, items}; SortedArray functionNames[FB_NELEM(arrays)]; SortedArray procedureNames[FB_NELEM(arrays)]; + SortedArray tableNames[FB_NELEM(arrays)]; for (unsigned i = 0; i < FB_NELEM(arrays); ++i) { @@ -710,12 +757,7 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) 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()); - } + raiseDuplicatePackageItem("FUNCTION", fun->name.object); functionNames[i].add(fun->name.object); @@ -734,13 +776,8 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) 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()); - } + if (procedureNames[i].exist(proc->name.object) || tableNames[i].exist(proc->name.object)) + raiseDuplicatePackageItem("PROCEDURE", proc->name.object); procedureNames[i].add(proc->name.object); @@ -754,6 +791,22 @@ DdlNode* CreatePackageBodyNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) break; } + case CreateAlterPackageNode::Item::TABLE: + { + CreateRelationNode* const table = (*arrays[i])[j].table; + ddlNode = table; + + if (tableNames[i].exist(table->name.object) || procedureNames[i].exist(table->name.object)) + raiseDuplicatePackageItem("TABLE", table->name.object); + + tableNames[i].add(table->name.object); + + table->name.schema = name.schema; + table->name.package = name.object; + + break; + } + default: fb_assert(false); } @@ -839,10 +892,12 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc SortedObjectsArray headerFuncs(pool); SortedObjectsArray headerProcs(pool); - collectPackagedItems(tdbb, transaction, name, headerFuncs, headerProcs, false); + SortedObjectsArray headerTables(pool); + collectPackagedItems(tdbb, transaction, name, headerFuncs, headerProcs, headerTables, false); SortedObjectsArray existingFuncs(pool); SortedObjectsArray existingProcs(pool); + SortedObjectsArray existingTables(pool); // process declaredItems and items Array* arrays[] = {declaredItems, items}; @@ -856,9 +911,10 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc { existingFuncs.clear(); existingProcs.clear(); + existingTables.clear(); } - collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, true); + collectPackagedItems(tdbb, transaction, name, existingFuncs, existingProcs, existingTables, true); for (unsigned j = 0; j < arrays[i]->getCount(); ++j) { @@ -871,14 +927,9 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc CreateAlterFunctionNode* func = elem.function; if (arrays[i] == items) - func->privateScope = !headerFuncs.exist(Signature(func->name.object)); + func->packagePrivate = !headerFuncs.exist(Signature(func->name.object)); else if (existingFuncs.exist(Signature(func->name.object))) - { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_duplicate_package_item) << - Arg::Str("FUNCTION") << func->name.toQuotedString()); - } + raiseDuplicatePackageItem("FUNCTION", func->name); func->packageOwner = owner; func->preserveDefaults = @@ -890,16 +941,18 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc case CreateAlterPackageNode::Item::PROCEDURE: { CreateAlterProcedureNode* proc = elem.procedure; + const bool conflictsWithTable = + headerTables.exist(proc->name.object) || existingTables.exist(proc->name.object); if (arrays[i] == items) - proc->privateScope = !headerProcs.exist(Signature(proc->name.object)); - else if (existingProcs.exist(Signature(proc->name.object))) { - status_exception::raise( - Arg::Gds(isc_no_meta_update) << - Arg::Gds(isc_dyn_duplicate_package_item) << - Arg::Str("PROCEDURE") << proc->name.toQuotedString()); + if (conflictsWithTable) + raiseDuplicatePackageItem("PROCEDURE", proc->name); + + proc->packagePrivate = !headerProcs.exist(Signature(proc->name.object)); } + else if (existingProcs.exist(Signature(proc->name.object)) || conflictsWithTable) + raiseDuplicatePackageItem("PROCEDURE", proc->name); proc->packageOwner = owner; proc->preserveDefaults = @@ -907,13 +960,40 @@ void CreatePackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlSc proc->executeDdl(tdbb, elem.dsqlScratch, transaction, true); break; } + + case CreateAlterPackageNode::Item::TABLE: + { + CreateRelationNode* table = elem.table; + const bool conflictsWithProcedure = + headerProcs.exist(Signature(table->name.object)) || + existingProcs.exist(Signature(table->name.object)); + + if (arrays[i] == items) + { + if (conflictsWithProcedure) + raiseDuplicatePackageItem("TABLE", table->name); + + table->packagePrivate = !headerTables.exist(table->name.object); + } + else + { + table->packagePrivate = true; + + if (existingTables.exist(table->name.object) || conflictsWithProcedure) + raiseDuplicatePackageItem("TABLE", table->name); + } + + table->executeDdl(tdbb, elem.dsqlScratch, transaction, true); + break; + } } } } SortedObjectsArray newFuncs(pool); SortedObjectsArray newProcs(pool); - collectPackagedItems(tdbb, transaction, name, newFuncs, newProcs, true); + SortedObjectsArray newTables(pool); + collectPackagedItems(tdbb, transaction, name, newFuncs, newProcs, newTables, true); for (SortedObjectsArray::iterator i = existingFuncs.begin(); i != existingFuncs.end(); ++i) @@ -1075,6 +1155,21 @@ void DropPackageBodyNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra } END_FOR + static const CachedRequestId requestTableCacheId; + AutoCacheRequest requestTable(tdbb, requestTableCacheId); + + FOR (REQUEST_HANDLE requestTable TRANSACTION_HANDLE transaction) + REL IN RDB$RELATIONS + WITH REL.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + REL.RDB$PACKAGE_NAME EQ name.object.c_str() AND + REL.RDB$PRIVATE_FLAG != 0 + { + DropRelationNode dropNode(pool, QualifiedName(REL.RDB$RELATION_NAME, name.schema, name.object)); + dropNode.dsqlPass(dsqlScratch); + dropNode.executeDdl(tdbb, dsqlScratch, transaction, true); + } + END_FOR + 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..ec2cb5ba8f3 100644 --- a/src/dsql/PackageNodes.h +++ b/src/dsql/PackageNodes.h @@ -39,7 +39,6 @@ class CreateAlterPackageNode : public DdlNode Item item; item.type = FUNCTION; item.function = function; - item.dsqlScratch = NULL; return item; } @@ -48,35 +47,42 @@ class CreateAlterPackageNode : public DdlNode Item item; item.type = PROCEDURE; item.procedure = procedure; - item.dsqlScratch = NULL; + return item; + } + + static Item create(CreateRelationNode* table) + { + Item item; + item.type = TABLE; + item.table = table; return item; } enum { FUNCTION, - PROCEDURE + PROCEDURE, + TABLE } type; union { CreateAlterFunctionNode* function; CreateAlterProcedureNode* procedure; + CreateRelationNode* table; }; - DsqlCompilerScratch* dsqlScratch; + DsqlCompilerScratch* dsqlScratch = nullptr; }; public: CreateAlterPackageNode(MemoryPool& pool, const QualifiedName& aName) : DdlNode(pool), name(pool, aName), - create(true), - alter(false), source(pool), - items(NULL), functionNames(pool), procedureNames(pool), + tableNames(pool), owner(pool) { } @@ -105,13 +111,14 @@ class CreateAlterPackageNode : public DdlNode public: QualifiedName name; - bool create; - bool alter; + bool create = true; + bool alter = false; bool createIfNotExistsOnly = false; Firebird::string source; - Firebird::Array* items; + Firebird::Array* items = nullptr; Firebird::SortedArray functionNames; Firebird::SortedArray procedureNames; + Firebird::SortedArray tableNames; std::optional ssDefiner; private: diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index 2beb3594acb..4ed13e48869 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -1357,7 +1357,8 @@ dsql_rel::dsql_rel(MemoryPool& p, jrd_rel* jrel) rel_owner(p, jrel->getOwnerName()), rel_id(jrel->getId()), rel_dbkey_length(jrel->rel_dbkey_length), - rel_flags((jrel->getExtFile() ? REL_external : 0) | (jrel->isView() ? REL_view : 0)) + rel_flags((jrel->getExtFile() ? REL_external : 0) | (jrel->isView() ? REL_view : 0)), + rel_private(jrel->isPrivate()) { if (!(jrel->rel_fields)) return; diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index c81cf09dff2..de0799f02fe 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -159,6 +159,7 @@ class dsql_rel : public pool_alloc USHORT rel_id = 0; // Relation id USHORT rel_dbkey_length = 0; USHORT rel_flags = 0; + bool rel_private = false; // Packaged private relation }; // rel_flags bits diff --git a/src/dsql/metd.epp b/src/dsql/metd.epp index 60827137d0e..5326b46b395 100644 --- a/src/dsql/metd.epp +++ b/src/dsql/metd.epp @@ -599,6 +599,9 @@ void METD_get_primary_key(jrd_tra* transaction, const QualifiedName& relationNam * node of the fields. * **************************************/ + if (relationName.package.hasData()) + return; + thread_db* tdbb = JRD_get_thread_data(); MemoryPool& pool = *tdbb->getDefaultPool(); @@ -611,6 +614,8 @@ void METD_get_primary_key(jrd_tra* transaction, const QualifiedName& relationNam CROSS Y IN RDB$INDEX_SEGMENTS OVER RDB$SCHEMA_NAME, RDB$INDEX_NAME CROSS Z IN RDB$RELATION_CONSTRAINTS OVER RDB$SCHEMA_NAME, RDB$INDEX_NAME WITH Z.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND + X.RDB$PACKAGE_NAME MISSING AND + Y.RDB$PACKAGE_NAME MISSING AND Z.RDB$RELATION_NAME EQ relationName.object.c_str() AND Z.RDB$CONSTRAINT_TYPE EQ "PRIMARY KEY" SORTED BY Y.RDB$FIELD_POSITION @@ -730,7 +735,7 @@ dsql_rel* METD_get_relation(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat if (ltt) { if ((dsqlScratch->flags & DsqlCompilerScratch::FLAG_DDL) && - !(dsqlScratch->flags & DsqlCompilerScratch::FLAG_ALLOW_LTT_REFERENCES)) + !(dsqlScratch->flags & DsqlCompilerScratch::FLAG_ALLOW_CREATED_LTT_REFERENCE)) { // LTTs cannot be referenced in persistent metadata (DDL other than LTT DDL itself) status_exception::raise(Arg::Gds(isc_dsql_ltt_invalid_reference) @@ -861,6 +866,13 @@ dsql_rel* METD_get_view_base(jrd_tra* transaction, DsqlCompilerScratch* dsqlScra while (cont) { + if (nextViewName.package.hasData()) + { + // Views cannot be defined inside packages, so packaged relations terminate recursion. + relation = nullptr; + break; + } + AutoCacheRequest handle1(tdbb, irq_view_base, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE transaction) @@ -876,7 +888,8 @@ dsql_rel* METD_get_view_base(jrd_tra* transaction, DsqlCompilerScratch* dsqlScra break; } - nextViewName = QualifiedName(X.RDB$RELATION_NAME, X.RDB$RELATION_SCHEMA_NAME); + nextViewName = QualifiedName(X.RDB$RELATION_NAME, X.RDB$RELATION_SCHEMA_NAME, + X.RDB$PACKAGE_NAME); relation = METD_get_relation(transaction, dsqlScratch, nextViewName); Array ambiguities; @@ -893,6 +906,7 @@ dsql_rel* METD_get_view_base(jrd_tra* transaction, DsqlCompilerScratch* dsqlScra FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction) RFL IN RDB$RELATION_FIELDS WITH RFL.RDB$SCHEMA_NAME EQ X.RDB$SCHEMA_NAME AND + RFL.RDB$PACKAGE_NAME MISSING AND RFL.RDB$RELATION_NAME EQ X.RDB$VIEW_NAME { if (RFL.RDB$BASE_FIELD.NULL || RFL.RDB$FIELD_NAME.NULL) @@ -973,6 +987,7 @@ bool METD_get_view_relation(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat thread_db* tdbb = JRD_get_thread_data(); validateTransaction(transaction); + fb_assert(view_name.package.isEmpty()); AutoCacheRequest handle(tdbb, irq_view, IRQ_REQUESTS); @@ -981,7 +996,8 @@ bool METD_get_view_relation(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat WITH X.RDB$SCHEMA_NAME EQ view_name.schema.c_str() AND X.RDB$VIEW_NAME EQ view_name.object.c_str() { - QualifiedName relationName(X.RDB$RELATION_NAME, X.RDB$RELATION_SCHEMA_NAME); + QualifiedName relationName(X.RDB$RELATION_NAME, X.RDB$RELATION_SCHEMA_NAME, + X.RDB$PACKAGE_NAME); ObjectsArray contextName; try @@ -1009,8 +1025,13 @@ bool METD_get_view_relation(jrd_tra* transaction, DsqlCompilerScratch* dsqlScrat return true; } - if (METD_get_view_relation(transaction, dsqlScratch, relationName, relation_or_alias, relation, procedure)) + // Views cannot be defined inside packages, so only package-less relations can recurse. + if (relationName.package.isEmpty() && + METD_get_view_relation(transaction, dsqlScratch, relationName, relation_or_alias, + relation, procedure)) + { return true; + } } END_FOR @@ -1054,4 +1075,3 @@ static void convert_dtype(TypeClause* field, SSHORT field_type) fb_assert(field->dtype != dtype_unknown); } } - diff --git a/src/dsql/parse-conflicts.txt b/src/dsql/parse-conflicts.txt index 661bcbe76e8..20b7367edcc 100644 --- a/src/dsql/parse-conflicts.txt +++ b/src/dsql/parse-conflicts.txt @@ -1 +1 @@ -137 shift/reduce conflicts, 7 reduce/reduce conflicts. +138 shift/reduce conflicts, 10 reduce/reduce conflicts. diff --git a/src/dsql/parse.y b/src/dsql/parse.y index a8b4786d33d..0d9d3b82172 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -2468,6 +2468,45 @@ ltt_table_clause } ; +%type package_ltt_table_clause +package_ltt_table_clause + : simple_table_name + { + $$ = newNode($1); + $$->tempFlag = REL_temp_ltt; + } + '(' table_elements($2) ')' ltt_subclause_opt($2) package_ltt_indexes_opt($2) + { + $$ = $2; + } + ; + +%type package_ltt_indexes_opt() +package_ltt_indexes_opt($createRelationNode) + : /* nothing */ + | package_ltt_indexes($createRelationNode) + ; + +%type package_ltt_indexes() +package_ltt_indexes($createRelationNode) + : package_ltt_index($createRelationNode) + | package_ltt_indexes($createRelationNode) ',' package_ltt_index($createRelationNode) + ; + +%type package_ltt_index() +package_ltt_index($createRelationNode) + : unique_opt order_direction INDEX valid_symbol_name column_parens + { + const auto node = newNode(QualifiedName(*$4)); + node->unique = $1; + node->descending = $2; + node->columns = $5; + + auto clause = newNode(node); + $createRelationNode->clauses.add(clause); + } + ; + %type ltt_subclause_opt() ltt_subclause_opt($createRelationNode) : // nothing by default. Will be set "on commit delete rows" in dsqlPass @@ -3175,6 +3214,8 @@ package_item { $$ = CreateAlterPackageNode::Item::create($2); } | PROCEDURE procedure_clause_start ';' { $$ = CreateAlterPackageNode::Item::create($2); } + | DECLARE LOCAL TEMPORARY TABLE package_ltt_table_clause ';' + { $$ = CreateAlterPackageNode::Item::create($5); } ; %type alter_package_clause @@ -6244,11 +6285,11 @@ set_statistics comment : COMMENT ON ddl_type0 IS ddl_desc { $$ = newNode($3, QualifiedName(), "", *$5); } - | COMMENT ON ddl_type1_schema symbol_ddl_name IS ddl_desc + | COMMENT ON ddl_type1_schema scoped_qualified_name IS ddl_desc { $$ = newNode($3, *$4, "", *$6); } | COMMENT ON ddl_type1_noschema valid_symbol_name IS ddl_desc { $$ = newNode($3, QualifiedName(*$4), "", *$6); } - | COMMENT ON COLUMN symbol_ddl_name '.' valid_symbol_name IS ddl_desc + | COMMENT ON COLUMN scoped_qualified_name '.' valid_symbol_name IS ddl_desc { $$ = newNode(obj_relation, *$4, *$6, *$8); } | COMMENT ON ddl_type3 scoped_qualified_name '.' valid_symbol_name IS ddl_desc { $$ = newNode($3, *$4, *$6, *$8); } diff --git a/src/dsql/pass1.cpp b/src/dsql/pass1.cpp index cad9b6cc039..ec1b1dbc974 100644 --- a/src/dsql/pass1.cpp +++ b/src/dsql/pass1.cpp @@ -386,7 +386,7 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* else { const auto resolvedObject = dsqlScratch->resolveRoutineOrRelation(name, - ((name.package.hasData() || (procNode && procNode->inputSources)) ? + (((procNode && procNode->inputSources)) ? std::initializer_list{obj_procedure} : std::initializer_list{obj_procedure, obj_relation})); @@ -428,8 +428,19 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* procNode->dsqlName = name; } - else if (relNode) - relNode->dsqlName = name; + else if (relation) + { + if (relation->rel_private && name.getSchemaAndPackage() != dsqlScratch->package) + { + status_exception::raise( + Arg::Gds(isc_private_table) << + name.object.toQuotedString() << + name.getSchemaAndPackage().toQuotedString()); + } + + if (relNode) + relNode->dsqlName = name; + } } // Set up context block. diff --git a/src/include/firebird/impl/msg/jrd.h b/src/include/firebird/impl/msg/jrd.h index 4bcafc9d62a..8420765665e 100644 --- a/src/include/firebird/impl/msg/jrd.h +++ b/src/include/firebird/impl/msg/jrd.h @@ -1005,3 +1005,4 @@ 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, private_table, -901, "42", "000", "Table @1 is private to package @2") diff --git a/src/include/gen/Firebird.pas b/src/include/gen/Firebird.pas index e1d0fd182fe..b5bd8c6e49b 100644 --- a/src/include/gen/Firebird.pas +++ b/src/include/gen/Firebird.pas @@ -5960,6 +5960,7 @@ IPerformanceStatsImpl = class(IPerformanceStats) isc_percetile_only_one_sort_item = 335545323; isc_argmustbe_const_within_group = 335545324; isc_update_overwrite = 335545325; + isc_private_table = 335545326; isc_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; isc_gfix_incmp_sw = 335740932; diff --git a/src/isql/FrontendParser.cpp b/src/isql/FrontendParser.cpp index 64218057132..06be7897e3f 100644 --- a/src/isql/FrontendParser.cpp +++ b/src/isql/FrontendParser.cpp @@ -501,11 +501,11 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() { const auto& text = showCommandToken.processedText; - if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_CHECKS, 5)) + if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_CHECKS, 5, false)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_COLLATES, 7)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_COLLATES, 7, false)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_COLLATIONS, 9)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_COLLATIONS, 9, false)) return parsed.value(); else if (text.length() >= 7 && TOKEN_COMMENTS.find(text) == 0) { @@ -517,44 +517,32 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() if (parseEof()) return ShowDatabaseNode(); } - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_DEPENDENCIES, 5)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_DEPENDENCIES, 5, false)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_DEPENDENCY, 5)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_DEPENDENCY, 5, false)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_DOMAINS, 6)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_DOMAINS, 6, false)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_EXCEPTIONS, 5)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_EXCEPTIONS, 5, false)) return parsed.value(); else if (const auto parsed = parseShowOptName(text, TOKEN_FILTERS, 6)) return parsed.value(); - else if (text.length() >= 4 && TOKEN_FUNCTIONS.find(text) == 0) - { - ShowFunctionsNode node; - node.name = parseQualifiedName(true); - - if (parseEof()) - return node; - } - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_INDEXES, 3)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_FUNCTIONS, 4, true)) + return parsed.value(); + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_INDEXES, 3, false)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_INDICES, 0)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_INDICES, 0, false)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_GENERATORS, 3)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_GENERATORS, 3, false)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_GRANTS, 5)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_GRANTS, 5, false)) return parsed.value(); else if (const auto parsed = parseShowOptName(text, TOKEN_MAPPINGS, 3)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_PACKAGES, 4)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_PACKAGES, 4, false)) + return parsed.value(); + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_PROCEDURES, 4, true)) return parsed.value(); - else if (text.length() >= 4 && TOKEN_PROCEDURES.find(text) == 0) - { - ShowProceduresNode node; - node.name = parseQualifiedName(true); - - if (parseEof()) - return node; - } else if (const auto parsed = parseShowOptName(text, TOKEN_PUBLICATIONS, 3)) return parsed.value(); else if (const auto parsed = parseShowOptName(text, TOKEN_ROLES, 4)) @@ -586,7 +574,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() if (parseEof()) return node; } - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_SEQUENCES, 3)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_SEQUENCES, 3, false)) return parsed.value(); else if (text == TOKEN_SQL) { @@ -633,9 +621,9 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() return node; } - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_TABLES, 5)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_TABLES, 5, true)) return parsed.value(); - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_TRIGGERS, 4)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_TRIGGERS, 4, false)) return parsed.value(); else if (text == TOKEN_USERS) { @@ -647,7 +635,7 @@ FrontendParser::AnyShowNode FrontendParser::parseShow() if (parseEof()) return ShowVersionNode(); } - else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_VIEWS, 4)) + else if (const auto parsed = parseShowOptQualifiedName(text, TOKEN_VIEWS, 4, false)) return parsed.value(); else if (text.length() >= 9 && TOKEN_WIRE_STATISTICS.find(text) == 0 || text == TOKEN_WIRE_STATS) @@ -688,14 +676,14 @@ std::optional FrontendParser::parseShowOptName(std: template std::optional FrontendParser::parseShowOptQualifiedName(std::string_view showCommand, - std::string_view testCommand, unsigned testCommandMinLen) + std::string_view testCommand, unsigned testCommandMinLen, bool allowPackage) { if (showCommand == testCommand || (testCommandMinLen && showCommand.length() >= testCommandMinLen && std::string(testCommand).find(showCommand) == 0)) { Node node; - node.name = parseQualifiedName(); + node.name = parseQualifiedName(allowPackage); if (!parseEof()) return InvalidNode(); diff --git a/src/isql/FrontendParser.h b/src/isql/FrontendParser.h index 28a1b9cc3ba..e71e2770993 100644 --- a/src/isql/FrontendParser.h +++ b/src/isql/FrontendParser.h @@ -254,7 +254,7 @@ class FrontendParser template std::optional parseShowOptQualifiedName(std::string_view showCommand, - std::string_view testCommand, unsigned testCommandMinLen = 0); + std::string_view testCommand, unsigned testCommandMinLen, bool allowPackage); bool parseEof() { diff --git a/src/isql/extract.epp b/src/isql/extract.epp index 1e651a52c98..a086b1d9146 100644 --- a/src/isql/extract.epp +++ b/src/isql/extract.epp @@ -341,11 +341,13 @@ int EXTRACT_list_table(const QualifiedMetaString& relation_name, RFR IN RDB$RELATION_FIELDS CROSS FLD IN RDB$FIELDS WITH RFR.RDB$SCHEMA_NAME EQUIV REL.RDB$SCHEMA_NAME AND + RFR.RDB$PACKAGE_NAME MISSING AND RFR.RDB$RELATION_NAME EQ REL.RDB$RELATION_NAME AND FLD.RDB$SCHEMA_NAME EQUIV RFR.RDB$FIELD_SOURCE_SCHEMA_NAME AND FLD.RDB$FIELD_NAME EQ RFR.RDB$FIELD_SOURCE AND REL.RDB$SCHEMA_NAME EQUIV NULLIF(relation_name.schema.c_str(), '') AND - REL.RDB$RELATION_NAME EQ relation_name.object.c_str() + REL.RDB$RELATION_NAME EQ relation_name.object.c_str() AND + REL.RDB$PACKAGE_NAME MISSING SORTED BY RFR.RDB$FIELD_POSITION, RFR.RDB$FIELD_NAME { if (!REL.RDB$RELATION_TYPE.NULL) @@ -656,6 +658,7 @@ static bool extract_rel_constraints(const QualifiedMetaString& relation_name) RELC.RDB$SCHEMA_NAME EQUIV NULLIF(relation_name.schema.c_str(), '') AND RELC.RDB$RELATION_NAME EQ relation_name.object.c_str() AND IDX.RDB$SCHEMA_NAME EQUIV RELC.RDB$SCHEMA_NAME AND + IDX.RDB$PACKAGE_NAME MISSING AND IDX.RDB$INDEX_NAME EQ RELC.RDB$INDEX_NAME SORTED BY RELC.RDB$CONSTRAINT_TYPE, RELC.RDB$CONSTRAINT_NAME { @@ -1535,7 +1538,8 @@ static void list_all_tables(LegacyTables flag, GetDefaultCharSetForSchemaFunc ge FOR REL IN RDB$RELATIONS WITH (REL.RDB$SYSTEM_FLAG NE 1 OR REL.RDB$SYSTEM_FLAG MISSING) AND - REL.RDB$VIEW_BLR MISSING + REL.RDB$VIEW_BLR MISSING AND + REL.RDB$PACKAGE_NAME MISSING SORTED BY REL.RDB$SCHEMA_NAME, REL.RDB$RELATION_NAME { // If this is not an SQL table and we aren't doing ALL objects @@ -2392,10 +2396,12 @@ static void listRelationComputed(LegacyTables flag, GetDefaultCharSetForSchemaFu FLD IN RDB$FIELDS WITH (REL.RDB$SYSTEM_FLAG NE 1 OR REL.RDB$SYSTEM_FLAG MISSING) AND REL.RDB$VIEW_BLR MISSING AND + REL.RDB$PACKAGE_NAME MISSING AND FLD.RDB$COMPUTED_BLR NOT MISSING AND RFR.RDB$FIELD_SOURCE_SCHEMA_NAME EQUIV FLD.RDB$SCHEMA_NAME AND RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND RFR.RDB$SCHEMA_NAME EQUIV REL.RDB$SCHEMA_NAME AND + RFR.RDB$PACKAGE_NAME MISSING AND RFR.RDB$RELATION_NAME EQ REL.RDB$RELATION_NAME SORTED BY REL.RDB$SCHEMA_NAME, REL.RDB$RELATION_NAME, RFR.RDB$FIELD_POSITION, RFR.RDB$FIELD_NAME { @@ -3112,12 +3118,15 @@ static void list_indexes() FOR IDX IN RDB$INDICES CROSS RELC IN RDB$RELATIONS WITH RELC.RDB$SCHEMA_NAME EQUIV IDX.RDB$SCHEMA_NAME AND + RELC.RDB$PACKAGE_NAME MISSING AND RELC.RDB$RELATION_NAME EQ IDX.RDB$RELATION_NAME AND (RELC.RDB$SYSTEM_FLAG NE 1 OR RELC.RDB$SYSTEM_FLAG MISSING) AND + IDX.RDB$PACKAGE_NAME MISSING AND NOT ( ANY RC IN RDB$RELATION_CONSTRAINTS WITH RC.RDB$SCHEMA_NAME EQUIV IDX.RDB$SCHEMA_NAME AND - RC.RDB$INDEX_NAME EQ IDX.RDB$INDEX_NAME) + RC.RDB$INDEX_NAME EQ IDX.RDB$INDEX_NAME + ) SORTED BY IDX.RDB$SCHEMA_NAME, IDX.RDB$RELATION_NAME, IDX.RDB$INDEX_NAME { if (first) @@ -3370,6 +3379,7 @@ static void list_views() FOR REL IN RDB$RELATIONS WITH (REL.RDB$SYSTEM_FLAG NE 1 OR REL.RDB$SYSTEM_FLAG MISSING) AND REL.RDB$VIEW_BLR NOT MISSING AND + REL.RDB$PACKAGE_NAME MISSING AND REL.RDB$FLAGS = REL_sql SORTED BY REL.RDB$SCHEMA_NAME, REL.RDB$RELATION_ID { @@ -3388,6 +3398,7 @@ static void list_views() // Get column list FOR RFR IN RDB$RELATION_FIELDS WITH RFR.RDB$SCHEMA_NAME EQUIV NULLIF(REL.RDB$SCHEMA_NAME, '') AND + RFR.RDB$PACKAGE_NAME MISSING AND RFR.RDB$RELATION_NAME = REL.RDB$RELATION_NAME SORTED BY RFR.RDB$FIELD_POSITION { diff --git a/src/isql/isql.epp b/src/isql/isql.epp index 999bfcab4ed..d980c9236ae 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -1746,6 +1746,7 @@ SLONG ISQL_get_index_segments(TEXT* segs, FOR SEG IN RDB$INDEX_SEGMENTS WITH SEG.RDB$SCHEMA_NAME EQUIV NULLIF(indexname.schema.c_str(), '') AND + SEG.RDB$PACKAGE_NAME EQUIV NULLIF(indexname.package.c_str(), '') AND SEG.RDB$INDEX_NAME EQ indexname.object.c_str() SORTED BY SEG.RDB$FIELD_POSITION { @@ -1826,6 +1827,13 @@ bool ISQL_get_base_column_null_flag(const QualifiedMetaString& view_name, bool error = false; while (!done && !error) { + if (save_view_name.package.hasData()) + { + fb_assert(false); + error = true; + break; + } + bool found = false; FOR FIRST 1 VR IN RDB$VIEW_RELATIONS @@ -1834,6 +1842,7 @@ bool ISQL_get_base_column_null_flag(const QualifiedMetaString& view_name, VR.RDB$VIEW_NAME EQ save_view_name.object.c_str() AND VR.RDB$VIEW_CONTEXT EQ save_view_context AND NEWRFR.RDB$SCHEMA_NAME EQUIV VR.RDB$RELATION_SCHEMA_NAME AND + NEWRFR.RDB$PACKAGE_NAME EQUIV VR.RDB$PACKAGE_NAME AND NEWRFR.RDB$RELATION_NAME = VR.RDB$RELATION_NAME AND NEWRFR.RDB$FIELD_NAME = save_base_field.c_str() { @@ -1847,7 +1856,8 @@ bool ISQL_get_base_column_null_flag(const QualifiedMetaString& view_name, } else { - save_view_name = QualifiedMetaString(NEWRFR.RDB$RELATION_NAME, NEWRFR.RDB$SCHEMA_NAME); + save_view_name = QualifiedMetaString(NEWRFR.RDB$RELATION_NAME, NEWRFR.RDB$SCHEMA_NAME, + NEWRFR.RDB$PACKAGE_NAME); save_view_context = NEWRFR.RDB$VIEW_CONTEXT; save_base_field = NEWRFR.RDB$BASE_FIELD; } @@ -1945,6 +1955,7 @@ bool ISQL_get_null_flag(const QualifiedMetaString& rel_name, WITH RFR.RDB$FIELD_SOURCE_SCHEMA_NAME EQUIV FLD.RDB$SCHEMA_NAME AND RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND RFR.RDB$SCHEMA_NAME EQUIV NULLIF(rel_name.schema.c_str(), '') AND + RFR.RDB$PACKAGE_NAME EQUIV NULLIF(rel_name.package.c_str(), '') AND RFR.RDB$RELATION_NAME EQ rel_name.object.c_str() AND RFR.RDB$FIELD_NAME EQ field_name.c_str() { @@ -1957,7 +1968,6 @@ bool ISQL_get_null_flag(const QualifiedMetaString& rel_name, if (RFR.RDB$BASE_FIELD.NULL) { - // Simple column. Did user define it not null? if (!RFR.RDB$NULL_FLAG.NULL && RFR.RDB$NULL_FLAG == 1) @@ -3974,6 +3984,7 @@ static void col_check(const QualifiedMetaString& tabname, unsigned* colnumber) WITH F.RDB$SCHEMA_NAME EQUIV R.RDB$FIELD_SOURCE_SCHEMA_NAME AND F.RDB$FIELD_NAME = R.RDB$FIELD_SOURCE AND R.RDB$SCHEMA_NAME EQUIV NULLIF(tabname.schema.c_str(), '') AND + R.RDB$PACKAGE_NAME EQUIV NULLIF(tabname.package.c_str(), '') AND R.RDB$RELATION_NAME EQ tabname.object.c_str() SORTED BY R.RDB$FIELD_POSITION, R.RDB$FIELD_NAME { diff --git a/src/isql/show.epp b/src/isql/show.epp index e905cc4de9b..1a67659e887 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -1091,7 +1091,8 @@ processing_state SHOW_grants(const std::optional& name, // Find the user specified relation and show its privileges FOR FIRST 1 R IN RDB$RELATIONS - WITH R.RDB$RELATION_NAME EQ name->object.c_str() + WITH R.RDB$PACKAGE_NAME MISSING AND + R.RDB$RELATION_NAME EQ name->object.c_str() SORTED BY R.RDB$SCHEMA_NAME { if (name->schema.hasData() && name->schema != R.RDB$SCHEMA_NAME) @@ -1107,6 +1108,7 @@ processing_state SHOW_grants(const std::optional& name, PRV.RDB$RELATION_SCHEMA_NAME EQUIV NULLIF(relationName.schema.c_str(), '') AND PRV.RDB$RELATION_NAME EQ relationName.object.c_str() AND REL.RDB$SCHEMA_NAME EQUIV PRV.RDB$RELATION_SCHEMA_NAME AND + REL.RDB$PACKAGE_NAME MISSING AND REL.RDB$RELATION_NAME EQ PRV.RDB$RELATION_NAME AND PRV.RDB$PRIVILEGE NE 'M' AND REL.RDB$OWNER_NAME NE PRV.RDB$USER @@ -2644,8 +2646,8 @@ static processing_state show_all_tables(SSHORT sys_flag) if (sys_flag == -1) { // Views - FOR REL IN RDB$RELATIONS WITH - REL.RDB$VIEW_BLR NOT MISSING + FOR REL IN RDB$RELATIONS + WITH REL.RDB$VIEW_BLR NOT MISSING SORTED BY REL.RDB$SCHEMA_NAME, REL.RDB$RELATION_NAME { first = false; @@ -2664,15 +2666,16 @@ static processing_state show_all_tables(SSHORT sys_flag) { // The rdb$system_flag is not always set for non-system objects... this // query may potentially fail. - FOR REL IN RDB$RELATIONS WITH - (REL.RDB$SYSTEM_FLAG EQ sys_flag - /*OR (sys_flag == 0 AND REL.RDB$SYSTEM_FLAG MISSING)*/) - AND REL.RDB$VIEW_BLR MISSING - SORTED BY REL.RDB$SCHEMA_NAME, REL.RDB$RELATION_NAME + FOR REL IN RDB$RELATIONS + WITH (REL.RDB$SYSTEM_FLAG EQ sys_flag + /*OR (sys_flag == 0 AND REL.RDB$SYSTEM_FLAG MISSING)*/) AND + REL.RDB$VIEW_BLR MISSING + SORTED BY REL.RDB$SCHEMA_NAME, REL.RDB$PACKAGE_NAME, REL.RDB$RELATION_NAME { first = false; - const QualifiedMetaString relationName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME); + const QualifiedMetaString relationName( + REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME, REL.RDB$PACKAGE_NAME); isqlGlob.printf("%s%s", IUTILS_name_to_string(relationName).c_str(), NEWLINE); } @@ -2999,9 +3002,9 @@ static processing_state show_comments(const commentMode showextract, const char* FOR RL IN RDB$RELATIONS WITH RL.RDB$VIEW_BLR MISSING AND (RL.RDB$SYSTEM_FLAG EQ 0 OR RL.RDB$SYSTEM_FLAG MISSING) - SORTED BY RL.RDB$SCHEMA_NAME, RL.RDB$RELATION_NAME + SORTED BY RL.RDB$SCHEMA_NAME, RL.RDB$PACKAGE_NAME, RL.RDB$RELATION_NAME { - const QualifiedMetaString name(RL.RDB$RELATION_NAME, RL.RDB$SCHEMA_NAME); + const QualifiedMetaString name(RL.RDB$RELATION_NAME, RL.RDB$SCHEMA_NAME, RL.RDB$PACKAGE_NAME); if (!RL.RDB$DESCRIPTION.NULL && !UserBlob::blobIsNull(RL.RDB$DESCRIPTION)) { @@ -3012,6 +3015,7 @@ static processing_state show_comments(const commentMode showextract, const char* FOR RF IN RDB$RELATION_FIELDS WITH RF.RDB$SCHEMA_NAME EQUIV NULLIF(name.schema.c_str(), '') AND + RF.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND RF.RDB$RELATION_NAME = RL.RDB$RELATION_NAME AND RF.RDB$DESCRIPTION NOT MISSING SORTED BY RF.RDB$FIELD_POSITION @@ -3070,7 +3074,7 @@ static processing_state show_comments(const commentMode showextract, const char* FOR PR IN RDB$PROCEDURES WITH (PR.RDB$SYSTEM_FLAG EQ 0 OR PR.RDB$SYSTEM_FLAG MISSING) - SORTED BY PR.RDB$SCHEMA_NAME, PR.RDB$PROCEDURE_NAME + SORTED BY PR.RDB$SCHEMA_NAME, PR.RDB$PACKAGE_NAME, PR.RDB$PROCEDURE_NAME { const QualifiedMetaString name(PR.RDB$PROCEDURE_NAME, PR.RDB$SCHEMA_NAME, PR.RDB$PACKAGE_NAME); @@ -3123,7 +3127,7 @@ static processing_state show_comments(const commentMode showextract, const char* FOR UD IN RDB$FUNCTIONS WITH UD.RDB$DESCRIPTION NOT MISSING AND (UD.RDB$SYSTEM_FLAG EQ 0 OR UD.RDB$SYSTEM_FLAG MISSING) - SORTED BY UD.RDB$SCHEMA_NAME, UD.RDB$FUNCTION_NAME + SORTED BY UD.RDB$SCHEMA_NAME, UD.RDB$PACKAGE_NAME, UD.RDB$FUNCTION_NAME { // Avoid syntax error when extracting scripts due to an historical bug in gbak. // See CORE-1174. @@ -3216,9 +3220,9 @@ static processing_state show_comments(const commentMode showextract, const char* FOR IX IN RDB$INDICES WITH IX.RDB$DESCRIPTION NOT MISSING AND (IX.RDB$SYSTEM_FLAG EQ 0 OR IX.RDB$SYSTEM_FLAG MISSING) - SORTED BY IX.RDB$SCHEMA_NAME, IX.RDB$INDEX_NAME + SORTED BY IX.RDB$SCHEMA_NAME, IX.RDB$PACKAGE_NAME, IX.RDB$INDEX_NAME { - const QualifiedMetaString name(IX.RDB$INDEX_NAME, IX.RDB$SCHEMA_NAME); + const QualifiedMetaString name(IX.RDB$INDEX_NAME, IX.RDB$SCHEMA_NAME, IX.RDB$PACKAGE_NAME); show_comment("INDEX", name, {}, &IX.RDB$DESCRIPTION, showextract, first ? banner : 0); @@ -4705,7 +4709,7 @@ static void show_index(const QualifiedMetaString& relationName, "%s%s%s INDEX ON %s", IUTILS_name_to_string(indexName).c_str(), (unique_flag ? " UNIQUE" : ""), - (index_type == 1 ? " DESCENDING" : ""), IUTILS_name_to_string(relationName.object).c_str()); + (index_type == 1 ? " DESCENDING" : ""), IUTILS_name_to_string(relationName).c_str()); // Get column names @@ -4741,17 +4745,18 @@ static processing_state show_indices(const std::optional& n if (name) { FOR IDX IN RDB$INDICES - WITH (IDX.RDB$RELATION_NAME EQ name->object.c_str() OR - IDX.RDB$INDEX_NAME EQ name->object.c_str()) AND + WITH IDX.RDB$PACKAGE_NAME EQUIV NULLIF(name->package.c_str(), '') AND + (IDX.RDB$RELATION_NAME EQ name->object.c_str() OR + IDX.RDB$INDEX_NAME EQ name->object.c_str()) AND IDX.RDB$INDEX_NAME NOT STARTING TEMP_DEPEND - SORTED BY IDX.RDB$SCHEMA_NAME, IDX.RDB$INDEX_NAME + SORTED BY IDX.RDB$SCHEMA_NAME, IDX.RDB$PACKAGE_NAME, IDX.RDB$INDEX_NAME { - const QualifiedMetaString indexName(IDX.RDB$INDEX_NAME, IDX.RDB$SCHEMA_NAME); + const QualifiedMetaString indexName(IDX.RDB$INDEX_NAME, IDX.RDB$SCHEMA_NAME, IDX.RDB$PACKAGE_NAME); if (name->schema.hasData() && name->schema != indexName.schema) continue; - const QualifiedMetaString relationName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME); + const QualifiedMetaString relationName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME, IDX.RDB$PACKAGE_NAME); if (IDX.RDB$INDEX_INACTIVE.NULL) IDX.RDB$INDEX_INACTIVE = 0; @@ -4792,13 +4797,14 @@ static processing_state show_indices(const std::optional& n FOR IDX IN RDB$INDICES CROSS REL IN RDB$RELATIONS WITH REL.RDB$SCHEMA_NAME EQUIV IDX.RDB$SCHEMA_NAME AND + REL.RDB$PACKAGE_NAME EQUIV IDX.RDB$PACKAGE_NAME AND REL.RDB$RELATION_NAME = IDX.RDB$RELATION_NAME AND (REL.RDB$SYSTEM_FLAG NE 1 OR REL.RDB$SYSTEM_FLAG MISSING) AND IDX.RDB$INDEX_NAME NOT STARTING TEMP_DEPEND - SORTED BY IDX.RDB$SCHEMA_NAME, IDX.RDB$RELATION_NAME, IDX.RDB$INDEX_NAME + SORTED BY IDX.RDB$SCHEMA_NAME, IDX.RDB$PACKAGE_NAME, IDX.RDB$RELATION_NAME, IDX.RDB$INDEX_NAME { - const QualifiedMetaString indexName(IDX.RDB$INDEX_NAME, IDX.RDB$SCHEMA_NAME); - const QualifiedMetaString relationName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME); + const QualifiedMetaString indexName(IDX.RDB$INDEX_NAME, IDX.RDB$SCHEMA_NAME, IDX.RDB$PACKAGE_NAME); + const QualifiedMetaString relationName(IDX.RDB$RELATION_NAME, IDX.RDB$SCHEMA_NAME, IDX.RDB$PACKAGE_NAME); first = false; @@ -6023,14 +6029,26 @@ static processing_state show_table(const QualifiedMetaString& name, bool isView) // Query to obtain relation information // REL.RDB$VIEW_BLR NOT MISSING + const QualifiedMetaString name1(name.object, name.schema, name.package); + const auto name2 = name1.package.hasData() ? + name1 : + QualifiedMetaString(name.object, "", name.schema); + FOR REL IN RDB$RELATIONS - WITH REL.RDB$RELATION_NAME EQ name.object.c_str() - SORTED BY REL.RDB$SCHEMA_NAME + WITH (REL.RDB$RELATION_NAME EQ name1.object.c_str() AND + REL.RDB$PACKAGE_NAME EQUIV NULLIF(name1.package.c_str(), '')) OR + (REL.RDB$RELATION_NAME EQ name2.object.c_str() AND + REL.RDB$PACKAGE_NAME EQUIV NULLIF(name2.package.c_str(), '')) + SORTED BY REL.RDB$SCHEMA_NAME, REL.RDB$PACKAGE_NAME { - const QualifiedMetaString relationName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME); + const QualifiedMetaString relationName( + REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME, REL.RDB$PACKAGE_NAME); - if (name.schema.hasData() && name.schema != relationName.schema) + if (!(name1.schema.isEmpty() || name1.schema == relationName.schema) && + !(name2.schema.isEmpty() || name2.schema == relationName.schema)) + { continue; + } if ((isView && REL.RDB$VIEW_BLR.NULL) || (!isView && !REL.RDB$VIEW_BLR.NULL)) continue; @@ -6059,6 +6077,7 @@ static processing_state show_table(const QualifiedMetaString& name, bool isView) WITH FLD.RDB$SCHEMA_NAME EQUIV RFR.RDB$FIELD_SOURCE_SCHEMA_NAME AND FLD.RDB$FIELD_NAME EQ RFR.RDB$FIELD_SOURCE AND RFR.RDB$SCHEMA_NAME EQUIV NULLIF(relationName.schema.c_str(), '') AND + RFR.RDB$PACKAGE_NAME EQUIV NULLIF(relationName.package.c_str(), '') AND RFR.RDB$RELATION_NAME EQ relationName.object.c_str() SORTED BY RFR.RDB$FIELD_POSITION, RFR.RDB$FIELD_NAME { @@ -6200,6 +6219,9 @@ static processing_state show_table(const QualifiedMetaString& name, bool isView) isqlGlob.printf(NEWLINE); } + if (relationName.package.isEmpty()) + { // FIXME: indent + // Handle any referential or primary constraint on this table SCHAR collist[BUFFER_LENGTH512]; @@ -6274,6 +6296,7 @@ static processing_state show_table(const QualifiedMetaString& name, bool isView) { FOR IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQUIV NULLIF(indexName1.schema.c_str(), '') AND + IDX.RDB$PACKAGE_NAME MISSING AND IDX.RDB$INDEX_NAME = indexName1.object.c_str() { // Yes, the same RDB$... naming convention is used for both domains and indices. @@ -6344,6 +6367,8 @@ static processing_state show_table(const QualifiedMetaString& name, bool isView) // Do publications show_pub_table(relationName); + } // FIXME: indent end + } END_FOR ON_ERROR diff --git a/src/isql/tests/FrontendParserTest.cpp b/src/isql/tests/FrontendParserTest.cpp index d4f62903351..787046b4450 100644 --- a/src/isql/tests/FrontendParserTest.cpp +++ b/src/isql/tests/FrontendParserTest.cpp @@ -589,6 +589,8 @@ BOOST_AUTO_TEST_CASE(ParseShowTest) "show table \"test\"")).name == QualifiedMetaString("test"))); BOOST_TEST((std::get(parseShow( "show table \"te\"\"st\"")).name == QualifiedMetaString("te\"st"))); + BOOST_TEST((std::get(parseShow( + "show table schema.package.name")).name == QualifiedMetaString("NAME", "SCHEMA", "PACKAGE"))); BOOST_TEST(!std::get(parseShow( "show table")).name); diff --git a/src/jrd/Monitoring.cpp b/src/jrd/Monitoring.cpp index df41d643ff9..1251779b9d7 100644 --- a/src/jrd/Monitoring.cpp +++ b/src/jrd/Monitoring.cpp @@ -787,7 +787,9 @@ void SnapshotData::putField(thread_db* tdbb, Record* record, const DumpField& fi from_desc.makeLong(0, &local_id); MOV_move(tdbb, &from_desc, &to_desc); } - else if (field.type == VALUE_TABLE_ID_OBJECT_NAME || field.type == VALUE_TABLE_ID_SCHEMA_NAME) + else if (field.type == VALUE_TABLE_ID_OBJECT_NAME || + field.type == VALUE_TABLE_ID_SCHEMA_NAME || + field.type == VALUE_TABLE_ID_PACKAGE_NAME) { // special case: translate relation ID into name fb_assert(field.length == sizeof(SLONG)); @@ -798,8 +800,13 @@ void SnapshotData::putField(thread_db* tdbb, Record* record, const DumpField& fi if (!relation || relation->rel_name.object.isEmpty()) return; - const auto& name = field.type == VALUE_TABLE_ID_OBJECT_NAME ? - relation->rel_name.object : relation->rel_name.schema; + const auto& name = field.type == + VALUE_TABLE_ID_OBJECT_NAME ? relation->rel_name.object : + field.type == VALUE_TABLE_ID_SCHEMA_NAME ? relation->rel_name.schema : + relation->rel_name.package; + + if (field.type == VALUE_TABLE_ID_PACKAGE_NAME && name.isEmpty()) + return; dsc from_desc; from_desc.makeText(name.length(), CS_METADATA, (UCHAR*) name.c_str()); @@ -1549,15 +1556,17 @@ void Monitoring::putStatistics(thread_db* tdbb, SnapshotData::DumpRecord& record bool lttFound = false; std::string_view tableType = "PERSISTENT"; + const RelationPermanent* relation = nullptr; if (stat_group != stat_database) { - if (const auto* relation = MetadataCache::getPerm(tdbb, counts.getGroupId(), - CacheFlag::AUTOCREATE)) + if ((relation = MetadataCache::getPerm(tdbb, counts.getGroupId(), + CacheFlag::AUTOCREATE))) { - if ((relation->rel_flags & REL_temp_ltt)) + if ((relation->rel_flags & REL_temp_ltt) || relation->rel_name.package.hasData()) { record.storeString(f_mon_tab_sch_name, relation->rel_name.schema); + record.storeString(f_mon_tab_pkg_name, relation->rel_name.package); record.storeString(f_mon_tab_name, relation->rel_name.object); lttFound = true; tableType = "LOCAL TEMPORARY"; @@ -1570,8 +1579,11 @@ void Monitoring::putStatistics(thread_db* tdbb, SnapshotData::DumpRecord& record if (!lttFound) { record.storeTableIdSchemaName(f_mon_tab_sch_name, counts.getGroupId()); + record.storeTableIdPackageName(f_mon_tab_pkg_name, counts.getGroupId()); record.storeTableIdObjectName(f_mon_tab_name, counts.getGroupId()); } + else + fb_assert(relation); record.storeString(f_mon_tab_type, tableType); diff --git a/src/jrd/Monitoring.h b/src/jrd/Monitoring.h index f86e779edfb..1c99fb2b9cd 100644 --- a/src/jrd/Monitoring.h +++ b/src/jrd/Monitoring.h @@ -62,6 +62,7 @@ class SnapshotData VALUE_STRING, VALUE_BOOLEAN, VALUE_TABLE_ID_SCHEMA_NAME, + VALUE_TABLE_ID_PACKAGE_NAME, VALUE_LAST_MARKER // Should be last item }; @@ -136,6 +137,11 @@ class SnapshotData storeField(field_id, VALUE_TABLE_ID_SCHEMA_NAME, sizeof(SLONG), &value); } + void storeTableIdPackageName(int field_id, SLONG value) + { + storeField(field_id, VALUE_TABLE_ID_PACKAGE_NAME, sizeof(SLONG), &value); + } + void storeInteger(int field_id, SINT64 value) { storeField(field_id, VALUE_INTEGER, sizeof(SINT64), &value); diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index f234b65bc32..adee7a867a0 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -805,13 +805,16 @@ RelationSourceNode* RelationSourceNode::parse(thread_db* tdbb, CompilerScratch* case blr_relation3: csb->csb_blr_reader.getMetaName(name.schema); + csb->csb_blr_reader.getMetaName(name.package); [[fallthrough]]; case blr_relation: case blr_relation2: { csb->csb_blr_reader.getMetaName(name.object); - csb->qualifyExistingName(tdbb, name, obj_relation); + + if (blrOp != blr_relation3) + csb->qualifyExistingName(tdbb, name, obj_relation); if (blrOp == blr_relation2 || blrOp == blr_relation3) { @@ -894,11 +897,13 @@ void RelationSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch) } else { - if (relation->rel_name.schema != dsqlScratch->ddlSchema) + if (relation->rel_name.package.hasData() || relation->rel_name.schema != dsqlScratch->ddlSchema) { dsqlScratch->appendUChar(blr_relation3); dsqlScratch->appendMetaString(relation->rel_name.schema.c_str()); + dsqlScratch->appendMetaString(relation->rel_name.package.c_str()); dsqlScratch->appendMetaString(relation->rel_name.object.c_str()); + if (dsqlContext->ctx_alias.isEmpty()) dsqlScratch->appendMetaString(""); } diff --git a/src/jrd/Relation.cpp b/src/jrd/Relation.cpp index c4ad47e27b9..7a6406d3e2b 100644 --- a/src/jrd/Relation.cpp +++ b/src/jrd/Relation.cpp @@ -1174,4 +1174,3 @@ GCLock::State GCLock::isGCEnabled() const return State::unknown; } #endif //DEV_BUILD - diff --git a/src/jrd/Relation.h b/src/jrd/Relation.h index d3e9fa28f6d..edfbee24fd0 100644 --- a/src/jrd/Relation.h +++ b/src/jrd/Relation.h @@ -663,6 +663,7 @@ class jrd_rel final : public ObjectBase bool isLTT() const noexcept; bool isVirtual() const noexcept; bool isView() const noexcept; + bool isPrivate() const noexcept; bool isReplicating(thread_db* tdbb); ObjectType getObjectType() const noexcept @@ -953,6 +954,7 @@ class RelationPermanent : public Firebird::PermanentStorage bool isLTT() const noexcept; bool isVirtual() const noexcept; bool isView() const noexcept; + bool isPrivate() const noexcept; bool isReplicating(thread_db* tdbb); static int partners_ast_relation(void* ast_object); @@ -988,6 +990,7 @@ class RelationPermanent : public Firebird::PermanentStorage MetaName rel_owner_name; // ascii owner QualifiedName rel_security_name; // security class name for relation + bool rel_private = false; std::atomic rel_flags; // flags Firebird::TriState rel_repl_state; // replication state @@ -1086,6 +1089,11 @@ inline bool jrd_rel::isView() const noexcept return rel_perm->isView(); } +inline bool jrd_rel::isPrivate() const noexcept +{ + return rel_perm->isPrivate(); +} + inline bool jrd_rel::isSystem() const noexcept { return rel_perm->isSystem(); @@ -1122,6 +1130,11 @@ inline bool RelationPermanent::isView() const noexcept return (rel_flags & REL_jrd_view); } +inline bool RelationPermanent::isPrivate() const noexcept +{ + return rel_private; +} + inline bool RelationPermanent::isLTT() const noexcept { return (rel_flags & REL_temp_ltt); diff --git a/src/jrd/SystemTriggers.epp b/src/jrd/SystemTriggers.epp index e20773be845..ebe62b57df1 100644 --- a/src/jrd/SystemTriggers.epp +++ b/src/jrd/SystemTriggers.epp @@ -118,6 +118,7 @@ void afterDeleteCheckConstraint(thread_db* tdbb, Record* record) RFR IN RDB$RELATION_FIELDS CROSS RLC IN RDB$RELATION_CONSTRAINTS WITH RFR.RDB$SCHEMA_NAME = schemaName.c_str() AND + RFR.RDB$PACKAGE_NAME MISSING AND RFR.RDB$FIELD_NAME = triggerName.c_str() AND RLC.RDB$SCHEMA_NAME = RFR.RDB$SCHEMA_NAME AND RLC.RDB$CONSTRAINT_NAME = constraintName.c_str() AND @@ -180,6 +181,9 @@ void beforeUpdateIndex(thread_db* tdbb, Record* orgRecord, Record* newRecord) const auto transaction = tdbb->getTransaction(); dsc desc; + if (EVL_field(nullptr, orgRecord, f_idx_pkg_name, &desc)) + return; + MetaName oldSchemaName; if (EVL_field(nullptr, orgRecord, f_idx_schema, &desc)) MOV_get_metaname(tdbb, &desc, oldSchemaName); @@ -325,6 +329,9 @@ void beforeDeleteIndexSegment(thread_db* tdbb, Record* record) const auto transaction = tdbb->getTransaction(); dsc desc; + if (EVL_field(nullptr, record, f_seg_pkg_name, &desc)) + return; + MetaName schemaName; if (EVL_field(nullptr, record, f_seg_schema, &desc)) MOV_get_metaname(tdbb, &desc, schemaName); @@ -351,6 +358,9 @@ void beforeUpdateIndexSegment(thread_db* tdbb, Record* orgRecord, Record* newRec const auto transaction = tdbb->getTransaction(); dsc desc; + if (EVL_field(nullptr, orgRecord, f_seg_pkg_name, &desc)) + return; + MetaName oldSchemaName; if (EVL_field(nullptr, orgRecord, f_seg_schema, &desc)) MOV_get_metaname(tdbb, &desc, oldSchemaName); @@ -459,9 +469,12 @@ void beforeUpdateField(thread_db* tdbb, Record* orgRecord, Record* newRecord) CROSS IDS IN RDB$INDEX_SEGMENTS WITH RFL.RDB$FIELD_SOURCE_SCHEMA_NAME = schemaName.c_str() AND RFL.RDB$FIELD_SOURCE = fieldName.c_str() AND + RFL.RDB$PACKAGE_NAME MISSING AND IND.RDB$SCHEMA_NAME = RFL.RDB$SCHEMA_NAME AND + IND.RDB$PACKAGE_NAME MISSING AND IND.RDB$RELATION_NAME = RFL.RDB$RELATION_NAME AND IDS.RDB$SCHEMA_NAME = IND.RDB$SCHEMA_NAME AND + IDS.RDB$PACKAGE_NAME MISSING AND IDS.RDB$INDEX_NAME = IND.RDB$INDEX_NAME AND IDS.RDB$FIELD_NAME = RFL.RDB$FIELD_NAME AND RLC.RDB$SCHEMA_NAME = IND.RDB$SCHEMA_NAME AND @@ -600,11 +613,14 @@ void beforeDeleteRelationConstraint(thread_db* tdbb, Record* record) WITH CHK.RDB$SCHEMA_NAME = schemaName.c_str() AND CHK.RDB$CONSTRAINT_NAME = constraintName.c_str() AND RFL.RDB$SCHEMA_NAME = CHK.RDB$SCHEMA_NAME AND + RFL.RDB$PACKAGE_NAME MISSING AND RFL.RDB$RELATION_NAME = relationName.c_str() AND RFL.RDB$FIELD_NAME = CHK.RDB$TRIGGER_NAME AND IND.RDB$SCHEMA_NAME = RFL.RDB$SCHEMA_NAME AND + IND.RDB$PACKAGE_NAME MISSING AND IND.RDB$RELATION_NAME = RFL.RDB$RELATION_NAME AND IDS.RDB$SCHEMA_NAME = IND.RDB$SCHEMA_NAME AND + IDS.RDB$PACKAGE_NAME MISSING AND IDS.RDB$INDEX_NAME = IND.RDB$INDEX_NAME AND IDS.RDB$FIELD_NAME = RFL.RDB$FIELD_NAME AND FLD.RDB$SCHEMA_NAME = RFL.RDB$FIELD_SOURCE_SCHEMA_NAME AND @@ -721,6 +737,7 @@ void afterDeleteRelationConstraint(thread_db* tdbb, Record* record) WITH CHK.RDB$SCHEMA_NAME = schemaName.c_str() AND CHK.RDB$CONSTRAINT_NAME = constraintName.c_str() AND RFL.RDB$SCHEMA_NAME = CHK.RDB$SCHEMA_NAME AND + RFL.RDB$PACKAGE_NAME MISSING AND RFL.RDB$RELATION_NAME = relationName.c_str() AND RFL.RDB$FIELD_NAME = CHK.RDB$TRIGGER_NAME { @@ -821,6 +838,9 @@ void beforeDeleteRelationField(thread_db* tdbb, Record* record) const auto transaction = tdbb->getTransaction(); dsc desc; + if (EVL_field(nullptr, record, f_rfr_pkg_name, &desc)) + return; + MetaName schemaName; if (!EVL_field(nullptr, record, f_rfr_schema, &desc)) return; @@ -844,8 +864,10 @@ void beforeDeleteRelationField(thread_db* tdbb, Record* record) CROSS RCL IN RDB$RELATION_CONSTRAINTS CROSS IDS IN RDB$INDEX_SEGMENTS WITH IND.RDB$SCHEMA_NAME = schemaName.c_str() AND + IND.RDB$PACKAGE_NAME MISSING AND IND.RDB$RELATION_NAME = relationName.c_str() AND IDS.RDB$SCHEMA_NAME = IND.RDB$SCHEMA_NAME AND + IDS.RDB$PACKAGE_NAME MISSING AND IDS.RDB$INDEX_NAME = IND.RDB$INDEX_NAME AND IDS.RDB$FIELD_NAME = fieldName.c_str() AND RCL.RDB$SCHEMA_NAME = IND.RDB$SCHEMA_NAME AND @@ -857,6 +879,7 @@ void beforeDeleteRelationField(thread_db* tdbb, Record* record) FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) IDS2 IN RDB$INDEX_SEGMENTS WITH IDS2.RDB$SCHEMA_NAME = IND.RDB$SCHEMA_NAME AND + IDS2.RDB$PACKAGE_NAME MISSING AND IDS2.RDB$INDEX_NAME = IND.RDB$INDEX_NAME AND IDS2.RDB$FIELD_NAME != fieldName.c_str() { @@ -913,6 +936,9 @@ void afterDeleteRelationField(thread_db* tdbb, Record* record) const auto transaction = tdbb->getTransaction(); dsc desc; + if (EVL_field(nullptr, record, f_rfr_pkg_name, &desc)) + return; + MetaName schemaName; if (!EVL_field(nullptr, record, f_rfr_schema, &desc)) return; @@ -952,6 +978,9 @@ void beforeUpdateRelationField(thread_db* tdbb, Record* orgRecord, Record* newRe const auto transaction = tdbb->getTransaction(); dsc desc; + if (EVL_field(nullptr, orgRecord, f_rfr_pkg_name, &desc)) + return; + MetaName oldSchemaName; MetaName oldRelationName; MetaName oldFieldName, newFieldName; @@ -993,8 +1022,10 @@ void beforeUpdateRelationField(thread_db* tdbb, Record* orgRecord, Record* newRe CROSS IDS IN RDB$INDEX_SEGMENTS CROSS RLC IN RDB$RELATION_CONSTRAINTS WITH IND.RDB$SCHEMA_NAME = oldSchemaName.c_str() AND + IND.RDB$PACKAGE_NAME MISSING AND IND.RDB$RELATION_NAME = oldRelationName.c_str() AND IDS.RDB$SCHEMA_NAME = IND.RDB$SCHEMA_NAME AND + IDS.RDB$PACKAGE_NAME MISSING AND IDS.RDB$INDEX_NAME = IND.RDB$INDEX_NAME AND IDS.RDB$FIELD_NAME = oldFieldName.c_str() AND RLC.RDB$SCHEMA_NAME = IND.RDB$SCHEMA_NAME AND @@ -1176,6 +1207,7 @@ void beforeDeleteUserPrivilege(thread_db* tdbb, Record* record) RFL IN RDB$RELATION_FIELDS CROSS SEC IN RDB$SECURITY_CLASSES WITH RFL.RDB$SCHEMA_NAME = schemaName.c_str() AND + RFL.RDB$PACKAGE_NAME MISSING AND RFL.RDB$RELATION_NAME = relationName.c_str() AND RFL.RDB$FIELD_NAME = fieldName.c_str() AND RFL.RDB$SECURITY_CLASS STARTING SQL_FLD_SECCLASS_PREFIX AND @@ -1250,6 +1282,7 @@ void beforeInsertUserPrivilege(thread_db* tdbb, Record* record) FOR (REQUEST_HANDLE request1 TRANSACTION_HANDLE transaction) REL IN RDB$RELATIONS WITH REL.RDB$SCHEMA_NAME EQ schemaName.c_str() AND + REL.RDB$PACKAGE_NAME MISSING AND REL.RDB$RELATION_NAME EQ relationName.c_str() { const MetaName relationOwner(REL.RDB$OWNER_NAME); @@ -1265,18 +1298,21 @@ void beforeInsertUserPrivilege(thread_db* tdbb, Record* record) AutoCacheRequest request2(tdbb, request2CacheId); FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) - RFL2 IN RDB$RELATION_FIELDS - CROSS VRL2 IN RDB$VIEW_RELATIONS - CROSS REL2 IN RDB$RELATIONS - WITH RFL2.RDB$SCHEMA_NAME = schemaName.c_str() AND - RFL2.RDB$RELATION_NAME = relationName.c_str() AND - RFL2.RDB$FIELD_NAME = fieldName.c_str() AND - RFL2.RDB$BASE_FIELD NOT MISSING AND - VRL2.RDB$SCHEMA_NAME = RFL2.RDB$SCHEMA_NAME AND - VRL2.RDB$VIEW_NAME = RFL2.RDB$RELATION_NAME AND - VRL2.RDB$VIEW_CONTEXT = RFL2.RDB$VIEW_CONTEXT AND - REL2.RDB$SCHEMA_NAME = VRL2.RDB$RELATION_SCHEMA_NAME AND - REL2.RDB$RELATION_NAME = VRL2.RDB$RELATION_NAME + RFL2 IN RDB$RELATION_FIELDS + CROSS VRL2 IN RDB$VIEW_RELATIONS + CROSS REL2 IN RDB$RELATIONS + WITH RFL2.RDB$SCHEMA_NAME = schemaName.c_str() AND + RFL2.RDB$PACKAGE_NAME MISSING AND + RFL2.RDB$RELATION_NAME = relationName.c_str() AND + RFL2.RDB$FIELD_NAME = fieldName.c_str() AND + RFL2.RDB$BASE_FIELD NOT MISSING AND + VRL2.RDB$SCHEMA_NAME = RFL2.RDB$SCHEMA_NAME AND + VRL2.RDB$PACKAGE_NAME MISSING AND + VRL2.RDB$VIEW_NAME = RFL2.RDB$RELATION_NAME AND + VRL2.RDB$VIEW_CONTEXT = RFL2.RDB$VIEW_CONTEXT AND + REL2.RDB$SCHEMA_NAME = VRL2.RDB$RELATION_SCHEMA_NAME AND + REL2.RDB$PACKAGE_NAME MISSING AND + REL2.RDB$RELATION_NAME = VRL2.RDB$RELATION_NAME { const MetaName relation2Owner(REL2.RDB$OWNER_NAME); @@ -1317,8 +1353,10 @@ void beforeInsertUserPrivilege(thread_db* tdbb, Record* record) VRL4 IN RDB$VIEW_RELATIONS CROSS REL4 IN RDB$RELATIONS WITH VRL4.RDB$SCHEMA_NAME = schemaName.c_str() AND + VRL4.RDB$PACKAGE_NAME MISSING AND VRL4.RDB$VIEW_NAME = relationName.c_str() AND REL4.RDB$SCHEMA_NAME = VRL4.RDB$RELATION_SCHEMA_NAME AND + REL4.RDB$PACKAGE_NAME MISSING AND REL4.RDB$RELATION_NAME = VRL4.RDB$RELATION_NAME { const MetaName relation4Owner(REL4.RDB$OWNER_NAME); @@ -1379,6 +1417,7 @@ void beforeInsertUserPrivilege(thread_db* tdbb, Record* record) FOR (REQUEST_HANDLE request6 TRANSACTION_HANDLE transaction) RFL6 IN RDB$RELATION_FIELDS WITH RFL6.RDB$SCHEMA_NAME = schemaName.c_str() AND + RFL6.RDB$PACKAGE_NAME MISSING AND RFL6.RDB$RELATION_NAME = relationName.c_str() AND RFL6.RDB$FIELD_NAME = fieldName.c_str() { @@ -1868,4 +1907,3 @@ void SystemTriggers::executeAfterUpdateTriggers(thread_db* tdbb, jrd_rel* relati break; } } - diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index 430b2a98145..7e281279dd2 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1707,6 +1707,7 @@ void DFW_update_index(const QualifiedName& name, USHORT id, const SelectivityLis FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) SEG IN RDB$INDEX_SEGMENTS WITH SEG.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + SEG.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND SEG.RDB$INDEX_NAME EQ name.object.c_str() SORTED BY SEG.RDB$FIELD_POSITION { @@ -1721,6 +1722,7 @@ void DFW_update_index(const QualifiedName& name, USHORT id, const SelectivityLis FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND IDX.RDB$INDEX_NAME EQ name.object.c_str() { MODIFY IDX USING @@ -2145,8 +2147,7 @@ static bool store_view_context_type(thread_db* tdbb, SSHORT phase, DeferredWork* { case 1: { - // If RDB$PACKAGE_NAME IS NOT NULL or no record is found in RDB$RELATIONS, - // the context is a procedure; + // If no record is found in RDB$RELATIONS, the context is a procedure; ViewContextType vct = VCT_PROCEDURE; AutoRequest handle1; @@ -2156,9 +2157,9 @@ static bool store_view_context_type(thread_db* tdbb, SSHORT phase, DeferredWork* WITH VRL.RDB$SCHEMA_NAME = work->dfw_schema.c_str() AND VRL.RDB$VIEW_NAME = work->dfw_name.c_str() AND VRL.RDB$VIEW_CONTEXT = work->dfw_id AND - VRL.RDB$PACKAGE_NAME MISSING AND - REL.RDB$SCHEMA_NAME EQ VRL.RDB$RELATION_SCHEMA_NAME AND - REL.RDB$RELATION_NAME EQ VRL.RDB$RELATION_NAME + REL.RDB$SCHEMA_NAME EQ VRL.RDB$RELATION_SCHEMA_NAME AND + REL.RDB$PACKAGE_NAME EQUIV VRL.RDB$PACKAGE_NAME AND + REL.RDB$RELATION_NAME EQ VRL.RDB$RELATION_NAME { vct = (REL.RDB$VIEW_BLR.NULL ? VCT_TABLE : VCT_VIEW); } @@ -2545,7 +2546,11 @@ void DFW_reset_icu(thread_db* tdbb) coalesce(rfl.RDB$COLLATION_ID, fld.RDB$COLLATION_ID) = coll.RDB$COLLATION_ID join SYSTEM.RDB$CHARACTER_SETS cs on coll.RDB$CHARACTER_SET_ID = cs.RDB$CHARACTER_SET_ID - where coll.RDB$SPECIFIC_ATTRIBUTES like '%COLL-VERSION=%' and + where ind.RDB$PACKAGE_NAME is null and + rel.RDB$PACKAGE_NAME is null and + seg.RDB$PACKAGE_NAME is null and + rfl.RDB$PACKAGE_NAME is null and + coll.RDB$SPECIFIC_ATTRIBUTES like '%COLL-VERSION=%' and coalesce(ind.RDB$INDEX_INACTIVE, 0) = 0 and coalesce(rel.RDB$RELATION_TYPE, 0) = 0 -- rel_persistent group by ind.RDB$SCHEMA_NAME, @@ -2629,6 +2634,7 @@ void DFW_reset_icu(thread_db* tdbb) FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ idx.schema.c_str() AND + IDX.RDB$PACKAGE_NAME MISSING AND IDX.RDB$INDEX_NAME EQ idx.object.c_str() { MODIFY IDX @@ -2830,7 +2836,8 @@ static bool find_depend_in_dfw(thread_db* tdbb, (work->dfw_type == dfw_modify_trigger && dfw_type == dfw_delete_trigger) || (work->dfw_type == dfw_modify_function && dfw_type == dfw_delete_function)) && work->dfw_schema == object_name.schema && - work->dfw_name == object_name.object.c_str() && work->dfw_package.isEmpty() && + work->dfw_name == object_name.object.c_str() && + work->dfw_package == object_name.package && (!rel_id || rel_id == work->dfw_id)) { if (work->dfw_type == dfw_modify_procedure || work->dfw_type == dfw_modify_function) @@ -2846,7 +2853,7 @@ static bool find_depend_in_dfw(thread_db* tdbb, } if (work->dfw_type == dfw_type && dfw_type == dfw_delete_index && - QualifiedName(work->dfw_name, work->dfw_schema) == object_name) + QualifiedName(work->dfw_name, work->dfw_schema, work->dfw_package) == object_name) { return true; } @@ -2870,6 +2877,7 @@ static bool find_depend_in_dfw(thread_db* tdbb, RFR.RDB$FIELD_SOURCE_SCHEMA_NAME EQ FLD.RDB$SCHEMA_NAME AND RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND REL.RDB$SCHEMA_NAME EQ RFR.RDB$SCHEMA_NAME AND + REL.RDB$PACKAGE_NAME EQUIV RFR.RDB$PACKAGE_NAME AND REL.RDB$RELATION_NAME EQ RFR.RDB$RELATION_NAME { if (!find_depend_in_dfw(tdbb, QualifiedName(RFR.RDB$FIELD_NAME), obj_computed, @@ -3006,9 +3014,12 @@ static bool delete_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ FOR(REQUEST_HANDLE handle) RFR IN RDB$RELATION_FIELDS - CROSS REL IN RDB$RELATIONS OVER RDB$SCHEMA_NAME, RDB$RELATION_NAME + CROSS REL IN RDB$RELATIONS WITH RFR.RDB$FIELD_SOURCE_SCHEMA_NAME EQ work->dfw_schema.c_str() AND - RFR.RDB$FIELD_SOURCE EQ work->dfw_name.c_str() + RFR.RDB$FIELD_SOURCE EQ work->dfw_name.c_str() AND + REL.RDB$SCHEMA_NAME EQ RFR.RDB$SCHEMA_NAME AND + REL.RDB$RELATION_NAME EQ RFR.RDB$RELATION_NAME AND + REL.RDB$PACKAGE_NAME EQUIV RFR.RDB$PACKAGE_NAME { // If the rfr field is also being deleted, there's no dependency if (!find_depend_in_dfw(tdbb, QualifiedName(RFR.RDB$FIELD_NAME), obj_computed, REL.RDB$RELATION_ID, @@ -3076,7 +3087,10 @@ static bool modify_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_ RFL.RDB$FIELD_SOURCE_SCHEMA_NAME EQ depName.schema.c_str() AND RFL.RDB$FIELD_SOURCE EQ depName.object.c_str() AND (RFL.RDB$NULL_FLAG MISSING OR RFL.RDB$NULL_FLAG = FALSE) AND - REL.RDB$VIEW_BLR MISSING + REL.RDB$VIEW_BLR MISSING AND + // Packaged tables are temporary and dfw_check_not_null do not work for them + REL.RDB$PACKAGE_NAME MISSING AND + RFL.RDB$PACKAGE_NAME MISSING REDUCED TO RFL.RDB$SCHEMA_NAME, RFL.RDB$RELATION_NAME, RFL.RDB$FIELD_ID { dsc schemaDesc; @@ -3394,6 +3408,7 @@ static bool set_statistics(thread_db* tdbb, SSHORT phase, DeferredWork* work, jr FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ work->dfw_schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), '') AND IDX.RDB$INDEX_NAME EQ work->dfw_name.c_str() { // Request to recalculate statistics? @@ -3621,7 +3636,8 @@ static bool delete_relation(thread_db* tdbb, SSHORT phase, DeferredWork* work, j FOR(REQUEST_HANDLE request) X IN RDB$VIEW_RELATIONS WITH X.RDB$RELATION_SCHEMA_NAME EQ work->dfw_schema.c_str() AND - X.RDB$RELATION_NAME EQ work->dfw_name.c_str() + X.RDB$RELATION_NAME EQ work->dfw_name.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), '') { // If the view is also being deleted, there's no dependency if (!find_depend_in_dfw(tdbb, QualifiedName(X.RDB$VIEW_NAME, X.RDB$SCHEMA_NAME), obj_view, 0, transaction)) @@ -3772,9 +3788,11 @@ static bool delete_rfr(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tr CROSS VFLD IN RDB$RELATION_FIELDS WITH REL.RDB$RELATION_ID EQ work->dfw_id AND VR.RDB$RELATION_SCHEMA_NAME EQ REL.RDB$SCHEMA_NAME AND + VR.RDB$PACKAGE_NAME EQUIV REL.RDB$PACKAGE_NAME AND VR.RDB$RELATION_NAME EQ REL.RDB$RELATION_NAME AND VFLD.RDB$VIEW_CONTEXT EQ VR.RDB$VIEW_CONTEXT AND VFLD.RDB$SCHEMA_NAME EQ VR.RDB$SCHEMA_NAME AND + VFLD.RDB$PACKAGE_NAME MISSING AND VFLD.RDB$RELATION_NAME EQ VR.RDB$VIEW_NAME AND VFLD.RDB$BASE_FIELD EQ work->dfw_name.c_str() { @@ -3832,10 +3850,14 @@ static bool delete_rfr(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tr handle.reset(); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) - REL IN RDB$RELATIONS CROSS - RFLD IN RDB$RELATION_FIELDS OVER RDB$RELATION_NAME - WITH REL.RDB$RELATION_ID EQ work->dfw_id + REL IN RDB$RELATIONS + CROSS RFLD IN RDB$RELATION_FIELDS + WITH RFLD.RDB$SCHEMA_NAME EQ REL.RDB$SCHEMA_NAME AND + RFLD.RDB$PACKAGE_NAME EQUIV REL.RDB$PACKAGE_NAME AND + REL.RDB$RELATION_ID EQ work->dfw_id + { field_count++; + } END_FOR if (!field_count) @@ -4405,6 +4427,7 @@ static bool check_not_null(thread_db* tdbb, SSHORT phase, DeferredWork* work, jr FLD IN RDB$FIELDS WITH RFL.RDB$SCHEMA_NAME EQ work->dfw_schema.c_str() AND RFL.RDB$RELATION_NAME EQ work->dfw_name.c_str() AND + RFL.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), '') AND FLD.RDB$SCHEMA_NAME EQ RFL.RDB$FIELD_SOURCE_SCHEMA_NAME AND FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE AND RFL.RDB$FIELD_ID EQ *itr AND diff --git a/src/jrd/dyn_util.epp b/src/jrd/dyn_util.epp index 5f9b769f0f3..44bfb049ba5 100644 --- a/src/jrd/dyn_util.epp +++ b/src/jrd/dyn_util.epp @@ -109,6 +109,7 @@ bool DYN_UTIL_check_unique_name_nothrow(thread_db* tdbb, FOR(REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) EREL IN RDB$RELATIONS WITH EREL.RDB$SCHEMA_NAME EQ object_name.schema.c_str() AND + EREL.RDB$PACKAGE_NAME EQUIV NULLIF(object_name.package.c_str(), '') AND EREL.RDB$RELATION_NAME EQ object_name.object.c_str() { *errorCode = 132; // isc_dyn_dup_table @@ -149,12 +150,13 @@ bool DYN_UTIL_check_unique_name_nothrow(thread_db* tdbb, FOR(REQUEST_HANDLE requestHandle TRANSACTION_HANDLE transaction) EIDX IN RDB$INDICES WITH EIDX.RDB$SCHEMA_NAME EQ object_name.schema.c_str() AND + EIDX.RDB$PACKAGE_NAME EQUIV NULLIF(object_name.package.c_str(), '') AND EIDX.RDB$INDEX_NAME EQ object_name.object.c_str() { if (!(freeWhenPossible && (MetadataCache::getIndexStatus(EIDX.RDB$INDEX_INACTIVE.NULL, EIDX.RDB$INDEX_INACTIVE) == MET_index_deferred_drop) && - BTR_cleanup_index(tdbb, QualifiedName(EIDX.RDB$RELATION_NAME, EIDX.RDB$SCHEMA_NAME), + BTR_cleanup_index(tdbb, QualifiedName(EIDX.RDB$RELATION_NAME, EIDX.RDB$SCHEMA_NAME, EIDX.RDB$PACKAGE_NAME), transaction, MetaId(EIDX.RDB$INDEX_ID - 1)))) { *errorCode = 251; // isc_dyn_dup_index @@ -510,6 +512,7 @@ void DYN_UTIL_generate_field_position(thread_db* tdbb, const QualifiedName& rela FOR(REQUEST_HANDLE request) X IN RDB$RELATION_FIELDS WITH X.RDB$SCHEMA_NAME EQ relation_name.schema.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(relation_name.package.c_str(), '') AND X.RDB$RELATION_NAME EQ relation_name.object.c_str() { if (X.RDB$FIELD_POSITION.NULL) @@ -567,6 +570,7 @@ void DYN_UTIL_generate_index_name(thread_db* tdbb, jrd_tra* /*transaction*/, FOR(REQUEST_HANDLE request) FIRST 1 X IN RDB$INDICES WITH X.RDB$SCHEMA_NAME EQ buffer.schema.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(buffer.package.c_str(), '') AND X.RDB$INDEX_NAME EQ buffer.object.c_str() { found = true; @@ -665,6 +669,7 @@ bool DYN_UTIL_find_field_source(thread_db* tdbb, * **************************************/ SET_TDBB(tdbb); + fb_assert(view_name.package.isEmpty()); AutoCacheRequest request(tdbb, drq_l_fld_src2, DYN_REQUESTS); bool found = false; @@ -676,8 +681,8 @@ bool DYN_UTIL_find_field_source(thread_db* tdbb, VRL.RDB$VIEW_NAME EQ view_name.object.c_str() AND VRL.RDB$VIEW_CONTEXT EQ context AND VRL.RDB$CONTEXT_TYPE BETWEEN VCT_TABLE AND VCT_VIEW AND - VRL.RDB$PACKAGE_NAME MISSING AND RFR.RDB$SCHEMA_NAME EQ VRL.RDB$RELATION_SCHEMA_NAME AND + RFR.RDB$PACKAGE_NAME EQUIV VRL.RDB$PACKAGE_NAME AND RFR.RDB$RELATION_NAME EQ VRL.RDB$RELATION_NAME AND RFR.RDB$FIELD_NAME EQ local_name { diff --git a/src/jrd/grant.epp b/src/jrd/grant.epp index a92f11ef3f3..5d5581fdc59 100644 --- a/src/jrd/grant.epp +++ b/src/jrd/grant.epp @@ -243,6 +243,7 @@ static void define_default_class(thread_db* tdbb, FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) REL IN RDB$RELATIONS WITH REL.RDB$SCHEMA_NAME EQ relationName.schema.c_str() AND + REL.RDB$PACKAGE_NAME EQUIV NULLIF(relationName.package.c_str(), '') AND REL.RDB$RELATION_NAME EQ relationName.object.c_str() { MODIFY REL USING @@ -355,6 +356,7 @@ static void get_object_info(thread_db* tdbb, FOR(REQUEST_HANDLE request) REL IN RDB$RELATIONS WITH REL.RDB$SCHEMA_NAME EQ object_name.schema.c_str() AND + REL.RDB$PACKAGE_NAME EQUIV NULLIF(object_name.package.c_str(), '') AND REL.RDB$RELATION_NAME EQ object_name.object.c_str() { s_class = REL.RDB$SECURITY_CLASS; @@ -749,6 +751,8 @@ static SecurityClass::flags_t save_field_privileges(thread_db* tdbb, * security class to be effective. * **************************************/ + fb_assert(relation_name.package.isEmpty()); + SET_TDBB(tdbb); Jrd::Attachment* attachment = tdbb->getAttachment(); @@ -772,9 +776,12 @@ static SecurityClass::flags_t save_field_privileges(thread_db* tdbb, CROSS PRV IN RDB$USER_PRIVILEGES OVER RDB$RELATION_NAME, RDB$FIELD_NAME CROSS REL IN RDB$RELATIONS OVER RDB$RELATION_NAME WITH PRV.RDB$RELATION_SCHEMA_NAME EQ FLD.RDB$SCHEMA_NAME AND + FLD.RDB$PACKAGE_NAME MISSING AND PRV.RDB$OBJECT_TYPE EQ obj_relation AND PRV.RDB$RELATION_SCHEMA_NAME EQ relation_name.schema.c_str() AND PRV.RDB$RELATION_NAME EQ relation_name.object.c_str() AND + REL.RDB$SCHEMA_NAME EQ FLD.RDB$SCHEMA_NAME AND + REL.RDB$PACKAGE_NAME MISSING AND PRV.RDB$FIELD_NAME NOT MISSING AND (PRV.RDB$USER NE owner.c_str() OR PRV.RDB$USER_TYPE NE obj_user) SORTED BY PRV.RDB$FIELD_NAME, PRV.RDB$USER, PRV.RDB$USER_SCHEMA_NAME @@ -837,6 +844,7 @@ static SecurityClass::flags_t save_field_privileges(thread_db* tdbb, FOR(REQUEST_HANDLE request2 TRANSACTION_HANDLE transaction) RFR IN RDB$RELATION_FIELDS WITH RFR.RDB$SCHEMA_NAME EQ FLD.RDB$SCHEMA_NAME AND + RFR.RDB$PACKAGE_NAME MISSING AND RFR.RDB$RELATION_NAME EQ FLD.RDB$RELATION_NAME AND RFR.RDB$FIELD_NAME EQ FLD.RDB$FIELD_NAME { diff --git a/src/jrd/idx.h b/src/jrd/idx.h index 0e7811a7870..66c90f9a4f0 100644 --- a/src/jrd/idx.h +++ b/src/jrd/idx.h @@ -314,9 +314,10 @@ static inline constexpr struct ini_idx_t indices[] = INDEX(58, rel_schemas, idx_unique, 1, ODS_14_0) SEGMENT(f_sch_schema, idx_metadata) // schema name }}, - // define index RDB$INDEX_59 for RDB$RELATIONS unique RDB$SCHEMA_NAME, RDB$RELATION_NAME; - INDEX(59, rel_relations, idx_unique, 2, ODS_14_0) + // define index RDB$INDEX_59 for RDB$RELATIONS unique RDB$SCHEMA_NAME, RDB$PACKAGE_NAME, RDB$RELATION_NAME; + INDEX(59, rel_relations, idx_unique, 3, ODS_14_0) SEGMENT(f_rel_schema, idx_metadata), // schema name + SEGMENT(f_rel_pkg_name, idx_metadata), // package name SEGMENT(f_rel_name, idx_metadata) // relation name }}, // define index RDB$INDEX_60 for RDB$FIELDS unique RDB$SCHEMA_NAME, RDB$FIELD_NAME; @@ -329,19 +330,22 @@ static inline constexpr struct ini_idx_t indices[] = SEGMENT(f_rfr_field_source_schema, idx_metadata), // field source schema name SEGMENT(f_rfr_sname, idx_metadata) // field source name }}, - // define index RDB$INDEX_62 for RDB$RELATION_FIELDS RDB$SCHEMA_NAME, RDB$RELATION_NAME; - INDEX(62, rel_rfr, 0, 2, ODS_14_0) + // define index RDB$INDEX_62 for RDB$RELATION_FIELDS RDB$SCHEMA_NAME, RDB$PACKAGE_NAME, RDB$RELATION_NAME; + INDEX(62, rel_rfr, 0, 3, ODS_14_0) SEGMENT(f_rfr_schema, idx_metadata), // schema name + SEGMENT(f_rfr_pkg_name, idx_metadata), // package name SEGMENT(f_rfr_rname, idx_metadata) // relation name in RFR }}, - // define index RDB$INDEX_63 for RDB$INDICES unique RDB$SCHEMA_NAME, RDB$INDEX_NAME; - INDEX(63, rel_indices, idx_unique, 2, ODS_14_0) + // define index RDB$INDEX_63 for RDB$INDICES unique RDB$SCHEMA_NAME, RDB$PACKAGE_NAME, RDB$INDEX_NAME; + INDEX(63, rel_indices, idx_unique, 3, ODS_14_0) SEGMENT(f_idx_schema, idx_metadata), // schema name + SEGMENT(f_idx_pkg_name, idx_metadata), // package name SEGMENT(f_idx_name, idx_metadata) // index name }}, - // define index RDB$INDEX_64 for RDB$INDEX_SEGMENTS RDB$SCHEMA_NAME, RDB$INDEX_NAME; - INDEX(64, rel_segments, 0, 2, ODS_14_0) + // define index RDB$INDEX_64 for RDB$INDEX_SEGMENTS RDB$SCHEMA_NAME, RDB$PACKAGE_NAME, RDB$INDEX_NAME; + INDEX(64, rel_segments, 0, 3, ODS_14_0) SEGMENT(f_seg_schema, idx_metadata), // schema name + SEGMENT(f_seg_pkg_name, idx_metadata), // package name SEGMENT(f_seg_name, idx_metadata) // index name in seg }}, // define index RDB$INDEX_65 for RDB$TRIGGERS unique RDB$SCHEMA_NAME, RDB$TRIGGER_NAME; @@ -381,10 +385,11 @@ static inline constexpr struct ini_idx_t indices[] = SEGMENT(f_ccon_schema, idx_metadata), // schema name SEGMENT(f_ccon_cname, idx_metadata) // constraint name }}, - // define index RDB$INDEX_72 for RDB$RELATION_FIELDS unique RDB$FIELD_NAME, RDB$SCHEMA_NAME, RDB$RELATION_NAME; - INDEX(72, rel_rfr, idx_unique, 3, ODS_14_0) + // define index RDB$INDEX_72 for RDB$RELATION_FIELDS unique RDB$FIELD_NAME, RDB$SCHEMA_NAME, RDB$PACKAGE_NAME, RDB$RELATION_NAME; + INDEX(72, rel_rfr, idx_unique, 4, ODS_14_0) SEGMENT(f_rfr_fname, idx_metadata), // field name SEGMENT(f_rfr_schema, idx_metadata), // schema name + SEGMENT(f_rfr_pkg_name, idx_metadata), // package name SEGMENT(f_rfr_rname, idx_metadata) // relation name }}, // define index RDB$INDEX_73 for RDB$PROCEDURE_PARAMETERS unique RDB$SCHEMA_NAME, RDB$PACKAGE_NAME, @@ -440,9 +445,10 @@ static inline constexpr struct ini_idx_t indices[] = SEGMENT(f_prv_user_schema, idx_metadata), // granted user schema name SEGMENT(f_prv_user, idx_metadata) // granted user }}, - // define index RDB$INDEX_82 for RDB$INDICES RDB$SCHEMA_NAME, RDB$RELATION_NAME; - INDEX(82, rel_indices, 0, 2, ODS_14_0) + // define index RDB$INDEX_82 for RDB$INDICES RDB$SCHEMA_NAME, RDB$PACKAGE_NAME, RDB$RELATION_NAME; + INDEX(82, rel_indices, 0, 3, ODS_14_0) SEGMENT(f_idx_schema, idx_metadata), // schema name + SEGMENT(f_idx_pkg_name, idx_metadata), // package name SEGMENT(f_idx_relation, idx_metadata) // indexed relation }}, // define index RDB$INDEX_83 for RDB$VIEW_RELATIONS RDB$SCHEMA_NAME, RDB$VIEW_NAME; @@ -453,7 +459,8 @@ static inline constexpr struct ini_idx_t indices[] = // define index RDB$INDEX_84 for RDB$VIEW_RELATIONS RDB$RELATION_SCHEMA_NAME, RDB$RELATION_NAME; INDEX(84, rel_vrel, 0, 2, ODS_14_0) SEGMENT(f_vrl_rname_schema, idx_metadata), // schema name - SEGMENT(f_vrl_rname, idx_metadata) // base relation name + SEGMENT(f_vrl_pkg_name, idx_metadata), // package name + SEGMENT(f_vrl_rname, idx_metadata) // base relation name }}, // define index RDB$INDEX_85 for RDB$TRIGGER_MESSAGES RDB$SCHEMA_NAME, RDB$TRIGGER_NAME; INDEX(85, rel_msgs, 0, 2, ODS_14_0) diff --git a/src/jrd/met.epp b/src/jrd/met.epp index 145135e851b..a122dc1edde 100644 --- a/src/jrd/met.epp +++ b/src/jrd/met.epp @@ -582,12 +582,15 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const FOR(REQUEST_HANDLE request) X IN RDB$RELATION_FIELDS CROSS R IN RDB$RELATIONS - OVER RDB$SCHEMA_NAME, RDB$RELATION_NAME - WITH X.RDB$SCHEMA_NAME EQ schemaName->dsc_address AND - X.RDB$FIELD_SOURCE EQ fieldSource->dsc_address + WITH X.RDB$FIELD_SOURCE_SCHEMA_NAME EQ schemaName->dsc_address AND + X.RDB$FIELD_SOURCE EQ fieldSource->dsc_address AND + R.RDB$SCHEMA_NAME EQ X.RDB$SCHEMA_NAME AND + R.RDB$PACKAGE_NAME EQUIV X.RDB$PACKAGE_NAME AND + R.RDB$RELATION_NAME EQ X.RDB$RELATION_NAME { - SCL_check_relation(tdbb, QualifiedName(X.RDB$RELATION_NAME, X.RDB$SCHEMA_NAME), SCL_alter); - { + SCL_check_relation(tdbb, QualifiedName(X.RDB$RELATION_NAME, X.RDB$SCHEMA_NAME, X.RDB$PACKAGE_NAME), SCL_alter); + + { // scope AutoSetRestore2 tempTrans(tdbb, &thread_db::getTransaction, &thread_db::setTransaction, @@ -595,6 +598,7 @@ Cached::Relation* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const calcFieldRelation = MetadataCache::newVersion(tdbb, R.RDB$RELATION_ID); } + DFW_post_work(transaction, dfw_commit_relation, nullptr, nullptr, R.RDB$RELATION_ID); AutoCacheRequest request2(tdbb, irq_m_fields4, IRQ_REQUESTS); @@ -1254,6 +1258,9 @@ bool MET_get_repl_state(Jrd::thread_db* tdbb, const QualifiedName& name) * for either database or given relation. * **************************************/ + if (name.package.hasData()) + return false; + SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -1583,6 +1590,8 @@ void MET_lookup_index_for_cnstrt(thread_db* tdbb, * index_name is output parameter. * **************************************/ + fb_assert(constraint_name.package.isEmpty()); + SET_TDBB(tdbb); Attachment* attachment = tdbb->getAttachment(); @@ -1621,6 +1630,10 @@ void MET_lookup_cnstrt_for_index(thread_db* tdbb, Attachment* attachment = tdbb->getAttachment(); constraint_name = ""; + + if (index_name.package.hasData()) + return; + AutoCacheRequest request(tdbb, irq_l_cnstrt, IRQ_REQUESTS); FOR(REQUEST_HANDLE request) @@ -2126,6 +2139,7 @@ ElementBase::ReturnedId MetadataCache::lookup_index_name(thread_db* tdbb, const FOR(REQUEST_HANDLE request) X IN RDB$INDICES WITH X.RDB$SCHEMA_NAME EQ index_name.schema.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(index_name.package.c_str(), '') AND X.RDB$INDEX_NAME EQ index_name.object.c_str() { *status = getIndexStatus(X.RDB$INDEX_INACTIVE.NULL, X.RDB$INDEX_INACTIVE); @@ -2136,7 +2150,9 @@ ElementBase::ReturnedId MetadataCache::lookup_index_name(thread_db* tdbb, const if (relationId) { auto* relation = MetadataCache::getPerm(tdbb, - QualifiedName(X.RDB$RELATION_NAME, X.RDB$SCHEMA_NAME), CacheFlag::AUTOCREATE); + QualifiedName(X.RDB$RELATION_NAME, X.RDB$SCHEMA_NAME, X.RDB$PACKAGE_NAME), + CacheFlag::AUTOCREATE); + if (relation) *relationId = relation->getId(); else @@ -2183,9 +2199,11 @@ void IndexPermanent::refreshIndexCode(thread_db* tdbb, Cached::Relation* relatio FOR(REQUEST_HANDLE handle) // Use system transaction IND IN RDB$INDICES CROSS REL IN RDB$RELATIONS - OVER RDB$RELATION_NAME, RDB$SCHEMA_NAME - WITH IND.RDB$INDEX_ID EQ getId() + 1 - AND REL.RDB$RELATION_ID EQ relId + WITH IND.RDB$INDEX_ID EQ getId() + 1 AND + REL.RDB$RELATION_ID EQ relId AND + REL.RDB$SCHEMA_NAME EQ IND.RDB$SCHEMA_NAME AND + REL.RDB$PACKAGE_NAME EQUIV IND.RDB$PACKAGE_NAME AND + REL.RDB$RELATION_NAME EQ IND.RDB$RELATION_NAME { if (!IND.RDB$EXPRESSION_BLR.NULL) expression = IND.RDB$EXPRESSION_BLR; @@ -2272,6 +2290,7 @@ bool MET_lookup_index_expr_cond_blr(thread_db* tdbb, const QualifiedName& index_ FOR(REQUEST_HANDLE request) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ index_name.schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(index_name.package.c_str(), '') AND IDX.RDB$INDEX_NAME EQ index_name.object.c_str() { found = !IDX.RDB$EXPRESSION_BLR.NULL || !IDX.RDB$CONDITION_BLR.NULL; @@ -2298,6 +2317,9 @@ bool MET_lookup_partner(thread_db* tdbb, RelationPermanent* relation, index_desc * **************************************/ + if (index_name.package.hasData()) + return false; + SET_TDBB(tdbb); fb_assert(index_name.object.isEmpty() || index_name.schema == relation->getName().schema); @@ -2311,6 +2333,9 @@ bool MET_lookup_partner(thread_db* tdbb, RelationPermanent* relation, index_desc { if (index_name.object.hasData()) { + fb_assert(index_name.package.isEmpty()); + fb_assert(relation->getName().package.isEmpty()); + // Since primary key index names aren't being cached, do a long // hard lookup. This is only called during index create for foreign keys. @@ -2321,10 +2346,12 @@ bool MET_lookup_partner(thread_db* tdbb, RelationPermanent* relation, index_desc IDX IN RDB$INDICES CROSS IND IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ relation->getName().schema.c_str() AND + IDX.RDB$PACKAGE_NAME MISSING AND IDX.RDB$RELATION_NAME EQ relation->getName().object.c_str() AND (IDX.RDB$INDEX_ID EQ idx->idx_id + 1 OR IDX.RDB$INDEX_NAME EQ index_name.object.c_str()) AND IND.RDB$SCHEMA_NAME EQ IDX.RDB$FOREIGN_KEY_SCHEMA_NAME AND + IND.RDB$PACKAGE_NAME MISSING AND IND.RDB$INDEX_NAME EQ IDX.RDB$FOREIGN_KEY AND IND.RDB$UNIQUE_FLAG = 1 { @@ -2468,6 +2495,7 @@ std::optional jrd_rel::getIdByName(thread_db* tdbb, const QualifiedName& FOR(REQUEST_HANDLE request) X IN RDB$RELATIONS WITH X.RDB$SCHEMA_NAME EQ name.schema.c_str() AND + X.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND X.RDB$RELATION_NAME EQ name.object.c_str() { fb_assert(!id.has_value()); @@ -3019,8 +3047,10 @@ ScanResult jrd_rel::scan(thread_db* tdbb, ObjectBase::Flag& flags) { found = true; - rel_perm->rel_name = QualifiedName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME); + rel_perm->rel_name = QualifiedName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME, REL.RDB$PACKAGE_NAME); rel_perm->rel_flags |= get_rel_flags_from_FLAGS(REL.RDB$FLAGS); + rel_perm->rel_private = !REL.RDB$PRIVATE_FLAG.NULL && REL.RDB$PRIVATE_FLAG != 0; + if (!REL.RDB$RELATION_TYPE.NULL) rel_perm->rel_flags |= MET_get_rel_flags_from_TYPE(REL.RDB$RELATION_TYPE); @@ -3092,9 +3122,11 @@ ScanResult jrd_rel::scan(thread_db* tdbb, ObjectBase::Flag& flags) found = true; if (getName().isEmpty()) - rel_perm->rel_name = QualifiedName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME); + rel_perm->rel_name = QualifiedName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME, REL.RDB$PACKAGE_NAME); rel_perm->rel_flags |= get_rel_flags_from_FLAGS(REL.RDB$FLAGS); + rel_perm->rel_private = !REL.RDB$PRIVATE_FLAG.NULL && REL.RDB$PRIVATE_FLAG != 0; + if (!REL.RDB$RELATION_TYPE.NULL) rel_perm->rel_flags |= MET_get_rel_flags_from_TYPE(REL.RDB$RELATION_TYPE); @@ -3271,8 +3303,18 @@ ScanResult jrd_rel::scan(thread_db* tdbb, ObjectBase::Flag& flags) { csb->csb_g_flags |= csb_get_dependencies; field->fld_source = PAR_make_field(tdbb, csb, view_context, (TEXT*) p); - const QualifiedName depName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME); - MET_store_dependencies(tdbb, csb->csb_dependencies, nullptr, depName, obj_view); + + QualifiedName depName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME); + ObjectType depType = obj_view; + + if (!REL.RDB$PACKAGE_NAME.NULL) + { + depName = QualifiedName(REL.RDB$PACKAGE_NAME, REL.RDB$SCHEMA_NAME); + depType = (!REL.RDB$PRIVATE_FLAG.NULL && REL.RDB$PRIVATE_FLAG != 0) ? + obj_package_body : obj_package_header; + } + + MET_store_dependencies(tdbb, csb->csb_dependencies, nullptr, depName, depType); } else field->fld_source = PAR_make_field(tdbb, csb, view_context, (TEXT*) p); @@ -3752,7 +3794,7 @@ static void lookup_view_contexts( thread_db* tdbb, jrd_rel* view) ViewContext* view_context = FB_NEW_POOL(*view->rel_pool) ViewContext(*view->rel_pool, V.RDB$CONTEXT_NAME, - QualifiedName(V.RDB$RELATION_NAME, V.RDB$RELATION_SCHEMA_NAME), + QualifiedName(V.RDB$RELATION_NAME, V.RDB$RELATION_SCHEMA_NAME, V.RDB$PACKAGE_NAME), V.RDB$VIEW_CONTEXT, (V.RDB$CONTEXT_TYPE.NULL ? VCT_TABLE : ViewContextType(V.RDB$CONTEXT_TYPE))); @@ -4128,7 +4170,7 @@ void RelationPermanent::scanPartners(thread_db* tdbb) * scan of primary dependencies on relation's primary key. * **************************************/ - if (rel_flags & REL_temp_ltt) + if ((rel_flags & REL_temp_ltt)) return; Attachment* attachment = tdbb->getAttachment(); @@ -4155,8 +4197,10 @@ void RelationPermanent::scanPartners(thread_db* tdbb) IND IN RDB$INDICES WITH RC.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND IDX.RDB$SCHEMA_NAME EQ getName().schema.c_str() AND + IDX.RDB$PACKAGE_NAME MISSING AND IDX.RDB$RELATION_NAME EQ getName().object.c_str() AND IND.RDB$SCHEMA_NAME EQ IDX.RDB$FOREIGN_KEY_SCHEMA_NAME AND + IND.RDB$PACKAGE_NAME MISSING AND IND.RDB$INDEX_NAME EQ IDX.RDB$FOREIGN_KEY AND IDX.RDB$INDEX_ID > 0 AND IND.RDB$INDEX_ID > 0 AND @@ -4201,8 +4245,10 @@ void RelationPermanent::scanPartners(thread_db* tdbb) IDX.RDB$INDEX_ID > 0 AND IND.RDB$INDEX_ID > 0 AND IDX.RDB$SCHEMA_NAME EQ getName().schema.c_str() AND + IDX.RDB$PACKAGE_NAME MISSING AND IDX.RDB$RELATION_NAME EQ getName().object.c_str() AND IND.RDB$FOREIGN_KEY_SCHEMA_NAME EQ IDX.RDB$SCHEMA_NAME AND + IND.RDB$PACKAGE_NAME MISSING AND IND.RDB$FOREIGN_KEY EQ IDX.RDB$INDEX_NAME { const QualifiedName partnerRelationName(IND.RDB$RELATION_NAME, IND.RDB$SCHEMA_NAME); @@ -4958,13 +5004,15 @@ ScanResult IndexVersion::scan(thread_db* tdbb, ObjectBase::Flag flags) FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction) IND IN RDB$INDICES CROSS REL IN RDB$RELATIONS - OVER RDB$RELATION_NAME, RDB$SCHEMA_NAME - WITH IND.RDB$INDEX_ID EQ getId() + 1 - AND REL.RDB$RELATION_ID EQ relId + WITH IND.RDB$INDEX_ID EQ getId() + 1 AND + REL.RDB$RELATION_ID EQ relId AND + REL.RDB$SCHEMA_NAME EQ IND.RDB$SCHEMA_NAME AND + REL.RDB$PACKAGE_NAME EQUIV IND.RDB$PACKAGE_NAME AND + REL.RDB$RELATION_NAME EQ IND.RDB$RELATION_NAME { found = true; - idv_name = QualifiedName(IND.RDB$INDEX_NAME, IND.RDB$SCHEMA_NAME); + idv_name = QualifiedName(IND.RDB$INDEX_NAME, IND.RDB$SCHEMA_NAME, IND.RDB$PACKAGE_NAME); idv_uniqFlag = IND.RDB$UNIQUE_FLAG; idv_segmentCount = IND.RDB$SEGMENT_COUNT; idv_type = IND.RDB$INDEX_TYPE; @@ -5026,6 +5074,7 @@ IndexVersion* RelationPermanent::lookup_index(thread_db* tdbb, const QualifiedNa // We need to look up the index in RDB$INDICES fb_assert(getName().schema == name.schema); + fb_assert(getName().package == name.package); AutoCacheRequest request(tdbb, irq_get_index_by_name, IRQ_REQUESTS); IndexVersion* idv = nullptr; @@ -5034,6 +5083,7 @@ IndexVersion* RelationPermanent::lookup_index(thread_db* tdbb, const QualifiedNa IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ name.object.c_str() AND IDX.RDB$SCHEMA_NAME EQ getName().schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND IDX.RDB$RELATION_NAME EQ getName().object.c_str() { if (!idv) @@ -5070,6 +5120,7 @@ Cached::Index* RelationPermanent::lookupIndex(thread_db* tdbb, const QualifiedNa IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ name.object.c_str() AND IDX.RDB$SCHEMA_NAME EQ getName().schema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND IDX.RDB$RELATION_NAME EQ getName().object.c_str() { if (!idp) @@ -5229,27 +5280,28 @@ std::optional MET_qualify_existing_name(thread_db* tdbb, QualifiedNa case obj_index: { - fb_assert(name.package.isEmpty()); - - // Check LTT indexes first - for (const auto& lttEntry : attachment->att_local_temporary_tables) + // Check Created LTT indexes first + if (name.package.isEmpty()) { - const auto ltt = lttEntry.second; + for (const auto& lttEntry : attachment->att_local_temporary_tables) + { + const auto ltt = lttEntry.second; - if (ltt->name.schema != searchSchema) - continue; + if (ltt->name.schema != searchSchema) + continue; - for (const auto& index : ltt->indexes) - { - if (index.name.object == name.object) + for (const auto& index : ltt->indexes) { - found = true; - break; + if (index.name.object == name.object) + { + found = true; + break; + } } - } - if (found) - break; + if (found) + break; + } } if (!found) @@ -5260,6 +5312,7 @@ std::optional MET_qualify_existing_name(thread_db* tdbb, QualifiedNa FOR (REQUEST_HANDLE handle) IDX IN RDB$INDICES WITH IDX.RDB$SCHEMA_NAME EQ searchSchema.c_str() AND + IDX.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '') AND IDX.RDB$INDEX_NAME EQ name.object.c_str() { found = true; @@ -5491,4 +5544,3 @@ jrd_rel* MetadataCache::getLtt(thread_db* tdbb, const QualifiedName& name) return nullptr; } - diff --git a/src/jrd/relations.h b/src/jrd/relations.h index 9bae656cb5c..d4d9c20a210 100644 --- a/src/jrd/relations.h +++ b/src/jrd/relations.h @@ -85,6 +85,7 @@ RELATION(nam_i_segments, rel_segments, ODS_8_0, rel_persistent) FIELD(f_seg_position, nam_f_position, fld_f_position, 1, ODS_8_0) FIELD(f_seg_statistics, nam_statistics, fld_statistics, 1, ODS_11_0) FIELD(f_seg_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) + FIELD(f_seg_pkg_name, nam_pkg_name, fld_pkg_name, 1, ODS_14_0) END_RELATION // Relation 4 (RDB$INDICES) @@ -107,6 +108,7 @@ RELATION(nam_indices, rel_indices, ODS_8_0, rel_persistent) FIELD(f_idx_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) FIELD(f_idx_foreign_schema, nam_foreign_sch_name, fld_sch_name, 1, ODS_14_0) FIELD(f_idx_format, nam_fmt, fld_format, 1, ODS_14_0) + FIELD(f_idx_pkg_name, nam_pkg_name, fld_pkg_name, 1, ODS_14_0) END_RELATION // Relation 5 (RDB$RELATION_FIELDS) @@ -134,6 +136,7 @@ RELATION(nam_r_fields, rel_rfr, ODS_8_0, rel_persistent) FIELD(f_rfr_identity_type, nam_identity_type, fld_identity_type, 1, ODS_12_0) FIELD(f_rfr_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) FIELD(f_rfr_field_source_schema, nam_field_source_sch_name, fld_sch_name, 1, ODS_14_0) + FIELD(f_rfr_pkg_name, nam_pkg_name, fld_pkg_name, 1, ODS_14_0) END_RELATION // Relation 6 (RDB$RELATIONS) @@ -157,6 +160,8 @@ RELATION(nam_relations, rel_relations, ODS_8_0, rel_persistent) FIELD(f_rel_type, nam_r_type, fld_r_type, 0, ODS_11_1) FIELD(f_rel_sql_security, nam_sql_security, fld_b_sql_security, 1, ODS_13_0) FIELD(f_rel_schema, nam_sch_name, fld_sch_name, 1, ODS_14_0) + FIELD(f_rel_pkg_name, nam_pkg_name, fld_pkg_name, 1, ODS_14_0) + FIELD(f_rel_private_flag, nam_private_flag, fld_flag_nullable, 1, ODS_14_0) END_RELATION // Relation 7 (RDB$VIEW_RELATIONS) @@ -750,6 +755,7 @@ RELATION(nam_mon_tab_stats, rel_mon_tab_stats, ODS_12_0, rel_virtual) FIELD(f_mon_tab_rec_stat_id, nam_mon_rec_stat_id, fld_stat_id, 0, ODS_12_0) FIELD(f_mon_tab_sch_name, nam_mon_sch_name, fld_sch_name, 0, ODS_14_0) FIELD(f_mon_tab_type, nam_mon_tab_type, fld_tab_type, 0, ODS_14_0) + FIELD(f_mon_tab_pkg_name, nam_mon_pkg_name, fld_pkg_name, 0, ODS_14_0) END_RELATION // Relation 50 (RDB$TIME_ZONES) diff --git a/src/jrd/scl.epp b/src/jrd/scl.epp index 7640072716d..43bd43c3ec5 100644 --- a/src/jrd/scl.epp +++ b/src/jrd/scl.epp @@ -464,9 +464,8 @@ void SCL_check_index(thread_db* tdbb, const QualifiedName& index_name, UCHAR ind // No security to check for if the index is not yet created - if ((index_name.object.length() == 0) && (index_id < 1)) { + if (index_name.object.isEmpty() && index_id < 1) return; - } QualifiedName reln_name, aux_idx_name; auto idx_name_ptr = &index_name; @@ -478,15 +477,20 @@ void SCL_check_index(thread_db* tdbb, const QualifiedName& index_name, UCHAR ind // No need to cache this request handle, it's only used when // new constraints are created - if (index_id < 1) - { - FOR(REQUEST_HANDLE request) + if (index_id < 1) + { + FOR(REQUEST_HANDLE request) IND IN RDB$INDICES - CROSS REL IN RDB$RELATIONS OVER RDB$SCHEMA_NAME, RDB$RELATION_NAME + CROSS REL IN RDB$RELATIONS WITH IND.RDB$SCHEMA_NAME EQ index_name.schema.c_str() AND - IND.RDB$INDEX_NAME EQ index_name.object.c_str() + IND.RDB$PACKAGE_NAME EQUIV NULLIF(index_name.package.c_str(), '') AND + IND.RDB$INDEX_NAME EQ index_name.object.c_str() AND + REL.RDB$SCHEMA_NAME EQ IND.RDB$SCHEMA_NAME AND + REL.RDB$PACKAGE_NAME EQUIV IND.RDB$PACKAGE_NAME AND + REL.RDB$RELATION_NAME EQ IND.RDB$RELATION_NAME { - reln_name = QualifiedName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME); + reln_name = QualifiedName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME, REL.RDB$PACKAGE_NAME); + if (!REL.RDB$SECURITY_CLASS.NULL) s_class = SCL_get_class(tdbb, REL.RDB$SECURITY_CLASS); if (!REL.RDB$DEFAULT_CLASS.NULL) @@ -500,17 +504,23 @@ void SCL_check_index(thread_db* tdbb, const QualifiedName& index_name, UCHAR ind idx_name_ptr = &aux_idx_name; FOR (REQUEST_HANDLE request) IND IN RDB$INDICES - CROSS REL IN RDB$RELATIONS OVER RDB$SCHEMA_NAME, RDB$RELATION_NAME + CROSS REL IN RDB$RELATIONS WITH IND.RDB$SCHEMA_NAME EQ relation_name_ptr->schema.c_str() AND + IND.RDB$PACKAGE_NAME EQUIV NULLIF(relation_name_ptr->package.c_str(), '') AND IND.RDB$RELATION_NAME EQ relation_name_ptr->object.c_str() AND - IND.RDB$INDEX_ID EQ index_id + IND.RDB$INDEX_ID EQ index_id AND + REL.RDB$SCHEMA_NAME EQ IND.RDB$SCHEMA_NAME AND + REL.RDB$PACKAGE_NAME EQUIV IND.RDB$PACKAGE_NAME AND + REL.RDB$RELATION_NAME EQ IND.RDB$RELATION_NAME { - reln_name = QualifiedName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME); - aux_idx_name = QualifiedName(IND.RDB$INDEX_NAME, IND.RDB$SCHEMA_NAME); + reln_name = QualifiedName(REL.RDB$RELATION_NAME, REL.RDB$SCHEMA_NAME, REL.RDB$PACKAGE_NAME); + aux_idx_name = QualifiedName(IND.RDB$INDEX_NAME, IND.RDB$SCHEMA_NAME, IND.RDB$PACKAGE_NAME); + if (!REL.RDB$SECURITY_CLASS.NULL) s_class = SCL_get_class(tdbb, REL.RDB$SECURITY_CLASS); if (!REL.RDB$DEFAULT_CLASS.NULL) default_s_class = SCL_get_class(tdbb, REL.RDB$DEFAULT_CLASS); + systemFlag = REL.RDB$SYSTEM_FLAG; } END_FOR @@ -529,24 +539,46 @@ void SCL_check_index(thread_db* tdbb, const QualifiedName& index_name, UCHAR ind if (reln_name.object.isEmpty()) return; + if (reln_name.package.hasData()) + { + request.reset(); + + FOR(REQUEST_HANDLE request) + PKG IN RDB$PACKAGES + WITH PKG.RDB$SCHEMA_NAME EQ reln_name.schema.c_str() AND + PKG.RDB$PACKAGE_NAME EQ reln_name.package.c_str() + { + s_class = !PKG.RDB$SECURITY_CLASS.NULL ? + SCL_get_class(tdbb, PKG.RDB$SECURITY_CLASS) : nullptr; + + // Packaged relations inherit relation/field fallback checks from the package. + default_s_class = s_class; + } + END_FOR + } + SCL_check_access(tdbb, s_class, 0, {}, mask, obj_relations, false, reln_name); request.reset(); // Check if the field used in the index has the appropriate // permission. If the field in question does not have a security class - // defined, then the default security class for the table applies for that - // field. + // defined, then the table default security class applies. For packaged + // relations, this fallback uses the package security class. // No need to cache this request handle, it's only used when // new constraints are created + fb_assert(reln_name.package == idx_name_ptr->package); FOR(REQUEST_HANDLE request) ISEG IN RDB$INDEX_SEGMENTS - CROSS RF IN RDB$RELATION_FIELDS OVER RDB$SCHEMA_NAME, RDB$FIELD_NAME - WITH RF.RDB$SCHEMA_NAME EQ reln_name.schema.c_str() AND + CROSS RF IN RDB$RELATION_FIELDS + WITH RF.RDB$SCHEMA_NAME EQ ISEG.RDB$SCHEMA_NAME AND + RF.RDB$PACKAGE_NAME EQUIV ISEG.RDB$PACKAGE_NAME AND + RF.RDB$FIELD_NAME EQ ISEG.RDB$FIELD_NAME AND + RF.RDB$SCHEMA_NAME EQ reln_name.schema.c_str() AND + RF.RDB$PACKAGE_NAME EQUIV NULLIF(reln_name.package.c_str(), '') AND RF.RDB$RELATION_NAME EQ reln_name.object.c_str() AND - ISEG.RDB$SCHEMA_NAME EQ idx_name_ptr->schema.c_str() AND ISEG.RDB$INDEX_NAME EQ idx_name_ptr->object.c_str() { s_class = (!RF.RDB$SECURITY_CLASS.NULL) ? @@ -729,6 +761,12 @@ void SCL_check_relation(thread_db* tdbb, const QualifiedName& name, SecurityClas **************************************/ SET_TDBB(tdbb); + if (name.package.hasData()) + { + SCL_check_package(tdbb, name.getSchemaAndPackage(), mask); + return; + } + Jrd::Attachment* const attachment = tdbb->getAttachment(); const SecurityClass* s_class = NULL; @@ -1808,4 +1846,3 @@ static bool check_object(thread_db* tdbb, } return found; } - diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index fb959b29e3d..4b8c40368ff 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -2092,7 +2092,9 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) } EVL_field(0, rpb->rpb_record, f_rel_name, &desc); EVL_field(0, rpb->rpb_record, f_rel_schema, &schemaDesc); - DFW_post_work(transaction, dfw_delete_relation, &desc, &schemaDesc, id); + if (EVL_field(0, rpb->rpb_record, f_rel_pkg_name, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); + DFW_post_work(transaction, dfw_delete_relation, &desc, &schemaDesc, id, object_name.package); } break; @@ -2165,6 +2167,10 @@ bool VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_rfr_schema, &schemaDesc); MOV_get_metaname(tdbb, &schemaDesc, object_name.schema); + + if (EVL_field(0, rpb->rpb_record, f_rfr_pkg_name, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); + EVL_field(0, rpb->rpb_record, f_rfr_rname, &desc); MOV_get_metaname(tdbb, &desc, object_name.object); @@ -3408,6 +3414,8 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j case rel_relations: EVL_field(0, org_rpb->rpb_record, f_rel_schema, &schemaDesc); EVL_field(0, org_rpb->rpb_record, f_rel_name, &desc1); + if (EVL_field(0, org_rpb->rpb_record, f_rel_pkg_name, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); MOV_get_metaname(tdbb, &schemaDesc, object_name.schema); MOV_get_metaname(tdbb, &desc1, object_name.object); @@ -3615,6 +3623,10 @@ bool VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j { dsc dscId; EVL_field(0, new_rpb->rpb_record, f_idx_schema, &schemaDesc); + + if (EVL_field(0, new_rpb->rpb_record, f_idx_pkg_name, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); + EVL_field(0, new_rpb->rpb_record, f_idx_name, &desc1); EVL_field(0, new_rpb->rpb_record, f_idx_relation, &desc2); EVL_field(0, new_rpb->rpb_record, f_idx_id, &dscId); @@ -4343,11 +4355,17 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) protect_system_table_insert(tdbb, request, relation); EVL_field(0, rpb->rpb_record, f_rel_schema, &schemaDesc); EVL_field(0, rpb->rpb_record, f_rel_name, &desc); - DFW_post_work(transaction, dfw_create_relation, &desc, &schemaDesc, 0); + if (EVL_field(0, rpb->rpb_record, f_rel_pkg_name, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); + DFW_post_work(transaction, dfw_create_relation, &desc, &schemaDesc, 0, object_name.package); set_system_flag(tdbb, rpb->rpb_record, f_rel_sys_flag); set_owner_name(tdbb, rpb->rpb_record, f_rel_owner); - if (set_security_class(tdbb, rpb->rpb_record, f_rel_class)) - DFW_post_work(transaction, dfw_grant, &desc, &schemaDesc, obj_relation); + + if (object_name.package.isEmpty()) + { + if (set_security_class(tdbb, rpb->rpb_record, f_rel_class)) + DFW_post_work(transaction, dfw_grant, &desc, &schemaDesc, obj_relation); + } break; case rel_packages: @@ -4431,6 +4449,10 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) { EVL_field(0, rpb->rpb_record, f_idx_schema, &schemaDesc); + + if (EVL_field(0, rpb->rpb_record, f_idx_pkg_name, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); + EVL_field(0, rpb->rpb_record, f_idx_relation, &desc); MOV_get_metaname(tdbb, &schemaDesc, object_name.schema); MOV_get_metaname(tdbb, &desc, object_name.object); @@ -4450,6 +4472,10 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_rfr: protect_system_table_insert(tdbb, request, relation); EVL_field(0, rpb->rpb_record, f_rfr_schema, &schemaDesc); + + if (EVL_field(0, rpb->rpb_record, f_rfr_pkg_name, &desc2)) + MOV_get_metaname(tdbb, &desc2, object_name.package); + EVL_field(0, rpb->rpb_record, f_rfr_rname, &desc); MOV_get_metaname(tdbb, &schemaDesc, object_name.schema); MOV_get_metaname(tdbb, &desc, object_name.object); @@ -5072,6 +5098,10 @@ static void check_rel_field_class(thread_db* tdbb, QualifiedName object_name; MOV_get_metaname(tdbb, &schemaDesc, object_name.schema); + + if (EVL_field(0, rpb->rpb_record, f_rfr_pkg_name, &desc)) + MOV_get_metaname(tdbb, &desc, object_name.package); + MOV_get_metaname(tdbb, &desc, object_name.object); RelationPermanent::newVersion(tdbb, object_name); } diff --git a/src/yvalve/gds.cpp b/src/yvalve/gds.cpp index 162fa9d8d65..113dc609027 100644 --- a/src/yvalve/gds.cpp +++ b/src/yvalve/gds.cpp @@ -360,6 +360,7 @@ static constexpr UCHAR relation2[] = { op_byte, op_literal, op_line, op_indent, op_byte, op_literal, op_pad, op_byte, op_line, 0}, relation3[] = { op_line, op_indent, op_byte, op_literal, + op_line, op_indent, op_byte, op_literal, op_line, op_indent, op_byte, op_literal, op_line, op_indent, op_byte, op_literal, op_line, op_indent, op_byte, op_line, 0},