/* * 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 #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 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_ 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; }