mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 15:23:03 +01:00
1823 lines
46 KiB
Plaintext
1823 lines
46 KiB
Plaintext
/*
|
|
* PROGRAM: Dynamic SQL runtime support
|
|
* MODULE: metd.epp
|
|
* DESCRIPTION: Meta-data interface
|
|
*
|
|
* 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): ______________________________________.
|
|
* 2001.11.28 Claudio Valderrama: load not only udfs but udf arguments;
|
|
* handle possible collisions with udf redefinitions (drop->declare).
|
|
* This closes SF Bug# 409769.
|
|
* 2001.12.06 Claudio Valderrama: METD_get_charset_bpc() was added to
|
|
* get only the bytes per char of a field, given its charset id.
|
|
* This request is not cached.
|
|
* 2001.02.23 Claudio Valderrama: Fix SF Bug #228135 with views spoiling
|
|
* NULLs in outer joins.
|
|
* 2004.01.16 Vlad Horsun: make METD_get_col_default and
|
|
* METD_get_domain_default return actual length of default BLR
|
|
* 2004.01.16 Vlad Horsun: added support for default parameters
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include <string.h>
|
|
#include "../dsql/dsql.h"
|
|
#include "../jrd/ibase.h"
|
|
#include "../jrd/align.h"
|
|
#include "../jrd/intl.h"
|
|
#include "../jrd/irq.h"
|
|
#include "../jrd/tra.h"
|
|
#include "../dsql/ddl_proto.h"
|
|
#include "../dsql/metd_proto.h"
|
|
#include "../dsql/hsh_proto.h"
|
|
#include "../dsql/make_proto.h"
|
|
#include "../dsql/errd_proto.h"
|
|
#include "../jrd/blb_proto.h"
|
|
#include "../jrd/cmp_proto.h"
|
|
#include "../jrd/exe_proto.h"
|
|
#include "../jrd/gds_proto.h"
|
|
#include "../jrd/met_proto.h"
|
|
#include "../jrd/thread_proto.h"
|
|
#include "../jrd/why_proto.h"
|
|
#include "../common/utils_proto.h"
|
|
#include "../common/classes/init.h"
|
|
|
|
using namespace Jrd;
|
|
using namespace Firebird;
|
|
|
|
// NOTE: The static definition of DB and gds_trans by gpre will not
|
|
// be used by the meta data routines. Each of those routines has
|
|
// its own local definition of these variables.
|
|
|
|
DATABASE DB = STATIC "yachts.lnk";
|
|
|
|
static const UCHAR blr_bpb[] =
|
|
{
|
|
isc_bpb_version1,
|
|
isc_bpb_source_type, 1, isc_blob_blr,
|
|
isc_bpb_target_type, 1, isc_blob_blr
|
|
};
|
|
|
|
static void convert_dtype(dsql_fld*, SSHORT);
|
|
static void free_relation(dsql_rel*);
|
|
|
|
namespace
|
|
{
|
|
inline void validateTransaction(const jrd_tra* transaction)
|
|
{
|
|
if (!transaction || !transaction->checkHandle())
|
|
{
|
|
ERR_post(Arg::Gds(isc_bad_trans_handle));
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void METD_drop_charset(jrd_tra* transaction, const MetaName& metaName)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ d r o p _ c h a r s e t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Drop a character set from our metadata, and the next caller who wants it will
|
|
* look up the new version.
|
|
* Dropping will be achieved by marking the character set
|
|
* as dropped. Anyone with current access can continue
|
|
* accessing it.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
dsql_intlsym* charSet;
|
|
|
|
if (dbb->dbb_charsets.get(metaName, charSet))
|
|
{
|
|
MET_dsql_cache_use(tdbb, SYM_intlsym_charset, metaName);
|
|
charSet->intlsym_flags |= INTLSYM_dropped;
|
|
dbb->dbb_charsets.remove(metaName);
|
|
dbb->dbb_charsets_by_id.remove(charSet->intlsym_charset_id);
|
|
}
|
|
}
|
|
|
|
|
|
void METD_drop_collation(jrd_tra* transaction, const MetaName& name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ d r o p _ c o l l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Drop a collation from our metadata, and
|
|
* the next caller who wants it will
|
|
* look up the new version.
|
|
*
|
|
* Dropping will be achieved by marking the collation
|
|
* as dropped. Anyone with current access can continue
|
|
* accessing it.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
|
|
dsql_intlsym* collation;
|
|
|
|
if (dbb->dbb_collations.get(name, collation))
|
|
{
|
|
MET_dsql_cache_use(tdbb, SYM_intlsym_collation, name);
|
|
collation->intlsym_flags |= INTLSYM_dropped;
|
|
dbb->dbb_collations.remove(name);
|
|
}
|
|
}
|
|
|
|
|
|
void METD_drop_function(jrd_tra* transaction, const QualifiedName& name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ d r o p _ f u n c t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Drop a user defined function from our metadata, and
|
|
* the next caller who wants it will
|
|
* look up the new version.
|
|
*
|
|
* Dropping will be achieved by marking the function
|
|
* as dropped. Anyone with current access can continue
|
|
* accessing it.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
|
|
dsql_udf* function;
|
|
|
|
if (dbb->dbb_functions.get(name, function))
|
|
{
|
|
MET_dsql_cache_use(tdbb, SYM_udf, name.identifier, name.package);
|
|
function->udf_flags |= UDF_dropped;
|
|
dbb->dbb_functions.remove(name);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
void METD_drop_procedure(jrd_tra* transaction, const QualifiedName& name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ d r o p _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Drop a procedure from our metadata, and
|
|
* the next caller who wants it will
|
|
* look up the new version.
|
|
*
|
|
* Dropping will be achieved by marking the procedure
|
|
* as dropped. Anyone with current access can continue
|
|
* accessing it.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
|
|
dsql_prc* procedure;
|
|
|
|
if (dbb->dbb_procedures.get(name, procedure))
|
|
{
|
|
MET_dsql_cache_use(tdbb, SYM_procedure, name.identifier, name.package);
|
|
procedure->prc_flags |= PRC_dropped;
|
|
dbb->dbb_procedures.remove(name);
|
|
}
|
|
}
|
|
|
|
|
|
void METD_drop_relation(jrd_tra* transaction, const MetaName& name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ d r o p _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Drop a relation from our metadata, and
|
|
* rely on the next guy who wants it to
|
|
* look up the new version.
|
|
*
|
|
* Dropping will be achieved by marking the relation
|
|
* as dropped. Anyone with current access can continue
|
|
* accessing it.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
|
|
dsql_rel* relation;
|
|
|
|
if (dbb->dbb_relations.get(name, relation))
|
|
{
|
|
MET_dsql_cache_use(tdbb, SYM_relation, name);
|
|
relation->rel_flags |= REL_dropped;
|
|
dbb->dbb_relations.remove(name);
|
|
}
|
|
}
|
|
|
|
|
|
dsql_intlsym* METD_get_collation(jrd_tra* transaction, const MetaName& name, USHORT charset_id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ c o l l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up an international text type object.
|
|
* If it doesn't exist, return NULL.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
|
|
// Start by seeing if symbol is already defined
|
|
|
|
dsql_intlsym* symbol;
|
|
if (dbb->dbb_collations.get(name, symbol) && !(symbol->intlsym_flags & INTLSYM_dropped))
|
|
{
|
|
if (MET_dsql_cache_use(tdbb, SYM_intlsym_collation, name))
|
|
symbol->intlsym_flags |= INTLSYM_dropped;
|
|
else
|
|
return symbol;
|
|
}
|
|
|
|
// Now see if it is in the database
|
|
|
|
symbol = NULL;
|
|
|
|
AutoCacheRequest handle(tdbb, irq_collation, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
|
X IN RDB$COLLATIONS
|
|
CROSS Y IN RDB$CHARACTER_SETS OVER RDB$CHARACTER_SET_ID
|
|
WITH X.RDB$COLLATION_NAME EQ name.c_str() AND
|
|
X.RDB$CHARACTER_SET_ID EQ charset_id;
|
|
{
|
|
symbol = FB_NEW(dbb->dbb_pool) dsql_intlsym(dbb->dbb_pool);
|
|
symbol->intlsym_name = name;
|
|
symbol->intlsym_flags = 0;
|
|
symbol->intlsym_charset_id = X.RDB$CHARACTER_SET_ID;
|
|
symbol->intlsym_collate_id = X.RDB$COLLATION_ID;
|
|
symbol->intlsym_ttype =
|
|
INTL_CS_COLL_TO_TTYPE(symbol->intlsym_charset_id, symbol->intlsym_collate_id);
|
|
symbol->intlsym_bytes_per_char =
|
|
(Y.RDB$BYTES_PER_CHARACTER.NULL) ? 1 : (Y.RDB$BYTES_PER_CHARACTER);
|
|
}
|
|
END_FOR
|
|
|
|
if (!symbol)
|
|
return NULL;
|
|
|
|
dbb->dbb_collations.put(name, symbol);
|
|
MET_dsql_cache_use(tdbb, SYM_intlsym_collation, name);
|
|
|
|
return symbol;
|
|
}
|
|
|
|
|
|
USHORT METD_get_col_default(jrd_tra* transaction, const char* for_rel_name,
|
|
const char* for_col_name, bool* has_default, UCHAR* buffer, USHORT buff_length)
|
|
{
|
|
/*************************************************************
|
|
*
|
|
* M E T D _ g e t _ c o l _ d e f a u l t
|
|
*
|
|
**************************************************************
|
|
*
|
|
* Function:
|
|
* Gets the default value for a column of an existing table.
|
|
* Will check the default for the column of the table, if that is
|
|
* not present, will check for the default of the relevant domain
|
|
*
|
|
* The default blr is returned in buffer. The blr is of the form
|
|
* blr_version4 blr_literal ..... blr_eoc
|
|
*
|
|
* Reads the system tables RDB$FIELDS and RDB$RELATION_FIELDS.
|
|
*
|
|
**************************************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
bid* blob_id;
|
|
|
|
USHORT result = 0;
|
|
blb* blob_handle = 0;
|
|
|
|
*has_default = false;
|
|
|
|
AutoCacheRequest handle(tdbb, irq_col_default, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
|
RFL IN RDB$RELATION_FIELDS CROSS
|
|
FLD IN RDB$FIELDS WITH
|
|
RFL.RDB$RELATION_NAME EQ for_rel_name AND
|
|
RFL.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND
|
|
RFL.RDB$FIELD_NAME EQ for_col_name
|
|
{
|
|
if (!RFL.RDB$DEFAULT_VALUE.NULL)
|
|
{
|
|
blob_id = &RFL.RDB$DEFAULT_VALUE;
|
|
*has_default = true;
|
|
}
|
|
else if (!FLD.RDB$DEFAULT_VALUE.NULL)
|
|
{
|
|
blob_id = &FLD.RDB$DEFAULT_VALUE;
|
|
*has_default = true;
|
|
}
|
|
else
|
|
*has_default = false;
|
|
|
|
if (*has_default)
|
|
{
|
|
blob_handle = BLB_open2(tdbb, transaction, blob_id, sizeof(blr_bpb), blr_bpb, true);
|
|
|
|
// fetch segments. Assuming here that the buffer is big enough.
|
|
UCHAR* ptr_in_buffer = buffer;
|
|
while (true)
|
|
{
|
|
const USHORT length = BLB_get_segment(tdbb, blob_handle, ptr_in_buffer, buff_length);
|
|
|
|
ptr_in_buffer += length;
|
|
buff_length -= length;
|
|
result += length;
|
|
|
|
if (blob_handle->blb_flags & BLB_eof)
|
|
{
|
|
// null terminate the buffer
|
|
*ptr_in_buffer = 0;
|
|
break;
|
|
}
|
|
if (blob_handle->blb_fragment_size)
|
|
status_exception::raise(Arg::Gds(isc_segment));
|
|
else
|
|
continue;
|
|
}
|
|
|
|
try
|
|
{
|
|
ThreadStatusGuard status_vector(tdbb);
|
|
|
|
BLB_close(tdbb, blob_handle);
|
|
blob_handle = NULL;
|
|
}
|
|
catch (Exception&)
|
|
{
|
|
}
|
|
|
|
// the default string must be of the form:
|
|
// blr_version4 blr_literal ..... blr_eoc
|
|
fb_assert((buffer[0] == blr_version4) || (buffer[0] == blr_version5));
|
|
fb_assert(buffer[1] == blr_literal);
|
|
}
|
|
else
|
|
{
|
|
if (dbb->dbb_db_SQL_dialect > SQL_DIALECT_V5)
|
|
buffer[0] = blr_version5;
|
|
else
|
|
buffer[0] = blr_version4;
|
|
buffer[1] = blr_eoc;
|
|
result = 2;
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
dsql_intlsym* METD_get_charset(jrd_tra* transaction, USHORT length, const char* name) // UTF-8
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ c h a r s e t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up an international text type object.
|
|
* If it doesn't exist, return NULL.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
MetaName metaName(name, length);
|
|
|
|
// Start by seeing if symbol is already defined
|
|
|
|
dsql_intlsym* symbol;
|
|
if (dbb->dbb_charsets.get(metaName, symbol) && !(symbol->intlsym_flags & INTLSYM_dropped))
|
|
{
|
|
if (MET_dsql_cache_use(tdbb, SYM_intlsym_charset, metaName))
|
|
symbol->intlsym_flags |= INTLSYM_dropped;
|
|
else
|
|
return symbol;
|
|
}
|
|
|
|
// Now see if it is in the database
|
|
|
|
symbol = NULL;
|
|
|
|
AutoCacheRequest handle(tdbb, irq_charset, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
|
X IN RDB$COLLATIONS
|
|
CROSS Y IN RDB$CHARACTER_SETS OVER RDB$CHARACTER_SET_ID
|
|
CROSS Z IN RDB$TYPES
|
|
WITH Z.RDB$TYPE EQ Y.RDB$CHARACTER_SET_ID
|
|
AND Z.RDB$TYPE_NAME EQ name
|
|
AND Z.RDB$FIELD_NAME EQ "RDB$CHARACTER_SET_NAME"
|
|
AND Y.RDB$DEFAULT_COLLATE_NAME EQ X.RDB$COLLATION_NAME;
|
|
{
|
|
symbol = FB_NEW(dbb->dbb_pool) dsql_intlsym(dbb->dbb_pool);
|
|
symbol->intlsym_name = metaName;
|
|
symbol->intlsym_flags = 0;
|
|
symbol->intlsym_charset_id = X.RDB$CHARACTER_SET_ID;
|
|
symbol->intlsym_collate_id = X.RDB$COLLATION_ID;
|
|
symbol->intlsym_ttype =
|
|
INTL_CS_COLL_TO_TTYPE(symbol->intlsym_charset_id, symbol->intlsym_collate_id);
|
|
symbol->intlsym_bytes_per_char =
|
|
(Y.RDB$BYTES_PER_CHARACTER.NULL) ? 1 : (Y.RDB$BYTES_PER_CHARACTER);
|
|
}
|
|
END_FOR
|
|
|
|
if (!symbol)
|
|
return NULL;
|
|
|
|
dbb->dbb_charsets.put(metaName, symbol);
|
|
dbb->dbb_charsets_by_id.put(symbol->intlsym_charset_id, symbol);
|
|
MET_dsql_cache_use(tdbb, SYM_intlsym_charset, metaName);
|
|
|
|
return symbol;
|
|
}
|
|
|
|
|
|
USHORT METD_get_charset_bpc(jrd_tra* transaction, SSHORT charset_id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ c h a r s e t _ b p c
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up an international text type object.
|
|
* If it doesn't exist, return NULL.
|
|
* Go directly to system tables & return only the
|
|
* number of bytes per character. Lookup by
|
|
* charset' id, not by name.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
|
|
if (charset_id == CS_dynamic)
|
|
charset_id = tdbb->getCharSet();
|
|
|
|
dsql_intlsym* symbol = NULL;
|
|
if (!dbb->dbb_charsets_by_id.get(charset_id, symbol))
|
|
{
|
|
const MetaName cs_name = METD_get_charset_name(transaction, charset_id);
|
|
symbol = METD_get_charset(transaction, cs_name.length(), cs_name.c_str());
|
|
}
|
|
|
|
fb_assert(symbol);
|
|
|
|
return symbol ? symbol->intlsym_bytes_per_char : 0;
|
|
}
|
|
|
|
|
|
MetaName METD_get_charset_name(jrd_tra* transaction, SSHORT charset_id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ c h a r s e t _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up an international text type object.
|
|
* If it doesn't exist, return empty string.
|
|
* Go directly to system tables & return only the
|
|
* name.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
|
|
if (charset_id == CS_dynamic)
|
|
charset_id = tdbb->getCharSet();
|
|
|
|
dsql_intlsym* sym = NULL;
|
|
if (dbb->dbb_charsets_by_id.get(charset_id, sym))
|
|
return sym->intlsym_name;
|
|
|
|
MetaName name;
|
|
|
|
AutoCacheRequest handle(tdbb, irq_cs_name, IRQ_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
|
Y IN RDB$CHARACTER_SETS
|
|
WITH Y.RDB$CHARACTER_SET_ID EQ charset_id
|
|
{
|
|
name = Y.RDB$CHARACTER_SET_NAME;
|
|
}
|
|
END_FOR
|
|
|
|
// put new charset into hash table if needed
|
|
METD_get_charset(transaction, name.length(), name.c_str());
|
|
|
|
return name;
|
|
}
|
|
|
|
|
|
dsql_str* METD_get_default_charset(jrd_tra* transaction)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ d e f a u l t _ c h a r s e t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Find the default character set for a database
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
if (dbb->dbb_no_charset)
|
|
return NULL;
|
|
|
|
if (dbb->dbb_dfl_charset)
|
|
return dbb->dbb_dfl_charset;
|
|
|
|
// Now see if it is in the database
|
|
|
|
AutoCacheRequest handle(tdbb, irq_default_cs, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
|
FIRST 1 DBB IN RDB$DATABASE
|
|
WITH DBB.RDB$CHARACTER_SET_NAME NOT MISSING;
|
|
{
|
|
// Terminate ASCIIZ string on first trailing blank
|
|
fb_utils::exact_name(DBB.RDB$CHARACTER_SET_NAME);
|
|
const USHORT length = strlen(DBB.RDB$CHARACTER_SET_NAME);
|
|
dbb->dbb_dfl_charset = FB_NEW_RPT(dbb->dbb_pool, length) dsql_str;
|
|
dbb->dbb_dfl_charset->str_length = length;
|
|
dbb->dbb_dfl_charset->str_charset = NULL;
|
|
memcpy(dbb->dbb_dfl_charset->str_data, DBB.RDB$CHARACTER_SET_NAME, length);
|
|
}
|
|
END_FOR
|
|
|
|
if (!dbb->dbb_dfl_charset)
|
|
dbb->dbb_no_charset = true;
|
|
|
|
return dbb->dbb_dfl_charset;
|
|
}
|
|
|
|
|
|
bool METD_get_domain(jrd_tra* transaction, dsql_fld* field, const char* name) // UTF-8
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ d o m a i n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Fetch domain information for field defined as 'name'
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
bool found = false;
|
|
|
|
AutoCacheRequest handle(tdbb, irq_domain, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
|
FLX IN RDB$FIELDS WITH FLX.RDB$FIELD_NAME EQ name
|
|
{
|
|
found = true;
|
|
field->fld_length = FLX.RDB$FIELD_LENGTH;
|
|
field->fld_scale = FLX.RDB$FIELD_SCALE;
|
|
field->fld_sub_type = FLX.RDB$FIELD_SUB_TYPE;
|
|
field->fld_dimensions = FLX.RDB$DIMENSIONS.NULL ? 0 : FLX.RDB$DIMENSIONS;
|
|
|
|
field->fld_character_set_id = 0;
|
|
if (!FLX.RDB$CHARACTER_SET_ID.NULL)
|
|
field->fld_character_set_id = FLX.RDB$CHARACTER_SET_ID;
|
|
field->fld_collation_id = 0;
|
|
if (!FLX.RDB$COLLATION_ID.NULL)
|
|
field->fld_collation_id = FLX.RDB$COLLATION_ID;
|
|
field->fld_character_length = 0;
|
|
if (!FLX.RDB$CHARACTER_LENGTH.NULL)
|
|
field->fld_character_length = FLX.RDB$CHARACTER_LENGTH;
|
|
|
|
if (!FLX.RDB$COMPUTED_BLR.NULL)
|
|
field->fld_flags |= FLD_computed;
|
|
|
|
if (FLX.RDB$SYSTEM_FLAG == 1)
|
|
field->fld_flags |= FLD_system;
|
|
|
|
convert_dtype(field, FLX.RDB$FIELD_TYPE);
|
|
|
|
if (FLX.RDB$FIELD_TYPE == blr_blob) {
|
|
field->fld_seg_length = FLX.RDB$SEGMENT_LENGTH;
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
USHORT METD_get_domain_default(jrd_tra* transaction, const TEXT* domain_name, bool* has_default,
|
|
UCHAR* buffer, USHORT buff_length)
|
|
{
|
|
/*************************************************************
|
|
*
|
|
* M E T D _ g e t _ d o m a i n _ d e f a u l t
|
|
*
|
|
**************************************************************
|
|
*
|
|
* Function:
|
|
* Gets the default value for a domain of an existing table.
|
|
*
|
|
**************************************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
*has_default = false;
|
|
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
USHORT result = 0;
|
|
|
|
AutoCacheRequest handle(tdbb, irq_domain_2, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
|
FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ domain_name
|
|
{
|
|
bid* blob_id;
|
|
if (!FLD.RDB$DEFAULT_VALUE.NULL)
|
|
{
|
|
blob_id = &FLD.RDB$DEFAULT_VALUE;
|
|
*has_default = true;
|
|
}
|
|
else
|
|
*has_default = false;
|
|
|
|
if (*has_default)
|
|
{
|
|
blb* blob_handle = BLB_open2(tdbb, transaction, blob_id, sizeof(blr_bpb), blr_bpb, true);
|
|
|
|
// fetch segments. Assume buffer is big enough.
|
|
UCHAR* ptr_in_buffer = buffer;
|
|
while (true)
|
|
{
|
|
const USHORT length = BLB_get_segment(tdbb, blob_handle, ptr_in_buffer, buff_length);
|
|
|
|
ptr_in_buffer += length;
|
|
buff_length -= length;
|
|
result += length;
|
|
|
|
if (blob_handle->blb_flags & BLB_eof)
|
|
{
|
|
// null terminate the buffer
|
|
*ptr_in_buffer = 0;
|
|
break;
|
|
}
|
|
if (blob_handle->blb_fragment_size)
|
|
status_exception::raise(Arg::Gds(isc_segment));
|
|
else
|
|
continue;
|
|
}
|
|
|
|
try
|
|
{
|
|
ThreadStatusGuard status_vector(tdbb);
|
|
|
|
BLB_close(tdbb, blob_handle);
|
|
blob_handle = NULL;
|
|
}
|
|
catch (Exception&)
|
|
{
|
|
}
|
|
|
|
// the default string must be of the form:
|
|
// blr_version4 blr_literal ..... blr_eoc
|
|
fb_assert((buffer[0] == blr_version4) || (buffer[0] == blr_version5));
|
|
fb_assert(buffer[1] == blr_literal);
|
|
}
|
|
else
|
|
{
|
|
if (dbb->dbb_db_SQL_dialect > SQL_DIALECT_V5)
|
|
buffer[0] = blr_version5;
|
|
else
|
|
buffer[0] = blr_version4;
|
|
buffer[1] = blr_eoc;
|
|
result = 2;
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
bool METD_get_exception(jrd_tra* transaction, const dsql_str* name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ e x c e p t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up an exception.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
bool found = false;
|
|
|
|
AutoCacheRequest handle(tdbb, irq_exception, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
|
X IN RDB$EXCEPTIONS WITH
|
|
X.RDB$EXCEPTION_NAME EQ name->str_data;
|
|
{
|
|
found = true;
|
|
}
|
|
END_FOR
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
dsql_udf* METD_get_function(jrd_tra* transaction, DsqlCompilerScratch* dsqlScratch,
|
|
const QualifiedName& name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ f u n c t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up a user defined function. If it doesn't exist,
|
|
* return NULL.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
QualifiedName metaName(name);
|
|
|
|
bool maybeUnqualified = dsqlScratch->package.hasData() && metaName.package.isEmpty();
|
|
if (maybeUnqualified)
|
|
metaName.package = dsqlScratch->package;
|
|
|
|
// Start by seeing if symbol is already defined
|
|
|
|
dsql_udf* userFunc = NULL;
|
|
if (dbb->dbb_functions.get(metaName, userFunc))
|
|
{
|
|
if (userFunc->udf_private && metaName.package != dsqlScratch->package)
|
|
{
|
|
status_exception::raise(Arg::Gds(isc_private_function) <<
|
|
Arg::Str(metaName.identifier) << Arg::Str(metaName.package));
|
|
}
|
|
|
|
if (MET_dsql_cache_use(tdbb, SYM_udf, metaName.identifier, metaName.package))
|
|
userFunc->udf_flags |= UDF_dropped;
|
|
|
|
return userFunc;
|
|
}
|
|
|
|
// Now see if it is in the database
|
|
|
|
USHORT return_arg = 0;
|
|
|
|
while (!userFunc)
|
|
{
|
|
AutoCacheRequest handle1(tdbb, irq_function, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE transaction)
|
|
X IN RDB$FUNCTIONS WITH
|
|
X.RDB$FUNCTION_NAME EQ metaName.identifier.c_str() AND
|
|
X.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.package.c_str(), '')
|
|
{
|
|
userFunc = FB_NEW(dbb->dbb_pool) dsql_udf(dbb->dbb_pool);
|
|
userFunc->udf_name = metaName;
|
|
userFunc->udf_private = !X.RDB$PRIVATE_FLAG.NULL && X.RDB$PRIVATE_FLAG != 0;
|
|
|
|
return_arg = X.RDB$RETURN_ARGUMENT;
|
|
}
|
|
END_FOR
|
|
|
|
if (!userFunc)
|
|
{
|
|
if (maybeUnqualified)
|
|
{
|
|
maybeUnqualified = false;
|
|
metaName.package = "";
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
AutoCacheRequest handle2(tdbb, irq_func_return, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction)
|
|
X IN RDB$FUNCTION_ARGUMENTS WITH
|
|
X.RDB$FUNCTION_NAME EQ metaName.identifier.c_str() AND
|
|
X.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.package.c_str(), '')
|
|
SORTED BY X.RDB$ARGUMENT_POSITION
|
|
{
|
|
if (!X.RDB$FIELD_SOURCE.NULL)
|
|
{
|
|
AutoCacheRequest handle3(tdbb, irq_func_ret_fld, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle3 TRANSACTION_HANDLE transaction)
|
|
F IN RDB$FIELDS WITH
|
|
F.RDB$FIELD_NAME EQ X.RDB$FIELD_SOURCE
|
|
{
|
|
if (X.RDB$ARGUMENT_POSITION == return_arg)
|
|
{
|
|
userFunc->udf_dtype = (F.RDB$FIELD_TYPE != blr_blob) ?
|
|
gds_cvt_blr_dtype[F.RDB$FIELD_TYPE] : dtype_blob;
|
|
userFunc->udf_scale = F.RDB$FIELD_SCALE;
|
|
|
|
if (!F.RDB$FIELD_SUB_TYPE.NULL) {
|
|
userFunc->udf_sub_type = F.RDB$FIELD_SUB_TYPE;
|
|
}
|
|
else {
|
|
userFunc->udf_sub_type = 0;
|
|
}
|
|
// CVC: We are overcoming a bug in ddl.cpp:put_field()
|
|
// when any field is defined: the length is not given for blobs.
|
|
if (F.RDB$FIELD_TYPE == blr_blob)
|
|
userFunc->udf_length = sizeof(ISC_QUAD);
|
|
else
|
|
userFunc->udf_length = F.RDB$FIELD_LENGTH;
|
|
|
|
if (!F.RDB$CHARACTER_SET_ID.NULL) {
|
|
userFunc->udf_character_set_id = F.RDB$CHARACTER_SET_ID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DSC d;
|
|
d.dsc_dtype = (F.RDB$FIELD_TYPE != blr_blob) ?
|
|
gds_cvt_blr_dtype[F.RDB$FIELD_TYPE] : dtype_blob;
|
|
// dimitr: adjust the UDF arguments for CSTRING
|
|
if (d.dsc_dtype == dtype_cstring) {
|
|
d.dsc_dtype = dtype_text;
|
|
}
|
|
d.dsc_scale = F.RDB$FIELD_SCALE;
|
|
if (!F.RDB$FIELD_SUB_TYPE.NULL) {
|
|
d.dsc_sub_type = F.RDB$FIELD_SUB_TYPE;
|
|
}
|
|
else {
|
|
d.dsc_sub_type = 0;
|
|
}
|
|
d.dsc_length = F.RDB$FIELD_LENGTH;
|
|
if (d.dsc_dtype == dtype_varying) {
|
|
d.dsc_length += sizeof(USHORT);
|
|
}
|
|
d.dsc_address = NULL;
|
|
|
|
if (!F.RDB$CHARACTER_SET_ID.NULL)
|
|
{
|
|
if (d.dsc_dtype != dtype_blob) {
|
|
d.dsc_ttype() = F.RDB$CHARACTER_SET_ID;
|
|
}
|
|
else {
|
|
d.dsc_scale = F.RDB$CHARACTER_SET_ID;
|
|
}
|
|
}
|
|
|
|
if (X.RDB$MECHANISM != FUN_value && X.RDB$MECHANISM != FUN_reference)
|
|
{
|
|
d.dsc_flags = DSC_nullable;
|
|
}
|
|
|
|
userFunc->udf_arguments.add(d);
|
|
}
|
|
}
|
|
END_FOR
|
|
}
|
|
else
|
|
{
|
|
if (X.RDB$ARGUMENT_POSITION == return_arg)
|
|
{
|
|
userFunc->udf_dtype = (X.RDB$FIELD_TYPE != blr_blob) ?
|
|
gds_cvt_blr_dtype[X.RDB$FIELD_TYPE] : dtype_blob;
|
|
userFunc->udf_scale = X.RDB$FIELD_SCALE;
|
|
|
|
if (!X.RDB$FIELD_SUB_TYPE.NULL) {
|
|
userFunc->udf_sub_type = X.RDB$FIELD_SUB_TYPE;
|
|
}
|
|
else {
|
|
userFunc->udf_sub_type = 0;
|
|
}
|
|
// CVC: We are overcoming a bug in ddl.c:put_field()
|
|
// when any field is defined: the length is not given for blobs.
|
|
if (X.RDB$FIELD_TYPE == blr_blob)
|
|
userFunc->udf_length = sizeof(ISC_QUAD);
|
|
else
|
|
userFunc->udf_length = X.RDB$FIELD_LENGTH;
|
|
|
|
if (!X.RDB$CHARACTER_SET_ID.NULL) {
|
|
userFunc->udf_character_set_id = X.RDB$CHARACTER_SET_ID;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DSC d;
|
|
d.dsc_dtype = (X.RDB$FIELD_TYPE != blr_blob) ?
|
|
gds_cvt_blr_dtype[X.RDB$FIELD_TYPE] : dtype_blob;
|
|
// dimitr: adjust the UDF arguments for CSTRING
|
|
if (d.dsc_dtype == dtype_cstring) {
|
|
d.dsc_dtype = dtype_text;
|
|
}
|
|
d.dsc_scale = X.RDB$FIELD_SCALE;
|
|
if (!X.RDB$FIELD_SUB_TYPE.NULL) {
|
|
d.dsc_sub_type = X.RDB$FIELD_SUB_TYPE;
|
|
}
|
|
else {
|
|
d.dsc_sub_type = 0;
|
|
}
|
|
d.dsc_length = X.RDB$FIELD_LENGTH;
|
|
if (d.dsc_dtype == dtype_varying) {
|
|
d.dsc_length += sizeof(USHORT);
|
|
}
|
|
d.dsc_address = NULL;
|
|
|
|
if (!X.RDB$CHARACTER_SET_ID.NULL)
|
|
{
|
|
if (d.dsc_dtype != dtype_blob) {
|
|
d.dsc_ttype() = X.RDB$CHARACTER_SET_ID;
|
|
}
|
|
else {
|
|
d.dsc_scale = X.RDB$CHARACTER_SET_ID;
|
|
}
|
|
}
|
|
|
|
if (X.RDB$MECHANISM != FUN_value && X.RDB$MECHANISM != FUN_reference)
|
|
{
|
|
d.dsc_flags = DSC_nullable;
|
|
}
|
|
|
|
userFunc->udf_arguments.add(d);
|
|
}
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
// Adjust the return type & length of the UDF to account for
|
|
// cstring & varying. While a UDF can return CSTRING, we convert it
|
|
// to VARCHAR for manipulation as CSTRING is not a SQL type.
|
|
|
|
if (userFunc->udf_dtype == dtype_cstring)
|
|
{
|
|
userFunc->udf_dtype = dtype_varying;
|
|
userFunc->udf_length += sizeof(USHORT);
|
|
if (userFunc->udf_length > MAX_SSHORT)
|
|
userFunc->udf_length = MAX_SSHORT;
|
|
}
|
|
else if (userFunc->udf_dtype == dtype_varying)
|
|
userFunc->udf_length += sizeof(USHORT);
|
|
|
|
dbb->dbb_functions.put(userFunc->udf_name, userFunc);
|
|
|
|
if (userFunc->udf_private && metaName.package != dsqlScratch->package)
|
|
{
|
|
status_exception::raise(Arg::Gds(isc_private_function) <<
|
|
Arg::Str(metaName.identifier) << Arg::Str(metaName.package));
|
|
}
|
|
|
|
MET_dsql_cache_use(tdbb, SYM_udf, userFunc->udf_name.identifier, userFunc->udf_name.package);
|
|
|
|
return userFunc;
|
|
}
|
|
|
|
|
|
dsql_nod* METD_get_primary_key(jrd_tra* transaction, const dsql_str* relation_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ p r i m a r y _ k e y
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup the fields for the primary key
|
|
* index on a relation, returning a list
|
|
* node of the fields.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
DsqlNodStack stack;
|
|
|
|
AutoCacheRequest handle(tdbb, irq_primary_key, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
|
X IN RDB$INDICES CROSS
|
|
Y IN RDB$INDEX_SEGMENTS
|
|
OVER RDB$INDEX_NAME CROSS
|
|
Z IN RDB$RELATION_CONSTRAINTS
|
|
OVER RDB$INDEX_NAME
|
|
WITH Z.RDB$RELATION_NAME EQ relation_name->str_data
|
|
AND Z.RDB$CONSTRAINT_TYPE EQ "PRIMARY KEY"
|
|
SORTED BY Y.RDB$FIELD_POSITION
|
|
{
|
|
stack.push(MAKE_field_name(Y.RDB$FIELD_NAME));
|
|
}
|
|
END_FOR
|
|
|
|
return (stack.getCount() ? MAKE_list(stack) : NULL);
|
|
}
|
|
|
|
|
|
dsql_prc* METD_get_procedure(jrd_tra* transaction, DsqlCompilerScratch* dsqlScratch,
|
|
const dsql_str* name, const dsql_str* package)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up a procedure. If it doesn't exist, return NULL.
|
|
* If it does, fetch field information as well.
|
|
* If it is marked dropped, try to read from system tables
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
QualifiedName metaName(MetaName(name->str_data, name->str_length),
|
|
(package ? MetaName(package->str_data, package->str_length) : ""));
|
|
|
|
// ASF: I've removed the code where we verify if the procedure being looked up is the one being
|
|
// defined (dsqlScratch->procedure). This code is totally incorrect, not considering
|
|
// transactions and savepoints, hence being incompatible with packages).
|
|
// Example (with autocommit off):
|
|
//
|
|
// SQL> create procedure p1 as begin end!
|
|
// SQL> create procedure p2 as begin execute procedure p1; end!
|
|
// SQL> rollback!
|
|
// SQL> execute procedure p2!
|
|
// Statement failed, SQLSTATE = 42000
|
|
// Dynamic SQL Error
|
|
// -SQL error code = -204
|
|
// -Procedure unknown
|
|
// -P2
|
|
// SQL> execute procedure p1!
|
|
// Statement failed, SQLSTATE = 42000
|
|
// invalid request BLR at offset 5
|
|
// -procedure P1 is not defined
|
|
//
|
|
// The side effect is that this occur in more cases now:
|
|
//
|
|
// SQL> create procedure p as begin execute procedure p; execute procedure p2; end!
|
|
// Statement failed, SQLSTATE = 42000
|
|
// Dynamic SQL Error
|
|
// -SQL error code = -204
|
|
// -Procedure unknown
|
|
// -P2
|
|
// SQL> execute procedure p!
|
|
// Statement failed, SQLSTATE = 42000
|
|
// invalid request BLR at offset 4
|
|
// -procedure P is not defined
|
|
//
|
|
// I hope for a solution, involving savepoint logic.
|
|
|
|
bool maybeUnqualified = dsqlScratch->package.hasData() && metaName.package.isEmpty();
|
|
if (maybeUnqualified)
|
|
metaName.package = dsqlScratch->package;
|
|
|
|
// Start by seeing if symbol is already defined
|
|
|
|
dsql_prc* procedure = NULL;
|
|
if (dbb->dbb_procedures.get(metaName, procedure))
|
|
{
|
|
if (procedure->prc_private && metaName.package != dsqlScratch->package)
|
|
{
|
|
status_exception::raise(Arg::Gds(isc_private_procedure) <<
|
|
Arg::Str(metaName.identifier) << Arg::Str(metaName.package));
|
|
}
|
|
|
|
if (MET_dsql_cache_use(tdbb, SYM_procedure, metaName.identifier, metaName.package))
|
|
procedure->prc_flags |= PRC_dropped;
|
|
|
|
return procedure;
|
|
}
|
|
|
|
// now see if it is in the database
|
|
|
|
while (!procedure)
|
|
{
|
|
AutoCacheRequest handle1(tdbb, irq_procedure, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE transaction)
|
|
X IN RDB$PROCEDURES
|
|
WITH X.RDB$PROCEDURE_NAME EQ name->str_data AND
|
|
X.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.package.c_str(), '')
|
|
{
|
|
fb_utils::exact_name(X.RDB$OWNER_NAME);
|
|
|
|
procedure = FB_NEW(dbb->dbb_pool) dsql_prc(dbb->dbb_pool);
|
|
procedure->prc_id = X.RDB$PROCEDURE_ID;
|
|
procedure->prc_name = metaName;
|
|
procedure->prc_owner = X.RDB$OWNER_NAME;
|
|
procedure->prc_private = !X.RDB$PRIVATE_FLAG.NULL && X.RDB$PRIVATE_FLAG != 0;
|
|
}
|
|
END_FOR
|
|
|
|
if (!procedure)
|
|
{
|
|
if (maybeUnqualified)
|
|
{
|
|
maybeUnqualified = false;
|
|
metaName.package = "";
|
|
}
|
|
else
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// Lookup parameter stuff
|
|
|
|
for (int type = 0; type < 2; type++)
|
|
{
|
|
dsql_fld** const ptr = type ? &procedure->prc_outputs : &procedure->prc_inputs;
|
|
|
|
SSHORT count = 0, defaults = 0;
|
|
|
|
AutoCacheRequest handle2(tdbb, irq_parameters, IRQ_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction)
|
|
PR IN RDB$PROCEDURE_PARAMETERS
|
|
CROSS FLD IN RDB$FIELDS
|
|
WITH FLD.RDB$FIELD_NAME EQ PR.RDB$FIELD_SOURCE AND
|
|
PR.RDB$PROCEDURE_NAME EQ name->str_data AND
|
|
PR.RDB$PARAMETER_TYPE = type AND
|
|
PR.RDB$PACKAGE_NAME EQUIV NULLIF(metaName.package.c_str(), '')
|
|
SORTED BY DESCENDING PR.RDB$PARAMETER_NUMBER
|
|
{
|
|
const SSHORT pr_collation_id_null = PR.RDB$COLLATION_ID.NULL;
|
|
const SSHORT pr_collation_id = PR.RDB$COLLATION_ID;
|
|
|
|
const SSHORT pr_default_value_null = PR.RDB$DEFAULT_VALUE.NULL;
|
|
|
|
const SSHORT pr_null_flag_null = PR.RDB$NULL_FLAG.NULL;
|
|
const SSHORT pr_null_flag = PR.RDB$NULL_FLAG;
|
|
|
|
const bool pr_type_of =
|
|
(!PR.RDB$PARAMETER_MECHANISM.NULL && PR.RDB$PARAMETER_MECHANISM == prm_mech_type_of);
|
|
|
|
count++;
|
|
// allocate the field block
|
|
|
|
fb_utils::exact_name(PR.RDB$PARAMETER_NAME);
|
|
fb_utils::exact_name(PR.RDB$FIELD_SOURCE);
|
|
|
|
dsql_fld* parameter = FB_NEW(dbb->dbb_pool) dsql_fld(dbb->dbb_pool);
|
|
parameter->fld_next = *ptr;
|
|
*ptr = parameter;
|
|
|
|
// get parameter information
|
|
|
|
parameter->fld_name = PR.RDB$PARAMETER_NAME;
|
|
parameter->fld_source = PR.RDB$FIELD_SOURCE;
|
|
|
|
parameter->fld_id = PR.RDB$PARAMETER_NUMBER;
|
|
parameter->fld_length = FLD.RDB$FIELD_LENGTH;
|
|
parameter->fld_scale = FLD.RDB$FIELD_SCALE;
|
|
parameter->fld_sub_type = FLD.RDB$FIELD_SUB_TYPE;
|
|
parameter->fld_procedure = procedure;
|
|
|
|
if (!FLD.RDB$CHARACTER_SET_ID.NULL)
|
|
parameter->fld_character_set_id = FLD.RDB$CHARACTER_SET_ID;
|
|
|
|
if (!pr_collation_id_null)
|
|
parameter->fld_collation_id = pr_collation_id;
|
|
else if (!FLD.RDB$COLLATION_ID.NULL)
|
|
parameter->fld_collation_id = FLD.RDB$COLLATION_ID;
|
|
|
|
convert_dtype(parameter, FLD.RDB$FIELD_TYPE);
|
|
|
|
if (!pr_null_flag_null)
|
|
{
|
|
if (!pr_null_flag)
|
|
parameter->fld_flags |= FLD_nullable;
|
|
}
|
|
else if (!FLD.RDB$NULL_FLAG || pr_type_of)
|
|
parameter->fld_flags |= FLD_nullable;
|
|
|
|
if (FLD.RDB$FIELD_TYPE == blr_blob)
|
|
parameter->fld_seg_length = FLD.RDB$SEGMENT_LENGTH;
|
|
|
|
if (type == 0 &&
|
|
(!pr_default_value_null ||
|
|
(fb_utils::implicit_domain(FLD.RDB$FIELD_NAME) && !FLD.RDB$DEFAULT_VALUE.NULL)))
|
|
{
|
|
defaults++;
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
if (type)
|
|
procedure->prc_out_count = count;
|
|
else
|
|
{
|
|
procedure->prc_in_count = count;
|
|
procedure->prc_def_count = defaults;
|
|
}
|
|
}
|
|
|
|
dbb->dbb_procedures.put(procedure->prc_name, procedure);
|
|
|
|
if (procedure->prc_private && metaName.package != dsqlScratch->package)
|
|
{
|
|
status_exception::raise(Arg::Gds(isc_private_procedure) <<
|
|
Arg::Str(metaName.identifier) << Arg::Str(metaName.package));
|
|
}
|
|
|
|
MET_dsql_cache_use(tdbb, SYM_procedure, procedure->prc_name.identifier,
|
|
procedure->prc_name.package);
|
|
|
|
return procedure;
|
|
}
|
|
|
|
|
|
dsql_rel* METD_get_relation(jrd_tra* transaction, DsqlCompilerScratch* dsqlScratch,
|
|
const MetaName& name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up a relation. If it doesn't exist, return NULL.
|
|
* If it does, fetch field information as well.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
dsql_dbb* dbb = transaction->getDsqlAttachment();
|
|
|
|
// See if the relation is the one currently being defined in this statement
|
|
|
|
dsql_rel* temp = dsqlScratch->relation;
|
|
if (temp != NULL && temp->rel_name == name)
|
|
return temp;
|
|
|
|
// Start by seeing if symbol is already defined
|
|
|
|
if (dbb->dbb_relations.get(name, temp) && !(temp->rel_flags & REL_dropped))
|
|
{
|
|
if (MET_dsql_cache_use(tdbb, SYM_relation, name))
|
|
temp->rel_flags |= REL_dropped;
|
|
else
|
|
return temp;
|
|
}
|
|
|
|
// If the relation id or any of the field ids have not yet been assigned,
|
|
// and this is a type of statement which does not use ids, prepare a
|
|
// temporary relation block to provide information without caching it
|
|
|
|
bool permanent = true;
|
|
|
|
AutoCacheRequest handle1(tdbb, irq_rel_ids, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE transaction)
|
|
REL IN RDB$RELATIONS
|
|
CROSS RFR IN RDB$RELATION_FIELDS OVER RDB$RELATION_NAME
|
|
WITH REL.RDB$RELATION_NAME EQ name.c_str()
|
|
AND (REL.RDB$RELATION_ID MISSING OR RFR.RDB$FIELD_ID MISSING)
|
|
{
|
|
permanent = false;
|
|
}
|
|
END_FOR
|
|
|
|
// Now see if it is in the database
|
|
|
|
MemoryPool& pool = permanent ? dbb->dbb_pool : *tdbb->getDefaultPool();
|
|
|
|
dsql_rel* relation = NULL;
|
|
|
|
AutoCacheRequest handle2(tdbb, irq_relation, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction)
|
|
X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ name.c_str()
|
|
{
|
|
fb_utils::exact_name(X.RDB$OWNER_NAME);
|
|
|
|
// Allocate from default or permanent pool as appropriate
|
|
|
|
if (!X.RDB$RELATION_ID.NULL)
|
|
{
|
|
relation = FB_NEW(pool) dsql_rel(pool);
|
|
relation->rel_id = X.RDB$RELATION_ID;
|
|
}
|
|
else if (!DDL_ids(dsqlScratch))
|
|
relation = FB_NEW(pool) dsql_rel(pool);
|
|
|
|
// fill out the relation information
|
|
|
|
if (relation)
|
|
{
|
|
relation->rel_name = name;
|
|
relation->rel_owner = X.RDB$OWNER_NAME;
|
|
if (!(relation->rel_dbkey_length = X.RDB$DBKEY_LENGTH))
|
|
relation->rel_dbkey_length = 8;
|
|
// CVC: let's see if this is a table or a view.
|
|
if (!X.RDB$VIEW_BLR.NULL)
|
|
relation->rel_flags |= REL_view;
|
|
if (!X.RDB$EXTERNAL_FILE.NULL)
|
|
relation->rel_flags |= REL_external;
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
if (!relation)
|
|
return NULL;
|
|
|
|
// Lookup field stuff
|
|
|
|
dsql_fld** ptr = &relation->rel_fields;
|
|
|
|
AutoCacheRequest handle3(tdbb, irq_fields, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle3 TRANSACTION_HANDLE transaction)
|
|
FLX IN RDB$FIELDS CROSS
|
|
RFR IN RDB$RELATION_FIELDS
|
|
WITH FLX.RDB$FIELD_NAME EQ RFR.RDB$FIELD_SOURCE
|
|
AND RFR.RDB$RELATION_NAME EQ name.c_str()
|
|
SORTED BY RFR.RDB$FIELD_POSITION
|
|
{
|
|
// allocate the field block
|
|
|
|
fb_utils::exact_name(RFR.RDB$FIELD_NAME);
|
|
fb_utils::exact_name(RFR.RDB$FIELD_SOURCE);
|
|
|
|
// Allocate from default or permanent pool as appropriate
|
|
|
|
dsql_fld* field = NULL;
|
|
|
|
if (!RFR.RDB$FIELD_ID.NULL)
|
|
{
|
|
field = FB_NEW(pool) dsql_fld(pool);
|
|
field->fld_id = RFR.RDB$FIELD_ID;
|
|
}
|
|
else if (!DDL_ids(dsqlScratch))
|
|
field = FB_NEW(pool) dsql_fld(pool);
|
|
|
|
if (field)
|
|
{
|
|
*ptr = field;
|
|
ptr = &field->fld_next;
|
|
|
|
// get field information
|
|
|
|
field->fld_name = RFR.RDB$FIELD_NAME;
|
|
field->fld_source = RFR.RDB$FIELD_SOURCE;
|
|
field->fld_length = FLX.RDB$FIELD_LENGTH;
|
|
field->fld_scale = FLX.RDB$FIELD_SCALE;
|
|
field->fld_sub_type = FLX.RDB$FIELD_SUB_TYPE;
|
|
field->fld_relation = relation;
|
|
|
|
if (!FLX.RDB$COMPUTED_BLR.NULL)
|
|
field->fld_flags |= FLD_computed;
|
|
|
|
convert_dtype(field, FLX.RDB$FIELD_TYPE);
|
|
|
|
if (FLX.RDB$FIELD_TYPE == blr_blob) {
|
|
field->fld_seg_length = FLX.RDB$SEGMENT_LENGTH;
|
|
}
|
|
|
|
if (!FLX.RDB$DIMENSIONS.NULL && FLX.RDB$DIMENSIONS)
|
|
{
|
|
field->fld_element_dtype = field->fld_dtype;
|
|
field->fld_element_length = field->fld_length;
|
|
field->fld_dtype = dtype_array;
|
|
field->fld_length = sizeof(ISC_QUAD);
|
|
field->fld_dimensions = FLX.RDB$DIMENSIONS;
|
|
}
|
|
|
|
if (!FLX.RDB$CHARACTER_SET_ID.NULL)
|
|
field->fld_character_set_id = FLX.RDB$CHARACTER_SET_ID;
|
|
|
|
if (!RFR.RDB$COLLATION_ID.NULL)
|
|
field->fld_collation_id = RFR.RDB$COLLATION_ID;
|
|
else if (!FLX.RDB$COLLATION_ID.NULL)
|
|
field->fld_collation_id = FLX.RDB$COLLATION_ID;
|
|
|
|
if (!(RFR.RDB$NULL_FLAG || FLX.RDB$NULL_FLAG) || (relation->rel_flags & REL_view))
|
|
{
|
|
field->fld_flags |= FLD_nullable;
|
|
}
|
|
|
|
if (RFR.RDB$SYSTEM_FLAG == 1 || FLX.RDB$SYSTEM_FLAG == 1)
|
|
field->fld_flags |= FLD_system;
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
if (dbb->dbb_relations.get(name, temp) && !(temp->rel_flags & REL_dropped))
|
|
{
|
|
free_relation(relation);
|
|
return temp;
|
|
}
|
|
|
|
// Add relation to the list
|
|
|
|
if (permanent)
|
|
{
|
|
dbb->dbb_relations.put(relation->rel_name, relation);
|
|
MET_dsql_cache_use(tdbb, SYM_relation, relation->rel_name);
|
|
}
|
|
else
|
|
relation->rel_flags |= REL_new_relation;
|
|
|
|
return relation;
|
|
}
|
|
|
|
|
|
bool METD_get_type(jrd_tra* transaction, const dsql_str* name, const char* field, SSHORT* value)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ t y p e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up a symbolic name in RDB$TYPES
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
bool found = false;
|
|
|
|
AutoCacheRequest handle(tdbb, irq_type, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
|
X IN RDB$TYPES WITH
|
|
X.RDB$FIELD_NAME EQ field AND X.RDB$TYPE_NAME EQ name->str_data;
|
|
{
|
|
found = true;
|
|
*value = X.RDB$TYPE;
|
|
}
|
|
END_FOR
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
dsql_rel* METD_get_view_base(jrd_tra* transaction, DsqlCompilerScratch* dsqlScratch,
|
|
const char* view_name, MetaNamePairMap& fields)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ v i e w _ b a s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Return the base table of a view or NULL if there
|
|
* is more than one.
|
|
* If there is only one base, put in fields a map of
|
|
* top view field name / bottom base field name.
|
|
* Ignores the field in the case of a base field name
|
|
* appearing more than one time in a level.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
dsql_rel* relation = NULL;
|
|
|
|
bool first = true;
|
|
bool cont = true;
|
|
MetaNamePairMap previousAux;
|
|
|
|
fields.clear();
|
|
|
|
while (cont)
|
|
{
|
|
AutoCacheRequest handle1(tdbb, irq_view_base, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE transaction)
|
|
X IN RDB$VIEW_RELATIONS
|
|
WITH X.RDB$VIEW_NAME EQ view_name
|
|
{
|
|
// return NULL if there is more than one context
|
|
if (X.RDB$VIEW_CONTEXT != 1 || X.RDB$CONTEXT_TYPE == VCT_PROCEDURE)
|
|
{
|
|
relation = NULL;
|
|
cont = false;
|
|
break;
|
|
}
|
|
|
|
fb_utils::exact_name(X.RDB$CONTEXT_NAME);
|
|
fb_utils::exact_name(X.RDB$RELATION_NAME);
|
|
|
|
relation = METD_get_relation(transaction, dsqlScratch, X.RDB$RELATION_NAME);
|
|
|
|
Array<MetaName> ambiguities;
|
|
MetaNamePairMap currentAux;
|
|
|
|
if (!relation)
|
|
{
|
|
cont = false;
|
|
break;
|
|
}
|
|
|
|
AutoCacheRequest handle2(tdbb, irq_view_base_flds, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE transaction)
|
|
RFL IN RDB$RELATION_FIELDS
|
|
WITH RFL.RDB$RELATION_NAME EQ X.RDB$VIEW_NAME
|
|
{
|
|
if (RFL.RDB$BASE_FIELD.NULL || RFL.RDB$FIELD_NAME.NULL)
|
|
continue;
|
|
|
|
const MetaName baseField(RFL.RDB$BASE_FIELD);
|
|
if (currentAux.exist(baseField))
|
|
ambiguities.add(baseField);
|
|
else
|
|
{
|
|
const MetaName fieldName(RFL.RDB$FIELD_NAME);
|
|
if (first)
|
|
{
|
|
fields.put(fieldName, baseField);
|
|
currentAux.put(baseField, fieldName);
|
|
}
|
|
else
|
|
{
|
|
MetaName field;
|
|
|
|
if (previousAux.get(fieldName, field))
|
|
{
|
|
fields.put(field, baseField);
|
|
currentAux.put(baseField, field);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
for (const MetaName* i = ambiguities.begin(); i != ambiguities.end(); ++i)
|
|
{
|
|
MetaName field;
|
|
|
|
if (currentAux.get(*i, field))
|
|
{
|
|
currentAux.remove(*i);
|
|
fields.remove(field);
|
|
}
|
|
}
|
|
|
|
previousAux.takeOwnership(currentAux);
|
|
|
|
if (relation->rel_flags & REL_view)
|
|
view_name = X.RDB$RELATION_NAME;
|
|
else
|
|
{
|
|
cont = false;
|
|
break;
|
|
}
|
|
|
|
first = false;
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
if (!relation)
|
|
fields.clear();
|
|
|
|
return relation;
|
|
}
|
|
|
|
|
|
dsql_rel* METD_get_view_relation(jrd_tra* transaction, DsqlCompilerScratch* dsqlScratch,
|
|
const char* view_name, const char* relation_or_alias)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T D _ g e t _ v i e w _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Return TRUE if the passed view_name represents a
|
|
* view with the passed relation as a base table
|
|
* (the relation could be an alias).
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
validateTransaction(transaction);
|
|
|
|
dsql_rel* relation = NULL;
|
|
|
|
AutoCacheRequest handle(tdbb, irq_view, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE transaction)
|
|
X IN RDB$VIEW_RELATIONS WITH X.RDB$VIEW_NAME EQ view_name
|
|
{
|
|
fb_utils::exact_name(X.RDB$CONTEXT_NAME);
|
|
fb_utils::exact_name(X.RDB$RELATION_NAME);
|
|
|
|
if (!strcmp(X.RDB$RELATION_NAME, relation_or_alias) ||
|
|
!strcmp(X.RDB$CONTEXT_NAME, relation_or_alias))
|
|
{
|
|
relation = METD_get_relation(transaction, dsqlScratch, X.RDB$RELATION_NAME);
|
|
return relation;
|
|
}
|
|
|
|
relation = METD_get_view_relation(transaction, dsqlScratch, X.RDB$RELATION_NAME,
|
|
relation_or_alias);
|
|
|
|
if (relation)
|
|
return relation;
|
|
}
|
|
END_FOR
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void convert_dtype(dsql_fld* field, SSHORT field_type)
|
|
{
|
|
/**************************************
|
|
*
|
|
* c o n v e r t _ d t y p e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Convert from the blr_<type> stored in system metadata
|
|
* to the internal dtype_* descriptor. Also set field
|
|
* length.
|
|
*
|
|
**************************************/
|
|
|
|
// fill out the type descriptor
|
|
switch (field_type)
|
|
{
|
|
case blr_text:
|
|
field->fld_dtype = dtype_text;
|
|
break;
|
|
case blr_varying:
|
|
field->fld_dtype = dtype_varying;
|
|
field->fld_length += sizeof(USHORT);
|
|
break;
|
|
case blr_blob:
|
|
field->fld_dtype = dtype_blob;
|
|
field->fld_length = type_lengths[field->fld_dtype];
|
|
break;
|
|
default:
|
|
field->fld_dtype = gds_cvt_blr_dtype[field_type];
|
|
field->fld_length = type_lengths[field->fld_dtype];
|
|
|
|
fb_assert(field->fld_dtype != dtype_unknown);
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef NOT_USED_OR_REPLACED
|
|
static void free_procedure(dsql_prc* procedure)
|
|
{
|
|
/**************************************
|
|
*
|
|
* f r e e _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Free memory allocated for a procedure block and params
|
|
*
|
|
**************************************/
|
|
dsql_fld* param;
|
|
|
|
// release the input & output parameter blocks
|
|
|
|
for (param = procedure->prc_inputs; param;)
|
|
{
|
|
dsql_fld* temp = param;
|
|
param = param->fld_next;
|
|
delete temp;
|
|
}
|
|
|
|
for (param = procedure->prc_outputs; param;)
|
|
{
|
|
dsql_fld* temp = param;
|
|
param = param->fld_next;
|
|
delete temp;
|
|
}
|
|
|
|
// release the procedure & symbol blocks
|
|
|
|
delete procedure;
|
|
}
|
|
#endif // NOT_USED_OR_REPLACED
|
|
|
|
|
|
static void free_relation(dsql_rel* relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* f r e e _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Free memory allocated for a relation block and fields
|
|
*
|
|
**************************************/
|
|
|
|
// release the field blocks
|
|
|
|
for (dsql_fld* field = relation->rel_fields; field;)
|
|
{
|
|
dsql_fld* temp = field;
|
|
field = field->fld_next;
|
|
delete temp;
|
|
}
|
|
|
|
// release the relation & symbol blocks
|
|
|
|
delete relation;
|
|
}
|