/*
 *	PROGRAM:	JRD Data Definition Utility
 *	MODULE:		dyn_mod.epp
 *	DESCRIPTION:	Dynamic data definition - DYN_modify_<x>
 *
 * 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): ______________________________________.
 * 2001.5.20: Claudio Valderrama: when changing a domain's name,
 *	if it has dimensions, rdb$field_dimensions should be updated, too.
 * 2001.5.23: Claudio Valderrama: Forbid zero length identifiers,
 *	they are not ANSI SQL compliant.
 * 2001.5.27 Claudio Valderrama: Prevent rdb$field_length from going
 *	out of sync when toggling between char and varchar data types.
 *	This caused check_update_fld_type() to lose ability to detect potentially
 *	dangerous changes. For example, you could alter a field or a domain and
 *	be able to change char(10) to varchar(8).
 *	Unfortunately, Borland chose to have DYN_modify_global_field() and add to the
 *	party DYN_modify_sql_field(); therefore bug fixes should be done twice.
 * 2001.10.08 Claudio Valderrama: put a comment with suggested code to hide
 *	special non-system triggers from user manipulation.
 * 2002-02-24 Sean Leyne - Code Cleanup of old Win 3.1 port (WINDOWS_ONLY)
 * 2002.08.10 Dmitry Yemanov: ALTER VIEW
 */

#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/ini.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_df_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/met_proto.h"
#include "../jrd/vio_proto.h"
#include "../jrd/dsc_proto.h"
#include "../common/utils_proto.h"
#include "../dsql/DdlNodes.h"
#include "../dsql/metd_proto.h"

using MsgFormat::SafeArg;

using namespace Jrd;
using namespace Firebird;


DATABASE DB = STATIC "ODS.RDB";

//const int MAX_CHARS_SHORT	= 6;		// 2**16  = 5 chars + sign
//const int MAX_CHARS_LONG	= 11;		// 2**32  = 10 chars + sign
//const int MAX_CHARS_INT64	= 20;		// 2**64  = 19 chars + sign
//const int MAX_CHARS_DOUBLE	= 22;	// 15 digits + 2 signs + E + decimal + 3 digit exp
//const int MAX_CHARS_FLOAT	= 13;		// 7 digits + 2 signs + E + decimal + 2 digit exp

static const UCHAR alloc_info[] = { isc_info_allocation, isc_info_end };
static void change_backup_mode(Global*, UCHAR verb);

static void modify_lfield_position(thread_db*, Global*, const MetaName&, const MetaName&,
	USHORT, USHORT);
static bool check_view_dependency(thread_db*, Global*, const MetaName&, const MetaName&);
static bool check_sptrig_dependency(thread_db*, Global*,  const MetaName&, const MetaName&);
static void modify_lfield_index(thread_db*, Global*, const MetaName&, const MetaName&,
	const MetaName&);
static bool field_exists(thread_db*, Global*, const MetaName&, const MetaName&);
static bool domain_exists(thread_db*, Global*, const MetaName&);
static void get_domain_type(thread_db*, Global*, dyn_fld&);
static ULONG check_update_fld_type(const dyn_fld&, const dyn_fld&);
static ULONG check_update_numeric_type(const dyn_fld&, const dyn_fld&);
static void modify_err_punt(thread_db*, ULONG, const dyn_fld&, const dyn_fld&);


// ***********************************
// D Y N _ m o d i f y _ c h a r s e t
// ***********************************
// Its purpose is to change the comment in the charset's record.
void DYN_modify_charset(Global* gbl, const UCHAR** ptr)
{
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_chset, DYN_REQUESTS);
	bool found = false;

	SqlIdentifier t;
	GET_STRING(ptr, t);

	try {
		found = false;
		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			X IN RDB$CHARACTER_SETS
				WITH X.RDB$CHARACTER_SET_NAME EQ t

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

			found = true;
			MODIFY X
				UCHAR verb;
				while ((verb = *(*ptr)++) != isc_dyn_end)
				{
					switch (verb)
					{
					case isc_dyn_description:
						if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
							X.RDB$DESCRIPTION.NULL = FALSE;
						else
							X.RDB$DESCRIPTION.NULL = TRUE;
						break;

					default:
						DYN_unsupported_verb();
					}
				}
			END_MODIFY;
		END_FOR;

		if (!DYN_REQUEST(drq_m_chset))
			DYN_REQUEST(drq_m_chset) = request;
	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 85);
		// msg 85: "MODIFY RDB$CHARACTER_SETS failed"
	}

	if (!found)
	{
		DYN_error_punt(false, 151, t);
		// msg 151: "Character set %s not found"
	}
}


// ***************************************
// D Y N _ m o d i f y _ c o l l a t i o n
// ***************************************
// Its purpose is to change the comment in the collation's record.
void DYN_modify_collation(Global* gbl, const UCHAR** ptr)
{
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_coll, DYN_REQUESTS);
	bool found = false;

	SqlIdentifier t;
	GET_STRING(ptr, t);

	try {
		found = false;
		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			X IN RDB$COLLATIONS
				WITH X.RDB$COLLATION_NAME EQ t

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

			found = true;
			MODIFY X
				UCHAR verb;
				while ((verb = *(*ptr)++) != isc_dyn_end)
				{
					switch (verb)
					{
					case isc_dyn_description:
						if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
							X.RDB$DESCRIPTION.NULL = FALSE;
						else
							X.RDB$DESCRIPTION.NULL = TRUE;
						break;

					default:
						DYN_unsupported_verb();
					}
				}
			END_MODIFY;
		END_FOR;

		if (!DYN_REQUEST(drq_m_coll))
			DYN_REQUEST(drq_m_coll) = request;
	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 86);
		// msg 86: "MODIFY RDB$COLLATIONS failed"
	}

	if (!found)
	{
		DYN_error_punt(false, 152, t);
		// msg 152: "Collation %s not found"
	}
}


void DYN_modify_database( Global* gbl, const UCHAR** ptr)
{
/**************************************
 *
 *	D Y N _ m o d i f y _ d a t a b a s e
 *
 **************************************
 *
 * Functional description
 *	Modify a database.
 *
 **************************************/
	UCHAR s[128];

	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = NULL;

	try {

	INF_database_info(alloc_info, sizeof(alloc_info), s, sizeof(s));

	if (s[0] != isc_info_allocation) {
		goto dyn_punt_84;
	}

	request = CMP_find_request(tdbb, drq_m_database, DYN_REQUESTS);

	const SSHORT length = gds__vax_integer(s + 1, 2);
	SLONG start = gds__vax_integer(s + 3, length);

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		DBB IN RDB$DATABASE

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

		MODIFY DBB USING
			UCHAR verb;
			while ((verb = *(*ptr)++) != isc_dyn_end)
				switch (verb)
				{
				case isc_dyn_security_class:
					if (GET_STRING(ptr, DBB.RDB$SECURITY_CLASS))
						DBB.RDB$SECURITY_CLASS.NULL = FALSE;
					else
						DBB.RDB$SECURITY_CLASS.NULL = TRUE;
					break;

				case isc_dyn_description:
					if (DYN_put_text_blob(gbl, ptr, &DBB.RDB$DESCRIPTION))
						DBB.RDB$DESCRIPTION.NULL = FALSE;
					else
						DBB.RDB$DESCRIPTION.NULL = TRUE;
					break;

				case isc_dyn_def_file:
					DYN_define_file(gbl, ptr, (SLONG) 0, &start, 84);
					break;

				case isc_dyn_def_difference:
					DYN_define_difference(gbl, ptr);
					break;

				case isc_dyn_drop_difference:
				case isc_dyn_begin_backup:
				case isc_dyn_end_backup:
					change_backup_mode(gbl, verb);
					break;

				case isc_dyn_fld_character_set_name:
					if (GET_STRING(ptr, DBB.RDB$CHARACTER_SET_NAME))
						DBB.RDB$CHARACTER_SET_NAME.NULL = FALSE;
					else
						DBB.RDB$CHARACTER_SET_NAME.NULL = TRUE;
					break;

				case isc_dyn_fld_collation:
					{
						MetaName collation;
						GET_STRING(ptr, collation);

						if (!DBB.RDB$CHARACTER_SET_NAME.NULL)
						{
							AlterCharSetNode node(*tdbb->getDefaultPool(),
								DBB.RDB$CHARACTER_SET_NAME, collation);
							node.execute(tdbb, gbl->gbl_transaction);
						}
					}
					break;

				default:
					--(*ptr);
					DYN_execute(gbl, ptr, NULL, NULL, NULL, NULL, NULL);
				}
		END_MODIFY;
	END_FOR;

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

	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 84);
		// msg 84: "MODIFY DATABASE failed"
	}

	return;

dyn_punt_84:
	DYN_error_punt(true, 84);
	// msg 84: "MODIFY DATABASE failed"
}


void DYN_modify_exception( Global* gbl, const UCHAR** ptr)
{
/**************************************
 *
 *	D Y N _ m o d i f y _ e x c e p t i o n
 *
 **************************************
 *
 * Functional description
 *	Modify an exception.
 *
 **************************************/
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_xcp, DYN_REQUESTS);
	bool found = false;

	try {

	MetaName t;
	GET_STRING(ptr, t);

	found = false;
	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		X IN RDB$EXCEPTIONS
			WITH X.RDB$EXCEPTION_NAME EQ t.c_str()

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

		found = true;
		MODIFY X
			UCHAR verb;
			while ((verb = *(*ptr)++) != isc_dyn_end)
				switch (verb)
				{
				case isc_dyn_xcp_msg:
					GET_BYTES(ptr, X.RDB$MESSAGE);
					X.RDB$MESSAGE.NULL = FALSE;
					break;

				case isc_dyn_description:
					if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
						X.RDB$DESCRIPTION.NULL = FALSE;
					else
						X.RDB$DESCRIPTION.NULL = TRUE;
					break;

				default:
					DYN_unsupported_verb();
				}
		END_MODIFY;
	END_FOR;

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

	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 145);
		// msg 145: "MODIFY EXCEPTION failed"
	}

	if (!found)
	{
		DYN_error_punt(false, 144);
		// msg 144: "Exception not found"
	}
}


// *********************************
// D Y N _ m o d i f y _ f i l t e r
// *********************************
// Its purpose is to change the comment in the filter's record.
void DYN_modify_filter(Global* gbl, const UCHAR** ptr)
{
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_bfil, DYN_REQUESTS);
	bool found = false;

	SqlIdentifier t;
	GET_STRING(ptr, t);

	try {
		found = false;
		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			X IN RDB$FILTERS
				WITH X.RDB$FUNCTION_NAME EQ t

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

			found = true;
			MODIFY X
				UCHAR verb;
				while ((verb = *(*ptr)++) != isc_dyn_end)
				{
					switch (verb)
					{
					case isc_dyn_description:
						if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
							X.RDB$DESCRIPTION.NULL = FALSE;
						else
							X.RDB$DESCRIPTION.NULL = TRUE;
						break;

					// Other cases should go there, like modifying the entry point or module name.
					default:
						DYN_unsupported_verb();
					}
				}
			END_MODIFY;
		END_FOR;

		if (!DYN_REQUEST(drq_m_bfil))
			DYN_REQUEST(drq_m_bfil) = request;
	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 88);
		// msg 88: "MODIFY RDB$BLOB_FILTERS failed"
	}

	if (!found)
	{
		DYN_error_punt(false, 37, t);
		// msg 37: "Blob Filter %s not found"
	}
}


// *************************************
// D Y N _ m o d i f y _ f u n c t i o n
// *************************************
// Its purpose is to change the comment in the function's record and to
// allow changing the entry point and/or the module name.
void DYN_modify_function(Global* gbl, const UCHAR** ptr)
{
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_fun, DYN_REQUESTS);
	bool found = false;

	SqlIdentifier t;
	GET_STRING(ptr, t);

	try {
		found = false;
		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			X IN RDB$FUNCTIONS
				WITH X.RDB$FUNCTION_NAME EQ t

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

			found = true;
			MODIFY X
				UCHAR verb;
				while ((verb = *(*ptr)++) != isc_dyn_end)
				{
					switch (verb)
					{
					case isc_dyn_description:
						if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
							X.RDB$DESCRIPTION.NULL = FALSE;
						else
							X.RDB$DESCRIPTION.NULL = TRUE;
						break;

					case isc_dyn_func_module_name:
						GET_STRING(ptr, X.RDB$MODULE_NAME);
						X.RDB$MODULE_NAME.NULL = FALSE;
						break;

					case isc_dyn_func_entry_point:
						GET_STRING(ptr, X.RDB$ENTRYPOINT);
						X.RDB$ENTRYPOINT.NULL = FALSE;
						break;

					default:
						DYN_unsupported_verb();
					}
				}
			END_MODIFY;
		END_FOR;

		if (!DYN_REQUEST(drq_m_fun))
			DYN_REQUEST(drq_m_fun) = request;
	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 92);
		// msg 92: "MODIFY RDB$FUNCTIONS failed"
	}

	if (!found)
	{
		DYN_error_punt(false, 41, t);
		// msg 41: "Function %s not found"
	}
}


// ***************************************
// D Y N _ m o d i f y _ g e n e r a t o r
// ***************************************
// Its purpose is to change the comment in the generator's record.
void DYN_modify_generator(Global* gbl, const UCHAR** ptr)
{
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_gen, DYN_REQUESTS);
	bool found = false;
	SqlIdentifier t;
	GET_STRING(ptr, t);

	try {
		found = false;
		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			X IN RDB$GENERATORS
				WITH X.RDB$GENERATOR_NAME EQ t

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

			found = true;
			MODIFY X
				UCHAR verb;
				while ((verb = *(*ptr)++) != isc_dyn_end)
				{
					switch (verb)
					{
					case isc_dyn_description:
						if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
							X.RDB$DESCRIPTION.NULL = FALSE;
						else
							X.RDB$DESCRIPTION.NULL = TRUE;
						break;

					default:
						DYN_unsupported_verb();
					}
				}
			END_MODIFY;
		END_FOR;

		if (!DYN_REQUEST(drq_m_gen))
			DYN_REQUEST(drq_m_gen) = request;
	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 94);
		// msg 94: "MODIFY GENERATOR failed"
	}

	if (!found)
	{
		DYN_error_punt(false, 214, t);
		// msg 214: "Generator %s not found"
	}
}


void DYN_modify_global_field(Global* gbl,
							 const UCHAR** ptr,
							 const MetaName* relation_name,
							 MetaName* field_name)
{
/**************************************
 *
 *	D Y N _ m o d i f y _ g l o b a l _ f i e l d
 *
 **************************************
 *
 * Functional description
 *	Execute a dynamic ddl statement.
 * Note: a global field here is a SQL domain.
 *
 **************************************/
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_gfield, DYN_REQUESTS);
	bool found = false;

	dyn_fld orig_dom, new_dom;

	try {

	bool dtype, scale, prec, subtype, charlen, collation, fldlen, nullflg, charset;
	dtype = scale = prec = subtype = charlen = collation = fldlen = nullflg = charset = false;

	bool bqryname, bqryhdr, bedtstr, bmissingval, bfldvald, bfldvaldsrc, bfielddesc,
		bdelvald, bdeldflt, bflddftval, bflddfltsrc;
	bqryname = bqryhdr = bedtstr = bmissingval = false;
	bfldvald = bfldvaldsrc = bfielddesc = bdelvald = bdeldflt = bflddftval = bflddfltsrc = false;

	const TEXT *qryname, *edtstr;
	const UCHAR *qryhdr, *missingval, *fldvald, *fldvaldsrc, *fielddesc, *flddftval, *flddfltsrc;

	GET_STRING(ptr, orig_dom.dyn_fld_name);

	found = false;
	int field_adjusted_count = 0;

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ orig_dom.dyn_fld_name.c_str()

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

		found = true;

		DSC_make_descriptor(&orig_dom.dyn_dsc,
							FLD.RDB$FIELD_TYPE,
							FLD.RDB$FIELD_SCALE,
							FLD.RDB$FIELD_LENGTH,
							FLD.RDB$FIELD_SUB_TYPE,
							FLD.RDB$CHARACTER_SET_ID, FLD.RDB$COLLATION_ID);

		orig_dom.dyn_charbytelen = FLD.RDB$FIELD_LENGTH;
		orig_dom.dyn_dtype = FLD.RDB$FIELD_TYPE;
		orig_dom.dyn_precision = FLD.RDB$FIELD_PRECISION;
		orig_dom.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE;
		orig_dom.dyn_charlen = FLD.RDB$CHARACTER_LENGTH;
		orig_dom.dyn_collation = FLD.RDB$COLLATION_ID;
		orig_dom.dyn_null_flag = FLD.RDB$NULL_FLAG != 0;

		/* If the original field type is an array, force its blr type to blr_blob */
		bool has_dimensions = false;
		if (FLD.RDB$DIMENSIONS != 0)
		{
			orig_dom.dyn_dtype = blr_blob;
			has_dimensions = true;
		}

		bool single_validate = false;

		UCHAR verb;
		while ((verb = *(*ptr)++) != isc_dyn_end)
		{
			switch (verb)
			{
			case isc_dyn_fld_name:
				{
					MetaName newfld;

					if (GET_STRING(ptr, newfld))
					{
						if (!domain_exists(tdbb, gbl, newfld))
						{
							MODIFY FLD USING
								strcpy(FLD.RDB$FIELD_NAME, newfld.c_str());
								FLD.RDB$FIELD_NAME.NULL = FALSE;
								jrd_req* old_request = request;
								request = NULL;
								/* CVC: Let's update the dimensions, too. */
								if (has_dimensions)
								{
									FOR (REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
										DIM_DOM IN RDB$FIELD_DIMENSIONS
										WITH DIM_DOM.RDB$FIELD_NAME EQ orig_dom.dyn_fld_name.c_str()
										MODIFY DIM_DOM USING
											strcpy (DIM_DOM.RDB$FIELD_NAME, newfld.c_str());
											DIM_DOM.RDB$FIELD_NAME.NULL = FALSE;
										END_MODIFY;
									END_FOR;
									CMP_release (tdbb, request);
									request = NULL;
								}
								/* CVC: End modification. */
								FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
									DOM IN RDB$RELATION_FIELDS
									WITH DOM.RDB$FIELD_SOURCE EQ orig_dom.dyn_fld_name.c_str()
									MODIFY DOM USING
										strcpy(DOM.RDB$FIELD_SOURCE, newfld.c_str());
										DOM.RDB$FIELD_SOURCE.NULL = FALSE;
									END_MODIFY;
									modify_lfield_index(tdbb, gbl,
														DOM.RDB$RELATION_NAME,
														DOM.RDB$FIELD_NAME,
														DOM.RDB$FIELD_NAME);
								END_FOR;
								CMP_release(tdbb, request);
								request = old_request;
							END_MODIFY;
						}
						else
						{
							DYN_error_punt(false, 204, SafeArg() << orig_dom.dyn_fld_name.c_str() <<
										   newfld.c_str());
							/* msg 204: Cannot rename domain %s to %s.  A domain with that name already exists. */
						}
					}
					else
					{
						DYN_error_punt(false, 212);
						/* msg 212: "Zero length identifiers not allowed" */
					}
					break;
				}

			case isc_dyn_rel_name:
				GET_STRING(ptr, new_dom.dyn_rel_name);
				break;

			/* CVC: The syntax for DDL alter domain was accepting multiple
					changes in one command even to the same features, IE two length alterations.
					This repetitive type change will cause havoc so it should be stopped in the future. */

			case isc_dyn_fld_length:
				fldlen = true;
				new_dom.dyn_dsc.dsc_length = DYN_get_number(ptr);
				if (++field_adjusted_count > 2)
				{
					EXE_unwind(tdbb, request);
					DYN_error_punt(false, 148, orig_dom.dyn_fld_name.c_str());
					// msg 148: "Only one data type change to the domain %s allowed at a time"
				}
				switch (new_dom.dyn_dtype)
				{

				case blr_text:
				case blr_text2:
				case blr_varying:
				case blr_varying2:
				case blr_cstring:
				case blr_cstring2:
					new_dom.dyn_charbytelen = new_dom.dyn_dsc.dsc_length;
					break;
				default:
					new_dom.dyn_charbytelen = 0; // It won't be used, anyway.
					break;
				}
				break;

			case isc_dyn_fld_type:
				dtype = true;
				new_dom.dyn_dtype = DYN_get_number(ptr);
				if (++field_adjusted_count > 2)
				{
					EXE_unwind(tdbb, request);
					DYN_error_punt(false, 148, orig_dom.dyn_fld_name.c_str());
					// msg 148: "Only one data type change to the domain %s allowed at a time"
				}

				switch (new_dom.dyn_dtype)
				{

				case blr_text:
				case blr_text2:
				case blr_varying:
				case blr_varying2:
				case blr_cstring:
				case blr_cstring2:
					if (new_dom.dyn_dsc.dsc_length && !new_dom.dyn_charbytelen)
						new_dom.dyn_charbytelen = new_dom.dyn_dsc.dsc_length;
					new_dom.dyn_dsc.dsc_length = DSC_string_length(&new_dom.dyn_dsc);
					break;
				case blr_short:
					new_dom.dyn_dsc.dsc_length = 2;
					break;

				case blr_long:
				case blr_float:
					new_dom.dyn_dsc.dsc_length = 4;
					break;

				case blr_int64:
				case blr_sql_time:
				case blr_sql_date:
				case blr_timestamp:
				case blr_double:
				case blr_d_float:
					new_dom.dyn_dsc.dsc_length = 8;
					break;

				default:
					break;
				}
				break;

			case isc_dyn_fld_scale:
				scale = true;
				new_dom.dyn_dsc.dsc_scale = DYN_get_number(ptr);
				break;

			case isc_dyn_fld_precision:
				prec = true;
				new_dom.dyn_precision = DYN_get_number(ptr);
				break;

			case isc_dyn_fld_sub_type:
				subtype = true;
				new_dom.dyn_sub_type = DYN_get_number(ptr);
				break;

			case isc_dyn_fld_char_length:
				charlen = true;
				new_dom.dyn_charlen = DYN_get_number(ptr);
				break;

			case isc_dyn_fld_collation:
				collation = true;
				new_dom.dyn_collation = DYN_get_number(ptr);
				break;

			case isc_dyn_fld_character_set:
				charset = true;
				new_dom.dyn_charset = DYN_get_number(ptr);
				break;

			case isc_dyn_fld_not_null:
				nullflg = true;
				new_dom.dyn_null_flag = true;
				break;

			case isc_dyn_fld_query_name:
				qryname = (TEXT*) * ptr;
				bqryname = true;
				DYN_skip_attribute(ptr);
				break;

			case isc_dyn_fld_query_header:
				qryhdr = *ptr;
				bqryhdr = true;
				DYN_skip_attribute(ptr);
				break;

			case isc_dyn_fld_edit_string:
				edtstr = (TEXT*) * ptr;
				bedtstr = true;
				DYN_skip_attribute(ptr);
				break;

			case isc_dyn_fld_missing_value:
				missingval = *ptr;
				bmissingval = true;
				DYN_skip_attribute(ptr);
				break;

			case isc_dyn_single_validation:
				if (single_validate) {
					EXE_unwind(tdbb, request);
					DYN_error_punt(false, 160);
					/* msg 160: "Only one constraint allowed for a domain" */
					break;
				}

				single_validate = true;
				break;

			case isc_dyn_fld_validation_blr:
				if (single_validate && (!FLD.RDB$VALIDATION_BLR.NULL)) {
					EXE_unwind(tdbb, request);
					DYN_error_punt(false, 160);
					/* msg 160: "Only one constraint allowed for a domain" */
					break;
				}

				single_validate = true;
				fldvald = *ptr;
				bfldvald = true;
				DYN_skip_attribute(ptr);
				break;

			case isc_dyn_fld_validation_source:
				fldvaldsrc = *ptr;
				bfldvaldsrc = true;
				DYN_skip_attribute(ptr);
				break;

			case isc_dyn_description:
				fielddesc = *ptr;
				bfielddesc = true;
				DYN_skip_attribute(ptr);
				break;

			case isc_dyn_del_validation:
				bdelvald = true;
				break;

			case isc_dyn_del_default:
				bdeldflt = true;
				break;

			case isc_dyn_fld_default_value:
				if (has_dimensions)
				{
					DYN_error_punt(false, 226, orig_dom.dyn_fld_name.c_str());
					// msg 226: "Default value is not allowed for array type in domain %s"
				}
				flddftval = *ptr;
				bflddftval = true;
				DYN_skip_attribute(ptr);
				break;

			case isc_dyn_fld_default_source:
				if (has_dimensions)
				{
					DYN_error_punt(false, 226, orig_dom.dyn_fld_name.c_str());
					// msg 226: "Default value is not allowed for array type in domain %s"
				}
				flddfltsrc = *ptr;
				bflddfltsrc = true;
				DYN_skip_attribute(ptr);
				break;

			case isc_dyn_fld_dimensions:
				new_dom.dyn_dtype = blr_blob;
				break;

				/* These should only be defined for BLOB types and should not come through with
				 * any other types.  Do nothing with them.
				 */

			case isc_dyn_fld_segment_length:
				DYN_get_number(ptr);
				break;

			default:
				--(*ptr);
				DYN_execute(gbl, ptr, relation_name, field_name, NULL, NULL, NULL);
			}
		}


		/* Now that we have all of the information needed, let's check to see if the field type can be modifed.
		 * Only do this, however, if we are actually modifying the datatype of the domain.
		 */

		if (dtype) {
			DSC_make_descriptor(&new_dom.dyn_dsc,
								new_dom.dyn_dtype,
								new_dom.dyn_dsc.dsc_scale,
								new_dom.dyn_dsc.dsc_length,
								new_dom.dyn_sub_type,
								new_dom.dyn_charset,
								new_dom.dyn_collation);
			const ULONG retval = check_update_fld_type(orig_dom, new_dom);
			if (retval != FB_SUCCESS)
				modify_err_punt(tdbb, retval, orig_dom, new_dom);
		}

		MODIFY FLD USING
			if (dtype) {
				FLD.RDB$FIELD_TYPE = new_dom.dyn_dtype;
				FLD.RDB$FIELD_TYPE.NULL = FALSE;

				/* If the datatype was changed, update any indexes that involved the domain */

				jrd_req* old_request = request;
				request = NULL;
				FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
					DOM IN RDB$RELATION_FIELDS
					WITH DOM.RDB$FIELD_SOURCE EQ orig_dom.dyn_fld_name.c_str()
					modify_lfield_index(tdbb, gbl,
										DOM.RDB$RELATION_NAME,
										DOM.RDB$FIELD_NAME,
										DOM.RDB$FIELD_NAME);
				END_FOR;

				CMP_release(tdbb, request);
				request = old_request;
			}

			if (scale) {
				FLD.RDB$FIELD_SCALE = new_dom.dyn_dsc.dsc_scale;
				FLD.RDB$FIELD_SCALE.NULL = FALSE;
			}

			if (prec) {
				FLD.RDB$FIELD_PRECISION = new_dom.dyn_precision;
				FLD.RDB$FIELD_PRECISION.NULL = FALSE;
			}

			if (subtype) {
				FLD.RDB$FIELD_SUB_TYPE = new_dom.dyn_sub_type;
				FLD.RDB$FIELD_SUB_TYPE.NULL = FALSE;
			}

			if (charlen) {
				FLD.RDB$CHARACTER_LENGTH = new_dom.dyn_charlen;
				FLD.RDB$CHARACTER_LENGTH.NULL = FALSE;
			}

			if (charset) {
				FLD.RDB$CHARACTER_SET_ID = new_dom.dyn_charset;
				FLD.RDB$CHARACTER_SET_ID.NULL = FALSE;
			}

			if (collation) {
				FLD.RDB$COLLATION_ID = new_dom.dyn_collation;
				FLD.RDB$COLLATION_ID.NULL = FALSE;
			}

			if (fldlen) {
				/* CVC: Rescue from the wrong field_length with a helper. */
				if (new_dom.dyn_dsc.dsc_dtype <= dtype_varying && new_dom.dyn_charbytelen)
					FLD.RDB$FIELD_LENGTH = new_dom.dyn_charbytelen;
				else
					FLD.RDB$FIELD_LENGTH = new_dom.dyn_dsc.dsc_length;
				FLD.RDB$FIELD_LENGTH.NULL = FALSE;
			}

			if (bqryname) {
				if (GET_STRING(&qryname, FLD.RDB$QUERY_NAME))
					FLD.RDB$QUERY_NAME.NULL = FALSE;
				else
					FLD.RDB$QUERY_NAME.NULL = TRUE;
			}

			if (bqryhdr) {
				if (DYN_put_blr_blob(gbl, &qryhdr, &FLD.RDB$QUERY_HEADER))
					FLD.RDB$QUERY_HEADER.NULL = FALSE;
				else
					FLD.RDB$QUERY_HEADER.NULL = TRUE;
			}

			if (bedtstr) {
				if (GET_STRING(&edtstr, FLD.RDB$EDIT_STRING))
					FLD.RDB$EDIT_STRING.NULL = FALSE;
				else
					FLD.RDB$EDIT_STRING.NULL = TRUE;
			}

			if (bmissingval) {
				if (DYN_put_blr_blob(gbl, &missingval, &FLD.RDB$MISSING_VALUE))
					FLD.RDB$MISSING_VALUE.NULL = FALSE;
				else
					FLD.RDB$MISSING_VALUE.NULL = TRUE;
			}

			if (bfldvald) {
				if (DYN_put_blr_blob(gbl, &fldvald, &FLD.RDB$VALIDATION_BLR))
					FLD.RDB$VALIDATION_BLR.NULL = FALSE;
				else
					FLD.RDB$VALIDATION_BLR.NULL = TRUE;
			}

			if (bfldvaldsrc) {
				if (DYN_put_text_blob(gbl, &fldvaldsrc, &FLD.RDB$VALIDATION_SOURCE))
				{
					FLD.RDB$VALIDATION_SOURCE.NULL = FALSE;
				}
				else
					FLD.RDB$VALIDATION_SOURCE.NULL = TRUE;
			}

			if (bfielddesc) {
				if (DYN_put_text_blob(gbl, &fielddesc, &FLD.RDB$DESCRIPTION))
					FLD.RDB$DESCRIPTION.NULL = FALSE;
				else
					FLD.RDB$DESCRIPTION.NULL = TRUE;
			}

			if (bdelvald) {
				FLD.RDB$VALIDATION_BLR.NULL = TRUE;
				FLD.RDB$VALIDATION_SOURCE.NULL = TRUE;
			}

			if (bdeldflt) {
				FLD.RDB$DEFAULT_VALUE.NULL = TRUE;
				FLD.RDB$DEFAULT_SOURCE.NULL = TRUE;
			}

			if (bflddftval) {
				if (DYN_put_blr_blob(gbl, &flddftval, &FLD.RDB$DEFAULT_VALUE))
					FLD.RDB$DEFAULT_VALUE.NULL = FALSE;
				else
					FLD.RDB$DEFAULT_VALUE.NULL = TRUE;
			}

			if (bflddfltsrc) {
				if (DYN_put_text_blob(gbl, &flddfltsrc, &FLD.RDB$DEFAULT_SOURCE))
					FLD.RDB$DEFAULT_SOURCE.NULL = FALSE;
				else
					FLD.RDB$DEFAULT_SOURCE.NULL = TRUE;
			}

		END_MODIFY;
	END_FOR;

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

	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 87);
		/* msg 87: "MODIFY RDB$FIELDS failed" */
	}

	if (!found)
	{
		DYN_error_punt(false, 89);
		/* msg 89: "Global field not found" */
	}
}


void DYN_modify_index( Global* gbl, const UCHAR** ptr)
{
/**************************************
 *
 *	D Y N _ m o d i f y _ i n d e x
 *
 **************************************
 *
 * Functional description
 *	Modify an existing index
 *
 **************************************/
	MetaName name;

	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_index, DYN_REQUESTS);
	bool found = false;

	try {

	GET_STRING(ptr, name);

	found = false;
	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		IDX IN RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ name.c_str()

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

		found = true;
		MODIFY IDX USING
			UCHAR verb;
			while ((verb = *(*ptr)++) != isc_dyn_end)
				switch (verb)
				{
				case isc_dyn_idx_unique:
					IDX.RDB$UNIQUE_FLAG = DYN_get_number(ptr);
					IDX.RDB$UNIQUE_FLAG.NULL = FALSE;
					break;

				case isc_dyn_idx_inactive:
					IDX.RDB$INDEX_INACTIVE = DYN_get_number(ptr);
					IDX.RDB$INDEX_INACTIVE.NULL = FALSE;
					break;

				case isc_dyn_description:
					if (DYN_put_text_blob(gbl, ptr, &IDX.RDB$DESCRIPTION))
						IDX.RDB$DESCRIPTION.NULL = FALSE;
					else
						IDX.RDB$DESCRIPTION.NULL = TRUE;
					break;

					/* For V4 index selectivity can be set only to -1 */

				case isc_dyn_idx_statistic:
					IDX.RDB$STATISTICS = -1.0;
					IDX.RDB$STATISTICS.NULL = FALSE;
					break;

				default:
					DYN_unsupported_verb();
				}
		END_MODIFY;
	END_FOR;

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

	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 91);
		// msg 91: "MODIFY RDB$INDICES failed"
	}

	if (!found)
	{
		DYN_error_punt(false, 48);
		/* msg 48: "Index not found" */
	}
}


void DYN_modify_local_field(Global* gbl,
							const UCHAR** ptr,
							const MetaName* relation_name)
{
/**************************************
 *
 *	D Y N _ m o d i f y _ l o c a l _ f i e l d
 *
 **************************************
 *
 * Functional description
 *	Execute a dynamic ddl statement.
 *
 **************************************/
	USHORT position;
	MetaName f, r;
	const UCHAR* query_name;
	const UCHAR* edit_string;
	const UCHAR* security_class;
	const UCHAR* new_name;
	const UCHAR* query_header;
	const UCHAR* description;
	const UCHAR* new_source = NULL;

	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	GET_STRING(ptr, f);

	bool sfflag, qnflag, qhflag, esflag, dflag, system_flag, scflag, nnflag, ntflag, npflag;
	sfflag = qnflag = qhflag = esflag = dflag = scflag = npflag = nnflag = ntflag = false;

	UCHAR verb;
	while ((verb = *(*ptr)++) != isc_dyn_end)
		switch (verb)
		{
		case isc_dyn_rel_name:
			GET_STRING(ptr, r);
			break;

		case isc_dyn_system_flag:
			system_flag = DYN_get_number(ptr);
			sfflag = true;
			break;

		case isc_dyn_fld_position:
			position = DYN_get_number(ptr);
			npflag = true;
			break;

		case isc_dyn_new_fld_name:
			new_name = *ptr;
			nnflag = true;
			DYN_skip_attribute(ptr);
			break;

		case isc_dyn_fld_query_name:
			query_name = *ptr;
			qnflag = true;
			DYN_skip_attribute(ptr);
			break;

		case isc_dyn_fld_query_header:
			query_header = *ptr;
			qhflag = true;
			DYN_skip_attribute(ptr);
			break;

		case isc_dyn_fld_edit_string:
			edit_string = *ptr;
			esflag = true;
			DYN_skip_attribute(ptr);
			break;

		case isc_dyn_security_class:
			security_class = *ptr;
			scflag = true;
			DYN_skip_attribute(ptr);
			break;

		case isc_dyn_fld_source:
			new_source = *ptr;
			DYN_skip_attribute(ptr);
			break;

		case isc_dyn_description:
			description = *ptr;
			dflag = true;
			DYN_skip_attribute(ptr);
			break;

		default:
			--(*ptr);
			DYN_execute(gbl, ptr, relation_name, NULL, NULL, NULL, NULL);
		}

	jrd_req* request = CMP_find_request(tdbb, drq_m_lfield, DYN_REQUESTS);
	bool found = false;

	try {

	USHORT existing_position;
	found = false;
	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		FLD IN RDB$RELATION_FIELDS
			WITH FLD.RDB$FIELD_NAME EQ f.c_str()
			 AND FLD.RDB$RELATION_NAME EQ r.c_str()

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

		found = true;

		MODIFY FLD USING
			if (npflag)
				existing_position = FLD.RDB$FIELD_POSITION;

			if (sfflag) {
				FLD.RDB$SYSTEM_FLAG = system_flag;
				FLD.RDB$SYSTEM_FLAG.NULL = FALSE;
			}
			if (qnflag) {
				if (GET_STRING(&query_name, FLD.RDB$QUERY_NAME))
					FLD.RDB$QUERY_NAME.NULL = FALSE;
				else
					FLD.RDB$QUERY_NAME.NULL = TRUE;
			}

			if (nnflag) {
				MetaName new_fld;
				GET_STRING(&new_name, new_fld);
				if (new_fld.length() == 0)
				{
					DYN_error_punt(false, 212);
					/* msg 212: "Zero length identifiers not allowed" */
				}

				check_view_dependency(tdbb, gbl, r, f);
				check_sptrig_dependency(tdbb, gbl, r, f);
				if (!field_exists(tdbb, gbl, r, new_fld)) {
					strcpy(FLD.RDB$FIELD_NAME, new_fld.c_str());
					modify_lfield_index(tdbb, gbl, r, f, FLD.RDB$FIELD_NAME);
				}
				else {
					DYN_error_punt(false, 205, SafeArg() << f.c_str() << new_fld.c_str() <<
									r.c_str());
					/* msg 205: Cannot rename field %s to %s.  A field with that name already exists in table %s. */
				}
			}

			if (qhflag) {
				if (DYN_put_blr_blob(gbl, &query_header, &FLD.RDB$QUERY_HEADER))
					FLD.RDB$QUERY_HEADER.NULL = FALSE;
				else
					FLD.RDB$QUERY_HEADER.NULL = TRUE;
			}
			if (esflag) {
				if (GET_STRING(&edit_string, FLD.RDB$EDIT_STRING))
					FLD.RDB$EDIT_STRING.NULL = FALSE;
				else
					FLD.RDB$EDIT_STRING.NULL = TRUE;
			}
			if (scflag) {
				if (GET_STRING(&security_class, FLD.RDB$SECURITY_CLASS))
					FLD.RDB$SECURITY_CLASS.NULL = FALSE;
				else
					FLD.RDB$SECURITY_CLASS.NULL = TRUE;
			}

			if (new_source)
				GET_STRING(&new_source, FLD.RDB$FIELD_SOURCE);

			if (dflag) {
				if (DYN_put_text_blob(gbl, &description, &FLD.RDB$DESCRIPTION))
					FLD.RDB$DESCRIPTION.NULL = FALSE;
				else
					FLD.RDB$DESCRIPTION.NULL = TRUE;
			}
		END_MODIFY;
	END_FOR;

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

	if (npflag && found && position != existing_position)
		modify_lfield_position(tdbb, gbl, r, f, position, existing_position);
	} // try
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 95);
		/* msg 95: "MODIFY RDB$RELATION_FIELDS failed" */
	}

	if (!found)
	{
		DYN_error_punt(false, 176, SafeArg() << f.c_str() << r.c_str());
		// msg 176: "column %s does not exist in table/view %s"
	}
}


// Modifies a procedure parameter.
void DYN_modify_parameter(Global* gbl, const UCHAR** ptr)
{
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_prm, DYN_REQUESTS);
	bool found = false;

	SqlIdentifier t;
	GET_STRING(ptr, t);
	SqlIdentifier p;

	try
	{
		if (**ptr == isc_dyn_prc_name)
		{
			++*ptr;
			GET_STRING(ptr, p);
		}
		else
			DYN_unsupported_verb();

		found = false;
		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			PRM IN RDB$PROCEDURE_PARAMETERS CROSS
			FLD IN RDB$FIELDS
			WITH PRM.RDB$PARAMETER_NAME EQ t AND
				 PRM.RDB$PROCEDURE_NAME EQ p AND
				 FLD.RDB$FIELD_NAME EQ PRM.RDB$FIELD_SOURCE
		{
			if (!DYN_REQUEST(drq_m_prm))
				DYN_REQUEST(drq_m_prm) = request;

			found = true;

			if (**ptr == isc_dyn_description)
			{
				MODIFY PRM
					++*ptr;
					if (DYN_put_text_blob(gbl, ptr, &PRM.RDB$DESCRIPTION))
						PRM.RDB$DESCRIPTION.NULL = FALSE;
					else
						PRM.RDB$DESCRIPTION.NULL = TRUE;

					fb_assert(**ptr == isc_dyn_end)
				END_MODIFY
			}
			else
			{
				MODIFY PRM MODIFY FLD
				{
					PRM.RDB$PARAMETER_NUMBER.NULL = TRUE;
					PRM.RDB$PARAMETER_TYPE.NULL = TRUE;
					PRM.RDB$DEFAULT_VALUE.NULL = TRUE;
					PRM.RDB$DEFAULT_SOURCE.NULL = TRUE;
					PRM.RDB$COLLATION_ID.NULL = TRUE;
					PRM.RDB$NULL_FLAG.NULL = TRUE;
					PRM.RDB$PARAMETER_MECHANISM.NULL = TRUE;

					FLD.RDB$DEFAULT_VALUE.NULL = TRUE;
					FLD.RDB$DEFAULT_SOURCE.NULL = TRUE;
					FLD.RDB$FIELD_LENGTH.NULL = TRUE;
					FLD.RDB$FIELD_SCALE.NULL = TRUE;
					FLD.RDB$FIELD_TYPE.NULL = TRUE;
					FLD.RDB$FIELD_SUB_TYPE.NULL = TRUE;
					FLD.RDB$SEGMENT_LENGTH.NULL = TRUE;
					FLD.RDB$NULL_FLAG.NULL = TRUE;
					FLD.RDB$CHARACTER_LENGTH.NULL = TRUE;
					FLD.RDB$COLLATION_ID.NULL = TRUE;
					FLD.RDB$CHARACTER_SET_ID.NULL = TRUE;
					FLD.RDB$FIELD_PRECISION.NULL = TRUE;

					UCHAR verb;
					while ((verb = *(*ptr)++) != isc_dyn_end)
					{
						switch (verb)
						{
						case isc_dyn_prm_number:
							PRM.RDB$PARAMETER_NUMBER = (SSHORT) DYN_get_number(ptr);
							PRM.RDB$PARAMETER_NUMBER.NULL = FALSE;
							break;

						case isc_dyn_prm_type:
							PRM.RDB$PARAMETER_TYPE = (SSHORT) DYN_get_number(ptr);
							PRM.RDB$PARAMETER_TYPE.NULL = FALSE;
							break;

						case isc_dyn_fld_not_null:
							if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) < ODS_11_1)
							{
								// Feature not supported on ODS version older than %d.%d
								ERR_post(Arg::Gds(isc_dsql_feature_not_supported_ods) <<
									Arg::Num(11) << Arg::Num(1));
							}

							PRM.RDB$NULL_FLAG = TRUE;
							PRM.RDB$NULL_FLAG.NULL = FALSE;
							break;

						case isc_dyn_fld_length:
							FLD.RDB$FIELD_LENGTH = (USHORT) DYN_get_number(ptr);
							FLD.RDB$FIELD_LENGTH.NULL = FALSE;
							break;

						case isc_dyn_fld_type:
							FLD.RDB$FIELD_TYPE = (USHORT) DYN_get_number(ptr);
							FLD.RDB$FIELD_TYPE.NULL = FALSE;

							switch (FLD.RDB$FIELD_TYPE)
							{
							case blr_short:
								FLD.RDB$FIELD_LENGTH = 2;
								FLD.RDB$FIELD_LENGTH.NULL = FALSE;
								break;

							case blr_long:
							case blr_float:
							case blr_sql_time:
							case blr_sql_date:
								FLD.RDB$FIELD_LENGTH = 4;
								FLD.RDB$FIELD_LENGTH.NULL = FALSE;
								break;

							case blr_int64:
							case blr_quad:
							case blr_timestamp:
							case blr_double:
							case blr_d_float:
								FLD.RDB$FIELD_LENGTH = 8;
								FLD.RDB$FIELD_LENGTH.NULL = FALSE;
								break;

							default:
								if (FLD.RDB$FIELD_TYPE == blr_blob)
								{
									FLD.RDB$FIELD_LENGTH = 8;
									FLD.RDB$FIELD_LENGTH.NULL = FALSE;
								}
								break;
							}
							break;

						case isc_dyn_fld_scale:
							FLD.RDB$FIELD_SCALE = (SSHORT) DYN_get_number(ptr);
							FLD.RDB$FIELD_SCALE.NULL = FALSE;
							break;

						case isc_dyn_fld_precision:
							FLD.RDB$FIELD_PRECISION = (SSHORT) DYN_get_number(ptr);
							FLD.RDB$FIELD_PRECISION.NULL = FALSE;
							break;

						case isc_dyn_fld_sub_type:
							FLD.RDB$FIELD_SUB_TYPE = (SSHORT) DYN_get_number(ptr);
							FLD.RDB$FIELD_SUB_TYPE.NULL = FALSE;
							break;

						case isc_dyn_fld_char_length:
							FLD.RDB$CHARACTER_LENGTH = (USHORT) DYN_get_number(ptr);
							FLD.RDB$CHARACTER_LENGTH.NULL = FALSE;
							break;

						case isc_dyn_fld_character_set:
							FLD.RDB$CHARACTER_SET_ID = (SSHORT) DYN_get_number(ptr);
							FLD.RDB$CHARACTER_SET_ID.NULL = FALSE;
							break;

						case isc_dyn_fld_collation:
							FLD.RDB$COLLATION_ID = (SSHORT) DYN_get_number(ptr);
							FLD.RDB$COLLATION_ID.NULL = FALSE;
							break;

						case isc_dyn_fld_segment_length:
							FLD.RDB$SEGMENT_LENGTH = (USHORT) DYN_get_number(ptr);
							FLD.RDB$SEGMENT_LENGTH.NULL = FALSE;
							break;

						case isc_dyn_fld_default_value:
							DYN_put_blr_blob(gbl, ptr, &FLD.RDB$DEFAULT_VALUE);
							FLD.RDB$DEFAULT_VALUE.NULL = FALSE;
							break;

						case isc_dyn_fld_default_source:
							DYN_put_text_blob(gbl, ptr, &FLD.RDB$DEFAULT_SOURCE);
							FLD.RDB$DEFAULT_SOURCE.NULL = FALSE;
							break;

						case isc_dyn_prm_mechanism:
							PRM.RDB$PARAMETER_MECHANISM = (prm_mech_t) DYN_get_number(ptr);
							PRM.RDB$PARAMETER_MECHANISM.NULL = FALSE;
							break;

						default:
							DYN_unsupported_verb();
						}
					}
				}
				END_MODIFY END_MODIFY
			}
		}
		END_FOR

		if (!DYN_REQUEST(drq_m_prm))
			DYN_REQUEST(drq_m_prm) = request;
	}
	catch (const Exception& ex)
	{
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 100);
		// msg 100: "MODIFY RDB$PROCEDURE_PARAMETERS failed"
	}

	if (!found)
	{
		DYN_error_punt(false, 146, SafeArg() << t << p);
		// msg : "Parameter %s in procedure %s not found"
	}
}


void DYN_modify_procedure( Global* gbl, const UCHAR** ptr)
{
/**************************************
 *
 *	D Y N _ m o d i f y _ p r o c e d u r e
 *
 **************************************
 *
 * Functional description
 *	Execute a dynamic ddl statement.
 *
 **************************************/
	MetaName procedure_name;

	GET_STRING(ptr, procedure_name);

	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = NULL;
	bool found = false;
	bool only_description = false;

	try {

	request = CMP_find_request(tdbb, drq_m_prcs, DYN_REQUESTS);

	found = false;
	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_NAME = procedure_name.c_str()

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

		found = true;

		/* Set NULL flags to TRUE only for fields which must be specified in the DYN string.
		   Retain existing values on other fields (RDB$DESCRIPTION, RDB$SECURITY_CLASS),
		   unless explicitly changed in the DYN string */
		MODIFY P

			if (**ptr == isc_dyn_description)
			{
				++*ptr;
				if (DYN_put_text_blob(gbl, ptr, &P.RDB$DESCRIPTION))
					P.RDB$DESCRIPTION.NULL = FALSE;
				else
					P.RDB$DESCRIPTION.NULL = TRUE;

				if (**ptr == isc_dyn_end)
					only_description = true;
			}

			if (!only_description)
			{
				P.RDB$SYSTEM_FLAG = 0;
				P.RDB$SYSTEM_FLAG.NULL = FALSE;
				P.RDB$PROCEDURE_BLR.NULL = TRUE;
				P.RDB$PROCEDURE_SOURCE.NULL = TRUE;
				P.RDB$PROCEDURE_INPUTS.NULL = TRUE;
				P.RDB$PROCEDURE_OUTPUTS.NULL = TRUE;

				// ODS_11_1 fields
				P.RDB$DEBUG_INFO.NULL = TRUE;

				P.RDB$PROCEDURE_TYPE = prc_legacy;
				P.RDB$PROCEDURE_TYPE.NULL = FALSE;

				P.RDB$VALID_BLR = TRUE;
				P.RDB$VALID_BLR.NULL = FALSE;
			}

			UCHAR verb;
			while ((verb = *(*ptr)++) != isc_dyn_end)
				switch (verb)
				{
				case isc_dyn_system_flag:
					P.RDB$SYSTEM_FLAG = DYN_get_number(ptr);
					P.RDB$SYSTEM_FLAG.NULL = FALSE;
					break;

				case isc_dyn_prc_blr:
					if (DYN_put_blr_blob(gbl, ptr, &P.RDB$PROCEDURE_BLR))
						P.RDB$PROCEDURE_BLR.NULL = FALSE;
					else
						P.RDB$PROCEDURE_BLR.NULL = TRUE;
					break;

				case isc_dyn_description:
					if (DYN_put_text_blob(gbl, ptr, &P.RDB$DESCRIPTION))
						P.RDB$DESCRIPTION.NULL = FALSE;
					else
						P.RDB$DESCRIPTION.NULL = TRUE;
					break;

				case isc_dyn_prc_source:
					if (DYN_put_text_blob(gbl, ptr, &P.RDB$PROCEDURE_SOURCE))
						P.RDB$PROCEDURE_SOURCE.NULL = FALSE;
					else
						P.RDB$PROCEDURE_SOURCE.NULL = TRUE;
					break;

				case isc_dyn_prc_inputs:
					P.RDB$PROCEDURE_INPUTS = DYN_get_number(ptr);
					P.RDB$PROCEDURE_INPUTS.NULL = FALSE;
					break;

				case isc_dyn_prc_outputs:
					P.RDB$PROCEDURE_OUTPUTS = DYN_get_number(ptr);
					P.RDB$PROCEDURE_OUTPUTS.NULL = FALSE;
					break;

				case isc_dyn_security_class:
					GET_STRING(ptr, P.RDB$SECURITY_CLASS);
					P.RDB$SECURITY_CLASS.NULL = FALSE;
					break;

				case isc_dyn_debug_info:
					DYN_put_blr_blob(gbl, ptr, &P.RDB$DEBUG_INFO);	// ODS_11_1 field
					P.RDB$DEBUG_INFO.NULL = FALSE;
					break;

				case isc_dyn_prc_type:
					P.RDB$PROCEDURE_TYPE = DYN_get_number(ptr);	// ODS_11_1 field
					break;

				default:
					--(*ptr);
					DYN_execute(gbl, ptr, NULL, NULL, NULL, NULL, &procedure_name);
				}

		END_MODIFY;
	END_FOR;

	if (!DYN_REQUEST(drq_m_prcs))
		DYN_REQUEST(drq_m_prcs) = request;
	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 141);
		/* msg 141: "MODIFY RDB$PROCEDURES failed" */
	}

	if (!found) {
		DYN_error_punt(false, 140, procedure_name.c_str());
		/* msg 140: "Procedure %s not found" */
	}
}


void DYN_modify_relation( Global* gbl, const UCHAR** ptr)
{
/**************************************
 *
 *	D Y N _ m o d i f y _ r e l a t i o n
 *
 **************************************
 *
 * Functional description
 *	Modify an existing relation
 *
 **************************************/
	MetaName name, field_name;

	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	GET_STRING(ptr, name);

	jrd_req* request = CMP_find_request(tdbb, drq_m_relation, DYN_REQUESTS);
	bool found = false;

	try {

	found = false;
	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ name.c_str()

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

		if (!REL.RDB$VIEW_BLR.NULL)
			DYN_error_punt(false, 177);

		found = true;
		MODIFY REL USING
			UCHAR verb;
			while ((verb = *(*ptr)++) != isc_dyn_end)
				switch (verb)
				{
				case isc_dyn_system_flag:
					REL.RDB$SYSTEM_FLAG = DYN_get_number(ptr);
					REL.RDB$SYSTEM_FLAG.NULL = FALSE;
					break;

				case isc_dyn_security_class:
					if (GET_STRING(ptr, REL.RDB$SECURITY_CLASS))
						REL.RDB$SECURITY_CLASS.NULL = FALSE;
					else
						REL.RDB$SECURITY_CLASS.NULL = TRUE;
					break;

				case isc_dyn_rel_ext_file:
					if (REL.RDB$EXTERNAL_FILE.NULL) {
						DYN_rundown_request(request, -1);
						DYN_error_punt(false, 97);
						/* msg 97: "add EXTERNAL FILE not allowed" */
					}
					if (!GET_STRING(ptr, REL.RDB$EXTERNAL_FILE)) {
						DYN_rundown_request(request, -1);
						DYN_error_punt(false, 98);
						/* msg 98: "drop EXTERNAL FILE not allowed" */
					}
					break;

				case isc_dyn_description:
					if (DYN_put_text_blob(gbl, ptr, &REL.RDB$DESCRIPTION))
						REL.RDB$DESCRIPTION.NULL = FALSE;
					else
						REL.RDB$DESCRIPTION.NULL = TRUE;
					break;

				default:
					--(*ptr);
					DYN_execute(gbl, ptr, &name, &field_name, NULL, NULL, NULL);
				}
		END_MODIFY;
	END_FOR;

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

	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 99);
		/* msg 99: "MODIFY RDB$RELATIONS failed" */
	}

	if (!found)
	{
		DYN_error_punt(false, 101);
		/* msg 101: "Relation field not found" */
	}
}


// *****************************
// D Y N _ m o d i f y _ r o l e
// *****************************
// Its purpose is to change the comment in the role's record.
void DYN_modify_role(Global* gbl, const UCHAR** ptr)
{
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_rol, DYN_REQUESTS);
	bool found = false;
	SqlIdentifier t;
	GET_STRING(ptr, t);

	try {
		found = false;
		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			X IN RDB$ROLES
				WITH X.RDB$ROLE_NAME EQ t

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

			found = true;
			MODIFY X
				UCHAR verb;
				while ((verb = *(*ptr)++) != isc_dyn_end)
				{
					switch (verb)
					{
					case isc_dyn_description:
						if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
							X.RDB$DESCRIPTION.NULL = FALSE;
						else
							X.RDB$DESCRIPTION.NULL = TRUE;
						break;

					default:
						DYN_unsupported_verb();
					}
				}
			END_MODIFY;
		END_FOR;

		if (!DYN_REQUEST(drq_m_rol))
			DYN_REQUEST(drq_m_rol) = request;
	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 158);
		// msg 158: "MODIFY RDB$ROLES failed"
	}

	if (!found)
	{
		DYN_error_punt(false, 155, t);
		// msg 155: "Role %s not found"
	}
}


void DYN_modify_trigger( Global* gbl, const UCHAR** ptr)
{
/**************************************
 *
 *	D Y N _ m o d i f y _ t r i g g e r
 *
 **************************************
 *
 * Functional description
 *	Modify a trigger for a relation.
 *
 **************************************/
	MetaName trigger_name;

	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_trigger, DYN_REQUESTS);
	bool found = false;
	bool only_description = false;

	try {

	GET_STRING(ptr, trigger_name);
	const UCHAR* source = NULL;
	const UCHAR* blr = NULL;
	const UCHAR* debug_info_ptr = NULL;

	found = false;
	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		X IN RDB$TRIGGERS WITH X.RDB$TRIGGER_NAME EQ trigger_name.c_str()

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

		if (X.RDB$RELATION_NAME.NULL && !tdbb->getAttachment()->locksmith())
			ERR_post(Arg::Gds(isc_adm_task_denied));

		// CVC: I think that we'll do well by hiding our automatic triggers from this function.
		// Why would a user want to fiddle with triggers that were generated automatically?

		if (!X.RDB$SYSTEM_FLAG.NULL)
		{
			switch (X.RDB$SYSTEM_FLAG)
			{
			case fb_sysflag_check_constraint:
			case fb_sysflag_referential_constraint:
			case fb_sysflag_view_check:
				DYN_error_punt(false, 109);
				// msg 109: "Triggers created automatically cannot be modified"
				break;
			default:
				break;
			}
		}

		found = true;

		MODIFY X

			if (**ptr == isc_dyn_description)
			{
				++*ptr;
				if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
					X.RDB$DESCRIPTION.NULL = FALSE;
				else
					X.RDB$DESCRIPTION.NULL = TRUE;

				if (**ptr == isc_dyn_end)
					only_description = true;
			}

			UCHAR verb;
			while ((verb = *(*ptr)++) != isc_dyn_end)
				switch (verb)
				{
				case isc_dyn_trg_name:
					{
						MetaName new_trigger_name;
						GET_STRING(ptr, new_trigger_name);
						if (new_trigger_name.length() == 0)
							DYN_error_punt(false, 212);
							// msg 212: "Zero length identifiers not allowed"
					    strcpy (X.RDB$TRIGGER_NAME, new_trigger_name.c_str());
					}
					break;

				case isc_dyn_trg_type:
					X.RDB$TRIGGER_TYPE = DYN_get_number(ptr);
					X.RDB$TRIGGER_TYPE.NULL = FALSE;
					break;

				case isc_dyn_trg_sequence:
					X.RDB$TRIGGER_SEQUENCE = DYN_get_number(ptr);
					X.RDB$TRIGGER_SEQUENCE.NULL = FALSE;
					break;

				case isc_dyn_trg_inactive:
					X.RDB$TRIGGER_INACTIVE = DYN_get_number(ptr);
					X.RDB$TRIGGER_INACTIVE.NULL = FALSE;
					break;

				case isc_dyn_rel_name:
					GET_STRING(ptr, X.RDB$RELATION_NAME);
					X.RDB$RELATION_NAME.NULL = FALSE;
					break;

				case isc_dyn_trg_blr:
					blr = *ptr;
					DYN_skip_attribute(ptr);
					if (DYN_put_blr_blob(gbl, &blr, &X.RDB$TRIGGER_BLR))
						X.RDB$TRIGGER_BLR.NULL = FALSE;
					else
						X.RDB$TRIGGER_BLR.NULL = TRUE;
					break;

				case isc_dyn_trg_source:
					source = *ptr;
					DYN_skip_attribute(ptr);
					if (DYN_put_text_blob(gbl, &source, &X.RDB$TRIGGER_SOURCE))
						X.RDB$TRIGGER_SOURCE.NULL = FALSE;
					else
						X.RDB$TRIGGER_SOURCE.NULL = TRUE;
					break;

				case isc_dyn_description:
					if (DYN_put_text_blob(gbl, ptr, &X.RDB$DESCRIPTION))
						X.RDB$DESCRIPTION.NULL = FALSE;
					else
						X.RDB$DESCRIPTION.NULL = TRUE;
					break;

				case isc_dyn_debug_info:
					debug_info_ptr = *ptr;
					DYN_skip_attribute(ptr);
					break;

				default:
					--(*ptr);
					DYN_execute(gbl, ptr, NULL, NULL, &trigger_name, NULL, NULL);
				}
		END_MODIFY;
	END_FOR;

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

	if (!only_description)
	{
		if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1)
		{
			jrd_req* sub_request = NULL;

			FOR(REQUEST_HANDLE sub_request TRANSACTION_HANDLE gbl->gbl_transaction)
				TRG IN RDB$TRIGGERS WITH TRG.RDB$TRIGGER_NAME EQ trigger_name.c_str()

				MODIFY TRG USING
					TRG.RDB$VALID_BLR = TRUE;
					TRG.RDB$VALID_BLR.NULL = FALSE;

					TRG.RDB$DEBUG_INFO.NULL = (debug_info_ptr == NULL) ? TRUE : FALSE;
					if (debug_info_ptr)
						DYN_put_blr_blob(gbl, &debug_info_ptr, &TRG.RDB$DEBUG_INFO);
				END_MODIFY;
			END_FOR;

			CMP_release(tdbb, sub_request);
		}
	}

	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 102);
		/* msg 102: "MODIFY TRIGGER failed" */
	}

	if (!found) {
		DYN_error_punt(false, 147, trigger_name.c_str());
		/* msg 147: "Trigger %s not found" */
	}
}


void DYN_modify_trigger_msg( Global* gbl, const UCHAR** ptr, MetaName* trigger_name)
{
/**************************************
 *
 *	D Y N _ m o d i f y _ t r i g g e r _ m s g
 *
 **************************************
 *
 * Functional description
 *	Modify a trigger message.
 *
 **************************************/
	MetaName t;

	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

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

	try {

	const int number = DYN_get_number(ptr);
	if (trigger_name)
		t = *trigger_name;
	else if (*(*ptr)++ == isc_dyn_trg_name)
		GET_STRING(ptr, t);
	else
		DYN_error_punt(false, 103);
	/* msg 103: "TRIGGER NAME expected" */

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		X IN RDB$TRIGGER_MESSAGES
			WITH X.RDB$MESSAGE_NUMBER EQ number AND X.RDB$TRIGGER_NAME EQ t.c_str()

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

		MODIFY X
			UCHAR verb;
			while ((verb = *(*ptr)++) != isc_dyn_end)
				switch (verb)
				{
				case isc_dyn_trg_msg_number:
					X.RDB$MESSAGE_NUMBER = DYN_get_number(ptr);
					X.RDB$MESSAGE_NUMBER.NULL = FALSE;
					break;

				case isc_dyn_trg_msg:
					GET_STRING(ptr, X.RDB$MESSAGE);
					X.RDB$MESSAGE.NULL = FALSE;
					break;

				default:
					DYN_unsupported_verb();
				}
		END_MODIFY;
	END_FOR;

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

	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 105);
		/* msg 105: "MODIFY TRIGGER MESSAGE failed" */
	}
}


void DYN_modify_view( Global* gbl, const UCHAR** ptr)
{
/**************************************
 *
 *	D Y N _ m o d i f y _ v i e w
 *
 **************************************
 *
 * Functional description
 *	Execute a dynamic ddl statement.
 *
 **************************************/
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	MetaName view_name;
	GET_STRING(ptr, view_name);

	if (view_name.length() == 0)
	{
		DYN_error_punt(false, 212);
		/* msg 212: "Zero length identifiers not allowed" */
	}

	jrd_req* request = NULL;
	bool found = false;

	try {

	request = CMP_find_request(tdbb, drq_m_view, DYN_REQUESTS);

	found = false;
	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		REL IN RDB$RELATIONS
			WITH REL.RDB$RELATION_NAME EQ view_name.c_str()
				AND REL.RDB$VIEW_BLR NOT MISSING

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

		found = true;
		bool only_description = false;

		MODIFY REL

			if (**ptr == isc_dyn_description)
			{
				++*ptr;
				if (DYN_put_text_blob(gbl, ptr, &REL.RDB$DESCRIPTION))
					REL.RDB$DESCRIPTION.NULL = FALSE;
				else
					REL.RDB$DESCRIPTION.NULL = TRUE;

				if (**ptr == isc_dyn_end)
					only_description = true;
			}

			if (!only_description)
			{
				REL.RDB$SYSTEM_FLAG = 0;
				REL.RDB$SYSTEM_FLAG.NULL = FALSE;
				REL.RDB$VIEW_BLR.NULL = TRUE;
				REL.RDB$VIEW_SOURCE.NULL = TRUE;

				jrd_req* request2 = NULL;

				FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE gbl->gbl_transaction)
					VR IN RDB$VIEW_RELATIONS
					WITH VR.RDB$VIEW_NAME EQ view_name.c_str()

					ERASE VR;
				END_FOR;

				CMP_release(tdbb, request2);

				request2 = NULL;

				FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE gbl->gbl_transaction)
					TRG IN RDB$TRIGGERS
					WITH TRG.RDB$RELATION_NAME EQ view_name.c_str() AND
						 TRG.RDB$SYSTEM_FLAG EQ fb_sysflag_view_check

					ERASE TRG;
				END_FOR

				CMP_release(tdbb, request2);
			}

			UCHAR verb;
			while ((verb = *(*ptr)++) != isc_dyn_end)
				switch (verb)
				{
				case isc_dyn_system_flag:
					REL.RDB$SYSTEM_FLAG = DYN_get_number(ptr);
					REL.RDB$SYSTEM_FLAG.NULL = FALSE;
					break;

				case isc_dyn_view_blr:
					if (DYN_put_blr_blob(gbl, ptr, &REL.RDB$VIEW_BLR))
						REL.RDB$VIEW_BLR.NULL = FALSE;
					else
						REL.RDB$VIEW_BLR.NULL = TRUE;
					break;

				case isc_dyn_view_source:
					if (DYN_put_text_blob(gbl, ptr, &REL.RDB$VIEW_SOURCE))
						REL.RDB$VIEW_SOURCE.NULL = FALSE;
					else
						REL.RDB$VIEW_SOURCE.NULL = TRUE;
					break;

				case isc_dyn_security_class:
					GET_STRING(ptr, REL.RDB$SECURITY_CLASS);
					REL.RDB$SECURITY_CLASS.NULL = FALSE;
					break;

				case isc_dyn_description:
					if (DYN_put_text_blob(gbl, ptr, &REL.RDB$DESCRIPTION))
						REL.RDB$DESCRIPTION.NULL = FALSE;
					else
						REL.RDB$DESCRIPTION.NULL = TRUE;
					break;

				default:
					--(*ptr);
					MetaTmp(REL.RDB$RELATION_NAME)
						DYN_execute(gbl, ptr, &tmp, NULL, NULL, NULL, NULL);
				}
		END_MODIFY;
	END_FOR;

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

	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, -1);
		DYN_error_punt(true, 99);
		/* msg 99: "MODIFY RDB$RELATIONS failed" */
	}

	if (!found) {
		DYN_error_punt(false, 54, view_name.c_str());
		/* msg 54: "View %s not found" */
	}
}


static void change_backup_mode( Global* gbl, UCHAR verb)
{
/**************************************
 *
 *	c h a n g e _ b a c k u p _ m o d e
 *
 **************************************
 *
 * Functional description
 *	Drop backup difference file for the database,
 *  begin or end backup
 *
 **************************************/
	bool invalid_state = false;

	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	if (!tdbb->getAttachment()->locksmith())
	{
		ERR_post(Arg::Gds(isc_adm_task_denied));
	}

	jrd_req* request = CMP_find_request(tdbb, drq_d_difference, DYN_REQUESTS);
	bool found = false;

	try {

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		X IN RDB$FILES

		if (X.RDB$FILE_FLAGS & FILE_difference) {
			found = true;
			switch (verb)
			{
			case isc_dyn_drop_difference:
				ERASE X;
				break;
			case isc_dyn_begin_backup:
				if (X.RDB$FILE_FLAGS & FILE_backing_up) {
					invalid_state = true;
				}
				else {
					MODIFY X USING
						X.RDB$FILE_FLAGS |= FILE_backing_up;
					END_MODIFY;
				}
				break;
			case isc_dyn_end_backup:
				if (X.RDB$FILE_FLAGS & FILE_backing_up) {
					if (X.RDB$FILE_NAME.NULL) {
						ERASE X;
					}
					else {
						MODIFY X USING
							X.RDB$FILE_FLAGS &= ~FILE_backing_up;
						END_MODIFY;
					}
				}
				else {
					invalid_state = true;
				}
				break;
			}
		}

	END_FOR;

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

	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_rundown_request(request, drq_d_difference);
		DYN_error_punt(true, 63);
		/* msg 63: ERASE RDB$FILE failed */
	}

	if (!found && verb == isc_dyn_begin_backup) {
		try {
			request = CMP_find_request(tdbb, drq_s2_difference, DYN_REQUESTS);
			STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
				X IN RDB$FILES
				X.RDB$FILE_NAME.NULL = TRUE;
				X.RDB$FILE_FLAGS.NULL = FALSE;
				X.RDB$FILE_FLAGS = FILE_difference | FILE_backing_up;
				X.RDB$FILE_START = 0;
				X.RDB$FILE_START.NULL = FALSE;
				X.RDB$FILE_LENGTH.NULL = TRUE;
				X.RDB$SHADOW_NUMBER.NULL = TRUE;
			END_STORE;

			found = true;

			if (!DYN_REQUEST(drq_s2_difference))
			{
				DYN_REQUEST(drq_s2_difference) = request;
			}
		}
		catch (const Exception& ex) {
			stuff_exception(tdbb->tdbb_status_vector, ex);
			DYN_rundown_request(request, drq_s2_difference);
			DYN_error_punt(true, 150);
			/* msg 150: STORE RDB$FILES failed */
		}
	}

	if (invalid_state) {
		DYN_error_punt(false, verb == isc_dyn_begin_backup ? 217 : 218);
		/* msg 217: "Database is already in the physical backup mode" */
		/* msg 218: "Database is not in the physical backup mode" */
	}

	if (!found)
	{
		DYN_error_punt(false, verb == isc_dyn_end_backup ? 218 : 215);
		/* msg 218: "Database is not in the physical backup mode" */
		/* msg 215: "Difference file is not defined" */
	}
}


static void modify_lfield_position(thread_db* tdbb,
								   Global* gbl,
								   const MetaName& relation_name,
								   const MetaName& field_name,
								   USHORT new_position,
								   USHORT existing_position)
{
/***********************************************************
 *
 *  m o d i f y _ l f i e l d _ p o s i t i o n
 ***********************************************************
 *
 *  Functional Description:
 *     Alters the position of a field with respect to the
 *     other fields in the relation.  This will only affect
 *     the order in which the fields will be returned when either
 *     viewing the relation or performing select * from the relation.
 *
 *     The rules of engagement are as follows:
 *          if new_position > original position
 *             increase RDB$FIELD_POSITION for all fields with RDB$FIELD_POSITION
 *             between the new_position and existing position of the field
 *	       then update the position of the field being altered.
 *              just update the position
 *
 *          if new_position < original position
 *             decrease RDB$FIELD_POSITION for all fields with RDB$FIELD_POSITION
 *             between the new_position and existing position of the field
 *	       then update the position of the field being altered.
 *
 *	    if new_position == original_position -- no_op
 *
 ***********************************************************/
	jrd_req* request = NULL;

	try {

/* Find the position of the last field in the relation */
	SLONG max_position = -1;
	DYN_UTIL_generate_field_position(tdbb, gbl, relation_name, &max_position);

/* if the existing position of the field is less than the new position of
 * the field, subtract 1 to move the fields to their new positions otherwise,
 * increase the value in RDB$FIELD_POSITION by one
 */

	bool move_down = false;
	if (existing_position < new_position)
		move_down = true;

/* Retrieve the records for the fields which have a position between the
 * existing field position and the new field position
 */

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		FLD IN RDB$RELATION_FIELDS
		WITH FLD.RDB$RELATION_NAME EQ relation_name.c_str() AND
		FLD.RDB$FIELD_POSITION >= MIN(new_position, existing_position) AND
		FLD.RDB$FIELD_POSITION <= MAX(new_position, existing_position)
		MODIFY FLD USING
			/* If the field is the one we want, change the position, otherwise
			 * increase the value of RDB$FIELD_POSITION
			 */
			if (field_name == FLD.RDB$FIELD_NAME) {
				if (new_position > max_position)
					/* This prevents gaps in the position sequence of the
					 * fields */
					FLD.RDB$FIELD_POSITION = max_position;
				else
					FLD.RDB$FIELD_POSITION = new_position;
			}
			else {
				if (move_down)
					FLD.RDB$FIELD_POSITION = FLD.RDB$FIELD_POSITION - 1;
				else
					FLD.RDB$FIELD_POSITION = FLD.RDB$FIELD_POSITION + 1;
			}

			FLD.RDB$FIELD_POSITION.NULL = FALSE;
		END_MODIFY;
	END_FOR;

	CMP_release(tdbb, request);
	request = NULL;

/* Once the field position has been changed, make sure that there are no
 * duplicate field positions and no gaps in the position sequence (this can
 * not be guaranteed by the query above */

	USHORT new_pos = 0;
	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		FLD IN RDB$RELATION_FIELDS WITH FLD.RDB$RELATION_NAME EQ relation_name.c_str()
			SORTED BY ASCENDING FLD.RDB$FIELD_POSITION

		if (FLD.RDB$FIELD_POSITION != new_pos) {
			MODIFY FLD USING
				FLD.RDB$FIELD_POSITION = new_pos;
			END_MODIFY;
		}
		new_pos += 1;
	END_FOR;

	CMP_release(tdbb, request);
	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_error_punt(true, 95);
		/* msg 95: "MODIFY RDB$RELATION_FIELDS failed" */
	}
}


static bool check_view_dependency(thread_db* tdbb,
									 Global* gbl,
									 const MetaName& relation_name,
									 const MetaName& field_name)
{
/***********************************************************
 *
 *  c h e c k _ v i e w _ d e p e n d e n c y
 ***********************************************************
 *
 *  Functional Description:
 *	Checks to see if the given field is referenced in a view. If the field
 *	is referenced in a view, return true, else return false
 *  CVC: The function never has a chance to return true because it punts.
 *
 ***********************************************************/
	jrd_req* request = NULL;
	bool retval = false;
	MetaName view_name;

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		FIRST 1
			X IN RDB$RELATION_FIELDS CROSS Y IN RDB$RELATION_FIELDS CROSS
			Z IN RDB$VIEW_RELATIONS WITH
			X.RDB$RELATION_NAME EQ relation_name.c_str() AND
			X.RDB$FIELD_NAME EQ field_name.c_str() AND
			X.RDB$FIELD_NAME EQ Y.RDB$BASE_FIELD AND
			X.RDB$FIELD_SOURCE EQ Y.RDB$FIELD_SOURCE AND
			Y.RDB$RELATION_NAME EQ Z.RDB$VIEW_NAME AND
			X.RDB$RELATION_NAME EQ Z.RDB$RELATION_NAME AND
			Y.RDB$VIEW_CONTEXT EQ Z.RDB$VIEW_CONTEXT
		retval = true;
		view_name = Z.RDB$VIEW_NAME;
	END_FOR;

	CMP_release(tdbb, request);

	if (retval)
		DYN_error_punt(false, 206, SafeArg() << field_name.c_str() <<
						relation_name.c_str() << view_name.c_str());
	/* msg 206: Column %s from table %s is referenced in  %s. */

	return retval;
}


static bool check_sptrig_dependency(thread_db* tdbb,
									   Global* gbl,
									   const MetaName& relation_name,
									   const MetaName& field_name)
{
/***********************************************************
 *
 *  c h e c k _ s p t r i g _ d e p e n d e n c y
 ***********************************************************
 *
 *  Functional Description:
 *	Checks to see if the given field is referenced in a stored procedure
 *	or trigger.  If the field is referenced, return true, else return
 *  false, but true causes the function to punt instead.
 ***********************************************************/
	jrd_req* request = NULL;
	bool retval = false;
	MetaName dep_name;

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		FIRST 1
			DEP IN RDB$DEPENDENCIES WITH
			DEP.RDB$DEPENDED_ON_NAME EQ relation_name.c_str() AND
			DEP.RDB$FIELD_NAME EQ field_name.c_str()
		retval = true;
		dep_name = DEP.RDB$DEPENDENT_NAME;
	END_FOR;

	CMP_release(tdbb, request);

	if (retval)
		DYN_error_punt(false, 206, SafeArg() << field_name.c_str() <<
						relation_name.c_str() << dep_name.c_str());
	/* msg 206: Column %s from table %s is referenced in %s. */

	return retval;
}


static void modify_lfield_index(thread_db* tdbb,
								Global* gbl,
								const MetaName&	relation_name,
								const MetaName&	field_name,
								const MetaName&	new_fld_name)
{
/***********************************************************
 *
 *  m o d i f y _ l f i e l d _ i n d e x
 ***********************************************************
 *
 *  Functional Description:
 *	Updates the field names in an index and forces the index to be rebuilt
 *	with the new field names
 *
 ***********************************************************/
	jrd_req* request = NULL;

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		IDX IN RDB$INDICES CROSS IDXS IN RDB$INDEX_SEGMENTS WITH
			IDX.RDB$INDEX_NAME EQ IDXS.RDB$INDEX_NAME AND
			IDX.RDB$RELATION_NAME EQ relation_name.c_str() AND
			IDXS.RDB$FIELD_NAME EQ field_name.c_str()
			/* Change the name of the field in the index */
		MODIFY IDXS USING
			memcpy(IDXS.RDB$FIELD_NAME, new_fld_name.c_str(), sizeof(IDXS.RDB$FIELD_NAME));
		END_MODIFY;

		/* Set the index name to itself to tell the index to rebuild */
		MODIFY IDX USING
			// This is to fool both gpre and gcc.
			char* p = IDX.RDB$INDEX_NAME;
			p[MAX_SQL_IDENTIFIER_LEN] = 0;
		END_MODIFY;
	END_FOR;

	CMP_release(tdbb, request);
}


static bool field_exists(thread_db* tdbb,
						 Global* gbl,
						 const MetaName& relation_name,
						 const MetaName& field_name)
{
/***********************************************************
 *
 *  f i e l d _ e x i s t s
 ***********************************************************
 *
 *  Functional Description:
 *	Checks to see if the given field already exists in a relation
 ***********************************************************/
	jrd_req* request = NULL;
	bool retval = false;

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		FLD IN RDB$RELATION_FIELDS WITH
			FLD.RDB$RELATION_NAME EQ relation_name.c_str() AND
			FLD.RDB$FIELD_NAME EQ field_name.c_str()
		retval = true;
	END_FOR;

	CMP_release(tdbb, request);
	return retval;
}


static bool domain_exists(thread_db* tdbb,
						  Global* gbl,
						  const MetaName& field_name)
{
/***********************************************************
 *
 *  d o m a i n _ e x i s t s
 ***********************************************************
 *
 *  Functional Description:
 *	Checks to see if the given field already exists in a relation
 ***********************************************************/
	jrd_req* request = NULL;
	bool retval = false;

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ field_name.c_str()
		retval = true;
	END_FOR;

	CMP_release(tdbb, request);
	return retval;
}


void DYN_modify_sql_field(Global* gbl,
						  const UCHAR** ptr,
						  const MetaName* relation_name)
{
/**************************************
 *
 *	D Y N _ m o d i f y _ s q l _ f i e l d
 *
 **************************************
 *
 * Functional description
 *	Execute a dynamic ddl statement
 *      to modify the datatype of a field.
 *
 *  If there are dependencies on the field, abort the operation
 *  unless the dependency is an index.  In this case, rebuild the
 *  index once the operation has completed.
 *
 *  If the original datatype of the field was a domain:
 *     if the new type is a domain, just make the change to the new domain
 *     if it exists
 *
 *     if the new type is a base type, just make the change
 *
 *  If the original datatype of the field was a base type:
 *     if the new type is a base type, just make the change
 *
 *     if the new type is a domain, make the change to the field
 *     definition and remove the entry for RDB$FIELD_SOURCE from the original
 *     field.  In other words ... clean up after ourselves
 *
 *  The following conversions are not allowed:
 *        Blob to anything
 *        Array to anything
 *        Date to anything
 *        Char to any numeric
 *        Varchar to any numeric
 *        Anything to Blob
 *        Anything to Array
 *
 *  The following operations return a warning
 *        Decreasing the length of a char (varchar) field
 *
 *	CVC: This is a misleading comment. There's no code that
 *	produces a warning. This condition raises an error, too.
 *
 **************************************/
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	dyn_fld orig_fld, new_fld, dom_fld;

	try {

	GET_STRING(ptr, orig_fld.dyn_fld_name);

	// Check to see if the field being altered is involved in any type of dependency.
	// If there is a dependency, call DYN_error_punt (inside the function).
	fb_assert(relation_name);

	// ASF: check disabled to allow change of field type to be used
	// with TYPE OF COLUMN table.column feature.
	//check_sptrig_dependency(tdbb, gbl, *relation_name,
	//						orig_fld.dyn_fld_name);

	jrd_req* request = NULL;
	jrd_req* first_request = NULL;
	bool found = false;
	bool dtype, scale, prec, subtype, charlen, collation, fldlen, nullflg, charset;
	dtype = scale = prec = subtype = charlen = collation = fldlen = nullflg = charset = false;

	int field_adjusted_count = 0;

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		RFR IN RDB$RELATION_FIELDS CROSS
		REL IN RDB$RELATIONS WITH
			REL.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME AND
			RFR.RDB$RELATION_NAME = relation_name->c_str() AND
			RFR.RDB$FIELD_NAME = orig_fld.dyn_fld_name.c_str()
		first_request = request;
		request = NULL;

		found = true;
		bool is_view = !REL.RDB$VIEW_BLR.NULL;
		bool has_dimensions = false;
		bool update_domain = false;
		bool domain_has_default = false;
		bool domain_is_computed = false;
		SSHORT fld_position = 0;
		bool fld_position_change = false;
		SSHORT view_context = 0;
		bool view_context_change = false;
		MetaName fld_base_field;
		bool fld_base_field_change = false;
		bool orig_computed = false;

		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
			FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE
			// Get information about the original field type.  If a conversion
			// can not be at any time made between the two datatypes, error.
			DSC_make_descriptor(&orig_fld.dyn_dsc,
								FLD.RDB$FIELD_TYPE,
								FLD.RDB$FIELD_SCALE,
								FLD.RDB$FIELD_LENGTH,
								FLD.RDB$FIELD_SUB_TYPE,
								FLD.RDB$CHARACTER_SET_ID,
								FLD.RDB$COLLATION_ID);

			orig_fld.dyn_charbytelen = FLD.RDB$FIELD_LENGTH;
			orig_fld.dyn_dtype = FLD.RDB$FIELD_TYPE;
			orig_fld.dyn_precision = FLD.RDB$FIELD_PRECISION;
			orig_fld.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE;
			orig_fld.dyn_charlen = FLD.RDB$CHARACTER_LENGTH;
			orig_fld.dyn_collation = FLD.RDB$COLLATION_ID;
			orig_fld.dyn_null_flag = FLD.RDB$NULL_FLAG != 0;
			orig_fld.dyn_fld_source = RFR.RDB$FIELD_SOURCE;
			orig_computed = !FLD.RDB$COMPUTED_BLR.NULL;

			// If the original field type is an array, force its blr type to blr_blob
			if (FLD.RDB$DIMENSIONS != 0)
			{
				orig_fld.dyn_dtype = blr_blob;
				has_dimensions = true;
			}

			domain_has_default = !FLD.RDB$DEFAULT_VALUE.NULL;
			domain_is_computed = !FLD.RDB$COMPUTED_BLR.NULL;

			UCHAR verb;
			while ((verb = *(*ptr)++) != isc_dyn_end) {
				switch (verb)
				{
				case isc_dyn_fld_source:
					GET_STRING(ptr, dom_fld.dyn_fld_source);
					if (fb_utils::implicit_domain(dom_fld.dyn_fld_source.c_str()))
					{
						DYN_error_punt(false, 224, SafeArg() << dom_fld.dyn_fld_source.c_str() <<
										orig_fld.dyn_fld_name.c_str());
						// msg 224: "Cannot use the internal domain %s as new type for field %s".
					}
					get_domain_type(tdbb, gbl, dom_fld);
					update_domain = true;
					break;

				case isc_dyn_rel_name:
					GET_STRING(ptr, new_fld.dyn_rel_name);
					break;

				case isc_dyn_fld_length:
					fldlen = true;
					new_fld.dyn_dsc.dsc_length = DYN_get_number(ptr);
					if (++field_adjusted_count > 2)
					{
						DYN_error_punt(false, 149, orig_fld.dyn_fld_name.c_str());
						// msg 149: "Only one data type change to the field %s allowed at a time"
					}

					switch (new_fld.dyn_dtype)
					{
					case blr_text:
					case blr_text2:
					case blr_varying:
					case blr_varying2:
					case blr_cstring:
					case blr_cstring2:
						new_fld.dyn_charbytelen = new_fld.dyn_dsc.dsc_length;
						break;
					default:
						new_fld.dyn_charbytelen = 0; // It won't be used, anyway.
						break;
					}
					break;

				case isc_dyn_fld_type:
					dtype = true;
					new_fld.dyn_dtype = DYN_get_number(ptr);
					if (++field_adjusted_count > 2)
					{
						DYN_error_punt(false, 149, orig_fld.dyn_fld_name.c_str());
						// msg 149: "Only one data type change to the field %s allowed at a time"
					}

					switch (new_fld.dyn_dtype)
					{
					case blr_text:
					case blr_text2:
					case blr_varying:
					case blr_varying2:
					case blr_cstring:
					case blr_cstring2:
						if (new_fld.dyn_dsc.dsc_length && !new_fld.dyn_charbytelen)
							new_fld.dyn_charbytelen = new_fld.dyn_dsc.dsc_length;
						new_fld.dyn_dsc.dsc_length = DSC_string_length (&new_fld.dyn_dsc);
						break;

					case blr_short:
						new_fld.dyn_dsc.dsc_length = 2;
						break;

					case blr_long:
					case blr_float:
						new_fld.dyn_dsc.dsc_length = 4;
						break;

					case blr_int64:
					case blr_sql_time:
					case blr_sql_date:
					case blr_timestamp:
					case blr_double:
					case blr_d_float:
						new_fld.dyn_dsc.dsc_length = 8;
						break;

					default:
						break;
					}
					break;

				case isc_dyn_fld_scale:
					scale = true;
					new_fld.dyn_dsc.dsc_scale = DYN_get_number(ptr);
					break;

				case isc_dyn_fld_precision:
					prec = true;
					new_fld.dyn_precision = DYN_get_number(ptr);
					break;

				case isc_dyn_fld_sub_type:
					subtype = true;
					new_fld.dyn_sub_type = DYN_get_number(ptr);
					break;

				case isc_dyn_fld_char_length:
					charlen = true;
					new_fld.dyn_charlen = DYN_get_number(ptr);
					break;

				case isc_dyn_fld_collation:
					collation = true;
					new_fld.dyn_collation = DYN_get_number(ptr);
					break;

				case isc_dyn_fld_character_set:
					charset = true;
					new_fld.dyn_charset = DYN_get_number(ptr);
					break;

				case isc_dyn_fld_not_null:
					nullflg = true;
					new_fld.dyn_null_flag = true;
					break;

				case isc_dyn_fld_dimensions:
					new_fld.dyn_dtype = blr_blob;
					break;

				case isc_dyn_fld_position:
					fld_position = DYN_get_number(ptr);
					fld_position_change = true;
					break;

				case isc_dyn_fld_base_fld:
					GET_STRING(ptr, fld_base_field);
					fld_base_field_change = true;
					break;

				case isc_dyn_view_context:
					view_context = DYN_get_number(ptr);
					view_context_change = true;
					break;

				case isc_dyn_fld_computed_blr:
					domain_is_computed = true;
					new_fld.dyn_computed_val = *ptr;
					DYN_skip_attribute(ptr);
					break;

				case isc_dyn_fld_computed_source:
					new_fld.dyn_computed_src = *ptr;
					DYN_skip_attribute(ptr);
					break;

				case isc_dyn_del_computed:
					domain_is_computed = false;
					new_fld.dyn_drop_computed = true;
					break;

				case isc_dyn_del_default:
					new_fld.dyn_drop_default = true;
					break;

				case isc_dyn_fld_default_value:
					if (has_dimensions)
					{
						DYN_error_punt(false, 225, orig_fld.dyn_fld_name.c_str());
						// msg 225: "Default value is not allowed for array type in field %s"
					}
					new_fld.dyn_default_val = *ptr;
					DYN_skip_attribute(ptr);
					break;

				case isc_dyn_fld_default_source:
					if (has_dimensions)
					{
						DYN_error_punt(false, 225, orig_fld.dyn_fld_name.c_str());
						// msg 225: "Default value is not allowed for array type in field %s"
					}
					new_fld.dyn_default_src = *ptr;
					DYN_skip_attribute(ptr);
					break;

				// These should only be defined for BLOB types and should not come through with
				// any other types.  BLOB types are detected above
				case isc_dyn_fld_segment_length:
					DYN_get_number(ptr);
					break;

				default:
					--(*ptr);
					MetaTmp(RFR.RDB$FIELD_SOURCE)
						DYN_execute(gbl, ptr, relation_name, &tmp, NULL, NULL, NULL);
				}
			}

			if (fld_base_field_change && view_context_change)
			{
				fb_assert(is_view);

				if (fld_base_field.hasData())
				{
					char field_name[MAX_SQL_IDENTIFIER_SIZE];
					DYN_UTIL_find_field_source(tdbb, gbl, RFR.RDB$RELATION_NAME, view_context,
						fld_base_field.c_str(), field_name);
					dom_fld.dyn_fld_source = field_name;
					update_domain = true;
				}
				else
					DYN_UTIL_generate_field_name(tdbb, gbl, new_fld.dyn_fld_source);
			}
		END_FOR; // FLD in RDB$FIELDS
		CMP_release(tdbb, request);
		request = NULL;

		if (!is_view &&
			((new_fld.dyn_computed_val && !orig_computed) ||
				(!new_fld.dyn_computed_val && orig_computed)))
		{
			// Cannot add or remove COMPUTED from column @1
			DYN_error_punt(false, 249, SafeArg() << orig_fld.dyn_fld_name.c_str());
		}

		const bool sourceIsInternalDomain =
			fb_utils::implicit_domain(orig_fld.dyn_fld_source.c_str()) && RFR.RDB$BASE_FIELD.NULL;
		const bool changeComputed = new_fld.dyn_computed_val || new_fld.dyn_drop_computed;

		// Now that we have all of the information needed, let's check to see
		// if the field type can be modified

		if (update_domain)
		{
			// a1. Internal domain -> domain.
			// a2. Domain -> domain.

			/* CVC: Since get_domain_type() called above already called DSC_make_descriptor,
					there's no point in calling it again, since it will increment AGAIN the length
					of varchar fields! This bug detected thanks to new check field dyn_charbytelen.
			DSC_make_descriptor(&dom_fld.dyn_dsc,
								dom_fld.dyn_dtype,
								dom_fld.dyn_dsc.dsc_scale,
								dom_fld.dyn_dsc.dsc_length,
								dom_fld.dyn_sub_type,
								dom_fld.dyn_charset, dom_fld.dyn_collation);
			*/

			if (!domain_is_computed && !is_view)
			{
				const ULONG retval = check_update_fld_type(orig_fld, dom_fld);
				if (retval != FB_SUCCESS)
					modify_err_punt(tdbb, retval, orig_fld, dom_fld);
			}

			// If the original definition was a base field type, remove the entries from RDB$FIELDS
			if (sourceIsInternalDomain)
			{
				// a1. Internal domain -> domain.
				FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
					FLD IN RDB$FIELDS
					WITH FLD.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE

					ERASE FLD;
				END_FOR;

				CMP_release(tdbb, request);
				request = NULL;
			}
			request = first_request;
			MODIFY RFR USING
				strcpy(RFR.RDB$FIELD_SOURCE, dom_fld.dyn_fld_source.c_str());
				RFR.RDB$FIELD_SOURCE.NULL = FALSE;
				if (domain_is_computed)
				{
					RFR.RDB$UPDATE_FLAG.NULL = FALSE;
					RFR.RDB$UPDATE_FLAG = 1;
				}

				RFR.RDB$COLLATION_ID.NULL = TRUE;	// CORE-2426
			END_MODIFY;
			first_request = request;
			request = NULL;

			if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_2)
			{
				FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
					PRM IN RDB$PROCEDURE_PARAMETERS
					WITH PRM.RDB$RELATION_NAME = relation_name->c_str() AND
						 PRM.RDB$FIELD_NAME = orig_fld.dyn_fld_name.c_str()

					MODIFY PRM USING
						strcpy(PRM.RDB$FIELD_SOURCE, dom_fld.dyn_fld_source.c_str());
					END_MODIFY;
				END_FOR;

				CMP_release(tdbb, request);
				request = NULL;
			}
		}
		else
		{
			// b1. Domain -> internal domain.
			// b2. Internal domain -> internal domain.

			const UCHAR* defVal = new_fld.dyn_default_val;
			const UCHAR* defSrc = new_fld.dyn_default_src;
			const bool dropDefault = new_fld.dyn_drop_default;
			const bool changeDefault = defVal || defSrc || dropDefault;

			// If we are altering only the default, we need the old field settings.
			if (changeDefault && !new_fld.dyn_dsc.dsc_dtype)
			{
				new_fld = orig_fld;
				// We already called DSC_make_descriptor on orig_fld, so fix new_fld.
				switch (new_fld.dyn_dtype)
				{
				case blr_text:
				case blr_varying:
				case blr_cstring:
				    new_fld.dyn_dsc.dsc_length = DSC_string_length(&orig_fld.dyn_dsc);
					break;
				}
			}

			DSC_make_descriptor(&new_fld.dyn_dsc,
								new_fld.dyn_dtype,
								new_fld.dyn_dsc.dsc_scale,
								new_fld.dyn_dsc.dsc_length,
								new_fld.dyn_sub_type,
								new_fld.dyn_charset, new_fld.dyn_collation);

			if (!changeComputed && !orig_computed && !is_view)
			{
				const ULONG retval = check_update_fld_type(orig_fld, new_fld);
				if (retval != FB_SUCCESS)
					modify_err_punt(tdbb, retval, orig_fld, new_fld);
			}

			// Check to see if the original data type for the field was based on a domain.  If it
			// was (and now it isn't), remove the domain information and replace it with a generated
			// field name for RDB$FIELDS

			if (!changeDefault && !sourceIsInternalDomain)
			{
				// b1. Domain -> internal domain. Not for changing DEFAULT value.

				request = first_request;
				MODIFY RFR USING
					DYN_UTIL_generate_field_name(tdbb, gbl, RFR.RDB$FIELD_SOURCE);
					new_fld.dyn_fld_source = RFR.RDB$FIELD_SOURCE;
				END_MODIFY;
				first_request = request;
				request = NULL;

				STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
					FLD IN RDB$FIELDS
					FLD.RDB$SYSTEM_FLAG = 0;
					FLD.RDB$SYSTEM_FLAG.NULL = FALSE;
					FLD.RDB$FIELD_SCALE.NULL = TRUE;
					FLD.RDB$FIELD_PRECISION.NULL = TRUE;
					FLD.RDB$FIELD_SUB_TYPE.NULL = TRUE;
					FLD.RDB$SEGMENT_LENGTH.NULL = TRUE;
					FLD.RDB$COMPUTED_BLR.NULL = TRUE;
					FLD.RDB$COMPUTED_SOURCE.NULL = TRUE;
					FLD.RDB$DEFAULT_VALUE.NULL = TRUE;
					FLD.RDB$DEFAULT_SOURCE.NULL = TRUE;
					FLD.RDB$VALIDATION_BLR.NULL = TRUE;
					FLD.RDB$VALIDATION_SOURCE.NULL = TRUE;
					FLD.RDB$NULL_FLAG.NULL = TRUE;
					FLD.RDB$EDIT_STRING.NULL = TRUE;
					FLD.RDB$DIMENSIONS.NULL = TRUE;
					FLD.RDB$CHARACTER_LENGTH.NULL = TRUE;
					FLD.RDB$CHARACTER_SET_ID.NULL = TRUE;
					FLD.RDB$COLLATION_ID.NULL = TRUE;

					if (dtype) {
						FLD.RDB$FIELD_TYPE = new_fld.dyn_dtype;
						FLD.RDB$FIELD_TYPE.NULL = FALSE;
					}

					if (scale) {
						FLD.RDB$FIELD_SCALE = new_fld.dyn_dsc.dsc_scale;
						FLD.RDB$FIELD_SCALE.NULL = FALSE;
					}

					if (prec) {
						FLD.RDB$FIELD_PRECISION = new_fld.dyn_precision;
						FLD.RDB$FIELD_PRECISION.NULL = FALSE;
					}

					if (subtype) {
						FLD.RDB$FIELD_SUB_TYPE = new_fld.dyn_sub_type;
						FLD.RDB$FIELD_SUB_TYPE.NULL = FALSE;
					}

					if (charlen) {
						FLD.RDB$CHARACTER_LENGTH = new_fld.dyn_charlen;
						FLD.RDB$CHARACTER_LENGTH.NULL = FALSE;
					}

					if (charset) {
						FLD.RDB$CHARACTER_SET_ID = new_fld.dyn_charset;
						FLD.RDB$CHARACTER_SET_ID.NULL = FALSE;
					}

					if (collation) {
						FLD.RDB$COLLATION_ID = new_fld.dyn_collation;
						FLD.RDB$COLLATION_ID.NULL = FALSE;
					}

					if (fldlen) {
						// CVC: Rescue from the wrong field_length with a helper.
						if (new_fld.dyn_dsc.dsc_dtype <= dtype_varying && new_fld.dyn_charbytelen)
							FLD.RDB$FIELD_LENGTH = new_fld.dyn_charbytelen;
						else
							FLD.RDB$FIELD_LENGTH = new_fld.dyn_dsc.dsc_length;
						FLD.RDB$FIELD_LENGTH.NULL = FALSE;
					}

					if (new_fld.dyn_computed_val)
					{
						DYN_put_blr_blob(gbl, &new_fld.dyn_computed_val, &FLD.RDB$COMPUTED_BLR);
						FLD.RDB$COMPUTED_BLR.NULL = FALSE;
					}

					if (new_fld.dyn_computed_src)
					{
						DYN_put_text_blob(gbl, &new_fld.dyn_computed_src, &FLD.RDB$COMPUTED_SOURCE);
						FLD.RDB$COMPUTED_SOURCE.NULL = FALSE;
					}

					// Copy the field name into RDB$FIELDS
					strcpy(FLD.RDB$FIELD_NAME, new_fld.dyn_fld_source.c_str());
				END_STORE;

				CMP_release(tdbb, request);
				request = NULL;

				if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_2)
				{
					FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
						PRM IN RDB$PROCEDURE_PARAMETERS
						WITH PRM.RDB$RELATION_NAME = relation_name->c_str() AND
							 PRM.RDB$FIELD_NAME = orig_fld.dyn_fld_name.c_str()

						MODIFY PRM USING
							strcpy(PRM.RDB$FIELD_SOURCE, new_fld.dyn_fld_source.c_str());
						END_MODIFY;
					END_FOR;

					CMP_release(tdbb, request);
					request = NULL;
				}
			}
			else if (changeDefault)
			{
				request = first_request;
				MODIFY RFR USING
					if (dropDefault)
					{
						if (RFR.RDB$DEFAULT_VALUE.NULL)
						{
							if (sourceIsInternalDomain || !domain_has_default)
							{
								DYN_error_punt(false, 229, orig_fld.dyn_fld_name.c_str());
								// msg 229: "Local column %s doesn't have a default"
							}
							else if (domain_has_default)
							{
								DYN_error_punt(false, 230, SafeArg() << orig_fld.dyn_fld_name.c_str() <<
												orig_fld.dyn_fld_source.c_str());
								// msg 230: "Local column %s default belongs to domain %s"
							}
						}
						else
						{
							RFR.RDB$DEFAULT_VALUE.NULL = TRUE;
							RFR.RDB$DEFAULT_SOURCE.NULL = TRUE;
						}
					}
					else
					{
						if (domain_is_computed)
						{
							DYN_error_punt(false, 233, orig_fld.dyn_fld_name.c_str());
							// msg 233: "Local column %s is computed, cannot set a default value"
						}
						if (defVal)
						{
							const UCHAR* p = defVal;
							if (DYN_put_blr_blob(gbl, &p, &RFR.RDB$DEFAULT_VALUE))
								RFR.RDB$DEFAULT_VALUE.NULL = FALSE;
						}

						if (defSrc)
						{
							const UCHAR* p = defSrc;
							if (DYN_put_text_blob(gbl, &p, &RFR.RDB$DEFAULT_SOURCE))
								RFR.RDB$DEFAULT_SOURCE.NULL = FALSE;
						}
					}
				END_MODIFY
				first_request = request;
				request = NULL;
			}
			else //if (sourceIsInternalDomain)
			{	// The original and new definitions are both base types.
				// b2. Internal domain -> internal domain.
				// Modify in place, since there cannot be other fields using it.

				//if (!sourceIsInternalDomain)
				//	DYN_UTIL_copy_domain(tdbb, gbl, orig_fld.dyn_fld_source, new_fld.dyn_fld_source);

				FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
					FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE
					MODIFY FLD USING

						if (dtype) {
							FLD.RDB$FIELD_TYPE = new_fld.dyn_dtype;
							FLD.RDB$FIELD_TYPE.NULL = FALSE;
						}

						if (scale) {
							FLD.RDB$FIELD_SCALE = new_fld.dyn_dsc.dsc_scale;
							FLD.RDB$FIELD_SCALE.NULL = FALSE;
						}

						if (prec) {
							FLD.RDB$FIELD_PRECISION = new_fld.dyn_precision;
							FLD.RDB$FIELD_PRECISION.NULL = FALSE;
						}

						if (subtype) {
							FLD.RDB$FIELD_SUB_TYPE = new_fld.dyn_sub_type;
							FLD.RDB$FIELD_SUB_TYPE.NULL = FALSE;
						}

						if (charlen) {
							FLD.RDB$CHARACTER_LENGTH = new_fld.dyn_charlen;
							FLD.RDB$CHARACTER_LENGTH.NULL = FALSE;
						}

						if (charset) {
							FLD.RDB$CHARACTER_SET_ID = new_fld.dyn_charset;
							FLD.RDB$CHARACTER_SET_ID.NULL = FALSE;
						}

						if (collation) {
							FLD.RDB$COLLATION_ID = new_fld.dyn_collation;
							FLD.RDB$COLLATION_ID.NULL = FALSE;
						}

						if (fldlen) {
							// CVC: Rescue from the wrong field_length with a helper.
							if (new_fld.dyn_dsc.dsc_dtype <= dtype_varying && new_fld.dyn_charbytelen)
								FLD.RDB$FIELD_LENGTH = new_fld.dyn_charbytelen;
							else
								FLD.RDB$FIELD_LENGTH = new_fld.dyn_dsc.dsc_length;
							FLD.RDB$FIELD_LENGTH.NULL = FALSE;
						}

						if (changeComputed)
						{
							FLD.RDB$DEFAULT_SOURCE.NULL = TRUE;
							FLD.RDB$DEFAULT_VALUE.NULL = TRUE;

							if (new_fld.dyn_computed_val)
							{
								DYN_put_blr_blob(gbl, &new_fld.dyn_computed_val, &FLD.RDB$COMPUTED_BLR);
								FLD.RDB$COMPUTED_BLR.NULL = FALSE;
							}

							if (new_fld.dyn_computed_src)
							{
								DYN_put_text_blob(gbl, &new_fld.dyn_computed_src, &FLD.RDB$COMPUTED_SOURCE);
								FLD.RDB$COMPUTED_SOURCE.NULL = FALSE;
							}
						}

					END_MODIFY;
				END_FOR; // FLD in RDB$FIELDS
				CMP_release(tdbb, request);
				if (domain_is_computed)
				{
					request = first_request;
					MODIFY RFR USING
						RFR.RDB$UPDATE_FLAG.NULL = FALSE;
						RFR.RDB$UPDATE_FLAG = 1;
					END_MODIFY;
					first_request = request;
				}

				request = NULL;
			}
		} // else not a domain

		request = first_request;

		if (fld_position_change || view_context_change || fld_base_field_change)
		{
			MODIFY RFR USING
				if (fld_position_change)
					RFR.RDB$FIELD_POSITION = fld_position;
				if (view_context_change)
					RFR.RDB$VIEW_CONTEXT = view_context;
				if (fld_base_field_change)
				{
					RFR.RDB$BASE_FIELD.NULL = fld_base_field.isEmpty();
					strcpy(RFR.RDB$BASE_FIELD, fld_base_field.c_str());
				}
			END_MODIFY;
		}
	END_FOR; // RFR IN RDB$RELATION_FIELDS
	CMP_release(tdbb, request);
	request = NULL;

	if (!found)
	{
		DYN_error_punt(false, 176, SafeArg() << orig_fld.dyn_fld_name.c_str() <<
						relation_name->c_str());
		// msg 176: "column %s does not exist in table/view %s"
	}

	// Update any indices that exist
	modify_lfield_index(tdbb, gbl, *relation_name, orig_fld.dyn_fld_name, orig_fld.dyn_fld_name);

	}
	catch (const Exception& ex) {
		stuff_exception(tdbb->tdbb_status_vector, ex);
		DYN_error_punt(true, 95);
		// msg 95: "MODIFY RDB$RELATION_FIELDS failed"
	}
}


// *************************************
// D Y N _ m o d i f y _ m a p p i n g
// *************************************
// It's purpose is to add/drop mapping from OS security name
// to DB security object
void DYN_modify_mapping(Global* gbl, const UCHAR** ptr)
{
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

	jrd_req* request = CMP_find_request(tdbb, drq_m_map, DYN_REQUESTS);
	bool found = false;

	string osName, dbName;
	GET_STRING(ptr, osName);
	const UCHAR op = *(*ptr)++;
	GET_STRING(ptr, dbName);

	// This is FB 2.5 limited implementation!
	// Later it should work with new system table, something like RDB$MAPPING.

	if (dbName != ADMIN_ROLE)
	{
		status_exception::raise(Arg::Gds(isc_no_meta_update) << Arg::Gds(isc_wish_list));
	}

	if (!(tdbb->getAttachment() && tdbb->getAttachment()->locksmith()))
		ERR_post(Arg::Gds(isc_adm_task_denied));

	found = false;
	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		X IN RDB$ROLES
			WITH X.RDB$ROLE_NAME EQ dbName.c_str()

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

		found = true;
		MODIFY X
			switch (op)
			{
			case isc_dyn_automap_role:
				X.RDB$SYSTEM_FLAG = ROLE_FLAG_DBO | ROLE_FLAG_MAY_TRUST;
				break;

			case isc_dyn_autounmap_role:
				X.RDB$SYSTEM_FLAG = ROLE_FLAG_DBO;
				break;

			default:
				DYN_unsupported_verb();
			}
		END_MODIFY;
	END_FOR;

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

	if (!found)
	{
		status_exception::raise(Arg::Gds(isc_no_meta_update) <<
			Arg::Gds(isc_random) << Arg::Str("Missing RDB$ADMIN role in the database"));
	}
}

void get_domain_type(thread_db* tdbb, Global* gbl, dyn_fld& dom_fld)
{
/**************************************
 *
 *	g e t _ d o m a i n _ t y p e
 *
 **************************************
 *
 * Functional description
 *	Retrieves the type information for a domain so
 *      that it can be compared to a local field before
 *      modifying the datatype of a field.
 *
 **************************************/
	jrd_req* request = NULL;

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
		FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ dom_fld.dyn_fld_source.c_str();

		DSC_make_descriptor(&dom_fld.dyn_dsc,
							FLD.RDB$FIELD_TYPE,
							FLD.RDB$FIELD_SCALE,
							FLD.RDB$FIELD_LENGTH,
							FLD.RDB$FIELD_SUB_TYPE,
							FLD.RDB$CHARACTER_SET_ID,
							FLD.RDB$COLLATION_ID);

		dom_fld.dyn_charbytelen = FLD.RDB$FIELD_LENGTH;
		dom_fld.dyn_dtype = FLD.RDB$FIELD_TYPE;
		dom_fld.dyn_precision = FLD.RDB$FIELD_PRECISION;
		dom_fld.dyn_sub_type = FLD.RDB$FIELD_SUB_TYPE;
		dom_fld.dyn_charlen = FLD.RDB$CHARACTER_LENGTH;
		dom_fld.dyn_collation = FLD.RDB$COLLATION_ID;
		dom_fld.dyn_null_flag = FLD.RDB$NULL_FLAG != 0;

		if (!FLD.RDB$DIMENSIONS.NULL && FLD.RDB$DIMENSIONS > 0)
			dom_fld.dyn_dtype = blr_blob;

	END_FOR;
	CMP_release(tdbb, request);
}


static ULONG check_update_fld_type(const dyn_fld& orig_fld,
									const dyn_fld& new_fld)
{
/**************************************
 *
 *	c h e c k _ u p d a t e _ f l d _ t y p e
 *
 **************************************
 *
 * Functional description
 *	Compare the original field type with the new field type to
 *      determine if the original type can be changed to the new type
 *
 *  The following conversions are not allowed:
 *        Blob to anything
 *        Array to anything
 *        Date to anything
 *        Char to any numeric
 *        Varchar to any numeric
 *        Anything to Blob
 *        Anything to Array
 *
 *  This function returns an error code if the conversion can not be
 *  made.  If the conversion can be made, FB_SUCCESS is returned
 **************************************/


/* Check to make sure that the old and new types are compatible */
	switch (orig_fld.dyn_dtype)
	{
/* CHARACTER types */
	case blr_text:
	case blr_varying:
	case blr_cstring:
		switch (new_fld.dyn_dtype)
		{
		case blr_blob:
		case blr_blob_id:
			return isc_dyn_dtype_invalid;
			/* Cannot change datatype for column %s.
			   The operation cannot be performed on BLOB, or ARRAY columns. */
			break;

		case blr_sql_date:
		case blr_sql_time:
		case blr_timestamp:
		case blr_int64:
		case blr_long:
		case blr_short:
		case blr_d_float:
		case blr_double:
		case blr_float:
			return isc_dyn_dtype_conv_invalid;
			/* Cannot convert column %s from character to non-character data. */
			break;

			/* If the original field is a character field and the new field is a character field,
			 * is there enough space in the new field? */
		case blr_text:
		case blr_varying:
		case blr_cstring:
			{
				/* CVC: Because our caller invoked DSC_make_descriptor() on new_fld previously,
						we should have the added bytes for varchar. For cstring, we are done, since
						DSC_make_descriptor(DSC_string_length) != DSC_string_length(DSC_make_descriptor). */

				const USHORT maxflen = DSC_string_length(&orig_fld.dyn_dsc);

				/* We can have this assertion since this case is for both string fields. */
				const ULONG new_len = DSC_string_length(&new_fld.dyn_dsc);
				fb_assert(new_len - maxflen == (ULONG) new_fld.dyn_charbytelen - orig_fld.dyn_charbytelen);
				// if (new_fld.dyn_dsc.dsc_length < maxflen)
				if (new_len < maxflen)
					return isc_dyn_char_fld_too_small;
				// msg 208: New size specified for column %s must be at least %d characters.
			}
			break;

		default:
			fb_assert(FALSE);
			return 87;			/* MODIFY RDB$FIELDS FAILED */
		}
		break;

/* BLOB and ARRAY types */
	case blr_blob:
	case blr_blob_id:
		return isc_dyn_dtype_invalid;
		/* Cannot change datatype for column %s.
		   The operation cannot be performed on BLOB, or ARRAY columns. */

/* DATE types */
	case blr_sql_date:
	case blr_sql_time:
	case blr_timestamp:
		switch (new_fld.dyn_dtype)
		{
		case blr_sql_date:
			if (orig_fld.dyn_dtype == blr_sql_time)
				return isc_dyn_invalid_dtype_conversion;
			/* Cannot change datatype for column %s.  Conversion from base type %s to base type %s is not supported. */
			break;

		case blr_sql_time:
			if (orig_fld.dyn_dtype == blr_sql_date)
				return isc_dyn_invalid_dtype_conversion;
			/* Cannot change datatype for column %s.  Conversion from base type %s to base type %s is not supported. */
			break;

		case blr_timestamp:
			if (orig_fld.dyn_dtype == blr_sql_time)
				return isc_dyn_invalid_dtype_conversion;
			/* Cannot change datatype for column %s.  Conversion from base type %s to base type %s is not supported. */
			break;

			/* If the original field is a date field and the new field is a character field,
			   * is there enough space in the new field? */
		case blr_text:
		case blr_text2:
		case blr_varying:
		case blr_varying2:
		case blr_cstring:
		case blr_cstring2:
			{
				const USHORT maxflen = DSC_string_length(&orig_fld.dyn_dsc);

				// CVC: Solve bug #910423, missing DSC_string_length call.
				// if (new_fld.dyn_dsc.dsc_length < maxflen)
				if (DSC_string_length(&new_fld.dyn_dsc) < maxflen)
					return isc_dyn_char_fld_too_small;
				// msg 208: New size specified for column %s must be at least %d characters.
			}

			break;

		default:
			return isc_dyn_invalid_dtype_conversion;
			/* Cannot change datatype for column %s.  Conversion from base type %s to base type %s is not supported. */
		}
		break;

/* NUMERIC types */
	case blr_int64:
	case blr_long:
	case blr_short:
	case blr_d_float:
	case blr_double:
	case blr_float:
		switch (new_fld.dyn_dtype)
		{
		case blr_blob:
		case blr_blob_id:
			return isc_dyn_dtype_invalid;
			/* Cannot change datatype for column %s.
			   The operation cannot be performed on BLOB, or ARRAY columns. */

		case blr_sql_date:
		case blr_sql_time:
		case blr_timestamp:
			return isc_dyn_invalid_dtype_conversion;
			/* Cannot change datatype for column %s.  Conversion from base type %s to base type %s is not supported. */

			/* If the original field is a numeric field and the new field is a numeric field,
			 * is there enough space in the new field (do not allow the base type to decrease) */

		case blr_short:
			switch (orig_fld.dyn_dtype)
			{
			case blr_short:
				return check_update_numeric_type(orig_fld, new_fld);

			default:
				return isc_dyn_invalid_dtype_conversion;
				/* Cannot change datatype for column %s.  Conversion from base type %s to base type %s is not supported. */
			}
			break;

		case blr_long:
			switch (orig_fld.dyn_dtype)
			{
			case blr_long:
			case blr_short:
				return check_update_numeric_type(orig_fld, new_fld);

			default:
				return isc_dyn_invalid_dtype_conversion;
				/* Cannot change datatype for column %s.  Conversion from base type %s to base type %s is not supported. */
			}
			break;

		case blr_float:
			switch (orig_fld.dyn_dtype)
			{
			case blr_float:
			case blr_short:
				break;

			default:
				return isc_dyn_invalid_dtype_conversion;
				/* Cannot change datatype for column %s.  Conversion from base type %s to base type %s is not supported. */
			}
			break;

		case blr_int64:
			switch (orig_fld.dyn_dtype)
			{
			case blr_int64:
			case blr_long:
			case blr_short:
				return check_update_numeric_type(orig_fld, new_fld);

			default:
				return isc_dyn_invalid_dtype_conversion;
				/* Cannot change datatype for column %s.  Conversion from base type %s to base type %s is not supported. */
			}
			break;

		case blr_d_float:
		case blr_double:
			switch (orig_fld.dyn_dtype)
			{
			case blr_double:
			case blr_d_float:
			case blr_float:
			case blr_short:
			case blr_long:
				break;

			default:
				return isc_dyn_invalid_dtype_conversion;
				/* Cannot change datatype for column %s.  Conversion from base type %s to base type %s is not supported. */
			}
			break;

			/* If the original field is a numeric field and the new field is a character field,
			 * is there enough space in the new field? */
		case blr_text:
		case blr_varying:
		case blr_cstring:
			{
				const USHORT maxflen = DSC_string_length(&orig_fld.dyn_dsc);

				// CVC: Solve bug #910423, missing DSC_string_length call.
				// if (new_fld.dyn_dsc.dsc_length < maxflen)
				if (DSC_string_length(&new_fld.dyn_dsc) < maxflen)
					return isc_dyn_char_fld_too_small;
				// msg 208: New size specified for column %s must be at least %d characters.
			}
			break;

		default:
			fb_assert(FALSE);
			return 87;			/* MODIFY RDB$FIELDS FAILED */
		}
		break;

	default:
		fb_assert(FALSE);
		return 87;				/* MODIFY RDB$FIELDS FAILED */
	}
	return FB_SUCCESS;
}


static ULONG check_update_numeric_type(const dyn_fld& orig_fld, const dyn_fld& new_fld)
{
/**************************************
 *
 *	c h e c k _ u p d a t e _ n u m e r i c _ t y p e
 *
 **************************************
 *
 * Functional description
 *	Compare the original field type with the new field type to
 *      determine if the original type can be changed to the new type.
 *  The types should be integral, since it tests only numeric/decimal subtypes
 *      to ensure the scale is not being widened at the expense of the precision,
 *      because the old stored values should fit in the new definition.
 *
 *  This function returns an error code if the conversion can not be
 *  made.  If the conversion can be made, FB_SUCCESS is returned.
 **************************************/

 	// Since dsc_scale is negative, the sum of precision and scale produces
	// the width of the integral part.
	if (orig_fld.dyn_sub_type && new_fld.dyn_sub_type &&
		orig_fld.dyn_precision + orig_fld.dyn_dsc.dsc_scale >
			new_fld.dyn_precision + new_fld.dyn_dsc.dsc_scale)
	{
		return isc_dyn_scale_too_big;
	}
	return FB_SUCCESS;
}


static void modify_err_punt(thread_db* /*tdbb*/,
							ULONG errorcode,
							const dyn_fld& orig_fld_def,
							const dyn_fld& new_fld_def)
{
/**************************************
 *
 *	m o d i f y _ e r r _ p u n t
 *
 **************************************
 *
 * Functional description
 *	A generic error routine that calls DYN_error_punt
 *      based on the error code returned by check_update_field_type.
 *
 *      This function is called by DYN_modify_global_field and by
 *	DYN_modify_sql_field
 **************************************/

	switch (errorcode)
	{
	case isc_dyn_dtype_invalid:
		DYN_error_punt(false, errorcode, orig_fld_def.dyn_fld_name.c_str());
		/* Cannot change datatype for column %s.The operation cannot be performed on DATE, BLOB, or ARRAY columns. */
		break;

	case isc_dyn_dtype_conv_invalid:
		DYN_error_punt(false, errorcode, orig_fld_def.dyn_fld_name.c_str());
		/* Cannot convert column %s from character to non-character data. */
		break;

	case isc_dyn_char_fld_too_small:
		DYN_error_punt(false,
						errorcode,
						SafeArg() << orig_fld_def.dyn_fld_name.c_str() <<
						DSC_string_length(&orig_fld_def.dyn_dsc));
		// msg 208: New size specified for column %s must be at least %d characters.
		break;

	case isc_dyn_scale_too_big:
		{
			int code = errorcode;
			int diff = new_fld_def.dyn_precision -
				(orig_fld_def.dyn_precision + orig_fld_def.dyn_dsc.dsc_scale);
			if (diff < 0)
			{
				// If new scale becomes negative externally, the message is useless for the user.
				// (The scale is always zero or negative for us but externally is non-negative.)
				// Let's ask the user to widen the precision, then. Example: numeric(4, 0) -> numeric(1, 1).
				code = isc_dyn_precision_too_small;
				diff = new_fld_def.dyn_precision - new_fld_def.dyn_dsc.dsc_scale - diff;
			}
			DYN_error_punt(false, code, SafeArg() << orig_fld_def.dyn_fld_name.c_str() << diff);
			// scale_too_big: New scale specified for column @1 must be at most @2.
			// precision_too_small: New precision specified for column @1 must be at least @2.
		}
		break;

	case isc_dyn_invalid_dtype_conversion:
		{
			TEXT orig_type[25], new_type[25];

			DSC_get_dtype_name(&orig_fld_def.dyn_dsc, orig_type, sizeof(orig_type));
			DSC_get_dtype_name(&new_fld_def.dyn_dsc, new_type, sizeof(new_type));

			DYN_error_punt(false, errorcode,
							SafeArg() << orig_fld_def.dyn_fld_name.c_str() <<
							orig_type << new_type);
			// Cannot change datatype for @1.  Conversion from base type @2 to @3 is not supported.
		}
		break;

	default:
		DYN_error_punt(true, 95);
		/* msg 95: "MODIFY RDB$RELATION_FIELDS failed" */
	}
}
