/*
 *	PROGRAM:	JRD Data Definition Utility
 *	MODULE:		dyn_util.epp
 *	DESCRIPTION:	Dynamic data definition - utility functions
 *
 * 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): ______________________________________.
 *
 * 2002-02-24 Sean Leyne - Code Cleanup of old Win 3.1 port (WINDOWS_ONLY)
 *
 */

#include "firebird.h"
#include "dyn_consts.h"
#include <stdio.h>
#include <string.h>

#include "../jrd/jrd.h"
#include "../jrd/tra.h"
#include "../jrd/scl.h"
#include "../jrd/drq.h"
#include "../jrd/flags.h"
#include "../jrd/lls.h"
#include "../jrd/met.h"
#include "../jrd/btr.h"
#include "../jrd/intl.h"
#include "../jrd/dyn.h"
#include "../jrd/ods.h"
#include "../jrd/blb_proto.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/dyn_ut_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/exe_proto.h"
#include "../yvalve/gds_proto.h"
#include "../jrd/inf_proto.h"
#include "../jrd/intl_proto.h"
#include "../common/isc_f_proto.h"
#include "../jrd/vio_proto.h"
#include "../common/utils_proto.h"

using MsgFormat::SafeArg;

using namespace Firebird;
using namespace Jrd;

DATABASE DB = STATIC "ODS.RDB";

static const UCHAR gen_id_blr1[] =
{
	blr_version5,
	blr_begin,
	blr_message, 0, 1, 0,
	blr_int64, 0,
	blr_begin,
	blr_send, 0,
	blr_begin,
	blr_assignment,
	blr_gen_id
};

static const UCHAR gen_id_blr2[] =
{
	blr_literal, blr_long, 0, 1, 0, 0, 0,
		blr_parameter, 0, 0, 0, blr_end, blr_end, blr_end, blr_eoc
};

void DYN_UTIL_check_unique_name(thread_db* tdbb, jrd_tra* transaction,
	const MetaName& object_name, int object_type)
{
/**************************************
 *
 *	D Y N _ U T I L _ c h e c k _ u n i q u e _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Check if an object already exists.
 *	If yes then return error.
 *
 **************************************/
	SET_TDBB(tdbb);

	USHORT error_code = 0;
	AutoCacheRequest request;

	switch (object_type)
	{
		case obj_relation:
		case obj_procedure:
			request.reset(tdbb, drq_l_rel_name, DYN_REQUESTS);

			FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
				EREL IN RDB$RELATIONS WITH EREL.RDB$RELATION_NAME EQ object_name.c_str()
			{
				error_code = 132;
			}
			END_FOR

			if (!error_code)
			{
				request.reset(tdbb, drq_l_prc_name, DYN_REQUESTS);

				FOR (REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
					EPRC IN RDB$PROCEDURES
					WITH EPRC.RDB$PROCEDURE_NAME EQ object_name.c_str() AND
						 EPRC.RDB$PACKAGE_NAME MISSING
				{
					error_code = 135;
				}
				END_FOR
			}
			break;

		case obj_index:
			request.reset(tdbb, drq_l_idx_name, DYN_REQUESTS);

			FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
				EIDX IN RDB$INDICES WITH EIDX.RDB$INDEX_NAME EQ object_name.c_str()
			{
				error_code = 251;
			}
			END_FOR

			break;

		case obj_exception:
			request.reset(tdbb, drq_l_xcp_name, DYN_REQUESTS);

			FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
				EXCP IN RDB$EXCEPTIONS WITH EXCP.RDB$EXCEPTION_NAME EQ object_name.c_str()
			{
				error_code = 253;
			}
			END_FOR

			break;

		case obj_generator:
			request.reset(tdbb, drq_l_gen_name, DYN_REQUESTS);

			FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
				EGEN IN RDB$GENERATORS WITH EGEN.RDB$GENERATOR_NAME EQ object_name.c_str()
			{
				error_code = 254;
			}
			END_FOR

			break;

		case obj_udf:
			request.reset(tdbb, drq_l_fun_name, DYN_REQUESTS);

			FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
				EFUN IN RDB$FUNCTIONS
				WITH EFUN.RDB$FUNCTION_NAME EQ object_name.c_str() AND
					 EFUN.RDB$PACKAGE_NAME MISSING
			{
				error_code = 268;
			}
			END_FOR

			break;

		default:
			fb_assert(false);
	}

	if (error_code)
		status_exception::raise(Arg::PrivateDyn(error_code) << object_name.c_str());
}


SINT64 DYN_UTIL_gen_unique_id(thread_db* tdbb, SSHORT id, const char* generator_name)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n _ u n i q u e _ i d
 *
 **************************************
 *
 * Functional description
 *	Generate a unique id using a generator.
 *
 **************************************/
	SET_TDBB(tdbb);
	Jrd::Attachment* attachment = tdbb->getAttachment();

	AutoCacheRequest request(tdbb, id, DYN_REQUESTS);
	SINT64 value = 0;

	if (!request)
	{
		const FB_SIZE_T name_length = fb_strlen(generator_name);
		fb_assert(name_length < MAX_SQL_IDENTIFIER_SIZE);
		const FB_SIZE_T blr_size = static_cast<FB_SIZE_T>(
			sizeof(gen_id_blr1) + sizeof(gen_id_blr2)) + 1 + name_length;

		Firebird::UCharBuffer blr;
		UCHAR* p = blr.getBuffer(blr_size);

		memcpy(p, gen_id_blr1, sizeof(gen_id_blr1));
		p += sizeof(gen_id_blr1);
		*p++ = name_length;
		memcpy(p, generator_name, name_length);
		p += name_length;
		memcpy(p, gen_id_blr2, sizeof(gen_id_blr2));
		p += sizeof(gen_id_blr2);
		fb_assert(size_t(p - blr.begin()) == blr_size);

		request.compile(tdbb, blr.begin(), (ULONG) blr.getCount());
	}

	EXE_start(tdbb, request, attachment->getSysTransaction());
	EXE_receive(tdbb, request, 0, sizeof(value), (UCHAR*) &value);

	return value;
}


void DYN_UTIL_generate_constraint_name(thread_db* tdbb, MetaName& buffer)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ c o n s t r a i n t _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Generate a name unique to RDB$RELATION_CONSTRAINTS.
 *
 **************************************/
	SET_TDBB(tdbb);
	Jrd::Attachment* attachment = tdbb->getAttachment();

	bool found = false;

	do
	{
		buffer.printf("INTEG_%" SQUADFORMAT,
				DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_con, "RDB$CONSTRAINT_NAME"));

		AutoCacheRequest request(tdbb, drq_f_nxt_con, DYN_REQUESTS);

		found = false;
		FOR(REQUEST_HANDLE request)
			FIRST 1 X IN RDB$RELATION_CONSTRAINTS
			WITH X.RDB$CONSTRAINT_NAME EQ buffer.c_str()
		{
			found = true;
		}
		END_FOR
	} while (found);
}


void DYN_UTIL_generate_field_name(thread_db* tdbb, TEXT* buffer)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ f i e l d _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Stub to make it work with char* too.
 *
 **************************************/
	MetaName temp;
	DYN_UTIL_generate_field_name(tdbb, temp);
	strcpy(buffer, temp.c_str());
}


void DYN_UTIL_generate_field_name(thread_db* tdbb, MetaName& buffer)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ f i e l d _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Generate a name unique to RDB$FIELDS.
 *
 **************************************/
	SET_TDBB(tdbb);
	Jrd::Attachment* attachment = tdbb->getAttachment();
	bool found = false;

	do
	{
		buffer.printf("RDB$%" SQUADFORMAT,
				DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_fld, "RDB$FIELD_NAME"));

		AutoCacheRequest request(tdbb, drq_f_nxt_fld, DYN_REQUESTS);

		found = false;
		FOR(REQUEST_HANDLE request)
			FIRST 1 X IN RDB$FIELDS
			WITH X.RDB$FIELD_NAME EQ buffer.c_str()
		{
			found = true;
		}
		END_FOR
	} while (found);
}


void DYN_UTIL_generate_field_position(thread_db* tdbb, const MetaName& relation_name,
	SLONG* field_pos)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ f i e l d _ p o s i t i o n
 *
 **************************************
 *
 * Functional description
 *	Generate a field position if not specified
 *
 **************************************/
	SLONG field_position = -1;

	SET_TDBB(tdbb);
	Jrd::Attachment* attachment = tdbb->getAttachment();

	AutoCacheRequest request(tdbb, drq_l_fld_pos, DYN_REQUESTS);

	FOR(REQUEST_HANDLE request)
		X IN RDB$RELATION_FIELDS
		WITH X.RDB$RELATION_NAME EQ relation_name.c_str()
	{
		if (X.RDB$FIELD_POSITION.NULL)
			continue;

		field_position = MAX(X.RDB$FIELD_POSITION, field_position);
	}
	END_FOR

	*field_pos = field_position;
}


void DYN_UTIL_generate_index_name(thread_db* tdbb, jrd_tra* /*transaction*/,
								  MetaName& buffer, UCHAR verb)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ i n d e x _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Generate a name unique to RDB$INDICES.
 *
 **************************************/
	SET_TDBB(tdbb);
	Jrd::Attachment* attachment = tdbb->getAttachment();
	bool found = false;

	do
	{
		const SCHAR* format;
		switch (verb)
		{
			case isc_dyn_def_primary_key:
				format = "RDB$PRIMARY%" SQUADFORMAT;
				break;
			case isc_dyn_def_foreign_key:
				format = "RDB$FOREIGN%" SQUADFORMAT;
				break;
			default:
				format = "RDB$%" SQUADFORMAT;
		}

		buffer.printf(format,
			DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_idx, "RDB$INDEX_NAME"));

		AutoCacheRequest request(tdbb, drq_f_nxt_idx, DYN_REQUESTS);

		found = false;
		FOR(REQUEST_HANDLE request)
			FIRST 1 X IN RDB$INDICES
			WITH X.RDB$INDEX_NAME EQ buffer.c_str()
		{
			found = true;
		}
		END_FOR
	} while (found);
}


// Generate a name unique to RDB$GENERATORS.
void DYN_UTIL_generate_generator_name(thread_db* tdbb, MetaName& buffer)
{
	SET_TDBB(tdbb);
	Jrd::Attachment* attachment = tdbb->getAttachment();

	AutoCacheRequest request(tdbb, drq_f_nxt_gen, DYN_REQUESTS);
	bool found = false;

	do
	{
		buffer.printf("RDB$%" SQUADFORMAT,
			DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_gen, "RDB$GENERATOR_NAME"));

		found = false;

		FOR (REQUEST_HANDLE request)
			FIRST 1 X IN RDB$GENERATORS WITH X.RDB$GENERATOR_NAME EQ buffer.c_str()
		{
			found = true;
		}
		END_FOR
	} while (found);
}


void DYN_UTIL_generate_trigger_name(thread_db* tdbb, jrd_tra* /*transaction*/, MetaName& buffer)
{
/**************************************
 *
 *	D Y N _ U T I L _ g e n e r a t e _ t r i g g e r _ n a m e
 *
 **************************************
 *
 * Functional description
 *	Generate a name unique to RDB$TRIGGERS.
 *
 **************************************/
	SET_TDBB(tdbb);
	Jrd::Attachment* attachment = tdbb->getAttachment();
	bool found = false;

	do
	{
		buffer.printf("CHECK_%" SQUADFORMAT,
			DYN_UTIL_gen_unique_id(tdbb, drq_g_nxt_trg, "RDB$TRIGGER_NAME"));

		AutoCacheRequest request(tdbb, drq_f_nxt_trg, DYN_REQUESTS);

		found = false;
		FOR(REQUEST_HANDLE request)
			FIRST 1 X IN RDB$TRIGGERS
			WITH X.RDB$TRIGGER_NAME EQ buffer.c_str()
		{
			found = true;
		}
		END_FOR
	} while (found);
}


bool DYN_UTIL_find_field_source(thread_db* tdbb,
								jrd_tra* transaction,
								const MetaName& view_name,
								USHORT context,
								const TEXT* local_name,
								TEXT* output_field_name)
{
/**************************************
 *
 *	D Y N _ U T I L _ f i n d _ f i e l d _ s o u r c e
 *
 **************************************
 *
 * Functional description
 *	Find the original source for a view field.
 *
 **************************************/
	SET_TDBB(tdbb);

	AutoCacheRequest request(tdbb, drq_l_fld_src2, DYN_REQUESTS);
	bool found = false;

	FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
		VRL IN RDB$VIEW_RELATIONS CROSS
			RFR IN RDB$RELATION_FIELDS OVER RDB$RELATION_NAME
			WITH VRL.RDB$VIEW_NAME EQ view_name.c_str() AND
			VRL.RDB$VIEW_CONTEXT EQ context AND
			VRL.RDB$CONTEXT_TYPE BETWEEN VCT_TABLE AND VCT_VIEW AND
			VRL.RDB$PACKAGE_NAME MISSING AND
			RFR.RDB$FIELD_NAME EQ local_name
	{
		found = true;
		fb_utils::exact_name_limit(RFR.RDB$FIELD_SOURCE, sizeof(RFR.RDB$FIELD_SOURCE));
		strcpy(output_field_name, RFR.RDB$FIELD_SOURCE);
	}
	END_FOR

	if (!found)
	{
		request.reset(tdbb, drq_l_fld_src3, DYN_REQUESTS);

		FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
			VRL IN RDB$VIEW_RELATIONS CROSS
				PPR IN RDB$PROCEDURE_PARAMETERS
				WITH VRL.RDB$RELATION_NAME EQ PPR.RDB$PROCEDURE_NAME AND
				VRL.RDB$VIEW_NAME EQ view_name.c_str() AND
				VRL.RDB$VIEW_CONTEXT EQ context AND
				VRL.RDB$CONTEXT_TYPE EQ VCT_PROCEDURE AND
				PPR.RDB$PACKAGE_NAME EQUIV VRL.RDB$PACKAGE_NAME AND
				PPR.RDB$PARAMETER_TYPE = 1 AND // output
				PPR.RDB$PARAMETER_NAME EQ local_name
		{
			found = true;
			fb_utils::exact_name_limit(PPR.RDB$FIELD_SOURCE, sizeof(PPR.RDB$FIELD_SOURCE));
			strcpy(output_field_name, PPR.RDB$FIELD_SOURCE);
		}
		END_FOR
	}

	return found;
}


void DYN_UTIL_store_check_constraints(thread_db* tdbb, jrd_tra* transaction,
	const MetaName& constraint_name, const MetaName& trigger_name)
{
/**************************************
 *
 *	D Y N _ U T I L _ s t o r e _ c h e c k _ c o n s t r a i n t s
 *
 **************************************
 *
 * Functional description
 *	Fill in rdb$check_constraints the association between a check name and the
 *	system defined trigger that implements that check.
 *
 **************************************/
	SET_TDBB(tdbb);

	AutoCacheRequest request(tdbb, drq_s_chk_con, DYN_REQUESTS);

	STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
		CHK IN RDB$CHECK_CONSTRAINTS
	{
        strcpy(CHK.RDB$CONSTRAINT_NAME, constraint_name.c_str());
		strcpy(CHK.RDB$TRIGGER_NAME, trigger_name.c_str());
	}
	END_STORE
}
