mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 22:43:04 +01:00
e6187cecd2
Some bug fixes Style
6996 lines
169 KiB
Plaintext
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
|
|
|
|
|