/*
 *	PROGRAM:	JRD Backup and Restore Program
 *	MODULE:		restore.epp
 *	DESCRIPTION:	Restore routine
 *
 * The contents of this file are subject to the Interbase Public
 * License Version 1.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy
 * of the License at http://www.Inprise.com/IPL.html
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * rights and limitations under the License.
 *
 * The Original Code was created by Inprise Corporation
 * and its predecessors. Portions created by Inprise Corporation are
 * Copyright (C) Inprise Corporation.
 *
 * All Rights Reserved.
 * Contributor(s): ______________________________________.
 * Toni Martir: Verbose records restored as RESTORE_VERBOSE_INTERVAL,
 * also verbose restoring indexes as DEFERRED when verbose
 * 2003.08.17 Claudio Valderrama: Fix SF Bug #750659.
 * Adriano dos Santos Fernandes
 *
 */

#include "firebird.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "../burp/burp.h"
#include "../jrd/align.h"
#include "../jrd/flags.h"
#include "../jrd/license.h"
#include "../jrd/obj.h"
#include "../jrd/ods.h"
#include "../common/stuff.h"
#include "../burp/burp_proto.h"
#include "../burp/canon_proto.h"
#include "../burp/misc_proto.h"
#include "../burp/mvol_proto.h"
#include "../burp/resto_proto.h"
#include "../common/gdsassert.h"
#include "../jrd/constants.h"
#include "../remote/protocol.h"
#ifdef DEBUG
#include "../common/prett_proto.h"
#endif
#include "../common/classes/ClumpletWriter.h"
#include "../common/classes/UserBlob.h"
#include "../common/classes/SafeArg.h"
#include "../common/utils_proto.h"
#include "memory_routines.h"
#include "../burp/OdsDetection.h"
#include "../auth/trusted/AuthSspi.h"
#include "../common/msg_encode.h"
#include "../common/StatusHolder.h"
#include "../common/StatusArg.h"

using MsgFormat::SafeArg;


//  For service APIs the follow DB handle is a value stored
//  in thread data.  This is also done for other statics generated by
//  GPRE.  This is to avoid multiple threading problems with module
//  level statics.

DATABASE DB = STATIC FILENAME "yachts.lnk";

#define DB			tdgbl->db_handle
#define gds_trans	tdgbl->tr_handle
#define isc_status	tdgbl->status_vector


namespace // unnamed, private
{

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


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_access_dpb(BurpGlobals* tdgbl, Firebird::ClumpletWriter& dpb);
void	add_files(BurpGlobals* tdgbl, const char*);
void	bad_attribute(scan_attr_t, att_type, USHORT);
void	create_database(BurpGlobals* tdgbl, const TEXT*);
void	decompress(BurpGlobals* tdgbl, UCHAR*, USHORT);
void	eat_blob(BurpGlobals* tdgbl);
void	eat_text(BurpGlobals* tdgbl);
void	eat_text2(BurpGlobals* tdgbl);
burp_rel*	find_relation(BurpGlobals* tdgbl, const TEXT*);
void	fix_security_class_name(BurpGlobals* tdgbl, TEXT* sec_class, bool is_field);
// CVC: when do these functions return false indeed???
// get_acl and get_index are the only exceptions but ironically their
// returned value is not checked by the caller!
bool	get_acl(BurpGlobals* tdgbl, const TEXT*, ISC_QUAD*, ISC_QUAD*);
void	get_array(BurpGlobals* tdgbl, burp_rel*, UCHAR*);
void	get_blob(BurpGlobals* tdgbl, const burp_fld*, UCHAR*);
void	get_blr_blob(BurpGlobals* tdgbl, ISC_QUAD&, bool);
bool	get_character_set(BurpGlobals* tdgbl);
bool	get_chk_constraint(BurpGlobals* tdgbl);
bool	get_collation(BurpGlobals* tdgbl);
rec_type	get_data(BurpGlobals* tdgbl, burp_rel*, bool);
bool	get_exception(BurpGlobals* tdgbl);
burp_fld*	get_field(BurpGlobals* tdgbl, burp_rel*);
bool	get_field_dimensions(BurpGlobals* tdgbl);
bool	get_files(BurpGlobals* tdgbl);
bool	get_filter(BurpGlobals* tdgbl);
bool	get_function(BurpGlobals* tdgbl);
void	get_function_arg(BurpGlobals* tdgbl, bool skip_arguments);
bool	get_generator(BurpGlobals* tdgbl);
bool	get_global_field(BurpGlobals* tdgbl);
bool	get_index(BurpGlobals* tdgbl, const burp_rel*);
void	get_misc_blob(BurpGlobals* tdgbl, ISC_QUAD&, bool);
SLONG	get_int32(BurpGlobals* tdgbl);
SINT64  get_int64(BurpGlobals* tdgbl);
bool	get_package(BurpGlobals* tdgbl);
bool	get_procedure(BurpGlobals* tdgbl);
bool	get_procedure_prm (BurpGlobals* tdgbl, GDS_NAME, GDS_NAME);
bool	get_ref_constraint(BurpGlobals* tdgbl);
bool	get_rel_constraint(BurpGlobals* tdgbl);
bool	get_relation(BurpGlobals* tdgbl);
bool	get_relation_data(BurpGlobals* tdgbl);
bool	get_sql_roles(BurpGlobals* tdgbl);
bool	get_mapping(BurpGlobals* tdgbl);
bool	get_db_creator(BurpGlobals* tdgbl);
bool	get_security_class(BurpGlobals* tdgbl);
void	get_source_blob(BurpGlobals* tdgbl, ISC_QUAD&, bool);
USHORT	get_text(BurpGlobals* tdgbl, TEXT*, ULONG);
USHORT	get_text2(BurpGlobals* tdgbl, TEXT* text, ULONG length);
bool	get_trigger(BurpGlobals* tdgbl);
bool	get_trigger_message(BurpGlobals* tdgbl);
bool	get_trigger_old (BurpGlobals* tdgbl, burp_rel*);
bool	get_type(BurpGlobals* tdgbl);
bool	get_user_privilege(BurpGlobals* tdgbl);
bool	get_view(BurpGlobals* tdgbl, burp_rel*);
void	ignore_array(BurpGlobals* tdgbl, burp_rel*);
void	ignore_blob(BurpGlobals* tdgbl);
rec_type	ignore_data(BurpGlobals* tdgbl, burp_rel*);
void	realign(BurpGlobals* tdgbl, UCHAR*, const burp_rel*);
#ifdef sparc
USHORT	recompute_length(BurpGlobals* tdgbl, burp_rel*);
#endif
bool	restore(BurpGlobals* tdgbl, const TEXT*, const TEXT*);
void	restore_security_class(BurpGlobals* tdgbl, const TEXT*, const TEXT*);
USHORT	get_view_base_relation_count(BurpGlobals* tdgbl, const TEXT*, USHORT, bool* error);
void	store_blr_gen_id(BurpGlobals* tdgbl, const TEXT* gen_name, SINT64 value, SINT64 initial_value,
	const ISC_QUAD* gen_desc, const char* secclass, const char* ownername, fb_sysflag sysFlag,
	SLONG increment);
void	update_global_field(BurpGlobals* tdgbl);
void	update_ownership(BurpGlobals* tdgbl);
void	update_view_dbkey_lengths(BurpGlobals* tdgbl);
void	fix_missing_privileges(BurpGlobals* tdgbl);
void	fix_system_generators(BurpGlobals* tdgbl);
void	general_on_error();
#ifdef DEBUG
UCHAR	debug_on = 0;   // able to turn this on in the debugger
#endif

#ifdef sparc
const SSHORT old_sparcs[] =
    {0, 0, 0, 2, 0, 0, 0, 0, 2, 4, 4, 4, 8, 8, 0, 0, 8, 8, 8};
#endif

//MVOL_read returns int
static inline int get(BurpGlobals* tdgbl)
{
	if (--(tdgbl->io_cnt) >= 0)
		return *(tdgbl->io_ptr)++;

	return MVOL_read(&tdgbl->io_cnt, &tdgbl->io_ptr);
}

static inline att_type get_attribute(att_type* att, BurpGlobals* tdgbl)
{
	*att = (att_type) get(tdgbl);
	return *att;
}

static inline rec_type get_record(rec_type *rec, BurpGlobals* tdgbl)
{
	*rec = (rec_type) get(tdgbl);
	return *rec;
}

#define GET_TEXT(text)	get_text(tdgbl, (text), sizeof(text))
#define GET_TEXT2(text)	get_text2(tdgbl, (text), sizeof(text))

static inline void get_skip(BurpGlobals* tdgbl, ULONG n)
{
	MVOL_skip_block(tdgbl, n);
}

static inline UCHAR* get_block(BurpGlobals* tdgbl, UCHAR* p, ULONG n)
{
	return MVOL_read_block(tdgbl, p, n);
}

// When skipping started, scan_next_attr will be changed from NO_SKIP
// to BEFORE_SKIP. When scanning for next valid attribute after skipping,
// it will flip-flop between BEFORE_SKIP and AFTER_SKIP. When next valid
// attribute is found, it will be changed back to NO_SKIP by 'skip_scan'

static inline void skip_init(scan_attr_t* scan_next_attr)
{
	*scan_next_attr = NO_SKIP;
}

static inline void skip_scan(scan_attr_t* scan_next_attr)
{
	if (*scan_next_attr == AFTER_SKIP)
		*scan_next_attr = BEFORE_SKIP;
	else if (*scan_next_attr == BEFORE_SKIP)
		*scan_next_attr = NO_SKIP;
	//else 0; => nothing, no change in the original macro
}

// User Privilege Flags

const int USER_PRIV_USER			= 1;
const int USER_PRIV_GRANTOR			= 2;
const int USER_PRIV_PRIVILEGE		= 4;
const int USER_PRIV_GRANT_OPTION	= 8;
const int USER_PRIV_OBJECT_NAME		= 16;
const int USER_PRIV_FIELD_NAME		= 32;
const int USER_PRIV_USER_TYPE		= 64;
const int USER_PRIV_OBJECT_TYPE		= 128;

static inline void collect_missing_privs(BurpGlobals* tdgbl, USHORT type, const GDS_NAME name, bool hasSecClass)
{
	burp_meta_obj* object = (burp_meta_obj*) BURP_alloc_zero(sizeof(burp_meta_obj));
	object->obj_next = tdgbl->miss_privs;
	object->obj_type = type;
	strcpy(object->obj_name, name);
	object->obj_class = hasSecClass;
	tdgbl->miss_privs = object;
}

} // namespace


int RESTORE_restore (const TEXT* file_name, const TEXT* database_name)
{
/**************************************
 *
 *	R E S T O R E _ r e s t o r e
 *
 **************************************
 *
 * Functional description
 *	Recreate a database from a backup.
 *
 **************************************/
	isc_req_handle  req_handle1 = 0, req_handle3 = 0, req_handle5 = 0;
	BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	tdgbl->io_ptr = NULL;
	tdgbl->io_cnt = 0;

	tdgbl->relations = NULL;
	tdgbl->packages = NULL;
	tdgbl->procedures = NULL;
	tdgbl->miss_privs = NULL;
	tdgbl->RESTORE_format = 0;
	tdgbl->runtimeODS = 0;
	tdgbl->global_trans = 0;

	tdgbl->gbl_sw_transportable = tdgbl->gbl_sw_compress = false;

	if (!restore(tdgbl, file_name, database_name))
		return FINI_ERROR;

	BURP_verbose (76);
	// msg 76 creating indexes

	COMMIT;
	ON_ERROR
		// Fix for bug_no 8055:
		// don't throw away the database just because an index
		// could not be made
		long error_code;
		while (error_code = tdgbl->status_vector[1])
		{
			switch (error_code)
			{
				case isc_sort_mem_err:
				case isc_no_dup:
					strcpy(index_name, (TEXT *)tdgbl->status_vector[3]);
					BURP_print_status(false, tdgbl->status_vector);
					FOR (REQUEST_HANDLE req_handle3)
						IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ index_name

						BURP_verbose(243, index_name);
						MODIFY IDX USING
							IDX.RDB$INDEX_INACTIVE = TRUE;
						END_MODIFY;
						BURP_print(false, 240, index_name);
						// msg 240 Index \"%s\" failed to activate because:
						if ( error_code == isc_no_dup )
						{
							BURP_print(false, 241);
							// msg 241 The unique index has duplicate values or NULLs
							BURP_print(false, 242);
							// msg 242 Delete or Update duplicate values or NULLs, and activate index with
						}
						else
						{
							BURP_print(false, 244);
							// msg 244 Not enough disk space to create the sort file for an index
							BURP_print(false, 245);
							// msg 245 Set the TMP environment variable to a directory on a filesystem that does have enough space, and activate index with
						}
						BURP_print(false, 243, index_name);
						// msg 243 ALTER INDEX \"%s\" ACTIVE;
					END_FOR;
					// don't bring the database on-line
					tdgbl->flag_on_line = false;
					break;
				default:
					general_on_error ();
				break;
			}
			COMMIT
			ON_ERROR
				continue;
			END_ERROR
		}
	END_ERROR;

	if (tdgbl->global_trans)
	{
		BURP_verbose (68);
		// msg 68 committing meta data
		EXEC SQL COMMIT TRANSACTION tdgbl->global_trans;
		if (gds_status[1])
			general_on_error ();
		// Check to see if there is a warning
		if (gds_status[0] == isc_arg_gds && gds_status[1] == 0 && gds_status[2] != isc_arg_end)
		{
			BURP_print_warning(gds_status);
		}
	}

	// Activate the indices for foreign keys and do another commit
	if (!tdgbl->gbl_sw_deactivate_indexes)
	{

		// Block added to verbose index creation by Toni Martir
		// Always try to activate deferred indices - it helps for some broken backups,
		// and in normal cases doesn't take much time to look for such indices. AP-2008.
		EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO;
		if (gds_status[1])
			EXEC SQL SET TRANSACTION;

		// Activate first indexes that are not foreign keys
		FOR (REQUEST_HANDLE req_handle1) IDS IN RDB$INDICES WITH
			IDS.RDB$INDEX_INACTIVE EQ DEFERRED_ACTIVE AND
			IDS.RDB$FOREIGN_KEY MISSING

			MISC_terminate(IDS.RDB$INDEX_NAME, index_name,
				(ULONG) MISC_symbol_length(IDS.RDB$INDEX_NAME, sizeof(IDS.RDB$INDEX_NAME)),
				sizeof(index_name));
			BURP_verbose(285, index_name);
			// activating and creating deferred index %s
			MODIFY IDS USING
				IDS.RDB$INDEX_INACTIVE = FALSE;
			END_MODIFY;
			ON_ERROR
				general_on_error();
			END_ERROR;

			SAVE
			// existing ON_ERROR continues past error, beck
			ON_ERROR
				BURP_print (false, 173, index_name);
				BURP_print_status(false, isc_status);
				MODIFY IDS USING
					IDS.RDB$INDEX_INACTIVE = TRUE;
				END_MODIFY;
				ON_ERROR
					general_on_error ();
				END_ERROR;
				tdgbl->flag_on_line = false;
			END_ERROR;
		END_FOR;
		ON_ERROR
			general_on_error ();
		END_ERROR;
		MISC_release_request_silent(req_handle1);
		COMMIT;
		ON_ERROR
			general_on_error ();
		END_ERROR;

		EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO;
		if (gds_status[1])
			EXEC SQL SET TRANSACTION;

		// Only activate Foreign keys that have been marked for deferred
		// activation.
		// -bsriram, 11-May-1999             BUG: 10016

		// In case error happens creating FK, triggers don't let set
		// INACTIVE = TRUE for FK index. Therefore use separate
		// transaction be able to rollback when needed.
		// AP, 2005

		FOR (REQUEST_HANDLE req_handle1)
			CNST IN RDB$RELATION_CONSTRAINTS
			CROSS IDS IN RDB$INDICES WITH
			CNST.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND
			CNST.RDB$INDEX_NAME EQ IDS.RDB$INDEX_NAME AND
			IDS.RDB$INDEX_INACTIVE EQ DEFERRED_ACTIVE

			MISC_terminate(IDS.RDB$INDEX_NAME, index_name,
				(ULONG) MISC_symbol_length(IDS.RDB$INDEX_NAME, sizeof(IDS.RDB$INDEX_NAME)),
				sizeof(index_name));
			BURP_verbose(285, index_name);
			// activating and creating deferred index %s

			bool fError = false;
			isc_tr_handle activateIndexTran = 0;
			ISC_STATUS_ARRAY local_status_vector;
			ISC_STATUS* local_status = local_status_vector;

			START_TRANSACTION activateIndexTran;
			FOR (TRANSACTION_HANDLE activateIndexTran REQUEST_HANDLE req_handle5)
				IND1 IN RDB$INDICES WITH IND1.RDB$INDEX_NAME EQ IDS.RDB$INDEX_NAME
				MODIFY IND1 USING
						IND1.RDB$INDEX_INACTIVE = FALSE;
				END_MODIFY;
			END_FOR;
			ON_ERROR
				fError = true;
				memcpy(local_status, isc_status, sizeof (ISC_STATUS_ARRAY));
			END_ERROR;
			MISC_release_request_silent(req_handle5);

			if (!fError)
			{
				COMMIT activateIndexTran;
				ON_ERROR
					fError = true;
					memcpy(local_status, isc_status, sizeof (ISC_STATUS_ARRAY));
				END_ERROR;
			}
			if (fError)
			{
				ROLLBACK activateIndexTran;
				ON_ERROR
					general_on_error ();
				END_ERROR;
				BURP_print (false, 173, index_name);
				BURP_print_status(false, local_status);
				tdgbl->flag_on_line = false;
			}
		END_FOR;
		ON_ERROR
			general_on_error ();
		END_ERROR;
		MISC_release_request_silent(req_handle1);
		COMMIT;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}

	EXEC SQL SET TRANSACTION ISOLATION LEVEL READ COMMITTED NO_AUTO_UNDO;
	if (gds_status[1])
		EXEC SQL SET TRANSACTION;

	// AB: Recalculate RDB$DBKEY_LENGTH for VIEWS
	// When VIEWs are not processed in correct dependency order
	// then on create time it doesn't know anything from the
	// VIEW that's referenced.
	//
	update_view_dbkey_lengths(tdgbl);

	update_ownership(tdgbl);

	// 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);
	}

	// Add missing privileges
	fix_missing_privileges(tdgbl);

	// Fix values of system generators.
	fix_system_generators(tdgbl);

	COMMIT;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	// Check to see if there is a warning
	if (gds_status[0] == isc_arg_gds && gds_status[1] == 0 && gds_status[2] != isc_arg_end)
	{
		BURP_print_warning(gds_status);
	}

	BURP_verbose (88);
	// msg 88 finishing, closing, and going home

	//FB_UINT64 cumul_count =
	MVOL_fini_read();

	if (tdgbl->gbl_stat_flags)
		BURP_verbose(369);
	// msg 369 total statistics

	// Close database before we attach to it again.
	FINISH
	ON_ERROR
		general_on_error ();
	END_ERROR;

	// attach database again to put it online

	BURP_verbose(360);

	Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE);
	add_access_dpb(tdgbl, dpb);

	if (tdgbl->flag_on_line)
	{
		dpb.insertTag(isc_dpb_online);
	}

	// set forced writes to the value which was in the header
	dpb.insertByte(isc_dpb_force_write, tdgbl->hdr_forced_writes ? 1 : 0);

	FB_API_HANDLE db_handle = 0;
	if (isc_attach_database(tdgbl->status_vector, 0, database_name, &db_handle,
							dpb.getBufferLength(), reinterpret_cast<const SCHAR*>(dpb.getBuffer())))
	{
		general_on_error();
	}
	if (isc_detach_database (tdgbl->status_vector, &db_handle))
		general_on_error();

	if (!tdgbl->flag_on_line)
	{
		ISC_STATUS_ARRAY st;
		(Firebird::Arg::Gds(ENCODE_ISC_MSG(246, burp_msg_fac)) <<
			// msg 246 Database is not online due to failure to activate one or more indices.
		 Firebird::Arg::Gds(ENCODE_ISC_MSG(247, burp_msg_fac))).copyTo(st);
			// msg 247 Run gfix -online to bring database online without active indices.
		BURP_print_status(true, st);

		return FINI_DB_NOT_ONLINE;
	}

	// If the database is to be restored ReadOnly, set it to read_only now!
	if (tdgbl->gbl_sw_mode && tdgbl->gbl_sw_mode_val)
	{
		BURP_verbose (280);
		// msg 280: setting database to read-only access

		dpb.reset(isc_dpb_version1);
		add_access_dpb(tdgbl, dpb);

		dpb.insertByte(isc_dpb_set_db_readonly, 1);

		if (isc_attach_database(tdgbl->status_vector, 0, database_name, &db_handle,
								dpb.getBufferLength(), reinterpret_cast<const SCHAR*>(dpb.getBuffer())))
		{
			general_on_error();
		}
		if (isc_detach_database (tdgbl->status_vector, &db_handle))
			general_on_error();

	}

	return FINI_OK;
}

namespace // unnamed, private
{

// Add the common DPB params to the two attach calls in RESTORE_restore()
void add_access_dpb(BurpGlobals* tdgbl, Firebird::ClumpletWriter& dpb)
{
	tdgbl->uSvc->fillDpb(dpb);

	const UCHAR* authBlock;
	unsigned int authSize = tdgbl->uSvc->getAuthBlock(&authBlock);
	if (authBlock)
	{
		dpb.insertBytes(isc_dpb_auth_block, authBlock, authSize);
	}

	if (tdgbl->gbl_sw_user)
	{
		dpb.insertString(isc_dpb_user_name, tdgbl->gbl_sw_user, fb_strlen(tdgbl->gbl_sw_user));
	}
	if (tdgbl->gbl_sw_password)
	{
		dpb.insertString(tdgbl->uSvc->isService() ? isc_dpb_password_enc : isc_dpb_password,
							 tdgbl->gbl_sw_password, fb_strlen(tdgbl->gbl_sw_password));
	}

	dpb.insertByte(isc_dpb_no_db_triggers, 1);
}

void add_files(BurpGlobals* tdgbl, const char* file_name)
{
/**************************************
 *
 *	a d d _ f i l e s
 *
 **************************************
 *
 * Functional description
 *	This should be a multi-file database.
 *	Store files and starting
 *	addresses & commit this much.
 *
 **************************************/
	isc_req_handle  req_handle1 = 0;

	// store the RDB$FILES records

	FB_UINT64 start = FB_CONST64(201); // Magic number, can be taken from some constant?
	SLONG count = 0;
	const char* prev_file_name = NULL;

	for (burp_fil* file = tdgbl->gbl_sw_files; file; file = file->fil_next)
	{
		if (file->fil_name != file_name)
		{
			if (start > MAX_ULONG)
			{
				BURP_error(262, true, prev_file_name ? prev_file_name : file->fil_name.c_str());
			}
			count++;
			STORE (REQUEST_HANDLE req_handle1)
				X IN RDB$FILES
				strcpy (X.RDB$FILE_NAME, file->fil_name.c_str());
				X.RDB$FILE_START = start;
			END_STORE;
			ON_ERROR
				general_on_error ();
			END_ERROR;
				MISC_release_request_silent(req_handle1);
			BURP_verbose (57, SafeArg() << file->fil_name.c_str() << start);
			// msg 57 adding file %s, starting at page %ld
		}
		else if (file->fil_length >= start - 1)
			file->fil_length -= start - 1;
		else
		{
			BURP_print (false, 96, SafeArg() << file->fil_length << (start - 1));
			// msg 96  length given for initial file (%ld) is less than minimum (%ld)
			file->fil_length = 0;
		}

		start += file->fil_length;
		prev_file_name = file->fil_name.c_str();
	}

	if (count)
	{
		BURP_verbose (70);
		// msg 70 committing secondary files
		COMMIT
		// existing ON_ERROR continues past error, beck
		ON_ERROR
			BURP_print (false, 174);
			// msg 174 cannot commit files
			BURP_print_status (false, tdgbl->status_vector);
			ROLLBACK;
			ON_ERROR
				general_on_error ();
			END_ERROR;
		END_ERROR;

		EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
		if (gds_status[1])
			EXEC SQL SET TRANSACTION;
	}
}

void bad_attribute(scan_attr_t scan_next_attr, att_type bad_attr, USHORT type)
{
/**************************************
 *
 *	b a d _ a t t r i b u t e
 *
 **************************************
 *
 * Functional description
 *	We ran into an unsupported attribute.
 *	but it isn't the end of the world.
 *	We will try to skip some bad data and
 *	look for next valid attribute to continue the process.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	SLONG skip_count = 0;

	if (!tdgbl->gbl_sw_skip_count)
	{
		static const SafeArg dummy;
		TEXT t_name[128];
		fb_msg_format(NULL, burp_msg_fac, type, sizeof(t_name), t_name, dummy);
		BURP_print (false, 80, SafeArg() << t_name << int(bad_attr));
		// msg 80  don't recognize %s attribute %ld -- continuing
		int skip_l = get(tdgbl);
		if (skip_l)
			get_skip(tdgbl, skip_l);
	}
	else
	{
		if (scan_next_attr == NO_SKIP)
		{
			skip_count = tdgbl->gbl_sw_skip_count;
			get_skip(tdgbl, skip_count);
			BURP_print (false, 203, SafeArg() << skip_count << int(bad_attr));
			// msg 203: skipped %d bytes after reading a bad attribute %d
		}
		else
		{
			++skip_count;
			BURP_print (false, 205, SafeArg() << skip_count << int(bad_attr));
			// msg 205: skipped %d bytes looking for next valid attribute, encountered attribute %d
		}
		scan_next_attr = AFTER_SKIP;
	}
}


void create_database(BurpGlobals* tdgbl, const TEXT* file_name)
{
/**************************************
 *
 *	c r e a t e _ d a t a b a s e
 *
 **************************************
 *
 * Functional description
 *	create the new database, looking
 *	to see if there are any interesting
 *	things to do.
 *
 **************************************/

	// Get (physical) database record

	ULONG page_size = DEFAULT_PAGE_SIZE;
	// sweep_interval = -1;
	// sweep_interval = 0xFFFFFFFF;
	ULONG sweep_interval = MAX_ULONG;
	bool no_reserve = false;
	bool db_read_only = false, SQL_dialect_flag = false;
	bool forced_writes = true; // turned on by default
	ULONG page_buffers = 0;
	USHORT SQL_dialect = 0;

	att_type attribute;
	rec_type record;
	if (get_record(&record, tdgbl) == rec_physical_db)
	{
		while (get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_SQL_dialect:
				SQL_dialect_flag = true;
				SQL_dialect = (USHORT) get_int32(tdgbl);
				break;

			case att_page_size:
				page_size = get_int32(tdgbl);
				break;

			case att_sweep_interval:
				sweep_interval = get_int32(tdgbl);
				break;

			case att_forced_writes:
				forced_writes = get_int32(tdgbl) != FALSE;
				break;

			case att_no_reserve:
				no_reserve = get_int32(tdgbl) != FALSE;
				break;

			case att_db_read_only:
				db_read_only = get_int32(tdgbl) != FALSE;
				break;

			case att_page_buffers:
				page_buffers = get_int32(tdgbl);
				break;

			default:
				{
					const SSHORT l = get(tdgbl);
					if (l) {
						get_skip(tdgbl, l);
					}
				}
				break;
			}
		}
		get_record(&record, tdgbl);
	}

	if (record != rec_database)
		BURP_error_redirect (NULL, 32);
		// msg 32 Expected database description record

	if (tdgbl->gbl_sw_page_size)
	{
		if (tdgbl->gbl_sw_page_size < page_size)
		{
			BURP_print (false, 110, SafeArg() << page_size << tdgbl->gbl_sw_page_size);
			// msg 110 Reducing the database page size from %ld bytes to %ld bytes
		}
		page_size = tdgbl->gbl_sw_page_size;
	}

	tdgbl->hdr_forced_writes = forced_writes;

	if (tdgbl->gbl_sw_no_reserve)
		no_reserve = tdgbl->gbl_sw_no_reserve;

	// Override attribute setting with user requirement
	if (tdgbl->gbl_sw_mode)
		db_read_only = tdgbl->gbl_sw_mode_val;
	else
	{
		// No access mode specified by user. Use attribute settings. Since the
		// database is set to readOnly only after making it Online in
		// RESTORE_restore(), pass on this information through Global structures
		tdgbl->gbl_sw_mode = true;
		tdgbl->gbl_sw_mode_val = db_read_only;
	}

	if (tdgbl->gbl_sw_page_buffers)
		page_buffers = tdgbl->gbl_sw_page_buffers;

	Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE);
	tdgbl->uSvc->fillDpb(dpb);

	const UCHAR* authBlock;
	unsigned int authSize = tdgbl->uSvc->getAuthBlock(&authBlock);
	if (authBlock)
	{
		dpb.insertBytes(isc_dpb_auth_block, authBlock, authSize);
	}

	dpb.insertInt(isc_dpb_page_size, page_size & 0xff00);
	dpb.insertString(isc_dpb_gbak_attach, FB_VERSION, fb_strlen(FB_VERSION));

	if (sweep_interval != MAX_ULONG)
	{
		dpb.insertInt(isc_dpb_sweep_interval, sweep_interval);
	}

	// If the database is to be restored "read_only", fillup the data pages
	if (no_reserve || db_read_only)
	{
		dpb.insertByte(isc_dpb_no_reserve, 1);
	}
	if (tdgbl->gbl_sw_user)
	{
		dpb.insertString(isc_dpb_user_name, tdgbl->gbl_sw_user, fb_strlen(tdgbl->gbl_sw_user));
	}
	if (tdgbl->gbl_sw_sql_role)
	{
		dpb.insertString(isc_dpb_sql_role_name,
						 tdgbl->gbl_sw_sql_role, fb_strlen(tdgbl->gbl_sw_sql_role));
	}
	if (tdgbl->gbl_sw_password)
	{
		dpb.insertString(tdgbl->uSvc->isService() ? isc_dpb_password_enc : isc_dpb_password,
						 tdgbl->gbl_sw_password, fb_strlen(tdgbl->gbl_sw_password));
	}
	if (page_buffers)
	{
		dpb.insertInt(isc_dpb_set_page_buffers, page_buffers);
	}

	// Turn off sync writes during restore
	dpb.insertByte(isc_dpb_force_write, 0);

	// which SQL dialect that this database speaks
	// When we restore backup files that came from prior
	// to V6, we force the SQL database dialect to 1

	dpb.insertByte(isc_dpb_sql_dialect, SQL_dialect_flag ? SQL_dialect : SQL_DIALECT_V5);

	// start database up shut down,
	// use single-user mode to avoid conflicts during restore process
	dpb.insertByte(isc_dpb_shutdown, isc_dpb_shut_attachment | isc_dpb_shut_single);
	dpb.insertInt(isc_dpb_shutdown_delay, 0);
	dpb.insertInt(isc_dpb_overwrite, tdgbl->gbl_sw_overwrite);

	dpb.insertByte(isc_dpb_no_db_triggers, 1);

	ISC_STATUS_ARRAY status_vector;

	if (tdgbl->gbl_sw_fix_fss_metadata)
	{
		dpb.insertString(isc_dpb_lc_ctype, tdgbl->gbl_sw_fix_fss_metadata,
			fb_strlen(tdgbl->gbl_sw_fix_fss_metadata));
	}

	if (isc_create_database(status_vector, 0, file_name, &DB,
							dpb.getBufferLength(), reinterpret_cast<const SCHAR*>(dpb.getBuffer()),
							0))
	{
		BURP_error_redirect (status_vector, 33, SafeArg() << file_name);
		// msg 33 failed to create database %s
	}

	if (tdgbl->gbl_sw_version && !tdgbl->uSvc->isService())
	{
		BURP_print(false, 139, file_name);
		// msg 139 Version(s) for database "%s"
		isc_version(&DB, BURP_output_version, (void*)"\t%s\n");
	}

	BURP_verbose (74, SafeArg() << file_name << page_size);
	// msg 74 created database %s, page_size %ld bytes
}

void decompress(BurpGlobals* tdgbl, UCHAR* buffer, USHORT length)
{
/**************************************
 *
 *	d e c o m p r e s s
 *
 **************************************
 *
 * Functional description
 *	Get a number of compressed bytes.
 *
 **************************************/
	UCHAR* p = buffer;
	const UCHAR* const end = p + length;

	while (p < end)
	{
		// This change was made to restore National Semi-Conductor's corrupted
		// gbak file and it is in the code base now.  -Andrew

		// so count really only between -128..127
		SSHORT count = (signed char) get(tdgbl);
		if (count > 0)
		{
			if (end - p < count)
			{
				BURP_print (false, 202, SafeArg() << count << (end - p));
				// msg 202: adjusting a decompression length error: invalid length  %d was adjusted to %d
				count = end - p;
			}
			p = get_block(tdgbl, p, count);
		}
		else if (count < 0)
		{
			if (end + count < p)
			{
				BURP_print(false, 202, SafeArg() << count << (p - end));
				// msg 202: adjusting a decompression length error: invalid length %d was adjusted to %d
				count = p - end;
			}
			const UCHAR c = get(tdgbl);
			memset (p, c, -count);
			p += -count;
		}
	}

	if (p > end) {
		BURP_error_redirect (NULL, 34); // msg 34 RESTORE: decompression length error
	}
}

void eat_blob(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	e a t _ b l o b
 *
 **************************************
 *
 * Functional description
 *	Discard a blob from backup file
 *
 **************************************/

	const SLONG length = get_int32(tdgbl);

	get_skip(tdgbl, length);
}

// *****************************
// e a t _ t e x t
// *****************************
// Discard a text field from the backup file.
void eat_text(BurpGlobals* tdgbl)
{
	const ULONG l = get(tdgbl);
	if (l)
		MVOL_skip_block(tdgbl, l);
}

// *****************************
// e a t _ t e x t 2
// *****************************
// Discard a text field from the backup file, using USHORT length indicator.
void eat_text2(BurpGlobals* tdgbl)
{
	UCHAR lenstr[sizeof(USHORT)] = "";
	get_block(tdgbl, lenstr, sizeof(lenstr));
	USHORT len = (USHORT) gds__vax_integer(lenstr, sizeof(lenstr));
	if (len)
		MVOL_skip_block(tdgbl, len);
}

burp_rel* find_relation(BurpGlobals* tdgbl, const TEXT* name)
{
/**************************************
 *
 *	f i n d _ r e l a t i o n
 *
 **************************************
 *
 * Functional description
 *	Given a relation name, find the relation block.  If there isn't
 *	one, produce a fatal error.
 *
 **************************************/

	// Why isn't strcmp used here?
	for (burp_rel* relation = tdgbl->relations; relation; relation = relation->rel_next)
	{
		for (const TEXT* p = relation->rel_name, *q = name; *p == *q; p++, q++)
		{
			if (!*p)
				return relation;
		}
	}

	BURP_error_redirect (NULL, 35, SafeArg() << name);
	// msg 35 can't find relation %s

	return NULL;
}

void fix_security_class_name(BurpGlobals* tdgbl, TEXT* sec_class, bool is_field)
{
/**************************************
 *
 *	f i x _ s e c u r i t y _ c l a s s _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Reassign a proper (unique) name for auto-generated
 *  security classes.
 *
 **************************************/

	const char* const prefix = is_field ? SQL_FLD_SECCLASS_PREFIX : SQL_SECCLASS_PREFIX;
	const int prefix_length = is_field ? SQL_FLD_SECCLASS_PREFIX_LEN : SQL_SECCLASS_PREFIX_LEN;

	if (strncmp(sec_class, prefix, prefix_length))
		return;

	if (tdgbl->runtimeODS < DB_VERSION_DDL11_2)
		return;

	ISC_STATUS_ARRAY status_vector;

	isc_req_handle& handle = tdgbl->handles_fix_security_class_name_req_handle1;

	if (!handle)
	{
		UCHAR blr_buffer[BUFFER_TINY];
		UCHAR* blr = blr_buffer;

		add_byte(blr, blr_version5);
		add_byte(blr, blr_begin);

		add_byte(blr, blr_message);
		add_byte(blr, 0);
		add_word(blr, 1);

		add_byte(blr, blr_int64);
		add_byte(blr, 0);

		add_byte(blr, blr_send);
		add_byte(blr, 0);

		add_byte(blr, blr_begin);
		add_byte(blr, blr_assignment);

		add_byte(blr, blr_gen_id);
		add_string(blr, SQL_SECCLASS_GENERATOR);

		add_byte(blr, blr_literal);
		add_byte(blr, blr_int64);
		add_byte(blr, 0);
		add_int64(blr, 1);

		add_byte(blr, blr_parameter);
		add_byte(blr, 0);
		add_word(blr, 0);

		add_byte(blr, blr_end);
		add_byte(blr, blr_end);
		add_byte(blr, blr_eoc);

		const USHORT blr_length = blr - blr_buffer;
		fb_assert(blr_length <= sizeof(blr_buffer));

		if (isc_compile_request(status_vector, &DB, &handle,
							    blr_length, (const SCHAR*) blr_buffer))
		{
			BURP_error_redirect(status_vector, 316);
			// msg 316 Failed while fixing the security class name
		}
	}

	if (isc_start_request(status_vector, &handle, &gds_trans, 0))
	{
		BURP_error_redirect(status_vector, 316);
		// msg 316 Failed while fixing the security class name
	}

	SINT64 id = 0;

	if (isc_receive(status_vector, &handle, 0, sizeof(SINT64), &id, 0))
	{
		BURP_error_redirect(status_vector, 316);
		// msg 316 Failed while fixing the security class name
	}

	fb_assert(id);

	snprintf(sec_class, MAX_SQL_IDENTIFIER_SIZE, "%s%" SQUADFORMAT, prefix, id);
}

void general_on_error()
{
/**************************************
 *
 *	g e n e r a l _ o n _ e r r o r
 *
 **************************************
 *
 * Functional description
 *	Handle any general ON_ERROR clause during restore.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (isc_status[1] == isc_malformed_string)
	{
		Firebird::Arg::StatusVector oldVector(isc_status);
		Firebird::Arg::Gds newVector(isc_gbak_invalid_metadata);
		newVector.append(oldVector);
		newVector.copyTo(isc_status);
	}
	BURP_print_status (true, isc_status);

	BURP_abort ();
}

bool get_acl(BurpGlobals* tdgbl, const TEXT* owner_nm, ISC_QUAD* blob_id, ISC_QUAD* new_blob_id)
{
/**************************************
 *
 *	g e t _ a c l
 *
 **************************************
 *
 * Functional description
 *
 *	open the blob that contains the ACL list
 *	get the ACL list of a relation
 *	replace the owner of the relation in the ACL list with
 *	  the creator of the relation
 *	create a new blob
 *	store the new ACL list in the new blob
 *
 **************************************/

	static const UCHAR blr_items[] =
	{
		isc_info_blob_max_segment,
		isc_info_blob_total_length,
		isc_info_blob_num_segments
	};

	// If the blob is null, don't store it.  It will be restored as null.

	if (!blob_id->gds_quad_high && !blob_id->gds_quad_low)
		return false;

	// Open the blob and get it's vital statistics

	ISC_STATUS_ARRAY status_vector;
	UserBlob blob(status_vector);

	if (! blob.open(DB, gds_trans, *blob_id))
	{
		// msg 24 isc_open_blob failed
		BURP_error_redirect (status_vector, 24);
	}

	UCHAR blob_info[32];
	if (!blob.getInfo(sizeof(blr_items), blr_items, sizeof(blob_info), blob_info))
	{
		// msg 20 isc_blob_info failed
		BURP_error_redirect (status_vector, 20);
	}

	ULONG length = 0;
	UCHAR item;
	USHORT max_segment;
	ULONG num_segments;
	const UCHAR* p = blob_info;

	while ((item = *p++) != isc_info_end)
	{
		const USHORT len = (USHORT) gds__vax_integer (p, 2);
		p += 2;
		const SLONG n = gds__vax_integer (p, len);
		p += len;
		switch (item)
		{
		case isc_info_blob_max_segment:
			max_segment = (USHORT) n;
			break;

		case isc_info_blob_total_length:
			length = n;
			break;

		case isc_info_blob_num_segments:
			num_segments = n;

			// we assume that the ACL list was written out as
			// in one big segment

			if (num_segments > 1)
			{
				// CVC: I can't see the effect of assert(true)
				fb_assert (num_segments > 1);
			}
			break;

		default:
			// msg 79 don't understand blob info item %ld
			BURP_print (false, 79, SafeArg() << int(item));
			// CVC: do you return, without closing the blob, dear function???
			if (!blob.close())
			{
				BURP_error_redirect (status_vector, 23);
				// msg 23 isc_close_blob failed
			}
			return false;
		}
	}

	if (!length)
	{
		if (!blob.close())
		{
			BURP_error_redirect (status_vector, 23);
			// msg 23 isc_close_blob failed
		}
		return false;
	}

	// Rdb sometimes gets the length messed up

	if (length < max_segment)
		length = max_segment;

	fb_assert(length <= ULONG(MAX_SLONG));

	// Allocate a buffer large enough for the largest segment and start grinding.

	BlobBuffer static_buffer;
	UCHAR* buffer = static_buffer.getBuffer(length);

	FB_SIZE_T return_length = 0;
	if (!blob.getData(length, buffer, return_length))
	{
		// msg 22 gds_$get_segment failed
		BURP_error_redirect (status_vector, 22);
	}
	// protect ourselves
	length = return_length;

	if (!blob.close())
	{
		// msg 23 isc_close_blob failed
		BURP_error_redirect (status_vector, 23);
	}

	const UCHAR* from = buffer + 3; // skip ACL_version, ACL_id_list, and id_person
	const SLONG id_person_len = (SLONG) *from;

	const UCHAR* c_1 = (UCHAR*) owner_nm;
	const size_t owner_nm_len = strlen(owner_nm);
	fb_assert(owner_nm_len <= size_t(MAX_UCHAR));

	// If some day, ACLs become bigger than MAX_SLONG, we should review this code.
	const SLONG bufSize = SLONG(length) - id_person_len + SLONG(owner_nm_len);
	fb_assert(bufSize > 0);
	BlobBuffer new_static_buffer;
	UCHAR* const new_buffer = new_static_buffer.getBuffer(bufSize);

	from = buffer;
	UCHAR* to = new_buffer;
	*to++ = *from++; // copy ACL_verion
	*to++ = *from++; // copy ACL_id_list
	*to++ = *from++; // copy id_person
	*to++ = UCHAR(owner_nm_len);

	FB_SIZE_T new_len = 4; //new_len + 4; Previously, new_len was set to zero at the top
	// from = buffer + id_person_len + 4; redundant, see 2nd loop below.
	for (ULONG cnt = 0; cnt < owner_nm_len; cnt++)
	{
		*to++ = *c_1++;
		new_len++;
	}

	const UCHAR* const end_buffer = buffer + length;
	for (from = buffer + id_person_len + 4; from < end_buffer; from++)
	{
		*to++ = *from;
		new_len++;
	}

	if (!blob.create(DB, gds_trans, *new_blob_id))
	{
		// msg 37 isc_create_blob failed
		BURP_error_redirect (status_vector, 37);
	}

	if (!blob.putData(new_len, new_buffer))
	{
		// msg 38 isc_put_segment failed
		BURP_error_redirect (status_vector, 38);
	}

	if (!blob.close())
	{
		// msg 23 isc_close_blob failed
		BURP_error_redirect (status_vector, 23);
	}

	return true;
}

void get_array(BurpGlobals* tdgbl, burp_rel* relation, UCHAR* record_buffer)
{
/**************************************
 *
 *	g e t _ a r r a y
 *
 **************************************
 *
 * Functional description
 *	Read array data from input file to nice,
 *	shiney, new array.
 *
 **************************************/
	burp_fld*		field = NULL;
	ISC_STATUS_ARRAY	status_vector;
	USHORT		count, field_number, field_length = 0;
	UCHAR*		buffer = NULL;
	UCHAR*		p = NULL;
	UCHAR		blr_buffer[200]; // enough for a sdl with 16 dimensions
	lstring		xdr_slice;

	// don't free something you don't allocate
	lstring xdr_buffer;
	xdr_buffer.lstr_allocated = 0;
	xdr_buffer.lstr_address = NULL;

	// Pick up attributes
	SLONG		fld_ranges[2 * MAX_DIMENSION];
	SLONG       slice_length = 0;
	SLONG       *range;
	const SLONG* end_ranges;
	scan_attr_t scan_next_attr;
	skip_init(&scan_next_attr);
	att_type	attribute;
	while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_blob_data)
	{
		switch (attribute)
		{
		case att_blob_field_number:
			field_number = (USHORT) get_int32(tdgbl);
			for (field = relation->rel_fields; field; field = field->fld_next)
			{
				if (field->fld_number == field_number)
					break;
			}
			if (!field) {
				BURP_error_redirect (NULL, 36);	// msg 36 Can't find field for blob
			}

			field_length = field->fld_length;
			if (field->fld_type == blr_varying)
				field_length += sizeof(USHORT);
			slice_length = field_length;
			//
			// Copy the ranges onto a buffer and let the program
			// mess with the copy rather than the original
			//
			memcpy(fld_ranges, field->fld_ranges, sizeof(fld_ranges));
			break;

		case att_array_dimensions:
			field->fld_dimensions = (SSHORT) get_int32(tdgbl);
			end_ranges = fld_ranges + 2 * field->fld_dimensions;
			for (range = fld_ranges; range < end_ranges; range += 2)
			{
				if (get_attribute(&attribute, tdgbl) != att_array_range_low)
					bad_attribute (scan_next_attr, attribute, 58);
					// msg 58 array
				else
					range[0] = get_int32(tdgbl);
				if (get_attribute(&attribute, tdgbl) != att_array_range_high)
					bad_attribute (scan_next_attr, attribute, 58);
					// msg 58 array
				else
					range[1] = get_int32(tdgbl);
				slice_length *= (range[1] - range[0] + 1);
			}
			break;

		default:
			bad_attribute (scan_next_attr, attribute, 58);
			// msg 58 array
			break;
		}
	}

	SLONG return_length = get(tdgbl);
	return_length |= get(tdgbl) << 8;
	return_length |= get(tdgbl) << 16;
	return_length |= get(tdgbl) << 24;

	ISC_QUAD* blob_id = (ISC_QUAD*) ((UCHAR*) record_buffer + field->fld_offset);

	SLONG last_element_dim[MAX_DIMENSION];
	if (return_length != slice_length)
	{
		int upper, lower;
		//
		// Ugh!  The full array wasn't returned and versions of gbak prior to
		// V3.2I don't explicitly signal this.  We must recompute the top
		// element to restore.
		//
		// Double Ugh!  gbak (Versions prior to 5.0) while backing up calculates
		// the top dimensions incorrectly So whatever was written as top dimensions
		// is useless. 5.0 gbak has written correct dimensions, but what the heck
		// we'll calculate it again
		//

		int elements_remaining = return_length / field_length;
		//
		// Backup (versions prior to 5.0) has surely written wrong dimensions.
		// Ignore whatever is read in fld_ranges and calculate the dimensions
		// of the last element. field->fld_ranges has the max dimensions.
		// last_element_dim holds only the upper bounds of each dimension.
		//
		for (int i1 = 0, i3 = 0; i1 < field->fld_dimensions; i1++)
		{
			int divisor = 1;
			for (int i2 = (2 * (i1 + 1) + 1); i2 <= field->fld_dimensions * 2; i2 += 2)
				divisor *= (field->fld_ranges[i2] - field->fld_ranges[i2 - 1] + 1);
			last_element_dim[i1] = (elements_remaining - 1) / divisor + field->fld_ranges[i3];
			elements_remaining  -= (last_element_dim[i1] - field->fld_ranges[i3]) * divisor;
			i3 += 2;
		}

		int current_dim;
#ifdef DEBUG
		fprintf(stderr, "\nLast element upper bounds read from backup file:\n");
		for (current_dim = 1; current_dim < field->fld_dimensions * 2; current_dim += 2)
			fprintf(stderr, "%ld ", fld_ranges[current_dim]);
		fprintf(stderr, "\nCalculated Last element upper bounds :\n");
		for (current_dim = 0; current_dim < field->fld_dimensions; current_dim++)
			fprintf(stderr, "%ld ", last_element_dim[current_dim]);
		fprintf(stderr, "return_length = %ld\n", return_length);
		fprintf(stderr, "elements_returned = %ld\n", return_length / field_length);
		fprintf(stderr, "Max dims[");
		for (current_dim = 1; current_dim < field->fld_dimensions * 2; current_dim += 2)
			fprintf(stderr, "%ld ", field->fld_ranges[current_dim]);
		fprintf(stderr, "]");
#endif
		int data_at = 0;
		//
		// We have an irregurlar shaped slice to write. The following for loop
		// chops the array into writable rectangular/square slice and sends it
		// to the engine. When the loop cycles through all dimensions, we would
		// have written the whole of the irregular slice.
		//
		for (current_dim = 0; current_dim < field->fld_dimensions; current_dim++)
		{
			UCHAR* blr = blr_buffer;
			bool dont_write = false;

			// build the sdl

			add_byte(blr, isc_sdl_version1);

			add_byte(blr, isc_sdl_struct);
			add_byte(blr, 1);

			switch (field->fld_type)
			{
			case blr_text:
			case blr_varying:
				if (field->fld_type == blr_text)
					add_byte(blr, blr_text2);
				else
					add_byte(blr, blr_varying2);
				add_word(blr, field->fld_character_set_id);
				add_word(blr, field->fld_length);
				break;
			case blr_short:
			case blr_long:
			case blr_quad:
			case blr_int64:
				add_byte(blr, field->fld_type);
				add_byte(blr, field->fld_scale);
				break;
			default:
				add_byte(blr, field->fld_type);
			}

			add_byte(blr, isc_sdl_relation);
			add_string(blr, relation->rel_name);
			add_byte(blr, isc_sdl_field);
			add_string(blr, field->fld_name);

			// each element spec starts here

#ifdef DEBUG
			fprintf(stderr, "\nBounds written[");
#endif
			int elements_written = 1;
			end_ranges = field->fld_ranges + 2 * field->fld_dimensions;
			//
			// Here is the important work. Calculate the the bounds to be written
			// so that the resulting slice is a rectangular/square slice.
			// For a 2 dimensional array of size 1..N, 1..M, which is partially
			// filled, we have already calculated the dims of last element. Say
			// if this was x,y (x is row, y is column) then we do
			//    isc_put_slice(1..x-1, 1..M);
			//    isc_put_slice(x..x, 1..y);
			// similarly for a 3D array [N,M,K] whose last element dims are (x,y,z)
			//    isc_put_slice(1..x-1, 1..M, 1..K);
			//    isc_put_slice(x..x, 1..y-1, 1..K);
			//    isc_put_slice(x..x, y..y, 1..z);
			// This is applicable for any number of dimensions.
			// Special cases:
			// for example in case of a 2D array (10,10) and if the last element
			// dims were (1,2), we would just do a isc_put_slice(1..1, 1..2).
			// This is applied for any number of dimensions.
			//
			for (range = field->fld_ranges, count = 0; range < end_ranges; range += 2, count++)
			{
				add_byte(blr, isc_sdl_do2);
				add_byte(blr, count);
				//
				// Normally we loop through all dimensions chopping off slices
				// and writing them. This works fine but this also means that
				// we blindly put slices without actually figuring out if we
				// really need to do so. For eg: if we have a 2D array of
				// size [10,4] and the last element dims are [6,4] then all
				// we need to do is is to put one slice as
				//   isc_put_slice(1..6,1..4)
				// rather than looping through the dimensions and putting
				//   isc_put_slice(1..5,1..4)
				//   isc_put_slice(6..6,1..4)
				// we could extend this logic to any no of dims. The following
				// if condition figures out such cases. This combined with
				// the Special case should optimize the no of isc_put_slice
				// we perform.
				//
				if (current_dim + 1 == field->fld_dimensions - 1 &&
					field->fld_dimensions - count == 2 && last_element_dim[count + 1]  == range[3])
				{
					add_byte(blr, isc_sdl_long_integer);
					add_long(blr, range[0]);
					lower = range[0];
					add_byte(blr, isc_sdl_long_integer);
					add_long(blr, last_element_dim[count]);
					upper = last_element_dim[count];
					elements_written *= (upper - lower + 1);
					range += 2;
					count++;
					add_byte(blr, isc_sdl_do2);
					add_byte(blr, count);
					add_byte(blr, isc_sdl_long_integer);
					add_long(blr, range[0]);
					lower = range[0];
					add_byte(blr, isc_sdl_long_integer);
					add_long(blr, last_element_dim[count]);
					upper = last_element_dim[count];
					elements_written *= (upper - lower + 1);
					++current_dim;
					break;
				}
				if (current_dim == count)
				{
					add_byte(blr, isc_sdl_long_integer);
					add_long(blr, range[0]);
					lower = range[0];
					add_byte(blr, isc_sdl_long_integer);
					upper = (current_dim == field->fld_dimensions - 1) ?
						last_element_dim[count] : (last_element_dim[count] - 1);
					if (upper < range[0])
					{
						// see Special Case above

						dont_write = true;
						break;
					}
					add_long(blr, upper);
				}
				else if (current_dim < count)
				{
					add_byte(blr, isc_sdl_long_integer);
					add_long(blr, range[0]);
					add_byte(blr, isc_sdl_long_integer);
					add_long(blr, range[1]);
					upper = range[1];
					lower = range[0];
				}
				else if (current_dim > count)
				{
					add_byte(blr, isc_sdl_long_integer);
					add_long(blr, last_element_dim[count]);
					add_byte(blr, isc_sdl_long_integer);
					add_long(blr, last_element_dim[count]);
					upper = lower = last_element_dim[count];
				}
				elements_written *= (upper - lower + 1);
#ifdef DEBUG
				fprintf(stderr, "%d..%d ", lower, upper);
#endif
			}
			if (dont_write)
				continue;
#ifdef DEBUG
			fprintf(stderr, "]");
			fprintf(stderr, "\n Elements Written=%d  ", elements_written);
#endif

			add_byte(blr, isc_sdl_element);
			add_byte(blr, 1);
			add_byte(blr, isc_sdl_scalar);
			add_byte(blr, 0);
			add_byte(blr, field->fld_dimensions);

			for (count = 0; count < field->fld_dimensions; count++)
			{
				add_byte(blr, isc_sdl_variable);
				add_byte(blr, count);
			}

			add_byte(blr, isc_sdl_eoc);

#ifdef DEBUG
			if (debug_on)
				PRETTY_print_sdl (blr_buffer, NULL, NULL, 0);
#endif

			const USHORT blr_length = blr - blr_buffer;

			if (data_at == 0)
			{
				buffer = BURP_alloc (return_length);
				SLONG lcount = 0;
				if (tdgbl->gbl_sw_transportable)
				{
					if (get_attribute(&attribute, tdgbl) != att_xdr_array)
					{
						// msg 55 Expected XDR record length
						BURP_error_redirect (NULL, 55);
					}
					else
					{
						lcount = get(tdgbl);
						lcount |= get(tdgbl) << 8;
						lcount |= get(tdgbl) << 16;
						lcount |= get(tdgbl) << 24;
						xdr_buffer.lstr_length = xdr_buffer.lstr_allocated = lcount;
						xdr_buffer.lstr_address = BURP_alloc(lcount);
						xdr_slice.lstr_allocated = xdr_slice.lstr_length = return_length;
						xdr_slice.lstr_address = buffer;
						p = xdr_buffer.lstr_address;
					}
				}
				else
				{
					p = buffer;
					lcount = return_length;
				}

				if (lcount)
					get_block(tdgbl, p, lcount);

				if (tdgbl->gbl_sw_transportable)
					CAN_slice (&xdr_buffer, &xdr_slice, FALSE, /*blr_length,*/ blr_buffer);
			}

			if (isc_put_slice(status_vector, &DB, &gds_trans,
							  blob_id, blr_length, reinterpret_cast<const char*>(blr_buffer),
							  0,	// param length for subset of an array handling
							  NULL,	// param for subset of an array handling
							  elements_written * field->fld_length, buffer + data_at))
			{
				BURP_print (false, 81, field->fld_name);
				// msg 81 error accessing blob field %s -- continuing
				BURP_print_status (true, status_vector);
#ifdef DEBUG
				PRETTY_print_sdl (blr_buffer, NULL, NULL, 0);
#endif
				return;
			}
			data_at += elements_written * field->fld_length;
#ifdef DEBUG
			fprintf(stderr, "next data_at = %d\n", data_at);
#endif
		}
	}
	else
	{
		// This is the regular case we've got the entire array
		UCHAR* blr = blr_buffer;

		// build the sdl

		add_byte(blr, isc_sdl_version1);

		add_byte(blr, isc_sdl_struct);
		add_byte(blr, 1);

		switch (field->fld_type)
		{
		case blr_text:
		case blr_varying:
			if (field->fld_type == blr_text)
				add_byte(blr, blr_text2);
			else
				add_byte(blr, blr_varying2);
			add_word(blr, field->fld_character_set_id);
			add_word(blr, field->fld_length);
			break;
		case blr_short:
		case blr_long:
		case blr_quad:
		case blr_int64:
			add_byte(blr, field->fld_type);
			add_byte(blr, field->fld_scale);
			break;
		default:
			add_byte(blr, field->fld_type);
		}

		add_byte(blr, isc_sdl_relation);
		add_string(blr, relation->rel_name);
		add_byte(blr, isc_sdl_field);
		add_string(blr, field->fld_name);

		// each element spec starts here

		for (range = fld_ranges, count = 0; range < end_ranges; range += 2, count++)
		{
			add_byte(blr, isc_sdl_do2);
			add_byte(blr, count);
			add_byte(blr, isc_sdl_long_integer);
			add_long(blr, range[0]);
			add_byte(blr, isc_sdl_long_integer);
			add_long(blr, range[1]);
		}

		add_byte(blr, isc_sdl_element);
		add_byte(blr, 1);
		add_byte(blr, isc_sdl_scalar);
		add_byte(blr, 0);
		add_byte(blr, field->fld_dimensions);

		for (count = 0; count < field->fld_dimensions; count++)
		{
			add_byte(blr, isc_sdl_variable);
			add_byte(blr, count);
		}

		add_byte(blr, isc_sdl_eoc);

#ifdef DEBUG
		if (debug_on)
			PRETTY_print_sdl (blr_buffer, NULL, NULL, 0);
#endif

		const USHORT blr_length = blr - blr_buffer;

		buffer = BURP_alloc (return_length);
		SLONG lcount = 0;
		if (tdgbl->gbl_sw_transportable)
		{
			if (get_attribute(&attribute, tdgbl) != att_xdr_array)
			{
				BURP_error_redirect (NULL, 55);
				// msg 55 Expected XDR record length
			}
			else
			{
				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 (false, 81, field->fld_name);
			// msg 81 error accessing blob field %s -- continuing
			BURP_print_status (false, status_vector);
#ifdef DEBUG
			PRETTY_print_sdl (blr_buffer, NULL, NULL, 0);
#endif
			return;
		}
	}

	BURP_free (buffer);
	if (tdgbl->gbl_sw_transportable && xdr_buffer.lstr_allocated)
		BURP_free (xdr_buffer.lstr_address);
}

void get_blob(BurpGlobals* tdgbl, const burp_fld* fields, UCHAR* record_buffer)
{
/**************************************
 *
 *	g e t _ b l o b
 *
 **************************************
 *
 * Functional description
 *	Read blob attributes and copy data from input file to nice,
 *	shiny, new blob.
 *
 **************************************/

	// Pick up attributes

	ULONG segments = 0;
	USHORT field_number = MAX_USHORT;
	USHORT max_segment = 0;
	UCHAR blob_type = 0;

	att_type	attribute;
	scan_attr_t	scan_next_attr;
	skip_init(&scan_next_attr);
	while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_blob_data)
	{
		switch (attribute)
		{
		case att_blob_field_number:
			field_number = (USHORT) get_int32(tdgbl);
			break;

		case att_blob_max_segment:
			max_segment = (USHORT) get_int32(tdgbl);
			break;

		case att_blob_number_segments:
			segments = get_int32(tdgbl);
			break;

		case att_blob_type:
			blob_type = (UCHAR) get_int32(tdgbl);
			break;

		default:
			bad_attribute(scan_next_attr, attribute, 64);
			// msg 64 blob
			break;
		}
	}

	// Find the field associated with the blob
	const burp_fld* field;
	for (field = fields; field; field = field->fld_next)
	{
		if (field->fld_number == field_number)
			break;
	}

	if (!field)
	{
		BURP_error_redirect (NULL, 36);
		// msg 36 Can't find field for blob
	}

	// Create new blob

	ISC_QUAD* blob_id = (ISC_QUAD*) ((UCHAR*) record_buffer + field->fld_offset);
	ISC_STATUS_ARRAY status_vector;
	UserBlob blob(status_vector);
	const UCHAR blob_desc[] = {isc_bpb_version1, isc_bpb_type, 1, blob_type};

	if (!blob.create(DB, gds_trans, *blob_id, sizeof(blob_desc), blob_desc))
	{
		BURP_error_redirect (status_vector, 37);
		// msg 37 isc_create_blob failed
	}

	// Allocate blob buffer if static buffer is too short
	BlobBuffer static_buffer;
	UCHAR* const buffer = static_buffer.getBuffer(max_segment);

	// Eat up blob segments

	for (; segments > 0; --segments )
	{
		USHORT length = get(tdgbl);
		length |= get(tdgbl) << 8;
		if (length)
		{
			get_block(tdgbl, buffer, length);
		}
		if (!blob.putSegment(length, buffer))
		{
			BURP_error_redirect (status_vector, 38);
			// msg 38 isc_put_segment failed
		}
	}

	if (!blob.close())
		BURP_error_redirect (status_vector, 23);
		// msg 23 isc_close_blob failed
}


void get_blr_blob(BurpGlobals* tdgbl, ISC_QUAD& blob_id, bool glb_trans)
{
/**************************************
 *
 *	g e t _ b l r _ b l o b
 *
 **************************************
 *
 * Functional description
 *	Read blob attributes and copy data from input file to nice,
 *	shiney, new blob.
 *
 **************************************/

	FB_SIZE_T length = get_int32(tdgbl);

	// Create new blob

	isc_tr_handle local_trans;
	if (glb_trans && tdgbl->global_trans)
		local_trans = tdgbl->global_trans;
	else
		local_trans = gds_trans;

	ISC_STATUS_ARRAY	status_vector;
	UserBlob blob(status_vector);
	if (!blob.create(DB, local_trans, blob_id))
	{
		BURP_error_redirect (status_vector, 37);
		// msg 37 isc_create_blob failed
	}

	BlobBuffer static_buffer;
	UCHAR* const buffer = static_buffer.getBuffer(length + 1);

	if (length)
	{
		UCHAR* p = get_block(tdgbl, buffer, length);
		// Make sure it has an eoc
		if (p[-1] != blr_eoc)
		{
			p[0] = blr_eoc;
			length++;
		}
	}

	if (!blob.putData(length, buffer))
	{
		BURP_error_redirect (status_vector, 38);
		// msg 38 isc_put_segment failed
	}

	if (!blob.close())
		BURP_error_redirect (status_vector, 23);
		// msg 23 isc_close_blob failed
}

bool get_character_set(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ c h a r a c t e r _ s e t
 *
 **************************************
 *
 * Functional description
 *	Restore data for user defined character sets
 *
 **************************************/
	class AbortException
	{
	};

	att_type		attribute;
	scan_attr_t		scan_next_attr;

	try
	{
		if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
		{
			GDS_NAME charset_name;
			bool securityClass = false;

			STORE (REQUEST_HANDLE tdgbl->handles_get_character_sets_req_handle1)
				X IN RDB$CHARACTER_SETS
				X.RDB$CHARACTER_SET_NAME.NULL = TRUE;
				X.RDB$FORM_OF_USE.NULL = TRUE;
				X.RDB$NUMBER_OF_CHARACTERS.NULL = TRUE;
				X.RDB$DEFAULT_COLLATE_NAME.NULL = TRUE;
				X.RDB$CHARACTER_SET_ID.NULL = TRUE;
				X.RDB$SYSTEM_FLAG = 0;
				X.RDB$SYSTEM_FLAG.NULL = FALSE;
				X.RDB$DESCRIPTION.NULL = TRUE;
				X.RDB$FUNCTION_NAME.NULL = TRUE;
				X.RDB$BYTES_PER_CHARACTER.NULL = TRUE;
				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_charset_name:
						X.RDB$CHARACTER_SET_NAME.NULL = FALSE;
						GET_TEXT(X.RDB$CHARACTER_SET_NAME);
						BURP_verbose (msgVerbose_restore_charset, X.RDB$CHARACTER_SET_NAME);
						break;

					case att_charset_form:
						X.RDB$FORM_OF_USE.NULL = FALSE;
						GET_TEXT(X.RDB$FORM_OF_USE);
						break;

					case att_charset_numchar:
						X.RDB$NUMBER_OF_CHARACTERS.NULL = FALSE;
						X.RDB$NUMBER_OF_CHARACTERS = (USHORT) get_int32(tdgbl);
						break;

					case att_charset_coll:
						X.RDB$DEFAULT_COLLATE_NAME.NULL = FALSE;
						GET_TEXT(X.RDB$DEFAULT_COLLATE_NAME);
						break;

					case att_charset_id:
						X.RDB$CHARACTER_SET_ID.NULL = FALSE;
						X.RDB$CHARACTER_SET_ID = (USHORT) get_int32(tdgbl);
						break;

					case att_charset_sysflag:
						X.RDB$SYSTEM_FLAG.NULL = FALSE;
						X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
						break;

					case att_charset_description:
						X.RDB$DESCRIPTION.NULL = FALSE;
						get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
						break;

					case att_charset_funct:
						X.RDB$FUNCTION_NAME.NULL = FALSE;
						GET_TEXT(X.RDB$FUNCTION_NAME);
						break;

					case att_charset_bytes_char:
						X.RDB$BYTES_PER_CHARACTER.NULL = FALSE;
						X.RDB$BYTES_PER_CHARACTER = (USHORT) get_int32(tdgbl);
						break;

					case att_charset_security_class:
						if (tdgbl->RESTORE_format >= 10)
						{
							GET_TEXT(X.RDB$SECURITY_CLASS);
							fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false);
							X.RDB$SECURITY_CLASS.NULL = FALSE;
							securityClass = true;
						}
						else
							bad_attribute(scan_next_attr, attribute, msgErr_restore_charset);
						break;

					case att_charset_owner_name:
						if (tdgbl->RESTORE_format >= 10)
						{
							GET_TEXT(X.RDB$OWNER_NAME);
							X.RDB$OWNER_NAME.NULL = FALSE;
						}
						else
							bad_attribute(scan_next_attr, attribute, msgErr_restore_charset);
						break;

					default:
						bad_attribute(scan_next_attr, attribute, msgErr_restore_charset);
						// 213 character set
						break;
					}
				}

				if (X.RDB$CHARACTER_SET_ID.NULL && !X.RDB$DEFAULT_COLLATE_NAME.NULL &&
					!X.RDB$CHARACTER_SET_NAME.NULL)
				{
					tdgbl->defaultCollations.add(
						Firebird::Pair<Firebird::NonPooled<Firebird::MetaName, Firebird::MetaName> >(
							X.RDB$CHARACTER_SET_NAME, X.RDB$DEFAULT_COLLATE_NAME));
					throw AbortException();	// prevent the STORE
				}

				strcpy(charset_name, X.RDB$CHARACTER_SET_NAME);

			END_STORE;
			ON_ERROR
				general_on_error ();
			END_ERROR;

			collect_missing_privs(tdgbl, obj_charset, charset_name, securityClass);
		}
		else
		{
			STORE (REQUEST_HANDLE tdgbl->handles_get_character_sets_req_handle1)
				X IN RDB$CHARACTER_SETS
				X.RDB$CHARACTER_SET_NAME.NULL = TRUE;
				X.RDB$FORM_OF_USE.NULL = TRUE;
				X.RDB$NUMBER_OF_CHARACTERS.NULL = TRUE;
				X.RDB$DEFAULT_COLLATE_NAME.NULL = TRUE;
				X.RDB$CHARACTER_SET_ID.NULL = TRUE;
				X.RDB$SYSTEM_FLAG = 0;
				X.RDB$SYSTEM_FLAG.NULL = FALSE;
				X.RDB$DESCRIPTION.NULL = TRUE;
				X.RDB$FUNCTION_NAME.NULL = TRUE;
				X.RDB$BYTES_PER_CHARACTER.NULL = TRUE;

				skip_init(&scan_next_attr);
				while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
				{
					switch (attribute)
					{
					case att_charset_name:
						X.RDB$CHARACTER_SET_NAME.NULL = FALSE;
						GET_TEXT(X.RDB$CHARACTER_SET_NAME);
						BURP_verbose (msgVerbose_restore_charset, X.RDB$CHARACTER_SET_NAME);
						break;

					case att_charset_form:
						X.RDB$FORM_OF_USE.NULL = FALSE;
						GET_TEXT(X.RDB$FORM_OF_USE);
						break;

					case att_charset_numchar:
						X.RDB$NUMBER_OF_CHARACTERS.NULL = FALSE;
						X.RDB$NUMBER_OF_CHARACTERS = (USHORT) get_int32(tdgbl);
						break;

					case att_charset_coll:
						X.RDB$DEFAULT_COLLATE_NAME.NULL = FALSE;
						GET_TEXT(X.RDB$DEFAULT_COLLATE_NAME);
						break;

					case att_charset_id:
						X.RDB$CHARACTER_SET_ID.NULL = FALSE;
						X.RDB$CHARACTER_SET_ID = (USHORT) get_int32(tdgbl);
						break;

					case att_charset_sysflag:
						X.RDB$SYSTEM_FLAG.NULL = FALSE;
						X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
						break;

					case att_charset_description:
						X.RDB$DESCRIPTION.NULL = FALSE;
						get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
						break;

					case att_charset_funct:
						X.RDB$FUNCTION_NAME.NULL = FALSE;
						GET_TEXT(X.RDB$FUNCTION_NAME);
						break;

					case att_charset_bytes_char:
						X.RDB$BYTES_PER_CHARACTER.NULL = FALSE;
						X.RDB$BYTES_PER_CHARACTER = (USHORT) get_int32(tdgbl);
						break;

					case att_charset_security_class:
					case att_charset_owner_name:
						if (tdgbl->RESTORE_format >= 10)
							eat_text(tdgbl);
						else
							bad_attribute(scan_next_attr, attribute, msgErr_restore_charset);
						break;

					default:
						bad_attribute(scan_next_attr, attribute, msgErr_restore_charset);
						// 213 character set
						break;
					}
				}

				if (X.RDB$CHARACTER_SET_ID.NULL && !X.RDB$DEFAULT_COLLATE_NAME.NULL &&
					!X.RDB$CHARACTER_SET_NAME.NULL)
				{
					tdgbl->defaultCollations.add(
						Firebird::Pair<Firebird::NonPooled<Firebird::MetaName, Firebird::MetaName> >(
							X.RDB$CHARACTER_SET_NAME, X.RDB$DEFAULT_COLLATE_NAME));
					throw AbortException();	// prevent the STORE
				}

			END_STORE;
			ON_ERROR
				general_on_error ();
			END_ERROR;
		}
	}
	catch (const AbortException&)
	{
	}

	return true;
}

bool get_chk_constraint(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ c h k _ c o n s t r a i n t
 *
 **************************************
 *
 * Functional description
 *	Restore data for check constraints.
 *
 **************************************/
	att_type		attribute;
	scan_attr_t		scan_next_attr;

	STORE (REQUEST_HANDLE tdgbl->handles_get_chk_constraint_req_handle1)
		X IN RDB$CHECK_CONSTRAINTS
		X.RDB$CONSTRAINT_NAME.NULL = TRUE;
		X.RDB$TRIGGER_NAME.NULL = TRUE;

		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_chk_constraint_name:
				X.RDB$CONSTRAINT_NAME.NULL = FALSE;
				GET_TEXT(X.RDB$CONSTRAINT_NAME);
				break;

			case att_chk_trigger_name:
				X.RDB$TRIGGER_NAME.NULL = FALSE;
				GET_TEXT(X.RDB$TRIGGER_NAME);
				break;

			default:
				bad_attribute(scan_next_attr, attribute, 286);
				// msg 286 check constraint
				break;
			}
		}
	END_STORE;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	return true;
}

bool get_collation(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ c o l l a t i o n
 *
 **************************************
 *
 * Functional description
 *	Restore data for user defined collations
 *
 **************************************/
	att_type		attribute;
	scan_attr_t		scan_next_attr;

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		GDS_NAME coll_name;
		bool securityClass = false;

		STORE (REQUEST_HANDLE tdgbl->handles_get_collation_req_handle1)
			X IN RDB$COLLATIONS
			X.RDB$COLLATION_NAME.NULL = TRUE;
			X.RDB$COLLATION_ID.NULL = TRUE;
			X.RDB$CHARACTER_SET_ID.NULL = TRUE;
			X.RDB$COLLATION_ATTRIBUTES.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$FUNCTION_NAME.NULL = TRUE;
			X.RDB$BASE_COLLATION_NAME.NULL = TRUE;
			X.RDB$SPECIFIC_ATTRIBUTES.NULL = TRUE;
			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_coll_name:
					X.RDB$COLLATION_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$COLLATION_NAME);
					BURP_verbose(msgVerbose_restore_collation, X.RDB$COLLATION_NAME);
					break;

				case att_coll_id:
					X.RDB$COLLATION_ID.NULL = FALSE;
					X.RDB$COLLATION_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_cs_id:
					X.RDB$CHARACTER_SET_ID.NULL = FALSE;
					X.RDB$CHARACTER_SET_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_attr:
					X.RDB$COLLATION_ATTRIBUTES.NULL = FALSE;
					X.RDB$COLLATION_ATTRIBUTES = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_subtype:	// No longer used: 93-11-15 DBS
					// still present to handle V4 R&D gbak files
					get_int32(tdgbl);
					break;

				case att_coll_sysflag:
					X.RDB$SYSTEM_FLAG.NULL = FALSE;
					X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
					break;

				case att_coll_funct:
					X.RDB$FUNCTION_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$FUNCTION_NAME);
					break;

				case att_coll_base_collation_name:
					if (tdgbl->RESTORE_format >= 7)
					{
						X.RDB$BASE_COLLATION_NAME.NULL = FALSE;
						GET_TEXT(X.RDB$BASE_COLLATION_NAME);
					}
					else
						bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					break;

				case att_coll_specific_attr:
					if (tdgbl->RESTORE_format >= 7)
					{
						X.RDB$SPECIFIC_ATTRIBUTES.NULL = FALSE;
						get_source_blob (tdgbl, X.RDB$SPECIFIC_ATTRIBUTES, false);
					}
					else
						bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					break;

				case att_coll_security_class:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$SECURITY_CLASS);
						fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false);
						X.RDB$SECURITY_CLASS.NULL = FALSE;
						securityClass = true;
					}
					else
						bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					break;

				case att_coll_owner_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$OWNER_NAME);
						X.RDB$OWNER_NAME.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					// Msg 215 collation
					break;
				}
			}

			strcpy(coll_name, X.RDB$COLLATION_NAME);

		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;

		collect_missing_privs(tdgbl, obj_collation, coll_name, securityClass);
	}
	else if (tdgbl->runtimeODS >= DB_VERSION_DDL11)
	{
		// This includes DDL11_0 that doesn't know to ignore unknown system fields.
		STORE (REQUEST_HANDLE tdgbl->handles_get_collation_req_handle1)
			X IN RDB$COLLATIONS
			X.RDB$COLLATION_NAME.NULL = TRUE;
			X.RDB$COLLATION_ID.NULL = TRUE;
			X.RDB$CHARACTER_SET_ID.NULL = TRUE;
			X.RDB$COLLATION_ATTRIBUTES.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$FUNCTION_NAME.NULL = TRUE;
			X.RDB$BASE_COLLATION_NAME.NULL = TRUE;
			X.RDB$SPECIFIC_ATTRIBUTES.NULL = TRUE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{

				case att_coll_name:
					X.RDB$COLLATION_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$COLLATION_NAME);
					BURP_verbose(msgVerbose_restore_collation, X.RDB$COLLATION_NAME);
					break;

				case att_coll_id:
					X.RDB$COLLATION_ID.NULL = FALSE;
					X.RDB$COLLATION_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_cs_id:
					X.RDB$CHARACTER_SET_ID.NULL = FALSE;
					X.RDB$CHARACTER_SET_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_attr:
					X.RDB$COLLATION_ATTRIBUTES.NULL = FALSE;
					X.RDB$COLLATION_ATTRIBUTES = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_subtype:	// No longer used: 93-11-15 DBS
					// still present to handle V4 R&D gbak files
					get_int32(tdgbl);
					break;

				case att_coll_sysflag:
					X.RDB$SYSTEM_FLAG.NULL = FALSE;
					X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
					break;

				case att_coll_funct:
					X.RDB$FUNCTION_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$FUNCTION_NAME);
					break;

				case att_coll_base_collation_name:
					if (tdgbl->RESTORE_format >= 7)
					{
						X.RDB$BASE_COLLATION_NAME.NULL = FALSE;
						GET_TEXT(X.RDB$BASE_COLLATION_NAME);
					}
					else
						bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					break;

				case att_coll_specific_attr:
					if (tdgbl->RESTORE_format >= 7)
					{
						X.RDB$SPECIFIC_ATTRIBUTES.NULL = FALSE;
						get_source_blob (tdgbl, X.RDB$SPECIFIC_ATTRIBUTES, false);
					}
					else
						bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					break;

				case att_coll_security_class:
				case att_coll_owner_name:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					// Msg 215 collation
					break;
				}
			}
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else
	{
		STORE (REQUEST_HANDLE tdgbl->handles_get_collation_req_handle1)
			X IN RDB$COLLATIONS
			X.RDB$COLLATION_NAME.NULL = TRUE;
			X.RDB$COLLATION_ID.NULL = TRUE;
			X.RDB$CHARACTER_SET_ID.NULL = TRUE;
			X.RDB$COLLATION_ATTRIBUTES.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$FUNCTION_NAME.NULL = TRUE;
			X.RDB$BASE_COLLATION_NAME.NULL = TRUE;
			X.RDB$SPECIFIC_ATTRIBUTES.NULL = TRUE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{

				case att_coll_name:
					X.RDB$COLLATION_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$COLLATION_NAME);
					BURP_verbose(msgVerbose_restore_collation, X.RDB$COLLATION_NAME);
					break;

				case att_coll_id:
					X.RDB$COLLATION_ID.NULL = FALSE;
					X.RDB$COLLATION_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_cs_id:
					X.RDB$CHARACTER_SET_ID.NULL = FALSE;
					X.RDB$CHARACTER_SET_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_attr:
					X.RDB$COLLATION_ATTRIBUTES.NULL = FALSE;
					X.RDB$COLLATION_ATTRIBUTES = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_subtype:	// No longer used: 93-11-15 DBS
					// still present to handle V4 R&D gbak files
					get_int32(tdgbl);
					break;

				case att_coll_sysflag:
					X.RDB$SYSTEM_FLAG.NULL = FALSE;
					X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
					break;

				case att_coll_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
					break;

				case att_coll_funct:
					X.RDB$FUNCTION_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$FUNCTION_NAME);
					break;

				case att_coll_base_collation_name:
					if (tdgbl->RESTORE_format >= 7)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					break;

				case att_coll_specific_attr:
					if (tdgbl->RESTORE_format >= 7)
						eat_blob(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					break;

				case att_coll_security_class:
				case att_coll_owner_name:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, msgErr_restore_collation);
					// Msg 215 collation
					break;
				}
			}
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}

	return true;
}

rec_type get_data(BurpGlobals* tdgbl, burp_rel* relation, bool skip_relation)
{
/**************************************
 *
 *	g e t _ d a t a
 *
 **************************************
 *
 * Functional description
 *	Write data records for a relation.
 *
 **************************************/
	isc_req_handle  req_handle = 0;
	BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name;

	// If we're only doing meta-data, ignore data records

	if (tdgbl->gbl_sw_meta || skip_relation)
		return ignore_data(tdgbl, relation);

	// Start by counting the interesting fields

	RCRD_OFFSET offset = 0;
	ULONG length = 0;
	USHORT count = 0;

	burp_fld* field;
	for (field = relation->rel_fields; field; field = field->fld_next)
	{
		if (!(field->fld_flags & FLD_computed))
		{
			count++;
			length += field->fld_name_length;
		}
	}

	if (tdgbl->RESTORE_format >= 2)
		count += count;

	// Time to generate blr to store data.  Whoppee.

	UCHAR* const blr_buffer = (UCHAR*) BURP_alloc (200 + length + count * 18);
	UCHAR* blr = blr_buffer;

	add_byte(blr, blr_version4);
	add_byte(blr, blr_begin);
	add_byte(blr, blr_message);
	add_byte(blr, 0);				// Message number
	add_word(blr, count);	// Number of fields, counting eof

	// Let's reset count.
	count = 0;

	for (field = relation->rel_fields; field; field = field->fld_next)
	{
		if (field->fld_flags & FLD_computed)
			continue;

		// arrays are of various fld_types but are really blobs

		SSHORT dtype = field->fld_type;
		length = field->fld_length;
		SSHORT alignment = 4;

		if (field->fld_flags & FLD_array)
			dtype = blr_blob;

		if (dtype <= DTYPE_BLR_MAX)
		{
			USHORT l = gds_cvt_blr_dtype[dtype];
			alignment = type_alignments[l];
			if (l = type_lengths[l])
				length = l;
		}

		switch (dtype)
		{
		case blr_text:
		case blr_varying:
			if (dtype == blr_text)
				add_byte(blr, blr_text2);
			else
				add_byte(blr, blr_varying2);

			if (tdgbl->gbl_sw_fix_fss_data && field->fld_character_set_id == CS_UNICODE_FSS)
				add_word(blr, tdgbl->gbl_sw_fix_fss_data_id);
			else
				add_word(blr, field->fld_character_set_id);

			add_word(blr, field->fld_length);
			if (dtype == blr_varying)
				length += sizeof(USHORT);
			break;

		case blr_short:
		case blr_long:
		case blr_quad:
		case blr_int64:
			add_byte(blr, field->fld_type);
			add_byte(blr, field->fld_scale);
			break;

		case blr_float:
		case blr_double:
		case blr_timestamp:
		case blr_sql_time:
		case blr_sql_date:
			add_byte(blr, field->fld_type);
			break;

		case blr_blob:
			alignment = type_alignments[dtype_blob];
			length = type_lengths[dtype_blob];

			if (tdgbl->gbl_sw_fix_fss_data && !(field->fld_flags & FLD_array) &&
				field->fld_sub_type == isc_blob_text && field->fld_character_set_id == CS_UNICODE_FSS)
			{
				add_byte(blr, blr_blob2);
				add_word(blr, field->fld_sub_type);
				add_word(blr, tdgbl->gbl_sw_fix_fss_data_id);
			}
			else
			{
				add_byte(blr, blr_quad);
				add_byte(blr, 0);
			}

			break;

		case blr_bool:
			add_byte(blr, field->fld_type);
			break;

		default:
			BURP_error(26, true, SafeArg() << field->fld_type);
			// msg 26 datatype %ld not understood
			break;
		}
		if (alignment)
			offset = FB_ALIGN(offset, alignment);
		field->fld_offset = offset;
		field->fld_parameter = count++;
		offset += length;
	}

	// If this is format version 2, build fields for null flags

	if (tdgbl->RESTORE_format >= 2)
		for (field = relation->rel_fields; field; field = field->fld_next)
		{
			if (field->fld_flags & FLD_computed)
				continue;
			add_byte(blr, blr_short);
			add_byte(blr, 0);
			offset = FB_ALIGN(offset, sizeof(SSHORT));
			field->fld_missing_parameter = count++;
			offset += sizeof(SSHORT);
		}

	length = offset;

	// Build STORE statement

	add_byte(blr, blr_loop);
	add_byte(blr, blr_receive);
	add_byte(blr, 0);
	add_byte(blr, blr_store);
	add_byte(blr, blr_relation);
	add_string(blr, relation->rel_name);
	add_byte(blr, 0);			// context variable
	add_byte(blr, blr_begin);

	for (field = relation->rel_fields; field; field = field->fld_next)
	{
		if (field->fld_flags & FLD_computed)
			continue;
		add_byte(blr, blr_assignment);
		if (tdgbl->RESTORE_format >= 2)
		{
			add_byte(blr, blr_parameter2);
			add_byte(blr, 0);
			add_word(blr, field->fld_parameter);
			add_word(blr, field->fld_missing_parameter);
		}
		else
		{
			add_byte(blr, blr_parameter);
			add_byte(blr, 0);
			add_word(blr, field->fld_parameter);
		}
		add_byte(blr, blr_field);
		add_byte(blr, 0);
		add_string(blr, field->fld_name);
	}

	add_byte(blr, blr_end);
	add_byte(blr, blr_end);
	add_byte(blr, blr_eoc);

	// Compile request

	USHORT blr_length = blr - blr_buffer;

#ifdef DEBUG
	fb_print_blr(blr_buffer, blr_length, NULL, NULL, 0);
#endif

	FB_API_HANDLE request = 0;
	ISC_STATUS_ARRAY status_vector;

	if (isc_compile_request (status_vector, &DB, &request,
							 blr_length, reinterpret_cast<const char*>(blr_buffer)))
	{
		fb_print_blr(blr_buffer, blr_length, NULL, NULL, 0);
		if (!tdgbl->gbl_sw_incremental)
			BURP_error_redirect (status_vector, 27); // msg 27 isc_compile_request failed
		else
		{
			BURP_print_status (false, status_vector);
			BURP_free (blr_buffer);
			return ignore_data(tdgbl, relation);
		}
	}

	BURP_free (blr_buffer);
	SSHORT* buffer = NULL;

	BURP_verbose (124, relation->rel_name); // msg 124  restoring data for relation %s

	lstring data;
	data.lstr_allocated = 0;
	data.lstr_address = NULL;
	ULONG old_length = 0;

	FB_UINT64 records = 0;
	rec_type record;
	bool resync = false;

	while (true)
	{
		if (get(tdgbl) != att_data_length)
			BURP_error_redirect (NULL, 39); // msg 39 expected record length
		USHORT len = (USHORT) get_int32(tdgbl);
		if (!tdgbl->gbl_sw_transportable && len != length)
		{
#ifdef sparc
			if (!old_length)
				old_length = recompute_length(tdgbl, relation);
#endif
			if (len != old_length)
			{
				BURP_error(40, true, SafeArg() << length << len);
				// msg 40 wrong length record, expected %ld encountered %ld
			}
		}
		if (!buffer) {
			buffer = (SSHORT *) BURP_alloc (MAX (length, len));
		}

		UCHAR* p;
		if (tdgbl->gbl_sw_transportable)
		{
			if (get(tdgbl) != att_xdr_length)
				BURP_error_redirect (NULL, 55);
				// msg 55 Expected XDR record length
			else
			{
				data.lstr_length = len = (USHORT) get_int32(tdgbl);
				if (len > data.lstr_allocated)
				{
					data.lstr_allocated = len;
					if (data.lstr_address)
						BURP_free (data.lstr_address);
					data.lstr_address = BURP_alloc(data.lstr_allocated);
				}
				p = data.lstr_address;
			}
		}
		else
			p = reinterpret_cast<UCHAR*>(buffer);
		if (get(tdgbl) != att_data_data)
			BURP_error_redirect (NULL, 41); // msg 41 expected data attribute

		if (tdgbl->gbl_sw_compress)
			decompress (tdgbl, p, len);
		else
		{
			get_block(tdgbl, p, len);
		}

		if (old_length)
			realign (tdgbl, (UCHAR*) buffer, relation);

		if (tdgbl->gbl_sw_transportable)
			CAN_encode_decode (relation, &data, (UCHAR *)buffer, FALSE);

		records++;

		if ((records % tdgbl->verboseInterval) == 0)
			BURP_verbose(107, SafeArg() << records);

		for (field = relation->rel_fields; field; field = field->fld_next)
		{
			if (!(field->fld_flags & FLD_computed))
			{
				if (field->fld_type == blr_blob || (field->fld_flags & FLD_array))
				{
					ISC_QUAD* blob_id = (ISC_QUAD*) ((SCHAR*) buffer + field->fld_offset);
					blob_id->gds_quad_high = 0;
					blob_id->gds_quad_low = 0;
				}
			}
		}

		get_record(&record, tdgbl);
		while (record == rec_blob || record == rec_array)
		{
			if (record == rec_blob)
				get_blob (tdgbl, relation->rel_fields, (UCHAR *) buffer);
			else if (record == rec_array)
				get_array (tdgbl, relation, (UCHAR *) buffer);
			get_record(&record, tdgbl);
		}

		ISC_STATUS s;

		// ASF: Preferable we should call isc_start_and_send only when records == 1, but this leaks
		// memory when there are blobs and arrays fields - CORE-3802.
		if (resync || records % 1000 == 1)
			s = isc_start_and_send(status_vector, &request, &gds_trans, 0, (USHORT) length, buffer, 0);
		else
			s = isc_send(status_vector, &request, 0, (USHORT) length, buffer, 0);

		resync = (s != 0);

		if (s)
		{
			if (status_vector[1] == isc_not_valid)
			{
				if (tdgbl->gbl_sw_incremental)
				{
					BURP_print (false, 138, relation->rel_name);
					// msg 138 validation error on field in relation %s
					BURP_print_status (false, status_vector);
				}
				else
					BURP_error_redirect (status_vector, 47);
					// msg 47 warning -- record could not be restored
			}
			else if (status_vector[1] == isc_malformed_string)
			{
				if (tdgbl->gbl_sw_incremental)
				{
					// msg 114 restore failed for record in relation %s
					BURP_print(false, 114, relation->rel_name);

					BURP_print_status(false, status_vector);
					BURP_print(false, 342);	// isc_gbak_invalid_data
				}
				else
					BURP_error_redirect(status_vector, 342);	// isc_gbak_invalid_data
			}
			else
			{
				if (tdgbl->gbl_sw_incremental && isc_sqlcode(status_vector) != -902)
				{
					BURP_print (false, 114, relation->rel_name);
					// msg 114 restore failed for record in relation %s
					BURP_print_status (false, status_vector);
				}
				else
					BURP_error_redirect (status_vector, 48);
					// msg 48 isc_send failed
			}

			records--;
		}
		if (record != rec_data)
			break;
	} // while (true)

	BURP_free (buffer);
	if (data.lstr_address)
		BURP_free (data.lstr_address);

	isc_release_request(status_vector, &request);
	if (tdgbl->gbl_sw_incremental)
	{
		BURP_verbose (72, relation->rel_name);
		// msg 72  committing data for relation %s
		COMMIT
		// existing ON_ERROR continues past error, beck
		ON_ERROR

			// Fix for bug_no 8055:
			// don't throw away the database just because an index
			// could not be made

			// don't bring the database on-line
			tdgbl->flag_on_line = false;
			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(false, 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(false, 240, index_name);
								// msg 240 Index \"%s\" failed to activate because:
								if ( error_code == isc_no_dup )
								{
									BURP_print(false, 241);
									// msg 241 The unique index has duplicate values or NULLs
									BURP_print(false, 242);
									// msg 242 Delete or Update duplicate values or NULLs, and activate index with
								}
								else
								{
									BURP_print(false, 244);
									// msg 244 Not enough disk space to create the sort file for an index
									BURP_print(false, 245);
									// msg 245 Set the TMP environment variable to a directory on a filesystem that does have enough space, and activate index with
								}
								BURP_print(false, 243, index_name);
								// msg 243 ALTER INDEX \"%s\" ACTIVE
							END_MODIFY;
						END_FOR;
						// commit one more time
						COMMIT
						ON_ERROR
							continue;
						END_ERROR
						break;
					default:
						BURP_print (false, 69, relation->rel_name);
						// msg 69 commit failed on relation %s
						BURP_print_status (false, tdgbl->status_vector);
						ROLLBACK;
						ON_ERROR
							general_on_error ();
						END_ERROR;
						break;
				} // end of switch
			} // end of while
		END_ERROR;

		EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
		if (gds_status[1])
			EXEC SQL SET TRANSACTION;
	}
	BURP_verbose (107, SafeArg() << records);
	// msg 107 %ld records restored

	return record;
}

// We have a corrupt backup, save the restore process from becoming useless.
void fix_exception(BurpGlobals* tdgbl, const char* exc_name, scan_attr_t& scan_next_attr,
	const att_type attribute, att_type& failed_attrib, UCHAR*& msg_ptr, ULONG& l2, bool& msg_seen)
{
	if (msg_seen && (tdgbl->RESTORE_format == 7 || tdgbl->RESTORE_format == 8))
	{
		if (!failed_attrib)
		{
			failed_attrib = attribute;
			BURP_print(false, 313, SafeArg() << failed_attrib << exc_name);
		}

		// Notice we use 1021 instead of 1023 because this is the maximum length
		// for this field in v2.0 and v2.1 and they produce the corrupt backups.
		const unsigned int FIELD_LIMIT = 1021;

		if (FIELD_LIMIT < l2 + 1) // not enough space
		{
			bad_attribute(scan_next_attr, failed_attrib, 287);
			return;
		}
		const unsigned int remaining = FIELD_LIMIT - l2;

		*msg_ptr++ = char(attribute); // (1)

		UCHAR* rc_ptr = get_block(tdgbl, msg_ptr, MIN(remaining - 1, 255));
		if (remaining > 1 && rc_ptr == msg_ptr) // we couldn't read anything
		{
			bad_attribute(scan_next_attr, failed_attrib, 287);
			return;
		}

		l2 += rc_ptr - msg_ptr + 1; // + 1 because (1)
		msg_ptr = rc_ptr;
		*msg_ptr = 0;

		if (l2 == FIELD_LIMIT)
			msg_seen = false;
	}
	else
		bad_attribute(scan_next_attr, attribute, 287); // msg 287 exception
}

bool get_exception(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ e x c e p t i o n
 *
 **************************************
 *
 * Functional description
 *	Reconstruct a exception.
 *
 **************************************/
	att_type	attribute;
	TEXT		temp[GDS_NAME_LEN];
	ULONG		l2 = 0;
	scan_attr_t		scan_next_attr;

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		GDS_NAME exception_name;
		bool securityClass = false;

		STORE (REQUEST_HANDLE tdgbl->handles_get_exception_req_handle1)
			X IN RDB$EXCEPTIONS
			X.RDB$EXCEPTION_NAME.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$MESSAGE.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$SECURITY_CLASS.NULL = TRUE;
			X.RDB$OWNER_NAME.NULL = TRUE;

			att_type failed_attrib = att_end;
			bool msg_seen = false; // only for att_exception_msg, not att_exception_msg2
			UCHAR* msg_ptr = reinterpret_cast<UCHAR*>(X.RDB$MESSAGE);

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_exception_name:
					if (!X.RDB$EXCEPTION_NAME.NULL)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else
					{
						const ULONG l = GET_TEXT(X.RDB$EXCEPTION_NAME);
						X.RDB$EXCEPTION_NAME.NULL = FALSE;
						MISC_terminate (X.RDB$EXCEPTION_NAME, temp, l, sizeof(temp));
						BURP_verbose (199, temp);
						// msg 199 restoring exception %s
					}
					break;

				case att_exception_description:
					if (!X.RDB$DESCRIPTION.NULL)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else
					{
						msg_seen = false;
						get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
						X.RDB$DESCRIPTION.NULL = FALSE;
					}
					break;

				case att_exception_description2:
					if (!X.RDB$DESCRIPTION.NULL)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else
					{
						msg_seen = false;
						get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
						X.RDB$DESCRIPTION.NULL = FALSE;
					}
					break;

				case att_exception_msg:
					if (msg_seen)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else if (!X.RDB$MESSAGE.NULL)
					{
						msg_seen = true;
						BURP_print(false, 312, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
						eat_text(tdgbl);
					}
					else
					{
						msg_seen = true;
						l2 = GET_TEXT(X.RDB$MESSAGE);
						msg_ptr += l2;
						X.RDB$MESSAGE.NULL = FALSE;
					}
					break;

				case att_exception_msg2:
					if (msg_seen)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else if (!X.RDB$MESSAGE.NULL)
					{
						BURP_print(false, 312, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
						eat_text2(tdgbl);
					}
					else
					{
						GET_TEXT2(X.RDB$MESSAGE);
						X.RDB$MESSAGE.NULL = FALSE;
					}
					break;

				case att_exception_security_class:
					if (!X.RDB$SECURITY_CLASS.NULL)
					{
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					}
					else
					{
						msg_seen = false;
						if (tdgbl->RESTORE_format >= 10)
						{
							GET_TEXT(X.RDB$SECURITY_CLASS);
							fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false);
							X.RDB$SECURITY_CLASS.NULL = FALSE;
							securityClass = true;
						}
						else
						{
							fix_exception(tdgbl, X.RDB$EXCEPTION_NAME, scan_next_attr, attribute,
								failed_attrib, msg_ptr, l2, msg_seen);
						}
					}
					break;

				case att_exception_owner_name:
					if (!X.RDB$OWNER_NAME.NULL)
					{
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					}
					else
					{
						msg_seen = false;
						if (tdgbl->RESTORE_format >= 10)
						{
							GET_TEXT(X.RDB$OWNER_NAME);
							X.RDB$OWNER_NAME.NULL = FALSE;
						}
						else
						{
							fix_exception(tdgbl, X.RDB$EXCEPTION_NAME, scan_next_attr, attribute,
								failed_attrib, msg_ptr, l2, msg_seen);
						}
					}
					break;

				default:
					// do we have a corrupt backup?
					fix_exception(tdgbl, X.RDB$EXCEPTION_NAME, scan_next_attr, attribute,
						failed_attrib, msg_ptr, l2, msg_seen);
					break;
				}
			}

			strcpy(exception_name, X.RDB$EXCEPTION_NAME);

		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;

		collect_missing_privs(tdgbl, obj_exception, exception_name, securityClass);
	}
	else
	{
		STORE (REQUEST_HANDLE tdgbl->handles_get_exception_req_handle1)
			X IN RDB$EXCEPTIONS
			X.RDB$EXCEPTION_NAME.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$MESSAGE.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;

			bool secclass_seen = false;
			bool ownername_seen = false;

			att_type failed_attrib = att_end;
			bool msg_seen = false; // only for att_exception_msg, not att_exception_msg2
			UCHAR* msg_ptr = reinterpret_cast<UCHAR*>(X.RDB$MESSAGE);

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_exception_name:
					if (!X.RDB$EXCEPTION_NAME.NULL)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else
					{
						const ULONG l = GET_TEXT(X.RDB$EXCEPTION_NAME);
						X.RDB$EXCEPTION_NAME.NULL = FALSE;
						MISC_terminate (X.RDB$EXCEPTION_NAME, temp, l, sizeof(temp));
						BURP_verbose (199, temp);
						// msg 199 restoring exception %s
					}
					break;

				case att_exception_description:
					if (!X.RDB$DESCRIPTION.NULL)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else
					{
						msg_seen = false;
						get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
						X.RDB$DESCRIPTION.NULL = FALSE;
					}
					break;

				case att_exception_description2:
					if (!X.RDB$DESCRIPTION.NULL)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else
					{
						msg_seen = false;
						get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
						X.RDB$DESCRIPTION.NULL = FALSE;
					}
					break;

				case att_exception_msg:
					if (msg_seen)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else if (!X.RDB$MESSAGE.NULL)
					{
						msg_seen = true;
						BURP_print(false, 312, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
						eat_text(tdgbl);
					}
					else
					{
						msg_seen = true;
						l2 = GET_TEXT(X.RDB$MESSAGE);
						msg_ptr += l2;
						X.RDB$MESSAGE.NULL = FALSE;
					}
					break;

				case att_exception_msg2:
					if (msg_seen)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else if (!X.RDB$MESSAGE.NULL)
					{
						BURP_print(false, 312, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
						eat_text2(tdgbl);
					}
					else
					{
						GET_TEXT2(X.RDB$MESSAGE);
						X.RDB$MESSAGE.NULL = FALSE;
					}
					break;

				case att_exception_security_class:
					if (secclass_seen)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else
					{
						msg_seen = false;
						if (tdgbl->RESTORE_format >= 10)
						{
							eat_text(tdgbl);
							secclass_seen = true;
						}
						else
						{
							fix_exception(tdgbl, X.RDB$EXCEPTION_NAME, scan_next_attr, attribute,
								failed_attrib, msg_ptr, l2, msg_seen);
						}
					}
					break;

				case att_exception_owner_name:
					if (ownername_seen)
						BURP_error(311, true, SafeArg() << attribute << X.RDB$EXCEPTION_NAME);
					else
					{
						msg_seen = false;
						if (tdgbl->RESTORE_format >= 10)
						{
							eat_text(tdgbl);
							ownername_seen = true;
						}
						else
						{
							fix_exception(tdgbl, X.RDB$EXCEPTION_NAME, scan_next_attr, attribute,
								failed_attrib, msg_ptr, l2, msg_seen);
						}
					}
					break;

				default:
					// do we have a corrupt backup?
					fix_exception(tdgbl, X.RDB$EXCEPTION_NAME, scan_next_attr, attribute,
						failed_attrib, msg_ptr, l2, msg_seen);
					break;
				}
			}
			// Versions prior to FB2.0 don't support a field longer than varchar(78).
			// Versions prior to FB2.5 use a field length of 1021, not 1023.
			if (tdgbl->runtimeODS < DB_VERSION_DDL11)
				X.RDB$MESSAGE[78] = 0;
			else if (tdgbl->runtimeODS < DB_VERSION_DDL11_2)
				X.RDB$MESSAGE[1021] = 0;
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}

	return true;
}


burp_fld* get_field(BurpGlobals* tdgbl, burp_rel* relation)
{
/**************************************
 *
 *	g e t _ f i e l d
 *
 **************************************
 *
 * Functional description
 *	Reconstruct a local field.
 *
 **************************************/
	att_type		attribute;
	scan_attr_t		scan_next_attr;

	// If it is a view and there is a global transaction then use it
	bool global_tr = false;
	isc_tr_handle local_trans;
	if ((relation->rel_flags & REL_view) && tdgbl->global_trans)
	{
		local_trans = tdgbl->global_trans;
		global_tr = true;
	}
	else
		local_trans = gds_trans;

	burp_fld* field = (burp_fld*) BURP_alloc_zero (sizeof(burp_fld));

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		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;
			X.RDB$VIEW_CONTEXT.NULL = TRUE;
			X.RDB$BASE_FIELD.NULL = TRUE;
			X.RDB$SECURITY_CLASS.NULL = TRUE;
			X.RDB$QUERY_NAME.NULL = TRUE;
			X.RDB$QUERY_HEADER.NULL = TRUE;
			X.RDB$EDIT_STRING.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$FIELD_POSITION.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$COMPLEX_NAME.NULL = TRUE;
			X.RDB$UPDATE_FLAG.NULL = TRUE;
			X.RDB$DEFAULT_SOURCE.NULL = TRUE;
			X.RDB$DEFAULT_VALUE.NULL = TRUE;
			X.RDB$NULL_FLAG.NULL = TRUE;
			X.RDB$COLLATION_ID.NULL = TRUE;
			// ODS 12
			X.RDB$GENERATOR_NAME.NULL = TRUE;
			X.RDB$IDENTITY_TYPE.NULL = TRUE;

			skip_init(&scan_next_attr);
			while (get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (skip_scan(&scan_next_attr), attribute)
				{
				case att_field_name:
					field->fld_name_length = GET_TEXT(field->fld_name);
					BURP_verbose (115, field->fld_name);
					// msg 115 restoring field %s
					strcpy (X.RDB$FIELD_NAME, field->fld_name);
					break;

				case att_field_source:
					GET_TEXT(X.RDB$FIELD_SOURCE);
					break;

				case att_field_security_class:
					GET_TEXT(X.RDB$SECURITY_CLASS);
					fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, true);
					X.RDB$SECURITY_CLASS.NULL = FALSE;
					break;

				case att_field_query_name:
					GET_TEXT(X.RDB$QUERY_NAME);
					X.RDB$QUERY_NAME.NULL = FALSE;
					break;

				case att_field_query_header:
					X.RDB$QUERY_HEADER.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$QUERY_HEADER, global_tr);
					break;

				case att_field_edit_string:
					GET_TEXT(X.RDB$EDIT_STRING);
					X.RDB$EDIT_STRING.NULL = FALSE;
					break;

				case att_field_position:
					X.RDB$FIELD_POSITION.NULL = FALSE;
					X.RDB$FIELD_POSITION = (USHORT) get_int32(tdgbl);
					break;

				case att_field_number:
					field->fld_number = (USHORT) get_int32(tdgbl);
					break;

				case att_field_type:
					field->fld_type = (USHORT) get_int32(tdgbl);
					break;

				case att_field_length:
					field->fld_length = (USHORT) get_int32(tdgbl);
					break;

				case att_field_scale:
					field->fld_scale = (USHORT) get_int32(tdgbl);
					break;

				case att_field_sub_type:
					field->fld_sub_type = (USHORT) get_int32(tdgbl);
					break;

				case att_field_system_flag:
					X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
					X.RDB$SYSTEM_FLAG.NULL = FALSE;
					break;

				case att_view_context:
					X.RDB$VIEW_CONTEXT = (USHORT) get_int32(tdgbl);
					X.RDB$VIEW_CONTEXT.NULL = FALSE;
					break;

				case att_field_computed_flag:
					if (get_int32(tdgbl))
						field->fld_flags |= FLD_computed;
					break;

				case att_base_field:
					GET_TEXT(X.RDB$BASE_FIELD);
					X.RDB$BASE_FIELD.NULL = FALSE;
					break;

				case att_field_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, global_tr);
					break;

				case att_field_description2:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, global_tr);
					break;

				case att_field_complex_name:
					GET_TEXT(X.RDB$COMPLEX_NAME);
					X.RDB$COMPLEX_NAME.NULL = FALSE;
					break;

				case att_field_dimensions:
					{
						field->fld_dimensions = (USHORT) get_int32(tdgbl);
						field->fld_flags |= FLD_array;
						USHORT n = field->fld_dimensions;
						for (SLONG* rp = field->fld_ranges; n; rp += 2, n--)
						{
							if (get_attribute(&attribute, tdgbl) != att_field_range_low)
								bad_attribute(scan_next_attr, attribute, 58);
							// msg 58 array
							else
								*rp = get_int32(tdgbl);
							if (get_attribute(&attribute, tdgbl) != att_field_range_high)
								bad_attribute(scan_next_attr, attribute, 58);
								// msg 58 array
							else
								*(rp + 1) = get_int32(tdgbl);
						}
					}
					break;

				case att_field_update_flag:
					X.RDB$UPDATE_FLAG.NULL = FALSE;
					X.RDB$UPDATE_FLAG = (USHORT) get_int32(tdgbl);
					break;

				case att_field_default_source:
					X.RDB$DEFAULT_SOURCE.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, global_tr);
					break;

				case att_field_default_value:
					X.RDB$DEFAULT_VALUE.NULL = FALSE;
					get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, global_tr);
					break;

				case att_field_null_flag:
					if (tdgbl->gbl_sw_novalidity) {
						get_int32(tdgbl); // skip
					}
					else
					{
						X.RDB$NULL_FLAG.NULL = FALSE;
						X.RDB$NULL_FLAG = (USHORT) get_int32(tdgbl);
					}
					break;

				case att_field_character_set:
					field->fld_character_set_id = (USHORT) get_int32(tdgbl);
					break;

				case att_field_collation_id:
					field->fld_collation_id = (USHORT) get_int32(tdgbl);
					X.RDB$COLLATION_ID.NULL = FALSE;
					X.RDB$COLLATION_ID = field->fld_collation_id;
					break;

				// ODS 12

				case att_field_generator_name:
					GET_TEXT(X.RDB$GENERATOR_NAME);
					X.RDB$GENERATOR_NAME.NULL = FALSE;
					break;

				case att_field_identity_type:
					X.RDB$IDENTITY_TYPE.NULL = FALSE;
					X.RDB$IDENTITY_TYPE = (SSHORT) get_int32(tdgbl);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 84);
					// msg 84 column
					break;
				}
			}
		}
		END_STORE
		ON_ERROR
			general_on_error();
		END_ERROR
	}
	else
	{
		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;
			X.RDB$VIEW_CONTEXT.NULL = TRUE;
			X.RDB$BASE_FIELD.NULL = TRUE;
			X.RDB$SECURITY_CLASS.NULL = TRUE;
			X.RDB$QUERY_NAME.NULL = TRUE;
			X.RDB$QUERY_HEADER.NULL = TRUE;
			X.RDB$EDIT_STRING.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$FIELD_POSITION.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$COMPLEX_NAME.NULL = TRUE;
			X.RDB$UPDATE_FLAG.NULL = TRUE;
			X.RDB$DEFAULT_SOURCE.NULL = TRUE;
			X.RDB$DEFAULT_VALUE.NULL = TRUE;
			X.RDB$NULL_FLAG.NULL = TRUE;
			X.RDB$COLLATION_ID.NULL = TRUE;

			skip_init(&scan_next_attr);
			while (get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (skip_scan(&scan_next_attr), attribute)
				{
				case att_field_name:
					field->fld_name_length = GET_TEXT(field->fld_name);
					BURP_verbose (115, field->fld_name);
					// msg 115 restoring field %s
					strcpy (X.RDB$FIELD_NAME, field->fld_name);
					break;

				case att_field_source:
					GET_TEXT(X.RDB$FIELD_SOURCE);
					break;

				case att_field_security_class:
					GET_TEXT(X.RDB$SECURITY_CLASS);
					fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, true);
					X.RDB$SECURITY_CLASS.NULL = FALSE;
					break;

				case att_field_query_name:
					GET_TEXT(X.RDB$QUERY_NAME);
					X.RDB$QUERY_NAME.NULL = FALSE;
					break;

				case att_field_query_header:
					X.RDB$QUERY_HEADER.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$QUERY_HEADER, global_tr);
					break;

				case att_field_edit_string:
					GET_TEXT(X.RDB$EDIT_STRING);
					X.RDB$EDIT_STRING.NULL = FALSE;
					break;

				case att_field_position:
					X.RDB$FIELD_POSITION.NULL = FALSE;
					X.RDB$FIELD_POSITION = (USHORT) get_int32(tdgbl);
					break;

				case att_field_number:
					field->fld_number = (USHORT) get_int32(tdgbl);
					break;

				case att_field_type:
					field->fld_type = (USHORT) get_int32(tdgbl);
					break;

				case att_field_length:
					field->fld_length = (USHORT) get_int32(tdgbl);
					break;

				case att_field_scale:
					field->fld_scale = (USHORT) get_int32(tdgbl);
					break;

				case att_field_sub_type:
					field->fld_sub_type = (USHORT) get_int32(tdgbl);
					break;

				case att_field_system_flag:
					X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
					X.RDB$SYSTEM_FLAG.NULL = FALSE;
					break;

				case att_view_context:
					X.RDB$VIEW_CONTEXT = (USHORT) get_int32(tdgbl);
					X.RDB$VIEW_CONTEXT.NULL = FALSE;
					break;

				case att_field_computed_flag:
					if (get_int32(tdgbl))
						field->fld_flags |= FLD_computed;
					break;

				case att_base_field:
					GET_TEXT(X.RDB$BASE_FIELD);
					X.RDB$BASE_FIELD.NULL = FALSE;
					break;

				case att_field_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, global_tr);
					break;

				case att_field_description2:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, global_tr);
					break;

				case att_field_complex_name:
					GET_TEXT(X.RDB$COMPLEX_NAME);
					X.RDB$COMPLEX_NAME.NULL = FALSE;
					break;

				case att_field_dimensions:
					{
						field->fld_dimensions = (USHORT) get_int32(tdgbl);
						field->fld_flags |= FLD_array;
						USHORT n = field->fld_dimensions;
						for (SLONG* rp = field->fld_ranges; n; rp += 2, n--)
						{
							if (get_attribute(&attribute, tdgbl) != att_field_range_low)
								bad_attribute(scan_next_attr, attribute, 58);
							// msg 58 array
							else
								*rp = get_int32(tdgbl);
							if (get_attribute(&attribute, tdgbl) != att_field_range_high)
								bad_attribute(scan_next_attr, attribute, 58);
								// msg 58 array
							else
								*(rp + 1) = get_int32(tdgbl);
						}
					}
					break;

				case att_field_update_flag:
					X.RDB$UPDATE_FLAG.NULL = FALSE;
					X.RDB$UPDATE_FLAG = (USHORT) get_int32(tdgbl);
					break;

				case att_field_default_source:
					X.RDB$DEFAULT_SOURCE.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, global_tr);
					break;

				case att_field_default_value:
					X.RDB$DEFAULT_VALUE.NULL = FALSE;
					get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, global_tr);
					break;

				case att_field_null_flag:
					if (tdgbl->gbl_sw_novalidity) {
						get_int32(tdgbl); // skip
					}
					else
					{
						X.RDB$NULL_FLAG.NULL = FALSE;
						X.RDB$NULL_FLAG = (USHORT) get_int32(tdgbl);
					}
					break;

				case att_field_character_set:
					field->fld_character_set_id = (USHORT) get_int32(tdgbl);
					break;

				case att_field_collation_id:
					field->fld_collation_id = (USHORT) get_int32(tdgbl);
					X.RDB$COLLATION_ID.NULL = FALSE;
					X.RDB$COLLATION_ID = field->fld_collation_id;
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 84);
					// msg 84 column
					break;
				}
			}
		}
		END_STORE
		ON_ERROR
			general_on_error();
		END_ERROR
	}

	return field;
}

bool get_field_dimensions(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ f i e l d _ d i m e n s i o n s
 *
 **************************************
 *
 * Functional description
 *	Get array field dimensions in rdb$field_dimensions.
 *
 **************************************/
	att_type		attribute;
	scan_attr_t		scan_next_attr;

	STORE (REQUEST_HANDLE tdgbl->handles_get_field_dimensions_req_handle1)
		X IN RDB$FIELD_DIMENSIONS

		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_field_name:
				GET_TEXT(X.RDB$FIELD_NAME);
				break;

			case att_field_dimensions:
				X.RDB$DIMENSION = (USHORT) get_int32(tdgbl);
				break;

			case att_field_range_low:
				X.RDB$LOWER_BOUND = get_int32(tdgbl);
				break;

			case att_field_range_high:
				X.RDB$UPPER_BOUND = get_int32(tdgbl);
				break;

			default:
				bad_attribute(scan_next_attr, attribute, 288);
				// msg 288 array dimensions
				break;
			}
		}
	END_STORE;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	return true;
}

bool get_files(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ f i l e s
 *
 **************************************
 *
 * Functional description
 *	Get any files that were stored; let
 *	somebody else worry about what to do with them.
 *
 **************************************/
	att_type		attribute;
	scan_attr_t		scan_next_attr;

	STORE (REQUEST_HANDLE tdgbl->handles_get_files_req_handle1)
		X IN RDB$FILES
		X.RDB$FILE_FLAGS = 0;

		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_file_filename:
				GET_TEXT(X.RDB$FILE_NAME);
				BURP_verbose (116, X.RDB$FILE_NAME);
				// msg 116 restoring file %s
				break;

			case att_file_sequence:
				X.RDB$FILE_SEQUENCE = (USHORT) get_int32(tdgbl);
				break;

			case att_file_start:
				X.RDB$FILE_START = get_int32(tdgbl);
				break;

			case att_file_length:
				X.RDB$FILE_LENGTH = get_int32(tdgbl);
				break;

			case att_file_flags:
				X.RDB$FILE_FLAGS |= get_int32(tdgbl);
				break;

			case att_shadow_number:
				X.RDB$SHADOW_NUMBER = (USHORT) get_int32(tdgbl);
				if (tdgbl->gbl_sw_kill && X.RDB$SHADOW_NUMBER)
					X.RDB$FILE_FLAGS |= FILE_inactive;
				break;

			default:
				bad_attribute(scan_next_attr, attribute, 85);
				// msg 85 file
				break;
			}
		}
	END_STORE;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	return true;
}

bool get_filter(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ f i l t e r
 *
 **************************************
 *
 * Functional description
 *	Get a type definition in rdb$filters.
 *
 **************************************/
	att_type		attribute;
	scan_attr_t		scan_next_attr;

	STORE (REQUEST_HANDLE tdgbl->handles_get_filter_req_handle1)
		X IN RDB$FILTERS
		X.RDB$DESCRIPTION.NULL = TRUE;
		X.RDB$SYSTEM_FLAG = 0;
		X.RDB$SYSTEM_FLAG.NULL = FALSE;

		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_filter_name:
				GET_TEXT(X.RDB$FUNCTION_NAME);
				BURP_verbose (117, X.RDB$FUNCTION_NAME);
				// msg 117 restoring filter %s
				break;

			case att_filter_description:
				X.RDB$DESCRIPTION.NULL = FALSE;
				get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
				break;

			case att_filter_description2:
				X.RDB$DESCRIPTION.NULL = FALSE;
				get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
				break;

			case att_filter_module_name:
				GET_TEXT(X.RDB$MODULE_NAME);
				break;

			case att_filter_entrypoint:
				GET_TEXT(X.RDB$ENTRYPOINT);
				break;

			case att_filter_input_sub_type:
				X.RDB$INPUT_SUB_TYPE = (USHORT) get_int32(tdgbl);
				break;

			case att_filter_output_sub_type:
				X.RDB$OUTPUT_SUB_TYPE = (USHORT) get_int32(tdgbl);
				break;

			default:
				bad_attribute(scan_next_attr, attribute, 87);
				// msg 87  filter
				break;
			}
		}
	END_STORE;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	return true;
}

bool get_function(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ f u n c t i o n
 *
 **************************************
 *
 * Functional description
 *	Reconstruct a function.
 *
 **************************************/
	att_type	attribute;
	TEXT		temp[GDS_NAME_LEN * 2];
	SSHORT		l;
	scan_attr_t	scan_next_attr;

	bool existFlag = false;

	isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		GDS_NAME function_name;
		bool securityClass = false;

		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_function_req_handle1)
			X IN RDB$FUNCTIONS
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$ENGINE_NAME.NULL = TRUE;
			X.RDB$PACKAGE_NAME.NULL = TRUE;
			X.RDB$PRIVATE_FLAG.NULL = TRUE;
			X.RDB$FUNCTION_BLR.NULL = TRUE;
			X.RDB$FUNCTION_SOURCE.NULL = TRUE;
			X.RDB$VALID_BLR.NULL = TRUE;
			X.RDB$DEBUG_INFO.NULL = TRUE;
			X.RDB$SECURITY_CLASS.NULL = TRUE;
			X.RDB$OWNER_NAME.NULL = TRUE;
			X.RDB$MODULE_NAME.NULL = TRUE;
			X.RDB$ENTRYPOINT.NULL = TRUE;

			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$SYSTEM_FLAG = 0;

			X.RDB$DETERMINISTIC_FLAG.NULL = FALSE;
			X.RDB$DETERMINISTIC_FLAG = 0;

			X.RDB$LEGACY_FLAG.NULL = FALSE;
			X.RDB$LEGACY_FLAG = 1;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_function_name:
				{
					SSHORT prefixLen = 0;
					if (!X.RDB$PACKAGE_NAME.NULL)
					{
						prefixLen = static_cast<SSHORT>(strlen(X.RDB$PACKAGE_NAME));
						memcpy(temp, X.RDB$PACKAGE_NAME, prefixLen);
						temp[prefixLen++] = '.';
					}

					l = GET_TEXT(X.RDB$FUNCTION_NAME);
					MISC_terminate (X.RDB$FUNCTION_NAME, temp + prefixLen, l,
						sizeof(temp) - prefixLen);
					BURP_verbose (118, temp);
					// msg 118 restoring function %s
					break;
				}

				case att_function_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
					break;

				case att_function_description2:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
					break;

				case att_function_module_name:
					X.RDB$MODULE_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$MODULE_NAME);
					break;

				case att_function_entrypoint:
					X.RDB$ENTRYPOINT.NULL = FALSE;
					GET_TEXT(X.RDB$ENTRYPOINT);
					break;

				case att_function_return_arg:
					X.RDB$RETURN_ARGUMENT = (USHORT) get_int32(tdgbl);
					break;

				case att_function_query_name:
					GET_TEXT(X.RDB$QUERY_NAME);
					break;

				case att_function_type:
					X.RDB$FUNCTION_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_function_engine_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$ENGINE_NAME);
						X.RDB$ENGINE_NAME.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_package_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$PACKAGE_NAME);
						fb_utils::exact_name(X.RDB$PACKAGE_NAME);
						X.RDB$PACKAGE_NAME.NULL = FALSE;
						securityClass = true;	// prevent creation of security class for packaged function
					}
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_private_flag:
					if (tdgbl->RESTORE_format >= 10)
					{
						X.RDB$PRIVATE_FLAG = (USHORT) get_int32(tdgbl);
						X.RDB$PRIVATE_FLAG.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_blr:
					if (tdgbl->RESTORE_format >= 10)
					{
						get_blr_blob(tdgbl, X.RDB$FUNCTION_BLR, true);
						X.RDB$FUNCTION_BLR.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_source:
					if (tdgbl->RESTORE_format >= 10)
					{
						get_source_blob(tdgbl, X.RDB$FUNCTION_SOURCE, true);
						X.RDB$FUNCTION_SOURCE.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_valid_blr:
					if (tdgbl->RESTORE_format >= 10)
					{
						X.RDB$VALID_BLR = get_int32(tdgbl);
						X.RDB$VALID_BLR.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_debug_info:
					if (tdgbl->RESTORE_format >= 10)
					{
						get_misc_blob(tdgbl, X.RDB$DEBUG_INFO, true);
						X.RDB$DEBUG_INFO.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_security_class:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$SECURITY_CLASS);
						fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false);
						X.RDB$SECURITY_CLASS.NULL = FALSE;
						securityClass = true;
					}
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_owner_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$OWNER_NAME);
						X.RDB$OWNER_NAME.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_legacy_flag:
					if (tdgbl->RESTORE_format >= 10)
					{
						X.RDB$LEGACY_FLAG = get_int32(tdgbl);
						X.RDB$LEGACY_FLAG.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_deterministic_flag:
					if (tdgbl->RESTORE_format >= 10)
					{
						X.RDB$DETERMINISTIC_FLAG = get_int32(tdgbl);
						X.RDB$DETERMINISTIC_FLAG.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 89);
					// msg 89 function
					break;
				}
			}

			strcpy(function_name, X.RDB$FUNCTION_NAME);
		END_STORE;
		ON_ERROR
			if (gds_status[1] != isc_no_dup)
				general_on_error ();
			else
				existFlag = true;
		END_ERROR;

		collect_missing_privs(tdgbl, obj_udf, function_name, securityClass);
	}
	else
	{
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_function_req_handle1)
			X IN RDB$FUNCTIONS
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$DESCRIPTION.NULL = TRUE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_function_name:
					l = GET_TEXT(X.RDB$FUNCTION_NAME);
					MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
					BURP_verbose (118, temp);
					// msg 118 restoring function %s
					break;

				case att_function_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
					break;

				case att_function_description2:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
					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_int32(tdgbl);
					break;

				case att_function_query_name:
					GET_TEXT(X.RDB$QUERY_NAME);
					break;

				case att_function_type:
					X.RDB$FUNCTION_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_function_engine_name:
				case att_function_package_name:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_private_flag:
					if (tdgbl->RESTORE_format >= 10)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_blr:
				case att_function_source:
					if (tdgbl->RESTORE_format >= 10)
						eat_blob(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_valid_blr:
					if (tdgbl->RESTORE_format >= 10)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_debug_info:
					if (tdgbl->RESTORE_format >= 10)
						eat_blob(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_security_class:
				case att_function_owner_name:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				case att_function_legacy_flag:
				case att_function_deterministic_flag:
					if (tdgbl->RESTORE_format >= 10)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 89);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 89);
					// msg 89 function
					break;
				}
			}

		END_STORE;
		ON_ERROR
			if (gds_status[1] != isc_no_dup)
				general_on_error ();
			else
				existFlag = true;
		END_ERROR;
	}

	// at the end of args for a function is the rec_function_end marker
	while (get(tdgbl) == rec_function_arg)
		get_function_arg(tdgbl, existFlag);

	return true;
}

void get_function_arg(BurpGlobals* tdgbl, bool skip_arguments)
{
/**************************************
 *
 *	g e t _ f u n c t i o n _ a r g
 *
 **************************************
 *
 * Functional description
 *	Reconstruct function argument.
 *
 **************************************/
	att_type	attribute;
	scan_attr_t		scan_next_attr;

	isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;

	if (skip_arguments)
	{
		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_functionarg_name:
				eat_text(tdgbl);
				break;

			case att_functionarg_position:
				get_int32(tdgbl);
				break;

			case att_functionarg_passing_mechanism:
				get_int32(tdgbl);
				break;

			case att_functionarg_field_type:
				get_int32(tdgbl);
				break;

			case att_functionarg_field_scale:
				get_int32(tdgbl);
				break;

			case att_functionarg_field_length:
				get_int32(tdgbl);
				break;

			case att_functionarg_field_sub_type:
				get_int32(tdgbl);
				break;

			case att_functionarg_character_set:
				get_int32(tdgbl);
				break;

			case att_functionarg_field_precision:
				get_int32(tdgbl);
				break;

			case att_functionarg_package_name:
			case att_functionarg_arg_name:
			case att_functionarg_field_source:
				if (tdgbl->RESTORE_format >= 10)
					eat_text(tdgbl);
				else
					bad_attribute(scan_next_attr, attribute, 90);
				break;

			case att_functionarg_default_value:
			case att_functionarg_default_source:
				if (tdgbl->RESTORE_format >= 10)
					eat_blob(tdgbl);
				else
					bad_attribute(scan_next_attr, attribute, 90);
				break;

			case att_functionarg_collation_id:
			case att_functionarg_null_flag:
			case att_functionarg_type_mechanism:
				if (tdgbl->RESTORE_format >= 10)
					get_int32(tdgbl);
				else
					bad_attribute(scan_next_attr, attribute, 90);
				break;

			case att_functionarg_field_name:
			case att_functionarg_relation_name:
				if (tdgbl->RESTORE_format >= 10)
					eat_text(tdgbl);
				else
					bad_attribute(scan_next_attr, attribute, 90);
				break;

			case att_functionarg_description:
				if (tdgbl->RESTORE_format >= 10)
					eat_blob(tdgbl);
				else
					bad_attribute(scan_next_attr, attribute, 90);
				break;

			default:
				bad_attribute(scan_next_attr, attribute, 90);
				// msg 90 function argument
				break;
			}
		}
		return;
	}

	SSHORT		l;
	TEXT		temp[GDS_NAME_LEN * 2];

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		// with RDB$FIELD_PRECISION
		STORE (TRANSACTION_HANDLE local_trans
				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;
			X.RDB$PACKAGE_NAME.NULL = TRUE;
			X.RDB$ARGUMENT_NAME.NULL = TRUE;
			X.RDB$FIELD_SOURCE.NULL = TRUE;
			X.RDB$DEFAULT_VALUE.NULL = TRUE;
			X.RDB$DEFAULT_SOURCE.NULL = TRUE;
			X.RDB$COLLATION_ID.NULL = TRUE;
			X.RDB$NULL_FLAG.NULL = TRUE;
			X.RDB$ARGUMENT_MECHANISM.NULL = TRUE;
			X.RDB$FIELD_NAME.NULL = TRUE;
			X.RDB$RELATION_NAME.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;

			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_functionarg_name:
					{
						SSHORT prefixLen = 0;
						if (!X.RDB$PACKAGE_NAME.NULL)
						{
							prefixLen = static_cast<SSHORT>(strlen(X.RDB$PACKAGE_NAME));
							memcpy(temp, X.RDB$PACKAGE_NAME, prefixLen);
							temp[prefixLen++] = '.';
						}

						l = GET_TEXT(X.RDB$FUNCTION_NAME);
						MISC_terminate(X.RDB$FUNCTION_NAME, temp + prefixLen, l,
							sizeof(temp) - prefixLen);

						// msg 119 restoring argument for function %s
						BURP_verbose(119, temp);
					}
					break;

				case att_functionarg_position:
					X.RDB$ARGUMENT_POSITION = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_passing_mechanism:
					X.RDB$MECHANISM = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_type:
					X.RDB$FIELD_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_scale:
					X.RDB$FIELD_SCALE = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_length:
					X.RDB$FIELD_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_sub_type:
					X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
					X.RDB$FIELD_SUB_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_character_set:
					X.RDB$CHARACTER_SET_ID.NULL = FALSE;
					X.RDB$CHARACTER_SET_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_precision:
					if (tdgbl->RESTORE_format >= 6)
					{
						X.RDB$FIELD_PRECISION.NULL = FALSE;
						X.RDB$FIELD_PRECISION = (USHORT) get_int32(tdgbl);
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_package_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$PACKAGE_NAME);
						X.RDB$PACKAGE_NAME.NULL = FALSE;
						fb_utils::exact_name(X.RDB$PACKAGE_NAME);
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_arg_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$ARGUMENT_NAME);
						X.RDB$ARGUMENT_NAME.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_field_source:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$FIELD_SOURCE);
						X.RDB$FIELD_SOURCE.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_default_value:
					if (tdgbl->RESTORE_format >= 10)
					{
						get_blr_blob(tdgbl, X.RDB$DEFAULT_VALUE, true);
						X.RDB$DEFAULT_VALUE.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_default_source:
					if (tdgbl->RESTORE_format >= 10)
					{
						get_source_blob(tdgbl, X.RDB$DEFAULT_SOURCE, true);
						X.RDB$DEFAULT_SOURCE.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_collation_id:
					if (tdgbl->RESTORE_format >= 10)
					{
						X.RDB$COLLATION_ID = get_int32(tdgbl);
						X.RDB$COLLATION_ID.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_null_flag:
					if (tdgbl->RESTORE_format >= 10)
					{
						X.RDB$NULL_FLAG = get_int32(tdgbl);
						X.RDB$NULL_FLAG.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_type_mechanism:
					if (tdgbl->RESTORE_format >= 10)
					{
						X.RDB$ARGUMENT_MECHANISM = get_int32(tdgbl);
						X.RDB$ARGUMENT_MECHANISM.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_field_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$FIELD_NAME);
						X.RDB$FIELD_NAME.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_relation_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$RELATION_NAME);
						X.RDB$RELATION_NAME.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_description:
					if (tdgbl->RESTORE_format >= 10)
					{
						get_source_blob(tdgbl, X.RDB$DESCRIPTION, true);
						X.RDB$DESCRIPTION.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 90);
					// msg 90 function argument
					break;
				}
			}
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else if (tdgbl->runtimeODS >= DB_VERSION_DDL10)
	{
		// with RDB$FIELD_PRECISION
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1)
			X IN RDB$FUNCTION_ARGUMENTS
			X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
			X.RDB$CHARACTER_SET_ID.NULL = TRUE;
			X.RDB$FIELD_PRECISION.NULL  = TRUE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_functionarg_name:
					l = GET_TEXT(X.RDB$FUNCTION_NAME);
					MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
					BURP_verbose (119, temp);
					// msg 119 restoring argument for function %s
					break;

				case att_functionarg_position:
					X.RDB$ARGUMENT_POSITION = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_passing_mechanism:
					X.RDB$MECHANISM = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_type:
					X.RDB$FIELD_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_scale:
					X.RDB$FIELD_SCALE = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_length:
					X.RDB$FIELD_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_sub_type:
					X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
					X.RDB$FIELD_SUB_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_character_set:
					X.RDB$CHARACTER_SET_ID.NULL = FALSE;
					X.RDB$CHARACTER_SET_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_precision:
					if (tdgbl->RESTORE_format >= 6)
					{
						X.RDB$FIELD_PRECISION.NULL = FALSE;
						X.RDB$FIELD_PRECISION = (USHORT) get_int32(tdgbl);
					}
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_package_name:
				case att_functionarg_arg_name:
				case att_functionarg_field_source:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_default_value:
				case att_functionarg_default_source:
					if (tdgbl->RESTORE_format >= 10)
						eat_blob(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_collation_id:
				case att_functionarg_null_flag:
				case att_functionarg_type_mechanism:
					if (tdgbl->RESTORE_format >= 10)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_field_name:
				case att_functionarg_relation_name:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_description:
					if (tdgbl->RESTORE_format >= 10)
						eat_blob(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 90);
					// msg 90 function argument
					break;
				}
			}
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else
	{
		// without RDB$FIELD_PRECISION
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_function_arg_req_handle1)
			X IN RDB$FUNCTION_ARGUMENTS
			X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
			X.RDB$CHARACTER_SET_ID.NULL = TRUE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_functionarg_name:
					l = GET_TEXT(X.RDB$FUNCTION_NAME);
					MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
					BURP_verbose (119, temp);
					// msg 119 restoring argument for function %s
					break;

				case att_functionarg_position:
					X.RDB$ARGUMENT_POSITION = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_passing_mechanism:
					X.RDB$MECHANISM = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_type:
					X.RDB$FIELD_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_scale:
					X.RDB$FIELD_SCALE = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_length:
					X.RDB$FIELD_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_sub_type:
					X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
					X.RDB$FIELD_SUB_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_character_set:
					X.RDB$CHARACTER_SET_ID.NULL = FALSE;
					X.RDB$CHARACTER_SET_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_functionarg_field_precision:
					if (tdgbl->RESTORE_format >= 6)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_package_name:
				case att_functionarg_arg_name:
				case att_functionarg_field_source:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_default_value:
				case att_functionarg_default_source:
					if (tdgbl->RESTORE_format >= 10)
						eat_blob(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_collation_id:
				case att_functionarg_null_flag:
				case att_functionarg_type_mechanism:
					if (tdgbl->RESTORE_format >= 10)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_field_name:
				case att_functionarg_relation_name:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				case att_functionarg_description:
					if (tdgbl->RESTORE_format >= 10)
						eat_blob(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 90);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 90);
					// msg 90 function argument
					break;
				}
			}
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
}

bool get_generator(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ g e n e r a t o r
 *
 **************************************
 *
 * Functional description
 *	Pick up a gen-id.  Like most things, there is ughly history.
 *	In the modern world, gen_id are free floating records.  In the
 *	bad old days they were attributes of relations.  Handle both
 *	nicely.
 *
 **************************************/
	SINT64 value = 0, initial_value = 0;

	BASED_ON RDB$GENERATORS.RDB$GENERATOR_NAME name = "";
	BASED_ON RDB$GENERATORS.RDB$SECURITY_CLASS secclass = "";
	BASED_ON RDB$GENERATORS.RDB$OWNER_NAME ownername = "";
	BASED_ON RDB$GENERATORS.RDB$GENERATOR_INCREMENT increment = 1;
	fb_sysflag sysFlag = fb_sysflag_user;
	att_type	attribute;
	scan_attr_t		scan_next_attr;

	ISC_QUAD gen_desc = {0, 0};

	ISC_QUAD* descPtr = NULL;
	const char* secPtr = NULL;
	const char* ownerPtr = NULL;

	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_int32(tdgbl);
			break;

		case att_gen_value_int64:
			// IB v6 or later, gen_id value is an SINT64
			value = get_int64(tdgbl);
			break;

		case att_gen_description:
			if (tdgbl->RESTORE_format >= 7)
			{
				get_source_blob (tdgbl, gen_desc, false);
				if (gen_desc.gds_quad_high || gen_desc.gds_quad_low)
					descPtr = &gen_desc;
			}
			else
				bad_attribute(scan_next_attr, attribute, 289);
			break;

		case att_gen_sysflag:
			sysFlag = (fb_sysflag) get_int32(tdgbl);
			break;

		case att_gen_security_class:
			if (tdgbl->RESTORE_format >= 10)
			{
				GET_TEXT(secclass);
				secPtr = reinterpret_cast<const char*>(&secclass);
			}
			else
				bad_attribute(scan_next_attr, attribute, 289);
			break;

		case att_gen_owner_name:
			if (tdgbl->RESTORE_format >= 10)
			{
				GET_TEXT(ownername);
				ownerPtr = reinterpret_cast<const char*>(&ownername);
			}
			else
				bad_attribute(scan_next_attr, attribute, 289);
			break;

		case att_gen_init_val:
			if (tdgbl->RESTORE_format >= 10)
				initial_value = get_int64(tdgbl);
			else
				bad_attribute(scan_next_attr, attribute, 289);
			break;

		case att_gen_id_increment:
			if (tdgbl->RESTORE_format >= 10)
				increment = get_int32(tdgbl);
			else
				bad_attribute(scan_next_attr, attribute, 289);
			break;

		default:
			bad_attribute(scan_next_attr, attribute, 289);
			// msg 289 generator
			break;
		}
	}

	if (tdgbl->gbl_sw_meta)
	{
		value = 0;
	}

	store_blr_gen_id(tdgbl, name, value, initial_value, descPtr, secPtr, ownerPtr, sysFlag, increment);

	return true;
}

bool get_global_field(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ g l o b a l _ f i e l d
 *
 **************************************
 *
 * Functional description
 *	Reconstruct a global field.
 *
 **************************************/
	att_type	attribute;
	TEXT		temp[GDS_NAME_LEN];
	SSHORT		l;
	scan_attr_t		scan_next_attr;

	gfld* gfield = NULL;

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		GDS_NAME field_name;
		bool securityClass = false;

		// with rdb$field_precision, rdb$security_class and rdb$owner_name.
		STORE (REQUEST_HANDLE tdgbl->handles_get_global_field_req_handle1)
			X IN RDB$FIELDS

			X.RDB$FIELD_SCALE = X.RDB$SEGMENT_LENGTH = 0;
			X.RDB$CHARACTER_SET_ID = X.RDB$COLLATION_ID = 0;
			X.RDB$FIELD_SUB_TYPE = 0;
			X.RDB$COMPUTED_BLR.NULL = TRUE;
			X.RDB$COMPUTED_SOURCE.NULL = TRUE;
			X.RDB$QUERY_NAME.NULL = TRUE;
			X.RDB$EDIT_STRING.NULL = TRUE;
			X.RDB$QUERY_HEADER.NULL = TRUE;
			X.RDB$MISSING_VALUE.NULL = TRUE;
			X.RDB$DEFAULT_VALUE.NULL = TRUE;
			X.RDB$VALIDATION_BLR.NULL = TRUE;
			X.RDB$VALIDATION_SOURCE.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$NULL_FLAG.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$DIMENSIONS.NULL = TRUE;
			X.RDB$EXTERNAL_LENGTH.NULL = TRUE;
			X.RDB$EXTERNAL_TYPE.NULL = TRUE;
			X.RDB$EXTERNAL_SCALE.NULL = TRUE;
			X.RDB$SEGMENT_LENGTH.NULL = TRUE;
			X.RDB$CHARACTER_LENGTH.NULL = TRUE;
			X.RDB$MISSING_SOURCE.NULL = TRUE;
			X.RDB$DEFAULT_SOURCE.NULL = TRUE;
			X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
			X.RDB$CHARACTER_SET_ID.NULL = TRUE;
			X.RDB$COLLATION_ID.NULL = TRUE;
			X.RDB$FIELD_PRECISION.NULL = TRUE;
			X.RDB$SECURITY_CLASS.NULL = TRUE;
			X.RDB$OWNER_NAME.NULL = TRUE;
			memset(X.RDB$QUERY_NAME, ' ', sizeof(X.RDB$QUERY_NAME));

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_field_name:
					l = GET_TEXT(X.RDB$FIELD_NAME);
					MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp));
					BURP_verbose (121, temp);
					// msg 121  restoring global field %s
					break;

				case att_field_query_name:
					GET_TEXT(X.RDB$QUERY_NAME);
					X.RDB$QUERY_NAME.NULL = FALSE;
					break;

				case att_field_edit_string:
					GET_TEXT(X.RDB$EDIT_STRING);
					X.RDB$EDIT_STRING.NULL = FALSE;
					break;

				case att_field_query_header:
					X.RDB$QUERY_HEADER.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$QUERY_HEADER, false);
					break;

				case att_field_type:
					X.RDB$FIELD_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_field_length:
					X.RDB$FIELD_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_field_scale:
					X.RDB$FIELD_SCALE = (USHORT) get_int32(tdgbl);
					X.RDB$FIELD_SCALE.NULL = FALSE;
					break;

				case att_field_sub_type:
					X.RDB$FIELD_SUB_TYPE = (USHORT) get_int32(tdgbl);
					X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
					break;

				case att_field_segment_length:
					X.RDB$SEGMENT_LENGTH = (USHORT) get_int32(tdgbl);
					if (X.RDB$SEGMENT_LENGTH)
						X.RDB$SEGMENT_LENGTH.NULL = FALSE;
					break;

				case att_field_computed_blr:
					// if we are going against a V4.0 database,
					// restore the global fields in 2 phases.

					if (tdgbl->global_trans)
					{
						if (!gfield)
							gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

						get_blr_blob (tdgbl, gfield->gfld_computed_blr, true);
						gfield->gfld_flags |= GFLD_computed_blr;
					}
					else
					{
						X.RDB$COMPUTED_BLR.NULL = FALSE;
						get_blr_blob (tdgbl, X.RDB$COMPUTED_BLR, false);
					}
					break;

				case att_field_computed_source:
					// if we are going against a V4.0 database,
					// restore the global fields in 2 phases.

					if (tdgbl->global_trans)
					{
						if (!gfield)
							gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

						get_misc_blob (tdgbl, gfield->gfld_computed_source, true);
						gfield->gfld_flags |= GFLD_computed_source;
					}
					else
					{
						X.RDB$COMPUTED_SOURCE.NULL = FALSE;
						get_misc_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false);
					}
					break;

				case att_field_computed_source2:
					// if we are going against a V4.0 database,
					// restore the global fields in 2 phases.

					if (tdgbl->global_trans)
					{
						if (!gfield)
							gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

						get_source_blob (tdgbl, gfield->gfld_computed_source2, true);
						gfield->gfld_flags |= GFLD_computed_source2;
					}
					else
					{
						X.RDB$COMPUTED_SOURCE.NULL = FALSE;
						get_source_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false);
					}
					break;

				case att_field_validation_blr:
					if (tdgbl->gbl_sw_novalidity)
						eat_blob(tdgbl);
					else
					{
						// if we are going against a V4.0 database,
						// restore the global fields in 2 phases.

						if (tdgbl->global_trans)
						{
							if (!gfield)
								gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

							get_blr_blob (tdgbl, gfield->gfld_vb, true);
							gfield->gfld_flags |= GFLD_validation_blr;
						}
						else
						{
							X.RDB$VALIDATION_BLR.NULL = FALSE;
							get_blr_blob (tdgbl, X.RDB$VALIDATION_BLR, false);
						}
					}
					break;

				case att_field_validation_source:
					if (tdgbl->gbl_sw_novalidity)
						eat_blob(tdgbl);
					else
					{
						// if we are going against a V4.0 database,
						// restore the global fields in 2 phases.

						if (tdgbl->global_trans)
						{
							if (!gfield)
								gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

							get_misc_blob (tdgbl, gfield->gfld_vs, true);
							gfield->gfld_flags |= GFLD_validation_source;
						}
						else
						{
							X.RDB$VALIDATION_SOURCE.NULL = FALSE;
							get_misc_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false);
						}
					}
					break;

				case att_field_validation_source2:
					if (tdgbl->gbl_sw_novalidity)
						eat_blob(tdgbl);
					else
					{
						// if we are going against a V4.0 database,
						// restore the global fields in 2 phases.

						if (tdgbl->global_trans)
						{
							if (!gfield)
								gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

							get_source_blob (tdgbl, gfield->gfld_vs2, true);
							gfield->gfld_flags |= GFLD_validation_source2;
						}
						else
						{
							X.RDB$VALIDATION_SOURCE.NULL = FALSE;
							get_source_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false);
						}
					}
					break;

				case att_field_missing_value:
					X.RDB$MISSING_VALUE.NULL = FALSE;
					get_blr_blob (tdgbl, X.RDB$MISSING_VALUE, false);
					break;

				case att_field_default_value:
					X.RDB$DEFAULT_VALUE.NULL = FALSE;
					get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, false);
					break;

				case att_field_system_flag:
					X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
					X.RDB$SYSTEM_FLAG.NULL = FALSE;
					break;

				case att_field_null_flag:
					if (tdgbl->gbl_sw_novalidity) {
						get_int32(tdgbl); // skip
					}
					else
					{
						X.RDB$NULL_FLAG = (USHORT) get_int32(tdgbl);
						X.RDB$NULL_FLAG.NULL = FALSE;
					}
					break;

				case att_field_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
					break;

				case att_field_description2:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
					break;

				case att_field_external_length:
					X.RDB$EXTERNAL_LENGTH.NULL = FALSE;
					X.RDB$EXTERNAL_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_field_external_scale:
					X.RDB$EXTERNAL_SCALE.NULL = FALSE;
					X.RDB$EXTERNAL_SCALE = (USHORT) get_int32(tdgbl);
					break;

				case att_field_external_type:
					X.RDB$EXTERNAL_TYPE.NULL = FALSE;
					X.RDB$EXTERNAL_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_field_dimensions:
					X.RDB$DIMENSIONS.NULL = FALSE;
					X.RDB$DIMENSIONS = (USHORT) get_int32(tdgbl);
					break;

				case att_field_character_length:
					X.RDB$CHARACTER_LENGTH.NULL = FALSE;
					X.RDB$CHARACTER_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_field_default_source:
					X.RDB$DEFAULT_SOURCE.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, false);
					break;

				case att_field_missing_source:
					X.RDB$MISSING_SOURCE.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$MISSING_SOURCE, false);
					break;

				case att_field_character_set:
					X.RDB$CHARACTER_SET_ID.NULL = FALSE;
					X.RDB$CHARACTER_SET_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_field_collation_id:
					X.RDB$COLLATION_ID.NULL = FALSE;
					X.RDB$COLLATION_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_field_precision:
					if (tdgbl->RESTORE_format >= 6)
					{
						X.RDB$FIELD_PRECISION.NULL = FALSE;
						X.RDB$FIELD_PRECISION = (USHORT) get_int32(tdgbl);
					}
					else
						bad_attribute(scan_next_attr, attribute, 92);
					break;

				case att_field_security_class:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$SECURITY_CLASS);
						fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false);
						X.RDB$SECURITY_CLASS.NULL = FALSE;
						securityClass = true;
					}
					else
						bad_attribute(scan_next_attr, attribute, 92);
					break;

				case att_field_owner_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$OWNER_NAME);
						X.RDB$OWNER_NAME.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 92);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 92);
					// msg 92  domain
					break;
				}
			}

			if (X.RDB$FIELD_TYPE <= DTYPE_BLR_MAX)
			{
				l = gds_cvt_blr_dtype[X.RDB$FIELD_TYPE];
				if (l = type_lengths[l])
					X.RDB$FIELD_LENGTH = l;
			}

			strcpy(field_name, X.RDB$FIELD_NAME);

			if (gfield)
				strcpy(gfield->gfld_name, field_name);

			if (tdgbl->gbl_sw_fix_fss_data && tdgbl->gbl_sw_fix_fss_data_id == 0 &&
				!X.RDB$CHARACTER_SET_ID.NULL && X.RDB$CHARACTER_SET_ID == CS_UNICODE_FSS &&
				((!X.RDB$CHARACTER_LENGTH.NULL &&
					(X.RDB$FIELD_TYPE == blr_text || X.RDB$FIELD_TYPE == blr_varying)) ||
				 X.RDB$FIELD_TYPE == blr_blob))
			{
				if (X.RDB$FIELD_TYPE != blr_blob)
					X.RDB$CHARACTER_LENGTH = X.RDB$FIELD_LENGTH;

				X.RDB$CHARACTER_SET_ID = CS_NONE;
				X.RDB$COLLATION_ID = 0;
			}

		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;

		collect_missing_privs(tdgbl, obj_field, field_name, securityClass);
	}
	else if (tdgbl->runtimeODS >= DB_VERSION_DDL10)
	{
		// with rdb$field_precision
		STORE (REQUEST_HANDLE tdgbl->handles_get_global_field_req_handle1)
			X IN RDB$FIELDS

			X.RDB$FIELD_SCALE = X.RDB$SEGMENT_LENGTH = 0;
			X.RDB$CHARACTER_SET_ID = X.RDB$COLLATION_ID = 0;
			X.RDB$FIELD_SUB_TYPE = 0;
			X.RDB$COMPUTED_BLR.NULL = TRUE;
			X.RDB$COMPUTED_SOURCE.NULL = TRUE;
			X.RDB$QUERY_NAME.NULL = TRUE;
			X.RDB$EDIT_STRING.NULL = TRUE;
			X.RDB$QUERY_HEADER.NULL = TRUE;
			X.RDB$MISSING_VALUE.NULL = TRUE;
			X.RDB$DEFAULT_VALUE.NULL = TRUE;
			X.RDB$VALIDATION_BLR.NULL = TRUE;
			X.RDB$VALIDATION_SOURCE.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$NULL_FLAG.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$DIMENSIONS.NULL = TRUE;
			X.RDB$EXTERNAL_LENGTH.NULL = TRUE;
			X.RDB$EXTERNAL_TYPE.NULL = TRUE;
			X.RDB$EXTERNAL_SCALE.NULL = TRUE;
			X.RDB$SEGMENT_LENGTH.NULL = TRUE;
			X.RDB$CHARACTER_LENGTH.NULL = TRUE;
			X.RDB$MISSING_SOURCE.NULL = TRUE;
			X.RDB$DEFAULT_SOURCE.NULL = TRUE;
			X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
			X.RDB$CHARACTER_SET_ID.NULL = TRUE;
			X.RDB$COLLATION_ID.NULL = TRUE;
			X.RDB$FIELD_PRECISION.NULL = TRUE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_field_name:
					l = GET_TEXT(X.RDB$FIELD_NAME);
					MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp));
					BURP_verbose (121, temp);
					// msg 121  restoring global field %s
					break;

				case att_field_query_name:
					GET_TEXT(X.RDB$QUERY_NAME);
					X.RDB$QUERY_NAME.NULL = FALSE;
					break;

				case att_field_edit_string:
					GET_TEXT(X.RDB$EDIT_STRING);
					X.RDB$EDIT_STRING.NULL = FALSE;
					break;

				case att_field_query_header:
					X.RDB$QUERY_HEADER.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$QUERY_HEADER, false);
					break;

				case att_field_type:
					X.RDB$FIELD_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_field_length:
					X.RDB$FIELD_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_field_scale:
					X.RDB$FIELD_SCALE = (USHORT) get_int32(tdgbl);
					X.RDB$FIELD_SCALE.NULL = FALSE;
					break;

				case att_field_sub_type:
					X.RDB$FIELD_SUB_TYPE = (USHORT) get_int32(tdgbl);
					X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
					break;

				case att_field_segment_length:
					X.RDB$SEGMENT_LENGTH = (USHORT) get_int32(tdgbl);
					if (X.RDB$SEGMENT_LENGTH)
						X.RDB$SEGMENT_LENGTH.NULL = FALSE;
					break;

				case att_field_computed_blr:
					// if we are going against a V4.0 database,
					// restore the global fields in 2 phases.

					if (tdgbl->global_trans)
					{
						if (!gfield)
							gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

						get_blr_blob (tdgbl, gfield->gfld_computed_blr, true);
						gfield->gfld_flags |= GFLD_computed_blr;
					}
					else
					{
						X.RDB$COMPUTED_BLR.NULL = FALSE;
						get_blr_blob (tdgbl, X.RDB$COMPUTED_BLR, false);
					}
					break;

				case att_field_computed_source:
					// if we are going against a V4.0 database,
					// restore the global fields in 2 phases.

					if (tdgbl->global_trans)
					{
						if (!gfield)
							gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

						get_misc_blob (tdgbl, gfield->gfld_computed_source, true);
						gfield->gfld_flags |= GFLD_computed_source;
					}
					else
					{
						X.RDB$COMPUTED_SOURCE.NULL = FALSE;
						get_misc_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false);
					}
					break;

				case att_field_computed_source2:
					// if we are going against a V4.0 database,
					// restore the global fields in 2 phases.

					if (tdgbl->global_trans)
					{
						if (!gfield)
							gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

						get_source_blob (tdgbl, gfield->gfld_computed_source2, true);
						gfield->gfld_flags |= GFLD_computed_source2;
					}
					else
					{
						X.RDB$COMPUTED_SOURCE.NULL = FALSE;
						get_source_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false);
					}
					break;

				case att_field_validation_blr:
					if (tdgbl->gbl_sw_novalidity)
						eat_blob(tdgbl);
					else
					{
						// if we are going against a V4.0 database,
						// restore the global fields in 2 phases.

						if (tdgbl->global_trans)
						{
							if (!gfield)
								gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

							get_blr_blob (tdgbl, gfield->gfld_vb, true);
							gfield->gfld_flags |= GFLD_validation_blr;
						}
						else
						{
							X.RDB$VALIDATION_BLR.NULL = FALSE;
							get_blr_blob (tdgbl, X.RDB$VALIDATION_BLR, false);
						}
					}
					break;

				case att_field_validation_source:
					if (tdgbl->gbl_sw_novalidity)
						eat_blob(tdgbl);
					else
					{
						// if we are going against a V4.0 database,
						// restore the global fields in 2 phases.

						if (tdgbl->global_trans)
						{
							if (!gfield)
								gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

							get_misc_blob (tdgbl, gfield->gfld_vs, true);
							gfield->gfld_flags |= GFLD_validation_source;
						}
						else
						{
							X.RDB$VALIDATION_SOURCE.NULL = FALSE;
							get_misc_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false);
						}
					}
					break;

				case att_field_validation_source2:
					if (tdgbl->gbl_sw_novalidity)
						eat_blob(tdgbl);
					else
					{
						// if we are going against a V4.0 database,
						// restore the global fields in 2 phases.

						if (tdgbl->global_trans)
						{
							if (!gfield)
								gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

							get_source_blob (tdgbl, gfield->gfld_vs2, true);
							gfield->gfld_flags |= GFLD_validation_source2;
						}
						else
						{
							X.RDB$VALIDATION_SOURCE.NULL = FALSE;
							get_source_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false);
						}
					}
					break;

				case att_field_missing_value:
					X.RDB$MISSING_VALUE.NULL = FALSE;
					get_blr_blob (tdgbl, X.RDB$MISSING_VALUE, false);
					break;

				case att_field_default_value:
					X.RDB$DEFAULT_VALUE.NULL = FALSE;
					get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, false);
					break;

				case att_field_system_flag:
					X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
					X.RDB$SYSTEM_FLAG.NULL = FALSE;
					break;

				case att_field_null_flag:
					if (tdgbl->gbl_sw_novalidity) {
						get_int32(tdgbl); // skip
					}
					else
					{
						X.RDB$NULL_FLAG = (USHORT) get_int32(tdgbl);
						X.RDB$NULL_FLAG.NULL = FALSE;
					}
					break;

				case att_field_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
					break;

				case att_field_description2:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
					break;

				case att_field_external_length:
					X.RDB$EXTERNAL_LENGTH.NULL = FALSE;
					X.RDB$EXTERNAL_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_field_external_scale:
					X.RDB$EXTERNAL_SCALE.NULL = FALSE;
					X.RDB$EXTERNAL_SCALE = (USHORT) get_int32(tdgbl);
					break;

				case att_field_external_type:
					X.RDB$EXTERNAL_TYPE.NULL = FALSE;
					X.RDB$EXTERNAL_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_field_dimensions:
					X.RDB$DIMENSIONS.NULL = FALSE;
					X.RDB$DIMENSIONS = (USHORT) get_int32(tdgbl);
					break;

				case att_field_character_length:
					X.RDB$CHARACTER_LENGTH.NULL = FALSE;
					X.RDB$CHARACTER_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_field_default_source:
					X.RDB$DEFAULT_SOURCE.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, false);
					break;

				case att_field_missing_source:
					X.RDB$MISSING_SOURCE.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$MISSING_SOURCE, false);
					break;

				case att_field_character_set:
					X.RDB$CHARACTER_SET_ID.NULL = FALSE;
					X.RDB$CHARACTER_SET_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_field_collation_id:
					X.RDB$COLLATION_ID.NULL = FALSE;
					X.RDB$COLLATION_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_field_precision:
					if (tdgbl->RESTORE_format >= 6)
					{
						X.RDB$FIELD_PRECISION.NULL = FALSE;
						X.RDB$FIELD_PRECISION = (USHORT) get_int32(tdgbl);
					}
					else
						bad_attribute(scan_next_attr, attribute, 92);
					break;

				case att_field_security_class:
				case att_field_owner_name:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 92);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 92);
					// msg 92  domain
					break;
				}
			}

			if (X.RDB$FIELD_TYPE <= DTYPE_BLR_MAX)
			{
				l = gds_cvt_blr_dtype[X.RDB$FIELD_TYPE];
				if (l = type_lengths[l])
					X.RDB$FIELD_LENGTH = l;
			}

			if (gfield)
				strcpy (gfield->gfld_name, X.RDB$FIELD_NAME);

			if (tdgbl->gbl_sw_fix_fss_data && tdgbl->gbl_sw_fix_fss_data_id == 0 &&
				!X.RDB$CHARACTER_SET_ID.NULL && X.RDB$CHARACTER_SET_ID == CS_UNICODE_FSS &&
				((!X.RDB$CHARACTER_LENGTH.NULL &&
					(X.RDB$FIELD_TYPE == blr_text || X.RDB$FIELD_TYPE == blr_varying)) ||
				 X.RDB$FIELD_TYPE == blr_blob))
			{
				if (X.RDB$FIELD_TYPE != blr_blob)
					X.RDB$CHARACTER_LENGTH = X.RDB$FIELD_LENGTH;

				X.RDB$CHARACTER_SET_ID = CS_NONE;
				X.RDB$COLLATION_ID = 0;
			}

		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;

	}
	else // runtimeODS < DB_VERSION_DDL10
	{
		// without rdb$field_precision

		STORE (REQUEST_HANDLE tdgbl->handles_get_global_field_req_handle1)
			X IN RDB$FIELDS

			X.RDB$FIELD_SCALE = X.RDB$SEGMENT_LENGTH = 0;
			X.RDB$CHARACTER_SET_ID = X.RDB$COLLATION_ID = 0;
			X.RDB$FIELD_SUB_TYPE = 0;
			X.RDB$COMPUTED_BLR.NULL = TRUE;
			X.RDB$COMPUTED_SOURCE.NULL = TRUE;
			X.RDB$QUERY_NAME.NULL = TRUE;
			X.RDB$EDIT_STRING.NULL = TRUE;
			X.RDB$QUERY_HEADER.NULL = TRUE;
			X.RDB$MISSING_VALUE.NULL = TRUE;
			X.RDB$DEFAULT_VALUE.NULL = TRUE;
			X.RDB$VALIDATION_BLR.NULL = TRUE;
			X.RDB$VALIDATION_SOURCE.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$NULL_FLAG.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$DIMENSIONS.NULL = TRUE;
			X.RDB$EXTERNAL_LENGTH.NULL = TRUE;
			X.RDB$EXTERNAL_TYPE.NULL = TRUE;
			X.RDB$EXTERNAL_SCALE.NULL = TRUE;
			X.RDB$SEGMENT_LENGTH.NULL = TRUE;
			X.RDB$CHARACTER_LENGTH.NULL = TRUE;
			X.RDB$MISSING_SOURCE.NULL = TRUE;
			X.RDB$DEFAULT_SOURCE.NULL = TRUE;
			X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
			X.RDB$CHARACTER_SET_ID.NULL = TRUE;
			X.RDB$COLLATION_ID.NULL = TRUE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_field_name:
					l = GET_TEXT(X.RDB$FIELD_NAME);
					MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp));
					BURP_verbose (121, temp);
					// msg 121  restoring global field %s
					break;

				case att_field_query_name:
					GET_TEXT(X.RDB$QUERY_NAME);
					X.RDB$QUERY_NAME.NULL = FALSE;
					break;

				case att_field_edit_string:
					GET_TEXT(X.RDB$EDIT_STRING);
					X.RDB$EDIT_STRING.NULL = FALSE;
					break;

				case att_field_query_header:
					X.RDB$QUERY_HEADER.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$QUERY_HEADER, false);
					break;

				case att_field_type:
					X.RDB$FIELD_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_field_length:
					X.RDB$FIELD_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_field_scale:
					X.RDB$FIELD_SCALE = (USHORT) get_int32(tdgbl);
					X.RDB$FIELD_SCALE.NULL = FALSE;
					break;

				case att_field_sub_type:
					X.RDB$FIELD_SUB_TYPE = (USHORT) get_int32(tdgbl);
					X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
					break;

				case att_field_segment_length:
					X.RDB$SEGMENT_LENGTH = (USHORT) get_int32(tdgbl);
					if (X.RDB$SEGMENT_LENGTH)
						X.RDB$SEGMENT_LENGTH.NULL = FALSE;
					break;

				case att_field_computed_blr:
					// if we are going against a V4.0 database,
					// restore the global fields in 2 phases.

					if (tdgbl->global_trans)
					{
						if (!gfield)
							gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

						get_blr_blob (tdgbl, gfield->gfld_computed_blr, true);
						gfield->gfld_flags |= GFLD_computed_blr;
					}
					else
					{
						X.RDB$COMPUTED_BLR.NULL = FALSE;
						get_blr_blob (tdgbl, X.RDB$COMPUTED_BLR, false);
					}
					break;

				case att_field_computed_source:
					// if we are going against a V4.0 database,
					// restore the global fields in 2 phases.

					if (tdgbl->global_trans)
					{
						if (!gfield)
							gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

						get_misc_blob (tdgbl, gfield->gfld_computed_source, true);
						gfield->gfld_flags |= GFLD_computed_source;
					}
					else
					{
						X.RDB$COMPUTED_SOURCE.NULL = FALSE;
						get_misc_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false);
					}
					break;

				case att_field_computed_source2:
					// if we are going against a V4.0 database,
					// restore the global fields in 2 phases.

					if (tdgbl->global_trans)
					{
						if (!gfield)
							gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

						get_source_blob (tdgbl, gfield->gfld_computed_source2, true);
						gfield->gfld_flags |= GFLD_computed_source2;
					}
					else
					{
						X.RDB$COMPUTED_SOURCE.NULL = FALSE;
						get_source_blob (tdgbl, X.RDB$COMPUTED_SOURCE, false);
					}
					break;

				case att_field_validation_blr:
					if (tdgbl->gbl_sw_novalidity)
						eat_blob(tdgbl);
					else
					{
						// if we are going against a V4.0 database,
						// restore the global fields in 2 phases.

						if (tdgbl->global_trans)
						{
							if (!gfield)
								gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

							get_blr_blob (tdgbl, gfield->gfld_vb, true);
							gfield->gfld_flags |= GFLD_validation_blr;
						}
						else
						{
							X.RDB$VALIDATION_BLR.NULL = FALSE;
							get_blr_blob (tdgbl, X.RDB$VALIDATION_BLR, false);
						}
					}
					break;

				case att_field_validation_source:
					if (tdgbl->gbl_sw_novalidity)
						eat_blob(tdgbl);
					else
					{
						// if we are going against a V4.0 database,
						// restore the global fields in 2 phases.

						if (tdgbl->global_trans)
						{
							if (!gfield)
								gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

							get_misc_blob (tdgbl, gfield->gfld_vs, true);
							gfield->gfld_flags |= GFLD_validation_source;
						}
						else
						{
							X.RDB$VALIDATION_SOURCE.NULL = FALSE;
							get_misc_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false);
						}
					}
					break;

				case att_field_validation_source2:
					if (tdgbl->gbl_sw_novalidity)
						eat_blob(tdgbl);
					else
					{
						// if we are going against a V4.0 database,
						// restore the global fields in 2 phases.

						if (tdgbl->global_trans)
						{
							if (!gfield)
								gfield = (gfld*) BURP_alloc_zero(sizeof(gfld));

							get_source_blob (tdgbl, gfield->gfld_vs2, true);
							gfield->gfld_flags |= GFLD_validation_source2;
						}
						else
						{
							X.RDB$VALIDATION_SOURCE.NULL = FALSE;
							get_source_blob (tdgbl, X.RDB$VALIDATION_SOURCE, false);
						}
					}
					break;

				case att_field_missing_value:
					X.RDB$MISSING_VALUE.NULL = FALSE;
					get_blr_blob (tdgbl, X.RDB$MISSING_VALUE, false);
					break;

				case att_field_default_value:
					X.RDB$DEFAULT_VALUE.NULL = FALSE;
					get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, false);
					break;

				case att_field_system_flag:
					X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
					X.RDB$SYSTEM_FLAG.NULL = FALSE;
					break;

				case att_field_null_flag:
					if (tdgbl->gbl_sw_novalidity) {
						get_int32(tdgbl); // skip
					}
					else
					{
						X.RDB$NULL_FLAG = (USHORT) get_int32(tdgbl);
						X.RDB$NULL_FLAG.NULL = FALSE;
					}
					break;

				case att_field_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
					break;

				case att_field_description2:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
					break;

				case att_field_external_length:
					X.RDB$EXTERNAL_LENGTH.NULL = FALSE;
					X.RDB$EXTERNAL_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_field_external_scale:
					X.RDB$EXTERNAL_SCALE.NULL = FALSE;
					X.RDB$EXTERNAL_SCALE = (USHORT) get_int32(tdgbl);
					break;

				case att_field_external_type:
					X.RDB$EXTERNAL_TYPE.NULL = FALSE;
					X.RDB$EXTERNAL_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_field_dimensions:
					X.RDB$DIMENSIONS.NULL = FALSE;
					X.RDB$DIMENSIONS = (USHORT) get_int32(tdgbl);
					break;

				case att_field_character_length:
					X.RDB$CHARACTER_LENGTH.NULL = FALSE;
					X.RDB$CHARACTER_LENGTH = (USHORT) get_int32(tdgbl);
					break;

				case att_field_default_source:
					X.RDB$DEFAULT_SOURCE.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, false);
					break;

				case att_field_missing_source:
					X.RDB$MISSING_SOURCE.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$MISSING_SOURCE, false);
					break;

				case att_field_character_set:
					X.RDB$CHARACTER_SET_ID.NULL = FALSE;
					X.RDB$CHARACTER_SET_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_field_collation_id:
					X.RDB$COLLATION_ID.NULL = FALSE;
					X.RDB$COLLATION_ID = (USHORT) get_int32(tdgbl);
					break;

				case att_field_precision:
					if (tdgbl->RESTORE_format >= 6)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 92);
					break;

				case att_field_security_class:
				case att_field_owner_name:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 92);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 92);
					// msg 92  domain
					break;
				}
			}

			if (X.RDB$FIELD_TYPE <= DTYPE_BLR_MAX)
			{
				l = gds_cvt_blr_dtype[X.RDB$FIELD_TYPE];
				if (l = type_lengths[l])
					X.RDB$FIELD_LENGTH = l;
			}

			if (gfield)
				strcpy (gfield->gfld_name, X.RDB$FIELD_NAME);

			if (tdgbl->gbl_sw_fix_fss_data && tdgbl->gbl_sw_fix_fss_data_id == 0 &&
				!X.RDB$CHARACTER_SET_ID.NULL && X.RDB$CHARACTER_SET_ID == CS_UNICODE_FSS &&
				((!X.RDB$CHARACTER_LENGTH.NULL &&
					(X.RDB$FIELD_TYPE == blr_text || X.RDB$FIELD_TYPE == blr_varying)) ||
				 X.RDB$FIELD_TYPE == blr_blob))
			{
				if (X.RDB$FIELD_TYPE != blr_blob)
					X.RDB$CHARACTER_LENGTH = X.RDB$FIELD_LENGTH;

				X.RDB$CHARACTER_SET_ID = CS_NONE;
				X.RDB$COLLATION_ID = 0;
			}

		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;

	}

	if (gfield)
	{
		gfield->gfld_next = tdgbl->gbl_global_fields;
		tdgbl->gbl_global_fields = gfield;
	}

	return true;
}

bool get_index(BurpGlobals* tdgbl, const burp_rel* relation)
{
/**************************************
 *
 *	g e t _ i n d e x
 *
 **************************************
 *
 * Functional description
 *	Build an index.  At the end stop
 *	and check that all fields are defined.
 *	If any fields are missing, delete the
 *	index.
 *
 **************************************/
	BASED_ON RDB$INDICES.RDB$INDEX_NAME index_name;
	att_type attribute;
	bool foreign_index = false;
	bool expr_index = false;
	scan_attr_t scan_next_attr;

	SSHORT count = 0, segments = 0;

	STORE (REQUEST_HANDLE tdgbl->handles_get_index_req_handle1)
		X IN RDB$INDICES
		strcpy (X.RDB$RELATION_NAME, relation->rel_name);
		X.RDB$UNIQUE_FLAG = 0;
		if (!tdgbl->gbl_sw_deactivate_indexes)
			X.RDB$INDEX_INACTIVE = FALSE;
		else
			X.RDB$INDEX_INACTIVE = TRUE;
		X.RDB$INDEX_TYPE.NULL = TRUE;
		X.RDB$DESCRIPTION.NULL = TRUE;
		X.RDB$FOREIGN_KEY.NULL = TRUE;
		X.RDB$EXPRESSION_SOURCE.NULL = TRUE;
		X.RDB$EXPRESSION_BLR.NULL = TRUE;
		X.RDB$SYSTEM_FLAG = 0;
		X.RDB$SYSTEM_FLAG.NULL = FALSE;

		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_index_name:
				GET_TEXT(X.RDB$INDEX_NAME);
				strcpy (index_name, X.RDB$INDEX_NAME);
				BURP_verbose (122, X.RDB$INDEX_NAME);
				break;

			case att_segment_count:
				X.RDB$SEGMENT_COUNT = segments = (USHORT) get_int32(tdgbl);
				break;

			case att_index_unique_flag:
				X.RDB$UNIQUE_FLAG = (USHORT) get_int32(tdgbl);
				break;

			case att_index_inactive:
				X.RDB$INDEX_INACTIVE = (USHORT) get_int32(tdgbl);
				// Defer foreign key index activation
				// Modified by Toni Martir, all index deferred when verbose
				if (tdgbl->gbl_sw_verbose)
				{
					if (!X.RDB$INDEX_INACTIVE)
						X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE;
				}
				else
				{
					if (!X.RDB$INDEX_INACTIVE && (foreign_index || expr_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_int32(tdgbl);
				break;

			case att_index_field_name:
				STORE (REQUEST_HANDLE tdgbl->handles_get_index_req_handle2)
					Y IN RDB$INDEX_SEGMENTS
					GET_TEXT(Y.RDB$FIELD_NAME);
					strcpy (Y.RDB$INDEX_NAME, X.RDB$INDEX_NAME);
					Y.RDB$FIELD_POSITION = count++;
				END_STORE;
				ON_ERROR
					general_on_error ();
				END_ERROR;
				break;

			case att_index_description:
				X.RDB$DESCRIPTION.NULL = FALSE;
				get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
				break;

			case att_index_description2:
				X.RDB$DESCRIPTION.NULL = FALSE;
				get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
				break;

			case att_index_expression_source:
				X.RDB$EXPRESSION_SOURCE.NULL = FALSE;
				get_source_blob (tdgbl, X.RDB$EXPRESSION_SOURCE, false);
				break;

			case att_index_expression_blr:
				expr_index = true;
				// Defer expression index activation
				if (!X.RDB$INDEX_INACTIVE)
					X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE;
				if (tdgbl->gbl_sw_deactivate_indexes)
					X.RDB$INDEX_INACTIVE = TRUE;
				X.RDB$EXPRESSION_BLR.NULL = FALSE;
				get_blr_blob (tdgbl, X.RDB$EXPRESSION_BLR, false);
				break;

			case att_index_foreign_key:
				foreign_index = true;
				// Defer foreign key index activation
				if (!X.RDB$INDEX_INACTIVE)
					X.RDB$INDEX_INACTIVE = DEFERRED_ACTIVE;
				if (tdgbl->gbl_sw_deactivate_indexes)
					X.RDB$INDEX_INACTIVE = TRUE;
				X.RDB$FOREIGN_KEY.NULL = FALSE;
				GET_TEXT(X.RDB$FOREIGN_KEY);
				break;

			default:
				bad_attribute(scan_next_attr, attribute, 93);
				// msg 93 index
				break;
			}
		}

		count = 0;
		FOR (REQUEST_HANDLE tdgbl->handles_get_index_req_handle3)
			RFR IN RDB$RELATION_FIELDS CROSS I_S IN RDB$INDEX_SEGMENTS
			OVER RDB$FIELD_NAME WITH I_S.RDB$INDEX_NAME = index_name AND
			RFR.RDB$RELATION_NAME = relation->rel_name
			count++;
		END_FOR;
		ON_ERROR
			general_on_error ();
		END_ERROR;

		if (count != segments)
		{
			FOR (REQUEST_HANDLE tdgbl->handles_get_index_req_handle4)
				I_S IN RDB$INDEX_SEGMENTS WITH I_S.RDB$INDEX_NAME = index_name
				ERASE I_S;
				ON_ERROR
					general_on_error ();
				END_ERROR;
			END_FOR;
			ON_ERROR
				general_on_error ();
			END_ERROR;
			return false;
		}
	END_STORE;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	return true;
}

void get_misc_blob(BurpGlobals* tdgbl, ISC_QUAD& blob_id, bool glb_trans)
{
/**************************************
 *
 *	g e t _ m i s c _ b l o b
 *
 **************************************
 *
 * Functional description
 *	Read blob attributes and copy data from input file to nice,
 *	shiney, new blob.
 *
 **************************************/
	ISC_STATUS_ARRAY	status_vector;

	const FB_SIZE_T length = get_int32(tdgbl);

	// Create new blob

	isc_tr_handle	local_trans;
	if (glb_trans && tdgbl->global_trans)
		local_trans = tdgbl->global_trans;
	else
		local_trans = gds_trans;

	UserBlob blob(status_vector);
	if (!blob.create(DB, local_trans, blob_id))
	{
		BURP_error_redirect (status_vector, 37);
		// msg 37 isc_create_blob failed
	}

	// Allocate blob buffer if static buffer is too short
	BlobBuffer static_buffer;
	UCHAR* const buffer = static_buffer.getBuffer(length);

	if (length)
	{
		get_block(tdgbl, buffer, length);
	}

	if (!blob.putData(length, buffer))
	{
		BURP_error_redirect (status_vector, 38);
		// msg 38 isc_put_segment failed
	}

	if (!blob.close())
		BURP_error_redirect (status_vector, 23);
		// msg 23 isc_close_blob failed
}

SLONG get_int32(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ i n t 3 2
 *
 **************************************
 *
 * Functional description
 *	Get a 32-bit numeric value from the input stream.
 *
 **************************************/
	SLONG	value[2];

	// get_text needs additional space for the terminator,
	// because it treats everything as strings.
	fb_assert(sizeof(value) > sizeof(SLONG));

	const SSHORT length = get_text(tdgbl, (TEXT*) value, sizeof(value));

	return isc_vax_integer ((SCHAR *) value, length);
}

SINT64 get_int64(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ i n t 6 4
 *
 **************************************
 *
 * Functional description
 *	Get a possibly-64-bit numeric value from the input stream.
 *
 **************************************/
	SLONG	value[4];

	// get_text needs additional space for the terminator,
	// because it treats everything as strings.
	fb_assert (sizeof(value) > sizeof(SINT64));

	const SSHORT length = get_text(tdgbl, (TEXT*) value, sizeof(value));

	return isc_portable_integer ((UCHAR *) value, length);
}

bool get_package(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ p a c k a g e
 *
 **************************************
 *
 * Functional description
 *	Reconstruct a package.
 *
 **************************************/
	att_type attribute;
	TEXT temp[GDS_NAME_LEN];
	SSHORT len;
	scan_attr_t scan_next_attr;

	if (tdgbl->RESTORE_format < 10)
		return false;

	isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;

	burp_pkg* package = (burp_pkg*) BURP_alloc_zero(sizeof(burp_pkg));
	package->pkg_next = tdgbl->packages;
	tdgbl->packages = package;

	STORE (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_package_req_handle1)
		X IN RDB$PACKAGES
	{
		X.RDB$PACKAGE_HEADER_SOURCE.NULL = TRUE;
		X.RDB$PACKAGE_BODY_SOURCE.NULL = TRUE;
		X.RDB$VALID_BODY_FLAG.NULL = TRUE;
		X.RDB$SECURITY_CLASS.NULL = TRUE;
		X.RDB$OWNER_NAME.NULL = TRUE;
		X.RDB$SYSTEM_FLAG = 0;
		X.RDB$SYSTEM_FLAG.NULL = FALSE;
		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_package_name:
				len = GET_TEXT(X.RDB$PACKAGE_NAME);
				strcpy(package->pkg_name, X.RDB$PACKAGE_NAME);
				MISC_terminate(X.RDB$PACKAGE_NAME, temp, len, sizeof(temp));
				BURP_verbose(337, temp);	// msg 337 restoring package %s
				break;

			case att_package_header_source:
				get_source_blob(tdgbl, X.RDB$PACKAGE_HEADER_SOURCE, true);
				X.RDB$PACKAGE_HEADER_SOURCE.NULL = FALSE;
				break;

			case att_package_body_source:
				get_source_blob(tdgbl, X.RDB$PACKAGE_BODY_SOURCE, true);
				X.RDB$PACKAGE_BODY_SOURCE.NULL = FALSE;
				break;

			case att_package_valid_body_flag:
				X.RDB$VALID_BODY_FLAG = (USHORT) get_int32(tdgbl);
				X.RDB$VALID_BODY_FLAG.NULL = FALSE;
				break;

			case att_package_security_class:
				GET_TEXT(X.RDB$SECURITY_CLASS);
				fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false);
				X.RDB$SECURITY_CLASS.NULL = FALSE;
				break;

			case att_package_owner_name:
				GET_TEXT(package->pkg_owner);
				break;

			case att_package_description:
				get_source_blob(tdgbl, X.RDB$DESCRIPTION, true);
				X.RDB$DESCRIPTION.NULL = FALSE;
				break;

			default:
				bad_attribute(scan_next_attr, attribute, 338);	// msg 338 package
				break;
			}
		}
	}
	END_STORE
	ON_ERROR
		general_on_error();
	END_ERROR

	return true;
}

bool get_procedure(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ p r o c e d u r e
 *
 **************************************
 *
 * Functional description
 *	Reconstruct a stored procedure.
 *	Use the global_trans so we don't have to commit
 *	until after the indices are activated.  This
 *	will allow us to use a PLAN in a SP.
 *
 **************************************/
	att_type	attribute;
	GDS_NAME	package_name = "";
	GDS_NAME	procedure_name = "";
	TEXT		temp[GDS_NAME_LEN * 2];
	SSHORT		l;
	scan_attr_t		scan_next_attr;

	isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;

	burp_prc* procedure = (burp_prc*) BURP_alloc_zero (sizeof(burp_prc));
	procedure->prc_next = tdgbl->procedures;
	tdgbl->procedures = procedure;

	if (tdgbl->runtimeODS >= DB_VERSION_DDL11_1)
	{
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_procedure_req_handle1)
			X IN RDB$PROCEDURES
			X.RDB$PROCEDURE_SOURCE.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$SECURITY_CLASS.NULL = TRUE;
			X.RDB$OWNER_NAME.NULL = TRUE;
			X.RDB$PROCEDURE_TYPE.NULL = FALSE;
			X.RDB$PROCEDURE_TYPE = 0;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$VALID_BLR.NULL = TRUE;
			X.RDB$DEBUG_INFO.NULL = TRUE;
			X.RDB$PROCEDURE_BLR.NULL = TRUE;
			X.RDB$ENGINE_NAME.NULL = TRUE;
			X.RDB$ENTRYPOINT.NULL = TRUE;
			X.RDB$PACKAGE_NAME.NULL = TRUE;
			X.RDB$PRIVATE_FLAG.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:
					{
						SSHORT prefixLen = 0;
						if (package_name[0])
						{
							prefixLen = static_cast<SSHORT>(strlen(package_name));
							memcpy(temp, package_name, prefixLen);
							temp[prefixLen++] = '.';
						}

						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 + prefixLen, l,
							sizeof(temp) - prefixLen);
						BURP_verbose (195, temp);
						// msg 195 restoring stored procedure %s
					}
					break;

				case att_procedure_description:
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
					X.RDB$DESCRIPTION.NULL = FALSE;
					break;

				case att_procedure_description2:
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
					X.RDB$DESCRIPTION.NULL = FALSE;
					break;

				case att_procedure_source:
					get_misc_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true);
					X.RDB$PROCEDURE_SOURCE.NULL = FALSE;
					break;

				case att_procedure_source2:
					get_source_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true);
					X.RDB$PROCEDURE_SOURCE.NULL = FALSE;
					break;

				case att_procedure_blr:
					get_blr_blob (tdgbl, X.RDB$PROCEDURE_BLR, true);
					X.RDB$PROCEDURE_BLR.NULL = FALSE;
					break;

				case att_procedure_security_class:
					GET_TEXT(X.RDB$SECURITY_CLASS);
					fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false);
					X.RDB$SECURITY_CLASS.NULL = FALSE;
					break;

				case att_procedure_owner_name:
					GET_TEXT(procedure->prc_owner);
					break;

				case att_procedure_inputs:
					X.RDB$PROCEDURE_INPUTS = (USHORT) get_int32(tdgbl);
					if (X.RDB$PROCEDURE_INPUTS == 0)
						X.RDB$PROCEDURE_INPUTS.NULL = TRUE;
					else
						X.RDB$PROCEDURE_INPUTS.NULL = FALSE;
					break;

				case att_procedure_outputs:
					X.RDB$PROCEDURE_OUTPUTS = (USHORT) get_int32(tdgbl);
					break;

				case att_procedure_type:
					if (tdgbl->RESTORE_format >= 8)
						X.RDB$PROCEDURE_TYPE = (USHORT) get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 290);
					break;

				case att_procedure_valid_blr:
					if (tdgbl->RESTORE_format >= 8)
					{
						X.RDB$VALID_BLR.NULL = FALSE;
						X.RDB$VALID_BLR = (USHORT) get_int32(tdgbl);
					}
					else
						bad_attribute(scan_next_attr, attribute, 290);
					break;

				case att_procedure_debug_info:
					if (tdgbl->RESTORE_format >= 8)
					{
						X.RDB$DEBUG_INFO.NULL = FALSE;
						get_misc_blob (tdgbl, X.RDB$DEBUG_INFO, true);
					}
					else
						bad_attribute(scan_next_attr, attribute, 290);
					break;

				case att_procedure_engine_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$ENGINE_NAME);
						X.RDB$ENGINE_NAME.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 290);
					break;

				case att_procedure_entrypoint:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$ENTRYPOINT);
						X.RDB$ENTRYPOINT.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 290);
					break;

				case att_procedure_package_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$PACKAGE_NAME);
						X.RDB$PACKAGE_NAME.NULL = FALSE;
						strcpy(procedure->prc_package, X.RDB$PACKAGE_NAME);
						strcpy(package_name, X.RDB$PACKAGE_NAME);
						fb_utils::exact_name(package_name);
					}
					else
						bad_attribute(scan_next_attr, attribute, 290);
					break;

				case att_procedure_private_flag:
					if (tdgbl->RESTORE_format >= 10)
					{
						X.RDB$PRIVATE_FLAG = (USHORT) get_int32(tdgbl);
						X.RDB$PRIVATE_FLAG.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 290);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 290);
					// msg 290 procedure
					break;
				}
			}
			strcpy (procedure_name, X.RDB$PROCEDURE_NAME);
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else
	{
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_procedure_req_handle1)
			X IN RDB$PROCEDURES
			X.RDB$PROCEDURE_SOURCE.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$SECURITY_CLASS.NULL = TRUE;
			X.RDB$OWNER_NAME.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_procedure_name:
					l = GET_TEXT(X.RDB$PROCEDURE_NAME);
					//procedure->prc_name_length = l;
					strcpy (procedure->prc_name, X.RDB$PROCEDURE_NAME);
					MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof(temp));
					BURP_verbose (195, temp);
					// msg 195 restoring stored procedure %s
					break;

				case att_procedure_description:
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
					X.RDB$DESCRIPTION.NULL = FALSE;
					break;

				case att_procedure_description2:
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
					X.RDB$DESCRIPTION.NULL = FALSE;
					break;

				case att_procedure_source:
					get_misc_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true);
					X.RDB$PROCEDURE_SOURCE.NULL = FALSE;
					break;

				case att_procedure_source2:
					get_source_blob (tdgbl, X.RDB$PROCEDURE_SOURCE, true);
					X.RDB$PROCEDURE_SOURCE.NULL = FALSE;
					break;

				case att_procedure_blr:
					get_blr_blob (tdgbl, X.RDB$PROCEDURE_BLR, true);
					break;

				case att_procedure_security_class:
					GET_TEXT(X.RDB$SECURITY_CLASS);
					fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false);
					X.RDB$SECURITY_CLASS.NULL = FALSE;
					break;

				case att_procedure_owner_name:
					GET_TEXT(procedure->prc_owner);
					break;

				case att_procedure_inputs:
					X.RDB$PROCEDURE_INPUTS = (USHORT) get_int32(tdgbl);
					if (X.RDB$PROCEDURE_INPUTS == 0)
						X.RDB$PROCEDURE_INPUTS.NULL = TRUE;
					else
						X.RDB$PROCEDURE_INPUTS.NULL = FALSE;
					break;

				case att_procedure_outputs:
					X.RDB$PROCEDURE_OUTPUTS = (USHORT) get_int32(tdgbl);
					break;

				case att_procedure_type:
				case att_procedure_valid_blr:
					if (tdgbl->RESTORE_format >= 8)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 290);
					break;

				case att_procedure_debug_info:
					if (tdgbl->RESTORE_format >= 8)
						eat_blob(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 290);
					break;

				case att_procedure_engine_name:
				case att_procedure_entrypoint:
				case att_procedure_package_name:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 290);
					break;

				case att_procedure_private_flag:
					if (tdgbl->RESTORE_format >= 10)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 290);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 290);
					// msg 290 procedure
					break;
				}
			}
			strcpy (procedure_name, X.RDB$PROCEDURE_NAME);
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}

	// at the end of prms for a procedure is the rec_procedure_end marker

	while (get(tdgbl) == rec_procedure_prm)
		get_procedure_prm (tdgbl, package_name, procedure_name);

	return true;
}

bool get_procedure_prm (BurpGlobals* tdgbl, GDS_NAME package_name, GDS_NAME procptr)
{
/**************************************
 *
 *	g e t _ p r o c e d u r e _ p r m
 *
 **************************************
 *
 * Functional description
 *	Reconstruct stored procedure parameter.
 *	Use the global_trans so we don't have to commit
 *	until after the indices are activated.  This
 *	will allow us to use a PLAN in a SP.
 *
 **************************************/
	att_type	attribute;
	SSHORT		l;
	TEXT		temp[GDS_NAME_LEN];
	scan_attr_t		scan_next_attr;

	isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;

	if (tdgbl->runtimeODS >= DB_VERSION_DDL11_1)
	{
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_procedure_prm_req_handle1)
			X IN RDB$PROCEDURE_PARAMETERS

			strcpy(X.RDB$PROCEDURE_NAME, procptr);

			if (package_name[0])
			{
				strcpy(X.RDB$PACKAGE_NAME, package_name);
				X.RDB$PACKAGE_NAME.NULL = FALSE;
			}
			else
				X.RDB$PACKAGE_NAME.NULL = TRUE;

			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$DEFAULT_VALUE.NULL = TRUE;
			X.RDB$DEFAULT_SOURCE.NULL = TRUE;
			X.RDB$COLLATION_ID.NULL = TRUE;

			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;

			X.RDB$NULL_FLAG = FALSE;
			X.RDB$NULL_FLAG.NULL = FALSE;

			X.RDB$PARAMETER_MECHANISM = prm_mech_normal;
			X.RDB$PARAMETER_MECHANISM.NULL = FALSE;

			// DB_VERSION_DDL11_2
			X.RDB$FIELD_NAME.NULL = TRUE;
			X.RDB$RELATION_NAME.NULL = TRUE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_procedureprm_name:
					l = GET_TEXT(X.RDB$PARAMETER_NAME);
					MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof(temp));
					BURP_verbose (196, temp);
					// msg 196 restoring parameter %s for stored procedure
					break;

				case att_procedureprm_type:
					X.RDB$PARAMETER_TYPE= (USHORT) get_int32(tdgbl);
					break;

				case att_procedureprm_number:
					X.RDB$PARAMETER_NUMBER= (USHORT) get_int32(tdgbl);
					break;

				case att_procedureprm_field_source:
					GET_TEXT(X.RDB$FIELD_SOURCE);
					break;

				case att_procedureprm_description:
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
					X.RDB$DESCRIPTION.NULL = FALSE;
					break;

				case att_procedureprm_description2:
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
					X.RDB$DESCRIPTION.NULL = FALSE;
					break;

				case att_procedureprm_default_value:
					if (tdgbl->RESTORE_format >= 8)
					{
						X.RDB$DEFAULT_VALUE.NULL = FALSE;
						get_blr_blob (tdgbl, X.RDB$DEFAULT_VALUE, true);
					}
					else
						bad_attribute(scan_next_attr, attribute, 291);
					break;

				case att_procedureprm_default_source:
					if (tdgbl->RESTORE_format >= 8)
					{
						X.RDB$DEFAULT_SOURCE.NULL = FALSE;
						get_source_blob (tdgbl, X.RDB$DEFAULT_SOURCE, true);
					}
					else
						bad_attribute(scan_next_attr, attribute, 291);
					break;

				case att_procedureprm_collation_id:
					if (tdgbl->RESTORE_format >= 8)
					{
						X.RDB$COLLATION_ID.NULL = FALSE;
						X.RDB$COLLATION_ID = (USHORT) get_int32(tdgbl);
					}
					else
						bad_attribute(scan_next_attr, attribute, 291);
					break;

				case att_procedureprm_null_flag:
					if (tdgbl->RESTORE_format >= 8)
						X.RDB$NULL_FLAG = (USHORT) get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 291);
					break;

				case att_procedureprm_mechanism:
					if (tdgbl->RESTORE_format >= 8)
						X.RDB$PARAMETER_MECHANISM = (USHORT) get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 291);
					break;

				// DB_VERSION_DDL11_2
				case att_procedureprm_field_name:
					if (tdgbl->RESTORE_format >= 9)
					{
						X.RDB$FIELD_NAME.NULL = FALSE;
						GET_TEXT(X.RDB$FIELD_NAME);
					}
					else
						bad_attribute(scan_next_attr, attribute, 291);
					break;

				// DB_VERSION_DDL11_2
				case att_procedureprm_relation_name:
					if (tdgbl->RESTORE_format >= 9)
					{
						X.RDB$RELATION_NAME.NULL = FALSE;
						GET_TEXT(X.RDB$RELATION_NAME);
					}
					else
						bad_attribute(scan_next_attr, attribute, 291);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 291);
					// msg 291 procedure parameter
					break;
				}
			}
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else
	{
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_procedure_prm_req_handle1)
			X IN RDB$PROCEDURE_PARAMETERS
			X.RDB$DESCRIPTION.NULL = TRUE;
			strcpy (X.RDB$PROCEDURE_NAME, procptr);
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_procedureprm_name:
					l = GET_TEXT(X.RDB$PARAMETER_NAME);
					MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof(temp));
					BURP_verbose (196, temp);
					// msg 196 restoring parameter %s for stored procedure
					break;

				case att_procedureprm_type:
					X.RDB$PARAMETER_TYPE= (USHORT) get_int32(tdgbl);
					break;

				case att_procedureprm_number:
					X.RDB$PARAMETER_NUMBER= (USHORT) get_int32(tdgbl);
					break;

				case att_procedureprm_field_source:
					GET_TEXT(X.RDB$FIELD_SOURCE);
					break;

				case att_procedureprm_description:
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
					X.RDB$DESCRIPTION.NULL = FALSE;
					break;

				case att_procedureprm_description2:
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
					X.RDB$DESCRIPTION.NULL = FALSE;
					break;

				case att_procedureprm_default_value:
				case att_procedureprm_default_source:
					if (tdgbl->RESTORE_format >= 8)
						eat_blob(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 291);
					break;

				case att_procedureprm_collation_id:
				case att_procedureprm_null_flag:
				case att_procedureprm_mechanism:
					if (tdgbl->RESTORE_format >= 8)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 291);
					break;

				// DB_VERSION_DDL11_2
				case att_procedureprm_field_name:
				case att_procedureprm_relation_name:
					if (tdgbl->RESTORE_format >= 9)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 291);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 291);
					// msg 291 procedure parameter
					break;
				}
			}
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}

	return true;
}

bool get_ref_constraint(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ r e f _ c o n s t r a i n t
 *
 **************************************
 *
 * Functional description
 *	Restore data for referential constraints.
 *
 **************************************/
	att_type        attribute;
	scan_attr_t		scan_next_attr;

	STORE (REQUEST_HANDLE tdgbl->handles_get_ref_constraint_req_handle1)
		X IN RDB$REF_CONSTRAINTS
		X.RDB$CONSTRAINT_NAME.NULL = TRUE;
		X.RDB$CONST_NAME_UQ.NULL = TRUE;
		X.RDB$MATCH_OPTION.NULL = TRUE;
		X.RDB$UPDATE_RULE.NULL = TRUE;
		X.RDB$DELETE_RULE.NULL = TRUE;

		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
				case att_ref_constraint_name:
					X.RDB$CONSTRAINT_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$CONSTRAINT_NAME);
					break;

				case att_ref_unique_const_name:
					X.RDB$CONST_NAME_UQ.NULL = FALSE;
					GET_TEXT(X.RDB$CONST_NAME_UQ);
					break;

				case att_ref_match_option:
					X.RDB$MATCH_OPTION.NULL = FALSE;
					GET_TEXT(X.RDB$MATCH_OPTION);
					break;

				case att_ref_update_rule:
					X.RDB$UPDATE_RULE.NULL = FALSE;
					GET_TEXT(X.RDB$UPDATE_RULE);
					break;

				case att_ref_delete_rule:
					X.RDB$DELETE_RULE.NULL = FALSE;
					GET_TEXT(X.RDB$DELETE_RULE);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 292);
					// msg 292 referential constraint
					break;
			}
		}
	END_STORE;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	return true;
}

bool get_relation(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ r e l a t i o n
 *
 **************************************
 *
 * Functional description
 *	Write relation meta-data and data.
 *	Use the default transaction for RELATIONS,
 *	and use the global_trans for VIEWS.  This
 *	enables us to have views of SP and views
 *	with plans.  Assume it is a view if it has
 *	RDB$VIEW_BLR, also assume RDB$VIEW_BLR is
 *	the first blob in the backup file.
 *
 *
 **************************************/
	SLONG		rel_flags = 0, sys_flag = fb_sysflag_user, type = rel_persistent;
	bool		rel_flags_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;

	// Before starting to restore relations, commit everything that was restored
	// prior to this point. This ensures that no pending error can later affect
	// other metadata being restored.

	if (!tdgbl->relations)
	{
		BURP_verbose (68);
		// msg 68 committing meta data
		COMMIT
		ON_ERROR
			general_on_error ();
		END_ERROR;

		EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
		if (gds_status[1])
			EXEC SQL SET TRANSACTION;
	}

	// Pick up relation attributes

	burp_rel* relation = (burp_rel*) BURP_alloc_zero (sizeof(burp_rel));
	relation->rel_next = tdgbl->relations;
	tdgbl->relations = relation;

	/*
	STORE (REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1)
		X IN RDB$RELATIONS
		X.RDB$SYSTEM_FLAG = 0;
		X.RDB$SYSTEM_FLAG.NULL = FALSE;
		X.RDB$FLAGS.NULL = TRUE;
		X.RDB$SECURITY_CLASS.NULL = TRUE;
		X.RDB$VIEW_BLR.NULL = TRUE;
		X.RDB$VIEW_SOURCE.NULL = TRUE;
		X.RDB$DESCRIPTION.NULL = TRUE;
		X.RDB$RUNTIME.NULL = TRUE;
		X.RDB$EXTERNAL_DESCRIPTION.NULL = TRUE;
	*/

	TEXT rel_name[GDS_NAME_LEN] = "";
	att_type attribute;
	scan_attr_t scan_next_attr;
	skip_init(&scan_next_attr);

	while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
	{
		switch (attribute)
		{
		case att_relation_name:
			{
				const SSHORT l = GET_TEXT(relation->rel_name);
				relation->rel_name_length = l;
				MISC_terminate (relation->rel_name, rel_name, l, sizeof(rel_name));
				BURP_verbose(167, rel_name); // msg 167 restoring table @1
			}
			break;

		case att_relation_security_class:
			sec_class_null = false;
			GET_TEXT(sec_class);
			fix_security_class_name(tdgbl, sec_class, false);
			break;

		case att_relation_view_blr:
			view_blr_null = false;
			get_blr_blob(tdgbl, view_blr, true);
			relation->rel_flags |= REL_view;
			fb_assert(rel_name[0] != 0);
			BURP_verbose(346, rel_name); // msg 346 '    table @1 is a view'
			break;

		case att_relation_view_source:
			view_src_null = false;
			get_misc_blob (tdgbl, view_src, !view_blr_null);
			break;

		case att_relation_view_source2:
			view_src_null = false;
			get_source_blob(tdgbl, view_src, !view_blr_null);
			break;

		case att_relation_description:
			rel_desc_null = false;
			get_misc_blob(tdgbl, rel_desc, !view_blr_null);
			break;

		case att_relation_description2:
			rel_desc_null = false;
			get_source_blob(tdgbl, rel_desc, !view_blr_null);
			break;

		case att_relation_flags:
			rel_flags_null = false;
			rel_flags = get_int32(tdgbl);
			break;

		case att_relation_system_flag:
			sys_flag = get_int32(tdgbl);
			break;

		case att_relation_ext_description:
			ext_desc_null = false;
			get_misc_blob(tdgbl, ext_desc, !view_blr_null);
			break;

		case att_relation_ext_description2:
			ext_desc_null = false;
			get_source_blob(tdgbl, ext_desc, !view_blr_null);
			break;

		case att_relation_owner_name:
			GET_TEXT(relation->rel_owner);
			break;

		case att_relation_ext_file_name:
			ext_file_name_null = false;
			GET_TEXT(ext_file_name);
			break;

		case att_relation_type:
			if (tdgbl->RESTORE_format >= 8)
				type = get_int32(tdgbl);
			else
				bad_attribute(scan_next_attr, attribute, 111);
			break;

		default:
			bad_attribute(scan_next_attr, attribute, 111);
			// msg 111 table
			break;
		}
	}

	// If this is a view and there is a global transaction then use it
	isc_tr_handle local_trans;
	if (view_blr_null || !tdgbl->global_trans)
		local_trans = gds_trans;
	else
		local_trans = tdgbl->global_trans;

	if (tdgbl->runtimeODS >= DB_VERSION_DDL11_1)
	{
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1)
			X IN RDB$RELATIONS

			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$FLAGS.NULL = rel_flags_null;
			X.RDB$SECURITY_CLASS.NULL = sec_class_null;
			X.RDB$VIEW_BLR.NULL = view_blr_null;
			X.RDB$VIEW_SOURCE.NULL = view_src_null;
			X.RDB$DESCRIPTION.NULL = rel_desc_null;
			X.RDB$RUNTIME.NULL = TRUE;
			X.RDB$EXTERNAL_DESCRIPTION.NULL = ext_desc_null;
			X.RDB$EXTERNAL_FILE.NULL = ext_file_name_null;
			X.RDB$RELATION_TYPE.NULL = FALSE;

			X.RDB$SYSTEM_FLAG = (USHORT) sys_flag;
			X.RDB$FLAGS = (USHORT) rel_flags;
			X.RDB$VIEW_BLR = view_blr;
			X.RDB$VIEW_SOURCE = view_src;
			X.RDB$DESCRIPTION = rel_desc;
			X.RDB$EXTERNAL_DESCRIPTION = ext_desc;

			strcpy(X.RDB$SECURITY_CLASS, sec_class);
			strcpy(X.RDB$RELATION_NAME, relation->rel_name);
			strcpy(X.RDB$EXTERNAL_FILE, ext_file_name);

			X.RDB$RELATION_TYPE = (USHORT) type;

		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else
	{
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_relation_req_handle1)
			X IN RDB$RELATIONS

			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$FLAGS.NULL = rel_flags_null;
			X.RDB$SECURITY_CLASS.NULL = sec_class_null;
			X.RDB$VIEW_BLR.NULL = view_blr_null;
			X.RDB$VIEW_SOURCE.NULL = view_src_null;
			X.RDB$DESCRIPTION.NULL = rel_desc_null;
			X.RDB$RUNTIME.NULL = TRUE;
			X.RDB$EXTERNAL_DESCRIPTION.NULL = ext_desc_null;
			X.RDB$EXTERNAL_FILE.NULL = ext_file_name_null;

			X.RDB$SYSTEM_FLAG = (USHORT) sys_flag;
			X.RDB$FLAGS = (USHORT) rel_flags;
			X.RDB$VIEW_BLR = view_blr;
			X.RDB$VIEW_SOURCE = view_src;
			X.RDB$DESCRIPTION = rel_desc;
			X.RDB$EXTERNAL_DESCRIPTION = ext_desc;

			strcpy(X.RDB$SECURITY_CLASS, sec_class);
			strcpy(X.RDB$RELATION_NAME, relation->rel_name);
			strcpy(X.RDB$EXTERNAL_FILE, ext_file_name);

		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}

	// Eat up misc. records
	burp_fld* field = NULL;
	burp_fld** ptr = &relation->rel_fields;

	rec_type record;
	while (get_record(&record, tdgbl) != rec_data)
	{
		switch (record)
		{
		case rec_relation_end:
			if (tdgbl->gbl_sw_incremental)
			{
				BURP_verbose (170, relation->rel_name);
				// msg 170: committing metadata for relation %s
				COMMIT
				// existing ON_ERROR continues past error, beck
				ON_ERROR
					BURP_print (false, 171, relation->rel_name);
					// msg 171: error committing metadata for relation %s
					BURP_print_status (false, tdgbl->status_vector);
					ROLLBACK;
					ON_ERROR
						general_on_error ();
					END_ERROR;
				END_ERROR;
				EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
				if (gds_status[1])
					EXEC SQL SET TRANSACTION;
			}
			return true;

		case rec_field:
			*ptr = field = get_field (tdgbl, relation);
			if (!field)
				return false;
			ptr = &field->fld_next;
			break;

		case rec_view:
			get_view(tdgbl, relation);
			break;

		default:
			BURP_error(43, true, SafeArg() << record);
			// msg 43 don't recognize record type %ld
			break;
		}
	}

	// If we fall thru, there are data records to be gotten
	// we can get here only when restoring ancient gbak'ed files where rec_data
	// was once embedded into rec_relation ... otherwise, meta commit happens
	// when we see the first rec_relation_data

	BURP_verbose (68);
	// msg 68 committing meta data

	COMMIT;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
	if (gds_status[1])
		EXEC SQL SET TRANSACTION;

	get_data(tdgbl, relation, tdgbl->skipRelation(rel_name));

	return true;
}

bool get_rel_constraint(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ r e l _ c o n s t r a i n t
 *
 **************************************
 *
 * Functional description
 *	Restore data for relation constraints.
 *
 **************************************/
	att_type		attribute;
	scan_attr_t		scan_next_attr;

	STORE (REQUEST_HANDLE tdgbl->handles_get_rel_constraint_req_handle1)
		X IN RDB$RELATION_CONSTRAINTS
		X.RDB$CONSTRAINT_NAME.NULL = TRUE;
		X.RDB$CONSTRAINT_TYPE.NULL = TRUE;
		X.RDB$RELATION_NAME.NULL = TRUE;
		X.RDB$DEFERRABLE.NULL = TRUE;
		X.RDB$INITIALLY_DEFERRED.NULL = TRUE;
		X.RDB$INDEX_NAME.NULL = TRUE;

		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
				case att_rel_constraint_name:
					X.RDB$CONSTRAINT_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$CONSTRAINT_NAME);
					break;

				case att_rel_constraint_type:
					X.RDB$CONSTRAINT_TYPE.NULL = FALSE;
					GET_TEXT(X.RDB$CONSTRAINT_TYPE);
					break;

				case att_rel_constraint_rel_name:
					X.RDB$RELATION_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$RELATION_NAME);
					break;

				case att_rel_constraint_defer:
					X.RDB$DEFERRABLE.NULL = FALSE;
					GET_TEXT(X.RDB$DEFERRABLE);
					break;

				case att_rel_constraint_init:
					X.RDB$INITIALLY_DEFERRED.NULL = FALSE;
					GET_TEXT(X.RDB$INITIALLY_DEFERRED);
					break;

				case att_rel_constraint_index:
					X.RDB$INDEX_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$INDEX_NAME);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 208);
					// msg 208 table constraint
					break;
			}
		}
	END_STORE;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	return true;
}

bool get_relation_data(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ r e l a t i o n _ d a t a
 *
 **************************************
 *
 * Functional description
 *	Restore data for a relation.  This is called when the data is
 *	standing free from the relation definition.  We first need to
 *	find the relation named.  If we can't find it, give up.
 *
 **************************************/
	BASED_ON RDB$RELATIONS.RDB$RELATION_NAME name;
	att_type	attribute;
	scan_attr_t		scan_next_attr;

	burp_rel* relation = NULL;

	skip_init(&scan_next_attr);
	while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
	{
		switch (attribute)
		{
		case att_relation_name:
			GET_TEXT(name);
			relation = find_relation (tdgbl, name);
			break;

		default:
			bad_attribute(scan_next_attr, attribute, 111);
			// msg 111 table
			break;
		}
	}

	if (!relation)
		BURP_error_redirect (NULL, 49);
	// msg 49 no relation name for data

	// Eat up misc. records
	rec_type record;
	get_record(&record, tdgbl);

	SLONG gen_id;
	bool skip_flag = tdgbl->skipRelation(name);

	skip_init(&scan_next_attr);
	while (skip_scan(&scan_next_attr), true)
	{
		switch (record)
		{
		case rec_relation_end:
			return true;

		case rec_data:
			record = get_data(tdgbl, relation, skip_flag);
			// get_data does a GET_RECORD
			break;

		case rec_gen_id:
			gen_id = get_int32(tdgbl);
			store_blr_gen_id(tdgbl, name, gen_id, 0, NULL, NULL, NULL, fb_sysflag_user, 1);
			get_record(&record, tdgbl);
			break;

		case rec_index:
			get_index (tdgbl, relation);
			get_record(&record, tdgbl);
			break;

		case rec_trigger:  // old style trigger
			get_trigger_old (tdgbl, relation);
			get_record(&record, tdgbl);
			break;

		default:
			bad_attribute(scan_next_attr, attribute, 111);
			// msg 111 relation
			get_record(&record, tdgbl);
			break;
		}
	}

	return true;
}

bool get_sql_roles(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ s q l _ r o l e s
 *
 **************************************
 *
 * Functional description
 *	Restore data for SQL roles
 *
 **************************************/
	att_type		attribute;
	scan_attr_t		scan_next_attr;
	TEXT			temp[GDS_NAME_LEN];
	SSHORT			l;

	if (tdgbl->runtimeODS >= DB_VERSION_DDL11)
	{
		STORE (REQUEST_HANDLE tdgbl->handles_get_sql_roles_req_handle1)
			X IN RDB$ROLES

			X.RDB$ROLE_NAME.NULL = TRUE;
			X.RDB$OWNER_NAME.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_role_name:
					X.RDB$ROLE_NAME.NULL = FALSE;
					l = GET_TEXT(X.RDB$ROLE_NAME);
					MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp));
					// msg 251, restoring SQL role: %s
					BURP_verbose (251, temp);
					break;

				case att_role_owner_name:
					X.RDB$OWNER_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$OWNER_NAME);
					break;

				case att_role_description:
					if (tdgbl->RESTORE_format >= 7)
					{
						get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
						X.RDB$DESCRIPTION.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 250);
				    break;

				default:
					// msg 250 SQL role
					bad_attribute(scan_next_attr, attribute, 250);
					break;
				}
			}
		END_STORE
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else if (tdgbl->runtimeODS >= DB_VERSION_DDL9)
	{
		// This is the first IB version (v5, ods9) where roles appeared.
		// They remained unchanged for IB6 / FB1 and FB1.5 (ods10).
		STORE (REQUEST_HANDLE tdgbl->handles_get_sql_roles_req_handle1)
			X IN RDB$ROLES

			X.RDB$ROLE_NAME.NULL = TRUE;
			X.RDB$OWNER_NAME.NULL = TRUE;
			// Here we didn't have RBD$SYSTEM_FLAG field.

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_role_name:
					X.RDB$ROLE_NAME.NULL = FALSE;
					l = GET_TEXT(X.RDB$ROLE_NAME);
					MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp));
					// msg 251, restoring SQL role: %s
					BURP_verbose (251, temp);
					break;

				case att_role_owner_name:
					X.RDB$OWNER_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$OWNER_NAME);
					break;

				case att_role_description:
					if (tdgbl->RESTORE_format >= 7)
						eat_blob(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 250);
					break;

				default:
					// msg 250 SQL role
					bad_attribute(scan_next_attr, attribute, 250);
					break;
				}
			}
		END_STORE
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else
	{
		// We say we support IB4, then we should skip roles.
		skip_init(&scan_next_attr);

		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_role_name:
			case att_role_owner_name:
				eat_text(tdgbl);
				break;

			case att_role_description:
				if (tdgbl->RESTORE_format >= 7)
					eat_blob(tdgbl);
				else
					bad_attribute(scan_next_attr, attribute, 250);
				break;

			default:
				// msg 250 SQL role
				bad_attribute(scan_next_attr, attribute, 250);
				break;
			}
		}
	}

	return true;
}

bool get_mapping(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ m a p p i n g
 *
 **************************************
 *
 * Functional description
 *	Restore mapping to users and roles
 *
 **************************************/
	att_type		attribute;
	scan_attr_t		scan_next_attr;
	TEXT			temp[GDS_NAME_LEN];
	SSHORT			l;
	Firebird::string role;

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		STORE (REQUEST_HANDLE tdgbl->handles_get_mapping_req_handle1)
			M IN RDB$AUTH_MAPPING

			M.RDB$MAP_TO_TYPE.NULL = TRUE;
			M.RDB$MAP_NAME.NULL = TRUE;
			M.RDB$MAP_USING.NULL = TRUE;
			M.RDB$MAP_PLUGIN.NULL = TRUE;
			M.RDB$MAP_DB.NULL = TRUE;
			M.RDB$MAP_FROM_TYPE.NULL = TRUE;
			M.RDB$MAP_FROM.NULL = TRUE;
			M.RDB$MAP_TO.NULL = TRUE;
			M.RDB$DESCRIPTION.NULL = TRUE;

			M.RDB$SYSTEM_FLAG.NULL = FALSE;
			M.RDB$SYSTEM_FLAG = 0;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_auto_map_role:
					GET_TEXT(temp);
					M.RDB$MAP_NAME.NULL = FALSE;
					strcpy(M.RDB$MAP_NAME, "AutoAdminImplementationMapping");
					M.RDB$MAP_FROM.NULL = FALSE;
					strcpy(M.RDB$MAP_FROM, FB_DOMAIN_ANY_RID_ADMINS);
					M.RDB$MAP_FROM_TYPE.NULL = FALSE;
					strcpy(M.RDB$MAP_FROM_TYPE, FB_PREDEFINED_GROUP);
					M.RDB$MAP_USING.NULL = FALSE;
					strcpy(M.RDB$MAP_USING, "P");
					M.RDB$MAP_PLUGIN.NULL = FALSE;
					strcpy(M.RDB$MAP_PLUGIN, "Win_Sspi");
					M.RDB$MAP_TO_TYPE.NULL = FALSE;
					M.RDB$MAP_TO_TYPE = 1;
					M.RDB$MAP_TO.NULL = FALSE;
					strcpy(M.RDB$MAP_TO, ADMIN_ROLE);

					BURP_verbose(301);
					// msg 301, restoring names mapping
					BURP_verbose (298, M.RDB$MAP_NAME);
					break;

				case att_map_name:
					M.RDB$MAP_NAME.NULL = FALSE;
					GET_TEXT(M.RDB$MAP_NAME);
					if (tdgbl->firstMap)
					{
						tdgbl->firstMap = false;
						BURP_verbose(301);
						// msg 301, restoring names mapping
					}
					BURP_verbose (298, M.RDB$MAP_NAME);
					break;

				case att_map_using:
					M.RDB$MAP_USING.NULL = FALSE;
					GET_TEXT(M.RDB$MAP_USING);
					break;

				case att_map_plugin:
					M.RDB$MAP_PLUGIN.NULL = FALSE;
					GET_TEXT(M.RDB$MAP_PLUGIN);
					break;

				case att_map_db:
					M.RDB$MAP_DB.NULL = FALSE;
					GET_TEXT(M.RDB$MAP_DB);
					break;

				case att_map_from_type:
					M.RDB$MAP_FROM_TYPE.NULL = FALSE;
					GET_TEXT(M.RDB$MAP_FROM_TYPE);
					break;

				case att_map_from:
					M.RDB$MAP_FROM.NULL = FALSE;
					GET_TEXT(M.RDB$MAP_FROM);
					break;

				case att_map_to_type:
					M.RDB$MAP_TO_TYPE.NULL = FALSE;
					M.RDB$MAP_TO_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_map_to:
					M.RDB$MAP_TO.NULL = FALSE;
					GET_TEXT(M.RDB$MAP_TO);
					break;

				case att_map_description:
					M.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, M.RDB$DESCRIPTION, false);
					break;

				default:
					// msg 299 name mapping
					bad_attribute(scan_next_attr, attribute, 299);
					break;
				}
			}

			if (M.RDB$MAP_NAME.NULL || M.RDB$MAP_USING.NULL ||
				M.RDB$MAP_FROM_TYPE.NULL || M.RDB$MAP_TO_TYPE.NULL)
			{
				// msg 353 required mapping attributes are missing in backup file
				BURP_error(353, true);
			}

		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else
	{
		int match = 0;
		const unsigned MATCH_FROM = 0x01;
		const unsigned MATCH_FROM_TYPE = 0x02;
		const unsigned MATCH_USING = 0x04;
		const unsigned MATCH_PLUGIN = 0x08;
		const unsigned MATCH_TO_TYPE = 0x10;
		const unsigned MATCH_TO = 0x20;
		const unsigned MATCH_ALL = 0x3f;
		Firebird::NoCaseString txt;

		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_auto_map_role:
				l = GET_TEXT(temp);
				role.assign(temp, l);
				break;

			case att_map_to_type:
				if (get_int32(tdgbl) == 1)
					match |= MATCH_TO_TYPE;
				break;

			case att_map_name:
			case att_map_using:
			case att_map_plugin:
			case att_map_db:
			case att_map_from_type:
			case att_map_from:
			case att_map_to:
				l = GET_TEXT(temp);
				txt.assign(temp, l);

				switch(attribute)
				{
				case att_map_using:
					if (txt == "P")
						match |= MATCH_USING;
					break;

				case att_map_plugin:
					if (txt == "Win_Sspi")
						match |= MATCH_PLUGIN;
					break;

				case att_map_from_type:
					if (txt == FB_PREDEFINED_GROUP)
						match |= MATCH_FROM_TYPE;
					break;

				case att_map_from:
					if (txt == FB_DOMAIN_ANY_RID_ADMINS)
						match |= MATCH_FROM;
					break;

				case att_map_to:
					if (txt == ADMIN_ROLE)
						match |= MATCH_TO;
					break;

				}
				break;

			case att_map_description:
				eat_blob(tdgbl);

			default:
				// msg 299 name mapping
				bad_attribute(scan_next_attr, attribute, 299);
				break;
			}
		}

		if (tdgbl->runtimeODS < DB_VERSION_DDL11_2)
		{
			return true;	// silently skip attributes on old server
		}

		if (tdgbl->firstMap)
		{
			tdgbl->firstMap = false;
			BURP_verbose(301);
			// msg 301, restoring names mapping
		}

		if (match == MATCH_ALL)
			role = ADMIN_ROLE;

		if (role != ADMIN_ROLE)
		{
			BURP_error(300, false);
			return true;
		}

		BURP_verbose(298, ADMIN_ROLE);
		// msg 298, restoring map @1
		Firebird::string sql;
		sql.printf("%s ('%s', %d) %s",
			"UPDATE OR INSERT INTO RDB$ROLES(RDB$ROLE_NAME, RDB$SYSTEM_FLAG) VALUES",
			ADMIN_ROLE, 6,		// constant 6 turns on auto admin mapping in FB 2.5
			"MATCHING (RDB$ROLE_NAME)");
		isc_dsql_execute_immediate(tdgbl->status_vector, &tdgbl->db_handle, &tdgbl->tr_handle,
								   sql.length(), sql.c_str(), 1, NULL);
		if (tdgbl->status_vector[1])
		{
			general_on_error ();
		}
	}
	return true;
}

bool get_db_creator(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ d b _ c r e a t o r
 *
 **************************************
 *
 * Functional description
 *	Restore database creators
 *
 **************************************/
	att_type		attribute;
	scan_attr_t		scan_next_attr;
	TEXT			usr[GDS_NAME_LEN];
	SSHORT			uType;
	bool			userSet = false, typeSet = false;

	skip_init(&scan_next_attr);
	while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
	{

		switch (attribute)
		{
		case att_dbc_user:
			userSet = true;
			GET_TEXT(usr);
			break;

		case att_dbc_type:
			typeSet = true;
			uType = (SSHORT) get_int32(tdgbl);
			break;

		default:
			// msg 395 database creator
			bad_attribute(scan_next_attr, attribute, 395);
			break;
		}
	}

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		if (tdgbl->firstDbc)
		{
			tdgbl->firstDbc = false;
			BURP_verbose(394);
			// msg 394 restoring database creators
		}

		STORE (REQUEST_HANDLE tdgbl->handles_db_creators_req_handle1)
			C IN RDB$DB_CREATORS

			BURP_verbose (393, usr);
			if (strlen(usr) > sizeof(C.RDB$USER))
				BURP_error_redirect(NULL, 46);

			C.RDB$USER.NULL = userSet ? FALSE : TRUE;
			if (userSet)
				strncpy(C.RDB$USER, usr, sizeof(C.RDB$USER));

			C.RDB$USER_TYPE.NULL = typeSet ? FALSE : TRUE;
			if (typeSet)
				C.RDB$USER_TYPE = uType;

		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 uppercased name
 *
 **************************************/
	SSHORT i = 0;

	while (i < len &&
			( (name[i] >= 'A' && name[i] <= 'Z') ||
			(name[i] >= '0' && name[i] <= '9') ||
			  name[i] == '_' || name[i] == '$' ) )
	{
		++i;
	}
	return (i == len);
}

bool get_security_class(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ s e c u r i t y _ c l a s s
 *
 **************************************
 *
 * Functional description
 *	Restore a security class record including access control list.
 *
 **************************************/
	att_type	attribute;
	TEXT		temp[GDS_NAME_LEN];
	SSHORT		l = 0;
	scan_attr_t		scan_next_attr;
	bool		is_valid_sec_class = false;

	STORE (REQUEST_HANDLE tdgbl->handles_get_security_class_req_handle1)
		X IN RDB$SECURITY_CLASSES

		X.RDB$DESCRIPTION.NULL = TRUE;

		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_class_security_class:
				l = GET_TEXT(X.RDB$SECURITY_CLASS);

				// Bug fix for bug_no 7299: There was a V3 bug that inserted
				// garbage security class entry when doing GBAK. In order to
				// restore the V3 gbak file with this bad security entry to
				// V4 database. We should check if the security class is a
				// valid ASCII name. If not, skip this entry by setting
				// 'is_valid_sec_class' to false.

				is_valid_sec_class = is_ascii_name(X.RDB$SECURITY_CLASS, l);
				if (!is_valid_sec_class)
				{
					MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp));
					BURP_print (false, 234, temp);
					// msg 234   Skipped bad security class entry: %s
					break;
				}

				MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp));
				BURP_verbose (125, temp);
				// msg 125   restoring security class %s
				break;

			case att_class_acl:
				get_misc_blob (tdgbl, X.RDB$ACL, false);
				break;

			case att_class_description:
				X.RDB$DESCRIPTION.NULL = FALSE;
				get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
				break;

			case att_class_description2:
				X.RDB$DESCRIPTION.NULL = FALSE;
				get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
				break;

			default:
				bad_attribute(scan_next_attr, attribute, 131);
				// msg 131 security class
				break;
			}
		}
		// If the security class is not valid ASCII name, don't store it to the
		// database. Simply return from here and the entry is discarded.
		if (!is_valid_sec_class)
		{
			return true;
		}
	END_STORE;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	return true;
}

void get_source_blob(BurpGlobals* tdgbl, ISC_QUAD& blob_id, bool glb_trans)
{
/**************************************
 *
 *	g e t _ s o u r c e _ b l o b
 *
 **************************************
 *
 * Functional description
 *	Read source blob and query header attributes and copy data from
 *	input file to nice, shiney, new blob.
 *
 **************************************/
	ISC_STATUS_ARRAY	status_vector;

	SLONG length = get_int32(tdgbl);

	// Create new blob

	UserBlob blob(status_vector);
	isc_tr_handle	local_trans;
	if (glb_trans && tdgbl->global_trans)
		local_trans = tdgbl->global_trans;
	else
		local_trans = gds_trans;

	bool ok;

	if (tdgbl->gbl_sw_fix_fss_metadata)
	{
		UCHAR bpb[15];
		UCHAR* p = bpb;

		*p++ = isc_bpb_version1;

		*p++ = isc_bpb_source_type;
		*p++ = 2;
		put_short(p, isc_blob_text);
		p += 2;
		*p++ = isc_bpb_source_interp;
		*p++ = 1;
		*p++ = tdgbl->gbl_sw_fix_fss_metadata_id;

		*p++ = isc_bpb_target_type;
		*p++ = 2;
		put_short(p, isc_blob_text);
		p += 2;
		*p++ = isc_bpb_target_interp;
		*p++ = 1;
		*p++ = CS_UNICODE_FSS;

		ok = blob.create(DB, local_trans, blob_id, p - bpb, bpb);
	}
	else
		ok = blob.create(DB, local_trans, blob_id);

	if (!ok)
	{
		BURP_error_redirect (status_vector, 37);
		// msg 37 isc_create_blob failed
	}

	// Allocate blob buffer if static buffer is too short
	BlobBuffer static_buffer;
	UCHAR* const buffer = static_buffer.getBuffer(length);

	while (length > 0)
	{
		UCHAR* p = buffer;
		while (*p++ = get(tdgbl))
			length--;
		--p;
		--length; // -- or ++ ??? p is decremented, will have to test.
		const USHORT seg_len = p - buffer;

		if (!blob.putSegment(seg_len, buffer))
		{
			BURP_error_redirect (status_vector, 38);
			// msg 38 isc_put_segment failed
		}
	}

	if (!blob.close())
		BURP_error_redirect (status_vector, 23);
		// msg 23 isc_close_blob failed
}

USHORT get_text(BurpGlobals* tdgbl, TEXT* text, ULONG length)
{
/**************************************
 *
 *	g e t _ t e x t
 *
 **************************************
 *
 * Functional description
 *	Move a text attribute to a string and fill.
 *
 **************************************/
	const ULONG l = get(tdgbl);

	if (length <= l)
		BURP_error_redirect (NULL, 46);
		// msg 46 string truncated

	if (l)
		text = (TEXT*) get_block(tdgbl, (UCHAR*) text, l);

	*text = 0;

	return (USHORT) l;
}

USHORT get_text2(BurpGlobals* tdgbl, TEXT* text, ULONG length)
{
/**************************************
 *
 *	g e t _ t e x t 2
 *
 **************************************
 *
 * Functional description
 *	Move a text attribute to a string and fill, using USHORT as length indicator.
 *
 **************************************/
	UCHAR lenstr[sizeof(USHORT)] = "";
	get_block(tdgbl, lenstr, sizeof(lenstr));
	const USHORT len = (USHORT) gds__vax_integer(lenstr, sizeof(lenstr));

	if (length <= len)
	{
		BURP_error_redirect (NULL, 46);
		// msg 46 string truncated
	}

	if (len)
		text = (TEXT*) get_block(tdgbl, (UCHAR*) text, len);

	*text = 0;

	return len;
}

bool get_trigger_old (BurpGlobals* tdgbl, burp_rel* relation)
{
/**************************************
 *
 *	g e t _ t r i g g e r _ o l d
 *
 **************************************
 *
 * Functional description
 *	Get a trigger definition for a relation.
 *
 **************************************/
	enum trig_t	type;
	att_type	attribute;
	TEXT		name[GDS_NAME_LEN];
	scan_attr_t		scan_next_attr;

	STORE (REQUEST_HANDLE tdgbl->handles_get_trigger_old_req_handle1)
		X IN RDB$TRIGGERS

		X.RDB$DESCRIPTION.NULL = TRUE;
		X.RDB$TRIGGER_BLR.NULL = TRUE;
		X.RDB$TRIGGER_SOURCE.NULL = TRUE;
		X.RDB$SYSTEM_FLAG = 0;
		X.RDB$SYSTEM_FLAG.NULL = FALSE;

		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_trig_type:
				type = (enum trig_t) get_int32(tdgbl);
				break;

			case att_trig_blr:
				X.RDB$TRIGGER_BLR.NULL = FALSE;
				get_blr_blob (tdgbl, X.RDB$TRIGGER_BLR, false);
				break;

			case att_trig_source:
				X.RDB$TRIGGER_SOURCE.NULL = FALSE;
				get_misc_blob (tdgbl, X.RDB$TRIGGER_SOURCE, false);
				break;

			case att_trig_source2:
				X.RDB$TRIGGER_SOURCE.NULL = FALSE;
				get_source_blob (tdgbl, X.RDB$TRIGGER_SOURCE, false);
				break;

			default:
				bad_attribute(scan_next_attr, attribute, 134);
				// msg 134 trigger
				break;
			}
		}

		// fill in rest of attributes unique to new trigger format

		TEXT* p = X.RDB$TRIGGER_NAME;
		const TEXT* const end = p + 31;
		const TEXT* q = relation->rel_name;
		while (*q) {
			*p++ = *q++;
		}

		switch (type)
		{
		case trig_pre_store:
			X.RDB$TRIGGER_TYPE = TRIG_TYPE_PRE_STORE;
			q = "$STORE";
			break;
		case trig_pre_modify:
			X.RDB$TRIGGER_TYPE = TRIG_TYPE_PRE_MODIFY;
			q = "$MODIFY";
			break;
		case trig_post_erase:
			X.RDB$TRIGGER_TYPE = TRIG_TYPE_POST_ERASE;
			q = "$ERASE";
			break;
		default:
			bad_attribute(scan_next_attr, attribute, 136);
			// msg 136 trigger type
			return 0;
		}

		while (*q && p < end) {
			*p++ = *q++;
		}
		*p = 0;
		BURP_verbose (126, X.RDB$TRIGGER_NAME);
		// msg 126 restoring trigger %s
		strncpy (X.RDB$RELATION_NAME, relation->rel_name, GDS_NAME_LEN);
		strcpy (name, X.RDB$TRIGGER_NAME);
		X.RDB$TRIGGER_SEQUENCE = TRIGGER_SEQUENCE_DEFAULT;

	END_STORE;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	if (tdgbl->gbl_sw_incremental)
	{
		COMMIT
		// existing ON_ERROR continues past error, beck
		ON_ERROR
			BURP_print (false, 94, name);
			// msg 94 trigger %s is invalid
			BURP_print_status (false, 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;
}

void write_skip_message(const char* trigName, const char* relName)
{
	BURP_output(false, "gbak:Skipped trigger %s on system table %s\n", trigName, relName);
}

bool get_trigger(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ t r i g g e r
 *
 **************************************
 *
 * Functional description
 *	Get a trigger definition in rdb$triggers.
 *
 **************************************/
	att_type attribute;
	BASED_ON RDB$TRIGGERS.RDB$TRIGGER_NAME name;
	BASED_ON RDB$TRIGGERS.RDB$RELATION_NAME relName;
	scan_attr_t scan_next_attr;
	bool skipTrig = false;

	isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;

	if (tdgbl->runtimeODS >= DB_VERSION_DDL11_1)
	{
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_trigger_req_handle1)
			X IN RDB$TRIGGERS

			X.RDB$RELATION_NAME.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$TRIGGER_BLR.NULL = TRUE;
			X.RDB$TRIGGER_SOURCE.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$FLAGS.NULL = TRUE;
			X.RDB$VALID_BLR.NULL = TRUE;
			X.RDB$DEBUG_INFO.NULL = TRUE;
			X.RDB$ENGINE_NAME.NULL = TRUE;
			X.RDB$ENTRYPOINT.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 = get_int32(tdgbl);
					break;

				case att_trig_type2:
					if (tdgbl->RESTORE_format >= 10)
						X.RDB$TRIGGER_TYPE = get_int64(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 134);
					break;

				case att_trig_flags:
					X.RDB$FLAGS = (USHORT) get_int32(tdgbl);
					X.RDB$FLAGS.NULL = FALSE;
					break;

				case att_trig_blr:
					X.RDB$TRIGGER_BLR.NULL = FALSE;
					get_blr_blob (tdgbl, X.RDB$TRIGGER_BLR, true);
					break;

				case att_trig_source:
					X.RDB$TRIGGER_SOURCE.NULL = FALSE;
					get_misc_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true);
					break;

				case att_trig_source2:
					X.RDB$TRIGGER_SOURCE.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true);
					break;

				case att_trig_name:
					GET_TEXT(X.RDB$TRIGGER_NAME);
					strcpy (name, X.RDB$TRIGGER_NAME);
					BURP_verbose (126, X.RDB$TRIGGER_NAME);
					// msg 126 restoring trigger %s
					break;

				case att_trig_relation_name:
					X.RDB$RELATION_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$RELATION_NAME);
					strcpy (relName, X.RDB$RELATION_NAME);

					// Check for trigger on system relation
					FOR (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_trigger_req_handle2)
						Y IN RDB$RELATIONS
						WITH Y.RDB$RELATION_NAME EQ relName
						 AND Y.RDB$SYSTEM_FLAG EQ 1

						skipTrig = true;
					END_FOR;
					break;

				case att_trig_sequence:
					X.RDB$TRIGGER_SEQUENCE = (USHORT) get_int32(tdgbl);
					break;

				case att_trig_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
					break;

				case att_trig_description2:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
					break;

				case att_trig_system_flag:
					X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
					X.RDB$SYSTEM_FLAG.NULL = FALSE;
					break;

				case att_trig_inactive:
					X.RDB$TRIGGER_INACTIVE = (USHORT) get_int32(tdgbl);
					break;

				case att_trig_valid_blr:
					if (tdgbl->RESTORE_format >= 8)
					{
						X.RDB$VALID_BLR.NULL = FALSE;
						X.RDB$VALID_BLR = (USHORT) get_int32(tdgbl);
					}
					else
						bad_attribute(scan_next_attr, attribute, 134);
					break;

				case att_trig_debug_info:
					if (tdgbl->RESTORE_format >= 8)
					{
						X.RDB$DEBUG_INFO.NULL = FALSE;
						get_misc_blob (tdgbl, X.RDB$DEBUG_INFO, true);
					}
					else
						bad_attribute(scan_next_attr, attribute, 134);
					break;

				case att_trig_engine_name:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$ENGINE_NAME);
						X.RDB$ENGINE_NAME.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 134);
					break;

				case att_trig_entrypoint:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$ENTRYPOINT);
						X.RDB$ENTRYPOINT.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 134);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 134);
					// msg 134 trigger
					break;
				}
			}

			// Skip trigger on system relation
			if (skipTrig)
			{
				write_skip_message(name, relName);
				return true;
			}

		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else
	{
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_trigger_req_handle1)
			X IN RDB$TRIGGERS

			X.RDB$RELATION_NAME.NULL = TRUE;
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$TRIGGER_BLR.NULL = TRUE;
			X.RDB$TRIGGER_SOURCE.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$FLAGS.NULL = TRUE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_trig_type:
					X.RDB$TRIGGER_TYPE = (USHORT) get_int32(tdgbl);
					break;

				case att_trig_flags:
					X.RDB$FLAGS = (USHORT) get_int32(tdgbl);
					X.RDB$FLAGS.NULL = FALSE;
					break;

				case att_trig_blr:
					X.RDB$TRIGGER_BLR.NULL = FALSE;
					get_blr_blob (tdgbl, X.RDB$TRIGGER_BLR, true);
					break;

				case att_trig_source:
					X.RDB$TRIGGER_SOURCE.NULL = FALSE;
					get_misc_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true);
					break;

				case att_trig_source2:
					X.RDB$TRIGGER_SOURCE.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$TRIGGER_SOURCE, true);
					break;

				case att_trig_name:
					GET_TEXT(X.RDB$TRIGGER_NAME);
					strcpy (name, X.RDB$TRIGGER_NAME);
					BURP_verbose (126, X.RDB$TRIGGER_NAME);
					// msg 126 restoring trigger %s
					break;

				case att_trig_relation_name:
					X.RDB$RELATION_NAME.NULL = FALSE;
					GET_TEXT(X.RDB$RELATION_NAME);
					strcpy (relName, X.RDB$RELATION_NAME);

					// Check for trigger on system relation
					FOR (TRANSACTION_HANDLE local_trans REQUEST_HANDLE tdgbl->handles_get_trigger_req_handle2)
						Y IN RDB$RELATIONS
						WITH Y.RDB$RELATION_NAME EQ relName
						 AND Y.RDB$SYSTEM_FLAG EQ 1

						skipTrig = true;
					END_FOR;
					break;

				case att_trig_sequence:
					X.RDB$TRIGGER_SEQUENCE = (USHORT) get_int32(tdgbl);
					break;

				case att_trig_description:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_misc_blob (tdgbl, X.RDB$DESCRIPTION, true);
					break;

				case att_trig_description2:
					X.RDB$DESCRIPTION.NULL = FALSE;
					get_source_blob (tdgbl, X.RDB$DESCRIPTION, true);
					break;

				case att_trig_system_flag:
					X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
					X.RDB$SYSTEM_FLAG.NULL = FALSE;
					break;

				case att_trig_inactive:
					X.RDB$TRIGGER_INACTIVE = (USHORT) get_int32(tdgbl);
					break;

				case att_trig_valid_blr:
					if (tdgbl->RESTORE_format >= 8)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 134);
					break;

				case att_trig_debug_info:
					if (tdgbl->RESTORE_format >= 8)
						eat_blob(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 134);
					break;

				case att_trig_engine_name:
				case att_trig_entrypoint:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 134);
					break;

				case att_trig_type2:
					if (tdgbl->RESTORE_format >= 10)
						get_int64(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 134);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 134);
					// msg 134 trigger
					break;
				}
			}

			// Skip trigger on system relation
			if (skipTrig)
			{
				write_skip_message(name, relName);
				return true;
			}

		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 (false, 94, name);
			// msg 94 trigger %s is invalid
			BURP_print_status (false, tdgbl->status_vector);
			ROLLBACK;
			ON_ERROR
				general_on_error ();
			END_ERROR;
		END_ERROR;
		EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
		if (gds_status[1])
			EXEC SQL SET TRANSACTION;
	}

	return true;
}

bool get_trigger_message(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ t r i g g e r _ m e s s a g e
 *
 **************************************
 *
 * Functional description
 *	Get a trigger message text.
 *
 **************************************/
	att_type	attribute;
	scan_attr_t		scan_next_attr;

	BASED_ON RDB$TRIGGER_MESSAGES.RDB$TRIGGER_NAME name;
	BASED_ON RDB$TRIGGER_MESSAGES.RDB$MESSAGE_NUMBER number = -1;
	BASED_ON RDB$TRIGGER_MESSAGES.RDB$MESSAGE message;

	bool sysflag = false;
	skip_init(&scan_next_attr);
	while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
	{
		switch (attribute)
		{
		case att_trigmsg_name:
			GET_TEXT(name);
			sysflag = false;
			FOR (REQUEST_HANDLE tdgbl->handles_get_trigger_message_req_handle1)
				FIRST 1 X IN RDB$TRIGGERS WITH
				X.RDB$SYSTEM_FLAG EQ 1 AND X.RDB$TRIGGER_NAME EQ name
				sysflag = true;
			END_FOR;
			ON_ERROR
				general_on_error ();
			END_ERROR;
			BURP_verbose (127, name);
			// msg 127 restoring trigger message for %s
			break;

		case att_trigmsg_number:
			number = (USHORT) get_int32(tdgbl);
			break;

		case att_trigmsg_text:
			GET_TEXT(message);
			break;

		default:
			bad_attribute(scan_next_attr, attribute, 135);
			// msg 135  trigger message
			break;
		}
	}

	if (sysflag)
		return true;

	// Versions prior to FB2.0 don't support a field longer than varchar(78).
	if (tdgbl->runtimeODS < DB_VERSION_DDL11)
		message[78] = 0;

	isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;

	STORE (TRANSACTION_HANDLE local_trans
			REQUEST_HANDLE tdgbl->handles_get_trigger_message_req_handle2)
		X IN RDB$TRIGGER_MESSAGES
		strcpy (X.RDB$TRIGGER_NAME, name);
		X.RDB$MESSAGE_NUMBER = number;
		strcpy (X.RDB$MESSAGE, message);
	END_STORE;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	if (tdgbl->gbl_sw_incremental)
	{
		COMMIT
		// existing ON_ERROR continues past error, beck
		ON_ERROR
			BURP_print (false, 94, name);
			// msg 94 trigger %s is invalid
			BURP_print_status (false, tdgbl->status_vector);
			ROLLBACK;
			ON_ERROR
				general_on_error ();
			END_ERROR;
		END_ERROR;
		EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
		if (gds_status[1])
			EXEC SQL SET TRANSACTION;
	}

	return true;
}

bool get_type(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ t y p e
 *
 **************************************
 *
 * Functional description
 *	Get a type definition in rdb$types.
 *
 **************************************/
	att_type	attribute;
	ULONG		len = 0;
	TEXT		temp[GDS_NAME_LEN];
	scan_attr_t		scan_next_attr;

	STORE (REQUEST_HANDLE tdgbl->handles_get_type_req_handle1)
		X IN RDB$TYPES

		X.RDB$DESCRIPTION.NULL = TRUE;
		X.RDB$SYSTEM_FLAG = 0;
		X.RDB$SYSTEM_FLAG.NULL = FALSE;

		skip_init(&scan_next_attr);
		while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
		{
			switch (attribute)
			{
			case att_type_name:
				len = GET_TEXT(X.RDB$TYPE_NAME);
				break;

			case att_type_type:
				X.RDB$TYPE = (USHORT) get_int32(tdgbl);
				break;

			case att_type_field_name:
				GET_TEXT(X.RDB$FIELD_NAME);
				break;

			case att_type_description:
				X.RDB$DESCRIPTION.NULL = FALSE;
				get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
				break;

			case att_type_description2:
				X.RDB$DESCRIPTION.NULL = FALSE;
				get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
				break;

			case att_type_system_flag:
				X.RDB$SYSTEM_FLAG = (USHORT) get_int32(tdgbl);
				X.RDB$SYSTEM_FLAG.NULL = FALSE;
				break;

			default:
				bad_attribute(scan_next_attr, attribute, 293);
				// msg 293 type (in RDB$TYPES)
				break;
			}
		}

		MISC_terminate (X.RDB$TYPE_NAME, temp, len, sizeof(temp));
		BURP_verbose (128, SafeArg() << temp << X.RDB$FIELD_NAME);
		// msg 128  restoring type %s for field %s

	END_STORE;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	return true;
}

bool get_user_privilege(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	g e t _ u s e r _ p r i v i l e g e
 *
 **************************************
 *
 * Functional description
 *	Get a user privilege.
 *	Get next interesting user privilege.
 *
 **************************************/
	att_type	attribute;
	scan_attr_t		scan_next_attr;
	USHORT		flags = 0;

	BASED_ON RDB$USER_PRIVILEGES.RDB$USER user;
	BASED_ON RDB$USER_PRIVILEGES.RDB$GRANTOR grantor;
	BASED_ON RDB$USER_PRIVILEGES.RDB$PRIVILEGE privilege;
	BASED_ON RDB$USER_PRIVILEGES.RDB$GRANT_OPTION grant_option = 0;
	BASED_ON RDB$USER_PRIVILEGES.RDB$RELATION_NAME relation_name;
	BASED_ON RDB$USER_PRIVILEGES.RDB$FIELD_NAME field_name;
	BASED_ON RDB$USER_PRIVILEGES.RDB$USER_TYPE user_type;
	BASED_ON RDB$USER_PRIVILEGES.RDB$OBJECT_TYPE object_type;

	user_type = obj_user;
	object_type = obj_relation;
	grantor[0] = 0;

	skip_init(&scan_next_attr);
	while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
	{
		switch (attribute)
		{
		case att_priv_user:
			// default USER_TYPE to USER
			flags |= USER_PRIV_USER;
			GET_TEXT(user);
			BURP_verbose (123, user);
			// msg 123 restoring privilege for user %s
			break;

		case att_priv_grantor:
			flags |= USER_PRIV_GRANTOR;
			GET_TEXT(grantor);
			break;

		case att_priv_privilege:
			flags |= USER_PRIV_PRIVILEGE;
			GET_TEXT(privilege);
			break;

		case att_priv_grant_option:
			flags |= USER_PRIV_GRANT_OPTION;
			grant_option = (USHORT) get_int32(tdgbl);
			break;

		case att_priv_object_name:
			flags |= USER_PRIV_OBJECT_NAME;
			// default OBJECT_TYPE to RELATION
			GET_TEXT(relation_name);
			break;

		case att_priv_field_name:
			flags |= USER_PRIV_FIELD_NAME;
			GET_TEXT(field_name);
			break;

		case att_priv_user_type:
			flags |= USER_PRIV_USER_TYPE;
			user_type = (USHORT) get_int32(tdgbl);
			break;

		case att_priv_obj_type:
			flags |= USER_PRIV_OBJECT_TYPE;
			object_type = (USHORT) get_int32(tdgbl);
			break;

		default:
			bad_attribute(scan_next_attr, attribute, 105);
			// msg 105 privilege
			break;
		}
	}

	// Check if object exists
	isc_tr_handle	local_trans = 0;
	bool exists = false;
	// if grantor is not set than it's system privilege which should not be restored
	if (grantor[0])
	{
		switch (object_type)
		{
		case obj_package_header:
			{
			for (const burp_pkg* pkg = tdgbl->packages; pkg; pkg = pkg->pkg_next)
				if (strcmp(pkg->pkg_name, relation_name) == 0)
				{
					exists = true;
					local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
					break;
				}
			}
			break;

		case obj_procedure:
			{
			for (const burp_prc* proc = tdgbl->procedures; proc; proc = proc->prc_next)
				if (!proc->prc_package[0] && strcmp(proc->prc_name, relation_name) == 0)
				{
					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) == 0)
				{
					exists = true;
					if (rel->rel_flags & REL_view)
						local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;
					break;
				}
			}
			break;

		case obj_charset:
		case obj_collation:
		case obj_exception:
		case obj_udf:
		case obj_field:
		case obj_generator:
			{
			for (burp_meta_obj* object = tdgbl->miss_privs; object; object = object->obj_next)
				if (object->obj_type == object_type && strcmp(object->obj_name, relation_name) == 0)
				{
					if (object->obj_class)
						exists = true;
					break;
				}
			}
			break;

		case obj_database:
			break;

		default:
			exists = true;
			break;
		}
	}

	if (object_type == obj_package_header)
	{
		if (tdgbl->RESTORE_format < 10)
			return false;

		// Discard packages for engine < FB3
		if (tdgbl->runtimeODS < DB_VERSION_DDL12)
			exists = false;
	}

	if (tdgbl->runtimeODS <= DB_VERSION_DDL8)
	{
		// Discard roles for IB4.
		if (user_type == obj_sql_role || object_type == obj_sql_role)
			exists = false;
	}

	if (exists)
	{
		if (!local_trans)
			local_trans = gds_trans;

		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_user_privilege_req_handle1)
			X IN RDB$USER_PRIVILEGES

			X.RDB$FIELD_NAME.NULL = TRUE;
			X.RDB$OBJECT_TYPE.NULL = TRUE;

			if (flags & USER_PRIV_USER)
				strcpy (X.RDB$USER, user);

			if (flags & USER_PRIV_GRANTOR)
				strcpy (X.RDB$GRANTOR, grantor);

			if (flags & USER_PRIV_PRIVILEGE)
				strcpy (X.RDB$PRIVILEGE, privilege);

			if (flags & USER_PRIV_GRANT_OPTION)
			{
				X.RDB$GRANT_OPTION = grant_option;
				if (grant_option == 0)
					X.RDB$GRANT_OPTION.NULL = TRUE;
				else
					X.RDB$GRANT_OPTION.NULL = FALSE;
			}

			if (flags & USER_PRIV_OBJECT_NAME)
				strcpy (X.RDB$RELATION_NAME, relation_name);

			if (flags & USER_PRIV_FIELD_NAME)
			{
				X.RDB$FIELD_NAME.NULL = FALSE;
				strcpy (X.RDB$FIELD_NAME, field_name);
			}

			// USER_TYPE & OBJECT_TYPE are fields that did not exist before
			// V4.0. So, we have to reconstruct them and initialize them to
			// reasonable values. If they existed before then user_type and
			// object_type contain the proper values. If they didn't exist
			// then user_type and object_type contain the reasonable default
			// values.

			X.RDB$USER_TYPE.NULL = FALSE;
			X.RDB$USER_TYPE = user_type;

			X.RDB$OBJECT_TYPE.NULL = FALSE;
			X.RDB$OBJECT_TYPE = object_type;

			// If OBJECT_TYPE didn't exist before and we have a field level
			// user privileges, then use obj_field instead.

			// NOTE: Scanning the V4.0 code base, obj_field has never been
			// used at all. The following code should be uncommented
			// in case we ever introduce obj_field to the picture.

/***********************************************************
			if ( !(flags & USER_PRIV_OBJECT_TYPE) )
			{
				if ( flags & USER_PRIV_FIELD_NAME )
				{
					X.RDB$OBJECT_TYPE = obj_field;
				}
			}
***********************************************************/

		END_STORE;
		ON_ERROR
			if (isc_status[1] == isc_integ_fail)
			{
				BURP_print_status(false, isc_status);
				tdgbl->flag_on_line = false;
				return true;
			}
			general_on_error ();
		END_ERROR;
	}

	return true;
}

bool get_view(BurpGlobals* tdgbl, burp_rel* relation)
{
/**************************************
 *
 *	g e t _ v i e w
 *
 **************************************
 *
 * Functional description
 *	Store a record in RDB$VIEW_RELATIONS.
 *
 **************************************/
	att_type	attribute;
	scan_attr_t		scan_next_attr;

	// If there is a global transaction then use it

	isc_tr_handle local_trans = tdgbl->global_trans ? tdgbl->global_trans : gds_trans;

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		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);
			X.RDB$CONTEXT_TYPE.NULL = TRUE;
			X.RDB$PACKAGE_NAME.NULL = TRUE;

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_view_relation_name:
					GET_TEXT(X.RDB$RELATION_NAME);
					break;

				case att_view_context_name:
					GET_TEXT(X.RDB$CONTEXT_NAME);
					if (tdgbl->runtimeODS < DB_VERSION_DDL11_2)
					{
						// Cannot use MAX_SQL_IDENTIFIER_LEN because this value shouldn't be upgraded.
						X.RDB$CONTEXT_NAME[31] = 0;
					}
					break;

				case att_view_context_id:
					X.RDB$VIEW_CONTEXT = (USHORT) get_int32(tdgbl);
					break;

				case att_view_context_type:
					if (tdgbl->RESTORE_format >= 10)
					{
						X.RDB$CONTEXT_TYPE = (USHORT) get_int32(tdgbl);
						X.RDB$CONTEXT_TYPE.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 140);
					break;

				case att_view_context_package:
					if (tdgbl->RESTORE_format >= 10)
					{
						GET_TEXT(X.RDB$PACKAGE_NAME);
						X.RDB$PACKAGE_NAME.NULL = FALSE;
					}
					else
						bad_attribute(scan_next_attr, attribute, 140);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 140);
					// msg 140 view
					break;
				}
			}
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else
	{
		STORE (TRANSACTION_HANDLE local_trans
				REQUEST_HANDLE tdgbl->handles_get_view_req_handle1)
			X IN RDB$VIEW_RELATIONS

			strcpy (X.RDB$VIEW_NAME, relation->rel_name);

			skip_init(&scan_next_attr);
			while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_end)
			{
				switch (attribute)
				{
				case att_view_relation_name:
					GET_TEXT(X.RDB$RELATION_NAME);
					break;

				case att_view_context_name:
					GET_TEXT(X.RDB$CONTEXT_NAME);
					if (tdgbl->runtimeODS < DB_VERSION_DDL11_2)
					{
						// Cannot use MAX_SQL_IDENTIFIER_LEN because this value shouldn't be upgraded.
						X.RDB$CONTEXT_NAME[31] = 0;
					}
					break;

				case att_view_context_id:
					X.RDB$VIEW_CONTEXT = (USHORT) get_int32(tdgbl);
					break;

				case att_view_context_type:
					if (tdgbl->RESTORE_format >= 10)
						get_int32(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 140);
					break;

				case att_view_context_package:
					if (tdgbl->RESTORE_format >= 10)
						eat_text(tdgbl);
					else
						bad_attribute(scan_next_attr, attribute, 140);
					break;

				default:
					bad_attribute(scan_next_attr, attribute, 140);
					// msg 140 view
					break;
				}
			}
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}

	return true;
}

void ignore_array(BurpGlobals* tdgbl, burp_rel* relation)
{
/**************************************
 *
 *	i g n o r e _ a r r a y
 *
 **************************************
 *
 * Functional description
 *	Ignore data from input file, like a
 *	dummy get_array().
 *
 **************************************/
	burp_fld* field = NULL;
	att_type attribute;
	SLONG* range;
	const SLONG* end_ranges;
	USHORT field_number;
	scan_attr_t scan_next_attr;

	// Pick up attributes

	skip_init(&scan_next_attr);
	while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_blob_data)
	{
		switch (attribute)
		{
		case att_blob_field_number:
			field_number = (USHORT) get_int32(tdgbl);
			for (field = relation->rel_fields; field; field = field->fld_next)
			{
				if (field->fld_number == field_number)
					break;
			}
			if (!field)
				BURP_error_redirect (NULL, 36);
				// msg 36 Can't find field for blob
			break;

		case att_array_dimensions:
			field->fld_dimensions = (SSHORT) get_int32(tdgbl);
			end_ranges = field->fld_ranges + 2 * field->fld_dimensions;
			for (range = field->fld_ranges; range < end_ranges; range += 2)
			{
				if (get_attribute(&attribute, tdgbl) != att_array_range_low)
					bad_attribute(scan_next_attr, attribute, 58);
					// msg 58 array
				else
					range[0] = get_int32(tdgbl);
				if (get_attribute(&attribute, tdgbl) != att_array_range_high)
					bad_attribute(scan_next_attr, attribute, 58);
					// msg 58 array
				else
					range[1] = get_int32(tdgbl);
			}
			break;

		default:
			bad_attribute(scan_next_attr, attribute, 58);
			// msg 58 array
			break;
		}
	}

	SLONG length = get(tdgbl);
	length |= get(tdgbl) << 8;
	length |= get(tdgbl) << 16;
	length |= get(tdgbl) << 24;

	SLONG lcount = 0;
	if (tdgbl->gbl_sw_transportable)
	{
		if (get_attribute(&attribute, tdgbl) != att_xdr_array)
			BURP_error_redirect (NULL, 55);
			// msg 55 Expected XDR record length
		else
		{
			lcount = get(tdgbl);
			lcount |= get(tdgbl) << 8;
			lcount |= get(tdgbl) << 16;
			lcount |= get(tdgbl) << 24;
		}
	}
	else
	{
		lcount = length;
	}

	if (lcount)
		get_skip(tdgbl, lcount);
}

void ignore_blob(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	i g n o r e _ b l o b
 *
 **************************************
 *
 * Functional description
 *	Skip over blob data records.
 *
 **************************************/
	att_type	attribute;
	scan_attr_t		scan_next_attr;

	// Pick up attributes

	SLONG segments = 0;

	skip_init(&scan_next_attr);
	while (skip_scan(&scan_next_attr), get_attribute(&attribute, tdgbl) != att_blob_data)
	{
		switch (attribute)
		{
		case att_blob_field_number:
			get_int32(tdgbl);
			break;

		case att_blob_max_segment:
			get_int32(tdgbl);
			break;

		case att_blob_number_segments:
			segments = get_int32(tdgbl);
			break;

		case att_blob_type:
			get_int32(tdgbl);
			break;

		default:
			bad_attribute(scan_next_attr, attribute, 64);
			// msg 64 blob
			break;
		}
	}

	// Eat up blob segments

	while (--segments >= 0)
	{
		USHORT length = get(tdgbl);
		length |= get(tdgbl) << 8;
		if (length)
			get_skip(tdgbl, length);
	}
}

rec_type ignore_data(BurpGlobals* tdgbl, burp_rel* relation)
{
/**************************************
 *
 *	i g n o r e _ d a t a
 *
 **************************************
 *
 * Functional description
 *	Ignore data records for a relation.
 *
 **************************************/
	FB_UINT64 records = 0;
	rec_type record;

	while (true)
	{
		if (get(tdgbl) != att_data_length)
			BURP_error_redirect (NULL, 39);
			// msg 39 expected record length
		USHORT len = (USHORT) get_int32(tdgbl);
		if (tdgbl->gbl_sw_transportable)
		{
			if (get(tdgbl) != att_xdr_length)
				BURP_error_redirect (NULL, 55);
				// msg 55 Expected XDR record length
			else
				len = (USHORT) get_int32(tdgbl);
		}
		if (get(tdgbl) != att_data_data)
			BURP_error_redirect (NULL, 41);
			// msg 41 expected data attribute
		if (len)
		{
			if (tdgbl->gbl_sw_compress)
			{
				UCHAR* buffer = (UCHAR*) BURP_alloc (len);
				decompress (tdgbl, buffer, len);
				BURP_free (buffer);
			}
			else
				get_skip(tdgbl, len);
		}
		++records;

		while (get_record(&record, tdgbl))
		{
			if (record == rec_blob)
				ignore_blob(tdgbl);
			else if (record == rec_array)
				ignore_array (tdgbl, relation);
			else
				break;
		}
		if (record != rec_data)
			break;
	}

	BURP_verbose (106, SafeArg() << records);
	// msg 106 %ld records ignored

	return record;
}

void realign(BurpGlobals* tdgbl, UCHAR* buffer, const burp_rel* relation)
{
/**************************************
 *
 *	r e a l i g n
 *
 **************************************
 *
 * Functional description
 *	Miserable input record is misaligned.
 *	Shuffle fields around.  N.B. this one
 *	only works if the old buffer is longer
 *	than the new.
 *
 **************************************/
	UCHAR* p = NULL;
	const UCHAR* q = NULL;

	for (const burp_fld* field = relation->rel_fields; field; field = field->fld_next)
	{
		if (field->fld_flags & FLD_computed)
			continue;

		p = buffer + field->fld_offset;
		q = buffer + field->fld_old_offset;
		USHORT l = field->fld_length;

		// CVC: This code assumes fld_offset < fld_old_offset,
		// why not use memmove() instead?

		// Beware of overlaps here - don't use memcpy
		while (l--) {
			*p++ = *q++;
		}

		if (field->fld_type == blr_varying)
		{
			*p++ = *q++;
			*p++ = *q++;
		}
	}

	// If this is format version 2 or greater, build fields for null flags

	if (tdgbl->RESTORE_format >= 2)
	{
		for (const burp_fld* field = relation->rel_fields; field; field = field->fld_next)
		{
			if (field->fld_flags & FLD_computed)
				continue;
			p = FB_ALIGN(p, sizeof(SSHORT));
			q = FB_ALIGN(q, sizeof(SSHORT));
			*p++ = *q++;
			*p++ = *q++;
		}
	}
}

#ifdef sparc
USHORT recompute_length(BurpGlobals* tdgbl, burp_rel* relation)
{
/**************************************
 *
 *	r e c o m p u t e _ l e n g t h
 *
 **************************************
 *
 * Functional description
 *	Recompute length of a record using an old
 *	alignment if there is one.  At the moment,
 *	only SPARC has one.
 *
 **************************************/

	ULONG	offset = 0; // there was garbage, possibly nobody uses sparc define?
	const SSHORT* alignments = old_sparcs;

	for (burp_fld* field = relation->rel_fields; field; field = field->fld_next)
	{
		if (field->fld_flags & FLD_computed)
			continue;
		ULONG length = field->fld_length;
		// ULONG alignment = 4; useless, see assignment below

		// arrays are of various fld_types but are really blobs

		ULONG dtype = field->fld_type;

		if (field->fld_flags & FLD_array)
		{
			dtype = blr_blob;
			length = 8;
		}

		const ULONG alignment = alignments[gds_cvt_blr_dtype[field->fld_type]];
		if (dtype == blr_varying)
			length += sizeof(USHORT);

		if (alignment)
			offset = FB_ALIGN(offset, alignment);
		field->fld_old_offset = offset;
		offset += length;
	}

	// If this is format version 2, build fields for null flags

	if (tdgbl->RESTORE_format >= 2)
	{
		for (const burp_fld* field = relation->rel_fields; field; field = field->fld_next)
		{
			if (field->fld_flags & FLD_computed)
				continue;
			offset = FB_ALIGN(offset, sizeof(SSHORT));
			offset += sizeof(SSHORT);
		}
	}

	return offset;
}
#endif

bool restore(BurpGlobals* tdgbl, const TEXT* file_name, const TEXT* database_name)
{
/**************************************
 *
 *	r e s t o r e
 *
 **************************************
 *
 * Functional description
 *	Perform the body of restore.
 *
 **************************************/

	// Read burp record first

	MVOL_init_read (file_name, &tdgbl->RESTORE_format, &tdgbl->io_cnt, &tdgbl->io_ptr);

	if (tdgbl->gbl_sw_transportable)
		BURP_verbose (133);
		// msg 133 transportable backup -- data in XDR format
	if (tdgbl->gbl_sw_compress)
		BURP_verbose (61);
		// msg 61 backup file is compressed


	// restore only from those backup files created by current or previous GBAK

	if (tdgbl->RESTORE_format < 1 || tdgbl->RESTORE_format > ATT_BACKUP_FORMAT)
	{
		BURP_error(344, true, SafeArg() << tdgbl->RESTORE_format << 1 << ATT_BACKUP_FORMAT);
		// msg 44 Expected backup version @2..@3.  Found @1
	}

	BURP_verbose(349, SafeArg() << tdgbl->RESTORE_format); // backup version is @1

	create_database(tdgbl, database_name);

	EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
	if (gds_status[1])
		EXEC SQL SET TRANSACTION;

	// For V4.0, start a read commited transaction.  This will be used
	// to create blobs for global fields and update the record in the
	// RDB$FIELDS table.

	EXEC SQL SET TRANSACTION NAME tdgbl->global_trans ISOLATION LEVEL READ COMMITTED;

	detectRuntimeODS();
	if (tdgbl->runtimeODS < DB_VERSION_OLDEST_SUPPORTED)
	{
		BURP_error(51, true, SafeArg() << tdgbl->runtimeODS);
		// msg 51 database format %ld is too old to restore to
	}

	BURP_verbose (129);
	// msg 129 started transaction

	att_type	attribute;
	isc_req_handle  req_handle2 = 0, req_handle3 = 0, req_handle4 = 0;

	while (get_attribute(&attribute, tdgbl) != att_end)
	{
		switch (attribute)
		{
		case att_database_security_class:
			// Instead of updating the security class in RDB$DATABASE,
			// just store the value in tdgbl. It will be updated at
			// the very end to prevent security class validation
			// failures during change table ownership operation
			GET_TEXT(tdgbl->database_security_class);
			break;

		case att_database_description:
		case att_database_description2:
			FOR (REQUEST_HANDLE req_handle2)
				X IN RDB$DATABASE
				MODIFY X USING
					if (attribute == att_database_description2)
						get_source_blob (tdgbl, X.RDB$DESCRIPTION, false);
					else
						get_misc_blob (tdgbl, X.RDB$DESCRIPTION, false);
				END_MODIFY;
				ON_ERROR
					general_on_error ();
				END_ERROR;
			END_FOR;
			ON_ERROR
				general_on_error ();
			END_ERROR;
			break;

		case att_database_dfl_charset:
			FOR (REQUEST_HANDLE req_handle3)
				X IN RDB$DATABASE
				MODIFY X USING
					GET_TEXT(X.RDB$CHARACTER_SET_NAME);
				END_MODIFY;
				ON_ERROR
					general_on_error ();
				END_ERROR;
			END_FOR;
			ON_ERROR
				general_on_error ();
			END_ERROR;
			break;

		case att_database_linger:
			if (tdgbl->RESTORE_format >= 10)
			{
				if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
				{
					FOR (REQUEST_HANDLE req_handle4)
						X IN RDB$DATABASE
						MODIFY X USING
							X.RDB$LINGER = get_int32(tdgbl);
						END_MODIFY;
						ON_ERROR
							general_on_error();
						END_ERROR;
					END_FOR;
					ON_ERROR
						general_on_error();
					END_ERROR;
				}
				else
					get_int32(tdgbl);
			}
			else
			{
				// Functions that use scan_next_attr initialize it to NO_SKIP using skip_init().
				// Here we don't use that logic, hence the first param to bad_attribute is hardcoded.
				bad_attribute(NO_SKIP, attribute, 352);
			}
			break;

		default:
			{
			SSHORT l = get(tdgbl);
			if (l)
				get_skip(tdgbl, l);
			break;
			}
		}
	}

	MISC_release_request_silent(req_handle2);
	MISC_release_request_silent(req_handle3);
	MISC_release_request_silent(req_handle4);

	if (tdgbl->gbl_sw_fix_fss_data)
	{
		bool found = false;
		Firebird::string name = tdgbl->gbl_sw_fix_fss_data;
		name.upper();

		req_handle3 = 0;

		FOR (REQUEST_HANDLE req_handle3)
			X IN RDB$CHARACTER_SETS
			WITH X.RDB$CHARACTER_SET_NAME EQ name.c_str()

			tdgbl->gbl_sw_fix_fss_data_id = X.RDB$CHARACTER_SET_ID;
			found = true;

		END_FOR;
		ON_ERROR
			general_on_error ();
		END_ERROR;

		MISC_release_request_silent(req_handle3);

		if (!found)
			BURP_error(305, true, SafeArg() << tdgbl->gbl_sw_fix_fss_data);
	}

	if (tdgbl->gbl_sw_fix_fss_metadata)
	{
		bool found = false;
		Firebird::string name = tdgbl->gbl_sw_fix_fss_metadata;
		name.upper();

		req_handle3 = 0;

		FOR (REQUEST_HANDLE req_handle3)
			X IN RDB$CHARACTER_SETS
			WITH X.RDB$CHARACTER_SET_NAME EQ name.c_str()

			tdgbl->gbl_sw_fix_fss_metadata_id = X.RDB$CHARACTER_SET_ID;
			found = true;

		END_FOR;
		ON_ERROR
			general_on_error ();
		END_ERROR;

		MISC_release_request_silent(req_handle3);

		if (!found)
			BURP_error(305, true, SafeArg() << tdgbl->gbl_sw_fix_fss_metadata);
	}

	// If this should be a multi-file database, add the files

	if (tdgbl->gbl_sw_files && tdgbl->gbl_sw_files->fil_next)
		add_files(tdgbl, database_name);

	// Get global fields and relations

	bool flag_norel = true;	// To fix bug 10098
	bool flag = false;
	rec_type record;

	while (get_record(&record, tdgbl) != rec_end)
	{
		switch (record)
		{
		case rec_charset:
			if (!get_character_set(tdgbl))
				return false;
			flag = true;
			break;

		case rec_collation:
			if (!get_collation(tdgbl))
				return false;
			flag = true;
			break;

		case rec_chk_constraint:
			if (!get_chk_constraint(tdgbl))
				return false;
			flag = true;
			break;

		case rec_global_field:
			if (!get_global_field(tdgbl))
				return false;
			flag = true;
			break;

		case rec_field_dimensions:
			if (!get_field_dimensions(tdgbl))
				return false;
			flag = true;
			break;

		case rec_relation:
			if (!get_relation(tdgbl))
				return false;
			flag = true;
			flag_norel = false;
			break;

		case rec_ref_constraint:
			if (!get_ref_constraint(tdgbl))
				return false;
			flag = true;
			break;

		case rec_rel_constraint:
			if (!get_rel_constraint(tdgbl))
				return false;
			flag = true;
			break;

		case rec_function:
			if (!get_function(tdgbl))
				return false;
			flag = true;
			break;

		case rec_package:
			if (!get_package(tdgbl))
				return false;
			flag = true;
			break;

		case rec_procedure:
			if (!get_procedure(tdgbl))
				return false;
			flag = true;
			break;

		case rec_exception:
			if (!get_exception(tdgbl))
				return false;
			flag = true;
			break;

		case rec_system_type:  // rdb$types
			if (!get_type(tdgbl))
				return false;
			flag = true;
			break;

		case rec_filter:  // rdb$filters
			if (!get_filter(tdgbl))
				return false;
			flag = true;
			break;

		case rec_generator:
			if (!get_generator(tdgbl))
				return false;
			flag = true;
			break;

		case rec_relation_data:
			if (flag)
			{
				BURP_verbose (68);
				// msg 68 committing meta data
				COMMIT;
				ON_ERROR
					general_on_error ();
				END_ERROR;
				EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
				if (gds_status[1])
					EXEC SQL SET TRANSACTION;
				flag = false;
			}
			if (!get_relation_data(tdgbl))
				return false;
			break;

		case rec_trigger:  // new trigger type
			if (!get_trigger(tdgbl))
				return false;
			flag = true;
			break;

		case rec_trigger_message:
			if (!get_trigger_message(tdgbl))
				return false;
			flag = true;
			break;

		case rec_user_privilege:
			if (!get_user_privilege(tdgbl))
				return false;
			flag = true;
			break;

		case rec_security_class:
			if (!get_security_class(tdgbl))
				return false;
			flag = true;
			break;

		case rec_files:
			if (!get_files(tdgbl))
				return false;
			flag = true;
			break;

		case rec_sql_roles:
			if (!get_sql_roles(tdgbl))
				return false;
			flag = true;
			break;

		case rec_mapping:
			if (!get_mapping(tdgbl))
				return false;
			flag = true;
			break;

		case rec_db_creator:
			if (!get_db_creator(tdgbl))
				return false;
			flag = true;
			break;

		default:
			BURP_error(43, true, SafeArg() << record);
			// msg 43 don't recognize record type %ld
			break;
		}
	}

	if (tdgbl->defaultCollations.getCount() > 0)
	{
		isc_req_handle req_handle5 = 0;

		FOR (REQUEST_HANDLE req_handle5)
			CS IN RDB$CHARACTER_SETS

			for (FB_SIZE_T i = 0; i < tdgbl->defaultCollations.getCount(); ++i)
			{
				if (tdgbl->defaultCollations[i].first == CS.RDB$CHARACTER_SET_NAME)
				{
					MODIFY CS;
						CS.RDB$DEFAULT_COLLATE_NAME.NULL = FALSE;
						strcpy(CS.RDB$DEFAULT_COLLATE_NAME,
							tdgbl->defaultCollations[i].second.c_str());
					END_MODIFY;
					ON_ERROR
						general_on_error ();
					END_ERROR;
				}
			}
		END_FOR;
		ON_ERROR
			general_on_error ();
		END_ERROR;

		MISC_release_request_silent(req_handle5);
	}

	// This piece of code is to fix bug 10098: restore of database with
	// only domains and no relations aborts with the message ERROR: deadlock
	// This is because insertion of domains into RDB$FIELDS is happening in
	// the default transaction, whereas updation of RDB$FIELDS to add
	// constraints to the domains is done in tdgbl->global_trans. In case of
	// no relations, no COMMIT of default transaction occurs till this point
	// because of which rows in RDB$FIELDS for domains are still locked by
	// default transaction. The below code COMMITs the default transaction
	// in that particular situation

	if (flag_norel)
	{
		COMMIT;
		ON_ERROR
			general_on_error ();
		END_ERROR;
		EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
		if (gds_status[1])
			EXEC SQL SET TRANSACTION;
	}

	// put validation clauses for global fields

	update_global_field(tdgbl);

	// Purge shadow metadata if necessary

	if (tdgbl->gbl_sw_kill)
	{
		isc_req_handle req_handle5 = 0;

		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);
	}

	// update statistics for system indices
	isc_req_handle req_handle6 = 0;

	FOR (REQUEST_HANDLE req_handle6)
		IND IN RDB$INDICES WITH IND.RDB$SYSTEM_FLAG EQ 1
		MODIFY IND
			IND.RDB$STATISTICS.NULL = FALSE;
			IND.RDB$STATISTICS = -1;
		END_MODIFY
		ON_ERROR
			general_on_error ();
		END_ERROR;
	END_FOR;
	ON_ERROR
		general_on_error ();
	END_ERROR;

	MISC_release_request_silent(req_handle6);

	return true;
}

void restore_security_class(BurpGlobals* tdgbl, const TEXT* owner_nm, const TEXT* sec_class_nm)
{
/**************************************
 *
 *	r e s t o r e _ s e c u r i t y _ c l a s s
 *
 **************************************
 *
 * Functional description
 *	restore the ownership of the relation in the ACL list
 *
 **************************************/
	isc_req_handle  req_handle2 = 0;

	//isc_tr_handle local_trans = gds_trans;

	FOR (REQUEST_HANDLE req_handle2)
		X IN RDB$SECURITY_CLASSES WITH X.RDB$SECURITY_CLASS EQ sec_class_nm

		ISC_QUAD new_blob_id;
		new_blob_id.gds_quad_high = 0;
		new_blob_id.gds_quad_low  = 0;
		get_acl(tdgbl, owner_nm, &X.RDB$ACL, &new_blob_id);

		MODIFY X;
			memcpy(&X.RDB$ACL, &new_blob_id, sizeof(ISC_QUAD));
		END_MODIFY;
		ON_ERROR
			MISC_release_request_silent(req_handle2);
			general_on_error ();
		END_ERROR;

	END_FOR;

	ON_ERROR
		MISC_release_request_silent(req_handle2);
		general_on_error ();
	END_ERROR;

	MISC_release_request_silent(req_handle2);
}


USHORT get_view_base_relation_count(BurpGlobals* tdgbl,
									const TEXT* current_view_name,
									USHORT depth,
									bool* error)
{
/**************************************
 *
 *	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)
	{
		*error = true;
		return 0;
	}

	isc_req_handle  req_handle1 = 0;

	USHORT result = 0;

	FOR (REQUEST_HANDLE req_handle1)
		V IN RDB$VIEW_RELATIONS
		CROSS R IN RDB$RELATIONS
		WITH
		  V.RDB$VIEW_NAME EQ current_view_name AND
		  R.RDB$RELATION_NAME EQ V.RDB$RELATION_NAME

		if (R.RDB$VIEW_BLR.NULL)
		{
			// This relation is a table, so increment count
			result++;
		}
		else
		{
			// Call recursive for VIEWS that are referenced in VIEWS
			result += get_view_base_relation_count(tdgbl, V.RDB$RELATION_NAME, depth, error);
			if (*error)
				break;
		}
	END_FOR;

	ON_ERROR
		MISC_release_request_silent(req_handle1);
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);

	return result;
}


void store_blr_gen_id(BurpGlobals* tdgbl, const TEXT* gen_name, SINT64 value, SINT64 initial_value,
	const ISC_QUAD* gen_desc, const char* secclass, const char* ownername, fb_sysflag sysFlag,
	SLONG increment)
{
/**************************************
 *
 *	s t o r e _ b l r _ g e n _ i d
 *
 **************************************
 *
 * Functional description
 *	Store the blr_gen_id for the relation.
 *
 **************************************/
	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		STORE (REQUEST_HANDLE tdgbl->handles_store_blr_gen_id_req_handle1)
			X IN RDB$GENERATORS

			strcpy (X.RDB$GENERATOR_NAME, gen_name);
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = (SSHORT) sysFlag;
			X.RDB$SECURITY_CLASS.NULL = TRUE;
			X.RDB$OWNER_NAME.NULL = TRUE;
			if (gen_desc)
			{
				X.RDB$DESCRIPTION = *gen_desc;
				X.RDB$DESCRIPTION.NULL = FALSE;
			}
			if (secclass)
			{
				strcpy(X.RDB$SECURITY_CLASS, secclass);
				fix_security_class_name(tdgbl, X.RDB$SECURITY_CLASS, false);
				X.RDB$SECURITY_CLASS.NULL = FALSE;
			}
			if (ownername)
			{
				strcpy(X.RDB$OWNER_NAME, ownername);
				X.RDB$OWNER_NAME.NULL = FALSE;
			}
			X.RDB$INITIAL_VALUE.NULL = FALSE;
			X.RDB$INITIAL_VALUE = initial_value;
			X.RDB$GENERATOR_INCREMENT = increment;
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;

		collect_missing_privs(tdgbl, obj_generator, gen_name, secclass);
	}
	else if (tdgbl->runtimeODS >= DB_VERSION_DDL11)
	{
		STORE (REQUEST_HANDLE tdgbl->handles_store_blr_gen_id_req_handle1)
			X IN RDB$GENERATORS

			strcpy (X.RDB$GENERATOR_NAME, gen_name);
			X.RDB$DESCRIPTION.NULL = TRUE;
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			if (gen_desc)
			{
				X.RDB$DESCRIPTION = *gen_desc;
				X.RDB$DESCRIPTION.NULL = FALSE;
			}
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}
	else
	{
		STORE (REQUEST_HANDLE tdgbl->handles_store_blr_gen_id_req_handle1)
			X IN RDB$GENERATORS

			strcpy (X.RDB$GENERATOR_NAME, gen_name);
			X.RDB$SYSTEM_FLAG = 0;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
		END_STORE;
		ON_ERROR
			general_on_error ();
		END_ERROR;
	}

	if (!value)
	{
		BURP_verbose (185, SafeArg() << gen_name << 0);
		// msg 185 restoring generator %s value: %ld
		return;
	}


	FB_API_HANDLE gen_id_reqh = 0;
	UCHAR blr_buffer[100];  // enough to fit blr
	UCHAR* blr = blr_buffer;

	// build the blr with the right relation name

	if (tdgbl->runtimeODS >= DB_VERSION_DDL10)
	{
		add_byte(blr, blr_version5);
	}
	else
	{
		add_byte(blr, blr_version4);
	}
	add_byte(blr, blr_begin);
	if (tdgbl->runtimeODS >= DB_VERSION_DDL10)
	{
		add_byte(blr, blr_dcl_variable);
		add_word(blr, 0);
		add_byte(blr, blr_int64);
		add_byte(blr, 0);
	}
	else
	{
		add_byte(blr, blr_dcl_variable);
		add_word(blr, 0);
		add_byte(blr, blr_long);
		add_byte(blr, 0);
	}
	add_byte(blr, blr_begin);
	add_byte(blr, blr_assignment);
	add_byte(blr, blr_gen_id);
	add_string(blr, gen_name);
	if (tdgbl->runtimeODS >= DB_VERSION_DDL10)
	{
		add_byte(blr, blr_literal);
		add_byte(blr, blr_int64);
		add_byte(blr, 0);
		add_int64(blr, value);
	}
	else
	{
		add_byte(blr, blr_literal);
		add_byte(blr, blr_long);
		add_byte(blr, 0);
		add_long(blr, (SLONG)value);
	}
	add_byte(blr, blr_variable);
	add_word(blr, 0);
	add_byte(blr, blr_end);
	add_byte(blr, blr_end);
	add_byte(blr, blr_eoc);

	const USHORT blr_length = blr - blr_buffer;
	fb_assert(blr_length <= sizeof(blr_buffer));

	ISC_STATUS_ARRAY status_vector;
	if (isc_compile_request (status_vector, &DB, &gen_id_reqh,
							 blr_length, (const SCHAR*) blr_buffer))
	{
		fb_print_blr(blr_buffer, blr_length, NULL, NULL, 0);
		BURP_error_redirect (status_vector, 42); // msg 42 Failed in store_blr_gen_id
	}

	if (isc_start_request (status_vector, &gen_id_reqh,
						   &gds_trans, // use the same one generated by gpre
						   0))
	{
		fb_print_blr(blr_buffer, blr_length, NULL, NULL, 0);
		BURP_error_redirect (status_vector, 42); // msg 42 Failed in store_blr_gen_id
	}

	BURP_verbose (185, SafeArg() << gen_name << value);
	// msg 185 restoring generator %s value: %ld

	isc_release_request (status_vector, &gen_id_reqh);
}


void update_global_field(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	u p d a t e _ g l o b a l _ f i e l d
 *
 **************************************
 *
 * Functional description
 *	Update the global field definition to add constraints.
 *	The blobs have been created already.
 *
 **************************************/
	isc_req_handle req_handle1 = 0;

	for (gfld* gfield = tdgbl->gbl_global_fields; gfield; )
	{
		FOR (TRANSACTION_HANDLE tdgbl->global_trans REQUEST_HANDLE req_handle1)
			X IN RDB$FIELDS WITH X.RDB$FIELD_NAME EQ gfield->gfld_name
			MODIFY X

				if (gfield->gfld_flags & GFLD_validation_blr)
				{
					X.RDB$VALIDATION_BLR.NULL = FALSE;
					memcpy(&X.RDB$VALIDATION_BLR, &gfield->gfld_vb, sizeof(ISC_QUAD));
				}

				if (gfield->gfld_flags & GFLD_validation_source)
				{
					X.RDB$VALIDATION_SOURCE.NULL = FALSE;
					memcpy(&X.RDB$VALIDATION_SOURCE, &gfield->gfld_vs, sizeof(ISC_QUAD));
				}

				if (gfield->gfld_flags & GFLD_validation_source2)
				{
					X.RDB$VALIDATION_SOURCE.NULL = FALSE;
					memcpy(&X.RDB$VALIDATION_SOURCE, &gfield->gfld_vs2, sizeof(ISC_QUAD));
				}

				if (gfield->gfld_flags & GFLD_computed_blr)
				{
					X.RDB$COMPUTED_BLR.NULL = FALSE;
					memcpy(&X.RDB$COMPUTED_BLR, &gfield->gfld_computed_blr, sizeof(ISC_QUAD));
				}

				if (gfield->gfld_flags & GFLD_computed_source)
				{
					X.RDB$COMPUTED_SOURCE.NULL = FALSE;
					memcpy(&X.RDB$COMPUTED_SOURCE, &gfield->gfld_computed_source, sizeof(ISC_QUAD));
				}

				if (gfield->gfld_flags & GFLD_computed_source2)
				{
					X.RDB$COMPUTED_SOURCE.NULL = FALSE;
					memcpy(&X.RDB$COMPUTED_SOURCE, &gfield->gfld_computed_source2, sizeof(ISC_QUAD));
				}

			END_MODIFY;
			ON_ERROR
				general_on_error ();
			END_ERROR;

		END_FOR;
		ON_ERROR
			general_on_error ();
		END_ERROR;

		gfld* n_gfield = gfield->gfld_next;
		BURP_free (gfield);
		gfield = n_gfield;
	}
	MISC_release_request_silent(req_handle1);

	tdgbl->gbl_global_fields = NULL;
}


void update_ownership(BurpGlobals* tdgbl)
{
	isc_req_handle req_handle2 = 0, req_handle4 = 0, req_handle6 = 0;
	BURP_verbose(358);

	// Change ownership of any packages
	for (burp_pkg* package = tdgbl->packages; package; package = package->pkg_next)
	{
		fb_assert(tdgbl->runtimeODS >= DB_VERSION_DDL12);

		if (package->pkg_owner[0])
		{
			FOR (REQUEST_HANDLE req_handle6)
				X IN RDB$PACKAGES
				WITH X.RDB$PACKAGE_NAME EQ package->pkg_name
			{
				MODIFY X
					strcpy(X.RDB$OWNER_NAME, package->pkg_owner);
				END_MODIFY;
				ON_ERROR
					MISC_release_request_silent(req_handle6);
					general_on_error();
				END_ERROR;

				if (!X.RDB$SECURITY_CLASS.NULL)
					restore_security_class(tdgbl, package->pkg_owner, X.RDB$SECURITY_CLASS);
			}
			END_FOR
			ON_ERROR
				MISC_release_request_silent(req_handle6);
				general_on_error ();
			END_ERROR
		}
	}

	MISC_release_request_silent(req_handle6);

	// Change ownership of any procedures necessary
	for (burp_prc* procedure = tdgbl->procedures; procedure; procedure = procedure->prc_next)
	{
		if (procedure->prc_owner[0])
		{
			if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
			{
				FOR (REQUEST_HANDLE req_handle4)
					X IN RDB$PROCEDURES
					WITH X.RDB$PROCEDURE_NAME EQ procedure->prc_name AND
						 X.RDB$PACKAGE_NAME EQUIV NULLIF(procedure->prc_package, '')
				{
					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

					if (!X.RDB$SECURITY_CLASS.NULL)
						restore_security_class(tdgbl, procedure->prc_owner, X.RDB$SECURITY_CLASS);
				}
				END_FOR;
				ON_ERROR
					MISC_release_request_silent(req_handle4);
					general_on_error ();
				END_ERROR
			}
			else
			{
				FOR (REQUEST_HANDLE req_handle4)
					X IN RDB$PROCEDURES WITH X.RDB$PROCEDURE_NAME EQ procedure->prc_name
				{
					MODIFY X
						strcpy (X.RDB$OWNER_NAME, procedure->prc_owner);
					END_MODIFY;
					ON_ERROR
						MISC_release_request_silent(req_handle4);
						general_on_error ();
					END_ERROR;

					restore_security_class(tdgbl, procedure->prc_owner, X.RDB$SECURITY_CLASS);
				}
				END_FOR
				ON_ERROR
					MISC_release_request_silent(req_handle4);
					general_on_error ();
				END_ERROR
			}
		}
	}

	MISC_release_request_silent(req_handle4);

	// Change ownership of any relations necessary

	for (burp_rel* relation = tdgbl->relations; relation; relation = relation->rel_next)
	{
		if (relation->rel_owner[0])
		{
			FOR (REQUEST_HANDLE req_handle2)
				X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ relation->rel_name
				MODIFY X
					strcpy (X.RDB$OWNER_NAME, relation->rel_owner);
				END_MODIFY;
				ON_ERROR
					MISC_release_request_silent(req_handle2);
					general_on_error ();
				END_ERROR;

				restore_security_class(tdgbl, relation->rel_owner, X.RDB$SECURITY_CLASS);
				restore_security_class(tdgbl, relation->rel_owner, X.RDB$DEFAULT_CLASS);

			END_FOR;
			ON_ERROR
				MISC_release_request_silent(req_handle2);
				general_on_error ();
			END_ERROR;
		}
	}

	MISC_release_request_silent(req_handle2);
}


void update_view_dbkey_lengths(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	u p d a t e _ v i e w _ d b k e y _ l e n g t h s
 *
 **************************************
 *
 * Functional description
 *	During the restore process VIEWs could
 *  be created that holds other VIEWs and
 *  which weren't restored yet.
 *  Then the RDB$DBKEY_LENGTH for VIEWs is
 *  calculated wrong. Therefore we need to
 *  recalculate the DBKEY_LENGTH else we
 *  get our famous "arithmetic exception,
 *  numeric overflow, or string truncation" error.
 *
 **************************************/
	isc_req_handle  req_handle2 = 0;

	BURP_verbose(357);

	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)

		bool error = false;
		const USHORT result = get_view_base_relation_count(tdgbl, R.RDB$RELATION_NAME, 0, &error);
		fb_utils::exact_name(R.RDB$RELATION_NAME);
		if (error)
			BURP_error(339, false, SafeArg() << MAX_UPDATE_DBKEY_RECURSION_DEPTH << R.RDB$RELATION_NAME);

		ULONG res2 = ULONG(result) * 8; // is a constant for DBKEY coded somewhere?
		if (res2 > USHORT(MAX_SSHORT))
			BURP_error(340, false, SafeArg() << MAX_SSHORT << R.RDB$RELATION_NAME);

		MODIFY R
			R.RDB$DBKEY_LENGTH = SSHORT(res2);
		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);
}


void fix_missing_privileges(BurpGlobals* tdgbl)
{
	BURP_verbose(359);

	GDS_NAME owner_name;
	isc_req_handle req_handle1 = 0, req_handle2 = 0;

	FOR (REQUEST_HANDLE req_handle1)
		REL IN RDB$RELATIONS
		WITH REL.RDB$RELATION_ID = 1

		strcpy(owner_name, REL.RDB$OWNER_NAME);

	END_FOR;
	ON_ERROR
		MISC_release_request_silent(req_handle1);
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);

	for (burp_meta_obj* object = tdgbl->miss_privs; object; object = object->obj_next)
	{
		if (object->obj_class)
			continue;

		const char* const privilege = (object->obj_type == obj_udf) ? "X" : "G";

		for (int i = 1; i >= 0; i--)
		{
			STORE (REQUEST_HANDLE req_handle2)
				X IN RDB$USER_PRIVILEGES

				X.RDB$FIELD_NAME.NULL = TRUE;
				X.RDB$GRANTOR.NULL = TRUE;

				X.RDB$PRIVILEGE.NULL = FALSE;
				strcpy(X.RDB$PRIVILEGE, privilege);

				X.RDB$USER.NULL = FALSE;
				strcpy(X.RDB$USER, (i == 0) ? "PUBLIC" : owner_name);

				X.RDB$RELATION_NAME.NULL = FALSE;
				strcpy(X.RDB$RELATION_NAME, object->obj_name);

				X.RDB$USER_TYPE.NULL = FALSE;
				X.RDB$USER_TYPE = obj_user;

				X.RDB$OBJECT_TYPE.NULL = FALSE;
				X.RDB$OBJECT_TYPE = object->obj_type;

				X.RDB$GRANT_OPTION.NULL = FALSE;
				X.RDB$GRANT_OPTION = i;

			END_STORE;
			ON_ERROR
				MISC_release_request_silent(req_handle2);
				general_on_error();
			END_ERROR;
		}
	}

	MISC_release_request_silent(req_handle2);
}

struct FixGenerator
{
	const char* name;
	const char* table;
	const char* field;
	const char* prefix;
	int ods;
};

void fix_generator(BurpGlobals* tdgbl, const FixGenerator* g)
{
/**************************************
 *
 *	f i x _ g e n e r a t o r
 *
 **************************************
 *
 * Functional description
 *	Set value of system generator based on
 *	current state of related table.
 *
 **************************************/

	int start = static_cast<int>(strlen(g->prefix) + 1);

	Firebird::string sql;
	sql.printf("EXECUTE BLOCK AS "
			   "DECLARE VARIABLE maxInTable INT; "
			   "DECLARE VARIABLE currentGen INT; "
			   "BEGIN "
			   " SELECT FIRST(1) CAST(SUBSTRING(%s FROM %d FOR 32) AS INT) FROM %s "
			   " WHERE SUBSTRING(%s FROM %d FOR 32) SIMILAR TO '[0-9]+ *' "
			   "   AND %s STARTING WITH '%s' ORDER BY 1 DESC INTO :maxInTable; "
			   " "
			   " currentGen = gen_id(%s, 0); "
			   " IF (currentGen < maxInTable) THEN "
			   "  EXECUTE STATEMENT 'SET GENERATOR %s TO ' || maxInTable; "
			   "END",
			   /* SELECT 1 */ g->field, start, g->table, g->field, start, g->field, g->prefix,
			   /* SELECT 2 */ g->name,
			   /* SET GEN  */ g->name);

	if (isc_execute_immediate(isc_status, &DB, &gds_trans, 0, sql.c_str()) != 0)
	{
		if (isc_status[1] == isc_dsql_error)
		{
			BURP_print_status(false, isc_status);
			tdgbl->flag_on_line = false;
		}
		else
			general_on_error();
	}
}

static const FixGenerator genToFix[] =
{
	{ "RDB$CONSTRAINT_NAME",	"RDB$RELATION_CONSTRAINTS",	"RDB$CONSTRAINT_NAME",	"INTEG_" 		, DB_VERSION_DDL8 },
	{ "RDB$FIELD_NAME",			"RDB$FIELDS",				"RDB$FIELD_NAME",		"RDB$"			, DB_VERSION_DDL8 },
	{ "RDB$INDEX_NAME",			"RDB$INDICES", 				"RDB$INDEX_NAME",		"RDB$"			, DB_VERSION_DDL8 },
	{ "RDB$INDEX_NAME",			"RDB$INDICES", 				"RDB$INDEX_NAME",		"RDB$PRIMARY"	, DB_VERSION_DDL8 },
	{ "RDB$INDEX_NAME",			"RDB$INDICES", 				"RDB$INDEX_NAME",		"RDB$FOREIGN"	, DB_VERSION_DDL8 },
	{ "RDB$TRIGGER_NAME",		"RDB$TRIGGERS",				"RDB$TRIGGER_NAME",		"CHECK_"		, DB_VERSION_DDL8 },
	///"RDB$BACKUP_HISTORY"	// unused ???
	{ "RDB$SECURITY_CLASS",		"RDB$SECURITY_CLASSES",		"RDB$SECURITY_CLASS",	"SQL$" 			, DB_VERSION_DDL8 },
	{ "SQL$DEFAULT",			"RDB$SECURITY_CLASSES",		"RDB$SECURITY_CLASS",	"SQL$DEFAULT" 	, DB_VERSION_DDL8 },
	{ "RDB$GENERATOR_NAME",		"RDB$GENERATORS",			"RDB$GENERATOR_NAME",	"RDB$" 			, DB_VERSION_DDL12 },
	{ NULL, NULL, NULL, NULL }
};

void fix_system_generators(BurpGlobals* tdgbl)
{
/**************************************
 *
 *	f i x _ s y s t e m _ g e n e r a t o r s
 *
 **************************************
 *
 * Functional description
 *	Set value of system generators based on
 *	current state of related tables.
 *
 **************************************/

	BURP_verbose(350);
	// msg 350 fixing system generators

	for (const FixGenerator* g = genToFix; g->name; ++g)
	{
		if (tdgbl->runtimeODS >= g->ods)
			fix_generator(tdgbl, g);
	}
}

} // namespace
