8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-26 10:43:03 +01:00
firebird-mirror/src/dsql/metd.epp
2003-10-16 08:51:06 +00:00

2013 lines
52 KiB
Plaintext

/*
* PROGRAM: Dynamic SQL runtime support
* MODULE: met.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.
*/
#include "firebird.h"
#include <string.h>
#include "../dsql/dsql.h"
#include "../jrd/gds.h"
#include "../jrd/align.h"
#include "../jrd/intl.h"
#include "../jrd/thd.h"
#include "../dsql/alld_proto.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/gds_proto.h"
#include "../jrd/sch_proto.h"
#include "../jrd/thd_proto.h"
#include "../jrd/constants.h"
/* 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. */
/**************************************************************
V4 Multi-threading changes.
-- direct calls to gds__ () & isc_ () entrypoints
THREAD_EXIT;
gds__ () or isc_ () call.
THREAD_ENTER;
-- calls through embedded GDML.
the following protocol will be used. Care should be taken if
nested FOR loops are added.
THREAD_EXIT; // last statment before FOR loop
FOR ...............
THREAD_ENTER; // First statment in FOR loop
.....some C code....
.....some C code....
THREAD_EXIT; // last statment in FOR loop
END_FOR;
THREAD_ENTER; // First statment after FOR loop
***************************************************************/
DATABASE DB = STATIC "yachts.lnk";
static const UCHAR blr_bpb[] = { isc_bpb_version1,
isc_bpb_source_type, 1, BLOB_blr,
isc_bpb_target_type, 1, BLOB_blr
};
static void check_array(DSQL_REQ, const SCHAR*, DSQL_FLD);
static void convert_dtype(DSQL_FLD, SSHORT);
static void free_function (UDF);
static void free_procedure(DSQL_PRC);
static void free_relation(DSQL_REL);
static TEXT* metd_exact_name(TEXT*);
#ifdef SUPERSERVER
static REC_MUTX_T rec_mutex; /* Recursive metadata mutex */
static USHORT rec_mutex_inited = 0;
static inline void metd_rec_lock()
{
if ( !rec_mutex_inited)
{
rec_mutex_inited = 1;
THD_rec_mutex_init (&rec_mutex);
}
THREAD_EXIT;
THD_rec_mutex_lock (&rec_mutex);
THREAD_ENTER;
}
static inline void metd_rec_unlock()
{
THD_rec_mutex_unlock (&rec_mutex);
}
#else
static inline void metd_rec_lock()
{
}
static inline void metd_rec_unlock()
{
}
#endif
void METD_drop_function (DSQL_REQ request, const str* 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.
*
**************************************/
UDF udf;
DSQL_SYM symbol;
metd_rec_lock();
/* If the symbol wasn't defined, we've got nothing to do */
symbol = HSHD_lookup(request->req_dbb,
reinterpret_cast<const TEXT*>(name->str_data),
name->str_length, SYM_udf, 0);
for (; symbol; symbol = symbol->sym_homonym) {
if ((symbol->sym_type == SYM_udf) &&
((udf = (UDF) symbol->sym_object) &&
(!(udf->udf_flags & UDF_dropped)))) {
break;
}
}
if (symbol) {
udf = (UDF) symbol->sym_object;
udf->udf_flags |= UDF_dropped;
}
/* mark other potential candidates as maybe dropped */
HSHD_set_flag(request->req_dbb,
reinterpret_cast<const TEXT*>(name->str_data),
name->str_length, SYM_udf, UDF_dropped);
metd_rec_unlock();
}
void METD_drop_procedure( DSQL_REQ request, const str* 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.
*
**************************************/
DSQL_PRC procedure;
DSQL_SYM symbol;
metd_rec_lock();
/* If the symbol wasn't defined, we've got nothing to do */
symbol = HSHD_lookup(request->req_dbb, (TEXT*)name->str_data,
name->str_length, SYM_procedure, 0);
for (; symbol; symbol = symbol->sym_homonym)
if ((symbol->sym_type == SYM_procedure) &&
((procedure = (DSQL_PRC) symbol->sym_object) &&
(!(procedure->prc_flags & PRC_dropped))))
break;
if (symbol) {
procedure = (DSQL_PRC) symbol->sym_object;
procedure->prc_flags |= PRC_dropped;
}
/* mark other potential candidates as maybe dropped */
HSHD_set_flag(request->req_dbb, (TEXT*)name->str_data, name->str_length,
SYM_procedure, PRC_dropped);
metd_rec_unlock();
}
void METD_drop_relation( DSQL_REQ request, const str* 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.
*
**************************************/
DSQL_REL relation;
DSQL_SYM symbol;
metd_rec_lock();
/* If the symbol wasn't defined, we've got nothing to do */
symbol = HSHD_lookup(request->req_dbb, (TEXT*)name->str_data,
name->str_length, SYM_relation, 0);
for (; symbol; symbol = symbol->sym_homonym)
if ((symbol->sym_type == SYM_relation) &&
((relation = (DSQL_REL) symbol->sym_object) &&
(!(relation->rel_flags & REL_dropped))))
break;
if (symbol) {
relation = (DSQL_REL) symbol->sym_object;
relation->rel_flags |= REL_dropped;
}
/* mark other potential candidates as maybe dropped */
HSHD_set_flag(request->req_dbb, (TEXT*)name->str_data, name->str_length,
SYM_relation, REL_dropped);
metd_rec_unlock();
}
INTLSYM METD_get_collation(DSQL_REQ request, const str* name)
{
/**************************************
*
* 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.
*
**************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
INTLSYM iname;
DSQL_SYM symbol;
/* Start by seeing if symbol is already defined */
symbol = HSHD_lookup(request->req_dbb, (TEXT*)name->str_data,
name->str_length, SYM_intlsym, 0);
for (; symbol; symbol = symbol->sym_homonym)
if ((symbol->sym_type == SYM_intlsym) &&
(((INTLSYM) symbol->sym_object)->intlsym_type ==
INTLSYM_collation)) return (INTLSYM) symbol->sym_object;
/* Now see if it is in the database */
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
iname = NULL;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_collation])
X IN RDB$COLLATIONS
CROSS Y IN RDB$CHARACTER_SETS OVER RDB$CHARACTER_SET_ID
WITH X.RDB$COLLATION_NAME EQ name->str_data;
THREAD_ENTER;
iname = FB_NEW_RPT(*dbb->dbb_pool, name->str_length) intlsym;
strcpy(iname->intlsym_name, (TEXT *) name->str_data);
iname->intlsym_type = INTLSYM_collation;
iname->intlsym_flags = 0;
iname->intlsym_charset_id = X.RDB$CHARACTER_SET_ID;
iname->intlsym_collate_id = X.RDB$COLLATION_ID;
iname->intlsym_ttype =
INTL_CS_COLL_TO_TTYPE(iname->intlsym_charset_id,
iname->intlsym_collate_id);
iname->intlsym_bytes_per_char =
(Y.RDB$BYTES_PER_CHARACTER.NULL) ? 1 : (Y.RDB$BYTES_PER_CHARACTER);
THREAD_EXIT;
END_FOR ON_ERROR
/* Assume V3 database */
END_ERROR;
THREAD_ENTER;
if (!iname)
return NULL;
/* Store in the symbol table */
symbol = iname->intlsym_symbol = FB_NEW_RPT(*dbb->dbb_pool, 0) dsql_sym;
symbol->sym_object = (BLK) iname;
symbol->sym_string = iname->intlsym_name;
symbol->sym_length = name->str_length;
symbol->sym_type = SYM_intlsym;
symbol->sym_dbb = dbb;
HSHD_insert(symbol);
return iname;
}
void METD_get_col_default(DSQL_REQ request,
const char* for_rel_name,
const char* for_col_name,
bool* has_default,
TEXT* 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.
*
**************************************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
ISC_STATUS_ARRAY status_vect;
ISC_QUAD *blob_id;
TEXT *ptr_in_buffer;
ISC_STATUS stat;
USHORT length;
isc_blob_handle blob_handle = NULL;
*has_default = false;
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
/* V4.x multi threading requirements */
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_col_default])
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
THREAD_ENTER;
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) {
/* open the blob */
THREAD_EXIT;
stat = isc_open_blob2(status_vect, &DB, &gds_trans,
&blob_handle, blob_id, sizeof(blr_bpb),
(UCHAR*)blr_bpb);
THREAD_ENTER;
if (stat)
ERRD_punt();
/* fetch segments. Assuming here that the buffer is big enough. */
ptr_in_buffer = buffer;
while (TRUE) {
THREAD_EXIT;
stat = isc_get_segment(status_vect, &blob_handle,
&length, buff_length, ptr_in_buffer);
THREAD_ENTER;
ptr_in_buffer = ptr_in_buffer + length;
buff_length = buff_length - length;
if (!stat)
continue;
else if (stat == gds_segstr_eof) {
/* null terminate the buffer */
*ptr_in_buffer = 0;
break;
}
else
ERRD_punt();
}
THREAD_EXIT;
isc_close_blob(status_vect, &blob_handle);
THREAD_ENTER;
/* the default string must be of the form:
blr_version4 blr_literal ..... blr_eoc */
assert((buffer[0] == blr_version4) || (buffer[0] == blr_version5));
assert(buffer[1] == blr_literal);
}
else {
if (request->req_dbb->dbb_db_SQL_dialect > SQL_DIALECT_V5)
buffer[0] = blr_version5;
else
buffer[0] = blr_version4;
buffer[1] = blr_eoc;
}
THREAD_EXIT;
END_FOR ON_ERROR ERRD_punt();
END_ERROR THREAD_ENTER;
}
INTLSYM METD_get_charset(DSQL_REQ request, 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.
*
**************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
INTLSYM iname;
DSQL_SYM symbol;
/* Start by seeing if symbol is already defined */
symbol = HSHD_lookup(request->req_dbb, (TEXT*) name, length, SYM_intlsym, 0);
for (; symbol; symbol = symbol->sym_homonym)
if ((symbol->sym_type == SYM_intlsym) &&
(((INTLSYM) (symbol->sym_object))->intlsym_type ==
INTLSYM_charset)) return (INTLSYM) symbol->sym_object;
/* Now see if it is in the database */
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
iname = NULL;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_charset])
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;
THREAD_ENTER;
iname = FB_NEW_RPT(*dbb->dbb_pool, length) intlsym;
strcpy(iname->intlsym_name, (char*) name);
iname->intlsym_type = INTLSYM_charset;
iname->intlsym_flags = 0;
iname->intlsym_charset_id = X.RDB$CHARACTER_SET_ID;
iname->intlsym_collate_id = X.RDB$COLLATION_ID;
iname->intlsym_ttype =
INTL_CS_COLL_TO_TTYPE(iname->intlsym_charset_id,
iname->intlsym_collate_id);
iname->intlsym_bytes_per_char =
(Y.RDB$BYTES_PER_CHARACTER.NULL) ? 1 : (Y.RDB$BYTES_PER_CHARACTER);
THREAD_EXIT;
END_FOR
ON_ERROR
/* Assume V3 database */
END_ERROR;
THREAD_ENTER;
if (!iname)
return NULL;
/* Store in the symbol table */
symbol = iname->intlsym_symbol = FB_NEW_RPT(*dbb->dbb_pool, 0) dsql_sym;
symbol->sym_object = (BLK) iname;
symbol->sym_string = iname->intlsym_name;
symbol->sym_length = length;
symbol->sym_type = SYM_intlsym;
symbol->sym_dbb = dbb;
HSHD_insert(symbol);
return iname;
}
USHORT METD_get_charset_bpc (DSQL_REQ request,
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.
*
**************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
isc_req_handle handle;
USHORT bpc = 0;
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
handle = 0;
THREAD_EXIT;
FOR (REQUEST_HANDLE handle)
Y IN RDB$CHARACTER_SETS
WITH Y.RDB$CHARACTER_SET_ID EQ charset_id
THREAD_ENTER;
bpc = (Y.RDB$BYTES_PER_CHARACTER.NULL) ? 1 :
(Y.RDB$BYTES_PER_CHARACTER);
THREAD_EXIT;
END_FOR
ON_ERROR
/* Assume V3 database */
END_ERROR;
/*************
THREAD_ENTER;
Un comment these if any code is added after the END_ERROR and
gds__release request ()
THREAD_EXIT;
*************/
isc_release_request (gds_status, &handle);
THREAD_ENTER;
return bpc;
}
STR METD_get_default_charset(DSQL_REQ request)
{
/**************************************
*
* 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
*
**************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
isc_req_handle handle;
UCHAR *str_;
USHORT length;
dbb = request->req_dbb;
if (dbb->dbb_flags & DBB_no_charset)
return NULL;
if (dbb->dbb_dfl_charset)
return request->req_dbb->dbb_dfl_charset;
/* Now see if it is in the database */
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
handle = 0;
THREAD_EXIT;
FOR(REQUEST_HANDLE handle)
FIRST 1 DBB IN RDB$DATABASE
WITH DBB.RDB$CHARACTER_SET_NAME NOT MISSING;
THREAD_ENTER;
/* Terminate ASCIIZ string on first trailing blank */
metd_exact_name(DBB.RDB$CHARACTER_SET_NAME);
length = strlen(DBB.RDB$CHARACTER_SET_NAME);
dbb->dbb_dfl_charset = FB_NEW_RPT(*dbb->dbb_pool, length) str;
dbb->dbb_dfl_charset->str_length = length;
dbb->dbb_dfl_charset->str_charset = NULL;
str_ = (UCHAR*) DBB.RDB$CHARACTER_SET_NAME;
for (char* p = dbb->dbb_dfl_charset->str_data; length; --length) {
*p++ = *str_++;
}
THREAD_EXIT;
END_FOR
ON_ERROR
/* Assume V3 database */
END_ERROR;
/*************
THREAD_ENTER;
Un comment these if any code is added after the END_ERROR and
gds__release request ()
THREAD_EXIT;
*************/
isc_release_request(gds_status, &handle);
THREAD_ENTER;
if (dbb->dbb_dfl_charset == NULL)
dbb->dbb_flags |= DBB_no_charset;
return dbb->dbb_dfl_charset;
}
USHORT METD_get_domain(DSQL_REQ request, 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'
*
**************************************/
bool found = false;
DBB dbb = request->req_dbb;
FRBRD* DB = dbb->dbb_database_handle;
FRBRD* gds_trans = request->req_trans;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_domain])
FLX IN RDB$FIELDS WITH FLX.RDB$FIELD_NAME EQ name
THREAD_ENTER;
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_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;
convert_dtype(field, FLX.RDB$FIELD_TYPE);
if (FLX.RDB$FIELD_TYPE == blr_blob) {
field->fld_seg_length = FLX.RDB$SEGMENT_LENGTH;
}
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
THREAD_ENTER;
return found ? TRUE : FALSE;
}
void METD_get_domain_default(DSQL_REQ request,
const TEXT* domain_name,
bool* has_default,
TEXT* 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.
*
**************************************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
ISC_STATUS_ARRAY status_vect;
ISC_QUAD *blob_id;
TEXT *ptr_in_buffer;
ISC_STATUS stat;
USHORT length;
isc_blob_handle blob_handle = NULL;
*has_default = false;
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
/* V4.x multi threading requirements */
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_domain_2])
FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ domain_name
THREAD_ENTER;
if (!FLD.RDB$DEFAULT_VALUE.NULL) {
blob_id = &FLD.RDB$DEFAULT_VALUE;
*has_default = true;
}
else
*has_default = false;
if (*has_default) {
/* open the blob */
THREAD_EXIT;
stat = isc_open_blob2(status_vect, &DB, &gds_trans,
&blob_handle, blob_id, sizeof(blr_bpb),
(UCHAR*)blr_bpb);
THREAD_ENTER;
if (stat)
ERRD_punt();
/* fetch segments. Assume buffer is big enough. */
ptr_in_buffer = buffer;
while (TRUE) {
THREAD_EXIT;
stat = isc_get_segment(status_vect,
&blob_handle,
&length, buff_length, ptr_in_buffer);
THREAD_ENTER;
ptr_in_buffer = ptr_in_buffer + length;
buff_length = buff_length - length;
if (!stat)
continue;
else if (stat == gds_segstr_eof) {
/* null terminate the buffer */
*ptr_in_buffer = 0;
break;
}
else
ERRD_punt();
}
THREAD_EXIT;
isc_close_blob(status_vect, &blob_handle);
THREAD_ENTER;
/* the default string must be of the form:
blr_version4 blr_literal ..... blr_eoc */
assert((buffer[0] == blr_version4) || (buffer[0] == blr_version5));
assert(buffer[1] == blr_literal);
}
else {
if (request->req_dbb->dbb_db_SQL_dialect > SQL_DIALECT_V5)
buffer[0] = blr_version5;
else
buffer[0] = blr_version4;
buffer[1] = blr_eoc;
}
THREAD_EXIT;
END_FOR
ON_ERROR
ERRD_punt();
END_ERROR
THREAD_ENTER;
}
UDF METD_get_function(DSQL_REQ request, const str* 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.
*
**************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
UDF udf_, temp;
USHORT return_arg, arg_count;
DSQL_SYM symbol;
DLLS stack, stack_temp;
DSQL_NOD udf_param_node, *ptr;
metd_rec_lock();
/* Start by seeing if symbol is already defined */
symbol = HSHD_lookup(request->req_dbb,
reinterpret_cast<const TEXT*>(name->str_data),
name->str_length, SYM_udf, 0);
if (symbol && (symbol->sym_type == SYM_udf) &&
((udf_ = (UDF) symbol->sym_object) &&
(!(udf_->udf_flags & UDF_dropped)))) {
metd_rec_unlock();
return (UDF) symbol->sym_object;
}
/* This was the old code that didn't do the loop above to check for the flag.
if (symbol) {
metd_rec_unlock();
return (UDF) symbol->sym_object;
}
*/
/* Now see if it is in the database */
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
udf_ = NULL;
stack = 0;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_function])
X IN RDB$FUNCTIONS WITH
X.RDB$FUNCTION_NAME EQ name->str_data
THREAD_ENTER;
udf_ = FB_NEW_RPT(*dbb->dbb_pool, name->str_length) udf;
/* Moved below as still can't say for sure it will be stored.
Following the same logic for MET_get_procedure and MET_get_relation
udf_->udf_next = dbb->dbb_functions;
dbb->dbb_functions = udf_;
*/
strcpy((char*) udf_->udf_name, (char*) name->str_data);
return_arg = X.RDB$RETURN_ARGUMENT;
udf_->udf_arguments = 0;
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
THREAD_ENTER;
if (!udf_) {
metd_rec_unlock();
return NULL;
}
/* Note: The following two requests differ in the fields which are
* new since ODS7 (DBB_v3 flag). One of the two requests
* here will be cached in dbb_requests [ird_func_return],
* the one that is appropriate for the database we are
* working against.
*/
THREAD_EXIT;
if (dbb->dbb_flags & DBB_v3) {
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_func_return])
X IN RDB$FUNCTION_ARGUMENTS WITH
X.RDB$FUNCTION_NAME EQ name->str_data AND
X.RDB$ARGUMENT_POSITION EQ return_arg
THREAD_ENTER;
udf_->udf_dtype = (X.RDB$FIELD_TYPE != blr_blob) ?
gds_cvt_blr_dtype[X.RDB$FIELD_TYPE] : dtype_blob;
udf_->udf_scale = X.RDB$FIELD_SCALE;
udf_->udf_sub_type = X.RDB$FIELD_SUB_TYPE;
udf_->udf_length = X.RDB$FIELD_LENGTH;
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
}
else { /* V4 or greater dbb */
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_func_return])
X IN RDB$FUNCTION_ARGUMENTS WITH
X.RDB$FUNCTION_NAME EQ name->str_data
SORTED BY X.RDB$ARGUMENT_POSITION
THREAD_ENTER;
if (X.RDB$ARGUMENT_POSITION == return_arg) {
udf_->udf_dtype = (X.RDB$FIELD_TYPE != blr_blob) ?
gds_cvt_blr_dtype[X.RDB$FIELD_TYPE] : dtype_blob;
udf_->udf_scale = X.RDB$FIELD_SCALE;
if (!X.RDB$FIELD_SUB_TYPE.NULL) {
udf_->udf_sub_type = X.RDB$FIELD_SUB_TYPE;
}
else {
udf_->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)
udf_->udf_length = sizeof (ISC_QUAD);
else
udf_->udf_length = X.RDB$FIELD_LENGTH;
if (!X.RDB$CHARACTER_SET_ID.NULL) {
udf_->udf_character_set_id = X.RDB$CHARACTER_SET_ID;
}
}
else {
DSC* d;
/* udf_param_node = MAKE_node (type_nod, 0); */
udf_param_node = FB_NEW_RPT(*dbb->dbb_pool, 0) dsql_nod;
d = &udf_param_node->nod_desc;
d->dsc_dtype = (X.RDB$FIELD_TYPE != blr_blob) ?
gds_cvt_blr_dtype [X.RDB$FIELD_TYPE] : dtype_blob;
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 = 0;
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;
}
LLS_PUSH (udf_param_node, &stack);
}
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
}
THREAD_ENTER;
/* Adjust the return type & length of the UDF to account for
* cstring & varying. While a UDF can return CSTRING, we convert it
* to CHAR for manipulation as CSTRING is not a SQL type.
* (Q: why not use varying?)
*/
if (udf_->udf_dtype == dtype_cstring) {
udf_->udf_dtype = dtype_text;
}
else if (udf_->udf_dtype == dtype_varying)
udf_->udf_length += sizeof(USHORT);
/* Can't call MAKE_list because it allocates an initial list node
in the default pool. */
for (stack_temp = stack, arg_count = 0; stack_temp; stack_temp = stack_temp->lls_next) {
++arg_count;
}
/* udf->udf_arguments = MAKE_node (nod_list, count); */
udf_->udf_arguments = FB_NEW_RPT(*dbb->dbb_pool, arg_count) dsql_nod;
udf_->udf_arguments->nod_type = nod_list;
udf_->udf_arguments->nod_count = arg_count;
ptr = udf_->udf_arguments->nod_arg + arg_count;
while (stack) {
*--ptr = (DSQL_NOD) LLS_POP (&stack);
}
/* Since we could give up control due to the THREAD_EXIT and THEAD_ENTER
* calls, another thread may have added the same udf in the mean time
*/
symbol = HSHD_lookup(request->req_dbb,
reinterpret_cast<const TEXT*>(name->str_data),
name->str_length, SYM_udf, 0);
for (; symbol; symbol = symbol->sym_homonym) {
if ((symbol->sym_type == SYM_udf) &&
((temp = (UDF) symbol->sym_object) &&
(!(temp->udf_flags & UDF_dropped)))) {
/* Get rid of all the stuff we just read in. Use existing one */
free_function (udf_);
metd_rec_unlock();
return (UDF) symbol->sym_object;
}
}
/* Add udf in the front of the list. */
udf_->udf_next = dbb->dbb_functions;
dbb->dbb_functions = udf_;
/* Store in the symbol table */
/* The UDF_new_udf flag is not used, so nothing extra to check. */
symbol = udf_->udf_symbol = FB_NEW_RPT(*dbb->dbb_pool, 0) dsql_sym;
symbol->sym_object = (BLK) udf_;
symbol->sym_string = udf_->udf_name;
symbol->sym_length = name->str_length;
symbol->sym_type = SYM_udf;
symbol->sym_dbb = dbb;
HSHD_insert(symbol);
metd_rec_unlock();
return udf_;
}
DSQL_NOD METD_get_primary_key(DSQL_REQ request, const 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.
*
**************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
DSQL_NOD list, field_node;
STR field_name;
USHORT count;
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
list = NULL;
count = 0;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_primary_key])
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
THREAD_ENTER;
if (!list)
list = MAKE_node(nod_list, (int) X.RDB$SEGMENT_COUNT);
field_name = MAKE_cstring(Y.RDB$FIELD_NAME);
field_node = MAKE_node(nod_field_name, (int) e_fln_count);
field_node->nod_arg[e_fln_name] = (DSQL_NOD) field_name;
list->nod_arg[count] = field_node;
count++;
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
THREAD_ENTER;
return list;
}
DSQL_PRC METD_get_procedure(DSQL_REQ request, const str* name)
{
/**************************************
*
* 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
*
**************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
DSQL_FLD parameter, *ptr;
DSQL_PRC procedure, temp;
DSQL_SYM symbol;
SSHORT type, count;
metd_rec_lock();
/* Start by seeing if symbol is already defined */
symbol = HSHD_lookup(request->req_dbb, (TEXT*) name->str_data,
name->str_length, SYM_procedure, 0);
for (; symbol; symbol = symbol->sym_homonym)
if ((symbol->sym_type == SYM_procedure) &&
((procedure = (DSQL_PRC) symbol->sym_object) &&
(!(procedure->prc_flags & PRC_dropped)))) {
metd_rec_unlock();
return (DSQL_PRC) symbol->sym_object;
}
/* see if the procedure is the one currently being defined in this request */
if (((temp = request->req_procedure) != NULL) &&
!strcmp((char*) temp->prc_name, (char*) name->str_data)) {
metd_rec_unlock();
return temp;
}
/* now see if it is in the database */
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
procedure = NULL;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_procedure])
X IN RDB$PROCEDURES WITH
X.RDB$PROCEDURE_NAME EQ name->str_data
THREAD_ENTER;
metd_exact_name(X.RDB$OWNER_NAME);
procedure = FB_NEW_RPT(*dbb->dbb_pool,
name->str_length + strlen(X.RDB$OWNER_NAME)) dsql_prc;
procedure->prc_id = X.RDB$PROCEDURE_ID;
procedure->prc_name = procedure->prc_data;
procedure->prc_owner = procedure->prc_data + name->str_length + 1;
strcpy(procedure->prc_name, (TEXT *) name->str_data);
strcpy(procedure->prc_owner, (TEXT *) X.RDB$OWNER_NAME);
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
THREAD_ENTER;
if (!procedure) {
metd_rec_unlock();
return NULL;
}
/* Lookup parameter stuff */
for (type = 0; type < 2; type++) {
if (type)
ptr = &procedure->prc_outputs;
else
ptr = &procedure->prc_inputs;
count = 0;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_parameters])
PR IN RDB$PROCEDURE_PARAMETERS CROSS
RFR IN RDB$FIELDS
WITH RFR.RDB$FIELD_NAME EQ PR.RDB$FIELD_SOURCE
AND PR.RDB$PROCEDURE_NAME EQ name->str_data
AND PR.RDB$PARAMETER_TYPE = type
SORTED BY DESCENDING PR.RDB$PARAMETER_NUMBER
THREAD_ENTER;
count++;
/* allocate the field block */
metd_exact_name(PR.RDB$PARAMETER_NAME);
parameter = FB_NEW_RPT(*dbb->dbb_pool,
strlen(PR.RDB$PARAMETER_NAME)) dsql_fld;
parameter->fld_next = *ptr;
*ptr = parameter;
/* get parameter information */
strcpy(parameter->fld_name, (char*) PR.RDB$PARAMETER_NAME);
parameter->fld_id = PR.RDB$PARAMETER_NUMBER;
parameter->fld_length = RFR.RDB$FIELD_LENGTH;
parameter->fld_scale = RFR.RDB$FIELD_SCALE;
parameter->fld_sub_type = RFR.RDB$FIELD_SUB_TYPE;
parameter->fld_procedure = procedure;
if (!RFR.RDB$CHARACTER_SET_ID.NULL)
parameter->fld_character_set_id = RFR.RDB$CHARACTER_SET_ID;
if (!RFR.RDB$COLLATION_ID.NULL)
parameter->fld_collation_id = RFR.RDB$COLLATION_ID;
convert_dtype(parameter, RFR.RDB$FIELD_TYPE);
if (!RFR.RDB$NULL_FLAG)
parameter->fld_flags |= FLD_nullable;
if (RFR.RDB$FIELD_TYPE == blr_blob) {
parameter->fld_seg_length = RFR.RDB$SEGMENT_LENGTH;
}
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
THREAD_ENTER;
if (type)
procedure->prc_out_count = count;
else
procedure->prc_in_count = count;
}
/* Since we could give up control due to the THREAD_EXIT and THEAD_ENTER
* calls, another thread may have added the same procedure in the mean time
*/
symbol = HSHD_lookup(request->req_dbb, (TEXT*) name->str_data,
name->str_length, SYM_procedure, 0);
for (; symbol; symbol = symbol->sym_homonym)
if ((symbol->sym_type == SYM_procedure) &&
((temp = (DSQL_PRC) symbol->sym_object) &&
(!(temp->prc_flags & PRC_dropped)))) {
/* Get rid of all the stuff we just read in. Use existing one */
free_procedure(procedure);
metd_rec_unlock();
return (DSQL_PRC) symbol->sym_object;
}
/* store in the symbol table unless the procedure is not yet committed */
/* CVC: This is strange, because PRC_new_procedure is never set. */
if (!(procedure->prc_flags & PRC_new_procedure)) {
/* add procedure in the front of the list */
procedure->prc_next = dbb->dbb_procedures;
dbb->dbb_procedures = procedure;
symbol = procedure->prc_symbol = FB_NEW_RPT(*dbb->dbb_pool, 0) dsql_sym;
symbol->sym_object = (BLK) procedure;
symbol->sym_string = procedure->prc_name;
symbol->sym_length = name->str_length;
symbol->sym_type = SYM_procedure;
symbol->sym_dbb = dbb;
HSHD_insert(symbol);
}
metd_rec_unlock();
return procedure;
}
DSQL_REL METD_get_relation(DSQL_REQ request, const str* 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.
*
**************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
DSQL_FLD field, *ptr;
DSQL_REL relation, temp;
DSQL_SYM symbol;
TSQL tdsql;
tdsql = GET_THREAD_DATA;
metd_rec_lock();
/* Start by seeing if symbol is already defined */
symbol = HSHD_lookup(request->req_dbb, (TEXT*) name->str_data,
name->str_length, SYM_relation, 0);
for (; symbol; symbol = symbol->sym_homonym)
if ((symbol->sym_type == SYM_relation) &&
((relation = (DSQL_REL) symbol->sym_object) &&
(!(relation->rel_flags & REL_dropped)))) {
metd_rec_unlock();
return (DSQL_REL) symbol->sym_object;
}
/* see if the relation is the one currently being defined in this request */
if (((temp = request->req_relation) != NULL) &&
!strcmp((char*) temp->rel_name, (char*) name->str_data)) {
metd_rec_unlock();
return temp;
}
/* now see if it is in the database */
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
relation = NULL;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_relation])
X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ name->str_data
/* allocate the relation block -- if the relation id has not yet
been assigned, and this is a type of request which does not
use ids, prepare a temporary relation block to provide
information without caching it */
THREAD_ENTER;
metd_exact_name(X.RDB$OWNER_NAME);
if (!X.RDB$RELATION_ID.NULL) {
relation = FB_NEW_RPT(*dbb->dbb_pool,
name->str_length + strlen(X.RDB$OWNER_NAME)) dsql_rel;
relation->rel_id = X.RDB$RELATION_ID;
}
else if (!DDL_ids(request)) {
relation =
FB_NEW_RPT(*tdsql->tsql_default,
name->str_length + strlen(X.RDB$OWNER_NAME)) dsql_rel;
relation->rel_flags |= REL_new_relation;
}
/* fill out the relation information */
if (relation) {
relation->rel_name = relation->rel_data;
relation->rel_owner = relation->rel_data + name->str_length + 1;
strcpy(relation->rel_name, (TEXT *) name->str_data);
strcpy(relation->rel_owner, (TEXT *) 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;
}
}
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
THREAD_ENTER;
if (!relation) {
metd_rec_unlock();
return NULL;
}
/* Lookup field stuff */
ptr = &relation->rel_fields;
THREAD_EXIT;
if (dbb->dbb_flags & DBB_v3) {
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_fields])
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->str_data
SORTED BY RFR.RDB$FIELD_POSITION
THREAD_ENTER;
/* allocate the field block */
metd_exact_name(RFR.RDB$FIELD_NAME);
/* Allocate from default or permanent pool as appropriate */
if (relation->rel_flags & REL_new_relation)
*ptr = field = FB_NEW_RPT(*tdsql->tsql_default,
strlen(RFR.RDB$FIELD_NAME)) dsql_fld;
else
*ptr = field = FB_NEW_RPT(*dbb->dbb_pool,
strlen(RFR.RDB$FIELD_NAME)) dsql_fld;
ptr = &field->fld_next;
/* get field information */
strcpy(field->fld_name, (char*) RFR.RDB$FIELD_NAME);
field->fld_id = RFR.RDB$FIELD_ID;
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 (!(dbb->dbb_flags & DBB_no_arrays))
check_array(request, FLX.RDB$FIELD_NAME, field);
field->fld_flags |= FLD_nullable;
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
}
else { /* V4 ODS8 dbb */
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_fields])
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->str_data
SORTED BY RFR.RDB$FIELD_POSITION
THREAD_ENTER;
/* allocate the field block */
metd_exact_name(RFR.RDB$FIELD_NAME);
/* Allocate from default or permanent pool as appropriate */
if (relation->rel_flags & REL_new_relation)
*ptr = field = FB_NEW_RPT(*tdsql->tsql_default,
strlen(RFR.RDB$FIELD_NAME)) dsql_fld;
else
*ptr = field = FB_NEW_RPT(*dbb->dbb_pool,
strlen(RFR.RDB$FIELD_NAME)) dsql_fld;
ptr = &field->fld_next;
/* get field information */
strcpy(field->fld_name, (char*) RFR.RDB$FIELD_NAME);
field->fld_id = RFR.RDB$FIELD_ID;
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(GDS__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)
/*XXX:IOL || (relation->rel_flags & REL_view)*/) {
field->fld_flags |= FLD_nullable;
}
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
}
THREAD_ENTER;
/* Since we could give up control due to the THREAD_EXIT and THEAD_ENTER,
* another thread may have added the same table in the mean time
*/
symbol = HSHD_lookup(request->req_dbb, (TEXT*) name->str_data,
name->str_length, SYM_relation, 0);
for (; symbol; symbol = symbol->sym_homonym)
if ((symbol->sym_type == SYM_relation) &&
((temp = (DSQL_REL) symbol->sym_object) &&
(!(temp->rel_flags & REL_dropped)))) {
free_relation(relation);
metd_rec_unlock();
return (DSQL_REL) symbol->sym_object;
}
/* Add relation to front of list */
if (!(relation->rel_flags & REL_new_relation)) {
relation->rel_next = dbb->dbb_relations;
dbb->dbb_relations = relation;
/* store in the symbol table unless the relation is not yet committed */
symbol = relation->rel_symbol = FB_NEW_RPT(*dbb->dbb_pool, 0) dsql_sym;
symbol->sym_object = (BLK) relation;
symbol->sym_string = relation->rel_name;
symbol->sym_length = name->str_length;
symbol->sym_type = SYM_relation;
symbol->sym_dbb = dbb;
HSHD_insert(symbol);
}
metd_rec_unlock();
return relation;
}
STR METD_get_trigger_relation(DSQL_REQ request, const str* name, USHORT* trig_type)
{
/**************************************
*
* M E T D _ g e t _ t r i g g e r _ r e l a t i o n
*
**************************************
*
* Functional description
* Look up a trigger's relation and return it's current type
*
**************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
STR relation;
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
relation = NULL;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_trigger])
X IN RDB$TRIGGERS WITH X.RDB$TRIGGER_NAME EQ name->str_data
THREAD_ENTER;
metd_exact_name(X.RDB$RELATION_NAME);
relation = MAKE_string(X.RDB$RELATION_NAME, strlen(X.RDB$RELATION_NAME));
*trig_type = X.RDB$TRIGGER_TYPE;
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
THREAD_ENTER;
return relation;
}
USHORT METD_get_type(DSQL_REQ request, const str* name, 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
*
**************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
USHORT found;
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
found = FALSE;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_type])
X IN RDB$TYPES WITH
X.RDB$FIELD_NAME EQ field AND X.RDB$TYPE_NAME EQ name->str_data;
THREAD_ENTER;
found = TRUE;
*value = X.RDB$TYPE;
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
THREAD_ENTER;
return found;
}
DSQL_REL METD_get_view_relation(DSQL_REQ request,
const char* view_name /* UTF-8 */,
const char* relation_or_alias /* UTF-8 */,
USHORT level)
{
/**************************************
*
* 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).
*
**************************************/
DSQL_REL relation = 0;
DBB dbb = request->req_dbb;
FRBRD* DB = dbb->dbb_database_handle;
FRBRD* gds_trans = request->req_trans;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_view], LEVEL level)
X IN RDB$VIEW_RELATIONS WITH X.RDB$VIEW_NAME EQ view_name
THREAD_ENTER;
metd_exact_name(X.RDB$CONTEXT_NAME);
metd_exact_name(X.RDB$RELATION_NAME);
if (!strcmp(X.RDB$RELATION_NAME, relation_or_alias) ||
!strcmp(X.RDB$CONTEXT_NAME, relation_or_alias))
{
STR relation_name = MAKE_string(X.RDB$RELATION_NAME,
strlen(X.RDB$RELATION_NAME));
relation = METD_get_relation(request, relation_name);
delete relation_name;
return relation;
}
relation =
METD_get_view_relation(request, X.RDB$RELATION_NAME,
relation_or_alias, (USHORT)level + 1);
if (relation) {
return relation;
}
THREAD_EXIT;
END_FOR
ON_ERROR
END_ERROR;
THREAD_ENTER;
return NULL;
}
static void check_array( DSQL_REQ request, const TEXT* field_name, DSQL_FLD field)
{
/**************************************
*
* c h e c k _ a r r a y
*
**************************************
*
* Functional description
* Try to get field dimensions (RDB$FIELD_DIMENSIONS may not be
* defined in the meta-data). If we're not successful, indicate
* so in the database block to avoid wasting further effort.
*
**************************************/
FRBRD *DB, *gds_trans;
DBB dbb;
dbb = request->req_dbb;
DB = dbb->dbb_database_handle;
gds_trans = request->req_trans;
THREAD_EXIT;
FOR(REQUEST_HANDLE dbb->dbb_requests[irq_dimensions])
X IN RDB$FIELDS WITH X.RDB$FIELD_NAME EQ field_name
THREAD_ENTER;
if (!X.RDB$DIMENSIONS.NULL && X.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(GDS__QUAD);
field->fld_dimensions = X.RDB$DIMENSIONS;
}
THREAD_EXIT;
END_FOR
ON_ERROR
dbb->dbb_flags |= DBB_no_arrays;
END_ERROR;
THREAD_ENTER;
}
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 */
if (field_type == blr_text) {
field->fld_dtype = dtype_text;
}
else if (field_type == blr_varying) {
field->fld_dtype = dtype_varying;
field->fld_length += sizeof(USHORT);
}
else if (field_type == blr_blob) {
field->fld_dtype = dtype_blob;
field->fld_length = type_lengths[field->fld_dtype];
}
else {
field->fld_dtype = gds_cvt_blr_dtype[field_type];
field->fld_length = type_lengths[field->fld_dtype];
assert(field->fld_dtype != dtype_null);
}
}
static void free_function (UDF udf)
{
/**************************************
*
* f r e e _ f u n c t i o n
*
**************************************
*
* Functional description
* Free memory allocated for a function block and args
*
**************************************/
USHORT arg_count;
DSQL_NOD args, udf_argument;
/* release the arguments blocks */
if (udf->udf_arguments) {
args = udf->udf_arguments;
for (arg_count = 0; arg_count < args->nod_count; ++arg_count) {
udf_argument = args->nod_arg [arg_count];
delete udf_argument;
}
}
/* release the udf & symbol blocks */
delete udf;
}
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, temp;
/* release the input & output parameter blocks */
for (param = procedure->prc_inputs; param;) {
temp = param;
param = param->fld_next;
delete temp;
}
for (param = procedure->prc_outputs; param;) {
temp = param;
param = param->fld_next;
delete temp;
}
/* release the procedure & symbol blocks */
delete procedure;
}
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
*
**************************************/
DSQL_FLD field, temp;
/* release the field blocks */
for (field = relation->rel_fields; field;) {
temp = field;
field = field->fld_next;
delete temp;
}
/* release the relation & symbol blocks */
delete relation;
}
static TEXT* metd_exact_name( TEXT* str)
{
/**************************************
*
* m e t d _ e x a c t _ n a m e
*
**************************************
*
* Functional description
* Trim trailing spaces off a metadata name.
*
* SQL delimited identifier may have blank as part of the name
*
* Parameters: str - the string to terminate
* Returns: str
*
**************************************/
const char BLANK = '\040';
TEXT* q = str - 1;
for (TEXT* p = str; *p; p++) {
if (*p != BLANK)
q = p;
}
*(q + 1) = '\0';
return str;
}