/*
 *	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/constants.h"
#include "../jrd/ibase.h"
#include "../yvalve/gds_proto.h"
#include "../common/intlobj_new.h"
#include "../isql/isql.h"
#include "../isql/extra_proto.h"
#include "../isql/isql_proto.h"
#include "../isql/show_proto.h"
#include "../isql/iutils_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";
DATABASE DB = EXTERN COMPILETIME "yachts.lnk" RUNTIME isqlGlob.global_Db_name;

static bool extract_rel_constraints(const char* relation_name);
static void get_procedure_args(const char*);
static void get_function_args_ods12(const char*, USHORT);
static void list_all_grants();
static processing_state list_all_grants2(bool, const SCHAR*);
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 listDomainConstraints();
static void listRelationComputed(LegacyTables flag, SSHORT);
static void list_exceptions();
static void list_filters();
static void list_foreign();
static void list_functions_ods12_headers();
static void list_functions_ods12_bodies();
static void list_functions_legacy();
static void list_generators();
static void list_indexes();
static void list_package_bodies();
static void list_package_headers();
static void list_procedure_bodies();
static void list_procedure_headers();
static void list_views();

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

static TEXT SQL_identifier[BUFFER_LENGTH128];
static TEXT SQL_identifier2[BUFFER_XLARGE]; // should be >= 1026 bytes (exception text + two quotes)



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)
	{
		DB = fbProvider->attachDatabase(fbStatus, isqlGlob.global_Db_name, 0, NULL);
		if (ISQL_errmsg(fbStatus))
			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 (!fbTrans)
	{
		fbTrans = DB->startTransaction(fbStatus, 0, NULL);
		if (ISQL_errmsg(fbStatus))
			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];
			IUTILS_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_generators();
		list_domains(default_char_set_id);
		list_all_tables(flag, default_char_set_id);
		list_functions_legacy();
		list_functions_ods12_headers();
		list_procedure_headers();
		list_package_headers();
		list_indexes();
		list_foreign();
		list_views();
		list_exceptions();
		list_functions_ods12_bodies();
		list_procedure_bodies();
		list_package_bodies();
		listDomainConstraints();
		list_check();
		listRelationComputed(flag, default_char_set_id);
		list_all_triggers();
		list_all_grants();
		SHOW_maps(true, "");
		SHOW_comments(false); // Let's make this an option later.
	}

	if (fbTrans && did_start)
	{
		fbTrans->commit(fbStatus);
		if (ISQL_errmsg(fbStatus))
			return FINI_ERROR;
		fbTrans = NULL;
	}

	if (DB && did_attach)
	{
		DB->detach(fbStatus);
		if (ISQL_errmsg(fbStatus))
			return FINI_ERROR;
		DB = NULL;
	}

	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];
	rel_t rel_type = rel_persistent;

	// 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

		if (!REL.RDB$RELATION_TYPE.NULL)
			rel_type = (rel_t) REL.RDB$RELATION_TYPE;

		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)
					IUTILS_copy_SQL_id (new_name, SQL_identifier, DBL_QUOTE);
				else
					IUTILS_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)
			{
				IUTILS_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)
		{
			IUTILS_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));

		/*
		** 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 (!(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)
			{
				IUTILS_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, true, char_sets);
				if (char_sets[0])
					isqlGlob.prints(char_sets);
			}
		}
		else
		{
			if (!ISQL_printNumericType(FLD.RDB$FIELD_NAME, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE))
				return FINI_ERROR;

			if ((FLD.RDB$FIELD_TYPE == T_CHAR) || (FLD.RDB$FIELD_TYPE == VARCHAR))
			{
				isqlGlob.printf("(%d)",
					ISQL_get_char_length(
						FLD.RDB$FIELD_LENGTH,
						FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH,
						FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID
					)
				);
			}

			// 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, true, 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;
			}
		}

		// Check for computed fields
		if (!FLD.RDB$COMPUTED_BLR.NULL)
		{
			isqlGlob.printf(" COMPUTED BY ");

			if (domain_flag)
			{
				if (!FLD.RDB$COMPUTED_SOURCE.NULL)
					ISQL_print_validation (isqlGlob.Out, &FLD.RDB$COMPUTED_SOURCE, true, fbTrans);
			}
			else if (collation)
			{
				SCHAR collate_name[CHARSET_COLLATE_SIZE];
				collate_name[0] = '\0';
				ISQL_get_character_sets(char_set_id, collation, true, false, true, collate_name);

				if (collate_name[0])
				{
					isqlGlob.printf("(CAST(NULL AS VARCHAR(1)%s)", char_sets);
					isqlGlob.printf("%s)", collate_name);
				}
				else
					isqlGlob.printf("(NULL)");

				collation = 0;
			}
			else
				isqlGlob.printf("(NULL)");
		}

		// Handle defaults for columns

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

		if (!RFR.RDB$GENERATOR_NAME.NULL)
		{
			FOR GEN IN RDB$GENERATORS
				WITH GEN.RDB$GENERATOR_NAME = RFR.RDB$GENERATOR_NAME
			{
				isqlGlob.printf(" GENERATED BY DEFAULT AS IDENTITY");

				if (!GEN.RDB$INITIAL_VALUE.NULL && GEN.RDB$INITIAL_VALUE != 0)
					isqlGlob.printf(" (START WITH %" SQUADFORMAT ")", GEN.RDB$INITIAL_VALUE);
			}
			END_FOR
			ON_ERROR
				ISQL_errmsg(fbStatus);
				return ps_ERR;
			END_ERROR
		}

		/* 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)
					{
						IUTILS_copy_SQL_id (CON.RDB$CONSTRAINT_NAME, SQL_identifier, DBL_QUOTE);
						isqlGlob.printf(" CONSTRAINT %s", SQL_identifier);
					}
					else
						isqlGlob.printf(" CONSTRAINT %s", CON.RDB$CONSTRAINT_NAME);
				}
			END_FOR
			ON_ERROR
				ISQL_errmsg (fbStatus);
				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, true, char_sets);
			if (char_sets[0])
				isqlGlob.prints(char_sets);
		}
	END_FOR
	ON_ERROR
		ISQL_errmsg(fbStatus);
		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)
			{
				IUTILS_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)
			{
				IUTILS_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(fbStatus);
		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.
 *  Make sure to pass here only the names of procedures that are global.
 *
 **************************************/
	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 AND
			PRM.RDB$PACKAGE_NAME MISSING
			SORTED BY PRM.RDB$PARAMETER_NUMBER

			bool prm_collation_id_null = PRM.RDB$COLLATION_ID.NULL;
			SSHORT prm_collation_id = PRM.RDB$COLLATION_ID;

			bool prm_default_source_null = PRM.RDB$DEFAULT_SOURCE.NULL;
			ISC_QUAD prm_default_source = PRM.RDB$DEFAULT_SOURCE;

			bool prm_null_flag_null = PRM.RDB$NULL_FLAG.NULL;
			bool prm_null_flag = PRM.RDB$NULL_FLAG;

			prm_mech_t prm_mech = prm_mech_normal;

			if (!PRM.RDB$PARAMETER_MECHANISM.NULL)
				prm_mech = (prm_mech_t) PRM.RDB$PARAMETER_MECHANISM;

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

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

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

			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)
				IUTILS_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)
					{
						IUTILS_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, true, 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, false, true);
					}
				}
			}
			else
			{
				if (!ISQL_printNumericType(FLD.RDB$FIELD_NAME, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE))
					return; // ps_ERR;

				// 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))
				{
					isqlGlob.printf("(%d)",
						ISQL_get_char_length(
							FLD.RDB$FIELD_LENGTH,
							FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH,
							FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID
						)
					);
				}

				// 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, true, 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, false, true);
					}
					else if (!FLD.RDB$DEFAULT_SOURCE.NULL)
					{
						isqlGlob.printf(" ");
						SHOW_print_metadata_text_blob(isqlGlob.Out, &FLD.RDB$DEFAULT_SOURCE, false, true);
					}
				}
			}
		END_FOR
		ON_ERROR
			ISQL_errmsg(fbStatus);
			return;
		END_ERROR;

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

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

	}						// end for ptype
}


static void get_function_args_ods12(const char* func_name, USHORT out_arg)
{
/**************************************
 *
 *	g e t _ f u n c t i o n _ a r g s _ o d s 1 2
 *
 **************************************
 *
 * Functional description
 *	This function extracts the function parameters and adds it to the
 *	extract file.
 *  Make sure to pass here only the names of functions that are global.
 *
 **************************************/
	SCHAR char_sets[95];

	// Pass 0 - inputs, pass 1 - return value

	for (int pass = 0; pass < 2; pass++)
	{
		bool first_time = true;

		FOR ARG IN RDB$FUNCTION_ARGUMENTS CROSS
			FLD IN RDB$FIELDS WITH
			ARG.RDB$FUNCTION_NAME = func_name AND
			ARG.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND
			ARG.RDB$PACKAGE_NAME MISSING
			SORTED BY ARG.RDB$ARGUMENT_POSITION

			if ((pass == 0 && ARG.RDB$ARGUMENT_POSITION == out_arg) ||
				(pass != 0 && ARG.RDB$ARGUMENT_POSITION != out_arg))
			{
				continue;
			}

			bool prm_collation_id_null = ARG.RDB$COLLATION_ID.NULL;
			SSHORT prm_collation_id = ARG.RDB$COLLATION_ID;

			bool prm_default_source_null = ARG.RDB$DEFAULT_SOURCE.NULL;
			ISC_QUAD prm_default_source = ARG.RDB$DEFAULT_SOURCE;

			bool prm_null_flag_null = ARG.RDB$NULL_FLAG.NULL;
			bool prm_null_flag = ARG.RDB$NULL_FLAG;

			prm_mech_t prm_mech = prm_mech_normal;

			if (!ARG.RDB$ARGUMENT_MECHANISM.NULL)
				prm_mech = (prm_mech_t) ARG.RDB$ARGUMENT_MECHANISM;

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

			if (!ARG.RDB$RELATION_NAME.NULL)
			{
				fb_utils::exact_name(ARG.RDB$RELATION_NAME);

				if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
					IUTILS_copy_SQL_id(ARG.RDB$RELATION_NAME, relationName, DBL_QUOTE);
				else
					strcpy(relationName, ARG.RDB$RELATION_NAME);
			}

			if (!ARG.RDB$FIELD_NAME.NULL)
			{
				fb_utils::exact_name(ARG.RDB$FIELD_NAME);

				if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
					IUTILS_copy_SQL_id(ARG.RDB$FIELD_NAME, relationField, DBL_QUOTE);
				else
					strcpy(relationField, ARG.RDB$FIELD_NAME);
			}

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

			if (pass == 0)
			{
				fb_utils::exact_name(ARG.RDB$ARGUMENT_NAME);

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

				if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
					IUTILS_copy_SQL_id(ARG.RDB$ARGUMENT_NAME, SQL_identifier, DBL_QUOTE);
				else
					strcpy (SQL_identifier, ARG.RDB$ARGUMENT_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)
					{
						IUTILS_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, true, char_sets);
					if (char_sets[0])
						isqlGlob.prints(char_sets);
				}
				else if (!prm_null_flag_null && prm_null_flag)
					isqlGlob.printf(" NOT NULL");

				if (pass == 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, false, true);
					}
				}
			}
			else
			{
				if (!ISQL_printNumericType(FLD.RDB$FIELD_NAME, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE))
					return; // ps_ERR;

				// 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))
				{
					isqlGlob.printf("(%d)",
						ISQL_get_char_length(
							FLD.RDB$FIELD_LENGTH,
							FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH,
							FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID
						)
					);
				}

				// 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, true, char_sets);

					if (char_sets[0])
						isqlGlob.prints(char_sets);
				}
				else if (!prm_null_flag_null && prm_null_flag)
					isqlGlob.printf(" NOT NULL");

				if (pass == 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, false, true);
					}
					else if (!FLD.RDB$DEFAULT_SOURCE.NULL)
					{
						isqlGlob.printf(" ");
						SHOW_print_metadata_text_blob(isqlGlob.Out, &FLD.RDB$DEFAULT_SOURCE, false, true);
					}
				}
			}
		END_FOR
		ON_ERROR
			ISQL_errmsg(fbStatus);
			return;
		END_ERROR;

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

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

	}						// end for ptype
}


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 = !XX.RDB$SYSTEM_FLAG.NULL && XX.RDB$SYSTEM_FLAG > 0;

			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)
				{
					IUTILS_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(fbStatus);
		    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(fbStatus);
		return OBJECT_NOT_FOUND;
	END_ERROR;

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

	// For stored procedures, but ignore procedures inside packages.
	FOR PRC IN RDB$PROCEDURES
		WITH (PRC.RDB$SYSTEM_FLAG NE 1 OR PRC.RDB$SYSTEM_FLAG MISSING)
		AND PRC.RDB$PACKAGE_NAME MISSING
		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(fbStatus);
		return OBJECT_NOT_FOUND;
	END_ERROR;

	if (isqlGlob.major_ods >= ODS_VERSION12)
	{
		FOR PACK IN RDB$PACKAGES
			WITH (PACK.RDB$SYSTEM_FLAG NE 1 OR PACK.RDB$SYSTEM_FLAG MISSING)
			SORTED BY PACK.RDB$PACKAGE_NAME

			// Null terminate name string
			fb_utils::exact_name(PACK.RDB$PACKAGE_NAME);
			const processing_state rc =
				SHOW_grants2(PACK.RDB$PACKAGE_NAME, terminator, obj_package_header,
													first ? banner : 0, mangle);
			if (rc == SKIP)
				first = false;
		END_FOR
		ON_ERROR
			ISQL_errmsg(fbStatus);
			return OBJECT_NOT_FOUND;
		END_ERROR

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

			// Null terminate name string
			fb_utils::exact_name(FUN.RDB$FUNCTION_NAME);
			const processing_state rc =
				SHOW_grants2(FUN.RDB$FUNCTION_NAME, terminator, obj_udf,
													first ? banner : 0, mangle);
			if (rc == SKIP)
				first = false;
		END_FOR
		ON_ERROR
			ISQL_errmsg(fbStatus);
			return OBJECT_NOT_FOUND;
		END_ERROR

		FOR GEN IN RDB$GENERATORS
			WITH (GEN.RDB$SYSTEM_FLAG NE 1 OR GEN.RDB$SYSTEM_FLAG MISSING)
			SORTED BY GEN.RDB$GENERATOR_NAME

			// Null terminate name string
			fb_utils::exact_name(GEN.RDB$GENERATOR_NAME);
			const processing_state rc =
				SHOW_grants2(GEN.RDB$GENERATOR_NAME, terminator, obj_generator,
													 first ? banner : 0, mangle);
			if (rc == SKIP)
				first = false;
		END_FOR
		ON_ERROR
			ISQL_errmsg(fbStatus);
			return OBJECT_NOT_FOUND;
		END_ERROR

		FOR XCP IN RDB$EXCEPTIONS
			WITH (XCP.RDB$SYSTEM_FLAG NE 1 OR XCP.RDB$SYSTEM_FLAG MISSING)
			SORTED BY XCP.RDB$EXCEPTION_NAME

			// Null terminate name string
			fb_utils::exact_name(XCP.RDB$EXCEPTION_NAME);
			const processing_state rc =
				SHOW_grants2(XCP.RDB$EXCEPTION_NAME, terminator, obj_exception,
													 first ? banner : 0, mangle);
			if (rc == SKIP)
				first = false;
		END_FOR
		ON_ERROR
			ISQL_errmsg(fbStatus);
			return OBJECT_NOT_FOUND;
		END_ERROR
/*
		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
		{
			// Null terminate name string
			fb_utils::exact_name(FLD.RDB$FIELD_NAME);
			const processing_state rc =
				SHOW_grants2(FLD.RDB$FIELD_NAME, terminator, obj_field,
					(first ? banner : 0), mangle);

			if (rc == SKIP)
				first = false;
		}
		END_FOR
		ON_ERROR
			ISQL_errmsg(fbStatus);
			return OBJECT_NOT_FOUND;
		END_ERROR

		FOR CS IN RDB$CHARACTER_SETS
			SORTED BY CS.RDB$CHARACTER_SET_NAME
		{
			// Null terminate name string
			fb_utils::exact_name(CS.RDB$CHARACTER_SET_NAME);
			const processing_state rc =
				SHOW_grants2(CS.RDB$CHARACTER_SET_NAME, terminator, obj_charset,
					(first ? banner : 0), mangle);

			if (rc == SKIP)
				first = false;
		}
		END_FOR
		ON_ERROR
			ISQL_errmsg(fbStatus);
			return OBJECT_NOT_FOUND;
		END_ERROR

		FOR COL IN RDB$COLLATIONS
			SORTED BY COL.RDB$COLLATION_NAME
		{
			// Null terminate name string
			fb_utils::exact_name(COL.RDB$COLLATION_NAME);
			const processing_state rc =
				SHOW_grants2(COL.RDB$COLLATION_NAME, terminator, obj_collation,
					(first ? banner : 0), mangle);

			if (rc == SKIP)
				first = false;
		}
		END_FOR
		ON_ERROR
			ISQL_errmsg(fbStatus);
			return OBJECT_NOT_FOUND;
		END_ERROR
*/
		// Process DDL permissions
		for (int i = obj_database; i < obj_type_MAX; i++)
		{
			const processing_state rc =
				SHOW_grants2(get_object_name(i), terminator, i, first ? banner : 0, mangle);
			if (rc == SKIP)
				first = false;
		}
	}

	return first_role && first ? OBJECT_NOT_FOUND : SKIP;
}


static void print_proc_prefix(int obj_type, bool headerOnly)
{
	if (obj_type == obj_procedure || obj_type == obj_udf ||
		obj_type == obj_package_header || obj_type == obj_package_body)
	{
		isqlGlob.printf("%sCOMMIT WORK%s%s", NEWLINE, isqlGlob.global_Term, NEWLINE);
	}

	if (obj_type != obj_trigger)
		isqlGlob.printf("SET AUTODDL OFF%s%s", isqlGlob.global_Term, NEWLINE);

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

	const char* legend = NULL;
	switch (obj_type)
	{
	case obj_procedure:
		legend = headerOnly ? "Stored procedures headers" : "Stored procedures bodies";
		break;
	case obj_udf:
		legend = headerOnly ? "Stored functions headers" : "Stored functions bodies";
		break;
	case obj_trigger:
		legend = "Triggers only will work for SQL triggers";
		break;
	case obj_package_header:
		legend = "Package headers";
		break;
	case obj_package_body:
		legend = "Package bodies";
		break;
	}
	if (legend)
		isqlGlob.printf("%s/* %s */%s", NEWLINE, legend, NEWLINE);
}


static void print_proc_suffix(int obj_type)
{
	isqlGlob.printf("%sSET TERM %s %s%s", NEWLINE, isqlGlob.global_Term, Procterm, NEWLINE);
	isqlGlob.printf("COMMIT WORK%s%s", isqlGlob.global_Term, NEWLINE);

	if (obj_type != obj_trigger)
		isqlGlob.printf("SET AUTODDL ON%s%s", isqlGlob.global_Term, NEWLINE);
}

static void list_procedure_headers()
{
/**************************************
 *
 *	l i s t _ p r o c e d u r e _ h e a d e r s
 *
 **************************************
 *
 * Functional description
 * Create all procedure declarations
 * with empty body. This will allow us to create objects
 * that depend on them. The alteration to their
 * correct form is postponed to list_procedure_bodies.
 * Add the parameter names when these procedures are created.
 *
 **************************************/

	fb_assert(isqlGlob.major_ods >= ODS_VERSION12);

	bool header = true;
	static const char* const create_procedure	= "CREATE OR ALTER PROCEDURE %s ";
	static const char* const body_execut_proc	= "BEGIN EXIT; END %s%s";
	static const char* const body_select_proc	= "BEGIN SUSPEND; END %s%s";

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

	FOR PRC IN RDB$PROCEDURES
		WITH (PRC.RDB$SYSTEM_FLAG NE 1 OR PRC.RDB$SYSTEM_FLAG MISSING)
		AND PRC.RDB$PACKAGE_NAME MISSING
		SORTED BY PRC.RDB$PROCEDURE_NAME
		if (header)
		{
			print_proc_prefix(obj_procedure, true);
			header = false;
		}
		fb_utils::exact_name(PRC.RDB$PROCEDURE_NAME);
		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			IUTILS_copy_SQL_id (PRC.RDB$PROCEDURE_NAME, SQL_identifier, DBL_QUOTE);
			isqlGlob.printf(create_procedure, SQL_identifier);
		}
		else
		{
			isqlGlob.printf(create_procedure, PRC.RDB$PROCEDURE_NAME);
		}

		get_procedure_args(PRC.RDB$PROCEDURE_NAME);
		isqlGlob.printf("AS %s", NEWLINE);

		prc_t proc_type = PRC.RDB$PROCEDURE_TYPE.NULL ? prc_legacy : (prc_t) PRC.RDB$PROCEDURE_TYPE;

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

	END_FOR
	ON_ERROR
		ISQL_errmsg(fbStatus);
		return;
	END_ERROR;
	// Only reset the terminators if there were procs to print
	if (!header)
		print_proc_suffix(obj_procedure);
}



static void list_procedure_bodies()
{
/**************************************
 *
 *	l i s t _  p r o c e d u r e _ b o d i e s
 *
 **************************************
 *
 * Functional description
 *	Fill them with life
 *
 **************************************/

	fb_assert(isqlGlob.major_ods >= ODS_VERSION12);

	bool header = true;

	// 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
		WITH (PRC.RDB$SYSTEM_FLAG NE 1 OR PRC.RDB$SYSTEM_FLAG MISSING)
		AND PRC.RDB$PACKAGE_NAME MISSING
		SORTED BY PRC.RDB$PROCEDURE_NAME
		if (header)
		{
			print_proc_prefix(obj_procedure, false);
			header = false;
		}
		fb_utils::exact_name(PRC.RDB$PROCEDURE_NAME);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			IUTILS_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$ENTRYPOINT.NULL)
		{
			fb_utils::exact_name(PRC.RDB$ENTRYPOINT);
			IUTILS_copy_SQL_id(PRC.RDB$ENTRYPOINT, SQL_identifier2, SINGLE_QUOTE);
			isqlGlob.printf("EXTERNAL NAME %s%s", SQL_identifier2, NEWLINE);
		}

		if (!PRC.RDB$ENGINE_NAME.NULL)
		{
			fb_utils::exact_name(PRC.RDB$ENGINE_NAME);
			isqlGlob.printf("ENGINE %s", PRC.RDB$ENGINE_NAME);

			if (!PRC.RDB$PROCEDURE_SOURCE.NULL)
			{
				isqlGlob.printf("%sAS '", NEWLINE);
				SHOW_print_metadata_text_blob(isqlGlob.Out, &PRC.RDB$PROCEDURE_SOURCE, true);
				isqlGlob.printf("'%s", NEWLINE);
			}
		}
		else if (!PRC.RDB$PROCEDURE_SOURCE.NULL)
		{
			isqlGlob.printf("AS %s", NEWLINE);
			SHOW_print_metadata_text_blob(isqlGlob.Out, &PRC.RDB$PROCEDURE_SOURCE);
		}

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

	END_FOR
	ON_ERROR
		IUTILS_msg_get(GEN_ERR, msg, SafeArg() << isc_sqlcode(fbStatus->getErrors()));
		STDERROUT(msg);			// Statement failed, SQLCODE = %d\n\n
		ISQL_errmsg(fbStatus);
		return;
	END_ERROR;

	// Only reset the terminators if there were procs to print
	if (!header)
		print_proc_suffix(obj_procedure);
}



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(fbStatus);
		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)
		{
			print_proc_prefix(obj_trigger, false);
			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)
			IUTILS_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"),
				 SHOW_trigger_action(TRG.RDB$TRIGGER_TYPE).c_str(), TRG.RDB$TRIGGER_SEQUENCE,
				 NEWLINE);

		if (!TRG.RDB$ENTRYPOINT.NULL)
		{
			fb_utils::exact_name(TRG.RDB$ENTRYPOINT);
			IUTILS_copy_SQL_id(TRG.RDB$ENTRYPOINT, SQL_identifier2, SINGLE_QUOTE);
			isqlGlob.printf("EXTERNAL NAME %s%s", SQL_identifier2, NEWLINE);
		}

		if (!TRG.RDB$ENGINE_NAME.NULL)
		{
			fb_utils::exact_name(TRG.RDB$ENGINE_NAME);
			isqlGlob.printf("ENGINE %s", TRG.RDB$ENGINE_NAME);

			if (!TRG.RDB$TRIGGER_SOURCE.NULL)
			{
				isqlGlob.printf("%sAS '", NEWLINE);
				SHOW_print_metadata_text_blob(isqlGlob.Out, &TRG.RDB$TRIGGER_SOURCE, true);
				isqlGlob.printf("'%s", NEWLINE);
			}
		}
		else 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(fbStatus);
		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)
		{
			IUTILS_copy_SQL_id (TRG.RDB$TRIGGER_NAME, SQL_identifier,  DBL_QUOTE);
			IUTILS_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"),
				 SHOW_trigger_action(TRG.RDB$TRIGGER_TYPE).c_str(), TRG.RDB$TRIGGER_SEQUENCE,
				 NEWLINE);

		if (!TRG.RDB$ENTRYPOINT.NULL)
		{
			fb_utils::exact_name(TRG.RDB$ENTRYPOINT);
			IUTILS_copy_SQL_id(TRG.RDB$ENTRYPOINT, SQL_identifier2, SINGLE_QUOTE);
			isqlGlob.printf("EXTERNAL NAME %s%s", SQL_identifier2, NEWLINE);
		}

		if (!TRG.RDB$ENGINE_NAME.NULL)
		{
			fb_utils::exact_name(TRG.RDB$ENGINE_NAME);
			isqlGlob.printf("ENGINE %s", TRG.RDB$ENGINE_NAME);

			if (!TRG.RDB$TRIGGER_SOURCE.NULL)
			{
				isqlGlob.printf("%sAS '", NEWLINE);
				SHOW_print_metadata_text_blob(isqlGlob.Out, &TRG.RDB$TRIGGER_SOURCE, true);
				isqlGlob.printf("'%s", NEWLINE);
			}
		}
		else 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(fbStatus);
		return;
	END_ERROR;

	if (!header)
		print_proc_suffix(obj_trigger);
}


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

	bool first = true;

	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

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

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

		isqlGlob.printf(NEWLINE);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			IUTILS_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)
			{
				IUTILS_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(fbStatus);
		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)
			IUTILS_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)
			IUTILS_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(fbStatus);
		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)
			IUTILS_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)
			IUTILS_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);
			IUTILS_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(fbStatus);
		return;
	END_ERROR;

	isqlGlob.printf(NEWLINE);
}


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 UCHAR 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%s",
			fb_utils::exact_name(DBP.RDB$CHARACTER_SET_NAME), isqlGlob.global_Term);
	END_FOR
	ON_ERROR
		ISQL_errmsg(fbStatus);
		return;
	END_ERROR;

	FOR DBP2 IN RDB$DATABASE
		WITH DBP2.RDB$LINGER NOT MISSING
		AND DBP2.RDB$LINGER > 0
		isqlGlob.printf("%sALTER DATABASE SET LINGER TO %d%s", NEWLINE, DBP2.RDB$LINGER,
			isqlGlob.global_Term);
	END_FOR
	ON_ERROR
		ISQL_errmsg(fbStatus);
		return;
	END_ERROR;


	if (nodb)
		isqlGlob.printf(" */%s", NEWLINE);
	else
		isqlGlob.printf("%s", 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

		// 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;
		if (!FIL.RDB$FILE_NAME.NULL)
			fb_utils::exact_name(FIL.RDB$FILE_NAME);

		// Pure secondary files
		if (FIL.RDB$FILE_FLAGS == 0)
		{
			if (first)
			{
				isqlGlob.printf("%s/* Add secondary files in comments %s", NEWLINE, NEWLINE);
				first = false;
			}

			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("%s%s", isqlGlob.global_Term, NEWLINE);
		}
		else if (FIL.RDB$FILE_FLAGS & FILE_shadow)
		{
			if (first)
			{
				isqlGlob.printf("%s/* Add secondary files in comments %s", NEWLINE, NEWLINE);
				first = false;
			}

			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("%s%s", isqlGlob.global_Term, NEWLINE);
		}
		else if ((FIL.RDB$FILE_FLAGS & FILE_difference) && !FIL.RDB$FILE_NAME.NULL)
		{
			if (first)
			{
				isqlGlob.printf("%s/* Add secondary files in comments %s", NEWLINE, NEWLINE);
				first = false;
			}

			// This is an explicit name for the difference file typically named <db_name>.delta
			isqlGlob.printf("%sALTER DATABASE ADD DIFFERENCE FILE '%s'", NEWLINE, FIL.RDB$FILE_NAME);
			isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);
		}

	END_FOR
	ON_ERROR
		ISQL_errmsg(fbStatus);
		return;
	END_ERROR;

	if (!first)
	{
		isqlGlob.printf("%s */%s", NEWLINE, NEWLINE);
		//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);

		if (!ISQL_printNumericType(FLD.RDB$FIELD_NAME, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE))
			return; // ps_ERR;

		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));
		}

		if (!FLD.RDB$DIMENSIONS.NULL)
			ISQL_array_dimensions(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, true,
					char_sets);
			}
			if (char_sets[0])
				isqlGlob.prints(char_sets);
		}

		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, false, true);
		}
		if (!FLD.RDB$VALIDATION_SOURCE.NULL)
		{
			isqlGlob.printf("%s%s ", NEWLINE, TAB_AS_SPACES);
			ISQL_print_validation (isqlGlob.Out, &FLD.RDB$VALIDATION_SOURCE, false, fbTrans);
		}
		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,
				true, char_sets);

			if (char_sets[0])
				isqlGlob.prints(char_sets);
		}

		isqlGlob.printf("%s%s", isqlGlob.global_Term, NEWLINE);
	END_FOR
	ON_ERROR
		ISQL_errmsg(fbStatus);
		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)
		{
			IUTILS_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);

		if (!ISQL_printNumericType(FLD.RDB$FIELD_NAME, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE))
			return; // ps_ERR;

		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));
		}

		if (!FLD.RDB$DIMENSIONS.NULL)
			ISQL_array_dimensions(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, true,
					char_sets);
			}
			if (char_sets[0])
				isqlGlob.prints(char_sets);
		}

		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, false, true);
		}
		// Validation moved to listDomainConstraints().
		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,
				true, char_sets);

			if (char_sets[0])
				isqlGlob.prints(char_sets);
		}

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


static void listDomainConstraints()
{
	bool first = true;

	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
		AND FLD.RDB$VALIDATION_SOURCE NOT MISSING
		SORTED BY FLD.RDB$FIELD_NAME

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

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			IUTILS_copy_SQL_id(FLD.RDB$FIELD_NAME, SQL_identifier, DBL_QUOTE);
			isqlGlob.printf("ALTER DOMAIN %s ADD CONSTRAINT", SQL_identifier);
		}
		else
			isqlGlob.printf("ALTER DOMAIN %s ADD CONSTRAINT", FLD.RDB$FIELD_NAME);

		isqlGlob.printf("%s%s ", NEWLINE, TAB_AS_SPACES);
		ISQL_print_validation (isqlGlob.Out, &FLD.RDB$VALIDATION_SOURCE, false, fbTrans);

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


// Fix computed field expressions.
static void listRelationComputed(LegacyTables flag, SSHORT default_char_set_id)
{
	// This version of cursor gets only sql tables identified by security class
	// and misses views, getting only null view_source

	Firebird::string lastTable;
	SCHAR char_sets[86];

	FOR REL IN RDB$RELATIONS CROSS
		RFR IN RDB$RELATION_FIELDS CROSS
		FLD IN RDB$FIELDS
		WITH (REL.RDB$SYSTEM_FLAG NE 1 OR REL.RDB$SYSTEM_FLAG MISSING) AND
			 REL.RDB$VIEW_BLR MISSING AND
			 FLD.RDB$COMPUTED_BLR NOT MISSING AND
			 RFR.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND
			 RFR.RDB$RELATION_NAME EQ REL.RDB$RELATION_NAME
		SORTED BY REL.RDB$RELATION_NAME, RFR.RDB$FIELD_POSITION, RFR.RDB$FIELD_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;

		SSHORT collation = 0;
		SSHORT char_set_id = 0;

		// Null terminate name string
		fb_utils::exact_name(REL.RDB$RELATION_NAME);

		if (flag || !strncmp(REL.RDB$SECURITY_CLASS, "SQL$", 4))
		{
			if (lastTable.isEmpty())
				isqlGlob.printf("%s/* Computed fields */%s%s", NEWLINE, NEWLINE, NEWLINE);

			if (lastTable != REL.RDB$RELATION_NAME)
			{
				if (!lastTable.isEmpty())
					isqlGlob.printf("%s%s%s", isqlGlob.global_Term, NEWLINE, NEWLINE);

				isqlGlob.printf("ALTER TABLE ");

				if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
				{
					IUTILS_copy_SQL_id(REL.RDB$RELATION_NAME, SQL_identifier, DBL_QUOTE);
					isqlGlob.printf("%s ", SQL_identifier);
				}
				else
					isqlGlob.printf("%s ", REL.RDB$RELATION_NAME);

				lastTable = REL.RDB$RELATION_NAME;
			}
			else
				isqlGlob.printf(",");

			isqlGlob.printf("%s%sALTER ", NEWLINE, TAB_AS_SPACES);

			if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			{
				IUTILS_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));

			isqlGlob.printf("TYPE ");

			/*
			** 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 (!(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)
				{
					IUTILS_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, true,
						char_sets);

					if (char_sets[0])
						isqlGlob.prints(char_sets);
				}
			}
			else
			{
				if (!ISQL_printNumericType(FLD.RDB$FIELD_NAME, FLD.RDB$FIELD_TYPE, FLD.RDB$FIELD_SCALE))
					return;

				if ((FLD.RDB$FIELD_TYPE == T_CHAR) || (FLD.RDB$FIELD_TYPE == VARCHAR))
				{
					isqlGlob.printf("(%d)",
						ISQL_get_char_length(
							FLD.RDB$FIELD_LENGTH,
							FLD.RDB$CHARACTER_LENGTH.NULL, FLD.RDB$CHARACTER_LENGTH,
							FLD.RDB$CHARACTER_SET_ID.NULL, FLD.RDB$CHARACTER_SET_ID
						)
					);
				}

				// 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, true, 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;
				}
			}

			isqlGlob.printf(" COMPUTED BY ");

			if (!FLD.RDB$COMPUTED_SOURCE.NULL)
				ISQL_print_validation(isqlGlob.Out, &FLD.RDB$COMPUTED_SOURCE, true, fbTrans);
		}
	END_FOR
	ON_ERROR
		ISQL_errmsg(fbStatus);
		ROLLBACK;
		return;
	END_ERROR;

	if (lastTable.hasData())
		isqlGlob.printf("%s%s%s", isqlGlob.global_Term, NEWLINE, NEWLINE);
}


static void list_exceptions()
{
/**************************************
 *
 *	l i s t _ e x c e p t i o n s
 *
 **************************************
 *
 * 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);

		IUTILS_copy_SQL_id (EXC.RDB$MESSAGE, SQL_identifier2, SINGLE_QUOTE);
		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			IUTILS_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(fbStatus);
		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)
			IUTILS_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);

		IUTILS_copy_SQL_id(FIL.RDB$ENTRYPOINT, SQL_identifier, SINGLE_QUOTE);
		IUTILS_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(fbStatus);
		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)
		{
			IUTILS_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))
		{
			IUTILS_truncate_term (RELC1.RDB$CONSTRAINT_NAME, static_cast<USHORT>(strlen(RELC1.RDB$CONSTRAINT_NAME)));
			if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			{
				IUTILS_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)
		{
			IUTILS_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)
		{
			IUTILS_truncate_term (REFC.RDB$UPDATE_RULE, static_cast<USHORT>(strlen(REFC.RDB$UPDATE_RULE)));
			ISQL_ri_action_print (REFC.RDB$UPDATE_RULE, " ON UPDATE", true);
		}

		if (!REFC.RDB$DELETE_RULE.NULL)
		{
			IUTILS_truncate_term (REFC.RDB$DELETE_RULE, static_cast<USHORT>(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(fbStatus);
		return;
	END_ERROR;
}


static void list_functions_legacy()
{
/**************************************
 *
 *	l i s t _ f u n c t i o n s _ l e g a c y
 *
 **************************************
 *
 * 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)
		AND FUN.RDB$MODULE_NAME NOT MISSING
		AND FUN.RDB$PACKAGE_NAME 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)
			IUTILS_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
			AND FNA.RDB$PACKAGE_NAME MISSING
			SORTED BY FNA.RDB$ARGUMENT_POSITION

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

			fb_assert(Column_types[i].type != 0);

			// 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 (fbStatus);
					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))
				{
					// We are ODS >= 10 and could be any Dialect
					// We are Dialect >=3 since FIELD_PRECISION is non-NULL
					if (!FNA.RDB$FIELD_PRECISION.NULL &&
						FNA.RDB$FIELD_SUB_TYPE > 0 &&
						FNA.RDB$FIELD_SUB_TYPE <= MAX_INTSUBTYPES)
					{
						sprintf (type_buffer, "%s(%d, %d)",
								Integral_subtypes[FNA.RDB$FIELD_SUB_TYPE],
								FNA.RDB$FIELD_PRECISION,
								-FNA.RDB$FIELD_SCALE);
						precision_known = true;
					}
				} // 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 (fbStatus);
			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
		IUTILS_copy_SQL_id(FUN.RDB$ENTRYPOINT, SQL_identifier, SINGLE_QUOTE);
		IUTILS_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(fbStatus);
		return;
	END_ERROR;
}

static void list_functions_ods12_headers()
{
	fb_assert(isqlGlob.major_ods >= ODS_VERSION12);

	bool header = true;
	static const char* const create_function	= "CREATE OR ALTER FUNCTION %s ";
	static const char* const body_function		= "BEGIN END %s%s";

	//  First the dummy functions (without bodies)

	FOR FUN IN RDB$FUNCTIONS
		WITH (FUN.RDB$SYSTEM_FLAG NE 1 OR FUN.RDB$SYSTEM_FLAG MISSING)
		AND FUN.RDB$PACKAGE_NAME MISSING
		AND FUN.RDB$MODULE_NAME MISSING
		SORTED BY FUN.RDB$FUNCTION_NAME

		if (header)
		{
			print_proc_prefix(obj_udf, true);
			header = false;
		}

		fb_utils::exact_name(FUN.RDB$FUNCTION_NAME);
		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			IUTILS_copy_SQL_id(FUN.RDB$FUNCTION_NAME, SQL_identifier, DBL_QUOTE);
			isqlGlob.printf(create_function, SQL_identifier);
		}
		else
		{
			isqlGlob.printf(create_function, FUN.RDB$FUNCTION_NAME);
		}

		get_function_args_ods12(FUN.RDB$FUNCTION_NAME, FUN.RDB$RETURN_ARGUMENT);

		if (!FUN.RDB$DETERMINISTIC_FLAG.NULL && FUN.RDB$DETERMINISTIC_FLAG)
			isqlGlob.printf("DETERMINISTIC %s", NEWLINE);

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

	END_FOR
	ON_ERROR
		ISQL_errmsg(fbStatus);
		return;
	END_ERROR;

	// Only reset the terminators if there were procs to print
	if (!header)
		print_proc_suffix(obj_udf);
}

static void list_functions_ods12_bodies()
{
	fb_assert(isqlGlob.major_ods >= ODS_VERSION12);

	bool header = true;

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

	FOR FUN IN RDB$FUNCTIONS
		WITH (FUN.RDB$SYSTEM_FLAG NE 1 OR FUN.RDB$SYSTEM_FLAG MISSING)
		AND FUN.RDB$PACKAGE_NAME MISSING
		AND FUN.RDB$MODULE_NAME MISSING
		SORTED BY FUN.RDB$FUNCTION_NAME
		if (header)
		{
			print_proc_prefix(obj_udf, false);
			header = false;
		}
		fb_utils::exact_name(FUN.RDB$FUNCTION_NAME);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
		{
			IUTILS_copy_SQL_id(FUN.RDB$FUNCTION_NAME, SQL_identifier, DBL_QUOTE);
			isqlGlob.printf("%sALTER FUNCTION %s ", NEWLINE, SQL_identifier);
		}
		else
			isqlGlob.printf("%sALTER FUNCTION %s ", NEWLINE, FUN.RDB$FUNCTION_NAME);

		get_function_args_ods12(FUN.RDB$FUNCTION_NAME, FUN.RDB$RETURN_ARGUMENT);

		if (!FUN.RDB$DETERMINISTIC_FLAG.NULL && FUN.RDB$DETERMINISTIC_FLAG)
			isqlGlob.printf("DETERMINISTIC %s", NEWLINE);

		// Print the function body

		if (!FUN.RDB$ENTRYPOINT.NULL)
		{
			fb_utils::exact_name(FUN.RDB$ENTRYPOINT);
			IUTILS_copy_SQL_id(FUN.RDB$ENTRYPOINT, SQL_identifier2, SINGLE_QUOTE);
			isqlGlob.printf("EXTERNAL NAME %s%s", SQL_identifier2, NEWLINE);
		}

		if (!FUN.RDB$ENGINE_NAME.NULL)
		{
			fb_utils::exact_name(FUN.RDB$ENGINE_NAME);
			isqlGlob.printf("ENGINE %s", FUN.RDB$ENGINE_NAME);

			if (!FUN.RDB$FUNCTION_SOURCE.NULL)
			{
				isqlGlob.printf("%sAS '", NEWLINE);
				SHOW_print_metadata_text_blob(isqlGlob.Out, &FUN.RDB$FUNCTION_SOURCE, true);
				isqlGlob.printf("'%s", NEWLINE);
			}
		}
		else if (!FUN.RDB$FUNCTION_SOURCE.NULL)
		{
			isqlGlob.printf("AS %s", NEWLINE);
			SHOW_print_metadata_text_blob(isqlGlob.Out, &FUN.RDB$FUNCTION_SOURCE);
		}

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

	END_FOR
	ON_ERROR
		IUTILS_msg_get(GEN_ERR, msg, SafeArg() << isc_sqlcode(fbStatus->getErrors()));
		STDERROUT(msg);			// Statement failed, SQLCODE = %d\n\n
		ISQL_errmsg(fbStatus);
		return;
	END_ERROR;

	// Only reset the terminators if there were procs to print
	if (!header)
		print_proc_suffix(obj_udf);
}


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)
			IUTILS_copy_SQL_id(GEN.RDB$GENERATOR_NAME, SQL_identifier, DBL_QUOTE);
		else
			strcpy(SQL_identifier, GEN.RDB$GENERATOR_NAME);

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

		if (isqlGlob.major_ods >= ODS_VERSION12)
		{
			FOR G2 IN RDB$GENERATORS
				WITH G2.RDB$GENERATOR_NAME = GEN.RDB$GENERATOR_NAME

				if (!G2.RDB$INITIAL_VALUE.NULL && G2.RDB$INITIAL_VALUE != 0)
					isqlGlob.printf(" START WITH %" SQUADFORMAT, G2.RDB$INITIAL_VALUE);

				if (G2.RDB$GENERATOR_INCREMENT != 1)
					isqlGlob.printf(" INCREMENT %" SLONGFORMAT, G2.RDB$GENERATOR_INCREMENT);

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

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

	isqlGlob.printf(NEWLINE);
}


static void list_indexes()
{
/**************************************
 *
 *	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)
		{
			IUTILS_copy_SQL_id (IDX.RDB$INDEX_NAME,    SQL_identifier,  DBL_QUOTE);
			IUTILS_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, false, true);
			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(fbStatus);
		return;
	END_ERROR;
}


static void list_package_bodies()
{
/**************************************
 *
 *	l i s t _ p a c k a g e _ b o d i e s
 *
 **************************************
 *
 * Functional description
 *	Create body of packages
 *	Use a SQL query to get the info and print it.
 *	Ignore the packages whose body has not been defined yet.
 *
 **************************************/

	if (isqlGlob.major_ods < ODS_VERSION12)
		return;

	bool header = true;

	FOR PACK IN RDB$PACKAGES WITH
		(PACK.RDB$SYSTEM_FLAG NE 1 OR PACK.RDB$SYSTEM_FLAG MISSING)
		AND PACK.RDB$PACKAGE_BODY_SOURCE NOT MISSING
		SORTED BY PACK.RDB$PACKAGE_NAME

		if (header)
		{
			print_proc_prefix(obj_package_body, false);
			header = false;
		}

		fb_utils::exact_name(PACK.RDB$PACKAGE_NAME);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			IUTILS_copy_SQL_id (PACK.RDB$PACKAGE_NAME, SQL_identifier, DBL_QUOTE);
		else
			strcpy(SQL_identifier, PACK.RDB$PACKAGE_NAME);

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

		isqlGlob.printf("%s/* Package body: %s, Owner: %s%s */%s",
				 NEWLINE,
				 PACK.RDB$PACKAGE_NAME,
				 PACK.RDB$OWNER_NAME,
				 (!PACK.RDB$VALID_BODY_FLAG.NULL && PACK.RDB$VALID_BODY_FLAG != 0 ? "" : ", Invalid"),
				 NEWLINE);

		if (!PACK.RDB$VALID_BODY_FLAG.NULL && PACK.RDB$VALID_BODY_FLAG != 0)
		{
			isqlGlob.printf("CREATE PACKAGE BODY %s AS%s", SQL_identifier, NEWLINE);
			SHOW_print_metadata_text_blob(isqlGlob.Out, &PACK.RDB$PACKAGE_BODY_SOURCE);
			isqlGlob.printf("%s%s", Procterm, NEWLINE);
		}

	END_FOR
	ON_ERROR
		ISQL_errmsg(fbStatus);
		return;
	END_ERROR;

	if (!header)
		print_proc_suffix(obj_package_body);
}


static void list_package_headers()
{
/**************************************
 *
 *	l i s t _ p a c k a g e _ h e a d e r s
 *
 **************************************
 *
 * Functional description
 *	Show header of packages
 *	Use a SQL query to get the info and print it.
 *
 **************************************/

 	if (isqlGlob.major_ods < ODS_VERSION12)
 		return;

 	bool header = true;

	FOR PACK IN RDB$PACKAGES WITH
		(PACK.RDB$SYSTEM_FLAG NE 1 OR PACK.RDB$SYSTEM_FLAG MISSING)
		SORTED BY PACK.RDB$PACKAGE_NAME

		if (header)
		{
			print_proc_prefix(obj_package_header, false);
			header = false;
		}

		fb_utils::exact_name(PACK.RDB$PACKAGE_NAME);

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			IUTILS_copy_SQL_id (PACK.RDB$PACKAGE_NAME, SQL_identifier, DBL_QUOTE);
		else
			strcpy(SQL_identifier, PACK.RDB$PACKAGE_NAME);

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

		isqlGlob.printf("%s/* Package header: %s, Owner: %s */%s",
				 NEWLINE,
				 PACK.RDB$PACKAGE_NAME,
				 PACK.RDB$OWNER_NAME,
				 NEWLINE);
		isqlGlob.printf("CREATE PACKAGE %s AS%s", SQL_identifier, NEWLINE);

		if (!PACK.RDB$PACKAGE_HEADER_SOURCE.NULL)
			SHOW_print_metadata_text_blob(isqlGlob.Out, &PACK.RDB$PACKAGE_HEADER_SOURCE);
		else
			isqlGlob.printf("BEGIN END /* Missing package header info. */");

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

	END_FOR
	ON_ERROR
		ISQL_errmsg(fbStatus);
		return;
	END_ERROR;

	if (!header)
		print_proc_suffix(obj_package_header);
}


static void list_views()
{
/**************************************
 *
 *	l i s t _ v i e w s
 *
 **************************************
 *
 * Functional description
 *	Create 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

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

		if (isqlGlob.db_SQL_dialect > SQL_DIALECT_V6_TRANSITION)
			IUTILS_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);

		bool first = true;

		// 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)
				IUTILS_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 (fbStatus);
			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(fbStatus);
		return;
	END_ERROR;
}
