/* * 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/sch_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; /* 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_procedure(dsql_prc*); static void free_relation(dsql_rel*); static void insert_symbol(dsql_dbb*, dsql_sym*); static dsql_sym* lookup_symbol(dsql_dbb*, USHORT, const char*, SYM_TYPE, USHORT = 0); static dsql_sym* lookup_symbol(dsql_dbb*, const dsql_str*, SYM_TYPE, USHORT = 0); #define DSQL_REQUEST(id) dbb->dbb_database->dbb_internal[id] namespace { class MutexHolder : public Database::CheckoutLockGuard { public: explicit MutexHolder(dsql_req* request) : CheckoutLockGuard(request->req_dbb->dbb_database, request->req_dbb->dbb_cache_mutex) {} private: // copying is prohibited MutexHolder(const MutexHolder&); MutexHolder& operator=(const MutexHolder&); }; inline void validateTransaction(const dsql_req* request) { if (!request->req_transaction->checkHandle()) { ERR_post(isc_bad_trans_handle, 0); } } } void METD_drop_collation(dsql_req* request, const dsql_str* 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. * **************************************/ MutexHolder holder(request); // If the symbol wasn't defined, we've got nothing to do dsql_sym* symbol = lookup_symbol(request->req_dbb, name, SYM_intlsym_collation, 0); if (symbol) { dsql_intlsym* intlsym = (dsql_intlsym*) symbol->sym_object; intlsym->intlsym_flags |= INTLSYM_dropped; } // mark other potential candidates as maybe dropped HSHD_set_flag(request->req_dbb, reinterpret_cast(name->str_data), name->str_length, SYM_intlsym_collation, INTLSYM_dropped); } void METD_drop_function(dsql_req* request, const dsql_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. * **************************************/ MutexHolder holder(request); // If the symbol wasn't defined, we've got nothing to do dsql_sym* symbol = lookup_symbol(request->req_dbb, name, SYM_udf); if (symbol) { dsql_udf* userFunc = (dsql_udf*) symbol->sym_object; userFunc->udf_flags |= UDF_dropped; } // mark other potential candidates as maybe dropped HSHD_set_flag(request->req_dbb, reinterpret_cast(name->str_data), name->str_length, SYM_udf, UDF_dropped); } void METD_drop_procedure(dsql_req* request, const dsql_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. * **************************************/ MutexHolder holder(request); // If the symbol wasn't defined, we've got nothing to do dsql_sym* symbol = lookup_symbol(request->req_dbb, name, SYM_procedure); if (symbol) { dsql_prc* 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); } void METD_drop_relation(dsql_req* request, const dsql_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. * **************************************/ MutexHolder holder(request); // If the symbol wasn't defined, we've got nothing to do dsql_sym* symbol = lookup_symbol(request->req_dbb, name, SYM_relation); if (symbol) { dsql_rel* 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); } dsql_intlsym* METD_get_collation(dsql_req* request, const dsql_str* 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. * **************************************/ MutexHolder holder(request); thread_db* tdbb = JRD_get_thread_data(); // Start by seeing if symbol is already defined dsql_sym* symbol = lookup_symbol(request->req_dbb, name, SYM_intlsym_collation, charset_id); if (symbol) return (dsql_intlsym*) symbol->sym_object; // Now see if it is in the database validateTransaction(request); dsql_dbb* dbb = request->req_dbb; dsql_intlsym* iname = NULL; jrd_req* handle = CMP_find_request(tdbb, irq_collation, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_transaction) X IN RDB$COLLATIONS CROSS Y IN RDB$CHARACTER_SETS OVER RDB$CHARACTER_SET_ID WITH X.RDB$COLLATION_NAME EQ name->str_data AND X.RDB$CHARACTER_SET_ID EQ charset_id; if (!DSQL_REQUEST(irq_collation)) DSQL_REQUEST(irq_collation) = handle; iname = FB_NEW_RPT(dbb->dbb_pool, name->str_length) dsql_intlsym; strcpy(iname->intlsym_name, (TEXT *) name->str_data); 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); END_FOR if (!DSQL_REQUEST(irq_collation)) DSQL_REQUEST(irq_collation) = handle; 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_collation; symbol->sym_dbb = dbb; insert_symbol(request->req_dbb, symbol); return iname; } USHORT METD_get_col_default(dsql_req* request, 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(request); dsql_dbb* dbb = request->req_dbb; bid* blob_id; USHORT length, result = 0; blb* blob_handle = 0; *has_default = false; jrd_req* handle = CMP_find_request(tdbb, irq_col_default, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_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 (!DSQL_REQUEST(irq_col_default)) DSQL_REQUEST(irq_col_default) = handle; 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, request->req_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) { 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) Firebird::status_exception::raise(isc_segment, 0); else continue; } ISC_STATUS* const old_status = tdbb->tdbb_status_vector; ISC_STATUS_ARRAY status_vector = {0}; tdbb->tdbb_status_vector = status_vector; try { BLB_close(tdbb, blob_handle); blob_handle = NULL; } catch (Firebird::Exception&) { } tdbb->tdbb_status_vector = old_status; // 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 (request->req_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 if (!DSQL_REQUEST(irq_col_default)) DSQL_REQUEST(irq_col_default) = handle; return result; } dsql_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. * **************************************/ MutexHolder holder(request); thread_db* tdbb = JRD_get_thread_data(); // Start by seeing if symbol is already defined dsql_sym* symbol = lookup_symbol(request->req_dbb, length, name, SYM_intlsym_charset); if (symbol) return (dsql_intlsym*) symbol->sym_object; // Now see if it is in the database validateTransaction(request); dsql_dbb* dbb = request->req_dbb; dsql_intlsym* iname = NULL; jrd_req* handle = CMP_find_request(tdbb, irq_charset, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_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; if (!DSQL_REQUEST(irq_charset)) DSQL_REQUEST(irq_charset) = handle; iname = FB_NEW_RPT(dbb->dbb_pool, length) dsql_intlsym; strcpy(iname->intlsym_name, name); 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); END_FOR if (!DSQL_REQUEST(irq_charset)) DSQL_REQUEST(irq_charset) = handle; 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_charset; symbol->sym_dbb = dbb; insert_symbol(request->req_dbb, symbol); dbb->dbb_charsets_by_id.add(iname); 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. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); dsql_dbb* dbb = request->req_dbb; USHORT bpc = 0; if (charset_id == CS_dynamic) charset_id = tdbb->getAttachment()->att_charset; dsql_intlsym* cs_sym = 0; size_t pos = 0; if (dbb->dbb_charsets_by_id.find(charset_id, pos)) { cs_sym = dbb->dbb_charsets_by_id[pos]; } else { const Firebird::MetaName cs_name = METD_get_charset_name(request, charset_id); cs_sym = METD_get_charset(request, cs_name.length(), cs_name.c_str()); } fb_assert(cs_sym); return cs_sym ? cs_sym->intlsym_bytes_per_char : 0; } Firebird::MetaName METD_get_charset_name(dsql_req* request, 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(); Firebird::MetaName name; dsql_dbb* dbb = request->req_dbb; if (charset_id == CS_dynamic) charset_id = tdbb->getAttachment()->att_charset; size_t pos = 0; if (dbb->dbb_charsets_by_id.find(charset_id, pos)) { return dbb->dbb_charsets_by_id[pos]->intlsym_name; } validateTransaction(request); jrd_req* handle = CMP_find_request(tdbb, irq_cs_name, IRQ_REQUESTS); FOR (REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_transaction) Y IN RDB$CHARACTER_SETS WITH Y.RDB$CHARACTER_SET_ID EQ charset_id if (!DSQL_REQUEST(irq_cs_name)) DSQL_REQUEST(irq_cs_name) = handle; name = Y.RDB$CHARACTER_SET_NAME; END_FOR if (!DSQL_REQUEST(irq_cs_name)) DSQL_REQUEST(irq_cs_name) = handle; // put new charset into hash table if needed METD_get_charset(request, name.length(), name.c_str()); return name; } dsql_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 * **************************************/ thread_db* tdbb = JRD_get_thread_data(); dsql_dbb* dbb = request->req_dbb; 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 validateTransaction(request); jrd_req* handle = CMP_find_request(tdbb, irq_default_cs, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_transaction) FIRST 1 DBB IN RDB$DATABASE WITH DBB.RDB$CHARACTER_SET_NAME NOT MISSING; if (!DSQL_REQUEST(irq_default_cs)) DSQL_REQUEST(irq_default_cs) = handle; // 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 (!DSQL_REQUEST(irq_default_cs)) DSQL_REQUEST(irq_default_cs) = handle; if (!dbb->dbb_dfl_charset) dbb->dbb_no_charset = true; 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' * **************************************/ thread_db* tdbb = JRD_get_thread_data(); validateTransaction(request); bool found = false; dsql_dbb* dbb = request->req_dbb; jrd_req* handle = CMP_find_request(tdbb, irq_domain, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_transaction) FLX IN RDB$FIELDS WITH FLX.RDB$FIELD_NAME EQ name if (!DSQL_REQUEST(irq_domain)) DSQL_REQUEST(irq_domain) = handle; 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; 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 if (!DSQL_REQUEST(irq_domain)) DSQL_REQUEST(irq_domain) = handle; return found ? TRUE : FALSE; } USHORT METD_get_domain_default(dsql_req* request, 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(request); *has_default = false; dsql_dbb* dbb = request->req_dbb; USHORT result = 0; jrd_req* handle = CMP_find_request(tdbb, irq_domain_2, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_transaction) FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ domain_name if (!DSQL_REQUEST(irq_domain_2)) DSQL_REQUEST(irq_domain_2) = handle; 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) { ISC_STATUS stat = 0; blb* blob_handle = BLB_open2(tdbb, request->req_transaction, blob_id, sizeof(blr_bpb), blr_bpb, true); // fetch segments. Assume buffer is big enough. UCHAR* ptr_in_buffer = buffer; while (true) { 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) Firebird::status_exception::raise(isc_segment, 0); else continue; } ISC_STATUS* const old_status = tdbb->tdbb_status_vector; ISC_STATUS_ARRAY status_vector = {0}; tdbb->tdbb_status_vector = status_vector; try { BLB_close(tdbb, blob_handle); blob_handle = NULL; } catch (Firebird::Exception&) { } tdbb->tdbb_status_vector = old_status; /* 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 (request->req_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 if (!DSQL_REQUEST(irq_domain_2)) DSQL_REQUEST(irq_domain_2) = handle; return result; } bool METD_get_exception(dsql_req* request, 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(request); dsql_dbb* dbb = request->req_dbb; bool found = false; jrd_req* handle = CMP_find_request(tdbb, irq_exception, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_transaction) X IN RDB$EXCEPTIONS WITH X.RDB$EXCEPTION_NAME EQ name->str_data; if (!DSQL_REQUEST(irq_exception)) DSQL_REQUEST(irq_exception) = handle; found = true; END_FOR if (!DSQL_REQUEST(irq_exception)) DSQL_REQUEST(irq_exception) = handle; return found; } dsql_udf* METD_get_function(dsql_req* request, const dsql_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. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); MutexHolder holder(request); // Start by seeing if symbol is already defined dsql_sym* symbol = lookup_symbol(request->req_dbb, name, SYM_udf); if (symbol) return (dsql_udf*) symbol->sym_object; // Now see if it is in the database validateTransaction(request); dsql_dbb* dbb = request->req_dbb; dsql_udf* userFunc = NULL; USHORT return_arg; jrd_req* handle1 = CMP_find_request(tdbb, irq_function, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE request->req_transaction) X IN RDB$FUNCTIONS WITH X.RDB$FUNCTION_NAME EQ name->str_data if (!DSQL_REQUEST(irq_function)) DSQL_REQUEST(irq_function) = handle1; userFunc = FB_NEW(dbb->dbb_pool) dsql_udf(dbb->dbb_pool); /* 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 userFunc->udf_next = dbb->dbb_functions; dbb->dbb_functions = userFunc; */ userFunc->udf_name = name->str_data; return_arg = X.RDB$RETURN_ARGUMENT; END_FOR if (!DSQL_REQUEST(irq_function)) DSQL_REQUEST(irq_function) = handle1; if (!userFunc) { 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_internal[ird_func_return], // the one that is appropriate for the database we are // working against. jrd_req* handle2 = CMP_find_request(tdbb, irq_func_return, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE request->req_transaction) X IN RDB$FUNCTION_ARGUMENTS WITH X.RDB$FUNCTION_NAME EQ name->str_data SORTED BY X.RDB$ARGUMENT_POSITION if (!DSQL_REQUEST(irq_func_return)) DSQL_REQUEST(irq_func_return) = handle2; 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 if (!DSQL_REQUEST(irq_func_return)) DSQL_REQUEST(irq_func_return) = handle2; // 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); /* Since we could give up control due to the THREAD_EXIT and THREAD_ENTER * calls, another thread may have added the same udf in the mean time */ if ((symbol = lookup_symbol(request->req_dbb, name, SYM_udf))) { // Get rid of all the stuff we just read in. Use existing one delete userFunc; return (dsql_udf*) symbol->sym_object; } // Add udf in the front of the list. userFunc->udf_next = dbb->dbb_functions; dbb->dbb_functions = userFunc; // Store in the symbol table // The UDF_new_udf flag is not used, so nothing extra to check. symbol = userFunc->udf_symbol = FB_NEW_RPT(dbb->dbb_pool, 0) dsql_sym; symbol->sym_object = (BLK) userFunc; symbol->sym_string = userFunc->udf_name.c_str(); symbol->sym_length = userFunc->udf_name.length(); symbol->sym_type = SYM_udf; symbol->sym_dbb = dbb; insert_symbol(request->req_dbb, symbol); return userFunc; } dsql_nod* METD_get_primary_key(dsql_req* request, 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(request); dsql_dbb* dbb = request->req_dbb; DsqlNodStack stack; jrd_req* handle = CMP_find_request(tdbb, irq_primary_key, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_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 if (!DSQL_REQUEST(irq_primary_key)) DSQL_REQUEST(irq_primary_key) = handle; stack.push(MAKE_field_name(Y.RDB$FIELD_NAME)); END_FOR if (!DSQL_REQUEST(irq_primary_key)) DSQL_REQUEST(irq_primary_key) = handle; return (stack.getCount() ? MAKE_list(stack) : NULL); } dsql_prc* METD_get_procedure(dsql_req* request, const dsql_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 * **************************************/ thread_db* tdbb = JRD_get_thread_data(); dsql_dbb* dbb = request->req_dbb; dsql_fld* parameter; dsql_fld** ptr; dsql_prc* temp; SSHORT type, count, defaults; // see if the procedure is the one currently being defined in this request if (((temp = request->req_procedure) != NULL) && temp->prc_name == name->str_data) { return temp; } MutexHolder holder(request); // Start by seeing if symbol is already defined dsql_sym* symbol = lookup_symbol(request->req_dbb, name, SYM_procedure); if (symbol) return (dsql_prc*) symbol->sym_object; // now see if it is in the database validateTransaction(request); dsql_prc* procedure = NULL; jrd_req* handle1 = CMP_find_request(tdbb, irq_procedure, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE request->req_transaction) X IN RDB$PROCEDURES WITH X.RDB$PROCEDURE_NAME EQ name->str_data if (!DSQL_REQUEST(irq_procedure)) DSQL_REQUEST(irq_procedure) = handle1; 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 = name->str_data; procedure->prc_owner = X.RDB$OWNER_NAME; END_FOR if (!DSQL_REQUEST(irq_procedure)) DSQL_REQUEST(irq_procedure) = handle1; if (!procedure) { return NULL; } // Lookup parameter stuff for (type = 0; type < 2; type++) { if (type) ptr = &procedure->prc_outputs; else ptr = &procedure->prc_inputs; count = defaults = 0; jrd_req* handle2 = CMP_find_request(tdbb, irq_parameters, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE request->req_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 SORTED BY DESCENDING PR.RDB$PARAMETER_NUMBER if (!DSQL_REQUEST(irq_parameters)) DSQL_REQUEST(irq_parameters) = handle2; SSHORT pr_collation_id_null = TRUE; SSHORT pr_collation_id; SSHORT pr_default_value_null = TRUE; SSHORT pr_null_flag_null = TRUE; SSHORT pr_null_flag; bool pr_type_of = false; if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_version) >= ODS_11_1) { jrd_req* handle3 = CMP_find_request(tdbb, irq_parameters2, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle3 TRANSACTION_HANDLE request->req_transaction) PR2 IN RDB$PROCEDURE_PARAMETERS WITH PR2.RDB$PROCEDURE_NAME EQ PR.RDB$PROCEDURE_NAME AND PR2.RDB$PARAMETER_NAME EQ PR.RDB$PARAMETER_NAME if (!DSQL_REQUEST(irq_parameters2)) DSQL_REQUEST(irq_parameters2) = handle3; pr_collation_id_null = PR2.RDB$COLLATION_ID.NULL; pr_collation_id = PR2.RDB$COLLATION_ID; pr_default_value_null = PR2.RDB$DEFAULT_VALUE.NULL; pr_null_flag_null = PR2.RDB$NULL_FLAG.NULL; pr_null_flag = PR2.RDB$NULL_FLAG; if (!PR2.RDB$PARAMETER_MECHANISM.NULL && PR2.RDB$PARAMETER_MECHANISM == prm_mech_type_of) pr_type_of = true; END_FOR if (!DSQL_REQUEST(irq_parameters2)) DSQL_REQUEST(irq_parameters2) = handle3; } count++; // allocate the field block fb_utils::exact_name(PR.RDB$PARAMETER_NAME); fb_utils::exact_name(PR.RDB$FIELD_SOURCE); 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 (!DSQL_REQUEST(irq_parameters)) DSQL_REQUEST(irq_parameters) = handle2; if (type) procedure->prc_out_count = count; else { procedure->prc_in_count = count; procedure->prc_def_count = defaults; } } // 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 if ((symbol = lookup_symbol(request->req_dbb, name, SYM_procedure))) { // Get rid of all the stuff we just read in. Use existing one free_procedure(procedure); 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.c_str(); symbol->sym_length = procedure->prc_name.length(); symbol->sym_type = SYM_procedure; symbol->sym_dbb = dbb; insert_symbol(request->req_dbb, symbol); } return procedure; } dsql_rel* METD_get_relation(dsql_req* request, const dsql_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. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); dsql_dbb* dbb = request->req_dbb; dsql_fld* field; dsql_fld** ptr; dsql_rel* temp; // See if the relation is the one currently being defined in this request if (((temp = request->req_relation) != NULL) && temp->rel_name == name->str_data) { return temp; } MutexHolder holder(request); // Start by seeing if symbol is already defined dsql_sym* symbol = lookup_symbol(request->req_dbb, name, SYM_relation); if (symbol) return (dsql_rel*) symbol->sym_object; // If the relation id or any of the field ids have 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 validateTransaction(request); bool permanent = true; jrd_req* handle1 = CMP_find_request(tdbb, irq_rel_ids, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE request->req_transaction) REL IN RDB$RELATIONS CROSS RFR IN RDB$RELATION_FIELDS OVER RDB$RELATION_NAME WITH REL.RDB$RELATION_NAME EQ name->str_data AND (REL.RDB$RELATION_ID MISSING OR RFR.RDB$FIELD_ID MISSING) if (!DSQL_REQUEST(irq_rel_ids)) DSQL_REQUEST(irq_rel_ids) = handle1; permanent = false; END_FOR if (!DSQL_REQUEST(irq_rel_ids)) DSQL_REQUEST(irq_rel_ids) = handle1; // Now see if it is in the database MemoryPool& pool = permanent ? dbb->dbb_pool : *tdbb->getDefaultPool(); dsql_rel* relation = NULL; jrd_req* handle2 = CMP_find_request(tdbb, irq_relation, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE request->req_transaction) X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ name->str_data if (!DSQL_REQUEST(irq_relation)) DSQL_REQUEST(irq_relation) = handle2; 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(request)) { relation = FB_NEW(pool) dsql_rel(pool); } // fill out the relation information if (relation) { relation->rel_name = name->str_data; 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 (!DSQL_REQUEST(irq_relation)) DSQL_REQUEST(irq_relation) = handle2; if (!relation) { return NULL; } // Lookup field stuff ptr = &relation->rel_fields; jrd_req* handle3 = CMP_find_request(tdbb, irq_fields, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle3 TRANSACTION_HANDLE request->req_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->str_data SORTED BY RFR.RDB$FIELD_POSITION if (!DSQL_REQUEST(irq_fields)) DSQL_REQUEST(irq_fields) = handle3; // allocate the field block fb_utils::exact_name(RFR.RDB$FIELD_NAME); fb_utils::exact_name(RFR.RDB$FIELD_SOURCE); const int length = strlen(RFR.RDB$FIELD_NAME) + strlen(RFR.RDB$FIELD_SOURCE); // Allocate from default or permanent pool as appropriate 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(request)) 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 (!DSQL_REQUEST(irq_fields)) DSQL_REQUEST(irq_fields) = handle3; // Since we could give up control due to the THREAD_EXIT and THREAD_ENTER, // another thread may have added the same table in the mean time if ((symbol = lookup_symbol(request->req_dbb, name, SYM_relation))) { free_relation(relation); return (dsql_rel*) symbol->sym_object; } // Add relation to front of list if (permanent) { 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.c_str(); symbol->sym_length = relation->rel_name.length(); symbol->sym_type = SYM_relation; symbol->sym_dbb = dbb; insert_symbol(request->req_dbb, symbol); } else { relation->rel_flags |= REL_new_relation; } return relation; } bool METD_get_trigger(dsql_req* request, const dsql_str* name, dsql_str** relation, USHORT* trig_type) { /************************************** * * M E T D _ g e t _ t r i g g e r * ************************************** * * Functional description * Look up a trigger's relation and it's current type * **************************************/ thread_db* tdbb = JRD_get_thread_data(); validateTransaction(request); dsql_dbb* dbb = request->req_dbb; bool found = false; if (relation) *relation = NULL; jrd_req* handle = CMP_find_request(tdbb, irq_trigger, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_transaction) X IN RDB$TRIGGERS WITH X.RDB$TRIGGER_NAME EQ name->str_data if (!DSQL_REQUEST(irq_trigger)) DSQL_REQUEST(irq_trigger) = handle; found = true; *trig_type = X.RDB$TRIGGER_TYPE; if (!X.RDB$RELATION_NAME.NULL) { if (relation) { fb_utils::exact_name(X.RDB$RELATION_NAME); *relation = MAKE_string(X.RDB$RELATION_NAME, strlen(X.RDB$RELATION_NAME)); } } END_FOR if (!DSQL_REQUEST(irq_trigger)) DSQL_REQUEST(irq_trigger) = handle; return found; } bool METD_get_type(dsql_req* request, const dsql_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 * **************************************/ thread_db* tdbb = JRD_get_thread_data(); validateTransaction(request); dsql_dbb* dbb = request->req_dbb; bool found = false; jrd_req* handle = CMP_find_request(tdbb, irq_type, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_transaction) X IN RDB$TYPES WITH X.RDB$FIELD_NAME EQ field AND X.RDB$TYPE_NAME EQ name->str_data; if (!DSQL_REQUEST(irq_type)) DSQL_REQUEST(irq_type) = handle; found = true; *value = X.RDB$TYPE; END_FOR if (!DSQL_REQUEST(irq_type)) DSQL_REQUEST(irq_type) = handle; return found; } dsql_rel* METD_get_view_base(dsql_req* request, const char* view_name, // UTF-8 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(request); dsql_rel* relation = NULL; dsql_dbb* dbb = request->req_dbb; bool first = true; bool cont = true; MetaNamePairMap previousAux; fields.clear(); while (cont) { jrd_req* handle1 = CMP_find_request(tdbb, irq_view_base, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle1 TRANSACTION_HANDLE request->req_transaction) X IN RDB$VIEW_RELATIONS WITH X.RDB$VIEW_NAME EQ view_name if (!DSQL_REQUEST(irq_view_base)) DSQL_REQUEST(irq_view_base) = handle1; // return NULL if there is more than one context if (X.RDB$VIEW_CONTEXT != 1) { relation = NULL; cont = false; break; } fb_utils::exact_name(X.RDB$CONTEXT_NAME); fb_utils::exact_name(X.RDB$RELATION_NAME); dsql_str* relation_name = MAKE_string(X.RDB$RELATION_NAME, strlen(X.RDB$RELATION_NAME)); relation = METD_get_relation(request, relation_name); delete relation_name; Firebird::Array ambiguities; MetaNamePairMap currentAux; if (!relation) { cont = false; break; } jrd_req* handle2 = CMP_find_request(tdbb, irq_view_base_flds, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle2 TRANSACTION_HANDLE request->req_transaction) RFL IN RDB$RELATION_FIELDS WITH RFL.RDB$RELATION_NAME EQ X.RDB$VIEW_NAME if (!DSQL_REQUEST(irq_view_base_flds)) DSQL_REQUEST(irq_view_base_flds) = handle2; if (RFL.RDB$BASE_FIELD.NULL || RFL.RDB$FIELD_NAME.NULL) continue; const Firebird::MetaName baseField(RFL.RDB$BASE_FIELD); if (currentAux.exist(baseField)) ambiguities.add(baseField); else { const Firebird::MetaName fieldName(RFL.RDB$FIELD_NAME); if (first) { fields.put(fieldName, baseField); currentAux.put(baseField, fieldName); } else { Firebird::MetaName field; if (previousAux.get(fieldName, field)) { fields.put(field, baseField); currentAux.put(baseField, field); } } } END_FOR if (!DSQL_REQUEST(irq_view_base_flds)) DSQL_REQUEST(irq_view_base_flds) = handle2; for (const Firebird::MetaName* i = ambiguities.begin(); i != ambiguities.end(); ++i) { Firebird::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 (!DSQL_REQUEST(irq_view_base)) DSQL_REQUEST(irq_view_base) = handle1; } if (!relation) fields.clear(); return relation; } dsql_rel* METD_get_view_relation(dsql_req* request, const char* view_name, // UTF-8 const char* relation_or_alias) // UTF-8 { /************************************** * * 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(request); dsql_rel* relation = NULL; dsql_dbb* dbb = request->req_dbb; jrd_req* handle = CMP_find_request(tdbb, irq_view, IRQ_REQUESTS); FOR(REQUEST_HANDLE handle TRANSACTION_HANDLE request->req_transaction) X IN RDB$VIEW_RELATIONS WITH X.RDB$VIEW_NAME EQ view_name if (!DSQL_REQUEST(irq_view)) DSQL_REQUEST(irq_view) = handle; 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)) { dsql_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); if (relation) { return relation; } END_FOR if (!DSQL_REQUEST(irq_view)) DSQL_REQUEST(irq_view) = handle; 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 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]; fb_assert(field->fld_dtype != dtype_unknown); } } 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; dsql_fld* 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; dsql_fld* 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 void insert_symbol(dsql_dbb* dbb, dsql_sym* symbol) { /************************************** * * i n s e r t _ s y m b o l * ************************************** * * Functional description * Insert a symbol in the hash table and * inform the engine that we're using it. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); HSHD_insert(symbol); MET_dsql_cache_use(tdbb, symbol->sym_type, symbol->sym_string); } static dsql_sym* lookup_symbol(dsql_dbb* dbb, USHORT length, const char* name, SYM_TYPE type, USHORT charset_id) { /************************************** * * l o o k u p _ s y m b o l * ************************************** * * Functional description * Lookup a symbol in the hash table and * inform the engine that we're using it. * The engine may inform that the symbol * is obsolete, in this case mark it as * dropped and return NULL. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); dsql_intlsym* intlSym; dsql_prc* procedure; dsql_rel* relation; dsql_udf* userFunc; dsql_sym* symbol = HSHD_lookup(dbb, name, length, type, 0); for (; symbol; symbol = symbol->sym_homonym) { if (symbol->sym_type == type) { if (type == SYM_intlsym_charset) break; else if (type == SYM_intlsym_collation && (intlSym = (dsql_intlsym*) symbol->sym_object) && (!(intlSym->intlsym_flags & INTLSYM_dropped)) && (charset_id == 0 || intlSym->intlsym_charset_id == charset_id)) { break; } else if (type == SYM_procedure && (procedure = (dsql_prc*) symbol->sym_object) && (!(procedure->prc_flags & PRC_dropped))) { break; } else if (type == SYM_relation && (relation = (dsql_rel*) symbol->sym_object) && (!(relation->rel_flags & REL_dropped))) { break; } else if (type == SYM_udf && (userFunc = (dsql_udf*) symbol->sym_object) && (!(userFunc->udf_flags & UDF_dropped))) { break; } } } if (symbol) { bool obsolete = MET_dsql_cache_use(tdbb, type, name); if (obsolete) { switch (type) { case SYM_intlsym_collation: intlSym->intlsym_flags |= INTLSYM_dropped; break; case SYM_procedure: procedure->prc_flags |= PRC_dropped; break; case SYM_relation: relation->rel_flags |= REL_dropped; break; case SYM_udf: userFunc->udf_flags |= UDF_dropped; break; default: return symbol; } symbol = NULL; } } return symbol; } static dsql_sym* lookup_symbol(dsql_dbb* dbb, const dsql_str* name, SYM_TYPE type, USHORT charset_id) { /************************************** * * l o o k u p _ s y m b o l * ************************************** * * Functional description * Wrapper for the above function. * **************************************/ return lookup_symbol(dbb, name->str_length, reinterpret_cast(name->str_data), type, charset_id); }