/*
 *	PROGRAM:	JRD Data Definition Utility
 *	MODULE:		dyn_util.epp
 *	DESCRIPTION:	Dynamic data definition - utility functions
 *
 * 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): ______________________________________.
 *
 * 2002-02-24 Sean Leyne - Code Cleanup of old Win 3.1 port (WINDOWS_ONLY)
 *
 */

#include "firebird.h"
#include <stdio.h>
#include <string.h>

#include "../jrd/common.h"
#include "../jrd/jrd.h"
#include "../jrd/tra.h"
#include "../jrd/scl.h"
#include "../jrd/drq.h"
#include "../jrd/flags.h"
#include "../jrd/ibase.h"
#include "../jrd/lls.h"
#include "../jrd/met.h"
#include "../jrd/btr.h"
#include "../jrd/intl.h"
#include "../jrd/dyn.h"
#include "../jrd/ods.h"
#include "../jrd/blb_proto.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/dyn_proto.h"
#include "../jrd/dyn_md_proto.h"
#include "../jrd/dyn_ut_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/exe_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/inf_proto.h"
#include "../jrd/intl_proto.h"
#include "../jrd/isc_f_proto.h"
#include "../jrd/vio_proto.h"
#include "../common/utils_proto.h"

using MsgFormat::SafeArg;

using namespace Jrd;

DATABASE DB = STATIC "ODS.RDB";

static const UCHAR gen_id_blr1[] =
{
	blr_version5,
	blr_begin,
	blr_message, 0, 1, 0,
	blr_int64, 0,
	blr_begin,
	blr_send, 0,
	blr_begin,
	blr_assignment,
	blr_gen_id
};

static const UCHAR gen_id_blr2[] =
{
	blr_literal, blr_long, 0, 1, 0, 0, 0,
		blr_parameter, 0, 0, 0, blr_end, blr_end, blr_end, blr_eoc
};

static const UCHAR prot_blr[] =
{
	blr_version5,
	blr_begin,
	blr_message, 1, 1, 0,
	blr_short, 0,
	blr_message, 0, 2, 0,
	blr_cstring, 32, 0,
	blr_cstring, 32, 0,
	blr_receive, 0,
	blr_begin,
	blr_send, 1,
	blr_begin,
	blr_assignment,
	blr_prot_mask,
	blr_parameter, 0, 0, 0,
	blr_parameter, 0, 1, 0,
	blr_parameter, 1, 0, 0,
	blr_end,
	blr_end,
	blr_end,
	blr_eoc
};



SINT64 DYN_UTIL_gen_unique_id(thread_db* tdbb,
							  Global* /*gbl*/,
							  SSHORT id,
							  const char* generator_name)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n _ u n i q u e _ i d
 *
 **************************************
 *
 * Functional description
 *	Generate a unique id using a generator.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, id, DYN_REQUESTS);

	SINT64 value = 0;

	try
	{
		if (!request)
		{
			const size_t name_length = strlen(generator_name);
			fb_assert(name_length < MAX_SQL_IDENTIFIER_SIZE);
			const size_t blr_size = sizeof(gen_id_blr1) + sizeof(gen_id_blr2) + 1 + name_length;

			Firebird::UCharBuffer blr;
			UCHAR* p = blr.getBuffer(blr_size);

			memcpy(p, gen_id_blr1, sizeof(gen_id_blr1));
			p += sizeof(gen_id_blr1);
			*p++ = name_length;
			memcpy(p, generator_name, name_length);
			p += name_length;
			memcpy(p, gen_id_blr2, sizeof(gen_id_blr2));
			p += sizeof(gen_id_blr2);
			fb_assert(size_t(p - blr.begin()) == blr_size);

			request = CMP_compile2(tdbb, blr.begin(), (ULONG) blr.getCount(), true);
		}

		EXE_start(tdbb, request, dbb->dbb_sys_trans);
		EXE_receive(tdbb, request, 0, sizeof(value), (UCHAR*) &value);
		EXE_unwind(tdbb, request);
	}
	catch (const Firebird::Exception&)
	{
		DYN_rundown_request(request, id);
		throw;
	}

	if (!DYN_REQUEST(id)) {
		DYN_REQUEST(id) = request;
	}

	return value;
}


void DYN_UTIL_generate_constraint_name( thread_db* tdbb, Global* gbl, Firebird::MetaName& buffer)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ c o n s t r a i n t _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Generate a name unique to RDB$RELATION_CONSTRAINTS.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = NULL;
	SSHORT id = -1;

	try
	{
		bool found = false;

		do {
			buffer.printf("INTEG_%" SQUADFORMAT,
					DYN_UTIL_gen_unique_id(tdbb, gbl, drq_g_nxt_con, "RDB$CONSTRAINT_NAME"));

			request = CMP_find_request(tdbb, drq_f_nxt_con, DYN_REQUESTS);
			id = drq_f_nxt_con;

			found = false;
			FOR(REQUEST_HANDLE request)
				FIRST 1 X IN RDB$RELATION_CONSTRAINTS
					WITH X.RDB$CONSTRAINT_NAME EQ buffer.c_str()

	            if (!DYN_REQUEST(drq_f_nxt_con))
	                DYN_REQUEST(drq_f_nxt_con) = request;
				found = true;
			END_FOR;

			if (!DYN_REQUEST(drq_f_nxt_con))
				DYN_REQUEST(drq_f_nxt_con) = request;
			request = NULL;
		} while (found);
	}
	catch (const Firebird::Exception& ex)
	{
		Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, id);
		DYN_error_punt(true, 131);
		// msg 131: "Generation of constraint name failed"
	}
}


void DYN_UTIL_generate_field_name( thread_db* tdbb, Global* gbl, TEXT* buffer)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ f i e l d _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Stub to make it work with char* too.
 *
 **************************************/
	Firebird::MetaName temp;
	DYN_UTIL_generate_field_name(tdbb, gbl, temp);
	strcpy(buffer, temp.c_str());
}


void DYN_UTIL_generate_field_name( thread_db* tdbb, Global* gbl, Firebird::MetaName& buffer)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ f i e l d _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Generate a name unique to RDB$FIELDS.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = NULL;
	SSHORT id = -1;

	try
	{
		bool found = false;

		do {
			buffer.printf("RDB$%" SQUADFORMAT,
					DYN_UTIL_gen_unique_id(tdbb, gbl, drq_g_nxt_fld, "RDB$FIELD_NAME"));

			request = CMP_find_request(tdbb, drq_f_nxt_fld, DYN_REQUESTS);
			id = drq_f_nxt_fld;

			found = false;
			FOR(REQUEST_HANDLE request)
				FIRST 1 X IN RDB$FIELDS WITH X.RDB$FIELD_NAME EQ buffer.c_str()

	            if (!DYN_REQUEST(drq_f_nxt_fld))
					DYN_REQUEST(drq_f_nxt_fld) = request;
				found = true;
			END_FOR;

			if (!DYN_REQUEST(drq_f_nxt_fld))
				DYN_REQUEST(drq_f_nxt_fld) = request;
			request = NULL;
		} while (found);
	}
	catch (const Firebird::Exception& ex)
	{
		Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, id);
		DYN_error_punt(true, 81);
		// msg 81: "Generation of field name failed"
	}
}


void DYN_UTIL_generate_field_position(thread_db* tdbb,
									  Global* /*gbl*/,
									  const Firebird::MetaName& relation_name,
									  SLONG* field_pos)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ f i e l d _ p o s i t i o n
 *
 **************************************
 *
 * Functional description
 *	Generate a field position if not specified
 *
 **************************************/
	SLONG field_position = -1;

	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = NULL;

	try
	{
		request = CMP_find_request(tdbb, drq_l_fld_pos, DYN_REQUESTS);

		FOR(REQUEST_HANDLE request)
			X IN RDB$RELATION_FIELDS
				WITH X.RDB$RELATION_NAME EQ relation_name.c_str()

	        if (!DYN_REQUEST(drq_l_fld_pos))
	            DYN_REQUEST(drq_l_fld_pos) = request;

			if (X.RDB$FIELD_POSITION.NULL)
				continue;

			field_position = MAX(X.RDB$FIELD_POSITION, field_position);
		END_FOR;

		*field_pos = field_position;
	}
	catch (const Firebird::Exception& ex)
	{
		Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 162);
		// msg 162: "Looking up field position failed"
	}
}


void DYN_UTIL_generate_index_name(thread_db* tdbb, Global* gbl,
								  Firebird::MetaName& buffer, UCHAR verb)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ i n d e x _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Generate a name unique to RDB$INDICES.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = NULL;
	SSHORT id = -1;

	try
	{
		bool found = false;

		do {
			const SCHAR* format;
			switch (verb)
			{
			case isc_dyn_def_primary_key:
				format = "RDB$PRIMARY%" SQUADFORMAT;
				break;
			case isc_dyn_def_foreign_key:
				format = "RDB$FOREIGN%" SQUADFORMAT;
				break;
			default:
				format = "RDB$%" SQUADFORMAT;
			}

			buffer.printf(format,
				DYN_UTIL_gen_unique_id(tdbb, gbl, drq_g_nxt_idx, "RDB$INDEX_NAME"));

			request = CMP_find_request(tdbb, drq_f_nxt_idx, DYN_REQUESTS);
			id = drq_f_nxt_idx;

			found = false;
			FOR(REQUEST_HANDLE request)
				FIRST 1 X IN RDB$INDICES WITH X.RDB$INDEX_NAME EQ buffer.c_str()

	            if (!DYN_REQUEST(drq_f_nxt_idx))
					DYN_REQUEST(drq_f_nxt_idx) = request;
				found = true;
			END_FOR;

			if (!DYN_REQUEST(drq_f_nxt_idx))
				DYN_REQUEST(drq_f_nxt_idx) = request;
			request = NULL;
		} while (found);
	}
	catch (const Firebird::Exception& ex)
	{
		Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, id);
		DYN_error_punt(true, 82);
		// msg 82: "Generation of index name failed"
	}
}


void DYN_UTIL_generate_trigger_name( thread_db* tdbb, Global* gbl, Firebird::MetaName& buffer)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ t r i g g e r _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Generate a name unique to RDB$TRIGGERS.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = NULL;
	SSHORT id = -1;

	try
	{
		bool found = false;

		do {
			buffer.printf("CHECK_%" SQUADFORMAT,
				DYN_UTIL_gen_unique_id(tdbb, gbl, drq_g_nxt_trg, "RDB$TRIGGER_NAME"));

			request = CMP_find_request(tdbb, drq_f_nxt_trg, DYN_REQUESTS);
			id = drq_f_nxt_trg;

			found = false;
			FOR(REQUEST_HANDLE request)
				FIRST 1 X IN RDB$TRIGGERS WITH X.RDB$TRIGGER_NAME EQ buffer.c_str()

	            if (!DYN_REQUEST(drq_f_nxt_trg))
					DYN_REQUEST(drq_f_nxt_trg) = request;
				found = true;
			END_FOR;

			if (!DYN_REQUEST(drq_f_nxt_trg))
				DYN_REQUEST(drq_f_nxt_trg) = request;
			request = NULL;
		} while (found);
	}
	catch (const Firebird::Exception& ex)
	{
		Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, id);
		DYN_error_punt(true, 83);
		// msg 83: "Generation of trigger name failed"
	}
}


bool DYN_UTIL_find_field_source(thread_db* tdbb,
								Global* gbl,
								const Firebird::MetaName& view_name,
								USHORT context,
								const TEXT* local_name,
								TEXT* output_field_name)
{
/**************************************
 *
 *	D Y N _U T I L _ f i n d _ f i e l d _ s o u r c e
 *
 **************************************
 *
 * Functional description
 *	Find the original source for a view field.
 *
 **************************************/

	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = NULL;

	/* CVC: It seems the logic of this function was changed over time. It's unlikely
	it will cause a failure that leads to call DYN_error_punt(), unless the request finds
	problems due to database corruption or unexpected ODS changes. Under normal
	circumstances, it will return either true or false. When true, we found a field source
	for the view's name/context/field and are loading this value in the last parameter,
	that can be used against rdb$fields' rdb$field_name. */

	bool found = false;

	try {
		request = CMP_find_request(tdbb, drq_l_fld_src2, DYN_REQUESTS);

		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			VRL IN RDB$VIEW_RELATIONS CROSS
				RFR IN RDB$RELATION_FIELDS OVER RDB$RELATION_NAME
				WITH VRL.RDB$VIEW_NAME EQ view_name.c_str() AND
				VRL.RDB$VIEW_CONTEXT EQ context AND
				RFR.RDB$FIELD_NAME EQ local_name

			if (!DYN_REQUEST(drq_l_fld_src2)) {
				DYN_REQUEST(drq_l_fld_src2) = request;
			}

			found = true;
			fb_utils::exact_name_limit(RFR.RDB$FIELD_SOURCE, sizeof(RFR.RDB$FIELD_SOURCE));
			strcpy(output_field_name, RFR.RDB$FIELD_SOURCE);
		END_FOR;
		if (!DYN_REQUEST(drq_l_fld_src2)) {
			DYN_REQUEST(drq_l_fld_src2) = request;
		}

		if (!found)
		{
			request = CMP_find_request(tdbb, drq_l_fld_src3, DYN_REQUESTS);

			FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
				VRL IN RDB$VIEW_RELATIONS CROSS
					PPR IN RDB$PROCEDURE_PARAMETERS
					WITH VRL.RDB$RELATION_NAME EQ PPR.RDB$PROCEDURE_NAME AND
					VRL.RDB$VIEW_NAME EQ view_name.c_str() AND
					VRL.RDB$VIEW_CONTEXT EQ context AND
					PPR.RDB$PARAMETER_TYPE = 1 AND // output
					PPR.RDB$PARAMETER_NAME EQ local_name

				if (!DYN_REQUEST(drq_l_fld_src3)) {
					DYN_REQUEST(drq_l_fld_src3) = request;
				}

				found = true;
				fb_utils::exact_name_limit(PPR.RDB$FIELD_SOURCE, sizeof(PPR.RDB$FIELD_SOURCE));
				strcpy(output_field_name, PPR.RDB$FIELD_SOURCE);
			END_FOR;
			if (!DYN_REQUEST(drq_l_fld_src3)) {
				DYN_REQUEST(drq_l_fld_src3) = request;
			}
		}
	}
	catch (const Firebird::Exception& ex)
	{
		Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 80);
		// msg 80: "Specified domain or source field does not exist"
	}

	return found;
}


bool DYN_UTIL_get_prot(thread_db*		tdbb,
						  Global*		gbl,
						  const SCHAR*	rname,
						  const SCHAR*	fname,
						  SecurityClass::flags_t*	prot_mask)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e t _ p r o t
 *
 **************************************
 *
 * Functional description
 *	Get protection mask for relation or relation_field
 *
 **************************************/
	struct
	{
		SqlIdentifier relation_name;
		SqlIdentifier field_name;
	} in_msg;

	SET_TDBB(tdbb);

	jrd_req* request = CMP_find_request(tdbb, drq_l_prot_mask, DYN_REQUESTS);

	try
	{
		if (!request)
		{
			request = CMP_compile2(tdbb, prot_blr, sizeof(prot_blr), true);
		}
		gds__vtov(rname, in_msg.relation_name, sizeof(in_msg.relation_name));
		gds__vtov(fname, in_msg.field_name, sizeof(in_msg.field_name));
		EXE_start(tdbb, request, gbl->gbl_transaction);
		EXE_send(tdbb, request, 0, sizeof(in_msg), (UCHAR*) &in_msg);
		EXE_receive(tdbb, request, 1, sizeof(SecurityClass::flags_t), (UCHAR*) prot_mask);

		DYN_rundown_request(request, drq_l_prot_mask);

	}
	catch (const Firebird::Exception& ex)
	{
		Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, drq_l_prot_mask);
		return false;
	}
	return true;
}


void DYN_UTIL_store_check_constraints(thread_db*				tdbb,
									  Global*					gbl,
									  const Firebird::MetaName&	constraint_name,
									  const Firebird::MetaName&	trigger_name)
{
/**************************************
 *
 *	D Y N _ U T I L _s t o r e _ c h e c k _ c o n s t r a i n t s
 *
 **************************************
 *
 * Functional description
 *	Fill in rdb$check_constraints the association between a check name and the
 *	system defined trigger that implements that check.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_s_chk_con, DYN_REQUESTS);

	try
	{
		STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			CHK IN RDB$CHECK_CONSTRAINTS
	        strcpy(CHK.RDB$CONSTRAINT_NAME, constraint_name.c_str());
			strcpy(CHK.RDB$TRIGGER_NAME, trigger_name.c_str());

		END_STORE;

		if (!DYN_REQUEST(drq_s_chk_con)) {
			DYN_REQUEST(drq_s_chk_con) = request;
		}
	}
	catch (const Firebird::Exception& ex)
	{
		Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, drq_s_chk_con);
		DYN_error_punt(true, 122);
		// msg 122: "STORE RDB$CHECK_CONSTRAINTS failed"
	}
}


//***************************************
//
// 	D Y N _ U T I L _ i s _ a r r a y
//
//**************************************
//
// Functional description
//	Discover if a given domain (either automatic or explicit) has dimensions.
//
//***************************************
bool DYN_UTIL_is_array(thread_db* tdbb, Global* gbl, const Firebird::MetaName& domain_name)
{
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_dom_is_array, DYN_REQUESTS);

	try
	{
		bool rc = false;
		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			X IN RDB$FIELDS WITH X.RDB$FIELD_NAME EQ domain_name.c_str()

			if (!DYN_REQUEST(drq_dom_is_array))
				DYN_REQUEST(drq_dom_is_array) = request;

			rc = !X.RDB$DIMENSIONS.NULL && X.RDB$DIMENSIONS > 0;
		END_FOR

		if (!DYN_REQUEST(drq_dom_is_array))
			DYN_REQUEST(drq_dom_is_array) = request;

		return rc;
	}
	catch (const Firebird::Exception& ex)
	{
		Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, drq_dom_is_array);
		DYN_error_punt(true, 227, domain_name.c_str());
		// msg 227: "DYN_UTIL_is_array failed for domain %s"
		return false; // Keep compiler happy.
	}
}


//***************************************
//
// 	D Y N _ U T I L _ c o p y _ d o m a i n
//
//**************************************
//
// Functional description
//	Copy a domain in another. More reliable than using dyn_fld as intermediate.
//  The source cannot be an array domain.
//  We don't copy the default, it may be useless work.
//
//***************************************
void DYN_UTIL_copy_domain(thread_db* tdbb,
						  Global* gbl,
						  const Firebird::MetaName& org_name,
						  const Firebird::MetaName& new_name)
{
	SET_TDBB(tdbb);
	//Database* dbb = tdbb->getDatabase();

	jrd_req* request = NULL; //CMP_find_request(tdbb, drq_dom_copy, DYN_REQUESTS);
	jrd_req* req2 = NULL;

	try
	{
		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			X IN RDB$FIELDS WITH X.RDB$FIELD_NAME EQ org_name.c_str()

			//if (!DYN_REQUEST(drq_dom_copy))
			//	DYN_REQUEST(drq_dom_copy) = request;

			if (!X.RDB$DIMENSIONS.NULL && X.RDB$DIMENSIONS > 0)
				ERR_punt();

			STORE(REQUEST_HANDLE req2 TRANSACTION_HANDLE gbl->gbl_transaction)
				X2 IN RDB$FIELDS

				X2.RDB$FIELD_NAME.NULL = FALSE;
				strcpy(X2.RDB$FIELD_NAME, new_name.c_str());

				X2.RDB$QUERY_NAME.NULL = X.RDB$QUERY_NAME.NULL;
				if (!X.RDB$QUERY_NAME.NULL)
					strcpy(X2.RDB$QUERY_NAME, X.RDB$QUERY_NAME);

				// The following fields may require blob copying:
				// rdb$validation_blr, rdb$validation_source,
				// rdb$computed_blr, rdb$computed_source,
				// rdb$default_value, rdb$default_source,
				// rdb$missing_value,  rdb$missing_source,
				// rdb$description and rdb$query_header.

				X2.RDB$VALIDATION_BLR.NULL    = X.RDB$VALIDATION_BLR.NULL;
				X2.RDB$VALIDATION_BLR         = X.RDB$VALIDATION_BLR;

				X2.RDB$VALIDATION_SOURCE.NULL = X.RDB$VALIDATION_SOURCE.NULL;
				X2.RDB$VALIDATION_SOURCE      = X.RDB$VALIDATION_SOURCE;

				X2.RDB$COMPUTED_BLR.NULL      = X.RDB$COMPUTED_BLR.NULL;
				X2.RDB$COMPUTED_BLR           = X.RDB$COMPUTED_BLR;

				X2.RDB$COMPUTED_SOURCE.NULL   = X.RDB$COMPUTED_SOURCE.NULL;
				X2.RDB$COMPUTED_SOURCE        = X.RDB$COMPUTED_SOURCE;

				//X2.RDB$DEFAULT_VALUE.NULL     = X.RDB$DEFAULT_VALUE.NULL;
				//X2.RDB$DEFAULT_VALUE          = X.RDB$DEFAULT_VALUE;

				//X2.RDB$DEFAULT_SOURCE.NULL    = X.RDB$DEFAULT_SOURCE.NULL;
				//X2.RDB$DEFAULT_SOURCE         = X.RDB$DEFAULT_SOURCE;

				X2.RDB$FIELD_LENGTH.NULL      = X.RDB$FIELD_LENGTH.NULL;
				X2.RDB$FIELD_LENGTH           = X.RDB$FIELD_LENGTH;

				X2.RDB$FIELD_SCALE.NULL       = X.RDB$FIELD_SCALE.NULL;
				X2.RDB$FIELD_SCALE            = X.RDB$FIELD_SCALE;

				X2.RDB$FIELD_TYPE.NULL        = X.RDB$FIELD_TYPE.NULL;
				X2.RDB$FIELD_TYPE             = X.RDB$FIELD_TYPE;

				X2.RDB$FIELD_SUB_TYPE.NULL    = X.RDB$FIELD_SUB_TYPE.NULL;
				X2.RDB$FIELD_SUB_TYPE         = X.RDB$FIELD_SUB_TYPE;

				X2.RDB$MISSING_VALUE.NULL     = X.RDB$MISSING_VALUE.NULL;
				X2.RDB$MISSING_VALUE          = X.RDB$MISSING_VALUE;

				X2.RDB$MISSING_SOURCE.NULL    = X.RDB$MISSING_SOURCE.NULL;
				X2.RDB$MISSING_SOURCE         = X.RDB$MISSING_SOURCE;

				X2.RDB$DESCRIPTION.NULL       = X.RDB$DESCRIPTION.NULL;
				X2.RDB$DESCRIPTION            = X.RDB$DESCRIPTION;

				X2.RDB$SYSTEM_FLAG.NULL       = X.RDB$SYSTEM_FLAG.NULL;
				X2.RDB$SYSTEM_FLAG            = X.RDB$SYSTEM_FLAG;

				X2.RDB$QUERY_HEADER.NULL      = X.RDB$QUERY_HEADER.NULL;
				X2.RDB$QUERY_HEADER           = X.RDB$QUERY_HEADER;

				X2.RDB$SEGMENT_LENGTH.NULL    = X.RDB$SEGMENT_LENGTH.NULL;
				X2.RDB$SEGMENT_LENGTH         = X.RDB$SEGMENT_LENGTH;

				X2.RDB$EDIT_STRING.NULL       = X.RDB$EDIT_STRING.NULL;
				if (!X.RDB$EDIT_STRING.NULL)
					strcpy(X2.RDB$EDIT_STRING, X.RDB$EDIT_STRING);

				X2.RDB$EXTERNAL_LENGTH.NULL   = X.RDB$EXTERNAL_LENGTH.NULL;
				X2.RDB$EXTERNAL_LENGTH        = X.RDB$EXTERNAL_LENGTH;

				X2.RDB$EXTERNAL_SCALE.NULL    = X.RDB$EXTERNAL_SCALE.NULL;
				X2.RDB$EXTERNAL_SCALE         = X.RDB$EXTERNAL_SCALE;

				X2.RDB$EXTERNAL_TYPE.NULL     = X.RDB$EXTERNAL_TYPE.NULL;
				X2.RDB$EXTERNAL_TYPE          = X.RDB$EXTERNAL_TYPE;

				X2.RDB$DIMENSIONS.NULL        = X.RDB$DIMENSIONS.NULL;
				X2.RDB$DIMENSIONS             = X.RDB$DIMENSIONS;

				X2.RDB$NULL_FLAG.NULL         = X.RDB$NULL_FLAG.NULL;
				X2.RDB$NULL_FLAG              = X.RDB$NULL_FLAG;

				X2.RDB$CHARACTER_LENGTH.NULL  = X.RDB$CHARACTER_LENGTH.NULL;
				X2.RDB$CHARACTER_LENGTH       = X.RDB$CHARACTER_LENGTH;

				X2.RDB$COLLATION_ID.NULL      = X.RDB$COLLATION_ID.NULL;
				X2.RDB$COLLATION_ID           = X.RDB$COLLATION_ID;

				X2.RDB$CHARACTER_SET_ID.NULL  = X.RDB$CHARACTER_SET_ID.NULL;
				X2.RDB$CHARACTER_SET_ID       = X.RDB$CHARACTER_SET_ID;

				X2.RDB$FIELD_PRECISION.NULL   = X.RDB$FIELD_PRECISION.NULL;
				X2.RDB$FIELD_PRECISION        = X.RDB$FIELD_PRECISION;
			END_STORE
			CMP_release(tdbb, req2);
			req2 = NULL;
		END_FOR

		CMP_release(tdbb, request);
		request = NULL;
		// For now, CMP_release used instead of this:
		//if (!DYN_REQUEST(drq_dom_copy))
		//	DYN_REQUEST(drq_dom_copy) = request;

	}
	catch (const Firebird::Exception& ex)
	{
		Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1); //drq_dom_copy);
		DYN_error_punt(true, 228, org_name.c_str());
		// msg 228: "DYN_UTIL_copy_domain failed for domain %s"
	}
}
