//____________________________________________________________
//  
//		PROGRAM:	C preprocessor
//		MODULE:		cme.cpp
//		DESCRIPTION:	Request expression compiler
//  
//  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): ______________________________________.
//  TMN (Mike Nordell) 11.APR.2001 - Reduce compiler warnings, buffer ptr bug
//  
//
//____________________________________________________________
//
//	$Id: cme.cpp,v 1.6 2002/11/30 17:40:24 hippoman Exp $
//

#include "firebird.h"
#include <stdlib.h>
#include <string.h>
#include "../jrd/gds.h"
#include "../gpre/gpre.h"
#include "../jrd/intl.h"
#include "../intl/charsets.h"
#include "../gpre/cme_proto.h"
#include "../gpre/cmp_proto.h"
#include "../gpre/gpre_proto.h"
#include "../gpre/gpre_meta.h"
#include "../gpre/movg_proto.h"
#include "../gpre/par_proto.h"
#include "../gpre/prett_proto.h"
#include "../jrd/dsc_proto.h"

static GPRE_NOD cmp_array(GPRE_NOD, GPRE_REQ);
static GPRE_NOD cmp_array_element(GPRE_NOD, GPRE_REQ);
static void cmp_cast(GPRE_NOD, GPRE_REQ);
static GPRE_NOD cmp_field(GPRE_NOD, GPRE_REQ);
static GPRE_NOD cmp_literal(GPRE_NOD, GPRE_REQ);
static void cmp_map(MAP, GPRE_REQ);
static void cmp_plan(GPRE_NOD, GPRE_REQ);
static void cmp_sdl_dtype(GPRE_FLD, REF);
static GPRE_NOD cmp_udf(GPRE_NOD, GPRE_REQ);
static GPRE_NOD cmp_value(GPRE_NOD, GPRE_REQ);
static USHORT get_string_len(GPRE_FLD);
static void stuff_cstring(GPRE_REQ, const char *);
static void stuff_sdl_dimension(DIM, REF, SSHORT);
static void stuff_sdl_element(REF, GPRE_FLD);
static void stuff_sdl_loops(REF, GPRE_FLD);
static void stuff_sdl_number(SLONG, REF);

#define USER_LENGTH	32
#define LONG_POS_MAX    2147483647

#define STUFF(blr)		*request->req_blr++ = (SCHAR) (blr)
#define STUFF_WORD(blr)		STUFF (blr); STUFF (blr >> 8)
#define STUFF_CSTRING(blr)	stuff_cstring (request, blr)

#define STUFF_SDL(sdl)		*reference->ref_sdl++ = (SCHAR) (sdl)
#define STUFF_SDL_WORD(sdl)	STUFF_SDL (sdl); STUFF_SDL (sdl >> 8);
#define STUFF_SDL_LONG(sdl)	STUFF_SDL (sdl); STUFF_SDL (sdl >> 8); STUFF_SDL (sdl >>16); STUFF_SDL (sdl >> 24);

#define ASSIGN_DTYPE(f,field)	\
	{ \
	(f)->fld_dtype = (field)->fld_dtype; \
	(f)->fld_length = (field)->fld_length; \
	(f)->fld_scale = (field)->fld_scale; \
	(f)->fld_precision = (field)->fld_precision; \
	(f)->fld_sub_type = (field)->fld_sub_type; \
	(f)->fld_charset_id = (field)->fld_charset_id; \
	(f)->fld_collate_id = (field)->fld_collate_id; \
	(f)->fld_ttype = (field)->fld_ttype; \
	}

/* One of d1,d2 is time, the other is date */
#define IS_DATE_AND_TIME(d1,d2)	\
  ((((d1)==dtype_sql_time)&&((d2)==dtype_sql_date)) || \
   (((d2)==dtype_sql_time)&&((d1)==dtype_sql_date)))

static BOOLEAN debug_on;

struct op_table
{
	enum nod_t op_type;
	UCHAR op_blr;
};

const op_table operators[] =
{
	{ nod_eq			, blr_eql },
	{ nod_ge			, blr_geq },
	{ nod_gt			, blr_gtr },
	{ nod_le			, blr_leq },
	{ nod_lt			, blr_lss },
	{ nod_ne			, blr_neq },
	{ nod_missing		, blr_missing },
	{ nod_between		, blr_between },
	{ nod_and			, blr_and },
	{ nod_or			, blr_or },
	{ nod_not			, blr_not },
	{ nod_matches		, blr_matching },
	{ nod_starting	, blr_starting },
	{ nod_containing	, blr_containing },
	{ nod_plus		, blr_add },
	{ nod_from		, blr_from },
	{ nod_via			, blr_via },
	{ nod_minus		, blr_subtract },
	{ nod_times		, blr_multiply },
	{ nod_divide		, blr_divide },
	{ nod_negate		, blr_negate },
	{ nod_null		, blr_null },
	{ nod_user_name	, blr_user_name },
//  { count2 }
//   { nod_count, blr_count2 },
//  
	{ nod_count		, blr_count },
	{ nod_max			, blr_maximum },
	{ nod_min			, blr_minimum },
	{ nod_average		, blr_average },
	{ nod_total		, blr_total },
	{ nod_any			, blr_any },
	{ nod_unique		, blr_unique },
	{ nod_agg_count	, blr_agg_count2 },
	{ nod_agg_count	, blr_agg_count },
	{ nod_agg_max		, blr_agg_max },
	{ nod_agg_min		, blr_agg_min },
	{ nod_agg_total	, blr_agg_total },
	{ nod_agg_average	, blr_agg_average },
	{ nod_upcase		, blr_upcase },
	{ nod_sleuth		, blr_matching2 },
	{ nod_concatenate	, blr_concatenate },
	{ nod_cast		, blr_cast },
	{ nod_ansi_any	, blr_ansi_any },
	{ nod_gen_id		, blr_gen_id },
	{ nod_ansi_all	, blr_ansi_all },
	{ nod_current_date, blr_current_date },
	{ nod_current_time, blr_current_time },
	{ nod_current_timestamp, blr_current_timestamp },
	{ nod_any, 0 }
};


//____________________________________________________________
//  
//		Compile a random expression.
//  

void CME_expr(GPRE_NOD node, GPRE_REQ request)
{
	GPRE_NOD *ptr, *end;
	MEL element;
	GPRE_CTX context;
	REF reference;
	TEXT *p, s[128];

	switch (node->nod_type)
	{
	case nod_field:
		if (!(reference = (REF) node->nod_arg[0]))
		{
			CPR_error("CME_expr: reference missing");
			return;
		}
		cmp_field(node, request);
		if (reference->ref_flags & REF_fetch_array)
			cmp_array(node, request);
		return;

	case nod_array:
		cmp_array_element(node, request);
		return;

	case nod_index:
		CME_expr(node->nod_arg[0], request);
		return;

	case nod_value:
		cmp_value(node, request);
		if ((reference = (REF) node->nod_arg[0]) &&
			(reference->ref_flags & REF_fetch_array))
		{
			cmp_array(node, request);
		}
		return;

	case nod_negate:
		if (node->nod_arg[0]->nod_type != nod_literal)
			break;
	case nod_literal:
		cmp_literal(node, request);
		return;

	case nod_like:
		STUFF((node->nod_count == 2) ? blr_like : blr_ansi_like);
		ptr = node->nod_arg;
		for (end = ptr + node->nod_count; ptr < end; ptr++)
			CME_expr(*ptr, request);
		return;

	case nod_udf:
		cmp_udf(node, request);
		return;

	case nod_gen_id:
		STUFF(blr_gen_id);
		p = (TEXT *) (node->nod_arg[1]);

		/* check if this generator really exists */

		if (!MET_generator(p, request->req_database))
		{
			sprintf(s, "generator %s not found", p);
			CPR_error(s);
		}
		STUFF(strlen(p));
		while (*p)
			STUFF(*p++);
		CME_expr(node->nod_arg[0], request);
		return;

	case nod_cast:
		cmp_cast(node, request);
		return;

	case nod_agg_count:
		if ((node->nod_arg[0]) &&
			!(request->req_database->dbb_flags & DBB_v3))
		{
			if (node->nod_arg[1])
				STUFF(blr_agg_count_distinct);
			else
				STUFF(blr_agg_count2);
			CME_expr(node->nod_arg[0], request);
		}
		else
			STUFF(blr_agg_count);
		return;

// ** Begin date/time/timestamp support *
	case nod_extract:
		STUFF(blr_extract);
		switch ((KWWORDS) (int) node->nod_arg[0])
		{
		case KW_YEAR:
			STUFF(blr_extract_year);
			break;
		case KW_MONTH:
			STUFF(blr_extract_month);
			break;
		case KW_DAY:
			STUFF(blr_extract_day);
			break;
		case KW_HOUR:
			STUFF(blr_extract_hour);
			break;
		case KW_MINUTE:
			STUFF(blr_extract_minute);
			break;
		case KW_SECOND:
			STUFF(blr_extract_second);
			break;
		case KW_WEEKDAY:
			STUFF(blr_extract_weekday);
			break;
		case KW_YEARDAY:
			STUFF(blr_extract_yearday);
			break;
		default:
			CPR_error("CME_expr:Invalid extract part");
		}
		CME_expr(node->nod_arg[1], request);
		return;

// ** End date/time/timestamp support *
//  count2
//   case nod_count:
// if (node->nod_arg [1])
//    break;
// STUFF (blr_count);
// CME_rse (node->nod_arg [0], request);
// return;
//  

	case nod_agg_total:
		if ((node->nod_arg[1]) &&
			!(request->req_database->dbb_flags & DBB_v3))
				STUFF(blr_agg_total_distinct);
		else
			STUFF(blr_agg_total);
		CME_expr(node->nod_arg[0], request);
		return;

	case nod_agg_average:
		if ((node->nod_arg[1]) &&
			!(request->req_database->dbb_flags & DBB_v3))
				STUFF(blr_agg_average_distinct);
		else
			STUFF(blr_agg_average);
		CME_expr(node->nod_arg[0], request);
		return;

	case nod_dom_value:
		STUFF(blr_fid);
		STUFF(0);				/* Context   */
		STUFF_WORD(0);			/* Field id  */
		return;

	case nod_map_ref:
		element = (MEL) node->nod_arg[0];
		context = element->mel_context;
		STUFF(blr_fid);
		STUFF(context->ctx_internal);
		STUFF_WORD(element->mel_position);
		return;
	}

	const op_table* operator_;
	for (operator_ = operators;
		operator_->op_type != node->nod_type;
		++operator_)
	{
		if (!operator_->op_blr)
		{
			CPR_bugcheck("node type not implemented");
			return;
		}
	}

	STUFF(operator_->op_blr);
	ptr = node->nod_arg;

	for (end = ptr + node->nod_count; ptr < end; ptr++)
		CME_expr(*ptr, request);

	switch (node->nod_type)
	{
	case nod_any:
	case nod_ansi_any:
	case nod_ansi_all:
	case nod_unique:
//  count2 next line would be deleted 
	case nod_count:
		CME_rse((RSE) node->nod_arg[0], request);
		break;

	case nod_max:
	case nod_min:
	case nod_average:
	case nod_total:
	case nod_from:
//  
//   case nod_count:
//  
		CME_rse((RSE) node->nod_arg[0], request);
		CME_expr(node->nod_arg[1], request);
		break;

	case nod_via:
		CME_rse((RSE) node->nod_arg[0], request);
		CME_expr(node->nod_arg[1], request);
		CME_expr(node->nod_arg[2], request);
	}
}


//____________________________________________________________
//  
//		Compute datatype, length, and scale of an expression.
//  

void CME_get_dtype( GPRE_NOD node, GPRE_FLD f)
{
	struct gpre_fld field1, field2;
	SSHORT dtype_max;
	TEXT *string;
	MEL element;
	REF reference;
	GPRE_FLD tmp_field;
	UDF udf;

	f->fld_dtype = 0;
	f->fld_length = 0;
	f->fld_scale = 0;
	f->fld_sub_type = 0;
	f->fld_charset_id = 0;
	f->fld_collate_id = 0;
	f->fld_ttype = 0;

	switch (node->nod_type)
	{
	case nod_null:
		/* This occurs when SQL statement specifies a literal NULL, eg:
		 *  SELECT NULL FROM TABLE1;
		 * As we don't have a <dtype_null, HOSTTYPE> datatype pairing,
		 * we don't know how to map this NULL to a host-language
		 * datatype.  Therefore we now describe it as a 
		 * CHAR(1) CHARACTER SET NONE type.
		 * No value will ever be sent back, as the value of the select
		 * will be NULL - this is only for purposes of allocating
		 * values in the message DESCRIBING
		 * the statement.  
		 * Other parts of gpre aren't too happy with a dtype_null datatype
		 */
		f->fld_dtype = dtype_text;
		f->fld_length = 1;
		f->fld_ttype = ttype_none;
		f->fld_charset_id = CS_NONE;
		return;

	case nod_map_ref:
		element = (MEL) node->nod_arg[0];
		CME_get_dtype(element->mel_expr, f);
		return;

	case nod_value:
	case nod_field:
	case nod_array:
		reference = (REF) node->nod_arg[0];
		if (!(tmp_field = reference->ref_field))
			CPR_error("CME_get_dtype: node type not supported");
		if (!(tmp_field->fld_dtype) || !(tmp_field->fld_length))
			PAR_error("Inappropriate self-reference of field");

		ASSIGN_DTYPE(f, tmp_field);
		return;

	case nod_agg_count:
	case nod_count:
		f->fld_dtype = dtype_long;
		f->fld_length = sizeof(SLONG);
		return;

	case nod_gen_id:
		if ((sw_sql_dialect == SQL_DIALECT_V5) || (sw_server_version < 6))
		{
			f->fld_dtype = dtype_long;
			f->fld_length = sizeof(SLONG);
		}
		else
		{
			f->fld_dtype = dtype_int64;
			f->fld_length = sizeof(ISC_INT64);
		}
		return;

	case nod_max:
	case nod_min:
	case nod_from:
		CME_get_dtype(node->nod_arg[1], f);
		return;

	case nod_agg_max:
	case nod_agg_min:
	case nod_negate:
		CME_get_dtype(node->nod_arg[0], f);
		if ((sw_sql_dialect == SQL_DIALECT_V5)
			&& (f->fld_dtype == dtype_int64))
		{
			f->fld_precision = 0;
			f->fld_scale = 0;
			f->fld_dtype = dtype_double;
			f->fld_length = sizeof(double);
		}
		return;

// ** Begin date/time/timestamp support *
	case nod_extract:
		{
			KWWORDS kw_word = (KWWORDS) (int) node->nod_arg[0];
			CME_get_dtype(node->nod_arg[1], f);
			switch (f->fld_dtype)
			{
			case dtype_timestamp:
				break;

			case dtype_sql_date:
				if (kw_word == KW_HOUR || kw_word == KW_MINUTE
					|| kw_word ==
					KW_SECOND)
		  CPR_error("Invalid extract part for SQL DATE type");
				break;

			case dtype_sql_time:
				if (kw_word != KW_HOUR && kw_word != KW_MINUTE
					&& kw_word !=
					KW_SECOND)
		  CPR_error("Invalid extract part for SQL TIME type");
				break;

			default:
				CPR_error("Invalid use of EXTRACT function");
			}
			switch (kw_word)
			{
			case KW_YEAR:
			case KW_MONTH:
			case KW_DAY:
			case KW_WEEKDAY:
			case KW_YEARDAY:
			case KW_HOUR:
			case KW_MINUTE:
				f->fld_dtype = dtype_short;
				f->fld_length = sizeof(short);
				break;
			case KW_SECOND:
				f->fld_dtype = dtype_long;
				f->fld_length = sizeof(long);
				f->fld_scale = ISC_TIME_SECONDS_PRECISION_SCALE;
				break;
			default:
				CPR_error("Invalid EXTRACT part");
			}
			return;
		}

	case nod_current_date:
		f->fld_dtype = dtype_sql_date;
		f->fld_length = sizeof(ISC_DATE);
		return;

	case nod_current_time:
		f->fld_dtype = dtype_sql_time;
		f->fld_length = sizeof(ISC_TIME);
		return;

	case nod_current_timestamp:
		f->fld_dtype = dtype_timestamp;
		f->fld_length = sizeof(ISC_TIMESTAMP);
		return;

// ** End date/time/timestamp support *

	case nod_times:
		CME_get_dtype(node->nod_arg[0], &field1);
		CME_get_dtype(node->nod_arg[1], &field2);
		if (sw_sql_dialect == SQL_DIALECT_V5)
		{
			if (field1.fld_dtype == dtype_int64)
			{
				field1.fld_dtype = dtype_double;
				field1.fld_scale = 0;
				field1.fld_length = sizeof(double);
			}
			if (field2.fld_dtype == dtype_int64)
			{
				field2.fld_dtype = dtype_double;
				field2.fld_scale = 0;
				field2.fld_length = sizeof(double);
			}
			dtype_max = MAX(field1.fld_dtype, field2.fld_dtype);
			if (DTYPE_IS_DATE(dtype_max) || DTYPE_IS_BLOB(dtype_max))
			{
				CPR_error("Invalid use of date/blob/array value");
			}
		}
		else
		{
			dtype_max =
				DSC_multiply_result[field1.fld_dtype][field2.fld_dtype];
			if (dtype_max == dtype_null)
			{
				CPR_error("Invalid operand used in multiplication");
			} else if (dtype_max == DTYPE_CANNOT)
			{
				CPR_error("expression evaluation not supported");
			}
		}
		if (dtype_max == dtype_short || dtype_max == dtype_long)
		{
			f->fld_dtype = dtype_long;
			f->fld_scale = field1.fld_scale + field2.fld_scale;
			f->fld_length = sizeof(SLONG);
		}
#ifdef NATIVE_QUAD
		else if (dtype_max == dtype_quad)
		{
			f->fld_dtype = dtype_quad;
			f->fld_scale = field1.fld_scale + field2.fld_scale;
			f->fld_length = sizeof(GDS_QUAD);
		}
#endif
		else if (dtype_max == dtype_int64)
		{
			f->fld_dtype = dtype_int64;
			f->fld_scale = field1.fld_scale + field2.fld_scale;
			f->fld_length = sizeof(ISC_INT64);
		}
		else
		{
			f->fld_dtype = dtype_double;
			f->fld_scale = 0;
			f->fld_length = sizeof(double);
		}
		return;

	case nod_concatenate:
		CME_get_dtype(node->nod_arg[0], &field1);
		CME_get_dtype(node->nod_arg[1], &field2);
		dtype_max = MAX(field1.fld_dtype, field2.fld_dtype);
		if (field1.fld_dtype > dtype_any_text)
		{
			f->fld_dtype		= dtype_varying;
			f->fld_char_length	=
				get_string_len(&field1) + get_string_len(&field2);
			f->fld_length		= f->fld_char_length + sizeof(USHORT);
			f->fld_charset_id	= CS_ASCII;
			f->fld_ttype		= ttype_ascii;
			return;
		}
		ASSIGN_DTYPE(f, &field1);
		f->fld_length = get_string_len(&field1) + get_string_len(&field2);
		if (f->fld_dtype == dtype_cstring)
			f->fld_length += 1;
		else if (f->fld_dtype == dtype_varying)
			f->fld_length += sizeof(USHORT);
		return;

	case nod_plus:
	case nod_minus:
		CME_get_dtype(node->nod_arg[0], &field1);
		CME_get_dtype(node->nod_arg[1], &field2);
		if (sw_sql_dialect == SQL_DIALECT_V5)
		{
			if (field1.fld_dtype == dtype_int64)
			{
				field1.fld_dtype = dtype_double;
				field1.fld_scale = 0;
				field1.fld_length = sizeof(double);
			}
			if (field2.fld_dtype == dtype_int64)
			{
				field2.fld_dtype = dtype_double;
				field2.fld_scale = 0;
				field2.fld_length = sizeof(double);
			}

			if (DTYPE_IS_DATE(field1.fld_dtype) &&
				DTYPE_IS_DATE(field2.fld_dtype) &&
				!((node->nod_type == nod_minus) ||
				  IS_DATE_AND_TIME(field1.fld_dtype, field2.fld_dtype)))
				CPR_error("Invalid use of timestamp/date/time value");
			else
				dtype_max = MAX(field1.fld_dtype, field2.fld_dtype);
			if (DTYPE_IS_BLOB(dtype_max))
				CPR_error("Invalid use of blob/array value");
		}
		else
		{
// ** Dialect is > 1 *

			if (node->nod_type == nod_plus)
				dtype_max =
					DSC_add_result[field1.fld_dtype][field2.fld_dtype];
			else
				dtype_max =
					DSC_sub_result[field1.fld_dtype][field2.fld_dtype];
			if (dtype_max == dtype_null)
				CPR_error("Illegal operands used in addition");
			else if (dtype_max == DTYPE_CANNOT)
				CPR_error("expression evaluation not supported");
		}

		if (dtype_max == dtype_short || dtype_max == dtype_long)
		{
			f->fld_dtype = dtype_long;
			f->fld_scale = MIN(field1.fld_scale, field2.fld_scale);
			f->fld_length = sizeof(SLONG);
		}
#ifdef NATIVE_QUAD
		else if (dtype_max == dtype_quad)
		{
			f->fld_dtype = dtype_quad;
			f->fld_scale = MIN(field1.fld_scale, field2.fld_scale);
			f->fld_length = sizeof(GDS__QUAD);
		}
#endif
// ** Begin date/time/timestamp support *
		else if (dtype_max == dtype_sql_date)
		{
			f->fld_dtype = dtype_sql_date;
			f->fld_scale = 0;
			f->fld_length = sizeof(ISC_DATE);
		}
		else if (dtype_max == dtype_sql_time)
		{
			f->fld_dtype = dtype_sql_time;
			f->fld_scale = 0;
			f->fld_length = sizeof(ISC_TIME);
		}
		else if (dtype_max == dtype_timestamp)
		{
			f->fld_dtype = dtype_timestamp;
			f->fld_scale = 0;
			f->fld_length = sizeof(ISC_TIMESTAMP);
		}
// ** End date/time/timestamp support *
		else if (dtype_max == dtype_int64)
		{
			f->fld_dtype = dtype_int64;
			f->fld_scale = MIN(field1.fld_scale, field2.fld_scale);
			f->fld_length = sizeof(ISC_INT64);
		}
		else
		{
			f->fld_dtype = dtype_double;
			f->fld_scale = 0;
			f->fld_length = sizeof(double);
		}
		return;

	case nod_via:
		CME_get_dtype(node->nod_arg[1], &field1);
		CME_get_dtype(node->nod_arg[2], &field2);
		if (field1.fld_dtype >= field2.fld_dtype)
		{
			ASSIGN_DTYPE(f, &field1);
		}
		else
		{
			ASSIGN_DTYPE(f, &field2);
		}
		return;


	case nod_divide:
		CME_get_dtype(node->nod_arg[0], &field1);
		CME_get_dtype(node->nod_arg[1], &field2);
		if (sw_sql_dialect == SQL_DIALECT_V5)
		{
			if (field1.fld_dtype == dtype_int64)
			{
				field1.fld_dtype = dtype_double;
				field1.fld_scale = 0;
				field1.fld_length = sizeof(double);
			}
			if (field2.fld_dtype == dtype_int64)
			{
				field2.fld_dtype = dtype_double;
				field2.fld_scale = 0;
				field2.fld_length = sizeof(double);
			}
			dtype_max = MAX(field1.fld_dtype, field2.fld_dtype);
			if (DTYPE_IS_DATE(dtype_max) || DTYPE_IS_BLOB(dtype_max))
				CPR_error("Invalid use of date/blob/array value");
			f->fld_dtype = dtype_double;
			f->fld_length = sizeof(double);
			return;
		}
		else
		{
			dtype_max =
				DSC_multiply_result[field1.fld_dtype][field2.fld_dtype];
			if (dtype_max == dtype_null)
				CPR_error("Illegal operands used in division");
			else if (dtype_max == DTYPE_CANNOT)
				CPR_error("expression evaluation not supported");
		}

		if (dtype_max == dtype_int64)
		{
			f->fld_dtype = dtype_int64;
			f->fld_scale = field1.fld_scale + field2.fld_scale;
			f->fld_length = sizeof(ISC_INT64);
		}
		else
		{
			f->fld_dtype = dtype_double;
			f->fld_scale = 0;
			f->fld_length = sizeof(double);
		}
		return;

	case nod_average:
	case nod_agg_average:
		if (node->nod_type == nod_average)
			CME_get_dtype(node->nod_arg[1], f);
		else
			CME_get_dtype(node->nod_arg[0], f);
		if (!DTYPE_CAN_AVERAGE(f->fld_dtype))
			CPR_error("expression evaluation not supported");
		if (sw_sql_dialect != SQL_DIALECT_V5)
		{
			if (DTYPE_IS_EXACT(f->fld_dtype))
			{
				f->fld_dtype = dtype_int64;
				f->fld_length = sizeof(SINT64);
			}
			else
			{
				f->fld_dtype = dtype_double;
				f->fld_length = sizeof(double);
			}
		}
		return;

	case nod_agg_total:
	case nod_total:
		if (node->nod_type == nod_total)
			CME_get_dtype(node->nod_arg[1], f);
		else
			CME_get_dtype(node->nod_arg[0], f);
		if (sw_sql_dialect == SQL_DIALECT_V5)
		{
			if ((f->fld_dtype == dtype_short) || (f->fld_dtype == dtype_long))
			{
				f->fld_dtype = dtype_long;
				f->fld_length = sizeof(SLONG);
			}
			else
			{
				f->fld_precision = 0;
				f->fld_scale = 0;
				f->fld_dtype = dtype_double;
				f->fld_length = sizeof(double);
			}
		}
		else
		{
// **  Dialect is 2 or 3 *

			if (DTYPE_IS_EXACT(f->fld_dtype))
			{
				f->fld_dtype = dtype_int64;
				f->fld_length = sizeof(SINT64);
			}
			else
			{
				f->fld_dtype = dtype_double;
				f->fld_length = sizeof(double);
			}
		}
		return;

	case nod_literal:
		reference = (REF) node->nod_arg[0];
		string = reference->ref_value;
		if (*string != '"' && *string != '\'')
		{
			/* Value didn't start with a quotemark - must be a numeric
			   that we stuffed away as a string during the parse */
			if (strpbrk(string, "Ee"))
			{
				f->fld_dtype = dtype_double;
				f->fld_length = sizeof(double);
			}
			else
			{
				char *ptr, *s_ptr;
				UINT64 uint64_val;
				int scale;

				s_ptr = string;

			/** Get the scale **/
				ptr = strpbrk(string, ".");
				if (!ptr)
					scale = 0;
				else
				{
					scale = (string + (strlen(string) - 1)) - ptr;
					scale = -scale;
				}

			/** Get rid of the decimal point **/
				uint64_val = 0;
				while (*s_ptr)
				{
					if (*s_ptr != '.')
						uint64_val = (uint64_val * 10) + (*s_ptr - '0');
					s_ptr++;
				}

				if (uint64_val <= MAX_SLONG)
				{
					f->fld_dtype = dtype_long;
					f->fld_scale = scale;
					f->fld_length = sizeof(SLONG);
				}
				else
				{
					f->fld_dtype = dtype_int64;
					f->fld_scale = scale;
					f->fld_length = sizeof(SINT64);
				}
			}
		}
		else
		{
			/* Did the reference include a character set specification? */

			if (reference->ref_flags & REF_ttype)
				f->fld_ttype = reference->ref_ttype;

			if (reference->ref_flags & REF_sql_date)
			{
				f->fld_dtype = dtype_sql_date;
				f->fld_length = sizeof(ISC_DATE);
			}
			else if (reference->ref_flags & REF_sql_time)
			{
				f->fld_dtype = dtype_sql_time;
				f->fld_length = sizeof(ISC_TIME);
			}
			else if (reference->ref_flags & REF_timestamp)
			{
				f->fld_dtype = dtype_timestamp;
				f->fld_length = sizeof(ISC_TIMESTAMP);
			}
			else
			{
				/* subtract 2 for starting & terminating quote */

				f->fld_length = strlen(string) - 2;
				if (sw_cstring)
				{
					/* add 1 back for the NULL byte */

					f->fld_length += 1;
					f->fld_dtype = dtype_cstring;
				}
				else
					f->fld_dtype = dtype_text;
			}
		}
		return;

	case nod_user_name:
		f->fld_dtype = dtype_text;
		f->fld_length = USER_LENGTH;
		f->fld_ttype = ttype_ascii;
		f->fld_charset_id = CS_ASCII;
		return;

	case nod_udf:
		udf = (UDF) node->nod_arg[1];
		f->fld_dtype = udf->udf_dtype;
		f->fld_length = udf->udf_length;
		f->fld_scale = udf->udf_scale;
		f->fld_sub_type = udf->udf_sub_type;
		f->fld_ttype = udf->udf_ttype;
		f->fld_charset_id = udf->udf_charset_id;
		return;

	case nod_cast:
		CME_get_dtype(node->nod_arg[0], &field1);
		tmp_field = (GPRE_FLD) node->nod_arg[1];
		ASSIGN_DTYPE(f, tmp_field);
		if (f->fld_length == 0)
			f->fld_length = field1.fld_length;
		return;

	case nod_upcase:
		CME_get_dtype(node->nod_arg[0], f);
		if (f->fld_dtype <= dtype_any_text)
			return;

		/* User has specified UPPER(5) - while silly, we'll cast
		   the value into a string, and upcase it anyway */

		f->fld_length = get_string_len(f) + sizeof(USHORT);
		f->fld_dtype = dtype_varying;
		f->fld_ttype = ttype_ascii;
		f->fld_charset_id = CS_ASCII;
		return;

	default:
		CPR_error("CME_get_dtype: node type not supported");
	}
}


//____________________________________________________________
//  
//		Generate a relation reference.
//  

void CME_relation(GPRE_CTX context, GPRE_REQ request)
{
	RSE rs_stream;
	GPRE_REL relation;
	GPRE_PRC procedure;
	GPRE_NOD inputs, *ptr, *end;

	CMP_check(request, 0);

	if (rs_stream = context->ctx_stream)
	{
		CME_rse(rs_stream, request);
		return;
	}

	if (relation = context->ctx_relation)
	{
		if (sw_ids)
		{
			if ((context->ctx_alias) &&
				!(request->req_database->dbb_flags & DBB_v3))
			{
				STUFF(blr_rid2);
			}
			else
			{
				STUFF(blr_rid);
			}
			STUFF_WORD(relation->rel_id);
		}
		else
		{
			if ((context->ctx_alias) &&
				!(request->req_database->dbb_flags & DBB_v3))
					STUFF(blr_relation2);
			else
				STUFF(blr_relation);
			CMP_stuff_symbol(request, relation->rel_symbol);
		}

		if ((context->ctx_alias) &&
			!(request->req_database->dbb_flags & DBB_v3))
				STUFF_CSTRING(context->ctx_alias);
		STUFF(context->ctx_internal);
	}
	else if (procedure = context->ctx_procedure)
	{
		if (sw_ids)
		{
			STUFF(blr_pid);
			STUFF_WORD(procedure->prc_id);
		}
		else
		{
			STUFF(blr_procedure);
			CMP_stuff_symbol(request, procedure->prc_symbol);
		}
		STUFF(context->ctx_internal);
		STUFF_WORD(procedure->prc_in_count);
		if (inputs = context->ctx_prc_inputs)
			for (ptr = inputs->nod_arg, end = ptr + inputs->nod_count;
				 ptr < end; ptr++)
				CME_expr(*ptr, request);
	}
}


//____________________________________________________________
//  
//		Generate blr for an rse node.
//  

void CME_rse(RSE rse, GPRE_REQ request)
{
	GPRE_NOD temp, union_node, *ptr, *end, list;
	RSE sub_rse;
	SSHORT i;

	if (rse->rse_join_type == (NOD_T) 0)
	{
		if ((rse->rse_flags & RSE_singleton) &&
			!(request->req_database->dbb_flags & DBB_v3))
		{
			STUFF(blr_singular);
		}
		STUFF(blr_rse);
	}
	else
		STUFF(blr_rs_stream);

//  Process unions, if any, otherwise process relations 

	if (union_node = rse->rse_union)
	{
		STUFF(1);
		STUFF(blr_union);
		STUFF(rse->rse_context[0]->ctx_internal);
		STUFF(union_node->nod_count);
		ptr = union_node->nod_arg;
		for (end = ptr + union_node->nod_count; ptr < end; ptr++)
		{
			sub_rse = (RSE) * ptr;
			CME_rse(sub_rse, request);
			cmp_map(sub_rse->rse_map, request);
		}
	}
	else if (sub_rse = rse->rse_aggregate)
	{
		STUFF(1);
		STUFF(blr_aggregate);
		STUFF(sub_rse->rse_map->map_context->ctx_internal);
		CME_rse(sub_rse, request);
		STUFF(blr_group_by);
		if (list = sub_rse->rse_group_by)
		{
			STUFF(list->nod_count);
			ptr = list->nod_arg;
			for (end = ptr + list->nod_count; ptr < end; ptr++)
				CME_expr(*ptr, request);
		}
		else
			STUFF(0);
		cmp_map(sub_rse->rse_map, request);
	}
	else
	{
		STUFF(rse->rse_count);
		for (i = 0; i < rse->rse_count; i++)
			CME_relation(rse->rse_context[i], request);
	}

//  Process the clauses present 

	if (rse->rse_first)
	{
		STUFF(blr_first);
		CME_expr(rse->rse_first, request);
	}

	if (rse->rse_boolean)
	{
		STUFF(blr_boolean);
		CME_expr(rse->rse_boolean, request);
	}

	if (temp = rse->rse_sort)
	{
		STUFF(blr_sort);
		STUFF(temp->nod_count);
		ptr = temp->nod_arg;
		for (i = 0; i < temp->nod_count; i++)
		{
			STUFF((*ptr++) ? blr_descending : blr_ascending);
			CME_expr(*ptr++, request);
		}
	}

	if (temp = rse->rse_reduced)
	{
		STUFF(blr_project);
		STUFF(temp->nod_count);
		ptr = temp->nod_arg;
		for (i = 0; i < temp->nod_count; i++)
			CME_expr(*ptr++, request);
	}

	if (temp = rse->rse_plan)
	{
		STUFF(blr_plan);
		cmp_plan(temp, request);
	}

	if (rse->rse_join_type != (NOD_T) 0
		&& rse->rse_join_type != nod_join_inner)
	{
		STUFF(blr_join_type);
		if (rse->rse_join_type == nod_join_left)
			STUFF(blr_left);
		else if (rse->rse_join_type == nod_join_right)
			STUFF(blr_right);
		else
			STUFF(blr_full);
	}

#ifdef SCROLLABLE_CURSORS
//  generate a statement to be executed if the user scrolls 
//  in a direction other than forward; a message is sent outside 
//  the normal send/receive protocol to specify the direction 
//  and offset to scroll; note that we do this only on a SELECT 
//  type statement and only when talking to a 4.1 engine or greater 

	if (request->req_flags & REQ_sql_cursor &&
		request->req_database->dbb_base_level >= 5)
	{
		STUFF(blr_receive);
		STUFF(request->req_aport->por_msg_number);
		STUFF(blr_seek);
		STUFF(blr_parameter);
		STUFF(request->req_aport->por_msg_number);
		STUFF_WORD(1);
		STUFF(blr_parameter);
		STUFF(request->req_aport->por_msg_number);
		STUFF_WORD(0);
	}
#endif

//  Finish up by making a BLR_END 

	STUFF(blr_end);
}


//____________________________________________________________
//  
//		Compile up an array reference putting
//       out sdl (slice description language)
//  

static GPRE_NOD cmp_array( GPRE_NOD node, GPRE_REQ request)
{
	GPRE_FLD field;
	REF reference;
	TEXT *p;

	CMP_check(request, 0);

	reference = (REF) node->nod_arg[0];

	if (!reference->ref_context)
	{
		CPR_error("cmp_array: context missing");
		return NULL;
	}

	if (!(field = reference->ref_field))
	{
		CPR_error("cmp_array: field missing");
		return NULL;
	}
	else
	{
		/*  Header stuff  */

		reference->ref_sdl = reference->ref_sdl_base = (TEXT *) ALLOC(500);
		reference->ref_sdl_length = 500;
		reference->ref_sdl_ident = CMP_next_ident();
		STUFF_SDL(gds_sdl_version1);
		STUFF_SDL(gds_sdl_struct);
		STUFF_SDL(1);

		/*  The datatype of the array elements  */

		cmp_sdl_dtype(field->fld_array, reference);

		/*  The relation and field identifiers or strings  */

		if (sw_ids)
		{
			STUFF_SDL(gds_sdl_rid);
			STUFF_SDL(reference->ref_id);
			STUFF_SDL(gds_sdl_fid);
			STUFF_SDL(field->fld_id);
		}
		else
		{
			STUFF_SDL(gds_sdl_relation);
			STUFF_SDL(strlen(field->fld_relation->rel_symbol->sym_string));
			for (p = field->fld_relation->rel_symbol->sym_string; *p; p++)
				STUFF_SDL(*p);
			STUFF_SDL(gds_sdl_field);
			STUFF_SDL(strlen(field->fld_symbol->sym_string));
			for (p = field->fld_symbol->sym_string; *p; p++)
				STUFF_SDL(*p);
		}

		/*  The loops for the dimensions  */

		stuff_sdl_loops(reference, field);

		/*  The array element and its "subscripts" */

		stuff_sdl_element(reference, field);

		STUFF_SDL(gds_sdl_eoc);
	}

	reference->ref_sdl_length = reference->ref_sdl - reference->ref_sdl_base;
	reference->ref_sdl = reference->ref_sdl_base;

	if (debug_on)
		PRETTY_print_sdl(reference->ref_sdl, 0, 0, 0);

	return node;
}


//____________________________________________________________
//  
//		Compile up a subscripted array reference
//       from an RSE and output blr for this reference
//  

static GPRE_NOD cmp_array_element( GPRE_NOD node, GPRE_REQ request)
{
	USHORT index_count;

	STUFF(blr_index);

	cmp_field(node, request);

	STUFF(node->nod_count - 1);

	for (index_count = 1; index_count < node->nod_count; index_count++)
		CME_expr(node->nod_arg[index_count], request);

	return node;
}


//____________________________________________________________
//  
//  

static void cmp_cast( GPRE_NOD node, GPRE_REQ request)
{

	STUFF(blr_cast);
	CMP_external_field(request, (GPRE_FLD) node->nod_arg[1]);
	CME_expr(node->nod_arg[0], request);
}


//____________________________________________________________
//  
//		Compile up a field reference.
//  

static GPRE_NOD cmp_field( GPRE_NOD node, GPRE_REQ request)
{
	GPRE_FLD field;
	REF reference;
	GPRE_CTX context;

	CMP_check(request, 0);

	if (!(reference = (REF) node->nod_arg[0]))
	{
		CPR_error("cmp_field: reference missing");
		return NULL;
	}

	if (!(context = reference->ref_context))
	{
		CPR_error("cmp_field: context missing");
		return NULL;
	}

	if (!(field = reference->ref_field))
	{
		CPR_error("cmp_field: field missing");
		return NULL;
	}

	if (!field)
		ib_puts("cmp_field: symbol missing");

	if (field->fld_flags & FLD_dbkey)
	{
		STUFF(blr_dbkey);
		STUFF(context->ctx_internal);
	}
	else if (reference->ref_flags & REF_union)
	{
		STUFF(blr_fid);
		STUFF(context->ctx_internal);
		STUFF_WORD(reference->ref_id);
	}
	else if (sw_ids)
	{
		STUFF(blr_fid);
		STUFF(context->ctx_internal);
		STUFF_WORD(field->fld_id);
	}
	else
	{
		STUFF(blr_field);
		STUFF(context->ctx_internal);
		CMP_stuff_symbol(request, field->fld_symbol);
	}

	return node;
}


//____________________________________________________________
//  
//		Handle a literal expression.
//  

static GPRE_NOD cmp_literal( GPRE_NOD node, GPRE_REQ request)
{
	REF reference;
	char *p;
	char buffer[MAXSYMLEN];
	char *string;
	SSHORT length;
	DSC from, to;
	BOOLEAN negate = FALSE;

	if (node->nod_type == nod_negate)
	{
		node = node->nod_arg[0];
		negate = TRUE;
	}

	STUFF(blr_literal);
	reference = (REF) node->nod_arg[0];
	string = (char *) reference->ref_value;

	if (*string != '"' && *string != '\'')
	{
	/** If the numeric string contains an 'E' or 'e' in it
	then the datatype is double.
    **/
		if (strpbrk(string, "Ee"))
		{
			string = (char *) reference->ref_value;

			if (!(request->req_database->dbb_flags & DBB_v3))
				STUFF(blr_double);
			else if (sw_know_interp)
			{	/* then must be using blr_version5 */
				STUFF(blr_text2);
				STUFF_WORD(ttype_ascii);
			}
			else
				STUFF(blr_text);

			STUFF_WORD(strlen(string));
			while (*string)
				STUFF(*string++);
		}
		else
		{
	/** The numeric string doesn't contain 'E' or 'e' in it.
	    Then this must be a scaled int.  Figure out if there
	    is a '.' in it and calculate its scale.
	**/
			char *ptr;
			char *s_ptr;
			UINT64 uint64_val;
			SINT64 sint64_val;
			long long_val;
			int scale;

			s_ptr = string;

	/** Get the scale **/
			ptr = strpbrk(string, ".");
			if (!ptr)
		/**  No '.' ?, Scale is 0 **/
				scale = 0;
			else
			{
		/** Aha!, there is a '.'. find the scale **/
				scale = (string + (strlen(string) - 1)) - ptr;
				scale = -scale;
			}

			uint64_val = 0;
			while (*s_ptr)
			{
				if (*s_ptr != '.')
					uint64_val = (uint64_val * 10) + (*s_ptr - '0');
				s_ptr++;
			}

	/** see if we can fit the value in a long or INT64.  **/
			if ((uint64_val <= MAX_SLONG) ||
				((uint64_val == (MAX_SLONG + (UINT64) 1))
				 && (negate == TRUE)))
			{
				if (negate == TRUE)
					long_val = -((long) uint64_val);
				else
					long_val = (long) uint64_val;
				STUFF(blr_long);
				STUFF(scale);	/* scale factor */
				STUFF_WORD(long_val);
				STUFF_WORD(long_val >> 16);
			}
			else if ((uint64_val <= MAX_SINT64) ||
					 ((uint64_val == ((UINT64) MAX_SINT64 + 1))
					  && (negate == TRUE)))
			{
				if (negate == TRUE)
					sint64_val = -((SINT64) uint64_val);
				else
					sint64_val = (SINT64) uint64_val;
				STUFF(blr_int64);
				STUFF(scale);	/* scale factor */
				STUFF_WORD(sint64_val);
				STUFF_WORD(sint64_val >> 16);
				STUFF_WORD(sint64_val >> 32);
				STUFF_WORD(sint64_val >> 48);
			}
			else
				CPR_error("cmp_literal : Numeric Value too big");

		}
	}
	else
	{
		/* Remove surrounding quotes from string, etc. */
		p = buffer;

		/* Skip introducing quote mark */
		if (*string)
			string++;

		while (*string)
			*p++ = *string++;

		/* Zap out terminating quote mark */
		*--p = 0;
		length = p - buffer;

		from.dsc_sub_type = ttype_ascii;
		from.dsc_flags = 0;
		from.dsc_dtype = dtype_text;
		from.dsc_length = length;
		from.dsc_address = (UCHAR *) buffer;
		to.dsc_sub_type = 0;
		to.dsc_flags = 0;
		if (reference->ref_flags & REF_sql_date)
		{
			ISC_DATE dt;
			STUFF(blr_sql_date);
			to.dsc_dtype = dtype_sql_date;
			to.dsc_length = sizeof(ISC_DATE);
			to.dsc_address = (UCHAR *) & dt;
			MOVG_move(&from, &to);
			STUFF_WORD(dt);
			STUFF_WORD(dt >> 16);
			return node;
		}
		else if (reference->ref_flags & REF_timestamp)
		{
			ISC_TIMESTAMP ts;
			STUFF(blr_timestamp);
			to.dsc_dtype = dtype_timestamp;
			to.dsc_length = sizeof(ISC_TIMESTAMP);
			to.dsc_address = (UCHAR *) & ts;
			MOVG_move(&from, &to);
			STUFF_WORD(ts.timestamp_date);
			STUFF_WORD(ts.timestamp_date >> 16);
			STUFF_WORD(ts.timestamp_time);
			STUFF_WORD(ts.timestamp_time >> 16);
			return node;
		}
		else if (reference->ref_flags & REF_sql_time)
		{
			ISC_TIME itim;
			STUFF(blr_sql_time);
			to.dsc_dtype = dtype_sql_time;
			to.dsc_length = sizeof(ISC_DATE);
			to.dsc_address = (UCHAR *) & itim;
			MOVG_move(&from, &to);
			STUFF_WORD(itim);
			STUFF_WORD(itim >> 16);
			return node;
		}
		else if (!(reference->ref_flags & REF_ttype))
			STUFF(blr_text);
		else
		{
			STUFF(blr_text2);
			STUFF_WORD(reference->ref_ttype);
		}

		STUFF_WORD(length);
		for (string = buffer; *string;)
			STUFF(*string++);
	}

	return node;
}


//____________________________________________________________
//  
//		Generate a map for a union or aggregate rse.
//  

static void cmp_map( MAP map, GPRE_REQ request)
{
	MEL element;

	STUFF(blr_map);
	STUFF_WORD(map->map_count);

	for (element = map->map_elements; element; element = element->mel_next)
	{
		STUFF_WORD(element->mel_position);
		CME_expr(element->mel_expr, request);
	}
}


//____________________________________________________________
//  
//		Generate an access plan for a query.
//  

static void cmp_plan( GPRE_NOD plan_expression, GPRE_REQ request)
{
	GPRE_NOD list, node, arg, *ptr, *end, *ptr2, *end2;

//  stuff the join type 

	list = plan_expression->nod_arg[1];
	if (list->nod_count > 1)
	{
		if (node = plan_expression->nod_arg[0])
			STUFF(blr_merge);
		else
			STUFF(blr_join);
		STUFF(list->nod_count);
	}

//  stuff one or more plan items 

	for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end; ptr++)
	{
		node = *ptr;
		if (node->nod_type == nod_plan_expr)
		{
			cmp_plan(node, request);
			continue;
		}

		/* if we're here, it must be a nod_plan_item */

		STUFF(blr_retrieve);

		/* stuff the relation--the relation id itself is redundant except 
		   when there is a need to differentiate the base tables of views */

		CME_relation((GPRE_CTX) node->nod_arg[2], request);

		/* now stuff the access method for this stream */

		arg = node->nod_arg[1];
		switch (arg->nod_type)
		{
		case nod_natural:
			STUFF(blr_sequential);
			break;

		case nod_index_order:
			STUFF(blr_navigational);
			STUFF_CSTRING((TEXT *) arg->nod_arg[0]);
			break;

		case nod_index:
			STUFF(blr_indices);
			arg = arg->nod_arg[0];
			STUFF(arg->nod_count);
			for (ptr2 = arg->nod_arg, end2 = ptr2 + arg->nod_count;
				 ptr2 < end2; ptr2++)
				STUFF_CSTRING((TEXT *) * ptr2);
			break;
		}
	}
}


//____________________________________________________________
//  
//		Print out the correct blr for
//       this datatype.
//  

static void cmp_sdl_dtype( GPRE_FLD field, REF reference)
{
	TEXT s[50];

	switch (field->fld_dtype)
	{
	case dtype_cstring:
		/* 3.2j has new, tagged blr intruction for cstring */

		if (sw_know_interp)
		{
			STUFF_SDL(blr_cstring2);
			STUFF_SDL_WORD(sw_interp);
			STUFF_SDL_WORD(field->fld_length);
		}
		else
		{
			STUFF_SDL(blr_cstring);
			STUFF_SDL_WORD(field->fld_length);
		}
		break;

	case dtype_text:
		/* 3.2j has new, tagged blr intruction for text too */

		if (sw_know_interp)
		{
			STUFF_SDL(blr_text2);
			STUFF_SDL_WORD(sw_interp);
			STUFF_SDL_WORD(field->fld_length);
		}
		else
		{
			STUFF_SDL(blr_text);
			STUFF_SDL_WORD(field->fld_length);
		}
		break;

	case dtype_varying:
		/* 3.2j has new, tagged blr intruction for varying also */

		if (sw_know_interp)
		{
			STUFF_SDL(blr_varying2);
			STUFF_SDL_WORD(sw_interp);
			STUFF_SDL_WORD(field->fld_length);
		}
		else
		{
			STUFF_SDL(blr_varying);
			STUFF_SDL_WORD(field->fld_length);
		}
		break;

	case dtype_short:
		STUFF_SDL(blr_short);
		STUFF_SDL(field->fld_scale);
		break;

	case dtype_long:
		STUFF_SDL(blr_long);
		STUFF_SDL(field->fld_scale);
		break;

	case dtype_quad:
		STUFF_SDL(blr_quad);
		STUFF_SDL(field->fld_scale);
		break;

// ** Begin date/time/timestamp support *
	case dtype_sql_date:
		STUFF_SDL(blr_sql_date);
		break;
	case dtype_sql_time:
		STUFF_SDL(blr_sql_time);
		break;

	case dtype_timestamp:
		STUFF_SDL(blr_timestamp);
		break;
// ** End date/time/timestamp support *

	case dtype_int64:
		STUFF_SDL(blr_int64);
		break;

	case dtype_real:
		STUFF_SDL(blr_float);
		break;

	case dtype_double:
		if (sw_d_float)
			STUFF_SDL(blr_d_float);
		else
			STUFF_SDL(blr_double);
		break;

	default:
		sprintf(s, "datatype %d not understood", field->fld_dtype);
		CPR_error(s);
	}
}


//____________________________________________________________
//  
//		Compile a reference to a user defined function.
//  

static GPRE_NOD cmp_udf( GPRE_NOD node, GPRE_REQ request)
{
	GPRE_NOD list, *ptr, *end;
	UDF udf;
	TEXT *p;

	udf = (UDF) node->nod_arg[1];
	STUFF(blr_function);
	p = udf->udf_function;
	STUFF(strlen(p));

	while (*p)
		STUFF(*p++);

	list = node->nod_arg[0];
	if (list)
	{
		STUFF(list->nod_count);

		for (ptr = list->nod_arg, end = ptr + list->nod_count; ptr < end;
			 ptr++)
			CME_expr(*ptr, request);
	}
	else
		STUFF(0);

	return node;
}


//____________________________________________________________
//  
//		Process a random value expression.
//  

static GPRE_NOD cmp_value( GPRE_NOD node, GPRE_REQ request)
{
	REF reference, flag;

	reference = (REF) node->nod_arg[0];

	if (!reference)
		ib_puts("cmp_value: missing reference");

	if (!reference->ref_port)
		ib_puts("cmp_value: port missing");

	if (flag = reference->ref_null)
	{
		STUFF(blr_parameter2);
		STUFF(reference->ref_port->por_msg_number);
		STUFF_WORD(reference->ref_parameter);
		STUFF_WORD(flag->ref_parameter);
	}
	else
	{
		STUFF(blr_parameter);
		STUFF(reference->ref_port->por_msg_number);
		STUFF_WORD(reference->ref_parameter);
	}

	return node;
}


//____________________________________________________________
//  
//		Figure out a text length from a datatype and a length
//  

static USHORT get_string_len( GPRE_FLD field)
{
	DSC tmp_dsc;

	assert(field->fld_dtype <= MAX_UCHAR);

	tmp_dsc.dsc_dtype = (UCHAR) field->fld_dtype;
	tmp_dsc.dsc_length = field->fld_length;
	tmp_dsc.dsc_scale = 0;
	tmp_dsc.dsc_sub_type = 0;
	tmp_dsc.dsc_flags = 0;
	tmp_dsc.dsc_address = NULL;

	return DSC_string_length(&tmp_dsc);
}


//____________________________________________________________
//  
//		Write out a null-terminated 
//		string with one byte of length.
//  

static void stuff_cstring( GPRE_REQ request, const char *string)
{
	UCHAR c;

	STUFF(strlen(string));

	while (c = *string++) {
		STUFF(c);
	}
}


//____________________________________________________________
//  
//		Write to the sdl string, the do
//       loop for a particular dimension.
//  

static void stuff_sdl_dimension(
								DIM dimension,
								REF reference, SSHORT dimension_count)
{

//   In the future, when we support slices, new code to handle the
//   user-defined slice ranges will be here.  

	if (dimension->dim_lower == 1)
	{
		STUFF_SDL(gds_sdl_do1);
		STUFF_SDL(dimension_count);
		stuff_sdl_number(dimension->dim_upper, reference);
	}
	else
	{
		STUFF_SDL(gds_sdl_do2);
		STUFF_SDL(dimension_count);
		stuff_sdl_number(dimension->dim_lower, reference);
		stuff_sdl_number(dimension->dim_upper, reference);
	}
}


//____________________________________________________________
//  
//		Write the element information
//       (including the subscripts) to
//       the SDL string for the array.
//  

static void stuff_sdl_element( REF reference, GPRE_FLD field)
{
	SSHORT i;

	STUFF_SDL(gds_sdl_element);
	STUFF_SDL(1);
	STUFF_SDL(gds_sdl_scalar);
	STUFF_SDL(0);

	STUFF_SDL(field->fld_array_info->ary_dimension_count);

//  Fortran needs the array in column-major order 

	if (sw_language == lang_fortran)
	{
		for (i = field->fld_array_info->ary_dimension_count - 1; i >= 0; i--)
		{
			STUFF_SDL(gds_sdl_variable);
			STUFF_SDL(i);
		}
	}
	else
	{
		for (i = 0; i < field->fld_array_info->ary_dimension_count; i++)
		{
			STUFF_SDL(gds_sdl_variable);
			STUFF_SDL(i);
		}
	}
}


//____________________________________________________________
//  
//		Write loop information to the SDL
//       string for the array dimensions.
//  

static void stuff_sdl_loops( REF reference, GPRE_FLD field)
{
	SSHORT i;
	DIM dimension;

//  Fortran needs the array in column-major order 

	if (sw_language == lang_fortran)
	{
		for (dimension = field->fld_array_info->ary_dimension;
			 dimension->dim_next; dimension = dimension->dim_next);

		for (i = 0; i < field->fld_array_info->ary_dimension_count;
			 i++, dimension = dimension->dim_previous)
			stuff_sdl_dimension(dimension, reference, i);
	}
	else
		for (i = 0, dimension = field->fld_array_info->ary_dimension;
			 i < field->fld_array_info->ary_dimension_count;
			 i++, dimension = dimension->dim_next)
				stuff_sdl_dimension(dimension, reference, i);
}


//____________________________________________________________
//  
//		Write the number in the 'smallest'
//       form possible to the SDL string.
//  

static void stuff_sdl_number( SLONG number, REF reference)
{

	if ((number > -16) && (number < 15))
	{
		STUFF_SDL(gds_sdl_tiny_integer);
		STUFF_SDL(number);
	}
	else if ((number > -32768) && (number < 32767))
	{
		STUFF_SDL(gds_sdl_short_integer);
		STUFF_SDL_WORD(number);
	}
	else
	{
		STUFF_SDL(gds_sdl_long_integer);
		STUFF_SDL_LONG(number);
	}
}
