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