/* * 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. */ /* $Id: restore.epp,v 1.47 2003-09-23 19:47:50 brodsom Exp $ */ #include "firebird.h" #include "../jrd/ib_stdio.h" #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 "../jrd/thd_proto.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 "../intl/charsets.h" #include "../jrd/gdsassert.h" #include "../remote/protocol.h" #ifdef DEBUG #include "../gpre/prett_proto.h" #endif // 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 = 4; /* ods4 db */ const int DB_VERSION_DDL5 = 5; /* ods5 db */ const int DB_VERSION_DDL8 = 8; /* ods8 db */ const int DB_VERSION_CURRENT = DB_VERSION_DDL8; /* v4.0 is ods8 */ const char* FOREIGN_KEY = "FOREIGN KEY"; 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 cvtbl_len = 28; const struct s_t_cvtbl { SSHORT sub_type; SSHORT character_set_id; SSHORT collation_id; } sub_type_cvtbl [] = { /* NOTE: The magic numbers for collation_id come from ordinal * position of the COLLATION() entries in jrd/intlnames.h */ {101, CS_DOS_437, 1}, /* PDOX_ASCII */ {102, CS_DOS_437, 2}, /* PDOX_INTL */ {106, CS_DOS_437, 3}, /* PDOX_SWEDFIN */ {160, CS_DOS_850, 0}, /* codepoint collation */ {107, CS_DOS_865, 0}, /* codepoint collation */ {105, CS_DOS_865, 1}, /* PDOX_NORDAN4 */ {139, CS_ISO8859_1, 1}, /* DA_DA */ {140, CS_ISO8859_1, 2}, /* DU_NL */ {141, CS_ISO8859_1, 3}, /* FI_FI */ {142, CS_ISO8859_1, 4}, /* FR_FR */ {143, CS_ISO8859_1, 5}, /* FR_CA */ {144, CS_ISO8859_1, 6}, /* DE_DE */ {145, CS_ISO8859_1, 7}, /* IS_IS */ {146, CS_ISO8859_1, 8}, /* IT_IT */ {148, CS_ISO8859_1, 9}, /* NO_NO */ {149, CS_ISO8859_1, 10}, /* ES_ES */ {151, CS_ISO8859_1, 11}, /* SV_SV */ {152, CS_ISO8859_1, 12}, /* EN_UK */ {153, CS_ISO8859_1, 14}, /* EN_US */ {154, CS_ISO8859_1, 15}, /* PT_PT */ {160, CS_ISO8859_2, 1}, /* CS_CZ */ {180, CS_NEXT, 1}, /* NXT_US */ {181, CS_NEXT, 2}, /* NXT_GERMANY */ {182, CS_NEXT, 3}, /* NXT_FRANCE */ {183, CS_NEXT, 4}, /* NXT_ITALY */ {184, CS_NEXT, 5}, /* NXT_SPAIN */ {201, CS_UNICODE_FSS, 0}, /* codepoint collation */ {220, CS_SJIS, 0}, /* codepoint collation */ {230, CS_EUCJ, 0} /* codepoint collation */ }; 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(const UCHAR *); void bad_attribute(scan_attr_t, ATT_TYPE, USHORT); USHORT check_db_version (void); void create_database (const TEXT *); void decompress (UCHAR *, USHORT); void eat_blob (void); BURP_REL find_relation (const TEXT *); // 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 (const TEXT*, ISC_QUAD *, ISC_QUAD *); void get_array (BURP_REL, UCHAR *); void get_blob (BURP_FLD, UCHAR *); void get_blr_blob (ISC_QUAD*, bool); bool get_character_set (void); bool get_chk_constraint (void); bool get_collation (void); rec_type get_data (BURP_REL); bool get_exception (void); BURP_FLD get_field (BURP_REL); bool get_field_dimensions (void); bool get_files (void); bool get_filter (void); bool get_function (void); void get_function_arg (); bool get_generator (void); bool get_global_field (void); bool get_index (BURP_REL); void get_misc_blob (ISC_QUAD*, USHORT, bool); SLONG get_numeric (void); SINT64 get_int64 (void); bool get_procedure (void); bool get_procedure_prm (GDS_NAME ); bool get_ref_constraint (void); bool get_rel_constraint (void); bool get_relation (void); bool get_relation_data (void); bool get_sql_roles (void); bool get_security_class (void); void get_source_blob (ISC_QUAD*, bool); USHORT get_text (TEXT *, ULONG); bool get_trigger (void); bool get_trigger_message (void); bool get_trigger_old (BURP_REL); bool get_type (void); bool get_user_privilege (void); bool get_view (BURP_REL); void ignore_array (BURP_REL); void ignore_blob (void); rec_type ignore_data (BURP_REL); void realign (UCHAR *, BURP_REL); USHORT recompute_length (BURP_REL); bool restore (const TEXT *, const TEXT *); void restore_security_class (const TEXT *, const TEXT *); void store_blr_gen_id (const TEXT*, SINT64); void stuff_string(UCHAR **, const TEXT *); void update_global_field (void); void general_on_error (void); #ifdef NOT_USED_OR_REPLACED bool bug_8183 (TGBL); #endif #ifdef DEBUG UCHAR debug_on = 0; /* able to turn this on in the debugger */ #endif bool flag_on_line = true; /* indicates whether we will bring the database on-line : true - we will false - we will not */ #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(tgbl* tdgbl) { if (--(tdgbl->io_cnt) >= 0) return *(tdgbl->io_ptr)++; else // => superfluous here return MVOL_read(&tdgbl->io_cnt, &tdgbl->io_ptr); } static inline ATT_TYPE get_attribute(ATT_TYPE *att, TGBL tdgbl) { *att = (ATT_TYPE) get(tdgbl); return *att; } static inline rec_type get_record(rec_type *rec, TGBL tdgbl) { *rec = (rec_type) get(tdgbl); return *rec; } #define GET_TEXT(text) get_text((text), sizeof(text)) static inline void get_skip(tgbl* tdgbl, ULONG n) { MVOL_skip_block(tdgbl, n); } static inline UCHAR* get_block(tgbl* 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 bool SKIP_SCAN(scan_attr_t *scan_next_attr) { return (*scan_next_attr == AFTER_SKIP ? *scan_next_attr = BEFORE_SKIP : *scan_next_attr == BEFORE_SKIP ? *scan_next_attr = NO_SKIP : 0); } /* 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 (TEXT *file_name, 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 = NULL, req_handle2 = NULL, req_handle4 = NULL; isc_req_handle req_handle3 = NULL; BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name; TGBL tdgbl = GET_THREAD_DATA; tdgbl->io_ptr = NULL; tdgbl->io_cnt = 0; tdgbl->relations = NULL; tdgbl->procedures = NULL; tdgbl->RESTORE_format = 0; tdgbl->global_trans = 0; tdgbl->gbl_sw_transportable = tdgbl->gbl_sw_compress = FALSE; if (!restore (file_name, database_name)) return FINI_ERROR; BURP_verbose (76, NULL, NULL, NULL, NULL, NULL); /* 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 gds_sort_mem_err: case gds_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, NULL, NULL, NULL, NULL); MODIFY IDX USING IDX.RDB$INDEX_INACTIVE = TRUE; } BURP_print(240, index_name, NULL, NULL, NULL, NULL); /* msg 240 Index \"%s\" failed to activate because: */ if ( error_code == gds_no_dup ) { BURP_print(241, NULL, NULL, NULL, NULL, NULL); /* msg 241 The unique index has duplicate values or NULLs */ BURP_print(242, NULL, NULL, NULL, NULL, NULL); /* msg 242 Delete or Update duplicate values or NULLs, and activate index with */ } else { BURP_print(244, NULL, NULL, NULL, NULL, NULL); /* msg 244 Not enough disk space to create the sort file for an index */ BURP_print(245, NULL, NULL, NULL, NULL, NULL); /* 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, NULL, NULL, NULL, NULL); /* msg 243 ALTER INDEX \"%s\" ACTIVE; */ END_MODIFY; END_FOR; /* don't bring the database on-line */ 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 */ if (tdgbl->gbl_sw_verbose) { 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 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, IDS.RDB$INDEX_NAME, NULL, NULL, NULL, NULL); BURP_print_status (isc_status); MODIFY IDS USING IDS.RDB$INDEX_INACTIVE = TRUE; END_MODIFY; ON_ERROR general_on_error (); END_ERROR; END_ERROR; BURP_verbose(122, IDS.RDB$INDEX_NAME, NULL, NULL, NULL, NULL); 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 */ 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 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, IDS.RDB$INDEX_NAME, NULL, NULL, NULL, NULL); BURP_print_status (isc_status); MODIFY IDS USING IDS.RDB$INDEX_INACTIVE = TRUE; END_MODIFY; ON_ERROR general_on_error (); END_ERROR; END_ERROR; BURP_verbose(122, IDS.RDB$INDEX_NAME, NULL, NULL, NULL, NULL); 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, NULL, NULL, NULL, NULL, NULL); /* 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] == gds_arg_gds && gds_status [1] == 0 && gds_status [2] != gds_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; /* ** 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 (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 (relation->rel_owner, X.RDB$SECURITY_CLASS); restore_security_class (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] == gds_arg_gds && gds_status [1] == 0 && gds_status [2] != gds_arg_end) { BURP_print_warning (gds_status); } BURP_verbose (88, NULL, NULL, NULL, NULL, NULL); /* msg 88 finishing, closing, and going home */ UINT64 cumul_count = MVOL_fini_read(); /* attach database again to put it online */ UCHAR dpb[128]; UCHAR *d = dpb; *d++ = (UCHAR) gds_dpb_version1; if (flag_on_line) { *d++ = (UCHAR) gds_dpb_online; *d++ = 0; } if (tdgbl->gbl_sw_user) { *d++ = (UCHAR) gds_dpb_user_name; *d++ = (UCHAR) strlen (tdgbl->gbl_sw_user); for (UCHAR* q = (UCHAR*) tdgbl->gbl_sw_user; *q;) *d++ = *q++; } if (tdgbl->gbl_sw_password) { if (!tdgbl->gbl_sw_service_gbak) *d++ = (UCHAR) gds_dpb_password; else *d++ = (UCHAR) gds_dpb_password_enc; *d++ = (UCHAR) strlen (tdgbl->gbl_sw_password); for (UCHAR* q = (UCHAR*) tdgbl->gbl_sw_password; *q;) *d++ = *q++; } /* set sync writes to engine default */ *d++ = (UCHAR) gds_dpb_force_write; *d++ = 1; *d++ = (UCHAR) tdgbl->hdr_forced_writes; // set forced writes to the value which was in the header SSHORT l = d - dpb; // long? Are you sure? Why not FRBRD* ??? long db_handle = 0; if (isc_attach_database (tdgbl->status_vector, 0, database_name, (isc_db_handle*) &db_handle, l, (SCHAR*) dpb)) { general_on_error(); } if (isc_detach_database (tdgbl->status_vector, (isc_db_handle*) &db_handle)) general_on_error(); FINISH ON_ERROR general_on_error (); END_ERROR; if (!flag_on_line) { BURP_print(246, NULL, NULL, NULL, NULL, NULL); /* msg 246 Database is not online due to failure to activate one or more indices. */ BURP_print(247, NULL, NULL, NULL, NULL, NULL); /* 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, NULL, NULL, NULL, NULL, NULL); /* msg 280: setting database to read-only access */ d = dpb; *d++ = (UCHAR) gds_dpb_version1; if (tdgbl->gbl_sw_user) { *d++ = (UCHAR) gds_dpb_user_name; *d++ = (UCHAR) strlen (tdgbl->gbl_sw_user); for (UCHAR* q = (UCHAR*) tdgbl->gbl_sw_user; *q;) *d++ = *q++; } if (tdgbl->gbl_sw_password) { if (!tdgbl->gbl_sw_service_gbak) *d++ = (UCHAR) gds_dpb_password; else *d++ = (UCHAR) gds_dpb_password_enc; *d++ = (UCHAR) strlen (tdgbl->gbl_sw_password); for (UCHAR* q = (UCHAR*) tdgbl->gbl_sw_password; *q;) *d++ = *q++; } *d++ = (UCHAR) isc_dpb_set_db_readonly; *d++ = 1; *d++ = TRUE; /* set database to readOnly mode */ l = d - dpb; db_handle = 0; if (isc_attach_database (tdgbl->status_vector, 0, database_name, (isc_db_handle*) &db_handle, l, (SCHAR*) dpb)) { general_on_error(); } if (isc_detach_database (tdgbl->status_vector, (isc_db_handle*) &db_handle)) general_on_error(); } return FINI_OK; } namespace // unnamed, private { void add_files (const UCHAR *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 = NULL; TGBL tdgbl = GET_THREAD_DATA; /* store the RDB$FILES records */ SLONG start = 201; SLONG count = 0; for (FIL file = tdgbl->gbl_sw_files; file; file = file->fil_next) { if (strcmp (file->fil_name, (char*) file_name)) { count++; STORE (REQUEST_HANDLE req_handle1) X IN RDB$FILES strcpy (X.RDB$FILE_NAME, file->fil_name); X.RDB$FILE_START = start; END_STORE; ON_ERROR general_on_error (); END_ERROR; MISC_release_request_silent(req_handle1); BURP_verbose (57, file->fil_name, (void*) start, NULL, NULL, NULL); /* msg 57 adding file %s, starting at page %ld */ } else if (((signed long )file->fil_length) >= start - 1) file->fil_length -= start - 1; else { BURP_print (96, (void*) file->fil_length, (void*) (start - 1), NULL, NULL, NULL); // 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, NULL, NULL, NULL, NULL, NULL); /* msg 70 committing secondary files */ COMMIT /* existing ON_ERROR continues past error, beck */ ON_ERROR BURP_print (174, NULL, NULL, NULL, NULL, NULL); /* 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. * **************************************/ SSHORT skip_l; TEXT t_name [128]; TGBL tdgbl = GET_THREAD_DATA; SLONG skip_count = 0; if (!tdgbl->gbl_sw_skip_count) { gds__msg_format(NULL, 12, type, sizeof(t_name), t_name, NULL, NULL, NULL, NULL, NULL); BURP_print (80, t_name, (void*) bad_attr, NULL, NULL, NULL); /* msg 80 don't recognize %s attribute %ld -- continuing */ 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, (void*) skip_count, (void*) bad_attr, NULL, NULL, NULL); /*msg 203: skipped %d bytes after reading a bad attribute %d */ } else { skip_count ++; BURP_print (205, (void*) skip_count, (void*) bad_attr, NULL, NULL, NULL); /* msg 205: skipped %d bytes looking for next valid attribute, encountered attribute %d */ } scan_next_attr = AFTER_SKIP; } } USHORT check_db_version (void) { /************************************** * * c h e c k _ d b _ v e r s i o n * ************************************** * * Functional description * Find the version number of the database. * **************************************/ isc_req_handle req_handle1 = NULL, req_handle2 = NULL; TGBL tdgbl = GET_THREAD_DATA; USHORT db_version = DB_VERSION_DDL4; FOR (REQUEST_HANDLE req_handle1) FIRST 1 X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME = "RDB$TRIGGERS" db_version = DB_VERSION_DDL5; END_FOR; ON_ERROR general_on_error (); END_ERROR; MISC_release_request_silent(req_handle1); FOR (REQUEST_HANDLE req_handle2) FIRST 1 X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME = "RDB$PROCEDURES" db_version = DB_VERSION_DDL8; END_FOR; ON_ERROR general_on_error (); END_ERROR; MISC_release_request_silent(req_handle2); return db_version; } void create_database (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. * **************************************/ SSHORT l; ISC_STATUS_ARRAY status_vector; ATT_TYPE attribute; UCHAR dpb [128], *d, *q; ULONG page_size, sweep_interval; USHORT SQL_dialect; TGBL tdgbl = GET_THREAD_DATA; /* Get (physical) database record */ page_size = DEFAULT_PAGE_SIZE; // sweep_interval = -1; // sweep_interval = 0xFFFFFFFF; sweep_interval = MAX_ULONG; bool no_reserve = false; bool db_read_only = false, SQL_dialect_flag = false; USHORT forced_writes = 2; /* default for the current platform */ ULONG page_buffers = 0; 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(); break; case att_page_size: page_size = get_numeric(); break; case att_sweep_interval: sweep_interval = get_numeric(); break; case att_forced_writes: forced_writes = (USHORT) get_numeric(); break; case att_no_reserve: no_reserve = (USHORT) get_numeric() != FALSE; break; case att_db_read_only: db_read_only = get_numeric() != FALSE; break; case att_page_buffers: page_buffers = get_numeric(); break; default: l = get(tdgbl); if (l) get_skip(tdgbl, l); break; } } get_record(&record, tdgbl); } if (record != rec_database) BURP_error_redirect (NULL, 32, 0, 0); /* msg 32 Expected database description record */ if (tdgbl->gbl_sw_page_size && (tdgbl->gbl_sw_page_size < page_size)) { BURP_print (110, (void*) page_size, (void*) (ULONG) tdgbl->gbl_sw_page_size, NULL, NULL, NULL); /* 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 != FALSE; /* 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; d = dpb; *d++ = (UCHAR) isc_dpb_version1; *d++ = (UCHAR) isc_dpb_page_size; *d++ = 2; *d++ = 0; *d++ = (UCHAR) (page_size >> 8); *d++ = (UCHAR) isc_dpb_gbak_attach; *d++ = (UCHAR) strlen(GDS_VERSION); for (q = (UCHAR*) GDS_VERSION; *q;) *d++ = *q++; if (sweep_interval != MAX_ULONG) { *d++ = (UCHAR) isc_dpb_sweep_interval; *d++ = 4; *d++ = (UCHAR) sweep_interval; *d++ = (UCHAR) (sweep_interval >> 8); *d++ = (UCHAR) (sweep_interval >> 16); *d++ = (UCHAR) (sweep_interval >> 24); } /* If the database is to be restored "read_only", fillup the data pages */ if (no_reserve || db_read_only) { *d++ = (UCHAR) isc_dpb_no_reserve; *d++ = 1; *d++ = TRUE; } if (tdgbl->gbl_sw_user) { *d++ = (UCHAR) isc_dpb_user_name; *d++ = (UCHAR) strlen (tdgbl->gbl_sw_user); for (q = (UCHAR*) tdgbl->gbl_sw_user; *q;) *d++ = *q++; } if (tdgbl->gbl_sw_password) { if (!tdgbl->gbl_sw_service_gbak) *d++ = (UCHAR) isc_dpb_password; else *d++ = (UCHAR) isc_dpb_password_enc; *d++ = (UCHAR) strlen (tdgbl->gbl_sw_password); for (q = (UCHAR*) tdgbl->gbl_sw_password; *q;) *d++ = *q++; } if (page_buffers) { *d++ = (UCHAR) isc_dpb_set_page_buffers; *d++ = 4; *d++ = (UCHAR) page_buffers; *d++ = (UCHAR) (page_buffers >> 8); *d++ = (UCHAR) (page_buffers >> 16); *d++ = (UCHAR) (page_buffers >> 24); } /* Turn off sync writes during restore */ *d++ = (UCHAR) isc_dpb_force_write; *d++ = 1; *d++ = 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 ** */ *d++ = (UCHAR) isc_dpb_sql_dialect; *d++ = 1; if (SQL_dialect_flag) *d++ = (UCHAR) SQL_dialect; else *d++ = (UCHAR) SQL_DIALECT_V5; /* start database up shut down */ *d++ = (UCHAR) isc_dpb_shutdown; *d++ = 1; *d++ = (UCHAR) isc_dpb_shut_attachment; *d++ = (UCHAR) isc_dpb_shutdown_delay; *d++ = 2; *d++ = 0; *d++ = 0; l = d - dpb; // We'll have to fix the API someday, so const params are declared const. if (isc_create_database (status_vector, 0, (TEXT*) file_name, &DB, l, (SCHAR*) dpb, 0)) { BURP_error_redirect (status_vector, 33, file_name, 0); /* msg 33 failed to create database %s */ } if (tdgbl->gbl_sw_version) { BURP_print(139, file_name, NULL, NULL, NULL, NULL); /* msg 139 Version(s) for database "%s" */ isc_version(&DB, (void (*)()) BURP_output_version, (void*) "\t%s\n"); } BURP_verbose (74, file_name, (void*) page_size, NULL, NULL, NULL); /* msg 74 created database %s, page_size %ld bytes */ } void decompress (UCHAR *buffer, USHORT length) { /************************************** * * d e c o m p r e s s * ************************************** * * Functional description * Get a number of compressed bytes. * **************************************/ UCHAR c, *p, *end; SSHORT count; TGBL tdgbl = GET_THREAD_DATA; p = buffer; 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 */ count = (SCHAR) get(tdgbl); if (count > 0) { if (end - p < count) { BURP_print (202, (void*) (SLONG) count, (void*) (ULONG) (end - p), NULL, NULL, NULL); /* 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, (void*) (SLONG) count, (void*) (ULONG) (p - end), NULL, NULL, NULL); /* msg 202: adjusting a decompression length error: invalid length %d was adjusted to %d */ count = p - end; } c = get(tdgbl); memset (p, c, -count); p += -count; } } if (p > end) BURP_error_redirect (NULL, 34, 0, 0); /* msg 34 RESTORE: decompression length error */ } void eat_blob (void) { /************************************** * * e a t _ b l o b * ************************************** * * Functional description * Discard a blob from backup file * **************************************/ TGBL tdgbl = GET_THREAD_DATA; SLONG length = get_numeric(); get_skip(tdgbl, length); } BURP_REL find_relation (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. * **************************************/ BURP_REL relation; TGBL tdgbl = GET_THREAD_DATA; // Why isn't strcmp used here? for (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, name, 0); /* msg 35 can't find relation %s */ return NULL; } void general_on_error (void) { /************************************** * * g e n e r a l _ o n _ e r r o r * ************************************** * * Functional description * Handle any general ON_ERROR clause during restore. * **************************************/ TGBL tdgbl = GET_THREAD_DATA; BURP_print_status (isc_status); BURP_abort (); } bool get_acl (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 SCHAR blr_items [] = {isc_info_blob_max_segment, isc_info_blob_total_length, isc_info_blob_num_segments}; ISC_STATUS_ARRAY status_vector; UCHAR *p, blob_info [32], item, *buffer, static_buffer [1024]; USHORT max_segment, num_segments, new_len = 0; UCHAR *c_1, *from, *to; SLONG id_person_len; isc_blob_handle blob_handle = NULL; TGBL tdgbl = GET_THREAD_DATA; /* 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 */ FRBRD* blob = NULL; if (isc_open_blob (status_vector, &DB, &gds_trans, &blob, blob_id)) { /* msg 24 gds__open_blob failed */ BURP_error_redirect (status_vector, 24, NULL, NULL); } if (isc_blob_info (status_vector, &blob, sizeof(blr_items), (SCHAR *) blr_items, sizeof(blob_info), (SCHAR*) blob_info)) { /* msg 20 gds__blob_info failed */ BURP_error_redirect (status_vector, 20, NULL, NULL); } SLONG length = 0; p = blob_info; while ((item = *p++) != gds_info_end) { USHORT l = (USHORT) gds__vax_integer (p, 2); p += 2; 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) assert (num_segments > 1); } break; default: /* msg 79 don't understand blob info item %ld */ BURP_print (79, (void*) (ULONG) item, NULL, NULL, NULL, NULL); // CVC: do you return, without closing the blob, dear function??? if (isc_close_blob (status_vector, &blob)) BURP_error_redirect (status_vector, 23, NULL, NULL); // msg 23 gds__close_blob failed return false; } } if (!length) { if (isc_close_blob (status_vector, &blob)) /* msg 23 gds__close_blob failed */ BURP_error_redirect (status_vector, 23, NULL, NULL); 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. */ if (!max_segment || max_segment <= sizeof(static_buffer)) buffer = static_buffer; else buffer = BURP_alloc (max_segment); USHORT return_length = 0; isc_get_segment (status_vector, &blob, &return_length, max_segment, (SCHAR*) buffer); // CVC: we don't check the result of the function. We don't check the // return_length's value either. if (isc_close_blob (status_vector, &blob)) { if (buffer != static_buffer) BURP_free (buffer); /* msg 23 gds__close_blob failed */ BURP_error_redirect (status_vector, 23, NULL, NULL); } from = buffer + 3; /* skip ACL_version, ACL_id_list, and id_person */ id_person_len = (int)*from; c_1 = (UCHAR*) owner_nm; UCHAR owner_nm_len = strlen (owner_nm); UCHAR* new_buffer = BURP_alloc (length - id_person_len + owner_nm_len); from = buffer; to = new_buffer; *to++ = *from++; /* copy ACL_verion */ *to++ = *from++; /* copy ACL_id_list */ *to++ = *from++; /* copy id_person */ *to++ = owner_nm_len; new_len = new_len + 4; from = buffer + id_person_len + 4; for (SLONG cnt = 0; cnt < owner_nm_len; cnt++) { *to++ = *c_1++; new_len++; } UCHAR* const end_buffer = buffer + length; for (from = buffer + id_person_len + 4; from < end_buffer; from++) { *to++ = *from; new_len++; } if (isc_create_blob2 (status_vector, &DB, &gds_trans, &blob_handle, new_blob_id, 0, NULL)) { if (buffer != static_buffer) BURP_free (buffer); if (new_buffer != NULL) BURP_free (new_buffer); /* msg 37 gds__create_blob failed */ BURP_error_redirect (status_vector, 37, 0, 0); } if (isc_put_segment (status_vector, &blob_handle, new_len, (SCHAR*) new_buffer)) { if (buffer != static_buffer) BURP_free (buffer); if (new_buffer != NULL) BURP_free (new_buffer); /* msg 38 gds__put_segment failed */ BURP_error_redirect (status_vector, 38, 0, 0); } if (isc_close_blob (status_vector, &blob_handle)) { if (buffer != static_buffer) BURP_free (buffer); if (new_buffer != NULL) BURP_free (new_buffer); /* msg 23 gds__close_blob failed */ BURP_error_redirect (status_vector, 23, 0, 0); } if (buffer != static_buffer) BURP_free (buffer); if (new_buffer != NULL) BURP_free (new_buffer); return true; } void get_array (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; ATT_TYPE attribute; ISC_STATUS_ARRAY status_vector; SLONG last_element_dim[MAX_DIMENSION]; SLONG fld_ranges[2 * MAX_DIMENSION]; SLONG *blob_id, return_length, slice_length, *range, *end_ranges, lcount; USHORT blr_length, count, field_number, field_length; UCHAR *buffer, *p; UCHAR *blr, blr_buffer [200]; /* enough for a sdl with 16 dimensions */ LSTRING xdr_buffer, xdr_slice; scan_attr_t scan_next_attr; TGBL tdgbl = GET_THREAD_DATA; /* don't free something you don't allocate */ xdr_buffer.lstr_allocated = 0; /* 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(); for (field = relation->rel_fields; field; field = field->fld_next) if (field->fld_number == field_number) break; if (!field) BURP_error_redirect (NULL, 36, 0, 0); /* 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(); 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(); if (get_attribute(&attribute, tdgbl) != att_array_range_high) bad_attribute (scan_next_attr, attribute, 58); /* msg 58 array */ else range [1] = get_numeric(); slice_length *= (range [1] - range [0] + 1); } break; default: bad_attribute (scan_next_attr, attribute, 58); /* msg 58 array */ break; } } return_length = get(tdgbl); return_length |= get(tdgbl) << 8; return_length |= get(tdgbl) << 16; return_length |= get(tdgbl) << 24; blob_id = (SLONG*) ((UCHAR*) record_buffer + field->fld_offset); if (return_length != slice_length) { int current_dim, divisor, elements_remaining; int data_at, elements_written, upper, lower; int i1, i2, i3; // // 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 // 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 (i1 = 0, i3 = 0; i1 < field->fld_dimensions; i1++) { divisor = 1; for (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; } #ifdef DEBUG ib_fprintf(ib_stderr, "\nLast element upper bounds read from backup file:\n"); for(current_dim=1;current_dimfld_dimensions*2;current_dim+=2) ib_fprintf(ib_stderr,"%ld ",fld_ranges[current_dim]); ib_fprintf(ib_stderr, "\nCalculated Last element upper bounds :\n"); for(current_dim=0;current_dimfld_dimensions;current_dim++) ib_fprintf(ib_stderr,"%ld ",last_element_dim[current_dim]); ib_fprintf(ib_stderr,"return_length = %ld\n", return_length); ib_fprintf(ib_stderr,"elements_returned = %ld\n", return_length/field_length); ib_fprintf(ib_stderr,"Max dims["); for(current_dim=1;current_dimfld_dimensions*2;current_dim+=2) ib_fprintf(ib_stderr,"%ld ",field->fld_ranges[current_dim]); ib_fprintf(ib_stderr,"]"); #endif 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_dimfld_dimensions; current_dim++) { blr = blr_buffer; bool dont_write = false; /* build the sdl */ stuff(&blr, gds_sdl_version1); stuff(&blr, gds_sdl_struct); stuff(&blr, 1); if (field->fld_type == blr_text || field->fld_type == blr_varying) { if (field->fld_type == blr_text) stuff(&blr, blr_text2); else stuff(&blr, blr_varying2); stuff_word(&blr, field->fld_character_set_id); stuff_word(&blr, field->fld_length); } else if (field->fld_type == blr_short || field->fld_type == blr_long || field->fld_type == blr_quad || field->fld_type == blr_int64) { stuff(&blr, field->fld_type); stuff(&blr, field->fld_scale); } else stuff(&blr, field->fld_type); stuff(&blr, gds_sdl_relation); stuff_string(&blr, relation->rel_name); stuff(&blr, gds_sdl_field); stuff_string(&blr, field->fld_name); /* each element spec starts here */ #ifdef DEBUG ib_fprintf(ib_stderr,"\nBounds written ["); #endif 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++) { stuff(&blr, gds_sdl_do2); stuff(&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] ) { stuff(&blr, gds_sdl_long_integer); stuff_long(&blr, range [0]); lower = range [0]; stuff(&blr, gds_sdl_long_integer); stuff_long(&blr, last_element_dim [count]); upper = last_element_dim [count]; elements_written *= (upper - lower + 1); range += 2; count++; stuff(&blr, gds_sdl_do2); stuff(&blr, count); stuff(&blr, gds_sdl_long_integer); stuff_long(&blr, range [0]); lower = range [0]; stuff(&blr, gds_sdl_long_integer); stuff_long(&blr, last_element_dim [count]); upper=last_element_dim [count]; elements_written *= (upper - lower + 1); ++current_dim; break; } if( current_dim == count) { stuff(&blr, gds_sdl_long_integer); stuff_long(&blr, range [0]); lower = range [0]; stuff(&blr, gds_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; } stuff_long(&blr, upper); } else if (current_dim < count) { stuff(&blr, gds_sdl_long_integer); stuff_long(&blr, range [0]); stuff(&blr, gds_sdl_long_integer); stuff_long(&blr, range [1]); upper = range[1]; lower = range[0]; } else if (current_dim > count) { stuff(&blr, gds_sdl_long_integer); stuff_long(&blr, last_element_dim [count]); stuff(&blr, gds_sdl_long_integer); stuff_long(&blr, last_element_dim [count]); upper=lower=last_element_dim [count]; } elements_written *= (upper - lower + 1); #ifdef DEBUG ib_fprintf(ib_stderr,"%d..%d ",lower,upper); #endif } if (dont_write) continue; #ifdef DEBUG ib_fprintf(ib_stderr,"]"); ib_fprintf(ib_stderr,"\n Elements Written=%d ",elements_written); #endif stuff(&blr, gds_sdl_element); stuff(&blr, 1); stuff(&blr, gds_sdl_scalar); stuff(&blr, 0); stuff(&blr, field->fld_dimensions); for (count = 0; count < field->fld_dimensions; count++) { stuff(&blr, gds_sdl_variable); stuff(&blr, count); } stuff(&blr, gds_sdl_eoc); #ifdef DEBUG if (debug_on) PRETTY_print_sdl (blr_buffer, NULL, NULL, 0); #endif blr_length = blr - blr_buffer; if(data_at == 0) { buffer = BURP_alloc (return_length); if (tdgbl->gbl_sw_transportable) { if (get_attribute(&attribute, tdgbl) != att_xdr_array) /* msg 55 Expected XDR record length */ BURP_error_redirect (NULL, 55, 0, 0); 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, (ISC_QUAD*) 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, NULL, NULL, NULL, NULL); /* 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 ib_fprintf(ib_stderr,"next data_at = %d\n", data_at); #endif } } else { /** This is the regular case we've got the entire array **/ blr = blr_buffer; /* build the sdl */ stuff(&blr, gds_sdl_version1); stuff(&blr, gds_sdl_struct); stuff(&blr, 1); if (field->fld_type == blr_text || field->fld_type == blr_varying) { if (field->fld_type == blr_text) stuff(&blr, blr_text2); else stuff(&blr, blr_varying2); stuff_word(&blr, field->fld_character_set_id); stuff_word(&blr, field->fld_length); } else if (field->fld_type == blr_short || field->fld_type == blr_long || field->fld_type == blr_quad || field->fld_type == blr_int64) { stuff(&blr, field->fld_type); stuff(&blr, field->fld_scale); } else stuff(&blr, field->fld_type); stuff(&blr, gds_sdl_relation); stuff_string(&blr, relation->rel_name); stuff(&blr, gds_sdl_field); stuff_string(&blr, field->fld_name); /* each element spec starts here */ for (range = fld_ranges, count = 0; range < end_ranges; range += 2, count++) { stuff(&blr, gds_sdl_do2); stuff(&blr, count); stuff(&blr, gds_sdl_long_integer); stuff_long(&blr, range [0]); stuff(&blr, gds_sdl_long_integer); stuff_long(&blr, range [1]); } stuff(&blr, gds_sdl_element); stuff(&blr, 1); stuff(&blr, gds_sdl_scalar); stuff(&blr, 0); stuff(&blr, field->fld_dimensions); for (count = 0; count < field->fld_dimensions; count++) { stuff(&blr, gds_sdl_variable); stuff(&blr, count); } stuff(&blr, gds_sdl_eoc); #ifdef DEBUG if (debug_on) PRETTY_print_sdl (blr_buffer, NULL, NULL, 0); #endif blr_length = blr - blr_buffer; buffer = BURP_alloc (return_length); if (tdgbl->gbl_sw_transportable) { if (get_attribute(&attribute, tdgbl) != att_xdr_array) BURP_error_redirect (NULL, 55, 0, 0); /* 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, (ISC_QUAD*) 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, NULL, NULL, NULL, NULL); /* 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 (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. * **************************************/ BURP_FLD field; ATT_TYPE attribute; ISC_STATUS_ARRAY status_vector; USHORT field_number, max_segment, length; UCHAR *buffer, static_buffer [1024]; scan_attr_t scan_next_attr; TGBL tdgbl = GET_THREAD_DATA; /* Pick up attributes */ ULONG 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: field_number = (USHORT)get_numeric(); break; case att_blob_max_segment: max_segment = (USHORT)get_numeric(); break; case att_blob_number_segments: segments = get_numeric(); break; case att_blob_type: get_numeric(); break; default: bad_attribute (scan_next_attr, attribute, 64); /* msg 64 blob */ break; } } /* Find the field associated with the blob */ for (field = fields; field; field = field->fld_next) if (field->fld_number == field_number) break; if (!field) BURP_error_redirect (NULL, 36, 0, 0); /* msg 36 Can't find field for blob */ /* Create new blob */ ISC_QUAD* blob_id = (ISC_QUAD*) ((UCHAR*) record_buffer + field->fld_offset); FRBRD* blob = NULL; if (isc_create_blob (status_vector, &DB, &gds_trans, &blob, (ISC_QUAD*) blob_id)) { BURP_error_redirect (status_vector, 37, 0, 0); /* msg 37 gds__create_blob failed */ } /* Allocate blob buffer is static buffer is too short */ if (!max_segment || max_segment <= sizeof(static_buffer)) buffer = static_buffer; else buffer = BURP_alloc (max_segment); /* Eat up blob segments */ for (; segments > 0; --segments ) { length = get(tdgbl); length |= get(tdgbl) << 8; if (length) { get_block(tdgbl, buffer, length); } if (isc_put_segment (status_vector, &blob, length, (SCHAR*) buffer)) BURP_error_redirect (status_vector, 38, 0, 0); /* msg 38 gds__put_segment failed */ } if (isc_close_blob (status_vector, &blob)) BURP_error_redirect (status_vector, 23, 0, 0); /* msg 23 gds__close_blob failed */ if (buffer != static_buffer) BURP_free (buffer); } void get_blr_blob (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. * **************************************/ ISC_STATUS_ARRAY status_vector; UCHAR *buffer, static_buffer [1024], *p; isc_tr_handle local_trans; TGBL tdgbl = GET_THREAD_DATA; ULONG length = (ULONG)get_numeric(); /* Create new blob */ FRBRD* blob = NULL; if (glb_trans && tdgbl->global_trans) local_trans = tdgbl->global_trans; else local_trans = gds_trans; if (isc_create_blob (status_vector, &DB, &local_trans, &blob, blob_id)) BURP_error_redirect (status_vector, 37, 0, 0); /* msg 37 gds__create_blob failed */ /* Allocate blob buffer is static buffer is too short */ /* 03 Jun 2003. Nickolay Samofatov. Workaround bug of GCC 3.2.X #11068. Original code was: if (!length || length + 1 <= (USHORT)(sizeof(static_buffer)) ) */ if (length + 1 <= sizeof(static_buffer) ) buffer = static_buffer; else buffer = BURP_alloc (length + 1); if (length) { p = get_block(tdgbl, buffer, length); /* Make sure it has an eoc */ if (p[-1] != blr_eoc) { p[0] = blr_eoc; length++; } } if (isc_put_segment (status_vector, &blob, length, (SCHAR*) buffer)) BURP_error_redirect (status_vector, 38, 0, 0); /* msg 38 gds__put_segment failed */ if (isc_close_blob (status_vector, &blob)) BURP_error_redirect (status_vector, 23, 0, 0); /* msg 23 gds__close_blob failed */ if (buffer != static_buffer) BURP_free (buffer); } bool get_character_set (void) { /************************************** * * g e t _ c h a r a c t e r _ s e t s * ************************************** * * Functional description * Restore data for user defined character sets * **************************************/ ATT_TYPE attribute; scan_attr_t scan_next_attr; TGBL tdgbl = GET_THREAD_DATA; 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.NULL = TRUE; 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, NULL, NULL, NULL, NULL); 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(); 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(); break; case att_charset_sysflag: X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); break; case att_charset_description: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (&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(); break; default: bad_attribute (scan_next_attr, attribute, msgErr_restore_charset); /* RDB$CHARSETS */ break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_chk_constraint (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; 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, 208); /* msg 208 relation constraint */ break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_collation (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; 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.NULL = TRUE; X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$FUNCTION_NAME.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, NULL, NULL, NULL, NULL); break; case att_coll_id: X.RDB$COLLATION_ID.NULL = FALSE; X.RDB$COLLATION_ID = (USHORT) get_numeric(); break; case att_coll_cs_id: X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(); break; case att_coll_attr: X.RDB$COLLATION_ATTRIBUTES.NULL = FALSE; X.RDB$COLLATION_ATTRIBUTES = (USHORT) get_numeric(); break; case att_coll_subtype: /* No longer used: 93-11-15 DBS */ /* still present to handle V4 R&D gbak files */ get_numeric(); break; case att_coll_sysflag: X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); break; case att_coll_description: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (&X.RDB$DESCRIPTION, false); break; case att_coll_funct: X.RDB$FUNCTION_NAME.NULL = FALSE; GET_TEXT(X.RDB$FUNCTION_NAME); break; default: bad_attribute (scan_next_attr, attribute, msgErr_restore_collation); /* Bad RDB$COLLATION */ break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } rec_type get_data (BURP_REL relation) { /************************************** * * g e t _ d a t a * ************************************** * * Functional description * Write data records for a relation. * **************************************/ BURP_FLD field; TEXT *p; USHORT l; SSHORT alignment, dtype; SSHORT *buffer; LSTRING data; SLONG *blob_id; isc_req_handle req_handle = NULL; BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name; TGBL tdgbl = GET_THREAD_DATA; /* If we're only doing meta-data, ignore data records */ if (tdgbl->gbl_sw_meta) return ignore_data (relation); /* Start by counting the interesting fields */ RCRD_OFFSET offset = 0; ULONG length = 0; USHORT count = 0; 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; stuff(&blr, blr_version4); stuff(&blr, blr_begin); stuff(&blr, blr_message); stuff(&blr, 0); /* Message number */ stuff_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 */ dtype = field->fld_type; length = field->fld_length; alignment = 4; if (field->fld_flags & FLD_array) dtype = blr_blob; if (dtype <= DTYPE_BLR_MAX) { 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) stuff(&blr, blr_text2); else stuff(&blr, blr_varying2); stuff_word(&blr, field->fld_character_set_id); stuff_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: stuff(&blr, field->fld_type); stuff(&blr, field->fld_scale); break; case blr_float: case blr_double: case blr_timestamp: case blr_sql_time: case blr_sql_date: stuff(&blr, field->fld_type); break; case blr_blob: alignment = type_alignments [dtype_blob]; length = type_lengths [dtype_blob]; stuff(&blr, blr_quad); stuff(&blr, 0); break; default: #ifdef SUPERSERVER BURP_svc_error (26, isc_arg_number, (void*) (SLONG) field->fld_type, 0, NULL, 0, NULL, 0, NULL, 0, NULL); #else BURP_error (26, (void*) (SLONG) field->fld_type, 0, 0, 0, 0); /* msg 26 datatype %ld not understood */ #endif 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; stuff(&blr, blr_short); stuff(&blr, 0); offset = FB_ALIGN(offset, sizeof(SSHORT)); field->fld_missing_parameter = count++; offset += sizeof(SSHORT); } length = offset; /* Build STORE statement */ stuff(&blr, blr_receive); stuff(&blr, 0); stuff(&blr, blr_store); stuff(&blr, blr_relation); stuff_string(&blr, relation->rel_name); stuff(&blr, 0); /* context variable */ stuff(&blr, blr_begin); for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; stuff(&blr, blr_assignment); if (tdgbl->RESTORE_format >= 2) { stuff(&blr, blr_parameter2); stuff(&blr, 0); stuff_word(&blr, field->fld_parameter); stuff_word(&blr, field->fld_missing_parameter); } else { stuff(&blr, blr_parameter); stuff(&blr, 0); stuff_word(&blr, field->fld_parameter); } stuff(&blr, blr_field); stuff(&blr, 0); stuff_string(&blr, field->fld_name); } stuff(&blr, blr_end); stuff(&blr, blr_end); stuff(&blr, blr_eoc); /* Compile request */ #ifdef DEBUG isc_print_blr (blr_buffer, NULL, NULL, 0); #endif FRBRD *request = NULL; 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, 0, 0); /* msg 27 gds__compile_request failed */ else { BURP_print_status (status_vector); BURP_free (blr_buffer); return ignore_data (relation); } } BURP_free (blr_buffer); buffer = NULL; BURP_verbose (124, relation->rel_name, NULL, NULL, NULL, NULL); /* msg 124 restoring data for relation %s */ 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, 0, 0); /* msg 39 expected record length */ l = (USHORT) get_numeric(); if (!tdgbl->gbl_sw_transportable && l != length) { if (!old_length) old_length = recompute_length (relation); if (l != old_length) { #ifdef SUPERSERVER BURP_svc_error(40, isc_arg_number, (void*)length, isc_arg_number, (void*) (ULONG) l, 0, NULL, 0, NULL, 0, NULL); #else BURP_error (40, (void*) length, (void*)(ULONG) l, 0, 0, 0); /* msg 40 wrong length record, expected %ld encountered %ld */ #endif } } if (!buffer) buffer = (SSHORT *) BURP_alloc (MAX (length, l)); if (tdgbl->gbl_sw_transportable) { if (get(tdgbl) != att_xdr_length) BURP_error_redirect (NULL, 55, 0, 0); /* msg 55 Expected XDR record length */ else { data.lstr_length = l = (USHORT) get_numeric(); if (l > data.lstr_allocated) { data.lstr_allocated = l; if (data.lstr_address) BURP_free (data.lstr_address); data.lstr_address = (UCHAR *) BURP_alloc (data.lstr_allocated); } p = (TEXT*) data.lstr_address; } } else p = (TEXT*) buffer; if (get(tdgbl) != att_data_data) BURP_error_redirect (NULL, 41, 0, 0); /* msg 41 expected data attribute */ if (tdgbl->gbl_sw_compress) decompress ((UCHAR*) p, l); else { get_block(tdgbl, (UCHAR*) p, l); } if (old_length) realign ((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, (void*) (SLONG) records, NULL, NULL, NULL, NULL); for (field = relation->rel_fields; field; field = field->fld_next) if ((field->fld_type == blr_blob) || (field->fld_flags & FLD_array)) { blob_id = (SLONG*) ((SCHAR*) buffer + field->fld_offset); blob_id[0] = blob_id[1] = 0; } get_record(&record, tdgbl); while (record == rec_blob || record == rec_array) { if (record == rec_blob) get_blob (relation->rel_fields, (UCHAR *) buffer); else if (record == rec_array) get_array (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] == gds_not_valid) { if (tdgbl->gbl_sw_incremental) { BURP_print (138, relation->rel_name, NULL, NULL, NULL, NULL); /* msg 138 validation error on field in relation %s */ BURP_print_status (status_vector); } else BURP_error_redirect (status_vector, 47, 0, 0); /* msg 47 warning -- record could not be restored */ } else { if (tdgbl->gbl_sw_incremental) { BURP_print (114, relation->rel_name, NULL, NULL, NULL, NULL); /* msg 114 restore failed for record in relation %s */ BURP_print_status (status_vector); } else BURP_error_redirect (status_vector, 48, 0, 0); /* msg 48 gds__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, NULL, NULL, NULL, NULL); /* 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 gds_sort_mem_err: case gds_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, NULL, NULL, NULL, NULL); /* msg 240 Index \"%s\" failed to activate because: */ if ( error_code == gds_no_dup ) { BURP_print(241, NULL, NULL, NULL, NULL, NULL); /* msg 241 The unique index has duplicate values or NULLs */ BURP_print(242, NULL, NULL, NULL, NULL, NULL); /* msg 242 Delete or Update duplicate values or NULLs, and activate index with */ } else { BURP_print(244, NULL, NULL, NULL, NULL, NULL); /* msg 244 Not enough disk space to create the sort file for an index */ BURP_print(245, NULL, NULL, NULL, NULL, NULL); /* 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, NULL, NULL, NULL, NULL); /* msg 243 ALTER INDEX \"%s\" ACTIVE; */ END_MODIFY; END_FOR; /* don't bring the database on-line */ flag_on_line = false; /* commit one more time */ COMMIT ON_ERROR continue; END_ERROR break; default: BURP_print (69, relation->rel_name, NULL, NULL, NULL, NULL); /* 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, (void*) (SLONG) records, NULL, NULL, NULL, NULL); /* msg 107 %ld records restored */ return record; } bool get_exception(void) { /************************************** * * 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 l; scan_attr_t scan_next_attr; TGBL tdgbl = GET_THREAD_DATA; STORE (REQUEST_HANDLE tdgbl->handles_get_exception_req_handle1) X IN RDB$EXCEPTIONS X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$MESSAGE.NULL = TRUE; SKIP_INIT(&scan_next_attr); while (SKIP_SCAN(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { switch (attribute) { case att_exception_name: l = GET_TEXT(X.RDB$EXCEPTION_NAME); MISC_terminate (X.RDB$EXCEPTION_NAME, temp, l, sizeof(temp)); BURP_verbose (199, temp, NULL, NULL, NULL, NULL); /* msg 199 restoring exception %s */ break; case att_exception_description: get_misc_blob (&X.RDB$DESCRIPTION, 0, false); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_exception_description2: get_source_blob (&X.RDB$DESCRIPTION, false); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_exception_msg: GET_TEXT(X.RDB$MESSAGE); X.RDB$MESSAGE.NULL = FALSE; break; default: bad_attribute (scan_next_attr, attribute, 89); /* msg 89 function */ break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool cvt_v3_to_v4_intl (SSHORT dtype, SSHORT *scale, SSHORT *sub_type, SSHORT *character_set_id, SSHORT *collation_id) { /************************************** * * c v t _ v 3 _ t o _ v 4 _ i n t l * ************************************** * * Functional description * Convert a V3 scale / sub_type to V4 character_set / collate_sequence * * Version 3.2 & 3.3 of InterBase used different methodologies * to tag international text character sets and country collation * sequences. This routine will convert a V3 metadata definition * into the corresponding V4 definition. * * In V3.x, sub-type 0 is normal text, subtype 1 is "text that can * contain a NULL byte, and can therefore not be represented * using a c-string datatype, always use fixed". * * Return true if a conversion was performed, FALSE otherwise. * **************************************/ SSHORT i; switch (dtype) { case blr_text2: case blr_varying2: case blr_cstring2: case blr_text: case blr_varying: case blr_cstring: /* Japanese V3.2 used SCALE 5 to indicate SJIS text */ if (*scale == 5 && ((*sub_type == 0) || (*sub_type == 1))) { *character_set_id = CS_JIS_0208; *collation_id = 0; BURP_verbose (237, (void*) (SLONG) *scale, (void*) (SLONG) *character_set_id, (void*) (SLONG) *collation_id, NULL, NULL); /* msg 237: Converted V3 scale: %d to character_set_id: %d and callate_id: %d. */ *scale = 0; return true; } /* Japanese V3.2 used SCALE 6 to indicate EUCJ text */ if (*scale == 6 && (*sub_type == 0 || *sub_type == 1)) { *character_set_id = CS_EUCJ; *collation_id = 0; BURP_verbose (237, (void*) (SLONG) *scale, (void*) (SLONG) *character_set_id, (void*) (SLONG) *collation_id, NULL, NULL); /* msg 237: Converted V3 scale: %d to character_set_id: %d and callate_id: %d. */ *scale = 0; return true; } /* V3.3 used SCALE 0 and Subtypes in range 100 - 255 for European text types and collations */ if (*scale != 0) return false; if (*sub_type == 0 || *sub_type == 1) /* Do not process sub_type 0,1 conversion */ return false; for (i = 0; i < cvtbl_len; i ++) if (sub_type_cvtbl [i].sub_type == *sub_type) { *character_set_id = sub_type_cvtbl [i].character_set_id; *collation_id = sub_type_cvtbl [i].collation_id; BURP_verbose (236, (void*) (SLONG) *sub_type, (void*) (SLONG) *character_set_id, (void*) (SLONG) *collation_id, NULL, NULL); /* msg 236: Converted V3 sub_type: %d to character_set_id: %d and callate_id: %d. */ *sub_type = 0; return true; } return false; default: /* Do not process sub_type conversion on non-text field */ return false; } } BURP_FLD get_field (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; isc_tr_handle local_trans; TGBL tdgbl = GET_THREAD_DATA; // If it is a view and there is a global transaction then use it bool global_tr = false; 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.NULL = TRUE; 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, NULL, NULL, NULL, NULL); // 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); 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 (&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(); break; case att_field_number: field->fld_number = (USHORT) get_numeric(); break; case att_field_type: field->fld_type = (USHORT) get_numeric(); break; case att_field_length: field->fld_length = (USHORT) get_numeric(); break; case att_field_scale: field->fld_scale = (USHORT) get_numeric(); break; case att_field_sub_type: field->fld_sub_type = (USHORT) get_numeric(); break; case att_field_system_flag: X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); X.RDB$SYSTEM_FLAG.NULL = FALSE; break; case att_view_context: X.RDB$VIEW_CONTEXT = (USHORT) get_numeric(); X.RDB$VIEW_CONTEXT.NULL = FALSE; break; case att_field_computed_flag: if (get_numeric()) 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 (&X.RDB$DESCRIPTION, 1, global_tr); break; case att_field_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (&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(); 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(); if (get_attribute(&attribute, tdgbl) != att_field_range_high) bad_attribute (scan_next_attr, attribute, 58); // msg 58 array else *(rp + 1) = get_numeric(); } } break; case att_field_update_flag: X.RDB$UPDATE_FLAG.NULL = FALSE; X.RDB$UPDATE_FLAG = (USHORT) get_numeric(); break; case att_field_character_length: field->fld_character_length = (USHORT) get_numeric(); break; case att_field_default_source: X.RDB$DEFAULT_SOURCE.NULL = FALSE; get_source_blob (&X.RDB$DEFAULT_SOURCE, global_tr); break; case att_field_default_value: X.RDB$DEFAULT_VALUE.NULL = FALSE; get_blr_blob (&X.RDB$DEFAULT_VALUE, global_tr); break; case att_field_null_flag: X.RDB$NULL_FLAG.NULL = FALSE; X.RDB$NULL_FLAG = (USHORT) get_numeric(); break; case att_field_character_set: field->fld_character_set_id = (USHORT) get_numeric(); break; case att_field_collation_id: field->fld_collation_id = (USHORT) get_numeric(); 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 field break; } } /* For migration from V3.3 to V4.0 format of International text * information - search the list of global fields which were * remapped from V3.3 format into V4.0 format. If we find that * this local field's source is one of those global fields, then * remap the local field's information. This is used to compose * the BLR which sends data to the newly restored database. */ for (BURP_FLD f = tdgbl->v3_cvt_fld_list; f; f = f->fld_next) { if (!strcmp(X.RDB$FIELD_SOURCE, f->fld_name)) { field->fld_sub_type = f->fld_sub_type; field->fld_scale = f->fld_scale; field->fld_character_set_id = f->fld_character_set_id; field->fld_collation_id = f->fld_collation_id; break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return field; } bool get_field_dimensions (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; 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(); break; case att_field_range_low: X.RDB$LOWER_BOUND = get_numeric(); break; case att_field_range_high: X.RDB$UPPER_BOUND = get_numeric(); break; default: bad_attribute (scan_next_attr, attribute, 58); /* msg 58 array */ break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_files (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; 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, /* msg 116 restoring file %s */ X.RDB$FILE_NAME, NULL, NULL, NULL, NULL); break; case att_file_sequence: X.RDB$FILE_SEQUENCE = (USHORT) get_numeric(); break; case att_file_start: X.RDB$FILE_START = get_numeric(); break; case att_file_length: X.RDB$FILE_LENGTH = get_numeric(); break; case att_file_flags: X.RDB$FILE_FLAGS |= get_numeric(); break; case att_shadow_number: X.RDB$SHADOW_NUMBER = (USHORT) get_numeric(); 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 (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; STORE (REQUEST_HANDLE tdgbl->handles_get_filter_req_handle1) X IN RDB$FILTERS 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_filter_name: GET_TEXT(X.RDB$FUNCTION_NAME); BURP_verbose (117, X.RDB$FUNCTION_NAME, NULL, NULL, NULL, NULL); /* msg 117 restoring filter %s */ break; case att_filter_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (&X.RDB$DESCRIPTION, 1, false); break; case att_filter_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (&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(); break; case att_filter_output_sub_type: X.RDB$OUTPUT_SUB_TYPE = (USHORT) get_numeric(); 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 (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; STORE (REQUEST_HANDLE tdgbl->handles_get_function_req_handle1) X IN RDB$FUNCTIONS 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, NULL, NULL, NULL, NULL); /* msg 118 restoring function %s */ break; case att_function_description: get_misc_blob (&X.RDB$DESCRIPTION, 0, false); break; case att_function_description2: get_source_blob (&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(); break; case att_function_query_name: GET_TEXT(X.RDB$QUERY_NAME); break; case att_function_type: X.RDB$FUNCTION_TYPE = (USHORT) get_numeric(); break; default: bad_attribute (scan_next_attr, attribute, 89); /* msg 89 function */ break; } } strcpy (function_name, X.RDB$FUNCTION_NAME); END_STORE; ON_ERROR general_on_error (); 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(); return true; } void get_function_arg() { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; if (tdgbl->RESTORE_format >= 6) { /* 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, NULL, NULL, NULL, NULL); /* msg 119 restoring argument for function %s */ break; case att_functionarg_position: X.RDB$ARGUMENT_POSITION = (USHORT) get_numeric(); break; case att_functionarg_mechanism: X.RDB$MECHANISM = (USHORT) get_numeric(); break; case att_functionarg_field_type: X.RDB$FIELD_TYPE = (USHORT) get_numeric(); break; case att_functionarg_field_scale: X.RDB$FIELD_SCALE = (USHORT) get_numeric(); break; case att_functionarg_field_length: X.RDB$FIELD_LENGTH = (USHORT) get_numeric(); break; case att_functionarg_field_sub_type: X.RDB$FIELD_SUB_TYPE.NULL = FALSE; X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(); break; case att_functionarg_character_set: X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(); break; case att_functionarg_field_precision: X.RDB$FIELD_PRECISION.NULL = FALSE; X.RDB$FIELD_PRECISION = (USHORT) get_numeric(); 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, NULL, NULL, NULL, NULL); /* msg 119 restoring argument for function %s */ break; case att_functionarg_position: X.RDB$ARGUMENT_POSITION = (USHORT) get_numeric(); break; case att_functionarg_mechanism: X.RDB$MECHANISM = (USHORT) get_numeric(); break; case att_functionarg_field_type: X.RDB$FIELD_TYPE = (USHORT) get_numeric(); break; case att_functionarg_field_scale: X.RDB$FIELD_SCALE = (USHORT) get_numeric(); break; case att_functionarg_field_length: X.RDB$FIELD_LENGTH = (USHORT) get_numeric(); break; case att_functionarg_field_sub_type: X.RDB$FIELD_SUB_TYPE.NULL = FALSE; X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(); break; case att_functionarg_character_set: X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(); break; 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 (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; SKIP_INIT(&scan_next_attr); 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(); break; case att_gen_value_int64: // IB v6 or later, gen_id value is an SINT64 value = get_int64(); break; default: bad_attribute (scan_next_attr, attribute, 93); // msg 93 index break; } } store_blr_gen_id (name, value); return true; } bool get_global_field (void) { /************************************** * * 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; SSHORT save_subtype; TGBL tdgbl = GET_THREAD_DATA; GFLD gfield = NULL; if (tdgbl->RESTORE_format >= 6) { /* 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.NULL = TRUE; 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, NULL, NULL, NULL, NULL); /* 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 (&X.RDB$QUERY_HEADER, false); break; case att_field_type: X.RDB$FIELD_TYPE = (USHORT) get_numeric(); break; case att_field_length: X.RDB$FIELD_LENGTH = (USHORT) get_numeric(); break; case att_field_scale: X.RDB$FIELD_SCALE = (USHORT) get_numeric(); X.RDB$FIELD_SCALE.NULL = FALSE; break; case att_field_sub_type: X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(); X.RDB$FIELD_SUB_TYPE.NULL = FALSE; break; case att_field_segment_length: X.RDB$SEGMENT_LENGTH = (USHORT) get_numeric(); if (X.RDB$SEGMENT_LENGTH) X.RDB$SEGMENT_LENGTH.NULL = FALSE; break; case att_field_computed_blr: X.RDB$COMPUTED_BLR.NULL = FALSE; get_blr_blob (&X.RDB$COMPUTED_BLR, false); break; case att_field_computed_source: X.RDB$COMPUTED_SOURCE.NULL = FALSE; get_misc_blob (&X.RDB$COMPUTED_SOURCE, 1, false); break; case att_field_computed_source2: X.RDB$COMPUTED_SOURCE.NULL = FALSE; get_source_blob (&X.RDB$COMPUTED_SOURCE, false); break; case att_field_validation_blr: if (tdgbl->gbl_sw_novalidity) eat_blob(); 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 (&gfield->gfld_vb, true); gfield->gfld_flags |= GFLD_validation_blr; } else { X.RDB$VALIDATION_BLR.NULL = FALSE; get_blr_blob (&X.RDB$VALIDATION_BLR, false); } } break; case att_field_validation_source: if (tdgbl->gbl_sw_novalidity) eat_blob(); 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 (&gfield->gfld_vs, 0, true); gfield->gfld_flags |= GFLD_validation_source; } else { X.RDB$VALIDATION_SOURCE.NULL = FALSE; get_misc_blob (&X.RDB$VALIDATION_SOURCE, 0, false); } } break; case att_field_validation_source2: if (tdgbl->gbl_sw_novalidity) eat_blob(); 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 (&gfield->gfld_vs2, true); gfield->gfld_flags |= GFLD_validation_source2; } else { X.RDB$VALIDATION_SOURCE.NULL = FALSE; get_source_blob (&X.RDB$VALIDATION_SOURCE, false); } } break; case att_field_missing_value: X.RDB$MISSING_VALUE.NULL = FALSE; get_blr_blob (&X.RDB$MISSING_VALUE, false); break; case att_field_default_value: X.RDB$DEFAULT_VALUE.NULL = FALSE; get_blr_blob (&X.RDB$DEFAULT_VALUE, false); break; case att_field_system_flag: X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); X.RDB$SYSTEM_FLAG.NULL = FALSE; break; case att_field_null_flag: X.RDB$NULL_FLAG = (USHORT) get_numeric(); X.RDB$NULL_FLAG.NULL = FALSE; break; case att_field_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (&X.RDB$DESCRIPTION, 1, false); break; case att_field_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (&X.RDB$DESCRIPTION, false); break; case att_field_external_length: X.RDB$EXTERNAL_LENGTH.NULL = FALSE; X.RDB$EXTERNAL_LENGTH = (USHORT) get_numeric(); break; case att_field_external_scale: X.RDB$EXTERNAL_SCALE.NULL = FALSE; X.RDB$EXTERNAL_SCALE = (USHORT) get_numeric(); break; case att_field_external_type: X.RDB$EXTERNAL_TYPE.NULL = FALSE; X.RDB$EXTERNAL_TYPE = (USHORT) get_numeric(); break; case att_field_dimensions: X.RDB$DIMENSIONS.NULL = FALSE; X.RDB$DIMENSIONS = (USHORT) get_numeric(); break; case att_field_character_length: X.RDB$CHARACTER_LENGTH.NULL = FALSE; X.RDB$CHARACTER_LENGTH = (USHORT) get_numeric(); break; case att_field_default_source: X.RDB$DEFAULT_SOURCE.NULL = FALSE; get_source_blob (&X.RDB$DEFAULT_SOURCE, false); break; case att_field_missing_source: X.RDB$MISSING_SOURCE.NULL = FALSE; get_source_blob (&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(); break; case att_field_collation_id: X.RDB$COLLATION_ID.NULL = FALSE; X.RDB$COLLATION_ID = (USHORT) get_numeric(); break; case att_field_precision: X.RDB$FIELD_PRECISION.NULL = FALSE; X.RDB$FIELD_PRECISION = (USHORT) get_numeric(); break; default: bad_attribute (scan_next_attr, attribute, 92); /* msg 92 global field */ 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); /* V3.3 used a different method from V4.0 for encoding International * text character set & collation information. Here we are * looking at the metadata information, deciding if it is * the V3.3 method, and converting it to the V4.0 method so * V3.3 customers can migrate their database to V4.0. */ save_subtype = X.RDB$FIELD_SUB_TYPE; if (X.RDB$CHARACTER_SET_ID.NULL && X.RDB$COLLATION_ID.NULL && cvt_v3_to_v4_intl (X.RDB$FIELD_TYPE, &X.RDB$FIELD_SCALE, &X.RDB$FIELD_SUB_TYPE, &X.RDB$CHARACTER_SET_ID, &X.RDB$COLLATION_ID)) { BURP_FLD f; /* If some value was reset, set the NULL flag */ if (save_subtype && !X.RDB$FIELD_SUB_TYPE) X.RDB$FIELD_SUB_TYPE.NULL = TRUE; X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$COLLATION_ID.NULL = FALSE; /* Add an entry to the converted-field link list * so we can also convert local fields based on this * global field */ f = (BURP_FLD) BURP_alloc_zero (sizeof(burp_fld)); strcpy (f->fld_name, X.RDB$FIELD_NAME); f->fld_sub_type = X.RDB$FIELD_SUB_TYPE; f->fld_scale = X.RDB$FIELD_SCALE; f->fld_character_set_id = X.RDB$CHARACTER_SET_ID; f->fld_collation_id = X.RDB$COLLATION_ID; f->fld_next = tdgbl->v3_cvt_fld_list; tdgbl->v3_cvt_fld_list = f; } END_STORE; ON_ERROR general_on_error (); END_ERROR; } else /* RESTORE_format < 6 */ { /* 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.NULL = TRUE; 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, NULL, NULL, NULL, NULL); /* 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 (&X.RDB$QUERY_HEADER, false); break; case att_field_type: X.RDB$FIELD_TYPE = (USHORT) get_numeric(); break; case att_field_length: X.RDB$FIELD_LENGTH = (USHORT) get_numeric(); break; case att_field_scale: X.RDB$FIELD_SCALE = (USHORT) get_numeric(); X.RDB$FIELD_SCALE.NULL = FALSE; break; case att_field_sub_type: X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(); X.RDB$FIELD_SUB_TYPE.NULL = FALSE; break; case att_field_segment_length: X.RDB$SEGMENT_LENGTH = (USHORT) get_numeric(); if (X.RDB$SEGMENT_LENGTH) X.RDB$SEGMENT_LENGTH.NULL = FALSE; break; case att_field_computed_blr: X.RDB$COMPUTED_BLR.NULL = FALSE; get_blr_blob (&X.RDB$COMPUTED_BLR, false); break; case att_field_computed_source: X.RDB$COMPUTED_SOURCE.NULL = FALSE; get_misc_blob (&X.RDB$COMPUTED_SOURCE, 1, false); break; case att_field_computed_source2: X.RDB$COMPUTED_SOURCE.NULL = FALSE; get_source_blob (&X.RDB$COMPUTED_SOURCE, false); break; case att_field_validation_blr: if (tdgbl->gbl_sw_novalidity) eat_blob(); 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 (&gfield->gfld_vb, true); gfield->gfld_flags |= GFLD_validation_blr; } else { X.RDB$VALIDATION_BLR.NULL = FALSE; get_blr_blob (&X.RDB$VALIDATION_BLR, false); } } break; case att_field_validation_source: if (tdgbl->gbl_sw_novalidity) eat_blob(); 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 (&gfield->gfld_vs, 0, true); gfield->gfld_flags |= GFLD_validation_source; } else { X.RDB$VALIDATION_SOURCE.NULL = FALSE; get_misc_blob (&X.RDB$VALIDATION_SOURCE, 0, false); } } break; case att_field_validation_source2: if (tdgbl->gbl_sw_novalidity) eat_blob(); 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 (&gfield->gfld_vs2, true); gfield->gfld_flags |= GFLD_validation_source2; } else { X.RDB$VALIDATION_SOURCE.NULL = FALSE; get_source_blob (&X.RDB$VALIDATION_SOURCE, false); } } break; case att_field_missing_value: X.RDB$MISSING_VALUE.NULL = FALSE; get_blr_blob (&X.RDB$MISSING_VALUE, false); break; case att_field_default_value: X.RDB$DEFAULT_VALUE.NULL = FALSE; get_blr_blob (&X.RDB$DEFAULT_VALUE, false); break; case att_field_system_flag: X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); X.RDB$SYSTEM_FLAG.NULL = FALSE; break; case att_field_null_flag: X.RDB$NULL_FLAG = (USHORT) get_numeric(); X.RDB$NULL_FLAG.NULL = FALSE; break; case att_field_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (&X.RDB$DESCRIPTION, 1, false); break; case att_field_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (&X.RDB$DESCRIPTION, false); break; case att_field_external_length: X.RDB$EXTERNAL_LENGTH.NULL = FALSE; X.RDB$EXTERNAL_LENGTH = (USHORT) get_numeric(); break; case att_field_external_scale: X.RDB$EXTERNAL_SCALE.NULL = FALSE; X.RDB$EXTERNAL_SCALE = (USHORT) get_numeric(); break; case att_field_external_type: X.RDB$EXTERNAL_TYPE.NULL = FALSE; X.RDB$EXTERNAL_TYPE = (USHORT) get_numeric(); break; case att_field_dimensions: X.RDB$DIMENSIONS.NULL = FALSE; X.RDB$DIMENSIONS = (USHORT) get_numeric(); break; case att_field_character_length: X.RDB$CHARACTER_LENGTH.NULL = FALSE; X.RDB$CHARACTER_LENGTH = (USHORT) get_numeric(); break; case att_field_default_source: X.RDB$DEFAULT_SOURCE.NULL = FALSE; get_source_blob (&X.RDB$DEFAULT_SOURCE, false); break; case att_field_missing_source: X.RDB$MISSING_SOURCE.NULL = FALSE; get_source_blob (&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(); break; case att_field_collation_id: X.RDB$COLLATION_ID.NULL = FALSE; X.RDB$COLLATION_ID = (USHORT) get_numeric(); break; default: bad_attribute (scan_next_attr, attribute, 92); /* msg 92 global field */ 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); /* V3.3 used a different method from V4.0 for encoding International * text character set & collation information. Here we are * looking at the metadata information, deciding if it is * the V3.3 method, and converting it to the V4.0 method so * V3.3 customers can migrate their database to V4.0. */ save_subtype = X.RDB$FIELD_SUB_TYPE; if (X.RDB$CHARACTER_SET_ID.NULL && X.RDB$COLLATION_ID.NULL && cvt_v3_to_v4_intl (X.RDB$FIELD_TYPE, &X.RDB$FIELD_SCALE, &X.RDB$FIELD_SUB_TYPE, &X.RDB$CHARACTER_SET_ID, &X.RDB$COLLATION_ID)) { /* If some value was reset, set the NULL flag */ if (save_subtype && !X.RDB$FIELD_SUB_TYPE) X.RDB$FIELD_SUB_TYPE.NULL = TRUE; X.RDB$CHARACTER_SET_ID.NULL = FALSE; X.RDB$COLLATION_ID.NULL = FALSE; /* Add an entry to the converted-field link list * so we can also convert local fields based on this * global field */ BURP_FLD f = (BURP_FLD) BURP_alloc_zero (sizeof(burp_fld)); strcpy (f->fld_name, X.RDB$FIELD_NAME); f->fld_sub_type = X.RDB$FIELD_SUB_TYPE; f->fld_scale = X.RDB$FIELD_SCALE; f->fld_character_set_id = X.RDB$CHARACTER_SET_ID; f->fld_collation_id = X.RDB$COLLATION_ID; f->fld_next = tdgbl->v3_cvt_fld_list; tdgbl->v3_cvt_fld_list = f; } 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 (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; TGBL tdgbl = GET_THREAD_DATA; 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; 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, NULL, NULL, NULL, NULL); break; case att_segment_count: X.RDB$SEGMENT_COUNT = segments = (USHORT) get_numeric(); break; case att_index_unique_flag: X.RDB$UNIQUE_FLAG = (USHORT) get_numeric(); break; case att_index_inactive: X.RDB$INDEX_INACTIVE = (USHORT) get_numeric(); /* Defer foreign key index activation */ /* Modified by Toni Martir, all index deferred when verbose */ if (tdgbl->gbl_sw_verbose) { if (X.RDB$INDEX_INACTIVE == FALSE) X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE; } else { if (X.RDB$INDEX_INACTIVE == FALSE && 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(); 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 (&X.RDB$DESCRIPTION, 0, false); break; case att_index_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (&X.RDB$DESCRIPTION, false); break; case att_index_expression_source: X.RDB$EXPRESSION_SOURCE.NULL = FALSE; get_source_blob (&X.RDB$EXPRESSION_SOURCE, false); break; case att_index_expression_blr: X.RDB$EXPRESSION_BLR.NULL = FALSE; get_blr_blob (&X.RDB$EXPRESSION_BLR, false); break; case att_index_foreign_key: foreign_index = true; /* Defer foreign key index activation */ if (X.RDB$INDEX_INACTIVE == FALSE) 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 (ISC_QUAD *blob_id, USHORT sub_type, 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; UCHAR static_buffer [1024]; isc_tr_handle local_trans; TGBL tdgbl = GET_THREAD_DATA; USHORT length = (USHORT)get_numeric(); // Create new blob FRBRD *blob = NULL; USHORT bpb_length = 0; UCHAR *bpb = NULL; if (glb_trans && tdgbl->global_trans) local_trans = tdgbl->global_trans; else local_trans = gds_trans; if (isc_create_blob2 (status_vector, &DB, &local_trans, &blob, blob_id, bpb_length, (SCHAR*) bpb)) { BURP_error_redirect (status_vector, 37, 0, 0); /* msg 37 gds__create_blob failed */ } /* Allocate blob buffer is static buffer is too short */ UCHAR *buffer; if (!length || length <= (USHORT)(sizeof(static_buffer)) ) buffer = static_buffer; else buffer = BURP_alloc (length); if (length) { get_block(tdgbl, buffer, length); } if (isc_put_segment (status_vector, &blob, length, (SCHAR*) buffer)) BURP_error_redirect (status_vector, 38, 0, 0); /* msg 38 gds__put_segment failed */ if (isc_close_blob (status_vector, &blob)) BURP_error_redirect (status_vector, 23, 0, 0); /* msg 23 gds__close_blob failed */ if (buffer != static_buffer) BURP_free (buffer); } SLONG get_numeric (void) { /************************************** * * 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. assert(sizeof(value) > sizeof(SLONG)); SSHORT length = get_text((TEXT*) value, sizeof(value)); return isc_vax_integer ((SCHAR *) value, length); } SINT64 get_int64 (void) { /************************************** * * 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. assert (sizeof(value) > sizeof(SINT64)); SSHORT length = get_text ((TEXT *) value, sizeof(value)); return isc_portable_integer ((UCHAR *) value, length); } bool get_procedure (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; 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; 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; 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, NULL, NULL, NULL, NULL); /* msg 195 restoring stored procedure %s */ break; case att_procedure_description: get_misc_blob (&X.RDB$DESCRIPTION, 0, true); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_procedure_description2: get_source_blob (&X.RDB$DESCRIPTION, true); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_procedure_source: get_misc_blob (&X.RDB$PROCEDURE_SOURCE, 0, true); X.RDB$PROCEDURE_SOURCE.NULL = FALSE; break; case att_procedure_source2: get_source_blob (&X.RDB$PROCEDURE_SOURCE, true); X.RDB$PROCEDURE_SOURCE.NULL = FALSE; break; case att_procedure_blr: get_blr_blob (&X.RDB$PROCEDURE_BLR, true); break; case att_procedure_security_class: GET_TEXT(X.RDB$SECURITY_CLASS); 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(); 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(); break; default: bad_attribute (scan_next_attr, attribute, 89); /* msg 89 function */ 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 (procedure_name); return true; } bool get_procedure_prm (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; TGBL tdgbl = GET_THREAD_DATA; local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; 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); 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, NULL, NULL, NULL, NULL); /* msg 196 restoring parameter %s for stored procedure */ break; case att_procedureprm_type: X.RDB$PARAMETER_TYPE= (USHORT) get_numeric(); break; case att_procedureprm_number: X.RDB$PARAMETER_NUMBER= (USHORT) get_numeric(); break; case att_procedureprm_field_source: GET_TEXT(X.RDB$FIELD_SOURCE); break; case att_procedureprm_description: get_misc_blob (&X.RDB$DESCRIPTION, 0, true); X.RDB$DESCRIPTION.NULL = FALSE; break; case att_procedureprm_description2: get_source_blob (&X.RDB$DESCRIPTION, true); X.RDB$DESCRIPTION.NULL = FALSE; break; default: bad_attribute (scan_next_attr, attribute, 90); /* msg 90 function argument */ break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_ref_constraint (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; 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, 208); /* msg 208 relation constraint */ break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_relation (void) { /************************************** * * 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; ATT_TYPE attribute; rec_type record; scan_attr_t scan_next_attr; SLONG rel_flags = 0, sys_flag = 0; bool rel_flags_null = true, sys_flag_null = true; GDS_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; isc_tr_handle local_trans; TGBL tdgbl = GET_THREAD_DATA; /* 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.NULL = TRUE; 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; */ 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, NULL, NULL, NULL, NULL); /* msg 167 restoring relation %s */ break; case att_relation_security_class: sec_class_null = false; GET_TEXT(sec_class); break; case att_relation_view_blr: view_blr_null = false; get_blr_blob(&view_blr, true); relation->rel_flags |= REL_view; break; case att_relation_view_source: view_src_null = false; get_misc_blob (&view_src, 1, !view_blr_null); break; case att_relation_view_source2: view_src_null = false; get_source_blob(&view_src, !view_blr_null); break; case att_relation_description: rel_desc_null = false; get_misc_blob(&rel_desc, 1, !view_blr_null); break; case att_relation_description2: rel_desc_null = false; get_source_blob(&rel_desc, !view_blr_null); break; case att_relation_flags: rel_flags_null = false; rel_flags = get_numeric(); break; case att_relation_system_flag: sys_flag_null = false; sys_flag = get_numeric(); break; case att_relation_ext_description: ext_desc_null = false; get_misc_blob(&ext_desc, 1, !view_blr_null); break; case att_relation_ext_description2: ext_desc_null = false; get_source_blob(&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; default: bad_attribute (scan_next_attr, attribute, 111); /* msg 111 relation */ break; } } /* If this is a view and there is a global transaction then use it */ if (view_blr_null || !tdgbl->global_trans) local_trans = gds_trans; else local_trans = tdgbl->global_trans; STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1) X IN RDB$RELATIONS X.RDB$SYSTEM_FLAG.NULL = sys_flag_null; 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; while (get_record(&record, tdgbl) != rec_data) { switch (record) { case rec_relation_end: if (tdgbl->gbl_sw_incremental) { BURP_verbose (170, relation->rel_name, NULL, NULL, NULL, NULL); /* msg 170: committing metadata for relation %s */ COMMIT /* existing ON_ERROR continues past error, beck */ ON_ERROR BURP_print (171, relation->rel_name, NULL, NULL, NULL, NULL); /* 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 (relation); if (!field) return false; ptr = &field->fld_next; break; case rec_view: get_view (relation); break; default: #ifdef SUPERSERVER BURP_svc_error (43, isc_arg_number, (void*) record, 0, NULL, 0, NULL, 0, NULL, 0, NULL); #else BURP_error (43, (void*) record, 0, 0, 0, 0); /* msg 43 don't recognize record type %ld */ #endif 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, NULL, NULL, NULL, NULL, NULL); /* 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 (relation); return true; } bool get_rel_constraint (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; 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 relation constraint */ break; } } END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_relation_data (void) { /************************************** * * 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. * **************************************/ BURP_REL relation; BASED_ON RDB$RELATIONS.RDB$RELATION_NAME name; SLONG gen_id; ATT_TYPE attribute; rec_type record; scan_attr_t scan_next_attr; TGBL tdgbl = GET_THREAD_DATA; 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 (name); break; default: bad_attribute (scan_next_attr, attribute, 111); /* msg 111 relation */ break; } } if (!relation) BURP_error_redirect (NULL, 49, 0, 0); /* msg 49 no relation name for data */ /* Eat up misc. records */ get_record(&record, tdgbl); 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 (relation); /* get_data does a GET_RECORD */ break; case rec_gen_id: gen_id = get_numeric(); store_blr_gen_id (name, gen_id); get_record(&record, tdgbl); break; case rec_index: get_index (relation); get_record(&record, tdgbl); break; case rec_trigger: /* old style trigger */ get_trigger_old (relation); get_record(&record, tdgbl); break; default: bad_attribute (scan_next_attr, attribute, 111); /* msg 111 relation */ get_record(&record, tdgbl); break; } } } bool get_sql_roles (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; 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; 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, NULL, NULL, NULL, NULL); break; case att_role_owner_name: X.RDB$OWNER_NAME.NULL = FALSE; GET_TEXT(X.RDB$OWNER_NAME); break; default: /************************************************* ** ** msg 250, Bad attribute for restoring SQL role ** **************************************************/ bad_attribute (scan_next_attr, attribute, 250); break; } } END_STORE ON_ERROR general_on_error (); END_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 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 (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; STORE (REQUEST_HANDLE tdgbl->handles_get_security_class_req_handle1) X IN RDB$SECURITY_CLASSES SKIP_INIT(&scan_next_attr); while (SKIP_SCAN(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end) { X.RDB$DESCRIPTION.NULL = TRUE; 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, NULL, NULL, NULL, NULL); /* msg 234 Skipped bad security class entry: %s */ break; } MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp)); BURP_verbose (125, temp, NULL, NULL, NULL, NULL); /* msg 125 restoring security class %s */ break; case att_class_acl: get_misc_blob (&X.RDB$ACL, 0, false); break; case att_class_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (&X.RDB$DESCRIPTION, 0, false); break; case att_class_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (&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 ( 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; UCHAR static_buffer [1024]; isc_tr_handle local_trans; TGBL tdgbl = GET_THREAD_DATA; SLONG length = get_numeric(); // Create new blob FRBRD *blob = NULL; if (glb_trans && tdgbl->global_trans) local_trans = tdgbl->global_trans; else local_trans = gds_trans; if (isc_create_blob (status_vector, &DB, &local_trans, &blob, blob_id)) { BURP_error_redirect (status_vector, 37, 0, 0); /* msg 37 gds__create_blob failed */ } /* Allocate blob buffer is static buffer is too short */ UCHAR *buffer; if (!length || length <= (USHORT)(sizeof(static_buffer)) ) buffer = static_buffer; else buffer = BURP_alloc (length); while (length) { UCHAR *p = buffer; while (*p++ = get(tdgbl)) length--; --p; --length; USHORT seg_len = p - buffer; if (isc_put_segment (status_vector, &blob, seg_len, (SCHAR*) buffer)) BURP_error_redirect (status_vector, 38, 0, 0); /* msg 38 gds__put_segment failed */ } if (isc_close_blob (status_vector, &blob)) BURP_error_redirect (status_vector, 23, 0, 0); /* msg 23 gds__close_blob failed */ if (buffer != static_buffer) BURP_free (buffer); } USHORT get_text ( TEXT *text, ULONG length) { /************************************** * * g e t _ t e x t * ************************************** * * Functional description * Move a text attribute to a string and fill. * **************************************/ TGBL tdgbl = GET_THREAD_DATA; ULONG l = get(tdgbl); if (length <= l) BURP_error_redirect (NULL, 46, 0, 0); /* msg 46 string truncated */ if (l) text = (TEXT*) get_block(tdgbl, (UCHAR*) text, l); *text = 0; return (USHORT) l; } bool get_trigger_old ( 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 *q, *p, *end, name [GDS_NAME_LEN]; scan_attr_t scan_next_attr; TGBL tdgbl = GET_THREAD_DATA; 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; 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(); break; case att_trig_blr: X.RDB$TRIGGER_BLR.NULL = FALSE; get_blr_blob (&X.RDB$TRIGGER_BLR, false); break; case att_trig_source: X.RDB$TRIGGER_SOURCE.NULL = FALSE; get_misc_blob (&X.RDB$TRIGGER_SOURCE, 1, false); break; case att_trig_source2: X.RDB$TRIGGER_SOURCE.NULL = FALSE; get_source_blob (&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 */ p = X.RDB$TRIGGER_NAME; end = p + 31; q = relation->rel_name; while (*q) *p++ = *q++; if (type == trig_pre_store) { X.RDB$TRIGGER_TYPE = TRIG_TYPE_PRE_STORE; q = "$STORE"; } else if (type == trig_pre_modify) { X.RDB$TRIGGER_TYPE = TRIG_TYPE_PRE_MODIFY; q = "$MODIFY"; } else if (type == trig_post_erase) { X.RDB$TRIGGER_TYPE = TRIG_TYPE_POST_ERASE; q = "$ERASE"; } else { 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, NULL, NULL, NULL, NULL); /* 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; X.RDB$SYSTEM_FLAG = 0; /* restore as vanilla user type */ 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, NULL, NULL, NULL, NULL); /* 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 (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans; STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_trigger_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.NULL = TRUE; 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(); break; case att_trig_flags: X.RDB$FLAGS = (USHORT) get_numeric(); X.RDB$FLAGS.NULL = FALSE; break; case att_trig_blr: X.RDB$TRIGGER_BLR.NULL = FALSE; get_blr_blob (&X.RDB$TRIGGER_BLR, true); break; case att_trig_source: X.RDB$TRIGGER_SOURCE.NULL = FALSE; get_misc_blob (&X.RDB$TRIGGER_SOURCE, 1, true); break; case att_trig_source2: X.RDB$TRIGGER_SOURCE.NULL = FALSE; get_source_blob (&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, NULL, NULL, NULL, NULL); /* msg 126 restoring trigger %s */ break; case att_trig_relation_name: GET_TEXT(X.RDB$RELATION_NAME); break; case att_trig_sequence: X.RDB$TRIGGER_SEQUENCE = (USHORT) get_numeric(); break; case att_trig_description: X.RDB$DESCRIPTION.NULL = FALSE; get_misc_blob (&X.RDB$DESCRIPTION, 1, true); break; case att_trig_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (&X.RDB$DESCRIPTION, true); break; case att_trig_system_flag: X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); X.RDB$SYSTEM_FLAG.NULL = FALSE; break; case att_trig_inactive: X.RDB$TRIGGER_INACTIVE = (USHORT) get_numeric(); 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, NULL, NULL, NULL, NULL); /* 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 (void) { /************************************** * * 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; isc_tr_handle local_trans; BASED_ON RDB$TRIGGER_MESSAGES.RDB$TRIGGER_NAME name; BASED_ON RDB$TRIGGER_MESSAGES.RDB$MESSAGE_NUMBER number; BASED_ON RDB$TRIGGER_MESSAGES.RDB$MESSAGE message; TGBL tdgbl = GET_THREAD_DATA; bool flag = 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); flag = 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 flag = true; END_FOR; ON_ERROR general_on_error (); END_ERROR; BURP_verbose (127, name, NULL, NULL, NULL, NULL); /* msg 127 restoring trigger message for %s */ break; case att_trigmsg_number: number = (USHORT) get_numeric(); break; case att_trigmsg_text: GET_TEXT(message); break; default: bad_attribute (scan_next_attr, attribute, 135); /* msg 135 trigger message */ break; } } if (flag) return true; 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, NULL, NULL, NULL, NULL); /* 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 (void) { /************************************** * * 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; TGBL tdgbl = GET_THREAD_DATA; STORE (REQUEST_HANDLE tdgbl->handles_get_type_req_handle1) X IN RDB$TYPES X.RDB$DESCRIPTION.NULL = TRUE; X.RDB$SYSTEM_FLAG.NULL = TRUE; 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(); 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 (&X.RDB$DESCRIPTION, 1, false); break; case att_type_description2: X.RDB$DESCRIPTION.NULL = FALSE; get_source_blob (&X.RDB$DESCRIPTION, false); break; case att_type_system_flag: X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(); X.RDB$SYSTEM_FLAG.NULL = FALSE; break; default: bad_attribute (scan_next_attr, attribute, 136); /* msg 136 trigger type */ break; } } MISC_terminate (X.RDB$TYPE_NAME, temp, l, sizeof(temp)); BURP_verbose (128, temp, X.RDB$FIELD_NAME, NULL, NULL, NULL); /* msg 128 restoring type %s for field %s */ END_STORE; ON_ERROR general_on_error (); END_ERROR; return true; } bool get_user_privilege (void) { /************************************** * * 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; isc_tr_handle local_trans = NULL; 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; 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; TGBL tdgbl = GET_THREAD_DATA; 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, NULL, NULL, NULL, NULL); /* 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(); 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(); break; case att_priv_obj_type: flags |= USER_PRIV_OBJECT_TYPE; object_type = (USHORT) get_numeric(); break; default: bad_attribute (scan_next_attr, attribute, 105); /* msg 105 privilege */ break; } } /* Check if object exists */ bool exists = false; switch (object_type) { case obj_procedure: { for (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 (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 (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 (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; isc_tr_handle local_trans; TGBL tdgbl = GET_THREAD_DATA; /* If there is a global transaction then use it */ 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); break; case att_view_context_id: X.RDB$VIEW_CONTEXT = (USHORT) get_numeric(); 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 (BURP_REL relation) { /************************************** * * i g n o r e _ a r r a y * ************************************** * * Functional description * Read array data from input file to nice, * shiney, new array. * **************************************/ BURP_FLD field; ATT_TYPE attribute; SLONG length, lcount, *range, *end_ranges; USHORT field_number; LSTRING xdr_buffer; scan_attr_t scan_next_attr; TGBL tdgbl = GET_THREAD_DATA; /* don't free something you don't allocate */ xdr_buffer.lstr_allocated = 0; /* 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(); for (field = relation->rel_fields; field; field = field->fld_next) if (field->fld_number == field_number) break; if (!field) BURP_error_redirect (NULL, 36, 0, 0); /* msg 36 Can't find field for blob */ break; case att_array_dimensions: field->fld_dimensions = (SSHORT)get_numeric(); 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(); if (get_attribute(&attribute, tdgbl) != att_array_range_high) bad_attribute (scan_next_attr, attribute, 58); /* msg 58 array */ else range [1] = get_numeric(); } break; default: bad_attribute (scan_next_attr, attribute, 58); /* msg 58 array */ break; } } length = get(tdgbl); length |= get(tdgbl) << 8; length |= get(tdgbl) << 16; length |= get(tdgbl) << 24; if (tdgbl->gbl_sw_transportable) { if (get_attribute(&attribute, tdgbl) != att_xdr_array) BURP_error_redirect (NULL, 55, 0, 0); /* 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; } } else { lcount = length; } if (lcount) get_skip(tdgbl, lcount); } void ignore_blob (void) { /************************************** * * i g n o r e _ b l o b * ************************************** * * Functional description * Skip over blob data records. * **************************************/ ATT_TYPE attribute; SLONG segments; USHORT length; scan_attr_t scan_next_attr; TGBL tdgbl = GET_THREAD_DATA; /* Pick up attributes */ 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(); break; case att_blob_max_segment: get_numeric(); break; case att_blob_number_segments: segments = get_numeric(); break; case att_blob_type: get_numeric(); break; default: bad_attribute (scan_next_attr, attribute, 64); /* msg 64 blob */ break; } } /* Eat up blob segments */ while (--segments >= 0) { length = get(tdgbl); length |= get(tdgbl) << 8; if (length) get_skip(tdgbl, length); } } rec_type ignore_data (BURP_REL relation) { /************************************** * * i g n o r e _ d a t a * ************************************** * * Functional description * Ignore data records for a relation. * **************************************/ rec_type record; TGBL tdgbl = GET_THREAD_DATA; ULONG records = 0; while (true) { if (get(tdgbl) != att_data_length) BURP_error_redirect (NULL, 39, 0, 0); /* msg 39 expected record length */ USHORT l = (USHORT) get_numeric(); if (tdgbl->gbl_sw_transportable) if (get(tdgbl) != att_xdr_length) BURP_error_redirect (NULL, 55, 0, 0); /* msg 55 Expected XDR record length */ else l = (USHORT) get_numeric(); if (get(tdgbl) != att_data_data) BURP_error_redirect (NULL, 41, 0, 0); /* msg 41 expected data attribute */ if (l) { if (tdgbl->gbl_sw_compress) { UCHAR *buffer = (UCHAR*) BURP_alloc (l); decompress (buffer, l); BURP_free (buffer); } else get_skip(tdgbl, l); } records++; while (get_record(&record, tdgbl)) { if (record == rec_blob) ignore_blob(); else if (record == rec_array) ignore_array (relation); else break; } if (record != rec_data) break; } BURP_verbose (106, (void*) records, NULL, NULL, NULL, NULL); /* msg 106 %ld records ignored */ return record; } void realign (UCHAR *buffer, 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. * **************************************/ BURP_FLD field; UCHAR *p, *q; USHORT l; TGBL tdgbl = GET_THREAD_DATA; for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; p = buffer + field->fld_offset; q = buffer + field->fld_old_offset; 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, 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; p = buffer + FB_ALIGN(p - buffer, sizeof(SSHORT)); q = buffer + FB_ALIGN(q - buffer, sizeof(SSHORT)); *p++ = *q++; *p++ = *q++; } } } USHORT recompute_length (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. * **************************************/ #ifdef sparc BURP_FLD field; ULONG offset, length, dtype, alignment; const SSHORT *alignments = old_sparcs; TGBL tdgbl = GET_THREAD_DATA; for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; length = field->fld_length; alignment = 4; /* arrays are of various fld_types but are really blobs */ dtype = field->fld_type; if (field->fld_flags & FLD_array) { dtype = blr_blob; length = 8; } 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 (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; #else return 0; #endif } bool restore (const TEXT *file_name, const TEXT *database_name) { /************************************** * * r e s t o r e * ************************************** * * Functional description * Perform the body of restore. * **************************************/ rec_type record; ATT_TYPE attribute; isc_req_handle req_handle1 = NULL, req_handle2 = NULL, req_handle3 = NULL, req_handle5 = NULL; TGBL tdgbl = GET_THREAD_DATA; /* Read burp record first */ MVOL_init_read ((UCHAR*) tdgbl->gbl_database_file_name, (UCHAR*) file_name, &tdgbl->RESTORE_format, &tdgbl->io_cnt, &tdgbl->io_ptr); if (tdgbl->gbl_sw_transportable) BURP_verbose (133, NULL, NULL, NULL, NULL, NULL); /* msg 133 transportable backup -- data in XDR format */ if (tdgbl->gbl_sw_compress) BURP_verbose (61, NULL, NULL, NULL, NULL, NULL); /* 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) #ifdef SUPERSERVER BURP_svc_error (44, isc_arg_number, (void*)(ULONG)tdgbl->RESTORE_format, 0, NULL, 0, NULL, 0, NULL, 0, NULL); #else BURP_error (44, (void*)(ULONG) tdgbl->RESTORE_format, 0, 0, 0, 0); /* msg 44 Expected backup version 1, 2, or 3. Found %ld */ #endif create_database (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; USHORT db_version = check_db_version(); if (db_version < DB_VERSION_CURRENT) #ifdef SUPERSERVER BURP_svc_error (51, isc_arg_number, (void*) (ULONG) db_version, 0, NULL, 0, NULL, 0, NULL, 0, NULL); #else BURP_error (51, (void*) (ULONG) db_version, 0, 0, 0, 0); /* msg 51 database format %ld is too old to restore to */ #endif BURP_verbose (129, NULL, NULL, NULL, NULL, NULL); /* msg 129 started transaction */ 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 (&X.RDB$DESCRIPTION, false); else get_misc_blob (&X.RDB$DESCRIPTION, 1, 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_handle1); MISC_release_request_silent(req_handle2); MISC_release_request_silent(req_handle3); // If this should be a multi-file database, add the files if (tdgbl->gbl_sw_files && tdgbl->gbl_sw_files->fil_next) add_files ((UCHAR*) database_name); // Get global fields and relations bool flag_norel = true; // To fix bug 10098 bool flag = false; while (get_record(&record, tdgbl) != rec_end) { switch (record) { case rec_charset: if (!get_character_set()) return false; flag = true; break; case rec_collation: if (!get_collation()) return false; flag = true; break; case rec_chk_constraint: if (!get_chk_constraint()) return false; flag = true; break; case rec_global_field: if (!get_global_field()) return false; flag = true; break; case rec_field_dimensions: if (!get_field_dimensions()) return false; flag = true; break; case rec_relation: if (!get_relation()) return false; flag = true; flag_norel = false; break; case rec_ref_constraint: if (!get_ref_constraint()) return false; flag = true; break; case rec_rel_constraint: if (!get_rel_constraint()) return false; flag = true; break; case rec_function: if (!get_function()) return false; flag = true; break; case rec_procedure: if (!get_procedure()) return false; flag = true; break; case rec_exception: if (!get_exception()) return false; flag = true; break; case rec_system_type: /* rdb$types */ if (!get_type()) return false; flag = true; break; case rec_filter: /* rdb$filters */ if (!get_filter()) return false; flag = true; break; case rec_generator: if (!get_generator()) return false; flag = true; break; case rec_relation_data: if (flag) { BURP_verbose (68, NULL, NULL, NULL, NULL, NULL); /* 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()) return false; break; case rec_trigger: /* new trigger type */ if (!get_trigger()) return false; flag = true; break; case rec_trigger_message: if (!get_trigger_message()) return false; flag = true; break; case rec_user_privilege: if (!get_user_privilege()) return false; flag = true; break; case rec_security_class: if (!get_security_class()) return false; flag = true; break; case rec_files: if (!get_files()) return false; flag = true; break; case rec_sql_roles: if (!get_sql_roles()) return false; flag = true; break; default: #ifdef SUPERSERVER BURP_svc_error (43, isc_arg_number, (void*) record, 0, NULL, 0, NULL, 0, NULL, 0, NULL); #else BURP_error (43, (void*) record, 0, 0, 0, 0); /* msg 43 don't recognize record type %ld */ #endif break; } } /* 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 (); /* Purge shadow metadata if necessary */ if (tdgbl->gbl_sw_kill) FOR (REQUEST_HANDLE req_handle5) 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_handle5); return true; } void restore_security_class (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_tr_handle local_trans; isc_req_handle req_handle2 = NULL; ISC_QUAD new_blob_id; TGBL tdgbl = GET_THREAD_DATA; local_trans = gds_trans; FOR (REQUEST_HANDLE req_handle2) X IN RDB$SECURITY_CLASSES WITH X.RDB$SECURITY_CLASS EQ sec_class_nm new_blob_id.gds_quad_high = 0; new_blob_id.gds_quad_low = 0; get_acl (owner_nm, &X.RDB$ACL, &new_blob_id); MODIFY X; MOVE_FAST (&new_blob_id, &X.RDB$ACL, 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); } void store_blr_gen_id (const TEXT* gen_name, // TEXT GDS_NAME[GDS_NAME_LEN] SINT64 value) { /************************************** * * s t o r e _ b l r _ g e n _ i d * ************************************** * * Functional description * Store the blr_gen_id for the relation. * **************************************/ ISC_STATUS_ARRAY status_vector; TGBL tdgbl = GET_THREAD_DATA; STORE (REQUEST_HANDLE tdgbl->handles_store_blr_gen_id_req_handle1) X IN RDB$GENERATORS strcpy (X.RDB$GENERATOR_NAME, gen_name); END_STORE; ON_ERROR general_on_error (); END_ERROR; if (!value) { #pragma FB_COMPILER_MESSAGE("BRS: casting SINT64 to SLONG") BURP_verbose (185, gen_name, (void*) (SLONG) value, NULL, NULL, NULL); /* msg 185 restoring generator %s value: %ld */ return; } FRBRD *gen_id_reqh = NULL; UCHAR blr_buffer [100]; // enough to fit blr UCHAR *blr = blr_buffer; // build the blr with the right relation name if (tdgbl->RESTORE_format >= 6) { stuff(&blr, blr_version5); } else { stuff(&blr, blr_version4); } stuff(&blr, blr_begin); if (tdgbl->RESTORE_format >= 6) { stuff(&blr, blr_dcl_variable); stuff_word(&blr, 0); stuff(&blr, blr_int64); stuff(&blr, 0); } else { stuff(&blr, blr_dcl_variable); stuff_word(&blr, 0); stuff(&blr, blr_long); stuff(&blr, 0); } stuff(&blr, blr_begin); stuff(&blr, blr_assignment); stuff(&blr, blr_gen_id); stuff_string(&blr, gen_name); if (tdgbl->RESTORE_format >= 6) { stuff(&blr, blr_literal); stuff(&blr, blr_int64); stuff(&blr, 0); stuff_int64(&blr, value); } else { stuff(&blr, blr_literal); stuff(&blr, blr_long); stuff(&blr, 0); stuff_long(&blr, (SLONG)value); } stuff(&blr, blr_variable); stuff_word(&blr, 0); stuff(&blr, blr_end); stuff(&blr, blr_end); stuff(&blr, blr_eoc); SSHORT blr_length = blr - blr_buffer; if (isc_compile_request (status_vector, &DB, &gen_id_reqh, blr_length, (SCHAR*) blr_buffer)) { isc_print_blr ((SCHAR*) blr_buffer, NULL, NULL, 0); BURP_error_redirect (status_vector, 42, 0, 0); /* 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 ((SCHAR*) blr_buffer, NULL, NULL, 0); BURP_error_redirect (status_vector, 42, 0, 0); /* msg 42 Failed in store_blr_gen_id */ } #pragma FB_COMPILER_MESSAGE("BRS: casting SINT64 to SLONG") BURP_verbose (185, gen_name, (void*) (SLONG) value, NULL, NULL, NULL); /* msg 185 restoring generator %s value: %ld */ isc_release_request (status_vector, &gen_id_reqh); } void stuff_string(UCHAR **ptr, const TEXT *string) { /************************************** * * s t u f f _ s t r i n g * ************************************** * * Functional description * Stuff a name input a BLR string -- byte count first. * **************************************/ stuff(ptr, strlen (string)); while (*string) stuff(ptr, *string++); } void update_global_field (void) { /************************************** * * 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. * **************************************/ GFLD n_gfield, gfield; USHORT length; UCHAR *p, *q; isc_req_handle req_handle1 = NULL; TGBL tdgbl = GET_THREAD_DATA; for (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; if (length = sizeof(ISC_QUAD)) { p = (UCHAR *)&X.RDB$VALIDATION_BLR; q = (UCHAR *)&gfield->gfld_vb; do *p++ = *q++; while (--length); } } if (gfield->gfld_flags & GFLD_validation_source) { X.RDB$VALIDATION_SOURCE.NULL = FALSE; if (length = sizeof(ISC_QUAD)) { p = (UCHAR *)&X.RDB$VALIDATION_SOURCE; q = (UCHAR *)&gfield->gfld_vs; do *p++ = *q++; while (--length); } } if (gfield->gfld_flags & GFLD_validation_source2) { X.RDB$VALIDATION_SOURCE.NULL = FALSE; if (length = sizeof(ISC_QUAD)) { p = (UCHAR *)&X.RDB$VALIDATION_SOURCE; q = (UCHAR *)&gfield->gfld_vs2; do *p++ = *q++; while (--length); } } END_MODIFY; ON_ERROR general_on_error (); END_ERROR; END_FOR; ON_ERROR general_on_error (); END_ERROR; n_gfield = gfield->gfld_next; BURP_free (gfield); gfield = n_gfield; } MISC_release_request_silent(req_handle1); tdgbl->gbl_global_fields = NULL; } #ifdef NOT_USED_OR_REPLACED bool bug_8183 (TGBL tdgbl) { /************************************** * * b u g _ 8 1 8 3 * ************************************** * * Name: bug_8183 * * Function: Bug fix for bug_no 8183: It is a migration bug between IB3.3 * and IB4.0. Gbak v4.0 can't restore database v3.3 if * database has an index definition with comment field. * It happens because of att_index_description2 attribute * (which indicates that index contains a comment field * and is equal 8 under IB3.3) accidently got new value 9 * under IB4.0. * At the same time, new attribute att_index_foreign_key * has been added with value 8. Thus, when gbak v4.0 faces * index attribute with value 8 during restore procedure of * database v3.3, it will recognize this attribute as * att_index_foreign_key instead of att_index_description2. * * This function is trying to recognize the next * data in tdgbl->io_ptr buffer as either name of foreign * key or comment field. Function returns TRUE in case of * comment field, otherwise FALSE. * * Usage: result = bug_8183(tdgbl); * * Parameters: tdgbl - pointer to the structure of global switches * and data * * Returns: result [TRUE/FALSE] * * Pre: gbak got an index attribute with value "8" from * backup database. * * Post: none * * Remarks: For more information see bug_no 8183 * **************************************/ UCHAR tmp[sizeof(ULONG) + 1], *p; USHORT i; ULONG len1, len2; bool result = false; USHORT io_cnt = tdgbl->io_cnt; UCHAR* io_ptr = tdgbl->io_ptr; if ( io_cnt > 0 ) { len1 = len2 = *io_ptr++; --io_cnt; /* len1 can be either length of att_index_foreign_key OR quantity of byte where seats length of att_index_description2. In case of att_index_description2, len1 should be as even and no bigger than sizeof(ULONG). Let's check it out */ if ( (len1 % 2) == 0 && (len1 <= sizeof(ULONG)) ) { /* it still can be a foreign key; so try to read it. Note: if internal buffer is over, then we wan't read next block */ memset(tmp, '\0', sizeof(tmp)); p = tmp; while (len1-- && io_cnt-- ) *p++ = *io_ptr++; /* if read array is a foreign key then it should contain following chars only : ['A'..'Z', '0'..'9', '_', '$'] */ for ( p = tmp, i = 0; *p && *p != ' ' && ((*p >= 'A' && *p <= 'Z') || (*p == '_') || (*p >= '0' && *p <= '9') || (*p == '$')) ; p++ ) { i++; } if ( (len2 - len1) != i ) result = true; } } return result; } #endif } // namespace