From ed2c21d52c78cabefe54482c2b9482e76f550579 Mon Sep 17 00:00:00 2001 From: Dmitry Yemanov Date: Fri, 13 Nov 2026 12:52:07 +0300 Subject: [PATCH] Improve metadata lookup during restore: use tree-based dictionary instead of linear search --- src/burp/burp.cpp | 28 +++++++-- src/burp/burp.h | 41 ++++++++++++- src/burp/restore.epp | 135 +++++++++++++++++-------------------------- 3 files changed, 115 insertions(+), 89 deletions(-) diff --git a/src/burp/burp.cpp b/src/burp/burp.cpp index 6b2752e6319..eb3eb9395bb 100644 --- a/src/burp/burp.cpp +++ b/src/burp/burp.cpp @@ -46,7 +46,7 @@ #include "../burp/std_desc.h" #include "../burp/split/spit.h" #include "../jrd/license.h" - +#include "../jrd/obj.h" #include "../common/classes/timestamp.h" #include "../burp/burp_proto.h" #include "../burp/backu_proto.h" @@ -1147,7 +1147,7 @@ int gbak(Firebird::UtilSvc* uSvc) BURP_error(441, true, SafeArg() << in_sw_tab->in_sw_name); // parameter for option -@1 is missing } - tdgbl->loadMapping(argv[itr], tdgbl->tablespace_mapping, false); + tdgbl->loadMapping(argv[itr], tdgbl->tablespaceFileMap, false); break; case IN_SW_BURP_TS_ORIGINAL_PATHS: if (tdgbl->gbl_sw_ts_orig_paths) @@ -1162,11 +1162,11 @@ int gbak(Firebird::UtilSvc* uSvc) } { - Firebird::string ts_name = argv[++itr]; - Firebird::string ts_path = argv[++itr]; + const string ts_name(argv[++itr]); + const string ts_path(argv[++itr]); if (ts_name.length() && ts_path.length()) - tdgbl->tablespace_mapping.put(ts_name, ts_path); + tdgbl->tablespaceFileMap.put(ts_name, ts_path); } break; @@ -2790,6 +2790,24 @@ void BurpGlobals::setupSkipIncludePattern(const string& regexp, USHORT alreadySe } } +burp_pkg* BurpGlobals::getPackage(const QualifiedMetaString& name) const +{ + const auto result = metaDictionary.get({obj_package_header, name}); + return result ? result->package : nullptr; +} + +burp_rel* BurpGlobals::getRelation(const QualifiedMetaString& name) const +{ + const auto result = metaDictionary.get({obj_relation, name}); + return result ? result->relation : nullptr; +} + +burp_prc* BurpGlobals::getProcedure(const QualifiedMetaString& name) const +{ + const auto result = metaDictionary.get({obj_procedure, name}); + return result ? result->procedure : nullptr; +} + Firebird::string BurpGlobals::toSystem(const Firebird::PathName& from) { Firebird::string to = from.ToString(); diff --git a/src/burp/burp.h b/src/burp/burp.h index f20f77d48fc..a1cb78b94f9 100644 --- a/src/burp/burp.h +++ b/src/burp/burp.h @@ -1067,7 +1067,8 @@ class BurpGlobals : public Firebird::ThreadData, public GblPool firstMap(true), firstDbc(true), stdIoMode(false), - tablespace_mapping(*getDefaultMemoryPool()) + tablespaceFileMap(*getDefaultMemoryPool()), + metaDictionary(*getDefaultMemoryPool()) { // this is VERY dirty hack to keep current (pre-FB2) behaviour memset (&gbl_database_file_name, 0, @@ -1321,9 +1322,43 @@ class BurpGlobals : public Firebird::ThreadData, public GblPool Firebird::AutoPtr skipDataMatcher; Firebird::AutoPtr includeSchemaDataMatcher; Firebird::AutoPtr includeDataMatcher; - StringMap tablespace_mapping; // Will be used to overwrite filename of tablespace with given name + StringMap tablespaceFileMap; // Will be used to overwrite filename of tablespace with given name + + union MetaObjectValue + { + burp_pkg* package; + burp_rel* relation; + burp_prc* procedure; + burp_meta_obj* object; + + MetaObjectValue(burp_pkg* value) : package(value) {} + MetaObjectValue(burp_rel* value) : relation(value) {} + MetaObjectValue(burp_prc* value) : procedure(value) {} + MetaObjectValue(burp_meta_obj* value) : object(value) {} + }; + + typedef Firebird::RightPooledPair MetaObjectKey; + typedef Firebird::LeftPooledMap MetaDictionary; + + MetaDictionary metaDictionary; + + template + void putToDictionary(SSHORT type, const Firebird::QualifiedMetaString& name, T* value) + { + fb_assert(name.hasData() && value != nullptr); + metaDictionary.put({type, name}, value); + } + + burp_meta_obj* getMetaObject(SSHORT type, const Firebird::QualifiedMetaString& name) const + { + const auto result = metaDictionary.get({type, name}); + return result ? result->object : nullptr; + } + + burp_pkg* getPackage(const Firebird::QualifiedMetaString& name) const; + burp_rel* getRelation(const Firebird::QualifiedMetaString& name) const; + burp_prc* getProcedure(const Firebird::QualifiedMetaString& name) const; -public: Firebird::string toSystem(const Firebird::PathName& from); enum StatCounter { TIME_TOTAL = 0, TIME_DELTA, READS, WRITES, LAST_COUNTER}; diff --git a/src/burp/restore.epp b/src/burp/restore.epp index f1b5eb56354..0c3786eeca2 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -94,7 +94,6 @@ using namespace Burp; DATABASE DB = STATIC FILENAME "yachts.lnk"; - namespace // unnamed, private { @@ -122,7 +121,6 @@ void decompress(BurpGlobals* tdgbl, UCHAR*, ULONG); void eat_blob(BurpGlobals* tdgbl); void eat_text(BurpGlobals* tdgbl); void eat_text2(BurpGlobals* tdgbl); -burp_rel* find_relation(BurpGlobals* tdgbl, const QualifiedMetaString&); void fix_security_class_name(BurpGlobals* tdgbl, TEXT* sec_class, bool is_field); // CVC: when do these functions return false indeed??? // get_acl and get_index are the only exceptions but ironically their @@ -283,6 +281,7 @@ static inline void collect_missing_privs(BurpGlobals* tdgbl, USHORT type, const object->obj_name = name; object->obj_class = hasSecClass; tdgbl->miss_privs = object; + tdgbl->putToDictionary(type, name, object); } } // namespace @@ -1426,33 +1425,6 @@ void eat_text2(BurpGlobals* tdgbl) MVOL_skip_block(tdgbl, len); } -burp_rel* find_relation(BurpGlobals* tdgbl, const QualifiedMetaString& name) -{ -/************************************** - * - * f i n d _ r e l a t i o n - * - ************************************** - * - * Functional description - * Given a relation name, find the relation block. If there isn't - * one, produce a fatal error. - * - **************************************/ - - // Why isn't strcmp used here? - for (burp_rel* relation = tdgbl->relations; relation; relation = relation->rel_next) - { - if (relation->rel_name == name) - return relation; - } - - BURP_error(35, true, SafeArg() << name.toQuotedString().c_str()); - // msg 35 can't find relation %s - - return NULL; -} - void fix_security_class_name(BurpGlobals* tdgbl, TEXT* sec_class, bool is_field) { /************************************** @@ -4140,7 +4112,7 @@ burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation, USHORT id) BASED_ON RDB$RELATIONS.RDB$TABLESPACE_NAME tablespaceName; GET_TEXT(tablespaceName); - const auto str = tdgbl->tablespace_mapping.get(tablespaceName); + const auto str = tdgbl->tablespaceFileMap.get(tablespaceName); if (str && !str->equals(PRIMARY_TABLESPACE_NAME)) strcpy(X.RDB$TABLESPACE_NAME, tablespaceName); @@ -7013,7 +6985,7 @@ bool get_index(BurpGlobals* tdgbl, const burp_rel* relation) BASED_ON RDB$RELATIONS.RDB$TABLESPACE_NAME tablespaceName; GET_TEXT(tablespaceName); - const auto str = tdgbl->tablespace_mapping.get(tablespaceName); + const auto str = tdgbl->tablespaceFileMap.get(tablespaceName); if (str && !str->equals(PRIMARY_TABLESPACE_NAME)) strcpy(X.RDB$TABLESPACE_NAME, tablespaceName); @@ -7213,9 +7185,6 @@ bool get_package(BurpGlobals* tdgbl) if (tdgbl->runtimeODS >= DB_VERSION_DDL14) package->pkg_name.schema = PUBLIC_SCHEMA; - package->pkg_next = tdgbl->packages; - tdgbl->packages = package; - STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_package_req_handle1) X IN RDB$PACKAGES { @@ -7299,6 +7268,12 @@ bool get_package(BurpGlobals* tdgbl) general_on_error(); END_ERROR + // Package object is now populated, register it in the dictionary + tdgbl->putToDictionary(obj_package_header, package->pkg_name, package); + + package->pkg_next = tdgbl->packages; + tdgbl->packages = package; + return true; } @@ -7350,7 +7325,7 @@ bool get_tablespace(BurpGlobals* tdgbl) SSHORT len = GET_TEXT(tablespace_name); MISC_terminate(tablespace_name, temp, len, sizeof(temp)); - const auto str = tdgbl->tablespace_mapping.get(tablespace_name); + const auto str = tdgbl->tablespaceFileMap.get(tablespace_name); if (str && str->equals(PRIMARY_TABLESPACE_NAME)) moveToPrimary = true; // Move the contents of this TS to PRIMARY else @@ -7439,7 +7414,7 @@ bool get_tablespace(BurpGlobals* tdgbl) X.RDB$READ_ONLY = (USHORT) read_only; Firebird::string newFile; - if (tdgbl->tablespace_mapping.get(X.RDB$TABLESPACE_NAME, newFile)) + if (tdgbl->tablespaceFileMap.get(X.RDB$TABLESPACE_NAME, newFile)) { PathUtils::fixupSeparators(newFile.begin()); Firebird::PathName ts_file_name; @@ -7505,9 +7480,6 @@ bool get_procedure(BurpGlobals* tdgbl) if (tdgbl->runtimeODS >= DB_VERSION_DDL14) procedure->prc_name.schema = PUBLIC_SCHEMA; - procedure->prc_next = tdgbl->procedures; - tdgbl->procedures = procedure; - if (tdgbl->runtimeODS >= DB_VERSION_DDL11_1) { STORE (TRANSACTION_HANDLE local_trans @@ -7811,6 +7783,14 @@ bool get_procedure(BurpGlobals* tdgbl) END_ERROR } + // Procedure object is now populated, register it in the dictionary + // (unless this procedure is packaged) + if (procedure->prc_name.package.isEmpty()) + tdgbl->putToDictionary(obj_procedure, procedure->prc_name, procedure); + + procedure->prc_next = tdgbl->procedures; + tdgbl->procedures = procedure; + // at the end of prms for a procedure is the rec_procedure_end marker while (get(tdgbl) == rec_procedure_prm) @@ -8419,9 +8399,6 @@ bool get_relation(BurpGlobals* tdgbl, Coordinator* coord, RestoreRelationTask* t if (tdgbl->runtimeODS >= DB_VERSION_DDL14) relation->rel_name.schema = PUBLIC_SCHEMA; - relation->rel_next = tdgbl->relations; - tdgbl->relations = relation; - TEXT temp[GDS_NAME_LEN]; att_type attribute; scan_attr_t scan_next_attr; @@ -8538,7 +8515,7 @@ bool get_relation(BurpGlobals* tdgbl, Coordinator* coord, RestoreRelationTask* t GET_TEXT(tablespaceName); - const auto str = tdgbl->tablespace_mapping.get(tablespaceName); + const auto str = tdgbl->tablespaceFileMap.get(tablespaceName); if (str && !str->equals(PRIMARY_TABLESPACE_NAME)) strcpy(tableSpace, tablespaceName); @@ -8556,6 +8533,12 @@ bool get_relation(BurpGlobals* tdgbl, Coordinator* coord, RestoreRelationTask* t relation->rel_system = (sys_flag != fb_sysflag_user); relation->rel_type = (SSHORT) type; + // Relation object is now populated, register it in the dictionary + tdgbl->putToDictionary(obj_relation, relation->rel_name, relation); + + relation->rel_next = tdgbl->relations; + tdgbl->relations = relation; + // If this is a view and there is a global transaction then use it Firebird::ITransaction* local_trans; if (view_blr_null || !tdgbl->global_trans) @@ -8859,7 +8842,12 @@ bool get_relation_data(BurpGlobals* tdgbl, Coordinator* coord, RestoreRelationTa case att_relation_name: GET_TEXT(temp); name.object = temp; - relation = find_relation(tdgbl, name); + relation = tdgbl->getRelation(name); + if (!relation) + { + BURP_error(35, true, SafeArg() << name.toQuotedString().c_str()); + // msg 35 can't find relation %s + } break; default: @@ -10509,7 +10497,7 @@ bool get_user_privilege(BurpGlobals* tdgbl) scan_attr_t scan_next_attr; USHORT flags = 0; - QualifiedMetaString user, relation; + QualifiedMetaString user, object_name; TEXT temp[GDS_NAME_LEN]; BASED_ON RDB$USER_PRIVILEGES.RDB$GRANTOR grantor; BASED_ON RDB$USER_PRIVILEGES.RDB$PRIVILEGE privilege; @@ -10558,14 +10546,14 @@ bool get_user_privilege(BurpGlobals* tdgbl) case att_priv_object_schema_name: GET_TEXT(temp); - relation.schema = temp; + object_name.schema = temp; break; case att_priv_object_name: flags |= USER_PRIV_OBJECT_NAME; // default OBJECT_TYPE to RELATION GET_TEXT(temp); - relation.object = temp; + object_name.object = temp; break; case att_priv_field_name: @@ -10592,8 +10580,8 @@ bool get_user_privilege(BurpGlobals* tdgbl) } } - if (relation.schema.isEmpty() && tdgbl->runtimeODS >= DB_VERSION_DDL14 && isSchemaBoundObject(object_type)) - relation.schema = PUBLIC_SCHEMA; + if (object_name.schema.isEmpty() && tdgbl->runtimeODS >= DB_VERSION_DDL14 && isSchemaBoundObject(object_type)) + object_name.schema = PUBLIC_SCHEMA; // Check if object exists Firebird::ITransaction* local_trans = nullptr; @@ -10604,39 +10592,28 @@ bool get_user_privilege(BurpGlobals* tdgbl) switch (object_type) { case obj_package_header: - for (const burp_pkg* pkg = tdgbl->packages; pkg; pkg = pkg->pkg_next) + if (tdgbl->getPackage(object_name)) { - if (pkg->pkg_name == relation) - { - exists = true; - local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; - break; - } + exists = true; + local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; } break; case obj_procedure: - for (const burp_prc* proc = tdgbl->procedures; proc; proc = proc->prc_next) + if (const auto procedure = tdgbl->getProcedure(object_name)) { - if (proc->prc_name.package.isEmpty() && proc->prc_name == relation) - { - exists = true; - local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; - break; - } + fb_assert(procedure->prc_name.package.isEmpty()); + exists = true; + local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; } break; case obj_relation: - for (const burp_rel* rel = tdgbl->relations; rel; rel = rel->rel_next) + if (const auto relation = tdgbl->getRelation(object_name)) { - if (rel->rel_name == relation) - { - exists = true; - if (rel->rel_flags & REL_view) - local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; - break; - } + exists = true; + if (relation->rel_flags & REL_view) + local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; } break; @@ -10647,14 +10624,10 @@ bool get_user_privilege(BurpGlobals* tdgbl) case obj_field: case obj_generator: case obj_schema: - for (burp_meta_obj* object = tdgbl->miss_privs; object; object = object->obj_next) + if (const auto metaObject = tdgbl->getMetaObject(object_type, object_name)) { - if (object->obj_type == object_type && object->obj_name == relation) - { - if (object->obj_class) - exists = true; - break; - } + if (metaObject->obj_class) + exists = true; } break; @@ -10727,14 +10700,14 @@ bool get_user_privilege(BurpGlobals* tdgbl) X.RDB$GRANT_OPTION = grant_option; } - if (relation.schema.hasData()) + if (object_name.schema.hasData()) { - strcpy(X.RDB$RELATION_SCHEMA_NAME, relation.schema.c_str()); + strcpy(X.RDB$RELATION_SCHEMA_NAME, object_name.schema.c_str()); X.RDB$RELATION_SCHEMA_NAME.NULL = FALSE; } if (flags & USER_PRIV_OBJECT_NAME) - strcpy(X.RDB$RELATION_NAME, relation.object.c_str()); + strcpy(X.RDB$RELATION_NAME, object_name.object.c_str()); if (flags & USER_PRIV_FIELD_NAME) {