/*
 *	PROGRAM:	JRD Access Method
 *	MODULE:		ini.epp
 *	DESCRIPTION:	Metadata initialization / population
 *
 * 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): ______________________________________.
 */

#include "firebird.h"
#include <stdio.h>
#include <string.h>
#include "../jrd/common.h"
#include "../jrd/flags.h"
#include "../jrd/jrd.h"
#include "../jrd/val.h"
#include "../jrd/ibase.h"
#include "../jrd/ods.h"
#include "../jrd/btr.h"
#include "gen/ids.h"
#include "../jrd/tra.h"
#include "../jrd/trig.h"
#include "../jrd/intl.h"
#include "../jrd/dflt.h"
#include "../jrd/ini.h"
#include "../jrd/idx.h"
#include "../jrd/gdsassert.h"
#include "../jrd/blb_proto.h"
#include "../jrd/cch_proto.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/dfw_proto.h"
#include "../jrd/dpm_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/exe_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/idx_proto.h"
#include "../jrd/ini_proto.h"
#include "../jrd/jrd_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/obj.h"
#include "../jrd/acl.h"
#include "../jrd/irq.h"
#include "../jrd/IntlManager.h"
#include "../jrd/constants.h"


using namespace Jrd;

DATABASE DB = FILENAME "ODS.RDB";

#define PAD(string, field) jrd_vtof ((char*)(string), field, sizeof (field))
const int FB_MAX_ACL_SIZE	= 4096;


static void add_index_set(Database*, bool, USHORT, USHORT);
static void add_relation_fields(thread_db*, USHORT);
static void add_security_to_sys_rel(thread_db*, const Firebird::MetaName&,
		const TEXT*, const UCHAR*, const SSHORT);
static int init2_helper(const int* fld, int n, jrd_rel* relation);
static void modify_relation_field(thread_db*, const int*, const int*, jrd_req**);
static void store_generator(thread_db*, const gen*, jrd_req**);
static void store_global_field(thread_db*, const gfld*, jrd_req**);
static void store_intlnames(thread_db*, Database*);
static void store_functions(thread_db*, Database*);
static void store_message(thread_db*, const trigger_msg*, jrd_req**);
static void store_relation_field(thread_db*, const int*, const int*, int, jrd_req**, bool);
static void store_trigger(thread_db*, const jrd_trg*, jrd_req**);


/* This is the table used in defining triggers; note that
   RDB$TRIGGER_0 was first changed to RDB$TRIGGER_7 to make it easier to
   upgrade a database to support field-level grant.  It has since been
   changed to RDB$TRIGGER_9 to handle SQL security on relations whose
   name is > 27 characters */

static const jrd_trg triggers[] =
{
	{ "RDB$TRIGGER_1", (UCHAR) nam_user_privileges,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger3), trigger3,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_8", (UCHAR) nam_user_privileges,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger2), trigger2,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_9", (UCHAR) nam_user_privileges,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_STORE, sizeof(trigger1), trigger1,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_2", (UCHAR) nam_trgs,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger4), trigger4,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_3", (UCHAR) nam_trgs,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger4), trigger4,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_4", (UCHAR) nam_relations,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_STORE, sizeof(trigger5), trigger5,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_5", (UCHAR) nam_relations,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger6), trigger6,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_6", (UCHAR) nam_gens,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_STORE, sizeof(trigger7), trigger7,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_26", (UCHAR) nam_rel_constr,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_STORE, sizeof(trigger26), trigger26,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_25", (UCHAR) nam_rel_constr,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger25),
		trigger25, 0, ODS_8_0 },
	{ "RDB$TRIGGER_10", (UCHAR) nam_rel_constr,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger10), trigger10,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_11", (UCHAR) nam_rel_constr,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.POST_ERASE, sizeof(trigger11),
		trigger11, 0, ODS_8_0 },
	{ "RDB$TRIGGER_12", (UCHAR) nam_ref_constr,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_STORE, sizeof(trigger12), trigger12,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_13", (UCHAR) nam_ref_constr,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger13),
		trigger13, 0, ODS_8_0 },
	{ "RDB$TRIGGER_14", (UCHAR) nam_chk_constr,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger14),
		trigger14, 0, ODS_8_0 },
	{ "RDB$TRIGGER_15", (UCHAR) nam_chk_constr,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger15), trigger15,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_16", (UCHAR) nam_chk_constr,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.POST_ERASE, sizeof(trigger16),
		trigger16, 0, ODS_8_0 },
	{ "RDB$TRIGGER_17", (UCHAR) nam_i_segments,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger17), trigger17,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_18", (UCHAR) nam_i_segments,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger18),
		trigger18, 0, ODS_8_0 },
	{ "RDB$TRIGGER_19", (UCHAR) nam_indices,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger19), trigger19,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_20", (UCHAR) nam_indices,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger20),
		trigger20, 0, ODS_8_0 },
	{ "RDB$TRIGGER_21", (UCHAR) nam_trgs,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger21), trigger21,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_22", (UCHAR) nam_trgs,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger22),
		trigger22, 0, ODS_8_0 },
	{ "RDB$TRIGGER_23", (UCHAR) nam_r_fields,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger23), trigger23,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_24", (UCHAR) nam_r_fields,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger24),
		trigger24, 0, ODS_8_0 },
	{ "RDB$TRIGGER_27", (UCHAR) nam_r_fields,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.POST_ERASE, sizeof(trigger27),
		trigger27, 0, ODS_8_0 },
	{ "RDB$TRIGGER_28", (UCHAR) nam_procedures,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_STORE, sizeof(trigger28), trigger28,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_29", (UCHAR) nam_procedures,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger29),
		trigger29, 0, ODS_8_0 },
	{ "RDB$TRIGGER_30", (UCHAR) nam_exceptions,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_STORE, sizeof(trigger30), trigger30,
		0, ODS_8_0 },
	{ "RDB$TRIGGER_31", (UCHAR) nam_user_privileges,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger31),
		trigger31, 0, ODS_8_1 },
	{ "RDB$TRIGGER_32", (UCHAR) nam_user_privileges,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_ERASE, sizeof(trigger31), trigger31,
		0, ODS_8_1 },
	{ "RDB$TRIGGER_33", (UCHAR) nam_user_privileges,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_STORE, sizeof(trigger31), trigger31,
		0, ODS_8_1 },
	{ "RDB$TRIGGER_34", (UCHAR) nam_rel_constr,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.POST_ERASE, sizeof(trigger34),
		trigger34, TRG_ignore_perm, ODS_8_1 },
	{ "RDB$TRIGGER_35", (UCHAR) nam_chk_constr,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.POST_ERASE, sizeof(trigger35),
		trigger35, TRG_ignore_perm, ODS_8_1 },
	{ "RDB$TRIGGER_36", (UCHAR) nam_fields,
		RDB$TRIGGERS.RDB$TRIGGER_TYPE.PRE_MODIFY, sizeof(trigger36),
		trigger36, 0, ODS_11_0 },
	{ 0, 0, 0, 0, 0, 0 }
};


/* this table is used in defining messages for system triggers */

static const trigger_msg trigger_messages[] =
{
	{ "RDB$TRIGGER_9", 0, "grant_obj_notfound", ODS_8_0 },
	{ "RDB$TRIGGER_9", 1, "grant_fld_notfound", ODS_8_0 },
	{ "RDB$TRIGGER_9", 2, "grant_nopriv", ODS_8_0 },
	{ "RDB$TRIGGER_9", 3, "nonsql_security_rel", ODS_8_0 },
	{ "RDB$TRIGGER_9", 4, "nonsql_security_fld", ODS_8_0 },
	{ "RDB$TRIGGER_9", 5, "grant_nopriv_on_base", ODS_8_0 },
	{ "RDB$TRIGGER_1", 0, "existing_priv_mod", ODS_8_0 },
	{ "RDB$TRIGGER_2", 0, "systrig_update", ODS_8_0 },
	{ "RDB$TRIGGER_3", 0, "systrig_update", ODS_8_0 },
	{ "RDB$TRIGGER_5", 0, "not_rel_owner", ODS_8_0 },
	{ "RDB$TRIGGER_24", 1, "cnstrnt_fld_rename", ODS_8_0 },
	{ "RDB$TRIGGER_23", 1, "cnstrnt_fld_del", ODS_8_0 },
	{ "RDB$TRIGGER_22", 1, "check_trig_update", ODS_8_0 },
	{ "RDB$TRIGGER_21", 1, "check_trig_del", ODS_8_0 },
	{ "RDB$TRIGGER_20", 1, "integ_index_mod", ODS_8_0 },
	{ "RDB$TRIGGER_20", 2, "integ_index_deactivate", ODS_8_0 },
	{ "RDB$TRIGGER_20", 3, "integ_deactivate_primary", ODS_8_0 },
	{ "RDB$TRIGGER_19", 1, "integ_index_del", ODS_8_0 },
	{ "RDB$TRIGGER_18", 1, "integ_index_seg_mod", ODS_8_0 },
	{ "RDB$TRIGGER_17", 1, "integ_index_seg_del", ODS_8_0 },
	{ "RDB$TRIGGER_15", 1, "check_cnstrnt_del", ODS_8_0 },
	{ "RDB$TRIGGER_14", 1, "check_cnstrnt_update", ODS_8_0 },
	{ "RDB$TRIGGER_13", 1, "ref_cnstrnt_update", ODS_8_0 },
	{ "RDB$TRIGGER_12", 1, "ref_cnstrnt_notfound", ODS_8_0 },
	{ "RDB$TRIGGER_12", 2, "foreign_key_notfound", ODS_8_0 },
	{ "RDB$TRIGGER_10", 1, "primary_key_ref", ODS_8_0 },
	{ "RDB$TRIGGER_10", 2, "primary_key_notnull", ODS_8_0 },
	{ "RDB$TRIGGER_25", 1, "rel_cnstrnt_update", ODS_8_0 },
	{ "RDB$TRIGGER_26", 1, "constaint_on_view", ODS_8_0 },
	{ "RDB$TRIGGER_26", 2, "invld_cnstrnt_type", ODS_8_0 },
	{ "RDB$TRIGGER_26", 3, "primary_key_exists", ODS_8_0 },
	{ "RDB$TRIGGER_31", 0, "no_write_user_priv", ODS_8_1 },
	{ "RDB$TRIGGER_32", 0, "no_write_user_priv", ODS_8_1 },
	{ "RDB$TRIGGER_33", 0, "no_write_user_priv", ODS_8_1 },
	{ "RDB$TRIGGER_24", 2, "integ_index_seg_mod", ODS_11_0 },
	{ "RDB$TRIGGER_36", 1, "integ_index_seg_mod", ODS_11_0 },
	{ 0, 0, 0, 0 }
};



void INI_format(const TEXT* owner, const TEXT* charset)
{
/**************************************
 *
 *	I N I _ f o r m a t
 *
 **************************************
 *
 * Functional description
 *	Initialize system relations in the database.
 *	The full complement of metadata should be
 *	stored here.
 *
 **************************************/
	thread_db* tdbb = JRD_get_thread_data();
	Database* dbb = tdbb->getDatabase();

/* Uppercase owner name */
	Firebird::MetaName string(owner ? owner : "");
	string.upper7();

/* Uppercase charset name */
	Firebird::MetaName string2(charset ? charset : "");
	string2.upper7();

	int n;
	const int* fld;

/* Make sure relations exist already */

	for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
	{
		if (relfld[RFLD_R_TYPE] == rel_persistent)
		{
			DPM_create_relation(tdbb, MET_relation(tdbb, relfld[RFLD_R_ID]));
		}

		for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
			;
	}

/* Store RELATIONS and RELATION_FIELDS */

	jrd_req* handle1 = NULL;
	jrd_req* handle2 = NULL;
	for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
	{
		for (n = 0, fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
		{
			if (!fld[RFLD_F_MINOR])
			{
				const int* pFld    = fld;
				const int* pRelFld = relfld;
				store_relation_field(tdbb, pFld, pRelFld, n, &handle2, true);
				n++;
			}
		}

		STORE(REQUEST_HANDLE handle1) X IN RDB$RELATIONS
			X.RDB$RELATION_ID = relfld[RFLD_R_ID];
			PAD(names[relfld[RFLD_R_NAME]], X.RDB$RELATION_NAME);
			X.RDB$FIELD_ID = n;
			X.RDB$FORMAT = 0;
			X.RDB$SYSTEM_FLAG = RDB_system;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$DBKEY_LENGTH = 8;
			X.RDB$OWNER_NAME.NULL = TRUE;
			X.RDB$RELATION_TYPE = relfld[RFLD_R_TYPE];

			if (string.length())
			{
				PAD(string.c_str(), X.RDB$OWNER_NAME);
				X.RDB$OWNER_NAME.NULL = FALSE;
			}

		END_STORE;
	}

	CMP_release(tdbb, handle1);
	CMP_release(tdbb, handle2);
	handle1 = handle2 = NULL;

/* Store global FIELDS */

	for (const gfld* gfield = gfields; gfield->gfld_name; gfield++)
	{
		store_global_field(tdbb, gfield, &handle1);
	}

	CMP_release(tdbb, handle1);
	handle1 = NULL;

	// Store DATABASE record

	STORE(REQUEST_HANDLE handle1) X IN RDB$DATABASE
		X.RDB$RELATION_ID = (int) USER_DEF_REL_INIT_ID;
		X.RDB$CHARACTER_SET_NAME.NULL = TRUE;
		if (string2.length()) {
			PAD (string2.c_str(), X.RDB$CHARACTER_SET_NAME);
			X.RDB$CHARACTER_SET_NAME.NULL = FALSE;
		}
		else {
			PAD (DEFAULT_DB_CHARACTER_SET_NAME, X.RDB$CHARACTER_SET_NAME);
			X.RDB$CHARACTER_SET_NAME.NULL = FALSE;
		}
	END_STORE

	CMP_release(tdbb, handle1);
	handle1 = NULL;

	// Store ADMIN_ROLE record

	STORE(REQUEST_HANDLE handle1) X IN RDB$ROLES
		PAD (ADMIN_ROLE, X.RDB$ROLE_NAME);
		X.RDB$ROLE_NAME.NULL = FALSE;
		if (string.length()) {
			PAD (string.c_str(), X.RDB$OWNER_NAME);
		}
		else {
			PAD (SYSDBA_USER_NAME, X.RDB$OWNER_NAME);
		}
		X.RDB$OWNER_NAME.NULL = FALSE;
		X.RDB$DESCRIPTION.NULL = TRUE;
		X.RDB$SYSTEM_FLAG = ROLE_FLAG_DBO;	// Mark it as privileged role
		X.RDB$SYSTEM_FLAG.NULL = FALSE;
	END_STORE

	CMP_release(tdbb, handle1);
	handle1 = NULL;

	// Create indices for system relations
	add_index_set(dbb, false, 0, 0);

	// Create parameter types

	handle1 = NULL;

	for (const rtyp* type = types; type->rtyp_name; ++type)
	{
		// this STORE should be compatible with two below,
		// as they use the same compiled request
		STORE(REQUEST_HANDLE handle1) X IN RDB$TYPES
			PAD(names[type->rtyp_field], X.RDB$FIELD_NAME);
			PAD(type->rtyp_name, X.RDB$TYPE_NAME);
			X.RDB$TYPE = type->rtyp_value;
			X.RDB$SYSTEM_FLAG = RDB_system;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
		END_STORE;
	}

	for (const IntlManager::CharSetDefinition* charSet = IntlManager::defaultCharSets;
		 charSet->name; ++charSet)
	{
		// this STORE should be compatible with one above and below,
		// as they use the same compiled request
		STORE(REQUEST_HANDLE handle1) X IN RDB$TYPES
			PAD(names[nam_charset_name], X.RDB$FIELD_NAME);
			PAD(charSet->name, X.RDB$TYPE_NAME);
			X.RDB$TYPE = charSet->id;
			X.RDB$SYSTEM_FLAG = RDB_system;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
		END_STORE;
	}

	for (const IntlManager::CharSetAliasDefinition* alias = IntlManager::defaultCharSetAliases;
		alias->name; ++alias)
	{
		// this STORE should be compatible with two above,
		// as they use the same compiled request
		STORE(REQUEST_HANDLE handle1) X IN RDB$TYPES
			PAD(names[nam_charset_name], X.RDB$FIELD_NAME);
			PAD(alias->name, X.RDB$TYPE_NAME);
			X.RDB$TYPE = alias->charSetId;
			X.RDB$SYSTEM_FLAG = RDB_system;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
		END_STORE;
	}

	CMP_release(tdbb, handle1);

	// Store symbols for international character sets & collations
	store_intlnames(tdbb, dbb);

	// Create generators to be used by system triggers

	handle1 = NULL;
	for (const gen* generator = generators; generator->gen_name; generator++)
	{
		store_generator(tdbb, generator, &handle1);
	}
	CMP_release(tdbb, handle1);

	// Adjust the value of the hidden generator RDB$GENERATORS
	DPM_gen_id(tdbb, 0, true, FB_NELEM(generators) - 1);

/* store system-defined triggers */

	handle1 = NULL;
	for (const jrd_trg* trigger = triggers; trigger->trg_relation; ++trigger)
	{
		store_trigger(tdbb, trigger, &handle1);
	}
	CMP_release(tdbb, handle1);

/* store trigger messages to go with triggers */

	handle1 = NULL;
	for (const trigger_msg* message = trigger_messages; message->trigmsg_name;
		++message)
	{
		store_message(tdbb, message, &handle1);
	}
	CMP_release(tdbb, handle1);

// Store IUDFs declared automatically as system functions.
// CVC: Demonstration code to register IUDF automatically moved to functions.h

	store_functions(tdbb, dbb);

	DFW_perform_system_work(tdbb);

	add_relation_fields(tdbb, 0);

/*
====================================================================
==
== Add security on RDB$ROLES system table
==
======================================================================
*/

	UCHAR buffer[FB_MAX_ACL_SIZE];
	UCHAR* acl = buffer;
	*acl++ = ACL_version;
	*acl++ = ACL_id_list;
	*acl++ = id_person;

	USHORT length = string.length();
	if (length > MAX_UCHAR)
		length = MAX_UCHAR;

	*acl++ = (UCHAR)length;
	if (length)
	{
		const TEXT* p_1 = string.c_str();
		memcpy(acl, p_1, length);
		acl += length;
	}
	*acl++ = ACL_end;
	*acl++ = ACL_priv_list;
	*acl++ = priv_protect;
	*acl++ = priv_control;
	*acl++ = priv_delete;
	*acl++ = priv_write;
	*acl++ = priv_read;
	*acl++ = ACL_end;
	*acl++ = ACL_id_list;
	*acl++ = ACL_end;
	*acl++ = ACL_priv_list;
	*acl++ = priv_read;
	*acl++ = ACL_end;
	*acl++ = ACL_end;
	length = acl - buffer;

	add_security_to_sys_rel(tdbb, string, "RDB$ROLES", buffer, length);
	add_security_to_sys_rel(tdbb, string, "RDB$PAGES", buffer, length);
	// DFW writes here
	add_security_to_sys_rel(tdbb, string, "RDB$FORMATS", buffer, length);
}


USHORT INI_get_trig_flags(const TEXT* trig_name)
{
/*********************************************
 *
 *      I N I _ g e t _ t r i g _ f l a g s
 *
 *********************************************
 *
 * Functional description
 *      Return the trigger flags for a system trigger.
 *
 **************************************/
	for (const jrd_trg* trig = triggers; trig->trg_length > 0; trig++)
	{
		if (!strcmp(trig->trg_name, trig_name))
		{
			return (trig->trg_flags);
		}
	}

	return (0);
}


void INI_init(thread_db* tdbb)
{
/**************************************
 *
 *	I N I _ i n i t
 *
 **************************************
 *
 * Functional description
 *	Initialize in memory meta data.  Assume that all meta data
 *	fields exist in the database even if this is not the case.
 *	Do not fill in the format length or the address in each
 *	format descriptor.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* const dbb = tdbb->getDatabase();
	CHECK_DBB(dbb);

	const int* fld;
	for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
	{
		jrd_rel* relation = MET_relation(tdbb, relfld[RFLD_R_ID]);
		relation->rel_flags |= REL_system;
		relation->rel_flags |= MET_get_rel_flags_from_TYPE(relfld[RFLD_R_TYPE]);
		relation->rel_name = names[relfld[RFLD_R_NAME]];
		int n = 0;
		for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
		{
			n++;
		}

		/* Set a flag if their is a trigger on the relation.  Later we may
		   need to compile it. */

		for (const jrd_trg* trigger = triggers; trigger->trg_relation; trigger++)
		{
			if (relation->rel_name == names[trigger->trg_relation])
			{
				relation->rel_flags |= REL_sys_triggers;
				break;
			}
		}

		vec<jrd_fld*>* fields = vec<jrd_fld*>::newVector(*dbb->dbb_permanent, n);
		relation->rel_fields = fields;
		vec<jrd_fld*>::iterator itr = fields->begin();
		Format* format = Format::newFormat(*dbb->dbb_permanent, n);
		relation->rel_current_format = format;
		vec<Format*>* formats = vec<Format*>::newVector(*dbb->dbb_permanent, 1);
		relation->rel_formats = formats;
		(*formats)[0] = format;
		Format::fmt_desc_iterator desc = format->fmt_desc.begin();

		for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH, ++desc, ++itr)
		{
			const gfld* gfield = &gfields[fld[RFLD_F_ID]];
			desc->dsc_length = gfield->gfld_length;
			if (gfield->gfld_dtype == dtype_varying)
			{
				fb_assert(desc->dsc_length <= MAX_USHORT - sizeof(USHORT));
				desc->dsc_length += sizeof(USHORT);
			}
			desc->dsc_dtype = gfield->gfld_dtype;
			desc->dsc_sub_type = gfield->gfld_sub_type;
			if (desc->dsc_dtype == dtype_blob && desc->dsc_sub_type == isc_blob_text)
				desc->dsc_scale = CS_METADATA;	// blob charset

			jrd_fld* field = FB_NEW(*dbb->dbb_permanent) jrd_fld(*dbb->dbb_permanent);
			*itr = field;
			field->fld_name = names[fld[RFLD_F_NAME]];
		}
	}
}


void INI_init2(thread_db* tdbb)
{
/**************************************
 *
 *	I N I _ i n i t 2
 *
 **************************************
 *
 * Functional description
 *	Re-initialize in memory meta data.  Fill in
 *	format 0 based on the minor ODS version of
 *	the database when it was created.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* const dbb = tdbb->getDatabase();

	const USHORT major_version = dbb->dbb_ods_version;
	const USHORT minor_original = dbb->dbb_minor_original;

	if (ENCODE_ODS(major_version, minor_original) < ODS_9_0) {
		dbb->dbb_max_sys_rel = USER_REL_INIT_ID_ODS8 - 1;
	}
	else {
		dbb->dbb_max_sys_rel = USER_DEF_REL_INIT_ID - 1;
	}

	vec<jrd_rel*>* vector = dbb->dbb_relations;

	const int* fld;
	for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
	{
		if (relfld[RFLD_R_ODS] > ENCODE_ODS(major_version, minor_original))
		{
		/*****************************************************
        **
        ** free the space allocated for RDB$ROLES
        **
        ******************************************************/
			const USHORT id = relfld[RFLD_R_ID];
			jrd_rel* relation = (*vector)[id];
			delete relation->rel_current_format;
			delete relation->rel_formats;
			delete relation->rel_fields;
			delete relation;
			(*vector)[id] = NULL;
			fld = relfld + RFLD_RPT;
			while (fld[RFLD_F_NAME])
			{
				fld += RFLD_F_LENGTH;
			}
		}
		else
		{
			jrd_rel* relation = MET_relation(tdbb, relfld[RFLD_R_ID]);
			Format* format = relation->rel_current_format;

			int n = 0;
			for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH)
			{
				/* If the ODS is less than 10, then remove all fields named
				 * RDB$FIELD_PRECISION and field RDB$CHARACTER_LENGTH from
				 * relation RDB$FUNCTION_ARGUMENTS , as they were not present
				 * in < 10 ODS
				 */
				if (fld[RFLD_F_NAME] == nam_f_precision ||
					(fld[RFLD_F_NAME] == nam_char_length && relfld[RFLD_R_NAME] == nam_args))
				{
					if (major_version >= ODS_VERSION10)
						n = init2_helper(fld, n, relation);
				}
				else if (fld[RFLD_F_NAME] == nam_statistics && relfld[RFLD_R_NAME] == nam_i_segments)
				{
					if (major_version >= ODS_VERSION11)
						n = init2_helper(fld, n, relation);
				}
				else if ((fld[RFLD_F_NAME] == nam_description || fld[RFLD_F_NAME] == nam_sys_flag) &&
					relfld[RFLD_R_NAME] == nam_roles)
				{
					if (major_version >= ODS_VERSION11)
						n = init2_helper(fld, n, relation);
				}
				else if (fld[RFLD_F_NAME] == nam_description && relfld[RFLD_R_NAME] == nam_gens)
				{
					if (major_version >= ODS_VERSION11)
						n = init2_helper(fld, n, relation);
				}
				else if ((fld[RFLD_F_NAME] == nam_base_collation_name ||
						fld[RFLD_F_NAME] == nam_specific_attr) &&
					relfld[RFLD_R_NAME] == nam_collations)
				{
					if (major_version >= ODS_VERSION11)
						n = init2_helper(fld, n, relation);
				}
				else if (fld[RFLD_F_NAME] == nam_r_type && relfld[RFLD_R_NAME] == nam_relations)
				{
					if (ENCODE_ODS(major_version, minor_original) >= ODS_11_1)
						n = init2_helper(fld, n, relation);
				}
				else if (fld[RFLD_F_NAME] == nam_valid_blr && relfld[RFLD_R_NAME] == nam_trgs)
				{
					if (ENCODE_ODS(major_version, minor_original) >= ODS_11_1)
						n = init2_helper(fld, n, relation);
				}
				else if ((fld[RFLD_F_NAME] == nam_prc_type || fld[RFLD_F_NAME] == nam_valid_blr) &&
					relfld[RFLD_R_NAME] == nam_procedures)
				{
					if (ENCODE_ODS(major_version, minor_original) >= ODS_11_1)
						n = init2_helper(fld, n, relation);
				}
				else if (fld[RFLD_F_NAME] == nam_debug_info &&
					(relfld[RFLD_R_NAME] == nam_trgs || relfld[RFLD_R_NAME] == nam_procedures))
				{
					if (ENCODE_ODS(major_version, minor_original) >= ODS_11_1)
						n = init2_helper(fld, n, relation);
				}
				else if ((fld[RFLD_F_NAME] == nam_default || fld[RFLD_F_NAME] == nam_d_source ||
						fld[RFLD_F_NAME] == nam_collate_id || fld[RFLD_F_NAME] == nam_null_flag ||
						fld[RFLD_F_NAME] == nam_prm_mechanism) &&
					relfld[RFLD_R_NAME] == nam_proc_parameters)
				{
					if (ENCODE_ODS(major_version, minor_original) >= ODS_11_1)
						n = init2_helper(fld, n, relation);
				}
				else if ((fld[RFLD_F_NAME] == nam_f_name || fld[RFLD_F_NAME] == nam_r_name) &&
					relfld[RFLD_R_NAME] == nam_proc_parameters)
				{
					if (ENCODE_ODS(major_version, minor_original) >= ODS_11_2)
						n = init2_helper(fld, n, relation);
				}
				else
					n = init2_helper(fld, n, relation);
			}

			relation->rel_fields->resize(n);
			format->fmt_count = n; // We are using less than the allocated members.
			format->fmt_length = (USHORT)FLAG_BYTES(n);
			Format::fmt_desc_iterator desc = format->fmt_desc.begin();
			for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; fld += RFLD_F_LENGTH, ++desc)
			{
				if (n-- > 0)
				{
					format->fmt_length = (USHORT)MET_align(dbb, &(*desc), format->fmt_length);
					desc->dsc_address = (UCHAR*) (IPTR) format->fmt_length;

					// In ODS11 length of RDB$MESSAGE was enlarged from 80 to 1023 bytes
					if ((fld[RFLD_F_NAME] == nam_msg) && (major_version < ODS_VERSION11))
					{
						desc->dsc_length = 80 + sizeof(USHORT);
					}

					// In ODS prior to 11.2 all varchar columns were actually
					// two bytes shorter than defined in fields.h
					if (desc->dsc_dtype == dtype_varying &&
						ENCODE_ODS(major_version, minor_original) < ODS_11_2)
					{
						desc->dsc_length -= sizeof(USHORT);
					}

					// In ODS11.2 length of RDB$CONTEXT_NAME was enlarged from 31 to 255 bytes
					if ((fld[RFLD_F_NAME] == nam_context) &&
						ENCODE_ODS(major_version, minor_original) < ODS_11_2)
					{
						desc->dsc_length = 31;
					}

					format->fmt_length += desc->dsc_length;
				}
			}
		}
	}
}


static void add_index_set(Database*	dbb,
						  bool		update_ods,
						  USHORT	major_version,
						  USHORT	minor_version)
{
/**************************************
 *
 *	a d d _ i n d e x _ s e t
 *
 **************************************
 *
 * Functional description
 *	Add system indices.  If update_ods is true we are performing
 *	an ODS update, and only add the indices marked as newer than
 *	ODS (major_version,minor_version).
 *
 **************************************/
	Firebird::MetaName string;
	index_desc idx;

	thread_db* tdbb = JRD_get_thread_data();
	jrd_req* handle1 = NULL;
	jrd_req* handle2 = NULL;

	for (int n = 0; n < SYSTEM_INDEX_COUNT; n++)
	{
		const ini_idx_t* index = &indices[n];

		/* For minor ODS updates, only add indices newer than on-disk ODS */
		if (update_ods &&
			((index->ini_idx_version_flag <= ENCODE_ODS(major_version, minor_version)) ||
				(index->ini_idx_version_flag > ODS_CURRENT_VERSION) ||
			 	(((USHORT) DECODE_ODS_MAJOR(index->ini_idx_version_flag)) != major_version)))
		{
			/* The DECODE_ODS_MAJOR() is used (in this case) to instruct the server
			   to perform updates for minor ODS versions only within a major ODS */
			continue;
		}

		STORE(REQUEST_HANDLE handle1) X IN RDB$INDICES
			jrd_rel* relation = MET_relation(tdbb, index->ini_idx_relid);
			PAD(relation->rel_name.c_str(), X.RDB$RELATION_NAME);
			string.printf("RDB$INDEX_%d", index->ini_idx_index_id);
			PAD(string.c_str(), X.RDB$INDEX_NAME);
			X.RDB$UNIQUE_FLAG = index->ini_idx_flags & idx_unique;
			X.RDB$SEGMENT_COUNT = index->ini_idx_segment_count;
			if (index->ini_idx_flags & idx_descending) {
				X.RDB$INDEX_TYPE.NULL = FALSE;
				X.RDB$INDEX_TYPE = 1;
			}
			else {
				X.RDB$INDEX_TYPE.NULL = TRUE;
			}
			X.RDB$SYSTEM_FLAG = 1;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$INDEX_INACTIVE = 0;

			/* Store each segment for the index */
			index_desc::idx_repeat* tail = idx.idx_rpt;
			for (USHORT position = 0; position < index->ini_idx_segment_count; position++, tail++)
			{
				const ini_idx_t::ini_idx_segment_t* segment = &index->ini_idx_segment[position];
				STORE(REQUEST_HANDLE handle2) Y IN RDB$INDEX_SEGMENTS
					jrd_fld* field = (*relation->rel_fields)[segment->ini_idx_rfld_id];

					Y.RDB$FIELD_POSITION = position;
					PAD(X.RDB$INDEX_NAME, Y.RDB$INDEX_NAME);
					PAD(field->fld_name.c_str(), Y.RDB$FIELD_NAME);
					tail->idx_field = segment->ini_idx_rfld_id;
					tail->idx_itype = segment->ini_idx_type;
				    tail->idx_selectivity = 0;
				END_STORE;
			}
			idx.idx_count = index->ini_idx_segment_count;
			idx.idx_flags = index->ini_idx_flags;
			SelectivityList selectivity(*tdbb->getDefaultPool());
			IDX_create_index(tdbb, relation, &idx, string.c_str(), NULL, dbb->dbb_sys_trans, selectivity);
			X.RDB$INDEX_ID = idx.idx_id + 1;
		END_STORE;

	}
	if (handle1) {
		CMP_release(tdbb, handle1);
	}
	if (handle2) {
		CMP_release(tdbb, handle2);
	}
}


static void add_relation_fields(thread_db* tdbb, USHORT minor_version)
{
/**************************************
 *
 *	a d d _ r e l a t i o n _ f i e l d s
 *
 **************************************
 *
 * Functional description
 *	Add any local fields which have a non-zero
 *	update number in the relfields table.  That
 *	is, those that have been added since the last
 *	ODS change.
 *
 **************************************/
	SET_TDBB(tdbb);
	//Database* dbb = tdbb->getDatabase();

/* add desired fields to system relations, forcing a new format version */

	jrd_req* s_handle = NULL;
	jrd_req* m_handle = NULL;
	const int* fld;
	for (const int* relfld = relfields; relfld[RFLD_R_NAME]; relfld = fld + 1)
	{
		int n = 0;
		for (fld = relfld + RFLD_RPT; fld[RFLD_F_NAME]; n++, fld += RFLD_F_LENGTH)
		{
			if (minor_version < fld[RFLD_F_MINOR] || minor_version < fld[RFLD_F_UPD_MINOR])
			{
				if (minor_version < fld[RFLD_F_MINOR])
				{
					store_relation_field(tdbb, fld, relfld, n, &s_handle, false);
				}
				else
				{
					modify_relation_field(tdbb, fld, relfld, &m_handle);
				}
				dsc desc;
				desc.dsc_dtype = dtype_text;
				INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE);
				desc.dsc_address = (UCHAR*) names[relfld[RFLD_R_NAME]];
				desc.dsc_length = strlen((char*)desc.dsc_address);
				DFW_post_system_work(tdbb, dfw_update_format, &desc, 0);
			}
		}
	}

	if (s_handle) {
		CMP_release(tdbb, s_handle);
	}
	if (m_handle) {
		CMP_release(tdbb, m_handle);
	}

	DFW_perform_system_work(tdbb);
}


// The caller used an UCHAR* to store the acl, it was converted to TEXT* to
// be passed to this function, only to be converted to UCHAR* to be passed
// to BLB_put_segment. Therefore, "acl" was changed to UCHAR* as param.
static void add_security_to_sys_rel(thread_db*	tdbb,
									const Firebird::MetaName&	user_name,
									const TEXT*	rel_name,
									const UCHAR*	acl,
									const SSHORT	acl_length)
{
/**************************************
 *
 *      a d d _ s e c u r i t y _ t o _ s y s _ r e l
 *
 **************************************
 *
 * Functional description
 *
 *      Add security to system relations. Only the owner of the
 *      database has SELECT/INSERT/UPDATE/DELETE privileges on
 *      any system relations. Any other users only has SELECT
 *      privilege.
 *
 **************************************/
	Firebird::MetaName security_class, default_class;

	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

    bid blob_id_1;
	blb* blob = BLB_create(tdbb, dbb->dbb_sys_trans, &blob_id_1);
	BLB_put_segment(tdbb, blob, acl, acl_length);
	BLB_close(tdbb, blob);

    bid blob_id_2;
	blob = BLB_create(tdbb, dbb->dbb_sys_trans, &blob_id_2);
	BLB_put_segment(tdbb, blob, acl, acl_length);
	BLB_close(tdbb, blob);

	security_class.printf("%s%" SQUADFORMAT, SQL_SECCLASS_PREFIX,
						  DPM_gen_id(tdbb, MET_lookup_generator(tdbb, SQL_SECCLASS_GENERATOR), false, 1));

	default_class.printf("%s%" SQUADFORMAT, DEFAULT_CLASS,
						 DPM_gen_id(tdbb, MET_lookup_generator(tdbb, DEFAULT_CLASS), false, 1));

	jrd_req* handle1 = NULL;

	STORE(REQUEST_HANDLE handle1)
		CLS IN RDB$SECURITY_CLASSES
		jrd_vtof(security_class.c_str(), CLS.RDB$SECURITY_CLASS, sizeof(CLS.RDB$SECURITY_CLASS));
		CLS.RDB$ACL = blob_id_1;
	END_STORE;

	CMP_release(tdbb, handle1);

	handle1 = NULL;

	STORE(REQUEST_HANDLE handle1)
		CLS IN RDB$SECURITY_CLASSES
		jrd_vtof(default_class.c_str(), CLS.RDB$SECURITY_CLASS, sizeof(CLS.RDB$SECURITY_CLASS));
		CLS.RDB$ACL = blob_id_2;
	END_STORE;

	CMP_release(tdbb, handle1);

	handle1 = NULL;

	FOR(REQUEST_HANDLE handle1) REL IN RDB$RELATIONS
		WITH REL.RDB$RELATION_NAME EQ rel_name
		MODIFY REL USING
			REL.RDB$SECURITY_CLASS.NULL = FALSE;
			jrd_vtof(security_class.c_str(), REL.RDB$SECURITY_CLASS, sizeof(REL.RDB$SECURITY_CLASS));

			REL.RDB$DEFAULT_CLASS.NULL = FALSE;
			jrd_vtof(default_class.c_str(), REL.RDB$DEFAULT_CLASS, sizeof(REL.RDB$DEFAULT_CLASS));
		END_MODIFY;

	END_FOR;

	CMP_release(tdbb, handle1);

	handle1 = NULL;

	for (int cnt = 0; cnt < 6; cnt++)
	{
		STORE(REQUEST_HANDLE handle1) PRIV IN RDB$USER_PRIVILEGES
			switch (cnt)
			{
			case 0:
				strcpy(PRIV.RDB$USER, user_name.c_str());
				PRIV.RDB$PRIVILEGE[0] = 'S';
				PRIV.RDB$GRANT_OPTION = 1;
				break;
			case 1:
				strcpy(PRIV.RDB$USER, user_name.c_str());
				PRIV.RDB$PRIVILEGE[0] = 'I';
				PRIV.RDB$GRANT_OPTION = 1;
				break;
			case 2:
				strcpy(PRIV.RDB$USER, user_name.c_str());
				PRIV.RDB$PRIVILEGE[0] = 'U';
				PRIV.RDB$GRANT_OPTION = 1;
				break;
			case 3:
				strcpy(PRIV.RDB$USER, user_name.c_str());
				PRIV.RDB$PRIVILEGE[0] = 'D';
				PRIV.RDB$GRANT_OPTION = 1;
				break;
			case 4:
				strcpy(PRIV.RDB$USER, user_name.c_str());
				PRIV.RDB$PRIVILEGE[0] = 'R';
				PRIV.RDB$GRANT_OPTION = 1;
				break;
			default:
				strcpy(PRIV.RDB$USER, "PUBLIC");
				PRIV.RDB$PRIVILEGE[0] = 'S';
				PRIV.RDB$GRANT_OPTION = 0;
				break;
			}
			strcpy(PRIV.RDB$GRANTOR, user_name.c_str());
			PRIV.RDB$PRIVILEGE[1] = 0;
			strcpy(PRIV.RDB$RELATION_NAME, rel_name);
			PRIV.RDB$FIELD_NAME.NULL = TRUE;
			PRIV.RDB$USER_TYPE = obj_user;
			PRIV.RDB$OBJECT_TYPE = obj_relation;
		END_STORE;
	}

	CMP_release(tdbb, handle1);
}


static int init2_helper(const int* fld, int n, jrd_rel* relation)
{
	if (!fld[RFLD_F_MINOR])
	{
		++n;
		if (fld[RFLD_F_UPD_MINOR])
			relation->rel_flags |= REL_force_scan;
	}
	else
	{
		relation->rel_flags |= REL_force_scan;
	}
	return n;
}


static void modify_relation_field(thread_db*	tdbb,
								  const int*	fld,
								  const int*	relfld,
								  jrd_req**	handle)
{
/**************************************
 *
 *	m o d i f y _ r e l a t i o n _ f i e l d
 *
 **************************************
 *
 * Functional description
 *	Modify a local field according to the
 *	passed information.  Note that the field id and
 *	field position do not change.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	FOR(REQUEST_HANDLE * handle) X IN RDB$RELATION_FIELDS WITH
		X.RDB$RELATION_NAME EQ names[relfld[RFLD_R_NAME]] AND
			X.RDB$FIELD_NAME EQ names[fld[RFLD_F_NAME]]
		MODIFY X USING
			const gfld* gfield = &gfields[fld[RFLD_F_UPD_ID]];
			PAD(names[gfield->gfld_name], X.RDB$FIELD_SOURCE);
			X.RDB$UPDATE_FLAG = fld[RFLD_F_UPDATE];
		END_MODIFY;
	END_FOR;
}


static void store_generator(thread_db* tdbb, const gen* generator, jrd_req** handle)
{
/**************************************
 *
 *	s t o r e _ g e n e r a t o r
 *
 **************************************
 *
 * Functional description
 *	Store the passed generator according to
 *	the information in the generator block.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	STORE(REQUEST_HANDLE * handle) X IN RDB$GENERATORS
		PAD(generator->gen_name, X.RDB$GENERATOR_NAME);
		X.RDB$GENERATOR_ID = generator->gen_id;
		X.RDB$SYSTEM_FLAG = RDB_system;
		X.RDB$SYSTEM_FLAG.NULL = FALSE;
		if (generator->gen_description)
		{
			blb* blob = BLB_create(tdbb, dbb->dbb_sys_trans, &X.RDB$DESCRIPTION);
			BLB_put_segment(tdbb,
							blob,
							reinterpret_cast<const UCHAR*>(generator->gen_description),
							strlen(generator->gen_description));
			BLB_close(tdbb, blob);
			X.RDB$DESCRIPTION.NULL = FALSE;
		}
		else
		{
			X.RDB$DESCRIPTION.NULL = TRUE;
		}

	END_STORE;
}


static void store_global_field(thread_db* tdbb, const gfld* gfield, jrd_req** handle)
{
/**************************************
 *
 *	s t o r e _ g l o b a l _ f i e l d
 *
 **************************************
 *
 * Functional description
 *	Store a global field according to the
 *	passed information.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	STORE(REQUEST_HANDLE * handle) X IN RDB$FIELDS
		PAD(names[(USHORT)gfield->gfld_name], X.RDB$FIELD_NAME);
		X.RDB$FIELD_LENGTH = gfield->gfld_length;
		X.RDB$FIELD_SCALE = 0;
		X.RDB$SYSTEM_FLAG = RDB_system;
		X.RDB$SYSTEM_FLAG.NULL = FALSE;
		X.RDB$FIELD_SUB_TYPE.NULL = TRUE;
		X.RDB$CHARACTER_SET_ID.NULL = TRUE;
		X.RDB$COLLATION_ID.NULL = TRUE;
		X.RDB$SEGMENT_LENGTH.NULL = TRUE;
		if (gfield->gfld_dflt_blr)
		{
			blb* blob = BLB_create(tdbb, dbb->dbb_sys_trans, &X.RDB$DEFAULT_VALUE);
			BLB_put_segment(tdbb,
							blob,
							gfield->gfld_dflt_blr,
							gfield->gfld_dflt_len);
			BLB_close(tdbb, blob);
			X.RDB$DEFAULT_VALUE.NULL = FALSE;
		}
		else
		{
			X.RDB$DEFAULT_VALUE.NULL = TRUE;
		}
		switch (gfield->gfld_dtype)
		{
		case dtype_timestamp:
			X.RDB$FIELD_TYPE = (int) blr_timestamp;
			break;

		case dtype_sql_time:
			X.RDB$FIELD_TYPE = (int) blr_sql_time;
			break;

		case dtype_sql_date:
			X.RDB$FIELD_TYPE = (int) blr_sql_date;
			break;

		case dtype_short:
		case dtype_long:
		case dtype_int64:
			if (gfield->gfld_dtype == dtype_short)
				X.RDB$FIELD_TYPE = (int) blr_short;
			else if (gfield->gfld_dtype == dtype_long)
				X.RDB$FIELD_TYPE = (int) blr_long;
			else
			{
				// Workaround to allow fld_counter domain
				// in dialect 1 databases
				if (dbb->dbb_flags & DBB_DB_SQL_dialect_3)
					X.RDB$FIELD_TYPE = (int) blr_int64;
				else
					X.RDB$FIELD_TYPE = (int) blr_double;
			}
			if ((gfield->gfld_sub_type == dsc_num_type_numeric) ||
				(gfield->gfld_sub_type == dsc_num_type_decimal))
			{
				X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
				X.RDB$FIELD_SUB_TYPE = gfield->gfld_sub_type;
			}
			break;

		case dtype_double:
			X.RDB$FIELD_TYPE = (int) blr_double;
			break;

		case dtype_text:
		case dtype_varying:
			if (gfield->gfld_dtype == dtype_text)
			{
				X.RDB$FIELD_TYPE = (int) blr_text;
			}
			else
			{
				X.RDB$FIELD_TYPE = (int) blr_varying;
			}
			switch (gfield->gfld_sub_type)
			{
			case dsc_text_type_metadata:
				X.RDB$CHARACTER_SET_ID.NULL = FALSE;
				X.RDB$CHARACTER_SET_ID = CS_METADATA;
				X.RDB$COLLATION_ID.NULL = FALSE;
				X.RDB$COLLATION_ID = COLLATE_NONE;
				X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
				X.RDB$FIELD_SUB_TYPE = gfield->gfld_sub_type;
				break;
			case dsc_text_type_ascii:
				X.RDB$CHARACTER_SET_ID.NULL = FALSE;
				X.RDB$CHARACTER_SET_ID = CS_ASCII;
				X.RDB$COLLATION_ID.NULL = FALSE;
				X.RDB$COLLATION_ID = COLLATE_NONE;
				X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
				X.RDB$FIELD_SUB_TYPE = gfield->gfld_sub_type;
				break;
			case dsc_text_type_fixed:
				X.RDB$CHARACTER_SET_ID.NULL = FALSE;
				X.RDB$CHARACTER_SET_ID = CS_BINARY;
				X.RDB$COLLATION_ID.NULL = FALSE;
				X.RDB$COLLATION_ID = COLLATE_NONE;
				X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
				X.RDB$FIELD_SUB_TYPE = gfield->gfld_sub_type;
				break;
			default:
				X.RDB$CHARACTER_SET_ID.NULL = FALSE;
				X.RDB$CHARACTER_SET_ID = CS_NONE;
				X.RDB$COLLATION_ID.NULL = FALSE;
				X.RDB$COLLATION_ID = COLLATE_NONE;
				break;
			}
			break;

		case dtype_blob:
			X.RDB$FIELD_TYPE = (int) blr_blob;
			X.RDB$FIELD_SUB_TYPE.NULL = FALSE;
			X.RDB$SEGMENT_LENGTH.NULL = FALSE;
			X.RDB$FIELD_SUB_TYPE = gfield->gfld_sub_type;
			X.RDB$SEGMENT_LENGTH = 80;
			if (gfield->gfld_sub_type == isc_blob_text)
			{
				X.RDB$CHARACTER_SET_ID.NULL = FALSE;
				X.RDB$CHARACTER_SET_ID = CS_METADATA;
			}
			break;

		default:
			fb_assert(FALSE);
			break;
		}

		/* For the next ODS change, when we'll have not NULL sys fields
		X.RDB$NULL_FLAG.NULL = FALSE;
		X.RDB$NULL_FLAG = gfield->gfld_null_flag;
		*/

	END_STORE;
}


static void store_intlnames(thread_db* tdbb, Database* dbb)
{
/**************************************
 *
 *	s t o r e _ i n t l n a m e s
 *
 **************************************
 *
 * Functional description
 *	Store symbolic names & information for international
 *	character sets & collations.
 *
 **************************************/
	SET_TDBB(tdbb);

	jrd_req* handle = NULL;

	for (const IntlManager::CharSetDefinition* charSet = IntlManager::defaultCharSets;
		 charSet->name; ++charSet)
	{
		STORE(REQUEST_HANDLE handle) X IN RDB$CHARACTER_SETS USING
			PAD(charSet->name, X.RDB$CHARACTER_SET_NAME);
			PAD(charSet->name, X.RDB$DEFAULT_COLLATE_NAME);
			X.RDB$CHARACTER_SET_ID = charSet->id;
			X.RDB$BYTES_PER_CHARACTER = charSet->maxBytes;
			X.RDB$SYSTEM_FLAG = RDB_system;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
		END_STORE;
	}

	CMP_release(tdbb, handle);
	handle = NULL;

	for (const IntlManager::CollationDefinition* collation = IntlManager::defaultCollations;
		collation->name; ++collation)
	{
		STORE(REQUEST_HANDLE handle) X IN RDB$COLLATIONS USING
			PAD(collation->name, X.RDB$COLLATION_NAME);

			if (collation->baseName)
			{
				X.RDB$BASE_COLLATION_NAME.NULL = false;
				PAD(collation->baseName, X.RDB$BASE_COLLATION_NAME);
			}
			else
				X.RDB$BASE_COLLATION_NAME.NULL = true;

			X.RDB$CHARACTER_SET_ID = collation->charSetId;
			X.RDB$COLLATION_ID = collation->collationId;
			X.RDB$SYSTEM_FLAG = RDB_system;
			X.RDB$SYSTEM_FLAG.NULL = FALSE;
			X.RDB$COLLATION_ATTRIBUTES = collation->attributes;

			if (collation->specificAttributes)
			{
				blb* blob = BLB_create(tdbb, dbb->dbb_sys_trans, &X.RDB$SPECIFIC_ATTRIBUTES);
				BLB_put_segment(tdbb,
								blob,
								reinterpret_cast<const UCHAR*>(collation->specificAttributes),
								strlen(collation->specificAttributes));
				BLB_close(tdbb, blob);
				X.RDB$SPECIFIC_ATTRIBUTES.NULL = FALSE;
			}
			else
				X.RDB$SPECIFIC_ATTRIBUTES.NULL = TRUE;

		END_STORE;
	}

	CMP_release(tdbb, handle);
	handle = NULL;
}


static void store_function_argument(thread_db* tdbb, Database* dbb, jrd_req*& handle,
	const char* function_name, SLONG argument_position, SLONG mechanism, SLONG type, SLONG scale,
	SLONG length, SLONG sub_type, SLONG charset, SLONG precision, SLONG char_length)
{
	STORE(REQUEST_HANDLE handle) FA IN RDB$FUNCTION_ARGUMENTS
		PAD(function_name, FA.RDB$FUNCTION_NAME);
		FA.RDB$FUNCTION_NAME.NULL          = FALSE;
		FA.RDB$ARGUMENT_POSITION           = argument_position;
		FA.RDB$MECHANISM                   = mechanism;
		FA.RDB$FIELD_TYPE                  = type;
		FA.RDB$FIELD_SCALE                 = scale;
		FA.RDB$FIELD_LENGTH                = length;
		FA.RDB$FIELD_SUB_TYPE              = sub_type;
		FA.RDB$CHARACTER_SET_ID            = charset;
		FA.RDB$FIELD_PRECISION             = precision;
		FA.RDB$CHARACTER_LENGTH            = char_length;
	END_STORE;
}


static void store_function(thread_db* tdbb, Database* dbb, jrd_req*& handle,
	const char* function_name, const char* module, const char* entrypoint, SLONG argument)
{
	STORE(REQUEST_HANDLE handle) F IN RDB$FUNCTIONS
		PAD(function_name, F.RDB$FUNCTION_NAME);
		F.RDB$FUNCTION_NAME.NULL          = FALSE;
		//F.RDB$FUNCTION_TYPE               <null>
		//F.RDB$QUERY_NAME                  <null>
		//F.RDB$DESCRIPTION                 <null>
		strcpy(F.RDB$MODULE_NAME, module);
		F.RDB$MODULE_NAME.NULL            = FALSE;
		PAD(entrypoint, F.RDB$ENTRYPOINT);
		F.RDB$ENTRYPOINT.NULL             = FALSE;
		F.RDB$RETURN_ARGUMENT             = argument;
		F.RDB$SYSTEM_FLAG                 = RDB_system;
		F.RDB$SYSTEM_FLAG.NULL            = FALSE;
	END_STORE;
}


static void store_functions(thread_db* tdbb, Database* dbb)
{
/**************************************
 *
 *	s t o r e _ f u n c t i o n s
 *
 **************************************
 *
 * Functional description
 *	Store built-in functions and their arguments
 *
 **************************************/
	SET_TDBB(tdbb);

	jrd_req *fun_handle = NULL, *arg_handle = NULL;
	const char *function_name;
	SLONG argument_position;

#define FUNCTION(ROUTINE, FUNCTION_NAME, MODULE_NAME, ENTRYPOINT, RET_ARG) \
	function_name = FUNCTION_NAME; \
	argument_position = RET_ARG ? 1 : 0; \
	store_function(tdbb, dbb, fun_handle, FUNCTION_NAME, MODULE_NAME, ENTRYPOINT, RET_ARG);
#define END_FUNCTION
#define FUNCTION_ARGUMENT(MECHANISM, TYPE, SCALE, LENGTH, SUB_TYPE, CHARSET, PRECISION, CHAR_LENGTH) \
	store_function_argument(tdbb, dbb, arg_handle, function_name, argument_position, \
		MECHANISM, TYPE, SCALE, LENGTH, SUB_TYPE, CHARSET, PRECISION, CHAR_LENGTH);	\
	argument_position++;

#include "../jrd/functions.h"

	CMP_release(tdbb, fun_handle);
	CMP_release(tdbb, arg_handle);
}


static void store_message(thread_db* tdbb, const trigger_msg* message, jrd_req** handle)
{
/**************************************
 *
 *	s t o r e _ m e s s a g e
 *
 **************************************
 *
 * Functional description
 *	Store system trigger messages.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

/* store the trigger */

	STORE(REQUEST_HANDLE * handle) X IN RDB$TRIGGER_MESSAGES
		PAD(message->trigmsg_name, X.RDB$TRIGGER_NAME);
		X.RDB$MESSAGE_NUMBER = message->trigmsg_number;
		PAD(message->trigmsg_text, X.RDB$MESSAGE);
	END_STORE;
}


static void store_relation_field(thread_db*		tdbb,
								 const int*		fld,
								 const int*		relfld,
								 int			field_id,
								 jrd_req**		handle,
								 bool			fmt0_flag)
{
/**************************************
 *
 *	s t o r e _ r e l a t i o n _ f i e l d
 *
 **************************************
 *
 * Functional description
 *	Store a local field according to the
 *	passed information.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

	STORE(REQUEST_HANDLE * handle) X IN RDB$RELATION_FIELDS
		const gfld* gfield = (fld[RFLD_F_UPD_MINOR] && !fmt0_flag) ?
			&gfields[fld[RFLD_F_UPD_ID]] : &gfields[fld[RFLD_F_ID]];
		PAD(names[relfld[RFLD_R_NAME]], X.RDB$RELATION_NAME);
		PAD(names[fld[RFLD_F_NAME]], X.RDB$FIELD_NAME);
		PAD(names[gfield->gfld_name], X.RDB$FIELD_SOURCE);
		X.RDB$FIELD_POSITION = field_id;
		X.RDB$FIELD_ID = field_id;
		X.RDB$SYSTEM_FLAG = RDB_system;
		X.RDB$SYSTEM_FLAG.NULL = FALSE;
		X.RDB$UPDATE_FLAG = fld[RFLD_F_UPDATE];
	END_STORE;
}


static void store_trigger(thread_db* tdbb, const jrd_trg* trigger, jrd_req** handle)
{
/**************************************
 *
 *	s t o r e _ t r i g g e r
 *
 **************************************
 *
 * Functional description
 *	Store the trigger according to the
 *	information in the trigger block.
 *
 **************************************/
	SET_TDBB(tdbb);
	Database* dbb = tdbb->getDatabase();

/* indicate that the relation format needs revising */
	dsc desc;
	desc.dsc_dtype = dtype_text;
	INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE);
	desc.dsc_address = (UCHAR*) names[trigger->trg_relation];
	desc.dsc_length = strlen((char*)desc.dsc_address);
	DFW_post_system_work(tdbb, dfw_update_format, &desc, 0);

/* store the trigger */

	STORE(REQUEST_HANDLE * handle) X IN RDB$TRIGGERS
		PAD(trigger->trg_name, X.RDB$TRIGGER_NAME);
		PAD(names[trigger->trg_relation], X.RDB$RELATION_NAME);
		X.RDB$TRIGGER_SEQUENCE = 0;
		X.RDB$SYSTEM_FLAG = RDB_system;
		X.RDB$SYSTEM_FLAG.NULL = FALSE;
		X.RDB$TRIGGER_TYPE = trigger->trg_type;
		X.RDB$FLAGS = trigger->trg_flags;
		blb* blob = BLB_create(tdbb, dbb->dbb_sys_trans, &X.RDB$TRIGGER_BLR);
		BLB_put_segment(tdbb, blob, trigger->trg_blr, trigger->trg_length);
		BLB_close(tdbb, blob);
	END_STORE;
}
