/* * PROGRAM: JRD Backup and Restore Program * MODULE: backup.epp * DESCRIPTION: Backup 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: Added verbose backup records as BACKUP_VERBOSE_INTERVAL * 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE" * conditionals, as the engine now fully supports * readonly databases. * 2001.11.20 Claudio Valderrama: fix problem with embedded blanks in * generators and use symbol_length effective length calculation from put_text. * This minimizes code redundancy and fixes SF Bug #483276. * 2001.12.15 Claudio Valderrama: copy should run through symbol_length instead * of using just another length calculation algorithm. Callers of put_text, copy * and symbol_length (if used directly) should use sizeof. Changed all callers * and sizeof() works because the strings are local to the functions. This * eliminates the problem with harcoded limits in each call. * 2002.10.29 Mike Nordell: UINT64 backup message. * 2003.08.17 Claudio Valderrama: Fix SF Bug #750659. */ /* $Id: backup.epp,v 1.65 2004-08-30 10:07:00 robocop Exp $ */ #include "firebird.h" #include #include #include #include "../burp/burp.h" #include "../jrd/ods.h" #include "../jrd/align.h" #include "../jrd/gdsassert.h" #include "../jrd/thd.h" #include "../common/stuff.h" #include "../burp/backu_proto.h" #include "../burp/burp_proto.h" #include "../burp/canon_proto.h" #include "../burp/mvol_proto.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" RUNTIME * dbb_file; #define DB tdgbl->db_handle #define gds_trans tdgbl->tr_handle #define isc_status tdgbl->status namespace // unnamed, private { // VERBOSE INTERVAL WHEN BACKING RECORDS const ULONG BACKUP_VERBOSE_INTERVAL = 20000; #define PUT_MESSAGE(attribute, message) put_message ((attribute), (message), sizeof(message)) #define PUT_TEXT(attribute, text) put_text ((attribute), (text), sizeof(text)) #define COPY(source, target) copy ((source), (target), sizeof(target)) inline void put(BurpGlobals* tdgbl, UCHAR c) { if (--(tdgbl->io_cnt) >= 0) *(tdgbl->io_ptr)++ = c; else MVOL_write(c, &tdgbl->io_cnt, &tdgbl->io_ptr); } inline const UCHAR* put_block(BurpGlobals* tdgbl, const UCHAR* p, ULONG n) { return MVOL_write_block (tdgbl, p, n); } void compress(const UCHAR*, ULONG); int copy(const TEXT *, TEXT *, ULONG); burp_fld* get_fields(burp_rel*); SINT64 get_gen_id(const TEXT *, SSHORT); void get_ranges(burp_fld*); void put_array(burp_fld*, burp_rel*, ISC_QUAD *); void put_asciz(const SCHAR, const TEXT*); void put_blob(burp_fld*, ISC_QUAD *, ULONG); bool put_blr_blob(SCHAR, ISC_QUAD *); void put_data(burp_rel*); void put_index(burp_rel*); int put_message(SCHAR, const TEXT *, ULONG); void put_numeric(SCHAR, SLONG); void put_relation(burp_rel*); bool put_source_blob(SCHAR, SCHAR, ISC_QUAD *); int put_text(SCHAR, const TEXT *, SSHORT); void set_capabilities(void); int symbol_length(const TEXT *, ULONG); void write_character_sets(void); void write_check_constraints(void); void write_collations(void); void write_database(const TEXT*); void write_exceptions(void); void write_field_dimensions(void); void write_filters(void); void write_functions(void); void write_function_args(GDS_NAME); void write_generators(void); void write_sql_roles(void); void write_global_fields(void); void write_procedures(void); void write_procedure_prms(GDS_NAME); void write_ref_constraints(void); void write_rel_constraints(void); void write_relations(void); void write_shadow_files(void); void write_triggers(void); void write_trigger_messages(void); void write_types(void); void write_user_privileges(void); void general_on_error(void); enum backup_capabilities { BCK_security = 1, BCK_files = 2, BCK_external = 4, BCK_idx_inactive = 8, BCK_triggers = 16, // Obsolete - 1996-Aug-05 BCK_context_name = 32, BCK_db_description = 64, BCK_ffmptt = 128, // rdb$functions, rdb$filters, rdb$trigger_messages, // rdb$user_privileges, rdb$triggers, rdb$types BCK_attributes_v3 = 256, // attributes in various system relations new to v3 BCK_rfr_sys_flag = 512, // system flag is missing from Rdb/VMS V3 RFR relation BCK_ods6 = 1024, // rdb$field_dimensions and shadow files BCK_ods8 = 2048, // stored procedures & exceptions & constraints BCK_ods9 = 4096, // SQL roles BCK_ods10 = 8192, // FIELD_PRECISION BCK_ods11 = 16384 // rdb$description in rdb$roles }; #ifdef DEBUG UCHAR debug_on = 0; // able to turn this on in debug mode #endif /* table used to determine capabilities, checking for specific fields in system relations */ struct rfr_tab_t { const TEXT *relation; const TEXT *field; // Let's be compatible with BurpGlobals.BCK_capabilities, although we could follow // the enumeration backup_capabilities but the enum then should be defined // in burp.h instead so BCK_capabilities isn't anymore a simple SLONG. SLONG bit_mask; }; const rfr_tab_t rfr_table[] = { {"RDB$INDICES", "RDB$INDEX_INACTIVE", BCK_idx_inactive}, /* Backup of V2 triggers no longer supported 1996-Aug-05 David Schnepper {"RDB$RELATIONS", "RDB$STORE_TRIGGER", BCK_triggers}, */ {"RDB$RELATIONS", "RDB$EXTERNAL_FILE", BCK_external}, {"RDB$SECURITY_CLASSES", "RDB$SECURITY_CLASS", BCK_security}, {"RDB$FILES", "RDB$FILE_NAME", BCK_files}, {"RDB$VIEW_RELATIONS", "RDB$CONTEXT_NAME", BCK_context_name}, {"RDB$DATABASE", "RDB$DESCRIPTION", BCK_db_description}, {"RDB$FUNCTIONS", "RDB$FUNCTION_NAME", BCK_ffmptt}, {"RDB$FIELDS", "RDB$EXTERNAL_LENGTH", BCK_attributes_v3}, {"RDB$RELATION_FIELDS", "RDB$SYSTEM_FLAG", BCK_rfr_sys_flag}, {"RDB$FIELD_DIMENSIONS", "RDB$DIMENSION", BCK_ods6}, {"RDB$PROCEDURES", "RDB$PROCEDURE_NAME", BCK_ods8}, {"RDB$ROLES", "RDB$ROLE_NAME", BCK_ods9}, {"RDB$FIELDS", "RDB$FIELD_PRECISION", BCK_ods10}, {"RDB$ROLES", "RDB$DESCRIPTION", BCK_ods11}, {0, 0, 0} }; const SCHAR blob_items[] = { isc_info_blob_max_segment, isc_info_blob_num_segments, isc_info_blob_type }; const SCHAR blr_items[] = { isc_info_blob_max_segment, isc_info_blob_total_length }; const SCHAR source_items[] = { isc_info_blob_max_segment, isc_info_blob_total_length, isc_info_blob_num_segments }; const SCHAR db_info_items[] = { isc_info_db_sql_dialect, isc_info_page_size, isc_info_sweep_interval, isc_info_forced_writes, isc_info_no_reserve, isc_info_set_page_buffers, isc_info_db_read_only, isc_info_end }; const SCHAR limbo_tpb[] = { isc_tpb_version1, isc_tpb_ignore_limbo }; const SCHAR limbo_nau_tpb[] = { isc_tpb_version1, isc_tpb_ignore_limbo, isc_tpb_no_auto_undo }; } // namespace int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name) { /************************************** * * B A C K U P _ b a c k u p * ************************************** * * Functional description * Backup a database. * **************************************/ ISC_STATUS_ARRAY status_vector; TEXT temp[GDS_NAME_LEN]; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); tdgbl->gbl_database_file_name = dbb_file; tdgbl->io_ptr = NULL; tdgbl->io_cnt = 0; tdgbl->relations = NULL; tdgbl->BCK_capabilities = 0; gds_trans = 0; BURP_verbose(130, NULL, NULL, NULL, NULL, NULL); // msg 130 starting transaction if (tdgbl->gbl_sw_ignore_limbo) { if (isc_start_transaction(status_vector, &gds_trans, 1, &DB, sizeof(limbo_nau_tpb), limbo_nau_tpb)) { isc_start_transaction(status_vector, &gds_trans, 1, &DB, sizeof(limbo_tpb), limbo_tpb); } } else { EXEC SQL SET TRANSACTION NO_AUTO_UNDO; if (isc_status[1]) EXEC SQL SET TRANSACTION; } if (!gds_trans) { EXEC SQL SET TRANSACTION NAME gds_trans NO_AUTO_UNDO; if (isc_status[1]) EXEC SQL SET TRANSACTION NAME gds_trans; } // decide what type of database we've got set_capabilities(); // Write burp record first with other valuable information // In case of split operation, write a 'split' header first to all the files if (tdgbl->action->act_action == ACT_backup_split) { for (burp_fil* fil = tdgbl->gbl_sw_files; fil; fil = fil->fil_next) { tdgbl->action->act_file = fil; if (MVOL_split_hdr_write() == FALSE) { BURP_error(269, true, tdgbl->action->act_file->fil_name, 0, 0, 0, 0); // msg 269 can't write a header record to file %s } } tdgbl->action->act_file = tdgbl->gbl_sw_files; } MVOL_init_write(dbb_file, file_name, &tdgbl->io_cnt, &tdgbl->io_ptr); // Write database record write_database(dbb_file); // Write global fields BURP_verbose(150, NULL, NULL, NULL, NULL, NULL); // msg 150 writing global fields write_global_fields(); if (tdgbl->BCK_capabilities & BCK_ods6) { write_field_dimensions(); BURP_verbose(162, NULL, NULL, NULL, NULL, NULL); // msg 162 writing shadow files write_shadow_files(); } // Write relations BURP_verbose(154, NULL, NULL, NULL, NULL, NULL); // msg 154 writing relations write_relations(); if (tdgbl->BCK_capabilities & BCK_ffmptt) { // Write functions BURP_verbose(148, NULL, NULL, NULL, NULL, NULL); // msg 148 writing functions write_functions(); // Write types BURP_verbose(161, NULL, NULL, NULL, NULL, NULL); // msg 161 writing types write_types(); // Write filters BURP_verbose(146, NULL, NULL, NULL, NULL, NULL); // msg 146 writing filters write_filters(); // Write generators BURP_verbose(164, NULL, NULL, NULL, NULL, NULL); // msg 164 writing id generators write_generators(); } if (tdgbl->BCK_capabilities & BCK_ods8) { // Write procedures BURP_verbose(192, NULL, NULL, NULL, NULL, NULL); // msg 192 writing stored procedures write_procedures(); // Write exceptions BURP_verbose(197, NULL, NULL, NULL, NULL, NULL); // msg 197 writing exceptions write_exceptions(); // Write Character Sets BURP_verbose(msgVerbose_write_charsets, NULL, NULL, NULL, NULL, NULL); write_character_sets(); // Write Collations BURP_verbose(msgVerbose_write_collations, NULL, NULL, NULL, NULL, NULL); write_collations(); } // Now go back and write all data for (burp_rel* relation = tdgbl->relations; relation; relation = relation->rel_next) { put(tdgbl, (UCHAR) (rec_relation_data)); PUT_TEXT(att_relation_name, relation->rel_name); put(tdgbl, (UCHAR) (att_end)); if (!(relation->rel_flags & REL_view) && !(relation->rel_flags & REL_external)) { put_index(relation); if (!tdgbl->gbl_sw_meta) put_data(relation); } put(tdgbl, (UCHAR) (rec_relation_end)); } // now for the new triggers in rdb$triggers if (tdgbl->BCK_capabilities & BCK_ffmptt) { BURP_verbose(159, NULL, NULL, NULL, NULL, NULL); // msg 159 writing triggers write_triggers(); BURP_verbose(158, NULL, NULL, NULL, NULL, NULL); // msg 158 writing trigger messages write_trigger_messages(); write_user_privileges(); } // Last, but not least, go back and add any access control lists if (tdgbl->BCK_capabilities & BCK_security) { isc_req_handle req_handle1 = 0; FOR (REQUEST_HANDLE req_handle1) X IN RDB$SECURITY_CLASSES WITH X.RDB$SECURITY_CLASS NOT STARTING "SQL$" put(tdgbl, rec_security_class); const ULONG l = PUT_TEXT (att_class_security_class, X.RDB$SECURITY_CLASS); MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp)); BURP_verbose (155, temp, NULL, NULL, NULL, NULL); // msg 155 writing security class %s put_blr_blob (att_class_acl, &X.RDB$ACL); put_source_blob (att_class_description2, att_class_description, &X.RDB$DESCRIPTION); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } if (tdgbl->BCK_capabilities & BCK_ods8) { // Write relation constraints BURP_verbose(206, NULL, NULL, NULL, NULL, NULL); // msg 206 writing relation constraints write_rel_constraints(); // Write referential constraints BURP_verbose(209, NULL, NULL, NULL, NULL, NULL); // msg 209 writing referential constraints write_ref_constraints(); // Write check constraints BURP_verbose(210, NULL, NULL, NULL, NULL, NULL); // msg 210 writing check constraints write_check_constraints(); } if (tdgbl->BCK_capabilities & BCK_ods9) { // Write SQL roles BURP_verbose(248, NULL, NULL, NULL, NULL, NULL); // msg 248 writing SQL roles write_sql_roles(); } // Finish up put(tdgbl, (UCHAR) (rec_end)); UINT64 cumul_count = MVOL_fini_write(&tdgbl->io_cnt, &tdgbl->io_ptr); if (cumul_count <= MAX_SLONG) { SLONG tempcount = cumul_count; BURP_verbose(176, (void*) (IPTR)tempcount, NULL, NULL, NULL, NULL); // msg 176 closing file, committing, and finishing. %ld bytes written } else { char psz[64]; sprintf(psz, "%" QUADFORMAT "d", cumul_count); BURP_verbose(283, psz, NULL, NULL, NULL, NULL); // msg 283 closing file, committing, and finishing. %s bytes written } COMMIT; ON_ERROR general_on_error(); END_ERROR; if (gds_trans) COMMIT gds_trans; ON_ERROR general_on_error(); END_ERROR; FINISH ON_ERROR general_on_error(); END_ERROR; return FINI_OK; } namespace // unnamed, private { void compress(const UCHAR* data, ULONG length) { /************************************** * * c o m p r e s s * ************************************** * * Functional description * Write out data in compressed form. * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); const UCHAR* p = data; const UCHAR* end = p + length; const UCHAR* q = NULL; while (p < end) { for (q = p + 2; q < end && (q[-2] != q[-1] || q[-1] != q[0]); q++) ; USHORT run = (q < end) ? q - p - 2 : end - p; if (run) { for (; run > 127; run -= 127) { USHORT l = 127; put(tdgbl, (UCHAR) (l)); p = put_block(tdgbl, p, l); } if (run) { put(tdgbl, (UCHAR) (run)); p = put_block(tdgbl, p, run); } } for (q = p; q < end && *q == *p; q++) ; if ((run = q - p) != 0) { for (; run > 127; run -= 127) { put(tdgbl, (UCHAR) (-127)); put(tdgbl, (UCHAR) (*p)); } if (run) { put(tdgbl, (UCHAR) (-run)); put(tdgbl, (UCHAR) (*p)); } p = q; } } } int copy( const TEXT* from, TEXT* to, ULONG size_len) { /************************************** * * c o p y * ************************************** * * Functional description * Copy a blank or null terminated string into a null terminated * string. * **************************************/ const ULONG l = (ULONG) symbol_length (from, size_len); MOVE_FAST(from, to, l); *(to + l) = '\0'; return (int) l; } 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 backup. * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); BURP_print_status(isc_status); BURP_abort(); } burp_fld* get_fields( burp_rel* relation) { /************************************** * * g e t _ f i e l d s * ************************************** * * Functional description * Get fields for a relation. Test * capabilities and get system specific * **************************************/ burp_fld* field; ISC_QUAD* blob_id; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); USHORT count = 1; burp_fld* fields = NULL; /* if we have all capabilities, use the first request to get the most performance out of the latest engine; if we don't have one of the capabilities we must use the second set of requests--this requires more code but it is well worth it for the performance benefits, especially remotely--deej */ if ((tdgbl->BCK_capabilities & BCK_attributes_v3) && (tdgbl->BCK_capabilities & BCK_ods8) && (tdgbl->BCK_capabilities & BCK_rfr_sys_flag) && (tdgbl->BCK_capabilities & BCK_security)) { FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle1) X IN RDB$RELATION_FIELDS CROSS Y IN RDB$FIELDS WITH X.RDB$FIELD_SOURCE = Y.RDB$FIELD_NAME AND X.RDB$RELATION_NAME EQ relation->rel_name field = (burp_fld*) BURP_alloc_zero(sizeof(burp_fld)); field->fld_number = count++; field->fld_type = Y.RDB$FIELD_TYPE; field->fld_sub_type = Y.RDB$FIELD_SUB_TYPE; field->fld_length = Y.RDB$FIELD_LENGTH; field->fld_scale = Y.RDB$FIELD_SCALE; field->fld_id = X.RDB$FIELD_ID; if (!X.RDB$DESCRIPTION.NULL) { blob_id = &X.RDB$DESCRIPTION; if (blob_id->gds_quad_low || blob_id->gds_quad_high) field->fld_description = X.RDB$DESCRIPTION; } if (!X.RDB$QUERY_HEADER.NULL) { blob_id = &X.RDB$QUERY_HEADER; if (blob_id->gds_quad_low || blob_id->gds_quad_high) field->fld_query_header = X.RDB$QUERY_HEADER; } if (X.RDB$FIELD_POSITION.NULL) field->fld_flags |= FLD_position_missing; else field->fld_position = X.RDB$FIELD_POSITION; field->fld_view_context = X.RDB$VIEW_CONTEXT; if (X.RDB$UPDATE_FLAG.NULL) field->fld_flags |= FLD_update_missing; else field->fld_update_flag = X.RDB$UPDATE_FLAG; COPY (X.RDB$FIELD_NAME, field->fld_name); COPY (X.RDB$FIELD_SOURCE, field->fld_source); COPY (X.RDB$BASE_FIELD, field->fld_base); COPY (X.RDB$QUERY_NAME, field->fld_query_name); COPY (X.RDB$EDIT_STRING, field->fld_edit_string); COPY (X.RDB$COMPLEX_NAME, field->fld_complex_name); blob_id = &Y.RDB$COMPUTED_BLR; if (blob_id->gds_quad_low || blob_id->gds_quad_high) { field->fld_flags |= FLD_computed; } field->fld_system_flag = X.RDB$SYSTEM_FLAG; COPY (X.RDB$SECURITY_CLASS, field->fld_security_class); // use the fld_flags to mark the field as an array and // to differentiate it from other blobs if (Y.RDB$DIMENSIONS) { field->fld_flags |= FLD_array; field->fld_dimensions = Y.RDB$DIMENSIONS; if (field->fld_dimensions < 0) { BURP_error_redirect (NULL, 52, field->fld_name, NULL); } // msg 52 array dimension for field %s is invalid get_ranges (field); } if (!X.RDB$NULL_FLAG.NULL) { field->fld_null_flag = X.RDB$NULL_FLAG; field->fld_flags |= FLD_null_flag; } if (!X.RDB$DEFAULT_VALUE.NULL) { blob_id = &X.RDB$DEFAULT_VALUE; if (blob_id->gds_quad_low || blob_id->gds_quad_high) { field->fld_default_value = X.RDB$DEFAULT_VALUE; } } if (!X.RDB$DEFAULT_SOURCE.NULL) { blob_id = &X.RDB$DEFAULT_SOURCE; if (blob_id->gds_quad_low || blob_id->gds_quad_high) { field->fld_default_source = X.RDB$DEFAULT_SOURCE; } } if (!(Y.RDB$CHARACTER_SET_ID.NULL)) { field->fld_character_set_id = Y.RDB$CHARACTER_SET_ID; field->fld_flags |= FLD_charset_flag; } if (!X.RDB$COLLATION_ID.NULL) { field->fld_collation_id = X.RDB$COLLATION_ID; field->fld_flags |= FLD_collate_flag; } field->fld_next = fields; fields = field; END_FOR; ON_ERROR general_on_error(); END_ERROR; } else { FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle1) X IN RDB$RELATION_FIELDS CROSS Y IN RDB$FIELDS WITH X.RDB$FIELD_SOURCE = Y.RDB$FIELD_NAME AND X.RDB$RELATION_NAME EQ relation->rel_name field = (burp_fld*) BURP_alloc_zero (sizeof(burp_fld)); field->fld_number = count++; field->fld_type = Y.RDB$FIELD_TYPE; field->fld_sub_type = Y.RDB$FIELD_SUB_TYPE; field->fld_length = Y.RDB$FIELD_LENGTH; field->fld_scale = Y.RDB$FIELD_SCALE; field->fld_id = X.RDB$FIELD_ID; if (!X.RDB$DESCRIPTION.NULL) { blob_id = &X.RDB$DESCRIPTION; if (blob_id->gds_quad_low || blob_id->gds_quad_high) field->fld_description = X.RDB$DESCRIPTION; } if (!X.RDB$QUERY_HEADER.NULL) { blob_id = &X.RDB$QUERY_HEADER; if (blob_id->gds_quad_low || blob_id->gds_quad_high) field->fld_query_header = X.RDB$QUERY_HEADER; } if (X.RDB$FIELD_POSITION.NULL) field->fld_flags |= FLD_position_missing; else field->fld_position = X.RDB$FIELD_POSITION; field->fld_view_context = X.RDB$VIEW_CONTEXT; if (X.RDB$UPDATE_FLAG.NULL) field->fld_flags |= FLD_update_missing; else field->fld_update_flag = X.RDB$UPDATE_FLAG; COPY (X.RDB$FIELD_NAME, field->fld_name); COPY (X.RDB$FIELD_SOURCE, field->fld_source); COPY (X.RDB$BASE_FIELD, field->fld_base); COPY (X.RDB$QUERY_NAME, field->fld_query_name); COPY (X.RDB$EDIT_STRING, field->fld_edit_string); if (tdgbl->BCK_capabilities & BCK_attributes_v3) { FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle2) RFR IN RDB$RELATION_FIELDS WITH RFR.RDB$FIELD_NAME = X.RDB$FIELD_NAME AND RFR.RDB$RELATION_NAME = X.RDB$RELATION_NAME COPY (RFR.RDB$COMPLEX_NAME, field->fld_complex_name); END_FOR; ON_ERROR general_on_error(); END_ERROR; } blob_id = &Y.RDB$COMPUTED_BLR; if (blob_id->gds_quad_low || blob_id->gds_quad_high) field->fld_flags |= FLD_computed; if (tdgbl->BCK_capabilities & BCK_rfr_sys_flag) { FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle3) RFR IN RDB$RELATION_FIELDS WITH RFR.RDB$RELATION_NAME = relation->rel_name AND RFR.RDB$FIELD_NAME = X.RDB$FIELD_NAME field->fld_system_flag = RFR.RDB$SYSTEM_FLAG; END_FOR; ON_ERROR general_on_error(); END_ERROR; } if (tdgbl->BCK_capabilities & BCK_security) { FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle4) RFR IN RDB$RELATION_FIELDS WITH RFR.RDB$RELATION_NAME = relation->rel_name AND RFR.RDB$FIELD_NAME = X.RDB$FIELD_NAME COPY (RFR.RDB$SECURITY_CLASS, field->fld_security_class); END_FOR; ON_ERROR general_on_error(); END_ERROR; } if (tdgbl->BCK_capabilities & BCK_attributes_v3) { FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle5) RF IN RDB$FIELDS WITH RF.RDB$FIELD_NAME = X.RDB$FIELD_SOURCE // use the fld_flags to mark the field as an array and // to differentiate it from other blobs if (RF.RDB$DIMENSIONS) { field->fld_flags |= FLD_array; field->fld_dimensions = RF.RDB$DIMENSIONS; if (field->fld_dimensions < 0) { BURP_error_redirect (NULL, 52, field->fld_name, NULL); } // msg 52 array dimension for field %s is invalid get_ranges (field); } END_FOR; ON_ERROR general_on_error(); END_ERROR; } if (tdgbl->BCK_capabilities & BCK_ods8) { FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle6) X2 IN RDB$RELATION_FIELDS CROSS F2 IN RDB$FIELDS WITH X2.RDB$FIELD_NAME = X.RDB$FIELD_NAME AND X2.RDB$RELATION_NAME EQ relation->rel_name AND X2.RDB$FIELD_SOURCE EQ F2.RDB$FIELD_NAME if (!X2.RDB$NULL_FLAG.NULL) { field->fld_null_flag = X2.RDB$NULL_FLAG; field->fld_flags |= FLD_null_flag; } if (!X2.RDB$DEFAULT_VALUE.NULL) { blob_id = &X2.RDB$DEFAULT_VALUE; if (blob_id->gds_quad_low || blob_id->gds_quad_high) field->fld_default_value = X2.RDB$DEFAULT_VALUE; } if (!X2.RDB$DEFAULT_SOURCE.NULL) { blob_id = &X2.RDB$DEFAULT_SOURCE; if (blob_id->gds_quad_low || blob_id->gds_quad_high) field->fld_default_source = X2.RDB$DEFAULT_SOURCE; } if (!(F2.RDB$CHARACTER_SET_ID.NULL)) { field->fld_character_set_id = F2.RDB$CHARACTER_SET_ID; field->fld_flags |= FLD_charset_flag; } if (!X2.RDB$COLLATION_ID.NULL) { field->fld_collation_id = X2.RDB$COLLATION_ID; field->fld_flags |= FLD_collate_flag; } END_FOR; ON_ERROR general_on_error(); END_ERROR; } field->fld_next = fields; fields = field; END_FOR; ON_ERROR general_on_error(); END_ERROR; } return fields; } SINT64 get_gen_id( const TEXT* name, SSHORT name_len) { /************************************** * * g e t _ g e n _ i d * ************************************** * * Functional description * Read id for a generator; * **************************************/ UCHAR blr_buffer[100]; // enough to fit blr BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FB_API_HANDLE gen_id_reqh = 0; UCHAR* blr = blr_buffer; /* If this is ODS 10 (IB version 6.0) or greater, build BLR to retrieve the 64-bit value of the generator. If not, build BLR to retrieve the 32-bit value, which we will cast to the expected INT64 format. */ if (tdgbl->BCK_capabilities & BCK_ods10) { // build the blr with the right relation name and 64-bit results. add_byte(blr, blr_version5); add_byte(blr, blr_begin); add_byte(blr, blr_message); add_byte(blr, 0); add_word(blr, 1); add_byte(blr, blr_int64); add_byte(blr, 0); add_byte(blr, blr_send); add_byte(blr, 0); add_byte(blr, blr_assignment); add_byte(blr, blr_gen_id); add_byte(blr, name_len); while (name_len--) { const UCHAR c = *name++; add_byte(blr, c); } add_byte(blr, blr_literal); add_byte(blr, blr_long); add_byte(blr, 0); add_word(blr, 0); add_word(blr, 0); add_byte(blr, blr_parameter); add_byte(blr, 0); add_word(blr, 0); add_byte(blr, blr_end); add_byte(blr, blr_eoc); } else { // build the blr with the right relation name and 32-bit results add_byte(blr, blr_version4); add_byte(blr, blr_begin); add_byte(blr, blr_message); add_byte(blr, 0); add_word(blr, 1); add_byte(blr, blr_long); add_byte(blr, 0); add_byte(blr, blr_send); add_byte(blr, 0); add_byte(blr, blr_assignment); add_byte(blr, blr_gen_id); add_byte(blr, name_len); while (name_len--) { const UCHAR c = *name++; add_byte(blr, c); } add_byte(blr, blr_literal); add_byte(blr, blr_long); add_byte(blr, 0); add_word(blr, 0); add_word(blr, 0); add_byte(blr, blr_parameter); add_byte(blr, 0); add_word(blr, 0); add_byte(blr, blr_end); add_byte(blr, blr_eoc); } #ifdef DEBUG if (debug_on) isc_print_blr((const char*)blr_buffer, NULL, NULL, 0); #endif ISC_STATUS_ARRAY status_vector; const SSHORT blr_length = blr - blr_buffer; if (isc_compile_request(status_vector, &DB, &gen_id_reqh, blr_length, (const char*) blr_buffer)) { // if there's no gen_id, never mind ... return 0; } if (isc_start_request(status_vector, &gen_id_reqh, &gds_trans, // use the same one generated by gpre 0)) { BURP_error_redirect(status_vector, 25, NULL, NULL); // msg 25 Failed in put_blr_gen_id } SINT64 read_msg1; if (tdgbl->BCK_capabilities & BCK_ods10) { if (isc_receive(status_vector, &gen_id_reqh, 0, sizeof(read_msg1), &read_msg1, 0)) { BURP_error_redirect(status_vector, 25, NULL, NULL); // msg 25 Failed in put_blr_gen_id } } else { SLONG read_msg0; if (isc_receive(status_vector, &gen_id_reqh, 0, sizeof(read_msg0), &read_msg0, 0)) { BURP_error_redirect(status_vector, 25, NULL, NULL); // msg 25 Failed in put_blr_gen_id } read_msg1 = (SINT64) read_msg0; } isc_release_request(status_vector, &gen_id_reqh); return read_msg1; } void get_ranges( burp_fld* field) { /************************************** * * g e t _ r a n g e s * ************************************** * * Functional description * Fill in the range low and high bounds by reading * the ranges in rdb$field_dimensions. * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); SLONG* rp = field->fld_ranges; USHORT count = 0; // Get the array dimensions in the rdb$field_dimensions FOR (REQUEST_HANDLE tdgbl->handles_get_ranges_req_handle1) X IN RDB$FIELD_DIMENSIONS WITH X.RDB$FIELD_NAME EQ field->fld_source SORTED BY X.RDB$DIMENSION if (count != X.RDB$DIMENSION) BURP_error_redirect (NULL, 52, field->fld_name, NULL); // msg 52 array dimension for field %s is invalid *rp++ = X.RDB$LOWER_BOUND; *rp++ = X.RDB$UPPER_BOUND; count++; END_FOR; ON_ERROR general_on_error(); END_ERROR; if (count != field->fld_dimensions) BURP_error_redirect(NULL, 52, field->fld_name, NULL); // msg 52 array dimension for field %s is invalid } void put_array( burp_fld* field, burp_rel* relation, ISC_QUAD* blob_id) { /************************************** * * p u t _ a r r a y * ************************************** * * Functional description * Write out an array. If, however, it's null, don't even bother. * **************************************/ SLONG range_buffer[16]; // enough for 16 dimensions UCHAR blr_buffer[200]; // enough for a sdl with 16 dimensions BurpGlobals* tdgbl = BurpGlobals::getSpecific(); // If the array is null, don't store it. It will be restored as null. if (!blob_id->gds_quad_low && !blob_id->gds_quad_high) return; lstring xdr_buffer; xdr_buffer.lstr_allocated = 0; UCHAR* blr = blr_buffer; const SLONG* const end_ranges = field->fld_ranges + 2 * field->fld_dimensions; USHORT field_length = field->fld_length; if (tdgbl->gbl_sw_transportable) xdr_buffer.lstr_length = field_length + 3; // build the sdl add_byte(blr, isc_sdl_version1); add_byte(blr, isc_sdl_struct); add_byte(blr, 1); add_byte(blr, field->fld_type); if (field->fld_type == blr_short || field->fld_type == blr_long || field->fld_type == blr_quad || field->fld_type == blr_int64) { add_byte(blr, field->fld_scale); } if (field->fld_type == blr_text || field->fld_type == blr_varying) add_word(blr, field->fld_length); if (field->fld_type == blr_varying) field_length += sizeof(USHORT); add_byte(blr, isc_sdl_rid); add_word(blr, relation->rel_id); add_byte(blr, isc_sdl_fid); add_word(blr, field->fld_id); USHORT count; const SLONG* range; // used in multiple loops. for (range = field->fld_ranges, count = 0; range < end_ranges; range += 2, count++) { add_byte(blr, isc_sdl_do2); add_byte(blr, count); add_byte(blr, isc_sdl_long_integer); add_long(blr, range[0]); add_byte(blr, isc_sdl_long_integer); add_long(blr, range[1]); } add_byte(blr, isc_sdl_element); add_byte(blr, 1); add_byte(blr, isc_sdl_scalar); add_byte(blr, 0); add_byte(blr, field->fld_dimensions); for (count = 0; count < field->fld_dimensions; count++) { add_byte(blr, isc_sdl_variable); add_byte(blr, count); } add_byte(blr, isc_sdl_eoc); #ifdef DEBUG if (debug_on) PRETTY_print_sdl(blr_buffer, NULL, NULL, 0); #endif const USHORT blr_length = blr - blr_buffer; // compute the range size for each dimension = high_range - low_range ULONG slice_length = field_length; for (range = field->fld_ranges; range < end_ranges; range += 2) { slice_length *= (range[1] - range[0] + 1); if (tdgbl->gbl_sw_transportable) xdr_buffer.lstr_length *= (range[1] - range[0] + 1); } UCHAR* slice = BURP_alloc(slice_length); // allocate space for the XDR representation if (tdgbl->gbl_sw_transportable) { xdr_buffer.lstr_address = BURP_alloc(xdr_buffer.lstr_length); xdr_buffer.lstr_allocated = xdr_buffer.lstr_length; } ISC_STATUS_ARRAY status_vector; ULONG return_length = 0; if (isc_get_slice(status_vector, &DB, &gds_trans, blob_id, blr_length, (const char*) blr_buffer, 0, // param length for subset of an array handling NULL, // param for subset of an array handling slice_length, slice, (SLONG*) &return_length)) { 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 // CVC: At this point I would expected calls to deallocate memory // See the end of this function. BURP_free(slice); if (xdr_buffer.lstr_allocated) BURP_free(xdr_buffer.lstr_address); return; } if (return_length != slice_length) { int i1, i3; /* Ugh. The full array wasn't returned. We must recompute the top element to backup. */ SLONG returned_elements = (return_length / field_length) - 1; SLONG* returned_range = range_buffer; for (i1 = 0, i3 = 0, range = end_ranges - 2; range >= field->fld_ranges; range -= 2, returned_range++, i1++) { int divisor = 1; for (int i2 = (2 * (i1 + 1) + 1); i2 <= field->fld_dimensions * 2; i2 += 2) { divisor *= (field->fld_ranges[i2] - field->fld_ranges[i2 - 1] + 1); } *returned_range = (returned_elements - 1) / divisor + field->fld_ranges[i3]; returned_elements -= (*returned_range - field->fld_ranges[i3]) * divisor; i3 += 2; } } put(tdgbl, (UCHAR) (rec_array)); put_numeric(att_blob_field_number, field->fld_number); put_numeric(att_array_dimensions, field->fld_dimensions); SLONG* returned_range = range_buffer; for (range = field->fld_ranges; range < end_ranges; range += 2, returned_range++) { put_numeric(att_array_range_low, (int) range[0]); if (return_length == slice_length) put_numeric(att_array_range_high, (int) range[1]); else put_numeric(att_array_range_high, (int) *returned_range); } put(tdgbl, (UCHAR) (att_blob_data)); put(tdgbl, (UCHAR) (return_length)); put(tdgbl, (UCHAR) (return_length >> 8)); put(tdgbl, (UCHAR) (return_length >> 16)); put(tdgbl, (UCHAR) (return_length >> 24)); if (return_length) { UCHAR* p; if (tdgbl->gbl_sw_transportable) { lstring xdr_slice; xdr_slice.lstr_allocated = xdr_slice.lstr_length = return_length; xdr_slice.lstr_address = slice; return_length = CAN_slice(&xdr_buffer, &xdr_slice, TRUE, blr_length, blr_buffer); put(tdgbl, (UCHAR) (att_xdr_array)); put(tdgbl, (UCHAR) (return_length)); put(tdgbl, (UCHAR) (return_length >> 8)); put(tdgbl, (UCHAR) (return_length >> 16)); put(tdgbl, (UCHAR) (return_length >> 24)); p = xdr_buffer.lstr_address; } else p = slice; put_block(tdgbl, p, return_length); } BURP_free(slice); if (xdr_buffer.lstr_allocated) BURP_free(xdr_buffer.lstr_address); } void put_asciz( const SCHAR attribute, const TEXT* string) { /************************************** * * p u t _ a s c i z * ************************************** * * Functional description * Write an attribute starting with a null terminated string. * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); ULONG l = strlen(string); put(tdgbl, (UCHAR) (attribute)); put(tdgbl, (UCHAR) (l)); if (l) put_block(tdgbl, (const UCHAR*) string, l); } void put_blob( burp_fld* field, ISC_QUAD* blob_id, ULONG count) { /************************************** * * p u t _ b l o b * ************************************** * * Functional description * Write out a blob. If, however, it's null, don't even bother. * **************************************/ ISC_STATUS_ARRAY status_vector; UCHAR blob_info[32], static_buffer[1024]; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); // 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; // Open the blob and get it's vital statistics FB_API_HANDLE blob = 0; if (isc_open_blob(status_vector, &DB, &gds_trans, &blob, blob_id)) { BURP_print(81, field->fld_name, NULL, NULL, NULL, NULL); // msg 81 error accessing blob field %s -- continuing BURP_print_status(status_vector); return; } if (isc_blob_info(status_vector, &blob, sizeof(blob_items), blob_items, sizeof(blob_info), (char*) blob_info)) { BURP_error_redirect(status_vector, 20, NULL, NULL); // msg 20 isc_blob_info failed } put(tdgbl, (UCHAR) (rec_blob)); put_numeric(att_blob_field_number, field->fld_number); ULONG segments = 0; USHORT max_segment = 0; const UCHAR* p = blob_info; UCHAR item; while ((item = *p++) != isc_info_end) { const USHORT l = (USHORT) isc_vax_integer((const char*) p, 2); p += 2; const USHORT n = (USHORT) isc_vax_integer((const char*) p, l); p += l; switch (item) { case isc_info_blob_max_segment: put_numeric(att_blob_max_segment, (int) n); max_segment = n; break; case isc_info_blob_type: put_numeric(att_blob_type, (int) n); break; case isc_info_blob_num_segments: put_numeric(att_blob_number_segments, (int) n); segments = n; break; default: BURP_error_redirect(NULL, 21, (void*) (IPTR) item, NULL); // msg 21 don't understand blob info item %ld } } // Allocate a buffer large enough for the largest segment and start grinding. UCHAR* buffer; if (!max_segment || max_segment <= sizeof(static_buffer)) buffer = static_buffer; else buffer = BURP_alloc(max_segment); put(tdgbl, (UCHAR) (att_blob_data)); while (segments > 0) { USHORT segment_length; if (isc_get_segment(status_vector, &blob, &segment_length, max_segment, (char*) buffer)) { BURP_error_redirect(status_vector, 22, NULL, NULL); } // msg 22 isc_get_segment failed put(tdgbl, (UCHAR) (segment_length)); put(tdgbl, (UCHAR) (segment_length >> 8)); if (segment_length) { put_block(tdgbl, buffer, segment_length); } --segments; } if (isc_close_blob(status_vector, &blob)) BURP_error_redirect(status_vector, 23, NULL, NULL); // msg 23 isc_close_blob failed if (buffer != static_buffer) BURP_free(buffer); } bool put_blr_blob( SCHAR attribute, ISC_QUAD * blob_id) { /************************************** * * p u t _ b l r _ b l o b * ************************************** * * Functional description * Write out a blr blob, if present. Otherwise do nothing. * Return true is there was the blob was present, false otherwise. * **************************************/ ISC_STATUS_ARRAY status_vector; UCHAR blob_info[32], static_buffer[1024]; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); // 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 FB_API_HANDLE blob = 0; if (isc_open_blob(status_vector, &DB, &gds_trans, &blob, blob_id)) { BURP_error_redirect(status_vector, 24, NULL, NULL); // msg 24 isc_open_blob failed } if (isc_blob_info(status_vector, &blob, sizeof(blr_items), (SCHAR *) blr_items, sizeof(blob_info), (char*) blob_info)) { BURP_error_redirect(status_vector, 20, NULL, NULL); // msg 20 isc_blob_info failed } ULONG length = 0; USHORT max_segment = 0; const UCHAR* p = blob_info; UCHAR item; while ((item = *p++) != isc_info_end) { const USHORT l = (USHORT) isc_vax_integer((const char*) p, 2); p += 2; const USHORT n = (USHORT) isc_vax_integer((const char*) p, l); p += l; switch (item) { case isc_info_blob_max_segment: max_segment = n; break; case isc_info_blob_total_length: length = n; break; default: BURP_print(79, (void *) (IPTR) item, NULL, NULL, NULL, NULL); // msg 79 don't understand blob info item %ld return false; } } if (!length) { if (isc_close_blob(status_vector, &blob)) BURP_error_redirect(status_vector, 23, NULL, NULL); // msg 23 isc_close_blob failed return false; } // Rdb sometimes gets the length messed up if (length < max_segment) length = max_segment; put_numeric(attribute, (int) length); // Allocate a buffer large enough for the largest segment and start grinding. UCHAR* buffer; if (!max_segment || max_segment <= sizeof(static_buffer)) buffer = static_buffer; else buffer = BURP_alloc(max_segment); USHORT segment_length; while (!isc_get_segment(status_vector, &blob, &segment_length, (USHORT) max_segment, (char*) buffer)) { if (segment_length) { put_block(tdgbl, buffer, segment_length); } } if (isc_close_blob(status_vector, &blob)) { BURP_error_redirect(status_vector, 23, NULL, NULL); } // msg 23 isc_close_blob failed if (buffer != static_buffer) BURP_free(buffer); return true; } void put_data(burp_rel* relation) { /************************************** * * p u t _ d a t a * ************************************** * * Functional description * Write relation meta-data and data. * **************************************/ burp_fld* field; ISC_STATUS_ARRAY status_vector; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); // CVC: A signed short isn't enough if the engine allows near 32K fields, // each being char(1) ASCII in the worst case. Looking at BLR generation // below, it's clear an extreme case won't compile => blr_length >= 32K. // However, SSHORT is the limit for request_length in isc_compile_request. SSHORT field_count = 1; for (field = relation->rel_fields; field; field = field->fld_next) { if (!(field->fld_flags & FLD_computed)) { field_count += 2; } } /* Time to generate blr to fetch data. Make sure we allocate a BLR buffer large enough to handle the per field overhead */ UCHAR* const blr_buffer = BURP_alloc(200 + field_count * 9); UCHAR* blr = blr_buffer; add_byte(blr, blr_version4); add_byte(blr, blr_begin); add_byte(blr, blr_message); add_byte(blr, 0); // Message number add_word(blr, field_count); // Number of fields, counting eof RCRD_OFFSET offset = 0; SSHORT count = 0; // This is param count. for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; SSHORT alignment = 4; FLD_LENGTH length = field->fld_length; SSHORT dtype = field->fld_type; if (field->fld_flags & FLD_array) { dtype = blr_blob; length = 8; } switch (dtype) { case blr_text: alignment = type_alignments[dtype_text]; add_byte(blr, field->fld_type); add_word(blr, field->fld_length); break; case blr_varying: alignment = type_alignments[dtype_varying]; add_byte(blr, field->fld_type); add_word(blr, field->fld_length); length += sizeof(USHORT); break; case blr_short: alignment = type_alignments[dtype_short]; add_byte(blr, field->fld_type); add_byte(blr, field->fld_scale); break; case blr_long: alignment = type_alignments[dtype_long]; add_byte(blr, field->fld_type); add_byte(blr, field->fld_scale); break; case blr_quad: alignment = type_alignments[dtype_quad]; add_byte(blr, field->fld_type); add_byte(blr, field->fld_scale); break; case blr_int64: alignment = type_alignments[dtype_int64]; add_byte(blr, field->fld_type); add_byte(blr, field->fld_scale); break; case blr_double: alignment = type_alignments[dtype_double]; add_byte(blr, field->fld_type); break; case blr_timestamp: alignment = type_alignments[dtype_timestamp]; add_byte(blr, field->fld_type); break; case blr_sql_time: alignment = type_alignments[dtype_sql_time]; add_byte(blr, field->fld_type); break; case blr_sql_date: alignment = type_alignments[dtype_sql_date]; add_byte(blr, field->fld_type); break; case blr_float: alignment = type_alignments[dtype_real]; add_byte(blr, field->fld_type); break; case blr_blob: alignment = type_alignments[dtype_blob]; add_byte(blr, blr_quad); add_byte(blr, 0); break; default: BURP_error_redirect(NULL, 26, (void *) (IPTR) field->fld_type, NULL); // msg 26 datatype %ld not understood break; } if (alignment) offset = FB_ALIGN(offset, alignment); field->fld_offset = offset; field->fld_parameter = count++; offset += length; } // Next, build fields for null flags for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; add_byte(blr, blr_short); add_byte(blr, 0); offset = FB_ALIGN(offset, sizeof(SSHORT)); field->fld_missing_parameter = count++; offset += sizeof(SSHORT); } // Finally, make up an EOF field add_byte(blr, blr_short); // eof field add_byte(blr, 0); // scale for eof field SSHORT eof_parameter = count++; RCRD_OFFSET record_length = offset; RCRD_OFFSET eof_offset = FB_ALIGN(offset, sizeof(SSHORT)); // To be used later for the buffer size to receive data const FLD_LENGTH length = (USHORT) (eof_offset + sizeof(SSHORT)); // Build FOR loop, body, and eof handler add_byte(blr, blr_for); add_byte(blr, blr_rse); add_byte(blr, 1); // count of relations add_byte(blr, blr_rid); add_word(blr, relation->rel_id); add_byte(blr, 0); // context variable add_byte(blr, blr_end); add_byte(blr, blr_send); add_byte(blr, 0); add_byte(blr, blr_begin); add_byte(blr, blr_assignment); add_byte(blr, blr_literal); add_byte(blr, blr_short); add_byte(blr, 0); add_word(blr, 1); add_byte(blr, blr_parameter); add_byte(blr, 0); add_word(blr, eof_parameter); for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_computed) continue; add_byte(blr, blr_assignment); add_byte(blr, blr_fid); add_byte(blr, 0); add_word(blr, field->fld_id); add_byte(blr, blr_parameter2); add_byte(blr, 0); add_word(blr, field->fld_parameter); add_word(blr, field->fld_missing_parameter); } add_byte(blr, blr_end); add_byte(blr, blr_send); add_byte(blr, 0); add_byte(blr, blr_assignment); add_byte(blr, blr_literal); add_byte(blr, blr_short); add_byte(blr, 0); add_word(blr, 0); add_byte(blr, blr_parameter); add_byte(blr, 0); add_word(blr, eof_parameter); add_byte(blr, blr_end); add_byte(blr, blr_eoc); #ifdef DEBUG if (debug_on) isc_print_blr((const char*)blr_buffer, NULL, NULL, 0); #endif // Compile request FB_API_HANDLE request = 0; SSHORT blr_length = blr - blr_buffer; if (isc_compile_request(status_vector, &DB, &request, blr_length, (const SCHAR*) blr_buffer)) { BURP_error_redirect(status_vector, 27, NULL, NULL); // msg 27 isc_compile_request failed isc_print_blr((const char*) blr_buffer, NULL, NULL, 0); } BURP_free(blr_buffer); BURP_verbose(142, relation->rel_name, NULL, NULL, NULL, NULL); // msg 142 writing data for relation %s if (isc_start_request(status_vector, &request, &gds_trans, 0)) { BURP_error_redirect(status_vector, 28, NULL, NULL); // msg 28 isc_start_request failed } /* Here is the crux of the problem -- writing data. All this work for the following small loop. */ UCHAR* buffer = BURP_alloc(length); SSHORT* eof = (SSHORT *) (buffer + eof_offset); // the XDR representation may be even fluffier lstring xdr_buffer; if (tdgbl->gbl_sw_transportable) { xdr_buffer.lstr_length = xdr_buffer.lstr_allocated = length + count * 3; xdr_buffer.lstr_address = BURP_alloc(xdr_buffer.lstr_length); } else xdr_buffer.lstr_address = NULL; ULONG records = 0; while (true) { if (isc_receive(status_vector, &request, 0, length, buffer, 0)) { BURP_error_redirect(status_vector, 29, NULL, NULL); // msg 29 isc_receive failed } if (!*eof) break; records++; // Verbose records if ((records % BACKUP_VERBOSE_INTERVAL) == 0) BURP_verbose(108, (void *) (IPTR) records, NULL, NULL, NULL, NULL); put(tdgbl, (UCHAR) (rec_data)); put_numeric(att_data_length, record_length); const UCHAR* p; if (tdgbl->gbl_sw_transportable) { record_length = CAN_encode_decode(relation, &xdr_buffer, buffer, TRUE); put_numeric(att_xdr_length, record_length); p = xdr_buffer.lstr_address; } else p = buffer; put(tdgbl, (UCHAR) (att_data_data)); if (tdgbl->gbl_sw_compress) compress(p, record_length); else if (record_length) put_block(tdgbl, p, record_length); // Look for any blobs to write for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_type == blr_blob && !(field->fld_flags & FLD_computed) && !(field->fld_flags & FLD_array)) { put_blob(field, (ISC_QUAD*) (buffer + field-> fld_offset), records); } } // Look for any array to write // we got back the blob_id for the array from isc_receive in the second param. for (field = relation->rel_fields; field; field = field->fld_next) { if (field->fld_flags & FLD_array) { put_array(field, relation, (ISC_QUAD *) (buffer + field->fld_offset)); } } } BURP_free(buffer); if (xdr_buffer.lstr_address) BURP_free(xdr_buffer.lstr_address); BURP_verbose(108, (void *) (IPTR) records, NULL, NULL, NULL, NULL); // msg 108 %ld records written if (isc_release_request(status_vector, &request)) BURP_error_redirect(status_vector, 30, NULL, NULL); // msg 30 isc_release_request failed } void put_index( burp_rel* relation) { /************************************** * * p u t _ i n d e x * ************************************** * * Functional description * Write information about an index. First * check that all the segments of the * index exist. * **************************************/ ULONG count; TEXT temp[GDS_NAME_LEN]; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); /* if we have all capabilities, use the first request to get the most performance out of the latest engine; if we don't have one of the capabilities we must use the second set of requests--this requires more code but it is well worth it for the performance benefits, especially remotely--deej */ if ((tdgbl->BCK_capabilities & BCK_idx_inactive) && (tdgbl->BCK_capabilities & BCK_attributes_v3) && (tdgbl->BCK_capabilities & BCK_ods8)) { FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle1) X IN RDB$INDICES WITH X.RDB$RELATION_NAME EQ relation->rel_name count = 0; FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle2) I_S IN RDB$INDEX_SEGMENTS CROSS RFR IN RDB$RELATION_FIELDS WITH I_S.RDB$FIELD_NAME = RFR.RDB$FIELD_NAME AND I_S.RDB$INDEX_NAME = X.RDB$INDEX_NAME AND RFR.RDB$RELATION_NAME = relation->rel_name count++; END_FOR; ON_ERROR general_on_error(); END_ERROR; if (count != (ULONG) X.RDB$SEGMENT_COUNT) { BURP_print(180, X.RDB$INDEX_NAME, (void*)(IPTR)count, (void*)(IPTR)X.RDB$SEGMENT_COUNT, NULL, NULL); continue; } put(tdgbl, rec_index); const ULONG l = PUT_TEXT (att_index_name, X.RDB$INDEX_NAME); MISC_terminate (X.RDB$INDEX_NAME, temp, l, sizeof(temp)); BURP_verbose (151, temp, NULL, NULL, NULL, NULL); // msg 151 writing index %s put_numeric (att_segment_count, X.RDB$SEGMENT_COUNT); put_numeric (att_index_inactive, X.RDB$INDEX_INACTIVE); put_numeric (att_index_unique_flag, X.RDB$UNIQUE_FLAG); FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle5) Y IN RDB$INDEX_SEGMENTS WITH Y.RDB$INDEX_NAME EQ X.RDB$INDEX_NAME SORTED BY Y.RDB$FIELD_POSITION PUT_TEXT (att_index_field_name, Y.RDB$FIELD_NAME); END_FOR; ON_ERROR general_on_error(); END_ERROR; put_source_blob (att_index_description2, att_index_description, &X.RDB$DESCRIPTION); put_numeric (att_index_type, X.RDB$INDEX_TYPE); if (!X.RDB$EXPRESSION_SOURCE.NULL) put_source_blob (att_index_expression_source, att_index_expression_source, &X.RDB$EXPRESSION_SOURCE); if (!X.RDB$EXPRESSION_BLR.NULL) put_blr_blob (att_index_expression_blr, &X.RDB$EXPRESSION_BLR); if (!X.RDB$FOREIGN_KEY.NULL) PUT_TEXT (att_index_foreign_key, X.RDB$FOREIGN_KEY); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; } else { FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle1) X IN RDB$INDICES WITH X.RDB$RELATION_NAME EQ relation->rel_name count = 0; FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle2) I_S IN RDB$INDEX_SEGMENTS WITH I_S.RDB$INDEX_NAME = X.RDB$INDEX_NAME bool match = false; FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle3) RFR IN RDB$RELATION_FIELDS WITH I_S.RDB$FIELD_NAME = RFR.RDB$FIELD_NAME AND RFR.RDB$RELATION_NAME = relation->rel_name match = true; END_FOR; ON_ERROR general_on_error(); END_ERROR; if (!match) BURP_print (179, I_S.RDB$FIELD_NAME, X.RDB$INDEX_NAME, NULL, NULL, NULL); else count++; END_FOR; ON_ERROR general_on_error(); END_ERROR; if (count != (ULONG) X.RDB$SEGMENT_COUNT) { BURP_print(180, X.RDB$INDEX_NAME, (void*)(IPTR) count, (void*)(IPTR) X.RDB$SEGMENT_COUNT, NULL, NULL); continue; } put(tdgbl, rec_index); const ULONG l = PUT_TEXT (att_index_name, X.RDB$INDEX_NAME); MISC_terminate (X.RDB$INDEX_NAME, temp, l, sizeof(temp)); BURP_verbose (151, temp, NULL, NULL, NULL, NULL); // msg 151 writing index %s put_numeric (att_segment_count, X.RDB$SEGMENT_COUNT); if (tdgbl->BCK_capabilities & BCK_idx_inactive) FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle4) I IN RDB$INDICES WITH I.RDB$INDEX_NAME = X.RDB$INDEX_NAME put_numeric (att_index_inactive, I.RDB$INDEX_INACTIVE); END_FOR; ON_ERROR general_on_error(); END_ERROR; put_numeric (att_index_unique_flag, X.RDB$UNIQUE_FLAG); FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle5) Y IN RDB$INDEX_SEGMENTS WITH Y.RDB$INDEX_NAME EQ X.RDB$INDEX_NAME SORTED BY Y.RDB$FIELD_POSITION PUT_TEXT (att_index_field_name, Y.RDB$FIELD_NAME); END_FOR; ON_ERROR general_on_error(); END_ERROR; put_source_blob (att_index_description2, att_index_description, &X.RDB$DESCRIPTION); if (tdgbl->BCK_capabilities & BCK_attributes_v3) FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle6) I IN RDB$INDICES WITH I.RDB$INDEX_NAME = X.RDB$INDEX_NAME put_numeric (att_index_type, I.RDB$INDEX_TYPE); END_FOR; ON_ERROR general_on_error(); END_ERROR; if (tdgbl->BCK_capabilities & BCK_ods8) FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle7) I IN RDB$INDICES WITH I.RDB$INDEX_NAME = X.RDB$INDEX_NAME if (!I.RDB$EXPRESSION_SOURCE.NULL) put_source_blob (att_index_expression_source, att_index_expression_source, &I.RDB$EXPRESSION_SOURCE); if (!I.RDB$EXPRESSION_BLR.NULL) put_blr_blob (att_index_expression_blr, &I.RDB$EXPRESSION_BLR); if (!I.RDB$FOREIGN_KEY.NULL) PUT_TEXT (att_index_foreign_key, I.RDB$FOREIGN_KEY); END_FOR; ON_ERROR general_on_error(); END_ERROR; put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; } } int put_message( SCHAR attribute, const TEXT* text, ULONG length) { /************************************** * * p u t _ m e s s a g e * ************************************** * * Functional description * Write a variable length text string, with embedded * blanks. Same as put_text but handles embedded blanks. * CVC: As v6 time, put_text handles embedded blanks, too! * The only difference is that put_text's length is SSHORT, so * in theory put_message can handle much longer input and it's * used for exception & trigger's messages (plus update/delete * rules for FKs and constraint types, where it's irrelevant * which function of the two you use). * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); ULONG l = 0; for (const TEXT* p = text; *p && l < length; p++) l++; l = length = MIN(l, length); put(tdgbl, (UCHAR) (attribute)); put(tdgbl, (UCHAR) (l)); if (l) put_block(tdgbl, reinterpret_cast(text), l); return length; } void put_numeric( SCHAR attribute, SLONG value) { /************************************** * * p u t _ n u m e r i c * ************************************** * * Functional description * Write a numeric value as an attribute. The number is represented * low byte first, high byte last, as in VAX. * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); const SLONG vax_value = (SLONG) isc_vax_integer((const char*) &value, sizeof(value)); put(tdgbl, (UCHAR) (attribute)); put(tdgbl, (UCHAR) sizeof(value)); put_block(tdgbl, (const UCHAR*) &vax_value, sizeof(vax_value)); } void put_int64( SCHAR attribute, SINT64 value) { /************************************** * * p u t _ i n t 6 4 * ************************************** * * Functional description * Write a 64-bit numeric value as an attribute. * The number is represented low byte first, high byte last, as in VAX. * This function is just like put_numeric, except that it handles an * INT64 value, while put_numeric handles a 32-bit value. * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); const UINT64 le_value = (UINT64) isc_portable_integer((const UCHAR*) &value, sizeof(value)); put(tdgbl, (UCHAR) (attribute)); put(tdgbl, (UCHAR) (sizeof(value))); put_block(tdgbl, (const UCHAR*) &le_value, sizeof(le_value)); } void put_relation( burp_rel* relation) { /************************************** * * p u t _ r e l a t i o n * ************************************** * * Functional description * Write relation meta-data and data. * **************************************/ TEXT temp[GDS_NAME_LEN]; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); /* Write local field information. This is made slightly more complicated by the requirement that computational fields be aligned. */ burp_fld* aligned = NULL; burp_fld* unaligned = NULL; burp_fld* aligned4 = NULL; burp_fld* aligned8 = NULL; burp_fld* fields = get_fields(relation); // sort the list of fields into three lists, depending on alignment burp_fld* field; for (field = fields; field = fields;) { fields = field->fld_next; USHORT l = field->fld_length; if (field->fld_type == blr_varying) l += sizeof(USHORT); if (!(l & 7)) { field->fld_next = aligned8; aligned8 = field; } else if (!(l & 3)) { field->fld_next = aligned4; aligned4 = field; } else if (l & 1) { field->fld_next = unaligned; unaligned = field; } else { field->fld_next = aligned; aligned = field; } } /* Next, merge the aligned and unaligned sub-lists. In the process, re-create (approximately) the original order of the fields. This is not strictly required, but it certainly is polite. */ while (field = unaligned) { unaligned = field->fld_next; field->fld_next = relation->rel_fields; relation->rel_fields = field; } while (field = aligned) { aligned = field->fld_next; field->fld_next = relation->rel_fields; relation->rel_fields = field; } while (field = aligned4) { aligned4 = field->fld_next; field->fld_next = relation->rel_fields; relation->rel_fields = field; } while (field = aligned8) { aligned8 = field->fld_next; field->fld_next = relation->rel_fields; relation->rel_fields = field; } // Now write the fields in what will become physical backup order for (field = relation->rel_fields; field; field = field->fld_next) { put(tdgbl, (UCHAR) (rec_field)); const USHORT l = PUT_TEXT(att_field_name, field->fld_name); MISC_terminate(field->fld_name, temp, l, sizeof(temp)); BURP_verbose(144, temp, NULL, NULL, NULL, NULL); // msg 144 writing field %s PUT_TEXT(att_field_source, field->fld_source); if (field->fld_query_name[0]) PUT_TEXT(att_field_query_name, field->fld_query_name); if (field->fld_complex_name[0]) PUT_TEXT(att_field_complex_name, field->fld_complex_name); if (field->fld_edit_string[0]) PUT_TEXT(att_field_edit_string, field->fld_edit_string); put_source_blob(att_field_description2, att_field_description, & field->fld_description); put_source_blob(att_field_query_header, att_field_query_header, & field->fld_query_header); if (field->fld_security_class[0]) PUT_TEXT(att_field_security_class, field->fld_security_class); if (!(field->fld_flags & FLD_position_missing)) put_numeric(att_field_position, field->fld_position); put_numeric(att_field_type, field->fld_type); put_numeric(att_field_length, field->fld_length); put_numeric(att_field_sub_type, field->fld_sub_type); put_numeric(att_field_scale, field->fld_scale); put_numeric(att_field_number, field->fld_number); put_numeric(att_field_system_flag, field->fld_system_flag); if (!(field->fld_flags & FLD_update_missing)) put_numeric(att_field_update_flag, field->fld_update_flag); if (field->fld_flags & FLD_null_flag) put_numeric(att_field_null_flag, field->fld_null_flag); if (field->fld_flags & FLD_charset_flag) put_numeric(att_field_character_set, field->fld_character_set_id); if (field->fld_flags & FLD_collate_flag) put_numeric(att_field_collation_id, field->fld_collation_id); put_blr_blob(att_field_default_value, & field->fld_default_value); put_source_blob(att_field_default_source, att_field_default_source, & field->fld_default_source); if (relation->rel_flags & REL_view) { put_numeric(att_view_context, field->fld_view_context); PUT_TEXT(att_base_field, field->fld_base); } if (field->fld_flags & FLD_computed) put_numeric(att_field_computed_flag, TRUE); if (field->fld_flags & FLD_array) { put_numeric(att_field_dimensions, field->fld_dimensions); const SLONG* rp = field->fld_ranges; for (USHORT n = field->fld_dimensions; n; rp += 2, n--) { put_numeric(att_field_range_low, *rp); put_numeric(att_field_range_high, *(rp + 1)); } } put(tdgbl, (UCHAR) (att_end)); } // Write out view relations (if a view, of course) if (relation->rel_flags & REL_view) { if (tdgbl->BCK_capabilities & BCK_context_name) { FOR (REQUEST_HANDLE tdgbl->handles_put_relation_req_handle1) X IN RDB$VIEW_RELATIONS WITH X.RDB$VIEW_NAME EQ relation->rel_name put(tdgbl, rec_view); PUT_TEXT (att_view_relation_name, X.RDB$RELATION_NAME); put_numeric (att_view_context_id, X.RDB$VIEW_CONTEXT); PUT_TEXT (att_view_context_name, X.RDB$CONTEXT_NAME); put(tdgbl, att_end); END_FOR ON_ERROR general_on_error(); END_ERROR; } else { FOR (REQUEST_HANDLE tdgbl->handles_put_relation_req_handle2) X IN RDB$VIEW_RELATIONS WITH X.RDB$VIEW_NAME EQ relation->rel_name put(tdgbl, rec_view); PUT_TEXT (att_view_relation_name, X.RDB$RELATION_NAME); put_numeric (att_view_context_id, X.RDB$VIEW_CONTEXT); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; } } put(tdgbl, (UCHAR) (rec_relation_end)); } bool put_source_blob(SCHAR attribute, SCHAR old_attribute, ISC_QUAD* blob_id) { /************************************** * * p u t _ s o u r c e _ b l o b * ************************************** * * Functional description * Write out a source blob or query header if present. * Return true is there was the blob was present, false otherwise. * **************************************/ ISC_STATUS_ARRAY status_vector; UCHAR blob_info[48], static_buffer[1024]; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); // 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; if (tdgbl->gbl_sw_old_descriptions && attribute != att_field_query_header) return put_blr_blob(old_attribute, blob_id); // Open the blob and get it's vital statistics FB_API_HANDLE blob = 0; if (isc_open_blob(status_vector, &DB, &gds_trans, &blob, blob_id)) { BURP_error_redirect(status_vector, 24, NULL, NULL); // msg 24 isc_open_blob failed } if (isc_blob_info(status_vector, &blob, sizeof(source_items), (SCHAR *) source_items, sizeof(blob_info), (SCHAR*) blob_info)) { BURP_error_redirect(status_vector, 20, NULL, NULL); // msg 20 isc_blob_info failed } SLONG length = 0; USHORT max_segment = 0, num_seg = 0; const UCHAR* p = blob_info; UCHAR item; while ((item = *p++) != isc_info_end) { const USHORT l = (USHORT) isc_vax_integer((const SCHAR*) p, 2); p += 2; const USHORT n = (USHORT) isc_vax_integer((const SCHAR*) p, l); p += l; switch (item) { case isc_info_blob_max_segment: max_segment = n; break; case isc_info_blob_total_length: length = n; break; case isc_info_blob_num_segments: num_seg = n; break; default: BURP_print(79, (void *)(IPTR) item, NULL, NULL, NULL, NULL); // msg 79 don't understand blob info item %ld return false; } } if (!length) { if (isc_close_blob(status_vector, &blob)) { BURP_error_redirect(status_vector, 23, NULL, NULL); // msg 23 isc_close_blob failed } return false; } // Rdb sometimes gets the length messed up if (length < max_segment) length = max_segment; put_numeric(attribute, length + num_seg); // Allocate a buffer large enough for the largest segment and start grinding. UCHAR* buffer; if (!max_segment || max_segment <= sizeof(static_buffer)) buffer = static_buffer; else buffer = BURP_alloc(max_segment); USHORT segment_length; while (!isc_get_segment(status_vector, &blob, &segment_length, max_segment, (SCHAR*) buffer)) { if (segment_length) { put_block(tdgbl, buffer, segment_length); } put(tdgbl, (UCHAR) (NULL)); } if (isc_close_blob(status_vector, &blob)) BURP_error_redirect(status_vector, 23, NULL, NULL); // msg 23 isc_close_blob failed if (buffer != static_buffer) BURP_free(buffer); return true; } int put_text( SCHAR attribute, const TEXT* text, SSHORT size_len) { /************************************** * * p u t _ t e x t * ************************************** * * Functional description * Write a variable length text string, with embedded spaces. * Truncate trailing spaces. * Now this routine does not truncate trailing spaces, 3-2002 MOD * transfering changes from fb1, I believe this is to do with problems * with quoted names and embedded spaces. * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); const SSHORT l = (SSHORT) symbol_length (text, (ULONG) size_len); put(tdgbl, (UCHAR) (attribute)); put(tdgbl, (UCHAR) (l)); if (l) put_block(tdgbl, (const UCHAR*) text, l); return l; } void set_capabilities(void) { /************************************** * * s e t _ c a p a b i l i t i e s * ************************************** * * Functional description * * set the capabilities bits for the * database being extracted to avoid * unpleasantness later. * **************************************/ BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FB_API_HANDLE req = 0; // Look for desireable fields in system relations for (const rfr_tab_t* rel_field_table = rfr_table; rel_field_table->relation; rel_field_table++) { const TEXT* field = rel_field_table->field; const TEXT* relation = rel_field_table->relation; FOR (REQUEST_HANDLE req) x IN RDB$RELATION_FIELDS WITH x.RDB$RELATION_NAME = relation AND x.RDB$FIELD_NAME = field tdgbl->BCK_capabilities |= rel_field_table->bit_mask; END_FOR; ON_ERROR general_on_error(); END_ERROR; } isc_release_request(isc_status, &req); } int symbol_length( const TEXT * symbol, ULONG size_len) { /************************************** * * s y m b o l _ l e n g t h * ************************************** * * Functional description * Compute length of null terminated symbol. * CVC: This function should acknowledge embedded blanks. * **************************************/ if (size_len < 2) { return 0; } --size_len; const TEXT* p = symbol; const TEXT* const q = p + size_len; while (*p && p < q) { // find end of string (null or end). p++; } --p; while (p >= symbol && *p == ' ') { // skip trailing blanks --p; } return p + 1 - symbol; } void write_character_sets(void) { /************************************** * * w r i t e _ c h a r a c t e r _ s e t s * ************************************** * * Functional description * write a record in the burp file for * each user defined character set. * **************************************/ isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$CHARACTER_SETS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 put(tdgbl, rec_charset); PUT_TEXT (att_charset_name, X.RDB$CHARACTER_SET_NAME); if (!X.RDB$FORM_OF_USE.NULL) PUT_TEXT (att_charset_form, X.RDB$FORM_OF_USE); if (!X.RDB$NUMBER_OF_CHARACTERS.NULL) put_numeric (att_charset_numchar, X.RDB$NUMBER_OF_CHARACTERS); PUT_TEXT (att_charset_coll, X.RDB$DEFAULT_COLLATE_NAME); put_numeric (att_charset_id, X.RDB$CHARACTER_SET_ID); if (X.RDB$SYSTEM_FLAG) put_numeric (att_charset_sysflag, X.RDB$SYSTEM_FLAG); if (!X.RDB$DESCRIPTION.NULL) put_source_blob (att_charset_description, att_charset_description, &X.RDB$DESCRIPTION); if (!X.RDB$FUNCTION_NAME.NULL) PUT_TEXT (att_charset_funct, X.RDB$FUNCTION_NAME); put_numeric (att_charset_bytes_char, X.RDB$BYTES_PER_CHARACTER); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_check_constraints(void) { /************************************** * * w r i t e _ c h e c k _ c o n s t r a i n t s * ************************************** * * Functional description * write a record in the burp file for * each check constraint. * **************************************/ isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$CHECK_CONSTRAINTS put(tdgbl, rec_chk_constraint); PUT_TEXT (att_chk_constraint_name, X.RDB$CONSTRAINT_NAME); if (!(X.RDB$TRIGGER_NAME.NULL)) PUT_TEXT (att_chk_trigger_name, X.RDB$TRIGGER_NAME); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_collations(void) { /************************************** * * w r i t e _ c o l l a t i o n s * ************************************** * * Functional description * write a record in the burp file for * each user defined collation * **************************************/ isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$COLLATIONS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 put(tdgbl, rec_collation); PUT_TEXT (att_coll_name, X.RDB$COLLATION_NAME); put_numeric (att_coll_id, X.RDB$COLLATION_ID); put_numeric (att_coll_cs_id, X.RDB$CHARACTER_SET_ID); put_numeric (att_coll_attr, X.RDB$COLLATION_ATTRIBUTES); if (X.RDB$SYSTEM_FLAG) put_numeric (att_coll_sysflag, X.RDB$SYSTEM_FLAG); if (!X.RDB$DESCRIPTION.NULL) put_source_blob (att_coll_description, att_coll_description, &X.RDB$DESCRIPTION); if (!X.RDB$FUNCTION_NAME.NULL) PUT_TEXT (att_coll_funct, X.RDB$FUNCTION_NAME); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_database( const TEXT* dbb_file) { /************************************** * * w r i t e _ d a t a b a s e * ************************************** * * Functional description * write a physical database record and a * logical database record in the burp file for * the database itself. * **************************************/ ISC_STATUS_ARRAY status_vector; SCHAR buffer[256]; isc_req_handle req_handle1 = 0, req_handle2 = 0, req_handle3 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); put(tdgbl, (UCHAR) (rec_physical_db)); if (isc_database_info(status_vector, &DB, sizeof(db_info_items), db_info_items, sizeof(buffer), buffer)) { BURP_error_redirect(status_vector, 31, NULL, NULL); // msg 31 isc_database_info failed } USHORT page_size = 0, forced_writes, no_reserve, SQL_dialect, db_read_only; ULONG sweep_interval, page_buffers; USHORT length = 0; for (const SCHAR* d = buffer; *d != isc_info_end; d += length) { const SCHAR item = *d++; length = (USHORT) isc_vax_integer(d, 2); d += 2; switch (item) { case isc_info_end: break; case isc_info_page_size: page_size = (USHORT) isc_vax_integer(d, length); put_numeric(att_page_size, page_size); break; case isc_info_sweep_interval: sweep_interval = isc_vax_integer(d, length); put_numeric(att_sweep_interval, sweep_interval); break; case isc_info_forced_writes: forced_writes = (USHORT) isc_vax_integer(d, length); put_numeric(att_forced_writes, forced_writes); break; case isc_info_no_reserve: if (no_reserve = (USHORT) isc_vax_integer(d, length)) put_numeric(att_no_reserve, no_reserve); break; case isc_info_set_page_buffers: if (page_buffers = isc_vax_integer(d, length)) put_numeric(att_page_buffers, page_buffers); break; case isc_info_error: // old server does not understand new isc_info break; // parameter and returns isc_info_error. skip it case isc_info_db_sql_dialect: SQL_dialect = (USHORT) isc_vax_integer(d, length); put_numeric(att_SQL_dialect, SQL_dialect); break; case isc_info_db_read_only: if (db_read_only = (USHORT) isc_vax_integer(d, length)) put_numeric(att_db_read_only, db_read_only); break; default: BURP_error_redirect(status_vector, 31, NULL, NULL); // msg 31 isc_database_info failed break; } } put_asciz(att_file_name, dbb_file); BURP_verbose(77, dbb_file, (void *) (IPTR) page_size, NULL, NULL, NULL); // msg 77 database %s has a page size of %ld bytes. put(tdgbl, (UCHAR) (att_end)); put(tdgbl, (UCHAR) (rec_database)); /* if we have all capabilities, use the first request to get the most performance out of the latest engine; if we don't have one of the capabilities we must use the second set of requests--this requires more code but it is well worth it for the performance benefits, especially remotely--deej */ if ((tdgbl->BCK_capabilities & BCK_security) && (tdgbl->BCK_capabilities & BCK_db_description) && (tdgbl->BCK_capabilities & BCK_ods8)) { FOR (REQUEST_HANDLE req_handle1) D IN RDB$DATABASE if (!D.RDB$SECURITY_CLASS.NULL) PUT_TEXT (att_database_security_class, D.RDB$SECURITY_CLASS); put_source_blob (att_database_description2, att_database_description, &D.RDB$DESCRIPTION); if (!D.RDB$CHARACTER_SET_NAME.NULL) PUT_TEXT (att_database_dfl_charset, D.RDB$CHARACTER_SET_NAME); END_FOR; ON_ERROR general_on_error(); END_ERROR; } else { if (tdgbl->BCK_capabilities & BCK_security) { FOR (REQUEST_HANDLE req_handle1) D IN RDB$DATABASE if (!D.RDB$SECURITY_CLASS.NULL) PUT_TEXT (att_database_security_class, D.RDB$SECURITY_CLASS); END_FOR; ON_ERROR general_on_error(); END_ERROR; } if (tdgbl->BCK_capabilities & BCK_db_description) { FOR (REQUEST_HANDLE req_handle2) D IN RDB$DATABASE put_source_blob (att_database_description2, att_database_description, &D.RDB$DESCRIPTION); END_FOR; ON_ERROR general_on_error(); END_ERROR; } if (tdgbl->BCK_capabilities & BCK_ods8) { FOR (REQUEST_HANDLE req_handle3) D IN RDB$DATABASE if (!D.RDB$CHARACTER_SET_NAME.NULL) PUT_TEXT (att_database_dfl_charset, D.RDB$CHARACTER_SET_NAME); END_FOR; ON_ERROR general_on_error(); END_ERROR; } } MISC_release_request_silent(req_handle1); MISC_release_request_silent(req_handle2); MISC_release_request_silent(req_handle3); put(tdgbl, (UCHAR) (att_end)); } void write_exceptions(void) { /************************************** * * w r i t e _ e x c e p t i o n s * ************************************** * * Functional description * write a record in the burp file for * each exception. * **************************************/ TEXT temp[GDS_NAME_LEN]; isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$EXCEPTIONS put(tdgbl, rec_exception); const SSHORT l = PUT_TEXT (att_exception_name, X.RDB$EXCEPTION_NAME); MISC_terminate (X.RDB$EXCEPTION_NAME, temp, l, sizeof(temp)); BURP_verbose (198, temp, NULL, NULL, NULL, NULL); // msg 198 writing exception %s PUT_MESSAGE(att_exception_msg, X.RDB$MESSAGE); put_source_blob (att_exception_description2, att_procedure_description, &X.RDB$DESCRIPTION); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_field_dimensions(void) { /************************************** * * w r i t e _ f i e l d _ d i m e n s i o n s * ************************************** * * Functional description * write a record in the burp file for * each array field dimension. * **************************************/ isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$FIELD_DIMENSIONS put(tdgbl, rec_field_dimensions); PUT_TEXT (att_field_name, X.RDB$FIELD_NAME); put_numeric (att_field_dimensions, X.RDB$DIMENSION); put_numeric (att_field_range_low, X.RDB$LOWER_BOUND); put_numeric (att_field_range_high, X.RDB$UPPER_BOUND); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_filters(void) { /************************************** * * w r i t e _ f i l t e r s * ************************************** * * Functional description * write a record in the burp file for * each filter. * **************************************/ TEXT temp[GDS_NAME_LEN]; isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$FILTERS put(tdgbl, rec_filter); const SSHORT l = PUT_TEXT (att_filter_name, X.RDB$FUNCTION_NAME); MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp)); BURP_verbose (145, temp, NULL, NULL, NULL, NULL); // msg 145 writing filter %s put_source_blob (att_filter_description2, att_filter_description, &X.RDB$DESCRIPTION); PUT_TEXT (att_filter_module_name, X.RDB$MODULE_NAME); PUT_TEXT (att_filter_entrypoint, X.RDB$ENTRYPOINT); put_numeric (att_filter_input_sub_type, X.RDB$INPUT_SUB_TYPE); put_numeric (att_filter_output_sub_type, X.RDB$OUTPUT_SUB_TYPE); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_functions(void) { /************************************** * * w r i t e _ f u n c t i o n s * ************************************** * * Functional description * write a record in the burp file for * each function. * **************************************/ GDS_NAME func; TEXT temp[GDS_NAME_LEN]; isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$FUNCTIONS put(tdgbl, rec_function); const SSHORT l = PUT_TEXT (att_function_name, X.RDB$FUNCTION_NAME); MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp)); BURP_verbose (147, temp, NULL, NULL, NULL, NULL); /* msg 147 writing function %.*s */ put_source_blob (att_function_description2, att_function_description, &X.RDB$DESCRIPTION); PUT_TEXT (att_function_module_name, X.RDB$MODULE_NAME); PUT_TEXT (att_function_entrypoint, X.RDB$ENTRYPOINT); put_numeric (att_function_return_arg, X.RDB$RETURN_ARGUMENT); put_numeric (att_function_type, X.RDB$FUNCTION_TYPE); PUT_TEXT (att_function_query_name, X.RDB$QUERY_NAME); put(tdgbl, att_end); COPY (X.RDB$FUNCTION_NAME, func); write_function_args (func); put(tdgbl, rec_function_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_function_args( GDS_NAME funcptr) { /************************************** * * w r i t e _ f u n c t i o n _ a r g s * ************************************** * * Functional description * write all arguments for a function. * **************************************/ TEXT temp[GDS_NAME_LEN]; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); /* if we have all capabilities, use the first request to get the most performance out of the latest engine; if we don't have one of the capabilities we must use the second set of requests--this requires more code but it is well worth it for the performance benefits, especially remotely--deej */ if (tdgbl->BCK_capabilities & BCK_ods10) { FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle1) X IN RDB$FUNCTION_ARGUMENTS WITH X.RDB$FUNCTION_NAME EQ funcptr put(tdgbl, rec_function_arg); const SSHORT l = PUT_TEXT (att_functionarg_name, X.RDB$FUNCTION_NAME); MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp)); BURP_verbose (141, temp, NULL, NULL, NULL, NULL); // msg 141 writing argument for function %s put_numeric (att_functionarg_position, X.RDB$ARGUMENT_POSITION); put_numeric (att_functionarg_mechanism, X.RDB$MECHANISM); put_numeric (att_functionarg_field_type, X.RDB$FIELD_TYPE); put_numeric (att_functionarg_field_scale, X.RDB$FIELD_SCALE); put_numeric (att_functionarg_field_length, X.RDB$FIELD_LENGTH); put_numeric (att_functionarg_field_sub_type, X.RDB$FIELD_SUB_TYPE); if (!(X.RDB$CHARACTER_SET_ID.NULL)) put_numeric (att_functionarg_character_set, X.RDB$CHARACTER_SET_ID); if (!(X.RDB$FIELD_PRECISION.NULL)) put_numeric (att_functionarg_field_precision, X.RDB$FIELD_PRECISION); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; } else { FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle1) X IN RDB$FUNCTION_ARGUMENTS WITH X.RDB$FUNCTION_NAME EQ funcptr put(tdgbl, rec_function_arg); const SSHORT l = PUT_TEXT (att_functionarg_name, X.RDB$FUNCTION_NAME); MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp)); BURP_verbose (141, temp, NULL, NULL, NULL, NULL); // msg 141 writing argument for function %s put_numeric (att_functionarg_position, X.RDB$ARGUMENT_POSITION); put_numeric (att_functionarg_mechanism, X.RDB$MECHANISM); put_numeric (att_functionarg_field_type, X.RDB$FIELD_TYPE); put_numeric (att_functionarg_field_scale, X.RDB$FIELD_SCALE); put_numeric (att_functionarg_field_length, X.RDB$FIELD_LENGTH); put_numeric (att_functionarg_field_sub_type, X.RDB$FIELD_SUB_TYPE); if (tdgbl->BCK_capabilities & BCK_ods8) { FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle2) X2 IN RDB$FUNCTION_ARGUMENTS WITH X2.RDB$FUNCTION_NAME EQ funcptr AND X2.RDB$ARGUMENT_POSITION = X.RDB$ARGUMENT_POSITION; if (!(X2.RDB$CHARACTER_SET_ID.NULL)) put_numeric (att_functionarg_character_set, X2.RDB$CHARACTER_SET_ID); /* Note that BCK_ods10 canNOT be set if we're in this "else" branch. Hence there is no need to test that bit and store the RDB$FIELD_PRECISION. */ END_FOR; ON_ERROR general_on_error(); END_ERROR; } put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; } } void write_generators(void) { /************************************** * * w r i t e _ g e n e r a t o r s * ************************************** * * Functional description * Write any defined generators. * **************************************/ isc_req_handle req_handle1 = 0; TEXT temp[GDS_NAME_LEN]; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$GENERATORS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 put(tdgbl, rec_generator); const SSHORT l = PUT_TEXT (att_gen_generator, X.RDB$GENERATOR_NAME); SINT64 value = 0; if (!tdgbl->gbl_sw_meta) { value = get_gen_id (X.RDB$GENERATOR_NAME, l); put_int64 (att_gen_value_int64, value); } put(tdgbl, att_end); MISC_terminate (X.RDB$GENERATOR_NAME, temp, l, sizeof(temp)); #pragma FB_COMPILER_MESSAGE("BRS: casting SINT64 to SLONG") BURP_verbose (165, temp, (void*) (IPTR) value, NULL, NULL, NULL); // msg 165 writing generator %s value %ld END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_global_fields(void) { /************************************** * * w r i t e _ g l o b a l _ f i e l d s * ************************************** * * Functional description * write a record in the burp file for * each global field. * **************************************/ TEXT temp[GDS_NAME_LEN]; isc_req_handle req_handle1 = 0, req_handle2 = 0, req_handle3 = 0, req_handle4 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); /* if we have all capabilities, use the first request to get the most performance out of the latest engine; if we don't have one of the capabilities we must use the second set of requests--this requires more code but it is well worth it for the performance benefits, especially remotely--deej */ if ((tdgbl->BCK_capabilities & BCK_attributes_v3) && (tdgbl->BCK_capabilities & BCK_ods8) && (tdgbl->BCK_capabilities & BCK_ods10)) { FOR (REQUEST_HANDLE req_handle1) X IN RDB$FIELDS WITH X.RDB$SYSTEM_FLAG NE 1 OR X.RDB$SYSTEM_FLAG MISSING put(tdgbl, rec_global_field); const SSHORT l = PUT_TEXT (att_field_name, X.RDB$FIELD_NAME); MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp)); BURP_verbose (149, temp, NULL, NULL, NULL, NULL); /* msg 149 writing global field %.*s */ if (X.RDB$QUERY_NAME [0] != ' ') PUT_TEXT (att_field_query_name, X.RDB$QUERY_NAME); if (X.RDB$EDIT_STRING [0] != ' ') PUT_TEXT (att_field_edit_string, X.RDB$EDIT_STRING); put_source_blob (att_field_query_header, att_field_query_header, &X.RDB$QUERY_HEADER); put_numeric (att_field_type, X.RDB$FIELD_TYPE); put_numeric (att_field_length, X.RDB$FIELD_LENGTH); put_numeric (att_field_sub_type, X.RDB$FIELD_SUB_TYPE); put_numeric (att_field_scale, X.RDB$FIELD_SCALE); put_blr_blob (att_field_missing_value, &X.RDB$MISSING_VALUE); put_blr_blob (att_field_default_value, &X.RDB$DEFAULT_VALUE); put_blr_blob (att_field_validation_blr, &X.RDB$VALIDATION_BLR); put_source_blob (att_field_validation_source2, att_field_validation_source, &X.RDB$VALIDATION_SOURCE); put_blr_blob (att_field_computed_blr, &X.RDB$COMPUTED_BLR); put_source_blob (att_field_computed_source2, att_field_computed_source, &X.RDB$COMPUTED_SOURCE); if (X.RDB$SEGMENT_LENGTH) put_numeric (att_field_segment_length, X.RDB$SEGMENT_LENGTH); if (X.RDB$SYSTEM_FLAG) put_numeric (att_field_system_flag, X.RDB$SYSTEM_FLAG); put_source_blob (att_field_description2, att_field_description, &X.RDB$DESCRIPTION); if (X.RDB$EXTERNAL_LENGTH) put_numeric (att_field_external_length, X.RDB$EXTERNAL_LENGTH); if (X.RDB$EXTERNAL_TYPE) put_numeric (att_field_external_type, X.RDB$EXTERNAL_TYPE); if (X.RDB$EXTERNAL_SCALE) put_numeric (att_field_external_scale, X.RDB$EXTERNAL_SCALE); if (X.RDB$DIMENSIONS) put_numeric (att_field_dimensions, X.RDB$DIMENSIONS); if (!(X.RDB$NULL_FLAG.NULL)) put_numeric (att_field_null_flag, X.RDB$NULL_FLAG); if (!(X.RDB$CHARACTER_LENGTH.NULL)) put_numeric (att_field_character_length, X.RDB$CHARACTER_LENGTH); if (!(X.RDB$DEFAULT_SOURCE.NULL)) put_source_blob (att_field_default_source, att_field_default_source, &X.RDB$DEFAULT_SOURCE); if (!(X.RDB$MISSING_SOURCE.NULL)) put_source_blob (att_field_missing_source, att_field_missing_source, &X.RDB$MISSING_SOURCE); if (!(X.RDB$CHARACTER_SET_ID.NULL)) put_numeric (att_field_character_set, X.RDB$CHARACTER_SET_ID); if (!(X.RDB$COLLATION_ID.NULL)) put_numeric (att_field_collation_id, X.RDB$COLLATION_ID); if (!(X.RDB$FIELD_PRECISION.NULL)) put_numeric (att_field_precision, X.RDB$FIELD_PRECISION); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; } else { FOR (REQUEST_HANDLE req_handle1) X IN RDB$FIELDS WITH X.RDB$SYSTEM_FLAG NE 1 OR X.RDB$SYSTEM_FLAG MISSING put(tdgbl, rec_global_field); const SSHORT l = PUT_TEXT (att_field_name, X.RDB$FIELD_NAME); MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp)); BURP_verbose (149, temp, NULL, NULL, NULL, NULL); /* msg 149 writing global field %.*s */ if (X.RDB$QUERY_NAME [0] != ' ') PUT_TEXT (att_field_query_name, X.RDB$QUERY_NAME); if (X.RDB$EDIT_STRING [0] != ' ') PUT_TEXT (att_field_edit_string, X.RDB$EDIT_STRING); put_source_blob (att_field_query_header, att_field_query_header, &X.RDB$QUERY_HEADER); put_numeric (att_field_type, X.RDB$FIELD_TYPE); put_numeric (att_field_length, X.RDB$FIELD_LENGTH); put_numeric (att_field_sub_type, X.RDB$FIELD_SUB_TYPE); put_numeric (att_field_scale, X.RDB$FIELD_SCALE); put_blr_blob (att_field_missing_value, &X.RDB$MISSING_VALUE); put_blr_blob (att_field_default_value, &X.RDB$DEFAULT_VALUE); put_blr_blob (att_field_validation_blr, &X.RDB$VALIDATION_BLR); put_source_blob (att_field_validation_source2, att_field_validation_source, &X.RDB$VALIDATION_SOURCE); put_blr_blob (att_field_computed_blr, &X.RDB$COMPUTED_BLR); put_source_blob (att_field_computed_source2, att_field_computed_source, &X.RDB$COMPUTED_SOURCE); if (X.RDB$SEGMENT_LENGTH) put_numeric (att_field_segment_length, X.RDB$SEGMENT_LENGTH); if (X.RDB$SYSTEM_FLAG) put_numeric (att_field_system_flag, X.RDB$SYSTEM_FLAG); put_source_blob (att_field_description2, att_field_description, &X.RDB$DESCRIPTION); if (tdgbl->BCK_capabilities & BCK_attributes_v3) { FOR (REQUEST_HANDLE req_handle2) F IN RDB$FIELDS WITH F.RDB$FIELD_NAME = X.RDB$FIELD_NAME if (F.RDB$EXTERNAL_LENGTH) put_numeric (att_field_external_length, F.RDB$EXTERNAL_LENGTH); if (F.RDB$EXTERNAL_TYPE) put_numeric (att_field_external_type, F.RDB$EXTERNAL_TYPE); if (F.RDB$EXTERNAL_SCALE) put_numeric (att_field_external_scale, F.RDB$EXTERNAL_SCALE); if (F.RDB$DIMENSIONS) put_numeric (att_field_dimensions, F.RDB$DIMENSIONS); END_FOR; ON_ERROR general_on_error(); END_ERROR; } if (tdgbl->BCK_capabilities & BCK_ods8) { FOR (REQUEST_HANDLE req_handle3) F IN RDB$FIELDS WITH F.RDB$FIELD_NAME = X.RDB$FIELD_NAME if (!(F.RDB$NULL_FLAG.NULL)) put_numeric (att_field_null_flag, F.RDB$NULL_FLAG); if (!(F.RDB$CHARACTER_LENGTH.NULL)) put_numeric (att_field_character_length, F.RDB$CHARACTER_LENGTH); if (!(F.RDB$DEFAULT_SOURCE.NULL)) put_source_blob (att_field_default_source, att_field_default_source, &F.RDB$DEFAULT_SOURCE); if (!(F.RDB$MISSING_SOURCE.NULL)) put_source_blob (att_field_missing_source, att_field_missing_source, &F.RDB$MISSING_SOURCE); if (!(F.RDB$CHARACTER_SET_ID.NULL)) put_numeric (att_field_character_set, F.RDB$CHARACTER_SET_ID); if (!(F.RDB$COLLATION_ID.NULL)) put_numeric (att_field_collation_id, F.RDB$COLLATION_ID); if (tdgbl->BCK_capabilities & BCK_ods10) { FOR (REQUEST_HANDLE req_handle4) K IN RDB$FIELDS WITH K.RDB$FIELD_NAME = X.RDB$FIELD_NAME if (!(K.RDB$FIELD_PRECISION.NULL)) put_numeric (att_field_precision, K.RDB$FIELD_PRECISION); END_FOR; ON_ERROR general_on_error(); END_ERROR; } END_FOR; ON_ERROR general_on_error(); END_ERROR; } put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; } MISC_release_request_silent(req_handle1); MISC_release_request_silent(req_handle2); MISC_release_request_silent(req_handle3); MISC_release_request_silent(req_handle4); } void write_procedures(void) { /************************************** * * w r i t e _ p r o c e d u r e s * ************************************** * * Functional description * write a record in the burp file for * each stored procedure. * **************************************/ GDS_NAME proc; TEXT temp[GDS_NAME_LEN]; isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$PROCEDURES put(tdgbl, rec_procedure); const SSHORT l = PUT_TEXT (att_procedure_name, X.RDB$PROCEDURE_NAME); MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof(temp)); BURP_verbose (193, temp, NULL, NULL, NULL, NULL); /* msg 193 writing stored procedure %.*s */ put_numeric (att_procedure_inputs, X.RDB$PROCEDURE_INPUTS); put_numeric (att_procedure_outputs, X.RDB$PROCEDURE_OUTPUTS); put_source_blob (att_procedure_description2, att_procedure_description, &X.RDB$DESCRIPTION); put_source_blob (att_procedure_source2, att_procedure_source, &X.RDB$PROCEDURE_SOURCE); put_blr_blob (att_procedure_blr, &X.RDB$PROCEDURE_BLR); if (!X.RDB$SECURITY_CLASS.NULL) PUT_TEXT (att_procedure_security_class, X.RDB$SECURITY_CLASS); if (!X.RDB$SECURITY_CLASS.NULL) PUT_TEXT (att_procedure_owner_name, X.RDB$OWNER_NAME); put(tdgbl, att_end); COPY(X.RDB$PROCEDURE_NAME, proc); write_procedure_prms (proc); put(tdgbl, rec_procedure_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_procedure_prms( GDS_NAME procptr) { /************************************** * * w r i t e _ p r o c e d u r e _ p r m s * ************************************** * * Functional description * write all parameters of a stored procedure. * **************************************/ TEXT temp[GDS_NAME_LEN]; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE tdgbl->handles_write_procedure_prms_req_handle1) X IN RDB$PROCEDURE_PARAMETERS WITH X.RDB$PROCEDURE_NAME EQ procptr put(tdgbl, rec_procedure_prm); const SSHORT l = PUT_TEXT (att_procedureprm_name, X.RDB$PARAMETER_NAME); MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof(temp)); BURP_verbose (194, temp, NULL, NULL, NULL, NULL); // msg 194 writing parameter %s for stored procedure put_numeric (att_procedureprm_number, X.RDB$PARAMETER_NUMBER); put_numeric (att_procedureprm_type, X.RDB$PARAMETER_type); PUT_TEXT (att_procedureprm_field_source, X.RDB$FIELD_SOURCE); put_source_blob (att_procedureprm_description2, att_procedureprm_description, &X.RDB$DESCRIPTION); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; } void write_ref_constraints(void) { /************************************** * * w r i t e _ r e f _ c o n s t r a i n t s * ************************************** * * Functional description * write a record in the burp file for * each referential constraint. * **************************************/ isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$REF_CONSTRAINTS put(tdgbl, rec_ref_constraint); PUT_TEXT (att_ref_constraint_name, X.RDB$CONSTRAINT_NAME); PUT_TEXT (att_ref_unique_const_name, X.RDB$CONST_NAME_UQ); PUT_TEXT (att_ref_match_option, X.RDB$MATCH_OPTION); PUT_MESSAGE (att_ref_update_rule, X.RDB$UPDATE_RULE); PUT_MESSAGE (att_ref_delete_rule, X.RDB$DELETE_RULE); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_rel_constraints(void) { /************************************** * * w r i t e _ r e l _ c o n s t r a i n t s * ************************************** * * Functional description * write a record in the burp file for * each relation constraint. * **************************************/ TEXT temp[GDS_NAME_LEN]; isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$RELATION_CONSTRAINTS put(tdgbl, rec_rel_constraint); const SSHORT l = PUT_TEXT (att_rel_constraint_name, X.RDB$CONSTRAINT_NAME); MISC_terminate (X.RDB$CONSTRAINT_NAME, temp, l, sizeof(temp)); BURP_verbose (207, temp, NULL, NULL, NULL, NULL); // msg 207 writing constraint %s PUT_MESSAGE (att_rel_constraint_type, X.RDB$CONSTRAINT_TYPE); PUT_TEXT (att_rel_constraint_rel_name, X.RDB$RELATION_NAME); PUT_TEXT (att_rel_constraint_defer, X.RDB$DEFERRABLE); PUT_TEXT (att_rel_constraint_init, X.RDB$INITIALLY_DEFERRED); if (!(X.RDB$INDEX_NAME.NULL)) PUT_TEXT (att_rel_constraint_index, X.RDB$INDEX_NAME); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_relations(void) { /************************************** * * w r i t e _ r e l a t i o n s * ************************************** * * Functional description * write a record in the burp file for * each relation. * **************************************/ TEXT temp[GDS_NAME_LEN]; isc_req_handle req_handle1 = 0, req_handle2 = 0, req_handle3 = 0, req_handle4 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); /* if we have all capabilities, use the first request to get the most performance out of the latest engine; if we don't have one of the capabilities we must use the second set of requests--this requires more code but it is well worth it for the performance benefits, especially remotely--deej */ if ((tdgbl->BCK_capabilities & BCK_ods8) && (tdgbl->BCK_capabilities & BCK_security) && (tdgbl->BCK_capabilities & BCK_attributes_v3)) { FOR (REQUEST_HANDLE req_handle1) X IN RDB$RELATIONS WITH X.RDB$SYSTEM_FLAG NE 1 OR X.RDB$SYSTEM_FLAG MISSING SSHORT flags = 0; put(tdgbl, rec_relation); const SSHORT l = PUT_TEXT (att_relation_name, X.RDB$RELATION_NAME); MISC_terminate (X.RDB$RELATION_NAME, temp, l, sizeof(temp)); BURP_verbose (153, temp, NULL, NULL, NULL, NULL); /* msg 153 writing relation %.*s */ /* RDB$VIEW_BLR must be the first blob field in the backup file. * RESTORE.EPP makes this assumption in get_relation(). */ if (put_blr_blob (att_relation_view_blr, &X.RDB$VIEW_BLR)) flags |= REL_view; if (X.RDB$SYSTEM_FLAG) put_numeric (att_relation_system_flag, X.RDB$SYSTEM_FLAG); if (!(X.RDB$FLAGS.NULL)) put_numeric (att_relation_flags, X.RDB$FLAGS); if (!X.RDB$SECURITY_CLASS.NULL) PUT_TEXT (att_relation_security_class, X.RDB$SECURITY_CLASS); put_source_blob (att_relation_description2, att_relation_description, &X.RDB$DESCRIPTION); put_source_blob (att_relation_view_source2, att_relation_view_source, &X.RDB$VIEW_SOURCE); put_source_blob (att_relation_ext_description2, att_relation_ext_description, &X.RDB$EXTERNAL_DESCRIPTION); PUT_TEXT (att_relation_owner_name, X.RDB$OWNER_NAME); if (!X.RDB$EXTERNAL_FILE.NULL) if (!tdgbl->gbl_sw_convert_ext_tables) { PUT_TEXT(att_relation_ext_file_name, X.RDB$EXTERNAL_FILE); flags |= REL_external; } put(tdgbl, att_end); burp_rel* relation = (burp_rel*) BURP_alloc_zero (sizeof(burp_rel)); relation->rel_next = tdgbl->relations; tdgbl->relations = relation; relation->rel_id = X.RDB$RELATION_ID; relation->rel_name_length = COPY(X.RDB$RELATION_NAME, relation->rel_name); relation->rel_flags |= flags; put_relation (relation); END_FOR; ON_ERROR general_on_error(); END_ERROR; } else { FOR (REQUEST_HANDLE req_handle1) X IN RDB$RELATIONS WITH X.RDB$SYSTEM_FLAG NE 1 OR X.RDB$SYSTEM_FLAG MISSING SSHORT flags = 0; put(tdgbl, rec_relation); const SSHORT l = PUT_TEXT(att_relation_name, X.RDB$RELATION_NAME); MISC_terminate (X.RDB$RELATION_NAME, temp, l, sizeof(temp)); BURP_verbose (153, temp, NULL, NULL, NULL, NULL); /* msg 153 writing relation %.*s */ /* RDB$VIEW_BLR must be the first blob field in the backup file. * RESTORE.E makes this assumption in get_relation(). */ if (put_blr_blob (att_relation_view_blr, &X.RDB$VIEW_BLR)) flags |= REL_view; if (X.RDB$SYSTEM_FLAG) put_numeric (att_relation_system_flag, X.RDB$SYSTEM_FLAG); if (tdgbl->BCK_capabilities & BCK_ods8) { FOR (REQUEST_HANDLE req_handle2) R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME = X.RDB$RELATION_NAME if (!(R.RDB$FLAGS.NULL)) put_numeric (att_relation_flags, R.RDB$FLAGS); END_FOR; ON_ERROR general_on_error(); END_ERROR; } if (tdgbl->BCK_capabilities & BCK_security) { FOR (REQUEST_HANDLE req_handle3) R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME = X.RDB$RELATION_NAME if (!R.RDB$SECURITY_CLASS.NULL) PUT_TEXT(att_relation_security_class, R.RDB$SECURITY_CLASS); END_FOR; ON_ERROR general_on_error(); END_ERROR; } put_source_blob (att_relation_description2, att_relation_description, &X.RDB$DESCRIPTION); put_source_blob (att_relation_view_source2, att_relation_view_source, &X.RDB$VIEW_SOURCE); if (tdgbl->BCK_capabilities & BCK_attributes_v3) { FOR (REQUEST_HANDLE req_handle4) R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME = X.RDB$RELATION_NAME put_source_blob (att_relation_ext_description2, att_relation_ext_description, &R.RDB$EXTERNAL_DESCRIPTION); PUT_TEXT(att_relation_owner_name, R.RDB$OWNER_NAME); if (!R.RDB$EXTERNAL_FILE.NULL) { if (!tdgbl->gbl_sw_convert_ext_tables) { PUT_TEXT(att_relation_ext_file_name, R.RDB$EXTERNAL_FILE); flags |= REL_external; } } END_FOR; ON_ERROR general_on_error(); END_ERROR; } put(tdgbl, att_end); burp_rel* relation = (burp_rel*) BURP_alloc_zero (sizeof(burp_rel)); relation->rel_next = tdgbl->relations; tdgbl->relations = relation; relation->rel_id = X.RDB$RELATION_ID; relation->rel_name_length = COPY(X.RDB$RELATION_NAME, relation->rel_name); relation->rel_flags |= flags; put_relation (relation); END_FOR; ON_ERROR general_on_error(); END_ERROR; } MISC_release_request_silent(req_handle1); MISC_release_request_silent(req_handle2); MISC_release_request_silent(req_handle3); MISC_release_request_silent(req_handle4); } void write_shadow_files(void) { /************************************** * * w r i t e _ s h a d o w _ f i l e s * ************************************** * * Functional description * Write out files to use as shadows. * **************************************/ TEXT temp[GDS_NAME_LEN]; isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$FILES WITH X.RDB$SHADOW_NUMBER NOT MISSING AND X.RDB$SHADOW_NUMBER NE 0 put(tdgbl, rec_files); const SSHORT l = PUT_TEXT (att_file_filename, X.RDB$FILE_NAME); MISC_terminate (X.RDB$FILE_NAME, temp, l, sizeof(temp)); BURP_verbose (163, temp, NULL, NULL, NULL, NULL); // msg 163 writing shadow file %s put_numeric (att_file_sequence, X.RDB$FILE_SEQUENCE); put_numeric (att_file_start, X.RDB$FILE_START); put_numeric (att_file_length, X.RDB$FILE_LENGTH); put_numeric (att_file_flags, X.RDB$FILE_FLAGS); put_numeric (att_shadow_number, X.RDB$SHADOW_NUMBER); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_sql_roles(void) { /************************************** * * w r i t e _ s q l _ r o l e s * ************************************** * * Functional description * write a record in the burp file for * each SQL roles. * **************************************/ isc_req_handle req_handle1 = 0; TEXT temp[GDS_NAME_LEN]; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); if (tdgbl->BCK_capabilities & BCK_ods11) { FOR (REQUEST_HANDLE req_handle1) X IN RDB$ROLES WITH X.RDB$SYSTEM_FLAG NE 1 OR X.RDB$SYSTEM_FLAG MISSING put(tdgbl, rec_sql_roles); const SSHORT l = PUT_TEXT(att_role_name, X.RDB$ROLE_NAME); PUT_TEXT (att_role_owner_name, X.RDB$OWNER_NAME); if (!X.RDB$DESCRIPTION.NULL) { put_source_blob (att_role_description, att_role_description, &X.RDB$DESCRIPTION); } put(tdgbl, att_end); MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp)); BURP_verbose (249, temp, NULL, NULL, NULL, NULL); // msg 249 writing SQL role: %s END_FOR; ON_ERROR general_on_error(); END_ERROR; } else { FOR (REQUEST_HANDLE req_handle1) X IN RDB$ROLES put(tdgbl, rec_sql_roles); const SSHORT l = PUT_TEXT(att_role_name, X.RDB$ROLE_NAME); PUT_TEXT (att_role_owner_name, X.RDB$OWNER_NAME); put(tdgbl, att_end); MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp)); BURP_verbose (249, temp, NULL, NULL, NULL, NULL); // msg 249 writing SQL role: %s END_FOR; ON_ERROR general_on_error(); END_ERROR; } MISC_release_request_silent(req_handle1); } void write_triggers(void) { /************************************** * * w r i t e _ t r i g g e r s * ************************************** * * Functional description * write the triggers in rdb$triggers * **************************************/ TEXT temp[GDS_NAME_LEN]; isc_req_handle req_handle1 = 0, req_handle2 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); /* if we have all capabilities, use the first request to get the most performance out of the latest engine; if we don't have one of the capabilities we must use the second set of requests--this requires more code but it is well worth it for the performance benefits, especially remotely--deej */ if (tdgbl->BCK_capabilities & BCK_ods8) { FOR (REQUEST_HANDLE req_handle1) X IN RDB$TRIGGERS WITH X.RDB$SYSTEM_FLAG NE 1 OR X.RDB$SYSTEM_FLAG MISSING put(tdgbl, rec_trigger); const SSHORT l = PUT_TEXT (att_trig_name, X.RDB$TRIGGER_NAME); MISC_terminate (X.RDB$TRIGGER_NAME, temp, l, sizeof(temp)); BURP_verbose (156, temp, NULL, NULL, NULL, NULL); // msg 156 writing trigger %s PUT_TEXT (att_trig_relation_name, X.RDB$RELATION_NAME); put_numeric (att_trig_sequence, X.RDB$TRIGGER_SEQUENCE); put_numeric (att_trig_type, X.RDB$TRIGGER_TYPE); put_blr_blob (att_trig_blr, &X.RDB$TRIGGER_BLR); put_source_blob (att_trig_source2, att_trig_source, &X.RDB$TRIGGER_SOURCE); put_source_blob (att_trig_description2, att_trig_description, &X.RDB$DESCRIPTION); put_numeric (att_trig_system_flag, X.RDB$SYSTEM_FLAG); put_numeric (att_trig_inactive, X.RDB$TRIGGER_INACTIVE); if (!(X.RDB$FLAGS.NULL)) put_numeric (att_trig_flags, X.RDB$FLAGS); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; } else { FOR (REQUEST_HANDLE req_handle1) X IN RDB$TRIGGERS WITH X.RDB$SYSTEM_FLAG NE 1 OR X.RDB$SYSTEM_FLAG MISSING put(tdgbl, rec_trigger); const SSHORT l = PUT_TEXT (att_trig_name, X.RDB$TRIGGER_NAME); MISC_terminate (X.RDB$TRIGGER_NAME, temp, l, sizeof(temp)); BURP_verbose (156, temp, NULL, NULL, NULL, NULL); // msg 156 writing trigger %s PUT_TEXT (att_trig_relation_name, X.RDB$RELATION_NAME); put_numeric (att_trig_sequence, X.RDB$TRIGGER_SEQUENCE); put_numeric (att_trig_type, X.RDB$TRIGGER_TYPE); put_blr_blob (att_trig_blr, &X.RDB$TRIGGER_BLR); put_source_blob (att_trig_source2, att_trig_source, &X.RDB$TRIGGER_SOURCE); put_source_blob (att_trig_description2, att_trig_description, &X.RDB$DESCRIPTION); put_numeric (att_trig_system_flag, X.RDB$SYSTEM_FLAG); put_numeric (att_trig_inactive, X.RDB$TRIGGER_INACTIVE); if (tdgbl->BCK_capabilities & BCK_ods8) { FOR (REQUEST_HANDLE req_handle2) Y IN RDB$TRIGGERS WITH X.RDB$TRIGGER_NAME = Y.RDB$TRIGGER_NAME if (!(Y.RDB$FLAGS.NULL)) put_numeric (att_trig_flags, Y.RDB$FLAGS); END_FOR; ON_ERROR general_on_error(); END_ERROR; } put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; } MISC_release_request_silent(req_handle1); MISC_release_request_silent(req_handle2); } void write_trigger_messages(void) { /************************************** * * w r i t e _ t r i g g e r _ m e s s a g e s * ************************************** * * Functional description * write a record in the burp file for * each trigger message. * **************************************/ TEXT temp[GDS_NAME_LEN]; isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) T IN RDB$TRIGGERS CROSS X IN RDB$TRIGGER_MESSAGES OVER RDB$TRIGGER_NAME WITH T.RDB$SYSTEM_FLAG NE 1 OR T.RDB$SYSTEM_FLAG MISSING; put(tdgbl, rec_trigger_message); const SSHORT l = PUT_TEXT (att_trigmsg_name, X.RDB$TRIGGER_NAME); MISC_terminate (X.RDB$TRIGGER_NAME, temp, l, sizeof(temp)); BURP_verbose (157, temp, NULL, NULL, NULL, NULL); /* msg 157 writing trigger message for *s */ put_numeric (att_trigmsg_number, X.RDB$MESSAGE_NUMBER); PUT_MESSAGE (att_trigmsg_text, X.RDB$MESSAGE); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_types(void) { /************************************** * * w r i t e _ t y p e s * ************************************** * * Functional description * write a record in the burp file for * each type. * **************************************/ isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); FOR (REQUEST_HANDLE req_handle1) X IN RDB$TYPES WITH X.RDB$SYSTEM_FLAG NE 1 OR X.RDB$SYSTEM_FLAG MISSING put(tdgbl, rec_system_type); PUT_TEXT (att_type_name, X.RDB$TYPE_NAME); PUT_TEXT (att_type_field_name, X.RDB$FIELD_NAME); BURP_verbose (160, X.RDB$TYPE_NAME, X.RDB$FIELD_NAME, NULL, NULL, NULL); // msg 160 writing type %s for field %s put_numeric (att_type_type, X.RDB$TYPE); put_source_blob (att_type_description2, att_type_description, &X.RDB$DESCRIPTION); if (X.RDB$SYSTEM_FLAG) put_numeric (att_type_system_flag, X.RDB$SYSTEM_FLAG); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; MISC_release_request_silent(req_handle1); } void write_user_privileges(void) { /************************************** * * w r i t e _ u s e r _ p r i v i l e g e s * ************************************** * * Functional description * write a record in the burp file for * each user privilege. * **************************************/ TEXT temp[GDS_NAME_LEN]; isc_req_handle req_handle1 = 0; BurpGlobals* tdgbl = BurpGlobals::getSpecific(); if (tdgbl->BCK_capabilities & BCK_ods8) { FOR (REQUEST_HANDLE req_handle1) X IN RDB$USER_PRIVILEGES put(tdgbl, rec_user_privilege); const SSHORT l = PUT_TEXT (att_priv_user, X.RDB$USER); MISC_terminate (X.RDB$USER, temp, l, sizeof(temp)); BURP_verbose (152, temp, NULL, NULL, NULL, NULL); // msg 152 writing privilege for user %s PUT_TEXT (att_priv_grantor, X.RDB$GRANTOR); PUT_TEXT (att_priv_privilege, X.RDB$PRIVILEGE); put_numeric (att_priv_grant_option, X.RDB$GRANT_OPTION); PUT_TEXT (att_priv_object_name, X.RDB$RELATION_NAME); if (!X.RDB$FIELD_NAME.NULL) PUT_TEXT (att_priv_field_name, X.RDB$FIELD_NAME); put_numeric (att_priv_user_type, X.RDB$USER_TYPE); put_numeric (att_priv_obj_type, X.RDB$OBJECT_TYPE); put(tdgbl, att_end); END_FOR ON_ERROR general_on_error(); END_ERROR; } else { FOR (REQUEST_HANDLE req_handle1) X IN RDB$USER_PRIVILEGES put(tdgbl, rec_user_privilege); const SSHORT l = PUT_TEXT (att_priv_user, X.RDB$USER); MISC_terminate (X.RDB$USER, temp, l, sizeof(temp)); BURP_verbose (152, temp, NULL, NULL, NULL, NULL); // msg 152 writing privilege for user %s PUT_TEXT (att_priv_grantor, X.RDB$GRANTOR); PUT_TEXT (att_priv_privilege, X.RDB$PRIVILEGE); put_numeric (att_priv_grant_option, X.RDB$GRANT_OPTION); PUT_TEXT (att_priv_object_name, X.RDB$RELATION_NAME); if (!X.RDB$FIELD_NAME.NULL) PUT_TEXT (att_priv_field_name, X.RDB$FIELD_NAME); put(tdgbl, att_end); END_FOR; ON_ERROR general_on_error(); END_ERROR; } MISC_release_request_silent(req_handle1); } } // namespace