/*
 *	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 "../common/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 "../common/prett_proto.h"
#endif

#include "../common/classes/BlobWrapper.h"
#include "../common/classes/MsgPrint.h"
#include "../burp/OdsDetection.h"

using MsgFormat::SafeArg;
using Firebird::FbLocalStatus;


// 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.

#define DB			tdgbl->db_handle
#define fbTrans		tdgbl->tr_handle
#define gds_trans	tdgbl->tr_handle
#define fbStatus	(&tdgbl->status_vector)
#define isc_status	(&tdgbl->status_vector)
#define gds_status	(&tdgbl->status_vector)

// unused
#define fbProvider
#define fbStatus2

DATABASE DB = STATIC FILENAME "yachts.lnk" RUNTIME * dbb_file;

namespace // unnamed, private
{

#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)
{
	tdgbl->put(c);
}

inline void put(BurpGlobals* tdgbl, const att_type c)
{
	put(tdgbl, UCHAR(c));
}

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_int32(att_type, SLONG);
void put_int64(att_type attribute, SINT64 value);
void put_boolean(att_type, FB_BOOLEAN 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 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(const GDS_NAME, GDS_NAME);
void write_global_fields();
void write_generators();
void write_sql_roles();
void write_mapping();
void write_db_creators();
void write_packages();
void write_procedures();
void write_procedure_prms(const GDS_NAME, const GDS_NAME);
void write_publications();
void write_pub_tables();
void write_ref_constraints();
void write_rel_constraints();
void write_relations();
void write_secclasses();
void write_shadow_files();
void write_triggers();
void write_trigger_messages();
void write_types();
void write_user_privileges();
void general_on_error();


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


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 UCHAR 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,
	fb_info_replica_mode,
	isc_info_end
};
const UCHAR limbo_tpb[] =
{
	isc_tpb_version1,
	isc_tpb_ignore_limbo
};
const UCHAR 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.
 *
 **************************************/
	FbLocalStatus status_vector;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	tdgbl->gbl_database_file_name = dbb_file;

	gds_trans = 0;

	BURP_verbose(130);
	// msg 130 starting transaction

	if (tdgbl->gbl_sw_ignore_limbo)
	{
		gds_trans = DB->startTransaction(&status_vector, sizeof(limbo_nau_tpb), limbo_nau_tpb);
		if (status_vector->getState() & Firebird::IStatus::STATE_ERRORS)
		{
			gds_trans = DB->startTransaction(&status_vector, sizeof(limbo_tpb), limbo_tpb);
		}
	}
	else
	{
		EXEC SQL SET TRANSACTION NO_AUTO_UNDO;
		if (isc_status->getState() & Firebird::IStatus::STATE_ERRORS)
			EXEC SQL SET TRANSACTION;
	}

	if (!gds_trans)
	{
		EXEC SQL SET TRANSACTION NAME gds_trans NO_AUTO_UNDO;
		if (isc_status->getState() & Firebird::IStatus::STATE_ERRORS)
			EXEC SQL SET TRANSACTION NAME gds_trans;
	}


	// decide what type of database we've got

	detectRuntimeODS();
	if (tdgbl->runtimeODS < DB_VERSION_OLDEST_SUPPORTED)
	{
		BURP_error(348, true, SafeArg() << tdgbl->runtimeODS);
		// msg 348 database format @1 is too old to backup
	}

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

	// Write database record

	write_database(dbb_file);

	// Write global fields

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

	write_field_dimensions();

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

	// Write Character Sets
	BURP_verbose(msgVerbose_write_charsets);
	write_character_sets();

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

	// Write relations

	BURP_verbose(154);
	// msg 154 writing tables

	write_relations();

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

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

	// Write functions
	BURP_verbose(148);
	// msg 148 writing functions
	write_functions();

	// Write procedures
	BURP_verbose(192);
	// msg 192 writing stored procedures
	write_procedures();

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		// Write packages
		BURP_verbose(336);	// msg 336 writing packages
		write_packages();
	}

	// 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 || tdgbl->skipRelation(relation->rel_name)))
				put_data(relation);
		}

		put(tdgbl, (UCHAR) rec_relation_end);
	}

	// now for the new triggers in rdb$triggers
	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

	BURP_verbose(347); // msg 347 writing security classes
	write_secclasses();

	// 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->runtimeODS >= DB_VERSION_DDL9)
	{
		// Write SQL roles
		BURP_verbose(248);
		// msg 248 writing SQL roles
		write_sql_roles();
	}

	if (tdgbl->runtimeODS >= DB_VERSION_DDL11)
	{
		// Write names mapping
		BURP_verbose(296);
		// msg 296 writing mapping
		write_mapping();
	}

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		// Write database creators
		write_db_creators();
	}

	if (tdgbl->runtimeODS >= DB_VERSION_DDL13)
	{
		// Write publications
		BURP_verbose(396);	// msg 396 writing publications

		write_publications();
		write_pub_tables();
	}

	// Finish up

	put(tdgbl, (UCHAR) rec_end);
	FB_UINT64 cumul_count = MVOL_fini_write();
	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;

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

	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++)
			;
		ULONG 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_abort(isc_status);
}


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->runtimeODS >= DB_VERSION_DDL12)
	{
		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;
			}

			// ODS 12
			if (!X.RDB$GENERATOR_NAME.NULL)
			{
				COPY(X.RDB$GENERATOR_NAME, field->fld_generator);
				field->fld_identity_type = X.RDB$IDENTITY_TYPE;
			}

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

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

		Firebird::string nm, sql;
		nm.assign(name, name_len);
		BURP_makeSymbol(tdgbl, nm);
		sql = "select first(1) gen_id(" + nm + ", 0) from rdb$database";

		BurpSql getGenerator(tdgbl, sql.c_str());
		FB_MESSAGE(GetGen, Firebird::ThrowWrapper, (FB_BIGINT, id));
		GetGen getGen(&tdgbl->throwStatus, Firebird::MasterInterfacePtr());

		getGenerator.singleSelect(tdgbl->tr_handle, &getGen);
		return getGen->id;
	}
	catch (const Firebird::FbException& ex)
	{
		Firebird::IStatus* st = ex.getStatus();

		// if there's no gen_id, never mind ...
		if (st->getErrors()[1] == isc_dsql_error)
			return 0;

		BURP_error_redirect(st, 25);
		// msg 25 Failed in put_blr_gen_id
	}
	return 0;	// warning silencer
}


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:
	case blr_int128:
		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;
	}

	FbLocalStatus status_vector;
	unsigned return_length = DB->getSlice(&status_vector, gds_trans, blob_id, blr_length, blr_buffer,
		0, nullptr,		// parameters for subset of an array handling
		slice_length, slice);

	if (!status_vector.isSuccess())
	{
		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_int32(att_blob_field_number, field->fld_number);
	put_int32(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_int32(att_array_range_low, (int) range[0]);
		if (return_length == slice_length)
			put_int32(att_array_range_high, (int) range[1]);
		else
			put_int32(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_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 len = static_cast<ULONG>(strlen(string));
	// CVC: We'll have to ensure that length < MAX_FILE_NAME_SIZE.
	// For now, we'll truncate.
	// We can't honor operating systems that allow longer file names.
	if (len >= MAX_FILE_NAME_SIZE)
	{
		BURP_print(false, 343, SafeArg() << int(attribute) << "put_asciz()" << (MAX_FILE_NAME_SIZE - 1));
		// msg 343: text for attribute @1 is too large in @2, truncating to @3 bytes
		len = MAX_FILE_NAME_SIZE - 1;
	}

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


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.
 *
 **************************************/
	FbLocalStatus status_vector;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

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

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

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

	BlobWrapper 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_int32(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_int32(att_blob_max_segment, max_segment);
	put_int32(att_blob_number_segments, segments);
	put_int32(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)
	{
		FB_SIZE_T segment_length;
		if (!blob.getSegment(max_segment, buffer, segment_length))
		{
			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.
 *
 **************************************/
	FbLocalStatus status_vector;
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

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

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

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

	BlobWrapper 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_int32(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.
	FB_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.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();
	USHORT field_count = 1;	// eof field
	burp_fld* field;
	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;
	USHORT 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_int128:
			alignment = type_alignments[dtype_int128];
			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_timestamp_tz:
			alignment = type_alignments[dtype_timestamp_tz];
			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_time_tz:
			alignment = type_alignments[dtype_sql_time_tz];
			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;

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

		case blr_dec64:
		case blr_dec128:
			alignment = type_alignments[dtype];
			add_byte(blr, field->fld_type);
			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 RCRD_LENGTH length = (RCRD_LENGTH) (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);

	unsigned blr_length = blr - blr_buffer;

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

	// Compile request

	FbLocalStatus status_vector;
	Firebird::IRequest* request = DB->compileRequest(&status_vector, blr_length, blr_buffer);
	if (!status_vector.isSuccess())
	{
		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

	request->start(&status_vector, gds_trans, 0);
	if (!status_vector.isSuccess())
	{
		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;

	FB_UINT64 records = 0;
	while (true)
	{
		request->receive(&status_vector, 0, 0, length, buffer);
		if (!status_vector.isSuccess())
		{
			BURP_error_redirect(&status_vector, 29);
			// msg 29 isc_receive failed
		}
		if (!*eof)
			break;
		records++;
		// Verbose records
		if ((records % tdgbl->verboseInterval) == 0)
			BURP_verbose(108, SafeArg() << records);

		put(tdgbl, (UCHAR) rec_data);
		put_int32(att_data_length, record_length);
		const UCHAR* p;
		if (tdgbl->gbl_sw_transportable)
		{
			record_length = CAN_encode_decode(relation, &xdr_buffer, buffer, true);
			put_int32(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

	request->free(&status_vector);
	if (!status_vector.isSuccess())
		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

	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_int32 (att_segment_count, X.RDB$SEGMENT_COUNT);
		put_int32 (att_index_inactive, X.RDB$INDEX_INACTIVE);
		put_int32 (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_int32 (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;
}


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 UCHAR, 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 function tries to maintain backwards compatibility where possible.
 *
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

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

	const ULONG newlen = MIN(len, 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, true);
		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, true);

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

	return newlen;
}


void put_int32( att_type attribute, SLONG value)
{
/**************************************
 *
 *	p u t _ i n t 3 2
 *
 **************************************
 *
 * 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_int32, except that it handles an
 *      INT64 value, while put_int32 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_boolean(att_type attribute, const FB_BOOLEAN value)
{
/**************************************
 *
 *	p u t _ b o o l e a n
 *
 **************************************
 *
 * Functional description
 *	Write a FB_BOOLEAN value as an attribute.
 **************************************/
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	put(tdgbl, attribute);
	put(tdgbl, (UCHAR) 1);
	put(tdgbl, value ? 1u : 0u);
}


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_int32(att_field_position, field->fld_position);
		put_int32(att_field_type, field->fld_type);
		put_int32(att_field_length, field->fld_length);
		put_int32(att_field_sub_type, field->fld_sub_type);
		put_int32(att_field_scale, field->fld_scale);
		put_int32(att_field_number, field->fld_number);
		put_int32(att_field_system_flag, field->fld_system_flag);
		if (!(field->fld_flags & FLD_update_missing))
			put_int32(att_field_update_flag, field->fld_update_flag);
		if (field->fld_flags & FLD_null_flag)
			put_int32(att_field_null_flag, field->fld_null_flag);
		if (field->fld_flags & FLD_charset_flag)
			put_int32(att_field_character_set, field->fld_character_set_id);
		if (field->fld_flags & FLD_collate_flag)
			put_int32(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_int32(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_int32(att_field_computed_flag, TRUE);

		if (field->fld_flags & FLD_array)
		{
			put_int32(att_field_dimensions, field->fld_dimensions);
			const SLONG* rp = field->fld_ranges;
			for (USHORT n = field->fld_dimensions; n; rp += 2, n--)
			{
				put_int32(att_field_range_low, *rp);
				put_int32(att_field_range_high, *(rp + 1));
			}
		}

		if (field->fld_generator[0])
		{
			PUT_TEXT(att_field_generator_name, field->fld_generator);
			put_int32(att_field_identity_type, field->fld_identity_type);
		}

		put(tdgbl, att_end);
	}

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

	if (relation->rel_flags & REL_view)
	{
		if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
		{
			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_int32 (att_view_context_id, X.RDB$VIEW_CONTEXT);
				// Will need PUT_MESSAGE if this field grows to more than 255 bytes.
				PUT_TEXT (att_view_context_name, X.RDB$CONTEXT_NAME);
				if (!X.RDB$CONTEXT_TYPE.NULL)
					put_int32(att_view_context_type, X.RDB$CONTEXT_TYPE);
				if (!X.RDB$PACKAGE_NAME.NULL)
					PUT_TEXT(att_view_context_package, X.RDB$PACKAGE_NAME);
				put(tdgbl, att_end);
			END_FOR
			ON_ERROR
				general_on_error();
			END_ERROR;
		}
		else
		{
			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_int32 (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;
		}
	}
	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.
 *
 **************************************/
	FbLocalStatus status_vector;
	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

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

	if (BlobWrapper::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

	BlobWrapper 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_int32(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.
	FB_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();

	SSHORT l = (SSHORT) MISC_symbol_length(text, (ULONG) size_len);
	fb_assert(l <= MAX_UCHAR);
	if (l > MAX_UCHAR)
	{
		BURP_print(true, 343, SafeArg() << int(attribute) << "put_text()" << MAX_UCHAR);
		// msg 343: text for attribute @1 is too large in @2, truncating to @3 bytes
		l = MAX_UCHAR;
	}

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

	return l;
}


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.
 *
 **************************************/
	Firebird::IRequest* req_handle1 = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$CHARACTER_SETS
			WITH 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 != 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_int32 (att_charset_numchar, X.RDB$NUMBER_OF_CHARACTERS);
				put_int32 (att_charset_id, X.RDB$CHARACTER_SET_ID);
				if (X.RDB$SYSTEM_FLAG)
					put_int32 (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_int32 (att_charset_bytes_char, X.RDB$BYTES_PER_CHARACTER);

				// CVC: Not sure if this code goes inside or outside this "if" block.
				if (!X.RDB$SECURITY_CLASS.NULL)
					PUT_TEXT(att_charset_security_class, X.RDB$SECURITY_CLASS);
				if (!X.RDB$OWNER_NAME.NULL)
					PUT_TEXT(att_charset_owner_name, X.RDB$OWNER_NAME);
			}

			PUT_TEXT (att_charset_coll, X.RDB$DEFAULT_COLLATE_NAME);
			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		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_int32 (att_charset_numchar, X.RDB$NUMBER_OF_CHARACTERS);
				put_int32 (att_charset_id, X.RDB$CHARACTER_SET_ID);
				if (X.RDB$SYSTEM_FLAG)
					put_int32 (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_int32 (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.
 *
 **************************************/
	Firebird::IRequest* req_handle1 = nullptr;

	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
 *
 **************************************/
	Firebird::IRequest* req_handle1 = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$COLLATIONS WITH X.RDB$SYSTEM_FLAG NE 1
			put(tdgbl, rec_collation);
			PUT_TEXT (att_coll_name, X.RDB$COLLATION_NAME);
			put_int32 (att_coll_id, X.RDB$COLLATION_ID);
			put_int32 (att_coll_cs_id, X.RDB$CHARACTER_SET_ID);
			put_int32 (att_coll_attr, X.RDB$COLLATION_ATTRIBUTES);
			if (X.RDB$SYSTEM_FLAG)
				put_int32 (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);

			if (!X.RDB$SECURITY_CLASS.NULL)
				PUT_TEXT(att_coll_security_class, X.RDB$SECURITY_CLASS);
			if (!X.RDB$OWNER_NAME.NULL)
				PUT_TEXT(att_coll_owner_name, X.RDB$OWNER_NAME);

			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else if (tdgbl->runtimeODS >= DB_VERSION_DDL11)
	{
		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_int32 (att_coll_id, X.RDB$COLLATION_ID);
			put_int32 (att_coll_cs_id, X.RDB$CHARACTER_SET_ID);
			put_int32 (att_coll_attr, X.RDB$COLLATION_ATTRIBUTES);
			if (X.RDB$SYSTEM_FLAG)
				put_int32 (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_int32 (att_coll_id, X.RDB$COLLATION_ID);
			put_int32 (att_coll_cs_id, X.RDB$CHARACTER_SET_ID);
			put_int32 (att_coll_attr, X.RDB$COLLATION_ATTRIBUTES);
			if (X.RDB$SYSTEM_FLAG)
				put_int32 (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.
 *
 **************************************/
	FbLocalStatus status_vector;
	UCHAR buffer[256];
	Firebird::IRequest* req_handle1 = nullptr;
	Firebird::IRequest* req_handle2 = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	put(tdgbl, (UCHAR) rec_physical_db);

	DB->getInfo(&status_vector, sizeof(db_info_items), db_info_items,
						  sizeof(buffer), buffer);
	if (!status_vector.isSuccess())
	{
		BURP_error_redirect(&status_vector, 31);
		// msg 31 isc_database_info failed
	}

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

		case isc_info_page_size:
			page_size = (USHORT) gds__vax_integer(d, length);
			put_int32(att_page_size, page_size);
			break;

		case isc_info_sweep_interval:
			sweep_interval = gds__vax_integer(d, length);
			put_int32(att_sweep_interval, sweep_interval);
			break;

		case isc_info_forced_writes:
			forced_writes = (USHORT) gds__vax_integer(d, length);
			put_int32(att_forced_writes, forced_writes);
			break;

		case isc_info_no_reserve:
			if (no_reserve = (USHORT) gds__vax_integer(d, length))
				put_int32(att_no_reserve, no_reserve);
			break;

		case isc_info_set_page_buffers:
			if (page_buffers = gds__vax_integer(d, length))
				put_int32(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:
			tdgbl->gbl_dialect = (USHORT) gds__vax_integer(d, length);
			put_int32(att_SQL_dialect, tdgbl->gbl_dialect);
			break;

		case isc_info_db_read_only:
			if (db_read_only = (USHORT) gds__vax_integer(d, length))
				put_int32(att_db_read_only, db_read_only);
			break;

		case fb_info_replica_mode:
			if (replica_mode = (USHORT) gds__vax_integer(d, length))
				put_int32(att_replica_mode, replica_mode);
			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 (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		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);
			if (!D.RDB$LINGER.NULL)
				put_int32(att_database_linger, D.RDB$LINGER);
			if (!D.RDB$SQL_SECURITY.NULL)
				put_boolean(att_database_sql_security, D.RDB$SQL_SECURITY);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;

		if (tdgbl->runtimeODS >= DB_VERSION_DDL13)
		{
			FOR (REQUEST_HANDLE req_handle2)
				PUB IN RDB$PUBLICATIONS
				WITH PUB.RDB$PUBLICATION_NAME EQ DEFAULT_PUBLICATION
			{
				fb_assert(PUB.RDB$SYSTEM_FLAG != 0);

				if (!PUB.RDB$ACTIVE_FLAG.NULL)
					put_boolean(att_default_pub_active, PUB.RDB$ACTIVE_FLAG);

				if (!PUB.RDB$AUTO_ENABLE.NULL)
					put_boolean(att_default_pub_auto_enable, PUB.RDB$AUTO_ENABLE);
			}
			END_FOR
			ON_ERROR
				general_on_error();
			END_ERROR
		}
	}
	else
	{
		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;
	}

	MISC_release_request_silent(req_handle1);
	MISC_release_request_silent(req_handle2);

	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];
	Firebird::IRequest* req_handle1 = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$EXCEPTIONS
			WITH 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_exception_description, X.RDB$DESCRIPTION);

			if (!X.RDB$SECURITY_CLASS.NULL)
				PUT_TEXT(att_exception_security_class, X.RDB$SECURITY_CLASS);
			if (!X.RDB$OWNER_NAME.NULL)
				PUT_TEXT(att_exception_owner_name, X.RDB$OWNER_NAME);

			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		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_exception_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.
 *
 **************************************/
	Firebird::IRequest* req_handle1 = nullptr;

	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_int32 (att_field_dimensions, X.RDB$DIMENSION);
		put_int32 (att_field_range_low, X.RDB$LOWER_BOUND);
		put_int32 (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];
	Firebird::IRequest* req_handle1 = nullptr;

	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_int32 (att_filter_input_sub_type, X.RDB$INPUT_SUB_TYPE);
		put_int32 (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 * 2];
	Firebird::IRequest* req_handle1 = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$FUNCTIONS WITH X.RDB$SYSTEM_FLAG NE 1
			put(tdgbl, rec_function);

			SSHORT prefixLen = 0;

			if (!X.RDB$PACKAGE_NAME.NULL)
			{
				prefixLen = PUT_TEXT(att_function_package_name, X.RDB$PACKAGE_NAME);
				MISC_terminate(X.RDB$PACKAGE_NAME, temp, prefixLen, sizeof(temp));
				temp[prefixLen++] = '.';
			}

			const SSHORT l = PUT_TEXT (att_function_name, X.RDB$FUNCTION_NAME);
			MISC_terminate (X.RDB$FUNCTION_NAME, temp + prefixLen, l, sizeof(temp) - prefixLen);
			BURP_verbose (147, temp);
			// msg 147 writing function %.*s
			put_source_blob (att_function_description2, att_function_description, X.RDB$DESCRIPTION);

			if (!X.RDB$RETURN_ARGUMENT.NULL)
				put_int32 (att_function_return_arg, X.RDB$RETURN_ARGUMENT);

			put_int32 (att_function_type, X.RDB$FUNCTION_TYPE);
			PUT_TEXT (att_function_query_name, X.RDB$QUERY_NAME);

			if (!X.RDB$MODULE_NAME.NULL)
				PUT_TEXT(att_function_module_name, X.RDB$MODULE_NAME);
			if (!X.RDB$ENTRYPOINT.NULL)
				PUT_TEXT(att_function_entrypoint, X.RDB$ENTRYPOINT);

			if (!X.RDB$ENGINE_NAME.NULL)
				PUT_TEXT(att_function_engine_name, X.RDB$ENGINE_NAME);
			if (!X.RDB$PRIVATE_FLAG.NULL)
				put_int32(att_function_private_flag, X.RDB$PRIVATE_FLAG);
			if (!X.RDB$FUNCTION_BLR.NULL)
				put_blr_blob(att_function_blr, X.RDB$FUNCTION_BLR);
			if (!X.RDB$FUNCTION_SOURCE.NULL)
				put_source_blob(att_function_source, att_function_source, X.RDB$FUNCTION_SOURCE);
			if (!X.RDB$VALID_BLR.NULL)
				put_int32(att_function_valid_blr, X.RDB$VALID_BLR);
			if (!X.RDB$DEBUG_INFO.NULL)
				put_blr_blob(att_function_debug_info, X.RDB$DEBUG_INFO);
			if (!X.RDB$SECURITY_CLASS.NULL)
				PUT_TEXT(att_function_security_class, X.RDB$SECURITY_CLASS);
			if (!X.RDB$OWNER_NAME.NULL)
				PUT_TEXT(att_function_owner_name, X.RDB$OWNER_NAME);
			if (!X.RDB$LEGACY_FLAG.NULL)
				put_int32(att_function_legacy_flag, X.RDB$LEGACY_FLAG);
			if (!X.RDB$DETERMINISTIC_FLAG.NULL)
				put_int32(att_function_deterministic_flag, X.RDB$DETERMINISTIC_FLAG);
			if (!X.RDB$SQL_SECURITY.NULL)
				put_boolean(att_function_sql_security, X.RDB$SQL_SECURITY);

			put(tdgbl, att_end);

			COPY (X.RDB$FUNCTION_NAME, func);
			write_function_args ((X.RDB$PACKAGE_NAME.NULL ? "" : X.RDB$PACKAGE_NAME), func);
			put(tdgbl, rec_function_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else
	{
		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);

			if (!X.RDB$RETURN_ARGUMENT.NULL)
				put_int32 (att_function_return_arg, X.RDB$RETURN_ARGUMENT);

			put_int32 (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(const GDS_NAME package, 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 * 2];

	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->runtimeODS >= DB_VERSION_DDL12)
	{
		FOR (REQUEST_HANDLE tdgbl->handles_write_function_args_req_handle1)
			X IN RDB$FUNCTION_ARGUMENTS
			WITH X.RDB$FUNCTION_NAME EQ funcptr AND
				 X.RDB$PACKAGE_NAME EQUIV NULLIF(package, '')

			put(tdgbl, rec_function_arg);

			SSHORT prefixLen = 0;

			if (!X.RDB$PACKAGE_NAME.NULL)
			{
				prefixLen = PUT_TEXT(att_functionarg_package_name, X.RDB$PACKAGE_NAME);
				MISC_terminate(X.RDB$PACKAGE_NAME, temp, prefixLen, sizeof(temp));
				temp[prefixLen++] = '.';
			}

			const SSHORT l = PUT_TEXT (att_functionarg_name, X.RDB$FUNCTION_NAME);
			MISC_terminate (X.RDB$FUNCTION_NAME, temp + prefixLen, l, sizeof(temp) - prefixLen);
			BURP_verbose (141, temp);
			// msg 141 writing argument for function %s

			if (!X.RDB$ARGUMENT_POSITION.NULL)
				put_int32 (att_functionarg_position, X.RDB$ARGUMENT_POSITION);

			put_int32 (att_functionarg_passing_mechanism, X.RDB$MECHANISM);
			put_int32 (att_functionarg_field_type, X.RDB$FIELD_TYPE);
			put_int32 (att_functionarg_field_scale, X.RDB$FIELD_SCALE);
			put_int32 (att_functionarg_field_length, X.RDB$FIELD_LENGTH);
			put_int32 (att_functionarg_field_sub_type, X.RDB$FIELD_SUB_TYPE);
			if (!X.RDB$CHARACTER_SET_ID.NULL)
				put_int32 (att_functionarg_character_set, X.RDB$CHARACTER_SET_ID);

			if (!X.RDB$FIELD_PRECISION.NULL)
				put_int32 (att_functionarg_field_precision, X.RDB$FIELD_PRECISION);

			if (!X.RDB$ARGUMENT_NAME.NULL)
				PUT_TEXT(att_functionarg_arg_name, X.RDB$ARGUMENT_NAME);
			if (!X.RDB$FIELD_SOURCE.NULL)
				PUT_TEXT(att_functionarg_field_source, X.RDB$FIELD_SOURCE);
			if (!X.RDB$DEFAULT_VALUE.NULL)
				put_blr_blob(att_functionarg_default_value, X.RDB$DEFAULT_VALUE);
			if (!X.RDB$DEFAULT_SOURCE.NULL)
				put_source_blob(att_functionarg_default_source, att_functionarg_default_source,
								X.RDB$DEFAULT_SOURCE);
			if (!X.RDB$COLLATION_ID.NULL)
				put_int32(att_functionarg_collation_id, X.RDB$COLLATION_ID);
			if (!X.RDB$NULL_FLAG.NULL)
				put_int32(att_functionarg_null_flag, X.RDB$NULL_FLAG);
			if (!X.RDB$ARGUMENT_MECHANISM.NULL)
				put_int32(att_functionarg_type_mechanism, X.RDB$ARGUMENT_MECHANISM);
			if (!X.RDB$FIELD_NAME.NULL)
				PUT_TEXT(att_functionarg_field_name, X.RDB$FIELD_NAME);
			if (!X.RDB$RELATION_NAME.NULL)
				PUT_TEXT(att_functionarg_relation_name, X.RDB$RELATION_NAME);
			if (!X.RDB$DESCRIPTION.NULL)
				put_source_blob(att_functionarg_description, att_functionarg_description, X.RDB$DESCRIPTION);

			put(tdgbl, att_end);
		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else if (tdgbl->runtimeODS >= DB_VERSION_DDL10)
	{
		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

			if (!X.RDB$ARGUMENT_POSITION.NULL)
				put_int32 (att_functionarg_position, X.RDB$ARGUMENT_POSITION);

			put_int32 (att_functionarg_passing_mechanism, X.RDB$MECHANISM);
			put_int32 (att_functionarg_field_type, X.RDB$FIELD_TYPE);
			put_int32 (att_functionarg_field_scale, X.RDB$FIELD_SCALE);
			put_int32 (att_functionarg_field_length, X.RDB$FIELD_LENGTH);
			put_int32 (att_functionarg_field_sub_type, X.RDB$FIELD_SUB_TYPE);
			if (!X.RDB$CHARACTER_SET_ID.NULL)
				put_int32 (att_functionarg_character_set, X.RDB$CHARACTER_SET_ID);

			if (!X.RDB$FIELD_PRECISION.NULL)
				put_int32 (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

			if (!X.RDB$ARGUMENT_POSITION.NULL)
				put_int32 (att_functionarg_position, X.RDB$ARGUMENT_POSITION);

			put_int32 (att_functionarg_passing_mechanism, X.RDB$MECHANISM);
			put_int32 (att_functionarg_field_type, X.RDB$FIELD_TYPE);
			put_int32 (att_functionarg_field_scale, X.RDB$FIELD_SCALE);
			put_int32 (att_functionarg_field_length, X.RDB$FIELD_LENGTH);
			put_int32 (att_functionarg_field_sub_type, X.RDB$FIELD_SUB_TYPE);

			if (!X.RDB$CHARACTER_SET_ID.NULL)
				put_int32 (att_functionarg_character_set, X.RDB$CHARACTER_SET_ID);
			// Note that DB_VERSION_DDL10 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.

			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.
 *
 **************************************/
	Firebird::IRequest* req_handle1 = nullptr;
	TEXT temp[GDS_NAME_LEN];

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$GENERATORS
			WITH 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);
			}

			if (X.RDB$SYSTEM_FLAG)
				put_int32(att_gen_sysflag, X.RDB$SYSTEM_FLAG);

			if (!X.RDB$SECURITY_CLASS.NULL)
				PUT_TEXT(att_gen_security_class, X.RDB$SECURITY_CLASS);
			if (!X.RDB$OWNER_NAME.NULL)
				PUT_TEXT(att_gen_owner_name, X.RDB$OWNER_NAME);
			if (!X.RDB$INITIAL_VALUE.NULL)
				put_int64(att_gen_init_val, X.RDB$INITIAL_VALUE);

			put_int32(att_gen_id_increment, X.RDB$GENERATOR_INCREMENT);

			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 if (tdgbl->runtimeODS >= DB_VERSION_DDL11)
	{
		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];
	Firebird::IRequest* req_handle1 = nullptr;

	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->runtimeODS >= DB_VERSION_DDL12)
	{
		FOR (REQUEST_HANDLE req_handle1)
			X IN RDB$FIELDS WITH
			X.RDB$SYSTEM_FLAG NE 1

			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_int32 (att_field_type, X.RDB$FIELD_TYPE);
			put_int32 (att_field_length, X.RDB$FIELD_LENGTH);
			put_int32 (att_field_sub_type, X.RDB$FIELD_SUB_TYPE);
			put_int32 (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_int32 (att_field_segment_length, X.RDB$SEGMENT_LENGTH);
			if (X.RDB$SYSTEM_FLAG)
				put_int32 (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_int32 (att_field_external_length, X.RDB$EXTERNAL_LENGTH);
			if (X.RDB$EXTERNAL_TYPE)
				put_int32 (att_field_external_type, X.RDB$EXTERNAL_TYPE);
			if (X.RDB$EXTERNAL_SCALE)
				put_int32 (att_field_external_scale, X.RDB$EXTERNAL_SCALE);
			if (X.RDB$DIMENSIONS)
				put_int32 (att_field_dimensions, X.RDB$DIMENSIONS);
			if (!X.RDB$NULL_FLAG.NULL)
				put_int32 (att_field_null_flag, X.RDB$NULL_FLAG);
			if (!X.RDB$CHARACTER_LENGTH.NULL)
				put_int32 (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_int32 (att_field_character_set, X.RDB$CHARACTER_SET_ID);
			if (!X.RDB$COLLATION_ID.NULL)
				put_int32 (att_field_collation_id, X.RDB$COLLATION_ID);
			if (!X.RDB$FIELD_PRECISION.NULL)
				put_int32 (att_field_precision, X.RDB$FIELD_PRECISION);

			if (!X.RDB$SECURITY_CLASS.NULL)
				PUT_TEXT(att_field_security_class, X.RDB$SECURITY_CLASS);
			if (!X.RDB$OWNER_NAME.NULL)
				PUT_TEXT(att_field_owner_name, X.RDB$OWNER_NAME);

			put(tdgbl, att_end);

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else if (tdgbl->runtimeODS >= DB_VERSION_DDL10)
	{
		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_int32 (att_field_type, X.RDB$FIELD_TYPE);
			put_int32 (att_field_length, X.RDB$FIELD_LENGTH);
			put_int32 (att_field_sub_type, X.RDB$FIELD_SUB_TYPE);
			put_int32 (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_int32 (att_field_segment_length, X.RDB$SEGMENT_LENGTH);
			if (X.RDB$SYSTEM_FLAG)
				put_int32 (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_int32 (att_field_external_length, X.RDB$EXTERNAL_LENGTH);
			if (X.RDB$EXTERNAL_TYPE)
				put_int32 (att_field_external_type, X.RDB$EXTERNAL_TYPE);
			if (X.RDB$EXTERNAL_SCALE)
				put_int32 (att_field_external_scale, X.RDB$EXTERNAL_SCALE);
			if (X.RDB$DIMENSIONS)
				put_int32 (att_field_dimensions, X.RDB$DIMENSIONS);
			if (!X.RDB$NULL_FLAG.NULL)
				put_int32 (att_field_null_flag, X.RDB$NULL_FLAG);
			if (!X.RDB$CHARACTER_LENGTH.NULL)
				put_int32 (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_int32 (att_field_character_set, X.RDB$CHARACTER_SET_ID);
			if (!X.RDB$COLLATION_ID.NULL)
				put_int32 (att_field_collation_id, X.RDB$COLLATION_ID);

			if (!X.RDB$FIELD_PRECISION.NULL)
				put_int32 (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_int32 (att_field_type, X.RDB$FIELD_TYPE);
			put_int32 (att_field_length, X.RDB$FIELD_LENGTH);
			put_int32 (att_field_sub_type, X.RDB$FIELD_SUB_TYPE);
			put_int32 (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_int32 (att_field_segment_length, X.RDB$SEGMENT_LENGTH);
			if (X.RDB$SYSTEM_FLAG)
				put_int32 (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_int32 (att_field_external_length, X.RDB$EXTERNAL_LENGTH);
			if (X.RDB$EXTERNAL_TYPE)
				put_int32 (att_field_external_type, X.RDB$EXTERNAL_TYPE);
			if (X.RDB$EXTERNAL_SCALE)
				put_int32 (att_field_external_scale, X.RDB$EXTERNAL_SCALE);
			if (X.RDB$DIMENSIONS)
				put_int32 (att_field_dimensions, X.RDB$DIMENSIONS);
			if (!X.RDB$NULL_FLAG.NULL)
				put_int32 (att_field_null_flag, X.RDB$NULL_FLAG);
			if (!X.RDB$CHARACTER_LENGTH.NULL)
				put_int32 (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_int32 (att_field_character_set, X.RDB$CHARACTER_SET_ID);
			if (!X.RDB$COLLATION_ID.NULL)
				put_int32 (att_field_collation_id, X.RDB$COLLATION_ID);

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

	MISC_release_request_silent(req_handle1);
}


void write_packages()
{
/**************************************
 *
 *	w r i t e _ p a c k a g e s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each package.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	Firebird::IRequest* req_handle1 = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	// This code is for DDL12 onwards, so no need to check for null sysflag.
	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$PACKAGES
		WITH X.RDB$SYSTEM_FLAG NE 1
	{
		put(tdgbl, rec_package);
		const SSHORT l = PUT_TEXT(att_package_name, X.RDB$PACKAGE_NAME);
		MISC_terminate(X.RDB$PACKAGE_NAME, temp, l, sizeof(temp));

		BURP_verbose(335, temp);	// msg 335 writing package @1

		if (!X.RDB$PACKAGE_HEADER_SOURCE.NULL)
		{
			put_source_blob(att_package_header_source, att_package_header_source,
				X.RDB$PACKAGE_HEADER_SOURCE);
		}

		if (!X.RDB$PACKAGE_BODY_SOURCE.NULL)
		{
			put_source_blob(att_package_body_source, att_package_body_source,
				X.RDB$PACKAGE_BODY_SOURCE);
		}

		if (!X.RDB$VALID_BODY_FLAG.NULL)
			put_int32(att_package_valid_body_flag, X.RDB$VALID_BODY_FLAG);

		if (!X.RDB$SECURITY_CLASS.NULL)
			PUT_TEXT(att_package_security_class, X.RDB$SECURITY_CLASS);
		if (!X.RDB$OWNER_NAME.NULL)
			PUT_TEXT(att_package_owner_name, X.RDB$OWNER_NAME);

		if (!X.RDB$DESCRIPTION.NULL)
			put_source_blob(att_package_description, att_package_description, X.RDB$DESCRIPTION);

		if (!X.RDB$SQL_SECURITY.NULL)
			put_boolean(att_package_sql_security, X.RDB$SQL_SECURITY);

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

	MISC_release_request_silent(req_handle1);
}


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 * 2];
	Firebird::IRequest* req_handle1 = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->runtimeODS >= DB_VERSION_DDL11_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);

			SSHORT prefixLen = 0;

			if (!X.RDB$PACKAGE_NAME.NULL)
			{
				prefixLen = PUT_TEXT(att_procedure_package_name, X.RDB$PACKAGE_NAME);
				MISC_terminate (X.RDB$PACKAGE_NAME, temp, prefixLen, sizeof(temp));
				temp[prefixLen++] = '.';
			}

			const SSHORT len = PUT_TEXT (att_procedure_name, X.RDB$PROCEDURE_NAME);
			MISC_terminate (X.RDB$PROCEDURE_NAME, temp + prefixLen, len, sizeof(temp) - prefixLen);

			BURP_verbose (193, temp);
			// msg 193 writing stored procedure %.*s
			put_int32 (att_procedure_inputs, X.RDB$PROCEDURE_INPUTS);
			put_int32 (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);

			if (!X.RDB$PROCEDURE_BLR.NULL)
				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_int32 (att_procedure_type, X.RDB$PROCEDURE_TYPE);
			if (!X.RDB$VALID_BLR.NULL)
				put_int32 (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);

			if (!X.RDB$ENGINE_NAME.NULL)
				PUT_TEXT(att_procedure_engine_name, X.RDB$ENGINE_NAME);

			if (!X.RDB$ENTRYPOINT.NULL)
				PUT_TEXT(att_procedure_entrypoint, X.RDB$ENTRYPOINT);

			if (!X.RDB$PRIVATE_FLAG.NULL)
				put_int32(att_procedure_private_flag, X.RDB$PRIVATE_FLAG);

			if (!X.RDB$SQL_SECURITY.NULL)
				put_boolean(att_procedure_sql_security, X.RDB$SQL_SECURITY);

			put(tdgbl, att_end);
			COPY(X.RDB$PROCEDURE_NAME, proc);
			write_procedure_prms ((X.RDB$PACKAGE_NAME.NULL ? "" : X.RDB$PACKAGE_NAME), 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_int32 (att_procedure_inputs, X.RDB$PROCEDURE_INPUTS);
			put_int32 (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(const GDS_NAME package, const 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->runtimeODS >= DB_VERSION_DDL11_1)
	{
		FOR (REQUEST_HANDLE tdgbl->handles_write_procedure_prms_req_handle1)
			X IN RDB$PROCEDURE_PARAMETERS
			WITH X.RDB$PROCEDURE_NAME EQ procptr AND
				 X.RDB$PACKAGE_NAME EQUIV NULLIF(package, '')
		{
			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_int32 (att_procedureprm_number, X.RDB$PARAMETER_NUMBER);
			put_int32 (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_int32 (att_procedureprm_collation_id, X.RDB$COLLATION_ID);
			if (!X.RDB$NULL_FLAG.NULL)
				put_int32 (att_procedureprm_null_flag, X.RDB$NULL_FLAG);
			if (!X.RDB$PARAMETER_MECHANISM.NULL)
				put_int32 (att_procedureprm_mechanism, X.RDB$PARAMETER_MECHANISM);

			// DB_VERSION_DDL11_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_int32 (att_procedureprm_number, X.RDB$PARAMETER_NUMBER);
			put_int32 (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_publications()
{
/**************************************
 *
 *	w r i t e _ p u b l i c a t i o n s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each publication.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	Firebird::IRequest* req_handle = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle)
		X IN RDB$PUBLICATIONS
		WITH X.RDB$SYSTEM_FLAG NE 1
	{
		put(tdgbl, rec_publication);

		const SSHORT l = PUT_TEXT(att_pub_name, X.RDB$PUBLICATION_NAME);
		MISC_terminate(X.RDB$PUBLICATION_NAME, temp, l, sizeof(temp));

		BURP_verbose(397, temp);
		// msg 397 writing publication %s

		if (!X.RDB$OWNER_NAME.NULL)
			PUT_TEXT(att_pub_owner_name, X.RDB$OWNER_NAME);

		if (!X.RDB$ACTIVE_FLAG.NULL)
			put_int32(att_pub_active_flag, X.RDB$ACTIVE_FLAG);

		if (!X.RDB$AUTO_ENABLE.NULL)
			put_int32(att_pub_auto_enable, X.RDB$AUTO_ENABLE);

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

	MISC_release_request_silent(req_handle);
}


void write_pub_tables()
{
/**************************************
 *
 *	w r i t e _ p u b _ t a b l e s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each publication table.
 *
 **************************************/
	TEXT temp[GDS_NAME_LEN];
	Firebird::IRequest* req_handle = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle)
		X IN RDB$PUBLICATION_TABLES
	{
		put(tdgbl, rec_pub_table);

		PUT_TEXT(att_ptab_pub_name, X.RDB$PUBLICATION_NAME);

		const SSHORT l = PUT_TEXT(att_ptab_table_name, X.RDB$TABLE_NAME);
		MISC_terminate(X.RDB$TABLE_NAME, temp, l, sizeof(temp));

		BURP_verbose(398, temp);
		// msg 398 writing publication for table %s

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

	MISC_release_request_silent(req_handle);
}


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.
 *
 **************************************/
	Firebird::IRequest* req_handle1 = nullptr;

	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];
	Firebird::IRequest* req_handle1 = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$RELATION_CONSTRAINTS CROSS
		REL IN RDB$RELATIONS
		WITH REL.RDB$RELATION_NAME EQ X.RDB$RELATION_NAME AND
			 (REL.RDB$SYSTEM_FLAG MISSING OR REL.RDB$SYSTEM_FLAG NE 1)
		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];
	Firebird::IRequest* req_handle1 = nullptr;

	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->runtimeODS >= DB_VERSION_DDL11_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));
			if (X.RDB$VIEW_BLR.NULL)
				BURP_verbose(153, temp); // msg 153 writing table @1
			else
				BURP_verbose(345, temp); // msg 345 writing view @1

			// 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_int32 (att_relation_system_flag, X.RDB$SYSTEM_FLAG);
			if (!X.RDB$FLAGS.NULL)
				put_int32 (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_int32 (att_relation_type, X.RDB$RELATION_TYPE);

			if (!X.RDB$SQL_SECURITY.NULL)
				put_boolean(att_relation_sql_security, X.RDB$SQL_SECURITY);

			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 table %.*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_int32 (att_relation_system_flag, X.RDB$SYSTEM_FLAG);
			if (!X.RDB$FLAGS.NULL)
				put_int32 (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;
				}
			}
			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);
}


void write_secclasses()
{
	TEXT temp[GDS_NAME_LEN];
	Firebird::IRequest* req_handle1 = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

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


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.
 *
 **************************************/
	BASED ON RDB$FILES.RDB$FILE_NAME temp;
	Firebird::IRequest* req_handle1 = nullptr;

	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_int32 (att_file_sequence, X.RDB$FILE_SEQUENCE);
		put_int32 (att_file_start, X.RDB$FILE_START);
		put_int32 (att_file_length, X.RDB$FILE_LENGTH);
		put_int32 (att_file_flags, X.RDB$FILE_FLAGS);
		put_int32 (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.
 *
 **************************************/
	Firebird::IRequest* req_handle1 = nullptr;
	TEXT temp[GDS_NAME_LEN];

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->runtimeODS >= DB_VERSION_DDL11)
	{
		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);
			}

			const UCHAR ll = sizeof(X.RDB$SYSTEM_PRIVILEGES);
			put(tdgbl, att_role_sys_priveleges);
			put(tdgbl, ll);
			put_block(tdgbl, (const UCHAR*) (X.RDB$SYSTEM_PRIVILEGES), ll);

			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.
 *
 **************************************/
	Firebird::IRequest* req_handle = nullptr;
	TEXT temp[GDS_NAME_LEN];

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	if (tdgbl->runtimeODS >= DB_VERSION_DDL12)
	{
		FOR (REQUEST_HANDLE req_handle)
			M IN RDB$AUTH_MAPPING
			WITH M.RDB$SYSTEM_FLAG EQ 0

			put(tdgbl, rec_mapping);
			const SSHORT l = PUT_TEXT(att_map_name, M.RDB$MAP_NAME);
			PUT_TEXT(att_map_using, M.RDB$MAP_USING);

			if (!M.RDB$MAP_PLUGIN.NULL)
				PUT_TEXT(att_map_plugin, M.RDB$MAP_PLUGIN);

			if (!M.RDB$MAP_DB.NULL)
				PUT_TEXT(att_map_db, M.RDB$MAP_DB);

			PUT_TEXT(att_map_from_type, M.RDB$MAP_FROM_TYPE);

			if (!M.RDB$MAP_FROM.NULL)
				PUT_TEXT(att_map_from, M.RDB$MAP_FROM);

			put_int32(att_map_to_type, M.RDB$MAP_TO_TYPE);

			if (!M.RDB$MAP_TO.NULL)
				PUT_TEXT(att_map_to, M.RDB$MAP_TO);

			if (!M.RDB$DESCRIPTION.NULL)
				put_source_blob(att_map_description, att_map_description, M.RDB$DESCRIPTION);

			put(tdgbl, att_end);

			MISC_terminate (M.RDB$MAP_NAME, temp, l, sizeof(temp));
			BURP_verbose (297, temp);
			// msg 297 writing mapping for %s

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}
	else if (tdgbl->runtimeODS >= DB_VERSION_DDL11_2)
	{
		FOR (REQUEST_HANDLE req_handle)
			X IN RDB$ROLES
			WITH X.RDB$ROLE_NAME EQ ADMIN_ROLE

			if (X.RDB$SYSTEM_FLAG == 6)		// constant 6 turns on auto admin mapping in FB 2.5
			{
				put(tdgbl, rec_mapping);
				put_text(att_auto_map_role, ADMIN_ROLE, static_cast<SSHORT>(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_db_creators()
{
/**************************************
 *
 *	w r i t e _ d b _ c r e a t o r s
 *
 **************************************
 *
 * Functional description
 * 	write a record in the burp file for
 *	each grant to create database.
 *
 **************************************/
	Firebird::IRequest* req_handle = nullptr;
	TEXT temp[GDS_NAME_LEN];
	bool first = true;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle)
		C IN RDB$DB_CREATORS

		if (first)
		{
			BURP_verbose(391);
			// msg 391 writing database create grants
			first = false;
		}

		bool fl = true;

		if (!C.RDB$USER_TYPE.NULL)
		{
			put(tdgbl, rec_db_creator);
			fl = false;
			put_int32(att_dbc_type, C.RDB$USER_TYPE);
		}

		if (!C.RDB$USER.NULL)
		{
			if (fl)
				put(tdgbl, rec_db_creator);
			fl = false;
			const SSHORT l = PUT_TEXT(att_dbc_user, C.RDB$USER);

			MISC_terminate (C.RDB$USER, temp, l, sizeof(temp));
			BURP_verbose (392, temp);
			// msg 392 writing db creator %s
		}

		if (!fl)
			put(tdgbl, att_end);

	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];
	Firebird::IRequest* req_handle1 = nullptr;

	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->runtimeODS >= DB_VERSION_DDL11_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_int32 (att_trig_sequence, X.RDB$TRIGGER_SEQUENCE);

			if (X.RDB$TRIGGER_TYPE >= SLONG_MIN && X.RDB$TRIGGER_TYPE <= SLONG_MAX)
				put_int32 (att_trig_type, (SLONG) X.RDB$TRIGGER_TYPE);
			else
				put_int64 (att_trig_type2, 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_int32 (att_trig_system_flag, X.RDB$SYSTEM_FLAG);
			put_int32 (att_trig_inactive, X.RDB$TRIGGER_INACTIVE);

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

			if (!X.RDB$VALID_BLR.NULL)
				put_int32 (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);

			if (!X.RDB$ENGINE_NAME.NULL)
				PUT_TEXT(att_trig_engine_name, X.RDB$ENGINE_NAME);

			if (!X.RDB$ENTRYPOINT.NULL)
				PUT_TEXT(att_trig_entrypoint, X.RDB$ENTRYPOINT);

			if (!X.RDB$SQL_SECURITY.NULL)
				put_boolean(att_trig_sql_security, X.RDB$SQL_SECURITY);

			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_int32 (att_trig_sequence, X.RDB$TRIGGER_SEQUENCE);
			put_int32 (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_int32 (att_trig_system_flag, X.RDB$SYSTEM_FLAG);
			put_int32 (att_trig_inactive, X.RDB$TRIGGER_INACTIVE);

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

			put(tdgbl, att_end);

		END_FOR;
		ON_ERROR
			general_on_error();
		END_ERROR;
	}

	MISC_release_request_silent(req_handle1);
}


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];
	Firebird::IRequest* req_handle1 = nullptr;

	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_int32 (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.
 *
 **************************************/
	Firebird::IRequest* req_handle1 = nullptr;

	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_int32 (att_type_type, X.RDB$TYPE);
		put_source_blob (att_type_description2, att_type_description, X.RDB$DESCRIPTION);
		if (X.RDB$SYSTEM_FLAG)
			put_int32 (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];
	Firebird::IRequest* req_handle1 = nullptr;

	BurpGlobals* tdgbl = BurpGlobals::getSpecific();

	FOR (REQUEST_HANDLE req_handle1)
		X IN RDB$USER_PRIVILEGES
		WITH X.RDB$GRANTOR NOT MISSING
		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);
		if (!X.RDB$GRANT_OPTION.NULL)
			put_int32 (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_int32 (att_priv_user_type, X.RDB$USER_TYPE);
		put_int32 (att_priv_obj_type, X.RDB$OBJECT_TYPE);
		put(tdgbl, att_end);
	END_FOR
	ON_ERROR
		general_on_error();
	END_ERROR;

	MISC_release_request_silent(req_handle1);
}

} // namespace

