/*
 *	PROGRAM:	JRD Backup and Restore Program
 *	MODULE:		backup.epp
 *	DESCRIPTION:	Backup 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: Added verbose backup records as BACKUP_VERBOSE_INTERVAL
 * 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
 *                         conditionals, as the engine now fully supports
 *                         readonly databases.
 * 2001.11.20 Claudio Valderrama: fix problem with embedded blanks in
 * generators and use symbol_length effective length calculation from put_text.
 * This minimizes code redundancy and fixes SF Bug #483276.
 * 2001.12.15 Claudio Valderrama: copy should run through symbol_length instead
 * of using just another length calculation algorithm. Callers of put_text, copy
 * and symbol_length (if used directly) should use sizeof. Changed all callers
 * and sizeof() works because the strings are local to the functions. This
 * eliminates the problem with harcoded limits in each call.
 * 2002.10.29 Mike Nordell: UINT64 backup message.
 * 2003.08.17 Claudio Valderrama: Fix SF Bug #750659.
 * 2005.04.29 Claudio Valderrama: Moved symbol_length to misc.cpp.
 */


#include "firebird.h"
#include <stdio.h>
#include <memory.h>
#include <string.h>
#include "../burp/burp.h"
#include "../jrd/ods.h"
#include "../jrd/align.h"
#include "../jrd/gdsassert.h"
#include "../jrd/constants.h"
#include "../common/stuff.h"
#include "../burp/backu_proto.h"
#include "../burp/burp_proto.h"
#include "../burp/canon_proto.h"
#include "../burp/mvol_proto.h"
#include "../remote/protocol.h"
#ifdef DEBUG
#include "../gpre/prett_proto.h"
#endif

#include "../common/classes/UserBlob.h"
#include "../common/classes/MsgPrint.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" RUNTIME * dbb_file;

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

namespace // unnamed, private
{

// VERBOSE INTERVAL WHEN BACKING RECORDS
const ULONG BACKUP_VERBOSE_INTERVAL = 20000;

#define PUT_MESSAGE(attrib, attrib2, message) put_message((attrib), (attrib2), (message), sizeof(message))
#define PUT_TEXT(attribute, text)	put_text ((attribute), (text), sizeof(text))
#define COPY(source, target)       copy ((source), (target), sizeof(target))

inline void put(BurpGlobals* tdgbl, const UCHAR c)
{
	if (--(tdgbl->io_cnt) >= 0)
		*(tdgbl->io_ptr)++ = c;
	else
		MVOL_write(c, &tdgbl->io_cnt, &tdgbl->io_ptr);
}

inline void put(BurpGlobals* tdgbl, const att_type c)
{
	if (--tdgbl->io_cnt >= 0)
		*(tdgbl->io_ptr)++ = UCHAR(c);
	else
		MVOL_write(UCHAR(c), &tdgbl->io_cnt, &tdgbl->io_ptr);
}

inline const UCHAR* put_block(BurpGlobals* tdgbl, const UCHAR* p, ULONG n)
{
	return MVOL_write_block (tdgbl, p, n);
}


void compress(const UCHAR*, ULONG);
int copy(const TEXT *, TEXT *, ULONG);
burp_fld* get_fields(burp_rel*);
SINT64 get_gen_id(const TEXT *, SSHORT);
void get_ranges(burp_fld*);
void put_array(burp_fld*, burp_rel*, ISC_QUAD*);
void put_asciz(const att_type, const TEXT*);
void put_blob(burp_fld*, ISC_QUAD&);
bool put_blr_blob(att_type, ISC_QUAD&);
void put_data(burp_rel*);
void put_index(burp_rel*);
int put_message(att_type, att_type, const TEXT*, const ULONG);
void put_numeric(att_type, SLONG);
void put_int64( att_type attribute, SINT64 value);
void put_relation(burp_rel*);
bool put_source_blob(att_type, att_type, ISC_QUAD&);
int put_text(att_type, const TEXT *, SSHORT);
void set_capabilities();
void write_character_sets();
void write_check_constraints();
void write_collations();
void write_database(const TEXT*);
void write_exceptions();
void write_field_dimensions();
void write_filters();
void write_functions();
void write_function_args(GDS_NAME);
void write_generators();
void write_sql_roles();
void write_mapping();
void write_global_fields();
void write_procedures();
void write_procedure_prms(GDS_NAME);
void write_ref_constraints();
void write_rel_constraints();
void write_relations();
void write_shadow_files();
void write_triggers();
void write_trigger_messages();
void write_types();
void write_user_privileges();
void general_on_error();

enum backup_capabilities
{
	BCK_security		= 1,
	BCK_files			= 2,
	BCK_external		= 4,
	BCK_idx_inactive	= 8,
	BCK_triggers		= 16,	// Obsolete - 1996-Aug-05
	BCK_context_name	= 32,
	BCK_db_description	= 64,
	BCK_ffmptt			= 128,	// rdb$functions, rdb$filters, rdb$trigger_messages,
								// rdb$user_privileges, rdb$triggers, rdb$types
	BCK_attributes_v3	= 256,	// attributes in various system relations new to v3
	BCK_rfr_sys_flag	= 512,	// system flag is missing from Rdb/VMS V3 RFR relation
	BCK_ods6			= 1024,	// rdb$field_dimensions and shadow files
	BCK_ods8			= 2048,	// stored procedures & exceptions & constraints
	BCK_ods9			= 4096,	// SQL roles
	BCK_ods10			= 8192,	// FIELD_PRECISION
	BCK_ods11			= 16384,// rdb$description in rdb$roles and rdb$generators
								// rdb$base_collation_name and rdb$specific_attributes in rdb$collations
								// rdb$message enlarged to 1021.
	BCK_ods11_1			= 32768,// rdb$relation_type in rdb$relations
								// rdb$procedure_type in rdb$procedures
								// rdb$valid_blr in rdb$triggers
								// rdb$valid_blr in rdb$procedures
								// rdb$default_value, rdb$default_source, rdb$collation_id,
								// rdb$null_flag and rdb$parameter_mechanism in rdb$procedure_parameters
	BCK_ods11_2			= 65536	// rdb$field_name and rdb$relation_name in rdb$procedure_parameters
								// rdb$admin system role
								// rdb$message enlarged to 1023.
};
// ASF: Engine that works with ODS11.1 supports access to non-existent system fields.
// Reads returns NULL and writes do nothing.

#ifdef DEBUG
UCHAR debug_on = 0;		// able to turn this on in debug mode
#endif


// table used to determine capabilities, checking for specific
// fields in system relations

struct rfr_tab_t
{
	const TEXT* relation;
	const TEXT* field;
	// Let's be compatible with BurpGlobals.BCK_capabilities, although we could follow
	// the enumeration backup_capabilities but the enum then should be defined
	// in burp.h instead so BCK_capabilities isn't anymore a simple SLONG.
	SLONG bit_mask;
};

const rfr_tab_t rfr_table[] =
{
	{"RDB$INDICES", "RDB$INDEX_INACTIVE", BCK_idx_inactive},
	// Backup of V2 triggers no longer supported 1996-Aug-05 David Schnepper
	// {"RDB$RELATIONS",	"RDB$STORE_TRIGGER",	BCK_triggers},
	{"RDB$RELATIONS", "RDB$EXTERNAL_FILE", BCK_external},
	{"RDB$SECURITY_CLASSES", "RDB$SECURITY_CLASS", BCK_security},
	{"RDB$FILES", "RDB$FILE_NAME", BCK_files},
	{"RDB$VIEW_RELATIONS", "RDB$CONTEXT_NAME", BCK_context_name},
	{"RDB$DATABASE", "RDB$DESCRIPTION", BCK_db_description},
	{"RDB$FUNCTIONS", "RDB$FUNCTION_NAME", BCK_ffmptt},
	{"RDB$FIELDS", "RDB$EXTERNAL_LENGTH", BCK_attributes_v3},
	{"RDB$RELATION_FIELDS", "RDB$SYSTEM_FLAG", BCK_rfr_sys_flag},
	{"RDB$FIELD_DIMENSIONS", "RDB$DIMENSION", BCK_ods6},
	{"RDB$PROCEDURES", "RDB$PROCEDURE_NAME", BCK_ods8},
	{"RDB$ROLES", "RDB$ROLE_NAME", BCK_ods9},
	{"RDB$FIELDS", "RDB$FIELD_PRECISION", BCK_ods10},
	{"RDB$ROLES", "RDB$DESCRIPTION", BCK_ods11},
	//{"RDB$ROLES", "RDB$SYSTEM_FLAG", BCK_ods11},
	//{"RDB$COLLATIONS", "RDB$BASE_COLLATION_NAME", BCK_ods11},
	//{"RDB$COLLATIONS", "RDB$SPECIFIC_ATTRIBUTES", BCK_ods11},
	{"RDB$RELATIONS", "RDB$RELATION_TYPE", BCK_ods11_1},
	//{"RDB$PROCEDURES", "RDB$PROCEDURE_TYPE", BCK_ods11_1},
	//{"RDB$TRIGGERS", "RDB$VALID_BLR", BCK_ods11_1},
	//{"RDB$PROCEDURES", "RDB$VALID_BLR", BCK_ods11_1},
	//{"RDB$PROCEDURE_PARAMETERS", "RDB$DEFAULT_VALUE", BCK_ods11_1},
	//{"RDB$PROCEDURE_PARAMETERS", "RDB$DEFAULT_SOURCE", BCK_ods11_1},
	//{"RDB$PROCEDURE_PARAMETERS", "RDB$COLLATION_ID", BCK_ods11_1},
	//{"RDB$PROCEDURE_PARAMETERS", "RDB$NULL_FLAG", BCK_ods11_1},
	//{"RDB$PROCEDURE_PARAMETERS", "RDB$PARAMETER_MECHANISM", BCK_ods11_1},
	{"RDB$PROCEDURE_PARAMETERS", "RDB$FIELD_NAME", BCK_ods11_2},
	//{"RDB$PROCEDURE_PARAMETERS", "RDB$RELATION_NAME", BCK_ods11_2},
	{0, 0, 0}
};

const UCHAR blob_items[] =
{
	isc_info_blob_max_segment,
	isc_info_blob_num_segments,
	isc_info_blob_type,
	isc_info_blob_total_length
};
const UCHAR blr_items[] =
{
	isc_info_blob_max_segment,
	isc_info_blob_total_length
};
const UCHAR source_items[] =
{
	isc_info_blob_max_segment,
	isc_info_blob_total_length,
	isc_info_blob_num_segments
};
const SCHAR db_info_items[] =
{
	isc_info_db_sql_dialect,
	isc_info_page_size,
	isc_info_sweep_interval,
	isc_info_forced_writes,
	isc_info_no_reserve,
	isc_info_set_page_buffers,
	isc_info_db_read_only,
	isc_info_end
};
const SCHAR limbo_tpb[] =
{
	isc_tpb_version1,
	isc_tpb_ignore_limbo
};
const SCHAR limbo_nau_tpb[] =
{
	isc_tpb_version1,
	isc_tpb_ignore_limbo,
	isc_tpb_no_auto_undo
};

} // namespace


int BACKUP_backup(const TEXT* dbb_file, const TEXT* file_name)
{
/**************************************
 *
 *	B A C K U P _ b a c k u p
 *
 **************************************
 *
 * Functional description
 *	Backup a database.
 *
 **************************************/
	ISC_STATUS_ARRAY status_vector;
	TEXT temp[GDS_NAME_LEN];

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	tdgbl->gbl_database_file_name = dbb_file;

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

	gds_trans = 0;

	BURP_verbose(130);
	// msg 130 starting transaction

	if (tdgbl->gbl_sw_ignore_limbo)
	{
		if (isc_start_transaction(status_vector, &gds_trans, 1, &DB,
								  sizeof(limbo_nau_tpb), limbo_nau_tpb))
		{
			isc_start_transaction(status_vector, &gds_trans, 1, &DB, sizeof(limbo_tpb), limbo_tpb);
		}
	}
	else
	{
		EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
		if (isc_status[1])
			EXEC SQL SET TRANSACTION;
	}

	if (!gds_trans)
	{
		EXEC SQL SET TRANSACTION NAME gds_trans NO_AUTO_UNDO;
		if (isc_status[1])
			EXEC SQL SET TRANSACTION NAME gds_trans;
	}




	// decide what type of database we've got

	set_capabilities();

	// Write burp record first with other valuable information
	// In case of split operation, write a 'split' header first to all the files

	if (tdgbl->action->act_action == ACT_backup_split)
	{
		for (burp_fil* fil = tdgbl->gbl_sw_files; fil; fil = fil->fil_next)
		{
			tdgbl->action->act_file = fil;
			if (!MVOL_split_hdr_write())
			{
				BURP_error(269, true, tdgbl->action->act_file->fil_name.c_str());
				// msg 269 can't write a header record to file %s
			}
		}
		tdgbl->action->act_file = tdgbl->gbl_sw_files;
	}

	MVOL_init_write(file_name, &tdgbl->io_cnt, &tdgbl->io_ptr);

	// Write database record

	write_database(dbb_file);

	// Write global fields

	BURP_verbose(150);
	// msg 150  writing global fields
	write_global_fields();

	if (tdgbl->BCK_capabilities & BCK_ods6)
	{
		write_field_dimensions();

		BURP_verbose(162);
		// msg 162  writing shadow files
		write_shadow_files();
	}

	if (tdgbl->BCK_capabilities & BCK_ods8)
	{
		// Write Character Sets
		BURP_verbose(msgVerbose_write_charsets);
		write_character_sets();

		// Write Collations
		BURP_verbose(msgVerbose_write_collations);
		write_collations();
	}

	if (tdgbl->BCK_capabilities & BCK_ffmptt)
	{
		// Write functions
		BURP_verbose(148);
		// msg 148 writing functions
		write_functions();
	}

	// Write relations

	BURP_verbose(154);
	// msg 154 writing relations

	write_relations();

	if (tdgbl->BCK_capabilities & BCK_ffmptt)
	{
		// Write types
		BURP_verbose(161);
		// msg 161  writing types
		write_types();

		// Write filters
		BURP_verbose(146);
		// msg 146 writing filters
		write_filters();

		// Write generators

		BURP_verbose(164);
		// msg 164  writing id generators
		write_generators();
	}

	if (tdgbl->BCK_capabilities & BCK_ods8)
	{
		// Write procedures
		BURP_verbose(192);
		// msg 192 writing stored procedures
		write_procedures();

		// Write exceptions
		BURP_verbose(197);
		// msg 197 writing exceptions
		write_exceptions();
	}

	// Now go back and write all data

	for (burp_rel* relation = tdgbl->relations; relation; relation = relation->rel_next)
	{
		put(tdgbl, (UCHAR) rec_relation_data);
		PUT_TEXT(att_relation_name, relation->rel_name);
		put(tdgbl, att_end);

		if (!(relation->rel_flags & REL_view) && !(relation->rel_flags & REL_external))
		{
			put_index(relation);
			if (!tdgbl->gbl_sw_meta)
				put_data(relation);
		}

		put(tdgbl, (UCHAR) rec_relation_end);
	}

	// now for the new triggers in rdb$triggers
	if (tdgbl->BCK_capabilities & BCK_ffmptt) {
		BURP_verbose(159);
		// msg 159  writing triggers
		write_triggers();
		BURP_verbose(158);
		// msg 158 writing trigger messages
		write_trigger_messages();
		write_user_privileges();
	}

	// Last, but not least, go back and add any access control lists

	if (tdgbl->BCK_capabilities & BCK_security)
	{
		isc_req_handle req_handle1 = 0;
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$SECURITY_CLASSES WITH X.RDB$SECURITY_CLASS NOT STARTING "SQL$"
			put(tdgbl, rec_security_class);
			const ULONG l = PUT_TEXT (att_class_security_class, X.RDB$SECURITY_CLASS);
			MISC_terminate (X.RDB$SECURITY_CLASS, temp, l, sizeof(temp));
			BURP_verbose (155, temp);
			// msg 155 writing security class %s
			put_blr_blob (att_class_acl, X.RDB$ACL);
			put_source_blob (att_class_description2, att_class_description, X.RDB$DESCRIPTION);
			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;

		MISC_release_request_silent(req_handle1);
	}

	if (tdgbl->BCK_capabilities & BCK_ods8)
	{
		// Write relation constraints
		BURP_verbose(206);
		// msg 206 writing relation constraints
		write_rel_constraints();

		// Write referential constraints
		BURP_verbose(209);
		// msg 209 writing referential constraints
		write_ref_constraints();

		// Write check constraints
		BURP_verbose(210);
		// msg 210 writing check constraints
		write_check_constraints();

	}

	if (tdgbl->BCK_capabilities & BCK_ods9)
	{
		// Write SQL roles
		BURP_verbose(248);
		// msg 248 writing SQL roles
		write_sql_roles();
	}

	if (tdgbl->BCK_capabilities & BCK_ods11)
	{
		// Write names mapping
		BURP_verbose(296);
		// msg 296 writing mapping
		write_mapping();
	}

	// Finish up

	put(tdgbl, (UCHAR) rec_end);
	FB_UINT64 cumul_count = MVOL_fini_write(&tdgbl->io_cnt, &tdgbl->io_ptr);
	tdgbl->action->act_action = ACT_backup_fini;
	BURP_verbose(176, SafeArg() << cumul_count);
	// msg 176 closing file, committing, and finishing.  %ld bytes written

	COMMIT;
	ON_ERROR
		general_on_error();
	END_ERROR;

	if (gds_trans)
		COMMIT gds_trans;
	ON_ERROR
		general_on_error();
	END_ERROR;

	FINISH
	ON_ERROR
		general_on_error();
	END_ERROR;

	return FINI_OK;
}

namespace // unnamed, private
{

void compress(const UCHAR* data, ULONG length)
{
/**************************************
 *
 *	c o m p r e s s
 *
 **************************************
 *
 * Functional description
 *	Write out data in compressed form.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	const UCHAR* p = data;
	const UCHAR* end = p + length;
	const UCHAR* q = NULL;

	while (p < end)
	{
		for (q = p + 2; q < end && (q[-2] != q[-1] || q[-1] != q[0]); q++)
			;
		USHORT run = (q < end) ? q - p - 2 : end - p;
		if (run)
		{
			for (; run > 127; run -= 127)
			{
				USHORT len = 127;
				put(tdgbl, (UCHAR) len);
				p = put_block(tdgbl, p, len);
			}
			if (run)
			{
				put(tdgbl, (UCHAR) run);
				p = put_block(tdgbl, p, run);
			}
		}
		for (q = p; q < end && *q == *p; q++)
			;
		if ((run = q - p) != 0)
		{
			for (; run > 127; run -= 127)
			{
				put(tdgbl, (UCHAR) (-127));
				put(tdgbl, (UCHAR) (*p));
			}
			if (run)
			{
				put(tdgbl, (UCHAR) (-run));
				put(tdgbl, (UCHAR) (*p));
			}
			p = q;
		}
	}
}


int copy( const TEXT* from, TEXT* to, ULONG size_len)
{
/**************************************
 *
 *	c o p y
 *
 **************************************
 *
 * Functional description
 *	Copy a blank or null terminated string into a null terminated
 *	string. It assumes there's enough room in the target.
 *
 **************************************/

	const ULONG len = (ULONG) MISC_symbol_length(from, size_len);

	memcpy(to, from, len);
	to[len] = '\0';

	return (int) len;
}


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 backup.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	BURP_print_status(true, isc_status);
	BURP_abort();
}


burp_fld* get_fields( burp_rel* relation)
{
/**************************************
 *
 *	g e t _ f i e l d s
 *
 **************************************
 *
 * Functional description
 *	Get fields for a relation.  Test
 *	capabilities and get system specific
 *
 **************************************/
	burp_fld* field;
	ISC_QUAD* blob_id;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	USHORT count = 1;

	Firebird::HalfStaticArray<burp_fld*, 16> field_list;

	// if we have all capabilities, use the first request to get the
	// most performance out of the latest engine; if we don't
	// have one of the capabilities we must use the second set of
	// requests--this requires more code but it is well worth it
	// for the performance benefits, especially remotely--deej

	if ((tdgbl->BCK_capabilities & BCK_attributes_v3) && (tdgbl->BCK_capabilities & BCK_ods8) &&
		(tdgbl->BCK_capabilities & BCK_rfr_sys_flag) && (tdgbl->BCK_capabilities & BCK_security))
	{
		FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle1)
			X IN RDB$RELATION_FIELDS CROSS
			Y IN RDB$FIELDS WITH
			X.RDB$FIELD_SOURCE = Y.RDB$FIELD_NAME AND
			X.RDB$RELATION_NAME EQ relation->rel_name

			field = (burp_fld*) BURP_alloc_zero(sizeof(burp_fld));
			field->fld_number = count++;
			field->fld_type = Y.RDB$FIELD_TYPE;
			field->fld_sub_type = Y.RDB$FIELD_SUB_TYPE;
			field->fld_length = Y.RDB$FIELD_LENGTH;
			field->fld_scale = Y.RDB$FIELD_SCALE;
			field->fld_id = X.RDB$FIELD_ID;

			if (!X.RDB$DESCRIPTION.NULL)
			{
				blob_id = &X.RDB$DESCRIPTION;
				if (blob_id->gds_quad_low || blob_id->gds_quad_high)
					field->fld_description = X.RDB$DESCRIPTION;
			}

			if (!X.RDB$QUERY_HEADER.NULL)
			{
				blob_id = &X.RDB$QUERY_HEADER;
				if (blob_id->gds_quad_low || blob_id->gds_quad_high)
					field->fld_query_header = X.RDB$QUERY_HEADER;
			}

			if (X.RDB$FIELD_POSITION.NULL)
				field->fld_flags |= FLD_position_missing;
			else
				field->fld_position = X.RDB$FIELD_POSITION;
			field->fld_view_context = X.RDB$VIEW_CONTEXT;
			if (X.RDB$UPDATE_FLAG.NULL)
				field->fld_flags |= FLD_update_missing;
			else
				field->fld_update_flag = X.RDB$UPDATE_FLAG;

			COPY (X.RDB$FIELD_NAME, field->fld_name);
			COPY (X.RDB$FIELD_SOURCE, field->fld_source);
			COPY (X.RDB$BASE_FIELD, field->fld_base);
			COPY (X.RDB$QUERY_NAME, field->fld_query_name);
			COPY (X.RDB$EDIT_STRING, field->fld_edit_string);
			COPY (X.RDB$COMPLEX_NAME, field->fld_complex_name);

			blob_id = &Y.RDB$COMPUTED_BLR;

			if (blob_id->gds_quad_low || blob_id->gds_quad_high) {
				field->fld_flags |= FLD_computed;
			}

			field->fld_system_flag = X.RDB$SYSTEM_FLAG;

			COPY (X.RDB$SECURITY_CLASS, field->fld_security_class);

			// use the fld_flags to mark the field as an array and
			// to differentiate it from other blobs

			if (Y.RDB$DIMENSIONS) {
				field->fld_flags |= FLD_array;
				field->fld_dimensions = Y.RDB$DIMENSIONS;
				if (field->fld_dimensions < 0) {
					BURP_error_redirect (NULL, 52, SafeArg() << field->fld_name);
				}
				// msg 52 array dimension for field %s is invalid
				get_ranges (field);
			}

			if (!X.RDB$NULL_FLAG.NULL) {
				field->fld_null_flag = X.RDB$NULL_FLAG;
				field->fld_flags |= FLD_null_flag;
			}

			if (!X.RDB$DEFAULT_VALUE.NULL) {
				blob_id = &X.RDB$DEFAULT_VALUE;
				if (blob_id->gds_quad_low || blob_id->gds_quad_high) {
					field->fld_default_value = X.RDB$DEFAULT_VALUE;
				}
			}

			if (!X.RDB$DEFAULT_SOURCE.NULL) {
				blob_id = &X.RDB$DEFAULT_SOURCE;
				if (blob_id->gds_quad_low || blob_id->gds_quad_high) {
					field->fld_default_source = X.RDB$DEFAULT_SOURCE;
				}
			}

			if (!Y.RDB$CHARACTER_SET_ID.NULL) {
				field->fld_character_set_id = Y.RDB$CHARACTER_SET_ID;
				field->fld_flags |= FLD_charset_flag;
			}

			if (!X.RDB$COLLATION_ID.NULL) {
				field->fld_collation_id = X.RDB$COLLATION_ID;
				field->fld_flags |= FLD_collate_flag;
			}

			field_list.add(field);

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else {
		FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle1)
			X IN RDB$RELATION_FIELDS CROSS Y IN RDB$FIELDS
			WITH X.RDB$FIELD_SOURCE = Y.RDB$FIELD_NAME AND
			X.RDB$RELATION_NAME EQ relation->rel_name

			field = (burp_fld*) BURP_alloc_zero (sizeof(burp_fld));
			field->fld_number = count++;
			field->fld_type = Y.RDB$FIELD_TYPE;
			field->fld_sub_type = Y.RDB$FIELD_SUB_TYPE;
			field->fld_length = Y.RDB$FIELD_LENGTH;
			field->fld_scale = Y.RDB$FIELD_SCALE;
			field->fld_id = X.RDB$FIELD_ID;
			if (!X.RDB$DESCRIPTION.NULL) {
				blob_id = &X.RDB$DESCRIPTION;
				if (blob_id->gds_quad_low || blob_id->gds_quad_high)
					field->fld_description = X.RDB$DESCRIPTION;
			}
			if (!X.RDB$QUERY_HEADER.NULL) {
				blob_id = &X.RDB$QUERY_HEADER;
				if (blob_id->gds_quad_low || blob_id->gds_quad_high)
					field->fld_query_header = X.RDB$QUERY_HEADER;
			}
			if (X.RDB$FIELD_POSITION.NULL)
				field->fld_flags |= FLD_position_missing;
			else
				field->fld_position = X.RDB$FIELD_POSITION;
			field->fld_view_context = X.RDB$VIEW_CONTEXT;
			if (X.RDB$UPDATE_FLAG.NULL)
				field->fld_flags |= FLD_update_missing;
			else
				field->fld_update_flag = X.RDB$UPDATE_FLAG;
			COPY (X.RDB$FIELD_NAME, field->fld_name);
			COPY (X.RDB$FIELD_SOURCE, field->fld_source);
			COPY (X.RDB$BASE_FIELD, field->fld_base);
			COPY (X.RDB$QUERY_NAME, field->fld_query_name);
			COPY (X.RDB$EDIT_STRING, field->fld_edit_string);
			if (tdgbl->BCK_capabilities & BCK_attributes_v3)
			{
				FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle2)
					RFR IN RDB$RELATION_FIELDS WITH RFR.RDB$FIELD_NAME = X.RDB$FIELD_NAME AND
					RFR.RDB$RELATION_NAME = X.RDB$RELATION_NAME
					COPY (RFR.RDB$COMPLEX_NAME, field->fld_complex_name);
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}
			blob_id = &Y.RDB$COMPUTED_BLR;
			if (blob_id->gds_quad_low || blob_id->gds_quad_high)
				field->fld_flags |= FLD_computed;
			if (tdgbl->BCK_capabilities & BCK_rfr_sys_flag)
			{
				FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle3)
					RFR IN RDB$RELATION_FIELDS WITH
					RFR.RDB$RELATION_NAME = relation->rel_name
					AND RFR.RDB$FIELD_NAME = X.RDB$FIELD_NAME
					field->fld_system_flag = RFR.RDB$SYSTEM_FLAG;
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}
			if (tdgbl->BCK_capabilities & BCK_security)
			{
				FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle4)
					RFR IN RDB$RELATION_FIELDS WITH
					RFR.RDB$RELATION_NAME = relation->rel_name
					AND RFR.RDB$FIELD_NAME = X.RDB$FIELD_NAME
					COPY (RFR.RDB$SECURITY_CLASS, field->fld_security_class);
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}
			if (tdgbl->BCK_capabilities & BCK_attributes_v3)
			{
				FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle5)
					RF IN RDB$FIELDS WITH RF.RDB$FIELD_NAME = X.RDB$FIELD_SOURCE
					// use the fld_flags to mark the field as an array and
					// to differentiate it from other blobs
					if (RF.RDB$DIMENSIONS)
					{
						field->fld_flags |= FLD_array;
						field->fld_dimensions = RF.RDB$DIMENSIONS;
						if (field->fld_dimensions < 0) {
							BURP_error_redirect (NULL, 52, SafeArg() << field->fld_name);
							// msg 52 array dimension for field %s is invalid
						}
						get_ranges (field);
					}
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}
			if (tdgbl->BCK_capabilities & BCK_ods8)
			{
				FOR (REQUEST_HANDLE tdgbl->handles_get_fields_req_handle6)
					X2 IN RDB$RELATION_FIELDS CROSS F2 IN RDB$FIELDS
					WITH X2.RDB$FIELD_NAME = X.RDB$FIELD_NAME
					AND X2.RDB$RELATION_NAME EQ relation->rel_name
					AND X2.RDB$FIELD_SOURCE EQ F2.RDB$FIELD_NAME

					if (!X2.RDB$NULL_FLAG.NULL)
					{
						field->fld_null_flag = X2.RDB$NULL_FLAG;
						field->fld_flags |= FLD_null_flag;
					}
					if (!X2.RDB$DEFAULT_VALUE.NULL)
					{
						blob_id = &X2.RDB$DEFAULT_VALUE;
						if (blob_id->gds_quad_low || blob_id->gds_quad_high)
							field->fld_default_value = X2.RDB$DEFAULT_VALUE;
					}
					if (!X2.RDB$DEFAULT_SOURCE.NULL)
					{
						blob_id = &X2.RDB$DEFAULT_SOURCE;
						if (blob_id->gds_quad_low || blob_id->gds_quad_high)
							field->fld_default_source = X2.RDB$DEFAULT_SOURCE;
					}
					if (!F2.RDB$CHARACTER_SET_ID.NULL)
					{
						field->fld_character_set_id = F2.RDB$CHARACTER_SET_ID;
						field->fld_flags |= FLD_charset_flag;
					}
					if (!X2.RDB$COLLATION_ID.NULL)
					{
						field->fld_collation_id = X2.RDB$COLLATION_ID;
						field->fld_flags |= FLD_collate_flag;
					}
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}

			field_list.add(field);

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	burp_fld* fields = NULL;

	while (field_list.getCount())
	{
		field = field_list.pop();
		field->fld_next = fields;
		fields = field;
	}

	return fields;
}


SINT64 get_gen_id( const TEXT* name, SSHORT name_len)
{
/**************************************
 *
 *	g e t _ g e n _ i d
 *
 **************************************
 *
 * Functional description
 *	Read id for a generator;
 *
 **************************************/
	UCHAR blr_buffer[100];	// enough to fit blr

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FB_API_HANDLE gen_id_reqh = 0;
	UCHAR* blr = blr_buffer;

	// If this is ODS 10 (IB version 6.0) or greater, build BLR to retrieve
	// the 64-bit value of the generator.  If not, build BLR to retrieve the
	// 32-bit value, which we will cast to the expected INT64 format.

	if (tdgbl->BCK_capabilities & BCK_ods10)
	{
		// build the blr with the right relation name and 64-bit results.
		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_assignment);
		add_byte(blr, blr_gen_id);
		add_byte(blr, name_len);
		while (name_len--)
		{
			const UCHAR c = *name++;
			add_byte(blr, c);
		}
		add_byte(blr, blr_literal);
		add_byte(blr, blr_long);
		add_byte(blr, 0);
		add_word(blr, 0);
		add_word(blr, 0);
		add_byte(blr, blr_parameter);
		add_byte(blr, 0);
		add_word(blr, 0);
		add_byte(blr, blr_end);
		add_byte(blr, blr_eoc);
	}
	else
	{
		// build the blr with the right relation name and 32-bit results
		add_byte(blr, blr_version4);
		add_byte(blr, blr_begin);
		add_byte(blr, blr_message);
		add_byte(blr, 0);
		add_word(blr, 1);
		add_byte(blr, blr_long);
		add_byte(blr, 0);
		add_byte(blr, blr_send);
		add_byte(blr, 0);
		add_byte(blr, blr_assignment);
		add_byte(blr, blr_gen_id);
		add_byte(blr, name_len);
		while (name_len--)
		{
			const UCHAR c = *name++;
			add_byte(blr, c);
		}
		add_byte(blr, blr_literal);
		add_byte(blr, blr_long);
		add_byte(blr, 0);
		add_word(blr, 0);
		add_word(blr, 0);
		add_byte(blr, blr_parameter);
		add_byte(blr, 0);
		add_word(blr, 0);
		add_byte(blr, blr_end);
		add_byte(blr, blr_eoc);
	}

	const SSHORT blr_length = blr - blr_buffer;

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

	ISC_STATUS_ARRAY status_vector;
	if (isc_compile_request(status_vector, &DB, &gen_id_reqh, blr_length, (const char*) blr_buffer))
	{
		// if there's no gen_id, never mind ...
		return 0;
	}

	// use the same gds_trans generated by gpre
	if (isc_start_request(status_vector, &gen_id_reqh, &gds_trans, 0))
	{
		BURP_error_redirect(status_vector, 25);
		// msg 25 Failed in put_blr_gen_id
	}


	SINT64 read_msg1;
	if (tdgbl->BCK_capabilities & BCK_ods10)
	{
		if (isc_receive(status_vector, &gen_id_reqh, 0, sizeof(read_msg1), &read_msg1, 0))
		{
			BURP_error_redirect(status_vector, 25);
			// msg 25 Failed in put_blr_gen_id
		}
	}
	else
	{
		SLONG read_msg0;
		if (isc_receive(status_vector, &gen_id_reqh, 0, sizeof(read_msg0), &read_msg0, 0))
		{
			BURP_error_redirect(status_vector, 25);
			// msg 25 Failed in put_blr_gen_id
		}
		read_msg1 = (SINT64) read_msg0;
	}

	isc_release_request(status_vector, &gen_id_reqh);

	return read_msg1;
}


void get_ranges( burp_fld* field)
{
/**************************************
 *
 *	g e t _ r a n g e s
 *
 **************************************
 *
 * Functional description
 *	Fill in the range low and high bounds by reading
 *      the ranges in rdb$field_dimensions.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	SLONG* rp = field->fld_ranges;
	USHORT count = 0;

	// Get the array dimensions in the rdb$field_dimensions

	FOR (REQUEST_HANDLE tdgbl->handles_get_ranges_req_handle1)
		X IN RDB$FIELD_DIMENSIONS
		WITH X.RDB$FIELD_NAME EQ field->fld_source
		SORTED BY X.RDB$DIMENSION

		if (count != X.RDB$DIMENSION)
			BURP_error_redirect (NULL, 52, SafeArg() << field->fld_name);
			// msg 52 array dimension for field %s is invalid
		*rp++ = X.RDB$LOWER_BOUND;
		*rp++ = X.RDB$UPPER_BOUND;
		count++;

	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	if (count != field->fld_dimensions)
		BURP_error_redirect(NULL, 52, SafeArg() << field->fld_name);
		// msg 52 array dimension for field %s is invalid
}


void put_array( burp_fld* field, burp_rel* relation, ISC_QUAD* blob_id)
{
/**************************************
 *
 *	p u t _ a r r a y
 *
 **************************************
 *
 * Functional description
 *	Write out an array.  If, however, it's null, don't even bother.
 *
 **************************************/
	SLONG range_buffer[16];	// enough for 16 dimensions
	UCHAR blr_buffer[200];		// enough for a sdl with 16 dimensions

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

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

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

	lstring xdr_buffer;
	xdr_buffer.lstr_allocated = 0;
	xdr_buffer.lstr_address = NULL;

	UCHAR* blr = blr_buffer;
	const SLONG* const end_ranges = field->fld_ranges + 2 * field->fld_dimensions;

	USHORT field_length = field->fld_length;
	if (tdgbl->gbl_sw_transportable)
		xdr_buffer.lstr_length = field_length + 3;

	// build the sdl

	add_byte(blr, isc_sdl_version1);

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

	add_byte(blr, field->fld_type);

	switch (field->fld_type)
	{
	case blr_short:
	case blr_long:
	case blr_quad:
	case blr_int64:
		add_byte(blr, field->fld_scale);
		break;
	case blr_text:
	case blr_varying:
		add_word(blr, field->fld_length);
		break;
	}

	if (field->fld_type == blr_varying)
		field_length += sizeof(USHORT);

	add_byte(blr, isc_sdl_rid);
	add_word(blr, relation->rel_id);
	add_byte(blr, isc_sdl_fid);
	add_word(blr, field->fld_id);

	USHORT count = 0;
	for (const SLONG* range = field->fld_ranges; 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;

	// compute the range size for each dimension = high_range - low_range

	ULONG slice_length = field_length;
	for (const SLONG* range = field->fld_ranges; range < end_ranges; range += 2)
	{
		slice_length *= (range[1] - range[0] + 1);
		if (tdgbl->gbl_sw_transportable)
			xdr_buffer.lstr_length *= (range[1] - range[0] + 1);
	}
	UCHAR* slice = BURP_alloc(slice_length);

	// allocate space for the XDR representation

	if (tdgbl->gbl_sw_transportable)
	{
		xdr_buffer.lstr_address = BURP_alloc(xdr_buffer.lstr_length);
		xdr_buffer.lstr_allocated = xdr_buffer.lstr_length;
	}

	ISC_STATUS_ARRAY status_vector;
	ULONG return_length = 0;
	if (isc_get_slice(status_vector, &DB, &gds_trans, blob_id, blr_length, (const char*) blr_buffer,
					  0,	// param length for subset of an array handling
					  NULL,	// param for subset of an array handling
					  slice_length, slice, (SLONG*) &return_length))
	{
		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
		// CVC: At this point I would expected calls to deallocate memory
		// See the end of this function.
		BURP_free(slice);
		if (xdr_buffer.lstr_allocated)
			BURP_free(xdr_buffer.lstr_address);
		return;
	}

	if (return_length != slice_length)
	{
		// Ugh.  The full array wasn't returned.  We must recompute the top
		// element to backup.

		SLONG returned_elements = (return_length / field_length) - 1;

		SLONG* returned_range = range_buffer;
		const SLONG* range = end_ranges - 2;
		for (int i1 = 0, i3 = 0;
			 range >= field->fld_ranges; range -= 2, returned_range++, 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);
			}
			*returned_range = (returned_elements - 1) / divisor + field->fld_ranges[i3];
			returned_elements -= (*returned_range - field->fld_ranges[i3]) * divisor;
			i3 += 2;
		}
	}

	put(tdgbl, (UCHAR) rec_array);
	put_numeric(att_blob_field_number, field->fld_number);
	put_numeric(att_array_dimensions, field->fld_dimensions);

	SLONG* returned_range = range_buffer;
	for (const SLONG* range = field->fld_ranges; range < end_ranges; range += 2, returned_range++)
	{
		put_numeric(att_array_range_low, (int) range[0]);
		if (return_length == slice_length)
			put_numeric(att_array_range_high, (int) range[1]);
		else
			put_numeric(att_array_range_high, (int) *returned_range);
	}

	put(tdgbl, att_blob_data);
	put(tdgbl, (UCHAR) (return_length));
	put(tdgbl, (UCHAR) (return_length >> 8));
	put(tdgbl, (UCHAR) (return_length >> 16));
	put(tdgbl, (UCHAR) (return_length >> 24));

	if (return_length)
	{
		const UCHAR* p;
		if (tdgbl->gbl_sw_transportable)
		{
			lstring xdr_slice;
			xdr_slice.lstr_allocated = xdr_slice.lstr_length = return_length;
			xdr_slice.lstr_address = slice;
			return_length = CAN_slice(&xdr_buffer, &xdr_slice, TRUE, /*blr_length,*/ blr_buffer);
			put(tdgbl, att_xdr_array);
			put(tdgbl, (UCHAR) (return_length));
			put(tdgbl, (UCHAR) (return_length >> 8));
			put(tdgbl, (UCHAR) (return_length >> 16));
			put(tdgbl, (UCHAR) (return_length >> 24));
			p = xdr_buffer.lstr_address;
		}
		else
			p = slice;
		put_block(tdgbl, p, return_length);
	}

	BURP_free(slice);
	if (xdr_buffer.lstr_allocated)
		BURP_free(xdr_buffer.lstr_address);
}


void put_asciz( const att_type attribute, const TEXT* string)
{
/**************************************
 *
 *	p u t _ a s c i z
 *
 **************************************
 *
 * Functional description
 *	Write an attribute starting with a null terminated string.
 *	Currently it's only called by write_database with the db's name as param.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	ULONG l = strlen(string);
	// CVC: We'll have to ensure that length < MAX_FILE_NAME_SIZE.
	// For now, we'll truncate silently until we define a message.
	// We can't honor operating systems that allow longer file names.
	if (l >= MAX_FILE_NAME_SIZE)
	{
		//BURP_print(CREATE A MESSAGE, SafeArg() << string << (MAX_FILE_NAME_SIZE - 1));
		// msg ZZZ: name %s too large, truncating to %d bytes.
		l = MAX_FILE_NAME_SIZE - 1;
	}

	put(tdgbl, attribute);
	put(tdgbl, (UCHAR) l);
	if (l)
		put_block(tdgbl, (const UCHAR*) string, l);
}


void put_blob( burp_fld* field, ISC_QUAD& blob_id)
{
/**************************************
 *
 *	p u t _ b l o b
 *
 **************************************
 *
 * Functional description
 *	Write out a blob.  If, however, it's null, don't even bother.
 *	This is for user data blobs.
 *
 **************************************/
	ISC_STATUS_ARRAY status_vector;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

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

	if (UserBlob::blobIsNull(blob_id))
		return;

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

	UserBlob blob(status_vector);

	if (!blob.open(DB, gds_trans, blob_id))
	{
		BURP_print(false, 81, field->fld_name);
		// msg 81 error accessing blob field %s -- continuing
		BURP_print_status(false, status_vector);
		return;
	}

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

	put(tdgbl, (UCHAR) rec_blob);
	put_numeric(att_blob_field_number, field->fld_number);

	ULONG segments = 0, total_length = 0;
	USHORT max_segment = 0;
	int blob_type = 0; // 0 - segmented, 1 - stream
	const UCHAR* p = blob_info;

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

		case isc_info_blob_type:
			blob_type = n;
			break;

		case isc_info_blob_num_segments:
			segments = n;
			break;

		case isc_info_blob_total_length:
			total_length = n;
			break;

		default:
			BURP_error_redirect(NULL, 21, SafeArg() << int(item));
			// msg 21 don't understand blob info item %ld
		}
	}

	// NS: Compute number of chunks we are going to write for stream blob based
	// on total blob length and maximum segment size. This way we tend to
	// preserve maximum internal segment size over backup/restore. I'm not sure
	// this is beneficial, but let's do it for the moment instead of inventing
	// our own chunk size. Note, the number of segments returned by information
	// call for stream blob is always unreliable, even for zero-length blobs
	if (blob_type == 1) {
		if (total_length != 0)
			segments = (total_length + max_segment - 1) / max_segment;
		else
			segments = 0;
	}

	put_numeric(att_blob_max_segment, max_segment);
	put_numeric(att_blob_number_segments, segments);
	put_numeric(att_blob_type, blob_type);

	// Allocate a buffer large enough for the largest segment and start grinding.
	UCHAR static_buffer[1024];
	UCHAR* buffer;
	if (!max_segment || max_segment <= sizeof(static_buffer))
		buffer = static_buffer;
	else
		buffer = BURP_alloc(max_segment);

	put(tdgbl, att_blob_data);

	while (segments > 0)
	{
		size_t segment_length;
		blob.getSegment(max_segment, buffer, segment_length);

		const ISC_STATUS status = blob.getCode();
		// Handle the errors. For stream blob isc_segment is not error here.
		if (status && (status != isc_segment || blob_type == 0))
		{
			BURP_error_redirect(status_vector, 22);
			// msg 22 isc_get_segment failed
		}

		put(tdgbl, (UCHAR) (segment_length));
		put(tdgbl, (UCHAR) (segment_length >> 8));
		if (segment_length)
		{
			put_block(tdgbl, buffer, segment_length);
		}
		--segments;
	}

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

	if (buffer != static_buffer)
		BURP_free(buffer);
}


bool put_blr_blob( att_type attribute, ISC_QUAD& blob_id)
{
/**************************************
 *
 *	p u t _ b l r _ b l o b
 *
 **************************************
 *
 * Functional description
 *	Write out a blr blob, if present.  Otherwise do nothing.
 *	Return true if the blob was present, false otherwise.
 *
 **************************************/
	ISC_STATUS_ARRAY status_vector;
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

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

	if (UserBlob::blobIsNull(blob_id))
		return false;

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

	UserBlob blob(status_vector);

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

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

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

	while ((item = *p++) != isc_info_end)
	{
		const USHORT l = isc_vax_integer((const char*) p, 2);
		p += 2;
		const ULONG n = isc_vax_integer((const char*) p, l);
		p += l;
		switch (item)
		{
		case isc_info_blob_max_segment:
			max_segment = n;
			break;

		case isc_info_blob_total_length:
			length = n;
			break;

		default:
			BURP_print(true, 79, SafeArg() << int(item));
			// msg 79 don't understand blob info item %ld
			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;

	put_numeric(attribute, (int) length);

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

	UCHAR static_buffer[1024];
	UCHAR* buffer;
	if (!max_segment || max_segment <= sizeof(static_buffer))
		buffer = static_buffer;
	else
		buffer = BURP_alloc(max_segment);

	// The old code didn't accept isc_segment so we check it.
	size_t segment_length;
	while (blob.getSegment(max_segment, buffer, segment_length) && !blob.getCode())
	{
		if (segment_length)
		{
			put_block(tdgbl, buffer, segment_length);
		}
	}

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

	if (buffer != static_buffer)
		BURP_free(buffer);

	return true;
}


void put_data(burp_rel* relation)
{
/**************************************
 *
 *	p u t _ d a t a
 *
 **************************************
 *
 * Functional description
 *	Write relation meta-data and data.
 *
 **************************************/
	burp_fld* field;
	ISC_STATUS_ARRAY status_vector;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	// CVC: A signed short isn't enough if the engine allows near 32K fields,
	// each being char(1) ASCII in the worst case. Looking at BLR generation
	// below, it's clear an extreme case won't compile => blr_length >= 32K.
	// However, SSHORT is the limit for request_length in isc_compile_request.
	SSHORT field_count = 1;

	for (field = relation->rel_fields; field; field = field->fld_next)
	{
		if (!(field->fld_flags & FLD_computed))
		{
			field_count += 2;
		}
	}
	fb_assert(field_count > 0 && field_count * 9 > 0 && field_count * 9 + 200 > 0);

	// Time to generate blr to fetch data.  Make sure we allocate a BLR buffer
	// large enough to handle the per field overhead
	UCHAR* const blr_buffer = BURP_alloc(200 + field_count * 9);
	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, field_count);		// Number of fields, counting eof

	RCRD_OFFSET offset = 0;
	SSHORT count = 0;   // This is param count.

	for (field = relation->rel_fields; field; field = field->fld_next)
	{
		if (field->fld_flags & FLD_computed)
			continue;
		SSHORT alignment = 4;
		FLD_LENGTH length = field->fld_length;
		SSHORT dtype = field->fld_type;
		if (field->fld_flags & FLD_array)
		{
			dtype = blr_blob;
			length = 8;
		}
		switch (dtype)
		{
		case blr_text:
			alignment = type_alignments[dtype_text];
			add_byte(blr, field->fld_type);
			add_word(blr, field->fld_length);
			break;

		case blr_varying:
			alignment = type_alignments[dtype_varying];
			add_byte(blr, field->fld_type);
			add_word(blr, field->fld_length);
			length += sizeof(USHORT);
			break;

		case blr_short:
			alignment = type_alignments[dtype_short];
			add_byte(blr, field->fld_type);
			add_byte(blr, field->fld_scale);
			break;

		case blr_long:
			alignment = type_alignments[dtype_long];
			add_byte(blr, field->fld_type);
			add_byte(blr, field->fld_scale);
			break;

		case blr_quad:
			alignment = type_alignments[dtype_quad];
			add_byte(blr, field->fld_type);
			add_byte(blr, field->fld_scale);
			break;

		case blr_int64:
			alignment = type_alignments[dtype_int64];
			add_byte(blr, field->fld_type);
			add_byte(blr, field->fld_scale);
			break;

		case blr_double:
			alignment = type_alignments[dtype_double];
			add_byte(blr, field->fld_type);
			break;

		case blr_timestamp:
			alignment = type_alignments[dtype_timestamp];
			add_byte(blr, field->fld_type);
			break;

		case blr_sql_time:
			alignment = type_alignments[dtype_sql_time];
			add_byte(blr, field->fld_type);
			break;

		case blr_sql_date:
			alignment = type_alignments[dtype_sql_date];
			add_byte(blr, field->fld_type);
			break;

		case blr_float:
			alignment = type_alignments[dtype_real];
			add_byte(blr, field->fld_type);
			break;

		case blr_blob:
			alignment = type_alignments[dtype_blob];
			add_byte(blr, blr_quad);
			add_byte(blr, 0);
			break;

		default:
			BURP_error_redirect(NULL, 26, 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;
	}

	// Next, build fields for null flags

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

	// Finally, make up an EOF field

	add_byte(blr, blr_short);			// eof field
	add_byte(blr, 0);					// scale for eof field
	SSHORT eof_parameter = count++;
	RCRD_OFFSET record_length = offset;
	RCRD_OFFSET eof_offset = FB_ALIGN(offset, sizeof(SSHORT));
	// To be used later for the buffer size to receive data
	const FLD_LENGTH length = (USHORT) (eof_offset + sizeof(SSHORT));

	// Build FOR loop, body, and eof handler

	add_byte(blr, blr_for);
	add_byte(blr, blr_rse);
	add_byte(blr, 1);					// count of relations
	add_byte(blr, blr_rid);
	add_word(blr, relation->rel_id);
	add_byte(blr, 0);					// context variable
	add_byte(blr, blr_end);

	add_byte(blr, blr_send);
	add_byte(blr, 0);
	add_byte(blr, blr_begin);
	add_byte(blr, blr_assignment);
	add_byte(blr, blr_literal);
	add_byte(blr, blr_short);
	add_byte(blr, 0);
	add_word(blr, 1);
	add_byte(blr, blr_parameter);
	add_byte(blr, 0);
	add_word(blr, eof_parameter);

	for (field = relation->rel_fields; field; field = field->fld_next)
	{
		if (field->fld_flags & FLD_computed)
			continue;
		add_byte(blr, blr_assignment);
		add_byte(blr, blr_fid);
		add_byte(blr, 0);
		add_word(blr, field->fld_id);
		add_byte(blr, blr_parameter2);
		add_byte(blr, 0);
		add_word(blr, field->fld_parameter);
		add_word(blr, field->fld_missing_parameter);
	}

	add_byte(blr, blr_end);

	add_byte(blr, blr_send);
	add_byte(blr, 0);
	add_byte(blr, blr_assignment);
	add_byte(blr, blr_literal);
	add_byte(blr, blr_short);
	add_byte(blr, 0);
	add_word(blr, 0);
	add_byte(blr, blr_parameter);
	add_byte(blr, 0);
	add_word(blr, eof_parameter);

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

	SSHORT blr_length = blr - blr_buffer;

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

	// Compile request

	FB_API_HANDLE request = 0;
	if (isc_compile_request(status_vector, &DB, &request, blr_length, (const SCHAR*) blr_buffer))
	{
		BURP_error_redirect(status_vector, 27);
		// msg 27 isc_compile_request failed
		fb_print_blr(blr_buffer, blr_length, NULL, NULL, 0);
	}

	BURP_free(blr_buffer);

	BURP_verbose(142, relation->rel_name);
	// msg 142  writing data for relation %s

	if (isc_start_request(status_vector, &request, &gds_trans, 0))
	{
		BURP_error_redirect(status_vector, 28);
		// msg 28 isc_start_request failed
	}

	// Here is the crux of the problem -- writing data.  All this work
	// for the following small loop.

	UCHAR* buffer = BURP_alloc(length);
	SSHORT* eof = (SSHORT *) (buffer + eof_offset);

	// the XDR representation may be even fluffier
	lstring xdr_buffer;
	if (tdgbl->gbl_sw_transportable)
	{
		xdr_buffer.lstr_length = xdr_buffer.lstr_allocated = length + count * 3;
		xdr_buffer.lstr_address = BURP_alloc(xdr_buffer.lstr_length);
	}
	else
		xdr_buffer.lstr_address = NULL;

	ULONG records = 0;
	while (true)
	{
		if (isc_receive(status_vector, &request, 0, length, buffer, 0))
		{
			BURP_error_redirect(status_vector, 29);
			// msg 29 isc_receive failed
		}
		if (!*eof)
			break;
		records++;
		// Verbose records
		if ((records % BACKUP_VERBOSE_INTERVAL) == 0)
			BURP_verbose(108, SafeArg() << records);

		put(tdgbl, (UCHAR) rec_data);
		put_numeric(att_data_length, record_length);
		const UCHAR* p;
		if (tdgbl->gbl_sw_transportable)
		{
			record_length = CAN_encode_decode(relation, &xdr_buffer, buffer, TRUE);
			put_numeric(att_xdr_length, record_length);
			p = xdr_buffer.lstr_address;
		}
		else
			p = buffer;
		put(tdgbl, att_data_data);
		if (tdgbl->gbl_sw_compress)
			compress(p, record_length);
		else if (record_length)
			put_block(tdgbl, p, record_length);

		// Look for any blobs to write

		for (field = relation->rel_fields; field; field = field->fld_next)
		{
			if (field->fld_type == blr_blob &&
				!(field->fld_flags & FLD_computed) && !(field->fld_flags & FLD_array))
			{
				put_blob(field, *(ISC_QUAD*) (buffer + field->fld_offset));
			}
		}

		// Look for any array to write
		// we got back the blob_id for the array from isc_receive in the second param.
		for (field = relation->rel_fields; field; field = field->fld_next)
		{
			if (field->fld_flags & FLD_array)
			{
				put_array(field, relation, (ISC_QUAD*) (buffer + field->fld_offset));
			}
		}
	}

	BURP_free(buffer);

	if (xdr_buffer.lstr_address)
		BURP_free(xdr_buffer.lstr_address);

	BURP_verbose(108, SafeArg() << records);
	// msg 108 %ld records written

	if (isc_release_request(status_vector, &request))
		BURP_error_redirect(status_vector, 30);
	// msg 30 isc_release_request failed
}


void put_index( burp_rel* relation)
{
/**************************************
 *
 *	p u t _ i n d e x
 *
 **************************************
 *
 * Functional description
 *	Write information about an index.  First
 *	check that all the segments of the
 *	index exist.
 *
 **************************************/
	ULONG count;
	TEXT temp[GDS_NAME_LEN];

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	// if we have all capabilities, use the first request to get the
	// most performance out of the latest engine; if we don't
	// have one of the capabilities we must use the second set of
	// requests--this requires more code but it is well worth it
	// for the performance benefits, especially remotely--deej

	if ((tdgbl->BCK_capabilities & BCK_idx_inactive) &&
		(tdgbl->BCK_capabilities & BCK_attributes_v3) &&
		(tdgbl->BCK_capabilities & BCK_ods8))
	{
		FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle1)
			X IN RDB$INDICES WITH
			X.RDB$RELATION_NAME EQ relation->rel_name

			count = 0;
			FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle2)
				I_S IN RDB$INDEX_SEGMENTS CROSS
				RFR IN RDB$RELATION_FIELDS WITH
				I_S.RDB$FIELD_NAME = RFR.RDB$FIELD_NAME AND
				I_S.RDB$INDEX_NAME = X.RDB$INDEX_NAME AND
				RFR.RDB$RELATION_NAME = relation->rel_name

				count++;

			END_FOR;
			ON_ERROR
				general_on_error();
			END_ERROR;

			if (count != (ULONG) X.RDB$SEGMENT_COUNT)
			{
				BURP_print(true, 180, SafeArg() << X.RDB$INDEX_NAME << count << X.RDB$SEGMENT_COUNT);
				continue;
			}

			put(tdgbl, rec_index);
			const ULONG l = PUT_TEXT (att_index_name, X.RDB$INDEX_NAME);
			MISC_terminate (X.RDB$INDEX_NAME, temp, l, sizeof(temp));
			BURP_verbose (151, temp);
			// msg 151 writing index %s
			put_numeric (att_segment_count, X.RDB$SEGMENT_COUNT);
			put_numeric (att_index_inactive, X.RDB$INDEX_INACTIVE);
			put_numeric (att_index_unique_flag, X.RDB$UNIQUE_FLAG);

			FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle5)
				Y IN RDB$INDEX_SEGMENTS WITH
				Y.RDB$INDEX_NAME EQ X.RDB$INDEX_NAME
				SORTED BY Y.RDB$FIELD_POSITION

				PUT_TEXT (att_index_field_name, Y.RDB$FIELD_NAME);

			END_FOR;
			ON_ERROR
				general_on_error();
			END_ERROR;

			put_source_blob (att_index_description2, att_index_description, X.RDB$DESCRIPTION);
			put_numeric (att_index_type, X.RDB$INDEX_TYPE);

			if (!X.RDB$EXPRESSION_SOURCE.NULL)
				put_source_blob (att_index_expression_source, att_index_expression_source,
								 X.RDB$EXPRESSION_SOURCE);
			if (!X.RDB$EXPRESSION_BLR.NULL)
				put_blr_blob (att_index_expression_blr, X.RDB$EXPRESSION_BLR);
			if (!X.RDB$FOREIGN_KEY.NULL)
				PUT_TEXT (att_index_foreign_key, X.RDB$FOREIGN_KEY);
			put(tdgbl, att_end);

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle1)
			X IN RDB$INDICES WITH
			X.RDB$RELATION_NAME EQ relation->rel_name

			count = 0;
			FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle2)
				I_S IN RDB$INDEX_SEGMENTS WITH
				I_S.RDB$INDEX_NAME = X.RDB$INDEX_NAME
				bool match = false;

				FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle3)
					RFR IN RDB$RELATION_FIELDS WITH
					I_S.RDB$FIELD_NAME = RFR.RDB$FIELD_NAME AND
					RFR.RDB$RELATION_NAME = relation->rel_name
					match = true;
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
				if (!match)
					BURP_print (true, 179, SafeArg() << I_S.RDB$FIELD_NAME << X.RDB$INDEX_NAME);
				else
					count++;
			END_FOR;
			ON_ERROR
				general_on_error();
			END_ERROR;

			if (count != (ULONG) X.RDB$SEGMENT_COUNT)
			{
				BURP_print(true, 180, SafeArg() << X.RDB$INDEX_NAME << count << X.RDB$SEGMENT_COUNT);
				continue;
			}

			put(tdgbl, rec_index);
			const ULONG l = PUT_TEXT (att_index_name, X.RDB$INDEX_NAME);
			MISC_terminate (X.RDB$INDEX_NAME, temp, l, sizeof(temp));
			BURP_verbose (151, temp);
			// msg 151 writing index %s
			put_numeric (att_segment_count, X.RDB$SEGMENT_COUNT);
			if (tdgbl->BCK_capabilities & BCK_idx_inactive)
				FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle4)
					I IN RDB$INDICES WITH I.RDB$INDEX_NAME = X.RDB$INDEX_NAME
					put_numeric (att_index_inactive, I.RDB$INDEX_INACTIVE);
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			put_numeric (att_index_unique_flag, X.RDB$UNIQUE_FLAG);
			FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle5)
				Y IN RDB$INDEX_SEGMENTS WITH Y.RDB$INDEX_NAME EQ X.RDB$INDEX_NAME
				SORTED BY Y.RDB$FIELD_POSITION
				PUT_TEXT (att_index_field_name, Y.RDB$FIELD_NAME);
			END_FOR;
			ON_ERROR
				general_on_error();
			END_ERROR;
			put_source_blob (att_index_description2, att_index_description, X.RDB$DESCRIPTION);
			if (tdgbl->BCK_capabilities & BCK_attributes_v3)
				FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle6)
					I IN RDB$INDICES WITH I.RDB$INDEX_NAME = X.RDB$INDEX_NAME
					put_numeric (att_index_type, I.RDB$INDEX_TYPE);
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			if (tdgbl->BCK_capabilities & BCK_ods8)
				FOR (REQUEST_HANDLE tdgbl->handles_put_index_req_handle7)
					I IN RDB$INDICES WITH I.RDB$INDEX_NAME = X.RDB$INDEX_NAME
					if (!I.RDB$EXPRESSION_SOURCE.NULL)
						put_source_blob (att_index_expression_source, att_index_expression_source,
										 I.RDB$EXPRESSION_SOURCE);
					if (!I.RDB$EXPRESSION_BLR.NULL)
						put_blr_blob (att_index_expression_blr, I.RDB$EXPRESSION_BLR);
					if (!I.RDB$FOREIGN_KEY.NULL)
						PUT_TEXT (att_index_foreign_key, I.RDB$FOREIGN_KEY);
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			put(tdgbl, att_end);

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
}


int put_message( att_type attribute, att_type attribute2, const TEXT* text, const ULONG length)
{
/**************************************
 *
 *	p u t _ m e s s a g e
 *
 **************************************
 *
 * Functional description
 *	Write a variable length text string, with embedded
 *      blanks.  Same as put_text but handles embedded blanks.
 * CVC: As v6 time, put_text handles embedded blanks, too!
 * The only difference is that put_text's length is SSHORT, so
 * in theory put_message can handle much longer input and it's
 * used for exception and trigger's messages (plus update/delete
 * rules for FKs and constraint types, where it's irrelevant
 * which function of the two you use).
 * CVC: Responsability for FKs and constraint types transferred to put_text.
 * This functions tries to maintain backwards compatibility where possible.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	ULONG l = 0;
	for (const TEXT* p = text; *p && l < length; p++)
		l++;

	const ULONG newlen = MIN(l, length);
	fb_assert(newlen <= MAX_USHORT);

	// If we can store the message using the old format, we do it.
	if (newlen <= MAX_UCHAR)
	{
		put(tdgbl, attribute);
		put(tdgbl, (UCHAR) newlen);
	}
	else if (newlen <= MAX_USHORT)
	{
		if (!attribute2) // In theory, this never happens, because the caller knows what it's doing.
			BURP_error(314, "");
		put(tdgbl, attribute2);
		USHORT vax_value = (USHORT) newlen;
		vax_value = (USHORT) gds__vax_integer((const UCHAR*) &vax_value, sizeof(vax_value));
		put_block(tdgbl, (const UCHAR*) &vax_value, sizeof(vax_value));
	}
	else
		BURP_error(315, "");

	if (newlen)
		put_block(tdgbl, reinterpret_cast<const UCHAR*>(text), newlen);

	return newlen;
}


void put_numeric( att_type attribute, SLONG value)
{
/**************************************
 *
 *	p u t _ n u m e r i c
 *
 **************************************
 *
 * Functional description
 *	Write a numeric value as an attribute.  The number is represented
 *	low byte first, high byte last, as in VAX.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	const SLONG vax_value = (SLONG) isc_vax_integer((const char*) &value, sizeof(value));

	put(tdgbl, attribute);
	put(tdgbl, (UCHAR) sizeof(value));
	put_block(tdgbl, (const UCHAR*) &vax_value, sizeof(vax_value));
}


void put_int64( att_type attribute, SINT64 value)
{
/**************************************
 *
 *	p u t _ i n t 6 4
 *
 **************************************
 *
 * Functional description
 *	Write a 64-bit numeric value as an attribute.
 *      The number is represented low byte first, high byte last, as in VAX.
 *      This function is just like put_numeric, except that it handles an
 *      INT64 value, while put_numeric handles a 32-bit value.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	const FB_UINT64 le_value = (FB_UINT64) isc_portable_integer((const UCHAR*) &value, sizeof(value));

	put(tdgbl, attribute);
	put(tdgbl, (UCHAR) sizeof(value));
	put_block(tdgbl, (const UCHAR*) &le_value, sizeof(le_value));
}


void put_relation( burp_rel* relation)
{
/**************************************
 *
 * 	p u t _ r e l a t i o n
 *
 **************************************
 *
 * Functional description
 *	Write relation meta-data and data.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	// Write local field information.  This is made slightly more complicated
	// by the requirement that computational fields be aligned.

	burp_fld* aligned = NULL;
	burp_fld* unaligned = NULL;
	burp_fld* aligned4 = NULL;
	burp_fld* aligned8 = NULL;

	burp_fld* fields = get_fields(relation);

	// sort the list of fields into three lists, depending on alignment
	burp_fld* field;
	for (field = fields; field = fields;)
	{
		fields = field->fld_next;
		USHORT l = field->fld_length;
		if (field->fld_type == blr_varying)
			l += sizeof(USHORT);
		if (!(l & 7))
		{
			field->fld_next = aligned8;
			aligned8 = field;
		}
		else if (!(l & 3))
		{
			field->fld_next = aligned4;
			aligned4 = field;
		}
		else if (l & 1)
		{
			field->fld_next = unaligned;
			unaligned = field;
		}
		else
		{
			field->fld_next = aligned;
			aligned = field;
		}
	}

	// Next, merge the aligned and unaligned sub-lists.  In the process,
	// re-create (approximately) the original order of the fields.  This is
	// not strictly required, but it certainly is polite.

	while (field = unaligned)
	{
		unaligned = field->fld_next;
		field->fld_next = relation->rel_fields;
		relation->rel_fields = field;
	}

	while (field = aligned)
	{
		aligned = field->fld_next;
		field->fld_next = relation->rel_fields;
		relation->rel_fields = field;
	}

	while (field = aligned4)
	{
		aligned4 = field->fld_next;
		field->fld_next = relation->rel_fields;
		relation->rel_fields = field;
	}

	while (field = aligned8)
	{
		aligned8 = field->fld_next;
		field->fld_next = relation->rel_fields;
		relation->rel_fields = field;
	}

	// Now write the fields in what will become physical backup order

	for (field = relation->rel_fields; field; field = field->fld_next)
	{
		put(tdgbl, (UCHAR) rec_field);
		const USHORT l = PUT_TEXT(att_field_name, field->fld_name);
		MISC_terminate(field->fld_name, temp, l, sizeof(temp));
		BURP_verbose(144, temp);
		// msg 144  writing field %s
		PUT_TEXT(att_field_source, field->fld_source);
		if (field->fld_query_name[0])
			PUT_TEXT(att_field_query_name, field->fld_query_name);
		if (field->fld_complex_name[0])
			PUT_TEXT(att_field_complex_name, field->fld_complex_name);
		if (field->fld_edit_string[0])
			PUT_TEXT(att_field_edit_string, field->fld_edit_string);
		put_source_blob(att_field_description2, att_field_description, field->fld_description);
		put_source_blob(att_field_query_header, att_field_query_header, field->fld_query_header);
		if (field->fld_security_class[0])
			PUT_TEXT(att_field_security_class, field->fld_security_class);
		if (!(field->fld_flags & FLD_position_missing))
			put_numeric(att_field_position, field->fld_position);
		put_numeric(att_field_type, field->fld_type);
		put_numeric(att_field_length, field->fld_length);
		put_numeric(att_field_sub_type, field->fld_sub_type);
		put_numeric(att_field_scale, field->fld_scale);
		put_numeric(att_field_number, field->fld_number);
		put_numeric(att_field_system_flag, field->fld_system_flag);
		if (!(field->fld_flags & FLD_update_missing))
			put_numeric(att_field_update_flag, field->fld_update_flag);
		if (field->fld_flags & FLD_null_flag)
			put_numeric(att_field_null_flag, field->fld_null_flag);
		if (field->fld_flags & FLD_charset_flag)
			put_numeric(att_field_character_set, field->fld_character_set_id);
		if (field->fld_flags & FLD_collate_flag)
			put_numeric(att_field_collation_id, field->fld_collation_id);
		put_blr_blob(att_field_default_value, field->fld_default_value);
		put_source_blob(att_field_default_source, att_field_default_source, field->fld_default_source);
		if (relation->rel_flags & REL_view)
		{
			put_numeric(att_view_context, field->fld_view_context);
			if (field->fld_base[0])
				PUT_TEXT(att_base_field, field->fld_base);
		}
		if (field->fld_flags & FLD_computed)
			put_numeric(att_field_computed_flag, TRUE);
		if (field->fld_flags & FLD_array)
		{
			put_numeric(att_field_dimensions, field->fld_dimensions);
			const SLONG* rp = field->fld_ranges;
			for (USHORT n = field->fld_dimensions; n; rp += 2, n--)
			{
				put_numeric(att_field_range_low, *rp);
				put_numeric(att_field_range_high, *(rp + 1));
			}
		}
		put(tdgbl, att_end);
	}

	// Write out view relations (if a view, of course)

	if (relation->rel_flags & REL_view)
	{
		if (tdgbl->BCK_capabilities & BCK_context_name)
		{
			FOR (REQUEST_HANDLE tdgbl->handles_put_relation_req_handle1)
				X IN RDB$VIEW_RELATIONS WITH X.RDB$VIEW_NAME EQ relation->rel_name
				put(tdgbl, rec_view);
				PUT_TEXT (att_view_relation_name, X.RDB$RELATION_NAME);
				put_numeric (att_view_context_id, X.RDB$VIEW_CONTEXT);
				PUT_TEXT (att_view_context_name, X.RDB$CONTEXT_NAME);
				put(tdgbl, att_end);
			END_FOR
			ON_ERROR
				general_on_error();
			END_ERROR;
		}
		else
		{
			FOR (REQUEST_HANDLE tdgbl->handles_put_relation_req_handle2)
				X IN RDB$VIEW_RELATIONS WITH X.RDB$VIEW_NAME EQ relation->rel_name
				put(tdgbl, rec_view);
				PUT_TEXT (att_view_relation_name, X.RDB$RELATION_NAME);
				put_numeric (att_view_context_id, X.RDB$VIEW_CONTEXT);
				put(tdgbl, att_end);
			END_FOR;
			ON_ERROR
				general_on_error();
			END_ERROR;
		}
	}
	put(tdgbl, (UCHAR) rec_relation_end);
}


bool put_source_blob(att_type attribute, att_type old_attribute, ISC_QUAD& blob_id)
{
/**************************************
 *
 *	p u t _ s o u r c e _ b l o b
 *
 **************************************
 *
 * Functional description
 *	Write out a source blob or query header if present.
 *	Return true is there was the blob was present, false otherwise.
 *	Include the NULL character to separate each segment.
 *
 **************************************/
	ISC_STATUS_ARRAY status_vector;
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

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

	if (UserBlob::blobIsNull(blob_id))
		return false;

	if (tdgbl->gbl_sw_old_descriptions && attribute != att_field_query_header)
		return put_blr_blob(old_attribute, blob_id);

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

	UserBlob blob(status_vector);

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

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

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

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

		case isc_info_blob_total_length:
			length = n;
			break;

		case isc_info_blob_num_segments:
			num_seg = n;
			break;

		default:
			BURP_print(true, 79, SafeArg() << int(item));
			// msg 79 don't understand blob info item %ld
			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(FB_UINT64(length) + num_seg <= FB_UINT64(MAX_ULONG));
	put_numeric(attribute, length + num_seg);

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

	UCHAR static_buffer[1024];
	UCHAR* buffer;
	if (!max_segment || max_segment <= sizeof(static_buffer))
		buffer = static_buffer;
	else
		buffer = BURP_alloc(max_segment);

	// The old code didn't accept isc_segment so we check it.
	size_t segment_length;
	while (blob.getSegment(max_segment, buffer, segment_length) && !blob.getCode())
	{
		if (segment_length)
		{
			put_block(tdgbl, buffer, segment_length);
		}
		put(tdgbl, (UCHAR) 0);
	}

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

	if (buffer != static_buffer)
		BURP_free(buffer);

	return true;
}


int put_text( att_type attribute, const TEXT* text, SSHORT size_len)
{
/**************************************
 *
 *	p u t _ t e x t
 *
 **************************************
 *
 * Functional description
 * Write a variable length text string, with embedded spaces.
 * Truncate trailing spaces.
 * Now this routine does not truncate trailing spaces, 3-2002 MOD
 * transfering changes from fb1, I believe this is to do with problems
 * with quoted names and embedded spaces.
 * CVC: This routine does trailing spaces truncation, but correctly.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	const SSHORT l = (SSHORT) MISC_symbol_length(text, (ULONG) size_len);
	fb_assert(l <= MAX_UCHAR);

	put(tdgbl, attribute);
	put(tdgbl, (UCHAR) l);
	if (l)
		put_block(tdgbl, (const UCHAR*) text, l);

	return l;
}


void set_capabilities()
{
/**************************************
 *
 *	s e t _ c a p a b i l i t i e s
 *
 **************************************
 *
 * Functional description
 *
 *	set the capabilities bits for the
 *	database being extracted to avoid
 *	unpleasantness later.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FB_API_HANDLE req = 0;

	// Look for desireable fields in system relations

	for (const rfr_tab_t* rel_field_table = rfr_table; rel_field_table->relation; rel_field_table++)
	{
		const TEXT* field = rel_field_table->field;
		const TEXT* relation = rel_field_table->relation;
		FOR (REQUEST_HANDLE req) x IN RDB$RELATION_FIELDS
			WITH x.RDB$RELATION_NAME = relation
			AND x.RDB$FIELD_NAME = field
			tdgbl->BCK_capabilities |= rel_field_table->bit_mask;
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	isc_release_request(isc_status, &req);
}


void write_character_sets()
{
/**************************************
 *
 *	w r i t e _ c h a r a c t e r _ s e t s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each user defined character set.
 *
 **************************************/
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$CHARACTER_SETS
		WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1 OR
			 X.RDB$DEFAULT_COLLATE_NAME NE X.RDB$CHARACTER_SET_NAME

		put(tdgbl, rec_charset);
		PUT_TEXT (att_charset_name, X.RDB$CHARACTER_SET_NAME);

		if (X.RDB$SYSTEM_FLAG.NULL || X.RDB$SYSTEM_FLAG != 1)
		{
			if (!X.RDB$FORM_OF_USE.NULL)
				PUT_TEXT (att_charset_form, X.RDB$FORM_OF_USE);
			if (!X.RDB$NUMBER_OF_CHARACTERS.NULL)
				put_numeric (att_charset_numchar, X.RDB$NUMBER_OF_CHARACTERS);
			put_numeric (att_charset_id, X.RDB$CHARACTER_SET_ID);
			if (X.RDB$SYSTEM_FLAG)
				put_numeric (att_charset_sysflag, X.RDB$SYSTEM_FLAG);
			if (!X.RDB$DESCRIPTION.NULL)
			{
				put_source_blob (att_charset_description, att_charset_description, X.RDB$DESCRIPTION);
			}
			if (!X.RDB$FUNCTION_NAME.NULL)
				PUT_TEXT (att_charset_funct, X.RDB$FUNCTION_NAME);
			put_numeric (att_charset_bytes_char, X.RDB$BYTES_PER_CHARACTER);
		}

		PUT_TEXT (att_charset_coll, X.RDB$DEFAULT_COLLATE_NAME);
		put(tdgbl, att_end);
	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}


void write_check_constraints()
{
/**************************************
 *
 *	w r i t e _ c h e c k _ c o n s t r a i n t s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each check constraint.
 *
 **************************************/
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$CHECK_CONSTRAINTS
		put(tdgbl, rec_chk_constraint);

		PUT_TEXT (att_chk_constraint_name, X.RDB$CONSTRAINT_NAME);
		if (!X.RDB$TRIGGER_NAME.NULL)
			PUT_TEXT (att_chk_trigger_name, X.RDB$TRIGGER_NAME);
		put(tdgbl, att_end);
	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}


void write_collations()
{
/**************************************
 *
 *	w r i t e _ c o l l a t i o n s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each user defined collation
 *
 **************************************/
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->BCK_capabilities & BCK_ods11)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$COLLATIONS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1
			put(tdgbl, rec_collation);
			PUT_TEXT (att_coll_name, X.RDB$COLLATION_NAME);
			put_numeric (att_coll_id, X.RDB$COLLATION_ID);
			put_numeric (att_coll_cs_id, X.RDB$CHARACTER_SET_ID);
			put_numeric (att_coll_attr, X.RDB$COLLATION_ATTRIBUTES);
			if (X.RDB$SYSTEM_FLAG)
				put_numeric (att_coll_sysflag, X.RDB$SYSTEM_FLAG);
			if (!X.RDB$DESCRIPTION.NULL)
				put_source_blob (att_coll_description, att_coll_description, X.RDB$DESCRIPTION);
			if (!X.RDB$FUNCTION_NAME.NULL)
				PUT_TEXT (att_coll_funct, X.RDB$FUNCTION_NAME);
			if (!X.RDB$BASE_COLLATION_NAME.NULL)
				PUT_TEXT(att_coll_base_collation_name, X.RDB$BASE_COLLATION_NAME);
			if (!X.RDB$SPECIFIC_ATTRIBUTES.NULL)
				put_source_blob (att_coll_specific_attr, att_coll_specific_attr, X.RDB$SPECIFIC_ATTRIBUTES);
			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$COLLATIONS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1
			put(tdgbl, rec_collation);
			PUT_TEXT (att_coll_name, X.RDB$COLLATION_NAME);
			put_numeric (att_coll_id, X.RDB$COLLATION_ID);
			put_numeric (att_coll_cs_id, X.RDB$CHARACTER_SET_ID);
			put_numeric (att_coll_attr, X.RDB$COLLATION_ATTRIBUTES);
			if (X.RDB$SYSTEM_FLAG)
				put_numeric (att_coll_sysflag, X.RDB$SYSTEM_FLAG);
			if (!X.RDB$DESCRIPTION.NULL)
				put_source_blob (att_coll_description, att_coll_description, X.RDB$DESCRIPTION);
			if (!X.RDB$FUNCTION_NAME.NULL)
				PUT_TEXT (att_coll_funct, X.RDB$FUNCTION_NAME);
			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	MISC_release_request_silent(req_handle1);
}

void write_database( const TEXT* dbb_file)
{
/**************************************
 *
 *	w r i t e _ d a t a b a s e
 *
 **************************************
 *
 * Functional description
 * 	write a physical database record and a
 *	logical database record in the burp file for
 *	the database itself.
 *
 **************************************/
	ISC_STATUS_ARRAY status_vector;
	SCHAR buffer[256];
	isc_req_handle req_handle1 = 0, req_handle2 = 0, req_handle3 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	put(tdgbl, (UCHAR) rec_physical_db);

	if (isc_database_info(status_vector, &DB, sizeof(db_info_items), db_info_items,
						  sizeof(buffer), buffer))
	{
		BURP_error_redirect(status_vector, 31);
		// msg 31 isc_database_info failed
	}

	USHORT page_size = 0, forced_writes, no_reserve, SQL_dialect, db_read_only;
 	ULONG sweep_interval, page_buffers;
 	USHORT length = 0;
	for (const SCHAR* d = buffer; *d != isc_info_end; d += length)
	{
		const UCHAR item = *d++;
		length = (USHORT) isc_vax_integer(d, 2);
		d += 2;
		switch (item)
		{
		case isc_info_end:
			break;

		case isc_info_page_size:
			page_size = (USHORT) isc_vax_integer(d, length);
			put_numeric(att_page_size, page_size);
			break;

		case isc_info_sweep_interval:
			sweep_interval = isc_vax_integer(d, length);
			put_numeric(att_sweep_interval, sweep_interval);
			break;

		case isc_info_forced_writes:
			forced_writes = (USHORT) isc_vax_integer(d, length);
			put_numeric(att_forced_writes, forced_writes);
			break;

		case isc_info_no_reserve:
			if (no_reserve = (USHORT) isc_vax_integer(d, length))
				put_numeric(att_no_reserve, no_reserve);
			break;

		case isc_info_set_page_buffers:
			if (page_buffers = isc_vax_integer(d, length))
				put_numeric(att_page_buffers, page_buffers);
			break;

		case isc_info_error:	// old server does not understand new isc_info
			break;				// parameter and returns isc_info_error. skip it

		case isc_info_db_sql_dialect:
			SQL_dialect = (USHORT) isc_vax_integer(d, length);
			put_numeric(att_SQL_dialect, SQL_dialect);
			break;

		case isc_info_db_read_only:
			if (db_read_only = (USHORT) isc_vax_integer(d, length))
				put_numeric(att_db_read_only, db_read_only);
			break;

		default:
			BURP_error_redirect(status_vector, 31);
			// msg 31 isc_database_info failed
			break;
		}
	}

	put_asciz(att_file_name, dbb_file);

	BURP_verbose(77, SafeArg() << dbb_file << page_size);
	// msg 77 database %s has a page size of %ld bytes.

	put(tdgbl, att_end);

	put(tdgbl, (UCHAR) rec_database);

	// if we have all capabilities, use the first request to get the
	// most performance out of the latest engine; if we don't
	// have one of the capabilities we must use the second set of
	// requests--this requires more code but it is well worth it
	// for the performance benefits, especially remotely--deej

	if ((tdgbl->BCK_capabilities & BCK_security) &&
		(tdgbl->BCK_capabilities & BCK_db_description) &&
		(tdgbl->BCK_capabilities & BCK_ods8))
	{
		FOR (REQUEST_HANDLE req_handle1)
			D IN RDB$DATABASE

			if (!D.RDB$SECURITY_CLASS.NULL)
				PUT_TEXT (att_database_security_class, D.RDB$SECURITY_CLASS);
			put_source_blob (att_database_description2, att_database_description, D.RDB$DESCRIPTION);
			if (!D.RDB$CHARACTER_SET_NAME.NULL)
				PUT_TEXT (att_database_dfl_charset, D.RDB$CHARACTER_SET_NAME);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		if (tdgbl->BCK_capabilities & BCK_security)
		{
			FOR (REQUEST_HANDLE req_handle1)
				D IN RDB$DATABASE
				if (!D.RDB$SECURITY_CLASS.NULL)
					PUT_TEXT (att_database_security_class, D.RDB$SECURITY_CLASS);
			END_FOR;
			ON_ERROR
				general_on_error();
			END_ERROR;
		}

		if (tdgbl->BCK_capabilities & BCK_db_description)
		{
			FOR (REQUEST_HANDLE req_handle2)
				D IN RDB$DATABASE
				put_source_blob (att_database_description2, att_database_description, D.RDB$DESCRIPTION);
			END_FOR;
			ON_ERROR
				general_on_error();
			END_ERROR;
		}

		if (tdgbl->BCK_capabilities & BCK_ods8)
		{
			FOR (REQUEST_HANDLE req_handle3)
				D IN RDB$DATABASE
				if (!D.RDB$CHARACTER_SET_NAME.NULL)
					PUT_TEXT (att_database_dfl_charset, D.RDB$CHARACTER_SET_NAME);
			END_FOR;
			ON_ERROR
				general_on_error();
			END_ERROR;
		}
	}

	MISC_release_request_silent(req_handle1);
	MISC_release_request_silent(req_handle2);
	MISC_release_request_silent(req_handle3);

	put(tdgbl, att_end);
}


void write_exceptions()
{
/**************************************
 *
 *	w r i t e _ e x c e p t i o n s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each exception.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$EXCEPTIONS
		WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1
		put(tdgbl, rec_exception);
		const SSHORT l = PUT_TEXT (att_exception_name, X.RDB$EXCEPTION_NAME);
		MISC_terminate (X.RDB$EXCEPTION_NAME, temp, l, sizeof(temp));
		BURP_verbose (198, temp);
		// msg 198 writing exception %s
		PUT_MESSAGE(att_exception_msg, att_exception_msg2, X.RDB$MESSAGE);
		put_source_blob (att_exception_description2, att_procedure_description, X.RDB$DESCRIPTION);
		put(tdgbl, att_end);
	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}


void write_field_dimensions()
{
/**************************************
 *
 *	w r i t e _ f i e l d _ d i m e n s i o n s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each array field dimension.
 *
 **************************************/
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$FIELD_DIMENSIONS
		put(tdgbl, rec_field_dimensions);
		PUT_TEXT (att_field_name, X.RDB$FIELD_NAME);
		put_numeric (att_field_dimensions, X.RDB$DIMENSION);
		put_numeric (att_field_range_low, X.RDB$LOWER_BOUND);
		put_numeric (att_field_range_high, X.RDB$UPPER_BOUND);
		put(tdgbl, att_end);
	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}


void write_filters()
{
/**************************************
 *
 *	w r i t e _ f i l t e r s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each filter.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$FILTERS
		WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1
		put(tdgbl, rec_filter);
		const SSHORT l = PUT_TEXT (att_filter_name, X.RDB$FUNCTION_NAME);
		MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
		BURP_verbose (145, temp);
		// msg 145 writing filter %s
		put_source_blob (att_filter_description2, att_filter_description, X.RDB$DESCRIPTION);
		PUT_TEXT (att_filter_module_name, X.RDB$MODULE_NAME);
		PUT_TEXT (att_filter_entrypoint, X.RDB$ENTRYPOINT);
		put_numeric (att_filter_input_sub_type, X.RDB$INPUT_SUB_TYPE);
		put_numeric (att_filter_output_sub_type, X.RDB$OUTPUT_SUB_TYPE);
		put(tdgbl, att_end);
	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}


void write_functions()
{
/**************************************
 *
 *	w r i t e _ f u n c t i o n s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each function.
 *
 **************************************/
	GDS_NAME func;
	TEXT temp[GDS_NAME_LEN];
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$FUNCTIONS WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1
		put(tdgbl, rec_function);
		const SSHORT l = PUT_TEXT (att_function_name, X.RDB$FUNCTION_NAME);
		MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
		BURP_verbose (147, temp);
		// msg 147 writing function %.*s
		put_source_blob (att_function_description2, att_function_description, X.RDB$DESCRIPTION);
		PUT_TEXT (att_function_module_name, X.RDB$MODULE_NAME);
		PUT_TEXT (att_function_entrypoint, X.RDB$ENTRYPOINT);
		put_numeric (att_function_return_arg, X.RDB$RETURN_ARGUMENT);
		put_numeric (att_function_type, X.RDB$FUNCTION_TYPE);
		PUT_TEXT (att_function_query_name, X.RDB$QUERY_NAME);
		put(tdgbl, att_end);
		COPY (X.RDB$FUNCTION_NAME, func);
		write_function_args (func);
		put(tdgbl, rec_function_end);
	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}


void write_function_args( GDS_NAME funcptr)
{
/**************************************
 *
 *	w r i t e _ f u n c t i o n _ a r g s
 *
 **************************************
 *
 * Functional description
 * 	write all arguments for a function.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	// if we have all capabilities, use the first request to get the
	// most performance out of the latest engine; if we don't
	// have one of the capabilities we must use the second set of
	// requests--this requires more code but it is well worth it
	// for the performance benefits, especially remotely--deej

	if (tdgbl->BCK_capabilities & BCK_ods10)
	{
		FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle1)
			X IN RDB$FUNCTION_ARGUMENTS WITH
			X.RDB$FUNCTION_NAME EQ funcptr

			put(tdgbl, rec_function_arg);
			const SSHORT l = PUT_TEXT (att_functionarg_name, X.RDB$FUNCTION_NAME);
			MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
			BURP_verbose (141, temp);
			// msg 141 writing argument for function %s
			put_numeric (att_functionarg_position, X.RDB$ARGUMENT_POSITION);
			put_numeric (att_functionarg_mechanism, X.RDB$MECHANISM);
			put_numeric (att_functionarg_field_type, X.RDB$FIELD_TYPE);
			put_numeric (att_functionarg_field_scale, X.RDB$FIELD_SCALE);
			put_numeric (att_functionarg_field_length, X.RDB$FIELD_LENGTH);
			put_numeric (att_functionarg_field_sub_type, X.RDB$FIELD_SUB_TYPE);
			if (!X.RDB$CHARACTER_SET_ID.NULL)
				put_numeric (att_functionarg_character_set, X.RDB$CHARACTER_SET_ID);

			if (!X.RDB$FIELD_PRECISION.NULL)
				put_numeric (att_functionarg_field_precision, X.RDB$FIELD_PRECISION);
			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle1)
			X IN RDB$FUNCTION_ARGUMENTS WITH
			X.RDB$FUNCTION_NAME EQ funcptr

			put(tdgbl, rec_function_arg);
			const SSHORT l = PUT_TEXT (att_functionarg_name, X.RDB$FUNCTION_NAME);
			MISC_terminate (X.RDB$FUNCTION_NAME, temp, l, sizeof(temp));
			BURP_verbose (141, temp);
			// msg 141 writing argument for function %s
			put_numeric (att_functionarg_position, X.RDB$ARGUMENT_POSITION);
			put_numeric (att_functionarg_mechanism, X.RDB$MECHANISM);
			put_numeric (att_functionarg_field_type, X.RDB$FIELD_TYPE);
			put_numeric (att_functionarg_field_scale, X.RDB$FIELD_SCALE);
			put_numeric (att_functionarg_field_length, X.RDB$FIELD_LENGTH);
			put_numeric (att_functionarg_field_sub_type, X.RDB$FIELD_SUB_TYPE);

			if (tdgbl->BCK_capabilities & BCK_ods8)
			{
				FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle2)
					X2 IN RDB$FUNCTION_ARGUMENTS WITH
					X2.RDB$FUNCTION_NAME EQ funcptr AND
					X2.RDB$ARGUMENT_POSITION = X.RDB$ARGUMENT_POSITION;

					if (!X2.RDB$CHARACTER_SET_ID.NULL)
						put_numeric (att_functionarg_character_set, X2.RDB$CHARACTER_SET_ID);
					// Note that BCK_ods10 canNOT be set if we're in this
					// "else" branch.  Hence there is no need to test that
					// bit and store the RDB$FIELD_PRECISION.

				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}
			put(tdgbl, att_end);

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
}


void write_generators()
{
/**************************************
 *
 *	w r i t e _ g e n e r a t o r s
 *
 **************************************
 *
 * Functional description
 *	Write any defined generators.
 *
 **************************************/
	isc_req_handle req_handle1 = 0;
	TEXT temp[GDS_NAME_LEN];

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->BCK_capabilities & BCK_ods11)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$GENERATORS
			WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1
			put(tdgbl, rec_generator);
			const SSHORT l = PUT_TEXT (att_gen_generator, X.RDB$GENERATOR_NAME);
			SINT64 value = 0;
			if (!tdgbl->gbl_sw_meta) {
				value = get_gen_id (X.RDB$GENERATOR_NAME, l);
				put_int64 (att_gen_value_int64, value);
			}
			if (!X.RDB$DESCRIPTION.NULL) {
				put_source_blob (att_gen_description, att_gen_description, X.RDB$DESCRIPTION);
			}
			put(tdgbl, att_end);
			MISC_terminate (X.RDB$GENERATOR_NAME, temp, l, sizeof(temp));
			BURP_verbose (165, SafeArg() << temp << value);
			// msg 165 writing generator %s value %ld
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$GENERATORS
			WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1
			put(tdgbl, rec_generator);
			const SSHORT l = PUT_TEXT (att_gen_generator, X.RDB$GENERATOR_NAME);
			SINT64 value = 0;
			if (!tdgbl->gbl_sw_meta) {
				value = get_gen_id (X.RDB$GENERATOR_NAME, l);
				put_int64 (att_gen_value_int64, value);
			}
			put(tdgbl, att_end);
			MISC_terminate (X.RDB$GENERATOR_NAME, temp, l, sizeof(temp));
			BURP_verbose (165, SafeArg() << temp << value);
			// msg 165 writing generator %s value %ld
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	MISC_release_request_silent(req_handle1);
}


void write_global_fields()
{
/**************************************
 *
 *	w r i t e _ g l o b a l _ f i e l d s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each global field.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	isc_req_handle req_handle1 = 0, req_handle2 = 0,
		req_handle3 = 0, req_handle4 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	// if we have all capabilities, use the first request to get the
	// most performance out of the latest engine; if we don't
	// have one of the capabilities we must use the second set of
	// requests--this requires more code but it is well worth it
	// for the performance benefits, especially remotely--deej

	if ((tdgbl->BCK_capabilities & BCK_attributes_v3) &&
		(tdgbl->BCK_capabilities & BCK_ods8) &&
		(tdgbl->BCK_capabilities & BCK_ods10))
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$FIELDS WITH
			X.RDB$SYSTEM_FLAG NE 1 OR
			X.RDB$SYSTEM_FLAG MISSING

			put(tdgbl, rec_global_field);
			const SSHORT l = PUT_TEXT (att_field_name, X.RDB$FIELD_NAME);
			MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp));
			BURP_verbose (149, temp);
			// msg 149  writing global field %.*s
			if (!X.RDB$QUERY_NAME.NULL && X.RDB$QUERY_NAME [0] != ' ')
				PUT_TEXT (att_field_query_name, X.RDB$QUERY_NAME);
			if (!X.RDB$EDIT_STRING.NULL && X.RDB$EDIT_STRING [0] != ' ')
				PUT_TEXT (att_field_edit_string, X.RDB$EDIT_STRING);
			put_source_blob (att_field_query_header, att_field_query_header, X.RDB$QUERY_HEADER);
			put_numeric (att_field_type, X.RDB$FIELD_TYPE);
			put_numeric (att_field_length, X.RDB$FIELD_LENGTH);
			put_numeric (att_field_sub_type, X.RDB$FIELD_SUB_TYPE);
			put_numeric (att_field_scale, X.RDB$FIELD_SCALE);
			put_blr_blob (att_field_missing_value, X.RDB$MISSING_VALUE);
			put_blr_blob (att_field_default_value, X.RDB$DEFAULT_VALUE);
			put_blr_blob (att_field_validation_blr, X.RDB$VALIDATION_BLR);
			put_source_blob(att_field_validation_source2, att_field_validation_source,
							X.RDB$VALIDATION_SOURCE);
			put_blr_blob (att_field_computed_blr, X.RDB$COMPUTED_BLR);
			put_source_blob(att_field_computed_source2, att_field_computed_source,
							X.RDB$COMPUTED_SOURCE);
			if (X.RDB$SEGMENT_LENGTH)
				put_numeric (att_field_segment_length, X.RDB$SEGMENT_LENGTH);
			if (X.RDB$SYSTEM_FLAG)
				put_numeric (att_field_system_flag, X.RDB$SYSTEM_FLAG);
			put_source_blob (att_field_description2, att_field_description, X.RDB$DESCRIPTION);

			if (X.RDB$EXTERNAL_LENGTH)
				put_numeric (att_field_external_length, X.RDB$EXTERNAL_LENGTH);
			if (X.RDB$EXTERNAL_TYPE)
				put_numeric (att_field_external_type, X.RDB$EXTERNAL_TYPE);
			if (X.RDB$EXTERNAL_SCALE)
				put_numeric (att_field_external_scale, X.RDB$EXTERNAL_SCALE);
			if (X.RDB$DIMENSIONS)
				put_numeric (att_field_dimensions, X.RDB$DIMENSIONS);
			if (!X.RDB$NULL_FLAG.NULL)
				put_numeric (att_field_null_flag, X.RDB$NULL_FLAG);
			if (!X.RDB$CHARACTER_LENGTH.NULL)
				put_numeric (att_field_character_length, X.RDB$CHARACTER_LENGTH);
			if (!X.RDB$DEFAULT_SOURCE.NULL)
				put_source_blob(att_field_default_source, att_field_default_source,
								X.RDB$DEFAULT_SOURCE);
			if (!X.RDB$MISSING_SOURCE.NULL)
				put_source_blob(att_field_missing_source, att_field_missing_source,
								X.RDB$MISSING_SOURCE);
			if (!X.RDB$CHARACTER_SET_ID.NULL)
				put_numeric (att_field_character_set, X.RDB$CHARACTER_SET_ID);
			if (!X.RDB$COLLATION_ID.NULL)
				put_numeric (att_field_collation_id, X.RDB$COLLATION_ID);

			if (!X.RDB$FIELD_PRECISION.NULL)
				put_numeric (att_field_precision, X.RDB$FIELD_PRECISION);

			put(tdgbl, att_end);

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$FIELDS WITH
			X.RDB$SYSTEM_FLAG NE 1 OR
			X.RDB$SYSTEM_FLAG MISSING

			put(tdgbl, rec_global_field);
			const SSHORT l = PUT_TEXT (att_field_name, X.RDB$FIELD_NAME);
			MISC_terminate (X.RDB$FIELD_NAME, temp, l, sizeof(temp));
			BURP_verbose (149, temp);
			// msg 149  writing global field %.*s
			if (!X.RDB$QUERY_NAME.NULL && X.RDB$QUERY_NAME [0] != ' ')
				PUT_TEXT (att_field_query_name, X.RDB$QUERY_NAME);
			if (!X.RDB$EDIT_STRING.NULL && X.RDB$EDIT_STRING [0] != ' ')
				PUT_TEXT (att_field_edit_string, X.RDB$EDIT_STRING);
			put_source_blob (att_field_query_header, att_field_query_header, X.RDB$QUERY_HEADER);
			put_numeric (att_field_type, X.RDB$FIELD_TYPE);
			put_numeric (att_field_length, X.RDB$FIELD_LENGTH);
			put_numeric (att_field_sub_type, X.RDB$FIELD_SUB_TYPE);
			put_numeric (att_field_scale, X.RDB$FIELD_SCALE);
			put_blr_blob (att_field_missing_value, X.RDB$MISSING_VALUE);
			put_blr_blob (att_field_default_value, X.RDB$DEFAULT_VALUE);
			put_blr_blob (att_field_validation_blr, X.RDB$VALIDATION_BLR);
			put_source_blob(att_field_validation_source2, att_field_validation_source,
							X.RDB$VALIDATION_SOURCE);
			put_blr_blob (att_field_computed_blr, X.RDB$COMPUTED_BLR);
			put_source_blob (att_field_computed_source2, att_field_computed_source,
				X.RDB$COMPUTED_SOURCE);
			if (X.RDB$SEGMENT_LENGTH)
				put_numeric (att_field_segment_length, X.RDB$SEGMENT_LENGTH);
			if (X.RDB$SYSTEM_FLAG)
				put_numeric (att_field_system_flag, X.RDB$SYSTEM_FLAG);
			put_source_blob (att_field_description2, att_field_description, X.RDB$DESCRIPTION);
			if (tdgbl->BCK_capabilities & BCK_attributes_v3)
			{
				FOR (REQUEST_HANDLE req_handle2)
					F IN RDB$FIELDS WITH F.RDB$FIELD_NAME = X.RDB$FIELD_NAME

					if (F.RDB$EXTERNAL_LENGTH)
						put_numeric (att_field_external_length, F.RDB$EXTERNAL_LENGTH);
					if (F.RDB$EXTERNAL_TYPE)
						put_numeric (att_field_external_type, F.RDB$EXTERNAL_TYPE);
					if (F.RDB$EXTERNAL_SCALE)
						put_numeric (att_field_external_scale, F.RDB$EXTERNAL_SCALE);
					if (F.RDB$DIMENSIONS)
						put_numeric (att_field_dimensions, F.RDB$DIMENSIONS);
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}
			if (tdgbl->BCK_capabilities & BCK_ods8)
			{
				FOR (REQUEST_HANDLE req_handle3)
					F IN RDB$FIELDS WITH F.RDB$FIELD_NAME = X.RDB$FIELD_NAME
					if (!F.RDB$NULL_FLAG.NULL)
						put_numeric (att_field_null_flag, F.RDB$NULL_FLAG);
					if (!F.RDB$CHARACTER_LENGTH.NULL)
						put_numeric (att_field_character_length, F.RDB$CHARACTER_LENGTH);
					if (!F.RDB$DEFAULT_SOURCE.NULL)
						put_source_blob(att_field_default_source, att_field_default_source,
										F.RDB$DEFAULT_SOURCE);
					if (!F.RDB$MISSING_SOURCE.NULL)
						put_source_blob(att_field_missing_source, att_field_missing_source,
										F.RDB$MISSING_SOURCE);
					if (!F.RDB$CHARACTER_SET_ID.NULL)
						put_numeric (att_field_character_set, F.RDB$CHARACTER_SET_ID);
					if (!F.RDB$COLLATION_ID.NULL)
						put_numeric (att_field_collation_id, F.RDB$COLLATION_ID);

					if (tdgbl->BCK_capabilities & BCK_ods10)
					{
						FOR (REQUEST_HANDLE req_handle4)
							K IN RDB$FIELDS WITH K.RDB$FIELD_NAME = X.RDB$FIELD_NAME
							if (!K.RDB$FIELD_PRECISION.NULL)
								put_numeric (att_field_precision, K.RDB$FIELD_PRECISION);
						END_FOR;
						ON_ERROR
							general_on_error();
						END_ERROR;
					}
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}
			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	MISC_release_request_silent(req_handle1);
	MISC_release_request_silent(req_handle2);
	MISC_release_request_silent(req_handle3);
	MISC_release_request_silent(req_handle4);
}


void write_procedures()
{
/**************************************
 *
 *	w r i t e _ p r o c e d u r e s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each stored procedure.
 *
 **************************************/
	GDS_NAME proc;
	TEXT temp[GDS_NAME_LEN];
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->BCK_capabilities & BCK_ods11_1)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$PROCEDURES
			WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1
			put(tdgbl, rec_procedure);
			const SSHORT l = PUT_TEXT (att_procedure_name, X.RDB$PROCEDURE_NAME);
			MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof(temp));
			BURP_verbose (193, temp);
			// msg 193 writing stored procedure %.*s
			put_numeric (att_procedure_inputs, X.RDB$PROCEDURE_INPUTS);
			put_numeric (att_procedure_outputs, X.RDB$PROCEDURE_OUTPUTS);
			put_source_blob(att_procedure_description2, att_procedure_description, X.RDB$DESCRIPTION);
			put_source_blob (att_procedure_source2, att_procedure_source, X.RDB$PROCEDURE_SOURCE);
			put_blr_blob (att_procedure_blr, X.RDB$PROCEDURE_BLR);
			if (!X.RDB$SECURITY_CLASS.NULL)
				PUT_TEXT (att_procedure_security_class, X.RDB$SECURITY_CLASS);
			if (!X.RDB$OWNER_NAME.NULL)
				PUT_TEXT (att_procedure_owner_name, X.RDB$OWNER_NAME);
			if (!X.RDB$PROCEDURE_TYPE.NULL)
				put_numeric (att_procedure_type, X.RDB$PROCEDURE_TYPE);
			if (!X.RDB$VALID_BLR.NULL)
				put_numeric (att_procedure_valid_blr, X.RDB$VALID_BLR);
			if (!X.RDB$DEBUG_INFO.NULL)
				put_blr_blob (att_procedure_debug_info, X.RDB$DEBUG_INFO);
			put(tdgbl, att_end);
			COPY(X.RDB$PROCEDURE_NAME, proc);
			write_procedure_prms (proc);
			put(tdgbl, rec_procedure_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else {
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$PROCEDURES
			WITH X.RDB$SYSTEM_FLAG MISSING OR X.RDB$SYSTEM_FLAG NE 1
			put(tdgbl, rec_procedure);
			const SSHORT l = PUT_TEXT (att_procedure_name, X.RDB$PROCEDURE_NAME);
			MISC_terminate (X.RDB$PROCEDURE_NAME, temp, l, sizeof(temp));
			BURP_verbose (193, temp);
			// msg 193 writing stored procedure %.*s
			put_numeric (att_procedure_inputs, X.RDB$PROCEDURE_INPUTS);
			put_numeric (att_procedure_outputs, X.RDB$PROCEDURE_OUTPUTS);
			put_source_blob (att_procedure_description2, att_procedure_description, X.RDB$DESCRIPTION);
			put_source_blob (att_procedure_source2, att_procedure_source, X.RDB$PROCEDURE_SOURCE);
			put_blr_blob (att_procedure_blr, X.RDB$PROCEDURE_BLR);
			if (!X.RDB$SECURITY_CLASS.NULL)
				PUT_TEXT (att_procedure_security_class, X.RDB$SECURITY_CLASS);
			if (!X.RDB$SECURITY_CLASS.NULL)
				PUT_TEXT (att_procedure_owner_name, X.RDB$OWNER_NAME);
			put(tdgbl, att_end);
			COPY(X.RDB$PROCEDURE_NAME, proc);
			write_procedure_prms (proc);
			put(tdgbl, rec_procedure_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	MISC_release_request_silent(req_handle1);
}


void write_procedure_prms( GDS_NAME procptr)
{
/**************************************
 *
 *	w r i t e _ p r o c e d u r e _ p r m s
 *
 **************************************
 *
 * Functional description
 * 	write all parameters of a stored procedure.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->BCK_capabilities & BCK_ods11_1)
	{
		FOR (REQUEST_HANDLE tdgbl->handles_write_procedure_prms_req_handle1)
			X IN RDB$PROCEDURE_PARAMETERS WITH X.RDB$PROCEDURE_NAME EQ procptr
			put(tdgbl, rec_procedure_prm);
			const SSHORT l = PUT_TEXT (att_procedureprm_name, X.RDB$PARAMETER_NAME);
			MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof(temp));
			BURP_verbose (194, temp);
			// msg 194 writing parameter %s for stored procedure
			put_numeric (att_procedureprm_number, X.RDB$PARAMETER_NUMBER);
			put_numeric (att_procedureprm_type, X.RDB$PARAMETER_type);
			PUT_TEXT (att_procedureprm_field_source, X.RDB$FIELD_SOURCE);
			put_source_blob(att_procedureprm_description2, att_procedureprm_description,
							X.RDB$DESCRIPTION);
			put_blr_blob (att_procedureprm_default_value, X.RDB$DEFAULT_VALUE);
			put_source_blob(att_procedureprm_default_source, att_procedureprm_default_source,
							X.RDB$DEFAULT_SOURCE);
			if (!X.RDB$COLLATION_ID.NULL)
				put_numeric (att_procedureprm_collation_id, X.RDB$COLLATION_ID);
			if (!X.RDB$NULL_FLAG.NULL)
				put_numeric (att_procedureprm_null_flag, X.RDB$NULL_FLAG);
			if (!X.RDB$PARAMETER_MECHANISM.NULL)
				put_numeric (att_procedureprm_mechanism, X.RDB$PARAMETER_MECHANISM);

			// BCK_ods11_2
			if (!X.RDB$FIELD_NAME.NULL)
				PUT_TEXT(att_procedureprm_field_name, X.RDB$FIELD_NAME);
			if (!X.RDB$RELATION_NAME.NULL)
				PUT_TEXT(att_procedureprm_relation_name, X.RDB$RELATION_NAME);

			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		FOR (REQUEST_HANDLE tdgbl->handles_write_procedure_prms_req_handle1)
			X IN RDB$PROCEDURE_PARAMETERS WITH X.RDB$PROCEDURE_NAME EQ procptr
			put(tdgbl, rec_procedure_prm);
			const SSHORT l = PUT_TEXT (att_procedureprm_name, X.RDB$PARAMETER_NAME);
			MISC_terminate (X.RDB$PARAMETER_NAME, temp, l, sizeof(temp));
			BURP_verbose (194, temp);
			// msg 194 writing parameter %s for stored procedure
			put_numeric (att_procedureprm_number, X.RDB$PARAMETER_NUMBER);
			put_numeric (att_procedureprm_type, X.RDB$PARAMETER_type);
			PUT_TEXT (att_procedureprm_field_source, X.RDB$FIELD_SOURCE);
			put_source_blob(att_procedureprm_description2, att_procedureprm_description,
							X.RDB$DESCRIPTION);
			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
}


void write_ref_constraints()
{
/**************************************
 *
 *	w r i t e _ r e f _ c o n s t r a i n t s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each referential constraint.
 *
 **************************************/
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$REF_CONSTRAINTS
		put(tdgbl, rec_ref_constraint);
		PUT_TEXT (att_ref_constraint_name, X.RDB$CONSTRAINT_NAME);
		PUT_TEXT (att_ref_unique_const_name, X.RDB$CONST_NAME_UQ);
		PUT_TEXT (att_ref_match_option, X.RDB$MATCH_OPTION);
		PUT_TEXT (att_ref_update_rule, X.RDB$UPDATE_RULE);
		PUT_TEXT (att_ref_delete_rule, X.RDB$DELETE_RULE);
		put(tdgbl, att_end);
	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}

void write_rel_constraints()
{
/**************************************
 *
 *	w r i t e _ r e l _ c o n s t r a i n t s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each relation constraint.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$RELATION_CONSTRAINTS
		put(tdgbl, rec_rel_constraint);
		const SSHORT l = PUT_TEXT (att_rel_constraint_name, X.RDB$CONSTRAINT_NAME);
		MISC_terminate (X.RDB$CONSTRAINT_NAME, temp, l, sizeof(temp));
		BURP_verbose (207, temp);
		// msg 207 writing constraint %s
		PUT_TEXT (att_rel_constraint_type, X.RDB$CONSTRAINT_TYPE);
		PUT_TEXT (att_rel_constraint_rel_name, X.RDB$RELATION_NAME);
		PUT_TEXT (att_rel_constraint_defer, X.RDB$DEFERRABLE);
		PUT_TEXT (att_rel_constraint_init, X.RDB$INITIALLY_DEFERRED);
		if (!X.RDB$INDEX_NAME.NULL)
			PUT_TEXT (att_rel_constraint_index, X.RDB$INDEX_NAME);
		put(tdgbl, att_end);
	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}

void write_relations()
{
/**************************************
 *
 *	w r i t e _ r e l a t i o n s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each relation.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	isc_req_handle req_handle1 = 0, req_handle2 = 0,
		req_handle3 = 0, req_handle4 = 0, req_handle5 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	// if we have all capabilities, use the first request to get the
	// most performance out of the latest engine; if we don't
	// have one of the capabilities we must use the second set of
	// requests--this requires more code but it is well worth it
	// for the performance benefits, especially remotely--deej

	if ((tdgbl->BCK_capabilities & BCK_ods8) &&
		(tdgbl->BCK_capabilities & BCK_security) &&
		(tdgbl->BCK_capabilities & BCK_attributes_v3) &&
		(tdgbl->BCK_capabilities & BCK_ods11_1))
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$RELATIONS WITH X.RDB$SYSTEM_FLAG NE 1 OR
			X.RDB$SYSTEM_FLAG MISSING

			SSHORT flags = 0;
			put(tdgbl, rec_relation);
			const SSHORT l = PUT_TEXT (att_relation_name, X.RDB$RELATION_NAME);
			MISC_terminate (X.RDB$RELATION_NAME, temp, l, sizeof(temp));
			BURP_verbose (153, temp);
			// msg 153 writing relation %.*s

			// RDB$VIEW_BLR must be the first blob field in the backup file.
			// RESTORE.EPP makes this assumption in get_relation().

			if (put_blr_blob (att_relation_view_blr, X.RDB$VIEW_BLR))
				flags |= REL_view;
			if (X.RDB$SYSTEM_FLAG)
				put_numeric (att_relation_system_flag, X.RDB$SYSTEM_FLAG);
			if (!X.RDB$FLAGS.NULL)
				put_numeric (att_relation_flags, X.RDB$FLAGS);
			if (!X.RDB$SECURITY_CLASS.NULL)
				PUT_TEXT (att_relation_security_class, X.RDB$SECURITY_CLASS);

			put_source_blob (att_relation_description2, att_relation_description,
				X.RDB$DESCRIPTION);
			put_source_blob (att_relation_view_source2, att_relation_view_source, X.RDB$VIEW_SOURCE);

			put_source_blob(att_relation_ext_description2, att_relation_ext_description,
							X.RDB$EXTERNAL_DESCRIPTION);
			PUT_TEXT (att_relation_owner_name, X.RDB$OWNER_NAME);
			if (!X.RDB$EXTERNAL_FILE.NULL)
			{
				if (!tdgbl->gbl_sw_convert_ext_tables)
				{
					PUT_TEXT(att_relation_ext_file_name, X.RDB$EXTERNAL_FILE);
					flags |= REL_external;
				}
			}

			if (!X.RDB$RELATION_TYPE.NULL)
				put_numeric (att_relation_type, X.RDB$RELATION_TYPE);

			put(tdgbl, att_end);
			burp_rel* relation = (burp_rel*) BURP_alloc_zero (sizeof(burp_rel));
			relation->rel_next = tdgbl->relations;
			tdgbl->relations = relation;
			relation->rel_id = X.RDB$RELATION_ID;
			relation->rel_name_length = COPY(X.RDB$RELATION_NAME, relation->rel_name);
			relation->rel_flags |= flags;
			put_relation (relation);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$RELATIONS WITH X.RDB$SYSTEM_FLAG NE 1 OR
			X.RDB$SYSTEM_FLAG MISSING

			SSHORT flags = 0;
			put(tdgbl, rec_relation);
			const SSHORT l = PUT_TEXT(att_relation_name, X.RDB$RELATION_NAME);
			MISC_terminate (X.RDB$RELATION_NAME, temp, l, sizeof(temp));
			BURP_verbose (153, temp);
			// msg 153 writing relation %.*s

			// RDB$VIEW_BLR must be the first blob field in the backup file.
			// RESTORE.EPP makes this assumption in get_relation().

			if (put_blr_blob (att_relation_view_blr, X.RDB$VIEW_BLR))
				flags |= REL_view;
			if (X.RDB$SYSTEM_FLAG)
				put_numeric (att_relation_system_flag, X.RDB$SYSTEM_FLAG);
			if (tdgbl->BCK_capabilities & BCK_ods8)
			{
				FOR (REQUEST_HANDLE req_handle2)
					R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME = X.RDB$RELATION_NAME
					if (!R.RDB$FLAGS.NULL)
						put_numeric (att_relation_flags, R.RDB$FLAGS);
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}
			if (tdgbl->BCK_capabilities & BCK_security)
			{
				FOR (REQUEST_HANDLE req_handle3)
					R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME = X.RDB$RELATION_NAME
					if (!R.RDB$SECURITY_CLASS.NULL)
						PUT_TEXT(att_relation_security_class, R.RDB$SECURITY_CLASS);
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}
			put_source_blob (att_relation_description2, att_relation_description, X.RDB$DESCRIPTION);
			put_source_blob (att_relation_view_source2, att_relation_view_source, X.RDB$VIEW_SOURCE);
			if (tdgbl->BCK_capabilities & BCK_attributes_v3)
			{
				FOR (REQUEST_HANDLE req_handle4)
					R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME = X.RDB$RELATION_NAME
					put_source_blob(att_relation_ext_description2, att_relation_ext_description,
									R.RDB$EXTERNAL_DESCRIPTION);
					PUT_TEXT(att_relation_owner_name, R.RDB$OWNER_NAME);
					if (!R.RDB$EXTERNAL_FILE.NULL)
					{
						if (!tdgbl->gbl_sw_convert_ext_tables)
						{
							PUT_TEXT(att_relation_ext_file_name, R.RDB$EXTERNAL_FILE);
							flags |= REL_external;
						}
					}
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}
			if (tdgbl->BCK_capabilities & BCK_ods11_1)
			{
				FOR (REQUEST_HANDLE req_handle5)
					R IN RDB$RELATIONS WITH R.RDB$RELATION_NAME = X.RDB$RELATION_NAME
					if (!R.RDB$RELATION_TYPE.NULL)
						put_numeric (att_relation_type, R.RDB$RELATION_TYPE);
				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}
			put(tdgbl, att_end);
			burp_rel* relation = (burp_rel*) BURP_alloc_zero (sizeof(burp_rel));
			relation->rel_next = tdgbl->relations;
			tdgbl->relations = relation;
			relation->rel_id = X.RDB$RELATION_ID;
			relation->rel_name_length = COPY(X.RDB$RELATION_NAME, relation->rel_name);
			relation->rel_flags |= flags;
			put_relation (relation);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	MISC_release_request_silent(req_handle1);
	MISC_release_request_silent(req_handle2);
	MISC_release_request_silent(req_handle3);
	MISC_release_request_silent(req_handle4);
	MISC_release_request_silent(req_handle5);
}


void write_shadow_files()
{
/**************************************
 *
 *	w r i t e _ s h a d o w _ f i l e s
 *
 **************************************
 *
 * Functional description
 *	Write out files to use as shadows.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$FILES
		WITH X.RDB$SHADOW_NUMBER NOT MISSING
		AND X.RDB$SHADOW_NUMBER NE 0
		put(tdgbl, rec_files);
		const SSHORT l = PUT_TEXT (att_file_filename, X.RDB$FILE_NAME);
		MISC_terminate (X.RDB$FILE_NAME, temp, l, sizeof(temp));
		BURP_verbose (163, temp);
		// msg 163 writing shadow file %s
		put_numeric (att_file_sequence, X.RDB$FILE_SEQUENCE);
		put_numeric (att_file_start, X.RDB$FILE_START);
		put_numeric (att_file_length, X.RDB$FILE_LENGTH);
		put_numeric (att_file_flags, X.RDB$FILE_FLAGS);
		put_numeric (att_shadow_number, X.RDB$SHADOW_NUMBER);
		put(tdgbl, att_end);
	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}

void write_sql_roles()
{
/**************************************
 *
 *	w r i t e _ s q l _ r o l e s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each SQL roles.
 *
 **************************************/
	isc_req_handle req_handle1 = 0;
	TEXT temp[GDS_NAME_LEN];

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->BCK_capabilities & BCK_ods11)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$ROLES
			WITH X.RDB$SYSTEM_FLAG EQ 0 OR X.RDB$SYSTEM_FLAG MISSING

			put(tdgbl, rec_sql_roles);
			const SSHORT l = PUT_TEXT(att_role_name, X.RDB$ROLE_NAME);
			PUT_TEXT (att_role_owner_name, X.RDB$OWNER_NAME);
			if (!X.RDB$DESCRIPTION.NULL) {
				put_source_blob (att_role_description, att_role_description, X.RDB$DESCRIPTION);
			}
			put(tdgbl, att_end);
			MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp));
			BURP_verbose (249, temp);
			// msg 249 writing SQL role: %s

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$ROLES

			put(tdgbl, rec_sql_roles);
			const SSHORT l = PUT_TEXT(att_role_name, X.RDB$ROLE_NAME);
			PUT_TEXT (att_role_owner_name, X.RDB$OWNER_NAME);
			put(tdgbl, att_end);
			MISC_terminate (X.RDB$ROLE_NAME, temp, l, sizeof(temp));
			BURP_verbose (249, temp);
			// msg 249 writing SQL role: %s

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	MISC_release_request_silent(req_handle1);
}

void write_mapping()
{
/**************************************
 *
 *	w r i t e _ m a p p i n g
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each names mapping.
 *
 **************************************/
	isc_req_handle req_handle = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->BCK_capabilities & BCK_ods11_2)
	{
		FOR (REQUEST_HANDLE req_handle)
			X IN RDB$ROLES
			WITH X.RDB$ROLE_NAME EQ ADMIN_ROLE

			if (X.RDB$SYSTEM_FLAG == (ROLE_FLAG_MAY_TRUST | ROLE_FLAG_DBO))
			{
				put(tdgbl, rec_mapping);
				//put_text(att_map_os, DOMAIN-ADMINS, strlen(DOMAIN-ADMINS) + 1);
				//put_text(att_map_role, ADMIN-ROLE, strlen(ADMIN-ROLE) + 1);
				put_text(att_auto_map_role, ADMIN_ROLE, strlen(ADMIN_ROLE) + 1);
				put(tdgbl, att_end);
				BURP_verbose (297, ADMIN_ROLE);
				// msg 297 writing mapping for @1
			}

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	MISC_release_request_silent(req_handle);
}

void write_triggers()
{
/**************************************
 *
 *	w r i t e _ t r i g g e r s
 *
 **************************************
 *
 * Functional description
 * 	write the triggers in rdb$triggers
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	isc_req_handle req_handle1 = 0, req_handle2 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	// if we have all capabilities, use the first request to get the
	// most performance out of the latest engine; if we don't
	// have one of the capabilities we must use the second set of
	// requests--this requires more code but it is well worth it
	// for the performance benefits, especially remotely--deej

	if (tdgbl->BCK_capabilities & BCK_ods11_1)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$TRIGGERS WITH
			X.RDB$SYSTEM_FLAG NE 1 OR
			X.RDB$SYSTEM_FLAG MISSING

			put(tdgbl, rec_trigger);
			const SSHORT l = PUT_TEXT (att_trig_name, X.RDB$TRIGGER_NAME);
			MISC_terminate (X.RDB$TRIGGER_NAME, temp, l, sizeof(temp));
			BURP_verbose (156, temp);
			// msg 156   writing trigger %s

			if (!X.RDB$RELATION_NAME.NULL)
				PUT_TEXT (att_trig_relation_name, X.RDB$RELATION_NAME);

			put_numeric (att_trig_sequence, X.RDB$TRIGGER_SEQUENCE);
			put_numeric (att_trig_type, X.RDB$TRIGGER_TYPE);
			put_blr_blob (att_trig_blr, X.RDB$TRIGGER_BLR);
			put_source_blob (att_trig_source2, att_trig_source, X.RDB$TRIGGER_SOURCE);
			put_source_blob (att_trig_description2, att_trig_description, X.RDB$DESCRIPTION);
			put_numeric (att_trig_system_flag, X.RDB$SYSTEM_FLAG);
			put_numeric (att_trig_inactive, X.RDB$TRIGGER_INACTIVE);

			if (!X.RDB$FLAGS.NULL)
				put_numeric (att_trig_flags, X.RDB$FLAGS);

			if (!X.RDB$VALID_BLR.NULL)
				put_numeric (att_trig_valid_blr, X.RDB$VALID_BLR);

			if (!X.RDB$DEBUG_INFO.NULL)
				put_blr_blob (att_trig_debug_info, X.RDB$DEBUG_INFO);

			put(tdgbl, att_end);

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else if (tdgbl->BCK_capabilities & BCK_ods8)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$TRIGGERS WITH
			X.RDB$SYSTEM_FLAG NE 1 OR
			X.RDB$SYSTEM_FLAG MISSING

			put(tdgbl, rec_trigger);
			const SSHORT l = PUT_TEXT (att_trig_name, X.RDB$TRIGGER_NAME);
			MISC_terminate (X.RDB$TRIGGER_NAME, temp, l, sizeof(temp));
			BURP_verbose (156, temp);
			// msg 156   writing trigger %s

			if (!X.RDB$RELATION_NAME.NULL)
				PUT_TEXT (att_trig_relation_name, X.RDB$RELATION_NAME);

			put_numeric (att_trig_sequence, X.RDB$TRIGGER_SEQUENCE);
			put_numeric (att_trig_type, X.RDB$TRIGGER_TYPE);
			put_blr_blob (att_trig_blr, X.RDB$TRIGGER_BLR);
			put_source_blob (att_trig_source2, att_trig_source, X.RDB$TRIGGER_SOURCE);
			put_source_blob (att_trig_description2, att_trig_description, X.RDB$DESCRIPTION);
			put_numeric (att_trig_system_flag, X.RDB$SYSTEM_FLAG);
			put_numeric (att_trig_inactive, X.RDB$TRIGGER_INACTIVE);

			if (!X.RDB$FLAGS.NULL)
				put_numeric (att_trig_flags, X.RDB$FLAGS);

			put(tdgbl, att_end);

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$TRIGGERS WITH
			X.RDB$SYSTEM_FLAG NE 1 OR
			X.RDB$SYSTEM_FLAG MISSING

			put(tdgbl, rec_trigger);
			const SSHORT l = PUT_TEXT (att_trig_name, X.RDB$TRIGGER_NAME);
			MISC_terminate (X.RDB$TRIGGER_NAME, temp, l, sizeof(temp));
			BURP_verbose (156, temp);
			// msg 156   writing trigger %s

			if (!X.RDB$RELATION_NAME.NULL)
				PUT_TEXT (att_trig_relation_name, X.RDB$RELATION_NAME);

			put_numeric (att_trig_sequence, X.RDB$TRIGGER_SEQUENCE);
			put_numeric (att_trig_type, X.RDB$TRIGGER_TYPE);
			put_blr_blob (att_trig_blr, X.RDB$TRIGGER_BLR);
			put_source_blob (att_trig_source2, att_trig_source, X.RDB$TRIGGER_SOURCE);
			put_source_blob (att_trig_description2, att_trig_description, X.RDB$DESCRIPTION);
			put_numeric (att_trig_system_flag, X.RDB$SYSTEM_FLAG);
			put_numeric (att_trig_inactive, X.RDB$TRIGGER_INACTIVE);

			if (tdgbl->BCK_capabilities & BCK_ods8)
			{
				FOR (REQUEST_HANDLE req_handle2)
					Y IN RDB$TRIGGERS WITH
					X.RDB$TRIGGER_NAME = Y.RDB$TRIGGER_NAME

					if (!Y.RDB$FLAGS.NULL)
						put_numeric (att_trig_flags, Y.RDB$FLAGS);

				END_FOR;
				ON_ERROR
					general_on_error();
				END_ERROR;
			}

			put(tdgbl, att_end);

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	MISC_release_request_silent(req_handle1);
	MISC_release_request_silent(req_handle2);
}


void write_trigger_messages()
{
/**************************************
 *
 *	w r i t e _ t r i g g e r _ m e s s a g e s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each trigger message.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		T IN RDB$TRIGGERS CROSS X IN RDB$TRIGGER_MESSAGES
		OVER RDB$TRIGGER_NAME
		WITH T.RDB$SYSTEM_FLAG NE 1 OR T.RDB$SYSTEM_FLAG MISSING;

		put(tdgbl, rec_trigger_message);
		const SSHORT l = PUT_TEXT (att_trigmsg_name, X.RDB$TRIGGER_NAME);
		MISC_terminate (X.RDB$TRIGGER_NAME, temp, l, sizeof(temp));
		BURP_verbose (157, temp);
		// msg 157 writing trigger message for *s
		put_numeric (att_trigmsg_number, X.RDB$MESSAGE_NUMBER);
		PUT_MESSAGE (att_trigmsg_text, att_end, X.RDB$MESSAGE);
		put(tdgbl, att_end);
	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}

void write_types()
{
/**************************************
 *
 *	w r i t e _ t y p e s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each type.
 *
 **************************************/
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$TYPES WITH X.RDB$SYSTEM_FLAG NE 1 OR
		X.RDB$SYSTEM_FLAG MISSING
		put(tdgbl, rec_system_type);
		PUT_TEXT (att_type_name, X.RDB$TYPE_NAME);
		PUT_TEXT (att_type_field_name, X.RDB$FIELD_NAME);
		BURP_verbose (160, SafeArg() << X.RDB$TYPE_NAME << X.RDB$FIELD_NAME);
		// msg 160 writing type %s for field %s
		put_numeric (att_type_type, X.RDB$TYPE);
		put_source_blob (att_type_description2, att_type_description, X.RDB$DESCRIPTION);
		if (X.RDB$SYSTEM_FLAG)
			put_numeric (att_type_system_flag, X.RDB$SYSTEM_FLAG);
		put(tdgbl, att_end);
	END_FOR;
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}


void write_user_privileges()
{
/**************************************
 *
 *	w r i t e _ u s e r _ p r i v i l e g e s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each user privilege.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	isc_req_handle req_handle1 = 0;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->BCK_capabilities & BCK_ods8)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$USER_PRIVILEGES
			put(tdgbl, rec_user_privilege);
			const SSHORT l = PUT_TEXT (att_priv_user, X.RDB$USER);
			MISC_terminate (X.RDB$USER, temp, l, sizeof(temp));
			BURP_verbose (152, temp);
			// msg 152 writing privilege for user %s
			PUT_TEXT (att_priv_grantor, X.RDB$GRANTOR);
			PUT_TEXT (att_priv_privilege, X.RDB$PRIVILEGE);
			put_numeric (att_priv_grant_option, X.RDB$GRANT_OPTION);
			PUT_TEXT (att_priv_object_name, X.RDB$RELATION_NAME);
			if (!X.RDB$FIELD_NAME.NULL)
			   PUT_TEXT (att_priv_field_name, X.RDB$FIELD_NAME);
			put_numeric (att_priv_user_type, X.RDB$USER_TYPE);
			put_numeric (att_priv_obj_type, X.RDB$OBJECT_TYPE);
			put(tdgbl, att_end);
		END_FOR
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$USER_PRIVILEGES
			put(tdgbl, rec_user_privilege);
			const SSHORT l = PUT_TEXT (att_priv_user, X.RDB$USER);
			MISC_terminate (X.RDB$USER, temp, l, sizeof(temp));
			BURP_verbose (152, temp);
			// msg 152 writing privilege for user %s
			PUT_TEXT (att_priv_grantor, X.RDB$GRANTOR);
			PUT_TEXT (att_priv_privilege, X.RDB$PRIVILEGE);
			put_numeric (att_priv_grant_option, X.RDB$GRANT_OPTION);
			PUT_TEXT (att_priv_object_name, X.RDB$RELATION_NAME);
			if (!X.RDB$FIELD_NAME.NULL)
				PUT_TEXT (att_priv_field_name, X.RDB$FIELD_NAME);
			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	MISC_release_request_silent(req_handle1);
}

} // namespace

