/* * PROGRAM: JRD Backup and Restore Program * MODULE: restore.epp * DESCRIPTION: Restore routine * * The contents of this file are subject to the Interbase Public * License Version 1.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy * of the License at http://www.Inprise.com/IPL.html * * Software distributed under the License is distributed on an * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express * or implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code was created by Inprise Corporation * and its predecessors. Portions created by Inprise Corporation are * Copyright (C) Inprise Corporation. * * All Rights Reserved. * Contributor(s): ______________________________________. * Toni Martir: Verbose records restored as RESTORE_VERBOSE_INTERVAL, * also verbose restoring indexes as DEFERRED when verbose * 2003.08.17 Claudio Valderrama: Fix SF Bug #750659. * Adriano dos Santos Fernandes * */ #include "firebird.h" #include #include #include #include "../burp/burp.h" #include "../jrd/align.h" #include "../jrd/common.h" #include "../jrd/flags.h" #include "../jrd/license.h" #include "../jrd/obj.h" #include "../jrd/ods.h" #include "../common/stuff.h" #include "../burp/burp_proto.h" #include "../burp/canon_proto.h" #include "../burp/misc_proto.h" #include "../burp/mvol_proto.h" #include "../burp/resto_proto.h" #include "../jrd/gdsassert.h" #include "../jrd/constants.h" #include "../remote/protocol.h" #ifdef DEBUG #include "../gpre/prett_proto.h" #endif #include "../common/classes/ClumpletWriter.h" #include "../common/classes/UserBlob.h" #include "../common/classes/SafeArg.h" #include "memory_routines.h" using MsgFormat::SafeArg; // For service APIs the follow DB handle is a value stored // in thread data. This is also done for other statics generated by // GPRE. This is to avoid multiple threading problems with module // level statics. DATABASE DB = STATIC FILENAME "yachts.lnk"; #define DB tdgbl->db_handle #define gds_trans tdgbl->tr_handle #define isc_status tdgbl->status namespace // unnamed, private { const int DB_VERSION_DDL4 = 40; // ods4 db const int DB_VERSION_DDL5 = 50; // ods5 db const int DB_VERSION_DDL8 = 80; // ods8 db, IB4 const int DB_VERSION_DDL9 = 90; // ods9 db, IB5 const int DB_VERSION_DDL10 = 100; // ods10 db, IB6, FB1, FB1.5 const int DB_VERSION_DDL11 = 110; // ods11 db, FB2 const int DB_VERSION_DDL11_1 = 111; // ods11.1 db, FB2.1 const int DB_VERSION_DDL11_2 = 112; // ods11.2 db, FB2.5 const int DB_VERSION_OLDEST_SUPPORTED = DB_VERSION_DDL8; // IB4.0 is ods8 const int DEFERRED_ACTIVE = 3; // RDB$INDEX_INACTIVE setting for Foreign Keys // This setting is used temporarily while // restoring a database. This was required // in order to differentiate a partial // "inactive" state of SOME indices from // "inactive" state of ALL indices (gbak -i) // -bsriram, 11-May-1999 BUG: 10016 const int RESTORE_VERBOSE_INTERVAL = 10000; const int burp_msg_fac = 12; enum scan_attr_t { NO_SKIP = 0, // Not in skipping and scanning mode BEFORE_SKIP = 1, // After skipping, before scanning next byte for valid attribute AFTER_SKIP = 2 // After skipping and after scanning next byte for valid attribute }; void add_files(BurpGlobals* tdgbl, const char*); void bad_attribute(scan_attr_t, att_type, USHORT); void check_db_version(BurpGlobals* tdgbl); void create_database(BurpGlobals* tdgbl, const TEXT*); void decompress(BurpGlobals* tdgbl, UCHAR*, USHORT); void eat_blob(BurpGlobals* tdgbl); void eat_text(BurpGlobals* tdgbl); void eat_text2(BurpGlobals* tdgbl); burp_rel* find_relation(BurpGlobals* tdgbl, const TEXT*); 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 // returned value is not checked by the caller! bool get_acl(BurpGlobals* tdgbl, const TEXT*, ISC_QUAD*, ISC_QUAD*); void get_array(BurpGlobals* tdgbl, burp_rel*, UCHAR*); void get_blob(BurpGlobals* tdgbl, const burp_fld*, UCHAR*); void get_blr_blob(BurpGlobals* tdgbl, ISC_QUAD&, bool); bool get_character_set(BurpGlobals* tdgbl); bool get_chk_constraint(BurpGlobals* tdgbl); bool get_collation(BurpGlobals* tdgbl); rec_type get_data(BurpGlobals* tdgbl, burp_rel*); bool get_exception(BurpGlobals* tdgbl); burp_fld* get_field(BurpGlobals* tdgbl, burp_rel*); bool get_field_dimensions(BurpGlobals* tdgbl); bool get_files(BurpGlobals* tdgbl); bool get_filter(BurpGlobals* tdgbl); bool get_function(BurpGlobals* tdgbl); void get_function_arg(BurpGlobals* tdgbl, bool skip_arguments); bool get_generator(BurpGlobals* tdgbl); bool get_global_field(BurpGlobals* tdgbl); bool get_index(BurpGlobals* tdgbl, const burp_rel*); void get_misc_blob(BurpGlobals* tdgbl, ISC_QUAD&, bool); SLONG get_numeric(BurpGlobals* tdgbl); SINT64 get_int64(BurpGlobals* tdgbl); bool get_procedure(BurpGlobals* tdgbl); bool get_procedure_prm (BurpGlobals* tdgbl, GDS_NAME ); bool get_ref_constraint(BurpGlobals* tdgbl); bool get_rel_constraint(BurpGlobals* tdgbl); bool get_relation(BurpGlobals* tdgbl); bool get_relation_data(BurpGlobals* tdgbl); bool get_sql_roles(BurpGlobals* tdgbl); bool get_mapping(BurpGlobals* tdgbl); bool get_security_class(BurpGlobals* tdgbl); void get_source_blob(BurpGlobals* tdgbl, ISC_QUAD&, bool); USHORT get_text(BurpGlobals* tdgbl, TEXT*, ULONG); USHORT get_text2(BurpGlobals* tdgbl, TEXT* text, ULONG length); bool get_trigger(BurpGlobals* tdgbl); bool get_trigger_message(BurpGlobals* tdgbl); bool get_trigger_old (BurpGlobals* tdgbl, burp_rel*); bool get_type(BurpGlobals* tdgbl); bool get_user_privilege(BurpGlobals* tdgbl); bool get_view(BurpGlobals* tdgbl, burp_rel*); void ignore_array(BurpGlobals* tdgbl, burp_rel*); void ignore_blob(BurpGlobals* tdgbl); rec_type ignore_data(BurpGlobals* tdgbl, burp_rel*); void realign(BurpGlobals* tdgbl, UCHAR*, const burp_rel*); #ifdef sparc USHORT recompute_length(BurpGlobals* tdgbl, burp_rel*); #endif bool restore(BurpGlobals* tdgbl, const TEXT*, const TEXT*); void restore_security_class(BurpGlobals* tdgbl, const TEXT*, const TEXT*); USHORT get_view_base_relation_count(BurpGlobals* tdgbl, const TEXT*, USHORT); void store_blr_gen_id(BurpGlobals* tdgbl, const TEXT*, SINT64, const ISC_QUAD*); void update_global_field(BurpGlobals* tdgbl); void update_view_dbkey_lengths(BurpGlobals* tdgbl); void general_on_error(); #ifdef DEBUG UCHAR debug_on = 0; // able to turn this on in the debugger #endif #ifdef sparc const SSHORT old_sparcs[] = {0, 0, 0, 2, 0, 0, 0, 0, 2, 4, 4, 4, 8, 8, 0, 0, 8, 8, 8}; #endif //MVOL_read returns int static inline int get(BurpGlobals* tdgbl) { if (--(tdgbl->io_cnt) >= 0) return *(tdgbl->io_ptr)++; return MVOL_read(&tdgbl->io_cnt, &tdgbl->io_ptr); } static inline att_type get_attribute(att_type* att, BurpGlobals* tdgbl) { *att = (att_type) get(tdgbl); return *att; } static inline rec_type get_record(rec_type *rec, BurpGlobals* tdgbl) { *rec = (rec_type) get(tdgbl); return *rec; } #define GET_TEXT(text) get_text(tdgbl, (text), sizeof(text)) #define GET_TEXT2(text) get_text2(tdgbl, (text), sizeof(text)) static inline void get_skip(BurpGlobals* tdgbl, ULONG n) { MVOL_skip_block(tdgbl, n); } static inline UCHAR* get_block(BurpGlobals* tdgbl, UCHAR* p, ULONG n) { return MVOL_read_block(tdgbl, p, n); } // When skipping started, scan_next_attr will be changed from NO_SKIP // to BEFORE_SKIP. When scanning for next valid attribute after skipping, // it will flip-flop between BEFORE_SKIP and AFTER_SKIP. When next valid // attribute is found, it will be changed back to NO_SKIP by 'skip_scan' static inline void skip_init(scan_attr_t* scan_next_attr) { *scan_next_attr = NO_SKIP; } static inline void skip_scan(scan_attr_t* scan_next_attr) { if (*scan_next_attr == AFTER_SKIP) *scan_next_attr = BEFORE_SKIP; else if (*scan_next_attr == BEFORE_SKIP) *scan_next_attr = NO_SKIP; //else 0; => nothing, no change in the original macro } // User Privilege Flags const int USER_PRIV_USER = 1; const int USER_PRIV_GRANTOR = 2; const int USER_PRIV_PRIVILEGE = 4; const int USER_PRIV_GRANT_OPTION = 8; const int USER_PRIV_OBJECT_NAME = 16; const int USER_PRIV_FIELD_NAME = 32; const int USER_PRIV_USER_TYPE = 64; const int USER_PRIV_OBJECT_TYPE = 128; } // namespace int RESTORE_restore (const TEXT* file_name, const TEXT* database_name) { /************************************** * * R E S T O R E _ r e s t o r e * ************************************** * * Functional description * Recreate a database from a backup. * **************************************/ isc_req_handle req_handle1 = 0, req_handle2 = 0, req_handle3 = 0; isc_req_handle req_handle4 = 0, req_handle5 = 0; BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); tdgbl->io_ptr = NULL; tdgbl->io_cnt = 0; tdgbl->relations = NULL; tdgbl->procedures = NULL; tdgbl->RESTORE_format = 0; tdgbl->RESTORE_ods = 0; tdgbl->global_trans = 0; tdgbl->gbl_sw_transportable = tdgbl->gbl_sw_compress = false; if (!restore(tdgbl, file_name, database_name)) return FINI_ERROR; BURP_verbose (76); // msg 76 creating indexes COMMIT; ON_ERROR // Fix for bug_no 8055: // don't throw away the database just because an index // could not be made long error_code; while (error_code = tdgbl->status_vector[1]) { switch (error_code) { case isc_sort_mem_err: case isc_no_dup: strcpy(index_name, (TEXT *)tdgbl->status_vector[3]); BURP_print_status(tdgbl->status_vector); FOR (REQUEST_HANDLE req_handle3) IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ index_name BURP_verbose(243, index_name); MODIFY IDX USING IDX.RDB$INDEX_INACTIVE = TRUE; END_MODIFY; BURP_print(240, index_name); // msg 240 Index \"%s\" failed to activate because: if ( error_code == isc_no_dup ) { BURP_print(241); // msg 241 The unique index has duplicate values or NULLs BURP_print(242); // msg 242 Delete or Update duplicate values or NULLs, and activate index with } else { BURP_print(244); // msg 244 Not enough disk space to create the sort file for an index BURP_print(245); // msg 245 Set the TMP environment variable to a directory on a filesystem that does have enough space, and activate index with } BURP_print(243, index_name); // msg 243 ALTER INDEX \"%s\" ACTIVE; END_FOR; // don't bring the database on-line tdgbl->flag_on_line = false; break; default: general_on_error (); break; } COMMIT ON_ERROR continue; END_ERROR } END_ERROR; // Activate the indices for foreign keys and do another commit if (!tdgbl->gbl_sw_deactivate_indexes) { // Block added to verbose index creation by Toni Martir // Always try to activate deferred indices - it helps for some broken backups, // and in normal cases doesn't take much time to look for such indices. AP-2008. EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; // Activate first indexes that are not foreign keys FOR (REQUEST_HANDLE req_handle1) IDS IN RDB$INDICES WITH IDS.RDB$INDEX_INACTIVE EQ DEFERRED_ACTIVE AND IDS.RDB$FOREIGN_KEY MISSING MISC_terminate(IDS.RDB$INDEX_NAME, index_name, (ULONG) MISC_symbol_length(IDS.RDB$INDEX_NAME, sizeof(IDS.RDB$INDEX_NAME)), sizeof(index_name)); BURP_verbose(285, index_name); // activating and creating deferred index %s MODIFY IDS USING IDS.RDB$INDEX_INACTIVE = FALSE; END_MODIFY; ON_ERROR general_on_error(); END_ERROR; SAVE // existing ON_ERROR continues past error, beck ON_ERROR BURP_print (173, index_name); BURP_print_status(isc_status); MODIFY IDS USING IDS.RDB$INDEX_INACTIVE = TRUE; END_MODIFY; ON_ERROR general_on_error (); END_ERROR; END_ERROR; END_FOR; ON_ERROR general_on_error (); END_ERROR; MISC_release_request_silent(req_handle1); COMMIT; ON_ERROR general_on_error (); END_ERROR; EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; // Only activate Foreign keys that have been marked for deferred // activation. // -bsriram, 11-May-1999 BUG: 10016 // In case error happens creating FK, triggers don't let set // INACTIVE = TRUE for FK index. Therefore use separate // transaction be able to rollback when needed. // AP, 2005 FOR (REQUEST_HANDLE req_handle1) CNST IN RDB$RELATION_CONSTRAINTS CROSS IDS IN RDB$INDICES WITH CNST.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND CNST.RDB$INDEX_NAME EQ IDS.RDB$INDEX_NAME AND IDS.RDB$INDEX_INACTIVE EQ DEFERRED_ACTIVE MISC_terminate(IDS.RDB$INDEX_NAME, index_name, (ULONG) MISC_symbol_length(IDS.RDB$INDEX_NAME, sizeof(IDS.RDB$INDEX_NAME)), sizeof(index_name)); BURP_verbose(285, index_name); // activating and creating deferred index %s bool fError = false; isc_tr_handle activateIndexTran = 0; ISC_STATUS_ARRAY local_status_vector; ISC_STATUS* local_status = local_status_vector; START_TRANSACTION activateIndexTran; FOR (TRANSACTION_HANDLE activateIndexTran REQUEST_HANDLE req_handle5) IND1 IN RDB$INDICES WITH IND1.RDB$INDEX_NAME EQ IDS.RDB$INDEX_NAME MODIFY IND1 USING IND1.RDB$INDEX_INACTIVE = FALSE; END_MODIFY; END_FOR; ON_ERROR fError = true; memcpy(local_status, isc_status, sizeof (ISC_STATUS_ARRAY)); END_ERROR; MISC_release_request_silent(req_handle5); if (!fError) { COMMIT activateIndexTran; ON_ERROR fError = true; memcpy(local_status, isc_status, sizeof (ISC_STATUS_ARRAY)); END_ERROR; } if (fError) { ROLLBACK activateIndexTran; ON_ERROR general_on_error (); END_ERROR; BURP_print (173, index_name); BURP_print_status(local_status); tdgbl->flag_on_line = false; } END_FOR; ON_ERROR general_on_error (); END_ERROR; MISC_release_request_silent(req_handle1); COMMIT; ON_ERROR general_on_error (); END_ERROR; } if (tdgbl->global_trans) { BURP_verbose (68); // msg 68 committing meta data EXEC SQL COMMIT TRANSACTION tdgbl->global_trans; if (gds_status[1]) general_on_error (); // Check to see if there is a warning if (gds_status[0] == isc_arg_gds && gds_status[1] == 0 && gds_status[2] != isc_arg_end) { BURP_print_warning(gds_status); } } EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; // AB: Recalculate RDB$DBKEY_LENGTH for VIEWS // When VIEWs are not processed in correct dependency order // then on create time it doesn't know anything from the // VIEW that's referenced. // update_view_dbkey_lengths(tdgbl); // Change ownership of any procedures necessary for (burp_prc* procedure = tdgbl->procedures; procedure; procedure = procedure->prc_next) { if (procedure->prc_owner[0]) { FOR (REQUEST_HANDLE req_handle4) X IN RDB$PROCEDURES WITH X.RDB$PROCEDURE_NAME EQ procedure->prc_name MODIFY X strcpy (X.RDB$OWNER_NAME, procedure->prc_owner); END_MODIFY; ON_ERROR MISC_release_request_silent(req_handle4); general_on_error (); END_ERROR; restore_security_class(tdgbl, procedure->prc_owner, X.RDB$SECURITY_CLASS); END_FOR; ON_ERROR MISC_release_request_silent(req_handle4); general_on_error (); END_ERROR; } } MISC_release_request_silent(req_handle4); // Change ownership of any relations necessary for (burp_rel* relation = tdgbl->relations; relation; relation = relation->rel_next) { if (relation->rel_owner[0]) { FOR (REQUEST_HANDLE req_handle2) X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ relation->rel_name MODIFY X strcpy (X.RDB$OWNER_NAME, relation->rel_owner); END_MODIFY; ON_ERROR MISC_release_request_silent(req_handle2); general_on_error (); END_ERROR; restore_security_class(tdgbl, relation->rel_owner, X.RDB$SECURITY_CLASS); restore_security_class(tdgbl, relation->rel_owner, X.RDB$DEFAULT_CLASS); END_FOR; ON_ERROR MISC_release_request_silent(req_handle2); general_on_error (); END_ERROR; } } MISC_release_request_silent(req_handle2); // Now that changing ownership of tables is over, it is safe to // update the database security class in RDB$DATABASE if (tdgbl->database_security_class[0]) // Do it only if it's not NULL { FOR (REQUEST_HANDLE req_handle1) X IN RDB$DATABASE MODIFY X USING strncpy(X.RDB$SECURITY_CLASS, tdgbl->database_security_class, sizeof(X.RDB$SECURITY_CLASS)); END_MODIFY; ON_ERROR MISC_release_request_silent(req_handle1); general_on_error (); END_ERROR; END_FOR; ON_ERROR MISC_release_request_silent(req_handle1); general_on_error (); END_ERROR; MISC_release_request_silent(req_handle1); } COMMIT; ON_ERROR general_on_error (); END_ERROR; // Check to see if there is a warning if (gds_status[0] == isc_arg_gds && gds_status[1] == 0 && gds_status[2] != isc_arg_end) { BURP_print_warning(gds_status); } BURP_verbose (88); // msg 88 finishing, closing, and going home //FB_UINT64 cumul_count = MVOL_fini_read(); // Close database before we attach to it again. FINISH ON_ERROR general_on_error (); END_ERROR; // attach database again to put it online Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1); tdgbl->uSvc->getAddressPath(dpb); if (tdgbl->flag_on_line) { dpb.insertTag(isc_dpb_online); } if (tdgbl->gbl_sw_user) { dpb.insertString(isc_dpb_user_name, tdgbl->gbl_sw_user, strlen(tdgbl->gbl_sw_user)); } if (tdgbl->gbl_sw_password) { dpb.insertString(tdgbl->uSvc->isService() ? isc_dpb_password_enc : isc_dpb_password, tdgbl->gbl_sw_password, strlen(tdgbl->gbl_sw_password)); } if (tdgbl->gbl_sw_tr_user) { dpb.insertString(isc_dpb_trusted_auth, tdgbl->gbl_sw_tr_user, strlen(tdgbl->gbl_sw_tr_user)); } dpb.insertByte(isc_dpb_no_db_triggers, 1); // set forced writes to the value which was in the header dpb.insertByte(isc_dpb_force_write, tdgbl->hdr_forced_writes ? 1 : 0); FB_API_HANDLE db_handle = 0; if (isc_attach_database(tdgbl->status_vector, 0, database_name, &db_handle, dpb.getBufferLength(), reinterpret_cast(dpb.getBuffer()))) { general_on_error(); } if (isc_detach_database (tdgbl->status_vector, &db_handle)) general_on_error(); if (!tdgbl->flag_on_line) { BURP_print(246); // msg 246 Database is not online due to failure to activate one or more indices. BURP_print(247); // msg 247 Run gfix -online to bring database online without active indices. return FINI_DB_NOT_ONLINE; } // If the database is to be restored ReadOnly, set it to read_only now! if (tdgbl->gbl_sw_mode && tdgbl->gbl_sw_mode_val) { BURP_verbose (280); // msg 280: setting database to read-only access dpb.reset(isc_dpb_version1); if (tdgbl->gbl_sw_user) { dpb.insertString(isc_dpb_user_name, tdgbl->gbl_sw_user, strlen(tdgbl->gbl_sw_user)); } if (tdgbl->gbl_sw_password) { dpb.insertString(tdgbl->uSvc->isService() ? isc_dpb_password_enc : isc_dpb_password, tdgbl->gbl_sw_password, strlen(tdgbl->gbl_sw_password)); } if (tdgbl->gbl_sw_tr_user) { dpb.insertString(isc_dpb_trusted_auth, tdgbl->gbl_sw_tr_user, strlen(tdgbl->gbl_sw_tr_user)); } dpb.insertByte(isc_dpb_set_db_readonly, 1); dpb.insertByte(isc_dpb_no_db_triggers, 1); if (isc_attach_database(tdgbl->status_vector, 0, database_name, &db_handle, dpb.getBufferLength(), reinterpret_cast(dpb.getBuffer()))) { general_on_error(); } if (isc_detach_database (tdgbl->status_vector, &db_handle)) general_on_error(); } return FINI_OK; } namespace // unnamed, private { void add_files(BurpGlobals* tdgbl, const char* file_name) { /************************************** * * a d d _ f i l e s * ************************************** * * Functional description * This should be a multi-file database. * Store files and starting * addresses & commit this much. * **************************************/ isc_req_handle req_handle1 = 0; // store the RDB$FILES records SLONG start = 201; // Magic number, can be taken from some constant? SLONG count = 0; for (burp_fil* file = tdgbl->gbl_sw_files; file; file = file->fil_next) { if (file->fil_name != file_name) { count++; STORE (REQUEST_HANDLE req_handle1) X IN RDB$FILES strcpy (X.RDB$FILE_NAME, file->fil_name.c_str()); X.RDB$FILE_START = start; END_STORE; ON_ERROR general_on_error (); END_ERROR; MISC_release_request_silent(req_handle1); BURP_verbose (57, SafeArg() << file->fil_name.c_str() << start); // msg 57 adding file %s, starting at page %ld } else if (((SLONG) file->fil_length) >= start - 1) file->fil_length -= start - 1; else { BURP_print (96, SafeArg() << file->fil_length << (start - 1)); // msg 96 length given for initial file (%ld) is less than minimum (%ld) file->fil_length = 0; } start += file->fil_length; } if (count) { BURP_verbose (70); // msg 70 committing secondary files COMMIT // existing ON_ERROR continues past error, beck ON_ERROR BURP_print (174); // msg 174 cannot commit files BURP_print_status (tdgbl->status_vector); ROLLBACK; ON_ERROR general_on_error (); END_ERROR; END_ERROR; EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; } } void bad_attribute(scan_attr_t scan_next_attr, att_type bad_attr, USHORT type) { /************************************** * * b a d _ a t t r i b u t e * ************************************** * * Functional description * We ran into an unsupported attribute. * but it isn't the end of the world. * We will try to skip some bad data and * look for next valid attribute to continue the process. * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); SLONG skip_count = 0; if (!tdgbl->gbl_sw_skip_count) { static const SafeArg dummy; TEXT t_name[128]; fb_msg_format(NULL, burp_msg_fac, type, sizeof(t_name), t_name, dummy); BURP_print (80, SafeArg() << t_name << int(bad_attr)); // msg 80 don't recognize %s attribute %ld -- continuing int skip_l = get(tdgbl); if (skip_l) get_skip(tdgbl, skip_l); } else { if (scan_next_attr == NO_SKIP) { skip_count = tdgbl->gbl_sw_skip_count; get_skip(tdgbl, skip_count); BURP_print (203, SafeArg() << skip_count << int(bad_attr)); // msg 203: skipped %d bytes after reading a bad attribute %d } else { ++skip_count; BURP_print (205, SafeArg() << skip_count << int(bad_attr)); // msg 205: skipped %d bytes looking for next valid attribute, encountered attribute %d } scan_next_attr = AFTER_SKIP; } } void check_db_version(BurpGlobals* tdgbl) { /************************************** * * c h e c k _ d b _ v e r s i o n * ************************************** * * Functional description * Find the ODS version number of the database. * **************************************/ struct rel_field_t { const char* rel; const char* fld; int ods_version; }; tdgbl->RESTORE_ods = DB_VERSION_DDL4; const rel_field_t relations[] = { {"RDB$TRIGGERS", 0, DB_VERSION_DDL5}, // IB3.3 {"RDB$PROCEDURES", 0, DB_VERSION_DDL8}, // IB4 {"RDB$ROLES", 0, DB_VERSION_DDL9}, // IB5 {0, 0, 0} }; isc_req_handle req_handle = 0; for (const rel_field_t* rel = relations; rel->rel; ++rel) { FOR (REQUEST_HANDLE req_handle) FIRST 1 X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME = rel->rel tdgbl->RESTORE_ods = rel->ods_version; END_FOR; ON_ERROR general_on_error (); END_ERROR; } MISC_release_request_silent(req_handle); if (tdgbl->RESTORE_ods < DB_VERSION_DDL8) return; const rel_field_t rel_fields[] = { {"RDB$FIELDS", "RDB$FIELD_PRECISION", DB_VERSION_DDL10}, // FB1, FB1.5 {"RDB$ROLES", "RDB$DESCRIPTION", DB_VERSION_DDL11}, // FB2 {"RDB$RELATIONS", "RDB$RELATION_TYPE", DB_VERSION_DDL11_1}, // FB2.1 {"RDB$PROCEDURE_PARAMETERS", "RDB$FIELD_NAME", DB_VERSION_DDL11_2}, // FB2.5 {0, 0, 0} }; isc_req_handle req_handle2 = 0; for (const rel_field_t* rf = rel_fields; rf->rel; ++rf) { FOR (REQUEST_HANDLE req_handle2) FIRST 1 X2 IN RDB$RELATION_FIELDS WITH X2.RDB$RELATION_NAME = rf->rel AND X2.RDB$FIELD_NAME = rf->fld tdgbl->RESTORE_ods = rf->ods_version; END_FOR; ON_ERROR general_on_error (); END_ERROR; } MISC_release_request_silent(req_handle2); } void create_database(BurpGlobals* tdgbl, const TEXT* file_name) { /************************************** * * c r e a t e _ d a t a b a s e * ************************************** * * Functional description * create the new database, looking * to see if there are any interesting * things to do. * **************************************/ // Get (physical) database record ULONG page_size = DEFAULT_PAGE_SIZE; // sweep_interval = -1; // sweep_interval = 0xFFFFFFFF; ULONG sweep_interval = MAX_ULONG; bool no_reserve = false; bool db_read_only = false, SQL_dialect_flag = false; bool forced_writes = true; // turned on by default ULONG page_buffers = 0; USHORT SQL_dialect = 0; att_type attribute; rec_type record; if (get_record(&record, tdgbl) == rec_physical_db) { while (get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_SQL_dialect: SQL_dialect_flag = true; SQL_dialect = (USHORT) get_numeric(tdgbl); break; case att_page_size: page_size = get_numeric(tdgbl); break; case att_sweep_interval: sweep_interval = get_numeric(tdgbl); break; case att_forced_writes: forced_writes = get_numeric(tdgbl) != FALSE; break; case att_no_reserve: no_reserve = get_numeric(tdgbl) != FALSE; break; case att_db_read_only: db_read_only = get_numeric(tdgbl) != FALSE; break; case att_page_buffers: page_buffers = get_numeric(tdgbl); break; default: { const SSHORT l = get(tdgbl); if (l) { get_skip(tdgbl, l); } break; } } } get_record(&record, tdgbl); } if (record != rec_database) BURP_error_redirect (NULL, 32); // msg 32 Expected database description record if (tdgbl->gbl_sw_page_size && (tdgbl->gbl_sw_page_size < page_size)) { BURP_print (110, SafeArg() << page_size << tdgbl->gbl_sw_page_size); // msg 110 Reducing the database page size from %ld bytes to %ld bytes } if (tdgbl->gbl_sw_page_size) page_size = tdgbl->gbl_sw_page_size; tdgbl->hdr_forced_writes = forced_writes; if (tdgbl->gbl_sw_no_reserve) no_reserve = tdgbl->gbl_sw_no_reserve; // Override attribute setting with user requirement if (tdgbl->gbl_sw_mode) db_read_only = tdgbl->gbl_sw_mode_val; else { // No access mode specified by user. Use attribute settings. Since the // database is set to readOnly only after making it Online in // RESTORE_restore(), pass on this information through Global structures tdgbl->gbl_sw_mode = true; tdgbl->gbl_sw_mode_val = db_read_only; } if (tdgbl->gbl_sw_page_buffers) page_buffers = tdgbl->gbl_sw_page_buffers; Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1); tdgbl->uSvc->getAddressPath(dpb); dpb.insertInt(isc_dpb_page_size, page_size & 0xff00); dpb.insertString(isc_dpb_gbak_attach, GDS_VERSION, strlen(GDS_VERSION)); if (sweep_interval != MAX_ULONG) { dpb.insertInt(isc_dpb_sweep_interval, sweep_interval); } // If the database is to be restored "read_only", fillup the data pages if (no_reserve || db_read_only) { dpb.insertByte(isc_dpb_no_reserve, 1); } if (tdgbl->gbl_sw_user) { dpb.insertString(isc_dpb_user_name, tdgbl->gbl_sw_user, strlen(tdgbl->gbl_sw_user)); } if (tdgbl->gbl_sw_password) { dpb.insertString(tdgbl->uSvc->isService() ? isc_dpb_password_enc : isc_dpb_password, tdgbl->gbl_sw_password, strlen(tdgbl->gbl_sw_password)); } if (tdgbl->gbl_sw_tr_user) { dpb.insertString(isc_dpb_trusted_auth, tdgbl->gbl_sw_tr_user, strlen(tdgbl->gbl_sw_tr_user)); } if (page_buffers) { dpb.insertInt(isc_dpb_set_page_buffers, page_buffers); } // Turn off sync writes during restore dpb.insertByte(isc_dpb_force_write, 0); // which SQL dialect that this database speaks // When we restore backup files that came from prior // to V6, we force the SQL database dialect to 1 dpb.insertByte(isc_dpb_sql_dialect, SQL_dialect_flag ? SQL_dialect : SQL_DIALECT_V5); // start database up shut down, // use single-user mode to avoid conflicts during restore process dpb.insertByte(isc_dpb_shutdown, isc_dpb_shut_attachment | isc_dpb_shut_single); dpb.insertInt(isc_dpb_shutdown_delay, 0); dpb.insertInt(isc_dpb_overwrite, tdgbl->gbl_sw_overwrite); dpb.insertByte(isc_dpb_no_db_triggers, 1); ISC_STATUS_ARRAY status_vector; if (tdgbl->gbl_sw_fix_fss_metadata) { dpb.insertString(isc_dpb_lc_ctype, tdgbl->gbl_sw_fix_fss_metadata, strlen(tdgbl->gbl_sw_fix_fss_metadata)); } if (isc_create_database(status_vector, 0, file_name, &DB, dpb.getBufferLength(), reinterpret_cast(dpb.getBuffer()), 0)) { BURP_error_redirect (status_vector, 33, SafeArg() << file_name); // msg 33 failed to create database %s } if (tdgbl->gbl_sw_version) { BURP_print(139, file_name); // msg 139 Version(s) for database "%s" isc_version(&DB, BURP_output_version, (void*)"\t%s\n"); } BURP_verbose (74, SafeArg() << file_name << page_size); // msg 74 created database %s, page_size %ld bytes } void decompress(BurpGlobals* tdgbl, UCHAR* buffer, USHORT length) { /************************************** * * d e c o m p r e s s * ************************************** * * Functional description * Get a number of compressed bytes. * **************************************/ UCHAR* p = buffer; const UCHAR* const end = p + length; while (p < end) { // This change was made to restore National Semi-Conductor's corrupted // gbak file and it is in the code base now. -Andrew // so count really only to 255 SSHORT count = (SCHAR) get(tdgbl); if (count > 0) { if (end - p < count) { BURP_print (202, SafeArg() << count << (end - p)); // msg 202: adjusting a decompression length error: invalid length %d was adjusted to %d count = end - p; } p = get_block(tdgbl, p, count); } else if (count < 0) { if (end + count < p) { BURP_print(202, SafeArg() << count << (p - end)); // msg 202: adjusting a decompression length error: invalid length %d was adjusted to %d count = p - end; } const UCHAR c = get(tdgbl); memset (p, c, -count); p += -count; } } if (p > end) { BURP_error_redirect (NULL, 34); // msg 34 RESTORE: decompression length error } } void eat_blob(BurpGlobals* tdgbl) { /************************************** * * e a t _ b l o b * ************************************** * * Functional description * Discard a blob from backup file * **************************************/ const SLONG length = get_numeric(tdgbl); get_skip(tdgbl, length); } // ***************************** // e a t _ t e x t // ***************************** // Discard a text field from the backup file. void eat_text(BurpGlobals* tdgbl) { const ULONG l = get(tdgbl); if (l) MVOL_skip_block(tdgbl, l); } // ***************************** // e a t _ t e x t 2 // ***************************** // Discard a text field from the backup file, using USHORT length indicator. void eat_text2(BurpGlobals* tdgbl) { UCHAR lenstr[sizeof(USHORT)] = ""; get_block(tdgbl, lenstr, sizeof(lenstr)); USHORT len = (USHORT) gds__vax_integer(lenstr, sizeof(lenstr)); if (len) MVOL_skip_block(tdgbl, len); } burp_rel* find_relation(BurpGlobals* tdgbl, const TEXT* 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) { for (const TEXT* p = relation->rel_name, *q = name; *p == *q; p++, q++) { if (!*p) return relation; } } BURP_error_redirect (NULL, 35, SafeArg() << name); // msg 35 can't find relation %s return NULL; } void fix_security_class_name(BurpGlobals* tdgbl, TEXT* sec_class, bool is_field) { /************************************** * * f i x _ s e c u r i t y _ c l a s s _ n a m e * ************************************** * * Functional description * Reassign a proper (unique) name for auto-generated * security classes. * **************************************/ const char* const prefix = is_field ? SQL_FLD_SECCLASS_PREFIX : SQL_SECCLASS_PREFIX; const int prefix_length = is_field ? SQL_FLD_SECCLASS_PREFIX_LEN : SQL_SECCLASS_PREFIX_LEN; if (strncmp(sec_class, prefix, prefix_length)) return; if (tdgbl->RESTORE_ods < DB_VERSION_DDL11_2) return; ISC_STATUS_ARRAY status_vector; isc_req_handle& handle = tdgbl->handles_fix_security_class_name_req_handle1; if (!handle) { UCHAR blr_buffer[BUFFER_TINY]; UCHAR* blr = blr_buffer; add_byte(blr, blr_version5); add_byte(blr, blr_begin); add_byte(blr, blr_message); add_byte(blr, 0); add_word(blr, 1); add_byte(blr, blr_int64); add_byte(blr, 0); add_byte(blr, blr_send); add_byte(blr, 0); add_byte(blr, blr_begin); add_byte(blr, blr_assignment); add_byte(blr, blr_gen_id); add_string(blr, SQL_SECCLASS_GENERATOR); add_byte(blr, blr_literal); add_byte(blr, blr_int64); add_byte(blr, 0); add_int64(blr, 1); add_byte(blr, blr_parameter); add_byte(blr, 0); add_word(blr, 0); add_byte(blr, blr_end); add_byte(blr, blr_end); add_byte(blr, blr_eoc); const USHORT blr_length = blr - blr_buffer; fb_assert(blr_length <= sizeof(blr_buffer)); if (isc_compile_request(status_vector, &DB, &handle, blr_length, (const SCHAR*) blr_buffer)) { BURP_error_redirect(status_vector, 316); // msg 316 Failed while fixing the security class name } } if (isc_start_request(status_vector, &handle, &gds_trans, 0)) { BURP_error_redirect(status_vector, 316); // msg 316 Failed while fixing the security class name } SINT64 id = 0; if (isc_receive(status_vector, &handle, 0, sizeof(SINT64), &id, 0)) { BURP_error_redirect(status_vector, 316); // msg 316 Failed while fixing the security class name } fb_assert(id); snprintf(sec_class, MAX_SQL_IDENTIFIER_SIZE, "%s%"SQUADFORMAT, prefix, id); } void general_on_error() { /************************************** * * g e n e r a l _ o n _ e r r o r * ************************************** * * Functional description * Handle any general ON_ERROR clause during restore. * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); BURP_print_status (isc_status, true); BURP_abort (); } bool get_acl(BurpGlobals* tdgbl, const TEXT* owner_nm, ISC_QUAD* blob_id, ISC_QUAD* new_blob_id) { /************************************** * * g e t _ a c l * ************************************** * * Functional description * * open the blob that contains the ACL list * get the ACL list of a relation * replace the owner of the relation in the ACL list with * the creator of the relation * create a new blob * store the new ACL list in the new blob * **************************************/ static const UCHAR blr_items[] = { isc_info_blob_max_segment, isc_info_blob_total_length, isc_info_blob_num_segments }; // If the blob is null, don't store it. It will be restored as null. if (!blob_id->gds_quad_high && !blob_id->gds_quad_low) return false; // Open the blob and get it's vital statistics ISC_STATUS_ARRAY status_vector; UserBlob blob(status_vector); if (! blob.open(DB, gds_trans, *blob_id)) { // msg 24 isc_open_blob failed BURP_error_redirect (status_vector, 24); } UCHAR blob_info[32]; if (!blob.getInfo(sizeof(blr_items), blr_items, sizeof(blob_info), blob_info)) { // msg 20 isc_blob_info failed BURP_error_redirect (status_vector, 20); } SLONG length = 0; UCHAR item; USHORT max_segment, num_segments; const UCHAR* p = blob_info; while ((item = *p++) != isc_info_end) { const USHORT l = (USHORT) gds__vax_integer (p, 2); p += 2; const SLONG n = gds__vax_integer (p, l); p += l; switch (item) { case isc_info_blob_max_segment: max_segment = (USHORT) n; break; case isc_info_blob_total_length: length = n; break; case isc_info_blob_num_segments: num_segments = (USHORT) n; // we assume that the ACL list was written out as // in one big segment if (num_segments > 1) { // CVC: I can't see the effect of assert(true) fb_assert (num_segments > 1); } break; default: // msg 79 don't understand blob info item %ld BURP_print (79, SafeArg() << int(item)); // CVC: do you return, without closing the blob, dear function??? if (!blob.close()) { BURP_error_redirect (status_vector, 23); // msg 23 isc_close_blob failed } return false; } } if (!length) { if (!blob.close()) { BURP_error_redirect (status_vector, 23); // msg 23 isc_close_blob failed } return false; } // Rdb sometimes gets the length messed up if (length < max_segment) length = max_segment; // Allocate a buffer large enough for the largest segment and start grinding. BlobBuffer static_buffer; UCHAR* buffer = static_buffer.getBuffer(length); size_t return_length = 0; if (!blob.getData(length, buffer, return_length)) { // msg 22 gds_$get_segment failed BURP_error_redirect (status_vector, 22); } // protect ourself length = return_length; if (!blob.close()) { // msg 23 isc_close_blob failed BURP_error_redirect (status_vector, 23); } const UCHAR* from = buffer + 3; // skip ACL_version, ACL_id_list, and id_person const SLONG id_person_len = (SLONG) *from; const UCHAR* c_1 = (UCHAR*) owner_nm; const UCHAR owner_nm_len = strlen(owner_nm); BlobBuffer new_static_buffer; UCHAR* new_buffer = new_static_buffer.getBuffer(length - id_person_len + owner_nm_len); from = buffer; UCHAR* to = new_buffer; *to++ = *from++; // copy ACL_verion *to++ = *from++; // copy ACL_id_list *to++ = *from++; // copy id_person *to++ = owner_nm_len; size_t new_len = 4; //new_len + 4; Previously, new_len was set to zero at the top // from = buffer + id_person_len + 4; redundant, see 2nd loop below. for (SLONG cnt = 0; cnt < owner_nm_len; cnt++) { *to++ = *c_1++; new_len++; } const UCHAR* const end_buffer = buffer + length; for (from = buffer + id_person_len + 4; from < end_buffer; from++) { *to++ = *from; new_len++; } if (!blob.create(DB, gds_trans, *new_blob_id)) { // msg 37 isc_create_blob failed BURP_error_redirect (status_vector, 37); } if (!blob.putData(new_len, new_buffer)) { // msg 38 isc_put_segment failed BURP_error_redirect (status_vector, 38); } if (!blob.close()) { // msg 23 isc_close_blob failed BURP_error_redirect (status_vector, 23); } return true; } void get_array(BurpGlobals* tdgbl, burp_rel* relation, UCHAR* record_buffer) { /************************************** * * g e t _ a r r a y * ************************************** * * Functional description * Read array data from input file to nice, * shiney, new array. * **************************************/ burp_fld* field = NULL; ISC_STATUS_ARRAY status_vector; USHORT count, field_number, field_length = 0; UCHAR* buffer = NULL; UCHAR* p = NULL; UCHAR blr_buffer[200]; // enough for a sdl with 16 dimensions lstring xdr_slice; // don't free something you don't allocate lstring xdr_buffer; xdr_buffer.lstr_allocated = 0; xdr_buffer.lstr_address = NULL; // Pick up attributes SLONG fld_ranges[2 * MAX_DIMENSION]; SLONG slice_length = 0; SLONG *range; const SLONG* end_ranges; scan_attr_t scan_next_attr; skip_init(&scan_next_attr); att_type attribute; while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_blob_data) { switch (attribute) { case att_blob_field_number: field_number = (USHORT) get_numeric(tdgbl); for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_number == field_number) break; } if (!field) { BURP_error_redirect (NULL, 36); // msg 36 Can't find field for blob } field_length = field->fld_length; if (field->fld_type == blr_varying) field_length += sizeof(USHORT); slice_length = field_length; // // Copy the ranges onto a buffer and let the program // mess with the copy rather than the original // memcpy(fld_ranges, field->fld_ranges, sizeof(fld_ranges)); break; case att_array_dimensions: field->fld_dimensions = (SSHORT) get_numeric(tdgbl); end_ranges = fld_ranges + 2 * field->fld_dimensions; for (range = fld_ranges; range < end_ranges; range += 2) { if (get_attribute(&attribute, tdgbl) != att_array_range_low) bad_attribute (scan_next_attr, attribute, 58); // msg 58 array else range[0] = get_numeric(tdgbl); if (get_attribute(&attribute, tdgbl) != att_array_range_high) bad_attribute (scan_next_attr, attribute, 58); // msg 58 array else range[1] = get_numeric(tdgbl); slice_length *= (range[1] - range[0] + 1); } break; default: bad_attribute (scan_next_attr, attribute, 58); // msg 58 array break; } } SLONG return_length = get(tdgbl); return_length |= get(tdgbl) << 8; return_length |= get(tdgbl) << 16; return_length |= get(tdgbl) << 24; ISC_QUAD* blob_id = (ISC_QUAD*) ((UCHAR*) record_buffer + field->fld_offset); SLONG last_element_dim[MAX_DIMENSION]; if (return_length != slice_length) { int upper, lower; // // Ugh! The full array wasn't returned and versions of gbak prior to // V3.2I don't explicitly signal this. We must recompute the top // element to restore. // // Double Ugh! gbak (Versions prior to 5.0) while backing up calculates // the top dimensions incorrectly So whatever was written as top dimensions // is useless. 5.0 gbak has written correct dimensions, but what the heck // we'll calculate it again // int elements_remaining = return_length / field_length; // // Backup (versions prior to 5.0) has surely written wrong dimensions. // Ignore whatever is read in fld_ranges and calculate the dimensions // of the last element. field->fld_ranges has the max dimensions. // last_element_dim holds only the upper bounds of each dimension. // for (int i1 = 0, i3 = 0; i1 < field->fld_dimensions; i1++) { int divisor = 1; for (int i2 = (2 * (i1 + 1) + 1); i2 <= field->fld_dimensions * 2; i2 += 2) divisor *= (field->fld_ranges[i2] - field->fld_ranges[i2 - 1] + 1); last_element_dim[i1] = (elements_remaining - 1) / divisor + field->fld_ranges[i3]; elements_remaining -= (last_element_dim[i1] - field->fld_ranges[i3]) * divisor; i3 += 2; } int current_dim; #ifdef DEBUG fprintf(stderr, "\nLast element upper bounds read from backup file:\n"); for (current_dim = 1; current_dim < field->fld_dimensions * 2; current_dim += 2) fprintf(stderr, "%ld ", fld_ranges[current_dim]); fprintf(stderr, "\nCalculated Last element upper bounds :\n"); for (current_dim = 0; current_dim < field->fld_dimensions; current_dim++) fprintf(stderr, "%ld ", last_element_dim[current_dim]); fprintf(stderr, "return_length = %ld\n", return_length); fprintf(stderr, "elements_returned = %ld\n", return_length / field_length); fprintf(stderr, "Max dims["); for (current_dim = 1; current_dim < field->fld_dimensions * 2; current_dim += 2) fprintf(stderr, "%ld ", field->fld_ranges[current_dim]); fprintf(stderr, "]"); #endif int data_at = 0; // // We have an irregurlar shaped slice to write. The following for loop // chops the array into writable rectangular/square slice and sends it // to the engine. When the loop cycles through all dimensions, we would // have written the whole of the irregular slice. // for (current_dim = 0; current_dim < field->fld_dimensions; current_dim++) { UCHAR* blr = blr_buffer; bool dont_write = false; // build the sdl add_byte(blr, isc_sdl_version1); add_byte(blr, isc_sdl_struct); add_byte(blr, 1); switch (field->fld_type) { case blr_text: case blr_varying: if (field->fld_type == blr_text) add_byte(blr, blr_text2); else add_byte(blr, blr_varying2); add_word(blr, field->fld_character_set_id); add_word(blr, field->fld_length); break; case blr_short: case blr_long: case blr_quad: case blr_int64: add_byte(blr, field->fld_type); add_byte(blr, field->fld_scale); break; default: add_byte(blr, field->fld_type); } add_byte(blr, isc_sdl_relation); add_string(blr, relation->rel_name); add_byte(blr, isc_sdl_field); add_string(blr, field->fld_name); // each element spec starts here #ifdef DEBUG fprintf(stderr, "\nBounds written["); #endif int elements_written = 1; end_ranges = field->fld_ranges + 2 * field->fld_dimensions; // // Here is the important work. Calculate the the bounds to be written // so that the resulting slice is a rectangular/square slice. // For a 2 dimensional array of size 1..N, 1..M, which is partially // filled, we have already calculated the dims of last element. Say // if this was x,y (x is row, y is column) then we do // isc_put_slice(1..x-1, 1..M); // isc_put_slice(x..x, 1..y); // similarly for a 3D array [N,M,K] whose last element dims are (x,y,z) // isc_put_slice(1..x-1, 1..M, 1..K); // isc_put_slice(x..x, 1..y-1, 1..K); // isc_put_slice(x..x, y..y, 1..z); // This is applicable for any number of dimensions. // Special cases: // for example in case of a 2D array (10,10) and if the last element // dims were (1,2), we would just do a isc_put_slice(1..1, 1..2). // This is applied for any number of dimensions. // for (range = field->fld_ranges, count = 0; range < end_ranges; range += 2, count++) { add_byte(blr, isc_sdl_do2); add_byte(blr, count); // // Normally we loop through all dimensions chopping off slices // and writing them. This works fine but this also means that // we blindly put slices without actually figuring out if we // really need to do so. For eg: if we have a 2D array of // size [10,4] and the last element dims are [6,4] then all // we need to do is is to put one slice as // isc_put_slice(1..6,1..4) // rather than looping through the dimensions and putting // isc_put_slice(1..5,1..4) // isc_put_slice(6..6,1..4) // we could extend this logic to any no of dims. The following // if condition figures out such cases. This combined with // the Special case should optimize the no of isc_put_slice // we perform. // if (current_dim + 1 == field->fld_dimensions - 1 && field->fld_dimensions - count == 2 && last_element_dim[count + 1] == range[3]) { add_byte(blr, isc_sdl_long_integer); add_long(blr, range[0]); lower = range[0]; add_byte(blr, isc_sdl_long_integer); add_long(blr, last_element_dim[count]); upper = last_element_dim[count]; elements_written *= (upper - lower + 1); range += 2; count++; add_byte(blr, isc_sdl_do2); add_byte(blr, count); add_byte(blr, isc_sdl_long_integer); add_long(blr, range[0]); lower = range[0]; add_byte(blr, isc_sdl_long_integer); add_long(blr, last_element_dim[count]); upper = last_element_dim[count]; elements_written *= (upper - lower + 1); ++current_dim; break; } if (current_dim == count) { add_byte(blr, isc_sdl_long_integer); add_long(blr, range[0]); lower = range[0]; add_byte(blr, isc_sdl_long_integer); upper = (current_dim == field->fld_dimensions - 1) ? last_element_dim[count] : (last_element_dim[count] - 1); if (upper < range[0]) { // see Special Case above dont_write = true; break; } add_long(blr, upper); } else if (current_dim < count) { add_byte(blr, isc_sdl_long_integer); add_long(blr, range[0]); add_byte(blr, isc_sdl_long_integer); add_long(blr, range[1]); upper = range[1]; lower = range[0]; } else if (current_dim > count) { add_byte(blr, isc_sdl_long_integer); add_long(blr, last_element_dim[count]); add_byte(blr, isc_sdl_long_integer); add_long(blr, last_element_dim[count]); upper = lower = last_element_dim[count]; } elements_written *= (upper - lower + 1); #ifdef DEBUG fprintf(stderr, "%d..%d ", lower, upper); #endif } if (dont_write) continue; #ifdef DEBUG fprintf(stderr, "]"); fprintf(stderr, "\n Elements Written=%d ", elements_written); #endif add_byte(blr, isc_sdl_element); add_byte(blr, 1); add_byte(blr, isc_sdl_scalar); add_byte(blr, 0); add_byte(blr, field->fld_dimensions); for (count = 0; count < field->fld_dimensions; count++) { add_byte(blr, isc_sdl_variable); add_byte(blr, count); } add_byte(blr, isc_sdl_eoc); #ifdef DEBUG if (debug_on) PRETTY_print_sdl (blr_buffer, NULL, NULL, 0); #endif const USHORT blr_length = blr - blr_buffer; if (data_at == 0) { buffer = BURP_alloc (return_length); SLONG lcount; if (tdgbl->gbl_sw_transportable) { if (get_attribute(&attribute, tdgbl) != att_xdr_array) // msg 55 Expected XDR record length BURP_error_redirect (NULL, 55); else { lcount = get(tdgbl); lcount |= get(tdgbl) << 8; lcount |= get(tdgbl) << 16; lcount |= get(tdgbl) << 24; xdr_buffer.lstr_length = xdr_buffer.lstr_allocated = lcount; xdr_buffer.lstr_address = BURP_alloc(lcount); xdr_slice.lstr_allocated = xdr_slice.lstr_length = return_length; xdr_slice.lstr_address = buffer; p = xdr_buffer.lstr_address; } } else { p = buffer; lcount = return_length; } if (lcount) get_block(tdgbl, p, lcount); if (tdgbl->gbl_sw_transportable) CAN_slice (&xdr_buffer, &xdr_slice, FALSE, /*blr_length,*/ blr_buffer); } if (isc_put_slice(status_vector, &DB, &gds_trans, blob_id, blr_length, reinterpret_cast(blr_buffer), 0, // param length for subset of an array handling NULL, // param for subset of an array handling elements_written * field->fld_length, buffer + data_at)) { BURP_print (81, field->fld_name); // msg 81 error accessing blob field %s -- continuing BURP_print_status (status_vector); #ifdef DEBUG PRETTY_print_sdl (blr_buffer, NULL, NULL, 0); #endif return; } data_at += elements_written * field->fld_length; #ifdef DEBUG fprintf(stderr, "next data_at = %d\n", data_at); #endif } } else { // This is the regular case we've got the entire array UCHAR* blr = blr_buffer; // build the sdl add_byte(blr, isc_sdl_version1); add_byte(blr, isc_sdl_struct); add_byte(blr, 1); switch (field->fld_type) { case blr_text: case blr_varying: if (field->fld_type == blr_text) add_byte(blr, blr_text2); else add_byte(blr, blr_varying2); add_word(blr, field->fld_character_set_id); add_word(blr, field->fld_length); break; case blr_short: case blr_long: case blr_quad: case blr_int64: add_byte(blr, field->fld_type); add_byte(blr, field->fld_scale); break; default: add_byte(blr, field->fld_type); } add_byte(blr, isc_sdl_relation); add_string(blr, relation->rel_name); add_byte(blr, isc_sdl_field); add_string(blr, field->fld_name); // each element spec starts here for (range = fld_ranges, count = 0; range < end_ranges; range += 2, count++) { add_byte(blr, isc_sdl_do2); add_byte(blr, count); add_byte(blr, isc_sdl_long_integer); add_long(blr, range[0]); add_byte(blr, isc_sdl_long_integer); add_long(blr, range[1]); } add_byte(blr, isc_sdl_element); add_byte(blr, 1); add_byte(blr, isc_sdl_scalar); add_byte(blr, 0); add_byte(blr, field->fld_dimensions); for (count = 0; count < field->fld_dimensions; count++) { add_byte(blr, isc_sdl_variable); add_byte(blr, count); } add_byte(blr, isc_sdl_eoc); #ifdef DEBUG if (debug_on) PRETTY_print_sdl (blr_buffer, NULL, NULL, 0); #endif const USHORT blr_length = blr - blr_buffer; buffer = BURP_alloc (return_length); SLONG lcount; if (tdgbl->gbl_sw_transportable) { if (get_attribute(&attribute, tdgbl) != att_xdr_array) BURP_error_redirect (NULL, 55); // msg 55 Expected XDR record length else { xdr_buffer.lstr_allocated = get(tdgbl); xdr_buffer.lstr_allocated |= get(tdgbl) << 8; xdr_buffer.lstr_allocated |= get(tdgbl) << 16; xdr_buffer.lstr_allocated |= get(tdgbl) << 24; lcount = xdr_buffer.lstr_length = xdr_buffer.lstr_allocated; xdr_buffer.lstr_address = BURP_alloc (xdr_buffer.lstr_allocated); xdr_slice.lstr_allocated = xdr_slice.lstr_length = return_length; xdr_slice.lstr_address = buffer; p = xdr_buffer.lstr_address; } } else { p = buffer; lcount = return_length; } if (lcount) get_block(tdgbl, p, lcount); if (tdgbl->gbl_sw_transportable) CAN_slice (&xdr_buffer, &xdr_slice, FALSE, /*blr_length,*/ blr_buffer); if (isc_put_slice(status_vector, &DB, &gds_trans, blob_id, blr_length, reinterpret_cast(blr_buffer), 0, // param length for subset of an array handling NULL, // param for subset of an array handling return_length, buffer)) { BURP_print (81, field->fld_name); // msg 81 error accessing blob field %s -- continuing BURP_print_status (status_vector); #ifdef DEBUG PRETTY_print_sdl (blr_buffer, NULL, NULL, 0); #endif return; } } BURP_free (buffer); if (tdgbl->gbl_sw_transportable && xdr_buffer.lstr_allocated) BURP_free (xdr_buffer.lstr_address); } void get_blob(BurpGlobals* tdgbl, const burp_fld* fields, UCHAR* record_buffer) { /************************************** * * g e t _ b l o b * ************************************** * * Functional description * Read blob attributes and copy data from input file to nice, * shiny, new blob. * **************************************/ // Pick up attributes ULONG segments = 0; USHORT field_number = MAX_USHORT; USHORT max_segment = 0; UCHAR blob_type = 0; att_type attribute; scan_attr_t scan_next_attr; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_blob_data) { switch (attribute) { case att_blob_field_number: field_number = (USHORT) get_numeric(tdgbl); break; case att_blob_max_segment: max_segment = (USHORT) get_numeric(tdgbl); break; case att_blob_number_segments: segments = get_numeric(tdgbl); break; case att_blob_type: blob_type = (UCHAR) get_numeric(tdgbl); break; default: bad_attribute(scan_next_attr, attribute, 64); // msg 64 blob break; } } // Find the field associated with the blob const burp_fld* field; for (field = fields; field; field = field->fld_next) { if (field->fld_number == field_number) break; } if (!field) { BURP_error_redirect (NULL, 36); // msg 36 Can't find field for blob } // Create new blob ISC_QUAD* blob_id = (ISC_QUAD*) ((UCHAR*) record_buffer + field->fld_offset); ISC_STATUS_ARRAY status_vector; UserBlob blob(status_vector); const UCHAR blob_desc[] = {isc_bpb_version1, isc_bpb_type, 1, blob_type}; if (!blob.create(DB, gds_trans, *blob_id, sizeof(blob_desc), blob_desc)) { BURP_error_redirect (status_vector, 37); // msg 37 isc_create_blob failed } // Allocate blob buffer if static buffer is too short BlobBuffer static_buffer; UCHAR* const buffer = static_buffer.getBuffer(max_segment); // Eat up blob segments for (; segments > 0; --segments ) { USHORT length = get(tdgbl); length |= get(tdgbl) << 8; if (length) { get_block(tdgbl, buffer, length); } if (!blob.putSegment(length, buffer)) { BURP_error_redirect (status_vector, 38); // msg 38 isc_put_segment failed } } if (!blob.close()) BURP_error_redirect (status_vector, 23); // msg 23 isc_close_blob failed } void get_blr_blob(BurpGlobals* tdgbl, ISC_QUAD& blob_id, bool glb_trans) { /************************************** * * g e t _ b l r _ b l o b * ************************************** * * Functional description * Read blob attributes and copy data from input file to nice, * shiney, new blob. * **************************************/ size_t length = get_numeric(tdgbl); // Create new blob isc_tr_handle local_trans; if (glb_trans && tdgbl->global_trans) local_trans = tdgbl->global_trans; else local_trans = gds_trans; ISC_STATUS_ARRAY status_vector; UserBlob blob(status_vector); if (!blob.create(DB, local_trans, blob_id)) { BURP_error_redirect (status_vector, 37); // msg 37 isc_create_blob failed } BlobBuffer static_buffer; UCHAR* const buffer = static_buffer.getBuffer(length + 1); if (length) { UCHAR* p = get_block(tdgbl, buffer, length); // Make sure it has an eoc if (p[-1] != blr_eoc) { p[0] = blr_eoc; length++; } } if (!blob.putData(length, buffer)) { BURP_error_redirect (status_vector, 38); // msg 38 isc_put_segment failed } if (!blob.close()) BURP_error_redirect (status_vector, 23); // msg 23 isc_close_blob failed } bool get_character_set(BurpGlobals* tdgbl) { /************************************** * * g e t _ c h a r a c t e r _ s e t * ************************************** * * Functional description * Restore data for user defined character sets * **************************************/ class AbortException { }; att_type attribute; scan_attr_t scan_next_attr; try { STORE (REQUEST_HANDLE tdgbl->handles_get_character_sets_req_handle1) X IN RDB$CHARACTER_SETS X.RDB$CHARACTER_SET_NAME.NULL = TRUE; X.RDB$FORM_OF_USE.NULL = TRUE; X.RDB$NUMBER_OF_CHARACTERS.NULL = TRUE; X.RDB$DEFAULT_COLLATE_NAME.NULL = TRUE; X.RDB$CHARACTER_SET_ID.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$FUNCTION_NAME.NULL = TRUE; X.RDB$BYTES_PER_CHARACTER.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_charset_name: X.RDB$CHARACTER_SET_NAME.NULL = FALSE; GET_TEXT(X.RDB$CHARACTER_SET_NAME); BURP_verbose (msgVerbose_restore_charset, X.RDB$CHARACTER_SET_NAME); break; case att_charset_form: X.RDB$FORM_OF_USE.NULL = FALSE; GET_TEXT(X.RDB$FORM_OF_USE); break; case att_charset_numchar: X.RDB$NUMBER_OF_CHARACTERS.NULL = FALSE; X.RDB$NUMBER_OF_CHARACTERS = (USHORT) get_numeric(tdgbl); break; case att_charset_coll: X.RDB$DEFAULT_COLLATE_NAME.NULL = FALSE; GET_TEXT(X.RDB$DEFAULT_COLLATE_NAME); break; case att_charset_id: X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl); break; case att_charset_sysflag: X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl); break; case att_charset_description: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_charset_funct: X.RDB$FUNCTION_NAME.NULL = FALSE; GET_TEXT(X.RDB$FUNCTION_NAME); break; case att_charset_bytes_char: X.RDB$BYTES_PER_CHARACTER.NULL = FALSE; X.RDB$BYTES_PER_CHARACTER = (USHORT) get_numeric(tdgbl); break; default: bad_attribute (scan_next_attr, attribute, msgErr_restore_charset); // 213 character set break; } } if (X.RDB$CHARACTER_SET_ID.NULL && !X.RDB$DEFAULT_COLLATE_NAME.NULL && !X.RDB$CHARACTER_SET_NAME.NULL) { tdgbl->defaultCollations.add( Firebird::Pair >( X.RDB$CHARACTER_SET_NAME, X.RDB$DEFAULT_COLLATE_NAME)); throw AbortException(); // prevent the STORE } END_STORE; ON_ERROR general_on_error (); END_ERROR; } catch (const AbortException&) { } return true; } bool get_chk_constraint(BurpGlobals* tdgbl) { /************************************** * * g e t _ c h k _ c o n s t r a i n t * ************************************** * * Functional description * Restore data for check constraints. * **************************************/ att_type attribute; scan_attr_t scan_next_attr; STORE (REQUEST_HANDLE tdgbl->handles_get_chk_constraint_req_handle1) X IN RDB$CHECK_CONSTRAINTS X.RDB$CONSTRAINT_NAME.NULL = TRUE; X.RDB$TRIGGER_NAME.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_chk_constraint_name: X.RDB$CONSTRAINT_NAME.NULL = FALSE; GET_TEXT(X.RDB$CONSTRAINT_NAME); break; case att_chk_trigger_name: X.RDB$TRIGGER_NAME.NULL = FALSE; GET_TEXT(X.RDB$TRIGGER_NAME); break; default: bad_attribute (scan_next_attr, attribute, 286); // msg 286 check constraint break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_collation(BurpGlobals* tdgbl) { /************************************** * * g e t _ c o l l a t i o n * ************************************** * * Functional description * Restore data for user defined collations * **************************************/ att_type attribute; scan_attr_t scan_next_attr; if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11) { STORE (REQUEST_HANDLE tdgbl->handles_get_collation_req_handle1) X IN RDB$COLLATIONS X.RDB$COLLATION_NAME.NULL = TRUE; X.RDB$COLLATION_ID.NULL = TRUE; X.RDB$CHARACTER_SET_ID.NULL = TRUE; X.RDB$COLLATION_ATTRIBUTES.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$FUNCTION_NAME.NULL = TRUE; X.RDB$BASE_COLLATION_NAME.NULL = TRUE; X.RDB$SPECIFIC_ATTRIBUTES.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_coll_name: X.RDB$COLLATION_NAME.NULL = FALSE; GET_TEXT(X.RDB$COLLATION_NAME); BURP_verbose(msgVerbose_restore_collation, X.RDB$COLLATION_NAME); break; case att_coll_id: X.RDB$COLLATION_ID.NULL = FALSE; X.RDB$COLLATION_ID = (USHORT) get_numeric(tdgbl); break; case att_coll_cs_id: X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl); break; case att_coll_attr: X.RDB$COLLATION_ATTRIBUTES.NULL = FALSE; X.RDB$COLLATION_ATTRIBUTES = (USHORT) get_numeric(tdgbl); break; case att_coll_subtype: // No longer used: 93-11-15 DBS // still present to handle V4 R&D gbak files get_numeric(tdgbl); break; case att_coll_sysflag: X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl); break; case att_coll_description: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_coll_funct: X.RDB$FUNCTION_NAME.NULL = FALSE; GET_TEXT(X.RDB$FUNCTION_NAME); break; case att_coll_base_collation_name: if (tdgbl->RESTORE_format >= 7) { X.RDB$BASE_COLLATION_NAME.NULL = FALSE; GET_TEXT(X.RDB$BASE_COLLATION_NAME); } else bad_attribute (scan_next_attr, attribute, msgErr_restore_collation); break; case att_coll_specific_attr: if (tdgbl->RESTORE_format >= 7) { X.RDB$SPECIFIC_ATTRIBUTES.NULL = FALSE; get_source_blob (tdgbl, X.RDB$SPECIFIC_ATTRIBUTES, false); } else bad_attribute (scan_next_attr, attribute, msgErr_restore_collation); break; default: bad_attribute (scan_next_attr, attribute, msgErr_restore_collation); // Msg 215 collation break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; } else { STORE (REQUEST_HANDLE tdgbl->handles_get_collation_req_handle1) X IN RDB$COLLATIONS X.RDB$COLLATION_NAME.NULL = TRUE; X.RDB$COLLATION_ID.NULL = TRUE; X.RDB$CHARACTER_SET_ID.NULL = TRUE; X.RDB$COLLATION_ATTRIBUTES.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$FUNCTION_NAME.NULL = TRUE; X.RDB$BASE_COLLATION_NAME.NULL = TRUE; X.RDB$SPECIFIC_ATTRIBUTES.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_coll_name: X.RDB$COLLATION_NAME.NULL = FALSE; GET_TEXT(X.RDB$COLLATION_NAME); BURP_verbose(msgVerbose_restore_collation, X.RDB$COLLATION_NAME); break; case att_coll_id: X.RDB$COLLATION_ID.NULL = FALSE; X.RDB$COLLATION_ID = (USHORT) get_numeric(tdgbl); break; case att_coll_cs_id: X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl); break; case att_coll_attr: X.RDB$COLLATION_ATTRIBUTES.NULL = FALSE; X.RDB$COLLATION_ATTRIBUTES = (USHORT) get_numeric(tdgbl); break; case att_coll_subtype: // No longer used: 93-11-15 DBS // still present to handle V4 R&D gbak files get_numeric(tdgbl); break; case att_coll_sysflag: X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl); break; case att_coll_description: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_coll_funct: X.RDB$FUNCTION_NAME.NULL = FALSE; GET_TEXT(X.RDB$FUNCTION_NAME); break; case att_coll_base_collation_name: if (tdgbl->RESTORE_format >= 7) eat_text(tdgbl); else bad_attribute (scan_next_attr, attribute, msgErr_restore_collation); break; case att_coll_specific_attr: if (tdgbl->RESTORE_format >= 7) eat_blob(tdgbl); else bad_attribute (scan_next_attr, attribute, msgErr_restore_collation); break; default: bad_attribute (scan_next_attr, attribute, msgErr_restore_collation); // Msg 215 collation break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; } return true; } rec_type get_data(BurpGlobals* tdgbl, burp_rel* relation) { /************************************** * * g e t _ d a t a * ************************************** * * Functional description * Write data records for a relation. * **************************************/ isc_req_handle req_handle = 0; BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name; // If we're only doing meta-data, ignore data records if (tdgbl->gbl_sw_meta) return ignore_data(tdgbl, relation); // Start by counting the interesting fields RCRD_OFFSET offset = 0; ULONG length = 0; USHORT count = 0; burp_fld* field; for (field = relation->rel_fields; field; field = field->fld_next) { if (!(field->fld_flags & FLD_computed)) { count++; length += field->fld_name_length; } } if (tdgbl->RESTORE_format >= 2) count += count; // Time to generate blr to store data. Whoppee. UCHAR* const blr_buffer = (UCHAR*) BURP_alloc (200 + length + count * 18); UCHAR* blr = blr_buffer; add_byte(blr, blr_version4); add_byte(blr, blr_begin); add_byte(blr, blr_message); add_byte(blr, 0); // Message number add_word(blr, count); // Number of fields, counting eof // Let's reset count. count = 0; for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; // arrays are of various fld_types but are really blobs SSHORT dtype = field->fld_type; length = field->fld_length; SSHORT alignment = 4; if (field->fld_flags & FLD_array) dtype = blr_blob; if (dtype <= DTYPE_BLR_MAX) { USHORT l = gds_cvt_blr_dtype[dtype]; alignment = type_alignments[l]; if (l = type_lengths[l]) length = l; } switch (dtype) { case blr_text: case blr_varying: if (dtype == blr_text) add_byte(blr, blr_text2); else add_byte(blr, blr_varying2); if (tdgbl->gbl_sw_fix_fss_data && field->fld_character_set_id == CS_UNICODE_FSS) add_word(blr, tdgbl->gbl_sw_fix_fss_data_id); else add_word(blr, field->fld_character_set_id); add_word(blr, field->fld_length); if (dtype == blr_varying) length += sizeof(USHORT); break; case blr_short: case blr_long: case blr_quad: case blr_int64: add_byte(blr, field->fld_type); add_byte(blr, field->fld_scale); break; case blr_float: case blr_double: case blr_timestamp: case blr_sql_time: case blr_sql_date: add_byte(blr, field->fld_type); break; case blr_blob: alignment = type_alignments[dtype_blob]; length = type_lengths[dtype_blob]; if (tdgbl->gbl_sw_fix_fss_data && !(field->fld_flags & FLD_array) && field->fld_sub_type == isc_blob_text && field->fld_character_set_id == CS_UNICODE_FSS) { add_byte(blr, blr_blob2); add_word(blr, field->fld_sub_type); add_word(blr, tdgbl->gbl_sw_fix_fss_data_id); } else { add_byte(blr, blr_quad); add_byte(blr, 0); } break; default: BURP_error(26, true, SafeArg() << field->fld_type); // msg 26 datatype %ld not understood break; } if (alignment) offset = FB_ALIGN(offset, alignment); field->fld_offset = offset; field->fld_parameter = count++; offset += length; } // If this is format version 2, build fields for null flags if (tdgbl->RESTORE_format >= 2) for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; add_byte(blr, blr_short); add_byte(blr, 0); offset = FB_ALIGN(offset, sizeof(SSHORT)); field->fld_missing_parameter = count++; offset += sizeof(SSHORT); } length = offset; // Build STORE statement add_byte(blr, blr_receive); add_byte(blr, 0); add_byte(blr, blr_store); add_byte(blr, blr_relation); add_string(blr, relation->rel_name); add_byte(blr, 0); // context variable add_byte(blr, blr_begin); for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; add_byte(blr, blr_assignment); if (tdgbl->RESTORE_format >= 2) { add_byte(blr, blr_parameter2); add_byte(blr, 0); add_word(blr, field->fld_parameter); add_word(blr, field->fld_missing_parameter); } else { add_byte(blr, blr_parameter); add_byte(blr, 0); add_word(blr, field->fld_parameter); } add_byte(blr, blr_field); add_byte(blr, 0); add_string(blr, field->fld_name); } add_byte(blr, blr_end); add_byte(blr, blr_end); add_byte(blr, blr_eoc); // Compile request #ifdef DEBUG isc_print_blr (reinterpret_cast(blr_buffer), NULL, NULL, 0); #endif FB_API_HANDLE request = 0; ISC_STATUS_ARRAY status_vector; USHORT blr_length = blr - blr_buffer; if (isc_compile_request (status_vector, &DB, &request, blr_length, reinterpret_cast(blr_buffer))) { isc_print_blr(reinterpret_cast(blr_buffer), NULL, NULL, 0); if (!tdgbl->gbl_sw_incremental) BURP_error_redirect (status_vector, 27); // msg 27 isc_compile_request failed else { BURP_print_status (status_vector); BURP_free (blr_buffer); return ignore_data(tdgbl, relation); } } BURP_free (blr_buffer); SSHORT* buffer = NULL; BURP_verbose (124, relation->rel_name); // msg 124 restoring data for relation %s lstring data; data.lstr_allocated = 0; data.lstr_address = NULL; ULONG old_length = 0; ULONG records = 0; rec_type record; while (true) { if (get(tdgbl) != att_data_length) BURP_error_redirect (NULL, 39); // msg 39 expected record length USHORT l = (USHORT) get_numeric(tdgbl); if (!tdgbl->gbl_sw_transportable && l != length) { #ifdef sparc if (!old_length) old_length = recompute_length(tdgbl, relation); #endif if (l != old_length) { BURP_error(40, true, SafeArg() << length << l); // msg 40 wrong length record, expected %ld encountered %ld } } if (!buffer) { buffer = (SSHORT *) BURP_alloc (MAX (length, l)); } UCHAR* p; if (tdgbl->gbl_sw_transportable) { if (get(tdgbl) != att_xdr_length) BURP_error_redirect (NULL, 55); // msg 55 Expected XDR record length else { data.lstr_length = l = (USHORT) get_numeric(tdgbl); if (l > data.lstr_allocated) { data.lstr_allocated = l; if (data.lstr_address) BURP_free (data.lstr_address); data.lstr_address = BURP_alloc(data.lstr_allocated); } p = data.lstr_address; } } else p = reinterpret_cast(buffer); if (get(tdgbl) != att_data_data) BURP_error_redirect (NULL, 41); // msg 41 expected data attribute if (tdgbl->gbl_sw_compress) decompress (tdgbl, p, l); else { get_block(tdgbl, p, l); } if (old_length) realign (tdgbl, (UCHAR*) buffer, relation); if (tdgbl->gbl_sw_transportable) CAN_encode_decode (relation, &data, (UCHAR *)buffer, FALSE); records++; if ((records % RESTORE_VERBOSE_INTERVAL) == 0) BURP_verbose(107, SafeArg() << records); for (field = relation->rel_fields; field; field = field->fld_next) { if (!(field->fld_flags & FLD_computed)) { if (field->fld_type == blr_blob || (field->fld_flags & FLD_array)) { ISC_QUAD* blob_id = (ISC_QUAD*) ((SCHAR*) buffer + field->fld_offset); blob_id->gds_quad_high = 0; blob_id->gds_quad_low = 0; } } } get_record(&record, tdgbl); while (record == rec_blob || record == rec_array) { if (record == rec_blob) get_blob (tdgbl, relation->rel_fields, (UCHAR *) buffer); else if (record == rec_array) get_array (tdgbl, relation, (UCHAR *) buffer); get_record(&record, tdgbl); } if (isc_start_and_send (status_vector, &request, &gds_trans, 0, (USHORT) length, buffer, 0)) { if (status_vector[1] == isc_not_valid) { if (tdgbl->gbl_sw_incremental) { BURP_print (138, relation->rel_name); // msg 138 validation error on field in relation %s BURP_print_status (status_vector); } else BURP_error_redirect (status_vector, 47); // msg 47 warning -- record could not be restored } else { if (tdgbl->gbl_sw_incremental) { BURP_print (114, relation->rel_name); // msg 114 restore failed for record in relation %s BURP_print_status (status_vector); } else BURP_error_redirect (status_vector, 48); // msg 48 isc_send failed } } if (record != rec_data) break; } // while (true) BURP_free (buffer); if (data.lstr_address) BURP_free (data.lstr_address); isc_release_request(status_vector, &request); if (tdgbl->gbl_sw_incremental) { BURP_verbose (72, relation->rel_name); // msg 72 committing data for relation %s COMMIT // existing ON_ERROR continues past error, beck ON_ERROR // Fix for bug_no 8055: // don't throw away the database just because an index // could not be made ISC_STATUS error_code; while (error_code = tdgbl->status_vector[1]) { switch (error_code) { case isc_sort_mem_err: case isc_no_dup: strcpy(index_name, (TEXT *)tdgbl->status_vector[3]); BURP_print_status(tdgbl->status_vector); FOR (REQUEST_HANDLE req_handle) IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ index_name MODIFY IDX USING IDX.RDB$INDEX_INACTIVE = TRUE; BURP_print(240, index_name); // msg 240 Index \"%s\" failed to activate because: if ( error_code == isc_no_dup ) { BURP_print(241); // msg 241 The unique index has duplicate values or NULLs BURP_print(242); // msg 242 Delete or Update duplicate values or NULLs, and activate index with } else { BURP_print(244); // msg 244 Not enough disk space to create the sort file for an index BURP_print(245); // msg 245 Set the TMP environment variable to a directory on a filesystem that does have enough space, and activate index with } BURP_print(243, index_name); // msg 243 ALTER INDEX \"%s\" ACTIVE END_MODIFY; END_FOR; // don't bring the database on-line tdgbl->flag_on_line = false; // commit one more time COMMIT ON_ERROR continue; END_ERROR break; default: BURP_print (69, relation->rel_name); // msg 69 commit failed on relation %s BURP_print_status (tdgbl->status_vector); ROLLBACK; ON_ERROR general_on_error (); END_ERROR; break; } // end of switch } // end of while END_ERROR; EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; } BURP_verbose (107, SafeArg() << records); // msg 107 %ld records restored return record; } bool get_exception(BurpGlobals* tdgbl) { /************************************** * * g e t _ e x c e p t i o n * ************************************** * * Functional description * Reconstruct a exception. * **************************************/ att_type attribute; TEXT temp[GDS_NAME_LEN]; ULONG l2 = 0; scan_attr_t scan_next_attr; STORE (REQUEST_HANDLE tdgbl->handles_get_exception_req_handle1) X IN RDB$EXCEPTIONS X.RDB$EXCEPTION_NAME.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$MESSAGE.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; att_type failed_attrib = att_end; bool msg_seen = false; // only for att_exception_msg, not att_exception_msg2 UCHAR* msg_ptr = reinterpret_cast(X.RDB$MESSAGE); skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_exception_name: if (!X.RDB$EXCEPTION_NAME.NULL) BURP_error(311, true, SafeArg() << att_exception_name << X.RDB$EXCEPTION_NAME); else { const ULONG l = GET_TEXT(X.RDB$EXCEPTION_NAME); X.RDB$EXCEPTION_NAME.NULL = FALSE; MISC_terminate (X.RDB$EXCEPTION_NAME, temp, l, sizeof(temp)); BURP_verbose (199, temp); // msg 199 restoring exception %s } break; case att_exception_description: if (!X.RDB$DESCRIPTION.NULL) BURP_error(311, true, SafeArg() << att_exception_description << X.RDB$EXCEPTION_NAME); else { msg_seen = false; get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); X.RDB$DESCRIPTION.NULL = FALSE; } break; case att_exception_description2: if (!X.RDB$DESCRIPTION.NULL) BURP_error(311, true, SafeArg() << att_exception_description2 << X.RDB$EXCEPTION_NAME); else { msg_seen = false; get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); X.RDB$DESCRIPTION.NULL = FALSE; } break; case att_exception_msg: if (msg_seen) BURP_error(311, true, SafeArg() << att_exception_msg << X.RDB$EXCEPTION_NAME); else if (!X.RDB$MESSAGE.NULL) { msg_seen = true; BURP_print(312, SafeArg() << att_exception_msg << X.RDB$EXCEPTION_NAME); eat_text(tdgbl); } else { msg_seen = true; l2 = GET_TEXT(X.RDB$MESSAGE); msg_ptr += l2; X.RDB$MESSAGE.NULL = FALSE; } break; case att_exception_msg2: if (msg_seen) BURP_error(311, true, SafeArg() << att_exception_msg2 << X.RDB$EXCEPTION_NAME); else if (!X.RDB$MESSAGE.NULL) { BURP_print(312, SafeArg() << att_exception_msg2 << X.RDB$EXCEPTION_NAME); eat_text2(tdgbl); } else { GET_TEXT2(X.RDB$MESSAGE); X.RDB$MESSAGE.NULL = FALSE; } break; default: if (msg_seen && (tdgbl->RESTORE_format == 7 || tdgbl->RESTORE_format == 8)) { // we have a corrupt backup if (!failed_attrib) { failed_attrib = attribute; BURP_print(313, SafeArg() << failed_attrib << X.RDB$EXCEPTION_NAME); } // Notice we use 1021 instead of 1023 because this is the maximum length // for this field in v2.0 and v2.1 and they produce the corrupt backups. const int FIELD_LIMIT = 1021; const int remaining = FIELD_LIMIT - l2; if (remaining < 1) // not enough space { bad_attribute(scan_next_attr, failed_attrib, 287); break; } *msg_ptr++ = char(attribute); // (1) UCHAR* rc_ptr = get_block(tdgbl, msg_ptr, MIN(remaining - 1, 255)); if (remaining > 1 && rc_ptr == msg_ptr) // we couldn't read anything { bad_attribute(scan_next_attr, failed_attrib, 287); break; } l2 += rc_ptr - msg_ptr + 1; // + 1 because (1) msg_ptr = rc_ptr; *msg_ptr = 0; if (l2 == FIELD_LIMIT) msg_seen = false; } else bad_attribute(scan_next_attr, attribute, 287); // msg 287 exception break; } } // Versions prior to FB2.0 don't support a field longer than varchar(78). // Versions prior to FB2.5 use a field length of 1021, not 1023. if (tdgbl->RESTORE_ods < DB_VERSION_DDL11) X.RDB$MESSAGE[78] = 0; else if (tdgbl->RESTORE_ods < DB_VERSION_DDL11_2) X.RDB$MESSAGE[1021] = 0; END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation) { /************************************** * * g e t _ f i e l d * ************************************** * * Functional description * Reconstruct a local field. * **************************************/ att_type attribute; scan_attr_t scan_next_attr; // If it is a view and there is a global transaction then use it bool global_tr = false; isc_tr_handle local_trans; if ((relation->rel_flags & REL_view) && tdgbl->global_trans) { local_trans = tdgbl->global_trans; global_tr = true; } else local_trans = gds_trans; burp_fld* field = (burp_fld*) BURP_alloc_zero (sizeof(burp_fld)); STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_field_req_handle1) X IN RDB$RELATION_FIELDS strcpy (X.RDB$RELATION_NAME, relation->rel_name); X.RDB$FIELD_POSITION = 0; memset (X.RDB$QUERY_NAME, ' ', sizeof(X.RDB$QUERY_NAME)); X.RDB$VIEW_CONTEXT.NULL = TRUE; X.RDB$BASE_FIELD.NULL = TRUE; X.RDB$SECURITY_CLASS.NULL = TRUE; X.RDB$QUERY_NAME.NULL = TRUE; X.RDB$QUERY_HEADER.NULL = TRUE; X.RDB$EDIT_STRING.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$FIELD_POSITION.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$COMPLEX_NAME.NULL = TRUE; X.RDB$UPDATE_FLAG.NULL = TRUE; X.RDB$DEFAULT_SOURCE.NULL = TRUE; X.RDB$DEFAULT_VALUE.NULL = TRUE; X.RDB$NULL_FLAG.NULL = TRUE; X.RDB$COLLATION_ID.NULL = TRUE; skip_init(&scan_next_attr); while (get_attribute(&attribute, tdgbl) != att_end) { switch (skip_scan(&scan_next_attr), attribute) { case att_field_name: field->fld_name_length = GET_TEXT(field->fld_name); BURP_verbose (115, field->fld_name); // msg 115 restoring field %s strcpy (X.RDB$FIELD_NAME, field->fld_name); break; case att_field_source: GET_TEXT(X.RDB$FIELD_SOURCE); break; case att_field_security_class: GET_TEXT(X.RDB$SECURITY_CLASS); fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, true); X.RDB$SECURITY_CLASS.NULL = FALSE; break; case att_field_query_name: GET_TEXT(X.RDB$QUERY_NAME); X.RDB$QUERY_NAME.NULL = FALSE; break; case att_field_query_header: X.RDB$QUERY_HEADER.NULL = FALSE; get_source_blob (tdgbl, X.RDB$QUERY_HEADER, global_tr); break; case att_field_edit_string: GET_TEXT(X.RDB$EDIT_STRING); X.RDB$EDIT_STRING.NULL = FALSE; break; case att_field_position: X.RDB$FIELD_POSITION.NULL = FALSE; X.RDB$FIELD_POSITION = (USHORT) get_numeric(tdgbl); break; case att_field_number: field->fld_number = (USHORT) get_numeric(tdgbl); break; case att_field_type: field->fld_type = (USHORT) get_numeric(tdgbl); break; case att_field_length: field->fld_length = (USHORT) get_numeric(tdgbl); break; case att_field_scale: field->fld_scale = (USHORT) get_numeric(tdgbl); break; case att_field_sub_type: field->fld_sub_type = (USHORT) get_numeric(tdgbl); break; case att_field_system_flag: X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl); X.RDB$SYSTEM_FLAG.NULL = FALSE; break; case att_view_context: X.RDB$VIEW_CONTEXT = (USHORT) get_numeric(tdgbl); X.RDB$VIEW_CONTEXT.NULL = FALSE; break; case att_field_computed_flag: if (get_numeric(tdgbl)) field->fld_flags |= FLD_computed; break; case att_base_field: GET_TEXT(X.RDB$BASE_FIELD); X.RDB$BASE_FIELD.NULL = FALSE; break; case att_field_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DESCRIPTION, global_tr); break; case att_field_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, global_tr); break; case att_field_complex_name: GET_TEXT(X.RDB$COMPLEX_NAME); X.RDB$COMPLEX_NAME.NULL = FALSE; break; case att_field_dimensions: { field->fld_dimensions = (USHORT) get_numeric(tdgbl); field->fld_flags |= FLD_array; USHORT n = field->fld_dimensions; for (SLONG* rp = field->fld_ranges; n; rp += 2, n--) { if (get_attribute(&attribute, tdgbl) != att_field_range_low) bad_attribute (scan_next_attr, attribute, 58); // msg 58 array else *rp = get_numeric(tdgbl); if (get_attribute(&attribute, tdgbl) != att_field_range_high) bad_attribute (scan_next_attr, attribute, 58); // msg 58 array else *(rp + 1) = get_numeric(tdgbl); } } break; case att_field_update_flag: X.RDB$UPDATE_FLAG.NULL = FALSE; X.RDB$UPDATE_FLAG = (USHORT) get_numeric(tdgbl); break; case att_field_character_length: field->fld_character_length = (USHORT) get_numeric(tdgbl); break; case att_field_default_source: X.RDB$DEFAULT_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, global_tr); break; case att_field_default_value: X.RDB$DEFAULT_VALUE.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, global_tr); break; case att_field_null_flag: if (tdgbl->gbl_sw_novalidity) { get_numeric(tdgbl); // skip } else { X.RDB$NULL_FLAG.NULL = FALSE; X.RDB$NULL_FLAG = (USHORT) get_numeric(tdgbl); } break; case att_field_character_set: field->fld_character_set_id = (USHORT) get_numeric(tdgbl); break; case att_field_collation_id: field->fld_collation_id = (USHORT) get_numeric(tdgbl); X.RDB$COLLATION_ID.NULL = FALSE; X.RDB$COLLATION_ID = field->fld_collation_id; break; default: bad_attribute (scan_next_attr, attribute, 84); // msg 84 column break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return field; } bool get_field_dimensions(BurpGlobals* tdgbl) { /************************************** * * g e t _ f i e l d _ d i m e n s i o n s * ************************************** * * Functional description * Get array field dimensions in rdb$field_dimensions. * **************************************/ att_type attribute; scan_attr_t scan_next_attr; STORE (REQUEST_HANDLE tdgbl->handles_get_field_dimensions_req_handle1) X IN RDB$FIELD_DIMENSIONS skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_field_name: GET_TEXT(X.RDB$FIELD_NAME); break; case att_field_dimensions: X.RDB$DIMENSION = (USHORT) get_numeric(tdgbl); break; case att_field_range_low: X.RDB$LOWER_BOUND = get_numeric(tdgbl); break; case att_field_range_high: X.RDB$UPPER_BOUND = get_numeric(tdgbl); break; default: bad_attribute (scan_next_attr, attribute, 288); // msg 288 array dimensions break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_files(BurpGlobals* tdgbl) { /************************************** * * g e t _ f i l e s * ************************************** * * Functional description * Get any files that were stored; let * somebody else worry about what to do with them. * **************************************/ att_type attribute; scan_attr_t scan_next_attr; STORE (REQUEST_HANDLE tdgbl->handles_get_files_req_handle1) X IN RDB$FILES X.RDB$FILE_FLAGS = 0; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_file_filename: GET_TEXT(X.RDB$FILE_NAME); BURP_verbose (116, X.RDB$FILE_NAME); // msg 116 restoring file %s break; case att_file_sequence: X.RDB$FILE_SEQUENCE = (USHORT) get_numeric(tdgbl); break; case att_file_start: X.RDB$FILE_START = get_numeric(tdgbl); break; case att_file_length: X.RDB$FILE_LENGTH = get_numeric(tdgbl); break; case att_file_flags: X.RDB$FILE_FLAGS |= get_numeric(tdgbl); break; case att_shadow_number: X.RDB$SHADOW_NUMBER = (USHORT) get_numeric(tdgbl); if (tdgbl->gbl_sw_kill && X.RDB$SHADOW_NUMBER) X.RDB$FILE_FLAGS |= FILE_inactive; break; default: bad_attribute (scan_next_attr, attribute, 85); // msg 85 file break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_filter(BurpGlobals* tdgbl) { /************************************** * * g e t _ f i l t e r * ************************************** * * Functional description * Get a type definition in rdb$filters. * **************************************/ att_type attribute; scan_attr_t scan_next_attr; STORE (REQUEST_HANDLE tdgbl->handles_get_filter_req_handle1) X IN RDB$FILTERS X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_filter_name: GET_TEXT(X.RDB$FUNCTION_NAME); BURP_verbose (117, X.RDB$FUNCTION_NAME); // msg 117 restoring filter %s break; case att_filter_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_filter_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_filter_module_name: GET_TEXT(X.RDB$MODULE_NAME); break; case att_filter_entrypoint: GET_TEXT(X.RDB$ENTRYPOINT); break; case att_filter_input_sub_type: X.RDB$INPUT_SUB_TYPE = (USHORT) get_numeric(tdgbl); break; case att_filter_output_sub_type: X.RDB$OUTPUT_SUB_TYPE = (USHORT) get_numeric(tdgbl); break; default: bad_attribute (scan_next_attr, attribute, 87); // msg 87 filter break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_function(BurpGlobals* tdgbl) { /************************************** * * g e t _ f u n c t i o n * ************************************** * * Functional description * Reconstruct a function. * **************************************/ att_type attribute; GDS_NAME function_name; TEXT temp[GDS_NAME_LEN]; SSHORT l; scan_attr_t scan_next_attr; bool existFlag = false; STORE (REQUEST_HANDLE tdgbl->handles_get_function_req_handle1) X IN RDB$FUNCTIONS X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$DESCRIPTION.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_function_name: l = GET_TEXT(X.RDB$FUNCTION_NAME); MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp)); BURP_verbose (118, temp); // msg 118 restoring function %s break; case att_function_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_function_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_function_module_name: GET_TEXT(X.RDB$MODULE_NAME); break; case att_function_entrypoint: GET_TEXT(X.RDB$ENTRYPOINT); break; case att_function_return_arg: X.RDB$RETURN_ARGUMENT = (USHORT) get_numeric(tdgbl); break; case att_function_query_name: GET_TEXT(X.RDB$QUERY_NAME); break; case att_function_type: X.RDB$FUNCTION_TYPE = (USHORT) get_numeric(tdgbl); break; default: bad_attribute (scan_next_attr, attribute, 89); // msg 89 function break; } } strcpy (function_name, X.RDB$FUNCTION_NAME); END_STORE; ON_ERROR if (gds_status[1] != isc_no_dup) { general_on_error (); } else { existFlag = true; } END_ERROR; // at the end of args for a function is the rec_function_end marker while (get(tdgbl) == rec_function_arg) get_function_arg(tdgbl, existFlag); return true; } void get_function_arg(BurpGlobals* tdgbl, bool skip_arguments) { /************************************** * * g e t _ f u n c t i o n _ a r g * ************************************** * * Functional description * Reconstruct function argument. * **************************************/ att_type attribute; SSHORT l; TEXT temp[GDS_NAME_LEN]; scan_attr_t scan_next_attr; if (skip_arguments) { char buf[MAX_SQL_IDENTIFIER_SIZE]; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_functionarg_name: GET_TEXT(buf); break; case att_functionarg_position: get_numeric(tdgbl); break; case att_functionarg_mechanism: get_numeric(tdgbl); break; case att_functionarg_field_type: get_numeric(tdgbl); break; case att_functionarg_field_scale: get_numeric(tdgbl); break; case att_functionarg_field_length: get_numeric(tdgbl); break; case att_functionarg_field_sub_type: get_numeric(tdgbl); break; case att_functionarg_character_set: get_numeric(tdgbl); break; case att_functionarg_field_precision: get_numeric(tdgbl); break; default: bad_attribute (scan_next_attr, attribute, 90); // msg 90 function argument break; } } return; } if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10) { // with RDB$FIELD_PRECISION STORE (REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1) X IN RDB$FUNCTION_ARGUMENTS X.RDB$FIELD_SUB_TYPE.NULL = TRUE; X.RDB$CHARACTER_SET_ID.NULL = TRUE; X.RDB$FIELD_PRECISION.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_functionarg_name: l = GET_TEXT(X.RDB$FUNCTION_NAME); MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp)); BURP_verbose (119, temp); // msg 119 restoring argument for function %s break; case att_functionarg_position: X.RDB$ARGUMENT_POSITION = (USHORT) get_numeric(tdgbl); break; case att_functionarg_mechanism: X.RDB$MECHANISM = (USHORT) get_numeric(tdgbl); break; case att_functionarg_field_type: X.RDB$FIELD_TYPE = (USHORT) get_numeric(tdgbl); break; case att_functionarg_field_scale: X.RDB$FIELD_SCALE = (USHORT) get_numeric(tdgbl); break; case att_functionarg_field_length: X.RDB$FIELD_LENGTH = (USHORT) get_numeric(tdgbl); break; case att_functionarg_field_sub_type: X.RDB$FIELD_SUB_TYPE.NULL = FALSE; X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(tdgbl); break; case att_functionarg_character_set: X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl); break; case att_functionarg_field_precision: if (tdgbl->RESTORE_format >= 6) { X.RDB$FIELD_PRECISION.NULL = FALSE; X.RDB$FIELD_PRECISION = (USHORT) get_numeric(tdgbl); } else bad_attribute (scan_next_attr, attribute, 90); break; default: bad_attribute (scan_next_attr, attribute, 90); // msg 90 function argument break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; } else { // without RDB$FIELD_PRECISION STORE (REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1) X IN RDB$FUNCTION_ARGUMENTS X.RDB$FIELD_SUB_TYPE.NULL = TRUE; X.RDB$CHARACTER_SET_ID.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_functionarg_name: l = GET_TEXT(X.RDB$FUNCTION_NAME); MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp)); BURP_verbose (119, temp); // msg 119 restoring argument for function %s break; case att_functionarg_position: X.RDB$ARGUMENT_POSITION = (USHORT) get_numeric(tdgbl); break; case att_functionarg_mechanism: X.RDB$MECHANISM = (USHORT) get_numeric(tdgbl); break; case att_functionarg_field_type: X.RDB$FIELD_TYPE = (USHORT) get_numeric(tdgbl); break; case att_functionarg_field_scale: X.RDB$FIELD_SCALE = (USHORT) get_numeric(tdgbl); break; case att_functionarg_field_length: X.RDB$FIELD_LENGTH = (USHORT) get_numeric(tdgbl); break; case att_functionarg_field_sub_type: X.RDB$FIELD_SUB_TYPE.NULL = FALSE; X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(tdgbl); break; case att_functionarg_character_set: X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl); break; case att_functionarg_field_precision: if (tdgbl->RESTORE_format >= 6) get_numeric(tdgbl); else bad_attribute (scan_next_attr, attribute, 90); default: bad_attribute (scan_next_attr, attribute, 90); // msg 90 function argument break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; } } bool get_generator(BurpGlobals* tdgbl) { /************************************** * * g e t _ g e n e r a t o r * ************************************** * * Functional description * Pick up a gen-id. Like most things, there is ughly history. * In the modern world, gen_id are free floating records. In the * bad old days they were attributes of relations. Handle both * nicely. * **************************************/ SINT64 value = 0; BASED_ON RDB$GENERATORS.RDB$GENERATOR_NAME name; name[0] = 0; // just in case. att_type attribute; scan_attr_t scan_next_attr; skip_init(&scan_next_attr); ISC_QUAD gen_desc = {0, 0}; bool got_desc = false; while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_gen_generator: GET_TEXT(name); break; case att_gen_value: // IB v5 or earlier, gen_id value is an SLONG value = (SINT64) get_numeric(tdgbl); break; case att_gen_value_int64: // IB v6 or later, gen_id value is an SINT64 value = get_int64(tdgbl); break; case att_gen_description: if (tdgbl->RESTORE_format >= 7) { get_source_blob (tdgbl, gen_desc, false); got_desc = gen_desc.gds_quad_high || gen_desc.gds_quad_low; } else bad_attribute(scan_next_attr, attribute, 289); break; default: bad_attribute (scan_next_attr, attribute, 289); // msg 289 generator break; } } if (tdgbl->gbl_sw_meta) { value = 0; } store_blr_gen_id(tdgbl, name, value, got_desc ? &gen_desc : NULL); return true; } bool get_global_field(BurpGlobals* tdgbl) { /************************************** * * g e t _ g l o b a l _ f i e l d * ************************************** * * Functional description * Reconstruct a global field. * **************************************/ att_type attribute; TEXT temp[GDS_NAME_LEN]; SSHORT l; scan_attr_t scan_next_attr; gfld* gfield = NULL; if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10) { // with rdb$field_precision STORE (REQUEST_HANDLE tdgbl->handles_get_global_field_req_handle1) X IN RDB$FIELDS X.RDB$FIELD_SCALE = X.RDB$SEGMENT_LENGTH = 0; X.RDB$CHARACTER_SET_ID = X.RDB$COLLATION_ID = 0; X.RDB$FIELD_SUB_TYPE = 0; X.RDB$COMPUTED_BLR.NULL = TRUE; X.RDB$COMPUTED_SOURCE.NULL = TRUE; X.RDB$QUERY_NAME.NULL = TRUE; X.RDB$EDIT_STRING.NULL = TRUE; X.RDB$QUERY_HEADER.NULL = TRUE; X.RDB$MISSING_VALUE.NULL = TRUE; X.RDB$DEFAULT_VALUE.NULL = TRUE; X.RDB$VALIDATION_BLR.NULL = TRUE; X.RDB$VALIDATION_SOURCE.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$NULL_FLAG.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$DIMENSIONS.NULL = TRUE; X.RDB$EXTERNAL_LENGTH.NULL = TRUE; X.RDB$EXTERNAL_TYPE.NULL = TRUE; X.RDB$EXTERNAL_SCALE.NULL = TRUE; X.RDB$SEGMENT_LENGTH.NULL = TRUE; X.RDB$CHARACTER_LENGTH.NULL = TRUE; X.RDB$MISSING_SOURCE.NULL = TRUE; X.RDB$DEFAULT_SOURCE.NULL = TRUE; X.RDB$FIELD_SUB_TYPE.NULL = TRUE; X.RDB$CHARACTER_SET_ID.NULL = TRUE; X.RDB$COLLATION_ID.NULL = TRUE; X.RDB$FIELD_PRECISION.NULL = TRUE; memset (X.RDB$QUERY_NAME, ' ', sizeof(X.RDB$QUERY_NAME)); skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_field_name: l = GET_TEXT(X.RDB$FIELD_NAME); MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp)); BURP_verbose (121, temp); // msg 121 restoring global field %s break; case att_field_query_name: GET_TEXT(X.RDB$QUERY_NAME); X.RDB$QUERY_NAME.NULL = FALSE; break; case att_field_edit_string: GET_TEXT(X.RDB$EDIT_STRING); X.RDB$EDIT_STRING.NULL = FALSE; break; case att_field_query_header: X.RDB$QUERY_HEADER.NULL = FALSE; get_source_blob (tdgbl, X.RDB$QUERY_HEADER, false); break; case att_field_type: X.RDB$FIELD_TYPE = (USHORT) get_numeric(tdgbl); break; case att_field_length: X.RDB$FIELD_LENGTH = (USHORT) get_numeric(tdgbl); break; case att_field_scale: X.RDB$FIELD_SCALE = (USHORT) get_numeric(tdgbl); X.RDB$FIELD_SCALE.NULL = FALSE; break; case att_field_sub_type: X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(tdgbl); X.RDB$FIELD_SUB_TYPE.NULL = FALSE; break; case att_field_segment_length: X.RDB$SEGMENT_LENGTH = (USHORT) get_numeric(tdgbl); if (X.RDB$SEGMENT_LENGTH) X.RDB$SEGMENT_LENGTH.NULL = FALSE; break; case att_field_computed_blr: // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_blr_blob (tdgbl, gfield->gfld_computed_blr, true); gfield->gfld_flags |= GFLD_computed_blr; } else { X.RDB$COMPUTED_BLR.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$COMPUTED_BLR, false); } break; case att_field_computed_source: // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_misc_blob (tdgbl, gfield->gfld_computed_source, true); gfield->gfld_flags |= GFLD_computed_source; } else { X.RDB$COMPUTED_SOURCE.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false); } break; case att_field_computed_source2: // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_source_blob (tdgbl, gfield->gfld_computed_source2, true); gfield->gfld_flags |= GFLD_computed_source2; } else { X.RDB$COMPUTED_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false); } break; case att_field_validation_blr: if (tdgbl->gbl_sw_novalidity) eat_blob(tdgbl); else { // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_blr_blob (tdgbl, gfield->gfld_vb, true); gfield->gfld_flags |= GFLD_validation_blr; } else { X.RDB$VALIDATION_BLR.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$VALIDATION_BLR, false); } } break; case att_field_validation_source: if (tdgbl->gbl_sw_novalidity) eat_blob(tdgbl); else { // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_misc_blob (tdgbl, gfield->gfld_vs, true); gfield->gfld_flags |= GFLD_validation_source; } else { X.RDB$VALIDATION_SOURCE.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false); } } break; case att_field_validation_source2: if (tdgbl->gbl_sw_novalidity) eat_blob(tdgbl); else { // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_source_blob (tdgbl, gfield->gfld_vs2, true); gfield->gfld_flags |= GFLD_validation_source2; } else { X.RDB$VALIDATION_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false); } } break; case att_field_missing_value: X.RDB$MISSING_VALUE.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$MISSING_VALUE, false); break; case att_field_default_value: X.RDB$DEFAULT_VALUE.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, false); break; case att_field_system_flag: X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl); X.RDB$SYSTEM_FLAG.NULL = FALSE; break; case att_field_null_flag: if (tdgbl->gbl_sw_novalidity) { get_numeric(tdgbl); // skip } else { X.RDB$NULL_FLAG = (USHORT) get_numeric(tdgbl); X.RDB$NULL_FLAG.NULL = FALSE; } break; case att_field_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_field_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_field_external_length: X.RDB$EXTERNAL_LENGTH.NULL = FALSE; X.RDB$EXTERNAL_LENGTH = (USHORT) get_numeric(tdgbl); break; case att_field_external_scale: X.RDB$EXTERNAL_SCALE.NULL = FALSE; X.RDB$EXTERNAL_SCALE = (USHORT) get_numeric(tdgbl); break; case att_field_external_type: X.RDB$EXTERNAL_TYPE.NULL = FALSE; X.RDB$EXTERNAL_TYPE = (USHORT) get_numeric(tdgbl); break; case att_field_dimensions: X.RDB$DIMENSIONS.NULL = FALSE; X.RDB$DIMENSIONS = (USHORT) get_numeric(tdgbl); break; case att_field_character_length: X.RDB$CHARACTER_LENGTH.NULL = FALSE; X.RDB$CHARACTER_LENGTH = (USHORT) get_numeric(tdgbl); break; case att_field_default_source: X.RDB$DEFAULT_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, false); break; case att_field_missing_source: X.RDB$MISSING_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$MISSING_SOURCE, false); break; case att_field_character_set: X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl); break; case att_field_collation_id: X.RDB$COLLATION_ID.NULL = FALSE; X.RDB$COLLATION_ID = (USHORT) get_numeric(tdgbl); break; case att_field_precision: if (tdgbl->RESTORE_format >= 6) { X.RDB$FIELD_PRECISION.NULL = FALSE; X.RDB$FIELD_PRECISION = (USHORT) get_numeric(tdgbl); } else bad_attribute (scan_next_attr, attribute, 92); break; default: bad_attribute (scan_next_attr, attribute, 92); // msg 92 domain break; } } if (X.RDB$FIELD_TYPE <= DTYPE_BLR_MAX) { l = gds_cvt_blr_dtype[X.RDB$FIELD_TYPE]; if (l = type_lengths[l]) X.RDB$FIELD_LENGTH = l; } if (gfield) strcpy (gfield->gfld_name, X.RDB$FIELD_NAME); if (tdgbl->gbl_sw_fix_fss_data && tdgbl->gbl_sw_fix_fss_data_id == 0 && !X.RDB$CHARACTER_SET_ID.NULL && X.RDB$CHARACTER_SET_ID == CS_UNICODE_FSS && ((!X.RDB$CHARACTER_LENGTH.NULL && (X.RDB$FIELD_TYPE == blr_text || X.RDB$FIELD_TYPE == blr_varying)) || X.RDB$FIELD_TYPE == blr_blob)) { if (X.RDB$FIELD_TYPE != blr_blob) X.RDB$CHARACTER_LENGTH = X.RDB$FIELD_LENGTH; X.RDB$CHARACTER_SET_ID = CS_NONE; X.RDB$COLLATION_ID = 0; } END_STORE; ON_ERROR general_on_error (); END_ERROR; } else // RESTORE_ods < DB_VERSION_DDL10 { // without rdb$field_precision STORE (REQUEST_HANDLE tdgbl->handles_get_global_field_req_handle1) X IN RDB$FIELDS X.RDB$FIELD_SCALE = X.RDB$SEGMENT_LENGTH = 0; X.RDB$CHARACTER_SET_ID = X.RDB$COLLATION_ID = 0; X.RDB$FIELD_SUB_TYPE = 0; X.RDB$COMPUTED_BLR.NULL = TRUE; X.RDB$COMPUTED_SOURCE.NULL = TRUE; X.RDB$QUERY_NAME.NULL = TRUE; X.RDB$EDIT_STRING.NULL = TRUE; X.RDB$QUERY_HEADER.NULL = TRUE; X.RDB$MISSING_VALUE.NULL = TRUE; X.RDB$DEFAULT_VALUE.NULL = TRUE; X.RDB$VALIDATION_BLR.NULL = TRUE; X.RDB$VALIDATION_SOURCE.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$NULL_FLAG.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$DIMENSIONS.NULL = TRUE; X.RDB$EXTERNAL_LENGTH.NULL = TRUE; X.RDB$EXTERNAL_TYPE.NULL = TRUE; X.RDB$EXTERNAL_SCALE.NULL = TRUE; X.RDB$SEGMENT_LENGTH.NULL = TRUE; X.RDB$CHARACTER_LENGTH.NULL = TRUE; X.RDB$MISSING_SOURCE.NULL = TRUE; X.RDB$DEFAULT_SOURCE.NULL = TRUE; X.RDB$FIELD_SUB_TYPE.NULL = TRUE; X.RDB$CHARACTER_SET_ID.NULL = TRUE; X.RDB$COLLATION_ID.NULL = TRUE; memset (X.RDB$QUERY_NAME, ' ', sizeof(X.RDB$QUERY_NAME)); skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_field_name: l = GET_TEXT(X.RDB$FIELD_NAME); MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp)); BURP_verbose (121, temp); // msg 121 restoring global field %s break; case att_field_query_name: GET_TEXT(X.RDB$QUERY_NAME); X.RDB$QUERY_NAME.NULL = FALSE; break; case att_field_edit_string: GET_TEXT(X.RDB$EDIT_STRING); X.RDB$EDIT_STRING.NULL = FALSE; break; case att_field_query_header: X.RDB$QUERY_HEADER.NULL = FALSE; get_source_blob (tdgbl, X.RDB$QUERY_HEADER, false); break; case att_field_type: X.RDB$FIELD_TYPE = (USHORT) get_numeric(tdgbl); break; case att_field_length: X.RDB$FIELD_LENGTH = (USHORT) get_numeric(tdgbl); break; case att_field_scale: X.RDB$FIELD_SCALE = (USHORT) get_numeric(tdgbl); X.RDB$FIELD_SCALE.NULL = FALSE; break; case att_field_sub_type: X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(tdgbl); X.RDB$FIELD_SUB_TYPE.NULL = FALSE; break; case att_field_segment_length: X.RDB$SEGMENT_LENGTH = (USHORT) get_numeric(tdgbl); if (X.RDB$SEGMENT_LENGTH) X.RDB$SEGMENT_LENGTH.NULL = FALSE; break; case att_field_computed_blr: // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_blr_blob (tdgbl, gfield->gfld_computed_blr, true); gfield->gfld_flags |= GFLD_computed_blr; } else { X.RDB$COMPUTED_BLR.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$COMPUTED_BLR, false); } break; case att_field_computed_source: // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_misc_blob (tdgbl, gfield->gfld_computed_source, true); gfield->gfld_flags |= GFLD_computed_source; } else { X.RDB$COMPUTED_SOURCE.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false); } break; case att_field_computed_source2: // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_source_blob (tdgbl, gfield->gfld_computed_source2, true); gfield->gfld_flags |= GFLD_computed_source2; } else { X.RDB$COMPUTED_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false); } break; case att_field_validation_blr: if (tdgbl->gbl_sw_novalidity) eat_blob(tdgbl); else { // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_blr_blob (tdgbl, gfield->gfld_vb, true); gfield->gfld_flags |= GFLD_validation_blr; } else { X.RDB$VALIDATION_BLR.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$VALIDATION_BLR, false); } } break; case att_field_validation_source: if (tdgbl->gbl_sw_novalidity) eat_blob(tdgbl); else { // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_misc_blob (tdgbl, gfield->gfld_vs, true); gfield->gfld_flags |= GFLD_validation_source; } else { X.RDB$VALIDATION_SOURCE.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false); } } break; case att_field_validation_source2: if (tdgbl->gbl_sw_novalidity) eat_blob(tdgbl); else { // if we are going against a V4.0 database, // restore the global fields in 2 phases. if (tdgbl->global_trans) { if (!gfield) gfield = (gfld*) BURP_alloc_zero(sizeof(gfld)); get_source_blob (tdgbl, gfield->gfld_vs2, true); gfield->gfld_flags |= GFLD_validation_source2; } else { X.RDB$VALIDATION_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false); } } break; case att_field_missing_value: X.RDB$MISSING_VALUE.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$MISSING_VALUE, false); break; case att_field_default_value: X.RDB$DEFAULT_VALUE.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, false); break; case att_field_system_flag: X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl); X.RDB$SYSTEM_FLAG.NULL = FALSE; break; case att_field_null_flag: if (tdgbl->gbl_sw_novalidity) { get_numeric(tdgbl); // skip } else { X.RDB$NULL_FLAG = (USHORT) get_numeric(tdgbl); X.RDB$NULL_FLAG.NULL = FALSE; } break; case att_field_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_field_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_field_external_length: X.RDB$EXTERNAL_LENGTH.NULL = FALSE; X.RDB$EXTERNAL_LENGTH = (USHORT) get_numeric(tdgbl); break; case att_field_external_scale: X.RDB$EXTERNAL_SCALE.NULL = FALSE; X.RDB$EXTERNAL_SCALE = (USHORT) get_numeric(tdgbl); break; case att_field_external_type: X.RDB$EXTERNAL_TYPE.NULL = FALSE; X.RDB$EXTERNAL_TYPE = (USHORT) get_numeric(tdgbl); break; case att_field_dimensions: X.RDB$DIMENSIONS.NULL = FALSE; X.RDB$DIMENSIONS = (USHORT) get_numeric(tdgbl); break; case att_field_character_length: X.RDB$CHARACTER_LENGTH.NULL = FALSE; X.RDB$CHARACTER_LENGTH = (USHORT) get_numeric(tdgbl); break; case att_field_default_source: X.RDB$DEFAULT_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, false); break; case att_field_missing_source: X.RDB$MISSING_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$MISSING_SOURCE, false); break; case att_field_character_set: X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl); break; case att_field_collation_id: X.RDB$COLLATION_ID.NULL = FALSE; X.RDB$COLLATION_ID = (USHORT) get_numeric(tdgbl); break; case att_field_precision: if (tdgbl->RESTORE_format >= 6) get_numeric(tdgbl); else bad_attribute (scan_next_attr, attribute, 92); break; default: bad_attribute (scan_next_attr, attribute, 92); // msg 92 domain break; } } if (X.RDB$FIELD_TYPE <= DTYPE_BLR_MAX) { l = gds_cvt_blr_dtype[X.RDB$FIELD_TYPE]; if (l = type_lengths[l]) X.RDB$FIELD_LENGTH = l; } if (gfield) strcpy (gfield->gfld_name, X.RDB$FIELD_NAME); if (tdgbl->gbl_sw_fix_fss_data && tdgbl->gbl_sw_fix_fss_data_id == 0 && !X.RDB$CHARACTER_SET_ID.NULL && X.RDB$CHARACTER_SET_ID == CS_UNICODE_FSS && ((!X.RDB$CHARACTER_LENGTH.NULL && (X.RDB$FIELD_TYPE == blr_text || X.RDB$FIELD_TYPE == blr_varying)) || X.RDB$FIELD_TYPE == blr_blob)) { if (X.RDB$FIELD_TYPE != blr_blob) X.RDB$CHARACTER_LENGTH = X.RDB$FIELD_LENGTH; X.RDB$CHARACTER_SET_ID = CS_NONE; X.RDB$COLLATION_ID = 0; } END_STORE; ON_ERROR general_on_error (); END_ERROR; } if (gfield) { gfield->gfld_next = tdgbl->gbl_global_fields; tdgbl->gbl_global_fields = gfield; } return true; } bool get_index(BurpGlobals* tdgbl, const burp_rel* relation) { /************************************** * * g e t _ i n d e x * ************************************** * * Functional description * Build an index. At the end stop * and check that all fields are defined. * If any fields are missing, delete the * index. * **************************************/ BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name; att_type attribute; bool foreign_index = false; scan_attr_t scan_next_attr; SSHORT count = 0, segments = 0; STORE (REQUEST_HANDLE tdgbl->handles_get_index_req_handle1) X IN RDB$INDICES strcpy (X.RDB$RELATION_NAME, relation->rel_name); X.RDB$UNIQUE_FLAG = 0; if (!tdgbl->gbl_sw_deactivate_indexes) X.RDB$INDEX_INACTIVE = FALSE; else X.RDB$INDEX_INACTIVE = TRUE; X.RDB$INDEX_TYPE.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$FOREIGN_KEY.NULL = TRUE; X.RDB$EXPRESSION_SOURCE.NULL = TRUE; X.RDB$EXPRESSION_BLR.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_index_name: GET_TEXT(X.RDB$INDEX_NAME); strcpy (index_name, X.RDB$INDEX_NAME); BURP_verbose (122, X.RDB$INDEX_NAME); break; case att_segment_count: X.RDB$SEGMENT_COUNT = segments = (USHORT) get_numeric(tdgbl); break; case att_index_unique_flag: X.RDB$UNIQUE_FLAG = (USHORT) get_numeric(tdgbl); break; case att_index_inactive: X.RDB$INDEX_INACTIVE = (USHORT) get_numeric(tdgbl); // Defer foreign key index activation // Modified by Toni Martir, all index deferred when verbose if (tdgbl->gbl_sw_verbose) { if (!X.RDB$INDEX_INACTIVE) X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE; } else { if (!X.RDB$INDEX_INACTIVE && foreign_index) X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE; } if (tdgbl->gbl_sw_deactivate_indexes) X.RDB$INDEX_INACTIVE = TRUE; break; case att_index_type: X.RDB$INDEX_TYPE.NULL = FALSE; X.RDB$INDEX_TYPE = (USHORT) get_numeric(tdgbl); break; case att_index_field_name: STORE (REQUEST_HANDLE tdgbl->handles_get_index_req_handle2) Y IN RDB$INDEX_SEGMENTS GET_TEXT(Y.RDB$FIELD_NAME); strcpy (Y.RDB$INDEX_NAME, X.RDB$INDEX_NAME); Y.RDB$FIELD_POSITION = count++; END_STORE; ON_ERROR general_on_error (); END_ERROR; break; case att_index_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_index_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_index_expression_source: X.RDB$EXPRESSION_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$EXPRESSION_SOURCE, false); break; case att_index_expression_blr: X.RDB$EXPRESSION_BLR.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$EXPRESSION_BLR, false); break; case att_index_foreign_key: foreign_index = true; // Defer foreign key index activation if (!X.RDB$INDEX_INACTIVE) X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE; if (tdgbl->gbl_sw_deactivate_indexes) X.RDB$INDEX_INACTIVE = TRUE; X.RDB$FOREIGN_KEY.NULL = FALSE; GET_TEXT(X.RDB$FOREIGN_KEY); break; default: bad_attribute (scan_next_attr, attribute, 93); // msg 93 index break; } } count = 0; FOR (REQUEST_HANDLE tdgbl->handles_get_index_req_handle3) RFR IN RDB$RELATION_FIELDS CROSS I_S IN RDB$INDEX_SEGMENTS OVER RDB$FIELD_NAME WITH I_S.RDB$INDEX_NAME = index_name AND RFR.RDB$RELATION_NAME = relation->rel_name count++; END_FOR; ON_ERROR general_on_error (); END_ERROR; if (count != segments) { FOR (REQUEST_HANDLE tdgbl->handles_get_index_req_handle4) I_S IN RDB$INDEX_SEGMENTS WITH I_S.RDB$INDEX_NAME = index_name ERASE I_S; ON_ERROR general_on_error (); END_ERROR; END_FOR; ON_ERROR general_on_error (); END_ERROR; return false; } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } void get_misc_blob(BurpGlobals* tdgbl, ISC_QUAD& blob_id, bool glb_trans) { /************************************** * * g e t _ m i s c _ b l o b * ************************************** * * Functional description * Read blob attributes and copy data from input file to nice, * shiney, new blob. * **************************************/ ISC_STATUS_ARRAY status_vector; const size_t length = get_numeric(tdgbl); // Create new blob isc_tr_handle local_trans; if (glb_trans && tdgbl->global_trans) local_trans = tdgbl->global_trans; else local_trans = gds_trans; UserBlob blob(status_vector); if (!blob.create(DB, local_trans, blob_id)) { BURP_error_redirect (status_vector, 37); // msg 37 isc_create_blob failed } // Allocate blob buffer if static buffer is too short BlobBuffer static_buffer; UCHAR* const buffer = static_buffer.getBuffer(length); if (length) { get_block(tdgbl, buffer, length); } if (!blob.putData(length, buffer)) { BURP_error_redirect (status_vector, 38); // msg 38 isc_put_segment failed } if (!blob.close()) BURP_error_redirect (status_vector, 23); // msg 23 isc_close_blob failed } SLONG get_numeric(BurpGlobals* tdgbl) { /************************************** * * g e t _ n u m e r i c * ************************************** * * Functional description * Get a numeric value from the input stream. * **************************************/ SLONG value[2]; // get_text needs additional space for the terminator, // because it treats everything as strings. fb_assert(sizeof(value) > sizeof(SLONG)); const SSHORT length = get_text(tdgbl, (TEXT*) value, sizeof(value)); return isc_vax_integer ((SCHAR *) value, length); } SINT64 get_int64(BurpGlobals* tdgbl) { /************************************** * * g e t _ i n t 6 4 * ************************************** * * Functional description * Get a possibly-64-bit numeric value from the input stream. * **************************************/ SLONG value[4]; // get_text needs additional space for the terminator, // because it treats everything as strings. fb_assert (sizeof(value) > sizeof(SINT64)); const SSHORT length = get_text(tdgbl, (TEXT*) value, sizeof(value)); return isc_portable_integer ((UCHAR *) value, length); } bool get_procedure(BurpGlobals* tdgbl) { /************************************** * * g e t _ p r o c e d u r e * ************************************** * * Functional description * Reconstruct a stored procedure. * Use the global_trans so we don't have to commit * until after the indices are activated. This * will allow us to use a PLAN in a SP. * **************************************/ att_type attribute; GDS_NAME procedure_name = ""; TEXT temp[GDS_NAME_LEN]; SSHORT l; scan_attr_t scan_next_attr; isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; burp_prc* procedure = (burp_prc*) BURP_alloc_zero (sizeof(burp_prc)); procedure->prc_next = tdgbl->procedures; tdgbl->procedures = procedure; if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11_1) { STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_procedure_req_handle1) X IN RDB$PROCEDURES X.RDB$PROCEDURE_SOURCE.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$SECURITY_CLASS.NULL = TRUE; X.RDB$OWNER_NAME.NULL = TRUE; X.RDB$PROCEDURE_TYPE.NULL = FALSE; X.RDB$PROCEDURE_TYPE = 0; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$VALID_BLR.NULL = TRUE; X.RDB$DEBUG_INFO.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_procedure_name: l = GET_TEXT(X.RDB$PROCEDURE_NAME); //procedure->prc_name_length = l; strcpy (procedure->prc_name, X.RDB$PROCEDURE_NAME); MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof(temp)); BURP_verbose (195, temp); // msg 195 restoring stored procedure %s break; case att_procedure_description: get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_procedure_description2: get_source_blob (tdgbl, X.RDB$DESCRIPTION, true); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_procedure_source: get_misc_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true); X.RDB$PROCEDURE_SOURCE.NULL = FALSE; break; case att_procedure_source2: get_source_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true); X.RDB$PROCEDURE_SOURCE.NULL = FALSE; break; case att_procedure_blr: get_blr_blob (tdgbl, X.RDB$PROCEDURE_BLR, true); break; case att_procedure_security_class: GET_TEXT(X.RDB$SECURITY_CLASS); fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false); X.RDB$SECURITY_CLASS.NULL = FALSE; break; case att_procedure_owner_name: GET_TEXT(procedure->prc_owner); break; case att_procedure_inputs: X.RDB$PROCEDURE_INPUTS = (USHORT) get_numeric(tdgbl); if (X.RDB$PROCEDURE_INPUTS == 0) X.RDB$PROCEDURE_INPUTS.NULL = TRUE; else X.RDB$PROCEDURE_INPUTS.NULL = FALSE; break; case att_procedure_outputs: X.RDB$PROCEDURE_OUTPUTS = (USHORT) get_numeric(tdgbl); break; case att_procedure_type: if (tdgbl->RESTORE_format >= 8) X.RDB$PROCEDURE_TYPE = (USHORT) get_numeric(tdgbl); else bad_attribute (scan_next_attr, attribute, 290); break; case att_procedure_valid_blr: if (tdgbl->RESTORE_format >= 8) { X.RDB$VALID_BLR.NULL = FALSE; X.RDB$VALID_BLR = (USHORT) get_numeric(tdgbl); } else bad_attribute (scan_next_attr, attribute, 290); break; case att_procedure_debug_info: if (tdgbl->RESTORE_format >= 8) { X.RDB$DEBUG_INFO.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DEBUG_INFO, true); } else bad_attribute (scan_next_attr, attribute, 290); break; default: bad_attribute (scan_next_attr, attribute, 290); // msg 290 procedure break; } } strcpy (procedure_name, X.RDB$PROCEDURE_NAME); END_STORE; ON_ERROR general_on_error (); END_ERROR; } else { STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_procedure_req_handle1) X IN RDB$PROCEDURES X.RDB$PROCEDURE_SOURCE.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$SECURITY_CLASS.NULL = TRUE; X.RDB$OWNER_NAME.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_procedure_name: l = GET_TEXT(X.RDB$PROCEDURE_NAME); //procedure->prc_name_length = l; strcpy (procedure->prc_name, X.RDB$PROCEDURE_NAME); MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof(temp)); BURP_verbose (195, temp); // msg 195 restoring stored procedure %s break; case att_procedure_description: get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_procedure_description2: get_source_blob (tdgbl, X.RDB$DESCRIPTION, true); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_procedure_source: get_misc_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true); X.RDB$PROCEDURE_SOURCE.NULL = FALSE; break; case att_procedure_source2: get_source_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true); X.RDB$PROCEDURE_SOURCE.NULL = FALSE; break; case att_procedure_blr: get_blr_blob (tdgbl, X.RDB$PROCEDURE_BLR, true); break; case att_procedure_security_class: GET_TEXT(X.RDB$SECURITY_CLASS); fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false); X.RDB$SECURITY_CLASS.NULL = FALSE; break; case att_procedure_owner_name: GET_TEXT(procedure->prc_owner); break; case att_procedure_inputs: X.RDB$PROCEDURE_INPUTS = (USHORT) get_numeric(tdgbl); if (X.RDB$PROCEDURE_INPUTS == 0) X.RDB$PROCEDURE_INPUTS.NULL = TRUE; else X.RDB$PROCEDURE_INPUTS.NULL = FALSE; break; case att_procedure_outputs: X.RDB$PROCEDURE_OUTPUTS = (USHORT) get_numeric(tdgbl); break; case att_procedure_type: if (tdgbl->RESTORE_format >= 8) get_numeric(tdgbl); else bad_attribute (scan_next_attr, attribute, 290); break; default: bad_attribute (scan_next_attr, attribute, 290); // msg 290 procedure break; } } strcpy (procedure_name, X.RDB$PROCEDURE_NAME); END_STORE; ON_ERROR general_on_error (); END_ERROR; } // at the end of prms for a procedure is the rec_procedure_end marker while (get(tdgbl) == rec_procedure_prm) get_procedure_prm (tdgbl, procedure_name); return true; } bool get_procedure_prm (BurpGlobals* tdgbl, GDS_NAME procptr) { /************************************** * * g e t _ p r o c e d u r e _ p r m * ************************************** * * Functional description * Reconstruct stored procedure parameter. * Use the global_trans so we don't have to commit * until after the indices are activated. This * will allow us to use a PLAN in a SP. * **************************************/ att_type attribute; SSHORT l; TEXT temp[GDS_NAME_LEN]; scan_attr_t scan_next_attr; isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11_1) { STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_procedure_prm_req_handle1) X IN RDB$PROCEDURE_PARAMETERS strcpy(X.RDB$PROCEDURE_NAME, procptr); X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$DEFAULT_VALUE.NULL = TRUE; X.RDB$DEFAULT_SOURCE.NULL = TRUE; X.RDB$COLLATION_ID.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$NULL_FLAG = FALSE; X.RDB$NULL_FLAG.NULL = FALSE; X.RDB$PARAMETER_MECHANISM = prm_mech_normal; X.RDB$PARAMETER_MECHANISM.NULL = FALSE; // DB_VERSION_DDL11_2 X.RDB$FIELD_NAME.NULL = TRUE; X.RDB$RELATION_NAME.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_procedureprm_name: l = GET_TEXT(X.RDB$PARAMETER_NAME); MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof(temp)); BURP_verbose (196, temp); // msg 196 restoring parameter %s for stored procedure break; case att_procedureprm_type: X.RDB$PARAMETER_TYPE= (USHORT) get_numeric(tdgbl); break; case att_procedureprm_number: X.RDB$PARAMETER_NUMBER= (USHORT) get_numeric(tdgbl); break; case att_procedureprm_field_source: GET_TEXT(X.RDB$FIELD_SOURCE); break; case att_procedureprm_description: get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_procedureprm_description2: get_source_blob (tdgbl, X.RDB$DESCRIPTION, true); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_procedureprm_default_value: if (tdgbl->RESTORE_format >= 8) { X.RDB$DEFAULT_VALUE.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, true); } else bad_attribute (scan_next_attr, attribute, 291); break; case att_procedureprm_default_source: if (tdgbl->RESTORE_format >= 8) { X.RDB$DEFAULT_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, true); } else bad_attribute (scan_next_attr, attribute, 291); break; case att_procedureprm_collation_id: if (tdgbl->RESTORE_format >= 8) { X.RDB$COLLATION_ID.NULL = FALSE; X.RDB$COLLATION_ID = (USHORT) get_numeric(tdgbl); } else bad_attribute (scan_next_attr, attribute, 291); break; case att_procedureprm_null_flag: if (tdgbl->RESTORE_format >= 8) X.RDB$NULL_FLAG = (USHORT) get_numeric(tdgbl); else bad_attribute (scan_next_attr, attribute, 291); break; case att_procedureprm_mechanism: if (tdgbl->RESTORE_format >= 8) X.RDB$PARAMETER_MECHANISM = (USHORT) get_numeric(tdgbl); else bad_attribute (scan_next_attr, attribute, 291); break; // DB_VERSION_DDL11_2 case att_procedureprm_field_name: X.RDB$FIELD_NAME.NULL = FALSE; GET_TEXT(X.RDB$FIELD_NAME); break; // DB_VERSION_DDL11_2 case att_procedureprm_relation_name: X.RDB$RELATION_NAME.NULL = FALSE; GET_TEXT(X.RDB$RELATION_NAME); break; default: bad_attribute (scan_next_attr, attribute, 291); // msg 291 procedure parameter break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; } else { STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_procedure_prm_req_handle1) X IN RDB$PROCEDURE_PARAMETERS X.RDB$DESCRIPTION.NULL = TRUE; strcpy (X.RDB$PROCEDURE_NAME, procptr); X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_procedureprm_name: l = GET_TEXT(X.RDB$PARAMETER_NAME); MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof(temp)); BURP_verbose (196, temp); // msg 196 restoring parameter %s for stored procedure break; case att_procedureprm_type: X.RDB$PARAMETER_TYPE= (USHORT) get_numeric(tdgbl); break; case att_procedureprm_number: X.RDB$PARAMETER_NUMBER= (USHORT) get_numeric(tdgbl); break; case att_procedureprm_field_source: GET_TEXT(X.RDB$FIELD_SOURCE); break; case att_procedureprm_description: get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_procedureprm_description2: get_source_blob (tdgbl, X.RDB$DESCRIPTION, true); X.RDB$DESCRIPTION.NULL = FALSE; break; default: bad_attribute (scan_next_attr, attribute, 291); // msg 291 procedure parameter break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; } return true; } bool get_ref_constraint(BurpGlobals* tdgbl) { /************************************** * * g e t _ r e f _ c o n s t r a i n t * ************************************** * * Functional description * Restore data for referential constraints. * **************************************/ att_type attribute; scan_attr_t scan_next_attr; STORE (REQUEST_HANDLE tdgbl->handles_get_ref_constraint_req_handle1) X IN RDB$REF_CONSTRAINTS X.RDB$CONSTRAINT_NAME.NULL = TRUE; X.RDB$CONST_NAME_UQ.NULL = TRUE; X.RDB$MATCH_OPTION.NULL = TRUE; X.RDB$UPDATE_RULE.NULL = TRUE; X.RDB$DELETE_RULE.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_ref_constraint_name: X.RDB$CONSTRAINT_NAME.NULL = FALSE; GET_TEXT(X.RDB$CONSTRAINT_NAME); break; case att_ref_unique_const_name: X.RDB$CONST_NAME_UQ.NULL = FALSE; GET_TEXT(X.RDB$CONST_NAME_UQ); break; case att_ref_match_option: X.RDB$MATCH_OPTION.NULL = FALSE; GET_TEXT(X.RDB$MATCH_OPTION); break; case att_ref_update_rule: X.RDB$UPDATE_RULE.NULL = FALSE; GET_TEXT(X.RDB$UPDATE_RULE); break; case att_ref_delete_rule: X.RDB$DELETE_RULE.NULL = FALSE; GET_TEXT(X.RDB$DELETE_RULE); break; default: bad_attribute (scan_next_attr, attribute, 292); // msg 292 referential constraint break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_relation(BurpGlobals* tdgbl) { /************************************** * * g e t _ r e l a t i o n * ************************************** * * Functional description * Write relation meta-data and data. * Use the default transaction for RELATIONS, * and use the global_trans for VIEWS. This * enables us to have views of SP and views * with plans. Assume it is a view if it has * RDB$VIEW_BLR, also assume RDB$VIEW_BLR is * the first blob in the backup file. * * **************************************/ TEXT temp[GDS_NAME_LEN]; SSHORT l; SLONG rel_flags = 0, sys_flag = 0, type = 0; bool rel_flags_null = true, type_null = true; ISC_QUAD view_blr = isc_blob_null, view_src = isc_blob_null, rel_desc = isc_blob_null, ext_desc = isc_blob_null; bool view_blr_null = true, view_src_null = true, rel_desc_null = true, ext_desc_null = true; BASED_ON RDB$RELATIONS.RDB$SECURITY_CLASS sec_class; sec_class[0] = '\0'; bool sec_class_null = true; BASED_ON RDB$RELATIONS.RDB$EXTERNAL_FILE ext_file_name; ext_file_name[0] = '\0'; bool ext_file_name_null = true; // Pick up relation attributes burp_rel* relation = (burp_rel*) BURP_alloc_zero (sizeof(burp_rel)); relation->rel_next = tdgbl->relations; tdgbl->relations = relation; /* STORE (REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1) X IN RDB$RELATIONS X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$FLAGS.NULL = TRUE; X.RDB$SECURITY_CLASS.NULL = TRUE; X.RDB$VIEW_BLR.NULL = TRUE; X.RDB$VIEW_SOURCE.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$RUNTIME.NULL = TRUE; X.RDB$EXTERNAL_DESCRIPTION.NULL = TRUE; */ att_type attribute; scan_attr_t scan_next_attr; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_relation_name: l = GET_TEXT(relation->rel_name); relation->rel_name_length = l; MISC_terminate (relation->rel_name, temp, l, sizeof(temp)); BURP_verbose (167, temp); // msg 167 restoring relation %s break; case att_relation_security_class: sec_class_null = false; GET_TEXT(sec_class); fix_security_class_name(tdgbl, sec_class, false); break; case att_relation_view_blr: view_blr_null = false; get_blr_blob(tdgbl, view_blr, true); relation->rel_flags |= REL_view; break; case att_relation_view_source: view_src_null = false; get_misc_blob (tdgbl, view_src, !view_blr_null); break; case att_relation_view_source2: view_src_null = false; get_source_blob(tdgbl, view_src, !view_blr_null); break; case att_relation_description: rel_desc_null = false; get_misc_blob(tdgbl, rel_desc, !view_blr_null); break; case att_relation_description2: rel_desc_null = false; get_source_blob(tdgbl, rel_desc, !view_blr_null); break; case att_relation_flags: rel_flags_null = false; rel_flags = get_numeric(tdgbl); break; case att_relation_system_flag: sys_flag = get_numeric(tdgbl); break; case att_relation_ext_description: ext_desc_null = false; get_misc_blob(tdgbl, ext_desc, !view_blr_null); break; case att_relation_ext_description2: ext_desc_null = false; get_source_blob(tdgbl, ext_desc, !view_blr_null); break; case att_relation_owner_name: GET_TEXT(relation->rel_owner); break; case att_relation_ext_file_name: ext_file_name_null = false; GET_TEXT(ext_file_name); break; case att_relation_type: if (tdgbl->RESTORE_format >= 8) { type_null = false; type = get_numeric(tdgbl); } else bad_attribute(scan_next_attr, attribute, 111); break; default: bad_attribute (scan_next_attr, attribute, 111); // msg 111 table break; } } // If this is a view and there is a global transaction then use it isc_tr_handle local_trans; if (view_blr_null || !tdgbl->global_trans) local_trans = gds_trans; else local_trans = tdgbl->global_trans; if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11_1) { STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1) X IN RDB$RELATIONS X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$FLAGS.NULL = rel_flags_null; X.RDB$SECURITY_CLASS.NULL = sec_class_null; X.RDB$VIEW_BLR.NULL = view_blr_null; X.RDB$VIEW_SOURCE.NULL = view_src_null; X.RDB$DESCRIPTION.NULL = rel_desc_null; X.RDB$RUNTIME.NULL = TRUE; X.RDB$EXTERNAL_DESCRIPTION.NULL = ext_desc_null; X.RDB$EXTERNAL_FILE.NULL = ext_file_name_null; X.RDB$RELATION_TYPE.NULL = type_null; X.RDB$SYSTEM_FLAG = (USHORT) sys_flag; X.RDB$FLAGS = (USHORT) rel_flags; X.RDB$VIEW_BLR = view_blr; X.RDB$VIEW_SOURCE = view_src; X.RDB$DESCRIPTION = rel_desc; X.RDB$EXTERNAL_DESCRIPTION = ext_desc; strcpy(X.RDB$SECURITY_CLASS, sec_class); strcpy(X.RDB$RELATION_NAME, relation->rel_name); strcpy(X.RDB$EXTERNAL_FILE, ext_file_name); X.RDB$RELATION_TYPE = (USHORT) type; END_STORE; ON_ERROR general_on_error (); END_ERROR; } else { STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1) X IN RDB$RELATIONS X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$FLAGS.NULL = rel_flags_null; X.RDB$SECURITY_CLASS.NULL = sec_class_null; X.RDB$VIEW_BLR.NULL = view_blr_null; X.RDB$VIEW_SOURCE.NULL = view_src_null; X.RDB$DESCRIPTION.NULL = rel_desc_null; X.RDB$RUNTIME.NULL = TRUE; X.RDB$EXTERNAL_DESCRIPTION.NULL = ext_desc_null; X.RDB$EXTERNAL_FILE.NULL = ext_file_name_null; X.RDB$SYSTEM_FLAG = (USHORT) sys_flag; X.RDB$FLAGS = (USHORT) rel_flags; X.RDB$VIEW_BLR = view_blr; X.RDB$VIEW_SOURCE = view_src; X.RDB$DESCRIPTION = rel_desc; X.RDB$EXTERNAL_DESCRIPTION = ext_desc; strcpy(X.RDB$SECURITY_CLASS, sec_class); strcpy(X.RDB$RELATION_NAME, relation->rel_name); strcpy(X.RDB$EXTERNAL_FILE, ext_file_name); END_STORE; ON_ERROR general_on_error (); END_ERROR; } // Eat up misc. records burp_fld* field = NULL; burp_fld** ptr = &relation->rel_fields; rec_type record; while (get_record(&record, tdgbl) != rec_data) { switch (record) { case rec_relation_end: if (tdgbl->gbl_sw_incremental) { BURP_verbose (170, relation->rel_name); // msg 170: committing metadata for relation %s COMMIT // existing ON_ERROR continues past error, beck ON_ERROR BURP_print (171, relation->rel_name); // msg 171: error committing metadata for relation %s BURP_print_status (tdgbl->status_vector); ROLLBACK; ON_ERROR general_on_error (); END_ERROR; END_ERROR; EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; } return true; case rec_field: *ptr = field = get_field (tdgbl, relation); if (!field) return false; ptr = &field->fld_next; break; case rec_view: get_view(tdgbl, relation); break; default: BURP_error(43, true, SafeArg() << record); // msg 43 don't recognize record type %ld break; } } // If we fall thru, there are data records to be gotten // we can get here only when restoring ancient gbak'ed files where rec_data // was once embedded into rec_relation ... otherwise, meta commit happens // when we see the first rec_relation_data BURP_verbose (68); // msg 68 committing meta data COMMIT; ON_ERROR general_on_error (); END_ERROR; EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; get_data(tdgbl, relation); return true; } bool get_rel_constraint(BurpGlobals* tdgbl) { /************************************** * * g e t _ r e l _ c o n s t r a i n t * ************************************** * * Functional description * Restore data for relation constraints. * **************************************/ att_type attribute; scan_attr_t scan_next_attr; STORE (REQUEST_HANDLE tdgbl->handles_get_rel_constraint_req_handle1) X IN RDB$RELATION_CONSTRAINTS X.RDB$CONSTRAINT_NAME.NULL = TRUE; X.RDB$CONSTRAINT_TYPE.NULL = TRUE; X.RDB$RELATION_NAME.NULL = TRUE; X.RDB$DEFERRABLE.NULL = TRUE; X.RDB$INITIALLY_DEFERRED.NULL = TRUE; X.RDB$INDEX_NAME.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_rel_constraint_name: X.RDB$CONSTRAINT_NAME.NULL = FALSE; GET_TEXT(X.RDB$CONSTRAINT_NAME); break; case att_rel_constraint_type: X.RDB$CONSTRAINT_TYPE.NULL = FALSE; GET_TEXT(X.RDB$CONSTRAINT_TYPE); break; case att_rel_constraint_rel_name: X.RDB$RELATION_NAME.NULL = FALSE; GET_TEXT(X.RDB$RELATION_NAME); break; case att_rel_constraint_defer: X.RDB$DEFERRABLE.NULL = FALSE; GET_TEXT(X.RDB$DEFERRABLE); break; case att_rel_constraint_init: X.RDB$INITIALLY_DEFERRED.NULL = FALSE; GET_TEXT(X.RDB$INITIALLY_DEFERRED); break; case att_rel_constraint_index: X.RDB$INDEX_NAME.NULL = FALSE; GET_TEXT(X.RDB$INDEX_NAME); break; default: bad_attribute (scan_next_attr, attribute, 208); // msg 208 table constraint break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_relation_data(BurpGlobals* tdgbl) { /************************************** * * g e t _ r e l a t i o n _ d a t a * ************************************** * * Functional description * Restore data for a relation. This is called when the data is * standing free from the relation definition. We first need to * find the relation named. If we can't find it, give up. * **************************************/ BASED_ON RDB$RELATIONS.RDB$RELATION_NAME name; att_type attribute; scan_attr_t scan_next_attr; burp_rel* relation = NULL; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_relation_name: GET_TEXT(name); relation = find_relation (tdgbl, name); break; default: bad_attribute (scan_next_attr, attribute, 111); // msg 111 table break; } } if (!relation) BURP_error_redirect (NULL, 49); // msg 49 no relation name for data // Eat up misc. records rec_type record; get_record(&record, tdgbl); SLONG gen_id; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), true) { switch (record) { case rec_relation_end: return true; case rec_data: record = get_data(tdgbl, relation); // get_data does a GET_RECORD break; case rec_gen_id: gen_id = get_numeric(tdgbl); store_blr_gen_id (tdgbl, name, gen_id, NULL); get_record(&record, tdgbl); break; case rec_index: get_index (tdgbl, relation); get_record(&record, tdgbl); break; case rec_trigger: // old style trigger get_trigger_old (tdgbl, relation); get_record(&record, tdgbl); break; default: bad_attribute (scan_next_attr, attribute, 111); // msg 111 relation get_record(&record, tdgbl); break; } } return true; } bool get_sql_roles(BurpGlobals* tdgbl) { /************************************** * * g e t _ s q l _ r o l e s * ************************************** * * Functional description * Restore data for SQL roles * **************************************/ att_type attribute; scan_attr_t scan_next_attr; TEXT temp[GDS_NAME_LEN]; SSHORT l; if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11) { STORE (REQUEST_HANDLE tdgbl->handles_get_sql_roles_req_handle1) X IN RDB$ROLES X.RDB$ROLE_NAME.NULL = TRUE; X.RDB$OWNER_NAME.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_role_name: X.RDB$ROLE_NAME.NULL = FALSE; l = GET_TEXT(X.RDB$ROLE_NAME); MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp)); // msg 251, restoring SQL role: %s BURP_verbose (251, temp); break; case att_role_owner_name: X.RDB$OWNER_NAME.NULL = FALSE; GET_TEXT(X.RDB$OWNER_NAME); break; case att_role_description: if (tdgbl->RESTORE_format >= 7) { get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); X.RDB$DESCRIPTION.NULL = FALSE; } else bad_attribute (scan_next_attr, attribute, 250); break; default: // msg 250 SQL role bad_attribute (scan_next_attr, attribute, 250); break; } } END_STORE ON_ERROR general_on_error (); END_ERROR; } else if (tdgbl->RESTORE_ods >= DB_VERSION_DDL9) { // This is the first IB version (v5, ods9) where roles appeared. // They remained unchanged for IB6 / FB1 and FB1.5 (ods10). STORE (REQUEST_HANDLE tdgbl->handles_get_sql_roles_req_handle1) X IN RDB$ROLES X.RDB$ROLE_NAME.NULL = TRUE; X.RDB$OWNER_NAME.NULL = TRUE; // Here we didn't have RBD$SYSTEM_FLAG field. skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_role_name: X.RDB$ROLE_NAME.NULL = FALSE; l = GET_TEXT(X.RDB$ROLE_NAME); MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp)); // msg 251, restoring SQL role: %s BURP_verbose (251, temp); break; case att_role_owner_name: X.RDB$OWNER_NAME.NULL = FALSE; GET_TEXT(X.RDB$OWNER_NAME); break; case att_role_description: if (tdgbl->RESTORE_format >= 7) eat_blob(tdgbl); else bad_attribute (scan_next_attr, attribute, 250); break; default: // msg 250 SQL role bad_attribute (scan_next_attr, attribute, 250); break; } } END_STORE ON_ERROR general_on_error (); END_ERROR; } else { // We say we support IB4, then we should skip roles. skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_role_name: case att_role_owner_name: eat_text(tdgbl); break; case att_role_description: if (tdgbl->RESTORE_format >= 7) eat_blob(tdgbl); else bad_attribute (scan_next_attr, attribute, 250); break; default: // msg 250 SQL role bad_attribute (scan_next_attr, attribute, 250); break; } } } return true; } bool get_mapping(BurpGlobals* tdgbl) { /************************************** * * g e t _ m a p p i n g * ************************************** * * Functional description * Restore mapping to users and roles * Restricted version - only single * mapping is accepted * **************************************/ att_type attribute; scan_attr_t scan_next_attr; TEXT temp[GDS_NAME_LEN]; SSHORT l; Firebird::string role, os; if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11_1) { skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { /* case att_map_role: l = GET_TEXT(temp); role.assign(temp, l); break; case att_map_os: l = GET_TEXT(temp); os.assign(temp, l); break; */ case att_auto_map_role: l = GET_TEXT(temp); role.assign(temp, l); break; default: // msg 299 name mapping bad_attribute (scan_next_attr, attribute, 299); break; } } if (tdgbl->RESTORE_ods < DB_VERSION_DDL11_2) { return true; // silently skip attributes on old server } if (role != ADMIN_ROLE) { BURP_error(300, false); return true; } if (tdgbl->firstMap) { tdgbl->firstMap = false; BURP_verbose(301); // msg 301, restoring names mapping } BURP_verbose(298, ADMIN_ROLE); // msg 298, restoring map @1 Firebird::string sql; sql.printf("%s ('%s', %d) %s", "UPDATE OR INSERT INTO RDB$ROLES(RDB$ROLE_NAME, RDB$SYSTEM_FLAG) VALUES", ADMIN_ROLE, ROLE_FLAG_MAY_TRUST | ROLE_FLAG_DBO, "MATCHING (RDB$ROLE_NAME)"); isc_dsql_execute_immediate(tdgbl->status, &tdgbl->db_handle, &tdgbl->tr_handle, sql.length(), sql.c_str(), 1, NULL); if (tdgbl->status[1]) { general_on_error (); } } return true; } bool is_ascii_name (const TEXT *name, const SSHORT len) { /************************************** * * i s _ a s c i i _ n a m e * ************************************** * * Functional description * Check if the input text is valid ASCII uppercased name * **************************************/ SSHORT i = 0; while (i < len && ( (name[i] >= 'A' && name[i] <= 'Z') || (name[i] >= '0' && name[i] <= '9') || name[i] == '_' || name[i] == '$' ) ) { ++i; } return (i == len); } bool get_security_class(BurpGlobals* tdgbl) { /************************************** * * g e t _ s e c u r i t y _ c l a s s * ************************************** * * Functional description * Restore a security class record including access control list. * **************************************/ att_type attribute; TEXT temp[GDS_NAME_LEN]; SSHORT l = 0; scan_attr_t scan_next_attr; bool is_valid_sec_class = false; STORE (REQUEST_HANDLE tdgbl->handles_get_security_class_req_handle1) X IN RDB$SECURITY_CLASSES X.RDB$DESCRIPTION.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_class_security_class: l = GET_TEXT(X.RDB$SECURITY_CLASS); // Bug fix for bug_no 7299: There was a V3 bug that inserted // garbage security class entry when doing GBAK. In order to // restore the V3 gbak file with this bad security entry to // V4 database. We should check if the security class is a // valid ASCII name. If not, skip this entry by setting // 'is_valid_sec_class' to false. is_valid_sec_class = is_ascii_name(X.RDB$SECURITY_CLASS, l); if (!is_valid_sec_class) { MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp)); BURP_print (234, temp); // msg 234 Skipped bad security class entry: %s break; } MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp)); BURP_verbose (125, temp); // msg 125 restoring security class %s break; case att_class_acl: get_misc_blob (tdgbl, X.RDB$ACL, false); break; case att_class_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_class_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); break; default: bad_attribute (scan_next_attr, attribute, 131); // msg 131 security class break; } } // If the security class is not valid ASCII name, don't store it to the // database. Simply return from here and the entry is discarded. if (!is_valid_sec_class) { return true; } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } void get_source_blob(BurpGlobals* tdgbl, ISC_QUAD& blob_id, bool glb_trans) { /************************************** * * g e t _ s o u r c e _ b l o b * ************************************** * * Functional description * Read source blob and query header attributes and copy data from * input file to nice, shiney, new blob. * **************************************/ ISC_STATUS_ARRAY status_vector; SLONG length = get_numeric(tdgbl); // Create new blob UserBlob blob(status_vector); isc_tr_handle local_trans; if (glb_trans && tdgbl->global_trans) local_trans = tdgbl->global_trans; else local_trans = gds_trans; bool ok; if (tdgbl->gbl_sw_fix_fss_metadata) { UCHAR bpb[15]; UCHAR* p = bpb; *p++ = isc_bpb_version1; *p++ = isc_bpb_source_type; *p++ = 2; put_short(p, isc_blob_text); p += 2; *p++ = isc_bpb_source_interp; *p++ = 1; *p++ = tdgbl->gbl_sw_fix_fss_metadata_id; *p++ = isc_bpb_target_type; *p++ = 2; put_short(p, isc_blob_text); p += 2; *p++ = isc_bpb_target_interp; *p++ = 1; *p++ = CS_UNICODE_FSS; ok = blob.create(DB, local_trans, blob_id, p - bpb, bpb); } else ok = blob.create(DB, local_trans, blob_id); if (!ok) { BURP_error_redirect (status_vector, 37); // msg 37 isc_create_blob failed } // Allocate blob buffer if static buffer is too short BlobBuffer static_buffer; UCHAR* const buffer = static_buffer.getBuffer(length); while (length > 0) { UCHAR* p = buffer; while (*p++ = get(tdgbl)) length--; --p; --length; // -- or ++ ??? p is decremented, will have to test. const USHORT seg_len = p - buffer; if (!blob.putSegment(seg_len, buffer)) { BURP_error_redirect (status_vector, 38); // msg 38 isc_put_segment failed } } if (!blob.close()) BURP_error_redirect (status_vector, 23); // msg 23 isc_close_blob failed } USHORT get_text(BurpGlobals* tdgbl, TEXT* text, ULONG length) { /************************************** * * g e t _ t e x t * ************************************** * * Functional description * Move a text attribute to a string and fill. * **************************************/ const ULONG l = get(tdgbl); if (length <= l) BURP_error_redirect (NULL, 46); // msg 46 string truncated if (l) text = (TEXT*) get_block(tdgbl, (UCHAR*) text, l); *text = 0; return (USHORT) l; } USHORT get_text2(BurpGlobals* tdgbl, TEXT* text, ULONG length) { /************************************** * * g e t _ t e x t 2 * ************************************** * * Functional description * Move a text attribute to a string and fill, using USHORT as length indicator. * **************************************/ UCHAR lenstr[sizeof(USHORT)] = ""; get_block(tdgbl, lenstr, sizeof(lenstr)); const USHORT len = (USHORT) gds__vax_integer(lenstr, sizeof(lenstr)); if (length <= len) { BURP_error_redirect (NULL, 46); // msg 46 string truncated } if (len) text = (TEXT*) get_block(tdgbl, (UCHAR*) text, len); *text = 0; return len; } bool get_trigger_old (BurpGlobals* tdgbl, burp_rel* relation) { /************************************** * * g e t _ t r i g g e r _ o l d * ************************************** * * Functional description * Get a trigger definition for a relation. * **************************************/ enum trig_t type; att_type attribute; TEXT name[GDS_NAME_LEN]; scan_attr_t scan_next_attr; STORE (REQUEST_HANDLE tdgbl->handles_get_trigger_old_req_handle1) X IN RDB$TRIGGERS X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$TRIGGER_BLR.NULL = TRUE; X.RDB$TRIGGER_SOURCE.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_trig_type: type = (enum trig_t) get_numeric(tdgbl); break; case att_trig_blr: X.RDB$TRIGGER_BLR.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$TRIGGER_BLR, false); break; case att_trig_source: X.RDB$TRIGGER_SOURCE.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$TRIGGER_SOURCE, false); break; case att_trig_source2: X.RDB$TRIGGER_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$TRIGGER_SOURCE, false); break; default: bad_attribute (scan_next_attr, attribute, 134); // msg 134 trigger break; } } // fill in rest of attributes unique to new trigger format TEXT* p = X.RDB$TRIGGER_NAME; const TEXT* const end = p + 31; const TEXT* q = relation->rel_name; while (*q) { *p++ = *q++; } switch (type) { case trig_pre_store: X.RDB$TRIGGER_TYPE = TRIG_TYPE_PRE_STORE; q = "$STORE"; break; case trig_pre_modify: X.RDB$TRIGGER_TYPE = TRIG_TYPE_PRE_MODIFY; q = "$MODIFY"; break; case trig_post_erase: X.RDB$TRIGGER_TYPE = TRIG_TYPE_POST_ERASE; q = "$ERASE"; break; default: bad_attribute (scan_next_attr, attribute, 136); // msg 136 trigger type return 0; } while (*q && p < end) { *p++ = *q++; } *p = 0; BURP_verbose (126, X.RDB$TRIGGER_NAME); // msg 126 restoring trigger %s strncpy (X.RDB$RELATION_NAME, relation->rel_name, GDS_NAME_LEN); strcpy (name, X.RDB$TRIGGER_NAME); X.RDB$TRIGGER_SEQUENCE = TRIGGER_SEQUENCE_DEFAULT; END_STORE; ON_ERROR general_on_error (); END_ERROR; if (tdgbl->gbl_sw_incremental) { COMMIT // existing ON_ERROR continues past error, beck ON_ERROR BURP_print (94, name); // msg 94 trigger %s is invalid BURP_print_status (tdgbl->status_vector); ROLLBACK; ON_ERROR general_on_error (); END_ERROR; END_ERROR; EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; } return true; } bool get_trigger(BurpGlobals* tdgbl) { /************************************** * * g e t _ t r i g g e r * ************************************** * * Functional description * Get a trigger definition in rdb$triggers. * **************************************/ att_type attribute; BASED_ON RDB$TRIGGERS.RDB$TRIGGER_NAME name; scan_attr_t scan_next_attr; isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11_1) { STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_trigger_req_handle1) X IN RDB$TRIGGERS X.RDB$RELATION_NAME.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$TRIGGER_BLR.NULL = TRUE; X.RDB$TRIGGER_SOURCE.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$FLAGS.NULL = TRUE; X.RDB$VALID_BLR.NULL = TRUE; X.RDB$DEBUG_INFO.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_trig_type: X.RDB$TRIGGER_TYPE = (USHORT) get_numeric(tdgbl); break; case att_trig_flags: X.RDB$FLAGS = (USHORT) get_numeric(tdgbl); X.RDB$FLAGS.NULL = FALSE; break; case att_trig_blr: X.RDB$TRIGGER_BLR.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$TRIGGER_BLR, true); break; case att_trig_source: X.RDB$TRIGGER_SOURCE.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true); break; case att_trig_source2: X.RDB$TRIGGER_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true); break; case att_trig_name: GET_TEXT(X.RDB$TRIGGER_NAME); strcpy (name, X.RDB$TRIGGER_NAME); BURP_verbose (126, X.RDB$TRIGGER_NAME); // msg 126 restoring trigger %s break; case att_trig_relation_name: X.RDB$RELATION_NAME.NULL = FALSE; GET_TEXT(X.RDB$RELATION_NAME); break; case att_trig_sequence: X.RDB$TRIGGER_SEQUENCE = (USHORT) get_numeric(tdgbl); break; case att_trig_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true); break; case att_trig_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, true); break; case att_trig_system_flag: X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl); X.RDB$SYSTEM_FLAG.NULL = FALSE; break; case att_trig_inactive: X.RDB$TRIGGER_INACTIVE = (USHORT) get_numeric(tdgbl); break; case att_trig_valid_blr: if (tdgbl->RESTORE_format >= 8) { X.RDB$VALID_BLR.NULL = FALSE; X.RDB$VALID_BLR = (USHORT) get_numeric(tdgbl); } else bad_attribute (scan_next_attr, attribute, 134); break; case att_trig_debug_info: if (tdgbl->RESTORE_format >= 8) { X.RDB$DEBUG_INFO.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DEBUG_INFO, true); } else bad_attribute (scan_next_attr, attribute, 134); break; default: bad_attribute (scan_next_attr, attribute, 134); // msg 134 trigger break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; } else { STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_trigger_req_handle1) X IN RDB$TRIGGERS X.RDB$RELATION_NAME.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$TRIGGER_BLR.NULL = TRUE; X.RDB$TRIGGER_SOURCE.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$FLAGS.NULL = TRUE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_trig_type: X.RDB$TRIGGER_TYPE = (USHORT) get_numeric(tdgbl); break; case att_trig_flags: X.RDB$FLAGS = (USHORT) get_numeric(tdgbl); X.RDB$FLAGS.NULL = FALSE; break; case att_trig_blr: X.RDB$TRIGGER_BLR.NULL = FALSE; get_blr_blob (tdgbl, X.RDB$TRIGGER_BLR, true); break; case att_trig_source: X.RDB$TRIGGER_SOURCE.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true); break; case att_trig_source2: X.RDB$TRIGGER_SOURCE.NULL = FALSE; get_source_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true); break; case att_trig_name: GET_TEXT(X.RDB$TRIGGER_NAME); strcpy (name, X.RDB$TRIGGER_NAME); BURP_verbose (126, X.RDB$TRIGGER_NAME); // msg 126 restoring trigger %s break; case att_trig_relation_name: X.RDB$RELATION_NAME.NULL = FALSE; GET_TEXT(X.RDB$RELATION_NAME); break; case att_trig_sequence: X.RDB$TRIGGER_SEQUENCE = (USHORT) get_numeric(tdgbl); break; case att_trig_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true); break; case att_trig_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, true); break; case att_trig_system_flag: X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl); X.RDB$SYSTEM_FLAG.NULL = FALSE; break; case att_trig_inactive: X.RDB$TRIGGER_INACTIVE = (USHORT) get_numeric(tdgbl); break; default: bad_attribute (scan_next_attr, attribute, 134); // msg 134 trigger break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; } if (tdgbl->gbl_sw_incremental) { COMMIT // existing ON_ERROR continues past error, beck ON_ERROR BURP_print (94, name); // msg 94 trigger %s is invalid BURP_print_status (tdgbl->status_vector); ROLLBACK; ON_ERROR general_on_error (); END_ERROR; END_ERROR; EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; } return true; } bool get_trigger_message(BurpGlobals* tdgbl) { /************************************** * * g e t _ t r i g g e r _ m e s s a g e * ************************************** * * Functional description * Get a trigger message text. * **************************************/ att_type attribute; scan_attr_t scan_next_attr; BASED_ON RDB$TRIGGER_MESSAGES.RDB$TRIGGER_NAME name; BASED_ON RDB$TRIGGER_MESSAGES.RDB$MESSAGE_NUMBER number = -1; BASED_ON RDB$TRIGGER_MESSAGES.RDB$MESSAGE message; bool sysflag = false; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_trigmsg_name: GET_TEXT(name); sysflag = false; FOR (REQUEST_HANDLE tdgbl->handles_get_trigger_message_req_handle1) FIRST 1 X IN RDB$TRIGGERS WITH X.RDB$SYSTEM_FLAG EQ 1 AND X.RDB$TRIGGER_NAME EQ name sysflag = true; END_FOR; ON_ERROR general_on_error (); END_ERROR; BURP_verbose (127, name); // msg 127 restoring trigger message for %s break; case att_trigmsg_number: number = (USHORT) get_numeric(tdgbl); break; case att_trigmsg_text: GET_TEXT(message); break; default: bad_attribute (scan_next_attr, attribute, 135); // msg 135 trigger message break; } } if (sysflag) return true; // Versions prior to FB2.0 don't support a field longer than varchar(78). if (tdgbl->RESTORE_ods < DB_VERSION_DDL11) message[78] = 0; isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_trigger_message_req_handle2) X IN RDB$TRIGGER_MESSAGES strcpy (X.RDB$TRIGGER_NAME, name); X.RDB$MESSAGE_NUMBER = number; strcpy (X.RDB$MESSAGE, message); END_STORE; ON_ERROR general_on_error (); END_ERROR; if (tdgbl->gbl_sw_incremental) { COMMIT // existing ON_ERROR continues past error, beck ON_ERROR BURP_print (94, name); // msg 94 trigger %s is invalid BURP_print_status (tdgbl->status_vector); ROLLBACK; ON_ERROR general_on_error (); END_ERROR; END_ERROR; EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; } return true; } bool get_type(BurpGlobals* tdgbl) { /************************************** * * g e t _ t y p e * ************************************** * * Functional description * Get a type definition in rdb$types. * **************************************/ att_type attribute; ULONG l; TEXT temp[GDS_NAME_LEN]; scan_attr_t scan_next_attr; STORE (REQUEST_HANDLE tdgbl->handles_get_type_req_handle1) X IN RDB$TYPES X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_type_name: l = GET_TEXT(X.RDB$TYPE_NAME); break; case att_type_type: X.RDB$TYPE = (USHORT) get_numeric(tdgbl); break; case att_type_field_name: GET_TEXT(X.RDB$FIELD_NAME); break; case att_type_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_type_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); break; case att_type_system_flag: X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl); X.RDB$SYSTEM_FLAG.NULL = FALSE; break; default: bad_attribute (scan_next_attr, attribute, 293); // msg 293 type (in RDB$TYPES) break; } } MISC_terminate (X.RDB$TYPE_NAME, temp, l, sizeof(temp)); BURP_verbose (128, SafeArg() << temp << X.RDB$FIELD_NAME); // msg 128 restoring type %s for field %s END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_user_privilege(BurpGlobals* tdgbl) { /************************************** * * g e t _ u s e r _ p r i v i l e g e * ************************************** * * Functional description * Get a user privilege. * Get next interesting user privilege. * **************************************/ att_type attribute; scan_attr_t scan_next_attr; USHORT flags = 0; BASED_ON RDB$USER_PRIVILEGES.RDB$USER user; BASED_ON RDB$USER_PRIVILEGES.RDB$GRANTOR grantor; BASED_ON RDB$USER_PRIVILEGES.RDB$PRIVILEGE privilege; BASED_ON RDB$USER_PRIVILEGES.RDB$GRANT_OPTION grant_option = 0; BASED_ON RDB$USER_PRIVILEGES.RDB$RELATION_NAME relation_name; BASED_ON RDB$USER_PRIVILEGES.RDB$FIELD_NAME field_name; BASED_ON RDB$USER_PRIVILEGES.RDB$USER_TYPE user_type; BASED_ON RDB$USER_PRIVILEGES.RDB$OBJECT_TYPE object_type; user_type = obj_user; object_type = obj_relation; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_priv_user: // default USER_TYPE to USER flags |= USER_PRIV_USER; GET_TEXT(user); BURP_verbose (123, user); // msg 123 restoring privilege for user %s break; case att_priv_grantor: flags |= USER_PRIV_GRANTOR; GET_TEXT(grantor); break; case att_priv_privilege: flags |= USER_PRIV_PRIVILEGE; GET_TEXT(privilege); break; case att_priv_grant_option: flags |= USER_PRIV_GRANT_OPTION; grant_option = (USHORT) get_numeric(tdgbl); break; case att_priv_object_name: flags |= USER_PRIV_OBJECT_NAME; // default OBJECT_TYPE to RELATION GET_TEXT(relation_name); break; case att_priv_field_name: flags |= USER_PRIV_FIELD_NAME; GET_TEXT(field_name); break; case att_priv_user_type: flags |= USER_PRIV_USER_TYPE; user_type = (USHORT) get_numeric(tdgbl); break; case att_priv_obj_type: flags |= USER_PRIV_OBJECT_TYPE; object_type = (USHORT) get_numeric(tdgbl); break; default: bad_attribute (scan_next_attr, attribute, 105); // msg 105 privilege break; } } // Check if object exists isc_tr_handle local_trans = 0; bool exists = false; switch (object_type) { case obj_procedure: { for (const burp_prc* proc = tdgbl->procedures; proc; proc = proc->prc_next) if (!strcmp(proc->prc_name, relation_name)) { exists = true; local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; break; } } break; case obj_relation: { for (const burp_rel* rel = tdgbl->relations; rel; rel = rel->rel_next) if (!strcmp(rel->rel_name, relation_name)) { exists = true; if (rel->rel_flags & REL_view) local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; break; } } break; default: exists = true; break; } if (tdgbl->RESTORE_ods <= DB_VERSION_DDL8) { // Discard roles for IB4. if (user_type == obj_sql_role || object_type == obj_sql_role) exists = false; } if (exists) { if (!local_trans) local_trans = gds_trans; STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_user_privilege_req_handle1) X IN RDB$USER_PRIVILEGES X.RDB$FIELD_NAME.NULL = TRUE; X.RDB$OBJECT_TYPE.NULL = TRUE; if (flags & USER_PRIV_USER) strcpy (X.RDB$USER, user); if (flags & USER_PRIV_GRANTOR) strcpy (X.RDB$GRANTOR, grantor); if (flags & USER_PRIV_PRIVILEGE) strcpy (X.RDB$PRIVILEGE, privilege); if (flags & USER_PRIV_GRANT_OPTION) { X.RDB$GRANT_OPTION = grant_option; if (grant_option == 0) X.RDB$GRANT_OPTION.NULL = TRUE; else X.RDB$GRANT_OPTION.NULL = FALSE; } if (flags & USER_PRIV_OBJECT_NAME) strcpy (X.RDB$RELATION_NAME, relation_name); if (flags & USER_PRIV_FIELD_NAME) { X.RDB$FIELD_NAME.NULL = FALSE; strcpy (X.RDB$FIELD_NAME, field_name); } // USER_TYPE & OBJECT_TYPE are fields that did not exist before // V4.0. So, we have to reconstruct them and initialize them to // reasonable values. If they existed before then user_type and // object_type contain the proper values. If they didn't exist // then user_type and object_type contain the reasonable default // values. X.RDB$USER_TYPE.NULL = FALSE; X.RDB$USER_TYPE = user_type; X.RDB$OBJECT_TYPE.NULL = FALSE; X.RDB$OBJECT_TYPE = object_type; // If OBJECT_TYPE didn't exist before and we have a field level // user privileges, then use obj_field instead. // NOTE: Scanning the V4.0 code base, obj_field has never been // used at all. The following code should be uncommented // in case we ever introduce obj_field to the picture. /*********************************************************** if ( !(flags & USER_PRIV_OBJECT_TYPE) ) { if ( flags & USER_PRIV_FIELD_NAME ) { X.RDB$OBJECT_TYPE = obj_field; } } ***********************************************************/ END_STORE; ON_ERROR general_on_error (); END_ERROR; } return true; } bool get_view(BurpGlobals* tdgbl, burp_rel* relation) { /************************************** * * g e t _ v i e w * ************************************** * * Functional description * Store a record in RDB$VIEW_RELATIONS. * **************************************/ att_type attribute; scan_attr_t scan_next_attr; // If there is a global transaction then use it isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_view_req_handle1) X IN RDB$VIEW_RELATIONS strcpy (X.RDB$VIEW_NAME, relation->rel_name); skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_view_relation_name: GET_TEXT(X.RDB$RELATION_NAME); break; case att_view_context_name: GET_TEXT(X.RDB$CONTEXT_NAME); if (tdgbl->RESTORE_ods < DB_VERSION_DDL11_2) { X.RDB$CONTEXT_NAME[31] = 0; } break; case att_view_context_id: X.RDB$VIEW_CONTEXT = (USHORT) get_numeric(tdgbl); break; default: bad_attribute (scan_next_attr, attribute, 140); // msg 140 view break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } void ignore_array(BurpGlobals* tdgbl, burp_rel* relation) { /************************************** * * i g n o r e _ a r r a y * ************************************** * * Functional description * Ignore data from input file, like a * dummy get_array(). * **************************************/ burp_fld* field = NULL; att_type attribute; SLONG* range; const SLONG* end_ranges; USHORT field_number; scan_attr_t scan_next_attr; // Pick up attributes skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_blob_data) { switch (attribute) { case att_blob_field_number: field_number = (USHORT) get_numeric(tdgbl); for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_number == field_number) break; } if (!field) BURP_error_redirect (NULL, 36); // msg 36 Can't find field for blob break; case att_array_dimensions: field->fld_dimensions = (SSHORT) get_numeric(tdgbl); end_ranges = field->fld_ranges + 2 * field->fld_dimensions; for (range = field->fld_ranges; range < end_ranges; range += 2) { if (get_attribute(&attribute, tdgbl) != att_array_range_low) bad_attribute (scan_next_attr, attribute, 58); // msg 58 array else range[0] = get_numeric(tdgbl); if (get_attribute(&attribute, tdgbl) != att_array_range_high) bad_attribute (scan_next_attr, attribute, 58); // msg 58 array else range[1] = get_numeric(tdgbl); } break; default: bad_attribute (scan_next_attr, attribute, 58); // msg 58 array break; } } SLONG length = get(tdgbl); length |= get(tdgbl) << 8; length |= get(tdgbl) << 16; length |= get(tdgbl) << 24; SLONG lcount = 0; if (tdgbl->gbl_sw_transportable) { if (get_attribute(&attribute, tdgbl) != att_xdr_array) BURP_error_redirect (NULL, 55); // msg 55 Expected XDR record length else { lcount = get(tdgbl); lcount |= get(tdgbl) << 8; lcount |= get(tdgbl) << 16; lcount |= get(tdgbl) << 24; } } else { lcount = length; } if (lcount) get_skip(tdgbl, lcount); } void ignore_blob(BurpGlobals* tdgbl) { /************************************** * * i g n o r e _ b l o b * ************************************** * * Functional description * Skip over blob data records. * **************************************/ att_type attribute; scan_attr_t scan_next_attr; // Pick up attributes SLONG segments = 0; skip_init(&scan_next_attr); while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_blob_data) { switch (attribute) { case att_blob_field_number: get_numeric(tdgbl); break; case att_blob_max_segment: get_numeric(tdgbl); break; case att_blob_number_segments: segments = get_numeric(tdgbl); break; case att_blob_type: get_numeric(tdgbl); break; default: bad_attribute (scan_next_attr, attribute, 64); // msg 64 blob break; } } // Eat up blob segments while (--segments >= 0) { USHORT length = get(tdgbl); length |= get(tdgbl) << 8; if (length) get_skip(tdgbl, length); } } rec_type ignore_data(BurpGlobals* tdgbl, burp_rel* relation) { /************************************** * * i g n o r e _ d a t a * ************************************** * * Functional description * Ignore data records for a relation. * **************************************/ ULONG records = 0; rec_type record; while (true) { if (get(tdgbl) != att_data_length) BURP_error_redirect (NULL, 39); // msg 39 expected record length USHORT l = (USHORT) get_numeric(tdgbl); if (tdgbl->gbl_sw_transportable) { if (get(tdgbl) != att_xdr_length) BURP_error_redirect (NULL, 55); // msg 55 Expected XDR record length else l = (USHORT) get_numeric(tdgbl); } if (get(tdgbl) != att_data_data) BURP_error_redirect (NULL, 41); // msg 41 expected data attribute if (l) { if (tdgbl->gbl_sw_compress) { UCHAR* buffer = (UCHAR*) BURP_alloc (l); decompress (tdgbl, buffer, l); BURP_free (buffer); } else get_skip(tdgbl, l); } ++records; while (get_record(&record, tdgbl)) { if (record == rec_blob) ignore_blob(tdgbl); else if (record == rec_array) ignore_array (tdgbl, relation); else break; } if (record != rec_data) break; } BURP_verbose (106, SafeArg() << records); // msg 106 %ld records ignored return record; } void realign(BurpGlobals* tdgbl, UCHAR* buffer, const burp_rel* relation) { /************************************** * * r e a l i g n * ************************************** * * Functional description * Miserable input record is misaligned. * Shuffle fields around. N.B. this one * only works if the old buffer is longer * than the new. * **************************************/ for (const burp_fld* field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; UCHAR* p = buffer + field->fld_offset; const UCHAR* q = buffer + field->fld_old_offset; USHORT l = field->fld_length; // CVC: This code assumes fld_offset < fld_old_offset, // why not use memmove() instead? // Beware of overlaps here - don't use memcpy while (l--) { *p++ = *q++; } if (field->fld_type == blr_varying) { *p++ = *q++; *p++ = *q++; } } // If this is format version 2 or greater, build fields for null flags if (tdgbl->RESTORE_format >= 2) { for (const burp_fld* field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; UCHAR* p = buffer + FB_ALIGN(p - buffer, sizeof(SSHORT)); const UCHAR* q = buffer + FB_ALIGN(q - buffer, sizeof(SSHORT)); *p++ = *q++; *p++ = *q++; } } } #ifdef sparc USHORT recompute_length(BurpGlobals* tdgbl, burp_rel* relation) { /************************************** * * r e c o m p u t e _ l e n g t h * ************************************** * * Functional description * Recompute length of a record using an old * alignment if there is one. At the moment, * only SPARC has one. * **************************************/ ULONG offset = 0; // there was garbage, possibly nobody uses sparc define? const SSHORT* alignments = old_sparcs; for (burp_fld* field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; ULONG length = field->fld_length; // ULONG alignment = 4; useless, see assignment below // arrays are of various fld_types but are really blobs ULONG dtype = field->fld_type; if (field->fld_flags & FLD_array) { dtype = blr_blob; length = 8; } const ULONG alignment = alignments[gds_cvt_blr_dtype[field->fld_type]]; if (dtype == blr_varying) length += sizeof(USHORT); if (alignment) offset = FB_ALIGN(offset, alignment); field->fld_old_offset = offset; offset += length; } // If this is format version 2, build fields for null flags if (tdgbl->RESTORE_format >= 2) { for (const burp_fld* field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; offset = FB_ALIGN(offset, sizeof(SSHORT)); offset += sizeof(SSHORT); } } return offset; } #endif bool restore(BurpGlobals* tdgbl, const TEXT* file_name, const TEXT* database_name) { /************************************** * * r e s t o r e * ************************************** * * Functional description * Perform the body of restore. * **************************************/ // Read burp record first MVOL_init_read (file_name, &tdgbl->RESTORE_format, &tdgbl->io_cnt, &tdgbl->io_ptr); if (tdgbl->gbl_sw_transportable) BURP_verbose (133); // msg 133 transportable backup -- data in XDR format if (tdgbl->gbl_sw_compress) BURP_verbose (61); // msg 61 backup file is compressed // restore only from those backup files created by current or previous GBAK if (tdgbl->RESTORE_format < 1 || tdgbl->RESTORE_format > ATT_BACKUP_FORMAT) { BURP_error(44, true, SafeArg() << tdgbl->RESTORE_format); // msg 44 Expected backup version 1..8. Found %ld } create_database(tdgbl, database_name); EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; // For V4.0, start a read commited transaction. This will be used // to create blobs for global fields and update the record in the // RDB$FIELDS table. EXEC SQL SET TRANSACTION NAME tdgbl->global_trans ISOLATION LEVEL READ COMMITTED; check_db_version(tdgbl); if (tdgbl->RESTORE_ods < DB_VERSION_OLDEST_SUPPORTED) { BURP_error(51, true, SafeArg() << tdgbl->RESTORE_ods); // msg 51 database format %ld is too old to restore to } BURP_verbose (129); // msg 129 started transaction att_type attribute; isc_req_handle req_handle2 = 0, req_handle3 = 0; while (get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_database_security_class: // Instead of updating the security class in RDB$DATABASE, // just store the value in tdgbl. It will be updated at // the very end to prevent security class validation // failures during change table ownership operation GET_TEXT(tdgbl->database_security_class); break; case att_database_description: case att_database_description2: FOR (REQUEST_HANDLE req_handle2) X IN RDB$DATABASE MODIFY X USING if (attribute == att_database_description2) get_source_blob (tdgbl, X.RDB$DESCRIPTION, false); else get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false); END_MODIFY; ON_ERROR general_on_error (); END_ERROR; END_FOR; ON_ERROR general_on_error (); END_ERROR; break; case att_database_dfl_charset: FOR (REQUEST_HANDLE req_handle3) X IN RDB$DATABASE MODIFY X USING GET_TEXT(X.RDB$CHARACTER_SET_NAME); END_MODIFY; ON_ERROR general_on_error (); END_ERROR; END_FOR; ON_ERROR general_on_error (); END_ERROR; break; default: { SSHORT l = get(tdgbl); if (l) get_skip(tdgbl, l); break; } } } MISC_release_request_silent(req_handle2); MISC_release_request_silent(req_handle3); if (tdgbl->gbl_sw_fix_fss_data) { bool found = false; Firebird::string name = tdgbl->gbl_sw_fix_fss_data; name.upper(); req_handle3 = 0; FOR (REQUEST_HANDLE req_handle3) X IN RDB$CHARACTER_SETS WITH X.RDB$CHARACTER_SET_NAME EQ name.c_str() tdgbl->gbl_sw_fix_fss_data_id = X.RDB$CHARACTER_SET_ID; found = true; END_FOR; ON_ERROR general_on_error (); END_ERROR; MISC_release_request_silent(req_handle3); if (!found) BURP_error(305, true, SafeArg() << tdgbl->gbl_sw_fix_fss_data); } if (tdgbl->gbl_sw_fix_fss_metadata) { bool found = false; Firebird::string name = tdgbl->gbl_sw_fix_fss_metadata; name.upper(); req_handle3 = 0; FOR (REQUEST_HANDLE req_handle3) X IN RDB$CHARACTER_SETS WITH X.RDB$CHARACTER_SET_NAME EQ name.c_str() tdgbl->gbl_sw_fix_fss_metadata_id = X.RDB$CHARACTER_SET_ID; found = true; END_FOR; ON_ERROR general_on_error (); END_ERROR; MISC_release_request_silent(req_handle3); if (!found) BURP_error(305, true, SafeArg() << tdgbl->gbl_sw_fix_fss_metadata); } // If this should be a multi-file database, add the files if (tdgbl->gbl_sw_files && tdgbl->gbl_sw_files->fil_next) add_files(tdgbl, database_name); // Get global fields and relations bool flag_norel = true; // To fix bug 10098 bool flag = false; rec_type record; while (get_record(&record, tdgbl) != rec_end) { switch (record) { case rec_charset: if (!get_character_set(tdgbl)) return false; flag = true; break; case rec_collation: if (!get_collation(tdgbl)) return false; flag = true; break; case rec_chk_constraint: if (!get_chk_constraint(tdgbl)) return false; flag = true; break; case rec_global_field: if (!get_global_field(tdgbl)) return false; flag = true; break; case rec_field_dimensions: if (!get_field_dimensions(tdgbl)) return false; flag = true; break; case rec_relation: if (!get_relation(tdgbl)) return false; flag = true; flag_norel = false; break; case rec_ref_constraint: if (!get_ref_constraint(tdgbl)) return false; flag = true; break; case rec_rel_constraint: if (!get_rel_constraint(tdgbl)) return false; flag = true; break; case rec_function: if (!get_function(tdgbl)) return false; flag = true; break; case rec_procedure: if (!get_procedure(tdgbl)) return false; flag = true; break; case rec_exception: if (!get_exception(tdgbl)) return false; flag = true; break; case rec_system_type: // rdb$types if (!get_type(tdgbl)) return false; flag = true; break; case rec_filter: // rdb$filters if (!get_filter(tdgbl)) return false; flag = true; break; case rec_generator: if (!get_generator(tdgbl)) return false; flag = true; break; case rec_relation_data: if (flag) { BURP_verbose (68); // msg 68 committing meta data COMMIT; ON_ERROR general_on_error (); END_ERROR; EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; flag = false; } if (!get_relation_data(tdgbl)) return false; break; case rec_trigger: // new trigger type if (!get_trigger(tdgbl)) return false; flag = true; break; case rec_trigger_message: if (!get_trigger_message(tdgbl)) return false; flag = true; break; case rec_user_privilege: if (!get_user_privilege(tdgbl)) return false; flag = true; break; case rec_security_class: if (!get_security_class(tdgbl)) return false; flag = true; break; case rec_files: if (!get_files(tdgbl)) return false; flag = true; break; case rec_sql_roles: if (!get_sql_roles(tdgbl)) return false; flag = true; break; case rec_mapping: if (!get_mapping(tdgbl)) return false; flag = true; break; default: BURP_error(43, true, SafeArg() << record); // msg 43 don't recognize record type %ld break; } } if (tdgbl->defaultCollations.getCount() > 0) { isc_req_handle req_handle4 = 0; FOR (REQUEST_HANDLE req_handle4) CS IN RDB$CHARACTER_SETS for (size_t i = 0; i < tdgbl->defaultCollations.getCount(); ++i) { if (tdgbl->defaultCollations[i].first == CS.RDB$CHARACTER_SET_NAME) { MODIFY CS; CS.RDB$DEFAULT_COLLATE_NAME.NULL = FALSE; strcpy(CS.RDB$DEFAULT_COLLATE_NAME, tdgbl->defaultCollations[i].second.c_str()); END_MODIFY; ON_ERROR general_on_error (); END_ERROR; } } END_FOR; ON_ERROR general_on_error (); END_ERROR; MISC_release_request_silent(req_handle4); } // This piece of code is to fix bug 10098: restore of database with // only domains and no relations aborts with the message ERROR: deadlock // This is because insertion of domains into RDB$FIELDS is happening in // the default transaction, whereas updation of RDB$FIELDS to add // constraints to the domains is done in tdgbl->global_trans. In case of // no relations, no COMMIT of default transaction occurs till this point // because of which rows in RDB$FIELDS for domains are still locked by // default transaction. The below code COMMITs the default transaction // in that particular situation if (flag_norel) { COMMIT; ON_ERROR general_on_error (); END_ERROR; EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (gds_status[1]) EXEC SQL SET TRANSACTION; } // put validation clauses for global fields update_global_field(tdgbl); // Purge shadow metadata if necessary if (tdgbl->gbl_sw_kill) { isc_req_handle req_handle4 = 0; FOR (REQUEST_HANDLE req_handle4) FIL IN RDB$FILES WITH FIL.RDB$SHADOW_NUMBER NOT MISSING AND FIL.RDB$SHADOW_NUMBER NE 0 ERASE FIL; ON_ERROR general_on_error (); END_ERROR; END_FOR; ON_ERROR general_on_error (); END_ERROR; MISC_release_request_silent(req_handle4); } // update statistics for system indices isc_req_handle req_handle5 = 0; FOR (REQUEST_HANDLE req_handle5) IND IN RDB$INDICES WITH IND.RDB$SYSTEM_FLAG EQ 1 MODIFY IND IND.RDB$STATISTICS.NULL = FALSE; IND.RDB$STATISTICS = -1; END_MODIFY ON_ERROR general_on_error (); END_ERROR; END_FOR; ON_ERROR general_on_error (); END_ERROR; MISC_release_request_silent(req_handle5); return true; } void restore_security_class(BurpGlobals* tdgbl, const TEXT* owner_nm, const TEXT* sec_class_nm) { /************************************** * * r e s t o r e _ s e c u r i t y _ c l a s s * ************************************** * * Functional description * restore the ownership of the relation in the ACL list * **************************************/ isc_req_handle req_handle2 = 0; //isc_tr_handle local_trans = gds_trans; FOR (REQUEST_HANDLE req_handle2) X IN RDB$SECURITY_CLASSES WITH X.RDB$SECURITY_CLASS EQ sec_class_nm ISC_QUAD new_blob_id; new_blob_id.gds_quad_high = 0; new_blob_id.gds_quad_low = 0; get_acl(tdgbl, owner_nm, &X.RDB$ACL, &new_blob_id); MODIFY X; memcpy(&X.RDB$ACL, &new_blob_id, sizeof(ISC_QUAD)); END_MODIFY; ON_ERROR MISC_release_request_silent(req_handle2); general_on_error (); END_ERROR; END_FOR; ON_ERROR MISC_release_request_silent(req_handle2); general_on_error (); END_ERROR; MISC_release_request_silent(req_handle2); } USHORT get_view_base_relation_count(BurpGlobals* tdgbl, const TEXT* current_view_name, USHORT depth) { /************************************** * * g e t _ v i e w _ b a s e _ r e l a t i o n _ c o u n t * ************************************** * * Functional description * Return the number of base relations * (tables) from a view. When a view is * referenced in the view this function * is called recursively. * **************************************/ depth++; if (depth > MAX_UPDATE_DBKEY_RECURSION_DEPTH) { return 0; } isc_req_handle req_handle1 = 0; USHORT result = 0; FOR (REQUEST_HANDLE req_handle1) V IN RDB$VIEW_RELATIONS CROSS R IN RDB$RELATIONS WITH V.RDB$VIEW_NAME EQ current_view_name AND R.RDB$RELATION_NAME EQ V.RDB$RELATION_NAME if (R.RDB$VIEW_BLR.NULL) { // This relation is a table, so increment count result++; } else { // Call recursive for VIEWS that are referenced in VIEWS result += get_view_base_relation_count(tdgbl, V.RDB$RELATION_NAME, depth); } END_FOR; ON_ERROR MISC_release_request_silent(req_handle1); general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); return result; } void store_blr_gen_id(BurpGlobals* tdgbl, const TEXT* gen_name, // TEXT GDS_NAME[GDS_NAME_LEN] SINT64 value, const ISC_QUAD* gen_desc) { /************************************** * * s t o r e _ b l r _ g e n _ i d * ************************************** * * Functional description * Store the blr_gen_id for the relation. * **************************************/ if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11) { STORE (REQUEST_HANDLE tdgbl->handles_store_blr_gen_id_req_handle1) X IN RDB$GENERATORS strcpy (X.RDB$GENERATOR_NAME, gen_name); X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; if (gen_desc) { X.RDB$DESCRIPTION = *gen_desc; X.RDB$DESCRIPTION.NULL = FALSE; } END_STORE; ON_ERROR general_on_error (); END_ERROR; } else { STORE (REQUEST_HANDLE tdgbl->handles_store_blr_gen_id_req_handle1) X IN RDB$GENERATORS strcpy (X.RDB$GENERATOR_NAME, gen_name); X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; END_STORE; ON_ERROR general_on_error (); END_ERROR; } if (!value) { BURP_verbose (185, SafeArg() << gen_name << 0); // msg 185 restoring generator %s value: %ld return; } FB_API_HANDLE gen_id_reqh = 0; UCHAR blr_buffer[100]; // enough to fit blr UCHAR* blr = blr_buffer; // build the blr with the right relation name if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10) { add_byte(blr, blr_version5); } else { add_byte(blr, blr_version4); } add_byte(blr, blr_begin); if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10) { add_byte(blr, blr_dcl_variable); add_word(blr, 0); add_byte(blr, blr_int64); add_byte(blr, 0); } else { add_byte(blr, blr_dcl_variable); add_word(blr, 0); add_byte(blr, blr_long); add_byte(blr, 0); } add_byte(blr, blr_begin); add_byte(blr, blr_assignment); add_byte(blr, blr_gen_id); add_string(blr, gen_name); if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10) { add_byte(blr, blr_literal); add_byte(blr, blr_int64); add_byte(blr, 0); add_int64(blr, value); } else { add_byte(blr, blr_literal); add_byte(blr, blr_long); add_byte(blr, 0); add_long(blr, (SLONG)value); } add_byte(blr, blr_variable); add_word(blr, 0); add_byte(blr, blr_end); add_byte(blr, blr_end); add_byte(blr, blr_eoc); const USHORT blr_length = blr - blr_buffer; fb_assert(blr_length <= sizeof(blr_buffer)); ISC_STATUS_ARRAY status_vector; if (isc_compile_request (status_vector, &DB, &gen_id_reqh, blr_length, (const SCHAR*) blr_buffer)) { isc_print_blr ((const SCHAR*) blr_buffer, NULL, NULL, 0); BURP_error_redirect (status_vector, 42); // msg 42 Failed in store_blr_gen_id } if (isc_start_request (status_vector, &gen_id_reqh, &gds_trans, // use the same one generated by gpre 0)) { isc_print_blr ((const SCHAR*) blr_buffer, NULL, NULL, 0); BURP_error_redirect (status_vector, 42); // msg 42 Failed in store_blr_gen_id } BURP_verbose (185, SafeArg() << gen_name << value); // msg 185 restoring generator %s value: %ld isc_release_request (status_vector, &gen_id_reqh); } void update_global_field(BurpGlobals* tdgbl) { /************************************** * * u p d a t e _ g l o b a l _ f i e l d * ************************************** * * Functional description * Update the global field definition to add constraints. * The blobs have been created already. * **************************************/ isc_req_handle req_handle1 = 0; for (gfld* gfield = tdgbl->gbl_global_fields; gfield; ) { FOR (TRANSACTION_HANDLE tdgbl->global_trans REQUEST_HANDLE req_handle1) X IN RDB$FIELDS WITH X.RDB$FIELD_NAME EQ gfield->gfld_name MODIFY X if (gfield->gfld_flags & GFLD_validation_blr) { X.RDB$VALIDATION_BLR.NULL = FALSE; memcpy(&X.RDB$VALIDATION_BLR, &gfield->gfld_vb, sizeof(ISC_QUAD)); } if (gfield->gfld_flags & GFLD_validation_source) { X.RDB$VALIDATION_SOURCE.NULL = FALSE; memcpy(&X.RDB$VALIDATION_SOURCE, &gfield->gfld_vs, sizeof(ISC_QUAD)); } if (gfield->gfld_flags & GFLD_validation_source2) { X.RDB$VALIDATION_SOURCE.NULL = FALSE; memcpy(&X.RDB$VALIDATION_SOURCE, &gfield->gfld_vs2, sizeof(ISC_QUAD)); } if (gfield->gfld_flags & GFLD_computed_blr) { X.RDB$COMPUTED_BLR.NULL = FALSE; memcpy(&X.RDB$COMPUTED_BLR, &gfield->gfld_computed_blr, sizeof(ISC_QUAD)); } if (gfield->gfld_flags & GFLD_computed_source) { X.RDB$COMPUTED_SOURCE.NULL = FALSE; memcpy(&X.RDB$COMPUTED_SOURCE, &gfield->gfld_computed_source, sizeof(ISC_QUAD)); } if (gfield->gfld_flags & GFLD_computed_source2) { X.RDB$COMPUTED_SOURCE.NULL = FALSE; memcpy(&X.RDB$COMPUTED_SOURCE, &gfield->gfld_computed_source2, sizeof(ISC_QUAD)); } END_MODIFY; ON_ERROR general_on_error (); END_ERROR; END_FOR; ON_ERROR general_on_error (); END_ERROR; gfld* n_gfield = gfield->gfld_next; BURP_free (gfield); gfield = n_gfield; } MISC_release_request_silent(req_handle1); tdgbl->gbl_global_fields = NULL; } void update_view_dbkey_lengths(BurpGlobals* tdgbl) { /************************************** * * u p d a t e _ v i e w _ d b k e y _ l e n g t h s * ************************************** * * Functional description * During the restore process VIEWs could * be created that holds other VIEWs and * which weren't restored yet. * Then the RDB$DBKEY_LENGTH for VIEWs is * calculated wrong. Therefore we need to * recalculate the DBKEY_LENGTH else we * get our famous "arithmetic exception, * numeric overflow, or string truncation" error. * **************************************/ isc_req_handle req_handle2 = 0; FOR (REQUEST_HANDLE req_handle2) R IN RDB$RELATIONS WITH R.RDB$VIEW_BLR NOT MISSING AND (R.RDB$SYSTEM_FLAG NE 1 OR R.RDB$SYSTEM_FLAG MISSING) const USHORT result = get_view_base_relation_count(tdgbl, R.RDB$RELATION_NAME, 0); MODIFY R; R.RDB$DBKEY_LENGTH = (result * 8); // is a constant for DBKEY coded somewhere? END_MODIFY; ON_ERROR MISC_release_request_silent(req_handle2); general_on_error(); END_ERROR; END_FOR; ON_ERROR MISC_release_request_silent(req_handle2); general_on_error(); END_ERROR; MISC_release_request_silent(req_handle2); } } // namespace