8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-25 01:23:03 +01:00
firebird-mirror/src/burp/restore.epp
asfernandes 75c0d0f353 Misc
2009-02-05 01:08:13 +00:00

8168 lines
200 KiB
Plaintext

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