8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 18:03:04 +01:00
firebird-mirror/src/burp/restore.epp
2009-08-05 06:35:57 +00:00

8253 lines
202 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 burp_msg_fac = 12;
enum scan_attr_t
{
NO_SKIP = 0, // Not in skipping and scanning mode
BEFORE_SKIP = 1, // After skipping, before scanning next byte for valid attribute
AFTER_SKIP = 2 // After skipping and after scanning next byte for valid attribute
};
void add_files(BurpGlobals* tdgbl, const char*);
void bad_attribute(scan_attr_t, att_type, USHORT);
void check_db_version(BurpGlobals* tdgbl);
void create_database(BurpGlobals* tdgbl, const TEXT*);
void decompress(BurpGlobals* tdgbl, UCHAR*, USHORT);
void eat_blob(BurpGlobals* tdgbl);
void eat_text(BurpGlobals* tdgbl);
void eat_text2(BurpGlobals* tdgbl);
burp_rel* find_relation(BurpGlobals* tdgbl, const TEXT*);
void fix_security_class_name(BurpGlobals* tdgbl, TEXT* sec_class, bool is_field);
// CVC: when do these functions return false indeed???
// get_acl and get_index are the only exceptions but ironically their
// returned value is not checked by the caller!
bool get_acl(BurpGlobals* tdgbl, const TEXT*, ISC_QUAD*, ISC_QUAD*);
void get_array(BurpGlobals* tdgbl, burp_rel*, UCHAR*);
void get_blob(BurpGlobals* tdgbl, const burp_fld*, UCHAR*);
void get_blr_blob(BurpGlobals* tdgbl, ISC_QUAD&, bool);
bool get_character_set(BurpGlobals* tdgbl);
bool get_chk_constraint(BurpGlobals* tdgbl);
bool get_collation(BurpGlobals* tdgbl);
rec_type get_data(BurpGlobals* tdgbl, burp_rel*);
bool get_exception(BurpGlobals* tdgbl);
burp_fld* get_field(BurpGlobals* tdgbl, burp_rel*);
bool get_field_dimensions(BurpGlobals* tdgbl);
bool get_files(BurpGlobals* tdgbl);
bool get_filter(BurpGlobals* tdgbl);
bool get_function(BurpGlobals* tdgbl);
void get_function_arg(BurpGlobals* tdgbl, bool skip_arguments);
bool get_generator(BurpGlobals* tdgbl);
bool get_global_field(BurpGlobals* tdgbl);
bool get_index(BurpGlobals* tdgbl, const burp_rel*);
void get_misc_blob(BurpGlobals* tdgbl, ISC_QUAD&, bool);
SLONG get_numeric(BurpGlobals* tdgbl);
SINT64 get_int64(BurpGlobals* tdgbl);
bool get_procedure(BurpGlobals* tdgbl);
bool get_procedure_prm (BurpGlobals* tdgbl, GDS_NAME );
bool get_ref_constraint(BurpGlobals* tdgbl);
bool get_rel_constraint(BurpGlobals* tdgbl);
bool get_relation(BurpGlobals* tdgbl);
bool get_relation_data(BurpGlobals* tdgbl);
bool get_sql_roles(BurpGlobals* tdgbl);
bool get_mapping(BurpGlobals* tdgbl);
bool get_security_class(BurpGlobals* tdgbl);
void get_source_blob(BurpGlobals* tdgbl, ISC_QUAD&, bool);
USHORT get_text(BurpGlobals* tdgbl, TEXT*, ULONG);
USHORT get_text2(BurpGlobals* tdgbl, TEXT* text, ULONG length);
bool get_trigger(BurpGlobals* tdgbl);
bool get_trigger_message(BurpGlobals* tdgbl);
bool get_trigger_old (BurpGlobals* tdgbl, burp_rel*);
bool get_type(BurpGlobals* tdgbl);
bool get_user_privilege(BurpGlobals* tdgbl);
bool get_view(BurpGlobals* tdgbl, burp_rel*);
void ignore_array(BurpGlobals* tdgbl, burp_rel*);
void ignore_blob(BurpGlobals* tdgbl);
rec_type ignore_data(BurpGlobals* tdgbl, burp_rel*);
void realign(BurpGlobals* tdgbl, UCHAR*, const burp_rel*);
#ifdef sparc
USHORT recompute_length(BurpGlobals* tdgbl, burp_rel*);
#endif
bool restore(BurpGlobals* tdgbl, const TEXT*, const TEXT*);
void restore_security_class(BurpGlobals* tdgbl, const TEXT*, const TEXT*);
USHORT get_view_base_relation_count(BurpGlobals* tdgbl, const TEXT*, USHORT);
void store_blr_gen_id(BurpGlobals* tdgbl, const TEXT*, SINT64, const ISC_QUAD*);
void update_global_field(BurpGlobals* tdgbl);
void update_view_dbkey_lengths(BurpGlobals* tdgbl);
void general_on_error();
#ifdef DEBUG
UCHAR debug_on = 0; // able to turn this on in the debugger
#endif
#ifdef sparc
const SSHORT old_sparcs[] =
{0, 0, 0, 2, 0, 0, 0, 0, 2, 4, 4, 4, 8, 8, 0, 0, 8, 8, 8};
#endif
//MVOL_read returns int
static inline int get(BurpGlobals* tdgbl)
{
if (--(tdgbl->io_cnt) >= 0)
return *(tdgbl->io_ptr)++;
return MVOL_read(&tdgbl->io_cnt, &tdgbl->io_ptr);
}
static inline att_type get_attribute(att_type* att, BurpGlobals* tdgbl)
{
*att = (att_type) get(tdgbl);
return *att;
}
static inline rec_type get_record(rec_type *rec, BurpGlobals* tdgbl)
{
*rec = (rec_type) get(tdgbl);
return *rec;
}
#define GET_TEXT(text) get_text(tdgbl, (text), sizeof(text))
#define GET_TEXT2(text) get_text2(tdgbl, (text), sizeof(text))
static inline void get_skip(BurpGlobals* tdgbl, ULONG n)
{
MVOL_skip_block(tdgbl, n);
}
static inline UCHAR* get_block(BurpGlobals* tdgbl, UCHAR* p, ULONG n)
{
return MVOL_read_block(tdgbl, p, n);
}
// When skipping started, scan_next_attr will be changed from NO_SKIP
// to BEFORE_SKIP. When scanning for next valid attribute after skipping,
// it will flip-flop between BEFORE_SKIP and AFTER_SKIP. When next valid
// attribute is found, it will be changed back to NO_SKIP by 'skip_scan'
static inline void skip_init(scan_attr_t* scan_next_attr)
{
*scan_next_attr = NO_SKIP;
}
static inline void skip_scan(scan_attr_t* scan_next_attr)
{
if (*scan_next_attr == AFTER_SKIP)
*scan_next_attr = BEFORE_SKIP;
else if (*scan_next_attr == BEFORE_SKIP)
*scan_next_attr = NO_SKIP;
//else 0; => nothing, no change in the original macro
}
// User Privilege Flags
const int USER_PRIV_USER = 1;
const int USER_PRIV_GRANTOR = 2;
const int USER_PRIV_PRIVILEGE = 4;
const int USER_PRIV_GRANT_OPTION = 8;
const int USER_PRIV_OBJECT_NAME = 16;
const int USER_PRIV_FIELD_NAME = 32;
const int USER_PRIV_USER_TYPE = 64;
const int USER_PRIV_OBJECT_TYPE = 128;
} // namespace
int RESTORE_restore (const TEXT* file_name, const TEXT* database_name)
{
/**************************************
*
* R E S T O R E _ r e s t o r e
*
**************************************
*
* Functional description
* Recreate a database from a backup.
*
**************************************/
isc_req_handle req_handle1 = 0, req_handle2 = 0, req_handle3 = 0;
isc_req_handle req_handle4 = 0, req_handle5 = 0;
BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name;
BurpGlobals* tdgbl = BurpGlobals::getSpecific();
tdgbl->io_ptr = NULL;
tdgbl->io_cnt = 0;
tdgbl->relations = NULL;
tdgbl->procedures = NULL;
tdgbl->RESTORE_format = 0;
tdgbl->RESTORE_ods = 0;
tdgbl->global_trans = 0;
tdgbl->gbl_sw_transportable = tdgbl->gbl_sw_compress = false;
if (!restore(tdgbl, file_name, database_name))
return FINI_ERROR;
BURP_verbose (76);
// msg 76 creating indexes
COMMIT;
ON_ERROR
// Fix for bug_no 8055:
// don't throw away the database just because an index
// could not be made
long error_code;
while (error_code = tdgbl->status_vector[1])
{
switch (error_code)
{
case isc_sort_mem_err:
case isc_no_dup:
strcpy(index_name, (TEXT *)tdgbl->status_vector[3]);
BURP_print_status(tdgbl->status_vector);
FOR (REQUEST_HANDLE req_handle3)
IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ index_name
BURP_verbose(243, index_name);
MODIFY IDX USING
IDX.RDB$INDEX_INACTIVE = TRUE;
END_MODIFY;
BURP_print(240, index_name);
// msg 240 Index \"%s\" failed to activate because:
if ( error_code == isc_no_dup )
{
BURP_print(241);
// msg 241 The unique index has duplicate values or NULLs
BURP_print(242);
// msg 242 Delete or Update duplicate values or NULLs, and activate index with
}
else
{
BURP_print(244);
// msg 244 Not enough disk space to create the sort file for an index
BURP_print(245);
// msg 245 Set the TMP environment variable to a directory on a filesystem that does have enough space, and activate index with
}
BURP_print(243, index_name);
// msg 243 ALTER INDEX \"%s\" ACTIVE;
END_FOR;
// don't bring the database on-line
tdgbl->flag_on_line = false;
break;
default:
general_on_error ();
break;
}
COMMIT
ON_ERROR
continue;
END_ERROR
}
END_ERROR;
// Activate the indices for foreign keys and do another commit
if (!tdgbl->gbl_sw_deactivate_indexes)
{
// Block added to verbose index creation by Toni Martir
// Always try to activate deferred indices - it helps for some broken backups,
// and in normal cases doesn't take much time to look for such indices. AP-2008.
EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
// Activate first indexes that are not foreign keys
FOR (REQUEST_HANDLE req_handle1) IDS IN RDB$INDICES WITH
IDS.RDB$INDEX_INACTIVE EQ DEFERRED_ACTIVE AND
IDS.RDB$FOREIGN_KEY MISSING
MISC_terminate(IDS.RDB$INDEX_NAME, index_name,
(ULONG) MISC_symbol_length(IDS.RDB$INDEX_NAME, sizeof(IDS.RDB$INDEX_NAME)),
sizeof(index_name));
BURP_verbose(285, index_name);
// activating and creating deferred index %s
MODIFY IDS USING
IDS.RDB$INDEX_INACTIVE = FALSE;
END_MODIFY;
ON_ERROR
general_on_error();
END_ERROR;
SAVE
// existing ON_ERROR continues past error, beck
ON_ERROR
BURP_print (173, index_name);
BURP_print_status(isc_status);
MODIFY IDS USING
IDS.RDB$INDEX_INACTIVE = TRUE;
END_MODIFY;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle1);
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
// Only activate Foreign keys that have been marked for deferred
// activation.
// -bsriram, 11-May-1999 BUG: 10016
// In case error happens creating FK, triggers don't let set
// INACTIVE = TRUE for FK index. Therefore use separate
// transaction be able to rollback when needed.
// AP, 2005
FOR (REQUEST_HANDLE req_handle1)
CNST IN RDB$RELATION_CONSTRAINTS
CROSS IDS IN RDB$INDICES WITH
CNST.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND
CNST.RDB$INDEX_NAME EQ IDS.RDB$INDEX_NAME AND
IDS.RDB$INDEX_INACTIVE EQ DEFERRED_ACTIVE
MISC_terminate(IDS.RDB$INDEX_NAME, index_name,
(ULONG) MISC_symbol_length(IDS.RDB$INDEX_NAME, sizeof(IDS.RDB$INDEX_NAME)),
sizeof(index_name));
BURP_verbose(285, index_name);
// activating and creating deferred index %s
bool fError = false;
isc_tr_handle activateIndexTran = 0;
ISC_STATUS_ARRAY local_status_vector;
ISC_STATUS* local_status = local_status_vector;
START_TRANSACTION activateIndexTran;
FOR (TRANSACTION_HANDLE activateIndexTran REQUEST_HANDLE req_handle5)
IND1 IN RDB$INDICES WITH IND1.RDB$INDEX_NAME EQ IDS.RDB$INDEX_NAME
MODIFY IND1 USING
IND1.RDB$INDEX_INACTIVE = FALSE;
END_MODIFY;
END_FOR;
ON_ERROR
fError = true;
memcpy(local_status, isc_status, sizeof (ISC_STATUS_ARRAY));
END_ERROR;
MISC_release_request_silent(req_handle5);
if (!fError)
{
COMMIT activateIndexTran;
ON_ERROR
fError = true;
memcpy(local_status, isc_status, sizeof (ISC_STATUS_ARRAY));
END_ERROR;
}
if (fError)
{
ROLLBACK activateIndexTran;
ON_ERROR
general_on_error ();
END_ERROR;
BURP_print (173, index_name);
BURP_print_status(local_status);
tdgbl->flag_on_line = false;
}
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle1);
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
}
if (tdgbl->global_trans)
{
BURP_verbose (68);
// msg 68 committing meta data
EXEC SQL COMMIT TRANSACTION tdgbl->global_trans;
if (gds_status[1])
general_on_error ();
// Check to see if there is a warning
if (gds_status[0] == isc_arg_gds && gds_status[1] == 0 && gds_status[2] != isc_arg_end)
{
BURP_print_warning(gds_status);
}
}
EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
// AB: Recalculate RDB$DBKEY_LENGTH for VIEWS
// When VIEWs are not processed in correct dependency order
// then on create time it doesn't know anything from the
// VIEW that's referenced.
//
update_view_dbkey_lengths(tdgbl);
// Change ownership of any procedures necessary
for (burp_prc* procedure = tdgbl->procedures; procedure; procedure = procedure->prc_next)
{
if (procedure->prc_owner[0])
{
FOR (REQUEST_HANDLE req_handle4)
X IN RDB$PROCEDURES WITH X.RDB$PROCEDURE_NAME EQ procedure->prc_name
MODIFY X
strcpy (X.RDB$OWNER_NAME, procedure->prc_owner);
END_MODIFY;
ON_ERROR
MISC_release_request_silent(req_handle4);
general_on_error ();
END_ERROR;
restore_security_class(tdgbl, procedure->prc_owner, X.RDB$SECURITY_CLASS);
END_FOR;
ON_ERROR
MISC_release_request_silent(req_handle4);
general_on_error ();
END_ERROR;
}
}
MISC_release_request_silent(req_handle4);
// Change ownership of any relations necessary
for (burp_rel* relation = tdgbl->relations; relation; relation = relation->rel_next)
{
if (relation->rel_owner[0])
{
FOR (REQUEST_HANDLE req_handle2)
X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ relation->rel_name
MODIFY X
strcpy (X.RDB$OWNER_NAME, relation->rel_owner);
END_MODIFY;
ON_ERROR
MISC_release_request_silent(req_handle2);
general_on_error ();
END_ERROR;
restore_security_class(tdgbl, relation->rel_owner, X.RDB$SECURITY_CLASS);
restore_security_class(tdgbl, relation->rel_owner, X.RDB$DEFAULT_CLASS);
END_FOR;
ON_ERROR
MISC_release_request_silent(req_handle2);
general_on_error ();
END_ERROR;
}
}
MISC_release_request_silent(req_handle2);
// Now that changing ownership of tables is over, it is safe to
// update the database security class in RDB$DATABASE
if (tdgbl->database_security_class[0]) // Do it only if it's not NULL
{
FOR (REQUEST_HANDLE req_handle1)
X IN RDB$DATABASE
MODIFY X USING
strncpy(X.RDB$SECURITY_CLASS, tdgbl->database_security_class,
sizeof(X.RDB$SECURITY_CLASS));
END_MODIFY;
ON_ERROR
MISC_release_request_silent(req_handle1);
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
MISC_release_request_silent(req_handle1);
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle1);
}
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
// Check to see if there is a warning
if (gds_status[0] == isc_arg_gds && gds_status[1] == 0 && gds_status[2] != isc_arg_end)
{
BURP_print_warning(gds_status);
}
BURP_verbose (88);
// msg 88 finishing, closing, and going home
//FB_UINT64 cumul_count =
MVOL_fini_read();
// Close database before we attach to it again.
FINISH
ON_ERROR
general_on_error ();
END_ERROR;
// attach database again to put it online
Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
tdgbl->uSvc->getAddressPath(dpb);
if (tdgbl->flag_on_line)
{
dpb.insertTag(isc_dpb_online);
}
if (tdgbl->gbl_sw_user)
{
dpb.insertString(isc_dpb_user_name, tdgbl->gbl_sw_user, strlen(tdgbl->gbl_sw_user));
}
if (tdgbl->gbl_sw_password)
{
dpb.insertString(tdgbl->uSvc->isService() ? isc_dpb_password_enc : isc_dpb_password,
tdgbl->gbl_sw_password, strlen(tdgbl->gbl_sw_password));
}
if (tdgbl->gbl_sw_tr_user)
{
dpb.insertString(isc_dpb_trusted_auth, tdgbl->gbl_sw_tr_user, strlen(tdgbl->gbl_sw_tr_user));
}
dpb.insertByte(isc_dpb_no_db_triggers, 1);
// set forced writes to the value which was in the header
dpb.insertByte(isc_dpb_force_write, tdgbl->hdr_forced_writes ? 1 : 0);
FB_API_HANDLE db_handle = 0;
if (isc_attach_database(tdgbl->status_vector, 0, database_name, &db_handle,
dpb.getBufferLength(), reinterpret_cast<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)
{
if (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
}
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 fix_security_class_name(BurpGlobals* tdgbl, TEXT* sec_class, bool is_field)
{
/**************************************
*
* f i x _ s e c u r i t y _ c l a s s _ n a m e
*
**************************************
*
* Functional description
* Reassign a proper (unique) name for auto-generated
* security classes.
*
**************************************/
const char* const prefix = is_field ? SQL_FLD_SECCLASS_PREFIX : SQL_SECCLASS_PREFIX;
const int prefix_length = is_field ? SQL_FLD_SECCLASS_PREFIX_LEN : SQL_SECCLASS_PREFIX_LEN;
if (strncmp(sec_class, prefix, prefix_length))
return;
if (tdgbl->RESTORE_ods < DB_VERSION_DDL11_2)
return;
ISC_STATUS_ARRAY status_vector;
isc_req_handle& handle = tdgbl->handles_fix_security_class_name_req_handle1;
if (!handle)
{
UCHAR blr_buffer[BUFFER_TINY];
UCHAR* blr = blr_buffer;
add_byte(blr, blr_version5);
add_byte(blr, blr_begin);
add_byte(blr, blr_message);
add_byte(blr, 0);
add_word(blr, 1);
add_byte(blr, blr_int64);
add_byte(blr, 0);
add_byte(blr, blr_send);
add_byte(blr, 0);
add_byte(blr, blr_begin);
add_byte(blr, blr_assignment);
add_byte(blr, blr_gen_id);
add_string(blr, SQL_SECCLASS_GENERATOR);
add_byte(blr, blr_literal);
add_byte(blr, blr_int64);
add_byte(blr, 0);
add_int64(blr, 1);
add_byte(blr, blr_parameter);
add_byte(blr, 0);
add_word(blr, 0);
add_byte(blr, blr_end);
add_byte(blr, blr_end);
add_byte(blr, blr_eoc);
const USHORT blr_length = blr - blr_buffer;
fb_assert(blr_length <= sizeof(blr_buffer));
if (isc_compile_request(status_vector, &DB, &handle,
blr_length, (const SCHAR*) blr_buffer))
{
BURP_error_redirect(status_vector, 316);
// msg 316 Failed while fixing the security class name
}
}
if (isc_start_request(status_vector, &handle, &gds_trans, 0))
{
BURP_error_redirect(status_vector, 316);
// msg 316 Failed while fixing the security class name
}
SINT64 id = 0;
if (isc_receive(status_vector, &handle, 0, sizeof(SINT64), &id, 0))
{
BURP_error_redirect(status_vector, 316);
// msg 316 Failed while fixing the security class name
}
fb_assert(id);
snprintf(sec_class, MAX_SQL_IDENTIFIER_SIZE, "%s%"SQUADFORMAT, prefix, id);
}
void general_on_error()
{
/**************************************
*
* g e n e r a l _ o n _ e r r o r
*
**************************************
*
* Functional description
* Handle any general ON_ERROR clause during restore.
*
**************************************/
BurpGlobals* tdgbl = BurpGlobals::getSpecific();
BURP_print_status (isc_status, true);
BURP_abort ();
}
bool get_acl(BurpGlobals* tdgbl,
const TEXT* owner_nm,
ISC_QUAD* blob_id,
ISC_QUAD* new_blob_id)
{
/**************************************
*
* g e t _ a c l
*
**************************************
*
* Functional description
*
* open the blob that contains the ACL list
* get the ACL list of a relation
* replace the owner of the relation in the ACL list with
* the creator of the relation
* create a new blob
* store the new ACL list in the new blob
*
**************************************/
static const UCHAR blr_items[] =
{
isc_info_blob_max_segment,
isc_info_blob_total_length,
isc_info_blob_num_segments
};
// If the blob is null, don't store it. It will be restored as null.
if (!blob_id->gds_quad_high && !blob_id->gds_quad_low)
return false;
// Open the blob and get it's vital statistics
ISC_STATUS_ARRAY status_vector;
UserBlob blob(status_vector);
if (! blob.open(DB, gds_trans, *blob_id))
{
// msg 24 isc_open_blob failed
BURP_error_redirect (status_vector, 24);
}
UCHAR blob_info[32];
if (!blob.getInfo(sizeof(blr_items), blr_items, sizeof(blob_info), blob_info))
{
// msg 20 isc_blob_info failed
BURP_error_redirect (status_vector, 20);
}
ULONG length = 0;
UCHAR item;
USHORT max_segment;
ULONG 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 = 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;
fb_assert(length <= ULONG(MAX_SLONG));
// 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 ourselves
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 size_t owner_nm_len = strlen(owner_nm);
fb_assert(owner_nm_len <= size_t(MAX_UCHAR));
// If some day, ACLs become bigger than MAX_SLONG, we should review this code.
const SLONG bufSize = SLONG(length) - id_person_len + SLONG(owner_nm_len);
fb_assert(bufSize > 0);
BlobBuffer new_static_buffer;
UCHAR* const new_buffer = new_static_buffer.getBuffer(bufSize);
from = buffer;
UCHAR* to = new_buffer;
*to++ = *from++; // copy ACL_verion
*to++ = *from++; // copy ACL_id_list
*to++ = *from++; // copy id_person
*to++ = UCHAR(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 (ULONG 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
USHORT blr_length = blr - blr_buffer;
#ifdef DEBUG
fb_print_blr(blr_buffer, blr_length, NULL, NULL, 0);
#endif
FB_API_HANDLE request = 0;
ISC_STATUS_ARRAY status_vector;
if (isc_compile_request (status_vector, &DB, &request,
blr_length, reinterpret_cast<const char*>(blr_buffer)))
{
fb_print_blr(blr_buffer, blr_length, NULL, NULL, 0);
if (!tdgbl->gbl_sw_incremental)
BURP_error_redirect (status_vector, 27);
// msg 27 isc_compile_request failed
else
{
BURP_print_status (status_vector);
BURP_free (blr_buffer);
return ignore_data(tdgbl, relation);
}
}
BURP_free (blr_buffer);
SSHORT* buffer = NULL;
BURP_verbose (124, relation->rel_name);
// msg 124 restoring data for relation %s
lstring data;
data.lstr_allocated = 0;
data.lstr_address = NULL;
ULONG old_length = 0;
ULONG records = 0;
rec_type record;
while (true)
{
if (get(tdgbl) != att_data_length)
BURP_error_redirect (NULL, 39);
// msg 39 expected record length
USHORT l = (USHORT) get_numeric(tdgbl);
if (!tdgbl->gbl_sw_transportable && l != length)
{
#ifdef sparc
if (!old_length)
old_length = recompute_length(tdgbl, relation);
#endif
if (l != old_length)
{
BURP_error(40, true, SafeArg() << length << l);
// msg 40 wrong length record, expected %ld encountered %ld
}
}
if (!buffer) {
buffer = (SSHORT *) BURP_alloc (MAX (length, l));
}
UCHAR* p;
if (tdgbl->gbl_sw_transportable)
{
if (get(tdgbl) != att_xdr_length)
BURP_error_redirect (NULL, 55);
// msg 55 Expected XDR record length
else
{
data.lstr_length = l = (USHORT) get_numeric(tdgbl);
if (l > data.lstr_allocated)
{
data.lstr_allocated = l;
if (data.lstr_address)
BURP_free (data.lstr_address);
data.lstr_address = BURP_alloc(data.lstr_allocated);
}
p = data.lstr_address;
}
}
else
p = reinterpret_cast<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, false);
X.RDB$DESCRIPTION.NULL = FALSE;
}
break;
case att_exception_description2:
if (!X.RDB$DESCRIPTION.NULL)
BURP_error(311, true, SafeArg() << att_exception_description2 << X.RDB$EXCEPTION_NAME);
else
{
msg_seen = false;
get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
X.RDB$DESCRIPTION.NULL = FALSE;
}
break;
case att_exception_msg:
if (msg_seen)
BURP_error(311, true, SafeArg() << att_exception_msg << X.RDB$EXCEPTION_NAME);
else if (!X.RDB$MESSAGE.NULL)
{
msg_seen = true;
BURP_print(312, SafeArg() << att_exception_msg << X.RDB$EXCEPTION_NAME);
eat_text(tdgbl);
}
else
{
msg_seen = true;
l2 = GET_TEXT(X.RDB$MESSAGE);
msg_ptr += l2;
X.RDB$MESSAGE.NULL = FALSE;
}
break;
case att_exception_msg2:
if (msg_seen)
BURP_error(311, true, SafeArg() << att_exception_msg2 << X.RDB$EXCEPTION_NAME);
else if (!X.RDB$MESSAGE.NULL)
{
BURP_print(312, SafeArg() << att_exception_msg2 << X.RDB$EXCEPTION_NAME);
eat_text2(tdgbl);
}
else
{
GET_TEXT2(X.RDB$MESSAGE);
X.RDB$MESSAGE.NULL = FALSE;
}
break;
default:
if (msg_seen && (tdgbl->RESTORE_format == 7 || tdgbl->RESTORE_format == 8))
{
// we have a corrupt backup
if (!failed_attrib)
{
failed_attrib = attribute;
BURP_print(313, SafeArg() << failed_attrib << X.RDB$EXCEPTION_NAME);
}
// Notice we use 1021 instead of 1023 because this is the maximum length
// for this field in v2.0 and v2.1 and they produce the corrupt backups.
const int FIELD_LIMIT = 1021;
const int remaining = FIELD_LIMIT - l2;
if (remaining < 1) // not enough space
{
bad_attribute(scan_next_attr, failed_attrib, 287);
break;
}
*msg_ptr++ = char(attribute); // (1)
UCHAR* rc_ptr = get_block(tdgbl, msg_ptr, MIN(remaining - 1, 255));
if (remaining > 1 && rc_ptr == msg_ptr) // we couldn't read anything
{
bad_attribute(scan_next_attr, failed_attrib, 287);
break;
}
l2 += rc_ptr - msg_ptr + 1; // + 1 because (1)
msg_ptr = rc_ptr;
*msg_ptr = 0;
if (l2 == FIELD_LIMIT)
msg_seen = false;
}
else
bad_attribute(scan_next_attr, attribute, 287); // msg 287 exception
break;
}
}
// Versions prior to FB2.0 don't support a field longer than varchar(78).
// Versions prior to FB2.5 use a field length of 1021, not 1023.
if (tdgbl->RESTORE_ods < DB_VERSION_DDL11)
X.RDB$MESSAGE[78] = 0;
else if (tdgbl->RESTORE_ods < DB_VERSION_DDL11_2)
X.RDB$MESSAGE[1021] = 0;
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation)
{
/**************************************
*
* g e t _ f i e l d
*
**************************************
*
* Functional description
* Reconstruct a local field.
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
// If it is a view and there is a global transaction then use it
bool global_tr = false;
isc_tr_handle local_trans;
if ((relation->rel_flags & REL_view) && tdgbl->global_trans)
{
local_trans = tdgbl->global_trans;
global_tr = true;
}
else
local_trans = gds_trans;
burp_fld* field = (burp_fld*) BURP_alloc_zero (sizeof(burp_fld));
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_field_req_handle1)
X IN RDB$RELATION_FIELDS
strcpy (X.RDB$RELATION_NAME, relation->rel_name);
X.RDB$FIELD_POSITION = 0;
memset (X.RDB$QUERY_NAME, ' ', sizeof(X.RDB$QUERY_NAME));
X.RDB$VIEW_CONTEXT.NULL = TRUE;
X.RDB$BASE_FIELD.NULL = TRUE;
X.RDB$SECURITY_CLASS.NULL = TRUE;
X.RDB$QUERY_NAME.NULL = TRUE;
X.RDB$QUERY_HEADER.NULL = TRUE;
X.RDB$EDIT_STRING.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$FIELD_POSITION.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$COMPLEX_NAME.NULL = TRUE;
X.RDB$UPDATE_FLAG.NULL = TRUE;
X.RDB$DEFAULT_SOURCE.NULL = TRUE;
X.RDB$DEFAULT_VALUE.NULL = TRUE;
X.RDB$NULL_FLAG.NULL = TRUE;
X.RDB$COLLATION_ID.NULL = TRUE;
skip_init(&scan_next_attr);
while (get_attribute(&attribute, tdgbl) != att_end)
{
switch (skip_scan(&scan_next_attr), attribute)
{
case att_field_name:
field->fld_name_length = GET_TEXT(field->fld_name);
BURP_verbose (115, field->fld_name);
// msg 115 restoring field %s
strcpy (X.RDB$FIELD_NAME, field->fld_name);
break;
case att_field_source:
GET_TEXT(X.RDB$FIELD_SOURCE);
break;
case att_field_security_class:
GET_TEXT(X.RDB$SECURITY_CLASS);
fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, true);
X.RDB$SECURITY_CLASS.NULL = FALSE;
break;
case att_field_query_name:
GET_TEXT(X.RDB$QUERY_NAME);
X.RDB$QUERY_NAME.NULL = FALSE;
break;
case att_field_query_header:
X.RDB$QUERY_HEADER.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$QUERY_HEADER, global_tr);
break;
case att_field_edit_string:
GET_TEXT(X.RDB$EDIT_STRING);
X.RDB$EDIT_STRING.NULL = FALSE;
break;
case att_field_position:
X.RDB$FIELD_POSITION.NULL = FALSE;
X.RDB$FIELD_POSITION = (USHORT) get_numeric(tdgbl);
break;
case att_field_number:
field->fld_number = (USHORT) get_numeric(tdgbl);
break;
case att_field_type:
field->fld_type = (USHORT) get_numeric(tdgbl);
break;
case att_field_length:
field->fld_length = (USHORT) get_numeric(tdgbl);
break;
case att_field_scale:
field->fld_scale = (USHORT) get_numeric(tdgbl);
break;
case att_field_sub_type:
field->fld_sub_type = (USHORT) get_numeric(tdgbl);
break;
case att_field_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl);
X.RDB$SYSTEM_FLAG.NULL = FALSE;
break;
case att_view_context:
X.RDB$VIEW_CONTEXT = (USHORT) get_numeric(tdgbl);
X.RDB$VIEW_CONTEXT.NULL = FALSE;
break;
case att_field_computed_flag:
if (get_numeric(tdgbl))
field->fld_flags |= FLD_computed;
break;
case att_base_field:
GET_TEXT(X.RDB$BASE_FIELD);
X.RDB$BASE_FIELD.NULL = FALSE;
break;
case att_field_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, global_tr);
break;
case att_field_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DESCRIPTION, global_tr);
break;
case att_field_complex_name:
GET_TEXT(X.RDB$COMPLEX_NAME);
X.RDB$COMPLEX_NAME.NULL = FALSE;
break;
case att_field_dimensions:
{
field->fld_dimensions = (USHORT) get_numeric(tdgbl);
field->fld_flags |= FLD_array;
USHORT n = field->fld_dimensions;
for (SLONG* rp = field->fld_ranges; n; rp += 2, n--)
{
if (get_attribute(&attribute, tdgbl) != att_field_range_low)
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
else
*rp = get_numeric(tdgbl);
if (get_attribute(&attribute, tdgbl) != att_field_range_high)
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
else
*(rp + 1) = get_numeric(tdgbl);
}
}
break;
case att_field_update_flag:
X.RDB$UPDATE_FLAG.NULL = FALSE;
X.RDB$UPDATE_FLAG = (USHORT) get_numeric(tdgbl);
break;
case att_field_character_length:
field->fld_character_length = (USHORT) get_numeric(tdgbl);
break;
case att_field_default_source:
X.RDB$DEFAULT_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, global_tr);
break;
case att_field_default_value:
X.RDB$DEFAULT_VALUE.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, global_tr);
break;
case att_field_null_flag:
if (tdgbl->gbl_sw_novalidity) {
get_numeric(tdgbl); // skip
}
else {
X.RDB$NULL_FLAG.NULL = FALSE;
X.RDB$NULL_FLAG = (USHORT) get_numeric(tdgbl);
}
break;
case att_field_character_set:
field->fld_character_set_id = (USHORT) get_numeric(tdgbl);
break;
case att_field_collation_id:
field->fld_collation_id = (USHORT) get_numeric(tdgbl);
X.RDB$COLLATION_ID.NULL = FALSE;
X.RDB$COLLATION_ID = field->fld_collation_id;
break;
default:
bad_attribute (scan_next_attr, attribute, 84);
// msg 84 column
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return field;
}
bool get_field_dimensions(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ f i e l d _ d i m e n s i o n s
*
**************************************
*
* Functional description
* Get array field dimensions in rdb$field_dimensions.
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
STORE (REQUEST_HANDLE tdgbl->handles_get_field_dimensions_req_handle1)
X IN RDB$FIELD_DIMENSIONS
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_field_name:
GET_TEXT(X.RDB$FIELD_NAME);
break;
case att_field_dimensions:
X.RDB$DIMENSION = (USHORT) get_numeric(tdgbl);
break;
case att_field_range_low:
X.RDB$LOWER_BOUND = get_numeric(tdgbl);
break;
case att_field_range_high:
X.RDB$UPPER_BOUND = get_numeric(tdgbl);
break;
default:
bad_attribute (scan_next_attr, attribute, 288);
// msg 288 array dimensions
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_files(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ f i l e s
*
**************************************
*
* Functional description
* Get any files that were stored; let
* somebody else worry about what to do with them.
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
STORE (REQUEST_HANDLE tdgbl->handles_get_files_req_handle1)
X IN RDB$FILES
X.RDB$FILE_FLAGS = 0;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_file_filename:
GET_TEXT(X.RDB$FILE_NAME);
BURP_verbose (116, X.RDB$FILE_NAME);
// msg 116 restoring file %s
break;
case att_file_sequence:
X.RDB$FILE_SEQUENCE = (USHORT) get_numeric(tdgbl);
break;
case att_file_start:
X.RDB$FILE_START = get_numeric(tdgbl);
break;
case att_file_length:
X.RDB$FILE_LENGTH = get_numeric(tdgbl);
break;
case att_file_flags:
X.RDB$FILE_FLAGS |= get_numeric(tdgbl);
break;
case att_shadow_number:
X.RDB$SHADOW_NUMBER = (USHORT) get_numeric(tdgbl);
if (tdgbl->gbl_sw_kill && X.RDB$SHADOW_NUMBER)
X.RDB$FILE_FLAGS |= FILE_inactive;
break;
default:
bad_attribute (scan_next_attr, attribute, 85);
// msg 85 file
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_filter(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ f i l t e r
*
**************************************
*
* Functional description
* Get a type definition in rdb$filters.
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
STORE (REQUEST_HANDLE tdgbl->handles_get_filter_req_handle1)
X IN RDB$FILTERS
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_filter_name:
GET_TEXT(X.RDB$FUNCTION_NAME);
BURP_verbose (117, X.RDB$FUNCTION_NAME);
// msg 117 restoring filter %s
break;
case att_filter_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_filter_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_filter_module_name:
GET_TEXT(X.RDB$MODULE_NAME);
break;
case att_filter_entrypoint:
GET_TEXT(X.RDB$ENTRYPOINT);
break;
case att_filter_input_sub_type:
X.RDB$INPUT_SUB_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_filter_output_sub_type:
X.RDB$OUTPUT_SUB_TYPE = (USHORT) get_numeric(tdgbl);
break;
default:
bad_attribute (scan_next_attr, attribute, 87);
// msg 87 filter
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_function(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ f u n c t i o n
*
**************************************
*
* Functional description
* Reconstruct a function.
*
**************************************/
att_type attribute;
GDS_NAME function_name;
TEXT temp[GDS_NAME_LEN];
SSHORT l;
scan_attr_t scan_next_attr;
bool existFlag = false;
STORE (REQUEST_HANDLE tdgbl->handles_get_function_req_handle1)
X IN RDB$FUNCTIONS
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$DESCRIPTION.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_function_name:
l = GET_TEXT(X.RDB$FUNCTION_NAME);
MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
BURP_verbose (118, temp);
// msg 118 restoring function %s
break;
case att_function_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_function_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_function_module_name:
GET_TEXT(X.RDB$MODULE_NAME);
break;
case att_function_entrypoint:
GET_TEXT(X.RDB$ENTRYPOINT);
break;
case att_function_return_arg:
X.RDB$RETURN_ARGUMENT = (USHORT) get_numeric(tdgbl);
break;
case att_function_query_name:
GET_TEXT(X.RDB$QUERY_NAME);
break;
case att_function_type:
X.RDB$FUNCTION_TYPE = (USHORT) get_numeric(tdgbl);
break;
default:
bad_attribute (scan_next_attr, attribute, 89);
// msg 89 function
break;
}
}
strcpy (function_name, X.RDB$FUNCTION_NAME);
END_STORE;
ON_ERROR
if (gds_status[1] != isc_no_dup)
{
general_on_error ();
}
else
{
existFlag = true;
}
END_ERROR;
// at the end of args for a function is the rec_function_end marker
while (get(tdgbl) == rec_function_arg)
get_function_arg(tdgbl, existFlag);
return true;
}
void get_function_arg(BurpGlobals* tdgbl, bool skip_arguments)
{
/**************************************
*
* g e t _ f u n c t i o n _ a r g
*
**************************************
*
* Functional description
* Reconstruct function argument.
*
**************************************/
att_type attribute;
SSHORT l;
TEXT temp[GDS_NAME_LEN];
scan_attr_t scan_next_attr;
if (skip_arguments)
{
char buf[MAX_SQL_IDENTIFIER_SIZE];
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_functionarg_name:
GET_TEXT(buf);
break;
case att_functionarg_position:
get_numeric(tdgbl);
break;
case att_functionarg_mechanism:
get_numeric(tdgbl);
break;
case att_functionarg_field_type:
get_numeric(tdgbl);
break;
case att_functionarg_field_scale:
get_numeric(tdgbl);
break;
case att_functionarg_field_length:
get_numeric(tdgbl);
break;
case att_functionarg_field_sub_type:
get_numeric(tdgbl);
break;
case att_functionarg_character_set:
get_numeric(tdgbl);
break;
case att_functionarg_field_precision:
get_numeric(tdgbl);
break;
default:
bad_attribute (scan_next_attr, attribute, 90);
// msg 90 function argument
break;
}
}
return;
}
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10)
{
// with RDB$FIELD_PRECISION
STORE (REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1)
X IN RDB$FUNCTION_ARGUMENTS
X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
X.RDB$CHARACTER_SET_ID.NULL = TRUE;
X.RDB$FIELD_PRECISION.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_functionarg_name:
l = GET_TEXT(X.RDB$FUNCTION_NAME);
MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
BURP_verbose (119, temp);
// msg 119 restoring argument for function %s
break;
case att_functionarg_position:
X.RDB$ARGUMENT_POSITION = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_mechanism:
X.RDB$MECHANISM = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_field_type:
X.RDB$FIELD_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_field_scale:
X.RDB$FIELD_SCALE = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_field_length:
X.RDB$FIELD_LENGTH = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_field_sub_type:
X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_character_set:
X.RDB$CHARACTER_SET_ID.NULL = FALSE;
X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_field_precision:
if (tdgbl->RESTORE_format >= 6)
{
X.RDB$FIELD_PRECISION.NULL = FALSE;
X.RDB$FIELD_PRECISION = (USHORT) get_numeric(tdgbl);
}
else
bad_attribute (scan_next_attr, attribute, 90);
break;
default:
bad_attribute (scan_next_attr, attribute, 90);
// msg 90 function argument
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
else
{
// without RDB$FIELD_PRECISION
STORE (REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1)
X IN RDB$FUNCTION_ARGUMENTS
X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
X.RDB$CHARACTER_SET_ID.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_functionarg_name:
l = GET_TEXT(X.RDB$FUNCTION_NAME);
MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
BURP_verbose (119, temp);
// msg 119 restoring argument for function %s
break;
case att_functionarg_position:
X.RDB$ARGUMENT_POSITION = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_mechanism:
X.RDB$MECHANISM = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_field_type:
X.RDB$FIELD_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_field_scale:
X.RDB$FIELD_SCALE = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_field_length:
X.RDB$FIELD_LENGTH = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_field_sub_type:
X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_character_set:
X.RDB$CHARACTER_SET_ID.NULL = FALSE;
X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl);
break;
case att_functionarg_field_precision:
if (tdgbl->RESTORE_format >= 6)
get_numeric(tdgbl);
else
bad_attribute (scan_next_attr, attribute, 90);
default:
bad_attribute (scan_next_attr, attribute, 90);
// msg 90 function argument
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
}
bool get_generator(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ g e n e r a t o r
*
**************************************
*
* Functional description
* Pick up a gen-id. Like most things, there is ughly history.
* In the modern world, gen_id are free floating records. In the
* bad old days they were attributes of relations. Handle both
* nicely.
*
**************************************/
SINT64 value = 0;
BASED_ON RDB$GENERATORS.RDB$GENERATOR_NAME name;
name[0] = 0; // just in case.
att_type attribute;
scan_attr_t scan_next_attr;
skip_init(&scan_next_attr);
ISC_QUAD gen_desc = {0, 0};
bool got_desc = false;
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_gen_generator:
GET_TEXT(name);
break;
case att_gen_value:
// IB v5 or earlier, gen_id value is an SLONG
value = (SINT64) get_numeric(tdgbl);
break;
case att_gen_value_int64:
// IB v6 or later, gen_id value is an SINT64
value = get_int64(tdgbl);
break;
case att_gen_description:
if (tdgbl->RESTORE_format >= 7)
{
get_source_blob (tdgbl, gen_desc, false);
got_desc = gen_desc.gds_quad_high || gen_desc.gds_quad_low;
}
else
bad_attribute(scan_next_attr, attribute, 289);
break;
default:
bad_attribute (scan_next_attr, attribute, 289);
// msg 289 generator
break;
}
}
if (tdgbl->gbl_sw_meta)
{
value = 0;
}
store_blr_gen_id(tdgbl, name, value, got_desc ? &gen_desc : NULL);
return true;
}
bool get_global_field(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ g l o b a l _ f i e l d
*
**************************************
*
* Functional description
* Reconstruct a global field.
*
**************************************/
att_type attribute;
TEXT temp[GDS_NAME_LEN];
SSHORT l;
scan_attr_t scan_next_attr;
gfld* gfield = NULL;
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10)
{
// with rdb$field_precision
STORE (REQUEST_HANDLE tdgbl->handles_get_global_field_req_handle1)
X IN RDB$FIELDS
X.RDB$FIELD_SCALE = X.RDB$SEGMENT_LENGTH = 0;
X.RDB$CHARACTER_SET_ID = X.RDB$COLLATION_ID = 0;
X.RDB$FIELD_SUB_TYPE = 0;
X.RDB$COMPUTED_BLR.NULL = TRUE;
X.RDB$COMPUTED_SOURCE.NULL = TRUE;
X.RDB$QUERY_NAME.NULL = TRUE;
X.RDB$EDIT_STRING.NULL = TRUE;
X.RDB$QUERY_HEADER.NULL = TRUE;
X.RDB$MISSING_VALUE.NULL = TRUE;
X.RDB$DEFAULT_VALUE.NULL = TRUE;
X.RDB$VALIDATION_BLR.NULL = TRUE;
X.RDB$VALIDATION_SOURCE.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$NULL_FLAG.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$DIMENSIONS.NULL = TRUE;
X.RDB$EXTERNAL_LENGTH.NULL = TRUE;
X.RDB$EXTERNAL_TYPE.NULL = TRUE;
X.RDB$EXTERNAL_SCALE.NULL = TRUE;
X.RDB$SEGMENT_LENGTH.NULL = TRUE;
X.RDB$CHARACTER_LENGTH.NULL = TRUE;
X.RDB$MISSING_SOURCE.NULL = TRUE;
X.RDB$DEFAULT_SOURCE.NULL = TRUE;
X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
X.RDB$CHARACTER_SET_ID.NULL = TRUE;
X.RDB$COLLATION_ID.NULL = TRUE;
X.RDB$FIELD_PRECISION.NULL = TRUE;
memset (X.RDB$QUERY_NAME, ' ', sizeof(X.RDB$QUERY_NAME));
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_field_name:
l = GET_TEXT(X.RDB$FIELD_NAME);
MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp));
BURP_verbose (121, temp);
// msg 121 restoring global field %s
break;
case att_field_query_name:
GET_TEXT(X.RDB$QUERY_NAME);
X.RDB$QUERY_NAME.NULL = FALSE;
break;
case att_field_edit_string:
GET_TEXT(X.RDB$EDIT_STRING);
X.RDB$EDIT_STRING.NULL = FALSE;
break;
case att_field_query_header:
X.RDB$QUERY_HEADER.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$QUERY_HEADER, false);
break;
case att_field_type:
X.RDB$FIELD_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_field_length:
X.RDB$FIELD_LENGTH = (USHORT) get_numeric(tdgbl);
break;
case att_field_scale:
X.RDB$FIELD_SCALE = (USHORT) get_numeric(tdgbl);
X.RDB$FIELD_SCALE.NULL = FALSE;
break;
case att_field_sub_type:
X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(tdgbl);
X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
break;
case att_field_segment_length:
X.RDB$SEGMENT_LENGTH = (USHORT) get_numeric(tdgbl);
if (X.RDB$SEGMENT_LENGTH)
X.RDB$SEGMENT_LENGTH.NULL = FALSE;
break;
case att_field_computed_blr:
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_blr_blob (tdgbl, gfield->gfld_computed_blr, true);
gfield->gfld_flags |= GFLD_computed_blr;
}
else
{
X.RDB$COMPUTED_BLR.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$COMPUTED_BLR, false);
}
break;
case att_field_computed_source:
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_misc_blob (tdgbl, gfield->gfld_computed_source, true);
gfield->gfld_flags |= GFLD_computed_source;
}
else
{
X.RDB$COMPUTED_SOURCE.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false);
}
break;
case att_field_computed_source2:
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_source_blob (tdgbl, gfield->gfld_computed_source2, true);
gfield->gfld_flags |= GFLD_computed_source2;
}
else
{
X.RDB$COMPUTED_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false);
}
break;
case att_field_validation_blr:
if (tdgbl->gbl_sw_novalidity)
eat_blob(tdgbl);
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_blr_blob (tdgbl, gfield->gfld_vb, true);
gfield->gfld_flags |= GFLD_validation_blr;
}
else
{
X.RDB$VALIDATION_BLR.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$VALIDATION_BLR, false);
}
}
break;
case att_field_validation_source:
if (tdgbl->gbl_sw_novalidity)
eat_blob(tdgbl);
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_misc_blob (tdgbl, gfield->gfld_vs, true);
gfield->gfld_flags |= GFLD_validation_source;
}
else
{
X.RDB$VALIDATION_SOURCE.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false);
}
}
break;
case att_field_validation_source2:
if (tdgbl->gbl_sw_novalidity)
eat_blob(tdgbl);
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_source_blob (tdgbl, gfield->gfld_vs2, true);
gfield->gfld_flags |= GFLD_validation_source2;
}
else
{
X.RDB$VALIDATION_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false);
}
}
break;
case att_field_missing_value:
X.RDB$MISSING_VALUE.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$MISSING_VALUE, false);
break;
case att_field_default_value:
X.RDB$DEFAULT_VALUE.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, false);
break;
case att_field_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl);
X.RDB$SYSTEM_FLAG.NULL = FALSE;
break;
case att_field_null_flag:
if (tdgbl->gbl_sw_novalidity) {
get_numeric(tdgbl); // skip
}
else {
X.RDB$NULL_FLAG = (USHORT) get_numeric(tdgbl);
X.RDB$NULL_FLAG.NULL = FALSE;
}
break;
case att_field_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_field_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_field_external_length:
X.RDB$EXTERNAL_LENGTH.NULL = FALSE;
X.RDB$EXTERNAL_LENGTH = (USHORT) get_numeric(tdgbl);
break;
case att_field_external_scale:
X.RDB$EXTERNAL_SCALE.NULL = FALSE;
X.RDB$EXTERNAL_SCALE = (USHORT) get_numeric(tdgbl);
break;
case att_field_external_type:
X.RDB$EXTERNAL_TYPE.NULL = FALSE;
X.RDB$EXTERNAL_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_field_dimensions:
X.RDB$DIMENSIONS.NULL = FALSE;
X.RDB$DIMENSIONS = (USHORT) get_numeric(tdgbl);
break;
case att_field_character_length:
X.RDB$CHARACTER_LENGTH.NULL = FALSE;
X.RDB$CHARACTER_LENGTH = (USHORT) get_numeric(tdgbl);
break;
case att_field_default_source:
X.RDB$DEFAULT_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, false);
break;
case att_field_missing_source:
X.RDB$MISSING_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$MISSING_SOURCE, false);
break;
case att_field_character_set:
X.RDB$CHARACTER_SET_ID.NULL = FALSE;
X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl);
break;
case att_field_collation_id:
X.RDB$COLLATION_ID.NULL = FALSE;
X.RDB$COLLATION_ID = (USHORT) get_numeric(tdgbl);
break;
case att_field_precision:
if (tdgbl->RESTORE_format >= 6)
{
X.RDB$FIELD_PRECISION.NULL = FALSE;
X.RDB$FIELD_PRECISION = (USHORT) get_numeric(tdgbl);
}
else
bad_attribute (scan_next_attr, attribute, 92);
break;
default:
bad_attribute (scan_next_attr, attribute, 92);
// msg 92 domain
break;
}
}
if (X.RDB$FIELD_TYPE <= DTYPE_BLR_MAX)
{
l = gds_cvt_blr_dtype[X.RDB$FIELD_TYPE];
if (l = type_lengths[l])
X.RDB$FIELD_LENGTH = l;
}
if (gfield)
strcpy (gfield->gfld_name, X.RDB$FIELD_NAME);
if (tdgbl->gbl_sw_fix_fss_data && tdgbl->gbl_sw_fix_fss_data_id == 0 &&
!X.RDB$CHARACTER_SET_ID.NULL && X.RDB$CHARACTER_SET_ID == CS_UNICODE_FSS &&
((!X.RDB$CHARACTER_LENGTH.NULL &&
(X.RDB$FIELD_TYPE == blr_text || X.RDB$FIELD_TYPE == blr_varying)) ||
X.RDB$FIELD_TYPE == blr_blob))
{
if (X.RDB$FIELD_TYPE != blr_blob)
X.RDB$CHARACTER_LENGTH = X.RDB$FIELD_LENGTH;
X.RDB$CHARACTER_SET_ID = CS_NONE;
X.RDB$COLLATION_ID = 0;
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
else // RESTORE_ods < DB_VERSION_DDL10
{
// without rdb$field_precision
STORE (REQUEST_HANDLE tdgbl->handles_get_global_field_req_handle1)
X IN RDB$FIELDS
X.RDB$FIELD_SCALE = X.RDB$SEGMENT_LENGTH = 0;
X.RDB$CHARACTER_SET_ID = X.RDB$COLLATION_ID = 0;
X.RDB$FIELD_SUB_TYPE = 0;
X.RDB$COMPUTED_BLR.NULL = TRUE;
X.RDB$COMPUTED_SOURCE.NULL = TRUE;
X.RDB$QUERY_NAME.NULL = TRUE;
X.RDB$EDIT_STRING.NULL = TRUE;
X.RDB$QUERY_HEADER.NULL = TRUE;
X.RDB$MISSING_VALUE.NULL = TRUE;
X.RDB$DEFAULT_VALUE.NULL = TRUE;
X.RDB$VALIDATION_BLR.NULL = TRUE;
X.RDB$VALIDATION_SOURCE.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$NULL_FLAG.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$DIMENSIONS.NULL = TRUE;
X.RDB$EXTERNAL_LENGTH.NULL = TRUE;
X.RDB$EXTERNAL_TYPE.NULL = TRUE;
X.RDB$EXTERNAL_SCALE.NULL = TRUE;
X.RDB$SEGMENT_LENGTH.NULL = TRUE;
X.RDB$CHARACTER_LENGTH.NULL = TRUE;
X.RDB$MISSING_SOURCE.NULL = TRUE;
X.RDB$DEFAULT_SOURCE.NULL = TRUE;
X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
X.RDB$CHARACTER_SET_ID.NULL = TRUE;
X.RDB$COLLATION_ID.NULL = TRUE;
memset (X.RDB$QUERY_NAME, ' ', sizeof(X.RDB$QUERY_NAME));
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_field_name:
l = GET_TEXT(X.RDB$FIELD_NAME);
MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp));
BURP_verbose (121, temp);
// msg 121 restoring global field %s
break;
case att_field_query_name:
GET_TEXT(X.RDB$QUERY_NAME);
X.RDB$QUERY_NAME.NULL = FALSE;
break;
case att_field_edit_string:
GET_TEXT(X.RDB$EDIT_STRING);
X.RDB$EDIT_STRING.NULL = FALSE;
break;
case att_field_query_header:
X.RDB$QUERY_HEADER.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$QUERY_HEADER, false);
break;
case att_field_type:
X.RDB$FIELD_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_field_length:
X.RDB$FIELD_LENGTH = (USHORT) get_numeric(tdgbl);
break;
case att_field_scale:
X.RDB$FIELD_SCALE = (USHORT) get_numeric(tdgbl);
X.RDB$FIELD_SCALE.NULL = FALSE;
break;
case att_field_sub_type:
X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric(tdgbl);
X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
break;
case att_field_segment_length:
X.RDB$SEGMENT_LENGTH = (USHORT) get_numeric(tdgbl);
if (X.RDB$SEGMENT_LENGTH)
X.RDB$SEGMENT_LENGTH.NULL = FALSE;
break;
case att_field_computed_blr:
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_blr_blob (tdgbl, gfield->gfld_computed_blr, true);
gfield->gfld_flags |= GFLD_computed_blr;
}
else
{
X.RDB$COMPUTED_BLR.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$COMPUTED_BLR, false);
}
break;
case att_field_computed_source:
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_misc_blob (tdgbl, gfield->gfld_computed_source, true);
gfield->gfld_flags |= GFLD_computed_source;
}
else
{
X.RDB$COMPUTED_SOURCE.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false);
}
break;
case att_field_computed_source2:
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_source_blob (tdgbl, gfield->gfld_computed_source2, true);
gfield->gfld_flags |= GFLD_computed_source2;
}
else
{
X.RDB$COMPUTED_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false);
}
break;
case att_field_validation_blr:
if (tdgbl->gbl_sw_novalidity)
eat_blob(tdgbl);
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_blr_blob (tdgbl, gfield->gfld_vb, true);
gfield->gfld_flags |= GFLD_validation_blr;
}
else
{
X.RDB$VALIDATION_BLR.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$VALIDATION_BLR, false);
}
}
break;
case att_field_validation_source:
if (tdgbl->gbl_sw_novalidity)
eat_blob(tdgbl);
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_misc_blob (tdgbl, gfield->gfld_vs, true);
gfield->gfld_flags |= GFLD_validation_source;
}
else
{
X.RDB$VALIDATION_SOURCE.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false);
}
}
break;
case att_field_validation_source2:
if (tdgbl->gbl_sw_novalidity)
eat_blob(tdgbl);
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));
get_source_blob (tdgbl, gfield->gfld_vs2, true);
gfield->gfld_flags |= GFLD_validation_source2;
}
else
{
X.RDB$VALIDATION_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false);
}
}
break;
case att_field_missing_value:
X.RDB$MISSING_VALUE.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$MISSING_VALUE, false);
break;
case att_field_default_value:
X.RDB$DEFAULT_VALUE.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, false);
break;
case att_field_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl);
X.RDB$SYSTEM_FLAG.NULL = FALSE;
break;
case att_field_null_flag:
if (tdgbl->gbl_sw_novalidity) {
get_numeric(tdgbl); // skip
}
else {
X.RDB$NULL_FLAG = (USHORT) get_numeric(tdgbl);
X.RDB$NULL_FLAG.NULL = FALSE;
}
break;
case att_field_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_field_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_field_external_length:
X.RDB$EXTERNAL_LENGTH.NULL = FALSE;
X.RDB$EXTERNAL_LENGTH = (USHORT) get_numeric(tdgbl);
break;
case att_field_external_scale:
X.RDB$EXTERNAL_SCALE.NULL = FALSE;
X.RDB$EXTERNAL_SCALE = (USHORT) get_numeric(tdgbl);
break;
case att_field_external_type:
X.RDB$EXTERNAL_TYPE.NULL = FALSE;
X.RDB$EXTERNAL_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_field_dimensions:
X.RDB$DIMENSIONS.NULL = FALSE;
X.RDB$DIMENSIONS = (USHORT) get_numeric(tdgbl);
break;
case att_field_character_length:
X.RDB$CHARACTER_LENGTH.NULL = FALSE;
X.RDB$CHARACTER_LENGTH = (USHORT) get_numeric(tdgbl);
break;
case att_field_default_source:
X.RDB$DEFAULT_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, false);
break;
case att_field_missing_source:
X.RDB$MISSING_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$MISSING_SOURCE, false);
break;
case att_field_character_set:
X.RDB$CHARACTER_SET_ID.NULL = FALSE;
X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric(tdgbl);
break;
case att_field_collation_id:
X.RDB$COLLATION_ID.NULL = FALSE;
X.RDB$COLLATION_ID = (USHORT) get_numeric(tdgbl);
break;
case att_field_precision:
if (tdgbl->RESTORE_format >= 6)
get_numeric(tdgbl);
else
bad_attribute (scan_next_attr, attribute, 92);
break;
default:
bad_attribute (scan_next_attr, attribute, 92);
// msg 92 domain
break;
}
}
if (X.RDB$FIELD_TYPE <= DTYPE_BLR_MAX)
{
l = gds_cvt_blr_dtype[X.RDB$FIELD_TYPE];
if (l = type_lengths[l])
X.RDB$FIELD_LENGTH = l;
}
if (gfield)
strcpy (gfield->gfld_name, X.RDB$FIELD_NAME);
if (tdgbl->gbl_sw_fix_fss_data && tdgbl->gbl_sw_fix_fss_data_id == 0 &&
!X.RDB$CHARACTER_SET_ID.NULL && X.RDB$CHARACTER_SET_ID == CS_UNICODE_FSS &&
((!X.RDB$CHARACTER_LENGTH.NULL &&
(X.RDB$FIELD_TYPE == blr_text || X.RDB$FIELD_TYPE == blr_varying)) ||
X.RDB$FIELD_TYPE == blr_blob))
{
if (X.RDB$FIELD_TYPE != blr_blob)
X.RDB$CHARACTER_LENGTH = X.RDB$FIELD_LENGTH;
X.RDB$CHARACTER_SET_ID = CS_NONE;
X.RDB$COLLATION_ID = 0;
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
if (gfield)
{
gfield->gfld_next = tdgbl->gbl_global_fields;
tdgbl->gbl_global_fields = gfield;
}
return true;
}
bool get_index(BurpGlobals* tdgbl, const burp_rel* relation)
{
/**************************************
*
* g e t _ i n d e x
*
**************************************
*
* Functional description
* Build an index. At the end stop
* and check that all fields are defined.
* If any fields are missing, delete the
* index.
*
**************************************/
BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name;
att_type attribute;
bool foreign_index = false;
scan_attr_t scan_next_attr;
SSHORT count = 0, segments = 0;
STORE (REQUEST_HANDLE tdgbl->handles_get_index_req_handle1)
X IN RDB$INDICES
strcpy (X.RDB$RELATION_NAME, relation->rel_name);
X.RDB$UNIQUE_FLAG = 0;
if (!tdgbl->gbl_sw_deactivate_indexes)
X.RDB$INDEX_INACTIVE = FALSE;
else
X.RDB$INDEX_INACTIVE = TRUE;
X.RDB$INDEX_TYPE.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$FOREIGN_KEY.NULL = TRUE;
X.RDB$EXPRESSION_SOURCE.NULL = TRUE;
X.RDB$EXPRESSION_BLR.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_index_name:
GET_TEXT(X.RDB$INDEX_NAME);
strcpy (index_name, X.RDB$INDEX_NAME);
BURP_verbose (122, X.RDB$INDEX_NAME);
break;
case att_segment_count:
X.RDB$SEGMENT_COUNT = segments = (USHORT) get_numeric(tdgbl);
break;
case att_index_unique_flag:
X.RDB$UNIQUE_FLAG = (USHORT) get_numeric(tdgbl);
break;
case att_index_inactive:
X.RDB$INDEX_INACTIVE = (USHORT) get_numeric(tdgbl);
// Defer foreign key index activation
// Modified by Toni Martir, all index deferred when verbose
if (tdgbl->gbl_sw_verbose)
{
if (!X.RDB$INDEX_INACTIVE)
X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE;
}
else
{
if (!X.RDB$INDEX_INACTIVE && foreign_index)
X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE;
}
if (tdgbl->gbl_sw_deactivate_indexes)
X.RDB$INDEX_INACTIVE = TRUE;
break;
case att_index_type:
X.RDB$INDEX_TYPE.NULL = FALSE;
X.RDB$INDEX_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_index_field_name:
STORE (REQUEST_HANDLE tdgbl->handles_get_index_req_handle2)
Y IN RDB$INDEX_SEGMENTS
GET_TEXT(Y.RDB$FIELD_NAME);
strcpy (Y.RDB$INDEX_NAME, X.RDB$INDEX_NAME);
Y.RDB$FIELD_POSITION = count++;
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
break;
case att_index_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_index_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_index_expression_source:
X.RDB$EXPRESSION_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$EXPRESSION_SOURCE, false);
break;
case att_index_expression_blr:
X.RDB$EXPRESSION_BLR.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$EXPRESSION_BLR, false);
break;
case att_index_foreign_key:
foreign_index = true;
// Defer foreign key index activation
if (!X.RDB$INDEX_INACTIVE)
X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE;
if (tdgbl->gbl_sw_deactivate_indexes)
X.RDB$INDEX_INACTIVE = TRUE;
X.RDB$FOREIGN_KEY.NULL = FALSE;
GET_TEXT(X.RDB$FOREIGN_KEY);
break;
default:
bad_attribute (scan_next_attr, attribute, 93);
// msg 93 index
break;
}
}
count = 0;
FOR (REQUEST_HANDLE tdgbl->handles_get_index_req_handle3)
RFR IN RDB$RELATION_FIELDS CROSS I_S IN RDB$INDEX_SEGMENTS
OVER RDB$FIELD_NAME WITH I_S.RDB$INDEX_NAME = index_name AND
RFR.RDB$RELATION_NAME = relation->rel_name
count++;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
if (count != segments)
{
FOR (REQUEST_HANDLE tdgbl->handles_get_index_req_handle4)
I_S IN RDB$INDEX_SEGMENTS WITH I_S.RDB$INDEX_NAME = index_name
ERASE I_S;
ON_ERROR
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
return false;
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
void get_misc_blob(BurpGlobals* tdgbl,
ISC_QUAD& blob_id,
bool glb_trans)
{
/**************************************
*
* g e t _ m i s c _ b l o b
*
**************************************
*
* Functional description
* Read blob attributes and copy data from input file to nice,
* shiney, new blob.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
const size_t length = get_numeric(tdgbl);
// Create new blob
isc_tr_handle local_trans;
if (glb_trans && tdgbl->global_trans)
local_trans = tdgbl->global_trans;
else
local_trans = gds_trans;
UserBlob blob(status_vector);
if (!blob.create(DB, local_trans, blob_id))
{
BURP_error_redirect (status_vector, 37);
// msg 37 isc_create_blob failed
}
// Allocate blob buffer if static buffer is too short
BlobBuffer static_buffer;
UCHAR* const buffer = static_buffer.getBuffer(length);
if (length)
{
get_block(tdgbl, buffer, length);
}
if (!blob.putData(length, buffer))
{
BURP_error_redirect (status_vector, 38);
// msg 38 isc_put_segment failed
}
if (!blob.close())
BURP_error_redirect (status_vector, 23);
// msg 23 isc_close_blob failed
}
SLONG get_numeric(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ n u m e r i c
*
**************************************
*
* Functional description
* Get a numeric value from the input stream.
*
**************************************/
SLONG value[2];
// get_text needs additional space for the terminator,
// because it treats everything as strings.
fb_assert(sizeof(value) > sizeof(SLONG));
const SSHORT length = get_text(tdgbl, (TEXT*) value, sizeof(value));
return isc_vax_integer ((SCHAR *) value, length);
}
SINT64 get_int64(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ i n t 6 4
*
**************************************
*
* Functional description
* Get a possibly-64-bit numeric value from the input stream.
*
**************************************/
SLONG value[4];
// get_text needs additional space for the terminator,
// because it treats everything as strings.
fb_assert (sizeof(value) > sizeof(SINT64));
const SSHORT length = get_text(tdgbl, (TEXT*) value, sizeof(value));
return isc_portable_integer ((UCHAR *) value, length);
}
bool get_procedure(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ p r o c e d u r e
*
**************************************
*
* Functional description
* Reconstruct a stored procedure.
* Use the global_trans so we don't have to commit
* until after the indices are activated. This
* will allow us to use a PLAN in a SP.
*
**************************************/
att_type attribute;
GDS_NAME procedure_name = "";
TEXT temp[GDS_NAME_LEN];
SSHORT l;
scan_attr_t scan_next_attr;
isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
burp_prc* procedure = (burp_prc*) BURP_alloc_zero (sizeof(burp_prc));
procedure->prc_next = tdgbl->procedures;
tdgbl->procedures = procedure;
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11_1)
{
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_procedure_req_handle1)
X IN RDB$PROCEDURES
X.RDB$PROCEDURE_SOURCE.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$SECURITY_CLASS.NULL = TRUE;
X.RDB$OWNER_NAME.NULL = TRUE;
X.RDB$PROCEDURE_TYPE.NULL = FALSE;
X.RDB$PROCEDURE_TYPE = 0;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$VALID_BLR.NULL = TRUE;
X.RDB$DEBUG_INFO.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_procedure_name:
l = GET_TEXT(X.RDB$PROCEDURE_NAME);
//procedure->prc_name_length = l;
strcpy (procedure->prc_name, X.RDB$PROCEDURE_NAME);
MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof(temp));
BURP_verbose (195, temp);
// msg 195 restoring stored procedure %s
break;
case att_procedure_description:
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_procedure_description2:
get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_procedure_source:
get_misc_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true);
X.RDB$PROCEDURE_SOURCE.NULL = FALSE;
break;
case att_procedure_source2:
get_source_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true);
X.RDB$PROCEDURE_SOURCE.NULL = FALSE;
break;
case att_procedure_blr:
get_blr_blob (tdgbl, X.RDB$PROCEDURE_BLR, true);
break;
case att_procedure_security_class:
GET_TEXT(X.RDB$SECURITY_CLASS);
fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false);
X.RDB$SECURITY_CLASS.NULL = FALSE;
break;
case att_procedure_owner_name:
GET_TEXT(procedure->prc_owner);
break;
case att_procedure_inputs:
X.RDB$PROCEDURE_INPUTS = (USHORT) get_numeric(tdgbl);
if (X.RDB$PROCEDURE_INPUTS == 0)
X.RDB$PROCEDURE_INPUTS.NULL = TRUE;
else
X.RDB$PROCEDURE_INPUTS.NULL = FALSE;
break;
case att_procedure_outputs:
X.RDB$PROCEDURE_OUTPUTS = (USHORT) get_numeric(tdgbl);
break;
case att_procedure_type:
if (tdgbl->RESTORE_format >= 8)
X.RDB$PROCEDURE_TYPE = (USHORT) get_numeric(tdgbl);
else
bad_attribute (scan_next_attr, attribute, 290);
break;
case att_procedure_valid_blr:
if (tdgbl->RESTORE_format >= 8)
{
X.RDB$VALID_BLR.NULL = FALSE;
X.RDB$VALID_BLR = (USHORT) get_numeric(tdgbl);
}
else
bad_attribute (scan_next_attr, attribute, 290);
break;
case att_procedure_debug_info:
if (tdgbl->RESTORE_format >= 8)
{
X.RDB$DEBUG_INFO.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DEBUG_INFO, true);
}
else
bad_attribute (scan_next_attr, attribute, 290);
break;
default:
bad_attribute (scan_next_attr, attribute, 290);
// msg 290 procedure
break;
}
}
strcpy (procedure_name, X.RDB$PROCEDURE_NAME);
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
else
{
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_procedure_req_handle1)
X IN RDB$PROCEDURES
X.RDB$PROCEDURE_SOURCE.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$SECURITY_CLASS.NULL = TRUE;
X.RDB$OWNER_NAME.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_procedure_name:
l = GET_TEXT(X.RDB$PROCEDURE_NAME);
//procedure->prc_name_length = l;
strcpy (procedure->prc_name, X.RDB$PROCEDURE_NAME);
MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof(temp));
BURP_verbose (195, temp);
// msg 195 restoring stored procedure %s
break;
case att_procedure_description:
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_procedure_description2:
get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_procedure_source:
get_misc_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true);
X.RDB$PROCEDURE_SOURCE.NULL = FALSE;
break;
case att_procedure_source2:
get_source_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true);
X.RDB$PROCEDURE_SOURCE.NULL = FALSE;
break;
case att_procedure_blr:
get_blr_blob (tdgbl, X.RDB$PROCEDURE_BLR, true);
break;
case att_procedure_security_class:
GET_TEXT(X.RDB$SECURITY_CLASS);
fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false);
X.RDB$SECURITY_CLASS.NULL = FALSE;
break;
case att_procedure_owner_name:
GET_TEXT(procedure->prc_owner);
break;
case att_procedure_inputs:
X.RDB$PROCEDURE_INPUTS = (USHORT) get_numeric(tdgbl);
if (X.RDB$PROCEDURE_INPUTS == 0)
X.RDB$PROCEDURE_INPUTS.NULL = TRUE;
else
X.RDB$PROCEDURE_INPUTS.NULL = FALSE;
break;
case att_procedure_outputs:
X.RDB$PROCEDURE_OUTPUTS = (USHORT) get_numeric(tdgbl);
break;
case att_procedure_type:
if (tdgbl->RESTORE_format >= 8)
get_numeric(tdgbl);
else
bad_attribute (scan_next_attr, attribute, 290);
break;
default:
bad_attribute (scan_next_attr, attribute, 290);
// msg 290 procedure
break;
}
}
strcpy (procedure_name, X.RDB$PROCEDURE_NAME);
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
// at the end of prms for a procedure is the rec_procedure_end marker
while (get(tdgbl) == rec_procedure_prm)
get_procedure_prm (tdgbl, procedure_name);
return true;
}
bool get_procedure_prm (BurpGlobals* tdgbl, GDS_NAME procptr)
{
/**************************************
*
* g e t _ p r o c e d u r e _ p r m
*
**************************************
*
* Functional description
* Reconstruct stored procedure parameter.
* Use the global_trans so we don't have to commit
* until after the indices are activated. This
* will allow us to use a PLAN in a SP.
*
**************************************/
att_type attribute;
SSHORT l;
TEXT temp[GDS_NAME_LEN];
scan_attr_t scan_next_attr;
isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11_1)
{
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_procedure_prm_req_handle1)
X IN RDB$PROCEDURE_PARAMETERS
strcpy(X.RDB$PROCEDURE_NAME, procptr);
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$DEFAULT_VALUE.NULL = TRUE;
X.RDB$DEFAULT_SOURCE.NULL = TRUE;
X.RDB$COLLATION_ID.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$NULL_FLAG = FALSE;
X.RDB$NULL_FLAG.NULL = FALSE;
X.RDB$PARAMETER_MECHANISM = prm_mech_normal;
X.RDB$PARAMETER_MECHANISM.NULL = FALSE;
// DB_VERSION_DDL11_2
X.RDB$FIELD_NAME.NULL = TRUE;
X.RDB$RELATION_NAME.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_procedureprm_name:
l = GET_TEXT(X.RDB$PARAMETER_NAME);
MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof(temp));
BURP_verbose (196, temp);
// msg 196 restoring parameter %s for stored procedure
break;
case att_procedureprm_type:
X.RDB$PARAMETER_TYPE= (USHORT) get_numeric(tdgbl);
break;
case att_procedureprm_number:
X.RDB$PARAMETER_NUMBER= (USHORT) get_numeric(tdgbl);
break;
case att_procedureprm_field_source:
GET_TEXT(X.RDB$FIELD_SOURCE);
break;
case att_procedureprm_description:
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_procedureprm_description2:
get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_procedureprm_default_value:
if (tdgbl->RESTORE_format >= 8)
{
X.RDB$DEFAULT_VALUE.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, true);
}
else
bad_attribute (scan_next_attr, attribute, 291);
break;
case att_procedureprm_default_source:
if (tdgbl->RESTORE_format >= 8)
{
X.RDB$DEFAULT_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, true);
}
else
bad_attribute (scan_next_attr, attribute, 291);
break;
case att_procedureprm_collation_id:
if (tdgbl->RESTORE_format >= 8)
{
X.RDB$COLLATION_ID.NULL = FALSE;
X.RDB$COLLATION_ID = (USHORT) get_numeric(tdgbl);
}
else
bad_attribute (scan_next_attr, attribute, 291);
break;
case att_procedureprm_null_flag:
if (tdgbl->RESTORE_format >= 8)
X.RDB$NULL_FLAG = (USHORT) get_numeric(tdgbl);
else
bad_attribute (scan_next_attr, attribute, 291);
break;
case att_procedureprm_mechanism:
if (tdgbl->RESTORE_format >= 8)
X.RDB$PARAMETER_MECHANISM = (USHORT) get_numeric(tdgbl);
else
bad_attribute (scan_next_attr, attribute, 291);
break;
// DB_VERSION_DDL11_2
case att_procedureprm_field_name:
X.RDB$FIELD_NAME.NULL = FALSE;
GET_TEXT(X.RDB$FIELD_NAME);
break;
// DB_VERSION_DDL11_2
case att_procedureprm_relation_name:
X.RDB$RELATION_NAME.NULL = FALSE;
GET_TEXT(X.RDB$RELATION_NAME);
break;
default:
bad_attribute (scan_next_attr, attribute, 291);
// msg 291 procedure parameter
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
else
{
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_procedure_prm_req_handle1)
X IN RDB$PROCEDURE_PARAMETERS
X.RDB$DESCRIPTION.NULL = TRUE;
strcpy (X.RDB$PROCEDURE_NAME, procptr);
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_procedureprm_name:
l = GET_TEXT(X.RDB$PARAMETER_NAME);
MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof(temp));
BURP_verbose (196, temp);
// msg 196 restoring parameter %s for stored procedure
break;
case att_procedureprm_type:
X.RDB$PARAMETER_TYPE= (USHORT) get_numeric(tdgbl);
break;
case att_procedureprm_number:
X.RDB$PARAMETER_NUMBER= (USHORT) get_numeric(tdgbl);
break;
case att_procedureprm_field_source:
GET_TEXT(X.RDB$FIELD_SOURCE);
break;
case att_procedureprm_description:
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_procedureprm_description2:
get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
default:
bad_attribute (scan_next_attr, attribute, 291);
// msg 291 procedure parameter
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
return true;
}
bool get_ref_constraint(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ r e f _ c o n s t r a i n t
*
**************************************
*
* Functional description
* Restore data for referential constraints.
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
STORE (REQUEST_HANDLE tdgbl->handles_get_ref_constraint_req_handle1)
X IN RDB$REF_CONSTRAINTS
X.RDB$CONSTRAINT_NAME.NULL = TRUE;
X.RDB$CONST_NAME_UQ.NULL = TRUE;
X.RDB$MATCH_OPTION.NULL = TRUE;
X.RDB$UPDATE_RULE.NULL = TRUE;
X.RDB$DELETE_RULE.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_ref_constraint_name:
X.RDB$CONSTRAINT_NAME.NULL = FALSE;
GET_TEXT(X.RDB$CONSTRAINT_NAME);
break;
case att_ref_unique_const_name:
X.RDB$CONST_NAME_UQ.NULL = FALSE;
GET_TEXT(X.RDB$CONST_NAME_UQ);
break;
case att_ref_match_option:
X.RDB$MATCH_OPTION.NULL = FALSE;
GET_TEXT(X.RDB$MATCH_OPTION);
break;
case att_ref_update_rule:
X.RDB$UPDATE_RULE.NULL = FALSE;
GET_TEXT(X.RDB$UPDATE_RULE);
break;
case att_ref_delete_rule:
X.RDB$DELETE_RULE.NULL = FALSE;
GET_TEXT(X.RDB$DELETE_RULE);
break;
default:
bad_attribute (scan_next_attr, attribute, 292);
// msg 292 referential constraint
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_relation(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ r e l a t i o n
*
**************************************
*
* Functional description
* Write relation meta-data and data.
* Use the default transaction for RELATIONS,
* and use the global_trans for VIEWS. This
* enables us to have views of SP and views
* with plans. Assume it is a view if it has
* RDB$VIEW_BLR, also assume RDB$VIEW_BLR is
* the first blob in the backup file.
*
*
**************************************/
TEXT temp[GDS_NAME_LEN];
SSHORT l;
SLONG rel_flags = 0, sys_flag = 0, type = 0;
bool rel_flags_null = true, type_null = true;
ISC_QUAD view_blr = isc_blob_null, view_src = isc_blob_null,
rel_desc = isc_blob_null, ext_desc = isc_blob_null;
bool view_blr_null = true, view_src_null = true, rel_desc_null = true,
ext_desc_null = true;
BASED_ON RDB$RELATIONS.RDB$SECURITY_CLASS sec_class;
sec_class[0] = '\0';
bool sec_class_null = true;
BASED_ON RDB$RELATIONS.RDB$EXTERNAL_FILE ext_file_name;
ext_file_name[0] = '\0';
bool ext_file_name_null = true;
// Pick up relation attributes
burp_rel* relation = (burp_rel*) BURP_alloc_zero (sizeof(burp_rel));
relation->rel_next = tdgbl->relations;
tdgbl->relations = relation;
/*
STORE (REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1)
X IN RDB$RELATIONS
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$FLAGS.NULL = TRUE;
X.RDB$SECURITY_CLASS.NULL = TRUE;
X.RDB$VIEW_BLR.NULL = TRUE;
X.RDB$VIEW_SOURCE.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$RUNTIME.NULL = TRUE;
X.RDB$EXTERNAL_DESCRIPTION.NULL = TRUE;
*/
att_type attribute;
scan_attr_t scan_next_attr;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_relation_name:
l = GET_TEXT(relation->rel_name);
relation->rel_name_length = l;
MISC_terminate (relation->rel_name, temp, l, sizeof(temp));
BURP_verbose (167, temp);
// msg 167 restoring relation %s
break;
case att_relation_security_class:
sec_class_null = false;
GET_TEXT(sec_class);
fix_security_class_name(tdgbl, sec_class, false);
break;
case att_relation_view_blr:
view_blr_null = false;
get_blr_blob(tdgbl, view_blr, true);
relation->rel_flags |= REL_view;
break;
case att_relation_view_source:
view_src_null = false;
get_misc_blob (tdgbl, view_src, !view_blr_null);
break;
case att_relation_view_source2:
view_src_null = false;
get_source_blob(tdgbl, view_src, !view_blr_null);
break;
case att_relation_description:
rel_desc_null = false;
get_misc_blob(tdgbl, rel_desc, !view_blr_null);
break;
case att_relation_description2:
rel_desc_null = false;
get_source_blob(tdgbl, rel_desc, !view_blr_null);
break;
case att_relation_flags:
rel_flags_null = false;
rel_flags = get_numeric(tdgbl);
break;
case att_relation_system_flag:
sys_flag = get_numeric(tdgbl);
break;
case att_relation_ext_description:
ext_desc_null = false;
get_misc_blob(tdgbl, ext_desc, !view_blr_null);
break;
case att_relation_ext_description2:
ext_desc_null = false;
get_source_blob(tdgbl, ext_desc, !view_blr_null);
break;
case att_relation_owner_name:
GET_TEXT(relation->rel_owner);
break;
case att_relation_ext_file_name:
ext_file_name_null = false;
GET_TEXT(ext_file_name);
break;
case att_relation_type:
if (tdgbl->RESTORE_format >= 8)
{
type_null = false;
type = get_numeric(tdgbl);
}
else
bad_attribute(scan_next_attr, attribute, 111);
break;
default:
bad_attribute (scan_next_attr, attribute, 111);
// msg 111 table
break;
}
}
// If this is a view and there is a global transaction then use it
isc_tr_handle local_trans;
if (view_blr_null || !tdgbl->global_trans)
local_trans = gds_trans;
else
local_trans = tdgbl->global_trans;
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11_1)
{
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1)
X IN RDB$RELATIONS
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$FLAGS.NULL = rel_flags_null;
X.RDB$SECURITY_CLASS.NULL = sec_class_null;
X.RDB$VIEW_BLR.NULL = view_blr_null;
X.RDB$VIEW_SOURCE.NULL = view_src_null;
X.RDB$DESCRIPTION.NULL = rel_desc_null;
X.RDB$RUNTIME.NULL = TRUE;
X.RDB$EXTERNAL_DESCRIPTION.NULL = ext_desc_null;
X.RDB$EXTERNAL_FILE.NULL = ext_file_name_null;
X.RDB$RELATION_TYPE.NULL = type_null;
X.RDB$SYSTEM_FLAG = (USHORT) sys_flag;
X.RDB$FLAGS = (USHORT) rel_flags;
X.RDB$VIEW_BLR = view_blr;
X.RDB$VIEW_SOURCE = view_src;
X.RDB$DESCRIPTION = rel_desc;
X.RDB$EXTERNAL_DESCRIPTION = ext_desc;
strcpy(X.RDB$SECURITY_CLASS, sec_class);
strcpy(X.RDB$RELATION_NAME, relation->rel_name);
strcpy(X.RDB$EXTERNAL_FILE, ext_file_name);
X.RDB$RELATION_TYPE = (USHORT) type;
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
else
{
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1)
X IN RDB$RELATIONS
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$FLAGS.NULL = rel_flags_null;
X.RDB$SECURITY_CLASS.NULL = sec_class_null;
X.RDB$VIEW_BLR.NULL = view_blr_null;
X.RDB$VIEW_SOURCE.NULL = view_src_null;
X.RDB$DESCRIPTION.NULL = rel_desc_null;
X.RDB$RUNTIME.NULL = TRUE;
X.RDB$EXTERNAL_DESCRIPTION.NULL = ext_desc_null;
X.RDB$EXTERNAL_FILE.NULL = ext_file_name_null;
X.RDB$SYSTEM_FLAG = (USHORT) sys_flag;
X.RDB$FLAGS = (USHORT) rel_flags;
X.RDB$VIEW_BLR = view_blr;
X.RDB$VIEW_SOURCE = view_src;
X.RDB$DESCRIPTION = rel_desc;
X.RDB$EXTERNAL_DESCRIPTION = ext_desc;
strcpy(X.RDB$SECURITY_CLASS, sec_class);
strcpy(X.RDB$RELATION_NAME, relation->rel_name);
strcpy(X.RDB$EXTERNAL_FILE, ext_file_name);
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
// Eat up misc. records
burp_fld* field = NULL;
burp_fld** ptr = &relation->rel_fields;
rec_type record;
while (get_record(&record, tdgbl) != rec_data)
{
switch (record)
{
case rec_relation_end:
if (tdgbl->gbl_sw_incremental)
{
BURP_verbose (170, relation->rel_name);
// msg 170: committing metadata for relation %s
COMMIT
// existing ON_ERROR continues past error, beck
ON_ERROR
BURP_print (171, relation->rel_name);
// msg 171: error committing metadata for relation %s
BURP_print_status (tdgbl->status_vector);
ROLLBACK;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
return true;
case rec_field:
*ptr = field = get_field (tdgbl, relation);
if (!field)
return false;
ptr = &field->fld_next;
break;
case rec_view:
get_view(tdgbl, relation);
break;
default:
BURP_error(43, true, SafeArg() << record);
// msg 43 don't recognize record type %ld
break;
}
}
// If we fall thru, there are data records to be gotten
// we can get here only when restoring ancient gbak'ed files where rec_data
// was once embedded into rec_relation ... otherwise, meta commit happens
// when we see the first rec_relation_data
BURP_verbose (68);
// msg 68 committing meta data
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
get_data(tdgbl, relation);
return true;
}
bool get_rel_constraint(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ r e l _ c o n s t r a i n t
*
**************************************
*
* Functional description
* Restore data for relation constraints.
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
STORE (REQUEST_HANDLE tdgbl->handles_get_rel_constraint_req_handle1)
X IN RDB$RELATION_CONSTRAINTS
X.RDB$CONSTRAINT_NAME.NULL = TRUE;
X.RDB$CONSTRAINT_TYPE.NULL = TRUE;
X.RDB$RELATION_NAME.NULL = TRUE;
X.RDB$DEFERRABLE.NULL = TRUE;
X.RDB$INITIALLY_DEFERRED.NULL = TRUE;
X.RDB$INDEX_NAME.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_rel_constraint_name:
X.RDB$CONSTRAINT_NAME.NULL = FALSE;
GET_TEXT(X.RDB$CONSTRAINT_NAME);
break;
case att_rel_constraint_type:
X.RDB$CONSTRAINT_TYPE.NULL = FALSE;
GET_TEXT(X.RDB$CONSTRAINT_TYPE);
break;
case att_rel_constraint_rel_name:
X.RDB$RELATION_NAME.NULL = FALSE;
GET_TEXT(X.RDB$RELATION_NAME);
break;
case att_rel_constraint_defer:
X.RDB$DEFERRABLE.NULL = FALSE;
GET_TEXT(X.RDB$DEFERRABLE);
break;
case att_rel_constraint_init:
X.RDB$INITIALLY_DEFERRED.NULL = FALSE;
GET_TEXT(X.RDB$INITIALLY_DEFERRED);
break;
case att_rel_constraint_index:
X.RDB$INDEX_NAME.NULL = FALSE;
GET_TEXT(X.RDB$INDEX_NAME);
break;
default:
bad_attribute (scan_next_attr, attribute, 208);
// msg 208 table constraint
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_relation_data(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ r e l a t i o n _ d a t a
*
**************************************
*
* Functional description
* Restore data for a relation. This is called when the data is
* standing free from the relation definition. We first need to
* find the relation named. If we can't find it, give up.
*
**************************************/
BASED_ON RDB$RELATIONS.RDB$RELATION_NAME name;
att_type attribute;
scan_attr_t scan_next_attr;
burp_rel* relation = NULL;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_relation_name:
GET_TEXT(name);
relation = find_relation (tdgbl, name);
break;
default:
bad_attribute (scan_next_attr, attribute, 111);
// msg 111 table
break;
}
}
if (!relation)
BURP_error_redirect (NULL, 49);
// msg 49 no relation name for data
// Eat up misc. records
rec_type record;
get_record(&record, tdgbl);
SLONG gen_id;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), true)
{
switch (record)
{
case rec_relation_end:
return true;
case rec_data:
record = get_data(tdgbl, relation);
// get_data does a GET_RECORD
break;
case rec_gen_id:
gen_id = get_numeric(tdgbl);
store_blr_gen_id (tdgbl, name, gen_id, NULL);
get_record(&record, tdgbl);
break;
case rec_index:
get_index (tdgbl, relation);
get_record(&record, tdgbl);
break;
case rec_trigger: // old style trigger
get_trigger_old (tdgbl, relation);
get_record(&record, tdgbl);
break;
default:
bad_attribute (scan_next_attr, attribute, 111);
// msg 111 relation
get_record(&record, tdgbl);
break;
}
}
return true;
}
bool get_sql_roles(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ s q l _ r o l e s
*
**************************************
*
* Functional description
* Restore data for SQL roles
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
TEXT temp[GDS_NAME_LEN];
SSHORT l;
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11)
{
STORE (REQUEST_HANDLE tdgbl->handles_get_sql_roles_req_handle1)
X IN RDB$ROLES
X.RDB$ROLE_NAME.NULL = TRUE;
X.RDB$OWNER_NAME.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_role_name:
X.RDB$ROLE_NAME.NULL = FALSE;
l = GET_TEXT(X.RDB$ROLE_NAME);
MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp));
// msg 251, restoring SQL role: %s
BURP_verbose (251, temp);
break;
case att_role_owner_name:
X.RDB$OWNER_NAME.NULL = FALSE;
GET_TEXT(X.RDB$OWNER_NAME);
break;
case att_role_description:
if (tdgbl->RESTORE_format >= 7)
{
get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
X.RDB$DESCRIPTION.NULL = FALSE;
}
else
bad_attribute (scan_next_attr, attribute, 250);
break;
default:
// msg 250 SQL role
bad_attribute (scan_next_attr, attribute, 250);
break;
}
}
END_STORE
ON_ERROR
general_on_error ();
END_ERROR;
}
else if (tdgbl->RESTORE_ods >= DB_VERSION_DDL9)
{
// This is the first IB version (v5, ods9) where roles appeared.
// They remained unchanged for IB6 / FB1 and FB1.5 (ods10).
STORE (REQUEST_HANDLE tdgbl->handles_get_sql_roles_req_handle1)
X IN RDB$ROLES
X.RDB$ROLE_NAME.NULL = TRUE;
X.RDB$OWNER_NAME.NULL = TRUE;
// Here we didn't have RBD$SYSTEM_FLAG field.
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_role_name:
X.RDB$ROLE_NAME.NULL = FALSE;
l = GET_TEXT(X.RDB$ROLE_NAME);
MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp));
// msg 251, restoring SQL role: %s
BURP_verbose (251, temp);
break;
case att_role_owner_name:
X.RDB$OWNER_NAME.NULL = FALSE;
GET_TEXT(X.RDB$OWNER_NAME);
break;
case att_role_description:
if (tdgbl->RESTORE_format >= 7)
eat_blob(tdgbl);
else
bad_attribute (scan_next_attr, attribute, 250);
break;
default:
// msg 250 SQL role
bad_attribute (scan_next_attr, attribute, 250);
break;
}
}
END_STORE
ON_ERROR
general_on_error ();
END_ERROR;
}
else
{
// We say we support IB4, then we should skip roles.
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_role_name:
case att_role_owner_name:
eat_text(tdgbl);
break;
case att_role_description:
if (tdgbl->RESTORE_format >= 7)
eat_blob(tdgbl);
else
bad_attribute (scan_next_attr, attribute, 250);
break;
default:
// msg 250 SQL role
bad_attribute (scan_next_attr, attribute, 250);
break;
}
}
}
return true;
}
bool get_mapping(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ m a p p i n g
*
**************************************
*
* Functional description
* Restore mapping to users and roles
* Restricted version - only single
* mapping is accepted
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
TEXT temp[GDS_NAME_LEN];
SSHORT l;
Firebird::string role, os;
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11_1)
{
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
/* case att_map_role:
l = GET_TEXT(temp);
role.assign(temp, l);
break;
case att_map_os:
l = GET_TEXT(temp);
os.assign(temp, l);
break;
*/
case att_auto_map_role:
l = GET_TEXT(temp);
role.assign(temp, l);
break;
default:
// msg 299 name mapping
bad_attribute (scan_next_attr, attribute, 299);
break;
}
}
if (tdgbl->RESTORE_ods < DB_VERSION_DDL11_2)
{
return true; // silently skip attributes on old server
}
if (role != ADMIN_ROLE)
{
BURP_error(300, false);
return true;
}
if (tdgbl->firstMap)
{
tdgbl->firstMap = false;
BURP_verbose(301);
// msg 301, restoring names mapping
}
BURP_verbose(298, ADMIN_ROLE);
// msg 298, restoring map @1
Firebird::string sql;
sql.printf("%s ('%s', %d) %s",
"UPDATE OR INSERT INTO RDB$ROLES(RDB$ROLE_NAME, RDB$SYSTEM_FLAG) VALUES",
ADMIN_ROLE, ROLE_FLAG_MAY_TRUST | ROLE_FLAG_DBO,
"MATCHING (RDB$ROLE_NAME)");
isc_dsql_execute_immediate(tdgbl->status, &tdgbl->db_handle, &tdgbl->tr_handle,
sql.length(), sql.c_str(), 1, NULL);
if (tdgbl->status[1])
{
general_on_error ();
}
}
return true;
}
bool is_ascii_name (const TEXT *name, const SSHORT len)
{
/**************************************
*
* i s _ a s c i i _ n a m e
*
**************************************
*
* Functional description
* Check if the input text is valid ASCII uppercased name
*
**************************************/
SSHORT i = 0;
while (i < len &&
( (name[i] >= 'A' && name[i] <= 'Z') ||
(name[i] >= '0' && name[i] <= '9') ||
name[i] == '_' || name[i] == '$' ) )
{
++i;
}
return (i == len);
}
bool get_security_class(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ s e c u r i t y _ c l a s s
*
**************************************
*
* Functional description
* Restore a security class record including access control list.
*
**************************************/
att_type attribute;
TEXT temp[GDS_NAME_LEN];
SSHORT l = 0;
scan_attr_t scan_next_attr;
bool is_valid_sec_class = false;
STORE (REQUEST_HANDLE tdgbl->handles_get_security_class_req_handle1)
X IN RDB$SECURITY_CLASSES
X.RDB$DESCRIPTION.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_class_security_class:
l = GET_TEXT(X.RDB$SECURITY_CLASS);
// Bug fix for bug_no 7299: There was a V3 bug that inserted
// garbage security class entry when doing GBAK. In order to
// restore the V3 gbak file with this bad security entry to
// V4 database. We should check if the security class is a
// valid ASCII name. If not, skip this entry by setting
// 'is_valid_sec_class' to false.
is_valid_sec_class = is_ascii_name(X.RDB$SECURITY_CLASS, l);
if (!is_valid_sec_class)
{
MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp));
BURP_print (234, temp);
// msg 234 Skipped bad security class entry: %s
break;
}
MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp));
BURP_verbose (125, temp);
// msg 125 restoring security class %s
break;
case att_class_acl:
get_misc_blob (tdgbl, X.RDB$ACL, false);
break;
case att_class_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_class_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
default:
bad_attribute (scan_next_attr, attribute, 131);
// msg 131 security class
break;
}
}
// If the security class is not valid ASCII name, don't store it to the
// database. Simply return from here and the entry is discarded.
if (!is_valid_sec_class)
{
return true;
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
void get_source_blob(BurpGlobals* tdgbl,
ISC_QUAD& blob_id,
bool glb_trans)
{
/**************************************
*
* g e t _ s o u r c e _ b l o b
*
**************************************
*
* Functional description
* Read source blob and query header attributes and copy data from
* input file to nice, shiney, new blob.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
SLONG length = get_numeric(tdgbl);
// Create new blob
UserBlob blob(status_vector);
isc_tr_handle local_trans;
if (glb_trans && tdgbl->global_trans)
local_trans = tdgbl->global_trans;
else
local_trans = gds_trans;
bool ok;
if (tdgbl->gbl_sw_fix_fss_metadata)
{
UCHAR bpb[15];
UCHAR* p = bpb;
*p++ = isc_bpb_version1;
*p++ = isc_bpb_source_type;
*p++ = 2;
put_short(p, isc_blob_text);
p += 2;
*p++ = isc_bpb_source_interp;
*p++ = 1;
*p++ = tdgbl->gbl_sw_fix_fss_metadata_id;
*p++ = isc_bpb_target_type;
*p++ = 2;
put_short(p, isc_blob_text);
p += 2;
*p++ = isc_bpb_target_interp;
*p++ = 1;
*p++ = CS_UNICODE_FSS;
ok = blob.create(DB, local_trans, blob_id, p - bpb, bpb);
}
else
ok = blob.create(DB, local_trans, blob_id);
if (!ok)
{
BURP_error_redirect (status_vector, 37);
// msg 37 isc_create_blob failed
}
// Allocate blob buffer if static buffer is too short
BlobBuffer static_buffer;
UCHAR* const buffer = static_buffer.getBuffer(length);
while (length > 0)
{
UCHAR* p = buffer;
while (*p++ = get(tdgbl))
length--;
--p;
--length; // -- or ++ ??? p is decremented, will have to test.
const USHORT seg_len = p - buffer;
if (!blob.putSegment(seg_len, buffer))
{
BURP_error_redirect (status_vector, 38);
// msg 38 isc_put_segment failed
}
}
if (!blob.close())
BURP_error_redirect (status_vector, 23);
// msg 23 isc_close_blob failed
}
USHORT get_text(BurpGlobals* tdgbl,
TEXT* text,
ULONG length)
{
/**************************************
*
* g e t _ t e x t
*
**************************************
*
* Functional description
* Move a text attribute to a string and fill.
*
**************************************/
const ULONG l = get(tdgbl);
if (length <= l)
BURP_error_redirect (NULL, 46);
// msg 46 string truncated
if (l)
text = (TEXT*) get_block(tdgbl, (UCHAR*) text, l);
*text = 0;
return (USHORT) l;
}
USHORT get_text2(BurpGlobals* tdgbl, TEXT* text, ULONG length)
{
/**************************************
*
* g e t _ t e x t 2
*
**************************************
*
* Functional description
* Move a text attribute to a string and fill, using USHORT as length indicator.
*
**************************************/
UCHAR lenstr[sizeof(USHORT)] = "";
get_block(tdgbl, lenstr, sizeof(lenstr));
const USHORT len = (USHORT) gds__vax_integer(lenstr, sizeof(lenstr));
if (length <= len)
{
BURP_error_redirect (NULL, 46);
// msg 46 string truncated
}
if (len)
text = (TEXT*) get_block(tdgbl, (UCHAR*) text, len);
*text = 0;
return len;
}
bool get_trigger_old (BurpGlobals* tdgbl,
burp_rel* relation)
{
/**************************************
*
* g e t _ t r i g g e r _ o l d
*
**************************************
*
* Functional description
* Get a trigger definition for a relation.
*
**************************************/
enum trig_t type;
att_type attribute;
TEXT name[GDS_NAME_LEN];
scan_attr_t scan_next_attr;
STORE (REQUEST_HANDLE tdgbl->handles_get_trigger_old_req_handle1)
X IN RDB$TRIGGERS
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$TRIGGER_BLR.NULL = TRUE;
X.RDB$TRIGGER_SOURCE.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_trig_type:
type = (enum trig_t) get_numeric(tdgbl);
break;
case att_trig_blr:
X.RDB$TRIGGER_BLR.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$TRIGGER_BLR, false);
break;
case att_trig_source:
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$TRIGGER_SOURCE, false);
break;
case att_trig_source2:
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$TRIGGER_SOURCE, false);
break;
default:
bad_attribute (scan_next_attr, attribute, 134);
// msg 134 trigger
break;
}
}
// fill in rest of attributes unique to new trigger format
TEXT* p = X.RDB$TRIGGER_NAME;
const TEXT* const end = p + 31;
const TEXT* q = relation->rel_name;
while (*q) {
*p++ = *q++;
}
switch (type)
{
case trig_pre_store:
X.RDB$TRIGGER_TYPE = TRIG_TYPE_PRE_STORE;
q = "$STORE";
break;
case trig_pre_modify:
X.RDB$TRIGGER_TYPE = TRIG_TYPE_PRE_MODIFY;
q = "$MODIFY";
break;
case trig_post_erase:
X.RDB$TRIGGER_TYPE = TRIG_TYPE_POST_ERASE;
q = "$ERASE";
break;
default:
bad_attribute (scan_next_attr, attribute, 136);
// msg 136 trigger type
return 0;
}
while (*q && p < end) {
*p++ = *q++;
}
*p = 0;
BURP_verbose (126, X.RDB$TRIGGER_NAME);
// msg 126 restoring trigger %s
strncpy (X.RDB$RELATION_NAME, relation->rel_name, GDS_NAME_LEN);
strcpy (name, X.RDB$TRIGGER_NAME);
X.RDB$TRIGGER_SEQUENCE = TRIGGER_SEQUENCE_DEFAULT;
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
if (tdgbl->gbl_sw_incremental)
{
COMMIT
// existing ON_ERROR continues past error, beck
ON_ERROR
BURP_print (94, name);
// msg 94 trigger %s is invalid
BURP_print_status (tdgbl->status_vector);
ROLLBACK;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
return true;
}
bool get_trigger(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ t r i g g e r
*
**************************************
*
* Functional description
* Get a trigger definition in rdb$triggers.
*
**************************************/
att_type attribute;
BASED_ON RDB$TRIGGERS.RDB$TRIGGER_NAME name;
scan_attr_t scan_next_attr;
isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11_1)
{
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_trigger_req_handle1)
X IN RDB$TRIGGERS
X.RDB$RELATION_NAME.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$TRIGGER_BLR.NULL = TRUE;
X.RDB$TRIGGER_SOURCE.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$FLAGS.NULL = TRUE;
X.RDB$VALID_BLR.NULL = TRUE;
X.RDB$DEBUG_INFO.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_trig_type:
X.RDB$TRIGGER_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_trig_flags:
X.RDB$FLAGS = (USHORT) get_numeric(tdgbl);
X.RDB$FLAGS.NULL = FALSE;
break;
case att_trig_blr:
X.RDB$TRIGGER_BLR.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$TRIGGER_BLR, true);
break;
case att_trig_source:
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true);
break;
case att_trig_source2:
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true);
break;
case att_trig_name:
GET_TEXT(X.RDB$TRIGGER_NAME);
strcpy (name, X.RDB$TRIGGER_NAME);
BURP_verbose (126, X.RDB$TRIGGER_NAME);
// msg 126 restoring trigger %s
break;
case att_trig_relation_name:
X.RDB$RELATION_NAME.NULL = FALSE;
GET_TEXT(X.RDB$RELATION_NAME);
break;
case att_trig_sequence:
X.RDB$TRIGGER_SEQUENCE = (USHORT) get_numeric(tdgbl);
break;
case att_trig_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
break;
case att_trig_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
break;
case att_trig_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl);
X.RDB$SYSTEM_FLAG.NULL = FALSE;
break;
case att_trig_inactive:
X.RDB$TRIGGER_INACTIVE = (USHORT) get_numeric(tdgbl);
break;
case att_trig_valid_blr:
if (tdgbl->RESTORE_format >= 8)
{
X.RDB$VALID_BLR.NULL = FALSE;
X.RDB$VALID_BLR = (USHORT) get_numeric(tdgbl);
}
else
bad_attribute (scan_next_attr, attribute, 134);
break;
case att_trig_debug_info:
if (tdgbl->RESTORE_format >= 8)
{
X.RDB$DEBUG_INFO.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DEBUG_INFO, true);
}
else
bad_attribute (scan_next_attr, attribute, 134);
break;
default:
bad_attribute (scan_next_attr, attribute, 134);
// msg 134 trigger
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
else
{
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_trigger_req_handle1)
X IN RDB$TRIGGERS
X.RDB$RELATION_NAME.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$TRIGGER_BLR.NULL = TRUE;
X.RDB$TRIGGER_SOURCE.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$FLAGS.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_trig_type:
X.RDB$TRIGGER_TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_trig_flags:
X.RDB$FLAGS = (USHORT) get_numeric(tdgbl);
X.RDB$FLAGS.NULL = FALSE;
break;
case att_trig_blr:
X.RDB$TRIGGER_BLR.NULL = FALSE;
get_blr_blob (tdgbl, X.RDB$TRIGGER_BLR, true);
break;
case att_trig_source:
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true);
break;
case att_trig_source2:
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true);
break;
case att_trig_name:
GET_TEXT(X.RDB$TRIGGER_NAME);
strcpy (name, X.RDB$TRIGGER_NAME);
BURP_verbose (126, X.RDB$TRIGGER_NAME);
// msg 126 restoring trigger %s
break;
case att_trig_relation_name:
X.RDB$RELATION_NAME.NULL = FALSE;
GET_TEXT(X.RDB$RELATION_NAME);
break;
case att_trig_sequence:
X.RDB$TRIGGER_SEQUENCE = (USHORT) get_numeric(tdgbl);
break;
case att_trig_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
break;
case att_trig_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
break;
case att_trig_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl);
X.RDB$SYSTEM_FLAG.NULL = FALSE;
break;
case att_trig_inactive:
X.RDB$TRIGGER_INACTIVE = (USHORT) get_numeric(tdgbl);
break;
default:
bad_attribute (scan_next_attr, attribute, 134);
// msg 134 trigger
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
if (tdgbl->gbl_sw_incremental)
{
COMMIT
// existing ON_ERROR continues past error, beck
ON_ERROR
BURP_print (94, name);
// msg 94 trigger %s is invalid
BURP_print_status (tdgbl->status_vector);
ROLLBACK;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
return true;
}
bool get_trigger_message(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ t r i g g e r _ m e s s a g e
*
**************************************
*
* Functional description
* Get a trigger message text.
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
BASED_ON RDB$TRIGGER_MESSAGES.RDB$TRIGGER_NAME name;
BASED_ON RDB$TRIGGER_MESSAGES.RDB$MESSAGE_NUMBER number = -1;
BASED_ON RDB$TRIGGER_MESSAGES.RDB$MESSAGE message;
bool sysflag = false;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_trigmsg_name:
GET_TEXT(name);
sysflag = false;
FOR (REQUEST_HANDLE tdgbl->handles_get_trigger_message_req_handle1)
FIRST 1 X IN RDB$TRIGGERS WITH
X.RDB$SYSTEM_FLAG EQ 1 AND X.RDB$TRIGGER_NAME EQ name
sysflag = true;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
BURP_verbose (127, name);
// msg 127 restoring trigger message for %s
break;
case att_trigmsg_number:
number = (USHORT) get_numeric(tdgbl);
break;
case att_trigmsg_text:
GET_TEXT(message);
break;
default:
bad_attribute (scan_next_attr, attribute, 135);
// msg 135 trigger message
break;
}
}
if (sysflag)
return true;
// Versions prior to FB2.0 don't support a field longer than varchar(78).
if (tdgbl->RESTORE_ods < DB_VERSION_DDL11)
message[78] = 0;
isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_trigger_message_req_handle2)
X IN RDB$TRIGGER_MESSAGES
strcpy (X.RDB$TRIGGER_NAME, name);
X.RDB$MESSAGE_NUMBER = number;
strcpy (X.RDB$MESSAGE, message);
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
if (tdgbl->gbl_sw_incremental)
{
COMMIT
// existing ON_ERROR continues past error, beck
ON_ERROR
BURP_print (94, name);
// msg 94 trigger %s is invalid
BURP_print_status (tdgbl->status_vector);
ROLLBACK;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
return true;
}
bool get_type(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ t y p e
*
**************************************
*
* Functional description
* Get a type definition in rdb$types.
*
**************************************/
att_type attribute;
ULONG l;
TEXT temp[GDS_NAME_LEN];
scan_attr_t scan_next_attr;
STORE (REQUEST_HANDLE tdgbl->handles_get_type_req_handle1)
X IN RDB$TYPES
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_type_name:
l = GET_TEXT(X.RDB$TYPE_NAME);
break;
case att_type_type:
X.RDB$TYPE = (USHORT) get_numeric(tdgbl);
break;
case att_type_field_name:
GET_TEXT(X.RDB$FIELD_NAME);
break;
case att_type_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_type_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
break;
case att_type_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric(tdgbl);
X.RDB$SYSTEM_FLAG.NULL = FALSE;
break;
default:
bad_attribute (scan_next_attr, attribute, 293);
// msg 293 type (in RDB$TYPES)
break;
}
}
MISC_terminate (X.RDB$TYPE_NAME, temp, l, sizeof(temp));
BURP_verbose (128, SafeArg() << temp << X.RDB$FIELD_NAME);
// msg 128 restoring type %s for field %s
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_user_privilege(BurpGlobals* tdgbl)
{
/**************************************
*
* g e t _ u s e r _ p r i v i l e g e
*
**************************************
*
* Functional description
* Get a user privilege.
* Get next interesting user privilege.
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
USHORT flags = 0;
BASED_ON RDB$USER_PRIVILEGES.RDB$USER user;
BASED_ON RDB$USER_PRIVILEGES.RDB$GRANTOR grantor;
BASED_ON RDB$USER_PRIVILEGES.RDB$PRIVILEGE privilege;
BASED_ON RDB$USER_PRIVILEGES.RDB$GRANT_OPTION grant_option = 0;
BASED_ON RDB$USER_PRIVILEGES.RDB$RELATION_NAME relation_name;
BASED_ON RDB$USER_PRIVILEGES.RDB$FIELD_NAME field_name;
BASED_ON RDB$USER_PRIVILEGES.RDB$USER_TYPE user_type;
BASED_ON RDB$USER_PRIVILEGES.RDB$OBJECT_TYPE object_type;
user_type = obj_user;
object_type = obj_relation;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_priv_user:
// default USER_TYPE to USER
flags |= USER_PRIV_USER;
GET_TEXT(user);
BURP_verbose (123, user);
// msg 123 restoring privilege for user %s
break;
case att_priv_grantor:
flags |= USER_PRIV_GRANTOR;
GET_TEXT(grantor);
break;
case att_priv_privilege:
flags |= USER_PRIV_PRIVILEGE;
GET_TEXT(privilege);
break;
case att_priv_grant_option:
flags |= USER_PRIV_GRANT_OPTION;
grant_option = (USHORT) get_numeric(tdgbl);
break;
case att_priv_object_name:
flags |= USER_PRIV_OBJECT_NAME;
// default OBJECT_TYPE to RELATION
GET_TEXT(relation_name);
break;
case att_priv_field_name:
flags |= USER_PRIV_FIELD_NAME;
GET_TEXT(field_name);
break;
case att_priv_user_type:
flags |= USER_PRIV_USER_TYPE;
user_type = (USHORT) get_numeric(tdgbl);
break;
case att_priv_obj_type:
flags |= USER_PRIV_OBJECT_TYPE;
object_type = (USHORT) get_numeric(tdgbl);
break;
default:
bad_attribute (scan_next_attr, attribute, 105);
// msg 105 privilege
break;
}
}
// Check if object exists
isc_tr_handle local_trans = 0;
bool exists = false;
switch (object_type)
{
case obj_procedure:
{
for (const burp_prc* proc = tdgbl->procedures; proc; proc = proc->prc_next)
if (!strcmp(proc->prc_name, relation_name))
{
exists = true;
local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
break;
}
}
break;
case obj_relation:
{
for (const burp_rel* rel = tdgbl->relations; rel; rel = rel->rel_next)
if (!strcmp(rel->rel_name, relation_name))
{
exists = true;
if (rel->rel_flags & REL_view)
local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
break;
}
}
break;
default:
exists = true;
break;
}
if (tdgbl->RESTORE_ods <= DB_VERSION_DDL8)
{
// Discard roles for IB4.
if (user_type == obj_sql_role || object_type == obj_sql_role)
exists = false;
}
if (exists)
{
if (!local_trans)
local_trans = gds_trans;
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_user_privilege_req_handle1)
X IN RDB$USER_PRIVILEGES
X.RDB$FIELD_NAME.NULL = TRUE;
X.RDB$OBJECT_TYPE.NULL = TRUE;
if (flags & USER_PRIV_USER)
strcpy (X.RDB$USER, user);
if (flags & USER_PRIV_GRANTOR)
strcpy (X.RDB$GRANTOR, grantor);
if (flags & USER_PRIV_PRIVILEGE)
strcpy (X.RDB$PRIVILEGE, privilege);
if (flags & USER_PRIV_GRANT_OPTION)
{
X.RDB$GRANT_OPTION = grant_option;
if (grant_option == 0)
X.RDB$GRANT_OPTION.NULL = TRUE;
else
X.RDB$GRANT_OPTION.NULL = FALSE;
}
if (flags & USER_PRIV_OBJECT_NAME)
strcpy (X.RDB$RELATION_NAME, relation_name);
if (flags & USER_PRIV_FIELD_NAME)
{
X.RDB$FIELD_NAME.NULL = FALSE;
strcpy (X.RDB$FIELD_NAME, field_name);
}
// USER_TYPE & OBJECT_TYPE are fields that did not exist before
// V4.0. So, we have to reconstruct them and initialize them to
// reasonable values. If they existed before then user_type and
// object_type contain the proper values. If they didn't exist
// then user_type and object_type contain the reasonable default
// values.
X.RDB$USER_TYPE.NULL = FALSE;
X.RDB$USER_TYPE = user_type;
X.RDB$OBJECT_TYPE.NULL = FALSE;
X.RDB$OBJECT_TYPE = object_type;
// If OBJECT_TYPE didn't exist before and we have a field level
// user privileges, then use obj_field instead.
// NOTE: Scanning the V4.0 code base, obj_field has never been
// used at all. The following code should be uncommented
// in case we ever introduce obj_field to the picture.
/***********************************************************
if ( !(flags & USER_PRIV_OBJECT_TYPE) )
{
if ( flags & USER_PRIV_FIELD_NAME )
{
X.RDB$OBJECT_TYPE = obj_field;
}
}
***********************************************************/
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
return true;
}
bool get_view(BurpGlobals* tdgbl,
burp_rel* relation)
{
/**************************************
*
* g e t _ v i e w
*
**************************************
*
* Functional description
* Store a record in RDB$VIEW_RELATIONS.
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
// If there is a global transaction then use it
isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_view_req_handle1)
X IN RDB$VIEW_RELATIONS
strcpy (X.RDB$VIEW_NAME, relation->rel_name);
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_view_relation_name:
GET_TEXT(X.RDB$RELATION_NAME);
break;
case att_view_context_name:
GET_TEXT(X.RDB$CONTEXT_NAME);
if (tdgbl->RESTORE_ods < DB_VERSION_DDL11_2) {
X.RDB$CONTEXT_NAME[31] = 0;
}
break;
case att_view_context_id:
X.RDB$VIEW_CONTEXT = (USHORT) get_numeric(tdgbl);
break;
default:
bad_attribute (scan_next_attr, attribute, 140);
// msg 140 view
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
void ignore_array(BurpGlobals* tdgbl,
burp_rel* relation)
{
/**************************************
*
* i g n o r e _ a r r a y
*
**************************************
*
* Functional description
* Ignore data from input file, like a
* dummy get_array().
*
**************************************/
burp_fld* field = NULL;
att_type attribute;
SLONG* range;
const SLONG* end_ranges;
USHORT field_number;
scan_attr_t scan_next_attr;
// Pick up attributes
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_blob_data)
{
switch (attribute)
{
case att_blob_field_number:
field_number = (USHORT) get_numeric(tdgbl);
for (field = relation->rel_fields; field; field = field->fld_next) {
if (field->fld_number == field_number)
break;
}
if (!field)
BURP_error_redirect (NULL, 36);
// msg 36 Can't find field for blob
break;
case att_array_dimensions:
field->fld_dimensions = (SSHORT) get_numeric(tdgbl);
end_ranges = field->fld_ranges + 2 * field->fld_dimensions;
for (range = field->fld_ranges; range < end_ranges; range += 2)
{
if (get_attribute(&attribute, tdgbl) != att_array_range_low)
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
else
range[0] = get_numeric(tdgbl);
if (get_attribute(&attribute, tdgbl) != att_array_range_high)
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
else
range[1] = get_numeric(tdgbl);
}
break;
default:
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
break;
}
}
SLONG length = get(tdgbl);
length |= get(tdgbl) << 8;
length |= get(tdgbl) << 16;
length |= get(tdgbl) << 24;
SLONG lcount = 0;
if (tdgbl->gbl_sw_transportable)
{
if (get_attribute(&attribute, tdgbl) != att_xdr_array)
BURP_error_redirect (NULL, 55);
// msg 55 Expected XDR record length
else
{
lcount = get(tdgbl);
lcount |= get(tdgbl) << 8;
lcount |= get(tdgbl) << 16;
lcount |= get(tdgbl) << 24;
}
}
else
{
lcount = length;
}
if (lcount)
get_skip(tdgbl, lcount);
}
void ignore_blob(BurpGlobals* tdgbl)
{
/**************************************
*
* i g n o r e _ b l o b
*
**************************************
*
* Functional description
* Skip over blob data records.
*
**************************************/
att_type attribute;
scan_attr_t scan_next_attr;
// Pick up attributes
SLONG segments = 0;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_blob_data)
{
switch (attribute)
{
case att_blob_field_number:
get_numeric(tdgbl);
break;
case att_blob_max_segment:
get_numeric(tdgbl);
break;
case att_blob_number_segments:
segments = get_numeric(tdgbl);
break;
case att_blob_type:
get_numeric(tdgbl);
break;
default:
bad_attribute (scan_next_attr, attribute, 64);
// msg 64 blob
break;
}
}
// Eat up blob segments
while (--segments >= 0)
{
USHORT length = get(tdgbl);
length |= get(tdgbl) << 8;
if (length)
get_skip(tdgbl, length);
}
}
rec_type ignore_data(BurpGlobals* tdgbl,
burp_rel* relation)
{
/**************************************
*
* i g n o r e _ d a t a
*
**************************************
*
* Functional description
* Ignore data records for a relation.
*
**************************************/
ULONG records = 0;
rec_type record;
while (true)
{
if (get(tdgbl) != att_data_length)
BURP_error_redirect (NULL, 39);
// msg 39 expected record length
USHORT l = (USHORT) get_numeric(tdgbl);
if (tdgbl->gbl_sw_transportable)
{
if (get(tdgbl) != att_xdr_length)
BURP_error_redirect (NULL, 55);
// msg 55 Expected XDR record length
else
l = (USHORT) get_numeric(tdgbl);
}
if (get(tdgbl) != att_data_data)
BURP_error_redirect (NULL, 41);
// msg 41 expected data attribute
if (l) {
if (tdgbl->gbl_sw_compress)
{
UCHAR* buffer = (UCHAR*) BURP_alloc (l);
decompress (tdgbl, buffer, l);
BURP_free (buffer);
}
else
get_skip(tdgbl, l);
}
++records;
while (get_record(&record, tdgbl))
{
if (record == rec_blob)
ignore_blob(tdgbl);
else if (record == rec_array)
ignore_array (tdgbl, relation);
else
break;
}
if (record != rec_data)
break;
}
BURP_verbose (106, SafeArg() << records);
// msg 106 %ld records ignored
return record;
}
void realign(BurpGlobals* tdgbl,
UCHAR* buffer,
const burp_rel* relation)
{
/**************************************
*
* r e a l i g n
*
**************************************
*
* Functional description
* Miserable input record is misaligned.
* Shuffle fields around. N.B. this one
* only works if the old buffer is longer
* than the new.
*
**************************************/
for (const burp_fld* field = relation->rel_fields; field; field = field->fld_next)
{
if (field->fld_flags & FLD_computed)
continue;
UCHAR* p = buffer + field->fld_offset;
const UCHAR* q = buffer + field->fld_old_offset;
USHORT l = field->fld_length;
// CVC: This code assumes fld_offset < fld_old_offset,
// why not use memmove() instead?
// Beware of overlaps here - don't use memcpy
while (l--) {
*p++ = *q++;
}
if (field->fld_type == blr_varying)
{
*p++ = *q++;
*p++ = *q++;
}
}
// If this is format version 2 or greater, build fields for null flags
if (tdgbl->RESTORE_format >= 2)
{
for (const burp_fld* field = relation->rel_fields; field; field = field->fld_next)
{
if (field->fld_flags & FLD_computed)
continue;
UCHAR* p = buffer + FB_ALIGN(p - buffer, sizeof(SSHORT));
const UCHAR* q = buffer + FB_ALIGN(q - buffer, sizeof(SSHORT));
*p++ = *q++;
*p++ = *q++;
}
}
}
#ifdef sparc
USHORT recompute_length(BurpGlobals* tdgbl,
burp_rel* relation)
{
/**************************************
*
* r e c o m p u t e _ l e n g t h
*
**************************************
*
* Functional description
* Recompute length of a record using an old
* alignment if there is one. At the moment,
* only SPARC has one.
*
**************************************/
ULONG offset = 0; // there was garbage, possibly nobody uses sparc define?
const SSHORT* alignments = old_sparcs;
for (burp_fld* field = relation->rel_fields; field; field = field->fld_next)
{
if (field->fld_flags & FLD_computed)
continue;
ULONG length = field->fld_length;
// ULONG alignment = 4; useless, see assignment below
// arrays are of various fld_types but are really blobs
ULONG dtype = field->fld_type;
if (field->fld_flags & FLD_array)
{
dtype = blr_blob;
length = 8;
}
const ULONG alignment = alignments[gds_cvt_blr_dtype[field->fld_type]];
if (dtype == blr_varying)
length += sizeof(USHORT);
if (alignment)
offset = FB_ALIGN(offset, alignment);
field->fld_old_offset = offset;
offset += length;
}
// If this is format version 2, build fields for null flags
if (tdgbl->RESTORE_format >= 2)
{
for (const burp_fld* field = relation->rel_fields; field; field = field->fld_next)
{
if (field->fld_flags & FLD_computed)
continue;
offset = FB_ALIGN(offset, sizeof(SSHORT));
offset += sizeof(SSHORT);
}
}
return offset;
}
#endif
bool restore(BurpGlobals* tdgbl,
const TEXT* file_name,
const TEXT* database_name)
{
/**************************************
*
* r e s t o r e
*
**************************************
*
* Functional description
* Perform the body of restore.
*
**************************************/
// Read burp record first
MVOL_init_read (file_name, &tdgbl->RESTORE_format, &tdgbl->io_cnt, &tdgbl->io_ptr);
if (tdgbl->gbl_sw_transportable)
BURP_verbose (133);
// msg 133 transportable backup -- data in XDR format
if (tdgbl->gbl_sw_compress)
BURP_verbose (61);
// msg 61 backup file is compressed
// restore only from those backup files created by current or previous GBAK
if (tdgbl->RESTORE_format < 1 || tdgbl->RESTORE_format > ATT_BACKUP_FORMAT)
{
BURP_error(44, true, SafeArg() << tdgbl->RESTORE_format);
// msg 44 Expected backup version 1..8. Found %ld
}
create_database(tdgbl, database_name);
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
// For V4.0, start a read commited transaction. This will be used
// to create blobs for global fields and update the record in the
// RDB$FIELDS table.
EXEC SQL SET TRANSACTION NAME tdgbl->global_trans ISOLATION LEVEL READ COMMITTED;
check_db_version(tdgbl);
if (tdgbl->RESTORE_ods < DB_VERSION_OLDEST_SUPPORTED)
{
BURP_error(51, true, SafeArg() << tdgbl->RESTORE_ods);
// msg 51 database format %ld is too old to restore to
}
BURP_verbose (129);
// msg 129 started transaction
att_type attribute;
isc_req_handle req_handle2 = 0, req_handle3 = 0;
while (get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_database_security_class:
// Instead of updating the security class in RDB$DATABASE,
// just store the value in tdgbl. It will be updated at
// the very end to prevent security class validation
// failures during change table ownership operation
GET_TEXT(tdgbl->database_security_class);
break;
case att_database_description:
case att_database_description2:
FOR (REQUEST_HANDLE req_handle2)
X IN RDB$DATABASE
MODIFY X USING
if (attribute == att_database_description2)
get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
else
get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
END_MODIFY;
ON_ERROR
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
break;
case att_database_dfl_charset:
FOR (REQUEST_HANDLE req_handle3)
X IN RDB$DATABASE
MODIFY X USING
GET_TEXT(X.RDB$CHARACTER_SET_NAME);
END_MODIFY;
ON_ERROR
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
break;
default:
{
SSHORT l = get(tdgbl);
if (l)
get_skip(tdgbl, l);
break;
}
}
}
MISC_release_request_silent(req_handle2);
MISC_release_request_silent(req_handle3);
if (tdgbl->gbl_sw_fix_fss_data)
{
bool found = false;
Firebird::string name = tdgbl->gbl_sw_fix_fss_data;
name.upper();
req_handle3 = 0;
FOR (REQUEST_HANDLE req_handle3)
X IN RDB$CHARACTER_SETS
WITH X.RDB$CHARACTER_SET_NAME EQ name.c_str()
tdgbl->gbl_sw_fix_fss_data_id = X.RDB$CHARACTER_SET_ID;
found = true;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle3);
if (!found)
BURP_error(305, true, SafeArg() << tdgbl->gbl_sw_fix_fss_data);
}
if (tdgbl->gbl_sw_fix_fss_metadata)
{
bool found = false;
Firebird::string name = tdgbl->gbl_sw_fix_fss_metadata;
name.upper();
req_handle3 = 0;
FOR (REQUEST_HANDLE req_handle3)
X IN RDB$CHARACTER_SETS
WITH X.RDB$CHARACTER_SET_NAME EQ name.c_str()
tdgbl->gbl_sw_fix_fss_metadata_id = X.RDB$CHARACTER_SET_ID;
found = true;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle3);
if (!found)
BURP_error(305, true, SafeArg() << tdgbl->gbl_sw_fix_fss_metadata);
}
// If this should be a multi-file database, add the files
if (tdgbl->gbl_sw_files && tdgbl->gbl_sw_files->fil_next)
add_files(tdgbl, database_name);
// Get global fields and relations
bool flag_norel = true; // To fix bug 10098
bool flag = false;
rec_type record;
while (get_record(&record, tdgbl) != rec_end)
{
switch (record)
{
case rec_charset:
if (!get_character_set(tdgbl))
return false;
flag = true;
break;
case rec_collation:
if (!get_collation(tdgbl))
return false;
flag = true;
break;
case rec_chk_constraint:
if (!get_chk_constraint(tdgbl))
return false;
flag = true;
break;
case rec_global_field:
if (!get_global_field(tdgbl))
return false;
flag = true;
break;
case rec_field_dimensions:
if (!get_field_dimensions(tdgbl))
return false;
flag = true;
break;
case rec_relation:
if (!get_relation(tdgbl))
return false;
flag = true;
flag_norel = false;
break;
case rec_ref_constraint:
if (!get_ref_constraint(tdgbl))
return false;
flag = true;
break;
case rec_rel_constraint:
if (!get_rel_constraint(tdgbl))
return false;
flag = true;
break;
case rec_function:
if (!get_function(tdgbl))
return false;
flag = true;
break;
case rec_procedure:
if (!get_procedure(tdgbl))
return false;
flag = true;
break;
case rec_exception:
if (!get_exception(tdgbl))
return false;
flag = true;
break;
case rec_system_type: // rdb$types
if (!get_type(tdgbl))
return false;
flag = true;
break;
case rec_filter: // rdb$filters
if (!get_filter(tdgbl))
return false;
flag = true;
break;
case rec_generator:
if (!get_generator(tdgbl))
return false;
flag = true;
break;
case rec_relation_data:
if (flag)
{
BURP_verbose (68);
// msg 68 committing meta data
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
flag = false;
}
if (!get_relation_data(tdgbl))
return false;
break;
case rec_trigger: // new trigger type
if (!get_trigger(tdgbl))
return false;
flag = true;
break;
case rec_trigger_message:
if (!get_trigger_message(tdgbl))
return false;
flag = true;
break;
case rec_user_privilege:
if (!get_user_privilege(tdgbl))
return false;
flag = true;
break;
case rec_security_class:
if (!get_security_class(tdgbl))
return false;
flag = true;
break;
case rec_files:
if (!get_files(tdgbl))
return false;
flag = true;
break;
case rec_sql_roles:
if (!get_sql_roles(tdgbl))
return false;
flag = true;
break;
case rec_mapping:
if (!get_mapping(tdgbl))
return false;
flag = true;
break;
default:
BURP_error(43, true, SafeArg() << record);
// msg 43 don't recognize record type %ld
break;
}
}
if (tdgbl->defaultCollations.getCount() > 0)
{
isc_req_handle req_handle4 = 0;
FOR (REQUEST_HANDLE req_handle4)
CS IN RDB$CHARACTER_SETS
for (size_t i = 0; i < tdgbl->defaultCollations.getCount(); ++i)
{
if (tdgbl->defaultCollations[i].first == CS.RDB$CHARACTER_SET_NAME)
{
MODIFY CS;
CS.RDB$DEFAULT_COLLATE_NAME.NULL = FALSE;
strcpy(CS.RDB$DEFAULT_COLLATE_NAME,
tdgbl->defaultCollations[i].second.c_str());
END_MODIFY;
ON_ERROR
general_on_error ();
END_ERROR;
}
}
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle4);
}
// This piece of code is to fix bug 10098: restore of database with
// only domains and no relations aborts with the message ERROR: deadlock
// This is because insertion of domains into RDB$FIELDS is happening in
// the default transaction, whereas updation of RDB$FIELDS to add
// constraints to the domains is done in tdgbl->global_trans. In case of
// no relations, no COMMIT of default transaction occurs till this point
// because of which rows in RDB$FIELDS for domains are still locked by
// default transaction. The below code COMMITs the default transaction
// in that particular situation
if (flag_norel)
{
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
// put validation clauses for global fields
update_global_field(tdgbl);
// Purge shadow metadata if necessary
if (tdgbl->gbl_sw_kill)
{
isc_req_handle req_handle4 = 0;
FOR (REQUEST_HANDLE req_handle4)
FIL IN RDB$FILES WITH FIL.RDB$SHADOW_NUMBER NOT MISSING
AND FIL.RDB$SHADOW_NUMBER NE 0
ERASE FIL;
ON_ERROR
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle4);
}
// update statistics for system indices
isc_req_handle req_handle5 = 0;
FOR (REQUEST_HANDLE req_handle5)
IND IN RDB$INDICES WITH IND.RDB$SYSTEM_FLAG EQ 1
MODIFY IND
IND.RDB$STATISTICS.NULL = FALSE;
IND.RDB$STATISTICS = -1;
END_MODIFY
ON_ERROR
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle5);
return true;
}
void restore_security_class(BurpGlobals* tdgbl,
const TEXT* owner_nm,
const TEXT* sec_class_nm)
{
/**************************************
*
* r e s t o r e _ s e c u r i t y _ c l a s s
*
**************************************
*
* Functional description
* restore the ownership of the relation in the ACL list
*
**************************************/
isc_req_handle req_handle2 = 0;
//isc_tr_handle local_trans = gds_trans;
FOR (REQUEST_HANDLE req_handle2)
X IN RDB$SECURITY_CLASSES WITH X.RDB$SECURITY_CLASS EQ sec_class_nm
ISC_QUAD new_blob_id;
new_blob_id.gds_quad_high = 0;
new_blob_id.gds_quad_low = 0;
get_acl(tdgbl, owner_nm, &X.RDB$ACL, &new_blob_id);
MODIFY X;
memcpy(&X.RDB$ACL, &new_blob_id, sizeof(ISC_QUAD));
END_MODIFY;
ON_ERROR
MISC_release_request_silent(req_handle2);
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
MISC_release_request_silent(req_handle2);
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle2);
}
USHORT get_view_base_relation_count(BurpGlobals* tdgbl,
const TEXT* current_view_name,
USHORT depth)
{
/**************************************
*
* g e t _ v i e w _ b a s e _ r e l a t i o n _ c o u n t
*
**************************************
*
* Functional description
* Return the number of base relations
* (tables) from a view. When a view is
* referenced in the view this function
* is called recursively.
*
**************************************/
depth++;
if (depth > MAX_UPDATE_DBKEY_RECURSION_DEPTH) {
return 0;
}
isc_req_handle req_handle1 = 0;
USHORT result = 0;
FOR (REQUEST_HANDLE req_handle1)
V IN RDB$VIEW_RELATIONS
CROSS R IN RDB$RELATIONS
WITH
V.RDB$VIEW_NAME EQ current_view_name AND
R.RDB$RELATION_NAME EQ V.RDB$RELATION_NAME
if (R.RDB$VIEW_BLR.NULL) {
// This relation is a table, so increment count
result++;
}
else {
// Call recursive for VIEWS that are referenced in VIEWS
result += get_view_base_relation_count(tdgbl, V.RDB$RELATION_NAME, depth);
}
END_FOR;
ON_ERROR
MISC_release_request_silent(req_handle1);
general_on_error();
END_ERROR;
MISC_release_request_silent(req_handle1);
return result;
}
void store_blr_gen_id(BurpGlobals* tdgbl,
const TEXT* gen_name, // TEXT GDS_NAME[GDS_NAME_LEN]
SINT64 value,
const ISC_QUAD* gen_desc)
{
/**************************************
*
* s t o r e _ b l r _ g e n _ i d
*
**************************************
*
* Functional description
* Store the blr_gen_id for the relation.
*
**************************************/
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL11)
{
STORE (REQUEST_HANDLE tdgbl->handles_store_blr_gen_id_req_handle1)
X IN RDB$GENERATORS
strcpy (X.RDB$GENERATOR_NAME, gen_name);
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
if (gen_desc)
{
X.RDB$DESCRIPTION = *gen_desc;
X.RDB$DESCRIPTION.NULL = FALSE;
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
else
{
STORE (REQUEST_HANDLE tdgbl->handles_store_blr_gen_id_req_handle1)
X IN RDB$GENERATORS
strcpy (X.RDB$GENERATOR_NAME, gen_name);
X.RDB$SYSTEM_FLAG = 0;
X.RDB$SYSTEM_FLAG.NULL = FALSE;
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
if (!value)
{
BURP_verbose (185, SafeArg() << gen_name << 0);
// msg 185 restoring generator %s value: %ld
return;
}
FB_API_HANDLE gen_id_reqh = 0;
UCHAR blr_buffer[100]; // enough to fit blr
UCHAR* blr = blr_buffer;
// build the blr with the right relation name
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10)
{
add_byte(blr, blr_version5);
}
else
{
add_byte(blr, blr_version4);
}
add_byte(blr, blr_begin);
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10)
{
add_byte(blr, blr_dcl_variable);
add_word(blr, 0);
add_byte(blr, blr_int64);
add_byte(blr, 0);
}
else
{
add_byte(blr, blr_dcl_variable);
add_word(blr, 0);
add_byte(blr, blr_long);
add_byte(blr, 0);
}
add_byte(blr, blr_begin);
add_byte(blr, blr_assignment);
add_byte(blr, blr_gen_id);
add_string(blr, gen_name);
if (tdgbl->RESTORE_ods >= DB_VERSION_DDL10)
{
add_byte(blr, blr_literal);
add_byte(blr, blr_int64);
add_byte(blr, 0);
add_int64(blr, value);
}
else
{
add_byte(blr, blr_literal);
add_byte(blr, blr_long);
add_byte(blr, 0);
add_long(blr, (SLONG)value);
}
add_byte(blr, blr_variable);
add_word(blr, 0);
add_byte(blr, blr_end);
add_byte(blr, blr_end);
add_byte(blr, blr_eoc);
const USHORT blr_length = blr - blr_buffer;
fb_assert(blr_length <= sizeof(blr_buffer));
ISC_STATUS_ARRAY status_vector;
if (isc_compile_request (status_vector, &DB, &gen_id_reqh,
blr_length, (const SCHAR*) blr_buffer))
{
fb_print_blr(blr_buffer, blr_length, 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))
{
fb_print_blr(blr_buffer, blr_length, 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