/*
 *	PROGRAM:	Dynamic SQL runtime support
 *	MODULE:		ddl.c
 *	DESCRIPTION:	Utilities for generating ddl
 *
 * 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): ______________________________________.
 *
 * $Id: ddl.cpp,v 1.50.2.6 2005/06/08 13:50:46 dimitr Exp $
 * 2001.5.20 Claudio Valderrama: Stop null pointer that leads to a crash,
 * caused by incomplete yacc syntax that allows ALTER DOMAIN dom SET;
 *
 * 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
 *                         conditionals, as the engine now fully supports
 *                         readonly databases.
 * December 2001 Mike Nordell - Attempt to make it C++
 *
 * 2001.5.20 Claudio Valderrama: Stop null pointer that leads to a crash,
 * caused by incomplete yacc syntax that allows ALTER DOMAIN dom SET;
 * 2001.5.29 Claudio Valderrama: Check for view v/s relation in DROP
 * command will stop a user that uses DROP VIEW and drops a table by
 * accident and vice-versa.
 * 2001.5.30 Claudio Valderrama: alter column should use 1..N for the
 * position argument since the call comes from SQL DDL.
 * 2001.6.27 Claudio Valderrama: DDL_resolve_intl_type() was adding 2 to the
 * length of varchars instead of just checking that len+2<=MAX_COLUMN_SIZE.
 * It required a minor change to put_field() where it was decremented, too.
 * 2001.6.27 Claudio Valderrama: Finally stop users from invoking the same option
 * several times when altering a domain. Specially dangerous with text data types.
 * Ex: alter domain d type char(5) type varchar(5) default 'x' default 'y';
 * Bear in mind that if DYN functions are addressed directly, this protection
 * becomes a moot point.
 * 2001.6.30 Claudio Valderrama: revert changes from 2001.6.26 because the code
 * is called from several places and there are more functions, even in metd.c,
 * playing the same nonsense game with the field's length, so it needs more
 * careful examination. For now, the new checks in DYN_MOD should catch most anomalies.
 * 2001.7.3 Claudio Valderrama: fix Firebird Bug #223059 with mismatch between number
 * of declared fields for a VIEW and effective fields in the SELECT statement.
 * 2001.07.22 Claudio Valderrama: minor fixes and improvements.
 * 2001.08.18 Claudio Valderrama: RECREATE PROCEDURE.
 * 2001.10.01 Claudio Valderrama: modify_privilege() should recognize that a ROLE can
 *   now be made an explicit grantee.
 * 2001.10.08 Claudio Valderrama: implement frb_sysflag enum values for autogenerated
 *   non-system triggers so DFW can recognize them easily.
 * 2001.10.26 Claudio Valderrama: added a call to the new METD_drop_function()
 *   in DDL_execute() so the metadata cache for udfs can be refreshed.
 * 2001.12.06 Claudio Valderrama: DDL_resolve_intl_type should calculate field length
 * 2002.08.04 Claudio Valderrama: allow declaring and defining variables at the same time
 * 2002.08.04 Dmitry Yemanov: ALTER VIEW
 * 2002.08.31 Dmitry Yemanov: allowed user-defined index names for PK/FK/UK constraints
 * 2002.09.01 Dmitry Yemanov: RECREATE VIEW
 * 2002.09.12 Nickolay Samofatov: fixed cached metadata errors
 */

#include "firebird.h"
#include "../jrd/ib_stdio.h"
#include <string.h>
#include "../dsql/dsql.h"
#include "../dsql/node.h"
#include "../jrd/gds.h"
#include "../jrd/thd.h"
#include "../jrd/intl.h"
#include "../jrd/flags.h"
#include "../jrd/constants.h"
#include "gen/codes.h"
#include "../dsql/alld_proto.h"
#include "../dsql/errd_proto.h"
#include "../dsql/ddl_proto.h"
#include "../dsql/gen_proto.h"
#include "../dsql/make_proto.h"
#include "../dsql/metd_proto.h"
#include "../dsql/pass1_proto.h"
#include "../jrd/sch_proto.h"
#include "../jrd/thd_proto.h"

#if defined(DEBUG) && !(defined REQUESTER && defined SUPERCLIENT)
#include "../gpre/prett_proto.h"
#endif


extern "C" {

}	// extern "C"

#define BLOB_BUFFER_SIZE   4096	/* to read in blr blob for default values */


static void assign_field_length(DSQL_FLD, USHORT);
static USHORT check_array_or_blob(DSQL_NOD);
static void check_constraint(DSQL_REQ, DSQL_NOD, SSHORT);
static void check_one_call(BOOLEAN *, SSHORT, TEXT *);
static void create_view_triggers(DSQL_REQ, DSQL_NOD, DSQL_NOD);
static void define_computed(DSQL_REQ, DSQL_NOD, DSQL_FLD, DSQL_NOD);
static void define_constraint_trigger(DSQL_REQ, DSQL_NOD);
static void define_database(DSQL_REQ);
static void define_del_cascade_trg(DSQL_REQ, DSQL_NOD, DSQL_NOD, DSQL_NOD, const char*, const char*);
//static void define_del_default_trg(DSQL_REQ, DSQL_NOD, DSQL_NOD, DSQL_NOD, TEXT *, TEXT *);
static void define_dimensions(DSQL_REQ, DSQL_FLD);
static void define_domain(DSQL_REQ);
static void define_exception(DSQL_REQ, NOD_TYPE);
static void define_field(DSQL_REQ, DSQL_NOD, SSHORT, STR);
static void define_filter(DSQL_REQ);
static void define_generator(DSQL_REQ);
static void define_role(DSQL_REQ);
static void define_index(DSQL_REQ);
#ifdef NOT_USED_OR_REPLACED
static DSQL_NOD define_insert_action(DSQL_REQ);
#endif
static void define_procedure(DSQL_REQ, NOD_TYPE);
static void define_rel_constraint(DSQL_REQ, DSQL_NOD);
static void define_relation(DSQL_REQ);
static void define_set_null_trg(DSQL_REQ, DSQL_NOD, DSQL_NOD, DSQL_NOD, const char*, const char*, bool);
static void define_set_default_trg(DSQL_REQ, DSQL_NOD, DSQL_NOD, DSQL_NOD, const char*, const char*, bool);
static void define_shadow(DSQL_REQ);
static void define_trigger(DSQL_REQ, DSQL_NOD);
static void define_udf(DSQL_REQ);
static void define_update_action(DSQL_REQ, DSQL_NOD *, DSQL_NOD *);
static void define_upd_cascade_trg(DSQL_REQ, DSQL_NOD, DSQL_NOD, DSQL_NOD, const char*, const char*);
static void define_view(DSQL_REQ, NOD_TYPE);
static void define_view_trigger(DSQL_REQ, DSQL_NOD, DSQL_NOD, DSQL_NOD);
static void delete_procedure(DSQL_REQ, DSQL_NOD, BOOLEAN);
static void delete_relation_view(DSQL_REQ, DSQL_NOD, BOOLEAN);
static void foreign_key(DSQL_REQ, DSQL_NOD, const char* index_name);
static void generate_dyn(DSQL_REQ, DSQL_NOD);
static void grant_revoke(DSQL_REQ);
static void make_index(DSQL_REQ, DSQL_NOD, DSQL_NOD, DSQL_NOD, const char*, const char*);
static void make_index_trg_ref_int(DSQL_REQ, DSQL_NOD, DSQL_NOD, DSQL_NOD, const char*, const char*);
static void modify_database(DSQL_REQ);
static void modify_domain(DSQL_REQ);
static void modify_field(DSQL_REQ, DSQL_NOD, SSHORT, STR);
static void modify_index(DSQL_REQ);
static void modify_privilege(DSQL_REQ, NOD_TYPE, SSHORT, UCHAR *, DSQL_NOD, DSQL_NOD, STR);
static SCHAR modify_privileges(DSQL_REQ, NOD_TYPE, SSHORT, DSQL_NOD, DSQL_NOD, DSQL_NOD);
static void modify_relation(DSQL_REQ);
static void process_role_nm_list(DSQL_REQ, SSHORT, DSQL_NOD, DSQL_NOD, NOD_TYPE);
static void put_descriptor(DSQL_REQ, DSC *);
static void put_dtype(DSQL_REQ, const dsql_fld*, USHORT);
static void put_field(DSQL_REQ, DSQL_FLD, BOOLEAN);
static void put_local_variable(DSQL_REQ, VAR, DSQL_NOD);
static SSHORT put_local_variables(DSQL_REQ, DSQL_NOD, SSHORT);
static void put_msg_field(DSQL_REQ, DSQL_FLD);
static DSQL_NOD replace_field_names(DSQL_NOD, DSQL_NOD, DSQL_NOD, SSHORT);
static void reset_context_stack(DSQL_REQ);
static void save_field(DSQL_REQ, SCHAR *);
static void save_relation(DSQL_REQ, STR);
static void set_statistics(DSQL_REQ);
static void stuff_default_blr(DSQL_REQ, TEXT *, USHORT);
static void stuff_matching_blr(DSQL_REQ, DSQL_NOD, DSQL_NOD);
static void stuff_trg_firing_cond(DSQL_REQ, DSQL_NOD);
static void set_nod_value_attributes(DSQL_NOD, DSQL_FLD);
static void clearPermanentField (DSQL_REL, bool);

#ifdef BLKCHK
#undef BLKCHK
#endif

#ifdef DEV_BUILD
static inline void BLKCHK(const void* p, USHORT type)
{
	if (p && MemoryPool::blk_type(p) != type) {
		ERRD_bugcheck("Invalid block type");
	}
}
#else
#define BLKCHK(blk, typ)
#endif

#define PRE_STORE_TRIGGER	1
#define POST_STORE_TRIGGER	2
#define PRE_MODIFY_TRIGGER	3
#define POST_MODIFY_TRIGGER	4
#define PRE_ERASE_TRIGGER	5
#define POST_ERASE_TRIGGER	6

#define OLD_CONTEXT		"OLD"
#define NEW_CONTEXT		"NEW"
#define TEMP_CONTEXT		"TEMP"

#define DEFAULT_BUFFER		2048

#define DEFAULT_BLOB_SEGMENT_SIZE	80	/* bytes */


static const USHORT blr_dtypes[] = {
	0,
	blr_text,					/* dtype_text */
	blr_cstring,				/* dtype_cstring */
	blr_varying,				/* dtype_varying */
	0,
	0,
	0,							/* dtype_packed */
	0,							/* dtype_byte */
	blr_short,					/* dtype_short */
	blr_long,					/* dtype_long */
	blr_quad,					/* dtype_quad */
	blr_float,					/* dtype_real */
	blr_double,					/* dtype_double */
	blr_double,					/* dtype_d_float */
	blr_sql_date,				/* dtype_sql_date */
	blr_sql_time,				/* dtype_sql_time */
	blr_timestamp,				/* dtype_timestamp */
	blr_blob,					/* dtype_blob */
	blr_short,					/* dtype_array */
	blr_int64					/* dtype_int64 */
};

static const UCHAR nonnull_validation_blr[] = {
	blr_version5,
	blr_not,
	blr_missing,
	blr_fid, 0, 0, 0,
	blr_eoc
};

ASSERT_FILENAME

static inline bool hasOldContext(int value)
{
	int val1 = ((value + 1) >> 1) & 3;
	int val2 = ((value + 1) >> 3) & 3;
	int val3 = ((value + 1) >> 5) & 3;
	return (val1 && val1 != 1) || (val2 && val2 != 1) || (val3 && val3 != 1);
}

static inline bool hasNewContext(int value)
{
	int val1 = ((value + 1) >> 1) & 3;
	int val2 = ((value + 1) >> 3) & 3;
	int val3 = ((value + 1) >> 5) & 3;
	return (val1 && val1 != 3) || (val2 && val2 != 3) || (val3 && val3 != 3);
}


void DDL_execute(DSQL_REQ request)
{
/**************************************
 *
 *	D D L _ e x e c u t e
 *
 **************************************
 *
 * Functional description
 *	Call access method layered service DYN
 *	to interpret dyn string and perform
 *	metadata updates.
 *
 **************************************/

	ISC_STATUS s;

	TSQL tdsql = GET_THREAD_DATA;

#ifdef DEBUG
#if !(defined REQUESTER && defined SUPERCLIENT)
	if (DSQL_debug & 4)
		PRETTY_print_dyn(request->req_blr_string->str_data,
						 NULL, "%4d %s\n", 0);
#endif
#endif

	USHORT length = request->req_blr -
	                reinterpret_cast<BLOB_PTR*>(request->req_blr_string->str_data);

	THREAD_EXIT;

	s = isc_ddl(GDS_VAL(tdsql->tsql_status),
				(GDS_REF(request->req_dbb->dbb_database_handle)),
				(GDS_REF(request->req_trans)),
				length,
				GDS_VAL(request->req_blr_string->str_data));

	THREAD_ENTER;

	// for delete & modify, get rid of the cached relation metadata

	STR string = NULL;
	DSQL_NOD relation_node;
	switch (request->req_ddl_node->nod_type)
	{
		case nod_mod_relation:
		case nod_redef_relation:
			relation_node = request->req_ddl_node->nod_arg[e_alt_name];
			string = (STR) relation_node->nod_arg[e_rln_name];
			break;
		case nod_mod_view:
		case nod_replace_view:
		case nod_redef_view:
		case nod_del_relation:
		case nod_del_view:
			string = (STR) request->req_ddl_node->nod_arg[e_alt_name];
			break;
	}
	if (string) {
		METD_drop_relation(request, string);
	}

	// for delete & modify, get rid of the cached procedure metadata

	if ((request->req_ddl_node->nod_type == nod_mod_procedure) ||
	    (request->req_ddl_node->nod_type == nod_del_procedure) ||
	    (request->req_ddl_node->nod_type == nod_replace_procedure) ||
	    (request->req_ddl_node->nod_type == nod_redef_procedure))
	{

		string = (STR) request->req_ddl_node->nod_arg[e_prc_name];
		METD_drop_procedure(request, string);
	}

/* Signal UDF for obsolescence */

	if (request->req_ddl_node->nod_type == nod_del_udf) {
		string = (STR) request->req_ddl_node->nod_arg [e_udf_name];
		METD_drop_function (request, string);
	}


	if (s) {
		Firebird::status_exception::raise(tdsql->tsql_status[1]);
	}
}


void DDL_generate(DSQL_REQ request, DSQL_NOD node)
{
/**************************************
 *
 *	D D L _ g e n e r a t e
 *
 **************************************
 *
 * Functional description
 *	Generate the DYN string for a
 *	metadata update.  Done during the
 *	prepare phase.
 *
 **************************************/

	if (request->req_dbb->dbb_flags & DBB_read_only) {
		ERRD_post(isc_read_only_database, 0);
		return;
	}
	
	request->append_uchar(gds_dyn_version_1);
	generate_dyn(request, node);
	request->append_uchar(gds_dyn_eoc);
}


//
// Determine whether ids or names should be referenced
// when generating blr for fields and relations.
//
bool DDL_ids(const dsql_req* request)
{
	const dsql_nod* ddl_node = request->req_ddl_node;

	if (!ddl_node) {
		return true;
	}

	switch (ddl_node->nod_type)
	{
		case nod_def_view:
		case nod_redef_view:
		case nod_mod_view:
		case nod_replace_view:
		case nod_def_constraint:
		case nod_def_trigger:
		case nod_mod_trigger:
		case nod_replace_trigger:
		case nod_def_procedure:
		case nod_def_computed:
		case nod_mod_procedure:
		case nod_replace_procedure:
		case nod_redef_procedure:
			return false;

		default:
			return true;
	}
}


//
// Emit blr that describes a descriptor.
// Note that this depends on the same STUFF variant
// as used in gen.c
//
void DDL_put_field_dtype(DSQL_REQ request, const dsql_fld* field, USHORT use_subtype)
{
	put_dtype(request, field, use_subtype);
}


//
// See the next function for description. This is only a
// wrapper that sets the last parameter to FALSE to indicate
// we are creating a field, not modifying one.
//
void DDL_resolve_intl_type(DSQL_REQ request, DSQL_FLD field, STR collation_name)
{
    DDL_resolve_intl_type2 (request, field, collation_name, FALSE);
}



void DDL_resolve_intl_type2(DSQL_REQ request, 
                            DSQL_FLD field, 
                            STR      collation_name, 
                            bool     modifying)
{
/**************************************
 *
 *  D D L _ r e s o l v e _ i n t l _ t y p e 2
 *
 **************************************
 *
 * Function

 *	If the field is defined with a character set or collation,
 *	resolve the names to a subtype now.
 *
 *	Also resolve the field length & whatnot.
 *
 *  If the field is being created, it will pick the db-wide charset
 *  and collation if not specified. If the field is being modified,
 *  since we don't allow changes to those attributes, we'll go and
 *  calculate the correct old lenth from the field itself so DYN
 *  can validate the change properly.
 *
 *	For International text fields, this is a good time to calculate
 *	their actual size - when declared they were declared in
 *	lengths of CHARACTERs, not BYTES.
 *
 **************************************/

	INTLSYM resolved_type;

	if ((field->fld_dtype > dtype_any_text) && field->fld_dtype != dtype_blob)
	{
		if (field->fld_character_set || collation_name ||
			field->fld_flags & FLD_national)
		{
				ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -204,
						  gds_arg_gds, gds_dsql_datatype_err,
						  gds_arg_gds, gds_collation_requires_text, 0);
		}
		return;
	}

	if (field->fld_dtype == dtype_blob)
	{
		if (field->fld_sub_type_name)
		{
			SSHORT blob_sub_type;
			if (!METD_get_type(request,
								reinterpret_cast<STR>(field->fld_sub_type_name),
								"RDB$FIELD_SUB_TYPE",
								&blob_sub_type))
			{
				ERRD_post(	gds_sqlerr,
							gds_arg_number,
							(SLONG) -204,
							gds_arg_gds,
							gds_dsql_datatype_err,
							gds_arg_gds,
							gds_dsql_blob_type_unknown,
							gds_arg_string,
							((STR) field->fld_sub_type_name)->str_data,
							0);
			}
			field->fld_sub_type = blob_sub_type;
		}
		if (field->fld_character_set && (field->fld_sub_type == BLOB_untyped))
		{
			field->fld_sub_type = BLOB_text;
		}
		if (field->fld_character_set && (field->fld_sub_type != BLOB_text))
		{
			ERRD_post(	gds_sqlerr,
						gds_arg_number,
						(SLONG) -204,
						gds_arg_gds,
						gds_dsql_datatype_err,
						gds_arg_gds,
						gds_collation_requires_text,
						0);
		}
		if (collation_name)
		{
			ERRD_post(	gds_sqlerr,
						gds_arg_number,
						(SLONG) -204,
						gds_arg_gds,
						gds_dsql_datatype_err,
						gds_arg_gds,
						gds_collation_requires_text,
						0);
		}
		if (field->fld_sub_type != BLOB_text) {
			return;
		}
	}

	if (field->fld_character_set_id != 0 && !collation_name) {
		// This field has already been resolved once, and the collation
		// hasn't changed.  Therefore, no need to do it again.
		return;
	}


    if (modifying)
	{
        DSQL_REL relation = request->req_relation;
        DSQL_FLD afield = field->fld_next;
        USHORT bpc = 0;
        while (afield) {
            /* The first test is redundant. */
            if (afield != field && afield->fld_relation
                && !strcmp (afield->fld_name, field->fld_name)) {
                assert (afield->fld_relation == relation || !relation);
                break;
            }
            afield = afield->fld_next;
        }
        if (afield) {
            field->fld_character_set_id = afield->fld_character_set_id;
            bpc = METD_get_charset_bpc (request, field->fld_character_set_id);
            field->fld_collation_id = afield->fld_collation_id;
            field->fld_ttype = afield->fld_ttype;

            if (afield->fld_flags & FLD_national) {
                field->fld_flags |= FLD_national;
            }
            else {
                field->fld_flags &= ~FLD_national;
            }
            
            assign_field_length (field, bpc);
            return;
        }
    }


	if (!(field->fld_character_set || field->fld_character_set_id ||	/* set if a domain */
		  (field->fld_flags & FLD_national)))
	{

		// Attach the database default character set, if not otherwise specified

		STR dfl_charset = METD_get_default_charset(request);
		if (dfl_charset)
		{
			field->fld_character_set = (DSQL_NOD) dfl_charset;
		}
		else
		{
			/* If field is not specified with NATIONAL, or CHARACTER SET
			 * treat it as a single-byte-per-character field of character set NONE.
			 */
            assign_field_length (field, 1);
            field->fld_ttype = 0;
			if (!collation_name) {
				return;
			}
		}
	}

	const char* charset_name;

	if (field->fld_flags & FLD_national) {
		charset_name = NATIONAL_CHARACTER_SET;
	} else if (field->fld_character_set) {
		charset_name = ((STR) field->fld_character_set)->str_data;
	} else {
		charset_name = 0;
	}


// Find an intlsym for any specified character set name & collation name

	if (charset_name)
	{
		INTLSYM resolved_charset =
			METD_get_charset(request,
							(USHORT) strlen(charset_name),
							charset_name);

		// Error code -204 (IBM's DB2 manual) is close enough
		if (!resolved_charset)
		{
			// specified character set not found
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -204,
					  gds_arg_gds, gds_dsql_datatype_err,
					  gds_arg_gds, gds_charset_not_found, gds_arg_string,
					  charset_name, 0);
		}
		field->fld_character_set_id = resolved_charset->intlsym_charset_id;
		resolved_type = resolved_charset;
	}

	if (collation_name)
	{
		INTLSYM resolved_collation =
			METD_get_collation(request, collation_name);

		if (!resolved_collation)
		{
			// Specified collation not found
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -204,
					  gds_arg_gds, gds_dsql_datatype_err,
					  gds_arg_gds, gds_collation_not_found, gds_arg_string,
					  collation_name->str_data, 0);
		}

		// If both specified, must be for same character set
		// A "literal constant" must be handled (charset as ttype_dynamic)

		resolved_type = resolved_collation;
		if ((field->fld_character_set_id != resolved_type->intlsym_charset_id)
			&& (field->fld_character_set_id != ttype_dynamic))
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -204, gds_arg_gds,
					  gds_dsql_datatype_err, gds_arg_gds,
					  gds_collation_not_for_charset, gds_arg_string,
					  collation_name->str_data, 0);
		}
	}

    assign_field_length (field, resolved_type->intlsym_bytes_per_char);

	field->fld_ttype            = resolved_type->intlsym_ttype;
	field->fld_character_set_id = resolved_type->intlsym_charset_id;
	field->fld_collation_id     = resolved_type->intlsym_collate_id;
}



static void assign_field_length (
    DSQL_FLD field,
    USHORT  bytes_per_char)
{
/**************************************
 *
 *  a s s i g n _ f i e l d _ l e n g t h
 *
 **************************************
 *
 * Function
 *  We'll see if the field's length fits in the maximum
 *  allowed field, including charset and space for varchars.
 *  Either we raise an error or assign the field's length.
 *  If the charlen comes as zero, we do nothing, although we
 *  know that DYN, MET and DFW will blindly set field length
 *  to zero if they don't catch charlen or another condition.
 *
 **************************************/


	if (field->fld_character_length)
	{
		ULONG field_length = (ULONG) (bytes_per_char * 
            field->fld_character_length);

		if (field->fld_dtype == dtype_varying) {
			field_length += sizeof(USHORT);
		}
		if (field_length > (ULONG) MAX_COLUMN_SIZE)
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -204,
					  gds_arg_gds, gds_dsql_datatype_err,
					  gds_arg_gds, gds_imp_exc,
					  gds_arg_gds, gds_field_name, gds_arg_string,
					  field->fld_name, 0);
		}
		field->fld_length = (USHORT) field_length;
	}

}

//
//	Write out a string of blr as part of a ddl string,
//	as in a view or computed field definition.
//
void dsql_req::begin_blr(UCHAR verb)
{
	if (verb) {
		append_uchar(verb);
	}

	req_base_offset = req_blr - reinterpret_cast<BLOB_PTR*>(req_blr_string->str_data);

	// put in a place marker for the size of the blr, since it is unknown
	append_ushort(0);
	append_uchar((req_flags & REQ_blr_version4) ? blr_version4 : blr_version5);
}


static USHORT check_array_or_blob(DSQL_NOD node)
{
/**************************************
 *
 *	c h e c k _ a r r a y _ o r _ b l o b
 *
 **************************************
 *
 * Functional description
 *	return TRUE if there is an array or blob in expression, else FALSE.
 *	Array and blob expressions have limited usefullness in a computed
 *	expression - so we detect it here to report a syntax error at
 *	definition time, rather than a runtime error at execution.
 *
 **************************************/

	BLKCHK(node, dsql_type_nod);

	switch (node->nod_type) {
	case nod_agg_count:
	case nod_count:
	case nod_gen_id:
	case nod_gen_id2:
	case nod_dbkey:
	case nod_current_date:
	case nod_current_time:
	case nod_current_timestamp:
	case nod_constant:
	case nod_via:
	case nod_internal_info:
		return FALSE;

	case nod_map:
	{
		MAP map = (MAP) node->nod_arg[e_map_map];
		return check_array_or_blob(map->map_node);
	}

	case nod_agg_max:
	case nod_agg_min:
	case nod_agg_average:
	case nod_agg_total:
	case nod_agg_average2:
	case nod_agg_total2:
	case nod_upcase:
	case nod_negate:
		return check_array_or_blob(node->nod_arg[0]);

	case nod_cast:
	{
		DSQL_FLD fld = (DSQL_FLD) node->nod_arg[e_cast_target];
		if ((fld->fld_dtype == dtype_blob) || (fld->fld_dtype == dtype_array)) {
			return TRUE;
		}
		return check_array_or_blob(node->nod_arg[e_cast_source]);
	}

	case nod_add:
	case nod_subtract:
	case nod_concatenate:
	case nod_multiply:
	case nod_divide:
	case nod_add2:
	case nod_subtract2:
	case nod_multiply2:
	case nod_divide2:

		if (check_array_or_blob(node->nod_arg[0])) {
			return TRUE;
		}
		return check_array_or_blob(node->nod_arg[1]);

	case nod_alias:
		return check_array_or_blob(node->nod_arg[e_alias_value]);

	case nod_udf:
	{
		UDF udf = (UDF) node->nod_arg[0];
		if ((udf->udf_dtype == dtype_blob) || (udf->udf_dtype == dtype_array)) {
			return TRUE;
		}
		// parameters to UDF don't need checking,
		// an blob or array can be passed
		return FALSE;
	}

	case nod_extract:
	case nod_list:
	{
		const DSQL_NOD* end = node->nod_arg + node->nod_count;
		for (DSQL_NOD* ptr = node->nod_arg; ptr < end; ++ptr)
		{
			if (check_array_or_blob(*ptr)) {
				return TRUE;
			}
		 }
	}

		return FALSE;

	case nod_field:
		if ((node->nod_desc.dsc_dtype == dtype_blob) ||
			(node->nod_desc.dsc_dtype == dtype_array))
		{
			return TRUE;
		}
		return FALSE;

	default:
		assert(FALSE);
		return FALSE;
	}
}


static void check_constraint(	DSQL_REQ		request,
								DSQL_NOD		element,
								SSHORT	delete_trigger_required)
{
/* *************************************
 *
 *	c h e c k _ c o n s t r a i n t
 *
 **************************************
 *
 * Function
 *	Generate triggers to implement the CHECK
 *	clause, either at the field or table level.
 *
 **************************************/

	DSQL_NOD ddl_node = request->req_ddl_node;

	if (!(element->nod_arg[e_cnstr_table])) {
		element->nod_arg[e_cnstr_table] = ddl_node->nod_arg[e_drl_name];
	}

	// specify that the trigger should abort if the condition is not met

	DSQL_NOD list_node = MAKE_node(nod_list, (int) 1);
	element->nod_arg[e_cnstr_actions] = list_node;
	list_node->nod_arg[0] = MAKE_node(nod_gdscode, (int) 1);

	DSQL_NOD* errorcode_node = &list_node->nod_arg[0]->nod_arg[0];
	*errorcode_node = (DSQL_NOD) MAKE_cstring("check_constraint");
	element->nod_arg[e_cnstr_message] = NULL;

	// create the INSERT trigger

// element->nod_arg [e_cnstr_message] =
//   (DSQL_NOD) MAKE_cstring ("insert violates CHECK constraint on table");

	element->nod_arg[e_cnstr_type] =
		MAKE_constant((STR) PRE_STORE_TRIGGER, 1);
	define_constraint_trigger(request, element);

	// create the UPDATE trigger

// element->nod_arg [e_cnstr_message] =
//  (DSQL_NOD) MAKE_cstring ("update violates CHECK constraint on table");

	element->nod_arg[e_cnstr_type] =
		MAKE_constant((STR) PRE_MODIFY_TRIGGER, 1);
	define_constraint_trigger(request, element);

	// create the DELETE trigger, if required
	if (delete_trigger_required)
	{
//
//		element->nod_arg [e_cnstr_message] =
//			(DSQL_NOD) MAKE_cstring ("delete violates CHECK constraint on table");
//
		element->nod_arg[e_cnstr_type] =
			MAKE_constant((STR) PRE_ERASE_TRIGGER, 1);
		define_constraint_trigger(request, element);
	}

	request->append_uchar(gds_dyn_end);	// For CHECK constraint definition
}


static void check_one_call (BOOLEAN *repetition_count,
                            SSHORT pos,
                            TEXT *error_msg)
{
/**************************************
 *
 *  c h e c k _ o n e _ c a l l
 *
 **************************************
 *
 * Function
 *  Ensure that each option in modify_domain() is called only once.
 *  This restriction cannot be enforced by the DSQL parser.
 *
 **************************************/
    if (++repetition_count [pos] > 1) {
        ERRD_post (gds_sqlerr, gds_arg_number, (SLONG) -637,
                   gds_arg_gds, gds_dsql_duplicate_spec,
                   gds_arg_string, error_msg,
                   0);
    }
}


static void create_view_triggers(DSQL_REQ request, DSQL_NOD element, DSQL_NOD items)
{								/* Fields in the VIEW actually  */
/* *************************************
 *
 *	c r e a t e _ v i e w _ t r i g g e r s
 *
 **************************************
 *
 * Function
 *	Generate triggers to implement the WITH CHECK OPTION
 *	clause for a VIEW
 *  
 **************************************/

	DSQL_NOD temp, base_relation, base_and_node;

	DSQL_NOD ddl_node = request->req_ddl_node;

	if (!(element->nod_arg[e_cnstr_table])) {
		element->nod_arg[e_cnstr_table] = ddl_node->nod_arg[e_drl_name];
	}

	// specify that the trigger should abort if the condition is not met

	DSQL_NOD list_node = MAKE_node(nod_list, (int) 1);
	element->nod_arg[e_cnstr_actions] = list_node;
	list_node->nod_arg[0] = MAKE_node(nod_gdscode, (int) 1);

	DSQL_NOD* errorcode_node = &list_node->nod_arg[0]->nod_arg[0];
	*errorcode_node = (DSQL_NOD) MAKE_cstring("check_constraint");
	element->nod_arg[e_cnstr_message] = NULL;

	// create the UPDATE trigger

// element->nod_arg [e_cnstr_message] =
//   (DSQL_NOD) MAKE_cstring ("update violates CHECK constraint on view");

	element->nod_arg[e_cnstr_type] =
		MAKE_constant((STR) PRE_MODIFY_TRIGGER, 1);
	define_update_action(request, &base_and_node, &base_relation);

	DSQL_NOD rse = MAKE_node(nod_rse, e_rse_count);
	rse->nod_arg[e_rse_boolean] = base_and_node;
	rse->nod_arg[e_rse_streams] = temp = MAKE_node(nod_list, 1);
	temp->nod_arg[0] = base_relation;
	define_view_trigger(request, element, rse, items);

	// create the INSERT trigger

// element->nod_arg [e_cnstr_message] =
//   (DSQL_NOD) MAKE_cstring ("insert violates CHECK constraint on view");

	element->nod_arg[e_cnstr_type] =
		MAKE_constant((STR) PRE_STORE_TRIGGER, 1);
	define_view_trigger(request, element, NULL, items);

	request->append_uchar(gds_dyn_end);	// For triggers definition
}


static void define_computed(DSQL_REQ request,
							DSQL_NOD relation_node,
							DSQL_FLD field,
							DSQL_NOD node)
{
/**************************************
 *
 *	d e f i n e _ c o m p u t e d
 *
 **************************************
 *
 * Function
 *	Create the ddl to define a computed field
 *	or an expression index.
 *
 **************************************/

	DSQL_NOD ddl_node = request->req_ddl_node;
	request->req_ddl_node = node;

	// Get the table node & set up correct context

	if (request->req_context_number) {
		reset_context_stack(request);
	}

	DSC save_desc;

	// Save the size of the field if it is specified
	save_desc.dsc_dtype = 0;

	if (field && field->fld_dtype)
	{
		assert(field->fld_dtype <= MAX_UCHAR);
		save_desc.dsc_dtype = (UCHAR) field->fld_dtype;
		save_desc.dsc_length = field->fld_length;
		assert(field->fld_scale <= MAX_SCHAR);
		save_desc.dsc_scale = (SCHAR) field->fld_scale;

		field->fld_dtype = 0;
		field->fld_length = 0;
		field->fld_scale = 0;
	}

	PASS1_make_context(request, relation_node);

	DSQL_NOD input = PASS1_node(request, node->nod_arg[e_cmp_expr], 0);

	// check if array or blobs are used in expression

	if (check_array_or_blob(input))
	{
		ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
				  gds_arg_gds, gds_dsql_no_blob_array, 0);
	}


	// generate the blr expression

	request->append_uchar(gds_dyn_fld_computed_blr);
	request->begin_blr(0);
	GEN_expr(request, input);
	request->end_blr();

	// try to calculate size of the computed field. The calculated size
	// may be ignored, but it will catch self references
	DSC desc;
	MAKE_desc(&desc, input);

	if (save_desc.dsc_dtype) {
		// restore the field size/type overrides
		field->fld_dtype  = save_desc.dsc_dtype;
		field->fld_length = save_desc.dsc_length;
		field->fld_scale  = save_desc.dsc_scale;
	}
	else if (field) {
		// use size calculated
		field->fld_dtype  = desc.dsc_dtype;
		field->fld_length = desc.dsc_length;
		field->fld_scale  = desc.dsc_scale;
	}

	request->req_type = REQ_DDL;
	request->req_ddl_node = ddl_node;
	reset_context_stack(request);

	// generate the source text
	STR source = (STR) node->nod_arg[e_cmp_text];
	assert(source->str_length <= MAX_USHORT);
	request->append_string(	gds_dyn_fld_computed_source,
							source->str_data,
							(USHORT) source->str_length);
}


static void define_constraint_trigger(DSQL_REQ request, DSQL_NOD node)
{
/**************************************
 *
 *	d e f i n e _ c o n s t r a i n t _ t r i g g e r
 *
 **************************************
 *
 * Function
 *	Create the ddl to define or alter a constraint trigger.
 * This is a SQL's check constraint.  
 *
 **************************************/

/* make the "define trigger" node the current request ddl node so
   that generating of BLR will be appropriate for trigger */

	DSQL_NOD ddl_node = request->req_ddl_node;

	request->req_ddl_node = node;

	if (node->nod_type != nod_def_constraint)
	{
		return;
	}

	STR trigger_name = (STR) node->nod_arg[e_cnstr_name];

	assert(trigger_name->str_length <= MAX_USHORT);

	request->append_string(	gds_dyn_def_trigger,
							trigger_name->str_data,
							(USHORT) trigger_name->str_length);

	DSQL_NOD relation_node = node->nod_arg[e_cnstr_table];
	STR relation_name = (STR) relation_node->nod_arg[e_rln_name];

	assert(trigger_name->str_length <= MAX_USHORT);

	request->append_string(	gds_dyn_rel_name,
							relation_name->str_data,
							(USHORT) relation_name->str_length);

	STR source = (STR) node->nod_arg[e_cnstr_source];
	if (source)
	{
		assert(source->str_length <= MAX_USHORT);
		request->append_string(	gds_dyn_trg_source,
								source->str_data,
								(USHORT) source->str_length);
	}

	DSQL_NOD constant = node->nod_arg[e_cnstr_position];
	if (constant)
	{
		request->append_number(gds_dyn_trg_sequence,
				   (SSHORT)(SLONG) (constant ? constant->nod_arg[0] : 0));
	}

	if ((constant = node->nod_arg[e_cnstr_type]) != NULL)
	{
		const SSHORT type = (SSHORT)(SLONG) constant->nod_arg[0];
		request->append_number(gds_dyn_trg_type, type);
	}

	request->append_uchar(gds_dyn_sql_object);

	STR message = (STR) node->nod_arg[e_cnstr_message];
	if (message)
	{
		request->append_number(gds_dyn_def_trigger_msg, 0);
		assert(message->str_length <= MAX_USHORT);
		request->append_string(	gds_dyn_trg_msg,
								message->str_data,
								(USHORT) message->str_length);
		request->append_uchar(gds_dyn_end);
	}

	// generate the trigger blr

	if (node->nod_arg[e_cnstr_condition] && node->nod_arg[e_cnstr_actions])
	{
		request->begin_blr(gds_dyn_trg_blr);
		request->append_uchar(blr_begin);

		/* create the "OLD" and "NEW" contexts for the trigger --
		   the new one could be a dummy place holder to avoid resolving
		   fields to that context but prevent relations referenced in
		   the trigger actions from referencing the predefined "1" context */

		if (request->req_context_number)
		{
			reset_context_stack(request);
		}

        // CVC: check_constraint() is the only caller and it always receives 
        // FALSE for the delete_trigger_required flag. Hence, I thought I could
        // disable the OLD context here to avoid "ambiguous field name" errors
        // in pre_store and pre_modify triggers. Also, what sense can I make 
        // from NEW in pre_delete? However, we clash at JRD with "no current 
        // record for fetch operation".

		relation_node->nod_arg[e_rln_alias] = (DSQL_NOD) MAKE_cstring(OLD_CONTEXT);
		PASS1_make_context(request, relation_node);
		relation_node->nod_arg[e_rln_alias] = (DSQL_NOD) MAKE_cstring(NEW_CONTEXT);
		PASS1_make_context(request, relation_node);

		// generate the condition for firing the trigger

		request->append_uchar(blr_if);
		GEN_expr(request,
				 PASS1_node(request, node->nod_arg[e_cnstr_condition], 0));

		request->append_uchar(blr_begin);
		request->append_uchar(blr_end);		// of begin

		// generate the action statements for the trigger
		DSQL_NOD  actions = node->nod_arg[e_cnstr_actions];
		DSQL_NOD* ptr;
		DSQL_NOD* end;
		for (ptr = actions->nod_arg, end = ptr + actions->nod_count;
			 ptr < end; ptr++)
		{
			GEN_statement(request, PASS1_statement(request, *ptr, 0));
		}

		/* generate the action statements for the trigger */

		if ((actions = node->nod_arg[e_cnstr_else]) != NULL)
		{
			request->append_uchar(blr_begin);
			for (ptr = actions->nod_arg, end = ptr + actions->nod_count;
				 ptr < end; ptr++)
			{
				GEN_statement(request, PASS1_statement(request, *ptr, 0));
			}
			request->append_uchar(blr_end);	// of begin
		}
		else
		{
			request->append_uchar(blr_end);	// of if
		}

		request->end_blr();
	}

    request->append_number(isc_dyn_system_flag, frb_sysflag_check_constraint);

	request->append_uchar(gds_dyn_end);

/* the request type may have been set incorrectly when parsing
   the trigger actions, so reset it to reflect the fact that this
   is a data definition request; also reset the ddl node */

	request->req_type = REQ_DDL;
	request->req_ddl_node = ddl_node;
	reset_context_stack(request);
}


static void define_database( DSQL_REQ request)
{
/**************************************
 *
 *	d e f i n e _ d a t a b a s e
 *
 **************************************
 *
 * Function
 *	Create a database. Assumes that
 *	database is created elsewhere with
 *	initial options. Modify the
 *	database using DYN to add the remaining
 *	options.
 *
 **************************************/
	DSQL_NOD ddl_node, elements, element, *ptr, *end;
	STR name;
	SLONG start = 0;
	FIL file;
	SSHORT number = 0;
	SLONG temp_long;
	SSHORT temp_short;

	ddl_node = request->req_ddl_node;

	request->append_uchar(gds_dyn_mod_database);
/*
request->append_number(gds_dyn_rel_sql_protection, 1);
*/

	elements = ddl_node->nod_arg[e_database_initial_desc];

	if (elements)
		for (ptr = elements->nod_arg, end = ptr + elements->nod_count;
			 ptr < end; ptr++) {
			element = *ptr;

			switch (element->nod_type) {
			case nod_file_length:
				start = (SLONG) (element->nod_arg[0]) + 1;
				break;

			default:
				break;
			}
		}

	elements = ddl_node->nod_arg[e_database_rem_desc];
	if (elements)
	{
		for (ptr = elements->nod_arg, end = ptr + elements->nod_count;
			 ptr < end; ptr++)
		{
			element = *ptr;

			switch (element->nod_type) {
			case nod_file_desc:
				file = (FIL) element->nod_arg[0];
				request->append_cstring(gds_dyn_def_file,
							file->fil_name->str_data);

				start = MAX(start, file->fil_start);
				request->append_file_start(start);
				request->append_file_length(file->fil_length);
				request->append_uchar(gds_dyn_end);
				start += file->fil_length;
				break;

			case nod_log_file_desc:
				file = (FIL) element->nod_arg[0];

				if (file->fil_flags & LOG_default) {
					request->append_uchar(gds_dyn_def_default_log);
					break;
				}
				request->append_cstring(gds_dyn_def_log_file,
							file->fil_name->str_data);
				request->append_file_length(file->fil_length);
				request->append_uchar(gds_dyn_log_file_sequence);
				request->append_ushort_with_length(number);
				++number;
				request->append_uchar(gds_dyn_log_file_partitions);
				request->append_ushort_with_length(file->fil_partitions);
				if (file->fil_flags & LOG_serial) {
					request->append_uchar(gds_dyn_log_file_serial);
				}
				if (file->fil_flags & LOG_overflow) {
					request->append_uchar(gds_dyn_log_file_overflow);
				}
				if (file->fil_flags & LOG_raw) {
					request->append_uchar(gds_dyn_log_file_raw);
				}
				request->append_uchar(gds_dyn_end);
				break;

			case nod_cache_file_desc:
				file = (FIL) element->nod_arg[0];
				request->append_cstring(gds_dyn_def_cache_file,
							file->fil_name->str_data);
				request->append_file_length(file->fil_length);
				request->append_uchar(gds_dyn_end);
				break;

			case nod_group_commit_wait:
				request->append_uchar(gds_dyn_log_group_commit_wait);
				temp_long = (SLONG) (element->nod_arg[0]);
				request->append_ulong_with_length(temp_long);
				break;

			case nod_check_point_len:
				request->append_uchar(gds_dyn_log_check_point_length);
				temp_long = (SLONG) (element->nod_arg[0]);
				request->append_ulong_with_length(temp_long);
				break;

			case nod_num_log_buffers:
				request->append_uchar(gds_dyn_log_num_of_buffers);
				temp_short = (SSHORT)(SLONG) (element->nod_arg[0]);
				request->append_ushort_with_length(temp_short);
				break;

			case nod_log_buffer_size:
				request->append_uchar(gds_dyn_log_buffer_size);
				temp_short = (SSHORT)(SLONG) (element->nod_arg[0]);
				request->append_ushort_with_length(temp_short);
				break;

			case nod_dfl_charset:
				name = (STR) element->nod_arg[0];
				request->append_cstring(gds_dyn_fld_character_set_name,
							name->str_data);
				break;

			default:
				break;
			}
		}
	}

	request->append_uchar(gds_dyn_end);
}


static void define_del_cascade_trg(	DSQL_REQ    request,
									DSQL_NOD    element,
									DSQL_NOD    for_columns,
									DSQL_NOD    prim_columns,
									const char* prim_rel_name,
									const char* for_rel_name)
{
/*****************************************************
 *
 *	d e f i n e _ d e l _ c a s c a d e _ t r g
 *
 *****************************************************
 *
 * Function
 *	define "on delete cascade" trigger (for referential integrity)
 *      along with its blr
 *
 *****************************************************/

	if (element->nod_type != nod_foreign) {
		return;
	}

	// stuff a trigger_name of size 0. So the dyn-parser will make one up.
	request->append_string(	gds_dyn_def_trigger, "", 0);

	request->append_number(gds_dyn_trg_type, (SSHORT) POST_ERASE_TRIGGER);

	request->append_uchar(gds_dyn_sql_object);
	request->append_number(gds_dyn_trg_sequence, (SSHORT) 1);
	request->append_number(gds_dyn_trg_inactive, (SSHORT) 0);
	request->append_cstring(gds_dyn_rel_name, prim_rel_name);

	// the trigger blr
	request->begin_blr(gds_dyn_trg_blr);
	request->append_uchar(blr_for);
	request->append_uchar(blr_rse);

	// the context for the prim. key relation
	request->append_uchar(1);

	request->append_uchar(blr_relation);
	request->append_cstring(0, for_rel_name);
	// the context for the foreign key relation
	request->append_uchar(2);

	stuff_matching_blr(request, for_columns, prim_columns);

	request->append_uchar(blr_erase);
	request->append_uchar(2);
	request->end_blr();
	// end of the blr

    request->append_number(isc_dyn_system_flag, 
                           frb_sysflag_referential_constraint);

	// no trg_source and no trg_description
	request->append_uchar(gds_dyn_end);

}


static void define_set_default_trg(	DSQL_REQ    request,
									DSQL_NOD    element,
									DSQL_NOD    for_columns,
									DSQL_NOD    prim_columns,
									const char*	prim_rel_name,
									const char*	for_rel_name,
									bool        on_upd_trg)
{
/*****************************************************
 *
 *	d e f i n e _ s e t _ d e f a u l t _ t r g
 *
 *****************************************************
 *
 * Function
 *	define "on delete|update set default" trigger (for
 *      referential integrity) along with its blr
 *
 *****************************************************/

	char default_val[BLOB_BUFFER_SIZE];

	if (element->nod_type != nod_foreign) {
		return;
	}

	request->generate_unnamed_trigger_beginning(on_upd_trg,
												prim_rel_name,
												prim_columns,
												for_rel_name,
												for_columns);

	USHORT num_fields = 0;
	DSQL_NOD*   for_key_flds = for_columns->nod_arg;
	DSQL_NOD    ddl_node     = request->req_ddl_node;

	do {
		/* for every column in the foreign key .... */
		STR for_key_fld_name_str = (STR) (*for_key_flds)->nod_arg[1];

		request->append_uchar(blr_assignment);

		/* here stuff the default value as blr_literal .... or blr_null
		   if this col. does not have an applicable default */

		/* the default is determined in many cases:
		   (1) the info. for the column is in memory. (This is because
		   the column is being created in this ddl statement)
		   (1-a) the table has a column level default. We get this by
		   searching the dsql parse tree starting from the ddl node.
		   (1-b) the table does not have a column level default, but
		   has a domain default. We get the domain name from the dsql
		   parse tree and call METD_get_domain_default to read the
		   default from the system tables.
		   (2) The default-info for this column is not in memory (This is
		   because this is an alter table ddl statement). The table
		   already exists; therefore we get the column and/or domain
		   default value from the system tables by calling:
		   METD_get_col_default().  */

		BOOLEAN found_default = FALSE;
		bool search_for_default = true;

		/* search the parse tree to find the column */

		DSQL_NOD elem = ddl_node->nod_arg[e_drl_elements];
		DSQL_NOD* end = elem->nod_arg + elem->nod_count;
		for (DSQL_NOD* ptr = elem->nod_arg; ptr < end; ++ptr)
		{
			elem = *ptr;
			if (elem->nod_type != nod_def_field) {
				continue;
			}

			DSQL_FLD field = (DSQL_FLD) elem->nod_arg[e_dfl_field];
			if (strcmp(field->fld_name, for_key_fld_name_str->str_data))
			{
				continue;
			}

			/* Now, we have the right column in the parse tree. case (1) above */

			DSQL_NOD default_node = elem->nod_arg[e_dfl_default];
			if (default_node)
			{
				/* case (1-a) above: there is a col. level default */
				GEN_expr(request, default_node);
				found_default = TRUE;
				search_for_default = false;
			}
			else
			{
				TEXT* domain_name;
				STR   domain_name_str;
				DSQL_NOD   domain_node;
				DSQL_NOD   tmp_node;

				if (!(domain_node = elem->nod_arg[e_dfl_domain]) ||
					!(tmp_node = domain_node->nod_arg[e_dom_name]) ||
					!(domain_name_str = (STR) tmp_node->nod_arg[e_fln_name])
					|| !(domain_name = domain_name_str->str_data))
				{
					break;
				}

				/* case: (1-b): domain name is available. Column level default
				   is not declared. so get the domain default */
				METD_get_domain_default(request, domain_name, &found_default,
										default_val,
										sizeof(default_val));

				search_for_default = false;
				if (found_default)
				{
					stuff_default_blr(request, default_val, sizeof(default_val));
				}
				else
				{
					// neither col level nor domain level default exists
					request->append_uchar(blr_null);
				}
			}
			break;
		}

		if (search_for_default)
		{
			/* case 2: see if the column/domain has already been created */

			METD_get_col_default(request, for_rel_name,
								 for_key_fld_name_str->str_data,
								 &found_default,
								 default_val,
								 sizeof(default_val));

			if (found_default) {
				stuff_default_blr(request, default_val, sizeof(default_val));
			} else {
				request->append_uchar(blr_null);
			}

		}

		// the context for the foreign key relation
		request->append_uchar(blr_field);
		request->append_uchar(2);
		request->append_cstring(0, for_key_fld_name_str->str_data);

		num_fields++;
		for_key_flds++;

	} while (num_fields < for_columns->nod_count);

	request->append_uchar(blr_end);

	if (on_upd_trg) {
		request->append_uchars(blr_end, 3);
	}

	request->end_blr();

    request->append_number(isc_dyn_system_flag, 
                            frb_sysflag_referential_constraint);

	// no trg_source and no trg_description
	request->append_uchar(gds_dyn_end);
}


static void define_dimensions( DSQL_REQ request, DSQL_FLD field)
{
/*****************************************
 *
 *	d e f i n e _ d i m e n s i o n s
 *
 *****************************************
 *
 * Function
 *	Define dimensions of an array
 *
 **************************************/

	DSQL_NOD elements = field->fld_ranges;
	USHORT dims = elements->nod_count / 2;

	if (dims > MAX_ARRAY_DIMENSIONS)
	{
		ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -604,
				  gds_arg_gds, gds_dsql_max_arr_dim_exceeded, 0);
	}

	request->append_number(gds_dyn_fld_dimensions, (SSHORT) dims);

	SSHORT position = 0;
	DSQL_NOD* ptr = elements->nod_arg;
	DSQL_NOD* end = ptr + elements->nod_count;
	for (; ptr < end; ++ptr, ++position)
	{
		request->append_number(gds_dyn_def_dimension, position);
		DSQL_NOD element = *ptr++;
		request->append_uchar(gds_dyn_dim_lower);
		SLONG lrange = (SLONG) (element->nod_arg[0]);
		request->append_ulong_with_length(lrange);
		element = *ptr;
		request->append_uchar(gds_dyn_dim_upper);
		SLONG hrange = (SLONG) (element->nod_arg[0]);
		request->append_ulong_with_length(hrange);
		request->append_uchar(gds_dyn_end);
		if (lrange >= hrange)
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -604,
					  gds_arg_gds, gds_dsql_arr_range_error, 0);
		}
	}
}


static void define_domain(DSQL_REQ request)
{
/**************************************
 *
 *	d e f i n e _ d o m a i n
 *
 **************************************
 *
 * Function
 *	Define a domain (global field)
 *
 **************************************/

	bool	null_flag = false;
	bool	check_flag = false;

	DSQL_NOD element = request->req_ddl_node;
	DSQL_FLD field = (DSQL_FLD) element->nod_arg[e_dom_name];

	request->append_cstring(gds_dyn_def_global_fld, field->fld_name);

	DDL_resolve_intl_type(request, field,
						  (STR) element->nod_arg[e_dom_collate]);
	put_field(request, field, FALSE);

	// check for a default value

	DSQL_NOD node = element->nod_arg[e_dom_default];
	if (node)
	{
		node = PASS1_node(request, node, 0);
		request->begin_blr(gds_dyn_fld_default_value);
		GEN_expr(request, node);
		request->end_blr();

		STR string = (STR) element->nod_arg[e_dom_default_source];
		if (string)
		{
			assert(string->str_length <= MAX_USHORT);
			request->append_string(	gds_dyn_fld_default_source,
									string->str_data,
									string->str_length);
		}
	}

	if (field->fld_ranges)
	{
		define_dimensions(request, field);
	}

	// check for constraints
	node = element->nod_arg[e_dom_constraint];
	if (node)
	{
		DSQL_NOD* ptr     = node->nod_arg;
		DSQL_NOD* end_ptr = ptr + node->nod_count;
		for (; ptr < end_ptr; ++ptr)
		{
			if ((*ptr)->nod_type == nod_rel_constraint)
			{
				DSQL_NOD node1 = (*ptr)->nod_arg[e_rct_type];
				if (node1->nod_type == nod_null)
				{
					if (!null_flag)
					{
						request->append_uchar(gds_dyn_fld_not_null);
						null_flag = true;
					}
					else
					{
						ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -637,
								  gds_arg_gds, gds_dsql_duplicate_spec,
								  gds_arg_string, "NOT NULL", 0);
					}
				}
				else if (node1->nod_type == nod_def_constraint)
				{
					if (check_flag)
					{
						ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -637,
								  gds_arg_gds, gds_dsql_duplicate_spec,
								  gds_arg_string, "DOMAIN CHECK CONSTRAINT",
								  0);
					}
					check_flag = true;

					STR string = (STR) node1->nod_arg[e_cnstr_source];
					if (string)
					{
						assert(string->str_length <= MAX_USHORT);
						request->append_string(	gds_dyn_fld_validation_source,
												string->str_data,
												string->str_length);
					}
					request->begin_blr(gds_dyn_fld_validation_blr);

					/* Set any VALUE nodes to the type of the domain being defined. */
					if (node1->nod_arg[e_cnstr_condition])
					{
						set_nod_value_attributes(node1->nod_arg[e_cnstr_condition],
												 field);
					}

					/* Increment the context level for this request, so
					   that the context number for any RSE generated for a
					   SELECT within the CHECK clause will be greater than
					   0.  In the environment of a domain check
					   constraint, context number 0 is reserved for the
					   "blr_fid, 0, 0,0," which is emitted for a
					   nod_dom_value, corresponding to an occurance of the
					   VALUE keyword in the bod of the check constraint.
					   -- chrisj 1999-08-20 */

					request->req_context_number++;

					GEN_expr(request,
							 PASS1_node(request,
										node1->nod_arg[e_cnstr_condition],
										0));

					request->end_blr();
				}
			}
		}
	}

	request->append_uchar(gds_dyn_end);
}


static void define_exception( DSQL_REQ request, NOD_TYPE op)
{
/**************************************
 *
 *	d e f i n e _ e x c e p t i o n
 *
 **************************************
 *
 * Function
 *	Generate ddl to create an exception code.
 *
 **************************************/
	DSQL_NOD ddl_node;
	STR text, name;

	ddl_node = request->req_ddl_node;
	name = (STR) ddl_node->nod_arg[e_xcp_name];
	text = (STR) ddl_node->nod_arg[e_xcp_text];

	if (op == nod_def_exception)
		request->append_cstring(gds_dyn_def_exception, name->str_data);
	else if (op == nod_mod_exception)
		request->append_cstring(gds_dyn_mod_exception, name->str_data);
	else
		request->append_cstring(gds_dyn_del_exception, name->str_data);

	if (op != nod_del_exception) {
		assert(text->str_length <= MAX_USHORT);
		request->append_string(gds_dyn_xcp_msg, text->str_data, text->str_length);
		request->append_uchar(gds_dyn_end);
	}
}


static void define_field(
						 DSQL_REQ request,
						 DSQL_NOD element, SSHORT position, STR relation_name)
{
/**************************************
 *
 *	d e f i n e _ f i e l d
 *
 **************************************
 *
 * Function
 *	Define a field, either as part of a create
 *	table or an alter table statement.
 *
 **************************************/
	DSQL_NOD domain_node, node, node1, *ptr;
	DSQL_FLD field;
	DSQL_REL relation;
	STR string, domain_name;
	USHORT cnstrt_flag = FALSE;
	DSQL_NOD computed_node;
	bool default_null_flag = false;

	field = (DSQL_FLD) element->nod_arg[e_dfl_field];

/* add the field to the relation being defined for parsing purposes */

	bool permanent = false;
	if ((relation = request->req_relation) != NULL) {
		if (! (relation->rel_flags & REL_new_relation)) {
			DSQL_FLD perm_field = FB_NEW_RPT(*request->req_dbb->dbb_pool,
								strlen(field->fld_name)) dsql_fld;
			*perm_field = *field;
			strcpy(perm_field->fld_name, field->fld_name);
			field = perm_field;
			permanent = true;
		}
		field->fld_next = relation->rel_fields;
		relation->rel_fields = field;
	}

	try {

	if (domain_node = element->nod_arg[e_dfl_domain]) {
		request->append_cstring(gds_dyn_def_local_fld, field->fld_name);
		node1 = domain_node->nod_arg[e_dom_name];
		domain_name = (STR) node1->nod_arg[e_fln_name];
		request->append_cstring(gds_dyn_fld_source, domain_name->str_data);

		/* Get the domain information */

		if (!(METD_get_domain(request, field, domain_name->str_data)))
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
					  gds_arg_gds, gds_dsql_command_err,
					  gds_arg_gds, gds_dsql_domain_not_found,
                      gds_arg_string, domain_name->str_data,
					  /* Specified domain or source field does not exist */
					  0);

		DDL_resolve_intl_type(	request,
								field,
								reinterpret_cast<STR>(element->nod_arg[e_dfl_collate]));
		if (element->nod_arg[e_dfl_collate]) {
			request->append_number(gds_dyn_fld_collation,
					   field->fld_collation_id);
		}
	}
	else
	{
		request->append_cstring(gds_dyn_def_sql_fld, field->fld_name);
		if (relation_name) {
			request->append_cstring(gds_dyn_rel_name, relation_name->str_data);
		}

		if (element->nod_arg[e_dfl_computed])
		{
			field->fld_flags |= FLD_computed;
			computed_node = element->nod_arg[e_dfl_computed];
			define_computed(request,
							request->req_ddl_node->nod_arg[e_drl_name], field,
							computed_node);
		}

		DDL_resolve_intl_type(request, field,
							  reinterpret_cast<STR>(element->nod_arg[e_dfl_collate]));
		put_field(request, field, FALSE);
	}

	if (position != -1)
		request->append_number(gds_dyn_fld_position, position);

	// check for a default value

	node = element->nod_arg[e_dfl_default];
	if (node)
	{
		node = PASS1_node(request, node, 0);
		request->begin_blr(gds_dyn_fld_default_value);
		if (node->nod_type == nod_null) {
			default_null_flag = true;
		}
		GEN_expr(request, node);
		request->end_blr();
		string = (STR) element->nod_arg[e_dfl_default_source];
		if (string)
		{
			assert(string->str_length <= MAX_USHORT);
			request->append_string(	gds_dyn_fld_default_source,
									string->str_data,
									string->str_length);
		}
	}

	if (field->fld_ranges) {
		define_dimensions(request, field);
	}

/* check for constraints */

	if (node = element->nod_arg[e_dfl_constraint])
	{
		DSQL_NOD* end_ptr = node->nod_arg + node->nod_count;
		for (ptr = node->nod_arg; ptr < end_ptr; ++ptr)
		{
			if ((*ptr)->nod_type == nod_rel_constraint)
			{
				string = (STR) (*ptr)->nod_arg[e_rct_name];
				node1 = (*ptr)->nod_arg[e_rct_type];

				if (node1->nod_type == nod_null)
				{
					if (default_null_flag)
					{
						ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -204,
								  gds_arg_gds, isc_bad_default_value,
								  gds_arg_gds, isc_invalid_clause,
								  gds_arg_string, "default null not null", 0);
					}
					request->append_uchar(gds_dyn_fld_not_null);
					if (cnstrt_flag == FALSE) {
						request->append_uchar(gds_dyn_end);	/* For field definition  */
						cnstrt_flag = TRUE;
					}
					request->append_cstring(gds_dyn_rel_constraint,
									string ? string->str_data : 0);
					request->append_uchar(gds_dyn_fld_not_null);
					request->append_uchar(gds_dyn_end);	/* For NOT NULL Constraint definition  */
				}
				else if (node1->nod_type == nod_primary
						 || node1->nod_type == nod_unique)
				{
					if (cnstrt_flag == FALSE)
					{
						request->append_uchar(gds_dyn_end);	/* For field definition  */
						cnstrt_flag = TRUE;
					}

					const char* constraint_name = string ? string->str_data : 0;
					request->append_cstring(gds_dyn_rel_constraint, constraint_name);

					DSQL_NOD index = node1->nod_arg[e_pri_index];
					assert(index);

					const char* index_name = constraint_name;
					string = (STR) index->nod_arg[e_idx_name];
					if (string)
					{
						index_name = string->str_data;
					}

					if (node1->nod_type == nod_primary)
					{
						request->append_cstring(gds_dyn_def_primary_key, index_name);
					}
					else if (node1->nod_type == nod_unique)
					{
						request->append_cstring(gds_dyn_def_unique, index_name);
					}

					request->append_number(gds_dyn_idx_unique, 1);

					if (index->nod_arg[e_idx_asc_dsc])
					{
						request->append_number(gds_dyn_idx_type, 1);
					}

					request->append_cstring(gds_dyn_fld_name, field->fld_name);
					request->append_uchar(gds_dyn_end);
				}
				else if (node1->nod_type == nod_foreign) {
					if (cnstrt_flag == FALSE) {
						request->append_uchar(gds_dyn_end);	/* For field definition  */
						cnstrt_flag = TRUE;
					}
					const char* constraint_name = string ? string->str_data : 0;
					request->append_cstring(gds_dyn_rel_constraint, constraint_name);
					foreign_key(request, node1, constraint_name);
				}
				else if (node1->nod_type == nod_def_constraint) {
					if (cnstrt_flag == FALSE) {
						request->append_uchar(gds_dyn_end);	/* For field definition  */
						cnstrt_flag = TRUE;
					}
					request->append_cstring(gds_dyn_rel_constraint,
									string ? string->str_data : 0);
					check_constraint(request, node1,
									 FALSE /* No delete trigger */ );
				}
			}
		}
	}

	if (cnstrt_flag == FALSE) {
		request->append_uchar(gds_dyn_end);
	}

	} // try
	catch (...)
	{
		clearPermanentField (relation, permanent);
		throw;
	}
	clearPermanentField (relation, permanent);
}


static void define_filter( DSQL_REQ request)
{
/**************************************
 *
 *	d e f i n e _ f i l t e r
 *
 **************************************
 *
 * Function
 *	define a filter to the database.
 *
 **************************************/
	DSQL_NOD *ptr, filter_node;

	filter_node = request->req_ddl_node;
	ptr = filter_node->nod_arg;
	request->append_cstring(gds_dyn_def_filter,
				((STR) (ptr[e_filter_name]))->str_data);
	request->append_number(gds_dyn_filter_in_subtype,
			   (SSHORT)(SLONG) ((ptr[e_filter_in_type])->nod_arg[0]));
	request->append_number(gds_dyn_filter_out_subtype,
			   (SSHORT)(SLONG) ((ptr[e_filter_out_type])->nod_arg[0]));
	request->append_cstring(gds_dyn_func_entry_point,
				((STR) (ptr[e_filter_entry_pt]))->str_data);
	request->append_cstring(gds_dyn_func_module_name,
				((STR) (ptr[e_filter_module]))->str_data);

	request->append_uchar(gds_dyn_end);
}


static void define_generator( DSQL_REQ request)
{
/**************************************
 *
 *	d e f i n e _ g e n e r a t o r
 *
 **************************************
 *
 * Function
 *	create a generator.
 *
 **************************************/

	STR gen_name = (STR) request->req_ddl_node->nod_arg[e_gen_name];
	request->append_cstring(gds_dyn_def_generator, gen_name->str_data);
	request->append_uchar(gds_dyn_end);
}


static void define_index(DSQL_REQ request)
{
/**************************************
 *
 *	d e f i n e _ i n d e x
 *
 **************************************
 *
 * Function
 *	Generate ddl to create an index.
 *
 **************************************/
	DSQL_NOD ddl_node, relation_node, field_list, *ptr, *end;
	STR relation_name, index_name;

	request->append_uchar(gds_dyn_begin);

	ddl_node = request->req_ddl_node;
	relation_node = (DSQL_NOD) ddl_node->nod_arg[e_idx_table];
	relation_name = (STR) relation_node->nod_arg[e_rln_name];
	field_list = ddl_node->nod_arg[e_idx_fields];
	index_name = (STR) ddl_node->nod_arg[e_idx_name];

	request->append_cstring(gds_dyn_def_idx, index_name->str_data);
	request->append_cstring(gds_dyn_rel_name, relation_name->str_data);

/* go through the fields list, making an index segment for each field,
   unless we have a computation, in which case generate an expression index */

	if (field_list->nod_type == nod_list)
		for (ptr = field_list->nod_arg, end = ptr + field_list->nod_count;
			 ptr < end; ptr++)
			request->append_cstring(gds_dyn_fld_name,
						((STR) (*ptr)->nod_arg[1])->str_data);
#ifdef EXPRESSION_INDICES
	else if (field_list->nod_type == nod_def_computed)
		define_computed(request, relation_node, NULL, field_list);
#endif

/* check for a unique index */

	if (ddl_node->nod_arg[e_idx_unique]) {
		request->append_number(gds_dyn_idx_unique, 1);
	}

	if (ddl_node->nod_arg[e_idx_asc_dsc]) {
		request->append_number(gds_dyn_idx_type, 1);
	}

	request->append_uchar(gds_dyn_end);			/* of define index */
	request->append_uchar(gds_dyn_end);			/* of begin */
}

#ifdef NOT_USED_OR_REPLACED
static DSQL_NOD define_insert_action( DSQL_REQ request)
{
/**************************************
 *
 *	d e f i n e _ i n s e r t _ a c t i o n
 *
 **************************************
 *
 * Function
 *	Define an action statement which, given a view
 *	definition, will store a record from
 *	a view of a single relation into the
 *	base relation.
 *
 **************************************/
	DSQL_NOD ddl_node, action_node, insert_node;
	DSQL_NOD select_node, select_expr, from_list, relation_node;
	DSQL_NOD fields_node, values_node, field_node, value_node;
	DSQL_NOD *ptr, *end, *ptr2, *end2;
	DLLS field_stack, value_stack;
	DSQL_REL relation;
	DSQL_FLD field;

	ddl_node = request->req_ddl_node;

/* check whether this is an updatable view definition */

	if ((ddl_node->nod_type != nod_def_view && ddl_node->nod_type != nod_redef_view) ||
		!(select_node = ddl_node->nod_arg[e_view_select]) ||
		/*
		   Handle VIEWS with UNION : nod_select now points to nod_list
		   which in turn points to nod_select_expr
		 */
		!(select_expr = select_node->nod_arg[0]->nod_arg[0]) ||
		!(from_list = select_expr->nod_arg[e_sel_from]) ||
		from_list->nod_count != 1)
		return NULL;

/* make up an action node consisting of a list of 1 insert statement */

	action_node = MAKE_node(nod_list, (int) 1);
	action_node->nod_arg[0] = insert_node =
		MAKE_node(nod_insert, (int) e_ins_count);

/* use the relation referenced in the select statement to insert into */

	relation_node = MAKE_node(nod_relation_name, (int) e_rln_count);
	insert_node->nod_arg[e_ins_relation] = relation_node;
	relation_node->nod_arg[e_rln_name] =
		from_list->nod_arg[0]->nod_arg[e_rln_name];
	relation_node->nod_arg[e_rln_alias] = (DSQL_NOD) MAKE_cstring(TEMP_CONTEXT);

/* get the list of values and fields to assign to -- if there is
   no list of fields, get all fields in the base relation that
   are not computed */

	values_node = ddl_node->nod_arg[e_view_fields];
	fields_node = select_expr->nod_arg[e_sel_list];
	if (!fields_node)
	{
		relation =
			METD_get_relation(request,
							  reinterpret_cast<STR>(relation_node->nod_arg[e_rln_name]));
		field_stack = NULL;
		for (field = relation->rel_fields; field; field = field->fld_next)
		{
			if (field->fld_flags & FLD_computed)
				continue;
			field_node = MAKE_node(nod_field_name, (int) e_fln_count);
			field_node->nod_arg[e_fln_name] = (DSQL_NOD)MAKE_cstring(field->fld_name);
			LLS_PUSH(field_node, &field_stack);
		}
		fields_node = MAKE_list(field_stack);
	}

	if (!values_node)
		values_node = fields_node;

/* generate the list of assignments to fields in the base relation */

	ptr = fields_node->nod_arg;
	end = ptr + fields_node->nod_count;
	ptr2 = values_node->nod_arg;
	end2 = ptr2 + values_node->nod_count;
	value_stack = field_stack = NULL;
	for (; (ptr < end) && (ptr2 < end2); ptr++, ptr2++) {
		field_node = *ptr;
		if (field_node->nod_type == nod_alias)
			field_node = field_node->nod_arg[e_alias_value];

		/* generate the actual assignment, assigning from a field in the "NEW" context */

		if (field_node->nod_type == nod_field_name) {
			field_node->nod_arg[e_fln_context] =
				(DSQL_NOD) MAKE_cstring(TEMP_CONTEXT);
			LLS_PUSH(field_node, &field_stack);

			value_node = MAKE_node(nod_field_name, (int) e_fln_count);
			value_node->nod_arg[e_fln_name] = (*ptr2)->nod_arg[e_fln_name];
			value_node->nod_arg[e_fln_context] =
				(DSQL_NOD) MAKE_cstring(NEW_CONTEXT);
			LLS_PUSH(value_node, &value_stack);
		}
	}

	insert_node->nod_arg[e_ins_values] = MAKE_list(value_stack);
	insert_node->nod_arg[e_ins_fields] = MAKE_list(field_stack);

	return action_node;
}
#endif

static void define_procedure( DSQL_REQ request, NOD_TYPE op)
{
/**************************************
 *
 *	d e f i n e _ p r o c e d u r e
 *
 **************************************
 *
 * Function
 *	Create DYN to store a procedure
 *
 **************************************/
	DSQL_NOD parameters, parameter, *ptr, *end;
	DSQL_PRC procedure;
	DSQL_FLD field, *field_ptr;
	SSHORT position;
	VAR variable;

	TSQL tdsql = GET_THREAD_DATA;

	SSHORT inputs  = 0;
	SSHORT outputs = 0;
	SSHORT locals  = 0;
	DSQL_NOD procedure_node = request->req_ddl_node;
	STR procedure_name = (STR) procedure_node->nod_arg[e_prc_name];

	if (op == nod_replace_procedure)
	{
		if (METD_get_procedure(request, procedure_name))
		{
			define_procedure(request, nod_mod_procedure);
		}
		else
		{
			define_procedure(request, nod_def_procedure);
		}
		return;
	}
	else if (op == nod_def_procedure || op == nod_redef_procedure)
	{
		request->append_cstring(gds_dyn_def_procedure,
					procedure_name->str_data);
		request->append_number(gds_dyn_rel_sql_protection, 1);
	}
	else // op == nod_mod_procedure
	{
		request->append_cstring(gds_dyn_mod_procedure,
					procedure_name->str_data);
		procedure = METD_get_procedure(request, procedure_name);
		if (procedure)
		{
			for (field = procedure->prc_inputs; field;
				 field = field->fld_next)
			{
				request->append_cstring(gds_dyn_delete_parameter,
							field->fld_name);
				request->append_uchar(gds_dyn_end);
			}
			for (field = procedure->prc_outputs; field;
				 field = field->fld_next)
			{
				request->append_cstring(gds_dyn_delete_parameter,
							field->fld_name);
				request->append_uchar(gds_dyn_end);
			}
		}
	}

	STR source = (STR) procedure_node->nod_arg[e_prc_source];
	if (source)
	{
		assert(source->str_length <= MAX_USHORT);
		request->append_string(	gds_dyn_prc_source,
								source->str_data,
								source->str_length);
	}

/* Fill req_procedure to allow procedure to self reference */
	const size_t nExtra = strlen(procedure_name->str_data);
	procedure = FB_NEW_RPT(*tdsql->tsql_default, nExtra) dsql_prc;
	procedure->prc_name = procedure->prc_data;
	procedure->prc_owner =
		procedure->prc_data + procedure_name->str_length + 1;
	strcpy(procedure->prc_name, (SCHAR *) procedure_name->str_data);
	*procedure->prc_owner = '\0';
	request->req_procedure = procedure;


/* now do the input parameters */

	field_ptr = &procedure->prc_inputs;

	if (parameters = procedure_node->nod_arg[e_prc_inputs])
	{
		position = 0;
		for (ptr = parameters->nod_arg, end = ptr + parameters->nod_count;
			 ptr < end; ptr++)
		{
			parameter = *ptr;
			field = (DSQL_FLD) parameter->nod_arg[e_dfl_field];

			request->append_cstring(gds_dyn_def_parameter, field->fld_name);
			request->append_number(gds_dyn_prm_number, position);
			request->append_number(gds_dyn_prm_type, 0);

			DDL_resolve_intl_type(request, field, NULL);
			put_field(request, field, FALSE);

			*ptr = MAKE_variable(field, field->fld_name,
								 VAR_input, 0, (USHORT) (2 * position),
								 locals);
			/* Put the field in a field list which will be stored to allow
			   procedure self referencing */
			*field_ptr = field;
			field_ptr = &field->fld_next;
			position++;

			request->append_uchar(gds_dyn_end);
			request->append_number(gds_dyn_prc_inputs, position);
		}
		inputs = position;
	}

/* Terminate the input list */

	*field_ptr = NULL;

/* now do the output parameters */
	field_ptr = &procedure->prc_outputs;

	if (parameters = procedure_node->nod_arg[e_prc_outputs])
	{
		position = 0;
		end = parameters->nod_arg + parameters->nod_count;
		for (ptr = parameters->nod_arg; ptr < end; ++ptr)
		{
			parameter = *ptr;
			field = (DSQL_FLD) parameter->nod_arg[e_dfl_field];
			request->append_cstring(gds_dyn_def_parameter, field->fld_name);
			request->append_number(gds_dyn_prm_number, position);
			request->append_number(gds_dyn_prm_type, 1);
			DDL_resolve_intl_type(request, field, NULL);
			put_field(request, field, FALSE);

			*ptr = MAKE_variable(field, field->fld_name,
								 VAR_output, 1, (USHORT) (2 * position),
								 locals);
			*field_ptr = field;
			field_ptr = &field->fld_next;
			position++;
			locals++;

			request->append_uchar(gds_dyn_end);
			request->append_number(gds_dyn_prc_outputs, position);
		}
		outputs = position;
	}

	*field_ptr = NULL;
	procedure->prc_out_count = outputs;
	procedure->prc_in_count = inputs;

	request->begin_blr(gds_dyn_prc_blr);
	request->append_uchar(blr_begin);
	if (inputs)
	{
		request->append_uchar(blr_message);
		request->append_uchar(0);
		request->append_ushort(2 * inputs);
		parameters = procedure_node->nod_arg[e_prc_inputs];
		for (ptr = parameters->nod_arg, end = ptr + parameters->nod_count;
			 ptr < end; ptr++)
		{
			parameter = *ptr;
			variable = (VAR) parameter->nod_arg[e_var_variable];
			field = variable->var_field;
			put_msg_field(request, field);
		}
	}
	request->append_uchar(blr_message);
	request->append_uchar(1);
	request->append_ushort(2 * outputs + 1);
	if (outputs)
	{
		parameters = procedure_node->nod_arg[e_prc_outputs];
		for (ptr = parameters->nod_arg, end = ptr + parameters->nod_count;
			 ptr < end; ptr++)
		{
			parameter = *ptr;
			variable = (VAR) parameter->nod_arg[e_var_variable];
			field = variable->var_field;
			put_msg_field(request, field);
		}
	}

/* add slot for EOS */

	request->append_uchar(blr_short);
	request->append_uchar(0);

	if (inputs) {
		request->append_uchar(blr_receive);
		request->append_uchar(0);
	}
	request->append_uchar(blr_begin);
	if (outputs)
	{
		parameters = procedure_node->nod_arg[e_prc_outputs];
		for (ptr = parameters->nod_arg, end = ptr + parameters->nod_count;
			 ptr < end; ptr++)
		{
			parameter = *ptr;
			variable = (VAR) parameter->nod_arg[e_var_variable];
			put_local_variable(request, variable, 0);
		}
	}

	locals = put_local_variables(request, procedure_node->nod_arg[e_prc_dcls],
								 locals);

	request->append_uchar(blr_stall);
/* Put a label before body of procedure, so that
   any exit statement can get out */
	request->append_uchar(blr_label);
	request->append_uchar(0);
	request->req_loop_level = 0;
	GEN_statement(request,
		PASS1_statement(request, procedure_node->nod_arg[e_prc_body], 1));
	request->req_type = REQ_DDL;
	request->append_uchar(blr_end);
	GEN_return(request, procedure_node, TRUE);
	request->append_uchar(blr_end);
	request->end_blr();

	request->append_uchar(gds_dyn_end);
}


//
// Define a constraint, either as part of a create
// table or an alter table statement.
//
static void define_rel_constraint( DSQL_REQ request, DSQL_NOD element)
{
	STR         string          = (STR) element->nod_arg[e_rct_name];
	const char* constraint_name = string ? string->str_data : 0;

	request->append_cstring(gds_dyn_rel_constraint, constraint_name);

	DSQL_NOD node = element->nod_arg[e_rct_type];

	if (node->nod_type == nod_unique || node->nod_type == nod_primary) {
		make_index(request, node, node->nod_arg[0], 0, 0, constraint_name);
	} else if (node->nod_type == nod_foreign) {
		foreign_key(request, node, constraint_name);
	} else if (node->nod_type == nod_def_constraint) {
		check_constraint(request, node, FALSE /* No delete trigger */ );
	}
}


static void define_relation( DSQL_REQ request)
{
/**************************************
 *
 *	d e f i n e _ r e l a t i o n
 *
 **************************************
 *
 * Function
 *	Create an SQL table, relying on DYN to generate
 *	global fields for the local fields.
 *
 **************************************/
	DSQL_NOD ddl_node, elements, element, *ptr, *end, relation_node;
	STR relation_name, external_file;
	SSHORT position;

	ddl_node = request->req_ddl_node;

	relation_node = ddl_node->nod_arg[e_drl_name];
	relation_name = (STR) relation_node->nod_arg[e_rln_name];
	request->append_cstring(gds_dyn_def_rel, relation_name->str_data);
	if (external_file = (STR) ddl_node->nod_arg[e_drl_ext_file])
	{
		request->append_cstring(gds_dyn_rel_ext_file, external_file->str_data);
	}
	save_relation(request, relation_name);
	request->append_number(gds_dyn_rel_sql_protection, 1);

/* now do the actual metadata definition */

	elements = ddl_node->nod_arg[e_drl_elements];
	for (ptr = elements->nod_arg, end = ptr + elements->nod_count, position =
		 0; ptr < end; ptr++) {
		element = *ptr;
		switch (element->nod_type) {
		case nod_def_field:
			define_field(request, element, position, relation_name);
			position++;
			break;

		case nod_rel_constraint:
			define_rel_constraint(request, element);
			break;

		default:
			break;
		}
	}

	request->append_uchar(gds_dyn_end);
}


//
//	Create a SQL role.
//
static void define_role(DSQL_REQ request)
{
	STR gen_name = (STR) request->req_ddl_node->nod_arg[e_gen_name];
	request->append_cstring(isc_dyn_def_sql_role, gen_name->str_data);
	request->append_uchar(gds_dyn_end);
}


static void define_set_null_trg(DSQL_REQ    request,
								DSQL_NOD    element,
								DSQL_NOD    for_columns,
								DSQL_NOD    prim_columns,
								const char*	prim_rel_name,
								const char*	for_rel_name,
								bool        on_upd_trg)
{
/*****************************************************
 *
 *	d e f i n e _ s e t _ n u l l _ t r g
 *
 *****************************************************
 *
 * Function
 *	define "on delete/update set null" trigger (for referential integrity)
 *      The trigger blr is the same for both the delete and update
 *      cases. Only difference is its TRIGGER_TYPE (ON DELETE or ON UPDATE)
 *      The on_upd_trg parameter == TRUE is an update trigger.
 *
 *****************************************************/

	if (element->nod_type != nod_foreign) {
		return;
	}

	// count of foreign key columns
	assert(prim_columns->nod_count == for_columns->nod_count);
	assert(prim_columns->nod_count != 0);

	request->generate_unnamed_trigger_beginning(on_upd_trg,
												prim_rel_name,
												prim_columns,
												for_rel_name,
												for_columns);

	USHORT num_fields = 0;
	DSQL_NOD*   for_key_flds = for_columns->nod_arg;

	do {
		STR for_key_fld_name_str = (STR) (*for_key_flds)->nod_arg[1];

		request->append_uchar(blr_assignment);
		request->append_uchar(blr_null);
		request->append_uchar(blr_field);
		request->append_uchar(2);
		request->append_cstring(0, for_key_fld_name_str->str_data);

		num_fields++;
		for_key_flds++;

	} while (num_fields < for_columns->nod_count);

	request->append_uchar(blr_end);

	if (on_upd_trg) {
		request->append_uchars(blr_end, 3);
	}
	request->end_blr();
	// end of the blr

    request->append_number(isc_dyn_system_flag, 
                            frb_sysflag_referential_constraint);

	// no trg_source and no trg_description
	request->append_uchar(gds_dyn_end);

}


//
// create a shadow for the database
//
static void define_shadow(DSQL_REQ request)
{
	DSQL_NOD  shadow_node = request->req_ddl_node;
	DSQL_NOD* ptr         = shadow_node->nod_arg;

	if (!ptr[e_shadow_number])
	{
		ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
				  gds_arg_gds, gds_dsql_command_err,
				  gds_arg_gds, gds_dsql_shadow_number_err, 0);
	}

	request->append_number(gds_dyn_def_shadow, (SSHORT)(SLONG) (ptr[e_shadow_number]));
	request->append_cstring(gds_dyn_def_file, ((STR) (ptr[e_shadow_name]))->str_data);
	request->append_number(gds_dyn_shadow_man_auto,
			   (SSHORT)(SLONG) ((ptr[e_shadow_man_auto])->nod_arg[0]));
	request->append_number(gds_dyn_shadow_conditional,
			   (SSHORT)(SLONG) ((ptr[e_shadow_conditional])->nod_arg[0]));

	request->append_file_start(0);

	SLONG length = (SLONG) ptr[e_shadow_length];
	request->append_file_length(length);

	request->append_uchar(gds_dyn_end);
	DSQL_NOD elements = ptr[e_shadow_sec_files];
	if (elements)
	{
		DSQL_NOD* end = elements->nod_arg + elements->nod_count;
		for (ptr = elements->nod_arg; ptr < end; ++ptr)
		{
			DSQL_NOD element = *ptr;
			FIL file    = (FIL) element->nod_arg[0];
			request->append_cstring(gds_dyn_def_file, file->fil_name->str_data);

			if (!length && !file->fil_start)
			{
				ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
						  gds_arg_gds, gds_dsql_command_err,
						  gds_arg_gds, gds_dsql_file_length_err,
						  gds_arg_number, file->fil_name->str_data,
						  /* Preceding file did not specify length, so %s must include starting page number */
						  0);
			}

			SLONG start = file->fil_start;
			request->append_file_start(start);
			length = file->fil_length;
			request->append_file_length(length);
			request->append_uchar(gds_dyn_end);
		}
	}

	request->append_uchar(gds_dyn_end);
}


//
// Create the ddl to define or alter a trigger.
//
static void define_trigger( DSQL_REQ request, DSQL_NOD node)
{
	STR relation_name;
	DSQL_NOD temp, constant, relation_node;
	USHORT trig_type;

	TSQL tdsql = GET_THREAD_DATA;

	// make the "define trigger" node the current request ddl node so
	// that generating of BLR will be appropriate for trigger

	request->req_ddl_node = node;

	STR trigger_name = (STR) node->nod_arg[e_trg_name];

	if (node->nod_type == nod_replace_trigger)
	{
		if (METD_get_trigger_relation(request,
									  trigger_name,
									  &trig_type))
		{
			node->nod_type = nod_mod_trigger;
		}
		else
		{
			node->nod_type = nod_def_trigger;
		}
		define_trigger(request, node);
		return;
	}
	else if (node->nod_type == nod_def_trigger)
	{
		assert(trigger_name->str_length <= MAX_USHORT);
		request->append_string(	gds_dyn_def_trigger,
								trigger_name->str_data,
								trigger_name->str_length);
		relation_node = node->nod_arg[e_trg_table];
		relation_name = (STR) relation_node->nod_arg[e_rln_name];
		assert(relation_name->str_length <= MAX_USHORT);
		request->append_string(	gds_dyn_rel_name,
								relation_name->str_data,
								relation_name->str_length);
		request->append_uchar(gds_dyn_sql_object);
	}
	else // nod_mod_trigger
	{						
		assert(node->nod_type == nod_mod_trigger);
		assert(trigger_name->str_length <= MAX_USHORT);
		request->append_string(	gds_dyn_mod_trigger,
								trigger_name->str_data,
								trigger_name->str_length);
		if (node->nod_arg[e_trg_actions])
		{
			/* Since we will be updating the body of the trigger, we need
			   to know what relation the trigger relates to. */

			relation_name =
				 METD_get_trigger_relation(	request,
											trigger_name,
											&trig_type);
			if (!relation_name)
			{
					ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -204,
							  gds_arg_gds, gds_dsql_trigger_err, gds_arg_gds,
							  gds_random, gds_arg_string,
							  trigger_name->str_data, 0);
			}
			relation_node = FB_NEW_RPT(*tdsql->tsql_default, e_rln_count) dsql_nod;
			node->nod_arg[e_trg_table] = relation_node;
			relation_node->nod_type = nod_relation_name;
			relation_node->nod_count = e_rln_count;
			relation_node->nod_arg[e_rln_name] = (DSQL_NOD) relation_name;
		}
	}

	STR source = (STR) node->nod_arg[e_trg_source];
	DSQL_NOD actions = (node->nod_arg[e_trg_actions]) ?
		node->nod_arg[e_trg_actions]->nod_arg[1] : NULL;

	if (source && actions) {
		assert(source->str_length <= MAX_USHORT);
		request->append_string(	gds_dyn_trg_source,
								source->str_data,
								source->str_length);
	}

	if (constant = node->nod_arg[e_trg_active])
		request->append_number(gds_dyn_trg_inactive,
				   (SSHORT)(SLONG) constant->nod_arg[0]);

	if (constant = node->nod_arg[e_trg_position])
		request->append_number(gds_dyn_trg_sequence,
				   (SSHORT)(SLONG) constant->nod_arg[0]);

	if (constant = node->nod_arg[e_trg_type]) {
		request->append_number(gds_dyn_trg_type, (SSHORT)(SLONG) constant->nod_arg[0]);
		trig_type = (USHORT)(ULONG) constant->nod_arg[0];
	}
	else {
		assert(node->nod_type == nod_mod_trigger);
	}

	if (actions)
	{
		/* create the "OLD" and "NEW" contexts for the trigger --
		   the new one could be a dummy place holder to avoid resolving
		   fields to that context but prevent relations referenced in
		   the trigger actions from referencing the predefined "1" context */

		if (request->req_context_number) {
			reset_context_stack(request);
		}

		temp = relation_node->nod_arg[e_rln_alias];
		if (hasOldContext(trig_type))
		{
			relation_node->nod_arg[e_rln_alias] =
				(DSQL_NOD) MAKE_cstring(OLD_CONTEXT);
			PASS1_make_context(request, relation_node);
		}
		else
		{
			request->req_context_number++;
		}

		if (hasNewContext(trig_type))
		{
			relation_node->nod_arg[e_rln_alias] =
				(DSQL_NOD) MAKE_cstring(NEW_CONTEXT);
			PASS1_make_context(request, relation_node);
		}
		else
		{
			request->req_context_number++;
		}

		relation_node->nod_arg[e_rln_alias] = temp;

		// generate the trigger blr

		request->begin_blr(gds_dyn_trg_blr);
		request->append_uchar(blr_begin);

		put_local_variables(request,
							node->nod_arg[e_trg_actions]->nod_arg[0], 0);

		request->req_scope_level++;
		// dimitr: I see no reason to deny EXIT command in triggers,
		//		   hence I've added zero label at the beginning.
		//		   My first suspicion regarding obvious conflict
		//		   with trigger messages (nod_abort) is wrong,
		//		   although the fact that they use the same BLR code
		//		   is still a potential dangerous and must be fixed.
		//		   Hopefully, system triggers are never recompiled.
		request->append_uchar(blr_label);
		request->append_uchar(0);
		request->req_loop_level = 0;
		GEN_statement(request, PASS1_statement(request, actions, 1));
		request->req_scope_level--;
		request->append_uchar(blr_end);
		request->end_blr();

		/* the request type may have been set incorrectly when parsing
		   the trigger actions, so reset it to reflect the fact that this
		   is a data definition request; also reset the ddl node */

		request->req_type = REQ_DDL;
	}

	if (temp = node->nod_arg[e_trg_messages])
	{
		DSQL_NOD* end = temp->nod_arg + temp->nod_count;
		for (DSQL_NOD* ptr = temp->nod_arg; ptr < end; ++ptr)
		{
			DSQL_NOD    message = *ptr;
			SSHORT number  = (SSHORT)(SLONG) message->nod_arg[e_msg_number];
			if (message->nod_type == nod_del_trigger_msg)
			{
				request->append_number(gds_dyn_delete_trigger_msg, number);
				request->append_uchar(gds_dyn_end);
			}
			else
			{
				STR message_text = (STR) message->nod_arg[e_msg_text];
				if (message->nod_type == nod_def_trigger_msg) {
					request->append_number(gds_dyn_def_trigger_msg, number);
				} else {
					request->append_number(gds_dyn_mod_trigger_msg, number);
				}
				assert(message_text->str_length <= MAX_USHORT);
				request->append_string(	gds_dyn_trg_msg,
										message_text->str_data,
										message_text->str_length);
				request->append_uchar(gds_dyn_end);
			}
		}
	}

	request->append_uchar(gds_dyn_end);
}


static void define_udf( DSQL_REQ request)
{
/**************************************
 *
 *	d e f i n e _ u d f
 *
 **************************************
 *
 * Function
 *	define a udf to the database.
 *
 **************************************/
	DSQL_NOD *end, *ret_val_ptr, *param_node;
	DSQL_FLD field;
	SSHORT position, blob_position;

	DSQL_NOD  udf_node = request->req_ddl_node;
	DSQL_NOD  arguments = udf_node->nod_arg[e_udf_args];
	DSQL_NOD* ptr = udf_node->nod_arg;
	const char* udf_name = ((STR) (ptr[e_udf_name]))->str_data;
	const STR func_entry_point_name = reinterpret_cast<STR>(ptr[e_udf_entry_pt]);
	const STR func_module_name      = reinterpret_cast<STR>(ptr[e_udf_module]);
	request->append_cstring(gds_dyn_def_function, udf_name);
	request->append_cstring(gds_dyn_func_entry_point, func_entry_point_name->str_data);
	request->append_cstring(gds_dyn_func_module_name, func_module_name->str_data);

	ret_val_ptr = ptr[e_udf_return_value]->nod_arg;


	if (field = (DSQL_FLD) ret_val_ptr[0]) {

        // CVC: This is case of "returns <type> [by value|reference]"
		/* Some data types can not be returned as value */

		if (((int)(SLONG) (ret_val_ptr[1]->nod_arg[0]) == FUN_value) &&
			(field->fld_dtype == dtype_text ||
			 field->fld_dtype == dtype_varying ||
			 field->fld_dtype == dtype_cstring ||
			 field->fld_dtype == dtype_blob ||
			 field->fld_dtype == dtype_timestamp))
		{
				ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
						  gds_arg_gds, gds_dsql_command_err,
						  gds_arg_gds, gds_return_mode_err,
						  /* Return mode by value not allowed for this data type */
						  0);
		}

		/* For functions returning a blob, coerce return argument position to
		   be the last parameter. */

		if (field->fld_dtype == dtype_blob)
		{
			blob_position = (arguments) ? arguments->nod_count + 1 : 1;
			if (blob_position > MAX_UDF_ARGUMENTS)
			{
				ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
						  gds_arg_gds, gds_dsql_command_err,
						  gds_arg_gds, gds_extern_func_err,
						  /* External functions can not have more than 10 parameters */
						  /* Or 9 if the function returns a BLOB */
						  0);
			}

			request->append_number(gds_dyn_func_return_argument, blob_position);
		}
		else
		{
			request->append_number(gds_dyn_func_return_argument, (SSHORT) 0);
		}

		position = 0;
	}
	else {

        // CVC: This is case of "returns parameter <N>"


		position = (SSHORT)(SLONG) (ret_val_ptr[1]->nod_arg[0]);
		/* Function modifies an argument whose value is the function return value */

		if (!arguments || position > arguments->nod_count || position < 1) {
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
                      gds_arg_gds, isc_dsql_udf_return_pos_err, /*gds__extern_func_err, */
                      gds_arg_number, (SLONG) (arguments ? arguments->nod_count : 0),
                      // CVC: We should devise new msg "position should be between 1 and #params";
                      // here it is: dsql_udf_return_pos_err

					  /* External functions can not have more than 10 parameters */
					  /* Not strictly correct -- return position error */
					  0);
		}

		request->append_number(gds_dyn_func_return_argument, position);
		position = 1;
	}

/* Now define all the arguments */
	if (!position)
	{
    /* CVC: This is case of "returns <type> [by value|reference]" */
		if (field->fld_dtype == dtype_blob)
		{
        /* CVC: I need to test returning blobs by descriptor before allowing the        change there. For now, I ignore the return type specification. */
			BOOLEAN free_it = ((SSHORT)(SLONG) ret_val_ptr[1]->nod_arg[0] < 0);
			request->append_number(gds_dyn_def_function_arg, blob_position);
			request->append_number(gds_dyn_func_mechanism,
					   (SSHORT)(SLONG) ((free_it ? -1 : 1) * FUN_blob_struct));
			/* if we have the free_it set then the blob has
			   to be freed on return */
		}
		else
		{
			request->append_number(gds_dyn_def_function_arg, (SSHORT) 0);
			request->append_number(gds_dyn_func_mechanism,
					   (SSHORT)(SLONG) (ret_val_ptr[1]->nod_arg[0]));
		}

		request->append_cstring(gds_dyn_function_name, udf_name);
		DDL_resolve_intl_type(request, field, NULL);
		put_field(request, field, TRUE);
		request->append_uchar(gds_dyn_end);
		position = 1;
	}

	assert(position == 1);

    /* CVC: This for all params, including the case of "returns parameter <N>" */

	if (arguments)
	{
		for (ptr = arguments->nod_arg, end = ptr + arguments->nod_count;
			 ptr < end; ptr++, position++)
		{
			if (position > MAX_UDF_ARGUMENTS)
			{
				ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
						  gds_arg_gds, gds_dsql_command_err,
						  gds_arg_gds, gds_extern_func_err,
						  /* External functions can not have more than 10 parameters */
						  0);
			}

            /*field = (DSQL_FLD) *ptr; */
            param_node = (*ptr)->nod_arg;
            field = (DSQL_FLD) param_node [e_udf_param_field];

			request->append_number(gds_dyn_def_function_arg, (SSHORT) position);

            if (param_node [e_udf_param_type]) {
                SSHORT arg_mechanism = (SSHORT)(SLONG) (param_node [e_udf_param_type]->nod_arg [0]);
				request->append_number(gds_dyn_func_mechanism, arg_mechanism);
            }
            else if (field->fld_dtype == dtype_blob) {
				request->append_number(gds_dyn_func_mechanism, 
                                       (SSHORT) FUN_blob_struct);
            }
			else {
				request->append_number(gds_dyn_func_mechanism,
						   (SSHORT) FUN_reference);
			}

			request->append_cstring(gds_dyn_function_name, udf_name);
			DDL_resolve_intl_type(request, field, NULL);
			put_field(request, field, TRUE);
			request->append_uchar(gds_dyn_end);
		}
	}

	request->append_uchar(gds_dyn_end);
}



static void define_update_action(
								 DSQL_REQ request,
								 DSQL_NOD * base_and_node, DSQL_NOD * base_relation)
{
/* *************************************
 *
 *	d e f i n e _ u p d a t e _ a c t i o n
 *
 **************************************
 *
 * Function
 *	Define an action statement which, given a view
 *	definition, will map a update to a  record from
 *	a view of a single relation into the
 *	base relation.
 *
 **************************************/
	DSQL_NOD ddl_node, eql_node, and_node, old_and;
	DSQL_NOD select_node, select_expr, from_list, relation_node;
	DSQL_NOD fields_node, values_node, field_node, value_node, old_value_node;
	DSQL_NOD *ptr, *end, *ptr2, *end2;
	DSQL_NOD iand_node, or_node, anull_node, bnull_node;
	DLLS field_stack;
	DSQL_REL relation;
	DSQL_FLD field;
	SSHORT and_arg = 0;

	ddl_node = request->req_ddl_node;

/* check whether this is an updatable view definition */

	if ((ddl_node->nod_type != nod_def_view && ddl_node->nod_type != nod_redef_view) ||
		!(select_node = ddl_node->nod_arg[e_view_select]) ||
		/*
		   Handle VIEWS with UNION : nod_select now points to nod_list
		   which in turn points to nod_select_expr
		 */
		!(select_expr = select_node->nod_arg[0]->nod_arg[0]) ||
		!(from_list = select_expr->nod_arg[e_sel_from]) ||
		from_list->nod_count != 1)
		return;

/* use the relation referenced in the select statement for rse*/

	relation_node = MAKE_node(nod_relation_name, (int) e_rln_count);
	relation_node->nod_arg[e_rln_name] =
		from_list->nod_arg[0]->nod_arg[e_rln_name];
	relation_node->nod_arg[e_rln_alias] = (DSQL_NOD) MAKE_cstring(TEMP_CONTEXT);
	*base_relation = relation_node;

/* get the list of values and fields to compare to -- if there is
   no list of fields, get all fields in the base relation that
   are not computed */

	values_node = ddl_node->nod_arg[e_view_fields];
	if (!(fields_node = select_expr->nod_arg[e_sel_list]))
	{
		relation =
			METD_get_relation(request,
							  reinterpret_cast<STR>(relation_node->nod_arg[e_rln_name]));
		field_stack = NULL;
		for (field = relation->rel_fields; field; field = field->fld_next)
		{
			if (field->fld_flags & FLD_computed)
				continue;
			field_node = MAKE_node(nod_field_name, (int) e_fln_count);
			field_node->nod_arg[e_fln_name] =
				(DSQL_NOD) MAKE_cstring(field->fld_name);
			LLS_PUSH(field_node, &field_stack);
		}
		fields_node = MAKE_list(field_stack);
	}
	if (!values_node)
		values_node = fields_node;

/* generate the list of assignments to fields in the base relation */

	ptr = fields_node->nod_arg;
	end = ptr + fields_node->nod_count;
	ptr2 = values_node->nod_arg;
	end2 = ptr2 + values_node->nod_count;
	field_stack = NULL;
	and_node = MAKE_node(nod_and, (int) 2);
	and_arg = 0;
	for (; (ptr < end) && (ptr2 < end2); ptr++, ptr2++) {
		field_node = *ptr;
		if (field_node->nod_type == nod_alias)
			field_node = field_node->nod_arg[e_alias_value];

		/* generate the actual comparisons */

		if (field_node->nod_type == nod_field_name) {
			field_node->nod_arg[e_fln_context] =
				(DSQL_NOD) MAKE_cstring(TEMP_CONTEXT);

			value_node = MAKE_node(nod_field_name, (int) e_fln_count);
			value_node->nod_arg[e_fln_name] = (*ptr2)->nod_arg[e_fln_name];
			value_node->nod_arg[e_fln_context] =
				(DSQL_NOD) MAKE_cstring(NEW_CONTEXT);

			old_value_node = MAKE_node(nod_field_name, (int) e_fln_count);
			old_value_node->nod_arg[e_fln_name] =
				(*ptr2)->nod_arg[e_fln_name];
			old_value_node->nod_arg[e_fln_context] =
				(DSQL_NOD) MAKE_cstring(OLD_CONTEXT);
			eql_node = MAKE_node(nod_eql, (int) 2);
			eql_node->nod_arg[0] = old_value_node;
			eql_node->nod_arg[1] = field_node;

			anull_node = MAKE_node(nod_missing, 1);
			anull_node->nod_arg[0] = old_value_node;
			bnull_node = MAKE_node(nod_missing, 1);
			bnull_node->nod_arg[0] = field_node;

			iand_node = MAKE_node(nod_and, (int) 2);
			iand_node->nod_arg[0] = anull_node;
			iand_node->nod_arg[1] = bnull_node;

			or_node = MAKE_node(nod_or, (int) 2);
			or_node->nod_arg[0] = eql_node;
			or_node->nod_arg[1] = iand_node;

			if (and_arg <= 1)
				and_node->nod_arg[and_arg++] = or_node;
			else {
				old_and = and_node;
				and_node = MAKE_node(nod_and, (int) 2);
				and_node->nod_arg[0] = old_and;
				and_node->nod_arg[1] = or_node;
			}
		}
	}

	if (and_arg <= 1)
		and_node->nod_arg[and_arg] = select_expr->nod_arg[e_sel_where];
	else {
		old_and = and_node;
		and_node = MAKE_node(nod_and, (int) 2);
		and_node->nod_arg[0] = old_and;
		and_node->nod_arg[1] = select_expr->nod_arg[e_sel_where];
	}
	*base_and_node = and_node;
}


static void define_upd_cascade_trg(	DSQL_REQ    request,
									DSQL_NOD    element,
									DSQL_NOD    for_columns,
									DSQL_NOD    prim_columns,
									const char* prim_rel_name,
									const char* for_rel_name)
{
/*****************************************************
 *
 *	d e f i n e _ u p d _ c a s c a d e _ t r g
 *
 *****************************************************
 *
 * Function
 *	define "on update cascade" trigger (for referential integrity)
 *      along with the trigger blr.
 *
 *****************************************************/

	if (element->nod_type != nod_foreign) {
		return;
	}

	// count of foreign key columns
	assert(prim_columns->nod_count == for_columns->nod_count);
	assert(prim_columns->nod_count != 0);

	request->generate_unnamed_trigger_beginning(true,
												prim_rel_name,
												prim_columns,
												for_rel_name,
												for_columns);

	USHORT num_fields = 0;
	DSQL_NOD*   for_key_flds  = for_columns->nod_arg;
	DSQL_NOD*   prim_key_flds = prim_columns->nod_arg;

	do {
		STR for_key_fld_name_str  = (STR) (*for_key_flds)->nod_arg[1];
		STR prim_key_fld_name_str = (STR) (*prim_key_flds)->nod_arg[1];

		request->append_uchar(blr_assignment);
		request->append_uchar(blr_field);
		request->append_uchar(1);
		request->append_cstring(0, prim_key_fld_name_str->str_data);
		request->append_uchar(blr_field);
		request->append_uchar(2);
		request->append_cstring(0, for_key_fld_name_str->str_data);

		num_fields++;
		prim_key_flds++;
		for_key_flds++;

	} while (num_fields < for_columns->nod_count);

	request->append_uchars(blr_end, 4);
	request->end_blr();
	// end of the blr

    request->append_number(isc_dyn_system_flag, 
                           frb_sysflag_referential_constraint);

	// no trg_source and no trg_description
	request->append_uchar(gds_dyn_end);

}


static void define_view( DSQL_REQ request, NOD_TYPE op)
{
/**************************************
 *
 *	d e f i n e _ v i e w
 *
 **************************************
 *
 * Function
 *	Create the ddl to define a view, using a SELECT
 *	statement as the source of the view.
 *
 **************************************/
	DSQL_NOD node, select, select_expr, rse, field_node;
	DSQL_NOD check, relation_node;
	DSQL_NOD view_fields, *ptr, *end;
	DSQL_NOD items, *i_ptr, *i_end;
	DSQL_REL relation;
	DSQL_FLD field;
	DSQL_CTX context;
	STR view_name, field_name, source;
	SSHORT position, updatable = TRUE;
	TEXT *field_string;
	DLLS temp;

	node = request->req_ddl_node;
	view_name = (STR) node->nod_arg[e_view_name];

	if (op == nod_replace_view)
	{
		if (METD_get_relation(request, view_name))
		{
			define_view(request, nod_mod_view);
		}
		else
		{
			define_view(request, nod_def_view);
		}
		return;
	}
	else if (op == nod_def_view || op == nod_redef_view)
	{
		request->append_cstring(gds_dyn_def_view,
					view_name->str_data);
		request->append_number(gds_dyn_rel_sql_protection, 1);
		save_relation(request, view_name);
	}
	else // op == nod_mod_view
	{
		request->append_cstring(gds_dyn_mod_view,
					view_name->str_data);
		relation = METD_get_relation(request, view_name);
		if (relation)
		{
			for (field = relation->rel_fields; field;
				 field = field->fld_next)
			{
				request->append_cstring(gds_dyn_delete_local_fld,
							field->fld_name);
				request->append_uchar(gds_dyn_end);
			}
		}
		else
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
					  /* gds_arg_gds, gds__dsql_command_err,
						 gds_arg_gds, gds__dsql_view_not_found, */
					  gds_arg_gds, 336068783L,
					  gds_arg_string, view_name->str_data,
					  gds_arg_end);
		}
	}

/* compile the SELECT statement into a record selection expression,
   making sure to bump the context number since view contexts start
   at 1 (except for computed fields)  -- note that calling PASS1_rse
   directly rather than PASS1_statement saves the context stack */

	if (request->req_context_number)
		reset_context_stack(request);
	request->req_context_number++;
	select = node->nod_arg[e_view_select];
	select_expr = select->nod_arg[0];
	rse = PASS1_rse(request, select_expr, select->nod_arg[1], NULL);

	// store the blr and source string for the view definition

	request->begin_blr(gds_dyn_view_blr);
	GEN_expr(request, rse);
	request->end_blr();

/* Store source for view. gdef -e cannot cope with it.
   We need to add something to rdb$views to indicate source type.
   Source will be for documentation purposes. */

	source = (STR) node->nod_arg[e_view_source];
	assert(source->str_length <= MAX_USHORT);
	request->append_string(	gds_dyn_view_source,
							source->str_data,
							source->str_length);

/* define the view source relations from the request contexts & union contexts*/

    while (request->req_union_context) {
        context = reinterpret_cast<DSQL_CTX>(LLS_POP(&request->req_union_context));
        LLS_PUSH(context, &request->req_context);
    }


	for (temp = request->req_context; temp; temp = temp->lls_next)
	{
		context = (DSQL_CTX) temp->lls_object;
		if (relation = context->ctx_relation)
		{
			request->append_cstring(gds_dyn_view_relation, relation->rel_name);
			request->append_number(gds_dyn_view_context, context->ctx_context);
			request->append_cstring(gds_dyn_view_context_name,
						context->ctx_alias ? context->ctx_alias : relation->
						rel_name);
			request->append_uchar(gds_dyn_end);
		}
	}

/* if there are field names defined for the view, match them in order
   with the items from the SELECT.  Otherwise use all the fields from
   the rse node that was created from the select expression */

	items = rse->nod_arg[e_rse_items];
	i_ptr = items->nod_arg;
	i_end = i_ptr + items->nod_count;

	ptr = end = NULL;
	if ((view_fields = node->nod_arg[e_view_fields]) != NULL) {
		ptr = view_fields->nod_arg;
		end = ptr + view_fields->nod_count;
	}

/* go through the fields list, defining the local fields;
   if an expression is specified rather than a field, define
   a global field for the computed value as well */

	for (position = 0; i_ptr < i_end; i_ptr++, position++) {
		field_node = *i_ptr;

		/* check if this is a field or an expression */

		field = NULL;
		context = NULL;
		if (field_node->nod_type == nod_field) {
			field = (DSQL_FLD) field_node->nod_arg[e_fld_field];
			context = (DSQL_CTX) field_node->nod_arg[e_fld_context];
		}
		else
			updatable = FALSE;

		/* if this is an expression, check to make sure there is a name specified */

		if (!ptr && !field)
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
					  gds_arg_gds, gds_dsql_command_err,
					  gds_arg_gds, gds_specify_field_err,
					  /* must specify field name for view select expression */
					  0);
		}

		/* determine the proper field name, replacing the default if necessary */

		if (field) {
			field_string = field->fld_name;
		}
		/* CVC: Small modification here to catch any mismatch between number of
				explicit field names in a view and number of fields in the select expression,
				see comment below. This closes Firebird Bug #223059. */
		if (ptr)
		{
			if (ptr < end)
			{
				field_name = (STR) (*ptr)->nod_arg[1];
				field_string = (TEXT *) field_name->str_data;
			}
			ptr++;
		}

		/* if not an expression, point to the proper base relation field,
		   else make up an SQL field with generated global field for calculations */

		if (field)
		{
			request->append_cstring(gds_dyn_def_local_fld, field_string);
			request->append_cstring(gds_dyn_fld_base_fld, field->fld_name);
			if (field->fld_dtype <= dtype_any_text) {
				request->append_number(gds_dyn_fld_collation, field->fld_collation_id);
			}
			request->append_number(gds_dyn_view_context, context->ctx_context);
		}
		else
		{
			request->append_cstring(gds_dyn_def_sql_fld, field_string);
			MAKE_desc(&field_node->nod_desc, field_node);
			put_descriptor(request, &field_node->nod_desc);
			request->begin_blr(gds_dyn_fld_computed_blr);
			GEN_expr(request, field_node);
			request->end_blr();
			request->append_number(gds_dyn_view_context, (SSHORT) 0);
		}

		save_field(request, field_string);

		request->append_number(gds_dyn_fld_position, position);
		request->append_uchar(gds_dyn_end);
	}

    /* CVC: This message was not catching the case when
       #fields<items in select list, see comment above. */

	if (ptr != end)
	{
		ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
				  gds_arg_gds, gds_dsql_command_err,
				  gds_arg_gds, gds_num_field_err,
				  /* number of fields does not match select list */
				  0);
	}

	// setup to define triggers for WITH CHECK OPTION

	check = node->nod_arg[e_view_check];

	if (check)
	{
		if (!updatable)
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
					  gds_arg_gds, gds_dsql_command_err,
					  gds_arg_gds, gds_col_name_err,
					  /* Only simple column names permitted for VIEW WITH CHECK OPTION */
					  0);
		}

		if (select_expr->nod_count != 1)
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
					  gds_arg_gds, gds_dsql_command_err,
					  gds_arg_gds, gds_table_view_err,
					  /* Only one table allowed for VIEW WITH CHECK OPTION */
					  0);
		}
		/*
		   Handle VIEWS with UNION : nod_select now points to nod_list
		   which in turn points to nod_select_expr
		 */
		else if (select_expr->nod_arg[0]->nod_arg[e_sel_from]->nod_count != 1)
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
					  gds_arg_gds, gds_dsql_command_err,
					  gds_arg_gds, gds_table_view_err,
					  /* Only one table allowed for VIEW WITH CHECK OPTION */
					  0);
		}

		/*
		   Handle VIEWS with UNION : nod_select now points to nod_list
		   which in turn points to nod_select_expr
		 */
		select_expr = select_expr->nod_arg[0];
		if (!(select_expr->nod_arg[e_sel_where]))
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
					  gds_arg_gds, gds_dsql_command_err,
					  gds_arg_gds, gds_where_err,
					  /* No where clause for VIEW WITH CHECK OPTION */
					  0);
		}

		if (select_expr->nod_arg[e_sel_distinct] ||
			select_expr->nod_arg[e_sel_group] ||
			select_expr->nod_arg[e_sel_having])
		{
				ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
						  gds_arg_gds, gds_dsql_command_err,
						  gds_arg_gds, gds_distinct_err,
						  /* DISTINCT, GROUP or HAVING not permitted for VIEW WITH CHECK OPTION */
						  0);
		}

		relation_node = MAKE_node(nod_relation_name, e_rln_count);
		relation_node->nod_arg[e_rln_name] = (DSQL_NOD) view_name;
		check->nod_arg[e_cnstr_table] = relation_node;

		check->nod_arg[e_cnstr_source] = (DSQL_NOD) source;

		/* the condition for the trigger is the converse of the selection
		   criteria for the view, suitably fixed up so that the fields in
		   the view are referenced */

		check->nod_arg[e_cnstr_condition] = select_expr->nod_arg[e_sel_where];

		/* Define the triggers   */

		create_view_triggers(request, check, rse->nod_arg[e_rse_items]);
	}

	request->append_uchar(gds_dyn_end);
	reset_context_stack(request);
}


static void define_view_trigger( DSQL_REQ request, DSQL_NOD node, DSQL_NOD rse, DSQL_NOD items)
{								/* The fields in VIEW actually  */
/**************************************
 *
 *	d e f i n e _ v i e w _ t r i g g e r
 *
 **************************************
 *
 * Function
 *	Create the ddl to define a trigger for a VIEW WITH CHECK OPTION.
 *
 **************************************/
	STR trigger_name, relation_name;
	DSQL_NOD temp_rse, temp, ddl_node, actions, *ptr, *end, constant;
	DSQL_NOD relation_node;
	USHORT trig_type;
	DSQL_NOD action_node, condition, select, select_expr, view_fields;
	DSQL_CTX context, sav_context = 0;
	DLLS stack;
	TSQL tdsql;

	tdsql = GET_THREAD_DATA;

	ddl_node = request->req_ddl_node;

	select = ddl_node->nod_arg[e_view_select];
/*
   Handle VIEWS with UNION : nod_select now points to nod_list
   which in turn points to nod_select_expr
*/
	select_expr = select->nod_arg[0]->nod_arg[0];
	view_fields = ddl_node->nod_arg[e_view_fields];

/* make the "define trigger" node the current request ddl node so
   that generating of BLR will be appropriate for trigger */

	request->req_ddl_node = node;

	trigger_name = (STR) node->nod_arg[e_cnstr_name];

	if (node->nod_type == nod_def_constraint)
	{
		assert(trigger_name->str_length <= MAX_USHORT);
		request->append_string(	gds_dyn_def_trigger,
								trigger_name->str_data,
								trigger_name->str_length);
		relation_node = node->nod_arg[e_cnstr_table];
		relation_name = (STR) relation_node->nod_arg[e_rln_name];
		assert(relation_name->str_length <= MAX_USHORT);
		request->append_string(	gds_dyn_rel_name,
								relation_name->str_data,
								relation_name->str_length);
	}
	else
	{
		return;
	}

	constant = node->nod_arg[e_cnstr_position];
	if (constant)
	{
		request->append_number(gds_dyn_trg_sequence,
				   (SSHORT)(SLONG) (constant ? constant->nod_arg[0] : 0));
	}

	constant = node->nod_arg[e_cnstr_type];
	if (constant)
	{
		trig_type = (USHORT)(ULONG) constant->nod_arg[0];
		request->append_number(gds_dyn_trg_type, trig_type);
	}
	else
	{
		/* If we don't have a trigger type assigned, then this is just a template
		   definition for use with domains.  The real triggers are defined when
		   the domain is used. */
		trig_type = 0;
	}

	request->append_uchar(gds_dyn_sql_object);

	STR message = (STR) node->nod_arg[e_cnstr_message];
	if (message)
	{
		request->append_number(gds_dyn_def_trigger_msg, 0);
		assert(message->str_length <= MAX_USHORT);
		request->append_string(	gds_dyn_trg_msg,
								message->str_data,
								message->str_length);
		request->append_uchar(gds_dyn_end);
	}

	// generate the trigger blr

	if (node->nod_arg[e_cnstr_condition] && node->nod_arg[e_cnstr_actions])
	{
		request->begin_blr(gds_dyn_trg_blr);
		request->append_uchar(blr_begin);

		/* create the "OLD" and "NEW" contexts for the trigger --
		   the new one could be a dummy place holder to avoid resolving
		   fields to that context but prevent relations referenced in
		   the trigger actions from referencing the predefined "1" context */

		if (request->req_context_number) {
			/* If an alias is specified for the single base table involved,
			   save and then add the context                               */

			stack = request->req_context;
			context = (DSQL_CTX) stack->lls_object;
			if (context->ctx_alias) {
				sav_context = FB_NEW(*tdsql->tsql_default) dsql_ctx;
				*sav_context = *context;
			}
		}
		reset_context_stack(request);
		temp = relation_node->nod_arg[e_rln_alias];
		relation_node->nod_arg[e_rln_alias] = (DSQL_NOD) MAKE_cstring(OLD_CONTEXT);
		PASS1_make_context(request, relation_node);
		relation_node->nod_arg[e_rln_alias] = (DSQL_NOD) MAKE_cstring(NEW_CONTEXT);
		PASS1_make_context(request, relation_node);
		relation_node->nod_arg[e_rln_alias] = temp;

		if (sav_context) {
			sav_context->ctx_context = request->req_context_number++;
			context->ctx_scope_level = request->req_scope_level;
			LLS_PUSH(sav_context, &request->req_context);
		}

		if (trig_type == PRE_MODIFY_TRIGGER) {
			request->append_uchar(blr_for);
			temp = rse->nod_arg[e_rse_streams];
			temp->nod_arg[0] = PASS1_node(request, temp->nod_arg[0], 0);
			temp = rse->nod_arg[e_rse_boolean];
			rse->nod_arg[e_rse_boolean] = PASS1_node(request, temp, 0);
			GEN_expr(request, rse);

			condition = MAKE_node(nod_not, 1);
			condition->nod_arg[0] =
				replace_field_names(select_expr->nod_arg[e_sel_where], items,
									view_fields, FALSE);
			request->append_uchar(blr_begin);
			request->append_uchar(blr_if);
			GEN_expr(request, PASS1_node(request, condition->nod_arg[0], 0));
			request->append_uchar(blr_begin);
			request->append_uchar(blr_end);
		}

		if (trig_type == PRE_STORE_TRIGGER) {
			condition = MAKE_node(nod_not, 1);
			condition->nod_arg[0] =
				replace_field_names(select_expr->nod_arg[e_sel_where], items,
									view_fields, TRUE);
			request->append_uchar(blr_if);
			GEN_expr(request, PASS1_node(request, condition->nod_arg[0], 0));
			request->append_uchar(blr_begin);
			request->append_uchar(blr_end);
		}

		/* generate the action statements for the trigger */

		actions = node->nod_arg[e_cnstr_actions];
		for (ptr = actions->nod_arg, end = ptr + actions->nod_count;
			 ptr < end; ptr++)
		{
			GEN_statement(request, PASS1_statement(request, *ptr, 0));
		}

		// generate the action statements for the trigger

		actions = node->nod_arg[e_cnstr_else];
		if (actions)
		{
			request->append_uchar(blr_begin);
			for (ptr = actions->nod_arg, end = ptr + actions->nod_count;
				 ptr < end; ptr++)
			{
				action_node = PASS1_statement(request, *ptr, 0);
				if (action_node->nod_type == nod_modify)
				{
					temp_rse = action_node->nod_arg[e_mod_rse];
					temp_rse->nod_arg[e_rse_first] =
						MAKE_constant((STR) 1, 1);
				}
				GEN_statement(request, action_node);
			}
			request->append_uchar(blr_end);		/* of begin */
		}

		request->append_uchar(blr_end);			/* of if */
		if (trig_type == PRE_MODIFY_TRIGGER) {
			request->append_uchar(blr_end);		/* of for  */
		}
		request->end_blr();
	}
    request->append_number(isc_dyn_system_flag, frb_sysflag_view_check);
	request->append_uchar(gds_dyn_end);

/* the request type may have been set incorrectly when parsing
   the trigger actions, so reset it to reflect the fact that this
   is a data definition request; also reset the ddl node */

	request->req_type = REQ_DDL;
	request->req_ddl_node = ddl_node;
	reset_context_stack(request);
}


static void delete_procedure (DSQL_REQ     request,
                              DSQL_NOD     node,
                              BOOLEAN silent_deletion)
{
/**************************************
 *
 *  d e l e t e _ p r o c e d u r e
 *
 **************************************
 *
 * Function
 *  Do nothing and don't throw error if the procedure doesn't exist
 *  and silent_deletion is true.
 *  CVC: Created this function to not clutter generate_dyn().
 *
 **************************************/
    STR string = (STR) node->nod_arg [e_prc_name];
    assert (string);
    if (node->nod_type == nod_redef_procedure || silent_deletion) {
        DSQL_PRC procedure = METD_get_procedure (request, string);
        if (!procedure) {
                return;
        }
    }
    request->append_cstring(gds_dyn_delete_procedure, string->str_data);
    request->append_uchar(gds_dyn_end);

}


static void delete_relation_view (
    DSQL_REQ     request,
    DSQL_NOD     node,
    BOOLEAN silent_deletion)
{
/**************************************
 *
 *  d e l e t e _ r e l a t i o n _ v i e w
 *
 **************************************
 *
 * Function
 *  Check that DROP TABLE is dropping a table and that
 *  DROP VIEW is dropping a view.
 *  Do nothing and don't throw error if the table or view doesn't exist
 *  and silent_deletion is true.
 *  CVC: Created this function to not clutter generate_dyn().
 *
 **************************************/

    STR string = 0;
    DSQL_REL relation = 0;

    if (node->nod_type == nod_redef_relation) {
        DSQL_NOD relation_node = node->nod_arg [e_alt_name];
        assert (relation_node);
        string = (STR) relation_node->nod_arg [e_rln_name];
    }
    else {
        string = (STR) node->nod_arg [e_alt_name];
    }

    assert (string);

    relation = METD_get_relation (request, string);

    if (node->nod_type == nod_del_relation || 
        node->nod_type == nod_redef_relation) {
        if (!relation && !silent_deletion || 
            relation && (relation->rel_flags & REL_view)) {
            ERRD_post (gds_sqlerr, gds_arg_number, (SLONG) -607,
                       /* gds_arg_gds, gds__dsql_command_err,
                          gds_arg_gds, gds__dsql_table_not_found, */
                       gds_arg_gds, 336068783L,
                       gds_arg_string, string->str_data,
                       gds_arg_end);
        }
    }
    else { /* node->nod_type == nod_del_view, nod_redef_view */
        if (!relation && !silent_deletion ||
			relation && !(relation->rel_flags & REL_view)) {
            ERRD_post (gds_sqlerr, gds_arg_number, (SLONG) -607,
                       /* gds_arg_gds, gds__dsql_command_err,
                          gds_arg_gds, gds__dsql_view_not_found, */
                       gds_arg_gds, 336068783L,
                       gds_arg_string, string->str_data,
                       gds_arg_end);
        }
    }
    if (relation) {
        request->append_cstring(gds_dyn_delete_rel, string->str_data);
        request->append_uchar(gds_dyn_end);
    }
}



//
//	Complete the stuffing of a piece of
//	blr by going back and inserting the length.
//
void dsql_req::end_blr()
{
	append_uchar(blr_eoc);

	// go back and stuff in the proper length

	char* blr_base = req_blr_string->str_data + req_base_offset;
	ULONG length   = (ULONG) (reinterpret_cast<char*>(req_blr) - blr_base) - 2;

	if (length > MAX_USHORT) {
		// TODO : need appropriate error message, like "too long BLR"
		ERRD_post(isc_invalid_blr, isc_arg_number, (SLONG) length,
				  0);
	}

	*blr_base++ = (UCHAR) length;
	*blr_base = (UCHAR) (length >> 8);
}


static void foreign_key( DSQL_REQ request, DSQL_NOD element, const char* index_name)
{
/* *************************************
 *
 *	f o r e i g n _ k e y
 *
 **************************************
 *
 * Function
 *	Generate ddl to create a foreign key
 *	constraint.
 *
 **************************************/
	DSQL_NOD relation2_node;
	DSQL_NOD columns1, columns2;
	STR relation2;

	columns1 = element->nod_arg[e_for_columns];

	relation2_node = element->nod_arg[e_for_reftable];
	relation2 = (STR) relation2_node->nod_arg[e_rln_name];

/* If there is a referenced table name but no referenced field names, the
   primary key of the referenced table designates the referenced fields. */
	if (!(columns2 = element->nod_arg[e_for_refcolumns]))
	{
		element->nod_arg[e_for_refcolumns] =
			columns2 = METD_get_primary_key(request, relation2);

		/* If there is NEITHER an explicitly referenced field name, NOR does
		   the referenced table have a primary key to serve as the implicitly
		   referenced field, fail. */
		if (!columns2)
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
					  gds_arg_gds, gds_dsql_command_err,
					  gds_arg_gds, gds_reftable_requires_pk,
					  /* "REFERENCES table" without "(column)" requires PRIMARY
					     KEY on referenced table */
					  0);
		}
	}

	if (columns2 && (columns1->nod_count != columns2->nod_count))
	{
		ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
				  gds_arg_gds, gds_dsql_command_err,
				  gds_arg_gds, gds_key_field_count_err,
				  /* foreign key field count does not match primary key */
				  0);
	}

/* define the foreign key index and the triggers that may be needed
   for referential integrity action. */

	make_index_trg_ref_int(request, element, columns1,
						   element->nod_arg[e_for_refcolumns],
						   relation2->str_data,
						   index_name);
}


static void generate_dyn( DSQL_REQ request, DSQL_NOD node)
{
/**************************************
 *
 *	g e n e r a t e _ d y n
 *
 **************************************
 *
 * Functional description
 *	Switch off the type of node to generate a
 *	DYN string.
 *
 **************************************/
	STR string;

	request->req_ddl_node = node;

	switch (node->nod_type) {
	case nod_def_domain:
		define_domain(request);
		break;

	case nod_mod_domain:
		modify_domain(request);
		break;

	case nod_def_index:
		define_index(request);
		break;

	case nod_def_relation:
		define_relation(request);
		break;

    case nod_redef_relation:
		STUFF (gds_dyn_begin);
		delete_relation_view (request, node, TRUE); /* silent. */
		define_relation (request);
		STUFF (gds_dyn_end);
		break;

	case nod_def_view:
	case nod_mod_view:
	case nod_replace_view:
		define_view(request, node->nod_type);
		break;

    case nod_redef_view:
		STUFF(gds_dyn_begin);
		delete_relation_view(request, node, TRUE); /* silent. */
		define_view(request, node->nod_type);
		STUFF(gds_dyn_end);
		break;

	case nod_def_exception:
	case nod_mod_exception:
	case nod_del_exception:
		define_exception(request, node->nod_type);
		break;

	case nod_def_procedure:
	case nod_mod_procedure:
	case nod_replace_procedure:
		define_procedure(request, node->nod_type);
		break;

    case nod_redef_procedure:
        STUFF (gds_dyn_begin);
        delete_procedure (request, node, TRUE); /* silent. */
        define_procedure (request, node->nod_type);
        STUFF (gds_dyn_end);
        break;


	case nod_def_constraint:
		define_constraint_trigger(request, node);
		break;

	case nod_def_trigger:
	case nod_mod_trigger:
	case nod_replace_trigger:
		define_trigger(request, node);
		break;

	case nod_mod_relation:
		modify_relation(request);
		break;

	case nod_del_domain:
		string = (STR) node->nod_arg[0];
		request->append_cstring(gds_dyn_delete_global_fld, string->str_data);
		request->append_uchar(gds_dyn_end);
		break;

	case nod_del_index:
		string = (STR) node->nod_arg[0];
		request->append_cstring(gds_dyn_delete_idx, string->str_data);
		request->append_uchar(gds_dyn_end);
		break;

    /* CVC: Handling drop table and drop view properly. */
    case nod_del_relation:
    case nod_del_view:
        delete_relation_view (request, node, FALSE); /* no silent. */
        break;

	case nod_del_procedure:
        delete_procedure(request, node, FALSE); /* no silent. */        
		break;

	case nod_del_trigger:
		string = (STR) node->nod_arg[0];
		request->append_cstring(gds_dyn_delete_trigger, string->str_data);
		request->append_uchar(gds_dyn_end);
		break;

	case nod_del_role:
		string = (STR) node->nod_arg[0];
		request->append_cstring(isc_dyn_del_sql_role, string->str_data);
		request->append_uchar(gds_dyn_end);
		break;

	case nod_grant:
	case nod_revoke:
		grant_revoke(request);
		break;

	case nod_def_generator:
		define_generator(request);
		break;

	case nod_def_role:
		define_role(request);
		break;

	case nod_def_filter:
		define_filter(request);
		break;

	case nod_del_generator:
		string = (STR) node->nod_arg[0];
        request->append_cstring(gds_dyn_delete_generator, string->str_data);
        request->append_uchar(gds_dyn_end);
		break;

	case nod_del_filter:
		string = (STR) node->nod_arg[0];
		request->append_cstring(gds_dyn_delete_filter, string->str_data);
		request->append_uchar(gds_dyn_end);
		break;

	case nod_def_udf:
		define_udf(request);
		break;

	case nod_del_udf:
		string = (STR) node->nod_arg[0];
		request->append_cstring(gds_dyn_delete_function, string->str_data);
		request->append_uchar(gds_dyn_end);
		break;

	case nod_def_shadow:
		define_shadow(request);
		break;

	case nod_del_shadow:
		request->append_number(gds_dyn_delete_shadow,
				   (SSHORT)(SLONG) (node->nod_arg[0]));
		request->append_uchar(gds_dyn_end);
		break;

	case nod_mod_database:
		modify_database(request);
		break;

	case nod_def_database:
		define_database(request);
		break;

	case nod_mod_index:
		modify_index(request);
		break;

	case nod_set_statistics:
		set_statistics(request);
		break;

	default:
		break;
	}
}


static void grant_revoke( DSQL_REQ request)
{
/**************************************
 *
 *	g r a n t _ r e v o k e
 *
 **************************************
 *
 * Functional description
 *	Build DYN string for GRANT/REVOKE statements
 *
 **************************************/

	DSQL_NOD* uptr;
	DSQL_NOD* uend;
	bool process_grant_role = false;

	SSHORT option = FALSE;
	DSQL_NOD    ddl_node = request->req_ddl_node;
	DSQL_NOD    privs    = ddl_node->nod_arg[e_grant_privs];

	if (privs->nod_arg[0] != NULL) {
		if (privs->nod_arg[0]->nod_type == nod_role_name) {
			process_grant_role = true;
		}
	}

	if (!process_grant_role)
	{
		DSQL_NOD table = ddl_node->nod_arg[e_grant_table];
		DSQL_NOD users = ddl_node->nod_arg[e_grant_users];
		if (ddl_node->nod_arg[e_grant_grant]) {
			option = TRUE;
		}

		request->append_uchar(gds_dyn_begin);

		uend = users->nod_arg + users->nod_count;
		for (uptr = users->nod_arg; uptr < uend; ++uptr)
		{
			modify_privileges(	request,
								ddl_node->nod_type,
								option,
								privs,
								table,
								*uptr);
		}
	}
	else
	{
		DSQL_NOD role_list = ddl_node->nod_arg[0];
		DSQL_NOD users = ddl_node->nod_arg[1];
		if (ddl_node->nod_arg[3]) {
			option = 2;
		}
		request->append_uchar(isc_dyn_begin);

		DSQL_NOD* role_end = role_list->nod_arg + role_list->nod_count;
		for (DSQL_NOD* role_ptr = role_list->nod_arg; role_ptr < role_end; ++role_ptr)
		{
			uend = users->nod_arg + users->nod_count;
			for (uptr = users->nod_arg; uptr < uend; ++uptr)
			{
				process_role_nm_list(	request,
										option,
										*uptr,
										*role_ptr,
										ddl_node->nod_type);
			}
		}
	}

	request->append_uchar(gds_dyn_end);
}


static void make_index(	DSQL_REQ    request,
						DSQL_NOD    element,
						DSQL_NOD    columns,
						DSQL_NOD    referenced_columns,
						const char* relation_name,
						const char* index_name)
{
/* *************************************
 *
 *	m a k e _ i n d e x
 *
 **************************************
 *
 * Function
 *	Generate ddl to create an index for a unique
 *	or primary key constraint.
 *      This is not called for a foreign key constraint.
 *      The func. make_index_trf_ref_int handles foreign key constraint
 *
 **************************************/

	/* stuff either user-defined name or
	   zero-length name, indicating that an index name
	   should be generated */

	assert(element->nod_type != nod_foreign);

	DSQL_NOD index = element->nod_arg[e_pri_index];
	assert(index);

	STR string = (STR) index->nod_arg[e_idx_name];
	if (string)
	{
		index_name = string->str_data;
	}

	if (element->nod_type == nod_primary)
	{
		request->append_cstring(gds_dyn_def_primary_key, index_name);
	}
	else if (element->nod_type == nod_unique)
	{
		request->append_cstring(gds_dyn_def_unique, index_name);
	}

	request->append_number(gds_dyn_idx_unique, 1);

	if (index->nod_arg[e_idx_asc_dsc])
	{
		request->append_number(gds_dyn_idx_type, 1);
	}

	const DSQL_NOD* end = columns->nod_arg + columns->nod_count;
	for (DSQL_NOD* ptr = columns->nod_arg; ptr < end; ++ptr)
	{
		STR field_name = (STR) (*ptr)->nod_arg[1];
		request->append_cstring(gds_dyn_fld_name,
					field_name->str_data);
	}

	request->append_uchar(gds_dyn_end);
}


static void make_index_trg_ref_int(	DSQL_REQ    request,
									DSQL_NOD    element,
									DSQL_NOD    columns,
									DSQL_NOD    referenced_columns,
									const char*	relation_name,
									const char* index_name)
{
/******************************************************
 *
 *	m a k e _ i n d e x _ t r g _ r e f _ i n t
 *
 ******************************************************
 *
 * Function
 *      This is called only when the element->nod_type == nod_foreign_key
 *
 *     o Generate ddl to create an index for a unique
 *       or primary key constraint.
 *     o Also make an index for the foreign key constraint
 *     o in the caase of foreign key, also generate an appropriate trigger for
 *       cascading referential integrity.
 *
 *
 *****************************************************/

	assert(element->nod_type == nod_foreign)

	/* for_rel_name_str is the name of the relation
	   on which the ddl operation is being done,
	   in this case the foreign key table  */

	DSQL_NOD ddl_node         = request->req_ddl_node;
	DSQL_NOD for_rel_node     = ddl_node->nod_arg[e_drl_name];
	STR      for_rel_name_str = (STR) for_rel_node->nod_arg[e_rln_name];

	/* stuff either user-defined name or
	   zero-length name, indicating that an index name
	   should be generated */

	DSQL_NOD index = element->nod_arg[e_for_index];
	assert(index);

	STR string = (STR) index->nod_arg[e_idx_name];
	if (string)
	{
		index_name = string->str_data;
	}

	request->append_cstring(gds_dyn_def_foreign_key, index_name);

	if (index->nod_arg[e_idx_asc_dsc])
	{
		request->append_number(gds_dyn_idx_type, 1);
	}

	if (element->nod_arg[e_for_action])
	{
		DSQL_NOD nod_for_action = element->nod_arg[e_for_action];
		assert(nod_for_action->nod_type == nod_ref_upd_del);

		DSQL_NOD nod_ref_upd_action = nod_for_action->nod_arg[e_ref_upd];
		if (nod_ref_upd_action)
		{
			assert(nod_ref_upd_action->nod_type == nod_ref_trig_action);

			request->append_uchar(gds_dyn_foreign_key_update);
			switch (nod_ref_upd_action->nod_flags)
			{
			case REF_ACTION_CASCADE:
				request->append_uchar(gds_dyn_foreign_key_cascade);
				define_upd_cascade_trg(request, element, columns,
									   referenced_columns, relation_name,
									   for_rel_name_str->str_data);
				break;
			case REF_ACTION_SET_DEFAULT:
				request->append_uchar(gds_dyn_foreign_key_default);
				define_set_default_trg(request, element, columns,
									   referenced_columns, relation_name,
									   for_rel_name_str->str_data,
									   true);
				break;
			case REF_ACTION_SET_NULL:
				request->append_uchar(gds_dyn_foreign_key_null);
				define_set_null_trg(request, element, columns,
									referenced_columns, relation_name,
									for_rel_name_str->str_data,
									true);
				break;
			case REF_ACTION_NONE:
				request->append_uchar(gds_dyn_foreign_key_none);
				break;
			default:
				assert(0);
				request->append_uchar(gds_dyn_foreign_key_none);	/* just in case */
				break;
			}
		}

		DSQL_NOD nod_ref_del_action = nod_for_action->nod_arg[e_ref_del];
		if (nod_ref_del_action)
		{
			assert(nod_ref_del_action->nod_type == nod_ref_trig_action);

			request->append_uchar(gds_dyn_foreign_key_delete);
			switch (nod_ref_del_action->nod_flags) {
			case REF_ACTION_CASCADE:
				request->append_uchar(gds_dyn_foreign_key_cascade);
				define_del_cascade_trg(request, element, columns,
									   referenced_columns, relation_name,
									   for_rel_name_str->str_data);
				break;
			case REF_ACTION_SET_DEFAULT:
				request->append_uchar(gds_dyn_foreign_key_default);
				define_set_default_trg(request, element, columns,
									   referenced_columns, relation_name,
									   for_rel_name_str->str_data,
									   false);
				break;
			case REF_ACTION_SET_NULL:
				request->append_uchar(gds_dyn_foreign_key_null);
				define_set_null_trg(request, element, columns,
									referenced_columns, relation_name,
									for_rel_name_str->str_data,
									false);
				break;
			case REF_ACTION_NONE:
				request->append_uchar(gds_dyn_foreign_key_none);
				break;
			default:
				assert(0);
				request->append_uchar(gds_dyn_foreign_key_none);	/* just in case */
				break;
				/* Error */
			}
		}
	}


	DSQL_NOD* ptr;
	DSQL_NOD* end = columns->nod_arg + columns->nod_count;
	for (ptr = columns->nod_arg; ptr < end; ++ptr)
	{
		STR field_name = (STR) (*ptr)->nod_arg[1];
		request->append_cstring(gds_dyn_fld_name,
					field_name->str_data);
	}

	request->append_cstring(gds_dyn_idx_foreign_key, relation_name);
	if (referenced_columns)
	{
		end = referenced_columns->nod_arg + referenced_columns->nod_count;
		for (ptr = referenced_columns->nod_arg; ptr < end; ++ptr)
		{
			STR field_name = (STR) (*ptr)->nod_arg[1];
			request->append_cstring(gds_dyn_idx_ref_column,
						field_name->str_data);
		}
	}

	request->append_uchar(gds_dyn_end);
}


static void modify_database( DSQL_REQ request)
{
/**************************************
 *
 *	m o d i f y _ d a t a b a s e
 *
 **************************************
 *
 * Function
 *	Modify a database.
 *
 **************************************/
	DSQL_NOD ddl_node, element, *ptr;
	SLONG start = 0;
	FIL file;
	SSHORT number = 0;
	SLONG temp_long;
	SSHORT temp_short;
	SSHORT drop_log = FALSE;
	SSHORT drop_cache = FALSE;

	ddl_node = request->req_ddl_node;

	request->append_uchar(gds_dyn_mod_database);
/*
request->append_number(gds_dyn_rel_sql_protection, 1);
*/
	DSQL_NOD  elements = ddl_node->nod_arg[e_adb_all];
	DSQL_NOD* end      = elements->nod_arg + elements->nod_count;
	for (ptr = elements->nod_arg; ptr < end; ptr++)
	{
		element = *ptr;
		switch (element->nod_type) {
		case nod_drop_log:
			drop_log = TRUE;
			break;
		case nod_drop_cache:
			drop_cache = TRUE;
			break;

		default:
			break;
		}
	}

	if (drop_log) {
		request->append_uchar(gds_dyn_drop_log);
	}
	if (drop_cache) {
		request->append_uchar(gds_dyn_drop_cache);
	}

	elements = ddl_node->nod_arg[e_adb_all];
	end = elements->nod_arg + elements->nod_count;
	for (ptr = elements->nod_arg; ptr < end; ptr++)
	{
		element = *ptr;

		switch (element->nod_type) {
		case nod_file_desc:
			file = (FIL) element->nod_arg[0];
			request->append_cstring(gds_dyn_def_file,
						file->fil_name->str_data);

			start = MAX(start, file->fil_start);
			request->append_file_start(start);

			request->append_file_length(file->fil_length);
			request->append_uchar(gds_dyn_end);
			start += file->fil_length;
			break;

		case nod_log_file_desc:
			file = (FIL) element->nod_arg[0];

			if (file->fil_flags & LOG_default) {
				request->append_uchar(gds_dyn_def_default_log);
				break;
			}
			request->append_cstring(gds_dyn_def_log_file,
						file->fil_name->str_data);
			request->append_file_length(file->fil_length);
			request->append_uchar(gds_dyn_log_file_sequence);
			request->append_ushort_with_length(number);
			number++;
			request->append_uchar(gds_dyn_log_file_partitions);
			request->append_ushort_with_length(file->fil_partitions);
			if (file->fil_flags & LOG_serial) {
				request->append_uchar(gds_dyn_log_file_serial);
			}
			if (file->fil_flags & LOG_overflow) {
				request->append_uchar(gds_dyn_log_file_overflow);
			}
			if (file->fil_flags & LOG_raw) {
				request->append_uchar(gds_dyn_log_file_raw);
			}
			request->append_uchar(gds_dyn_end);
			break;

		case nod_cache_file_desc:
			file = (FIL) element->nod_arg[0];
			request->append_cstring(gds_dyn_def_cache_file,
						file->fil_name->str_data);
			request->append_file_length(file->fil_length);
			request->append_uchar(gds_dyn_end);
			break;

		case nod_group_commit_wait:
			request->append_uchar(gds_dyn_log_group_commit_wait);
			temp_long = (SLONG) (element->nod_arg[0]);
			request->append_ulong_with_length(temp_long);
			break;

		case nod_check_point_len:
			request->append_uchar(gds_dyn_log_check_point_length);
			temp_long = (SLONG) (element->nod_arg[0]);
			request->append_ulong_with_length(temp_long);
			break;

		case nod_num_log_buffers:
			request->append_uchar(gds_dyn_log_num_of_buffers);
			temp_short = (SSHORT)(SLONG) (element->nod_arg[0]);
			request->append_ushort_with_length(temp_short);
			break;

		case nod_log_buffer_size:
			request->append_uchar(gds_dyn_log_buffer_size);
			temp_short = (SSHORT)(SLONG) (element->nod_arg[0]);
			request->append_ushort_with_length(temp_short);
			break;
		case nod_drop_log:
		case nod_drop_cache:
			break;

		default:
			break;
		}
	}

	request->append_uchar(gds_dyn_end);
}


static void modify_domain( DSQL_REQ request)
{
/**************************************
 *
 *	m o d i f y _ d o m a i n
 *
 **************************************
 *
 * Function
 *	Alter an SQL domain.
 *
 **************************************/
	DSQL_NOD ddl_node, domain_node, ops, element, *ptr, *end;
	STR string, domain_name;
	DSQL_FLD field;
	dsql_fld local_field;
    /* CVC: This array used with check_one_call to ensure each modification 
       option is called only once. Enlarge it if the switch() below gets more 
       cases. */
    BOOLEAN repetition_count [6];



	ddl_node = request->req_ddl_node;

	domain_node = ddl_node->nod_arg[e_alt_dom_name];
	domain_name = (STR) domain_node->nod_arg[e_fln_name];

	request->append_cstring(gds_dyn_mod_global_fld,
				domain_name->str_data);



    /* Is MOVE_CLEAR enough for all platforms?
    MOVE_CLEAR (repetition_count, sizeof (repetition_count)); */
    USHORT rtop = FB_NELEM(repetition_count);
    BOOLEAN *p = repetition_count;
    while (p < repetition_count + rtop) {
        *p++ = 0;
    }


	ops = ddl_node->nod_arg[e_alt_dom_ops];
	for (ptr = ops->nod_arg, end = ptr + ops->nod_count; ptr < end; ptr++)
	{
		element = *ptr;
		switch (element->nod_type)
		{
		case nod_def_default:
            check_one_call (repetition_count, 0, "DOMAIN DEFAULT");
			/* CVC: So do you want to crash me with ALTER DOMAIN dom SET; ??? */
			if (!element->nod_arg[e_dft_default]) 
            {
				ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -104,
							gds_arg_gds, gds_command_end_err,    /* Unexpected end of command */
							0);
			}
			/* CVC End modification. */
			element->nod_arg[e_dft_default] =
				PASS1_node(request, element->nod_arg[e_dft_default], 0);

			request->begin_blr(gds_dyn_fld_default_value);
			GEN_expr(request, element->nod_arg[e_dft_default]);
			request->end_blr();

			string = (STR) element->nod_arg[e_dft_default_source];
			if (string)
			{
				assert(string->str_length <= MAX_USHORT);
				request->append_string(	gds_dyn_fld_default_source,
										string->str_data,
										(USHORT) string->str_length);
			}
			break;

		case nod_def_constraint:
            check_one_call (repetition_count, 1, "DOMAIN CONSTRAINT");
			request->append_uchar(gds_dyn_single_validation);
			request->begin_blr(gds_dyn_fld_validation_blr);

			/* Get the attributes of the domain, and set any occurances of
			   nod_dom_value (corresponding to the keyword VALUE) to the
			   correct type, length, scale, etc. */
			if (!METD_get_domain(request, &local_field, domain_name->str_data))
			{
					ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
							  gds_arg_gds, gds_dsql_command_err,
							  gds_arg_gds, gds_dsql_domain_not_found,
                              gds_arg_string, domain_name->str_data,
							  /* Specified domain or source field does not exist */
							  0);
			}
			if (element->nod_arg[e_cnstr_condition])
			{
				set_nod_value_attributes(element->nod_arg[e_cnstr_condition],
										 &local_field);
			}

			/* Increment the context level for this request, so that
			   the context number for any RSE generated for a SELECT
			   within the CHECK clause will be greater than 0.  In the
			   environment of a domain check constraint, context
			   number 0 is reserved for the "blr_fid, 0, 0,0," which
			   is emitted for a nod_dom_value, corresponding to an
			   occurance of the VALUE keyword in the body of the check
			   constraint.  -- chrisj 1999-08-20 */

			request->req_context_number++;

			GEN_expr(request,
					 PASS1_node(request, element->nod_arg[e_cnstr_condition],
								0));

			request->end_blr();
			if ((string = (STR) element->nod_arg[e_cnstr_source]) != NULL) {
				assert(string->str_length <= MAX_USHORT);
				request->append_string(	gds_dyn_fld_validation_source,
										string->str_data,
										string->str_length);
			}
			break;

		case nod_mod_domain_type:
			field = (DSQL_FLD) element->nod_arg[e_mod_dom_new_dom_type];
			DDL_resolve_intl_type(request, field, NULL);
			put_field(request, field, FALSE);
			break;

		case nod_field_name:
			{
                check_one_call(repetition_count, 3, "DOMAIN NAME");

				STR new_dom_name;

				new_dom_name = (STR) element->nod_arg[e_fln_name];
				request->append_cstring(gds_dyn_fld_name,
							new_dom_name->str_data);
				break;
			}

		case nod_delete_rel_constraint:
            check_one_call (repetition_count, 4, "DOMAIN DROP CONSTRAINT");
			request->append_uchar(gds_dyn_del_validation);
			break;

		case nod_del_default:
            check_one_call (repetition_count, 5, "DOMAIN DROP DEFAULT");         			request->append_uchar(gds_dyn_del_default);
			break;

		default:
			break;
		}
	}

	request->append_uchar(gds_dyn_end);
}


static void modify_index( DSQL_REQ request)
{
/**************************************
 *
 *	m o d i f y _ i n d e x
 *
 **************************************
 *
 * Function
 *	Alter an index (only active or inactive for now)
 *
 **************************************/
	DSQL_NOD ddl_node, index_node;
	STR index_name;

	ddl_node = request->req_ddl_node;

	index_node = ddl_node->nod_arg[e_alt_index];
	index_name = (STR) index_node->nod_arg[e_alt_idx_name];

	request->append_cstring(gds_dyn_mod_idx, index_name->str_data);

	if (index_node->nod_type == nod_idx_active) {
		request->append_number(gds_dyn_idx_inactive, 0);
	} else if (index_node->nod_type == nod_idx_inactive) {
		request->append_number(gds_dyn_idx_inactive, 1);
	}

	request->append_uchar(gds_dyn_end);
}


static void modify_privilege(	DSQL_REQ			request,
								NOD_TYPE	type,
								SSHORT		option,
								UCHAR*		privs,
								DSQL_NOD			table,
								DSQL_NOD			user,
								STR			field_name)
{
/**************************************
 *
 *	m o d i f y _ p r i v i l e g e
 *
 **************************************
 *
 * Functional description
 *	Stuff a single grant/revoke verb and all its options.
 *
 **************************************/

	if (type == nod_grant) {
		request->append_uchar(gds_dyn_grant);
	} else {
		request->append_uchar(gds_dyn_revoke);
	}

/* stuff the privileges string */

	SSHORT priv_count = 0;
	request->append_ushort(0);
	for (; *privs; privs++) {
		priv_count++;
		request->append_uchar(*privs);
	}

	UCHAR* dynsave = request->req_blr;
	for (SSHORT i = priv_count + 2; i; i--) {
		--dynsave;
	}

	*dynsave++ = (UCHAR) priv_count;
	*dynsave = (UCHAR) (priv_count >> 8);

	STR name = (STR) table->nod_arg[0];
	if (table->nod_type == nod_procedure_name) {
		request->append_cstring(gds_dyn_prc_name, name->str_data);
	} else {
		request->append_cstring(gds_dyn_rel_name, name->str_data);
	}
	name = (STR) user->nod_arg[0];
	switch (user->nod_type) {
	case nod_user_group:		/* GRANT priv ON tbl TO GROUP unix_group */
		request->append_cstring(isc_dyn_grant_user_group, name->str_data);
		break;

	case nod_user_name:
        if (user->nod_count == 2) {
           request->append_cstring(isc_dyn_grant_user_explicit, name->str_data);
        }
        else {
            request->append_cstring(gds_dyn_grant_user, name->str_data);
        }
		break;

	case nod_proc_obj:
		request->append_cstring(gds_dyn_grant_proc, name->str_data);
		break;

	case nod_trig_obj:
		request->append_cstring(gds_dyn_grant_trig, name->str_data);
		break;

	case nod_view_obj:
		request->append_cstring(gds_dyn_grant_view, name->str_data);
		break;

    case nod_role_name:
        request->append_cstring(isc_dyn_grant_role, name->str_data);
        break;

	default:
        /* CVC: Here we should complain: DYN doesn't check parameters
           and it will write trash in rdb$user_privileges. We probably
           should complain in most cases when "name" is blank, too. */        
		break;
	}

	if (field_name) {
		request->append_cstring(gds_dyn_fld_name, field_name->str_data);
	}

	if ((option) &&
		((type == nod_grant) ||
		(!(request->req_dbb->dbb_flags & DBB_v3))))
	{
		request->append_number(gds_dyn_grant_options, option);
	}

	request->append_uchar(gds_dyn_end);
}



static SCHAR modify_privileges(DSQL_REQ		request,
							   NOD_TYPE	type,
							   SSHORT	option,
							   DSQL_NOD		privs,
							   DSQL_NOD		table,
							   DSQL_NOD		user)
{
/**************************************
 *
 *	m o d i f y _ p r i v i l e g e s
 *
 **************************************
 *
 * Functional description
 *     Return a SCHAR indicating the privilege to be modified
 *
 **************************************/

	SCHAR  privileges[10];
	SCHAR* p;
	DSQL_NOD fields, *ptr, *end;

	switch (privs->nod_type) {
	case nod_all:
		p = "A";
		break;

	case nod_select:
		return 'S';

	case nod_execute:
		return 'X';

	case nod_insert:
		return 'I';

	case nod_references:
	case nod_update:
		p = (privs->nod_type == nod_references) ? (SCHAR*) "R" : (SCHAR*) "U";
		fields = privs->nod_arg[0];
		if (!fields) {
			return *p;
		}

		for (ptr = fields->nod_arg, end = ptr + fields->nod_count; ptr < end;
			 ptr++)
		{
			modify_privilege(request, type, option,
							 reinterpret_cast<UCHAR*>(p), table, user,
							 reinterpret_cast<STR>((*ptr)->nod_arg[1]));
		}
		return 0;

	case nod_delete:
		return 'D';

	case nod_list:
		p = privileges;
		for (ptr = privs->nod_arg, end = ptr + privs->nod_count; ptr < end;
			 ptr++)
		{
			*p = modify_privileges(request, type, option, *ptr, table,
								  user);
			if (*p) {
				p++;
			}
		}
		*p = 0;
		p = privileges;
		break;

	default:
		break;
	}

	if (*p) {
		modify_privilege(	request,
							type,
							option,
							reinterpret_cast<UCHAR*>(p),
							table,
							user,
							0);
	}

	return 0;
}


static void modify_relation( DSQL_REQ request)
{
/**************************************
 *
 *	m o d i f y _ r e l a t i o n
 *
 **************************************
 *
 * Function
 *	Alter an SQL table, relying on DYN to generate
 *	global fields for the local fields.
 *
 **************************************/
	DSQL_NOD ddl_node, ops, element, *ptr, *end, relation_node, field_node;
	STR relation_name, field_name;
	TSQL tdsql;


	tdsql = GET_THREAD_DATA;

	ddl_node = request->req_ddl_node;

	relation_node = ddl_node->nod_arg[e_alt_name];
	relation_name = (STR) relation_node->nod_arg[e_rln_name];

	request->append_cstring(gds_dyn_mod_rel, relation_name->str_data);
	save_relation(request, relation_name);

/* need to handle error that occur in generating dyn string.
 * If there is an error, get rid of the cached data
 */

	try {

	ops = ddl_node->nod_arg[e_alt_ops];
	for (ptr = ops->nod_arg, end = ptr + ops->nod_count; ptr < end; ptr++) {
		element = *ptr;
		switch (element->nod_type) {
		case nod_mod_field_name:
			{
				DSQL_NOD old_field, new_field;
				STR old_field_name, new_field_name;

				old_field = element->nod_arg[e_mod_fld_name_orig_name];
				old_field_name = (STR) old_field->nod_arg[e_fln_name];
				request->append_cstring(gds_dyn_mod_local_fld,
							old_field_name->str_data);

				new_field = element->nod_arg[e_mod_fld_name_new_name];
				new_field_name = (STR) new_field->nod_arg[e_fln_name];
				request->append_cstring(gds_dyn_rel_name,
							relation_name->str_data);
				request->append_cstring(isc_dyn_new_fld_name,
							new_field_name->str_data);
				request->append_uchar(gds_dyn_end);
				break;
			}

		case nod_mod_field_pos:
			{
				SSHORT constant;
				DSQL_NOD const_node;

				field_node = element->nod_arg[e_mod_fld_pos_orig_name];
				field_name = (STR) field_node->nod_arg[e_fln_name];
				request->append_cstring(gds_dyn_mod_local_fld, field_name->str_data);
				const_node = element->nod_arg[e_mod_fld_pos_new_position];

                /* CVC: Since now the parser accepts pos=1..N, let's subtract one here. */
                constant = (SSHORT)(SLONG) const_node->nod_arg [0] - 1;

				request->append_cstring(gds_dyn_rel_name,
							relation_name->str_data);
				request->append_number(gds_dyn_fld_position, constant);
				request->append_uchar(gds_dyn_end);
				break;
			}


		case nod_mod_field_type:
			modify_field(request, element, (SSHORT) - 1, relation_name);
			break;

		case nod_def_field:
			define_field(request, element, (SSHORT) - 1, relation_name);
			break;

		case nod_del_field:

			/* Fix for bug 8054:

			   [CASCADE | RESTRICT] syntax is available in IB4.5, but not
			   required until v5.0.

			   Option CASCADE causes an error :
			   unsupported DSQL construct

			   Option RESTRICT is default behaviour.
			 */

			field_node = element->nod_arg[0];
			field_name = (STR) field_node->nod_arg[e_fln_name];

			if ((element->nod_arg[1])->nod_type == nod_cascade)
			{
				/* Unsupported DSQL construct */
				ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -901,
						  gds_arg_gds, gds_dsql_command_err,
						  gds_arg_gds, gds_dsql_construct_err, 0);
			}

			assert((element->nod_arg[1])->nod_type == nod_restrict);
			request->append_cstring(gds_dyn_delete_local_fld, field_name->str_data);
			request->append_uchar(gds_dyn_end);
			break;

		case nod_delete_rel_constraint:
			field_name = (STR) element->nod_arg[0];
			request->append_cstring(gds_dyn_delete_rel_constraint, field_name->str_data);
			break;

		case nod_rel_constraint:
			define_rel_constraint(request, element);
			break;

		default:
			break;
		}
	}

	request->append_uchar(gds_dyn_end);

	}	// try
	catch (const std::exception&)
	{
		METD_drop_relation(request, relation_name);
		request->req_relation = 0;
		Firebird::status_exception::raise(tdsql->tsql_status[1]);
	}
}


static void process_role_nm_list(	DSQL_REQ			request,
									SSHORT		option,
									DSQL_NOD			user_ptr,
									DSQL_NOD			role_ptr,
									NOD_TYPE	type)
{
/**************************************
 *
 *  p r o c e s s _ r o l e _ n m _ l i s t
 *
 **************************************
 *
 * Functional description
 *     Build req_blr for grant & revoke role stmt
 *
 **************************************/
	STR role_nm, user_nm;

	if (type == nod_grant) {
		request->append_uchar(isc_dyn_grant);
	} else {
		request->append_uchar(isc_dyn_revoke);
	}

	request->append_ushort(1);
	request->append_uchar('M');

	role_nm = (STR) role_ptr->nod_arg[0];
	request->append_cstring(isc_dyn_sql_role_name, role_nm->str_data);

	user_nm = (STR) user_ptr->nod_arg[0];
	request->append_cstring(isc_dyn_grant_user, user_nm->str_data);

	if (option) {
		request->append_number(isc_dyn_grant_admin_options, option);
	}

	request->append_uchar(gds_dyn_end);
}


static void put_descriptor(DSQL_REQ request, DSC * desc)
{
/**************************************
 *
 *	p u t _ d e s c r i p t o r
 *
 **************************************
 *
 * Function
 *	Write out field description in ddl, given the
 *	input descriptor.
 *
 **************************************/

	request->append_number(gds_dyn_fld_type, blr_dtypes[desc->dsc_dtype]);
	if (desc->dsc_dtype == dtype_varying) {
		request->append_number(gds_dyn_fld_length,
				   (SSHORT) (desc->dsc_length - sizeof(USHORT)));
	} else {
		request->append_number(gds_dyn_fld_length, desc->dsc_length);
	}
	if (desc->dsc_dtype <= dtype_any_text) {
		request->append_number(gds_dyn_fld_character_set, DSC_GET_CHARSET(desc));
		request->append_number(gds_dyn_fld_collation, DSC_GET_COLLATE(desc));
	}
	else if (desc->dsc_dtype == dtype_blob) {
		request->append_number(gds_dyn_fld_sub_type, desc->dsc_sub_type);
		if (desc->dsc_sub_type == isc_blob_text) {
			request->append_number(gds_dyn_fld_character_set, desc->dsc_scale);
		}
	}
	else {
		request->append_number(gds_dyn_fld_sub_type, desc->dsc_sub_type);
		request->append_number(gds_dyn_fld_scale, desc->dsc_scale);
	}
}


//
// Write out field data type
// Taking special care to declare international text.
//
static void put_dtype(DSQL_REQ request, const dsql_fld* field, USHORT use_subtype)
{

#ifdef DEV_BUILD
/* Check if the field describes a known datatype */

	if (field->fld_dtype > FB_NELEM(blr_dtypes) ||
		!blr_dtypes[field->fld_dtype]) {
		SCHAR buffer[100];

		sprintf(buffer, "Invalid dtype %d in put_dtype", field->fld_dtype);
		ERRD_bugcheck(buffer);
	}
#endif

	if (field->fld_dtype == dtype_cstring ||
		field->fld_dtype == dtype_text || field->fld_dtype == dtype_varying)
	{
		if (!use_subtype || (request->req_dbb->dbb_flags & DBB_v3)) {
			request->append_uchar(blr_dtypes[field->fld_dtype]);
		}
		else if (field->fld_dtype == dtype_varying) {
			request->append_uchar(blr_varying2);
			request->append_ushort(field->fld_ttype);
		}
		else if (field->fld_dtype == dtype_cstring) {
			request->append_uchar(blr_cstring2);
			request->append_ushort(field->fld_ttype);
		}
		else {
			request->append_uchar(blr_text2);
			request->append_ushort(field->fld_ttype);
		}

		if (field->fld_dtype == dtype_varying) {
			request->append_ushort(field->fld_length - sizeof(USHORT));
		} else {
			request->append_ushort(field->fld_length);
		}
	}
	else
	{
		request->append_uchar(blr_dtypes[field->fld_dtype]);
		if (DTYPE_IS_EXACT(field->fld_dtype) ||
			(dtype_quad == field->fld_dtype))
		{
			request->append_uchar(field->fld_scale);
		}
	}
}


static void put_field( DSQL_REQ request, DSQL_FLD field, BOOLEAN udf_flag)
{
/**************************************
 *
 *	p u t _ f i e l d
 *
 **************************************
 *
 * Function
 *	Emit dyn which describes a field data type.
 *	This field could be a column, a procedure input,
 *	or a procedure output.
 *
 **************************************/

	request->append_number(gds_dyn_fld_type, blr_dtypes[field->fld_dtype]);
	if (field->fld_dtype == dtype_blob)
	{
		request->append_number(gds_dyn_fld_sub_type, field->fld_sub_type);
		request->append_number(gds_dyn_fld_scale, 0);
		if (!udf_flag)
		{
			if (!field->fld_seg_length) {
				field->fld_seg_length = DEFAULT_BLOB_SEGMENT_SIZE;
			}
			request->append_number(gds_dyn_fld_segment_length,
					   field->fld_seg_length);
		}
		if (!(request->req_dbb->dbb_flags & DBB_v3))
		{
			if (field->fld_sub_type == BLOB_text) {
				request->append_number(gds_dyn_fld_character_set,
						   field->fld_character_set_id);
			}
		}
	}
	else if (field->fld_dtype <= dtype_any_text)
	{
		if (field->fld_sub_type) {
			request->append_number(gds_dyn_fld_sub_type, field->fld_sub_type);
		}
		request->append_number(gds_dyn_fld_scale, 0);
		if (field->fld_dtype == dtype_varying)
		{
            /* CVC: Fix the assertion */
            assert((field->fld_length) <= MAX_SSHORT);
			request->append_number(gds_dyn_fld_length,
					   (SSHORT) (field->fld_length - sizeof(USHORT)));
		}
		else
		{
			request->append_number(gds_dyn_fld_length, field->fld_length);
		}
		if (!(request->req_dbb->dbb_flags & DBB_v3))
		{
			request->append_number(gds_dyn_fld_char_length,
					   field->fld_character_length);
			request->append_number(gds_dyn_fld_character_set,
					   field->fld_character_set_id);
			if (!udf_flag)
				request->append_number(gds_dyn_fld_collation,
						   field->fld_collation_id);
		}
	}
	else {
		request->append_number(gds_dyn_fld_scale, field->fld_scale);
		request->append_number(gds_dyn_fld_length, field->fld_length);
		if (DTYPE_IS_EXACT(field->fld_dtype))
		{
			request->append_number(isc_dyn_fld_precision, field->fld_precision);
			if (field->fld_sub_type)
			{
				request->append_number(isc_dyn_fld_sub_type,
						   field->fld_sub_type);
			}
		}
	}
}


static void put_local_variable( DSQL_REQ request, VAR variable, DSQL_NOD host_param)
{
/**************************************
 *
 *	p u t _ l o c a l _ v a r i a b l e
 *
 **************************************
 *
 * Function
 *	Write out local variable field data type
 *
 **************************************/

	DSQL_FLD field = variable->var_field;

	request->append_uchar(blr_dcl_variable);
	request->append_ushort(variable->var_variable_number);
	DDL_resolve_intl_type(request, field, NULL);

	const USHORT dtype = field->fld_dtype;

	if (dtype == dtype_blob) {
		field->fld_dtype = dtype_quad;
	}

	put_dtype(request, field, TRUE);
	field->fld_dtype = dtype;

	/* Check for a default value, borrowed from define_domain */

	request->append_uchar(blr_assignment);
	DSQL_NOD node = (host_param) ? host_param->nod_arg[e_dfl_default] : 0;
	if (node)
	{
		node = PASS1_node(request, node, 0);
		GEN_expr(request, node);
	}
	else
	{
		// Initialize variable to NULL
		request->append_uchar(blr_null);
	}
	request->append_uchar(blr_variable);
	request->append_ushort(variable->var_variable_number);
}


static SSHORT put_local_variables(DSQL_REQ request, DSQL_NOD parameters, SSHORT locals)
{
/**************************************
 *
 *	p u t _ l o c a l _ v a r i a b l e s
 *
 **************************************
 *
 * Function
 *	Emit dyn for the local variables declared
 *	in a procedure or trigger.
 *
 **************************************/

	if (parameters)
	{
		DSQL_NOD* ptr = parameters->nod_arg;
		for (DSQL_NOD* end = ptr + parameters->nod_count; ptr < end; ptr++)
		{
			DSQL_NOD parameter = *ptr;
			DSQL_FLD field     = (DSQL_FLD) parameter->nod_arg[e_dfl_field];
			DSQL_NOD* rest     = ptr;
			while ((++rest) != end)
			{
				DSQL_FLD rest_field = (DSQL_FLD) (*rest)->nod_arg[e_dfl_field];
				if (!strcmp(field->fld_name, rest_field->fld_name))
				{
					ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -637,
							  gds_arg_gds, gds_dsql_duplicate_spec,
							  gds_arg_string, field->fld_name, 0);
				}
			}

			DSQL_NOD var_node =
				MAKE_variable(field, field->fld_name, VAR_output, 0, 0,
							  locals);
			*ptr = var_node;
			VAR variable = (VAR) var_node->nod_arg[e_var_variable];
			put_local_variable(request, variable, parameter);

			// Some field attributes are calculated inside
			// put_local_variable(), so we reinitialize the
			// descriptor
			MAKE_desc_from_field(&var_node->nod_desc, field);

			locals++;
		}
	}

	return locals;
}


static void put_msg_field( DSQL_REQ request, DSQL_FLD field)
{
/**************************************
 *
 *	p u t _ m s g _ f i e l d
 *
 **************************************
 *
 * Function
 *	Write out message field data type
 *
 **************************************/

	const USHORT dtype = field->fld_dtype;

	if (dtype == dtype_blob) {
		field->fld_dtype = dtype_quad;
	}

	put_dtype(request, field, TRUE);
	field->fld_dtype = dtype;

	// add slot for null flag (parameter2)
	request->append_uchar(blr_short);
	request->append_uchar(0);
}


void dsql_req::append_number(UCHAR verb, SSHORT number)
{
/**************************************
 *
 * Input
 *	blr_ptr: current position of blr being generated
 *	verb: blr byte of which number is an argument
 *	number: value to be written to blr
 * Function
 *	Write out a numeric valued attribute.
 *
 **************************************/

	if (verb) {
		append_uchar(verb);
	}

	append_ushort_with_length(number);
}


//
//	Write out a string valued attribute.
//
void dsql_req::append_cstring(UCHAR verb, const char* string)
{
	const USHORT length = string ? strlen(string) : 0;
	append_string(verb, string, length);
}


//
//	Write out a string valued attribute.
//
void dsql_req::append_string(UCHAR verb, const char* string, USHORT length)
{
	// TMN: Doesn't this look pretty awkward? If we are given
	// a verb, the length is a ushort, else it's uchar.
	if (verb) {
		append_uchar(verb);
		append_ushort(length);
	}
	else {
		append_uchar(length);
	}

	if (string) {
		for (; *string && length--; string++) {
			append_uchar(*string);
		}
	}
}


static DSQL_NOD replace_field_names(DSQL_NOD		input,
							   DSQL_NOD		search_fields,
							   DSQL_NOD		replace_fields,
							   SSHORT	null_them)
{
/* *************************************
 *
 *	r e p l a c e _ f i e l d _ n a m e s
 *
 **************************************
 *
 * Function
 *	Given an input node tree, find any field name nodes
 *	and replace them according to the mapping provided.
 *	Used to create view  WITH CHECK OPTION.
 *
 **************************************/

	if (!input || MemoryPool::blk_type(input) != dsql_type_nod) {
		return input;
	}

	const DSQL_NOD* endo = input->nod_arg + input->nod_count;

	for (DSQL_NOD* ptr = input->nod_arg; ptr < endo; ++ptr)
	{

		if ((*ptr)->nod_type == nod_select_expr)
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
					  gds_arg_gds, gds_dsql_command_err,
					  gds_arg_gds, gds_subquery_err,
					  /* No subqueries permitted for VIEW WITH CHECK OPTION */
					  0);
		}

		if ((*ptr)->nod_type == nod_field_name)
		{
			// found a field node, check if it needs to be replaced

			STR  field_name = (STR) (*ptr)->nod_arg[e_fln_name];
			DSQL_NOD* search     = search_fields->nod_arg;
			DSQL_NOD* end        = search + search_fields->nod_count;
			DSQL_NOD* replace;
			if (replace_fields) {
				replace = replace_fields->nod_arg;
			}
			bool found = false;
			for (; search < end; search++, (replace_fields) ? replace++ : NULL)
			{
				STR replace_name;
				if (replace_fields) {
					replace_name = (STR) (*replace)->nod_arg[e_fln_name];
				}
				DSQL_NOD field_node = *search;
				DSQL_FLD field = (DSQL_FLD) field_node->nod_arg[e_fld_field];
				TEXT* search_name = field->fld_name;
				if (!strcmp((SCHAR *) field_name->str_data,
							(SCHAR *) search_name))
				{
					found = true;
					if (replace_fields) {
						(*ptr)->nod_arg[e_fln_name] = (*replace)->nod_arg[e_fln_name];
					}
					(*ptr)->nod_arg[e_fln_context] = (DSQL_NOD) MAKE_cstring(NEW_CONTEXT);

				}
				if (null_them &&
					replace_fields &&
					!strcmp((SCHAR *) field_name->str_data,
							(SCHAR *) replace_name->str_data))
				{
					found = true;
				}
			}
			if (null_them && !found) {
				(*ptr) = MAKE_node(nod_null, (int) 0);
			}
		}
		else
		{
			// recursively go through the input tree
			// looking for field name nodes
			replace_field_names(*ptr, search_fields, replace_fields,
								null_them);
		}
	}

	return input;
}


static void reset_context_stack( DSQL_REQ request)
{
/**************************************
 *
 *	r e s e t _ c o n t e x t _ s t a c k
 *
 **************************************
 *
 * Function
 *	Get rid of any predefined contexts created
 *	for a view or trigger definition.
 *
 **************************************/

	while (request->req_context) {
		LLS_POP(&request->req_context);
	}
	request->req_context_number = 0;
}


static void save_field(DSQL_REQ request, TEXT* field_name)
{
/**************************************
 *
 *	s a v e _ f i e l d
 *
 **************************************
 *
 * Function
 *	Save the name of a field in the relation or view currently
 *	being defined.  This is done to support definition
 *	of triggers which will depend on the metadata created
 *	in this request.
 *
 **************************************/

	TSQL tdsql = GET_THREAD_DATA;

	DSQL_REL relation = request->req_relation;
	if (!relation) {
		return;
	}

	MemoryPool* p = relation->rel_flags & REL_new_relation ? 
					tdsql->tsql_default : request->req_dbb->dbb_pool;
	DSQL_FLD field = FB_NEW_RPT(*p, strlen(field_name) + 1) dsql_fld;
	strcpy(field->fld_name, field_name);
	field->fld_next = relation->rel_fields;
	relation->rel_fields = field;
}


static void save_relation( DSQL_REQ request, STR relation_name)
{
/**************************************
 *
 *	s a v e _ r e l a t i o n
 *
 **************************************
 *
 * Function
 *	Save the name of the relation or view currently
 *	being defined.  This is done to support definition
 *	of triggers which will depend on the metadata created
 *	in this request.
 *
 **************************************/

	TSQL tdsql = GET_THREAD_DATA;

	if (request->req_flags & REQ_save_metadata) {
		return;
	}

	request->req_flags |= REQ_save_metadata;

	DSQL_NOD ddl_node = request->req_ddl_node;
	DSQL_REL relation;

	if (ddl_node->nod_type == nod_mod_relation)
	{
		relation = METD_get_relation(request, relation_name);
	}
	else
	{
		relation = FB_NEW_RPT(*tdsql->tsql_default, relation_name->str_length) dsql_rel;
		relation->rel_flags |= REL_new_relation;
		relation->rel_name = relation->rel_data;
		relation->rel_owner =
			relation->rel_data + relation_name->str_length + 1;
		strcpy(relation->rel_name, (SCHAR *) relation_name->str_data);
		*relation->rel_owner = 0;
	}
	request->req_relation = relation;
}


static void set_statistics( DSQL_REQ request)
{
/**************************************
 *
 *	s e t _ s t a t i s t i c s
 *
 **************************************
 *
 * Function
 *	Alter an index/.. statistics
 *
 **************************************/
	DSQL_NOD ddl_node;
	STR index_name;

	ddl_node = request->req_ddl_node;

	index_name = (STR) ddl_node->nod_arg[e_stat_name];

	request->append_cstring(gds_dyn_mod_idx, index_name->str_data);

	request->append_uchar(gds_dyn_idx_statistic);

	request->append_uchar(gds_dyn_end);
}


static void stuff_default_blr(	DSQL_REQ		request,
								TEXT*	default_buff,
								USHORT	buff_size)
{
/********************************************
 *
 *      s t u f f _ d e f a u l t _ b l r
 *
 ********************************************
 * Function:
 *   The default_blr is passed in default_buffer. It is of the form:
 *   blr_version4 blr_literal ..... blr_eoc.
 *   strip the blr_version4 and blr_eoc verbs and stuff the remaining
 *   blr in the blr stream in the request.
 *
 *********************************************/
	unsigned int i;
	assert((*default_buff == blr_version4)
		   || (*default_buff == blr_version5));

	for (i = 1; ((i < buff_size) && (default_buff[i] != blr_eoc)); i++) {
		request->append_uchar(default_buff[i]);
	}

	assert(default_buff[i] == blr_eoc);
}


static void stuff_matching_blr(DSQL_REQ request, DSQL_NOD for_columns, DSQL_NOD prim_columns)
{
/********************************************
 *
 *      s t u f f _ m a t c h i n g _ b l r
 *
 ********************************************
 *
 * Function
 *   Generate blr to express: foreign_key == primary_key
 *   ie.,  for_key.column_1 = prim_key.column_1 and
 *         for_key.column_2 = prim_key.column_2 and ....  so on..
 *
 **************************************/

/* count of foreign key columns */
	assert(prim_columns->nod_count == for_columns->nod_count);
	assert(prim_columns->nod_count != 0);

	request->append_uchar(blr_boolean);
	if (prim_columns->nod_count > 1) {
		request->append_uchar(blr_and);
	}

	USHORT num_fields = 0;
	DSQL_NOD* for_key_flds = for_columns->nod_arg;
	DSQL_NOD* prim_key_flds = prim_columns->nod_arg;

	do {
		request->append_uchar(blr_eql);

		STR for_key_fld_name_str = (STR) (*for_key_flds)->nod_arg[1];
		STR prim_key_fld_name_str = (STR) (*prim_key_flds)->nod_arg[1];

		request->append_uchar(blr_field);
		request->append_uchar(2);
		request->append_cstring(0, for_key_fld_name_str->str_data);
		request->append_uchar(blr_field);
		request->append_uchar(0);
		request->append_cstring(0, prim_key_fld_name_str->str_data);

		num_fields++;

		if (prim_columns->nod_count - num_fields >= 2) {
			request->append_uchar(blr_and);
		}

		for_key_flds++;
		prim_key_flds++;

	} while (num_fields < for_columns->nod_count);

	request->append_uchar(blr_end);
}


static void stuff_trg_firing_cond(DSQL_REQ request, DSQL_NOD prim_columns)
{
/********************************************
 *
 *      s t u f f _ t r g _ f i r i n g _ c o n d
 *
 ********************************************
 *
 * Function
 *   Generate blr to express: if (old.primary_key != new.primary_key).
 *   do a column by column comparison.
 *
 **************************************/


	request->append_uchar(blr_if);

	if (prim_columns->nod_count > 1) {
		request->append_uchar(blr_or);
	}

	USHORT num_fields = 0;
	DSQL_NOD*   prim_key_flds = prim_columns->nod_arg;

	do {
		request->append_uchar(blr_neq);

		STR prim_key_fld_name_str = (STR) (*prim_key_flds)->nod_arg[1];

		request->append_uchar(blr_field);
		request->append_uchar(0);
		request->append_cstring(0, prim_key_fld_name_str->str_data);
		request->append_uchar(blr_field);
		request->append_uchar(1);
		request->append_cstring(0, prim_key_fld_name_str->str_data);

		num_fields++;

		if (prim_columns->nod_count - num_fields >= 2)
			request->append_uchar(blr_or);

		prim_key_flds++;

	} while (num_fields < prim_columns->nod_count);
}


static void modify_field(DSQL_REQ	request,
						 DSQL_NOD	element,
						 SSHORT	position,
						 STR	relation_name)
{
/**************************************
 *
 *	m o d i f y _ f i e l d
 *
 **************************************
 *
 * Function
 *	Modify a field, either as part of an
 *	alter table or alter domain statement.
 *
 **************************************/
	DSQL_NOD domain_node;
	DSQL_FLD field;
	DSQL_REL relation;

	field = (DSQL_FLD) element->nod_arg[e_dfl_field];
	request->append_cstring(isc_dyn_mod_sql_fld, field->fld_name);

// add the field to the relation being defined for parsing purposes
	bool permanent = false;
	if ((relation = request->req_relation) != NULL)
	{
		if (! (relation->rel_flags & REL_new_relation)) {
			DSQL_FLD perm_field = FB_NEW_RPT(*request->req_dbb->dbb_pool,
								strlen(field->fld_name)) dsql_fld;
			*perm_field = *field;
			strcpy(perm_field->fld_name, field->fld_name);
			field = perm_field;
			permanent = true;
		}
		field->fld_next = relation->rel_fields;
		relation->rel_fields = field;
	}

	try {

	if (domain_node = element->nod_arg[e_mod_fld_type_dom_name])
	{
		DSQL_NOD node1;
		STR domain_name;

		node1 = domain_node->nod_arg[e_dom_name];
		domain_name = (STR) node1->nod_arg[e_fln_name];
		request->append_cstring(gds_dyn_fld_source, domain_name->str_data);

		// Get the domain information

		if (!METD_get_domain(request, field, domain_name->str_data))
		{
			ERRD_post(gds_sqlerr, gds_arg_number, (SLONG) -607,
					  gds_arg_gds, gds_dsql_command_err,
					  gds_arg_gds, gds_dsql_domain_not_found,
					  /* Specified domain or source field does not exist */
					  0);
		}
		DDL_resolve_intl_type(request, field, NULL);
	}
	else
	{
		if (relation_name) {
			request->append_cstring(gds_dyn_rel_name, relation_name->str_data);
		}

		DDL_resolve_intl_type2(request, field, NULL, TRUE);
		put_field(request, field, FALSE);
	}
	request->append_uchar(gds_dyn_end);
	} // try

	catch (...)
	{
		clearPermanentField (relation, permanent);
		throw;
	}
	clearPermanentField (relation, permanent);
}


static void set_nod_value_attributes( DSQL_NOD node, DSQL_FLD field)
{
/**************************************
 *
 *	s e t _ n o d _ v a l u e _ a t t r i b u t e s
 *
 **************************************
 *
 * Function
 *	Examine all the children of the argument node:
 *	if any is a nod_dom_value, set its dtype, size, and scale
 *      to those of the field argument
 *
 **************************************/
	ULONG child_number;

	for (child_number = 0; child_number < node->nod_count; ++child_number)
	{
		DSQL_NOD child = node->nod_arg[child_number];
		if (child && MemoryPool::blk_type(child) == dsql_type_nod)
		{
			if (nod_dom_value == child->nod_type)
			{
				assert(field->fld_dtype <= MAX_UCHAR);
				child->nod_desc.dsc_dtype = (UCHAR) field->fld_dtype;
				child->nod_desc.dsc_length = field->fld_length;
				assert(field->fld_scale <= MAX_SCHAR);
				child->nod_desc.dsc_scale = (SCHAR) field->fld_scale;
			}
			else if ((nod_constant != child->nod_type) &&
					 (child->nod_count > 0))
			{
				/* A nod_constant can have nod_arg entries which are not really
				   pointers to other nodes, but rather integer values, so
				   it is not safe to scan through its children.  Fortunately,
				   it cannot have a nod_dom_value as a child in any case, so
				   we lose nothing by skipping it.
				 */

				set_nod_value_attributes(child, field);
			}
		}						/* if it's a node */
	}							/* for (child_number ... */
	return;
}


void dsql_req::append_uchar(UCHAR byte)
{
	if (req_blr < req_blr_yellow) {
		*req_blr++ = byte;
	} else {
		GEN_expand_buffer(this, byte);
	}
}

void dsql_req::append_uchars(UCHAR byte, UCHAR count)
{
	for (int i=0; i< count ; ++i) {
		append_uchar(byte);
	}
}

void dsql_req::append_ushort(USHORT val)
{
	append_uchar(val);
	append_uchar(val >> 8);
}

void dsql_req::append_ulong(ULONG val)
{
	append_ushort(val);
	append_ushort(val >> 16);
}

void dsql_req::append_ushort_with_length(USHORT val)
{
	// append an USHORT value, prepended with the USHORT length of an USHORT
	append_ushort(2);
	append_ushort(val);
}

void dsql_req::append_ulong_with_length(ULONG val)
{
	// append an ULONG value, prepended with the USHORT length of an ULONG
	append_ushort(4);
	append_ulong(val);
}

void dsql_req::append_file_length(ULONG length)
{
	append_uchar(gds_dyn_file_length);
	append_ulong_with_length(length);
}

void dsql_req::append_file_start(ULONG start)
{
	append_uchar(gds_dyn_file_start);
	append_ulong_with_length(start);
}

//
// common code factored out
//
void dsql_req::generate_unnamed_trigger_beginning(	bool		on_update_trigger,
												const char*		prim_rel_name,
												struct dsql_nod*	prim_columns,
												const char*		for_rel_name,
												struct dsql_nod*	for_columns)
{
	// no trigger name. It is generated by the engine
	append_string(gds_dyn_def_trigger, "", 0);

	append_number(gds_dyn_trg_type,
			   (SSHORT) (on_update_trigger ? POST_MODIFY_TRIGGER :
						 POST_ERASE_TRIGGER));

	append_uchar(gds_dyn_sql_object);
	append_number(gds_dyn_trg_sequence, 1);
	append_number(gds_dyn_trg_inactive, 0);
	append_cstring(gds_dyn_rel_name, prim_rel_name);

	// the trigger blr
	begin_blr(gds_dyn_trg_blr);

/* for ON UPDATE TRIGGER only: generate the trigger firing condition:
   if prim_key.old_value != prim_key.new value.
   Note that the key could consist of multiple columns */

	if (on_update_trigger) {
		stuff_trg_firing_cond(this, prim_columns);
		append_uchars(blr_begin, 2);
	}

	append_uchar(blr_for);
	append_uchar(blr_rse);

	// the context for the prim. key relation
	append_uchar(1);
	append_uchar(blr_relation);
	append_cstring(0, for_rel_name);
	// the context for the foreign key relation
	append_uchar(2);

	// generate the blr for: foreign_key == primary_key
	stuff_matching_blr(this, for_columns, prim_columns);

	append_uchar(blr_modify);
	append_uchar(2);
	append_uchar(2);
	append_uchar(blr_begin);
}

// removes temporary pool pointers from field, stored in permanent cache
void clearPermanentField (DSQL_REL relation, bool perm) 
{
	if (relation && relation->rel_fields && perm)
	{
		relation->rel_fields->fld_procedure = 0;
		relation->rel_fields->fld_ranges = 0;
		relation->rel_fields->fld_character_set = 0;
		relation->rel_fields->fld_sub_type_name = 0;
		relation->rel_fields->fld_relation = relation;
	}
}
