/* * PROGRAM: JRD Command Oriented Query Language * MODULE: meta.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): ______________________________________. */ #include "firebird.h" #include #include #include "../qli/dtr.h" #include "../qli/compile.h" #include "../qli/exe.h" //#include "../jrd/license.h" #include "../jrd/flags.h" #include "../jrd/ibase.h" #include "../qli/reqs.h" #include "../qli/all_proto.h" #include "../qli/err_proto.h" #include "../qli/gener_proto.h" #include "../qli/hsh_proto.h" #include "../qli/meta_proto.h" #include "../gpre/prett_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/isc_f_proto.h" #include "../jrd/utl_proto.h" #include "../common/utils_proto.h" #include "../common/classes/ClumpletWriter.h" #include "../common/classes/UserBlob.h" using MsgFormat::SafeArg; #ifdef HAVE_UNISTD_H #include #endif DATABASE DB = FILENAME "yachts.lnk"; DATABASE DB1 = FILENAME "yachts.lnk"; static void add_field(qli_rel*, qli_fld*, USHORT); static void add_sql_field(qli_rel*, qli_fld*, USHORT, qli_rlb*); static void blob_copy(qli_rlb*, qli_rel*, ISC_QUAD&); static void change_field(qli_rel*, qli_fld*); static bool check_global_field(qli_dbb*, qli_fld*, const TEXT*); static bool check_relation(qli_rel*); static void clone_fields(qli_rel*, qli_rel*); static void clone_global_fields(qli_rel*, qli_rel*); static void define_global_field(qli_dbb*, qli_fld*, qli_symbol*); static void delete_fields(qli_rel*); static ISC_STATUS detach(ISC_STATUS *, qli_dbb*); static void execute_dynamic_ddl(qli_dbb*, qli_rlb*); static int field_length(USHORT, USHORT); static void get_database_type(qli_dbb*); static TEXT* get_query_header(qli_dbb*, ISC_QUAD&); static void install(qli_dbb*); static qli_syntax* make_node(nod_t, USHORT); static TEXT* make_string(TEXT*, SSHORT); static qli_symbol* make_symbol(TEXT*, SSHORT); static qli_const* missing_value(ISC_QUAD&, qli_symbol*); static qli_syntax* parse_blr(UCHAR**, qli_symbol*); static qli_syntax* parse_blr_blob(ISC_QUAD&, qli_symbol*); static void purge_relation(qli_rel*); static void put_dyn_string(qli_rlb*, const TEXT*); static void release_request(qli_dbb* db, FB_API_HANDLE& request_handle); static void rollback_update(qli_dbb*); static void set_capabilities(qli_dbb*); static qli_dbb* setup_update(qli_dbb*); static void sql_grant_revoke(qli_syntax*, USHORT); static void stuff_priv(qli_rlb*, USHORT, const TEXT*, USHORT, const TEXT*, const TEXT*); static int truncate_string(TEXT*); static const UCHAR tpb[] = { isc_tpb_version1, isc_tpb_write }; static const UCHAR db_info[] = { isc_info_implementation, isc_info_end }; static UCHAR global_parm_buffer[256]; // static const UCHAR dpb_trace [] = { isc_dpb_version1, isc_dpb_trace, 1, 1 }; // static const UCHAR dpb_num_buffers [ ] = { isc_dpb_version1, isc_dpb_num_buffers, 1, 1 }; #define STUFF_STRING(str) put_dyn_string (rlb, str) #define BLR_BYTE *blr++ static const USHORT blr_dtypes[] = { 0, blr_text, blr_text, blr_varying, 0, 0, 0, 0, blr_short, blr_long, blr_quad, blr_float, blr_double, blr_double, blr_sql_date, blr_sql_time, blr_timestamp, blr_blob, 0, blr_int64 }; // table used to determine capabilities, checking for specific // fields in system relations struct rfr_tab_t { const TEXT* relation; const TEXT* field; int bit_mask; }; static const rfr_tab_t rfr_table[] = { { "RDB$INDICES", "RDB$INDEX_INACTIVE", DBB_cap_idx_inactive }, // OBSOLETE - 1996-Aug-06 David Schnepper //{ "RDB$RELATIONS", "RDB$STORE_TRIGGER", DBB_cap_triggers }, { "RDB$RELATIONS", "RDB$EXTERNAL_FILE", DBB_cap_extern_file }, { "RDB$SECURITY_CLASSES", "RDB$SECURITY_CLASS", DBB_cap_security }, { "RDB$FILES", "RDB$FILE_NAME", DBB_cap_files }, { "RDB$FUNCTIONS", "RDB$FUNCTION_NAME", DBB_cap_functions }, { "RDB$TRIGGERS", "RDB$TRIGGER_BLR", DBB_cap_new_triggers }, { "RDB$CONSTRAINTS", "RDB$CONSTRAINT_NAME", DBB_cap_single_trans }, { "RDB$FILES", "RDB$SHADOW_NUMBER", DBB_cap_shadowing }, { "RDB$TYPES", "RDB$TYPE_NAME", DBB_cap_types }, { "RDB$FIELDS", "RDB$DIMENSIONS", DBB_cap_dimensions }, // { "RDB$FIELDS", "RDB$EXTERNAL_TYPE", DBB_cap_external_type }, Unused { "RDB$RELATION_FIELDS", "RDB$SYSTEM_FLAG", DBB_cap_rfr_sys_flag }, { "RDB$FILTERS", "RDB$FUNCTION_NAME", DBB_cap_filters }, { "RDB$INDICES", "RDB$INDEX_TYPE", DBB_cap_index_type }, { 0, 0, 0 } }; bool MET_declare( qli_dbb* database, qli_fld* variable, const qli_name* field_name) { /************************************** * * M E T _ d e c l a r e * ************************************** * * Functional description * Find a global field referenced in a DECLARE. * **************************************/ if (!database) database = QLI_databases; for (; database; database = database->dbb_next) { MET_meta_transaction(database, false); if (check_global_field(database, variable, field_name->nam_string)) return true; } return false; } void MET_define_field( qli_dbb* database, qli_fld* field) { /************************************** * * M E T _ d e f i n e _ f i e l d * ************************************** * * Functional description * Define a new global field. * **************************************/ database = setup_update(database); if (check_global_field(database, NULL, field->fld_name->sym_string)) { rollback_update(database); ERRQ_print_error(233, field->fld_name->sym_string); // Msg233 global field %s already exists } define_global_field(database, field, field->fld_name); MET_meta_commit(database); } void MET_define_index( qli_syntax* node) { /************************************** * * M E T _ d e f i n e _ i n d e x * ************************************** * * Functional description * Define a new index. * **************************************/ qli_symbol* symbol = (qli_symbol*) node->syn_arg[s_dfi_name]; qli_rel* relation = (qli_rel*) node->syn_arg[s_dfi_relation]; qli_syntax* fields = node->syn_arg[s_dfi_fields]; qli_dbb* database = setup_update(relation->rel_database); if (relation->rel_flags & REL_view) IBERROR(234); // Msg234 Can't define an index in a view FOR(REQUEST_HANDLE database->dbb_requests[REQ_def_index1]) X IN DB.RDB$INDICES WITH X.RDB$INDEX_NAME EQ symbol->sym_string ERRQ_print_error(235, symbol->sym_string); // Msg235 Index %s already exists END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; STORE(REQUEST_HANDLE database->dbb_requests[REQ_def_index2]) X IN DB.RDB$INDICES strcpy(X.RDB$RELATION_NAME, relation->rel_symbol->sym_string); strcpy(X.RDB$INDEX_NAME, symbol->sym_string); X.RDB$UNIQUE_FLAG = (node->syn_flags & s_dfi_flag_unique); X.RDB$INDEX_INACTIVE = (node->syn_flags & s_dfi_flag_inactive) ? TRUE : FALSE; X.RDB$SEGMENT_COUNT = fields->syn_count; X.RDB$INDEX_TYPE = (node->syn_flags & s_dfi_flag_descending) ? TRUE : FALSE; END_STORE ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; // at this point force garbage collection of any index segments left over // from previous failures. The system transaction (which might notice them // later does NOT recognize rolled back stuff! Should someone have actually // left an orphanned segment around, kill that too. FOR(REQUEST_HANDLE database->dbb_requests[REQ_scan_index]) X IN DB.RDB$INDEX_SEGMENTS WITH X.RDB$INDEX_NAME = symbol->sym_string ERASE X ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; END_FOR ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; qli_name** ptr = (qli_name**) fields->syn_arg; for (int position = 0; position < fields->syn_count; ptr++, position++) { MET_fields(relation); bool present = false; for (qli_fld* field = relation->rel_fields; field; field = field->fld_next) { if (!(strcmp((*ptr)->nam_string, field->fld_name->sym_string))) { present = true; break; } } if (!present) { rollback_update(database); ERRQ_print_error(236, SafeArg() << (*ptr)->nam_string << relation->rel_symbol->sym_string); // Msg236 Field %s does not occur in relation %s } setup_update(database); STORE(REQUEST_HANDLE database->dbb_requests[REQ_def_index3]) X IN DB.RDB$INDEX_SEGMENTS strcpy(X.RDB$INDEX_NAME, symbol->sym_string); strcpy(X.RDB$FIELD_NAME, (*ptr)->nam_string); X.RDB$FIELD_POSITION = position; END_STORE ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; } MET_meta_commit(database); } void MET_define_relation( qli_rel* relation, qli_rel* source) { /************************************** * * M E T _ d e f i n e _ r e l a t i o n * ************************************** * * Functional description * Define a new relation. There may * be field definitions here or we may * copy the field definitions from somebody * else. * **************************************/ qli_symbol* symbol = relation->rel_symbol; if (source) { source->rel_database = setup_update(source->rel_database); if (!(check_relation(source))) ERRQ_print_error(483, source->rel_symbol->sym_string); // Msg 483 Relation %s does not exist } qli_dbb* database = setup_update(relation->rel_database); relation->rel_database = database; if (check_relation(relation)) ERRQ_print_error(237, symbol->sym_string); // Msg237 Relation %s already exists STORE(REQUEST_HANDLE database->dbb_requests[REQ_store_relation]) X IN DB.RDB$RELATIONS strcpy(X.RDB$RELATION_NAME, symbol->sym_string); END_STORE ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; if (source) clone_fields(relation, source); else { USHORT position = 1; for (qli_fld* field = relation->rel_fields; field; field = field->fld_next, position++) { add_field(relation, field, position); } } MET_meta_commit(database); relation = (qli_rel*) ALLOCP(type_rel); relation->rel_next = database->dbb_relations; database->dbb_relations = relation; relation->rel_database = database; // Go back and pick up relation id setup_update(database); FOR(REQUEST_HANDLE database->dbb_requests[REQ_relation_id]) X IN DB.RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ symbol->sym_string relation->rel_id = X.RDB$RELATION_ID; symbol = make_symbol(X.RDB$RELATION_NAME, sizeof(X.RDB$RELATION_NAME)); symbol->sym_type = SYM_relation; symbol->sym_object = (BLK) relation; relation->rel_symbol = symbol; HSH_insert(symbol); END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; delete_fields(relation); MET_fields(relation); } void MET_define_sql_relation( qli_rel* relation) { /************************************** * * M E T _ d e f i n e _ s q l _ r e l a t i o n * ************************************** * * Functional description * Define a new SQL relation, using dynamic ddl. * **************************************/ qli_symbol* symbol = relation->rel_symbol; qli_dbb* database = setup_update(relation->rel_database); relation->rel_database = database; FOR(REQUEST_HANDLE database->dbb_requests[REQ_relation_def]) X IN DB.RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ symbol->sym_string ERRQ_print_error(237, symbol->sym_string); // Msg237 Relation %s already exists END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; qli_rlb* rlb = NULL; CHECK_RLB(rlb); STUFF(isc_dyn_version_1); STUFF(isc_dyn_begin); STUFF(isc_dyn_def_rel); STUFF_STRING(symbol->sym_string); STUFF(isc_dyn_rel_sql_protection); STUFF_WORD(2); STUFF_WORD(1); USHORT position = 1; for (qli_fld* field = relation->rel_fields; field; field = field->fld_next, position++) { add_sql_field(relation, field, position, rlb); } STUFF(isc_dyn_end); STUFF(isc_dyn_end); STUFF(isc_dyn_eoc); execute_dynamic_ddl(database, rlb); MET_meta_commit(database); relation = (qli_rel*) ALLOCP(type_rel); relation->rel_next = database->dbb_relations; database->dbb_relations = relation; relation->rel_database = database; // Go back and pick up relation id setup_update(database); FOR(REQUEST_HANDLE database->dbb_requests[REQ_relation_id]) X IN DB.RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ symbol->sym_string relation->rel_id = X.RDB$RELATION_ID; symbol = make_symbol(X.RDB$RELATION_NAME, sizeof(X.RDB$RELATION_NAME)); symbol->sym_type = SYM_relation; symbol->sym_object = (BLK) relation; relation->rel_symbol = symbol; HSH_insert(symbol); END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; delete_fields(relation); MET_fields(relation); } void MET_delete_database( qli_dbb* dbb) { /************************************** * * M E T _ d e l e t e _ d a t a b a s e * ************************************** * * Functional description * Drop an existing database, and all its files, * and finish any copies we have active. * **************************************/ // generate a database parameter block to include the user/password, if necessary const TEXT* user; USHORT user_length; if (dbb->dbb_user) { user = (TEXT*) dbb->dbb_user->con_data; user_length = dbb->dbb_user->con_desc.dsc_length; } else { user = QLI_default_user; user_length = strlen(QLI_default_user); } const TEXT* password; USHORT password_length; if (dbb->dbb_password) { password = (TEXT*) dbb->dbb_password->con_data; password_length = dbb->dbb_user->con_desc.dsc_length; } else { password = QLI_default_password; password_length = strlen(QLI_default_password); } Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1); if (user_length) { dpb.insertString(isc_dpb_user_name, user, user_length); } if (password_length) { dpb.insertString(isc_dpb_password, password, password_length); } ISC_STATUS_ARRAY status_vector; if (isc_attach_database(status_vector, 0, dbb->dbb_filename, &dbb->dbb_handle, dpb.getBufferLength(), reinterpret_cast(dpb.getBuffer()))) { ERRQ_database_error(dbb, status_vector); } qli_lls* stack = NULL; gds_trans = MET_transaction(nod_start_trans, dbb); DB = dbb->dbb_handle; FOR F IN DB.RDB$FILES SORTED BY F.RDB$FILE_START qli_str* astring = (qli_str*) ALLOCDV(type_str, strlen(F.RDB$FILE_NAME)); strcpy(astring->str_data, F.RDB$FILE_NAME); ALLQ_push((blk*) astring, &stack); END_FOR ON_ERROR ERRQ_database_error(dbb, gds_status); END_ERROR; MET_transaction(nod_commit, dbb); if (isc_detach_database(gds_status, &dbb->dbb_handle)) isc_print_status(gds_status); qli_dbb* next = NULL; for (qli_dbb* database = QLI_databases; database; database = next) { next = database->dbb_next; if (!strcmp(database->dbb_filename, dbb->dbb_filename)) MET_finish(database); } while (stack) { qli_str* sstring = (qli_str*) ALLQ_pop(&stack); if (unlink(sstring->str_data)) ERRQ_print_error(431, sstring->str_data); // Msg431 Could not drop database file "%s" } if (unlink(dbb->dbb_filename)) ERRQ_print_error(431, dbb->dbb_filename); // Msg431 Could not drop database file "%s" } void MET_delete_field( qli_dbb* database, qli_name* name) { /************************************** * * M E T _ d e l e t e _ f i e l d * ************************************** * * Functional description * Delete a global field. * **************************************/ database = setup_update(database); USHORT count = 0; FOR(REQUEST_HANDLE database->dbb_requests[REQ_check_fld]) RFR IN DB.RDB$RELATION_FIELDS WITH RFR.RDB$FIELD_SOURCE EQ name->nam_string REDUCED TO RFR.RDB$RELATION_NAME if (!count) ERRQ_msg_put(238, name->nam_string); // Msg238 Field %s is in use in the following relations: printf("\t%s\n", RFR.RDB$RELATION_NAME); count++; END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; if (count) ERRQ_print_error(239, SafeArg() << name->nam_string << database->dbb_filename); // Msg239 Field %s is in use in database %s FOR(REQUEST_HANDLE database->dbb_requests[REQ_erase_fld]) FLD IN DB.RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ name->nam_string qli_rlb* rlb = NULL; CHECK_RLB(rlb); STUFF(isc_dyn_version_1); STUFF(isc_dyn_delete_dimensions); STUFF_STRING(FLD.RDB$FIELD_NAME); STUFF(isc_dyn_end); STUFF(isc_dyn_eoc); execute_dynamic_ddl(database, rlb); ERASE FLD ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; count++; END_FOR ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; if (!count) ERRQ_print_error(240, SafeArg() << name->nam_string << database->dbb_filename); // Msg240 Field %s is not defined in database %s MET_meta_commit(database); } void MET_delete_index( qli_dbb* database, qli_name* name) { /************************************** * * M E T _ d e l e t e _ i n d e x * ************************************** * * Functional description * Delete an index. * **************************************/ database = setup_update(database); USHORT count = 0; FOR(REQUEST_HANDLE database->dbb_requests[REQ_erase_index]) IDX IN DB.RDB$INDICES WITH IDX.RDB$INDEX_NAME EQ name->nam_string count++; ERASE IDX ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; END_FOR ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; if (!count) ERRQ_print_error(241, SafeArg() << name->nam_string << database->dbb_filename); // Msg241 Index %s is not defined in database %s FOR(REQUEST_HANDLE database->dbb_requests[REQ_erase_segments]) SEG IN DB.RDB$INDEX_SEGMENTS WITH SEG.RDB$INDEX_NAME EQ name->nam_string ERASE SEG ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; END_FOR ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; MET_meta_commit(database); } void MET_delete_relation( qli_rel* relation) { /************************************** * * M E T _ d e l e t e _ r e l a t i o n * ************************************** * * Functional description * Delete a relation. * **************************************/ // Pass the mess off to dynamic DDL and let it worry qli_symbol* symbol = relation->rel_symbol; qli_rlb* rlb = NULL; CHECK_RLB(rlb); STUFF(isc_dyn_version_1); STUFF(isc_dyn_delete_rel); STUFF_STRING(symbol->sym_string); STUFF(isc_dyn_end); STUFF(isc_dyn_eoc); setup_update(relation->rel_database); execute_dynamic_ddl(relation->rel_database, rlb); MET_meta_commit(relation->rel_database); // Unlink and release various blocks purge_relation(relation); } int MET_dimensions( qli_dbb* database, const TEXT* field_name) { /************************************** * * M E T _ d i m e n s i o n s * ************************************** * * Functional description * Determine if the field has any dimensions. * **************************************/ if (!(database->dbb_capabilities & DBB_cap_dimensions)) return 0; USHORT dimensions = 0; FOR(REQUEST_HANDLE database->dbb_requests[REQ_fld_dimensions]) F IN DB.RDB$FIELDS WITH F.RDB$FIELD_NAME = field_name dimensions = F.RDB$DIMENSIONS; END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; return dimensions; } void MET_fields( qli_rel* relation) { /************************************** * * M E T _ f i e l d s * ************************************** * * Functional description * Get all fields for a relation. * **************************************/ // If we already have fetched the fields for the relation, don't do it again. if (relation->rel_flags & REL_fields) return; qli_dbb* database = relation->rel_database; MET_meta_transaction(database, false); relation->rel_flags |= REL_fields; qli_fld** ptr = &relation->rel_fields; FOR(REQUEST_HANDLE database->dbb_field_request) RFR IN DB.RDB$RELATION_FIELDS CROSS RFL IN DB.RDB$FIELDS WITH RFR.RDB$FIELD_SOURCE EQ RFL.RDB$FIELD_NAME AND RFR.RDB$RELATION_NAME EQ relation->rel_symbol->sym_string SORTED BY RFR.RDB$FIELD_POSITION, RFR.RDB$FIELD_NAME; if (RFR.RDB$FIELD_POSITION > relation->rel_max_field_pos) relation->rel_max_field_pos = RFR.RDB$FIELD_POSITION; qli_fld* field = (qli_fld*) ALLOCPV(type_fld, 0); *ptr = field; ptr = &field->fld_next; field->fld_relation = relation; qli_symbol* symbol = make_symbol(RFR.RDB$FIELD_NAME, sizeof(RFR.RDB$FIELD_NAME)); if (symbol) { symbol->sym_object = (BLK) field; symbol->sym_type = SYM_field; field->fld_name = symbol; } if ((symbol = make_symbol(RFR.RDB$QUERY_NAME, sizeof(RFR.RDB$QUERY_NAME))) || (symbol = make_symbol(RFL.RDB$QUERY_NAME, sizeof(RFL.RDB$QUERY_NAME)))) { symbol->sym_object = (BLK) field; symbol->sym_type = SYM_field; field->fld_query_name = symbol; } field->fld_scale = RFL.RDB$FIELD_SCALE; field->fld_id = RFR.RDB$FIELD_ID; if (RFL.RDB$SEGMENT_LENGTH.NULL) field->fld_segment_length = 80; else { field->fld_segment_length = ((RFL.RDB$SEGMENT_LENGTH) < 256 && (RFL.RDB$SEGMENT_LENGTH > 0)) ? RFL.RDB$SEGMENT_LENGTH : 255; } ISC_QUAD* blob = & RFR.RDB$QUERY_HEADER; if (UserBlob::blobIsNull(*blob)) blob = & RFL.RDB$QUERY_HEADER; if (!UserBlob::blobIsNull(*blob)) field->fld_query_header = get_query_header(database, *blob); blob = & RFL.RDB$COMPUTED_BLR; if (!UserBlob::blobIsNull(*blob)) field->fld_flags |= FLD_computed; field->fld_dtype = MET_get_datatype(RFL.RDB$FIELD_TYPE); field->fld_length = field_length(field->fld_dtype, RFL.RDB$FIELD_LENGTH); if (field->fld_dtype == dtype_varying) field->fld_length += sizeof(SSHORT); field->fld_sub_type = RFL.RDB$FIELD_SUB_TYPE; field->fld_sub_type_missing = RFL.RDB$FIELD_SUB_TYPE.NULL; if (!RFL.RDB$MISSING_VALUE.NULL) field->fld_missing = missing_value(RFL.RDB$MISSING_VALUE, field->fld_name); if (!(field->fld_edit_string = make_string(RFR.RDB$EDIT_STRING, sizeof(RFR.RDB$EDIT_STRING)))) { field->fld_edit_string = make_string(RFL.RDB$EDIT_STRING, sizeof(RFL.RDB$EDIT_STRING)); } field->fld_validation = parse_blr_blob(RFL.RDB$VALIDATION_BLR, field->fld_name); if (MET_dimensions(database, RFL.RDB$FIELD_NAME) > 0) field->fld_flags |= FLD_array; END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; } void MET_finish( qli_dbb* dbb) { /************************************** * * M E T _ f i n i s h * ************************************** * * Functional description * Finish a database and release all associated blocks. * **************************************/ // Get rid of relation and field blocks. qli_rel* relation; while (relation = dbb->dbb_relations) { purge_relation(relation); } // Get rid of any functions qli_fun* function; while (function = dbb->dbb_functions) { dbb->dbb_functions = function->fun_next; HSH_remove(function->fun_symbol); ALLQ_release((qli_frb*) function->fun_symbol); ALLQ_release((qli_frb*) function); } // Finally, actually close down database connection. ISC_STATUS_ARRAY status_vector; detach(status_vector, dbb); if (dbb->dbb_symbol) { HSH_remove(dbb->dbb_symbol); ALLQ_release((qli_frb*) dbb->dbb_symbol); } int count = 0; for (qli_dbb** ptr = &QLI_databases; *ptr; ptr = &(*ptr)->dbb_next) { if (*ptr == dbb) { *ptr = dbb->dbb_next; ALLQ_release((qli_frb*) dbb); count++; if (!*ptr) { break; } } } if (!count) ERRQ_bugcheck(231); // Msg231 database block not found for removal if (status_vector[1]) ERRQ_database_error(NULL, status_vector); } int MET_get_datatype( USHORT blr_datatype) { /************************************** * * M E T _ g e t _ d a t a t y p e * ************************************** * * Functional description * Convert a blr datatype to a QLI dtype. * **************************************/ USHORT retvalue; switch (blr_datatype) { case blr_text: retvalue = dtype_text; break; case blr_varying: retvalue = dtype_varying; break; case blr_cstring: retvalue = dtype_cstring; break; case blr_short: retvalue = dtype_short; break; case blr_long: retvalue = dtype_long; break; case blr_quad: retvalue = dtype_quad; break; case blr_float: retvalue = dtype_real; break; case blr_double: retvalue = dtype_double; break; case blr_timestamp: retvalue = dtype_timestamp; break; case blr_sql_date: retvalue = dtype_sql_date; break; case blr_sql_time: retvalue = dtype_sql_time; break; case blr_blob: retvalue = dtype_blob; break; case blr_int64: retvalue = dtype_int64; break; default: retvalue = dtype_unknown; break; } return retvalue; } #ifdef DEV_BUILD void MET_index_info(qli_dbb* db, const SCHAR* relation_name, const SCHAR* index_name, SCHAR* const buffer, size_t bufsize) { /************************************** * * M E T _ i n d e x _ i n f o * ************************************** * * Functional description * Get info about a particular index. * **************************************/ FB_API_HANDLE request_handle = 0; SCHAR* b = buffer; FOR(REQUEST_HANDLE request_handle) IDX IN DB.RDB$INDICES CROSS SEG IN DB.RDB$INDEX_SEGMENTS OVER RDB$INDEX_NAME WITH IDX.RDB$INDEX_NAME EQ index_name AND IDX.RDB$RELATION_NAME EQ relation_name SORTED BY SEG.RDB$FIELD_POSITION const SCHAR* p; if (b == buffer) { for (p = fb_utils::exact_name(IDX.RDB$INDEX_NAME); *p;) { *b++ = *p++; } *b++ = ' '; *b++ = '('; } p = fb_utils::exact_name(SEG.RDB$FIELD_NAME); if (p + strlen(p) + 2 > buffer + bufsize) break; while (*p) { *b++ = *p++; } *b++ = ' '; END_FOR ON_ERROR ERRQ_database_error(NULL, gds_status); END_ERROR; release_request(db, request_handle); // back up over the last space and finish off b--; *b++ = ')'; *b++ = 0; } #endif void MET_meta_commit( qli_dbb* database) { /************************************** * * M E T _ m e t a _ c o m m i t * ************************************** * * Functional description * Commit the meta data lookup & update * transaction. * **************************************/ if (database->dbb_capabilities & DBB_cap_multi_trans) { ISC_STATUS_ARRAY status_vector; if (isc_commit_transaction(status_vector, &database->dbb_meta_trans)) { rollback_update(database); ERRQ_database_error(database, status_vector); } } } void MET_meta_rollback( qli_dbb* database) { /************************************** * * M E T _ m e t a _ r o l l b a c k * ************************************** * * Functional description * Rollback the metadata transaction, * if there is one. * **************************************/ if (database->dbb_capabilities & DBB_cap_multi_trans) rollback_update(database); } FB_API_HANDLE MET_meta_transaction(qli_dbb* database, bool update_flag) { /************************************** * * M E T _ m e t a _ t r a n s a c t i o n * ************************************** * * Functional description * Setup transaction for meta-data operation. Set up * DB and gds_trans, and return meta-data transaction * handle for yucks. If we're doing metadata updates, * and lookups we'll use the metadat date transaction, * unless this is an rdb database or gateway in which * case, we'll use the only transacti. * * In any event, we will set up the met_transaction because * it's widely used and somebody is going to forget to test * that the database is multi_transaction before using it. * This means that we have to be very careful about committing * or rolling back, because this could affect user data. * **************************************/ ISC_STATUS_ARRAY status_vector; if (!database) IBERROR(243); // Msg243 no active database for operation FB_API_HANDLE transaction = (database->dbb_capabilities & DBB_cap_multi_trans) ? database->dbb_meta_trans : 0; DB = database->dbb_handle; // If we don't know whether or not the database can handle // multiple transactions, find out now if (!transaction && ((database->dbb_capabilities & DBB_cap_multi_trans) || !(database->dbb_capabilities & DBB_cap_single_trans))) { if (isc_start_transaction(status_vector, &transaction, 1, &database->dbb_handle, sizeof(tpb), tpb)) { database->dbb_capabilities |= DBB_cap_single_trans; } else { database->dbb_capabilities |= DBB_cap_multi_trans; } } // If we already have a meta-data transaction, there's more nothing to do gds_trans = transaction; // If we only support a single transaction, use the data transaction if (!gds_trans && (database->dbb_capabilities & DBB_cap_single_trans)) { if (update_flag) IBERROR(244); // Msg244 Interactive metadata updates are not available on Rdb if (!(gds_trans = database->dbb_transaction)) gds_trans = MET_transaction(nod_start_trans, database); } else if (!gds_trans) { // otherwise make one more effort to start the transaction START_TRANSACTION ON_ERROR ERRQ_database_error(database, status_vector); END_ERROR; } database->dbb_meta_trans = gds_trans; return gds_trans; } void MET_modify_field( qli_dbb* database, qli_fld* field) { /************************************** * * M E T _ m o d i f y _ f i e l d * ************************************** * * Functional description * Modify an existing global field. * **************************************/ database = setup_update(database); qli_symbol* field_name = field->fld_name; SSHORT flag = 0; FOR(REQUEST_HANDLE database->dbb_requests[REQ_modify_fld]) X IN DB.RDB$FIELDS WITH X.RDB$FIELD_NAME EQ field_name->sym_string if (field->fld_dtype && (X.RDB$FIELD_TYPE == blr_blob || blr_dtypes[field->fld_dtype] == blr_blob) && X.RDB$FIELD_TYPE != blr_dtypes[field->fld_dtype]) { flag = -1; } else { flag = 1; MODIFY X USING if (field->fld_dtype) { X.RDB$FIELD_TYPE = blr_dtypes[field->fld_dtype]; X.RDB$FIELD_SCALE = field->fld_scale; X.RDB$FIELD_LENGTH = (field->fld_dtype == dtype_varying) ? field->fld_length - sizeof(SSHORT) : field->fld_length; } if (!field->fld_sub_type_missing) { X.RDB$FIELD_SUB_TYPE.NULL = FALSE; X.RDB$FIELD_SUB_TYPE = field->fld_sub_type; } qli_symbol* asymbol = field->fld_query_name; if (asymbol) { X.RDB$QUERY_NAME.NULL = FALSE; strcpy(X.RDB$QUERY_NAME, asymbol->sym_string); } if (field->fld_edit_string) { X.RDB$EDIT_STRING.NULL = FALSE; strcpy(X.RDB$EDIT_STRING, field->fld_edit_string); } END_MODIFY ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; } END_FOR ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; if (flag <= 0) { rollback_update(database); // Msg245 global field %s is not defined // Msg468 Datatype of field %s may not be changed to or from blob const USHORT nErr = flag ? 468 : 245; ERRQ_print_error(nErr, field_name->sym_string); } MET_meta_commit(database); // Now go back and re-fetch fields for affected databases setup_update(database); FOR(REQUEST_HANDLE database->dbb_requests[REQ_update_fld]) X IN DB.RDB$RELATION_FIELDS WITH X.RDB$FIELD_SOURCE EQ field_name->sym_string REDUCED TO X.RDB$RELATION_NAME const TEXT* p = X.RDB$RELATION_NAME; while (*p && *p != ' ') { p++; } qli_symbol* symbol = HSH_lookup(X.RDB$RELATION_NAME, p - X.RDB$RELATION_NAME); for (; symbol; symbol = symbol->sym_homonym) { qli_rel* relation; if (symbol->sym_type == SYM_relation && (relation = (qli_rel*) symbol->sym_object) && relation->rel_database == database) { delete_fields(relation); MET_fields(relation); } } END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; } void MET_modify_index( qli_syntax* node) { /************************************** * * M E T _ m o d i f y _ i n d e x * ************************************** * * Functional description * Change the changeable options * of an index. * **************************************/ qli_name* name = (qli_name*) node->syn_arg[s_mfi_name]; qli_dbb* database = (qli_dbb*) node->syn_arg[s_mfi_database]; database = setup_update(database); USHORT count = 0; FOR(REQUEST_HANDLE database->dbb_requests[REQ_mdf_index]) X IN DB.RDB$INDICES WITH X.RDB$INDEX_NAME = name->nam_string MODIFY X USING if (node->syn_flags & s_dfi_flag_selectivity) X.RDB$UNIQUE_FLAG = (node->syn_flags & s_dfi_flag_unique); if (node->syn_flags & s_dfi_flag_activity) X.RDB$INDEX_INACTIVE = (node->syn_flags & s_dfi_flag_inactive) ? TRUE : FALSE; if (node->syn_flags & s_dfi_flag_order) X.RDB$INDEX_TYPE = (node->syn_flags & s_dfi_flag_descending) ? TRUE : FALSE; if (node->syn_flags & s_dfi_flag_statistics) X.RDB$STATISTICS = -1.0; END_MODIFY ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; count++; END_FOR ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; if (!count) ERRQ_print_error(246, SafeArg() << name->nam_string << database->dbb_filename); // Msg246 Index %s does not exist MET_meta_commit(database); } void MET_modify_relation( qli_rel* relation, qli_fld* fields) { /************************************** * * M E T _ m o d i f y _ r e l a t i o n * ************************************** * * Functional description * Modify an existing relation. * **************************************/ qli_dbb* database = setup_update(relation->rel_database); relation->rel_database = database; qli_symbol* relation_name = relation->rel_symbol; for (qli_fld* field = fields; field; field = field->fld_next) { if (field->fld_flags & FLD_drop) { USHORT count = 0; qli_symbol* field_name = field->fld_name; FOR(REQUEST_HANDLE database->dbb_requests[REQ_modify_rel]) X IN DB.RDB$RELATION_FIELDS WITH X.RDB$FIELD_NAME EQ field_name->sym_string AND X.RDB$RELATION_NAME EQ relation_name->sym_string count++; ERASE X ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; END_FOR ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; if (!count) { rollback_update(database); ERRQ_print_error(247, field_name->sym_string); // Msg247 field %s doesn't exist } } else if (field->fld_flags & FLD_modify) change_field(relation, field); else add_field(relation, field, 0); } MET_meta_commit(database); setup_update(database); delete_fields(relation); MET_fields(relation); } void MET_ready( qli_syntax* node, const bool create_flag) { /************************************** * * M E T _ r e a d y * ************************************** * * Functional description * Create or Ready one or more databases. If any * fail, all fail. Fair enough? * **************************************/ Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1); qli_dbb* temp = (qli_dbb*) node->syn_arg[0]; // Only a SQL CREATE DATABASE will have a pagesize parameter, // so only looking at the first pointer is justified. SSHORT length; const TEXT* lc_ctype = QLI_charset; if (lc_ctype && (length = strlen(lc_ctype))) { dpb.insertString(isc_dpb_lc_ctype, lc_ctype, length); } // It does absolutely nothing inside the engine these days: ignored. if (QLI_trace) { dpb.insertByte(isc_dpb_trace, 1); } if (sw_buffers) { dpb.insertInt(isc_dpb_num_buffers, ULONG(sw_buffers)); } if (temp->dbb_pagesize) { dpb.insertInt(isc_dpb_page_size, ULONG(temp->dbb_pagesize)); } // get a username, if specified globally or on the command line const TEXT* q; if (temp->dbb_user) { q = (TEXT*) temp->dbb_user->con_data; length = temp->dbb_user->con_desc.dsc_length; } else { q = QLI_default_user; length = strlen(QLI_default_user); } if (length) { dpb.insertString(isc_dpb_user_name, q, length); } // get a password, if specified if (temp->dbb_password) { q = (TEXT*) temp->dbb_password->con_data; length = temp->dbb_password->con_desc.dsc_length; } else { q = QLI_default_password; length = strlen(QLI_default_password); } if (length) { dpb.insertString(isc_dpb_password, q, length); } #ifdef TRUSTED_AUTH if (QLI_trusted) { dpb.insertTag(isc_dpb_trusted_auth); } #endif const qli_syntax* const* const end = node->syn_arg + node->syn_count; // Start by attaching all databases ISC_STATUS_ARRAY status_vector; qli_syntax** ptr; qli_dbb* dbb = NULL; for (ptr = node->syn_arg; ptr < end; ptr++) { dbb = (qli_dbb*) *ptr; if (create_flag) isc_create_database(status_vector, 0, dbb->dbb_filename, &dbb->dbb_handle, dpb.getBufferLength(), reinterpret_cast(dpb.getBuffer()), 0); else isc_attach_database(status_vector, 0, dbb->dbb_filename, &dbb->dbb_handle, dpb.getBufferLength(), reinterpret_cast(dpb.getBuffer())); if (status_vector[1]) break; } // If any attach failed, cleanup and give up if (ptr < end) { for (qli_syntax** ptr2 = node->syn_arg; ptr2 < ptr; ptr2++) detach(gds_status, (qli_dbb*) *ptr2); ERRQ_database_error(dbb, status_vector); } // Databases are up and running. Install each. for (ptr = node->syn_arg; ptr < end; ptr++) install((qli_dbb*) *ptr); } void MET_shutdown() { /************************************** * * M E T _ s h u t d o w n * ************************************** * * Functional description * Shut down all attached databases. * **************************************/ ISC_STATUS_ARRAY status_vector; for (qli_dbb* dbb = QLI_databases; dbb; dbb = dbb->dbb_next) { if (detach(status_vector, dbb)) isc_print_status(status_vector); } } void MET_sql_alter_table( qli_rel* relation, qli_fld* fields) { /************************************** * * M E T _ s q l _ a l t e r _ t a b l e * ************************************** * * Functional description * Alter table data based on a SQL metadata request * **************************************/ qli_symbol* symbol = relation->rel_symbol; qli_dbb* database = setup_update(relation->rel_database); relation->rel_database = database; USHORT count = 0; FOR(REQUEST_HANDLE database->dbb_requests[REQ_relation_def]) X IN DB.RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ symbol->sym_string count++; END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; if (!count) ERRQ_print_error(414, symbol->sym_string); // Msg237 Relation %s is lost qli_rlb* rlb = NULL; CHECK_RLB(rlb); STUFF(isc_dyn_version_1); STUFF(isc_dyn_begin); STUFF(isc_dyn_mod_rel); STUFF_STRING(symbol->sym_string); for (qli_fld* field = fields; field; field = field->fld_next) { if (field->fld_flags & FLD_add) add_sql_field(relation, field, 0, rlb); else if (field->fld_flags & FLD_drop) { STUFF(isc_dyn_delete_local_fld); STUFF_STRING(field->fld_name->sym_string); STUFF(isc_dyn_end); } } STUFF(isc_dyn_end); STUFF(isc_dyn_end); STUFF(isc_dyn_eoc); execute_dynamic_ddl(database, rlb); MET_meta_commit(database); setup_update(database); delete_fields(relation); MET_fields(relation); } void MET_sql_cr_view( qli_syntax* node) { /************************************** * * M E T _ s q l _ c r _ v i e w * ************************************** * * Functional description * Create a view from a SQL metadata request * **************************************/ qli_rel* relation = (qli_rel*) node->syn_arg[s_crv_name]; qli_dbb* database = setup_update(relation->rel_database); relation->rel_database = database; qli_symbol* symbol = relation->rel_symbol; qli_rlb* rlb = NULL; CHECK_RLB(rlb); STUFF(isc_dyn_version_1); STUFF(isc_dyn_begin); STUFF(isc_dyn_def_view); STUFF_STRING(symbol->sym_string); // The meat of the blr generation will go here STUFF(isc_dyn_end); STUFF(isc_dyn_eoc); execute_dynamic_ddl(database, rlb); MET_meta_commit(database); } void MET_sql_grant( qli_syntax* node) { /************************************** * * M E T _ s q l _ g r a n t * ************************************** * * Functional description * Grant access privilege(s) on a SQL table * **************************************/ sql_grant_revoke(node, isc_dyn_grant); } void MET_sql_revoke( qli_syntax* node) { /************************************** * * M E T _ s q l _ r e v o k e * ************************************** * * Functional description * Revoke access privilege(s) on a SQL table * **************************************/ sql_grant_revoke(node, isc_dyn_revoke); } FB_API_HANDLE MET_transaction( nod_t node_type, qli_dbb* database) { /************************************** * * M E T _ t r a n s a c t i o n * ************************************** * * Functional description * Finish off a transaction with a commit, prepare, or rollback. * For commit and rollback, start a new transaction. * * Multiple transaction coordination is handled * by the caller. This is a singleminded routine. * **************************************/ ISC_STATUS_ARRAY status_vector; switch (node_type) { case nod_commit: isc_commit_transaction(status_vector, &database->dbb_transaction); break; case nod_rollback: isc_rollback_transaction(status_vector, &database->dbb_transaction); break; case nod_prepare: isc_prepare_transaction(status_vector, &database->dbb_transaction); break; case nod_start_trans: isc_start_transaction(status_vector, &database->dbb_transaction, 1, &database->dbb_handle, sizeof(tpb), tpb); database->dbb_flags &= ~DBB_updates & ~DBB_prepared; break; } if (status_vector[1]) ERRQ_database_error(database, status_vector); return database->dbb_transaction; } static void add_field( qli_rel* relation, qli_fld* field, USHORT position) { /************************************** * * a d d _ f i e l d * ************************************** * * Functional description * Add a field to a relation. Do all sorts of sanity checks. * **************************************/ qli_dbb* database = relation->rel_database; qli_symbol* relation_name = relation->rel_symbol; qli_symbol* field_name = field->fld_name; qli_symbol* global_field = field->fld_based; if (!global_field) global_field = field_name; // Check to see if it already exits in relation FOR(REQUEST_HANDLE database->dbb_requests[REQ_rfr_def]) X IN DB.RDB$RELATION_FIELDS WITH X.RDB$RELATION_NAME EQ relation_name->sym_string AND X.RDB$FIELD_NAME EQ field_name->sym_string rollback_update(relation->rel_database); ERRQ_print_error(251, SafeArg() << field_name->sym_string << relation_name->sym_string); // Msg251 Field %s already exists in relation %s END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; // Check global field. Define it if it doesn't exist. bool global_flag = false; if (!check_global_field(database, field, global_field->sym_string)) { global_flag = true; define_global_field(database, field, global_field); } // Finally, store into RFR. If we defined a global field, assume that // the query name and edit string will be inherited from there, otherwise // include them here STORE(REQUEST_HANDLE database->dbb_requests[REQ_store_rfr]) X IN DB.RDB$RELATION_FIELDS strcpy(X.RDB$RELATION_NAME, relation_name->sym_string); strcpy(X.RDB$FIELD_NAME, field_name->sym_string); strcpy(X.RDB$FIELD_SOURCE, global_field->sym_string); if (position) { X.RDB$FIELD_POSITION.NULL = FALSE; X.RDB$FIELD_POSITION = position; } else X.RDB$FIELD_POSITION.NULL = TRUE; const qli_symbol* symbol; if (!global_flag && (symbol = field->fld_query_name)) { X.RDB$QUERY_NAME.NULL = FALSE; strcpy(X.RDB$QUERY_NAME, symbol->sym_string); } else X.RDB$QUERY_NAME.NULL = TRUE; if (!global_flag && (field->fld_edit_string)) { X.RDB$EDIT_STRING.NULL = FALSE; strcpy(X.RDB$EDIT_STRING, field->fld_edit_string); } else X.RDB$EDIT_STRING.NULL = TRUE; END_STORE ON_ERROR rollback_update(relation->rel_database); ERRQ_database_error(relation->rel_database, gds_status); END_ERROR; } static void add_sql_field( qli_rel* relation, qli_fld* field, USHORT position, qli_rlb* rlb) { /************************************** * * a d d _ s q l _ f i e l d * ************************************** * * Functional description * Add a SQL field to a relation via dyn. * **************************************/ CHECK_RLB(rlb); qli_dbb* database = relation->rel_database; const qli_symbol* relation_name = relation->rel_symbol; const qli_symbol* field_name = field->fld_name; // Check to see if it already exists in relation FOR(REQUEST_HANDLE database->dbb_requests[REQ_rfr_def]) X IN DB.RDB$RELATION_FIELDS WITH X.RDB$RELATION_NAME EQ relation_name->sym_string AND X.RDB$FIELD_NAME EQ field_name->sym_string rollback_update(relation->rel_database); ERRQ_print_error(251, SafeArg() << field_name->sym_string << relation_name->sym_string); // Msg251 Field %s already exists in relation %s END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; STUFF(isc_dyn_def_sql_fld); STUFF_STRING(field->fld_name->sym_string); STUFF(isc_dyn_fld_type); STUFF_WORD(2); STUFF_WORD(blr_dtypes[field->fld_dtype]); STUFF(isc_dyn_fld_length); STUFF_WORD(2); const USHORT l = (field->fld_dtype == dtype_varying) ? field->fld_length - sizeof(SSHORT) : field->fld_length; STUFF_WORD(l); STUFF(isc_dyn_fld_scale); STUFF_WORD(2); STUFF_WORD(field->fld_scale); if (DTYPE_IS_EXACT(field->fld_dtype)) { STUFF(isc_dyn_fld_precision); STUFF_WORD(2); STUFF_WORD(field->fld_precision); STUFF(isc_dyn_fld_sub_type); STUFF_WORD(2); STUFF_WORD(field->fld_sub_type); } if (position) { STUFF(isc_dyn_fld_position); STUFF_WORD(2); STUFF_WORD(position); } CHECK_RLB(rlb); if (field->fld_flags & FLD_not_null) { STUFF(isc_dyn_fld_validation_blr); STUFF_WORD(8); STUFF(blr_version4); STUFF(blr_not); STUFF(blr_missing); STUFF(blr_fid); STUFF(0); STUFF(0); STUFF(0); STUFF(blr_eoc); } STUFF(isc_dyn_end); } static void blob_copy( qli_rlb* rlb, qli_rel* source, ISC_QUAD& source_blob_id) { /************************************** * * b l o b _ c o p y * ************************************** * * Functional description * Copy information from one blob to a request * language block to stick it into a new definition. * **************************************/ ISC_STATUS_ARRAY status_vector; qli_dbb* source_dbb = source->rel_database ? source->rel_database : QLI_databases; DB = source_dbb->dbb_handle; UserBlob source_blob(status_vector); if (!source_blob.open(DB, source_dbb->dbb_meta_trans, source_blob_id)) { rollback_update(source_dbb); ERRQ_database_error(source_dbb, status_vector); } SLONG size, segment_count, max_segment; if (!getBlobSize(source_blob, &size, &segment_count, &max_segment)) { rollback_update(source_dbb); ERRQ_database_error(source_dbb, status_vector); } TEXT fixed_buffer[4096]; TEXT* buffer; USHORT buffer_length; if (max_segment < static_cast(sizeof(fixed_buffer))) { buffer_length = sizeof(fixed_buffer); buffer = fixed_buffer; } else { buffer_length = max_segment; buffer = (TEXT*) gds__alloc((SLONG) buffer_length); } STUFF_WORD((USHORT) size); size_t length; while (source_blob.getSegment(buffer_length, buffer, length) && !source_blob.getCode()) { while (rlb->rlb_data + length > rlb->rlb_limit) rlb = GEN_rlb_extend(rlb); const TEXT* p = buffer; while (length--) STUFF(*p++); } if (status_vector[1] != isc_segstr_eof) { rollback_update(source_dbb); if (buffer != fixed_buffer) gds__free(buffer); ERRQ_database_error(source_dbb, status_vector); } if (buffer != fixed_buffer) gds__free(buffer); if (!source_blob.close()) { rollback_update(source_dbb); ERRQ_database_error(source_dbb, status_vector); } //return TRUE; } static void change_field( qli_rel* relation, qli_fld* field) { /************************************** * * c h a n g e _ f i e l d * ************************************** * * Functional description * Change optional attributes of a field. * **************************************/ qli_dbb* database = relation->rel_database; const qli_symbol* relation_name = relation->rel_symbol; const qli_symbol* field_name = field->fld_name; const qli_symbol* global_field = field->fld_based; if (field->fld_dtype) { rollback_update(database); ERRQ_print_error(252); // Msg252 datatype can not be changed locally } if (global_field && !(check_global_field(database, NULL, global_field->sym_string))) { rollback_update(database); ERRQ_print_error(253, global_field->sym_string); // Msg253 global field %s does not exist } // Modify RFR USHORT count = 0; FOR(REQUEST_HANDLE database->dbb_requests[REQ_mdf_rfr]) X IN DB.RDB$RELATION_FIELDS WITH X.RDB$RELATION_NAME = relation_name->sym_string AND X.RDB$FIELD_NAME = field_name->sym_string count++; MODIFY X USING if (global_field) strcpy(X.RDB$FIELD_SOURCE, global_field->sym_string); const qli_symbol* symbol = field->fld_query_name; if (symbol) strcpy(X.RDB$QUERY_NAME, symbol->sym_string); if (field->fld_edit_string) strcpy(X.RDB$EDIT_STRING, field->fld_edit_string); END_MODIFY ON_ERROR rollback_update(relation->rel_database); ERRQ_database_error(relation->rel_database, gds_status); END_ERROR; END_FOR ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; if (!count) { rollback_update(database); ERRQ_print_error(254, SafeArg() << field_name->sym_string << relation_name->sym_string); // Msg254 field %s not found in relation %s } } static bool check_global_field(qli_dbb* database, qli_fld* field, const TEXT* name) { /************************************** * * c h e c k _ g l o b a l _ f i e l d * ************************************** * * Functional description * Given a name, check for the existence of a global field * by that name. If a qli_fld block is supplied, * check its characteristics against the global. * If it is not fully defined, flesh it out from the global. * **************************************/ bool previously_defined = false; FOR(REQUEST_HANDLE database->dbb_requests[REQ_field_def]) X IN DB.RDB$FIELDS WITH X.RDB$FIELD_NAME EQ name if (field) { if (field->fld_dtype && (X.RDB$FIELD_TYPE != blr_dtypes[field->fld_dtype] || X.RDB$FIELD_LENGTH != static_cast( ((field->fld_dtype == dtype_varying) ? field->fld_length - sizeof(SSHORT) : field->fld_length)) || X.RDB$FIELD_SCALE != field->fld_scale)) { rollback_update(database); ERRQ_print_error(255, name); // Msg255 Datatype conflict with existing global field %s } field->fld_dtype = MET_get_datatype(X.RDB$FIELD_TYPE); field->fld_length = field_length(field->fld_dtype, X.RDB$FIELD_LENGTH); if (field->fld_dtype == dtype_varying) { field->fld_length += sizeof(SSHORT); } field->fld_scale = X.RDB$FIELD_SCALE; field->fld_sub_type = X.RDB$FIELD_SUB_TYPE; field->fld_sub_type_missing = X.RDB$FIELD_SUB_TYPE.NULL; if (!field->fld_edit_string) { field->fld_edit_string = make_string(X.RDB$EDIT_STRING, sizeof(X.RDB$EDIT_STRING)); } if (!field->fld_query_name) { field->fld_query_name = make_symbol(X.RDB$QUERY_NAME, sizeof(X.RDB$QUERY_NAME)); } if (!field->fld_query_header) { ISC_QUAD& blob = X.RDB$QUERY_HEADER; if (!UserBlob::blobIsNull(blob)) { field->fld_query_header = get_query_header(database, blob); } } } previously_defined = true; END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; return previously_defined; } static bool check_relation( qli_rel* relation) { /************************************** * * c h e c k _ r e l a t i o n * ************************************** * * Functional description * Check the existence of the named relation. * **************************************/ bool previously_defined = false; FB_API_HANDLE spare = DB; DB = relation->rel_database->dbb_handle; FOR(REQUEST_HANDLE relation->rel_database->dbb_requests[REQ_relation_def] TRANSACTION_HANDLE relation->rel_database->dbb_meta_trans) X IN DB.RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ relation->rel_symbol->sym_string previously_defined = true; END_FOR ON_ERROR ERRQ_database_error(relation->rel_database, gds_status); END_ERROR; DB = spare; return previously_defined; } static void clone_fields( qli_rel* target, qli_rel* source) { /************************************** * * c l o n e _ f i e l d s * ************************************** * * Functional description * Copy the RFR records for one relation * into another. The target database is already * setup for updates. If there is a different source * database, get it ready too. Then make sure that * the source relation actually exists. * * If it's two different databases, create the global * fields that are missing in the target (except those * that support computed fields), then create the local * fields and during that pass create the computed * fields and the SQL fields. * **************************************/ FB_API_HANDLE req1, req2; req1 = req2 = 0; FB_API_HANDLE s_trans = gds_trans; DB = DB1 = target->rel_database->dbb_handle; if (target->rel_database != source->rel_database) { MET_meta_transaction(source->rel_database, false); s_trans = gds_trans; } if (target->rel_database != source->rel_database) clone_global_fields(target, source); qli_rlb* rlb = NULL; CHECK_RLB(rlb); STUFF(isc_dyn_version_1); STUFF(isc_dyn_begin); FOR(REQUEST_HANDLE req1 TRANSACTION_HANDLE s_trans) Y IN DB.RDB$RELATION_FIELDS CROSS F IN DB.RDB$FIELDS WITH Y.RDB$RELATION_NAME = source->rel_symbol->sym_string AND Y.RDB$FIELD_SOURCE = F.RDB$FIELD_NAME // If SQL made this field, create a new global field for it if (F.RDB$COMPUTED_BLR.NULL && !fb_utils::implicit_domain(Y.RDB$FIELD_NAME) && fb_utils::implicit_domain(F.RDB$FIELD_NAME)) { STUFF(isc_dyn_def_sql_fld); STUFF_STRING(Y.RDB$FIELD_NAME); STUFF(isc_dyn_fld_type); STUFF_WORD(2); STUFF_WORD(F.RDB$FIELD_TYPE); STUFF(isc_dyn_fld_length); STUFF_WORD(2); STUFF_WORD(F.RDB$FIELD_LENGTH); STUFF(isc_dyn_fld_scale); STUFF_WORD(2); STUFF_WORD(F.RDB$FIELD_SCALE); if (!F.RDB$VALIDATION_BLR.NULL) { STUFF(isc_dyn_fld_validation_blr); blob_copy(rlb, source, F.RDB$VALIDATION_BLR); } } else { STUFF(isc_dyn_def_local_fld); STUFF_STRING(Y.RDB$FIELD_NAME); STUFF(isc_dyn_fld_source); STUFF_STRING(Y.RDB$FIELD_SOURCE); } STUFF(isc_dyn_rel_name); STUFF_STRING(target->rel_symbol->sym_string); if (!Y.RDB$FIELD_POSITION.NULL) { STUFF(isc_dyn_fld_position); STUFF_WORD(2); STUFF_WORD(Y.RDB$FIELD_POSITION); } if (!Y.RDB$QUERY_NAME.NULL) { STUFF(isc_dyn_fld_query_name); STUFF_STRING(Y.RDB$QUERY_NAME); } if (!Y.RDB$EDIT_STRING.NULL) { STUFF(isc_dyn_fld_edit_string); STUFF_STRING(Y.RDB$EDIT_STRING); } if (!Y.RDB$UPDATE_FLAG.NULL) { STUFF(isc_dyn_update_flag); STUFF_WORD(2); STUFF_WORD(Y.RDB$UPDATE_FLAG); } if (!Y.RDB$QUERY_HEADER.NULL) { STUFF(isc_dyn_fld_query_header); blob_copy(rlb, source, Y.RDB$QUERY_HEADER); } if (!Y.RDB$DESCRIPTION.NULL) { STUFF(isc_dyn_description); blob_copy(rlb, source, Y.RDB$DESCRIPTION); } if (!Y.RDB$DEFAULT_VALUE.NULL) { STUFF(isc_dyn_fld_default_value); blob_copy(rlb, source, Y.RDB$DEFAULT_VALUE); } if ((source->rel_database->dbb_capabilities & DBB_cap_rfr_sys_flag) && (target->rel_database->dbb_capabilities & DBB_cap_rfr_sys_flag)) { FOR(REQUEST_HANDLE req2 TRANSACTION_HANDLE s_trans) S_RFR IN DB.RDB$RELATION_FIELDS WITH S_RFR.RDB$FIELD_NAME = Y.RDB$FIELD_NAME AND S_RFR.RDB$RELATION_NAME = Y.RDB$RELATION_NAME if (!S_RFR.RDB$SYSTEM_FLAG.NULL) { STUFF(isc_dyn_system_flag); STUFF_WORD(2); STUFF_WORD(S_RFR.RDB$SYSTEM_FLAG); } END_FOR ON_ERROR rollback_update(target->rel_database); ERRQ_database_error(source->rel_database, gds_status); END_ERROR; } if (!F.RDB$COMPUTED_BLR.NULL) { STUFF(isc_dyn_fld_computed_blr); blob_copy(rlb, source, F.RDB$COMPUTED_BLR); STUFF(isc_dyn_fld_computed_source); blob_copy(rlb, source, F.RDB$COMPUTED_SOURCE); STUFF(isc_dyn_fld_type); STUFF_WORD(2); STUFF_WORD(F.RDB$FIELD_TYPE); STUFF(isc_dyn_fld_length); STUFF_WORD(2); STUFF_WORD(F.RDB$FIELD_LENGTH); if (!F.RDB$FIELD_SCALE.NULL) { STUFF(isc_dyn_fld_scale); STUFF_WORD(2); STUFF_WORD(F.RDB$FIELD_SCALE); } if (!F.RDB$FIELD_SUB_TYPE.NULL) { STUFF(isc_dyn_fld_sub_type); STUFF_WORD(2); STUFF_WORD(F.RDB$FIELD_SUB_TYPE); } } STUFF(isc_dyn_end); END_FOR ON_ERROR rollback_update(source->rel_database); ERRQ_database_error(source->rel_database, gds_status); END_ERROR; STUFF(isc_dyn_end); STUFF(isc_dyn_eoc); execute_dynamic_ddl(target->rel_database, rlb); release_request(source->rel_database, req1); release_request(source->rel_database, req2); } static void clone_global_fields( qli_rel* target, qli_rel* source) { /************************************** * * c l o n e _ g l o b a l _ f i e l d s * ************************************** * * Functional description * Copy the global field definitions required by a relation * from one database into another. Both databases are * setup correctly. (Set up the handles again just because * we're compulsive.) * * In this routine, as in clone fields, be careful to probe * for our extensions rather than just using them so we * can copy relations from V2 and from Rdb. * * Don't clone fields that already exist and have the * correct datatype and length. * * Don't clone computed fields since they will be cloned * later with the local field definitions. Don't clone * SQL defined fields for the same reason. * **************************************/ FB_API_HANDLE req1, req2, req3; req1 = req2 = req3 = 0; FB_API_HANDLE s_trans = source->rel_database->dbb_meta_trans; FB_API_HANDLE t_trans = target->rel_database->dbb_meta_trans; DB = source->rel_database->dbb_handle; DB1 = target->rel_database->dbb_handle; qli_rlb* rlb = NULL; bool first_field = true; FOR(REQUEST_HANDLE req1 TRANSACTION_HANDLE s_trans) RFR IN DB.RDB$RELATION_FIELDS CROSS Y IN DB.RDB$FIELDS WITH RFR.RDB$RELATION_NAME = source->rel_symbol->sym_string AND Y.RDB$FIELD_NAME = RFR.RDB$FIELD_SOURCE REDUCED TO Y.RDB$FIELD_NAME if (!Y.RDB$COMPUTED_BLR.NULL || (!fb_utils::implicit_domain(RFR.RDB$FIELD_NAME) && fb_utils::implicit_domain(Y.RDB$FIELD_NAME))) { continue; } bool predefined = false; FOR(REQUEST_HANDLE req2 TRANSACTION_HANDLE t_trans) A IN DB1.RDB$FIELDS WITH A.RDB$FIELD_NAME = Y.RDB$FIELD_NAME if ((A.RDB$FIELD_TYPE != Y.RDB$FIELD_TYPE) || (A.RDB$FIELD_LENGTH != Y.RDB$FIELD_LENGTH) || (A.RDB$FIELD_SCALE.NULL != Y.RDB$FIELD_SCALE.NULL) || ((!Y.RDB$FIELD_SCALE.NULL) && (A.RDB$FIELD_SCALE != Y.RDB$FIELD_SCALE)) || (A.RDB$FIELD_SUB_TYPE.NULL != Y.RDB$FIELD_SUB_TYPE.NULL) || ((!Y.RDB$FIELD_SUB_TYPE.NULL) && (A.RDB$FIELD_SUB_TYPE != Y.RDB$FIELD_SUB_TYPE))) { TEXT* name = ALLQ_malloc((SLONG) sizeof(Y.RDB$FIELD_NAME)); strcpy(name, Y.RDB$FIELD_NAME); rollback_update(target->rel_database); // CVC: When is this memory deallocated? ERRQ_error(413, name); // conflicting previous definition } else predefined = true; END_FOR ON_ERROR ERRQ_database_error(target->rel_database, gds_status); END_ERROR; if (predefined) continue; if (first_field) { CHECK_RLB(rlb); STUFF(isc_dyn_version_1); STUFF(isc_dyn_begin); first_field = false; } STUFF(isc_dyn_def_global_fld); STUFF_STRING(Y.RDB$FIELD_NAME); STUFF(isc_dyn_fld_type); STUFF_WORD(2); STUFF_WORD(Y.RDB$FIELD_TYPE); STUFF(isc_dyn_fld_length); STUFF_WORD(2); STUFF_WORD(Y.RDB$FIELD_LENGTH); if (!Y.RDB$FIELD_SCALE.NULL) { STUFF(isc_dyn_fld_scale); STUFF_WORD(2); STUFF_WORD(Y.RDB$FIELD_SCALE); } if (!Y.RDB$FIELD_SUB_TYPE.NULL) { STUFF(isc_dyn_fld_sub_type); STUFF_WORD(2); STUFF_WORD(Y.RDB$FIELD_SUB_TYPE); } if (!Y.RDB$SEGMENT_LENGTH.NULL) { STUFF(isc_dyn_fld_segment_length); STUFF_WORD(2); STUFF_WORD(Y.RDB$SEGMENT_LENGTH); } if (!Y.RDB$SYSTEM_FLAG.NULL) { STUFF(isc_dyn_system_flag); STUFF_WORD(2); STUFF_WORD(Y.RDB$SYSTEM_FLAG); } if (!Y.RDB$QUERY_NAME.NULL) { STUFF(isc_dyn_fld_query_name); STUFF_STRING(Y.RDB$QUERY_NAME); } if (!Y.RDB$EDIT_STRING.NULL) { STUFF(isc_dyn_fld_edit_string); STUFF_STRING(Y.RDB$EDIT_STRING); } if (!Y.RDB$MISSING_VALUE.NULL) { STUFF(isc_dyn_fld_missing_value); blob_copy(rlb, source, Y.RDB$MISSING_VALUE); } if (!Y.RDB$DEFAULT_VALUE.NULL) { STUFF(isc_dyn_fld_default_value); blob_copy(rlb, source, Y.RDB$DEFAULT_VALUE); } if (!Y.RDB$QUERY_HEADER.NULL) { STUFF(isc_dyn_fld_query_header); blob_copy(rlb, source, Y.RDB$QUERY_HEADER); } if (!Y.RDB$DESCRIPTION.NULL) { STUFF(isc_dyn_description); blob_copy(rlb, source, Y.RDB$DESCRIPTION); } if (!Y.RDB$VALIDATION_BLR.NULL) { STUFF(isc_dyn_fld_validation_blr); blob_copy(rlb, source, Y.RDB$VALIDATION_BLR); } if (!Y.RDB$VALIDATION_SOURCE.NULL) { STUFF(isc_dyn_fld_validation_source); blob_copy(rlb, source, Y.RDB$VALIDATION_SOURCE); } if ((target->rel_database->dbb_capabilities & DBB_cap_dimensions) && (source->rel_database->dbb_capabilities & DBB_cap_dimensions)) { bool first_dimension = true; FOR(REQUEST_HANDLE req3 TRANSACTION_HANDLE s_trans) F IN DB.RDB$FIELDS CROSS D IN DB.RDB$FIELD_DIMENSIONS WITH F.RDB$FIELD_NAME = D.RDB$FIELD_NAME AND F.RDB$FIELD_NAME = Y.RDB$FIELD_NAME if (first_dimension) { first_dimension = false; STUFF(isc_dyn_fld_dimensions); STUFF_WORD(2); STUFF_WORD(F.RDB$DIMENSIONS); } STUFF(isc_dyn_def_dimension); STUFF_WORD(2); STUFF_WORD(D.RDB$DIMENSION); if (!D.RDB$LOWER_BOUND.NULL) { STUFF(isc_dyn_dim_lower); STUFF_WORD(2); STUFF_WORD(D.RDB$LOWER_BOUND); } if (!D.RDB$UPPER_BOUND.NULL) { STUFF(isc_dyn_dim_upper); STUFF_WORD(2); STUFF_WORD(D.RDB$UPPER_BOUND); } STUFF(isc_dyn_end); END_FOR ON_ERROR if (target->rel_database->dbb_meta_trans) rollback_update(target->rel_database); rollback_update(source->rel_database); ERRQ_database_error(source->rel_database, gds_status); END_ERROR; } STUFF(isc_dyn_end); END_FOR ON_ERROR rollback_update(source->rel_database); if (target->rel_database->dbb_meta_trans) rollback_update(target->rel_database); ERRQ_database_error(source->rel_database, gds_status); END_ERROR; if (rlb) { STUFF(isc_dyn_end); STUFF(isc_dyn_eoc); execute_dynamic_ddl(target->rel_database, rlb); } release_request(source->rel_database, req1); release_request(target->rel_database, req2); release_request(source->rel_database, req3); } static void define_global_field( qli_dbb* database, qli_fld* field, qli_symbol* name) { /************************************** * * d e f i n e _ g l o b a l _ f i e l d * ************************************** * * Functional description * * Define a global field if we've got enough * information. * **************************************/ if (!field->fld_dtype) { rollback_update(database); ERRQ_print_error(256, name->sym_string); // Msg256 No datatype specified for field %s } STORE(REQUEST_HANDLE database->dbb_requests[REQ_store_field]) X IN DB.RDB$FIELDS strcpy(X.RDB$FIELD_NAME, name->sym_string); X.RDB$FIELD_TYPE = blr_dtypes[field->fld_dtype]; X.RDB$FIELD_SCALE = field->fld_scale; X.RDB$FIELD_LENGTH = (field->fld_dtype == dtype_varying) ? field->fld_length - sizeof(SSHORT) : field->fld_length; if (!field->fld_sub_type_missing) { X.RDB$FIELD_SUB_TYPE.NULL = FALSE; X.RDB$FIELD_SUB_TYPE = field->fld_sub_type; } else X.RDB$FIELD_SUB_TYPE.NULL = TRUE; const qli_symbol* symbol = field->fld_query_name; if (symbol) { X.RDB$QUERY_NAME.NULL = FALSE; strcpy(X.RDB$QUERY_NAME, symbol->sym_string); } else X.RDB$QUERY_NAME.NULL = TRUE; if (field->fld_edit_string) { X.RDB$EDIT_STRING.NULL = FALSE; strcpy(X.RDB$EDIT_STRING, field->fld_edit_string); } else X.RDB$EDIT_STRING.NULL = TRUE; END_STORE ON_ERROR rollback_update(database); ERRQ_database_error(database, gds_status); END_ERROR; } static void delete_fields( qli_rel* relation) { /************************************** * * d e l e t e _ f i e l d s * ************************************** * * Functional description * Delete field definitions. * **************************************/ while (qli_fld* field = relation->rel_fields) { relation->rel_fields = field->fld_next; ALLQ_release((qli_frb*) field->fld_name); if (field->fld_query_name) ALLQ_release((qli_frb*) field->fld_query_name); ALLQ_release((qli_frb*) field); } relation->rel_flags &= ~REL_fields; } static ISC_STATUS detach(ISC_STATUS* status_vector, qli_dbb* dbb) { /************************************** * * d e t a c h * ************************************** * * Functional description * Shut down a database. Return the status of the * first failing call, if any. * **************************************/ if (!dbb->dbb_handle) return FB_SUCCESS; ISC_STATUS_ARRAY alt_vector; ISC_STATUS* status = status_vector; if (dbb->dbb_transaction) if (isc_commit_transaction(status, &dbb->dbb_transaction)) status = alt_vector; if (dbb->dbb_proc_trans && (dbb->dbb_capabilities & DBB_cap_multi_trans)) if (isc_commit_transaction(status, &dbb->dbb_proc_trans)) status = alt_vector; if (dbb->dbb_meta_trans && (dbb->dbb_capabilities & DBB_cap_multi_trans)) if (isc_commit_transaction(status, &dbb->dbb_meta_trans)) status = alt_vector; isc_detach_database(status, &dbb->dbb_handle); return status_vector[1]; } static void execute_dynamic_ddl( qli_dbb* database, qli_rlb* rlb) { /************************************** * * e x e c u t e _ d y n a m i c _ d d l * ************************************** * * Functional description * Execute a ddl command, for better or for worse. * **************************************/ const USHORT length = rlb->rlb_data - rlb->rlb_base; if (QLI_blr) PRETTY_print_dyn(rlb->rlb_base, NULL, NULL, 0); if (isc_ddl(gds_status, &database->dbb_handle, &database->dbb_meta_trans, length, (const char*) rlb->rlb_base)) { rollback_update(database); ERRQ_database_error(database, gds_status); } GEN_rlb_release (rlb); } static int field_length( USHORT dtype, USHORT length) { /************************************** * * f i e l d _ l e n g t h * ************************************** * * Functional description * Return implementation specific length for field. * **************************************/ switch (dtype) { case dtype_short: return sizeof(SSHORT); case dtype_long: return sizeof(SLONG); case dtype_real: return sizeof(float); case dtype_double: return sizeof(double); case dtype_int64: return sizeof(SINT64); case dtype_sql_time: case dtype_sql_date: return sizeof(SLONG); case dtype_timestamp: case dtype_blob: case dtype_quad: return sizeof(ISC_QUAD); } return length; } static void get_database_type( qli_dbb* new_dbb) { /************************************** * * g e t _ d a t a b a s e _ t y p e * ************************************** * * Functional description * Ask what type of database this is. * **************************************/ ISC_STATUS_ARRAY status_vector; UCHAR buffer[1024]; isc_database_info(status_vector, &new_dbb->dbb_handle, sizeof(db_info), reinterpret_cast(db_info), sizeof(buffer), (char*) buffer); if (status_vector[1]) ERRQ_database_error(new_dbb, gds_status); const UCHAR* p = buffer; while (*p != isc_info_end && p < buffer + sizeof(buffer)) { UCHAR item = *p++; const USHORT l = gds__vax_integer(p, 2); p += 2; switch (item) { case isc_info_implementation: { const UCHAR* q = p; int count = *q++; while (count--) { //new_dbb->dbb_type = *q; // Not used ++q; if (*q++ == isc_info_db_class_access) break; } } break; default: ERRQ_error(257); // Msg257 database info call failed } p += l; } } static TEXT* get_query_header( qli_dbb* database, ISC_QUAD& blob_id) { /************************************** * * g e t _ q u e r y _ h e a d e r * ************************************** * * Functional description * Pick up a query header for a field. * **************************************/ ISC_STATUS_ARRAY status_vector; TEXT header[1024], buffer[1024]; UserBlob blob(status_vector); if (!blob.open(database->dbb_handle, gds_trans, blob_id)) { ERRQ_database_error(database, status_vector); } TEXT* p = header; const char* const end = header + sizeof(header) - 3; // CVC: No bounds check here: it's assumed that 1024 is enough, but "p" // might overflow "header" eventually. for (;;) { if (p >= end) break; size_t length; if (!blob.getSegment(sizeof(buffer), buffer, length)) break; if (length && buffer[length - 1] == '\n') --length; buffer[length] = 0; const TEXT* q = buffer; if (*q == '"') do { *p++ = *q++; } while (*q && p < end); else { *p++ = '"'; while (*q && p < end) *p++ = *q++; *p++ = '"'; } } if (!blob.close()) ERRQ_database_error(database, gds_status); *p = 0; if (!strcmp(header, "\" \"")) { header[0] = '-'; header[1] = 0; p = header + 1; } const USHORT len = p - header; if (len) return make_string(header, len); return NULL; } static void install( qli_dbb* old_dbb) { /************************************** * * i n s t a l l * ************************************** * * Functional description * Install a database. Scan relations, etc., make up relation * blocks, etc, and copy the database block to someplace more * permanent. * **************************************/ // Copy the database block to one out of the permanent pool SSHORT l = old_dbb->dbb_filename_length; qli_dbb* new_dbb = (qli_dbb*) ALLOCPV(type_dbb, l); new_dbb->dbb_filename_length = l; memcpy(new_dbb->dbb_filename, old_dbb->dbb_filename, l); DB = new_dbb->dbb_handle = old_dbb->dbb_handle; new_dbb->dbb_capabilities = old_dbb->dbb_capabilities; new_dbb->dbb_next = QLI_databases; QLI_databases = new_dbb; // If a name was given, make up a symbol for the database. qli_name* name = (qli_name*) old_dbb->dbb_symbol; if (name) { qli_symbol* asymbol = make_symbol(name->nam_string, name->nam_length); new_dbb->dbb_symbol = asymbol; asymbol->sym_type = SYM_database; asymbol->sym_object = (BLK) new_dbb; HSH_insert(asymbol, true); } gds_trans = MET_transaction(nod_start_trans, new_dbb); // Find out whose database this is so later we don't ask Rdb/VMS // or its pals to do any unnatural acts. While we're at it get // general capabilities get_database_type(new_dbb); set_capabilities(new_dbb); // Get all relations in database. For each relation make up a // relation block, make up a symbol block, and insert the symbol // into the hash table. FB_API_HANDLE request = 0; FOR(REQUEST_HANDLE request) X IN DB.RDB$RELATIONS SORTED BY DESCENDING X.RDB$RELATION_NAME qli_rel* relation = (qli_rel*) ALLOCP(type_rel); relation->rel_next = new_dbb->dbb_relations; new_dbb->dbb_relations = relation; relation->rel_database = new_dbb; relation->rel_id = X.RDB$RELATION_ID; qli_symbol* rsymbol = make_symbol(X.RDB$RELATION_NAME, sizeof(X.RDB$RELATION_NAME)); rsymbol->sym_type = SYM_relation; rsymbol->sym_object = (BLK) relation; relation->rel_symbol = rsymbol; HSH_insert(rsymbol); if (strcmp(rsymbol->sym_string, "QLI$PROCEDURES") == 0) new_dbb->dbb_flags |= DBB_procedures; if (relation->rel_system_flag = X.RDB$SYSTEM_FLAG) relation->rel_flags |= REL_system; if (!X.RDB$VIEW_BLR.NULL) relation->rel_flags |= REL_view; END_FOR ON_ERROR ERRQ_database_error(new_dbb, gds_status); END_ERROR; release_request(new_dbb, request); // Pick up functions, if appropriate if (new_dbb->dbb_capabilities & DBB_cap_functions) { FB_API_HANDLE request2 = 0; FOR(REQUEST_HANDLE request) X IN DB.RDB$FUNCTIONS qli_symbol* fsymbol = make_symbol(X.RDB$FUNCTION_NAME, sizeof(X.RDB$FUNCTION_NAME)); if (!fsymbol) continue; //qli_fun* function = (qli_fun*) ALLOCPV(type_fun, 0); qli_fun* function = (qli_fun*) ALLOCP(type_fun); function->fun_next = new_dbb->dbb_functions; new_dbb->dbb_functions = function; function->fun_symbol = fsymbol; function->fun_database = new_dbb; FOR(REQUEST_HANDLE request2) Y IN DB.RDB$FUNCTION_ARGUMENTS WITH Y.RDB$FUNCTION_NAME EQ X.RDB$FUNCTION_NAME AND Y.RDB$ARGUMENT_POSITION EQ X.RDB$RETURN_ARGUMENT function->fun_return.dsc_dtype = MET_get_datatype(Y.RDB$FIELD_TYPE); function->fun_return.dsc_length = field_length(function->fun_return.dsc_dtype, Y.RDB$FIELD_LENGTH); function->fun_return.dsc_scale = Y.RDB$FIELD_SCALE; END_FOR ON_ERROR ERRQ_database_error(new_dbb, gds_status); END_ERROR; if (!function->fun_return.dsc_dtype) { function->fun_return.dsc_dtype = dtype_text; function->fun_return.dsc_length = 20; } fsymbol->sym_type = SYM_function; fsymbol->sym_object = (BLK) function; HSH_insert(fsymbol); // Insert another symbol if there is a function query name qli_symbol* symbol2 = make_symbol(X.RDB$QUERY_NAME, sizeof(X.RDB$QUERY_NAME)); if (symbol2) { function->fun_query_name = symbol2; symbol2->sym_type = SYM_function; symbol2->sym_object = (BLK) function; HSH_insert(symbol2, true); } END_FOR ON_ERROR ERRQ_database_error(new_dbb, gds_status); END_ERROR; release_request(new_dbb, request); release_request(new_dbb, request2); } } static qli_syntax* make_node( nod_t type, USHORT count) { /************************************** * * m a k e _ n o d e * ************************************** * * Functional description * Generate a syntax node. * **************************************/ qli_syntax* node = (qli_syntax*) ALLOCPV(type_syn, count); node->syn_count = count; node->syn_type = type; return node; } static TEXT* make_string( TEXT* string, SSHORT length) { /************************************** * * m a k e _ s t r i n g * ************************************** * * Functional description * Make up a permanent string from a large string. Chop off * trailing blanks. If there's nothing left, return NULL. * **************************************/ fb_assert(length > 0); string[length] = 0; if (!(length = truncate_string(string))) return NULL; qli_str* str = (qli_str*) ALLOCPV(type_str, length + 1); TEXT* p = str->str_data; do { *p++ = *string++; } while (--length); *p = 0; return str->str_data; } static qli_symbol* make_symbol( TEXT* string, SSHORT length) { /************************************** * * m a k e _ s y m b o l * ************************************** * * Functional description * Make up a symbol from a string. If the string is all blanks, * don't make up a symbol. Phooey. * **************************************/ if (!(length = truncate_string(string))) return NULL; qli_symbol* symbol = (qli_symbol*) ALLOCPV(type_sym, length); symbol->sym_type = SYM_relation; symbol->sym_length = length; TEXT* p = symbol->sym_name; symbol->sym_string = p; memcpy(p, string, length); return symbol; } static qli_const* missing_value( ISC_QUAD& blob_id, qli_symbol* symbol) { /************************************** * * m i s s i n g _ v a l u e * ************************************** * * Functional description * Get a missing value into a constant * block for later reference. * **************************************/ qli_syntax* element = parse_blr_blob(blob_id, symbol); if (element) return ((qli_const*) element->syn_arg[0]); return NULL; } static qli_syntax* parse_blr( UCHAR** ptr, qli_symbol* symbol) { /************************************** * * p a r s e _ b l r * ************************************** * * Functional description * Parse a BLR expression. * **************************************/ qli_syntax** arg; qli_const* constant; nod_t operatr; qli_name* name; UCHAR* p; SSHORT dtype, scale, length, l, op; UCHAR* blr = *ptr; SSHORT args = 2; qli_syntax* node = NULL; switch (op = BLR_BYTE) { case blr_any: case blr_unique: case blr_eoc: return NULL; case blr_eql: operatr = nod_eql; break; case blr_neq: operatr = nod_neq; break; case blr_gtr: operatr = nod_gtr; break; case blr_geq: operatr = nod_geq; break; case blr_lss: operatr = nod_lss; break; case blr_leq: operatr = nod_leq; break; case blr_containing: operatr = nod_containing; break; case blr_starting: operatr = nod_starts; break; case blr_matching: operatr = nod_matches; break; case blr_matching2: operatr = nod_sleuth; break; case blr_like: operatr = nod_like; break; case blr_and: operatr = nod_and; break; case blr_or: operatr = nod_or; break; case blr_add: operatr = nod_add; break; case blr_subtract: operatr = nod_subtract; break; case blr_multiply: operatr = nod_multiply; break; case blr_divide: operatr = nod_divide; break; case blr_upcase: operatr = nod_upcase; break; case blr_lowcase: operatr = nod_lowcase; break; case blr_null: operatr = nod_null; break; case blr_between: operatr = nod_between; args = 3; break; case blr_not: operatr = nod_not; args = 1; break; case blr_negate: operatr = nod_negate; args = 1; break; case blr_missing: operatr = nod_missing; args = 1; break; case blr_fid: BLR_BYTE; blr += 2; node = make_node(nod_field, 1); name = (qli_name*) ALLOCPV(type_nam, symbol->sym_length); node->syn_arg[0] = (qli_syntax*) name; name->nam_length = symbol->sym_length; strcpy(name->nam_string, symbol->sym_string); break; case blr_field: BLR_BYTE; length = BLR_BYTE; node = make_node(nod_field, 1); name = (qli_name*) ALLOCPV(type_nam, length); node->syn_arg[0] = (qli_syntax*) name; name->nam_length = length; p = (UCHAR*) name->nam_string; do { *p++ = BLR_BYTE; } while (--length); break; case blr_function: length = BLR_BYTE; node = make_node(nod_function, s_fun_count); node->syn_arg[s_fun_function] = (qli_syntax*) HSH_lookup((SCHAR*) blr, length); blr += length; args = BLR_BYTE; node->syn_arg[s_fun_args] = make_node(nod_list, args); arg = node->syn_arg[s_fun_args]->syn_arg; while (--args >= 0) { if (!(*arg++ = parse_blr(&blr, symbol))) return NULL; } break; case blr_literal: scale = 0; switch (BLR_BYTE) { case blr_text: dtype = dtype_text; length = l = gds__vax_integer(blr, 2); blr += 2; break; case blr_text2: dtype = dtype_text; scale = gds__vax_integer(blr, 2); blr += 2; length = l = gds__vax_integer(blr, 2); blr += 2; break; case blr_varying: dtype = dtype_varying; length = l = gds__vax_integer(blr, 2) + sizeof(USHORT); blr += 2; break; case blr_varying2: dtype = dtype_varying; scale = gds__vax_integer(blr, 2); blr += 2; length = l = gds__vax_integer(blr, 2) + sizeof(USHORT); blr += 2; break; case blr_short: dtype = dtype_short; l = 2; length = sizeof(SSHORT); scale = (int) BLR_BYTE; break; case blr_long: dtype = dtype_long; length = sizeof(SLONG); l = 4; scale = (int) BLR_BYTE; break; case blr_quad: dtype = dtype_quad; l = 8; length = sizeof(ISC_QUAD); scale = (int) BLR_BYTE; break; case blr_int64: dtype = dtype_int64; l = 8; length = sizeof(SINT64); scale = (int) BLR_BYTE; break; case blr_timestamp: dtype = dtype_timestamp; l = 8; length = sizeof(ISC_QUAD); break; case blr_sql_date: dtype = dtype_sql_date; l = 4; length = sizeof(SLONG); break; case blr_sql_time: dtype = dtype_sql_time; l = 4; length = sizeof(ULONG); break; } constant = (qli_const*) ALLOCPV(type_con, length); constant->con_desc.dsc_dtype = dtype; constant->con_desc.dsc_scale = scale; constant->con_desc.dsc_length = length; constant->con_desc.dsc_address = p = constant->con_data; QLI_validate_desc(constant->con_desc); switch (dtype) { case dtype_text: while (l--) *p++ = BLR_BYTE; break; case dtype_short: *(SSHORT*) p = gds__vax_integer(blr, l); blr += l; break; case dtype_long: *(SLONG*) p = gds__vax_integer(blr, l); blr += l; break; } node = make_node(nod_constant, 1); node->syn_count = 0; node->syn_arg[0] = (qli_syntax*) constant; break; default: ERRQ_print_error(258, SafeArg() << op); // Msg258 don't understand BLR operatr %d } if (!node) { node = make_node(operatr, args); arg = node->syn_arg; while (--args >= 0) { if (!(*arg++ = parse_blr(&blr, symbol))) return NULL; } } *ptr = blr; return node; } static qli_syntax* parse_blr_blob(ISC_QUAD& blob_id, qli_symbol* symbol) { /************************************** * * p a r s e _ b l r _ b l o b * ************************************** * * Functional description * Gobble up a BLR blob, if possible. * **************************************/ ISC_STATUS_ARRAY status_vector; if (UserBlob::blobIsNull(blob_id)) return NULL; UserBlob handle(status_vector); if (!handle.open(DB, gds_trans, blob_id)) return NULL; UCHAR buffer[1024]; UCHAR* ptr = buffer; for (;;) { size_t length = buffer + sizeof(buffer) - ptr; if (!length) break; // The old code considered getting a portion of a segment a bug. // Anyway, 1024 is not much to contain the BLR stream, even for validation. if (!handle.getSegment(length, ptr, length) || handle.getCode()) break; ptr += length; } if (!handle.close()) return NULL; if (ptr == buffer) return NULL; ptr = buffer; if (*ptr++ != blr_version4) return NULL; qli_syntax* node = parse_blr(&ptr, symbol); if (*ptr != blr_eoc) return NULL; return node; } static void purge_relation(qli_rel* relation) { /************************************** * * p u r g e _ r e l a t i o n * ************************************** * * Functional description * Purge a relation out of internal data structures. * **************************************/ qli_symbol* symbol = relation->rel_symbol; qli_dbb* database = relation->rel_database; HSH_remove(symbol); for (qli_rel** ptr = &database->dbb_relations; *ptr; ptr = &(*ptr)->rel_next) { if (*ptr == relation) { *ptr = relation->rel_next; break; } } qli_fld* field; while (field = relation->rel_fields) { relation->rel_fields = field->fld_next; ALLQ_release((qli_frb*) field->fld_name); if (symbol = field->fld_query_name) { ALLQ_release((qli_frb*) symbol); } ALLQ_release((qli_frb*) field); } ALLQ_release((qli_frb*) relation); } static void put_dyn_string( qli_rlb* rlb, const TEXT* string) { /************************************** * * p u t _ d y n _ s t r i n g * ************************************** * * Functional description * Move a string to a dynamic DDL command block. * **************************************/ const SSHORT l = strlen(string); while ((SSHORT) (rlb->rlb_limit - rlb->rlb_data) < l) { rlb = GEN_rlb_extend(rlb); } STUFF_WORD(l); while (*string) { STUFF(*string++); } CHECK_RLB(rlb); } static void release_request(qli_dbb* db, FB_API_HANDLE& request_handle) { if (request_handle) { if (isc_release_request(gds_status, &request_handle)) ERRQ_database_error(db, gds_status); } } static void rollback_update( qli_dbb* database) { /************************************** * * r o l l b a c k _ u p d a t e * ************************************** * * Functional description * Roll back a meta-data update. * **************************************/ if (gds_trans == database->dbb_meta_trans && (database->dbb_capabilities & DBB_cap_multi_trans)) { ISC_STATUS_ARRAY alt_vector; isc_rollback_transaction(alt_vector, &database->dbb_meta_trans); // Note: No message given if rollback fails } gds_trans = 0; } static void set_capabilities( qli_dbb* database) { /************************************** * * s e t _ c a p a b i l i t i e s * ************************************** * * Functional description * Probe the database to determine * what metadata capabilities it has. * The table, rfr_table, in the static * data declarations has the relation * and field names, together with the * capabilities bits. * **************************************/ FB_API_HANDLE req = 0; // Look for desireable fields in system relations for (const rfr_tab_t* rel_field_table = rfr_table; rel_field_table->relation; rel_field_table++) { FOR(REQUEST_HANDLE req) x IN DB.RDB$RELATION_FIELDS WITH x.RDB$RELATION_NAME = rel_field_table->relation AND x.RDB$FIELD_NAME = rel_field_table->field database->dbb_capabilities |= rel_field_table->bit_mask; END_FOR ON_ERROR ERRQ_database_error(database, gds_status); END_ERROR; } release_request(database, req); } static qli_dbb* setup_update( qli_dbb* database) { /************************************** * * s e t u p _ u p d a t e * ************************************** * * Functional description * Get ready to do a meta data update. * If no database was specified, use the * most recently readied one. * **************************************/ if (!database) database = QLI_databases; MET_meta_transaction(database, true); return database; } static void sql_grant_revoke( qli_syntax* node, USHORT type) { /***************************************************** * * s q l _ g r a n t _ r e v o k e * ***************************************************** * * Build a DYN string for a SQL GRANT or REVOKE statement * *****************************************************/ const USHORT privileges = (IPTR) node->syn_arg[s_grant_privileges]; qli_rel* relation = (qli_rel*) node->syn_arg[s_grant_relation]; qli_dbb* database = setup_update(relation->rel_database); relation->rel_database = database; const TEXT* relation_name = relation->rel_symbol->sym_string; qli_rlb* rlb = NULL; CHECK_RLB(rlb); STUFF(isc_dyn_version_1); STUFF(isc_dyn_begin); // For each user create a separate grant string // For each specified field create a separate grant string qli_syntax* names = node->syn_arg[s_grant_users]; qli_syntax* fields = node->syn_arg[s_grant_fields]; qli_name** name = (qli_name**) names->syn_arg; for (qli_name** const end = name + names->syn_count; name < end; name++) { if (fields->syn_count) { qli_name** field = (qli_name**) fields->syn_arg; qli_name** const end_field = field + fields->syn_count; for (; field < end_field; field++) { stuff_priv(rlb, type, relation_name, privileges, (*name)->nam_string, (*field)->nam_string); } } else stuff_priv(rlb, type, relation_name, privileges, (*name)->nam_string, NULL); } STUFF(isc_dyn_end); STUFF(isc_dyn_eoc); execute_dynamic_ddl(database, rlb); MET_meta_commit(database); } static int truncate_string( TEXT* string) { /************************************** * * t r u n c a t e _ s t r i n g * ************************************** * * Functional description * Convert a blank filled string to * a null terminated string without * trailing blanks. Because some * strings contain embedded blanks * (e.g. query headers & edit strings) * truncate from the back forward. * Return the number of characters found, * not including the terminating null. * **************************************/ const int len = fb_utils::name_length(string); string[len] = 0; return len; } static void stuff_priv(qli_rlb* rlb, USHORT type, const TEXT* relation, USHORT privileges, const TEXT* user, const TEXT* field) { /************************************** * * s t u f f _ p r i v * ************************************** * * Functional description * Stuff a privilege for grant/revoke. * **************************************/ CHECK_RLB(rlb); STUFF(type); // Stuff privileges first USHORT priv_count = 0; if (privileges & PRV_select) priv_count++; if (privileges & PRV_insert) priv_count++; if (privileges & PRV_delete) priv_count++; if (privileges & PRV_update) priv_count++; STUFF_WORD(priv_count); if (privileges & PRV_select) STUFF('S'); if (privileges & PRV_insert) STUFF('I'); if (privileges & PRV_delete) STUFF('D'); if (privileges & PRV_update) STUFF('U'); STUFF(isc_dyn_rel_name); STUFF_STRING(relation); STUFF(isc_dyn_grant_user); STUFF_STRING(user); if (field) { STUFF(isc_dyn_fld_name); STUFF_STRING(field); } if (privileges & PRV_grant_option) { STUFF(isc_dyn_grant_options); STUFF_WORD(2); STUFF_WORD(TRUE); } STUFF(isc_dyn_end); }