8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 22:43:04 +01:00
firebird-mirror/src/burp/restore.epp
robocop e6187cecd2 Cleanup
Some bug fixes
Style
2004-03-07 07:58:55 +00:00

6996 lines
169 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.
*/
/*
$Id: restore.epp,v 1.71 2004-03-07 07:58:08 robocop Exp $
*/
#include "firebird.h"
#include "../jrd/ib_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 "../jrd/thd_proto.h"
#include "../burp/burp_proto.h"
#include "../burp/canon_proto.h"
#include "../burp/misc_proto.h"
#include "../burp/mvol_proto.h"
#include "../burp/resto_proto.h"
#include "../jrd/gdsassert.h"
#include "../remote/protocol.h"
#ifdef DEBUG
#include "../gpre/prett_proto.h"
#endif
// For service APIs the follow DB handle is a value stored
// in thread data. This is also done for other statics generated by
// GPRE. This is to avoid multiple threading problems with module
// level statics.
DATABASE DB = STATIC FILENAME "yachts.lnk";
#define DB tdgbl->db_handle
#define gds_trans tdgbl->tr_handle
#define isc_status tdgbl->status
namespace // unnamed, private
{
const int DB_VERSION_DDL4 = 4; // ods4 db
const int DB_VERSION_DDL5 = 5; // ods5 db
const int DB_VERSION_DDL8 = 8; // ods8 db
const int DB_VERSION_CURRENT = DB_VERSION_DDL8; // v4.0 is ods8
const char* FOREIGN_KEY = "FOREIGN KEY";
const int DEFERRED_ACTIVE = 3; /* RDB$INDEX_INACTIVE setting for Foreign Keys
* This setting is used temporarily while
* restoring a database. This was required
* in order to differentiate a partial
* "inactive" state of SOME indices from
* "inactive" state of ALL indices (gbak -i)
* -bsriram, 11-May-1999 BUG: 10016
*/
const int RESTORE_VERBOSE_INTERVAL = 10000;
const int cvtbl_len = 28;
enum scan_attr_t {
NO_SKIP = 0, // Not in skipping and scanning mode
BEFORE_SKIP = 1, // After skipping, before scanning next byte for valid attribute
AFTER_SKIP = 2 // After skipping and after scanning next byte for valid attribute
};
void add_files(const UCHAR*);
void bad_attribute(scan_attr_t, ATT_TYPE, USHORT);
USHORT check_db_version();
void create_database (const TEXT*);
void decompress(UCHAR*, USHORT);
void eat_blob();
burp_rel* find_relation (const TEXT *);
// CVC: when do these functions return false indeed???
// get_acl and get_index are the only exceptions but ironically their
// returned value is not checked by the caller!
bool get_acl (const TEXT*, ISC_QUAD*, ISC_QUAD*);
void get_array (burp_rel*, UCHAR*);
void get_blob (const burp_fld*, UCHAR*);
void get_blr_blob (ISC_QUAD*, bool);
bool get_character_set();
bool get_chk_constraint();
bool get_collation();
rec_type get_data (burp_rel*);
bool get_exception();
burp_fld* get_field (burp_rel*);
bool get_field_dimensions();
bool get_files();
bool get_filter();
bool get_function();
void get_function_arg ();
bool get_generator();
bool get_global_field();
bool get_index (const burp_rel*);
void get_misc_blob (ISC_QUAD*, USHORT, bool);
SLONG get_numeric();
SINT64 get_int64();
bool get_procedure();
bool get_procedure_prm (GDS_NAME );
bool get_ref_constraint();
bool get_rel_constraint();
bool get_relation();
bool get_relation_data();
bool get_sql_roles();
bool get_security_class();
void get_source_blob (ISC_QUAD*, bool);
USHORT get_text (TEXT*, ULONG);
bool get_trigger();
bool get_trigger_message();
bool get_trigger_old (burp_rel*);
bool get_type();
bool get_user_privilege();
bool get_view (burp_rel*);
void ignore_array (burp_rel*);
void ignore_blob();
rec_type ignore_data (burp_rel*);
void realign(UCHAR*, const burp_rel*);
USHORT recompute_length (burp_rel*);
bool restore (const TEXT*, const TEXT*);
void restore_security_class (const TEXT*, const TEXT*);
USHORT get_view_base_relation_count(const TEXT*, USHORT);
void store_blr_gen_id (const TEXT*, SINT64);
void stuff_string(UCHAR**, const TEXT*);
void update_global_field();
void update_view_dbkey_lengths();
void general_on_error();
#ifdef DEBUG
UCHAR debug_on = 0; // able to turn this on in the debugger
#endif
bool flag_on_line = true; /* indicates whether we will bring
the database on-line :
true - we will
false - we will not */
#ifdef sparc
const SSHORT old_sparcs[] =
{0, 0, 0, 2, 0, 0, 0, 0, 2, 4, 4, 4, 8, 8, 0, 0, 8, 8, 8};
#endif
//MVOL_read returns int
static inline int get(tgbl* tdgbl)
{
if (--(tdgbl->io_cnt) >= 0)
return *(tdgbl->io_ptr)++;
else // => superfluous here
return MVOL_read(&tdgbl->io_cnt, &tdgbl->io_ptr);
}
static inline ATT_TYPE get_attribute(ATT_TYPE *att, TGBL tdgbl)
{
*att = (ATT_TYPE) get(tdgbl);
return *att;
}
static inline rec_type get_record(rec_type *rec, TGBL tdgbl)
{
*rec = (rec_type) get(tdgbl);
return *rec;
}
#define GET_TEXT(text) get_text((text), sizeof(text))
static inline void get_skip(tgbl* tdgbl, ULONG n)
{
MVOL_skip_block(tdgbl, n);
}
static inline UCHAR* get_block(tgbl* tdgbl, UCHAR* p, ULONG n)
{
return MVOL_read_block(tdgbl, p, n);
}
// When skipping started, scan_next_attr will be changed from NO_SKIP
// to BEFORE_SKIP. When scanning for next valid attribute after skipping,
// it will flip-flop between BEFORE_SKIP and AFTER_SKIP. When next valid
// attribute is found, it will be changed back to NO_SKIP by 'skip_scan'
static inline void skip_init(scan_attr_t* scan_next_attr)
{
*scan_next_attr = NO_SKIP;
}
static inline 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 = NULL, req_handle2 = NULL, req_handle3 = NULL;
isc_req_handle req_handle4 = NULL;
BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name;
TGBL tdgbl = GET_THREAD_DATA;
tdgbl->io_ptr = NULL;
tdgbl->io_cnt = 0;
tdgbl->relations = NULL;
tdgbl->procedures = NULL;
tdgbl->RESTORE_format = 0;
tdgbl->global_trans = 0;
tdgbl->gbl_sw_transportable = tdgbl->gbl_sw_compress = false;
if (!restore (file_name, database_name))
return FINI_ERROR;
BURP_verbose (76, NULL, NULL, NULL, NULL, NULL);
// msg 76 creating indexes
COMMIT;
ON_ERROR
/* Fix for bug_no 8055:
don't throw away the database just because an index
could not be made */
long error_code;
while (error_code = tdgbl->status_vector[1])
{
switch (error_code)
{
case 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, NULL, NULL, NULL, NULL);
MODIFY IDX USING IDX.RDB$INDEX_INACTIVE = TRUE;
}
BURP_print(240, index_name, NULL, NULL, NULL, NULL);
// msg 240 Index \"%s\" failed to activate because:
if ( error_code == isc_no_dup )
{
BURP_print(241, NULL, NULL, NULL, NULL, NULL);
// msg 241 The unique index has duplicate values or NULLs
BURP_print(242, NULL, NULL, NULL, NULL, NULL);
// msg 242 Delete or Update duplicate values or NULLs, and activate index with
}
else
{
BURP_print(244, NULL, NULL, NULL, NULL, NULL);
// msg 244 Not enough disk space to create the sort file for an index
BURP_print(245, NULL, NULL, NULL, NULL, NULL);
// msg 245 Set the TMP environment variable to a directory on a filesystem that does have enough space, and activate index with
}
BURP_print(243, index_name, NULL, NULL, NULL, NULL);
/* msg 243 ALTER INDEX \"%s\" ACTIVE; */
END_MODIFY;
END_FOR;
// don't bring the database on-line
flag_on_line = false;
break;
default:
general_on_error ();
break;
}
COMMIT
ON_ERROR
continue;
END_ERROR
}
END_ERROR;
// Activate the indices for foreign keys and do another commit
if (!(tdgbl->gbl_sw_deactivate_indexes))
{
// Block added to verbose index creation by Toni Martir
if (tdgbl->gbl_sw_verbose)
{
EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
// Activate first indexes that are not foreign keys
FOR (REQUEST_HANDLE req_handle1) IDS IN RDB$INDICES WITH
IDS.RDB$INDEX_INACTIVE EQ DEFERRED_ACTIVE AND
IDS.RDB$FOREIGN_KEY MISSING
MODIFY IDS USING IDS.RDB$INDEX_INACTIVE = FALSE;
END_MODIFY;
ON_ERROR
general_on_error();
END_ERROR;
SAVE
// existing ON_ERROR continues past error, beck
ON_ERROR
BURP_print (173, IDS.RDB$INDEX_NAME, NULL, NULL, NULL, NULL);
BURP_print_status(isc_status);
MODIFY IDS USING
IDS.RDB$INDEX_INACTIVE = TRUE;
END_MODIFY;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
BURP_verbose(122, IDS.RDB$INDEX_NAME, NULL, NULL, NULL, NULL);
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle1);
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
}
EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
/* Only activate Foreign keys that have been marked for deferred
* activation.
* -bsriram, 11-May-1999 BUG: 10016
*/
FOR (REQUEST_HANDLE req_handle1)
CNST IN RDB$RELATION_CONSTRAINTS
CROSS IDS IN RDB$INDICES WITH
CNST.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND
CNST.RDB$INDEX_NAME EQ IDS.RDB$INDEX_NAME AND
IDS.RDB$INDEX_INACTIVE EQ DEFERRED_ACTIVE
MODIFY IDS USING
IDS.RDB$INDEX_INACTIVE = FALSE;
END_MODIFY;
ON_ERROR
general_on_error ();
END_ERROR;
SAVE
// existing ON_ERROR continues past error, beck
ON_ERROR
BURP_print (173, IDS.RDB$INDEX_NAME, NULL, NULL, NULL, NULL);
BURP_print_status(isc_status);
MODIFY IDS USING
IDS.RDB$INDEX_INACTIVE = TRUE;
END_MODIFY;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
BURP_verbose(122, IDS.RDB$INDEX_NAME, NULL, NULL, NULL, NULL);
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle1);
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
}
if (tdgbl->global_trans)
{
BURP_verbose (68, NULL, NULL, NULL, NULL, NULL);
// msg 68 committing meta data
EXEC SQL COMMIT TRANSACTION tdgbl->global_trans;
if (gds_status[1])
general_on_error ();
// Check to see if there is a warning
if (gds_status[0] == 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();
/*
** Change ownership of any procedures necessary
*/
for (burp_prc* procedure = tdgbl->procedures; procedure;
procedure = procedure->prc_next)
{
if (procedure->prc_owner[0])
{
FOR (REQUEST_HANDLE req_handle4)
X IN RDB$PROCEDURES WITH X.RDB$PROCEDURE_NAME EQ procedure->prc_name
MODIFY X
strcpy (X.RDB$OWNER_NAME, procedure->prc_owner);
END_MODIFY;
ON_ERROR
MISC_release_request_silent(req_handle4);
general_on_error ();
END_ERROR;
restore_security_class (procedure->prc_owner, X.RDB$SECURITY_CLASS);
END_FOR;
ON_ERROR
MISC_release_request_silent(req_handle4);
general_on_error ();
END_ERROR;
}
}
MISC_release_request_silent(req_handle4);
// Change ownership of any relations necessary
for (burp_rel* relation = tdgbl->relations; relation; relation = relation->rel_next)
{
if (relation->rel_owner[0])
{
FOR (REQUEST_HANDLE req_handle2)
X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ relation->rel_name
MODIFY X
strcpy (X.RDB$OWNER_NAME, relation->rel_owner);
END_MODIFY;
ON_ERROR
MISC_release_request_silent(req_handle2);
general_on_error ();
END_ERROR;
restore_security_class (relation->rel_owner, X.RDB$SECURITY_CLASS);
restore_security_class (relation->rel_owner, X.RDB$DEFAULT_CLASS);
END_FOR;
ON_ERROR
MISC_release_request_silent(req_handle2);
general_on_error ();
END_ERROR;
}
}
MISC_release_request_silent(req_handle2);
/* Now that changing ownership of tables is over, it is safe to
update the database security class in RDB$DATABASE */
if (tdgbl->database_security_class[0]) // Do it only if it's not NULL
{
FOR (REQUEST_HANDLE req_handle1)
X IN RDB$DATABASE
MODIFY X USING
strncpy(X.RDB$SECURITY_CLASS, tdgbl->database_security_class,
sizeof(X.RDB$SECURITY_CLASS));
END_MODIFY;
ON_ERROR
MISC_release_request_silent(req_handle1);
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
MISC_release_request_silent(req_handle1);
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle1);
}
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
// Check to see if there is a warning
if (gds_status[0] == isc_arg_gds && gds_status[1] == 0
&& gds_status[2] != isc_arg_end)
{
BURP_print_warning(gds_status);
}
BURP_verbose (88, NULL, NULL, NULL, NULL, NULL);
// msg 88 finishing, closing, and going home
UINT64 cumul_count = MVOL_fini_read();
// attach database again to put it online
UCHAR dpb[128];
UCHAR* d = dpb;
*d++ = (UCHAR) isc_dpb_version1;
if (flag_on_line)
{
*d++ = (UCHAR) isc_dpb_online;
*d++ = 0;
}
if (tdgbl->gbl_sw_user)
{
*d++ = (UCHAR) isc_dpb_user_name;
*d++ = (UCHAR) strlen (tdgbl->gbl_sw_user);
for (UCHAR* q = (UCHAR*) tdgbl->gbl_sw_user; *q;)
*d++ = *q++;
}
if (tdgbl->gbl_sw_password)
{
if (!tdgbl->gbl_sw_service_gbak)
*d++ = (UCHAR) isc_dpb_password;
else
*d++ = (UCHAR) isc_dpb_password_enc;
*d++ = (UCHAR) strlen (tdgbl->gbl_sw_password);
for (UCHAR* q = (UCHAR*) tdgbl->gbl_sw_password; *q;)
*d++ = *q++;
}
// set sync writes to engine default
*d++ = (UCHAR) isc_dpb_force_write;
*d++ = 1;
*d++ = (UCHAR) tdgbl->hdr_forced_writes;
// set forced writes to the value which was in the header
SSHORT l = d - dpb;
// long? Are you sure? Why not FRBRD* ???
long db_handle = 0;
if (isc_attach_database (tdgbl->status_vector, 0, database_name,
(isc_db_handle*) &db_handle, l, (SCHAR*) dpb))
{
general_on_error();
}
if (isc_detach_database (tdgbl->status_vector, (isc_db_handle*) &db_handle))
general_on_error();
FINISH
ON_ERROR
general_on_error ();
END_ERROR;
if (!flag_on_line)
{
BURP_print(246, NULL, NULL, NULL, NULL, NULL);
// msg 246 Database is not online due to failure to activate one or more indices.
BURP_print(247, NULL, NULL, NULL, NULL, NULL);
// msg 247 Run gfix -online to bring database online without active indices.
return FINI_DB_NOT_ONLINE;
}
// If the database is to be restored ReadOnly, set it to read_only now!
if (tdgbl->gbl_sw_mode && tdgbl->gbl_sw_mode_val)
{
BURP_verbose (280, NULL, NULL, NULL, NULL, NULL);
// msg 280: setting database to read-only access
d = dpb;
*d++ = (UCHAR) isc_dpb_version1;
if (tdgbl->gbl_sw_user)
{
*d++ = (UCHAR) isc_dpb_user_name;
*d++ = (UCHAR) strlen (tdgbl->gbl_sw_user);
for (UCHAR* q = (UCHAR*) tdgbl->gbl_sw_user; *q;)
*d++ = *q++;
}
if (tdgbl->gbl_sw_password)
{
if (!tdgbl->gbl_sw_service_gbak)
*d++ = (UCHAR) isc_dpb_password;
else
*d++ = (UCHAR) isc_dpb_password_enc;
*d++ = (UCHAR) strlen (tdgbl->gbl_sw_password);
for (UCHAR* q = (UCHAR*) tdgbl->gbl_sw_password; *q;)
*d++ = *q++;
}
*d++ = (UCHAR) isc_dpb_set_db_readonly;
*d++ = 1;
*d++ = TRUE; // set database to readOnly mode
l = d - dpb;
db_handle = 0;
if (isc_attach_database (tdgbl->status_vector, 0, database_name,
(isc_db_handle*) &db_handle, l, (SCHAR*) dpb))
{
general_on_error();
}
if (isc_detach_database (tdgbl->status_vector, (isc_db_handle*) &db_handle))
general_on_error();
}
return FINI_OK;
}
namespace // unnamed, private
{
void add_files (const UCHAR* file_name)
{
/**************************************
*
* a d d _ f i l e s
*
**************************************
*
* Functional description
* This should be a multi-file database.
* Store files and starting
* addresses & commit this much.
*
**************************************/
isc_req_handle req_handle1 = NULL;
TGBL tdgbl = GET_THREAD_DATA;
// store the RDB$FILES records
SLONG start = 201; // 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 (strcmp (file->fil_name, (char*) file_name))
{
count++;
STORE (REQUEST_HANDLE req_handle1)
X IN RDB$FILES
strcpy (X.RDB$FILE_NAME, file->fil_name);
X.RDB$FILE_START = start;
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle1);
BURP_verbose (57, file->fil_name, (void*)(IPTR)start, NULL, NULL, NULL);
// 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, (void*)(IPTR) file->fil_length, (void*)(IPTR) (start - 1), NULL,
NULL, NULL);
// msg 96 length given for initial file (%ld) is less than minimum (%ld)
file->fil_length = 0;
}
start += file->fil_length;
}
if (count)
{
BURP_verbose (70, NULL, NULL, NULL, NULL, NULL);
// msg 70 committing secondary files
COMMIT
// existing ON_ERROR continues past error, beck
ON_ERROR
BURP_print (174, NULL, NULL, NULL, NULL, NULL);
// msg 174 cannot commit files
BURP_print_status (tdgbl->status_vector);
ROLLBACK;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
}
void bad_attribute (scan_attr_t scan_next_attr,
ATT_TYPE bad_attr,
USHORT type)
{
/**************************************
*
* b a d _ a t t r i b u t e
*
**************************************
*
* Functional description
* We ran into an unsupported attribute.
* but it isn't the end of the world.
* We will try to skip some bad data and
* look for next valid attribute to continue the process.
*
**************************************/
TGBL tdgbl = GET_THREAD_DATA;
SLONG skip_count = 0;
if (!tdgbl->gbl_sw_skip_count)
{
TEXT t_name[128];
gds__msg_format(NULL, 12, type, sizeof(t_name), t_name, NULL, NULL,
NULL, NULL, NULL);
BURP_print (80, t_name, (void*) bad_attr, NULL, NULL, NULL);
// msg 80 don't recognize %s attribute %ld -- continuing
const SSHORT skip_l = get(tdgbl);
if (skip_l)
get_skip(tdgbl, skip_l);
}
else
{
if (scan_next_attr == NO_SKIP)
{
skip_count = tdgbl->gbl_sw_skip_count;
get_skip(tdgbl, skip_count);
BURP_print (203, (void*)(IPTR) skip_count, (void*) bad_attr, NULL, NULL, NULL);
//msg 203: skipped %d bytes after reading a bad attribute %d
}
else
{
++skip_count;
BURP_print (205, (void*)(IPTR) skip_count, (void*) bad_attr, NULL, NULL, NULL);
// msg 205: skipped %d bytes looking for next valid attribute, encountered attribute %d
}
scan_next_attr = AFTER_SKIP;
}
}
USHORT check_db_version()
{
/**************************************
*
* c h e c k _ d b _ v e r s i o n
*
**************************************
*
* Functional description
* Find the version number of the database.
*
**************************************/
isc_req_handle req_handle1 = NULL, req_handle2 = NULL;
TGBL tdgbl = GET_THREAD_DATA;
USHORT db_version = DB_VERSION_DDL4;
FOR (REQUEST_HANDLE req_handle1)
FIRST 1 X IN RDB$RELATIONS
WITH X.RDB$RELATION_NAME = "RDB$TRIGGERS"
db_version = DB_VERSION_DDL5;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle1);
FOR (REQUEST_HANDLE req_handle2)
FIRST 1 X IN RDB$RELATIONS
WITH X.RDB$RELATION_NAME = "RDB$PROCEDURES"
db_version = DB_VERSION_DDL8;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle2);
return db_version;
}
void create_database (const TEXT* file_name)
{
/**************************************
*
* c r e a t e _ d a t a b a s e
*
**************************************
*
* Functional description
* create the new database, looking
* to see if there are any interesting
* things to do.
*
**************************************/
TGBL tdgbl = GET_THREAD_DATA;
// 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;
USHORT forced_writes = 2; // default for the current platform
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();
break;
case att_page_size:
page_size = get_numeric();
break;
case att_sweep_interval:
sweep_interval = get_numeric();
break;
case att_forced_writes:
forced_writes = (USHORT) get_numeric();
break;
case att_no_reserve:
no_reserve = (USHORT) get_numeric() != FALSE;
break;
case att_db_read_only:
db_read_only = get_numeric() != FALSE;
break;
case att_page_buffers:
page_buffers = get_numeric();
break;
default:
{
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, 0, 0);
// msg 32 Expected database description record
if (tdgbl->gbl_sw_page_size &&
(tdgbl->gbl_sw_page_size < page_size))
{
BURP_print (110, (void*)(IPTR) page_size,
(void*)(IPTR) tdgbl->gbl_sw_page_size, NULL, NULL, NULL);
// msg 110 Reducing the database page size from %ld bytes to %ld bytes
}
if (tdgbl->gbl_sw_page_size)
page_size = tdgbl->gbl_sw_page_size;
tdgbl->hdr_forced_writes = forced_writes;
if (tdgbl->gbl_sw_no_reserve)
no_reserve = tdgbl->gbl_sw_no_reserve;
// 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;
UCHAR dpb[128];
UCHAR* d = dpb;
*d++ = (UCHAR) isc_dpb_version1;
*d++ = (UCHAR) isc_dpb_page_size;
*d++ = 2;
*d++ = 0;
*d++ = (UCHAR) (page_size >> 8);
*d++ = (UCHAR) isc_dpb_gbak_attach;
*d++ = (UCHAR) strlen(GDS_VERSION);
for (const TEXT* gvp = GDS_VERSION; *gvp;)
*d++ = *gvp++;
if (sweep_interval != MAX_ULONG)
{
*d++ = (UCHAR) isc_dpb_sweep_interval;
*d++ = 4;
*d++ = (UCHAR) sweep_interval;
*d++ = (UCHAR) (sweep_interval >> 8);
*d++ = (UCHAR) (sweep_interval >> 16);
*d++ = (UCHAR) (sweep_interval >> 24);
}
// If the database is to be restored "read_only", fillup the data pages
if (no_reserve || db_read_only)
{
*d++ = (UCHAR) isc_dpb_no_reserve;
*d++ = 1;
*d++ = TRUE;
}
if (tdgbl->gbl_sw_user)
{
*d++ = (UCHAR) isc_dpb_user_name;
*d++ = (UCHAR) strlen (tdgbl->gbl_sw_user);
for (const UCHAR* q = (UCHAR*) tdgbl->gbl_sw_user; *q;)
*d++ = *q++;
}
if (tdgbl->gbl_sw_password)
{
if (!tdgbl->gbl_sw_service_gbak)
*d++ = (UCHAR) isc_dpb_password;
else
*d++ = (UCHAR) isc_dpb_password_enc;
*d++ = (UCHAR) strlen (tdgbl->gbl_sw_password);
for (const UCHAR* q = (UCHAR*) tdgbl->gbl_sw_password; *q;)
*d++ = *q++;
}
if (page_buffers)
{
*d++ = (UCHAR) isc_dpb_set_page_buffers;
*d++ = 4;
*d++ = (UCHAR) page_buffers;
*d++ = (UCHAR) (page_buffers >> 8);
*d++ = (UCHAR) (page_buffers >> 16);
*d++ = (UCHAR) (page_buffers >> 24);
}
// Turn off sync writes during restore
*d++ = (UCHAR) isc_dpb_force_write;
*d++ = 1;
*d++ = 0;
/*
**
** which SQL dialect that this database speaks
** When we restore backup files that came from prior
** to V6, we force the SQL database dialect to 1
**
*/
*d++ = (UCHAR) isc_dpb_sql_dialect;
*d++ = 1;
if (SQL_dialect_flag)
*d++ = (UCHAR) SQL_dialect;
else
*d++ = (UCHAR) SQL_DIALECT_V5;
// start database up shut down,
// use single-user mode to avoid conflicts during restore process
*d++ = (UCHAR) isc_dpb_shutdown;
*d++ = 1;
*d++ = (UCHAR) isc_dpb_shut_attachment | isc_dpb_shut_single;
*d++ = (UCHAR) isc_dpb_shutdown_delay;
*d++ = 2;
*d++ = 0;
*d++ = 0;
const SSHORT l = d - dpb;
ISC_STATUS_ARRAY status_vector;
if (isc_create_database (status_vector, 0, file_name,
&DB, l, (const SCHAR*) dpb, 0))
{
BURP_error_redirect (status_vector, 33, file_name, 0);
// msg 33 failed to create database %s
}
if (tdgbl->gbl_sw_version)
{
BURP_print(139, file_name, NULL, NULL, NULL, NULL);
// msg 139 Version(s) for database "%s"
isc_version(&DB, BURP_output_version, (void*)"\t%s\n");
}
BURP_verbose (74, file_name, (void*) (IPTR)page_size, NULL, NULL, NULL);
// msg 74 created database %s, page_size %ld bytes
}
void decompress(UCHAR* buffer,
USHORT length)
{
/**************************************
*
* d e c o m p r e s s
*
**************************************
*
* Functional description
* Get a number of compressed bytes.
*
**************************************/
TGBL tdgbl = GET_THREAD_DATA;
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, (void*)(IPTR) count,
(void*)(IPTR) (end - p), NULL, NULL, NULL);
// msg 202: adjusting a decompression length error: invalid length %d was adjusted to %d
count = end - p;
}
p = get_block(tdgbl, p, count);
}
else if (count < 0)
{
if (end + count < p)
{
BURP_print(202, (void*)(IPTR) count,
(void*)(IPTR) (p - end), NULL, NULL, NULL);
// 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, 0, 0);
// msg 34 RESTORE: decompression length error
}
}
void eat_blob()
{
/**************************************
*
* e a t _ b l o b
*
**************************************
*
* Functional description
* Discard a blob from backup file
*
**************************************/
TGBL tdgbl = GET_THREAD_DATA;
const SLONG length = get_numeric();
get_skip(tdgbl, length);
}
burp_rel* find_relation (const TEXT* name)
{
/**************************************
*
* f i n d _ r e l a t i o n
*
**************************************
*
* Functional description
* Given a relation name, find the relation block. If there isn't
* one, produce a fatal error.
*
**************************************/
TGBL tdgbl = GET_THREAD_DATA;
// 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, name, 0);
// 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.
*
**************************************/
TGBL tdgbl = GET_THREAD_DATA;
BURP_print_status (isc_status);
BURP_abort ();
}
bool get_acl (const TEXT *owner_nm,
ISC_QUAD *blob_id,
ISC_QUAD *new_blob_id)
{
/**************************************
*
* g e t _ a c l
*
**************************************
*
* Functional description
*
* open the blob that contains the ACL list
* get the ACL list of a relation
* replace the owner of the relation in the ACL list with
* the creator of the relation
* create a new blob
* store the new ACL list in the new blob
*
**************************************/
static const SCHAR blr_items[] = {isc_info_blob_max_segment,
isc_info_blob_total_length,
isc_info_blob_num_segments};
TGBL tdgbl = GET_THREAD_DATA;
// If the blob is null, don't store it. It will be restored as null.
if (!blob_id->gds_quad_high && !blob_id->gds_quad_low)
return false;
ISC_STATUS_ARRAY status_vector;
// Open the blob and get it's vital statistics
FRBRD* blob = NULL;
if (isc_open_blob (status_vector, &DB, &gds_trans, &blob, blob_id))
{
// msg 24 isc_open_blob failed
BURP_error_redirect (status_vector, 24, NULL, NULL);
}
UCHAR blob_info[32];
if (isc_blob_info (status_vector, &blob, sizeof(blr_items),
blr_items, sizeof(blob_info), (SCHAR*) blob_info))
{
// msg 20 isc_blob_info failed
BURP_error_redirect (status_vector, 20, NULL, NULL);
}
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, (void*)(IPTR) item, NULL, NULL, NULL, NULL);
// CVC: do you return, without closing the blob, dear function???
if (isc_close_blob (status_vector, &blob))
BURP_error_redirect (status_vector, 23, NULL, NULL);
// msg 23 isc_close_blob failed
return false;
}
}
if (!length)
{
if (isc_close_blob (status_vector, &blob))
BURP_error_redirect (status_vector, 23, NULL, NULL);
// msg 23 isc_close_blob failed
return false;
}
// Rdb sometimes gets the length messed up
if (length < max_segment)
length = max_segment;
/*
** Allocate a buffer large enough for the largest segment and start
** grinding.
*/
UCHAR static_buffer[1024];
UCHAR* buffer;
if (!max_segment || max_segment <= sizeof(static_buffer))
buffer = static_buffer;
else
buffer = BURP_alloc (max_segment);
USHORT return_length = 0;
isc_get_segment (status_vector, &blob, &return_length, max_segment,
(SCHAR*) buffer);
// CVC: we don't check the result of the function. We don't check the
// return_length's value either.
if (isc_close_blob (status_vector, &blob))
{
if (buffer != static_buffer)
BURP_free (buffer);
// msg 23 isc_close_blob failed
BURP_error_redirect (status_vector, 23, NULL, NULL);
}
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);
UCHAR* new_buffer = BURP_alloc (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;
USHORT 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++;
}
isc_blob_handle blob_handle = NULL;
if (isc_create_blob2 (status_vector, &DB, &gds_trans,
&blob_handle, new_blob_id, 0, NULL))
{
if (buffer != static_buffer)
BURP_free (buffer);
if (new_buffer != NULL)
BURP_free (new_buffer);
// msg 37 isc_create_blob failed
BURP_error_redirect (status_vector, 37, 0, 0);
}
if (isc_put_segment (status_vector, &blob_handle, new_len,
reinterpret_cast<const SCHAR*>(new_buffer)))
{
if (buffer != static_buffer)
BURP_free (buffer);
if (new_buffer != NULL)
BURP_free (new_buffer);
// msg 38 isc_put_segment failed
BURP_error_redirect (status_vector, 38, 0, 0);
}
if (isc_close_blob (status_vector, &blob_handle))
{
if (buffer != static_buffer)
BURP_free (buffer);
if (new_buffer != NULL)
BURP_free (new_buffer);
// msg 23 isc_close_blob failed
BURP_error_redirect (status_vector, 23, 0, 0);
}
if (buffer != static_buffer)
BURP_free (buffer);
if (new_buffer != NULL)
BURP_free (new_buffer);
return true;
}
void get_array (burp_rel* relation,
UCHAR *record_buffer)
{
/**************************************
*
* g e t _ a r r a y
*
**************************************
*
* Functional description
* Read array data from input file to nice,
* shiney, new array.
*
**************************************/
burp_fld* field;
ISC_STATUS_ARRAY status_vector;
USHORT count, field_number, field_length;
UCHAR *buffer, *p;
UCHAR blr_buffer[200]; // enough for a sdl with 16 dimensions
lstring xdr_slice;
TGBL tdgbl = GET_THREAD_DATA;
// don't free something you don't allocate
lstring xdr_buffer;
xdr_buffer.lstr_allocated = 0;
// Pick up attributes
SLONG fld_ranges[2 * MAX_DIMENSION];
SLONG slice_length;
SLONG *range, *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();
for (field = relation->rel_fields; field; field = field->fld_next) {
if (field->fld_number == field_number)
break;
}
if (!field) {
BURP_error_redirect (NULL, 36, 0, 0);
// msg 36 Can't find field for blob
}
field_length = field->fld_length;
if (field->fld_type == blr_varying)
field_length += sizeof(USHORT);
slice_length = field_length;
//
// Copy the ranges onto a buffer and let the program
// mess with the copy rather than the original
//
memcpy(fld_ranges, field->fld_ranges, sizeof(fld_ranges));
break;
case att_array_dimensions:
field->fld_dimensions = (SSHORT)get_numeric();
end_ranges = fld_ranges + 2 * field->fld_dimensions;
for (range = fld_ranges; range < end_ranges; range += 2)
{
if (get_attribute(&attribute, tdgbl) != att_array_range_low)
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
else
range[0] = get_numeric();
if (get_attribute(&attribute, tdgbl) != att_array_range_high)
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
else
range[1] = get_numeric();
slice_length *= (range[1] - range[0] + 1);
}
break;
default:
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
break;
}
}
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
ib_fprintf(ib_stderr, "\nLast element upper bounds read from backup file:\n");
for (current_dim = 1; current_dim < field->fld_dimensions * 2; current_dim += 2)
ib_fprintf(ib_stderr,"%ld ",fld_ranges[current_dim]);
ib_fprintf(ib_stderr, "\nCalculated Last element upper bounds :\n");
for (current_dim = 0; current_dim < field->fld_dimensions; current_dim++)
ib_fprintf(ib_stderr,"%ld ",last_element_dim[current_dim]);
ib_fprintf(ib_stderr,"return_length = %ld\n", return_length);
ib_fprintf(ib_stderr,"elements_returned = %ld\n", return_length/field_length);
ib_fprintf(ib_stderr,"Max dims[");
for (current_dim = 1; current_dim < field->fld_dimensions * 2; current_dim += 2)
ib_fprintf(ib_stderr,"%ld ",field->fld_ranges[current_dim]);
ib_fprintf(ib_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
stuff(&blr, isc_sdl_version1);
stuff(&blr, isc_sdl_struct);
stuff(&blr, 1);
if (field->fld_type == blr_text || field->fld_type == blr_varying)
{
if (field->fld_type == blr_text)
stuff(&blr, blr_text2);
else
stuff(&blr, blr_varying2);
stuff_word(&blr, field->fld_character_set_id);
stuff_word(&blr, field->fld_length);
}
else if (field->fld_type == blr_short || field->fld_type == blr_long ||
field->fld_type == blr_quad || field->fld_type == blr_int64)
{
stuff(&blr, field->fld_type);
stuff(&blr, field->fld_scale);
}
else
stuff(&blr, field->fld_type);
stuff(&blr, isc_sdl_relation);
stuff_string(&blr, relation->rel_name);
stuff(&blr, isc_sdl_field);
stuff_string(&blr, field->fld_name);
// each element spec starts here
#ifdef DEBUG
ib_fprintf(ib_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++)
{
stuff(&blr, isc_sdl_do2);
stuff(&blr, count);
//
// Normally we loop through all dimensions chopping off slices
// and writing them. This works fine but this also means that
// we blindly put slices without actually figuring out if we
// really need to do so. For eg: if we have a 2D array of
// size [10,4] and the last element dims are [6,4] then all
// we need to do is is to put one slice as
// isc_put_slice(1..6,1..4)
// rather than looping through the dimensions and putting
// isc_put_slice(1..5,1..4)
// isc_put_slice(6..6,1..4)
// we could extend this logic to any no of dims. The following
// if condition figures out such cases. This combined with
// the Special case should optimize the no of isc_put_slice
// we perform.
//
if (current_dim + 1 == field->fld_dimensions - 1 &&
field->fld_dimensions - count == 2 &&
last_element_dim[count + 1] == range[3])
{
stuff(&blr, isc_sdl_long_integer);
stuff_long(&blr, range[0]);
lower = range[0];
stuff(&blr, isc_sdl_long_integer);
stuff_long(&blr, last_element_dim[count]);
upper = last_element_dim[count];
elements_written *= (upper - lower + 1);
range += 2;
count++;
stuff(&blr, isc_sdl_do2);
stuff(&blr, count);
stuff(&blr, isc_sdl_long_integer);
stuff_long(&blr, range[0]);
lower = range[0];
stuff(&blr, isc_sdl_long_integer);
stuff_long(&blr, last_element_dim[count]);
upper = last_element_dim[count];
elements_written *= (upper - lower + 1);
++current_dim;
break;
}
if (current_dim == count)
{
stuff(&blr, isc_sdl_long_integer);
stuff_long(&blr, range[0]);
lower = range[0];
stuff(&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;
}
stuff_long(&blr, upper);
}
else if (current_dim < count)
{
stuff(&blr, isc_sdl_long_integer);
stuff_long(&blr, range[0]);
stuff(&blr, isc_sdl_long_integer);
stuff_long(&blr, range[1]);
upper = range[1];
lower = range[0];
}
else if (current_dim > count)
{
stuff(&blr, isc_sdl_long_integer);
stuff_long(&blr, last_element_dim[count]);
stuff(&blr, isc_sdl_long_integer);
stuff_long(&blr, last_element_dim[count]);
upper = lower = last_element_dim[count];
}
elements_written *= (upper - lower + 1);
#ifdef DEBUG
ib_fprintf(ib_stderr,"%d..%d ", lower, upper);
#endif
}
if (dont_write)
continue;
#ifdef DEBUG
ib_fprintf(ib_stderr,"]");
ib_fprintf(ib_stderr,"\n Elements Written=%d ",elements_written);
#endif
stuff(&blr, isc_sdl_element);
stuff(&blr, 1);
stuff(&blr, isc_sdl_scalar);
stuff(&blr, 0);
stuff(&blr, field->fld_dimensions);
for (count = 0; count < field->fld_dimensions; count++)
{
stuff(&blr, isc_sdl_variable);
stuff(&blr, count);
}
stuff(&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, 0, 0);
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, NULL, NULL, NULL, NULL);
// msg 81 error accessing blob field %s -- continuing
BURP_print_status (status_vector);
#ifdef DEBUG
PRETTY_print_sdl (blr_buffer, NULL, NULL, 0);
#endif
return;
}
data_at += elements_written * field->fld_length;
#ifdef DEBUG
ib_fprintf(ib_stderr,"next data_at = %d\n", data_at);
#endif
}
}
else
{ /** This is the regular case we've got the entire array **/
UCHAR* blr = blr_buffer;
// build the sdl
stuff(&blr, isc_sdl_version1);
stuff(&blr, isc_sdl_struct);
stuff(&blr, 1);
if (field->fld_type == blr_text || field->fld_type == blr_varying)
{
if (field->fld_type == blr_text)
stuff(&blr, blr_text2);
else
stuff(&blr, blr_varying2);
stuff_word(&blr, field->fld_character_set_id);
stuff_word(&blr, field->fld_length);
}
else if (field->fld_type == blr_short || field->fld_type == blr_long ||
field->fld_type == blr_quad || field->fld_type == blr_int64)
{
stuff(&blr, field->fld_type);
stuff(&blr, field->fld_scale);
}
else
stuff(&blr, field->fld_type);
stuff(&blr, isc_sdl_relation);
stuff_string(&blr, relation->rel_name);
stuff(&blr, isc_sdl_field);
stuff_string(&blr, field->fld_name);
// each element spec starts here
for (range = fld_ranges, count = 0; range < end_ranges; range += 2, count++)
{
stuff(&blr, isc_sdl_do2);
stuff(&blr, count);
stuff(&blr, isc_sdl_long_integer);
stuff_long(&blr, range[0]);
stuff(&blr, isc_sdl_long_integer);
stuff_long(&blr, range[1]);
}
stuff(&blr, isc_sdl_element);
stuff(&blr, 1);
stuff(&blr, isc_sdl_scalar);
stuff(&blr, 0);
stuff(&blr, field->fld_dimensions);
for (count = 0; count < field->fld_dimensions; count++)
{
stuff(&blr, isc_sdl_variable);
stuff(&blr, count);
}
stuff(&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, 0, 0);
// msg 55 Expected XDR record length
else
{
xdr_buffer.lstr_allocated = get(tdgbl);
xdr_buffer.lstr_allocated |= get(tdgbl) << 8;
xdr_buffer.lstr_allocated |= get(tdgbl) << 16;
xdr_buffer.lstr_allocated |= get(tdgbl) << 24;
lcount = xdr_buffer.lstr_length = xdr_buffer.lstr_allocated;
xdr_buffer.lstr_address = BURP_alloc (xdr_buffer.lstr_allocated);
xdr_slice.lstr_allocated = xdr_slice.lstr_length = return_length;
xdr_slice.lstr_address = buffer;
p = xdr_buffer.lstr_address;
}
}
else
{
p = buffer;
lcount = return_length;
}
if (lcount)
get_block(tdgbl, p, lcount);
if (tdgbl->gbl_sw_transportable)
CAN_slice (&xdr_buffer, &xdr_slice, FALSE, blr_length, blr_buffer);
if (isc_put_slice (status_vector, &DB, &gds_trans,
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, NULL, NULL, NULL, NULL);
// msg 81 error accessing blob field %s -- continuing
BURP_print_status (status_vector);
#ifdef DEBUG
PRETTY_print_sdl (blr_buffer, NULL, NULL, 0);
#endif
return;
}
}
BURP_free (buffer);
if (tdgbl->gbl_sw_transportable && xdr_buffer.lstr_allocated)
BURP_free (xdr_buffer.lstr_address);
}
void get_blob (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.
*
**************************************/
TGBL tdgbl = GET_THREAD_DATA;
// 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();
break;
case att_blob_max_segment:
max_segment = (USHORT)get_numeric();
break;
case att_blob_number_segments:
segments = get_numeric();
break;
case att_blob_type:
blob_type = (UCHAR) get_numeric();
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, 0, 0);
// msg 36 Can't find field for blob
}
// Create new blob
ISC_QUAD* blob_id = (ISC_QUAD*) ((UCHAR*) record_buffer + field->fld_offset);
FRBRD* blob = NULL;
ISC_STATUS_ARRAY status_vector;
const char blob_desc[] = {isc_bpb_version1, isc_bpb_type, 1, blob_type};
if (isc_create_blob2 (status_vector, &DB, &gds_trans, &blob, blob_id,
sizeof(blob_desc), blob_desc))
{
BURP_error_redirect (status_vector, 37, 0, 0);
// msg 37 isc_create_blob failed
}
// Allocate blob buffer if static buffer is too short
UCHAR static_buffer[1024];
UCHAR* buffer;
if (!max_segment || max_segment <= sizeof(static_buffer))
buffer = static_buffer;
else
buffer = BURP_alloc (max_segment);
// Eat up blob segments
for (; segments > 0; --segments )
{
USHORT length = get(tdgbl);
length |= get(tdgbl) << 8;
if (length)
{
get_block(tdgbl, buffer, length);
}
if (isc_put_segment (status_vector, &blob, length,
reinterpret_cast<const SCHAR*>(buffer)))
{
BURP_error_redirect (status_vector, 38, 0, 0);
// msg 38 isc_put_segment failed
}
}
if (isc_close_blob (status_vector, &blob))
BURP_error_redirect (status_vector, 23, 0, 0);
// msg 23 isc_close_blob failed
if (buffer != static_buffer)
BURP_free (buffer);
}
void get_blr_blob (ISC_QUAD *blob_id,
bool glb_trans)
{
/**************************************
*
* g e t _ b l r _ b l o b
*
**************************************
*
* Functional description
* Read blob attributes and copy data from input file to nice,
* shiney, new blob.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
UCHAR *buffer, static_buffer[1024], *p;
TGBL tdgbl = GET_THREAD_DATA;
ULONG length = (ULONG)get_numeric();
// Create new blob
isc_tr_handle local_trans;
if (glb_trans && tdgbl->global_trans)
local_trans = tdgbl->global_trans;
else
local_trans = gds_trans;
FRBRD* blob = NULL;
if (isc_create_blob (status_vector, &DB, &local_trans, &blob, blob_id))
{
BURP_error_redirect (status_vector, 37, 0, 0);
// msg 37 isc_create_blob failed
}
// Allocate blob buffer if static buffer is too short
/* 03 Jun 2003. Nickolay Samofatov. Workaround bug of GCC 3.2.X #11068.
Original code was:
if (!length || length + 1 <= (USHORT)(sizeof(static_buffer)) )
*/
if (length + 1 <= sizeof(static_buffer) )
buffer = static_buffer;
else
buffer = BURP_alloc (length + 1);
if (length)
{
p = get_block(tdgbl, buffer, length);
// Make sure it has an eoc
if (p[-1] != blr_eoc) {
p[0] = blr_eoc;
length++;
}
}
if (isc_put_segment (status_vector, &blob, length,
reinterpret_cast<const SCHAR*>(buffer)))
{
BURP_error_redirect (status_vector, 38, 0, 0);
// msg 38 isc_put_segment failed
}
if (isc_close_blob (status_vector, &blob))
BURP_error_redirect (status_vector, 23, 0, 0);
// msg 23 isc_close_blob failed
if (buffer != static_buffer)
BURP_free (buffer);
}
bool get_character_set()
{
/**************************************
*
* g e t _ c h a r a c t e r _ s e t s
*
**************************************
*
* Functional description
* Restore data for user defined character sets
*
**************************************/
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_character_sets_req_handle1)
X IN RDB$CHARACTER_SETS
X.RDB$CHARACTER_SET_NAME.NULL = TRUE;
X.RDB$FORM_OF_USE.NULL = TRUE;
X.RDB$NUMBER_OF_CHARACTERS.NULL = TRUE;
X.RDB$DEFAULT_COLLATE_NAME.NULL = TRUE;
X.RDB$CHARACTER_SET_ID.NULL = TRUE;
X.RDB$SYSTEM_FLAG.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$FUNCTION_NAME.NULL = TRUE;
X.RDB$BYTES_PER_CHARACTER.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_charset_name:
X.RDB$CHARACTER_SET_NAME.NULL = FALSE;
GET_TEXT(X.RDB$CHARACTER_SET_NAME);
BURP_verbose (msgVerbose_restore_charset,
X.RDB$CHARACTER_SET_NAME, NULL, NULL, NULL, NULL);
break;
case att_charset_form:
X.RDB$FORM_OF_USE.NULL = FALSE;
GET_TEXT(X.RDB$FORM_OF_USE);
break;
case att_charset_numchar:
X.RDB$NUMBER_OF_CHARACTERS.NULL = FALSE;
X.RDB$NUMBER_OF_CHARACTERS = (USHORT) get_numeric();
break;
case att_charset_coll:
X.RDB$DEFAULT_COLLATE_NAME.NULL = FALSE;
GET_TEXT(X.RDB$DEFAULT_COLLATE_NAME);
break;
case att_charset_id:
X.RDB$CHARACTER_SET_ID.NULL = FALSE;
X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric();
break;
case att_charset_sysflag:
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric();
break;
case att_charset_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (&X.RDB$DESCRIPTION, false);
break;
case att_charset_funct:
X.RDB$FUNCTION_NAME.NULL = FALSE;
GET_TEXT(X.RDB$FUNCTION_NAME);
break;
case att_charset_bytes_char:
X.RDB$BYTES_PER_CHARACTER.NULL = FALSE;
X.RDB$BYTES_PER_CHARACTER = (USHORT) get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, msgErr_restore_charset);
// RDB$CHARSETS
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_chk_constraint()
{
/**************************************
*
* g e t _ c h k _ c o n s t r a i n t
*
**************************************
*
* Functional description
* Restore data for check constraints.
*
**************************************/
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_chk_constraint_req_handle1)
X IN RDB$CHECK_CONSTRAINTS
X.RDB$CONSTRAINT_NAME.NULL = TRUE;
X.RDB$TRIGGER_NAME.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_chk_constraint_name:
X.RDB$CONSTRAINT_NAME.NULL = FALSE;
GET_TEXT(X.RDB$CONSTRAINT_NAME);
break;
case att_chk_trigger_name:
X.RDB$TRIGGER_NAME.NULL = FALSE;
GET_TEXT(X.RDB$TRIGGER_NAME);
break;
default:
bad_attribute (scan_next_attr, attribute, 208);
// msg 208 relation constraint
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_collation()
{
/**************************************
*
* g e t _ c o l l a t i o n
*
**************************************
*
* Functional description
* Restore data for user defined collations
*
**************************************/
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_collation_req_handle1)
X IN RDB$COLLATIONS
X.RDB$COLLATION_NAME.NULL = TRUE;
X.RDB$COLLATION_ID.NULL = TRUE;
X.RDB$CHARACTER_SET_ID.NULL = TRUE;
X.RDB$COLLATION_ATTRIBUTES.NULL = TRUE;
X.RDB$SYSTEM_FLAG.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$FUNCTION_NAME.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_coll_name:
X.RDB$COLLATION_NAME.NULL = FALSE;
GET_TEXT(X.RDB$COLLATION_NAME);
BURP_verbose(msgVerbose_restore_collation, X.RDB$COLLATION_NAME,
NULL, NULL, NULL, NULL);
break;
case att_coll_id:
X.RDB$COLLATION_ID.NULL = FALSE;
X.RDB$COLLATION_ID = (USHORT) get_numeric();
break;
case att_coll_cs_id:
X.RDB$CHARACTER_SET_ID.NULL = FALSE;
X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric();
break;
case att_coll_attr:
X.RDB$COLLATION_ATTRIBUTES.NULL = FALSE;
X.RDB$COLLATION_ATTRIBUTES = (USHORT) get_numeric();
break;
case att_coll_subtype: // No longer used: 93-11-15 DBS
// still present to handle V4 R&D gbak files
get_numeric();
break;
case att_coll_sysflag:
X.RDB$SYSTEM_FLAG.NULL = FALSE;
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric();
break;
case att_coll_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (&X.RDB$DESCRIPTION, false);
break;
case att_coll_funct:
X.RDB$FUNCTION_NAME.NULL = FALSE;
GET_TEXT(X.RDB$FUNCTION_NAME);
break;
default:
bad_attribute (scan_next_attr, attribute, msgErr_restore_collation);
// Bad RDB$COLLATION
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
rec_type get_data (burp_rel* relation)
{
/**************************************
*
* g e t _ d a t a
*
**************************************
*
* Functional description
* Write data records for a relation.
*
**************************************/
isc_req_handle req_handle = NULL;
BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name;
TGBL tdgbl = GET_THREAD_DATA;
// If we're only doing meta-data, ignore data records
if (tdgbl->gbl_sw_meta)
return ignore_data (relation);
// Start by counting the interesting fields
RCRD_OFFSET offset = 0;
ULONG length = 0;
USHORT count = 0;
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;
stuff(&blr, blr_version4);
stuff(&blr, blr_begin);
stuff(&blr, blr_message);
stuff(&blr, 0); // Message number
stuff_word(&blr, count); // Number of fields, counting eof
// Let's reset count.
count = 0;
for (field = relation->rel_fields; field; field = field->fld_next)
{
if (field->fld_flags & FLD_computed)
continue;
// arrays are of various fld_types but are really blobs
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)
stuff(&blr, blr_text2);
else
stuff(&blr, blr_varying2);
stuff_word(&blr, field->fld_character_set_id);
stuff_word(&blr, field->fld_length);
if (dtype == blr_varying)
length += sizeof(USHORT);
break;
case blr_short:
case blr_long:
case blr_quad:
case blr_int64:
stuff(&blr, field->fld_type);
stuff(&blr, field->fld_scale);
break;
case blr_float:
case blr_double:
case blr_timestamp:
case blr_sql_time:
case blr_sql_date:
stuff(&blr, field->fld_type);
break;
case blr_blob:
alignment = type_alignments[dtype_blob];
length = type_lengths[dtype_blob];
stuff(&blr, blr_quad);
stuff(&blr, 0);
break;
default:
BURP_error (26, true, isc_arg_number, (void*) (IPTR) field->fld_type,
0, NULL, 0, NULL, 0, NULL, 0, NULL);
// msg 26 datatype %ld not understood
break;
}
if (alignment)
offset = FB_ALIGN(offset, alignment);
field->fld_offset = offset;
field->fld_parameter = count++;
offset += length;
}
// If this is format version 2, build fields for null flags
if (tdgbl->RESTORE_format >= 2)
for (field = relation->rel_fields; field; field = field->fld_next)
{
if (field->fld_flags & FLD_computed)
continue;
stuff(&blr, blr_short);
stuff(&blr, 0);
offset = FB_ALIGN(offset, sizeof(SSHORT));
field->fld_missing_parameter = count++;
offset += sizeof(SSHORT);
}
length = offset;
// Build STORE statement
stuff(&blr, blr_receive);
stuff(&blr, 0);
stuff(&blr, blr_store);
stuff(&blr, blr_relation);
stuff_string(&blr, relation->rel_name);
stuff(&blr, 0); // context variable
stuff(&blr, blr_begin);
for (field = relation->rel_fields; field; field = field->fld_next)
{
if (field->fld_flags & FLD_computed)
continue;
stuff(&blr, blr_assignment);
if (tdgbl->RESTORE_format >= 2)
{
stuff(&blr, blr_parameter2);
stuff(&blr, 0);
stuff_word(&blr, field->fld_parameter);
stuff_word(&blr, field->fld_missing_parameter);
}
else
{
stuff(&blr, blr_parameter);
stuff(&blr, 0);
stuff_word(&blr, field->fld_parameter);
}
stuff(&blr, blr_field);
stuff(&blr, 0);
stuff_string(&blr, field->fld_name);
}
stuff(&blr, blr_end);
stuff(&blr, blr_end);
stuff(&blr, blr_eoc);
// Compile request
#ifdef DEBUG
isc_print_blr (reinterpret_cast<const char*>(blr_buffer), NULL, NULL, 0);
#endif
FRBRD* request = NULL;
ISC_STATUS_ARRAY status_vector;
USHORT blr_length = blr - blr_buffer;
if (isc_compile_request (status_vector, &DB, &request,
blr_length, reinterpret_cast<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, 0, 0);
// msg 27 isc_compile_request failed
else
{
BURP_print_status (status_vector);
BURP_free (blr_buffer);
return ignore_data (relation);
}
}
BURP_free (blr_buffer);
SSHORT* buffer = NULL;
BURP_verbose (124, relation->rel_name, NULL, NULL, NULL, NULL);
// 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, 0, 0);
// msg 39 expected record length
USHORT l = (USHORT) get_numeric();
if (!tdgbl->gbl_sw_transportable && l != length)
{
if (!old_length)
old_length = recompute_length (relation);
if (l != old_length)
{
BURP_error(40, true, isc_arg_number, (void*)(IPTR)length,
isc_arg_number, (void*) (IPTR) l,
0, NULL, 0, NULL, 0, NULL);
// 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, 0, 0);
// msg 55 Expected XDR record length
else
{
data.lstr_length = l = (USHORT) get_numeric();
if (l > data.lstr_allocated)
{
data.lstr_allocated = l;
if (data.lstr_address)
BURP_free (data.lstr_address);
data.lstr_address = (UCHAR *) BURP_alloc (data.lstr_allocated);
}
p = data.lstr_address;
}
}
else
p = reinterpret_cast<UCHAR*>(buffer);
if (get(tdgbl) != att_data_data)
BURP_error_redirect (NULL, 41, 0, 0);
// msg 41 expected data attribute
if (tdgbl->gbl_sw_compress)
decompress (p, l);
else
{
get_block(tdgbl, p, l);
}
if (old_length)
realign ((UCHAR *)buffer, relation);
if (tdgbl->gbl_sw_transportable)
CAN_encode_decode (relation, &data, (UCHAR *)buffer, FALSE);
records++;
if ((records % RESTORE_VERBOSE_INTERVAL) == 0)
BURP_verbose(107, (void*) (IPTR) records, NULL, NULL, NULL, NULL);
for (field = relation->rel_fields; field; field = field->fld_next)
if ((field->fld_type == blr_blob) || (field->fld_flags & FLD_array))
{
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 (relation->rel_fields, (UCHAR *) buffer);
else if (record == rec_array)
get_array (relation, (UCHAR *) buffer);
get_record(&record, tdgbl);
}
if (isc_start_and_send (status_vector, &request, &gds_trans, 0,
(USHORT) length, buffer, 0))
{
if (status_vector[1] == isc_not_valid)
{
if (tdgbl->gbl_sw_incremental)
{
BURP_print (138, relation->rel_name, NULL, NULL, NULL, NULL);
// msg 138 validation error on field in relation %s
BURP_print_status (status_vector);
}
else
BURP_error_redirect (status_vector, 47, 0, 0);
// msg 47 warning -- record could not be restored
}
else {
if (tdgbl->gbl_sw_incremental)
{
BURP_print (114, relation->rel_name, NULL, NULL, NULL, NULL);
// msg 114 restore failed for record in relation %s
BURP_print_status (status_vector);
}
else
BURP_error_redirect (status_vector, 48, 0, 0);
// msg 48 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, NULL, NULL, NULL, NULL);
// msg 72 committing data for relation %s
COMMIT
// existing ON_ERROR continues past error, beck
ON_ERROR
/* Fix for bug_no 8055:
don't throw away the database just because an index
could not be made */
ISC_STATUS error_code;
while (error_code = tdgbl->status_vector[1])
{
switch (error_code)
{
case 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, NULL, NULL, NULL, NULL);
// msg 240 Index \"%s\" failed to activate because:
if ( error_code == isc_no_dup )
{
BURP_print(241, NULL, NULL, NULL, NULL, NULL);
// msg 241 The unique index has duplicate values or NULLs
BURP_print(242, NULL, NULL, NULL, NULL, NULL);
// msg 242 Delete or Update duplicate values or NULLs, and activate index with
}
else
{
BURP_print(244, NULL, NULL, NULL, NULL, NULL);
// msg 244 Not enough disk space to create the sort file for an index
BURP_print(245, NULL, NULL, NULL, NULL, NULL);
// msg 245 Set the TMP environment variable to a directory on a filesystem that does have enough space, and activate index with
}
BURP_print(243, index_name, NULL, NULL, NULL, NULL);
/* msg 243 ALTER INDEX \"%s\" ACTIVE; */
END_MODIFY;
END_FOR;
// don't bring the database on-line
flag_on_line = false;
// commit one more time
COMMIT
ON_ERROR
continue;
END_ERROR
break;
default:
BURP_print (69, relation->rel_name, NULL, NULL, NULL, NULL);
// msg 69 commit failed on relation %s
BURP_print_status (tdgbl->status_vector);
ROLLBACK;
ON_ERROR
general_on_error ();
END_ERROR;
break;
} // end of switch
} // end of while
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
BURP_verbose (107, (void*) (IPTR) records, NULL, NULL, NULL, NULL);
// msg 107 %ld records restored
return record;
}
bool get_exception(void)
{
/**************************************
*
* g e t _ e x c e p t i o n
*
**************************************
*
* Functional description
* Reconstruct a exception.
*
**************************************/
ATT_TYPE attribute;
TEXT temp[GDS_NAME_LEN];
ULONG l;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_exception_req_handle1)
X IN RDB$EXCEPTIONS
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$MESSAGE.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_exception_name:
l = GET_TEXT(X.RDB$EXCEPTION_NAME);
MISC_terminate (X.RDB$EXCEPTION_NAME, temp, l, sizeof(temp));
BURP_verbose (199, temp, NULL, NULL, NULL, NULL);
// msg 199 restoring exception %s
break;
case att_exception_description:
get_misc_blob (&X.RDB$DESCRIPTION, 0, false);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_exception_description2:
get_source_blob (&X.RDB$DESCRIPTION, false);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_exception_msg:
GET_TEXT(X.RDB$MESSAGE);
X.RDB$MESSAGE.NULL = FALSE;
break;
default:
bad_attribute (scan_next_attr, attribute, 89);
// msg 89 function
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
burp_fld* get_field (burp_rel* relation)
{
/**************************************
*
* g e t _ f i e l d
*
**************************************
*
* Functional description
* Reconstruct a local field.
*
**************************************/
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
// 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.NULL = TRUE;
X.RDB$COMPLEX_NAME.NULL = TRUE;
X.RDB$UPDATE_FLAG.NULL = TRUE;
X.RDB$DEFAULT_SOURCE.NULL = TRUE;
X.RDB$DEFAULT_VALUE.NULL = TRUE;
X.RDB$NULL_FLAG.NULL = TRUE;
X.RDB$COLLATION_ID.NULL = TRUE;
skip_init(&scan_next_attr);
while (get_attribute(&attribute, tdgbl) != att_end)
{
switch (skip_scan(&scan_next_attr), attribute)
{
case att_field_name:
field->fld_name_length = GET_TEXT(field->fld_name);
BURP_verbose (115, field->fld_name, NULL, NULL, NULL, NULL);
// msg 115 restoring field %s
strcpy (X.RDB$FIELD_NAME, field->fld_name);
break;
case att_field_source:
GET_TEXT(X.RDB$FIELD_SOURCE);
break;
case att_field_security_class:
GET_TEXT(X.RDB$SECURITY_CLASS);
X.RDB$SECURITY_CLASS.NULL = FALSE;
break;
case att_field_query_name:
GET_TEXT(X.RDB$QUERY_NAME);
X.RDB$QUERY_NAME.NULL = FALSE;
break;
case att_field_query_header:
X.RDB$QUERY_HEADER.NULL = FALSE;
get_source_blob (&X.RDB$QUERY_HEADER, global_tr);
break;
case att_field_edit_string:
GET_TEXT(X.RDB$EDIT_STRING);
X.RDB$EDIT_STRING.NULL = FALSE;
break;
case att_field_position:
X.RDB$FIELD_POSITION.NULL = FALSE;
X.RDB$FIELD_POSITION = (USHORT) get_numeric();
break;
case att_field_number:
field->fld_number = (USHORT) get_numeric();
break;
case att_field_type:
field->fld_type = (USHORT) get_numeric();
break;
case att_field_length:
field->fld_length = (USHORT) get_numeric();
break;
case att_field_scale:
field->fld_scale = (USHORT) get_numeric();
break;
case att_field_sub_type:
field->fld_sub_type = (USHORT) get_numeric();
break;
case att_field_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric();
X.RDB$SYSTEM_FLAG.NULL = FALSE;
break;
case att_view_context:
X.RDB$VIEW_CONTEXT = (USHORT) get_numeric();
X.RDB$VIEW_CONTEXT.NULL = FALSE;
break;
case att_field_computed_flag:
if (get_numeric())
field->fld_flags |= FLD_computed;
break;
case att_base_field:
GET_TEXT(X.RDB$BASE_FIELD);
X.RDB$BASE_FIELD.NULL = FALSE;
break;
case att_field_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (&X.RDB$DESCRIPTION, 1, global_tr);
break;
case att_field_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (&X.RDB$DESCRIPTION, global_tr);
break;
case att_field_complex_name:
GET_TEXT(X.RDB$COMPLEX_NAME);
X.RDB$COMPLEX_NAME.NULL = FALSE;
break;
case att_field_dimensions:
{
field->fld_dimensions = (USHORT) get_numeric();
field->fld_flags |= FLD_array;
USHORT n = field->fld_dimensions;
for (SLONG* rp = field->fld_ranges; n; rp += 2, n--)
{
if (get_attribute(&attribute, tdgbl) != att_field_range_low)
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
else
*rp = get_numeric();
if (get_attribute(&attribute, tdgbl) != att_field_range_high)
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
else
*(rp + 1) = get_numeric();
}
}
break;
case att_field_update_flag:
X.RDB$UPDATE_FLAG.NULL = FALSE;
X.RDB$UPDATE_FLAG = (USHORT) get_numeric();
break;
case att_field_character_length:
field->fld_character_length = (USHORT) get_numeric();
break;
case att_field_default_source:
X.RDB$DEFAULT_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$DEFAULT_SOURCE, global_tr);
break;
case att_field_default_value:
X.RDB$DEFAULT_VALUE.NULL = FALSE;
get_blr_blob (&X.RDB$DEFAULT_VALUE, global_tr);
break;
case att_field_null_flag:
if (tdgbl->gbl_sw_novalidity) {
get_numeric(); // skip
}
else {
X.RDB$NULL_FLAG.NULL = FALSE;
X.RDB$NULL_FLAG = (USHORT) get_numeric();
}
break;
case att_field_character_set:
field->fld_character_set_id = (USHORT) get_numeric();
break;
case att_field_collation_id:
field->fld_collation_id = (USHORT) get_numeric();
X.RDB$COLLATION_ID.NULL = FALSE;
X.RDB$COLLATION_ID = field->fld_collation_id;
break;
default:
bad_attribute (scan_next_attr, attribute, 84);
// msg 84 field
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return field;
}
bool get_field_dimensions()
{
/**************************************
*
* g e t _ f i e l d _ d i m e n s i o n s
*
**************************************
*
* Functional description
* Get array field dimensions in rdb$field_dimensions.
*
**************************************/
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_field_dimensions_req_handle1)
X IN RDB$FIELD_DIMENSIONS
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_field_name:
GET_TEXT(X.RDB$FIELD_NAME);
break;
case att_field_dimensions:
X.RDB$DIMENSION = (USHORT) get_numeric();
break;
case att_field_range_low:
X.RDB$LOWER_BOUND = get_numeric();
break;
case att_field_range_high:
X.RDB$UPPER_BOUND = get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_files()
{
/**************************************
*
* g e t _ f i l e s
*
**************************************
*
* Functional description
* Get any files that were stored; let
* somebody else worry about what to do with them.
*
**************************************/
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_files_req_handle1)
X IN RDB$FILES
X.RDB$FILE_FLAGS = 0;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_file_filename:
GET_TEXT(X.RDB$FILE_NAME);
BURP_verbose (116,
X.RDB$FILE_NAME, NULL, NULL, NULL, NULL);
// msg 116 restoring file %s
break;
case att_file_sequence:
X.RDB$FILE_SEQUENCE = (USHORT) get_numeric();
break;
case att_file_start:
X.RDB$FILE_START = get_numeric();
break;
case att_file_length:
X.RDB$FILE_LENGTH = get_numeric();
break;
case att_file_flags:
X.RDB$FILE_FLAGS |= get_numeric();
break;
case att_shadow_number:
X.RDB$SHADOW_NUMBER = (USHORT) get_numeric();
if (tdgbl->gbl_sw_kill && X.RDB$SHADOW_NUMBER)
X.RDB$FILE_FLAGS |= FILE_inactive;
break;
default:
bad_attribute (scan_next_attr, attribute, 85);
// msg 85 file
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_filter()
{
/**************************************
*
* g e t _ f i l t e r
*
**************************************
*
* Functional description
* Get a type definition in rdb$filters.
*
**************************************/
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_filter_req_handle1)
X IN RDB$FILTERS
X.RDB$DESCRIPTION.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_filter_name:
GET_TEXT(X.RDB$FUNCTION_NAME);
BURP_verbose (117, X.RDB$FUNCTION_NAME, NULL, NULL, NULL, NULL);
// msg 117 restoring filter %s
break;
case att_filter_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (&X.RDB$DESCRIPTION, 1, false);
break;
case att_filter_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (&X.RDB$DESCRIPTION, false);
break;
case att_filter_module_name:
GET_TEXT(X.RDB$MODULE_NAME);
break;
case att_filter_entrypoint:
GET_TEXT(X.RDB$ENTRYPOINT);
break;
case att_filter_input_sub_type:
X.RDB$INPUT_SUB_TYPE = (USHORT) get_numeric();
break;
case att_filter_output_sub_type:
X.RDB$OUTPUT_SUB_TYPE = (USHORT) get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 87);
// msg 87 filter
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_function()
{
/**************************************
*
* g e t _ f u n c t i o n
*
**************************************
*
* Functional description
* Reconstruct a function.
*
**************************************/
ATT_TYPE attribute;
GDS_NAME function_name;
TEXT temp[GDS_NAME_LEN];
SSHORT l;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_function_req_handle1)
X IN RDB$FUNCTIONS
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_function_name:
l = GET_TEXT(X.RDB$FUNCTION_NAME);
MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
BURP_verbose (118, temp, NULL, NULL, NULL, NULL);
// msg 118 restoring function %s
break;
case att_function_description:
get_misc_blob (&X.RDB$DESCRIPTION, 0, false);
break;
case att_function_description2:
get_source_blob (&X.RDB$DESCRIPTION, false);
break;
case att_function_module_name:
GET_TEXT(X.RDB$MODULE_NAME);
break;
case att_function_entrypoint:
GET_TEXT(X.RDB$ENTRYPOINT);
break;
case att_function_return_arg:
X.RDB$RETURN_ARGUMENT = (USHORT) get_numeric();
break;
case att_function_query_name:
GET_TEXT(X.RDB$QUERY_NAME);
break;
case att_function_type:
X.RDB$FUNCTION_TYPE = (USHORT) get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 89);
// msg 89 function
break;
}
}
strcpy (function_name, X.RDB$FUNCTION_NAME);
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
// at the end of args for a function is the rec_function_end marker
while (get(tdgbl) == rec_function_arg)
get_function_arg();
return true;
}
void get_function_arg()
{
/**************************************
*
* g e t _ f u n c t i o n _ a r g
*
**************************************
*
* Functional description
* Reconstruct function argument.
*
**************************************/
ATT_TYPE attribute;
SSHORT l;
TEXT temp[GDS_NAME_LEN];
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
if (tdgbl->RESTORE_format >= 6)
{
// with RDB$FIELD_PRECISION
STORE (REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1)
X IN RDB$FUNCTION_ARGUMENTS
X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
X.RDB$CHARACTER_SET_ID.NULL = TRUE;
X.RDB$FIELD_PRECISION.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_functionarg_name:
l = GET_TEXT(X.RDB$FUNCTION_NAME);
MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
BURP_verbose (119, temp, NULL, NULL, NULL, NULL);
// msg 119 restoring argument for function %s
break;
case att_functionarg_position:
X.RDB$ARGUMENT_POSITION = (USHORT) get_numeric();
break;
case att_functionarg_mechanism:
X.RDB$MECHANISM = (USHORT) get_numeric();
break;
case att_functionarg_field_type:
X.RDB$FIELD_TYPE = (USHORT) get_numeric();
break;
case att_functionarg_field_scale:
X.RDB$FIELD_SCALE = (USHORT) get_numeric();
break;
case att_functionarg_field_length:
X.RDB$FIELD_LENGTH = (USHORT) get_numeric();
break;
case att_functionarg_field_sub_type:
X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric();
break;
case att_functionarg_character_set:
X.RDB$CHARACTER_SET_ID.NULL = FALSE;
X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric();
break;
case att_functionarg_field_precision:
X.RDB$FIELD_PRECISION.NULL = FALSE;
X.RDB$FIELD_PRECISION = (USHORT) get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 90);
// msg 90 function argument
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
else
{
// without RDB$FIELD_PRECISION
STORE (REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1)
X IN RDB$FUNCTION_ARGUMENTS
X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
X.RDB$CHARACTER_SET_ID.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_functionarg_name:
l = GET_TEXT(X.RDB$FUNCTION_NAME);
MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
BURP_verbose (119, temp, NULL, NULL, NULL, NULL);
// msg 119 restoring argument for function %s
break;
case att_functionarg_position:
X.RDB$ARGUMENT_POSITION = (USHORT) get_numeric();
break;
case att_functionarg_mechanism:
X.RDB$MECHANISM = (USHORT) get_numeric();
break;
case att_functionarg_field_type:
X.RDB$FIELD_TYPE = (USHORT) get_numeric();
break;
case att_functionarg_field_scale:
X.RDB$FIELD_SCALE = (USHORT) get_numeric();
break;
case att_functionarg_field_length:
X.RDB$FIELD_LENGTH = (USHORT) get_numeric();
break;
case att_functionarg_field_sub_type:
X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric();
break;
case att_functionarg_character_set:
X.RDB$CHARACTER_SET_ID.NULL = FALSE;
X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 90);
// msg 90 function argument
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
}
bool get_generator()
{
/**************************************
*
* g e t _ g e n e r a t o r
*
**************************************
*
* Functional description
* Pick up a gen-id. Like most things, there is ughly history.
* In the modern world, gen_id are free floating records. In the
* bad old days they were attributes of relations. Handle both
* nicely.
*
**************************************/
SINT64 value = 0;
BASED_ON RDB$GENERATORS.RDB$GENERATOR_NAME name;
name[0] = 0; // just in case.
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_gen_generator:
GET_TEXT(name);
break;
case att_gen_value:
// IB v5 or earlier, gen_id value is an SLONG
value = (SINT64) get_numeric();
break;
case att_gen_value_int64:
// IB v6 or later, gen_id value is an SINT64
value = get_int64();
break;
default:
bad_attribute (scan_next_attr, attribute, 93);
// msg 93 index
break;
}
}
store_blr_gen_id (name, value);
return true;
}
bool get_global_field()
{
/**************************************
*
* 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;
TGBL tdgbl = GET_THREAD_DATA;
GFLD gfield = NULL;
if (tdgbl->RESTORE_format >= 6)
{
// with rdb$field_precision
STORE (REQUEST_HANDLE tdgbl->handles_get_global_field_req_handle1)
X IN RDB$FIELDS
X.RDB$FIELD_SCALE = X.RDB$SEGMENT_LENGTH = 0;
X.RDB$CHARACTER_SET_ID = X.RDB$COLLATION_ID = 0;
X.RDB$FIELD_SUB_TYPE = 0;
X.RDB$COMPUTED_BLR.NULL = TRUE;
X.RDB$COMPUTED_SOURCE.NULL = TRUE;
X.RDB$QUERY_NAME.NULL = TRUE;
X.RDB$EDIT_STRING.NULL = TRUE;
X.RDB$QUERY_HEADER.NULL = TRUE;
X.RDB$MISSING_VALUE.NULL = TRUE;
X.RDB$DEFAULT_VALUE.NULL = TRUE;
X.RDB$VALIDATION_BLR.NULL = TRUE;
X.RDB$VALIDATION_SOURCE.NULL = TRUE;
X.RDB$SYSTEM_FLAG.NULL = TRUE;
X.RDB$NULL_FLAG.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$DIMENSIONS.NULL = TRUE;
X.RDB$EXTERNAL_LENGTH.NULL = TRUE;
X.RDB$EXTERNAL_TYPE.NULL = TRUE;
X.RDB$EXTERNAL_SCALE.NULL = TRUE;
X.RDB$SEGMENT_LENGTH.NULL = TRUE;
X.RDB$CHARACTER_LENGTH.NULL = TRUE;
X.RDB$MISSING_SOURCE.NULL = TRUE;
X.RDB$DEFAULT_SOURCE.NULL = TRUE;
X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
X.RDB$CHARACTER_SET_ID.NULL = TRUE;
X.RDB$COLLATION_ID.NULL = TRUE;
X.RDB$FIELD_PRECISION.NULL = TRUE;
memset (X.RDB$QUERY_NAME, ' ', sizeof(X.RDB$QUERY_NAME));
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_field_name:
l = GET_TEXT(X.RDB$FIELD_NAME);
MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp));
BURP_verbose (121, temp, NULL, NULL, NULL, NULL);
// msg 121 restoring global field %s
break;
case att_field_query_name:
GET_TEXT(X.RDB$QUERY_NAME);
X.RDB$QUERY_NAME.NULL = FALSE;
break;
case att_field_edit_string:
GET_TEXT(X.RDB$EDIT_STRING);
X.RDB$EDIT_STRING.NULL = FALSE;
break;
case att_field_query_header:
X.RDB$QUERY_HEADER.NULL = FALSE;
get_source_blob (&X.RDB$QUERY_HEADER, false);
break;
case att_field_type:
X.RDB$FIELD_TYPE = (USHORT) get_numeric();
break;
case att_field_length:
X.RDB$FIELD_LENGTH = (USHORT) get_numeric();
break;
case att_field_scale:
X.RDB$FIELD_SCALE = (USHORT) get_numeric();
X.RDB$FIELD_SCALE.NULL = FALSE;
break;
case att_field_sub_type:
X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric();
X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
break;
case att_field_segment_length:
X.RDB$SEGMENT_LENGTH = (USHORT) get_numeric();
if (X.RDB$SEGMENT_LENGTH)
X.RDB$SEGMENT_LENGTH.NULL = FALSE;
break;
case att_field_computed_blr:
X.RDB$COMPUTED_BLR.NULL = FALSE;
get_blr_blob (&X.RDB$COMPUTED_BLR, false);
break;
case att_field_computed_source:
X.RDB$COMPUTED_SOURCE.NULL = FALSE;
get_misc_blob (&X.RDB$COMPUTED_SOURCE, 1, false);
break;
case att_field_computed_source2:
X.RDB$COMPUTED_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$COMPUTED_SOURCE, false);
break;
case att_field_validation_blr:
if (tdgbl->gbl_sw_novalidity)
eat_blob();
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (GFLD)BURP_alloc_zero(sizeof(gfld));
get_blr_blob (&gfield->gfld_vb, true);
gfield->gfld_flags |= GFLD_validation_blr;
}
else
{
X.RDB$VALIDATION_BLR.NULL = FALSE;
get_blr_blob (&X.RDB$VALIDATION_BLR, false);
}
}
break;
case att_field_validation_source:
if (tdgbl->gbl_sw_novalidity)
eat_blob();
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (GFLD)BURP_alloc_zero(sizeof(gfld));
get_misc_blob (&gfield->gfld_vs, 0, true);
gfield->gfld_flags |= GFLD_validation_source;
}
else
{
X.RDB$VALIDATION_SOURCE.NULL = FALSE;
get_misc_blob (&X.RDB$VALIDATION_SOURCE, 0, false);
}
}
break;
case att_field_validation_source2:
if (tdgbl->gbl_sw_novalidity)
eat_blob();
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (GFLD)BURP_alloc_zero(sizeof(gfld));
get_source_blob (&gfield->gfld_vs2, true);
gfield->gfld_flags |= GFLD_validation_source2;
}
else
{
X.RDB$VALIDATION_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$VALIDATION_SOURCE, false);
}
}
break;
case att_field_missing_value:
X.RDB$MISSING_VALUE.NULL = FALSE;
get_blr_blob (&X.RDB$MISSING_VALUE, false);
break;
case att_field_default_value:
X.RDB$DEFAULT_VALUE.NULL = FALSE;
get_blr_blob (&X.RDB$DEFAULT_VALUE, false);
break;
case att_field_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric();
X.RDB$SYSTEM_FLAG.NULL = FALSE;
break;
case att_field_null_flag:
if (tdgbl->gbl_sw_novalidity) {
get_numeric(); // skip
}
else {
X.RDB$NULL_FLAG = (USHORT) get_numeric();
X.RDB$NULL_FLAG.NULL = FALSE;
}
break;
case att_field_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (&X.RDB$DESCRIPTION, 1, false);
break;
case att_field_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (&X.RDB$DESCRIPTION, false);
break;
case att_field_external_length:
X.RDB$EXTERNAL_LENGTH.NULL = FALSE;
X.RDB$EXTERNAL_LENGTH = (USHORT) get_numeric();
break;
case att_field_external_scale:
X.RDB$EXTERNAL_SCALE.NULL = FALSE;
X.RDB$EXTERNAL_SCALE = (USHORT) get_numeric();
break;
case att_field_external_type:
X.RDB$EXTERNAL_TYPE.NULL = FALSE;
X.RDB$EXTERNAL_TYPE = (USHORT) get_numeric();
break;
case att_field_dimensions:
X.RDB$DIMENSIONS.NULL = FALSE;
X.RDB$DIMENSIONS = (USHORT) get_numeric();
break;
case att_field_character_length:
X.RDB$CHARACTER_LENGTH.NULL = FALSE;
X.RDB$CHARACTER_LENGTH = (USHORT) get_numeric();
break;
case att_field_default_source:
X.RDB$DEFAULT_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$DEFAULT_SOURCE, false);
break;
case att_field_missing_source:
X.RDB$MISSING_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$MISSING_SOURCE, false);
break;
case att_field_character_set:
X.RDB$CHARACTER_SET_ID.NULL = FALSE;
X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric();
break;
case att_field_collation_id:
X.RDB$COLLATION_ID.NULL = FALSE;
X.RDB$COLLATION_ID = (USHORT) get_numeric();
break;
case att_field_precision:
X.RDB$FIELD_PRECISION.NULL = FALSE;
X.RDB$FIELD_PRECISION = (USHORT) get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 92);
// msg 92 global field
break;
}
}
if (X.RDB$FIELD_TYPE <= DTYPE_BLR_MAX)
{
l = gds_cvt_blr_dtype[X.RDB$FIELD_TYPE];
if (l = type_lengths[l])
X.RDB$FIELD_LENGTH = l;
}
if (gfield)
strcpy (gfield->gfld_name, X.RDB$FIELD_NAME);
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
else /* RESTORE_format < 6 */
{
// without rdb$field_precision
STORE (REQUEST_HANDLE tdgbl->handles_get_global_field_req_handle1)
X IN RDB$FIELDS
X.RDB$FIELD_SCALE = X.RDB$SEGMENT_LENGTH = 0;
X.RDB$CHARACTER_SET_ID = X.RDB$COLLATION_ID = 0;
X.RDB$FIELD_SUB_TYPE = 0;
X.RDB$COMPUTED_BLR.NULL = TRUE;
X.RDB$COMPUTED_SOURCE.NULL = TRUE;
X.RDB$QUERY_NAME.NULL = TRUE;
X.RDB$EDIT_STRING.NULL = TRUE;
X.RDB$QUERY_HEADER.NULL = TRUE;
X.RDB$MISSING_VALUE.NULL = TRUE;
X.RDB$DEFAULT_VALUE.NULL = TRUE;
X.RDB$VALIDATION_BLR.NULL = TRUE;
X.RDB$VALIDATION_SOURCE.NULL = TRUE;
X.RDB$SYSTEM_FLAG.NULL = TRUE;
X.RDB$NULL_FLAG.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$DIMENSIONS.NULL = TRUE;
X.RDB$EXTERNAL_LENGTH.NULL = TRUE;
X.RDB$EXTERNAL_TYPE.NULL = TRUE;
X.RDB$EXTERNAL_SCALE.NULL = TRUE;
X.RDB$SEGMENT_LENGTH.NULL = TRUE;
X.RDB$CHARACTER_LENGTH.NULL = TRUE;
X.RDB$MISSING_SOURCE.NULL = TRUE;
X.RDB$DEFAULT_SOURCE.NULL = TRUE;
X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
X.RDB$CHARACTER_SET_ID.NULL = TRUE;
X.RDB$COLLATION_ID.NULL = TRUE;
memset (X.RDB$QUERY_NAME, ' ', sizeof(X.RDB$QUERY_NAME));
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_field_name:
l = GET_TEXT(X.RDB$FIELD_NAME);
MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp));
BURP_verbose (121, temp, NULL, NULL, NULL, NULL);
// msg 121 restoring global field %s
break;
case att_field_query_name:
GET_TEXT(X.RDB$QUERY_NAME);
X.RDB$QUERY_NAME.NULL = FALSE;
break;
case att_field_edit_string:
GET_TEXT(X.RDB$EDIT_STRING);
X.RDB$EDIT_STRING.NULL = FALSE;
break;
case att_field_query_header:
X.RDB$QUERY_HEADER.NULL = FALSE;
get_source_blob (&X.RDB$QUERY_HEADER, false);
break;
case att_field_type:
X.RDB$FIELD_TYPE = (USHORT) get_numeric();
break;
case att_field_length:
X.RDB$FIELD_LENGTH = (USHORT) get_numeric();
break;
case att_field_scale:
X.RDB$FIELD_SCALE = (USHORT) get_numeric();
X.RDB$FIELD_SCALE.NULL = FALSE;
break;
case att_field_sub_type:
X.RDB$FIELD_SUB_TYPE = (USHORT) get_numeric();
X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
break;
case att_field_segment_length:
X.RDB$SEGMENT_LENGTH = (USHORT) get_numeric();
if (X.RDB$SEGMENT_LENGTH)
X.RDB$SEGMENT_LENGTH.NULL = FALSE;
break;
case att_field_computed_blr:
X.RDB$COMPUTED_BLR.NULL = FALSE;
get_blr_blob (&X.RDB$COMPUTED_BLR, false);
break;
case att_field_computed_source:
X.RDB$COMPUTED_SOURCE.NULL = FALSE;
get_misc_blob (&X.RDB$COMPUTED_SOURCE, 1, false);
break;
case att_field_computed_source2:
X.RDB$COMPUTED_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$COMPUTED_SOURCE, false);
break;
case att_field_validation_blr:
if (tdgbl->gbl_sw_novalidity)
eat_blob();
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (GFLD)BURP_alloc_zero(sizeof(gfld));
get_blr_blob (&gfield->gfld_vb, true);
gfield->gfld_flags |= GFLD_validation_blr;
}
else
{
X.RDB$VALIDATION_BLR.NULL = FALSE;
get_blr_blob (&X.RDB$VALIDATION_BLR, false);
}
}
break;
case att_field_validation_source:
if (tdgbl->gbl_sw_novalidity)
eat_blob();
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (GFLD)BURP_alloc_zero(sizeof(gfld));
get_misc_blob (&gfield->gfld_vs, 0, true);
gfield->gfld_flags |= GFLD_validation_source;
}
else
{
X.RDB$VALIDATION_SOURCE.NULL = FALSE;
get_misc_blob (&X.RDB$VALIDATION_SOURCE, 0, false);
}
}
break;
case att_field_validation_source2:
if (tdgbl->gbl_sw_novalidity)
eat_blob();
else
{
// if we are going against a V4.0 database,
// restore the global fields in 2 phases.
if (tdgbl->global_trans)
{
if (!gfield)
gfield = (GFLD)BURP_alloc_zero(sizeof(gfld));
get_source_blob (&gfield->gfld_vs2, true);
gfield->gfld_flags |= GFLD_validation_source2;
}
else
{
X.RDB$VALIDATION_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$VALIDATION_SOURCE, false);
}
}
break;
case att_field_missing_value:
X.RDB$MISSING_VALUE.NULL = FALSE;
get_blr_blob (&X.RDB$MISSING_VALUE, false);
break;
case att_field_default_value:
X.RDB$DEFAULT_VALUE.NULL = FALSE;
get_blr_blob (&X.RDB$DEFAULT_VALUE, false);
break;
case att_field_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric();
X.RDB$SYSTEM_FLAG.NULL = FALSE;
break;
case att_field_null_flag:
if (tdgbl->gbl_sw_novalidity) {
get_numeric(); // skip
}
else {
X.RDB$NULL_FLAG = (USHORT) get_numeric();
X.RDB$NULL_FLAG.NULL = FALSE;
}
break;
case att_field_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (&X.RDB$DESCRIPTION, 1, false);
break;
case att_field_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (&X.RDB$DESCRIPTION, false);
break;
case att_field_external_length:
X.RDB$EXTERNAL_LENGTH.NULL = FALSE;
X.RDB$EXTERNAL_LENGTH = (USHORT) get_numeric();
break;
case att_field_external_scale:
X.RDB$EXTERNAL_SCALE.NULL = FALSE;
X.RDB$EXTERNAL_SCALE = (USHORT) get_numeric();
break;
case att_field_external_type:
X.RDB$EXTERNAL_TYPE.NULL = FALSE;
X.RDB$EXTERNAL_TYPE = (USHORT) get_numeric();
break;
case att_field_dimensions:
X.RDB$DIMENSIONS.NULL = FALSE;
X.RDB$DIMENSIONS = (USHORT) get_numeric();
break;
case att_field_character_length:
X.RDB$CHARACTER_LENGTH.NULL = FALSE;
X.RDB$CHARACTER_LENGTH = (USHORT) get_numeric();
break;
case att_field_default_source:
X.RDB$DEFAULT_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$DEFAULT_SOURCE, false);
break;
case att_field_missing_source:
X.RDB$MISSING_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$MISSING_SOURCE, false);
break;
case att_field_character_set:
X.RDB$CHARACTER_SET_ID.NULL = FALSE;
X.RDB$CHARACTER_SET_ID = (USHORT) get_numeric();
break;
case att_field_collation_id:
X.RDB$COLLATION_ID.NULL = FALSE;
X.RDB$COLLATION_ID = (USHORT) get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 92);
// msg 92 global field
break;
}
}
if (X.RDB$FIELD_TYPE <= DTYPE_BLR_MAX)
{
l = gds_cvt_blr_dtype[X.RDB$FIELD_TYPE];
if (l = type_lengths[l])
X.RDB$FIELD_LENGTH = l;
}
if (gfield)
strcpy (gfield->gfld_name, X.RDB$FIELD_NAME);
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 (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;
TGBL tdgbl = GET_THREAD_DATA;
SSHORT count = 0, segments = 0;
STORE (REQUEST_HANDLE tdgbl->handles_get_index_req_handle1)
X IN RDB$INDICES
strcpy (X.RDB$RELATION_NAME, relation->rel_name);
X.RDB$UNIQUE_FLAG = 0;
if (!tdgbl->gbl_sw_deactivate_indexes)
X.RDB$INDEX_INACTIVE = FALSE;
else
X.RDB$INDEX_INACTIVE = TRUE;
X.RDB$INDEX_TYPE.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$FOREIGN_KEY.NULL = TRUE;
X.RDB$EXPRESSION_SOURCE.NULL = TRUE;
X.RDB$EXPRESSION_BLR.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_index_name:
GET_TEXT(X.RDB$INDEX_NAME);
strcpy (index_name, X.RDB$INDEX_NAME);
BURP_verbose (122, X.RDB$INDEX_NAME, NULL, NULL, NULL, NULL);
break;
case att_segment_count:
X.RDB$SEGMENT_COUNT = segments = (USHORT) get_numeric();
break;
case att_index_unique_flag:
X.RDB$UNIQUE_FLAG = (USHORT) get_numeric();
break;
case att_index_inactive:
X.RDB$INDEX_INACTIVE = (USHORT) get_numeric();
// Defer foreign key index activation
// Modified by Toni Martir, all index deferred when verbose
if (tdgbl->gbl_sw_verbose)
{
if (X.RDB$INDEX_INACTIVE == FALSE)
X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE;
}
else
{
if (X.RDB$INDEX_INACTIVE == FALSE && foreign_index)
X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE;
}
if (tdgbl->gbl_sw_deactivate_indexes)
X.RDB$INDEX_INACTIVE = TRUE;
break;
case att_index_type:
X.RDB$INDEX_TYPE.NULL = FALSE;
X.RDB$INDEX_TYPE = (USHORT) get_numeric();
break;
case att_index_field_name:
STORE (REQUEST_HANDLE tdgbl->handles_get_index_req_handle2)
Y IN RDB$INDEX_SEGMENTS
GET_TEXT(Y.RDB$FIELD_NAME);
strcpy (Y.RDB$INDEX_NAME, X.RDB$INDEX_NAME);
Y.RDB$FIELD_POSITION = count++;
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
break;
case att_index_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (&X.RDB$DESCRIPTION, 0, false);
break;
case att_index_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (&X.RDB$DESCRIPTION, false);
break;
case att_index_expression_source:
X.RDB$EXPRESSION_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$EXPRESSION_SOURCE, false);
break;
case att_index_expression_blr:
X.RDB$EXPRESSION_BLR.NULL = FALSE;
get_blr_blob (&X.RDB$EXPRESSION_BLR, false);
break;
case att_index_foreign_key:
foreign_index = true;
// Defer foreign key index activation
if (X.RDB$INDEX_INACTIVE == FALSE)
X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE;
if (tdgbl->gbl_sw_deactivate_indexes)
X.RDB$INDEX_INACTIVE = TRUE;
X.RDB$FOREIGN_KEY.NULL = FALSE;
GET_TEXT(X.RDB$FOREIGN_KEY);
break;
default:
bad_attribute (scan_next_attr, attribute, 93);
// msg 93 index
break;
}
}
count = 0;
FOR (REQUEST_HANDLE tdgbl->handles_get_index_req_handle3)
RFR IN RDB$RELATION_FIELDS CROSS I_S IN RDB$INDEX_SEGMENTS
OVER RDB$FIELD_NAME WITH I_S.RDB$INDEX_NAME = index_name AND
RFR.RDB$RELATION_NAME = relation->rel_name
count++;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
if (count != segments)
{
FOR (REQUEST_HANDLE tdgbl->handles_get_index_req_handle4)
I_S IN RDB$INDEX_SEGMENTS WITH I_S.RDB$INDEX_NAME = index_name
ERASE I_S;
ON_ERROR
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
return false;
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
void get_misc_blob (ISC_QUAD *blob_id,
USHORT sub_type,
bool glb_trans)
{
/**************************************
*
* g e t _ m i s c _ b l o b
*
**************************************
*
* Functional description
* Read blob attributes and copy data from input file to nice,
* shiney, new blob.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
TGBL tdgbl = GET_THREAD_DATA;
const USHORT length = (USHORT)get_numeric();
// Create new blob
isc_tr_handle local_trans;
if (glb_trans && tdgbl->global_trans)
local_trans = tdgbl->global_trans;
else
local_trans = gds_trans;
FRBRD* blob = NULL;
USHORT bpb_length = 0;
UCHAR* bpb = NULL;
if (isc_create_blob2 (status_vector, &DB, &local_trans, &blob,
blob_id, bpb_length,
reinterpret_cast<const SCHAR*>(bpb)))
{
BURP_error_redirect (status_vector, 37, 0, 0);
// msg 37 isc_create_blob failed
}
// Allocate blob buffer if static buffer is too short
UCHAR static_buffer[1024];
UCHAR* buffer;
if (!length || length <= (USHORT)(sizeof(static_buffer)) )
buffer = static_buffer;
else
buffer = BURP_alloc (length);
if (length)
{
get_block(tdgbl, buffer, length);
}
if (isc_put_segment (status_vector, &blob, length,
reinterpret_cast<const SCHAR*>(buffer)))
{
BURP_error_redirect (status_vector, 38, 0, 0);
// msg 38 isc_put_segment failed
}
if (isc_close_blob (status_vector, &blob))
BURP_error_redirect (status_vector, 23, 0, 0);
// msg 23 isc_close_blob failed
if (buffer != static_buffer)
BURP_free (buffer);
}
SLONG get_numeric()
{
/**************************************
*
* 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((TEXT*) value, sizeof(value));
return isc_vax_integer ((SCHAR *) value, length);
}
SINT64 get_int64()
{
/**************************************
*
* 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 ((TEXT *) value, sizeof(value));
return isc_portable_integer ((UCHAR *) value, length);
}
bool get_procedure()
{
/**************************************
*
* 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;
TGBL tdgbl = GET_THREAD_DATA;
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;
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_procedure_req_handle1)
X IN RDB$PROCEDURES
X.RDB$PROCEDURE_SOURCE.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$SECURITY_CLASS.NULL = TRUE;
X.RDB$OWNER_NAME.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_procedure_name:
l = GET_TEXT(X.RDB$PROCEDURE_NAME);
procedure->prc_name_length = l;
strcpy (procedure->prc_name, X.RDB$PROCEDURE_NAME);
MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof(temp));
BURP_verbose (195, temp, NULL, NULL, NULL, NULL);
// msg 195 restoring stored procedure %s
break;
case att_procedure_description:
get_misc_blob (&X.RDB$DESCRIPTION, 0, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_procedure_description2:
get_source_blob (&X.RDB$DESCRIPTION, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_procedure_source:
get_misc_blob (&X.RDB$PROCEDURE_SOURCE, 0, true);
X.RDB$PROCEDURE_SOURCE.NULL = FALSE;
break;
case att_procedure_source2:
get_source_blob (&X.RDB$PROCEDURE_SOURCE, true);
X.RDB$PROCEDURE_SOURCE.NULL = FALSE;
break;
case att_procedure_blr:
get_blr_blob (&X.RDB$PROCEDURE_BLR, true);
break;
case att_procedure_security_class:
GET_TEXT(X.RDB$SECURITY_CLASS);
X.RDB$SECURITY_CLASS.NULL = FALSE;
break;
case att_procedure_owner_name:
GET_TEXT(procedure->prc_owner);
break;
case att_procedure_inputs:
X.RDB$PROCEDURE_INPUTS = (USHORT) get_numeric();
if (X.RDB$PROCEDURE_INPUTS == 0)
X.RDB$PROCEDURE_INPUTS.NULL = TRUE;
else
X.RDB$PROCEDURE_INPUTS.NULL = FALSE;
break;
case att_procedure_outputs:
X.RDB$PROCEDURE_OUTPUTS = (USHORT) get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 89);
// msg 89 function
break;
}
}
strcpy (procedure_name, X.RDB$PROCEDURE_NAME);
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
// at the end of prms for a procedure is the rec_procedure_end marker
while (get(tdgbl) == rec_procedure_prm)
get_procedure_prm (procedure_name);
return true;
}
bool get_procedure_prm (GDS_NAME procptr)
{
/**************************************
*
* g e t _ p r o c e d u r e _ p r m
*
**************************************
*
* Functional description
* Reconstruct stored procedure parameter.
* Use the global_trans so we don't have to commit
* until after the indices are activated. This
* will allow us to use a PLAN in a SP.
*
**************************************/
ATT_TYPE attribute;
SSHORT l;
TEXT temp[GDS_NAME_LEN];
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
isc_tr_handle local_trans =
tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_procedure_prm_req_handle1)
X IN RDB$PROCEDURE_PARAMETERS
X.RDB$DESCRIPTION.NULL = TRUE;
strcpy (X.RDB$PROCEDURE_NAME, procptr);
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_procedureprm_name:
l = GET_TEXT(X.RDB$PARAMETER_NAME);
MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof(temp));
BURP_verbose (196, temp, NULL, NULL, NULL, NULL);
// msg 196 restoring parameter %s for stored procedure
break;
case att_procedureprm_type:
X.RDB$PARAMETER_TYPE= (USHORT) get_numeric();
break;
case att_procedureprm_number:
X.RDB$PARAMETER_NUMBER= (USHORT) get_numeric();
break;
case att_procedureprm_field_source:
GET_TEXT(X.RDB$FIELD_SOURCE);
break;
case att_procedureprm_description:
get_misc_blob (&X.RDB$DESCRIPTION, 0, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
case att_procedureprm_description2:
get_source_blob (&X.RDB$DESCRIPTION, true);
X.RDB$DESCRIPTION.NULL = FALSE;
break;
default:
bad_attribute (scan_next_attr, attribute, 90);
// msg 90 function argument
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_ref_constraint()
{
/**************************************
*
* g e t _ r e f _ c o n s t r a i n t
*
**************************************
*
* Functional description
* Restore data for referential constraints.
*
**************************************/
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_ref_constraint_req_handle1)
X IN RDB$REF_CONSTRAINTS
X.RDB$CONSTRAINT_NAME.NULL = TRUE;
X.RDB$CONST_NAME_UQ.NULL = TRUE;
X.RDB$MATCH_OPTION.NULL = TRUE;
X.RDB$UPDATE_RULE.NULL = TRUE;
X.RDB$DELETE_RULE.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_ref_constraint_name:
X.RDB$CONSTRAINT_NAME.NULL = FALSE;
GET_TEXT(X.RDB$CONSTRAINT_NAME);
break;
case att_ref_unique_const_name:
X.RDB$CONST_NAME_UQ.NULL = FALSE;
GET_TEXT(X.RDB$CONST_NAME_UQ);
break;
case att_ref_match_option:
X.RDB$MATCH_OPTION.NULL = FALSE;
GET_TEXT(X.RDB$MATCH_OPTION);
break;
case att_ref_update_rule:
X.RDB$UPDATE_RULE.NULL = FALSE;
GET_TEXT(X.RDB$UPDATE_RULE);
break;
case att_ref_delete_rule:
X.RDB$DELETE_RULE.NULL = FALSE;
GET_TEXT(X.RDB$DELETE_RULE);
break;
default:
bad_attribute (scan_next_attr, attribute, 208);
// msg 208 relation constraint
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_relation()
{
/**************************************
*
* g e t _ r e l a t i o n
*
**************************************
*
* Functional description
* Write relation meta-data and data.
* Use the default transaction for RELATIONS,
* and use the global_trans for VIEWS. This
* enables us to have views of SP and views
* with plans. Assume it is a view if it has
* RDB$VIEW_BLR, also assume RDB$VIEW_BLR is
* the first blob in the backup file.
*
*
**************************************/
TEXT temp[GDS_NAME_LEN];
SSHORT l;
ATT_TYPE attribute;
rec_type record;
scan_attr_t scan_next_attr;
SLONG rel_flags = 0, sys_flag = 0;
bool rel_flags_null = true, sys_flag_null = true;
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;
TGBL tdgbl = GET_THREAD_DATA;
// Pick up relation attributes
burp_rel* relation = (burp_rel*) BURP_alloc_zero (sizeof(burp_rel));
relation->rel_next = tdgbl->relations;
tdgbl->relations = relation;
/*
STORE (REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1)
X IN RDB$RELATIONS
X.RDB$SYSTEM_FLAG.NULL = TRUE;
X.RDB$FLAGS.NULL = TRUE;
X.RDB$SECURITY_CLASS.NULL = TRUE;
X.RDB$VIEW_BLR.NULL = TRUE;
X.RDB$VIEW_SOURCE.NULL = TRUE;
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$RUNTIME.NULL = TRUE;
X.RDB$EXTERNAL_DESCRIPTION.NULL = TRUE;
*/
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_relation_name:
l = GET_TEXT(relation->rel_name);
relation->rel_name_length = l;
MISC_terminate (relation->rel_name, temp, l, sizeof(temp));
BURP_verbose (167, temp, NULL, NULL, NULL, NULL);
// msg 167 restoring relation %s
break;
case att_relation_security_class:
sec_class_null = false;
GET_TEXT(sec_class);
break;
case att_relation_view_blr:
view_blr_null = false;
get_blr_blob(&view_blr, true);
relation->rel_flags |= REL_view;
break;
case att_relation_view_source:
view_src_null = false;
get_misc_blob (&view_src, 1, !view_blr_null);
break;
case att_relation_view_source2:
view_src_null = false;
get_source_blob(&view_src, !view_blr_null);
break;
case att_relation_description:
rel_desc_null = false;
get_misc_blob(&rel_desc, 1, !view_blr_null);
break;
case att_relation_description2:
rel_desc_null = false;
get_source_blob(&rel_desc, !view_blr_null);
break;
case att_relation_flags:
rel_flags_null = false;
rel_flags = get_numeric();
break;
case att_relation_system_flag:
sys_flag_null = false;
sys_flag = get_numeric();
break;
case att_relation_ext_description:
ext_desc_null = false;
get_misc_blob(&ext_desc, 1, !view_blr_null);
break;
case att_relation_ext_description2:
ext_desc_null = false;
get_source_blob(&ext_desc, !view_blr_null);
break;
case att_relation_owner_name:
GET_TEXT(relation->rel_owner);
break;
case att_relation_ext_file_name:
ext_file_name_null = false;
GET_TEXT(ext_file_name);
break;
default:
bad_attribute (scan_next_attr, attribute, 111);
// msg 111 relation
break;
}
}
// If this is a view and there is a global transaction then use it
isc_tr_handle local_trans;
if (view_blr_null || !tdgbl->global_trans)
local_trans = gds_trans;
else
local_trans = tdgbl->global_trans;
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1)
X IN RDB$RELATIONS
X.RDB$SYSTEM_FLAG.NULL = sys_flag_null;
X.RDB$FLAGS.NULL = rel_flags_null;
X.RDB$SECURITY_CLASS.NULL = sec_class_null;
X.RDB$VIEW_BLR.NULL = view_blr_null;
X.RDB$VIEW_SOURCE.NULL = view_src_null;
X.RDB$DESCRIPTION.NULL = rel_desc_null;
X.RDB$RUNTIME.NULL = TRUE;
X.RDB$EXTERNAL_DESCRIPTION.NULL = ext_desc_null;
X.RDB$EXTERNAL_FILE.NULL = ext_file_name_null;
X.RDB$SYSTEM_FLAG = (USHORT) sys_flag;
X.RDB$FLAGS = (USHORT) rel_flags;
X.RDB$VIEW_BLR = view_blr;
X.RDB$VIEW_SOURCE = view_src;
X.RDB$DESCRIPTION = rel_desc;
X.RDB$EXTERNAL_DESCRIPTION = ext_desc;
strcpy(X.RDB$SECURITY_CLASS, sec_class);
strcpy(X.RDB$RELATION_NAME, relation->rel_name);
strcpy(X.RDB$EXTERNAL_FILE, ext_file_name);
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
// Eat up misc. records
burp_fld* field = NULL;
burp_fld** ptr = &relation->rel_fields;
while (get_record(&record, tdgbl) != rec_data)
{
switch (record)
{
case rec_relation_end:
if (tdgbl->gbl_sw_incremental)
{
BURP_verbose (170, relation->rel_name, NULL, NULL, NULL, NULL);
// msg 170: committing metadata for relation %s
COMMIT
// existing ON_ERROR continues past error, beck
ON_ERROR
BURP_print (171, relation->rel_name, NULL, NULL, NULL, NULL);
// msg 171: error committing metadata for relation %s
BURP_print_status (tdgbl->status_vector);
ROLLBACK;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
return true;
case rec_field:
*ptr = field = get_field (relation);
if (!field)
return false;
ptr = &field->fld_next;
break;
case rec_view:
get_view (relation);
break;
default:
BURP_error(43, true, isc_arg_number, (void*) (IPTR) record,
0, NULL, 0, NULL, 0, NULL, 0, NULL);
// 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, NULL, NULL, NULL, NULL, NULL);
// msg 68 committing meta data
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
get_data (relation);
return true;
}
bool get_rel_constraint()
{
/**************************************
*
* g e t _ r e l _ c o n s t r a i n t
*
**************************************
*
* Functional description
* Restore data for relation constraints.
*
**************************************/
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_rel_constraint_req_handle1)
X IN RDB$RELATION_CONSTRAINTS
X.RDB$CONSTRAINT_NAME.NULL = TRUE;
X.RDB$CONSTRAINT_TYPE.NULL = TRUE;
X.RDB$RELATION_NAME.NULL = TRUE;
X.RDB$DEFERRABLE.NULL = TRUE;
X.RDB$INITIALLY_DEFERRED.NULL = TRUE;
X.RDB$INDEX_NAME.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_rel_constraint_name:
X.RDB$CONSTRAINT_NAME.NULL = FALSE;
GET_TEXT(X.RDB$CONSTRAINT_NAME);
break;
case att_rel_constraint_type:
X.RDB$CONSTRAINT_TYPE.NULL = FALSE;
GET_TEXT(X.RDB$CONSTRAINT_TYPE);
break;
case att_rel_constraint_rel_name:
X.RDB$RELATION_NAME.NULL = FALSE;
GET_TEXT(X.RDB$RELATION_NAME);
break;
case att_rel_constraint_defer:
X.RDB$DEFERRABLE.NULL = FALSE;
GET_TEXT(X.RDB$DEFERRABLE);
break;
case att_rel_constraint_init:
X.RDB$INITIALLY_DEFERRED.NULL = FALSE;
GET_TEXT(X.RDB$INITIALLY_DEFERRED);
break;
case att_rel_constraint_index:
X.RDB$INDEX_NAME.NULL = FALSE;
GET_TEXT(X.RDB$INDEX_NAME);
break;
default:
bad_attribute (scan_next_attr, attribute, 208);
// msg 208 relation constraint
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_relation_data()
{
/**************************************
*
* 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;
TGBL tdgbl = GET_THREAD_DATA;
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 (name);
break;
default:
bad_attribute (scan_next_attr, attribute, 111);
// msg 111 relation
break;
}
}
if (!relation)
BURP_error_redirect (NULL, 49, 0, 0);
// msg 49 no relation name for data
// Eat up misc. records
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 (relation);
// get_data does a GET_RECORD
break;
case rec_gen_id:
gen_id = get_numeric();
store_blr_gen_id (name, gen_id);
get_record(&record, tdgbl);
break;
case rec_index:
get_index (relation);
get_record(&record, tdgbl);
break;
case rec_trigger: // old style trigger
get_trigger_old (relation);
get_record(&record, tdgbl);
break;
default:
bad_attribute (scan_next_attr, attribute, 111);
// msg 111 relation
get_record(&record, tdgbl);
break;
}
}
}
bool get_sql_roles()
{
/**************************************
*
* g e t _ s q l _ r o l e s
*
**************************************
*
* Functional description
* Restore data for SQL roles
*
**************************************/
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TEXT temp[GDS_NAME_LEN];
SSHORT l;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_sql_roles_req_handle1)
X IN RDB$ROLES
X.RDB$ROLE_NAME.NULL = TRUE;
X.RDB$OWNER_NAME.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_role_name:
X.RDB$ROLE_NAME.NULL = FALSE;
l = GET_TEXT(X.RDB$ROLE_NAME);
MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp));
/************************************************
**
** msg 251, restoring SQL role: %s
**
*************************************************/
BURP_verbose (251, temp, NULL, NULL, NULL, NULL);
break;
case att_role_owner_name:
X.RDB$OWNER_NAME.NULL = FALSE;
GET_TEXT(X.RDB$OWNER_NAME);
break;
default:
/*************************************************
**
** msg 250, Bad attribute for restoring SQL role
**
**************************************************/
bad_attribute (scan_next_attr, attribute, 250);
break;
}
}
END_STORE
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool is_ascii_name (const TEXT *name, const SSHORT len)
{
/**************************************
*
* i s _ a s c i i _ n a m e
*
**************************************
*
* Functional description
* Check if the input text is valid ASCII name
*
**************************************/
SSHORT i = 0;
while (i < len &&
( (name[i] >= 'A' && name[i] <= 'Z') ||
(name[i] >= '0' && name[i] <= '9') ||
name[i] == '_' || name[i] == '$' ) )
{
++i;
}
return (i == len);
}
bool get_security_class()
{
/**************************************
*
* g e t _ s e c u r i t y _ c l a s s
*
**************************************
*
* Functional description
* Restore a security class record including access control list.
*
**************************************/
ATT_TYPE attribute;
TEXT temp[GDS_NAME_LEN];
SSHORT l = 0;
scan_attr_t scan_next_attr;
bool is_valid_sec_class = false;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_security_class_req_handle1)
X IN RDB$SECURITY_CLASSES
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
X.RDB$DESCRIPTION.NULL = TRUE;
switch (attribute)
{
case att_class_security_class:
l = GET_TEXT(X.RDB$SECURITY_CLASS);
/* Bug fix for bug_no 7299: There was a V3 bug that inserted
garbage security class entry when doing GBAK. In order to
restore the V3 gbak file with this bad security entry to
V4 database. We should check if the security class is a
valid ASCII name. If not, skip this entry by setting
'is_valid_sec_class' to false.
*/
is_valid_sec_class = is_ascii_name(X.RDB$SECURITY_CLASS, l);
if (!is_valid_sec_class)
{
MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp));
BURP_print (234, temp, NULL, NULL, NULL, NULL);
// msg 234 Skipped bad security class entry: %s
break;
}
MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp));
BURP_verbose (125, temp, NULL, NULL, NULL, NULL);
// msg 125 restoring security class %s
break;
case att_class_acl:
get_misc_blob (&X.RDB$ACL, 0, false);
break;
case att_class_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (&X.RDB$DESCRIPTION, 0, false);
break;
case att_class_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (&X.RDB$DESCRIPTION, false);
break;
default:
bad_attribute (scan_next_attr, attribute, 131);
// msg 131 security class
break;
}
}
// If the security class is not valid ASCII name, don't store it to the
// database. Simply return from here and the entry is discarded.
if (!is_valid_sec_class)
{
return true;
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
void get_source_blob (
ISC_QUAD *blob_id,
bool glb_trans)
{
/**************************************
*
* g e t _ s o u r c e _ b l o b
*
**************************************
*
* Functional description
* Read source blob and query header attributes and copy data from
* input file to nice, shiney, new blob.
*
**************************************/
ISC_STATUS_ARRAY status_vector;
TGBL tdgbl = GET_THREAD_DATA;
SLONG length = get_numeric();
// Create new blob
FRBRD* blob = NULL;
isc_tr_handle local_trans;
if (glb_trans && tdgbl->global_trans)
local_trans = tdgbl->global_trans;
else
local_trans = gds_trans;
if (isc_create_blob (status_vector, &DB, &local_trans, &blob, blob_id))
{
BURP_error_redirect (status_vector, 37, 0, 0);
// msg 37 isc_create_blob failed
}
// Allocate blob buffer if static buffer is too short
UCHAR static_buffer[1024];
UCHAR* buffer;
if (!length || length <= (USHORT)(sizeof(static_buffer)) )
buffer = static_buffer;
else
buffer = BURP_alloc (length);
while (length)
{
UCHAR* p = buffer;
while (*p++ = get(tdgbl))
length--;
--p;
--length; // -- or ++ ??? p is decremented, will have to test.
const USHORT seg_len = p - buffer;
if (isc_put_segment (status_vector, &blob, seg_len,
reinterpret_cast<const SCHAR*>(buffer)))
{
BURP_error_redirect (status_vector, 38, 0, 0);
// msg 38 isc_put_segment failed
}
}
if (isc_close_blob (status_vector, &blob))
BURP_error_redirect (status_vector, 23, 0, 0);
// msg 23 isc_close_blob failed
if (buffer != static_buffer)
BURP_free (buffer);
}
USHORT get_text (
TEXT *text,
ULONG length)
{
/**************************************
*
* g e t _ t e x t
*
**************************************
*
* Functional description
* Move a text attribute to a string and fill.
*
**************************************/
TGBL tdgbl = GET_THREAD_DATA;
const ULONG l = get(tdgbl);
if (length <= l)
BURP_error_redirect (NULL, 46, 0, 0);
// msg 46 string truncated
if (l)
text = (TEXT*) get_block(tdgbl, (UCHAR*) text, l);
*text = 0;
return (USHORT) l;
}
bool get_trigger_old (
burp_rel* relation)
{
/**************************************
*
* g e t _ t r i g g e r _ o l d
*
**************************************
*
* Functional description
* Get a trigger definition for a relation.
*
**************************************/
enum trig_t type;
ATT_TYPE attribute;
TEXT name[GDS_NAME_LEN];
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_trigger_old_req_handle1)
X IN RDB$TRIGGERS
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$TRIGGER_BLR.NULL = TRUE;
X.RDB$TRIGGER_SOURCE.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_trig_type:
type = (enum trig_t) get_numeric();
break;
case att_trig_blr:
X.RDB$TRIGGER_BLR.NULL = FALSE;
get_blr_blob (&X.RDB$TRIGGER_BLR, false);
break;
case att_trig_source:
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
get_misc_blob (&X.RDB$TRIGGER_SOURCE, 1, false);
break;
case att_trig_source2:
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$TRIGGER_SOURCE, false);
break;
default:
bad_attribute (scan_next_attr, attribute, 134);
// msg 134 trigger
break;
}
}
// fill in rest of attributes unique to new trigger format
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, NULL, NULL, NULL, NULL);
// msg 126 restoring trigger %s
strncpy (X.RDB$RELATION_NAME, relation->rel_name, GDS_NAME_LEN);
strcpy (name, X.RDB$TRIGGER_NAME);
X.RDB$TRIGGER_SEQUENCE = TRIGGER_SEQUENCE_DEFAULT;
X.RDB$SYSTEM_FLAG = 0; // restore as vanilla user type
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
if (tdgbl->gbl_sw_incremental)
{
COMMIT
// existing ON_ERROR continues past error, beck
ON_ERROR
BURP_print (94, name, NULL, NULL, NULL, NULL);
// msg 94 trigger %s is invalid
BURP_print_status (tdgbl->status_vector);
ROLLBACK;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
return true;
}
bool get_trigger()
{
/**************************************
*
* 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;
TGBL tdgbl = GET_THREAD_DATA;
isc_tr_handle local_trans =
tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_trigger_req_handle1)
X IN RDB$TRIGGERS
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$TRIGGER_BLR.NULL = TRUE;
X.RDB$TRIGGER_SOURCE.NULL = TRUE;
X.RDB$SYSTEM_FLAG.NULL = TRUE;
X.RDB$FLAGS.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_trig_type:
X.RDB$TRIGGER_TYPE = (USHORT) get_numeric();
break;
case att_trig_flags:
X.RDB$FLAGS = (USHORT) get_numeric();
X.RDB$FLAGS.NULL = FALSE;
break;
case att_trig_blr:
X.RDB$TRIGGER_BLR.NULL = FALSE;
get_blr_blob (&X.RDB$TRIGGER_BLR, true);
break;
case att_trig_source:
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
get_misc_blob (&X.RDB$TRIGGER_SOURCE, 1, true);
break;
case att_trig_source2:
X.RDB$TRIGGER_SOURCE.NULL = FALSE;
get_source_blob (&X.RDB$TRIGGER_SOURCE, true);
break;
case att_trig_name:
GET_TEXT(X.RDB$TRIGGER_NAME);
strcpy (name, X.RDB$TRIGGER_NAME);
BURP_verbose (126, X.RDB$TRIGGER_NAME, NULL, NULL, NULL, NULL);
// msg 126 restoring trigger %s
break;
case att_trig_relation_name:
GET_TEXT(X.RDB$RELATION_NAME);
break;
case att_trig_sequence:
X.RDB$TRIGGER_SEQUENCE = (USHORT) get_numeric();
break;
case att_trig_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (&X.RDB$DESCRIPTION, 1, true);
break;
case att_trig_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (&X.RDB$DESCRIPTION, true);
break;
case att_trig_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric();
X.RDB$SYSTEM_FLAG.NULL = FALSE;
break;
case att_trig_inactive:
X.RDB$TRIGGER_INACTIVE = (USHORT) get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 134);
// msg 134 trigger
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
if (tdgbl->gbl_sw_incremental)
{
COMMIT
// existing ON_ERROR continues past error, beck
ON_ERROR
BURP_print (94, name, NULL, NULL, NULL, NULL);
// msg 94 trigger %s is invalid
BURP_print_status (tdgbl->status_vector);
ROLLBACK;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
return true;
}
bool get_trigger_message()
{
/**************************************
*
* 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;
BASED_ON RDB$TRIGGER_MESSAGES.RDB$MESSAGE message;
TGBL tdgbl = GET_THREAD_DATA;
bool flag = false;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_trigmsg_name:
GET_TEXT(name);
flag = false;
FOR (REQUEST_HANDLE tdgbl->handles_get_trigger_message_req_handle1)
FIRST 1 X IN RDB$TRIGGERS WITH
X.RDB$SYSTEM_FLAG EQ 1 AND X.RDB$TRIGGER_NAME EQ name
flag = true;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
BURP_verbose (127, name, NULL, NULL, NULL, NULL);
// msg 127 restoring trigger message for %s
break;
case att_trigmsg_number:
number = (USHORT) get_numeric();
break;
case att_trigmsg_text:
GET_TEXT(message);
break;
default:
bad_attribute (scan_next_attr, attribute, 135);
// msg 135 trigger message
break;
}
}
if (flag)
return true;
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, NULL, NULL, NULL, NULL);
// msg 94 trigger %s is invalid
BURP_print_status (tdgbl->status_vector);
ROLLBACK;
ON_ERROR
general_on_error ();
END_ERROR;
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
return true;
}
bool get_type()
{
/**************************************
*
* g e t _ t y p e
*
**************************************
*
* Functional description
* Get a type definition in rdb$types.
*
**************************************/
ATT_TYPE attribute;
ULONG l;
TEXT temp[GDS_NAME_LEN];
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_get_type_req_handle1)
X IN RDB$TYPES
X.RDB$DESCRIPTION.NULL = TRUE;
X.RDB$SYSTEM_FLAG.NULL = TRUE;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_type_name:
l = GET_TEXT(X.RDB$TYPE_NAME);
break;
case att_type_type:
X.RDB$TYPE = (USHORT) get_numeric();
break;
case att_type_field_name:
GET_TEXT(X.RDB$FIELD_NAME);
break;
case att_type_description:
X.RDB$DESCRIPTION.NULL = FALSE;
get_misc_blob (&X.RDB$DESCRIPTION, 1, false);
break;
case att_type_description2:
X.RDB$DESCRIPTION.NULL = FALSE;
get_source_blob (&X.RDB$DESCRIPTION, false);
break;
case att_type_system_flag:
X.RDB$SYSTEM_FLAG = (USHORT) get_numeric();
X.RDB$SYSTEM_FLAG.NULL = FALSE;
break;
default:
bad_attribute (scan_next_attr, attribute, 136);
// msg 136 trigger type
break;
}
}
MISC_terminate (X.RDB$TYPE_NAME, temp, l, sizeof(temp));
BURP_verbose (128, temp, X.RDB$FIELD_NAME, NULL, NULL, NULL);
// msg 128 restoring type %s for field %s
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
bool get_user_privilege()
{
/**************************************
*
* 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;
BASED_ON RDB$USER_PRIVILEGES.RDB$RELATION_NAME relation_name;
BASED_ON RDB$USER_PRIVILEGES.RDB$FIELD_NAME field_name;
BASED_ON RDB$USER_PRIVILEGES.RDB$USER_TYPE user_type;
BASED_ON RDB$USER_PRIVILEGES.RDB$OBJECT_TYPE object_type;
TGBL tdgbl = GET_THREAD_DATA;
user_type = obj_user;
object_type = obj_relation;
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_priv_user:
// default USER_TYPE to USER
flags |= USER_PRIV_USER;
GET_TEXT(user);
BURP_verbose (123, user, NULL, NULL, NULL, NULL);
// msg 123 restoring privilege for user %s
break;
case att_priv_grantor:
flags |= USER_PRIV_GRANTOR;
GET_TEXT(grantor);
break;
case att_priv_privilege:
flags |= USER_PRIV_PRIVILEGE;
GET_TEXT(privilege);
break;
case att_priv_grant_option:
flags |= USER_PRIV_GRANT_OPTION;
grant_option = (USHORT) get_numeric();
break;
case att_priv_object_name:
flags |= USER_PRIV_OBJECT_NAME;
// default OBJECT_TYPE to RELATION
GET_TEXT(relation_name);
break;
case att_priv_field_name:
flags |= USER_PRIV_FIELD_NAME;
GET_TEXT(field_name);
break;
case att_priv_user_type:
flags |= USER_PRIV_USER_TYPE;
user_type = (USHORT) get_numeric();
break;
case att_priv_obj_type:
flags |= USER_PRIV_OBJECT_TYPE;
object_type = (USHORT) get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 105);
// msg 105 privilege
break;
}
}
// Check if object exists
isc_tr_handle local_trans = NULL;
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 (exists)
{
if (!local_trans)
local_trans = gds_trans;
STORE (TRANSACTION_HANDLE local_trans
REQUEST_HANDLE tdgbl->handles_get_user_privilege_req_handle1) X
IN RDB$USER_PRIVILEGES
X.RDB$FIELD_NAME.NULL = TRUE;
X.RDB$OBJECT_TYPE.NULL = TRUE;
if (flags & USER_PRIV_USER)
strcpy (X.RDB$USER, user);
if (flags & USER_PRIV_GRANTOR)
strcpy (X.RDB$GRANTOR, grantor);
if (flags & USER_PRIV_PRIVILEGE)
strcpy (X.RDB$PRIVILEGE, privilege);
if (flags & USER_PRIV_GRANT_OPTION)
{
X.RDB$GRANT_OPTION = grant_option;
if (grant_option == 0)
X.RDB$GRANT_OPTION.NULL = TRUE;
else
X.RDB$GRANT_OPTION.NULL = FALSE;
}
if (flags & USER_PRIV_OBJECT_NAME)
strcpy (X.RDB$RELATION_NAME, relation_name);
if (flags & USER_PRIV_FIELD_NAME)
{
X.RDB$FIELD_NAME.NULL = FALSE;
strcpy (X.RDB$FIELD_NAME, field_name);
}
/*
* USER_TYPE & OBJECT_TYPE are fields that did not exist before
* V4.0. So, we have to reconstruct them and initialize them to
* reasonable values. If they existed before then user_type and
* object_type contain the proper values. If they didn't exist
* then user_type and object_type contain the reasonable default
* values.
*/
X.RDB$USER_TYPE.NULL = FALSE;
X.RDB$USER_TYPE = user_type;
X.RDB$OBJECT_TYPE.NULL = FALSE;
X.RDB$OBJECT_TYPE = object_type;
/*
* If OBJECT_TYPE didn't exist before and we have a field level
* user privileges, then use obj_field instead.
*
* NOTE: Scanning the V4.0 code base, obj_field has never been
* used at all. The following code should be uncommented
* in case we ever introduce obj_field to the picture.
*/
/***********************************************************
if ( !(flags & USER_PRIV_OBJECT_TYPE) )
{
if ( flags & USER_PRIV_FIELD_NAME )
{
X.RDB$OBJECT_TYPE = obj_field;
}
}
***********************************************************/
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
}
return true;
}
bool get_view (burp_rel* relation)
{
/**************************************
*
* g e t _ v i e w
*
**************************************
*
* Functional description
* Store a record in RDB$VIEW_RELATIONS.
*
**************************************/
ATT_TYPE attribute;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
// 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);
break;
case att_view_context_id:
X.RDB$VIEW_CONTEXT = (USHORT) get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 140);
// msg 140 view
break;
}
}
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
return true;
}
void ignore_array (burp_rel* relation)
{
/**************************************
*
* i g n o r e _ a r r a y
*
**************************************
*
* Functional description
* Ignore data from input file, like a
* dummy get_array().
*
**************************************/
burp_fld* field;
ATT_TYPE attribute;
SLONG *range, *end_ranges;
USHORT field_number;
scan_attr_t scan_next_attr;
TGBL tdgbl = GET_THREAD_DATA;
// Pick up attributes
skip_init(&scan_next_attr);
while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_blob_data)
{
switch (attribute)
{
case att_blob_field_number:
field_number = (USHORT) get_numeric();
for (field = relation->rel_fields; field; field = field->fld_next) {
if (field->fld_number == field_number)
break;
}
if (!field)
BURP_error_redirect (NULL, 36, 0, 0);
// msg 36 Can't find field for blob
break;
case att_array_dimensions:
field->fld_dimensions = (SSHORT)get_numeric();
end_ranges = field->fld_ranges + 2 * field->fld_dimensions;
for (range = field->fld_ranges; range < end_ranges; range += 2)
{
if (get_attribute(&attribute, tdgbl) != att_array_range_low)
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
else
range[0] = get_numeric();
if (get_attribute(&attribute, tdgbl) != att_array_range_high)
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
else
range[1] = get_numeric();
}
break;
default:
bad_attribute (scan_next_attr, attribute, 58);
// msg 58 array
break;
}
}
SLONG length = get(tdgbl);
length |= get(tdgbl) << 8;
length |= get(tdgbl) << 16;
length |= get(tdgbl) << 24;
SLONG lcount;
if (tdgbl->gbl_sw_transportable)
{
if (get_attribute(&attribute, tdgbl) != att_xdr_array)
BURP_error_redirect (NULL, 55, 0, 0);
// msg 55 Expected XDR record length
else
{
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()
{
/**************************************
*
* 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;
TGBL tdgbl = GET_THREAD_DATA;
// 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();
break;
case att_blob_max_segment:
get_numeric();
break;
case att_blob_number_segments:
segments = get_numeric();
break;
case att_blob_type:
get_numeric();
break;
default:
bad_attribute (scan_next_attr, attribute, 64);
// msg 64 blob
break;
}
}
// Eat up blob segments
while (--segments >= 0)
{
USHORT length = get(tdgbl);
length |= get(tdgbl) << 8;
if (length)
get_skip(tdgbl, length);
}
}
rec_type ignore_data (burp_rel* relation)
{
/**************************************
*
* i g n o r e _ d a t a
*
**************************************
*
* Functional description
* Ignore data records for a relation.
*
**************************************/
TGBL tdgbl = GET_THREAD_DATA;
ULONG records = 0;
rec_type record;
while (true)
{
if (get(tdgbl) != att_data_length)
BURP_error_redirect (NULL, 39, 0, 0);
// msg 39 expected record length
USHORT l = (USHORT) get_numeric();
if (tdgbl->gbl_sw_transportable)
{
if (get(tdgbl) != att_xdr_length)
BURP_error_redirect (NULL, 55, 0, 0);
// msg 55 Expected XDR record length
else
l = (USHORT) get_numeric();
}
if (get(tdgbl) != att_data_data)
BURP_error_redirect (NULL, 41, 0, 0);
// msg 41 expected data attribute
if (l) {
if (tdgbl->gbl_sw_compress)
{
UCHAR* buffer = (UCHAR*) BURP_alloc (l);
decompress (buffer, l);
BURP_free (buffer);
}
else
get_skip(tdgbl, l);
}
++records;
while (get_record(&record, tdgbl))
{
if (record == rec_blob)
ignore_blob();
else if (record == rec_array)
ignore_array (relation);
else
break;
}
if (record != rec_data)
break;
}
BURP_verbose (106, (void*)(IPTR) records, NULL, NULL, NULL, NULL);
// msg 106 %ld records ignored
return record;
}
void realign(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.
*
**************************************/
TGBL tdgbl = GET_THREAD_DATA;
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, 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 (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;
TGBL tdgbl = GET_THREAD_DATA;
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 (const TEXT* file_name,
const TEXT* database_name)
{
/**************************************
*
* r e s t o r e
*
**************************************
*
* Functional description
* Perform the body of restore.
*
**************************************/
rec_type record;
ATT_TYPE attribute;
isc_req_handle req_handle1 = NULL, req_handle2 = NULL, req_handle3 = NULL,
req_handle5 = NULL;
TGBL tdgbl = GET_THREAD_DATA;
// Read burp record first
MVOL_init_read ((UCHAR*) tdgbl->gbl_database_file_name, (UCHAR*) file_name,
&tdgbl->RESTORE_format, &tdgbl->io_cnt, &tdgbl->io_ptr);
if (tdgbl->gbl_sw_transportable)
BURP_verbose (133, NULL, NULL, NULL, NULL, NULL);
// msg 133 transportable backup -- data in XDR format
if (tdgbl->gbl_sw_compress)
BURP_verbose (61, NULL, NULL, NULL, NULL, NULL);
// msg 61 backup file is compressed
// restore only from those backup files created by current or previous GBAK
if (tdgbl->RESTORE_format < 1 || tdgbl->RESTORE_format > ATT_BACKUP_FORMAT)
{
BURP_error(44, true, isc_arg_number, (void*)(IPTR)tdgbl->RESTORE_format,
0, NULL, 0, NULL, 0, NULL, 0, NULL);
// msg 44 Expected backup version 1, 2, or 3. Found %ld
}
create_database (database_name);
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
/* For V4.0, start a read commited transaction. This will be used
* to create blobs for global fields and update the record in the
* RDB$FIELDS table.
*/
EXEC SQL SET TRANSACTION NAME tdgbl->global_trans ISOLATION LEVEL READ COMMITTED;
USHORT db_version = check_db_version();
if (db_version < DB_VERSION_CURRENT)
{
BURP_error(51, true, isc_arg_number, (void*) (IPTR) db_version,
0, NULL, 0, NULL, 0, NULL, 0, NULL);
// msg 51 database format %ld is too old to restore to
}
BURP_verbose (129, NULL, NULL, NULL, NULL, NULL);
// msg 129 started transaction
while (get_attribute(&attribute, tdgbl) != att_end)
{
switch (attribute)
{
case att_database_security_class:
/* Instead of updating the security class in RDB$DATABASE,
just store the value in tdgbl. It will be updated at
the very end to prevent security class validation
failures during change table ownership operation */
GET_TEXT(tdgbl->database_security_class);
break;
case att_database_description:
case att_database_description2:
FOR (REQUEST_HANDLE req_handle2)
X IN RDB$DATABASE
MODIFY X USING
if (attribute == att_database_description2)
get_source_blob (&X.RDB$DESCRIPTION, false);
else
get_misc_blob (&X.RDB$DESCRIPTION, 1, false);
END_MODIFY;
ON_ERROR
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
break;
case att_database_dfl_charset:
FOR (REQUEST_HANDLE req_handle3)
X IN RDB$DATABASE
MODIFY X USING
GET_TEXT(X.RDB$CHARACTER_SET_NAME);
END_MODIFY;
ON_ERROR
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
break;
default:
{
SSHORT l = get(tdgbl);
if (l)
get_skip(tdgbl, l);
break;
}
}
}
MISC_release_request_silent(req_handle1);
MISC_release_request_silent(req_handle2);
MISC_release_request_silent(req_handle3);
// If this should be a multi-file database, add the files
if (tdgbl->gbl_sw_files && tdgbl->gbl_sw_files->fil_next)
add_files ((UCHAR*) database_name);
// Get global fields and relations
bool flag_norel = true; // To fix bug 10098
bool flag = false;
while (get_record(&record, tdgbl) != rec_end)
{
switch (record)
{
case rec_charset:
if (!get_character_set())
return false;
flag = true;
break;
case rec_collation:
if (!get_collation())
return false;
flag = true;
break;
case rec_chk_constraint:
if (!get_chk_constraint())
return false;
flag = true;
break;
case rec_global_field:
if (!get_global_field())
return false;
flag = true;
break;
case rec_field_dimensions:
if (!get_field_dimensions())
return false;
flag = true;
break;
case rec_relation:
if (!get_relation())
return false;
flag = true;
flag_norel = false;
break;
case rec_ref_constraint:
if (!get_ref_constraint())
return false;
flag = true;
break;
case rec_rel_constraint:
if (!get_rel_constraint())
return false;
flag = true;
break;
case rec_function:
if (!get_function())
return false;
flag = true;
break;
case rec_procedure:
if (!get_procedure())
return false;
flag = true;
break;
case rec_exception:
if (!get_exception())
return false;
flag = true;
break;
case rec_system_type: // rdb$types
if (!get_type())
return false;
flag = true;
break;
case rec_filter: // rdb$filters
if (!get_filter())
return false;
flag = true;
break;
case rec_generator:
if (!get_generator())
return false;
flag = true;
break;
case rec_relation_data:
if (flag)
{
BURP_verbose (68, NULL, NULL, NULL, NULL, NULL);
// msg 68 committing meta data
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
flag = false;
}
if (!get_relation_data())
return false;
break;
case rec_trigger: // new trigger type
if (!get_trigger())
return false;
flag = true;
break;
case rec_trigger_message:
if (!get_trigger_message())
return false;
flag = true;
break;
case rec_user_privilege:
if (!get_user_privilege())
return false;
flag = true;
break;
case rec_security_class:
if (!get_security_class())
return false;
flag = true;
break;
case rec_files:
if (!get_files())
return false;
flag = true;
break;
case rec_sql_roles:
if (!get_sql_roles())
return false;
flag = true;
break;
default:
BURP_error(43, true, isc_arg_number, (void*) (IPTR) record,
0, NULL, 0, NULL, 0, NULL, 0, NULL);
// msg 43 don't recognize record type %ld
break;
}
}
/* This piece of code is to fix bug 10098: restore of database with
only domains and no relations aborts with the message ERROR: deadlock
This is because insertion of domains into RDB$FIELDS is happening in
the default transaction, whereas updation of RDB$FIELDS to add
constraints to the domains is done in tdgbl->global_trans. In case of
no relations, no COMMIT of default transaction occurs till this point
because of which rows in RDB$FIELDS for domains are still locked by
default transaction. The below code COMMITs the default transaction
in that particular situation */
if (flag_norel)
{
COMMIT;
ON_ERROR
general_on_error ();
END_ERROR;
EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
if (gds_status[1])
EXEC SQL SET TRANSACTION;
}
// put validation clauses for global fields
update_global_field ();
// Purge shadow metadata if necessary
if (tdgbl->gbl_sw_kill)
FOR (REQUEST_HANDLE req_handle5)
FIL IN RDB$FILES WITH FIL.RDB$SHADOW_NUMBER NOT MISSING
AND FIL.RDB$SHADOW_NUMBER NE 0
ERASE FIL;
ON_ERROR
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle5);
return true;
}
void restore_security_class (const TEXT *owner_nm,
const TEXT *sec_class_nm)
{
/**************************************
*
* r e s t o r e _ s e c u r i t y _ c l a s s
*
**************************************
*
* Functional description
* restore the ownership of the relation in the ACL list
*
**************************************/
isc_req_handle req_handle2 = NULL;
TGBL tdgbl = GET_THREAD_DATA;
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 (owner_nm, &X.RDB$ACL, &new_blob_id);
MODIFY X;
MOVE_FAST (&new_blob_id, &X.RDB$ACL, sizeof(ISC_QUAD));
END_MODIFY;
ON_ERROR
MISC_release_request_silent(req_handle2);
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
MISC_release_request_silent(req_handle2);
general_on_error ();
END_ERROR;
MISC_release_request_silent(req_handle2);
}
USHORT get_view_base_relation_count (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 = NULL;
TGBL tdgbl = GET_THREAD_DATA;
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(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 (const TEXT* gen_name, // TEXT GDS_NAME[GDS_NAME_LEN]
SINT64 value)
{
/**************************************
*
* s t o r e _ b l r _ g e n _ i d
*
**************************************
*
* Functional description
* Store the blr_gen_id for the relation.
*
**************************************/
TGBL tdgbl = GET_THREAD_DATA;
STORE (REQUEST_HANDLE tdgbl->handles_store_blr_gen_id_req_handle1)
X IN RDB$GENERATORS
strcpy (X.RDB$GENERATOR_NAME, gen_name);
END_STORE;
ON_ERROR
general_on_error ();
END_ERROR;
if (!value)
{
#pragma FB_COMPILER_MESSAGE("BRS: casting SINT64 to SLONG")
BURP_verbose (185, gen_name, (void*) (IPTR) value, NULL, NULL, NULL);
// msg 185 restoring generator %s value: %ld
return;
}
FRBRD* gen_id_reqh = NULL;
UCHAR blr_buffer[100]; // enough to fit blr
UCHAR* blr = blr_buffer;
// build the blr with the right relation name
if (tdgbl->RESTORE_format >= 6)
{
stuff(&blr, blr_version5);
}
else
{
stuff(&blr, blr_version4);
}
stuff(&blr, blr_begin);
if (tdgbl->RESTORE_format >= 6)
{
stuff(&blr, blr_dcl_variable);
stuff_word(&blr, 0);
stuff(&blr, blr_int64);
stuff(&blr, 0);
}
else
{
stuff(&blr, blr_dcl_variable);
stuff_word(&blr, 0);
stuff(&blr, blr_long);
stuff(&blr, 0);
}
stuff(&blr, blr_begin);
stuff(&blr, blr_assignment);
stuff(&blr, blr_gen_id);
stuff_string(&blr, gen_name);
if (tdgbl->RESTORE_format >= 6)
{
stuff(&blr, blr_literal);
stuff(&blr, blr_int64);
stuff(&blr, 0);
stuff_int64(&blr, value);
}
else
{
stuff(&blr, blr_literal);
stuff(&blr, blr_long);
stuff(&blr, 0);
stuff_long(&blr, (SLONG)value);
}
stuff(&blr, blr_variable);
stuff_word(&blr, 0);
stuff(&blr, blr_end);
stuff(&blr, blr_end);
stuff(&blr, blr_eoc);
const SSHORT blr_length = blr - 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, 0, 0);
// msg 42 Failed in store_blr_gen_id
}
if (isc_start_request (status_vector, &gen_id_reqh,
&gds_trans, // use the same one generated by gpre
0))
{
isc_print_blr ((const SCHAR*) blr_buffer, NULL, NULL, 0);
BURP_error_redirect (status_vector, 42, 0, 0);
// msg 42 Failed in store_blr_gen_id
}
#pragma FB_COMPILER_MESSAGE("BRS: casting SINT64 to SLONG")
BURP_verbose (185, gen_name, (void*) (IPTR) value, NULL, NULL, NULL);
// msg 185 restoring generator %s value: %ld
isc_release_request (status_vector, &gen_id_reqh);
}
void stuff_string(UCHAR **ptr,
const TEXT* string)
{
/**************************************
*
* s t u f f _ s t r i n g
*
**************************************
*
* Functional description
* Stuff a name input a BLR string -- byte count first.
*
**************************************/
stuff(ptr, strlen (string));
while (*string)
stuff(ptr, *string++);
}
void update_global_field()
{
/**************************************
*
* 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.
*
**************************************/
USHORT length;
isc_req_handle req_handle1 = NULL;
TGBL tdgbl = GET_THREAD_DATA;
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;
if (length = sizeof(ISC_QUAD))
{
UCHAR* p = (UCHAR *)&X.RDB$VALIDATION_BLR;
const UCHAR* q = (UCHAR *)&gfield->gfld_vb;
do { *p++ = *q++; } while (--length);
}
}
if (gfield->gfld_flags & GFLD_validation_source)
{
X.RDB$VALIDATION_SOURCE.NULL = FALSE;
if (length = sizeof(ISC_QUAD))
{
UCHAR* p = (UCHAR *)&X.RDB$VALIDATION_SOURCE;
const UCHAR* q = (UCHAR *)&gfield->gfld_vs;
do { *p++ = *q++; } while (--length);
}
}
if (gfield->gfld_flags & GFLD_validation_source2)
{
X.RDB$VALIDATION_SOURCE.NULL = FALSE;
if (length = sizeof(ISC_QUAD))
{
UCHAR* p = (UCHAR *)&X.RDB$VALIDATION_SOURCE;
const UCHAR* q = (UCHAR *)&gfield->gfld_vs2;
do { *p++ = *q++; } while (--length);
}
}
END_MODIFY;
ON_ERROR
general_on_error ();
END_ERROR;
END_FOR;
ON_ERROR
general_on_error ();
END_ERROR;
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()
{
/**************************************
*
* 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 = NULL;
TGBL tdgbl = GET_THREAD_DATA;
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(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