/*
 *	PROGRAM:	Interactive SQL utility
 *	MODULE:		extract.epp
 *	DESCRIPTION:	Definition extract routines
 *
 * 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): ______________________________________.
 * Revision 1.3  2000/11/22 17:07:25  patrickgriffin
 * In get_procedure_args change comment style from // to c style
 *
 * ...pat
 *
 * 2001.09.09 Claudio Valderrama: procedure's parameter names may need
 *   double quotes if they are in dialect 3 and have special characters.
 * 2001.09.21 Claudio Valderrama: Show correct mechanism for UDF parameters
 *   and support the RETURNS PARAMETER <n> syntax.
 * 2001.10.01 Claudio Valderrama: list_all_grants2() and EXTRACT_list_grants()
 *   to better organize the code that should be called to handle SHOW GRANTS.
 *
 * Revision 1.2  2000/11/18 16:49:24  fsg
 * Increased PRINT_BUFFER_LENGTH to 2048 to show larger plans
 * Fixed Bug #122563 in extract.e get_procedure_args
 * Apparently this has to be done in show.e also,
 * but that is for another day :-)
 *
 * 2003.02.04 Dmitry Yemanov: support for universal triggers
 */

#include "firebird.h"
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>				// isdigit
#include "../jrd/common.h"
#include "../jrd/constants.h"
#include "../jrd/ibase.h"
#include "../jrd/gds_proto.h"
#include "../jrd/intlobj_new.h"
#include "../isql/isql.h"
#include "../isql/extra_proto.h"
#include "../isql/isql_proto.h"
#include "../isql/show_proto.h"
#include "../jrd/ini.h"
#include "../jrd/obj.h"
#include "../jrd/ods.h"
#include "../common/utils_proto.h"
#include "../jrd/constants.h"

using MsgFormat::SafeArg;


DATABASE DB = EXTERN COMPILETIME "yachts.lnk";

static bool extract_rel_constraints(const char* relation_name);
static void get_procedure_args(const char*);
static void list_all_grants();
static processing_state list_all_grants2(bool, const SCHAR*);
static void list_all_procs();
static void list_all_tables(LegacyTables flag, SSHORT);
static void list_all_triggers();
static void list_check();
static void list_charsets();
static void list_collations();
static void list_create_db();
static void list_domain_table(const SCHAR*, SSHORT);
static void list_domains(SSHORT);
static void list_exception();
static void list_filters();
static void list_foreign();
static void list_functions();
static void list_generators();
static void list_index();
static void list_views();

extern const char* trigger_action(int);

static const char* Procterm = "^";	// TXNN: script use only

/* Maybe 512 would be really enough as Print_buffer size, but
   as we have PRINT_BUFFER_LENGTH in isql.h, we should use it
   FSG 17.Nov.2000
*/

static TEXT Print_buffer[PRINT_BUFFER_LENGTH];
static TEXT SQL_identifier[BUFFER_LENGTH128];
static TEXT SQL_identifier2[BUFFER_LENGTH128];



int EXTRACT_ddl(LegacyTables flag, const SCHAR* tabname)
{
/**************************************
 *
 *	E X T R A C T _ d d l
 *
 **************************************
 *
 * Functional description
 *	Extract all sql information
 *	0 flag means SQL only tables. 1 flag means all tables
 *
 **************************************/
	bool did_attach = false;

	if (!DB)
	{
		if (isc_attach_database(gds_status, 0, isqlGlob.global_Db_name, &DB, 0, NULL))
		{
			ISQL_errmsg(gds_status);
			return FINI_ERROR;
		}
		did_attach = true;

		// Make it read owner name to display grantor correctly
		SHOW_read_owner();
	}

	ISQL_get_version(false);
	if (isqlGlob.SQL_dialect != isqlGlob.db_SQL_dialect)
	{
		isqlGlob.printf("/*=========================================================*/%s", NEWLINE);
		isqlGlob.printf("/*=                                                      ==*/%s", NEWLINE);
		isqlGlob.printf("/*=     Command Line -sqldialect %d is overwritten by    ==*/%s",
				isqlGlob.SQL_dialect, NEWLINE);
		isqlGlob.printf("/*=     Database SQL Dialect %d.                          ==*/%s",
				isqlGlob.db_SQL_dialect, NEWLINE);
		isqlGlob.printf("/*=                                                      ==*/%s", NEWLINE);
		isqlGlob.printf("/*=========================================================*/%s", NEWLINE);
	}
	isqlGlob.printf(NEWLINE);

	isqlGlob.printf("SET SQL DIALECT %d; %s", isqlGlob.db_SQL_dialect, NEWLINE);

	isqlGlob.printf(NEWLINE);

	bool did_start = false;
	if (!gds_trans)
	{
		if (isc_start_transaction(gds_status, &gds_trans, 1, &DB, 0, NULL))
		{
			ISQL_errmsg(gds_status);
			return FINI_ERROR;
		}
		did_start = true;
	}

	const SSHORT default_char_set_id = ISQL_get_default_char_set_id();
	int ret_code = FINI_OK;

	// If a table name was passed, extract only that table and domains
	if (*tabname)
	{
		if (EXTRACT_list_table(tabname, NULL, true, default_char_set_id))
		{
			SCHAR errbuf[MSG_LENGTH];
			ISQL_msg_get(NOT_FOUND_MSG, errbuf, SafeArg() << tabname);
			STDERROUT(errbuf);
			ret_code = FINI_ERROR;
		}
	}
	else
	{
		list_create_db();
		list_filters();
		list_charsets();
		list_collations();
		list_functions();
		list_generators();
		list_domains(default_char_set_id);
		list_all_tables(flag, default_char_set_id);
		list_index();
		list_foreign();
		list_views();
		list_check();
		list_exception();
		list_all_procs();
		list_all_triggers();
		list_all_grants();
		SHOW_comments(false); // Let's make this an option later.
	}

	if (gds_trans && did_start)
		if (isc_commit_transaction(gds_status, &gds_trans))
		{
			ISQL_errmsg(gds_status);
			return FINI_ERROR;
		}

	if (DB && did_attach)
	{
		if (isc_detach_database(gds_status, &DB))
		{
			ISQL_errmsg(gds_status);
			return FINI_ERROR;
		}
		DB = 0;
	}

	return ret_code;
}


processing_state EXTRACT_list_grants(const SCHAR* terminator)
{
/**************************************
 *
 *	E X T R A C T _ l i s t _ g r a n t s
 *
 **************************************
 *
 * Functional description
 *	Print the permissions on all user tables, views and procedures.
 *
 **************************************/
	return list_all_grants2(false, terminator);
}


int EXTRACT_list_table(const SCHAR* relation_name,
						  const SCHAR* new_name,
						  bool domain_flag,
						  SSHORT default_char_set_id)
{
/**************************************
 *
 *	E X T R A C T _ l i s t _ t a b l e
 *
 **************************************
 *
 * Functional description
 *	Shows columns, types, info for a given table name
 *	and text of views.
 *	Use a GDML query to get the info and print it.
 *	If a new_name is passed, substitute it for relation_name
 *
 *	relation_name -- Name of table to investigate
 *	new_name -- Name of a new name for a replacement table
 *	domain_flag -- extract needed domains before the table
 *	default_char_set_id -- character set def to supress
 *
 **************************************/
/**************************************
 *	default_char_set_id warrants special
 *	consideration.  If the metadata for a
 *	table is being extracted when there is
 *	really no need to redundantly and repeatedly
 *	list the databases default character set
 *	for every field.
 *
 *	At the same time there is a need to list
 *	the character set NONE when it is not
 *	the default character set for the database.
 *
 *	EXCEPT!  If the metadata is being extracted
 *	with the intention of coping that tables structure
 *	into another database, and it is not possible
 *	to know the default character set for the
 *	target database, then list every fields
 *	character set.  This includes the character
 *	set NONE.
 *
 *	Fields with no character set definition will
 *	not have any character set listed.
 *
 *	Use -1 as the default_char_set_id
 *	in this case.
 *
 *	POTENTIAL TRAP!  Consider the following:
 *	When copying a table from one database
 *	to another how should fields using the
 *	default character set be handled?
 *
 *	If both databases have the same default
 *	character set, then there is no problem
 *	or confusion.
 *
 *	If the databases have different default
 *	character sets then should fields using
 *	the default is the source database use
 *	the default of the target database?
 *
 **************************************/

	bool first = true;
	SCHAR char_sets[86];
	//USHORT rel_flags = 0;
	//bool intchar = false;
	// CVC: shouldn't this var be initialized inside the FOR statement instead?
	// CVC: 2004.12.03: I discovered someone made this variable obsolete;
	// it's set but never used. Hope the change was right. I commented it.

	rel_t rel_type = rel_persistent;

	if (ENCODE_ODS(isqlGlob.major_ods, isqlGlob.minor_ods) >= ODS_11_1)
	{
		FOR RT IN RDB$RELATIONS
			WITH RT.RDB$RELATION_NAME EQ relation_name AND
			RT.RDB$RELATION_TYPE NOT MISSING

			rel_type = (rel_t) RT.RDB$RELATION_TYPE;

		END_FOR
		ON_ERROR
			ISQL_errmsg(gds_status);
			return FINI_ERROR;
		END_ERROR;
	}

// Query to obtain relation detail information

	FOR REL IN RDB$RELATIONS CROSS
		RFR IN RDB$RELATION_FIELDS CROSS
		FLD IN RDB$FIELDS WITH
		RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND
		RFR.RDB$RELATION_NAME EQ REL.RDB$RELATION_NAME AND
		REL.RDB$RELATION_NAME EQ relation_name
		SORTED BY RFR.RDB$FIELD_POSITION, RFR.RDB$FIELD_NAME

		SSHORT collation = 0;
		SSHORT char_set_id = 0;

		if (first)
		{
			first = false;
			// Do we need to print domains
			if (domain_flag)
				list_domain_table (relation_name, default_char_set_id);

			fb_utils::exact_name(REL.RDB$OWNER_NAME);
			isqlGlob.printf("%s/* Table: %s, Owner: %s */%s",
					 NEWLINE,
					 relation_name,
					 REL.RDB$OWNER_NAME,
					 NEWLINE);

			if (rel_type == rel_global_temp_preserve || rel_type == rel_global_temp_delete)
			{
				isqlGlob.printf("CREATE GLOBAL TEMPORARY TABLE ");
			}
			else
			{
				isqlGlob.printf("CREATE TABLE ");
			}

			if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			{
				if (new_name)
					ISQL_copy_SQL_id (new_name, SQL_identifier, DBL_QUOTE);
				else
					ISQL_copy_SQL_id (relation_name, SQL_identifier, DBL_QUOTE);
				isqlGlob.printf("%s ", SQL_identifier);
			}
			else
			{
				isqlGlob.printf("%s ", new_name ? new_name : relation_name);
			}

			if (!REL.RDB$EXTERNAL_FILE.NULL)
			{
				ISQL_copy_SQL_id (REL.RDB$EXTERNAL_FILE, SQL_identifier2, SINGLE_QUOTE);
				isqlGlob.printf("EXTERNAL FILE %s ", SQL_identifier2);
			}
			isqlGlob.printf("(");
		}
		else
		{
			isqlGlob.printf(",%s%s", NEWLINE, TAB_AS_SPACES);
		}

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			ISQL_copy_SQL_id (fb_utils::exact_name(RFR.RDB$FIELD_NAME), SQL_identifier, DBL_QUOTE);
			isqlGlob.printf("%s ", SQL_identifier);
		}
		else
			isqlGlob.printf("%s ", fb_utils::exact_name(RFR.RDB$FIELD_NAME));

		/*
		** Check first for computed fields, then domains.
		** If this is a known domain, then just print the domain rather than type
		** Domains won't have length, array, or blob definitions, but they
		** may have not null, default and check overriding their definitions
		*/

		if (!FLD.RDB$COMPUTED_BLR.NULL)
		{
			isqlGlob.printf("COMPUTED BY ");
			if (!FLD.RDB$COMPUTED_SOURCE.NULL)
				ISQL_print_validation (isqlGlob.Out, &FLD.RDB$COMPUTED_SOURCE, true, gds_trans);
		}
		else if (!(fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) && FLD.RDB$SYSTEM_FLAG != 1))
		{
			fb_utils::exact_name(FLD.RDB$FIELD_NAME);
			if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			{
				ISQL_copy_SQL_id (FLD.RDB$FIELD_NAME, SQL_identifier, DBL_QUOTE);
				isqlGlob.prints(SQL_identifier);
			}
			else
				isqlGlob.prints(FLD.RDB$FIELD_NAME);

			// International character sets
			// Print only the character set
			if ((FLD.RDB$FIELD_TYPE == T_CHAR || FLD.RDB$FIELD_TYPE == VARCHAR) &&
				!RFR.RDB$COLLATION_ID.NULL && RFR.RDB$COLLATION_ID)
			{
				char_sets[0] = '\0';
				collation = RFR.RDB$COLLATION_ID;
				char_set_id = FLD.RDB$CHARACTER_SET_ID;
				ISQL_get_character_sets (FLD.RDB$CHARACTER_SET_ID, 0, true, false, char_sets);
				if (char_sets[0])
					isqlGlob.prints(char_sets);
			}
		}
		else
		{
			// Look through types array
			for (int i = 0; Column_types[i].type; i++)
			{
				if (FLD.RDB$FIELD_TYPE == Column_types[i].type)
				{
					bool precision_known = false;

					if (isqlGlob.major_ods >= ODS_VERSION10)
					{
						// Handle Integral subtypes NUMERIC and DECIMAL
						if ((FLD.RDB$FIELD_TYPE == SMALLINT) ||
							(FLD.RDB$FIELD_TYPE == INTEGER)  ||
							(FLD.RDB$FIELD_TYPE == BIGINT))
						{
							FOR FLD1 IN RDB$FIELDS WITH
								FLD1.RDB$FIELD_NAME EQ FLD.RDB$FIELD_NAME

								/* We are ODS >= 10 and could be any Dialect */
								if (!FLD1.RDB$FIELD_PRECISION.NULL)
								{
									/* We are Dialect >=3 since FIELD_PRECISION is non-NULL */
									if (FLD1.RDB$FIELD_SUB_TYPE > 0 &&
										FLD1.RDB$FIELD_SUB_TYPE <= MAX_INTSUBTYPES)
									{
										sprintf (Print_buffer, "%s(%d, %d)",
												 Integral_subtypes[FLD1.RDB$FIELD_SUB_TYPE],
												 FLD1.RDB$FIELD_PRECISION,
												 -FLD1.RDB$FIELD_SCALE);
										precision_known = true;
									}
								}
							END_FOR
							ON_ERROR
								ISQL_errmsg (isc_status);
								return ps_ERR;
							END_ERROR;
						}
					}

					if (!precision_known)
					{

						// Take a stab at numerics and decimals
						if ((FLD.RDB$FIELD_TYPE == SMALLINT) && (FLD.RDB$FIELD_SCALE < 0))
						{
							sprintf (Print_buffer, "NUMERIC(4, %d)", -FLD.RDB$FIELD_SCALE);
						}
						else if ((FLD.RDB$FIELD_TYPE == INTEGER) && (FLD.RDB$FIELD_SCALE < 0))
						{
							sprintf (Print_buffer, "NUMERIC(9, %d)", -FLD.RDB$FIELD_SCALE);
						}
						else if ((FLD.RDB$FIELD_TYPE == DOUBLE_PRECISION) && (FLD.RDB$FIELD_SCALE < 0))
						{
							sprintf (Print_buffer, "NUMERIC(15, %d)", -FLD.RDB$FIELD_SCALE);
						}
						else
							sprintf (Print_buffer, "%s", Column_types[i].type_name);
					}
					isqlGlob.prints(Print_buffer);
					break;
				}
			}

			if ((FLD.RDB$FIELD_TYPE == T_CHAR) || (FLD.RDB$FIELD_TYPE == VARCHAR))
			{
				if (FLD.RDB$CHARACTER_LENGTH.NULL)
				{

					isqlGlob.printf("(%d)", FLD.RDB$FIELD_LENGTH);
				}
				else
				{
					isqlGlob.printf("(%d)", FLD.RDB$CHARACTER_LENGTH);
				}
			}

			// Catch arrays after printing the type

			if (!FLD.RDB$DIMENSIONS.NULL)
				ISQL_array_dimensions (FLD.RDB$FIELD_NAME);

			if (FLD.RDB$FIELD_TYPE == BLOB)
			{
				const int subtype = FLD.RDB$FIELD_SUB_TYPE;
				isqlGlob.printf(" SUB_TYPE ");
				if ((subtype > 0) && (subtype <= MAX_BLOBSUBTYPES))
					isqlGlob.prints(Sub_types[subtype]);
				else
				{
					isqlGlob.printf("%d", subtype);
				}
				isqlGlob.printf(" SEGMENT SIZE %u", (USHORT) FLD.RDB$SEGMENT_LENGTH);
			}

			// International character sets
			if ((FLD.RDB$FIELD_TYPE == T_CHAR || FLD.RDB$FIELD_TYPE == VARCHAR ||
				 FLD.RDB$FIELD_TYPE == BLOB) &&
				!FLD.RDB$CHARACTER_SET_ID.NULL)
			{
				char_sets[0] = '\0';

				// Override rdb$fields id with relation_fields if present

				if (!RFR.RDB$COLLATION_ID.NULL)
					collation = RFR.RDB$COLLATION_ID;
				else if (!FLD.RDB$COLLATION_ID.NULL)
					collation = FLD.RDB$COLLATION_ID;

				if (!FLD.RDB$CHARACTER_SET_ID.NULL)
					char_set_id = FLD.RDB$CHARACTER_SET_ID;
				if ((char_set_id != default_char_set_id) || collation)
					ISQL_get_character_sets (char_set_id, 0, false, false, char_sets);
				if (char_sets[0])
					isqlGlob.prints(char_sets);
				// CVC: Someone deleted the code that checks intchar when handling collations
				// several lines below, so it didn't have any effect. Commented it.
				//if (!char_set_id)
				//	intchar = true;
			}
		}


		// Handle defaults for columns

		if (!RFR.RDB$DEFAULT_SOURCE.NULL)
		{
			isqlGlob.printf(" ");
			SHOW_print_metadata_text_blob (isqlGlob.Out, &RFR.RDB$DEFAULT_SOURCE);
		}

		/* The null flag is either 1 or null (for nullable) .  if there is
		**  a constraint name, print that too.  Domains cannot have named
		**  constraints.  The column name is in rdb$trigger_name in
		**  rdb$check_constraints.  We hope we get at most one row back.
		*/

		if (RFR.RDB$NULL_FLAG == 1)
		{
			FOR RCO IN RDB$RELATION_CONSTRAINTS CROSS
				CON IN RDB$CHECK_CONSTRAINTS WITH
				CON.RDB$TRIGGER_NAME = RFR.RDB$FIELD_NAME AND
				CON.RDB$CONSTRAINT_NAME = RCO.RDB$CONSTRAINT_NAME AND
				RCO.RDB$CONSTRAINT_TYPE EQ "NOT NULL" AND
				RCO.RDB$RELATION_NAME = RFR.RDB$RELATION_NAME

				if (!fb_utils::implicit_integrity(CON.RDB$CONSTRAINT_NAME))
				{
					fb_utils::exact_name(CON.RDB$CONSTRAINT_NAME);
					if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
					{
						ISQL_copy_SQL_id (CON.RDB$CONSTRAINT_NAME, SQL_identifier, DBL_QUOTE);
						sprintf (Print_buffer, " CONSTRAINT %s", SQL_identifier);
					}
					else
						isqlGlob.printf(" CONSTRAINT %s", CON.RDB$CONSTRAINT_NAME);
				}
			END_FOR
			ON_ERROR
				ISQL_errmsg (gds_status);
				return FINI_ERROR;
			END_ERROR;

			isqlGlob.printf(" NOT NULL");
		}

		// Handle collations after defaults

		if (collation)
		{
			char_sets[0] = '\0';
			ISQL_get_character_sets (char_set_id, collation, true, false, char_sets);
			if (char_sets[0])
				isqlGlob.prints(char_sets);
		}
	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return FINI_ERROR;
	END_ERROR;

	// Do primary and unique keys only. References come later.
	if (!extract_rel_constraints(relation_name))
		return FINI_ERROR;

	// Check constaints are now deferred

	if (first) // we extracted nothing
		return FINI_ERROR;

	if (rel_type == rel_global_temp_preserve)
	{
		isqlGlob.printf(")%sON COMMIT PRESERVE ROWS", NEWLINE);
	}
	else if (rel_type == rel_global_temp_delete)
	{
		isqlGlob.printf(")%sON COMMIT DELETE ROWS", NEWLINE);
	}
	else
	{
		isqlGlob.printf(")");
	}
	isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);
	return FINI_OK;
}


static bool extract_rel_constraints(const char* relation_name)
{
/**************************************************
 *
 *	e x t r a c t _ r e l _ c o n s t r a i n t s
 *
 **************************************************
 *
 * Functional description
 *	This function extracts the relation constraints of type PK and UK.
 *
 **************************************/

	SCHAR collist[BUFFER_LENGTH512 * 2];

	// PK and UK are always based on indices for now, hence this join is safe.
	FOR RELC IN RDB$RELATION_CONSTRAINTS
		CROSS IDX IN RDB$INDICES OVER RDB$INDEX_NAME
		WITH
		(RELC.RDB$CONSTRAINT_TYPE EQ "PRIMARY KEY" OR
		RELC.RDB$CONSTRAINT_TYPE EQ "UNIQUE") AND
		RELC.RDB$RELATION_NAME EQ relation_name
		SORTED BY RELC.RDB$CONSTRAINT_TYPE, RELC.RDB$CONSTRAINT_NAME

		isqlGlob.printf(",%s", NEWLINE);
		fb_utils::exact_name(RELC.RDB$CONSTRAINT_NAME);
		fb_utils::exact_name(IDX.RDB$INDEX_NAME);

		// If the name of the constraint is not INTEG_..., print it
 		if (!fb_utils::implicit_integrity(RELC.RDB$CONSTRAINT_NAME))
		{
			if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			{
				ISQL_copy_SQL_id (RELC.RDB$CONSTRAINT_NAME, SQL_identifier, DBL_QUOTE);
				isqlGlob.printf("CONSTRAINT %s ", SQL_identifier);
			}
			else
				isqlGlob.printf("CONSTRAINT %s ", RELC.RDB$CONSTRAINT_NAME);
		}

		ISQL_get_index_segments (collist, sizeof(collist), RELC.RDB$INDEX_NAME, true);

		const bool isPK = !strncmp (RELC.RDB$CONSTRAINT_TYPE, "PRIMARY", 7);
		if (isPK)
			isqlGlob.printf("PRIMARY KEY (%s)", collist);
		else //if (!strncmp (RELC.RDB$CONSTRAINT_TYPE, "UNIQUE", 6)) // redundant
			isqlGlob.printf("UNIQUE (%s)", collist);

		// Yes, the same RDB$... naming convention is used for both domains and indices.
		const bool explicit_index = (isPK && !fb_utils::implicit_pk(IDX.RDB$INDEX_NAME) ||
			!isPK && !fb_utils::implicit_domain(IDX.RDB$INDEX_NAME)) &&
		    strcmp(RELC.RDB$CONSTRAINT_NAME, IDX.RDB$INDEX_NAME);
		const bool descending_index = !IDX.RDB$INDEX_TYPE.NULL && IDX.RDB$INDEX_TYPE == 1;
		if (explicit_index || descending_index)
		{
			isqlGlob.printf(" USING %sINDEX", descending_index ? "DESCENDING " : "");
			if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			{
				ISQL_copy_SQL_id(IDX.RDB$INDEX_NAME, SQL_identifier, DBL_QUOTE);
				isqlGlob.printf(" %s", SQL_identifier);
			}
			else
				isqlGlob.printf(" %s", IDX.RDB$INDEX_NAME);
		}

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return false;
	END_ERROR;

	return true;
}


static void get_procedure_args(const char* proc_name)
{
/**************************************
 *
 *	g e t _ p r o c e d u r e _ a r g s
 *
 **************************************
 *
 * Functional description
 *	This function extracts the procedure parameters and adds it to the
 *	extract file
 *
 **************************************/
	SCHAR char_sets[95];

// query to retrieve the parameters.


/* placed the two identical code blocks into one
   for loop as suggested by Ann H. and Claudio V.
   FSG 18.Nov.2000
*/

	//  Parameter types 0 = input
	//  Parameter types 1 = return

	for (SSHORT ptype = 0; ptype < 2; ptype++)
	{
		bool first_time = true;

		FOR PRM IN RDB$PROCEDURE_PARAMETERS CROSS
			FLD IN RDB$FIELDS WITH
			PRM.RDB$PROCEDURE_NAME = proc_name AND
			PRM.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND
			PRM.RDB$PARAMETER_TYPE = ptype
			SORTED BY PRM.RDB$PARAMETER_NUMBER

			bool prm_collation_id_null = true;
			SSHORT prm_collation_id = 0;

			bool prm_default_source_null = true;
			ISC_QUAD prm_default_source;

			bool prm_null_flag_null = true;
			bool prm_null_flag;

			prm_mech_t prm_mech = prm_mech_normal;

			if (ENCODE_ODS(isqlGlob.major_ods, isqlGlob.minor_ods) >= ODS_11_1)
			{
				FOR PRM2 IN RDB$PROCEDURE_PARAMETERS
					WITH PRM2.RDB$PROCEDURE_NAME EQ PRM.RDB$PROCEDURE_NAME AND
						 PRM2.RDB$PARAMETER_NAME EQ PRM.RDB$PARAMETER_NAME

					prm_collation_id_null = PRM2.RDB$COLLATION_ID.NULL;
					prm_collation_id = PRM2.RDB$COLLATION_ID;

					prm_default_source_null = PRM2.RDB$DEFAULT_SOURCE.NULL;
					prm_default_source = PRM2.RDB$DEFAULT_SOURCE;

					prm_null_flag_null = PRM2.RDB$NULL_FLAG.NULL;
					prm_null_flag = PRM2.RDB$NULL_FLAG;

					if (!PRM2.RDB$PARAMETER_MECHANISM.NULL)
						prm_mech = (prm_mech_t) PRM2.RDB$PARAMETER_MECHANISM;
				END_FOR
			}

			char relationName[BUFFER_LENGTH128] = "";
			char relationField[BUFFER_LENGTH128] = "";

			if (ENCODE_ODS(isqlGlob.major_ods, isqlGlob.minor_ods) >= ODS_11_2)
			{
				FOR PRM2 IN RDB$PROCEDURE_PARAMETERS
					WITH PRM2.RDB$PROCEDURE_NAME EQ PRM.RDB$PROCEDURE_NAME AND
						 PRM2.RDB$PARAMETER_NAME EQ PRM.RDB$PARAMETER_NAME

					if (!PRM2.RDB$RELATION_NAME.NULL)
					{
						fb_utils::exact_name(PRM2.RDB$RELATION_NAME);
						if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
							ISQL_copy_SQL_id(PRM2.RDB$RELATION_NAME, relationName, DBL_QUOTE);
						else
							strcpy(relationName, PRM2.RDB$RELATION_NAME);
					}

					if (!PRM2.RDB$FIELD_NAME.NULL)
					{
						fb_utils::exact_name(PRM2.RDB$FIELD_NAME);
						if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
							ISQL_copy_SQL_id(PRM2.RDB$FIELD_NAME, relationField, DBL_QUOTE);
						else
							strcpy(relationField, PRM2.RDB$FIELD_NAME);
					}

				END_FOR
			}

			if (first_time)
			{
				first_time = false;
				if (ptype == 0)
				{ // this is the input part
					isqlGlob.printf("(");
				}
				else
				{ // we are in the output part
					isqlGlob.printf("RETURNS (");
				}
			}
			else
			{
				isqlGlob.printf(",%s", NEWLINE);
			}

			fb_utils::exact_name(PRM.RDB$PARAMETER_NAME);

			// CVC: Parameter names need check for dialect 3, too.

			if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
				ISQL_copy_SQL_id (PRM.RDB$PARAMETER_NAME, SQL_identifier, DBL_QUOTE);
			else
				strcpy (SQL_identifier, PRM.RDB$PARAMETER_NAME);

			isqlGlob.printf("%s ", SQL_identifier);

			const bool basedOnColumn = relationName[0] && relationField[0];
			if (!fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) || FLD.RDB$SYSTEM_FLAG == 1 ||
				basedOnColumn)
			{
				if (prm_mech == prm_mech_type_of)
					isqlGlob.printf("TYPE OF ");

				if (basedOnColumn)
					isqlGlob.printf("COLUMN %s.%s", relationName, relationField);
				else
				{
					fb_utils::exact_name(FLD.RDB$FIELD_NAME);
					if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
					{
						ISQL_copy_SQL_id (FLD.RDB$FIELD_NAME, SQL_identifier, DBL_QUOTE);
						isqlGlob.prints(SQL_identifier);
					}
					else
						isqlGlob.prints(FLD.RDB$FIELD_NAME);
				}

				// International character sets
				// Print only the collation
				if ((FLD.RDB$FIELD_TYPE == T_CHAR || FLD.RDB$FIELD_TYPE == VARCHAR) &&
					!prm_collation_id_null && prm_collation_id)
				{
					char_sets[0] = '\0';
					ISQL_get_character_sets(FLD.RDB$CHARACTER_SET_ID, prm_collation_id, true,
						!prm_null_flag_null && prm_null_flag, char_sets);
					if (char_sets[0])
						isqlGlob.prints(char_sets);
				}
				else if (!prm_null_flag_null && prm_null_flag)
					isqlGlob.printf(" NOT NULL");

				if (ptype == 0) // input, try to extract default and make Vlad happy.
				{
					if (!prm_default_source_null)
					{
						isqlGlob.printf(" ");
						SHOW_print_metadata_text_blob(isqlGlob.Out, &prm_default_source);
					}
				}
			}
			else
			{
				// Get column type name to print
				for (int i = 0; Column_types[i].type; i++)
				{
					if (FLD.RDB$FIELD_TYPE == Column_types[i].type)
					{
						bool precision_known = false;

						if (isqlGlob.major_ods >= ODS_VERSION10)
						{
							// Handle Integral subtypes NUMERIC and DECIMAL
							if ((FLD.RDB$FIELD_TYPE == SMALLINT) ||
								(FLD.RDB$FIELD_TYPE == INTEGER) ||
								(FLD.RDB$FIELD_TYPE == BIGINT))
							{
								/* We are ODS >= 10 and could be any Dialect */

								FOR FLD1 IN RDB$FIELDS WITH
									FLD1.RDB$FIELD_NAME EQ FLD.RDB$FIELD_NAME

									if (!FLD1.RDB$FIELD_PRECISION.NULL)
									{
										/* We are Dialect >=3 since FIELD_PRECISION is non-NULL */
										if (FLD1.RDB$FIELD_SUB_TYPE > 0 &&
											FLD1.RDB$FIELD_SUB_TYPE <= MAX_INTSUBTYPES)
										{
											sprintf (Print_buffer, "%s(%d, %d)",
													Integral_subtypes[FLD1.RDB$FIELD_SUB_TYPE],
													FLD1.RDB$FIELD_PRECISION,
													-FLD1.RDB$FIELD_SCALE);
											precision_known = true;
										}
									}
								END_FOR
								ON_ERROR
									ISQL_errmsg (isc_status);
									return;
								END_ERROR;
							}
						}
						if (!precision_known)
						{
							// Take a stab at numerics and decimals
							if ((FLD.RDB$FIELD_TYPE == SMALLINT) && (FLD.RDB$FIELD_SCALE < 0))
							{
								sprintf (Print_buffer, "NUMERIC(4, %d)", -FLD.RDB$FIELD_SCALE);
							}
							else if ((FLD.RDB$FIELD_TYPE == INTEGER) && (FLD.RDB$FIELD_SCALE < 0))
							{
								sprintf (Print_buffer, "NUMERIC(9, %d)", -FLD.RDB$FIELD_SCALE);
							}
							else if ((FLD.RDB$FIELD_TYPE == DOUBLE_PRECISION) && (FLD.RDB$FIELD_SCALE < 0))
							{
								sprintf (Print_buffer, "NUMERIC(15, %d)", -FLD.RDB$FIELD_SCALE);
							}
							else
							{
								sprintf (Print_buffer, "%s", Column_types[i].type_name);
							}
						}
						isqlGlob.prints(Print_buffer);
						break;
					}
				}

				/* Changed this to return RDB$CHARACTER_LENGTH if available
				   Fix for Bug #122563
				   FSG 18.Nov.2000
				*/
				if ((FLD.RDB$FIELD_TYPE == T_CHAR) || (FLD.RDB$FIELD_TYPE == VARCHAR))
				{
					if (FLD.RDB$CHARACTER_LENGTH.NULL)
					{
						isqlGlob.printf("(%d)", FLD.RDB$FIELD_LENGTH);
					}
					else
					{
						isqlGlob.printf("(%d)", FLD.RDB$CHARACTER_LENGTH);
					}
				}

				// Show international character sets and collations
				if (!FLD.RDB$COLLATION_ID.NULL || !FLD.RDB$CHARACTER_SET_ID.NULL)
				{
					char_sets[0] = 0;

					SSHORT collation = 0;

					if (!prm_collation_id_null)
						collation = prm_collation_id;
					else if (!FLD.RDB$COLLATION_ID.NULL)
						collation = FLD.RDB$COLLATION_ID;

					if (FLD.RDB$CHARACTER_SET_ID.NULL)
						FLD.RDB$CHARACTER_SET_ID = 0;

					ISQL_get_character_sets (FLD.RDB$CHARACTER_SET_ID, collation, false,
						!prm_null_flag_null && prm_null_flag, char_sets);
					if (char_sets[0])
						isqlGlob.prints(char_sets);
				}
				else if (!prm_null_flag_null && prm_null_flag)
					isqlGlob.printf(" NOT NULL");

				if (ptype == 0) // input, try to extract default and make Vlad happy.
				{
					if (!prm_default_source_null)
					{
						isqlGlob.printf(" ");
						SHOW_print_metadata_text_blob(isqlGlob.Out, &prm_default_source);
					}
					else if (!FLD.RDB$DEFAULT_SOURCE.NULL)
					{
						isqlGlob.printf(" ");
						SHOW_print_metadata_text_blob(isqlGlob.Out, &FLD.RDB$DEFAULT_SOURCE);
					}
				}
			}
		END_FOR
		ON_ERROR
			ISQL_errmsg(gds_status);
			return;
		END_ERROR;

		// If there was at least one param, close parens

		if (!first_time)
		{
			isqlGlob.printf(")%s", NEWLINE);
		}

	}						// end for ptype
	isqlGlob.printf("AS %s", NEWLINE);
}


static void list_all_grants()
{
/**************************************
 *
 *	l i s t _ a l l _ g r a n t s
 *
 **************************************
 *
 * Functional description
 *	Print the permissions on all user tables, views and procedures.
 *
 *	Wrapper around list_all_grants2().
 *
 **************************************/
	list_all_grants2(true, isqlGlob.global_Term);
}


static processing_state list_all_grants2(bool show_role_list, const SCHAR* terminator)
{
/**************************************
 *
 *	l i s t _ a l l _ g r a n t s
 *
 **************************************
 *
 * Functional description
 *	Print the permissions on all user tables.
 *
 *	Get separate permissions on table/views and then procedures.
 *
 **************************************/
	bool first_role = true;
	TEXT prev_owner[44];

	// Only extract (not show) wants the role list and with escaped quoted identifiers.
	const bool mangle = show_role_list;

	// Process GRANT roles
	if (isqlGlob.major_ods >= ODS_VERSION9 && show_role_list)
	{
		prev_owner[0] = '\0';

		FOR XX IN RDB$ROLES
			SORTED BY XX.RDB$ROLE_NAME

			bool system_flag = false;
			if (isqlGlob.major_ods >= ODS_VERSION11)
			{
				FOR SF IN RDB$ROLES
					WITH SF.RDB$ROLE_NAME = XX.RDB$ROLE_NAME
					 AND SF.RDB$SYSTEM_FLAG > 0

					system_flag = true;

				END_FOR
			}

			if (! system_flag)
			{
				if (first_role)
				{
					isqlGlob.printf("%s/* Grant roles for this database */%s", NEWLINE, NEWLINE);
					first_role = false;
				}

				// Null terminate name string
				fb_utils::exact_name(XX.RDB$ROLE_NAME);
				fb_utils::exact_name(XX.RDB$OWNER_NAME);

				if (strcmp (prev_owner, XX.RDB$OWNER_NAME) != 0)
				{
					isqlGlob.printf("%s/* Role: %s, Owner: %s */%s",
							 NEWLINE,
							 XX.RDB$ROLE_NAME,
							 XX.RDB$OWNER_NAME,
							 NEWLINE);
					strcpy (prev_owner, XX.RDB$OWNER_NAME);
				}

				if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
				{
					ISQL_copy_SQL_id (XX.RDB$ROLE_NAME, SQL_identifier, DBL_QUOTE);
					isqlGlob.printf("CREATE ROLE %s;%s", SQL_identifier, NEWLINE);
				}
				else
					isqlGlob.printf("CREATE ROLE %s;%s", XX.RDB$ROLE_NAME, NEWLINE);
			}

		END_FOR
		ON_ERROR
			ISQL_errmsg(gds_status);
		    return OBJECT_NOT_FOUND;
		END_ERROR;
	}

	// This version of cursor gets only sql tables identified by security class
	// and misses views, getting only null view_source

	char banner[100];
	sprintf(banner, "%s/* Grant permissions for this database */%s", NEWLINE, NEWLINE);

	bool first = true;

	FOR REL IN RDB$RELATIONS WITH
		(REL.RDB$SYSTEM_FLAG NE 1 OR REL.RDB$SYSTEM_FLAG MISSING) AND
		REL.RDB$SECURITY_CLASS STARTING "SQL$"
		SORTED BY REL.RDB$RELATION_NAME

		// Null terminate name string

		fb_utils::exact_name(REL.RDB$RELATION_NAME);

		const processing_state rc =
			SHOW_grants2(REL.RDB$RELATION_NAME, terminator, obj_relation,
						   first ? banner : 0, mangle);
		if (rc == SKIP) {
			first = false;
		}

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return OBJECT_NOT_FOUND;
	END_ERROR;

	if (first)
		SHOW_grant_roles2(terminator, &first, banner, mangle);
	else
		SHOW_grant_roles2(terminator, 0, 0, mangle);


	// Again for stored procedures
	FOR PRC IN RDB$PROCEDURES
		SORTED BY PRC.RDB$PROCEDURE_NAME

		// Null terminate name string

		fb_utils::exact_name(PRC.RDB$PROCEDURE_NAME);

		const processing_state rc =
			SHOW_grants2(PRC.RDB$PROCEDURE_NAME, terminator, obj_procedure,
						   first ? banner: 0, mangle);
		if (rc == SKIP)
			first = false;

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return OBJECT_NOT_FOUND;
	END_ERROR;

	return first_role && first ? OBJECT_NOT_FOUND : SKIP;
}


static void list_all_procs()
{
/**************************************
 *
 *	l i s t _ a l l _ p r o c s
 *
 **************************************
 *
 * Functional description
 *	Shows text of a stored procedure given a name.
 *	or lists procedures if no argument.
 *	Since procedures may reference each other, we will create all
 *	dummy procedures of the correct name, then alter these to their
 *	correct form.
 *      Add the parameter names when these procedures are created.
 *
 *	procname -- Name of procedure to investigate
 *
 **************************************/
	bool header = true;
	static const char* create_procedure_str1 = "CREATE PROCEDURE %s ";
	static const char* create_procedure_str2 = "BEGIN EXIT; END %s%s";
	static const char* create_procedure_str3 = "BEGIN SUSPEND; END %s%s";


	//  First the dummy procedures
	// create the procedures with their parameters

	FOR PRC IN RDB$PROCEDURES
		SORTED BY PRC.RDB$PROCEDURE_NAME
		if (header)
		{
			isqlGlob.printf("COMMIT WORK;%s", NEWLINE);
			isqlGlob.printf("SET AUTODDL OFF;%s", NEWLINE);
			isqlGlob.printf("SET TERM %s %s%s", Procterm, isqlGlob.global_Term, NEWLINE);
			isqlGlob.printf("%s/* Stored procedures */%s", NEWLINE, NEWLINE);
			header = false;
		}
		fb_utils::exact_name(PRC.RDB$PROCEDURE_NAME);
		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			ISQL_copy_SQL_id (PRC.RDB$PROCEDURE_NAME, SQL_identifier, DBL_QUOTE);
			isqlGlob.printf(create_procedure_str1, SQL_identifier);
		}
		else
		{
			isqlGlob.printf(create_procedure_str1, PRC.RDB$PROCEDURE_NAME);
		}

		get_procedure_args (PRC.RDB$PROCEDURE_NAME);

		prc_t proc_type = prc_legacy;

		if (ENCODE_ODS(isqlGlob.major_ods, isqlGlob.minor_ods) >= ODS_11_1)
		{
			FOR PRC2 IN RDB$PROCEDURES
				WITH PRC2.RDB$PROCEDURE_NAME EQ PRC.RDB$PROCEDURE_NAME AND
				PRC2.RDB$PROCEDURE_TYPE NOT MISSING

				proc_type = (prc_t) PRC2.RDB$PROCEDURE_TYPE;

			END_FOR
			ON_ERROR
				ISQL_errmsg(gds_status);
				return;
			END_ERROR;
		}

		// We'll assume for safety that prc_legacy can be mapped to prc_selectable.
		if (proc_type == prc_executable)
			isqlGlob.printf(create_procedure_str2, Procterm, NEWLINE);
		else
			isqlGlob.printf(create_procedure_str3, Procterm, NEWLINE);

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;

	// This query gets the procedure name and the source.  We then nest a query
	// to retrieve the parameters. Alter is used, because the procedures are already there
	TEXT msg[MSG_LENGTH];

	FOR PRC IN RDB$PROCEDURES
		SORTED BY PRC.RDB$PROCEDURE_NAME

		fb_utils::exact_name(PRC.RDB$PROCEDURE_NAME);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			ISQL_copy_SQL_id (PRC.RDB$PROCEDURE_NAME, SQL_identifier, DBL_QUOTE);
			isqlGlob.printf("%sALTER PROCEDURE %s ", NEWLINE, SQL_identifier);
		}
		else
			isqlGlob.printf("%sALTER PROCEDURE %s ", NEWLINE, PRC.RDB$PROCEDURE_NAME);
		get_procedure_args (PRC.RDB$PROCEDURE_NAME);

		// Print the procedure body

		if (!PRC.RDB$PROCEDURE_SOURCE.NULL)
			SHOW_print_metadata_text_blob (isqlGlob.Out, &PRC.RDB$PROCEDURE_SOURCE);

		isqlGlob.printf(" %s%s", Procterm, NEWLINE);

	END_FOR
	ON_ERROR
		ISQL_msg_get(GEN_ERR, msg, SafeArg() << isc_sqlcode(gds_status));
		STDERROUT(msg);			// Statement failed, SQLCODE = %d\n\n
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;

	// Only reset the terminators is there were procs to print
	if (!header)
	{
		isqlGlob.printf("SET TERM %s %s%s", isqlGlob.global_Term, Procterm, NEWLINE);
		isqlGlob.printf("COMMIT WORK %s%s", isqlGlob.global_Term, NEWLINE);
		isqlGlob.printf("SET AUTODDL ON;%s", NEWLINE);
	}
}


static void list_all_tables(LegacyTables flag, SSHORT default_char_set_id)
{
/**************************************
 *
 *	l i s t _ a l l _ t a b l e s
 *
 **************************************
 *
 * Functional description
 *	Extract the names of all user tables from
 *	rdb$relations.  Filter SQL tables by
 *	security class after we fetch them
 *	Parameters:  flag -- 0, get all tables
 *
 **************************************/

/* This version of cursor gets only sql tables identified by security class
   and misses views, getting only null view_source */

	FOR REL IN RDB$RELATIONS WITH
		(REL.RDB$SYSTEM_FLAG NE 1 OR REL.RDB$SYSTEM_FLAG MISSING) AND
		REL.RDB$VIEW_BLR MISSING
		SORTED BY REL.RDB$RELATION_NAME

		// If this is not an SQL table and we aren't doing ALL objects
		if ((REL.RDB$FLAGS.NULL || !(REL.RDB$FLAGS & REL_sql)) && (flag != ALL_objects) )
			continue;
		// Null terminate name string

		fb_utils::exact_name(REL.RDB$RELATION_NAME);

		if (flag || !strncmp (REL.RDB$SECURITY_CLASS, "SQL$", 4))
			EXTRACT_list_table (REL.RDB$RELATION_NAME, NULL,  false, default_char_set_id);
	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		ROLLBACK;
		return;
	END_ERROR;

}


static void list_all_triggers()
{
/**************************************
 *
 *	l i s t _ a l l _ t r i g g e r s
 *
 **************************************
 *
 * Functional description
 *	Lists triggers in general on non-system
 *	tables with sql source only.
 *
 **************************************/
	bool header = true;

	// Query gets the trigger info for non-system triggers with
	// source that are not part of an SQL constraint.

	FOR TRG IN RDB$TRIGGERS
		WITH (TRG.RDB$SYSTEM_FLAG EQ 0 OR TRG.RDB$SYSTEM_FLAG MISSING) AND
			 TRG.RDB$RELATION_NAME MISSING
		SORTED BY TRG.RDB$TRIGGER_TYPE, TRG.RDB$TRIGGER_SEQUENCE, TRG.RDB$TRIGGER_NAME

		if (header)
		{
			isqlGlob.printf("SET TERM %s %s%s", Procterm, isqlGlob.global_Term, NEWLINE);
			isqlGlob.printf("%s/* Triggers only will work for SQL triggers */%s",
					 NEWLINE,
					 NEWLINE);
			header = false;
		}
		fb_utils::exact_name(TRG.RDB$TRIGGER_NAME);

		if (TRG.RDB$TRIGGER_INACTIVE.NULL)
			TRG.RDB$TRIGGER_INACTIVE = 0;

		//  If trigger is not SQL put it in comments
		if (!(TRG.RDB$FLAGS & TRG_sql))
			isqlGlob.printf("/* ");

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			ISQL_copy_SQL_id (TRG.RDB$TRIGGER_NAME, SQL_identifier,  DBL_QUOTE);
		else
			strcpy (SQL_identifier,  TRG.RDB$TRIGGER_NAME);

		isqlGlob.printf("CREATE TRIGGER %s %s%s %s POSITION %d %s",
				 SQL_identifier, NEWLINE,
				 (TRG.RDB$TRIGGER_INACTIVE ? "INACTIVE" : "ACTIVE"),
				 trigger_action (TRG.RDB$TRIGGER_TYPE), TRG.RDB$TRIGGER_SEQUENCE,
				 NEWLINE);

		if (!TRG.RDB$TRIGGER_SOURCE.NULL)
			SHOW_print_metadata_text_blob (isqlGlob.Out, &TRG.RDB$TRIGGER_SOURCE);

		isqlGlob.printf(" %s%s", Procterm, NEWLINE);
		isqlGlob.printf(NEWLINE);

		if (!(TRG.RDB$FLAGS & TRG_sql))
		{
			isqlGlob.printf("*/%s", NEWLINE);
		}

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;

	FOR TRG IN RDB$TRIGGERS CROSS REL IN RDB$RELATIONS OVER RDB$RELATION_NAME
		//WITH (REL.RDB$SYSTEM_FLAG NE 1 OR REL.RDB$SYSTEM_FLAG MISSING) AND
		//NOT (ANY CHK IN RDB$CHECK_CONSTRAINTS WITH
		//TRG.RDB$TRIGGER_NAME EQ CHK.RDB$TRIGGER_NAME)
		WITH (TRG.RDB$SYSTEM_FLAG EQ 0 OR TRG.RDB$SYSTEM_FLAG MISSING)
		SORTED BY TRG.RDB$RELATION_NAME, TRG.RDB$TRIGGER_TYPE,
			TRG.RDB$TRIGGER_SEQUENCE, TRG.RDB$TRIGGER_NAME

		if (header)
		{
			isqlGlob.printf("SET TERM %s %s%s", Procterm, isqlGlob.global_Term, NEWLINE);
			isqlGlob.printf("%s/* Triggers only will work for SQL triggers */%s",
					 NEWLINE,
					 NEWLINE);
			header = false;
		}
		fb_utils::exact_name(TRG.RDB$TRIGGER_NAME);
		fb_utils::exact_name(TRG.RDB$RELATION_NAME);

		if (TRG.RDB$TRIGGER_INACTIVE.NULL)
			TRG.RDB$TRIGGER_INACTIVE = 0;

		//  If trigger is not SQL put it in comments
		if (!(TRG.RDB$FLAGS & TRG_sql))
			isqlGlob.printf("/* ");

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			ISQL_copy_SQL_id (TRG.RDB$TRIGGER_NAME, SQL_identifier,  DBL_QUOTE);
			ISQL_copy_SQL_id (TRG.RDB$RELATION_NAME, SQL_identifier2, DBL_QUOTE);
		}
		else
		{
			strcpy (SQL_identifier,  TRG.RDB$TRIGGER_NAME);
			strcpy (SQL_identifier2, TRG.RDB$RELATION_NAME);
		}

		isqlGlob.printf("CREATE TRIGGER %s FOR %s %s%s %s POSITION %d %s",
				 SQL_identifier, SQL_identifier2, NEWLINE,
				 (TRG.RDB$TRIGGER_INACTIVE ? "INACTIVE" : "ACTIVE"),
				 trigger_action (TRG.RDB$TRIGGER_TYPE), TRG.RDB$TRIGGER_SEQUENCE,
				 NEWLINE);

		if (!TRG.RDB$TRIGGER_SOURCE.NULL)
			SHOW_print_metadata_text_blob (isqlGlob.Out, &TRG.RDB$TRIGGER_SOURCE);

		isqlGlob.printf(" %s%s", Procterm, NEWLINE);
		isqlGlob.printf(NEWLINE);

		if (!(TRG.RDB$FLAGS & TRG_sql))
		{
			isqlGlob.printf("*/%s", NEWLINE);
		}

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;

	if (!header)
	{
		isqlGlob.printf("COMMIT WORK %s%s", Procterm, NEWLINE);
		isqlGlob.printf("SET TERM %s %s%s", isqlGlob.global_Term, Procterm, NEWLINE);
	}
}


static void list_check()
{
/**************************************
 *
 *	l i s t _ c h e c k
 *
 **************************************
 *
 * Functional description
 *	List check constraints for all objects to allow forward references
 *
 **************************************/

	// Query gets the check clauses for triggers stored for check constraints

	FOR TRG IN RDB$TRIGGERS CROSS
		CHK IN RDB$CHECK_CONSTRAINTS WITH
		TRG.RDB$TRIGGER_TYPE EQ 1 AND
		TRG.RDB$SYSTEM_FLAG EQ int(fb_sysflag_check_constraint) AND
		TRG.RDB$TRIGGER_NAME EQ CHK.RDB$TRIGGER_NAME AND
		(ANY RELC IN RDB$RELATION_CONSTRAINTS WITH
		CHK.RDB$CONSTRAINT_NAME EQ RELC.RDB$CONSTRAINT_NAME
		AND RELC.RDB$CONSTRAINT_TYPE EQ "CHECK")
		REDUCED TO CHK.RDB$CONSTRAINT_NAME
		SORTED BY CHK.RDB$CONSTRAINT_NAME

		fb_utils::exact_name(TRG.RDB$RELATION_NAME);

		isqlGlob.printf(NEWLINE);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			ISQL_copy_SQL_id (TRG.RDB$RELATION_NAME, SQL_identifier, DBL_QUOTE);
			isqlGlob.printf("ALTER TABLE %s ADD %s%s", SQL_identifier, NEWLINE, TAB_AS_SPACES);
		}
		else
			isqlGlob.printf("ALTER TABLE %s ADD %s%s", TRG.RDB$RELATION_NAME, NEWLINE, TAB_AS_SPACES);

		// If the name of the constraint is not INTEG_..., print it
		if (!fb_utils::implicit_integrity(CHK.RDB$CONSTRAINT_NAME))
		{
			fb_utils::exact_name(CHK.RDB$CONSTRAINT_NAME);
			if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			{
				ISQL_copy_SQL_id (CHK.RDB$CONSTRAINT_NAME, SQL_identifier, DBL_QUOTE);
				isqlGlob.printf("CONSTRAINT %s ", SQL_identifier);
			}
			else
				isqlGlob.printf("CONSTRAINT %s ", CHK.RDB$CONSTRAINT_NAME);
		}

		if (!TRG.RDB$TRIGGER_SOURCE.NULL)
			SHOW_print_metadata_text_blob (isqlGlob.Out, &TRG.RDB$TRIGGER_SOURCE);

		isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;
}


static void list_charsets()
{
/**************************************
 *
 *	l i s t _ c h a r s e t s
 *
 **************************************
 *
 * Functional description
 *	Alter character sets.
 *
 **************************************/

	bool first = true;

	FOR CS IN RDB$CHARACTER_SETS
		WITH NOT CS.RDB$CHARACTER_SET_NAME EQ CS.RDB$DEFAULT_COLLATE_NAME
		SORTED BY CS.RDB$CHARACTER_SET_NAME

		if (first)
		{
			isqlGlob.printf("%s/*  Character sets */%s", NEWLINE, NEWLINE);
			first = false;
		}

		isqlGlob.printf("ALTER CHARACTER SET ");

		fb_utils::exact_name(CS.RDB$CHARACTER_SET_NAME);
		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			ISQL_copy_SQL_id (CS.RDB$CHARACTER_SET_NAME, SQL_identifier, DBL_QUOTE);
		else
			strcpy (SQL_identifier, CS.RDB$CHARACTER_SET_NAME);
		isqlGlob.printf("%s", SQL_identifier);

		fb_utils::exact_name(CS.RDB$DEFAULT_COLLATE_NAME);
		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			ISQL_copy_SQL_id (CS.RDB$DEFAULT_COLLATE_NAME, SQL_identifier, DBL_QUOTE);
		else
			strcpy (SQL_identifier, CS.RDB$DEFAULT_COLLATE_NAME);
		isqlGlob.printf(" SET DEFAULT COLLATION %s", SQL_identifier);

		isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);
	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;

	if (! first)
		isqlGlob.printf(NEWLINE);
}


static void list_collations()
{
/**************************************
 *
 *	l i s t _ c o l l a t i o n s
 *
 **************************************
 *
 * Functional description
 *	Re create all non-system collations
 *
 **************************************/

	if (isqlGlob.major_ods < ODS_VERSION11)
		return;

	bool first = true;

	FOR CL IN RDB$COLLATIONS CROSS
		CS IN RDB$CHARACTER_SETS WITH
		CS.RDB$CHARACTER_SET_ID EQ CL.RDB$CHARACTER_SET_ID AND
		(CL.RDB$SYSTEM_FLAG MISSING OR CL.RDB$SYSTEM_FLAG NE 1)
		SORTED BY CS.RDB$CHARACTER_SET_NAME, CL.RDB$COLLATION_NAME

		if (first)
		{
			isqlGlob.printf("%s/*  Collations */%s", NEWLINE, NEWLINE);
			first = false;
		}

		isqlGlob.printf("CREATE COLLATION ");

		fb_utils::exact_name(CL.RDB$COLLATION_NAME);
		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			ISQL_copy_SQL_id (CL.RDB$COLLATION_NAME, SQL_identifier, DBL_QUOTE);
		else
			strcpy (SQL_identifier, CL.RDB$COLLATION_NAME);
		isqlGlob.printf("%s", SQL_identifier);

		fb_utils::exact_name(CS.RDB$CHARACTER_SET_NAME);
		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			ISQL_copy_SQL_id (CS.RDB$CHARACTER_SET_NAME, SQL_identifier, DBL_QUOTE);
		else
			strcpy (SQL_identifier, CS.RDB$CHARACTER_SET_NAME);
		isqlGlob.printf(" FOR %s", SQL_identifier);

		if (!CL.RDB$BASE_COLLATION_NAME.NULL)
		{
			fb_utils::exact_name(CL.RDB$BASE_COLLATION_NAME);
			ISQL_copy_SQL_id (CL.RDB$BASE_COLLATION_NAME, SQL_identifier, SINGLE_QUOTE);
			isqlGlob.printf(" FROM EXTERNAL (%s)", SQL_identifier);
		}

		if (!CL.RDB$COLLATION_ATTRIBUTES.NULL)
		{
			if (CL.RDB$COLLATION_ATTRIBUTES & TEXTTYPE_ATTR_PAD_SPACE)
				isqlGlob.printf(" PAD SPACE");

			if (CL.RDB$COLLATION_ATTRIBUTES & TEXTTYPE_ATTR_CASE_INSENSITIVE)
				isqlGlob.printf(" CASE INSENSITIVE");

			if (CL.RDB$COLLATION_ATTRIBUTES & TEXTTYPE_ATTR_ACCENT_INSENSITIVE)
				isqlGlob.printf(" ACCENT INSENSITIVE");
		}

		if (!CL.RDB$SPECIFIC_ATTRIBUTES.NULL)
		{
			isqlGlob.printf(" '");
			SHOW_print_metadata_text_blob (isqlGlob.Out, &CL.RDB$SPECIFIC_ATTRIBUTES);
			isqlGlob.printf("'");
		}

		isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);
	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;

	isqlGlob.printf(NEWLINE);
}


#ifdef NOT_USED_OR_REPLACED
static void print_set(bool* set_used)
{
/**************************************
 *
 *	p r i n t _ s e t
 *
 **************************************
 *
 * Functional description
 *    print the word "SET"
 *    in the first line of the ALTER DATABASE
 *    settings options. Also, add trailing
 *    comma for end of prior line if needed.
 *
 * uses Print_buffer, a global
 *
 **************************************/
	if (!*set_used)
	{
		isqlGlob.printf("  SET ");
		*set_used = true;
	}
	else
	{
		isqlGlob.printf(", %s      ", NEWLINE);
	}
}
#endif


static void list_create_db()
{
/**************************************
 *
 *	l i s t _ c r e a t e _ d b
 *
 **************************************
 *
 * Functional description
 *	Print the create database command if requested.  At least put
 *	the page size in a comment with the extracted db name
 *
 **************************************/
	static const SCHAR page_items[] =
	{
		isc_info_page_size,
		isc_info_end
	};

// Comment out the create database if no db param was specified
	bool nodb = false;
	if (!*isqlGlob.global_Target_db)
	{
		isqlGlob.printf("/* ");
		strcpy(isqlGlob.global_Target_db, isqlGlob.global_Db_name);
		nodb = true;
	}
	isqlGlob.printf("CREATE DATABASE '%s' ", isqlGlob.global_Target_db);

	// Get the page size from db_info call
	SCHAR info_buf[20];
	// CVC: Finally I got the idea: translate is associated with WISQL that
	// no longer exists. Localizing the messages means also not printing
	// any CRLF and therefore the output looks ugly.
	const bool translate = true;
	if (SHOW_dbb_parameters(DB, info_buf, page_items, sizeof(page_items), translate, " "))
	{
		isqlGlob.printf("%s", info_buf);
	}

	FOR DBP IN RDB$DATABASE
		WITH DBP.RDB$CHARACTER_SET_NAME NOT MISSING
		AND DBP.RDB$CHARACTER_SET_NAME != " ";
		isqlGlob.printf("DEFAULT CHARACTER SET %s",
			fb_utils::exact_name(DBP.RDB$CHARACTER_SET_NAME));
	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;

	if (nodb)
	{
		isqlGlob.printf(" */%s", NEWLINE);
	}
	else
	{
		isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);
	}

// List secondary files and shadows as alter db and create shadow in comment

	bool first = true;

	FOR FIL IN RDB$FILES
		SORTED BY FIL.RDB$SHADOW_NUMBER, FIL.RDB$FILE_SEQUENCE

		if (first)
		{
			isqlGlob.printf("%s/* Add secondary files in comments %s", NEWLINE, NEWLINE);
		}

		first = false;
		// reset nulls to zero

		if (FIL.RDB$FILE_FLAGS.NULL)
			FIL.RDB$FILE_FLAGS = 0;
		if (FIL.RDB$FILE_LENGTH.NULL)
			FIL.RDB$FILE_LENGTH = 0;
		if (FIL.RDB$FILE_SEQUENCE.NULL)
			FIL.RDB$FILE_SEQUENCE = 0;
		if (FIL.RDB$FILE_START.NULL)
			FIL.RDB$FILE_START = 0;
		fb_utils::exact_name(FIL.RDB$FILE_NAME);

		// Pure secondary files
		if (FIL.RDB$FILE_FLAGS == 0)
		{
			isqlGlob.printf("%sALTER DATABASE ADD FILE '%s'", NEWLINE, FIL.RDB$FILE_NAME);
			if (FIL.RDB$FILE_START)
			{
				isqlGlob.printf(" STARTING %ld", FIL.RDB$FILE_START);
			}
			if (FIL.RDB$FILE_LENGTH)
			{
				isqlGlob.printf(" LENGTH %ld", FIL.RDB$FILE_LENGTH);
			}
			isqlGlob.printf(NEWLINE);
		}

		if (FIL.RDB$FILE_FLAGS & FILE_shadow)
		{
			if (FIL.RDB$FILE_SEQUENCE)
			{
				isqlGlob.printf("%sFILE '%s' ", TAB_AS_SPACES, FIL.RDB$FILE_NAME);
			}
			else
			{
				isqlGlob.printf("%sCREATE SHADOW %d '%s' ",
						 NEWLINE,
						 FIL.RDB$SHADOW_NUMBER,
						 FIL.RDB$FILE_NAME);
				if (FIL.RDB$FILE_FLAGS & FILE_inactive)
					isqlGlob.printf("INACTIVE ");
				if (FIL.RDB$FILE_FLAGS & FILE_manual)
					isqlGlob.printf("MANUAL ");
				else
					isqlGlob.printf("AUTO ");
				if (FIL.RDB$FILE_FLAGS & FILE_conditional)
					isqlGlob.printf("CONDITIONAL ");
			}
			if (FIL.RDB$FILE_LENGTH)
			{
				isqlGlob.printf("LENGTH %ld ", FIL.RDB$FILE_LENGTH);
			}
			if (FIL.RDB$FILE_START)
			{
				isqlGlob.printf("STARTING %ld ", FIL.RDB$FILE_START);
			}
			isqlGlob.printf(NEWLINE);
		}

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;

	if (!first)
	{
		if (nodb)
		{
			isqlGlob.printf("%s */%s", NEWLINE, NEWLINE);
		}
		else
		{
			isqlGlob.printf("%s%s%s", isqlGlob.global_Term, NEWLINE, NEWLINE);
		}
	}
}


static void list_domain_table(const SCHAR* table_name, SSHORT default_char_set_id)
{
/**************************************
 *
 *	l i s t _ d o m a i n _ t a b l e
 *
 **************************************
 *
 * Functional description
 *	List domains as identified by fields with any constraints on them
 *	for the named table
 *
 *	Parameters:  table_name == only extract domains for this table
 *	default_char_set_id -- character set def to supress
 *
 **************************************/
	bool first = true;
	SCHAR char_sets[86];

	FOR FLD IN RDB$FIELDS CROSS
		RFR IN RDB$RELATION_FIELDS WITH
		RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND
		RFR.RDB$RELATION_NAME EQ table_name
		SORTED BY FLD.RDB$FIELD_NAME

		// Skip over artificial domains
		if (fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) &&
			(FLD.RDB$SYSTEM_FLAG.NULL || FLD.RDB$SYSTEM_FLAG != 1))
		{
			continue;
		}

		if (first)
		{
			isqlGlob.printf("/* Domain definitions */%s", NEWLINE);
			first = false;
		}
		fb_utils::exact_name(FLD.RDB$FIELD_NAME);

		isqlGlob.printf("CREATE DOMAIN %s AS ", FLD.RDB$FIELD_NAME);

		for (int i = 0; Column_types[i].type; i++)
			if (FLD.RDB$FIELD_TYPE == Column_types[i].type)
			{
				bool precision_known = false;

				if (isqlGlob.major_ods >= ODS_VERSION10)
				{
					// Handle Integral subtypes NUMERIC and DECIMAL
					if ((FLD.RDB$FIELD_TYPE == SMALLINT) ||
						(FLD.RDB$FIELD_TYPE == INTEGER) ||
						(FLD.RDB$FIELD_TYPE == BIGINT))
					{
						/* We are ODS >= 10 and could be any Dialect */

						FOR FLD1 IN RDB$FIELDS WITH
							FLD1.RDB$FIELD_NAME EQ FLD.RDB$FIELD_NAME
							if (!FLD1.RDB$FIELD_PRECISION.NULL)
							{
								/* We are Dialect >=3 since FIELD_PRECISION is non-NULL */
								if (FLD1.RDB$FIELD_SUB_TYPE > 0 &&
									FLD1.RDB$FIELD_SUB_TYPE <= MAX_INTSUBTYPES)
								{
									sprintf (Print_buffer, "%s(%d, %d)",
											 Integral_subtypes[FLD1.RDB$FIELD_SUB_TYPE],
											 FLD1.RDB$FIELD_PRECISION,
												 -FLD1.RDB$FIELD_SCALE);
									precision_known = true;
								}
							}
						END_FOR
						ON_ERROR
							ISQL_errmsg (isc_status);
							return;
						END_ERROR;
					}
				}

				if (!precision_known)
				{
					// Take a stab at numerics and decimals
					if ((FLD.RDB$FIELD_TYPE == SMALLINT) && (FLD.RDB$FIELD_SCALE < 0))
					{
						sprintf (Print_buffer, "NUMERIC(4, %d)", -FLD.RDB$FIELD_SCALE);
					}
					else if ((FLD.RDB$FIELD_TYPE == INTEGER) && (FLD.RDB$FIELD_SCALE < 0))
					{
						sprintf (Print_buffer, "NUMERIC(9, %d)", -FLD.RDB$FIELD_SCALE);
					}
					else if ((FLD.RDB$FIELD_TYPE == DOUBLE_PRECISION) &&
							 (FLD.RDB$FIELD_SCALE < 0))
					{
						sprintf (Print_buffer, "NUMERIC(15, %d)", -FLD.RDB$FIELD_SCALE);
					}
					else
					{
						sprintf (Print_buffer, "%s", Column_types[i].type_name);
					}
				}
				isqlGlob.prints(Print_buffer);
				break;
			}


		if (FLD.RDB$FIELD_TYPE == BLOB)
		{
			const int subtype = FLD.RDB$FIELD_SUB_TYPE;
			isqlGlob.printf(" SUB_TYPE ");
			if ((subtype > 0) && (subtype <= MAX_BLOBSUBTYPES))
			{
				isqlGlob.prints(Sub_types[subtype]);
			}
			else
			{
				isqlGlob.printf("%d", subtype);
			}
			isqlGlob.printf(" SEGMENT SIZE %u", (USHORT) FLD.RDB$SEGMENT_LENGTH);
		}

		else if ((FLD.RDB$FIELD_TYPE == T_CHAR) || (FLD.RDB$FIELD_TYPE == VARCHAR))
		{
			// Length for chars
			isqlGlob.printf("(%d)", ISQL_get_field_length(FLD.RDB$FIELD_NAME));
		}

		/* Bug 8261: do not show the collation information just yet!  If you
		   do, then the domain syntax when printed is not correct */

		/* since the character set is part of the field type, display that
		   information now. */
		if (!FLD.RDB$CHARACTER_SET_ID.NULL)
		{

			char_sets[0] = 0;
			if ((FLD.RDB$CHARACTER_SET_ID != default_char_set_id) ||
				(!FLD.RDB$COLLATION_ID.NULL && FLD.RDB$COLLATION_ID != 0))
			{
				ISQL_get_character_sets (FLD.RDB$CHARACTER_SET_ID, 0, false, false, char_sets);
			}
			if (char_sets[0])
				isqlGlob.prints(char_sets);
		}

		if (!FLD.RDB$DIMENSIONS.NULL)
			ISQL_array_dimensions (FLD.RDB$FIELD_NAME);

		if (!FLD.RDB$DEFAULT_SOURCE.NULL)
		{
			isqlGlob.printf("%s%s ", NEWLINE, TAB_AS_SPACES);
			SHOW_print_metadata_text_blob (isqlGlob.Out, &FLD.RDB$DEFAULT_SOURCE);
		}
		if (!FLD.RDB$VALIDATION_SOURCE.NULL)
		{
			isqlGlob.printf("%s%s ", NEWLINE, TAB_AS_SPACES);
			ISQL_print_validation (isqlGlob.Out, &FLD.RDB$VALIDATION_SOURCE, false, gds_trans);
		}
		if (FLD.RDB$NULL_FLAG == 1)
			isqlGlob.printf(" NOT NULL");

		// Bug 8261:  Now show the collation order information
		/* Show the collation order if one has been specified.  If the collation
		   order is the default for the character set being used, then no collation
		   order will be shown ( because it isn't needed ).

		If the collation id is 0, then the default for the character set is
		being used so there is no need to retrieve the collation information.*/

		if (!FLD.RDB$COLLATION_ID.NULL && FLD.RDB$COLLATION_ID != 0)
		{
			char_sets[0] = 0;
			ISQL_get_character_sets (FLD.RDB$CHARACTER_SET_ID, FLD.RDB$COLLATION_ID, true, false, char_sets);
			if (char_sets[0])
				isqlGlob.prints(char_sets);
		}

		isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);
	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;
}


static void list_domains(SSHORT default_char_set_id)
{
/**************************************
 *
 *	l i s t _ d o m a i n s
 *
 **************************************
 *
 * Functional description
 *	List domains
 *
 *	Parameters:
 *	default_char_set_id -- character set def to supress
 *
 **************************************/
	bool first = true;
	SCHAR char_sets[86];

	FOR FLD IN RDB$FIELDS WITH
		FLD.RDB$FIELD_NAME NOT MATCHING "RDB$+" USING "+=[0-9][0-9]* *"
		AND FLD.RDB$SYSTEM_FLAG NE 1
		SORTED BY FLD.RDB$FIELD_NAME

		if (first)
		{
			isqlGlob.printf("/* Domain definitions */%s", NEWLINE);
			first = false;
		}
		fb_utils::exact_name(FLD.RDB$FIELD_NAME);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			ISQL_copy_SQL_id(FLD.RDB$FIELD_NAME, SQL_identifier, DBL_QUOTE);
			isqlGlob.printf("CREATE DOMAIN %s AS ", SQL_identifier);
		}
		else
			isqlGlob.printf("CREATE DOMAIN %s AS ", FLD.RDB$FIELD_NAME);

		// Get domain type
		for (int i = 0; Column_types[i].type; i++)
			if (FLD.RDB$FIELD_TYPE == Column_types[i].type)
			{
				bool precision_known = false;

				if (isqlGlob.major_ods >= ODS_VERSION10)
				{
					// Handle Integral subtypes NUMERIC and DECIMAL
					if ((FLD.RDB$FIELD_TYPE == SMALLINT) ||
						(FLD.RDB$FIELD_TYPE == INTEGER) ||
						(FLD.RDB$FIELD_TYPE == BIGINT))
					{
						/* We are ODS >= 10 and could be any Dialect */
						FOR FLD1 IN RDB$FIELDS WITH
							FLD1.RDB$FIELD_NAME EQ FLD.RDB$FIELD_NAME

							if (!FLD1.RDB$FIELD_PRECISION.NULL)
							{
								/* We are Dialect >=3 since FIELD_PRECISION is non-NULL */
								if (FLD1.RDB$FIELD_SUB_TYPE > 0 &&
									FLD1.RDB$FIELD_SUB_TYPE <= MAX_INTSUBTYPES)
								{
									sprintf (Print_buffer, "%s(%d, %d)",
											 Integral_subtypes[FLD1.RDB$FIELD_SUB_TYPE],
											 FLD1.RDB$FIELD_PRECISION,
											 -FLD1.RDB$FIELD_SCALE);
									precision_known = true;
								}
							}
						END_FOR
						ON_ERROR
							ISQL_errmsg (isc_status);
							return;
						END_ERROR;
					}
				}

				if (!precision_known)
				{
					// Take a stab at numerics and decimals
					if ((FLD.RDB$FIELD_TYPE == SMALLINT) && (FLD.RDB$FIELD_SCALE < 0))
					{
						sprintf (Print_buffer, "NUMERIC(4, %d)", -FLD.RDB$FIELD_SCALE);
					}
					else if ((FLD.RDB$FIELD_TYPE == INTEGER) && (FLD.RDB$FIELD_SCALE < 0))
					{
						sprintf (Print_buffer, "NUMERIC(9, %d)", -FLD.RDB$FIELD_SCALE);
					}
					else if ((FLD.RDB$FIELD_TYPE == DOUBLE_PRECISION) &&
							 (FLD.RDB$FIELD_SCALE < 0))
					{
						sprintf (Print_buffer, "NUMERIC(15, %d)", -FLD.RDB$FIELD_SCALE);
					}
					else
					{
						sprintf (Print_buffer, "%s", Column_types[i].type_name);
					}
				}
				isqlGlob.prints(Print_buffer);
				break;
			}

		if (FLD.RDB$FIELD_TYPE == BLOB)
		{
			const int subtype = FLD.RDB$FIELD_SUB_TYPE;
			isqlGlob.printf(" SUB_TYPE ");

			if ((subtype > 0) && (subtype <= MAX_BLOBSUBTYPES))
			{
				isqlGlob.prints(Sub_types[subtype]);
			}
			else
			{
				isqlGlob.printf("%d", subtype);
			}
			isqlGlob.printf(" SEGMENT SIZE %u", (USHORT) FLD.RDB$SEGMENT_LENGTH);
		}

		else if ((FLD.RDB$FIELD_TYPE == T_CHAR) || (FLD.RDB$FIELD_TYPE == VARCHAR))
		{
			// Length for chars
			isqlGlob.printf("(%d)", ISQL_get_field_length(FLD.RDB$FIELD_NAME));
		}

		/* Bug 8261: do not show the collation information just yet!  If you
		   do, then the domain syntax when printed is not correct */

		/* since the character set is part of the field type, display that
		   information now. */
		if (!FLD.RDB$CHARACTER_SET_ID.NULL)
		{
			char_sets[0] = 0;
			if ((FLD.RDB$CHARACTER_SET_ID != default_char_set_id) ||
				(!FLD.RDB$COLLATION_ID.NULL && FLD.RDB$COLLATION_ID != 0))
			{
				ISQL_get_character_sets (FLD.RDB$CHARACTER_SET_ID, 0, false, false, char_sets);
			}
			if (char_sets[0])
				isqlGlob.prints(char_sets);
		}

		if (!FLD.RDB$DIMENSIONS.NULL)
			ISQL_array_dimensions (FLD.RDB$FIELD_NAME);

		if (!FLD.RDB$DEFAULT_SOURCE.NULL)
		{
			isqlGlob.printf("%s%s ", NEWLINE, TAB_AS_SPACES);
			SHOW_print_metadata_text_blob (isqlGlob.Out, &FLD.RDB$DEFAULT_SOURCE);
		}
		if (!FLD.RDB$VALIDATION_SOURCE.NULL)
		{
			isqlGlob.printf("%s%s ", NEWLINE, TAB_AS_SPACES);
			ISQL_print_validation (isqlGlob.Out, &FLD.RDB$VALIDATION_SOURCE, false, gds_trans);
		}
		if (FLD.RDB$NULL_FLAG == 1)
			isqlGlob.printf(" NOT NULL");

		// Bug 8261:  Now show the collation order information
		/* Show the collation order if one has been specified.  If the collation
		   order is the default for the character set being used, then no collation
		   order will be shown ( because it isn't needed

		   If the collation id is 0, then the default for the character set is
		   being used so there is no need to retrieve the collation information.*/

		if (!FLD.RDB$COLLATION_ID.NULL && FLD.RDB$COLLATION_ID != 0)
		{
			char_sets[0] = 0;
			ISQL_get_character_sets (FLD.RDB$CHARACTER_SET_ID, FLD.RDB$COLLATION_ID, true, false, char_sets);
			if (char_sets[0])
				isqlGlob.prints(char_sets);
		}

		isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);
	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;
}


static void list_exception()
{
/**************************************
 *
 *	l i s t _ e x c e p t i o n
 *
 **************************************
 *
 * Functional description
 *	List all exceptions defined in the database
 *
 *	Parameters:  none
 *
 **************************************/
	bool first = true;

	FOR EXC IN RDB$EXCEPTIONS
		SORTED BY EXC.RDB$EXCEPTION_NAME

		if (first)
		{
			isqlGlob.printf("%s/*  Exceptions */%s", NEWLINE, NEWLINE);
		}
		first = false;
		fb_utils::exact_name(EXC.RDB$EXCEPTION_NAME);

		ISQL_copy_SQL_id (EXC.RDB$MESSAGE, SQL_identifier2, SINGLE_QUOTE);
		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			ISQL_copy_SQL_id (EXC.RDB$EXCEPTION_NAME, SQL_identifier, DBL_QUOTE);
			isqlGlob.printf("CREATE EXCEPTION %s %s%s%s",
					 SQL_identifier, SQL_identifier2, isqlGlob.global_Term, NEWLINE);
		}
		else
			isqlGlob.printf("CREATE EXCEPTION %s %s%s%s",
					 EXC.RDB$EXCEPTION_NAME, SQL_identifier2, isqlGlob.global_Term, NEWLINE);

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;
}


static void list_filters()
{
/**************************************
 *
 *	l i s t _ f i l t e r s
 *
 **************************************
 *
 * Functional description
 *	List all blob filters
 *
 *	Parameters:  none
 * Results in
 * DECLARE FILTER <fname> INPUT_TYPE <blob_sub_type> OUTPUT_TYPE <blob_subtype>
 *		 ENTRY_POINT <string> MODULE_NAME <string>
 **************************************/
	bool first = true;

	FOR FIL IN RDB$FILTERS
		SORTED BY FIL.RDB$FUNCTION_NAME

		fb_utils::exact_name(FIL.RDB$FUNCTION_NAME);
		fb_utils::exact_name(FIL.RDB$MODULE_NAME);
		fb_utils::exact_name(FIL.RDB$ENTRYPOINT);

		if (first)
		{
			isqlGlob.printf("%s/*  BLOB Filter declarations */%s", NEWLINE, NEWLINE);
			first = false;
		}

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			ISQL_copy_SQL_id(FIL.RDB$FUNCTION_NAME, SQL_identifier, DBL_QUOTE);
		else
			strcpy(SQL_identifier, FIL.RDB$FUNCTION_NAME);

		isqlGlob.printf("DECLARE FILTER %s INPUT_TYPE %d OUTPUT_TYPE %d%s",
			SQL_identifier, FIL.RDB$INPUT_SUB_TYPE, FIL.RDB$OUTPUT_SUB_TYPE, NEWLINE);

		ISQL_copy_SQL_id(FIL.RDB$ENTRYPOINT, SQL_identifier, SINGLE_QUOTE);
		ISQL_copy_SQL_id(FIL.RDB$MODULE_NAME, SQL_identifier2, SINGLE_QUOTE);

		isqlGlob.printf("%sENTRY_POINT %s MODULE_NAME %s%s%s%s",
			TAB_AS_SPACES, SQL_identifier, SQL_identifier2,
			isqlGlob.global_Term, NEWLINE, NEWLINE);

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;

}


static void list_foreign()
{
/**************************************
 *
 *	l i s t _ f o r e i g n
 *
 **************************************
 *
 * Functional description
 *	List all foreign key constraints and alter the tables
 *
 **************************************/
	SCHAR collist[BUFFER_LENGTH512 * 2];

/* Static queries for obtaining foreign constraints, where RELC1 is the
   foreign key constraints, RELC2 is the primary key lookup and REFC
   is the join table */

	FOR RELC1 IN RDB$RELATION_CONSTRAINTS CROSS
		RELC2 IN RDB$RELATION_CONSTRAINTS CROSS
		REFC IN RDB$REF_CONSTRAINTS WITH
		RELC1.RDB$CONSTRAINT_TYPE EQ "FOREIGN KEY" AND
		REFC.RDB$CONST_NAME_UQ EQ RELC2.RDB$CONSTRAINT_NAME AND
		REFC.RDB$CONSTRAINT_NAME EQ RELC1.RDB$CONSTRAINT_NAME AND
		(RELC2.RDB$CONSTRAINT_TYPE EQ "UNIQUE" OR
		RELC2.RDB$CONSTRAINT_TYPE EQ "PRIMARY KEY")
		SORTED BY RELC1.RDB$RELATION_NAME, RELC1.RDB$CONSTRAINT_NAME

		fb_utils::exact_name(RELC1.RDB$RELATION_NAME);
		fb_utils::exact_name(RELC2.RDB$RELATION_NAME);

		ISQL_get_index_segments (collist, sizeof(collist), RELC1.RDB$INDEX_NAME, true);

		isqlGlob.printf(NEWLINE);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			ISQL_copy_SQL_id (RELC1.RDB$RELATION_NAME, SQL_identifier, DBL_QUOTE);
			isqlGlob.printf("ALTER TABLE %s ADD ", SQL_identifier);
		}
		else
			isqlGlob.printf("ALTER TABLE %s ADD ", RELC1.RDB$RELATION_NAME);

		/* If the name of the constraint is not INTEG..., print it.
	      INTEG_... are internally generated names. */
		if (!RELC1.RDB$CONSTRAINT_NAME.NULL &&
			!fb_utils::implicit_integrity(RELC1.RDB$CONSTRAINT_NAME))
		{
			ISQL_truncate_term (RELC1.RDB$CONSTRAINT_NAME, strlen(RELC1.RDB$CONSTRAINT_NAME));
			if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			{
				ISQL_copy_SQL_id (RELC1.RDB$CONSTRAINT_NAME, SQL_identifier, DBL_QUOTE);
				isqlGlob.printf("CONSTRAINT %s ", SQL_identifier);
			}
			else
				isqlGlob.printf("CONSTRAINT %s ", RELC1.RDB$CONSTRAINT_NAME);
		}

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			ISQL_copy_SQL_id (RELC2.RDB$RELATION_NAME, SQL_identifier, DBL_QUOTE);
			isqlGlob.printf("FOREIGN KEY (%s) REFERENCES %s ", collist, SQL_identifier);
		}
		else
			isqlGlob.printf("FOREIGN KEY (%s) REFERENCES %s ", collist, RELC2.RDB$RELATION_NAME);

		// Get the column list for the primary key

		ISQL_get_index_segments (collist, sizeof(collist), RELC2.RDB$INDEX_NAME, true);

		isqlGlob.printf("(%s)", collist);

		// Add the referential actions, if any
		if (!REFC.RDB$UPDATE_RULE.NULL)
		{
			ISQL_truncate_term (REFC.RDB$UPDATE_RULE, strlen(REFC.RDB$UPDATE_RULE));
			ISQL_ri_action_print (REFC.RDB$UPDATE_RULE, " ON UPDATE", true);
		}

		if (!REFC.RDB$DELETE_RULE.NULL)
		{
			ISQL_truncate_term (REFC.RDB$DELETE_RULE, strlen(REFC.RDB$DELETE_RULE));
			ISQL_ri_action_print (REFC.RDB$DELETE_RULE, " ON DELETE", true);
		}

		isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;
}


static void list_functions()
{
/**************************************
 *
 *	l i s t _ f u n c t i o n s
 *
 **************************************
 *
 * Functional description
 *	List all external functions
 *
 *	Parameters:  none
 * Results in
 * DECLARE EXTERNAL FUNCTION function_name
 *               CHAR(256) , INTEGER, ....
 *               RETURNS INTEGER BY VALUE
 *               ENTRY_POINT entrypoint MODULE_NAME module;
 **************************************/
	char type_buffer[BUFFER_LENGTH128];
	char return_buffer[BUFFER_LENGTH128];

	bool first = true;

	FOR FUN IN RDB$FUNCTIONS
		WITH FUN.RDB$SYSTEM_FLAG NE 1 OR FUN.RDB$SYSTEM_FLAG MISSING
		SORTED BY FUN.RDB$FUNCTION_NAME

		fb_utils::exact_name(FUN.RDB$FUNCTION_NAME);
		fb_utils::exact_name(FUN.RDB$MODULE_NAME);
		fb_utils::exact_name(FUN.RDB$ENTRYPOINT);
		if (first)
		{
			isqlGlob.printf("%s/*  External Function declarations */%s", NEWLINE, NEWLINE);
			first = false;
		}

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			ISQL_copy_SQL_id(FUN.RDB$FUNCTION_NAME, SQL_identifier, DBL_QUOTE);
		else
			strcpy(SQL_identifier, FUN.RDB$FUNCTION_NAME);

		// Start new function declaration
		isqlGlob.printf("DECLARE EXTERNAL FUNCTION %s%s", SQL_identifier, NEWLINE);

		bool firstarg = true;

		FOR FNA IN RDB$FUNCTION_ARGUMENTS WITH
			FUN.RDB$FUNCTION_NAME EQ FNA.RDB$FUNCTION_NAME
			SORTED BY FNA.RDB$ARGUMENT_POSITION

			// Find parameter type
			int i = 0;
			while (FNA.RDB$FIELD_TYPE != Column_types[i].type)
				i++;

			// Print length where appropriate
			if ((FNA.RDB$FIELD_TYPE == T_CHAR) ||
				(FNA.RDB$FIELD_TYPE == VARCHAR) ||
				(FNA.RDB$FIELD_TYPE == CSTRING))
			{
				bool did_charset = false;
				FOR CHARSET IN RDB$CHARACTER_SETS
					WITH CHARSET.RDB$CHARACTER_SET_ID = FNA.RDB$CHARACTER_SET_ID

					did_charset = true;
					fb_utils::exact_name(CHARSET.RDB$CHARACTER_SET_NAME);
					sprintf (type_buffer, "%s(%d) CHARACTER SET %s",
							 Column_types[i].type_name,
							 (FNA.RDB$FIELD_LENGTH / MAX (1, CHARSET.RDB$BYTES_PER_CHARACTER)),
							 CHARSET.RDB$CHARACTER_SET_NAME);

				END_FOR
				ON_ERROR
					ISQL_errmsg (gds_status);
					return;
				END_ERROR;

				if (!did_charset)
					sprintf (type_buffer, "%s(%d)",  Column_types[i].type_name, FNA.RDB$FIELD_LENGTH);
			}
			else
			{
				bool precision_known = false;

				if ( (isqlGlob.major_ods >= ODS_VERSION10) &&
					((FNA.RDB$FIELD_TYPE == SMALLINT) ||
					(FNA.RDB$FIELD_TYPE == INTEGER) ||
					(FNA.RDB$FIELD_TYPE == BIGINT)))
				{
					FOR FNA1 IN RDB$FUNCTION_ARGUMENTS WITH
						FNA1.RDB$FUNCTION_NAME EQ FNA.RDB$FUNCTION_NAME AND
						FNA1.RDB$ARGUMENT_POSITION EQ FNA.RDB$ARGUMENT_POSITION

						/* We are ODS >= 10 and could be any Dialect */
						if (!FNA1.RDB$FIELD_PRECISION.NULL)
						{
							/* We are Dialect >=3 since FIELD_PRECISION is non-NULL */
							if (FNA1.RDB$FIELD_SUB_TYPE > 0 &&
								FNA1.RDB$FIELD_SUB_TYPE <= MAX_INTSUBTYPES)
							{
								sprintf (type_buffer, "%s(%d, %d)",
										Integral_subtypes[FNA1.RDB$FIELD_SUB_TYPE],
										FNA1.RDB$FIELD_PRECISION,
										-FNA1.RDB$FIELD_SCALE);
								precision_known = true;
							}
						} // if field_precision is not null
					END_FOR
					ON_ERROR
						ISQL_errmsg (isc_status);
						return;
					END_ERROR;
				} /* if isqlGlob.major_ods >= ods_version10 && */

				if (!precision_known)
				{
					// Take a stab at numerics and decimals
					if ((FNA.RDB$FIELD_TYPE == SMALLINT) && (FNA.RDB$FIELD_SCALE < 0))
					{
						sprintf (type_buffer, "NUMERIC(4, %d)", -FNA.RDB$FIELD_SCALE);
					}
					else if ((FNA.RDB$FIELD_TYPE == INTEGER) && (FNA.RDB$FIELD_SCALE < 0))
					{
						sprintf (type_buffer, "NUMERIC(9, %d)", -FNA.RDB$FIELD_SCALE);
					}
					else if ((FNA.RDB$FIELD_TYPE == DOUBLE_PRECISION) && (FNA.RDB$FIELD_SCALE < 0))
					{
						sprintf (type_buffer, "NUMERIC(15, %d)", -FNA.RDB$FIELD_SCALE);
					}
					else
						sprintf (type_buffer, "%s", Column_types[i].type_name);
				} // if !precision_known
			} // if T_CHAR or VARCHAR or CSTRING ... else

			// If a return argument, save it for the end, otherwise print

			/*
			 * Changed the following to not return a BLOB by value.
			 * To be sincere, this code doesn't cater for the RETURNS PARAMETER syntax but
			 * it would require more surgery than I'm willing to do, since I'm sick of isql
			 * so I started my own metadata extraction utility based on IBO that does this
			 * trick and others.
			 * Claudio Valderrama (by way of) MOD 23-Apr-2001

			 CVC: Finally enhanced the UDF metadata extraction.
			*/

			int ptype = (int) abs(FNA.RDB$MECHANISM);
			if (ptype > MAX_UDFPARAM_TYPES)
				ptype = MAX_UDFPARAM_TYPES;

			bool printarg = true;

			if (FUN.RDB$RETURN_ARGUMENT == FNA.RDB$ARGUMENT_POSITION)
			{
				if (FUN.RDB$RETURN_ARGUMENT) {
					sprintf (return_buffer, "RETURNS PARAMETER %d", FUN.RDB$RETURN_ARGUMENT);
				}
				else
				{
					sprintf (return_buffer, "RETURNS %s%s %s", type_buffer,
							 UDF_param_types[ptype],
							 (FNA.RDB$MECHANISM < 0 ? "FREE_IT" : ""));
					printarg = false;
				}

			}

			if (printarg)
			{
				// First arg needs no comma
				isqlGlob.printf("%s%s%s", (firstarg ? "" : ", "), type_buffer, UDF_param_types[ptype]);
				firstarg = false;
			}

		END_FOR
		ON_ERROR
			ISQL_errmsg (gds_status);
			return;
		END_ERROR;

		// Print the return type -- All functions return a type
		isqlGlob.printf("%s%s%s", NEWLINE, return_buffer, NEWLINE);

		// Print out entrypoint information
		ISQL_copy_SQL_id(FUN.RDB$ENTRYPOINT, SQL_identifier, SINGLE_QUOTE);
		ISQL_copy_SQL_id(FUN.RDB$MODULE_NAME, SQL_identifier2, SINGLE_QUOTE);

		isqlGlob.printf("ENTRY_POINT %s MODULE_NAME %s%s%s%s",
				 SQL_identifier,
				 SQL_identifier2,
				 isqlGlob.global_Term,
				 NEWLINE,
				 NEWLINE);

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;
}




static void list_generators()
{
/**************************************
 *
 *	l i s t _ g e n e r a t o r s
 *
 **************************************
 *
 * Functional description
 *	Re create all non-system generators
 *
 **************************************/

	bool first = true;

	FOR GEN IN RDB$GENERATORS WITH
		GEN.RDB$GENERATOR_NAME NOT MATCHING "RDB$+" USING "+=[0-9][0-9]* *" AND
		GEN.RDB$GENERATOR_NAME NOT MATCHING "SQL$+" USING "+=[0-9][0-9]* *" AND
		(GEN.RDB$SYSTEM_FLAG MISSING OR GEN.RDB$SYSTEM_FLAG NE 1)
		SORTED BY GEN.RDB$GENERATOR_NAME

		if (first)
		{
			isqlGlob.printf("%s/*  Generators or sequences */%s", NEWLINE, NEWLINE);
			first = false;
		}

		fb_utils::exact_name(GEN.RDB$GENERATOR_NAME);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			ISQL_copy_SQL_id (GEN.RDB$GENERATOR_NAME, SQL_identifier, DBL_QUOTE);
		else
			strcpy (SQL_identifier, GEN.RDB$GENERATOR_NAME);

		isqlGlob.printf("CREATE GENERATOR %s%s%s", SQL_identifier, isqlGlob.global_Term, NEWLINE);
	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;

	isqlGlob.printf(NEWLINE);
}


static void list_index()
{
/**************************************
 *
 *	l i s t _ i n d e x
 *
 **************************************
 *
 * Functional description
 *	Define all non-constraint indices
 *	Use a static SQL query to get the info and print it.
 *
 *	Uses get_index_segment to provide a key list for each index
 *
 **************************************/
	char collist[BUFFER_LENGTH512 * 2];

	bool first = true;

	FOR IDX IN RDB$INDICES CROSS RELC IN RDB$RELATIONS
		OVER RDB$RELATION_NAME
		WITH (RELC.RDB$SYSTEM_FLAG NE 1 OR RELC.RDB$SYSTEM_FLAG MISSING)
		AND NOT (ANY RC IN RDB$RELATION_CONSTRAINTS
				WITH RC.RDB$INDEX_NAME EQ IDX.RDB$INDEX_NAME)
		SORTED BY IDX.RDB$RELATION_NAME, IDX.RDB$INDEX_NAME

		if (first)
		{
			isqlGlob.printf("%s/*  Index definitions for all user tables */%s", NEWLINE, NEWLINE);
			first = false;
		}

		// Strip trailing blanks
		fb_utils::exact_name(IDX.RDB$RELATION_NAME);
		fb_utils::exact_name(IDX.RDB$INDEX_NAME);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			ISQL_copy_SQL_id (IDX.RDB$INDEX_NAME,    SQL_identifier,  DBL_QUOTE);
			ISQL_copy_SQL_id (IDX.RDB$RELATION_NAME, SQL_identifier2, DBL_QUOTE);
			isqlGlob.printf("CREATE%s%s INDEX %s ON %s",
					(IDX.RDB$UNIQUE_FLAG ? " UNIQUE" : ""),
					(IDX.RDB$INDEX_TYPE ? " DESCENDING" : ""),
					SQL_identifier,
					SQL_identifier2);
		}
		else
			isqlGlob.printf("CREATE%s%s INDEX %s ON %s",
					(IDX.RDB$UNIQUE_FLAG ? " UNIQUE" : ""),
					(IDX.RDB$INDEX_TYPE ? " DESCENDING" : ""),
					IDX.RDB$INDEX_NAME,
					IDX.RDB$RELATION_NAME);

		// Get index expression or column names

		if (!IDX.RDB$EXPRESSION_BLR.NULL)
		{
			isqlGlob.printf(" COMPUTED BY ");
			if (!IDX.RDB$EXPRESSION_SOURCE.NULL)
				SHOW_print_metadata_text_blob (isqlGlob.Out, &IDX.RDB$EXPRESSION_SOURCE);
			isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);
		}
		else if (ISQL_get_index_segments (collist, sizeof(collist), IDX.RDB$INDEX_NAME, true))
		{
			isqlGlob.printf(" (%s)%s%s", collist, isqlGlob.global_Term, NEWLINE);
		}

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;
}


static void list_views()
{
/**************************************
 *
 *	l i s t _ v i e w s
 *
 **************************************
 *
 * Functional description
 *	Show text of views.
 *	Use a SQL query to get the info and print it.
 *	Note: This should also contain check option
 *
 **************************************/

// If this is a view, use print_blob to print the view text

	FOR REL IN RDB$RELATIONS WITH
		(REL.RDB$SYSTEM_FLAG NE 1 OR REL.RDB$SYSTEM_FLAG MISSING) AND
		REL.RDB$VIEW_BLR NOT MISSING AND
		REL.RDB$FLAGS = REL_sql
		SORTED BY REL.RDB$RELATION_ID

		bool first = true;
		fb_utils::exact_name(REL.RDB$RELATION_NAME);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			ISQL_copy_SQL_id (REL.RDB$RELATION_NAME, SQL_identifier, DBL_QUOTE);
		else
			strcpy (SQL_identifier, REL.RDB$RELATION_NAME);

		fb_utils::exact_name(REL.RDB$OWNER_NAME);

		isqlGlob.printf("%s/* View: %s, Owner: %s */%s",
				 NEWLINE,
				 REL.RDB$RELATION_NAME,
				 REL.RDB$OWNER_NAME,
				 NEWLINE);
		isqlGlob.printf("CREATE VIEW %s (", SQL_identifier);

		// Get column list
		FOR RFR IN RDB$RELATION_FIELDS WITH
			RFR.RDB$RELATION_NAME = REL.RDB$RELATION_NAME
			SORTED BY RFR.RDB$FIELD_POSITION

			fb_utils::exact_name(RFR.RDB$FIELD_NAME);

			if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
				ISQL_copy_SQL_id (RFR.RDB$FIELD_NAME, SQL_identifier, DBL_QUOTE);
			else
				strcpy (SQL_identifier, RFR.RDB$FIELD_NAME);

			isqlGlob.printf("%s%s", (first ? "" : ", "), SQL_identifier);
			first = false;

		END_FOR
		ON_ERROR
			ISQL_errmsg (gds_status);
			return;
		END_ERROR;
		isqlGlob.printf(") AS%s", NEWLINE);

		if (!REL.RDB$VIEW_SOURCE.NULL)
			SHOW_print_metadata_text_blob (isqlGlob.Out, &REL.RDB$VIEW_SOURCE);

		isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);

	END_FOR
	ON_ERROR
		ISQL_errmsg(gds_status);
		return;
	END_ERROR;
}
