/* * PROGRAM: JRD Data Definition Utility * MODULE: dyn_define.epp * DESCRIPTION: Dynamic data definition DYN_define_ * * 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): ______________________________________. * * 23-May-2001 Claudio Valderrama - Forbid zero length identifiers, * they are not ANSI SQL compliant. * 2001.10.08 Claudio Valderrama: Add case isc_dyn_system_flag to * DYN_define_trigger() in order to receive values for special triggers * as defined in constants.h. * 2001.10.08 Ann Harrison: Changed dyn_create_index so it doesn't consider * simple unique indexes when finding a "referred index", but only * indexes that support unique constraints or primary keys. * 26-Sep-2001 Paul Beach - External File Directory Config. Parameter * 2002-02-24 Sean Leyne - Code Cleanup of old Win 3.1 port (WINDOWS_ONLY) * 2002.08.10 Dmitry Yemanov: ALTER VIEW * * 2002.10.29 Sean Leyne - Removed obsolete "Netware" port * * 2004.01.16 Vlad Horsun: added support for default parameters */ #include "firebird.h" #include "dyn_consts.h" #include "../common/classes/fb_string.h" #include #include #include #include "../jrd/common.h" #include "../jrd/jrd.h" #include "../jrd/ods.h" #include "../jrd/tra.h" #include "../jrd/scl.h" #include "../jrd/drq.h" #include "../jrd/req.h" #include "../jrd/flags.h" #include "../jrd/ibase.h" #include "../jrd/lls.h" #include "../jrd/met.h" #include "../jrd/btr.h" #include "../jrd/ini.h" #include "../jrd/intl.h" #include "../jrd/dyn.h" #include "../jrd/gdsassert.h" #include "../jrd/blb_proto.h" #include "../jrd/cmp_proto.h" #include "../jrd/dyn_proto.h" #include "../jrd/dyn_df_proto.h" #include "../jrd/dyn_ut_proto.h" #include "../jrd/err_proto.h" #include "../jrd/exe_proto.h" #include "../jrd/gds_proto.h" #include "../jrd/inf_proto.h" #include "../jrd/intl_proto.h" #include "../jrd/isc_f_proto.h" #include "../jrd/met_proto.h" #include "../jrd/vio_proto.h" #include "../jrd/scl_proto.h" #include "../jrd/gdsassert.h" #include "../jrd/os/path_utils.h" #include "../common/utils_proto.h" #include "../jrd/IntlManager.h" #include "../jrd/IntlUtil.h" #include "../dsql/DdlNodes.h" using MsgFormat::SafeArg; using namespace Jrd; using namespace Firebird; typedef Firebird::ObjectsArray MetaNameArray; const int FOR_KEY_UPD_CASCADE = 0x01; const int FOR_KEY_UPD_NULL = 0x02; const int FOR_KEY_UPD_DEFAULT = 0x04; const int FOR_KEY_UPD_NONE = 0x08; const int FOR_KEY_DEL_CASCADE = 0x10; const int FOR_KEY_DEL_NULL = 0x20; const int FOR_KEY_DEL_DEFAULT = 0x40; const int FOR_KEY_DEL_NONE = 0x80; DATABASE DB = STATIC "ODS.RDB"; static bool is_it_user_name(Global*, const Firebird::MetaName&, thread_db*); void DYN_define_file(Global* gbl, const UCHAR** ptr, SLONG shadow_number, SLONG* start, USHORT msg) { /************************************** * * D Y N _ d e f i n e _ f i l e * ************************************** * * Functional description * Define a database or shadow file. * **************************************/ UCHAR verb; SLONG temp; USHORT man_auto; SSHORT id; thread_db* tdbb = JRD_get_thread_data(); Database* dbb = tdbb->getDatabase(); if (!tdbb->getAttachment()->locksmith()) { ERR_post(Arg::Gds(isc_adm_task_denied)); } try { id = -1; Firebird::PathName temp_f; GET_STRING(ptr, temp_f); if (!ISC_expand_filename(temp_f, false)) DYN_error_punt(false, 231); // File name is invalid. if (dbb->dbb_filename == temp_f) DYN_error_punt(false, 166); AutoCacheRequest request(tdbb, id = drq_l_files, DYN_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FIRST 1 X IN RDB$FILES WITH X.RDB$FILE_NAME EQ temp_f.c_str() { DYN_error_punt(false, 166); } END_FOR request.reset(tdbb, id = drq_s_files, DYN_REQUESTS); STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$FILES { temp_f.copyTo(X.RDB$FILE_NAME, sizeof(X.RDB$FILE_NAME)); X.RDB$SHADOW_NUMBER = (SSHORT)shadow_number; X.RDB$FILE_FLAGS = 0; X.RDB$FILE_FLAGS.NULL = FALSE; X.RDB$FILE_START.NULL = TRUE; X.RDB$FILE_LENGTH.NULL = TRUE; while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_file_start: temp = DYN_get_number(ptr); *start = MAX(*start, temp); X.RDB$FILE_START = *start; X.RDB$FILE_START.NULL = FALSE; break; case isc_dyn_file_length: X.RDB$FILE_LENGTH = DYN_get_number(ptr); X.RDB$FILE_LENGTH.NULL = FALSE; break; case isc_dyn_shadow_man_auto: man_auto = (USHORT)DYN_get_number(ptr); if (man_auto) X.RDB$FILE_FLAGS |= FILE_manual; break; case isc_dyn_shadow_conditional: if (DYN_get_number(ptr)) X.RDB$FILE_FLAGS |= FILE_conditional; break; default: DYN_unsupported_verb(); } } *start += X.RDB$FILE_LENGTH; } END_STORE } catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); if (id == drq_l_files) DYN_error_punt(false, 166); else DYN_error_punt(true, msg); } } void DYN_define_difference(Global* gbl, const UCHAR** ptr) { /************************************** * * D Y N _ d e f i n e _ d i f f e r e n c e * ************************************** * * Functional description * Define backup difference file. * **************************************/ SSHORT id = -1; thread_db* tdbb = JRD_get_thread_data(); if (!tdbb->getAttachment()->locksmith()) { ERR_post(Arg::Gds(isc_adm_task_denied)); } try { bool found = false; id = drq_l_difference; AutoCacheRequest request(tdbb, drq_l_difference, DYN_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FIL IN RDB$FILES { if (FIL.RDB$FILE_FLAGS & FILE_difference) found = true; } END_FOR if (found) goto dyn_punt_216; request.reset(tdbb, drq_s_difference, DYN_REQUESTS); id = drq_s_difference; STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$FILES { GET_STRING(ptr, X.RDB$FILE_NAME); X.RDB$FILE_FLAGS = FILE_difference; X.RDB$FILE_FLAGS.NULL = FALSE; X.RDB$FILE_START = 0; X.RDB$FILE_START.NULL = FALSE; X.RDB$FILE_LENGTH.NULL = TRUE; X.RDB$SHADOW_NUMBER.NULL = TRUE; } END_STORE } catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); if (id == drq_s_difference) { DYN_error_punt(true, 150); // msg 150: STORE RDB$FILES failed } else { DYN_error_punt(true, 156); // msg 156: Difference file lookup failed } } return; dyn_punt_216: DYN_error_punt(false, 216); // msg 216: "Difference file is already defined" } void DYN_define_filter( Global* gbl, const UCHAR** ptr) { /************************************** * * D Y N _ d e f i n e _ f i l t e r * ************************************** * * Functional description * Define a blob filter. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); Firebird::MetaName filter_name; GET_STRING(ptr, filter_name); AutoCacheRequest request(tdbb, drq_s_filters, DYN_REQUESTS); bool b_ending_store = false; try { STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$FILTERS USING strcpy(X.RDB$FUNCTION_NAME, filter_name.c_str()); X.RDB$OUTPUT_SUB_TYPE.NULL = TRUE; X.RDB$INPUT_SUB_TYPE.NULL = TRUE; X.RDB$MODULE_NAME.NULL = TRUE; X.RDB$ENTRYPOINT.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; { UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_filter_in_subtype: X.RDB$INPUT_SUB_TYPE = (SSHORT)DYN_get_number(ptr); X.RDB$INPUT_SUB_TYPE.NULL = FALSE; break; case isc_dyn_filter_out_subtype: X.RDB$OUTPUT_SUB_TYPE = (SSHORT)DYN_get_number(ptr); X.RDB$OUTPUT_SUB_TYPE.NULL = FALSE; break; case isc_dyn_func_module_name: GET_STRING(ptr, X.RDB$MODULE_NAME); X.RDB$MODULE_NAME.NULL = FALSE; break; case isc_dyn_func_entry_point: GET_STRING(ptr, X.RDB$ENTRYPOINT); X.RDB$ENTRYPOINT.NULL = FALSE; break; default: DYN_unsupported_verb(); } } } END_STORE } catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); if (b_ending_store) { DYN_error_punt(true, 7); // msg 7: "DEFINE BLOB FILTER failed" } throw; } } void DYN_define_function( Global* gbl, const UCHAR** ptr) { /************************************** * * D Y N _ d e f i n e _ f u n c t i o n * ************************************** * * Functional description * Define a user defined function. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); Firebird::MetaName function_name; GET_STRING(ptr, function_name); DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, DDL_TRIGGER_CREATE_FUNCTION, function_name, gbl->sqlText); AutoCacheRequest request(tdbb, drq_s_funcs, DYN_REQUESTS); bool b_ending_store = false; try { STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$FUNCTIONS USING { strcpy(X.RDB$FUNCTION_NAME, function_name.c_str()); X.RDB$RETURN_ARGUMENT.NULL = TRUE; X.RDB$QUERY_NAME.NULL = TRUE; X.RDB$MODULE_NAME.NULL = TRUE; X.RDB$ENTRYPOINT.NULL = TRUE; X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; X.RDB$LEGACY_FLAG = 1; X.RDB$LEGACY_FLAG.NULL = FALSE; X.RDB$INVARIANT_FLAG.NULL = TRUE; X.RDB$ENGINE_NAME.NULL = TRUE; UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_func_return_argument: X.RDB$RETURN_ARGUMENT = (SSHORT)DYN_get_number(ptr); X.RDB$RETURN_ARGUMENT.NULL = FALSE; if (X.RDB$RETURN_ARGUMENT > MAX_UDF_ARGUMENTS) DYN_error_punt(true, 10); // msg 10: "DEFINE FUNCTION failed" break; case isc_dyn_func_module_name: GET_STRING(ptr, X.RDB$MODULE_NAME); X.RDB$MODULE_NAME.NULL = FALSE; break; case isc_dyn_fld_query_name: GET_STRING(ptr, X.RDB$QUERY_NAME); X.RDB$QUERY_NAME.NULL = FALSE; break; case isc_dyn_func_entry_point: GET_STRING(ptr, X.RDB$ENTRYPOINT); X.RDB$ENTRYPOINT.NULL = FALSE; break; default: --(*ptr); { MetaNameProxy tmp(X.RDB$FUNCTION_NAME); DYN_execute(gbl, ptr, NULL, NULL, NULL, &tmp, NULL); } } } b_ending_store = true; } END_STORE } catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); if (b_ending_store) { DYN_error_punt(true, 10); // msg 10: "DEFINE FUNCTION failed" } throw; } DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_CREATE_FUNCTION, function_name, gbl->sqlText); } void DYN_define_function_arg(Global* gbl, const UCHAR** ptr, Firebird::MetaName* function_name) { /************************************** * * D Y N _ d e f i n e _ f u n c t i o n _ a r g * ************************************** * * Functional description * Define a user defined function argument. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); try { AutoCacheRequest request(tdbb, drq_s_func_args, DYN_REQUESTS); STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$FUNCTION_ARGUMENTS { X.RDB$ARGUMENT_POSITION = (SSHORT)DYN_get_number(ptr); if (X.RDB$ARGUMENT_POSITION > MAX_UDF_ARGUMENTS) DYN_error_punt(true, 12); // msg 12: "DEFINE FUNCTION ARGUMENT failed" if (function_name) { strcpy(X.RDB$FUNCTION_NAME, function_name->c_str()); X.RDB$FUNCTION_NAME.NULL = FALSE; } else X.RDB$FUNCTION_NAME.NULL = TRUE; X.RDB$MECHANISM.NULL = TRUE; X.RDB$FIELD_TYPE.NULL = TRUE; X.RDB$FIELD_SCALE.NULL = TRUE; X.RDB$FIELD_LENGTH.NULL = TRUE; X.RDB$FIELD_SUB_TYPE.NULL = TRUE; X.RDB$CHARACTER_SET_ID.NULL = TRUE; X.RDB$FIELD_PRECISION.NULL = TRUE; X.RDB$CHARACTER_LENGTH.NULL = TRUE; UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) switch (verb) { case isc_dyn_function_name: GET_STRING(ptr, X.RDB$FUNCTION_NAME); X.RDB$FUNCTION_NAME.NULL = FALSE; break; case isc_dyn_func_mechanism: X.RDB$MECHANISM = (SSHORT)DYN_get_number(ptr); X.RDB$MECHANISM.NULL = FALSE; break; case isc_dyn_fld_type: X.RDB$FIELD_TYPE = (SSHORT)DYN_get_number(ptr); X.RDB$FIELD_TYPE.NULL = FALSE; break; case isc_dyn_fld_sub_type: X.RDB$FIELD_SUB_TYPE = (SSHORT)DYN_get_number(ptr); X.RDB$FIELD_SUB_TYPE.NULL = FALSE; break; case isc_dyn_fld_scale: X.RDB$FIELD_SCALE = (SSHORT)DYN_get_number(ptr); X.RDB$FIELD_SCALE.NULL = FALSE; break; case isc_dyn_fld_length: X.RDB$FIELD_LENGTH = (SSHORT)DYN_get_number(ptr); X.RDB$FIELD_LENGTH.NULL = FALSE; break; case isc_dyn_fld_character_set: X.RDB$CHARACTER_SET_ID = (SSHORT)DYN_get_number(ptr); X.RDB$CHARACTER_SET_ID.NULL = FALSE; break; case isc_dyn_fld_precision: X.RDB$FIELD_PRECISION = (SSHORT)DYN_get_number(ptr); X.RDB$FIELD_PRECISION.NULL = FALSE; break; // Ignore the field character length as the system UDF parameter // table has no place to store the information // But IB6/FB has the place for this information. CVC 2001. case isc_dyn_fld_char_length: X.RDB$CHARACTER_LENGTH = (SSHORT)DYN_get_number (ptr); X.RDB$CHARACTER_LENGTH.NULL = FALSE; break; default: DYN_unsupported_verb(); } } END_STORE } catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); DYN_error_punt(true, 12); // msg 12: "DEFINE FUNCTION ARGUMENT failed" } } void DYN_define_index(Global* gbl, const UCHAR** ptr, const Firebird::MetaName* relation_name, UCHAR index_type) { /************************************** * * D Y N _ d e f i n e _ i n d e x * ************************************** * * Functional description * Execute a dynamic ddl statement that creates an index. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); Firebird::MetaName index_name; UCHAR verb; Firebird::MetaName trigger_name; GET_STRING(ptr, index_name); if (index_name.isEmpty()) DYN_UTIL_generate_index_name(tdbb, gbl->gbl_transaction, index_name, index_type); DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, DDL_TRIGGER_CREATE_INDEX, index_name, gbl->sqlText); try { DYN_UTIL_check_unique_name(tdbb, gbl->gbl_transaction, index_name, obj_index); } catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); DYN_error_punt(true, 21); // msg 21: "STORE RDB$INDICES failed" } try { CreateIndexNode::Definition definition; definition.type = index_type; if (relation_name) definition.relation = *relation_name; else if (*(*ptr)++ == isc_dyn_rel_name) GET_STRING(ptr, definition.relation); else DYN_error_punt(false, 14); // msg 14: "No relation specified for index" while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_idx_unique: definition.unique = bool(DYN_get_number(ptr)); break; case isc_dyn_idx_inactive: definition.inactive = bool(DYN_get_number(ptr)); break; case isc_dyn_idx_type: definition.descending = bool(DYN_get_number(ptr)); break; case isc_dyn_fld_name: { MetaName& str = definition.columns.add(); GET_STRING(ptr, str); break; } case isc_dyn_fld_computed_blr: DYN_put_blr_blob(gbl, ptr, &definition.expressionBlr); break; case isc_dyn_fld_computed_source: DYN_put_text_blob(gbl, ptr, &definition.expressionSource); break; default: DYN_unsupported_verb(); } } CreateIndexNode::store(tdbb, gbl->gbl_transaction, index_name, definition); } catch (const Exception& ex) { stuff_exception(tdbb->tdbb_status_vector, ex); throw; } DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_CREATE_INDEX, index_name, gbl->sqlText); } void DYN_define_role( Global* gbl, const UCHAR** ptr) { /************************************** * * D Y N _ d e f i n e _ r o l e * ************************************** * * Functional description * * Define a SQL role. * ROLES cannot be named the same as any existing user name * **************************************/ thread_db* tdbb = JRD_get_thread_data(); Firebird::MetaName owner_name(tdbb->getAttachment()->att_user->usr_user_name); owner_name.upper7(); Firebird::MetaName role_name; GET_STRING(ptr, role_name); DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_BEFORE, DDL_TRIGGER_CREATE_ROLE, role_name, gbl->sqlText); if (role_name == owner_name) { // user name could not be used for SQL role DYN_error(false, 193, SafeArg() << owner_name.c_str()); ERR_punt(); } if (role_name == NULL_ROLE) { // keyword NONE could not be used as SQL role name DYN_error(false, 195, SafeArg() << role_name.c_str()); ERR_punt(); } try { if (is_it_user_name(gbl, role_name, tdbb)) { // user name could not be used for SQL role DYN_error(false, 193, SafeArg() << role_name.c_str()); goto do_err_punt; } Firebird::MetaName dummy_name; if (DYN_is_it_sql_role(gbl, role_name, dummy_name, tdbb)) { // SQL role @1 already exists DYN_error(false, 194, SafeArg() << role_name.c_str()); goto do_err_punt; } AutoCacheRequest request(tdbb, drq_role_gens, DYN_REQUESTS); STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) X IN RDB$ROLES { strcpy(X.RDB$ROLE_NAME, role_name.c_str()); strcpy(X.RDB$OWNER_NAME, owner_name.c_str()); X.RDB$SYSTEM_FLAG = 0; X.RDB$SYSTEM_FLAG.NULL = FALSE; } END_STORE if (*(*ptr)++ != isc_dyn_end) goto do_error_punt_9; } catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); DYN_error_punt(true, 8); // msg 8: "DEFINE ROLE failed" } DdlNode::executeDdlTrigger(tdbb, gbl->gbl_transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_CREATE_ROLE, role_name, gbl->sqlText); return; do_err_punt: ERR_punt(); return; do_error_punt_9: DYN_error_punt(true, 9); // msg 9: "DEFINE ROLE unexpected dyn verb" } void DYN_define_shadow( Global* gbl, const UCHAR** ptr) { /************************************** * * D Y N _ d e f i n e _ s h a d o w * ************************************** * * Functional description * Define a shadow. * **************************************/ thread_db* tdbb = JRD_get_thread_data(); bool found = false; const SLONG shadow_number = DYN_get_number(ptr); // If a shadow set identified by the shadow number already exists return error. AutoCacheRequest request(tdbb, drq_l_shadow, DYN_REQUESTS); try { FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) FIRST 1 X IN RDB$FILES WITH X.RDB$SHADOW_NUMBER EQ shadow_number { found = true; } END_FOR } catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); DYN_error_punt(true, 164); // msg 164: "Shadow lookup failed" } if (found) { DYN_error_punt(false, 165, SafeArg() << shadow_number); // msg 165: "Shadow %ld already exists" } SLONG start = 0; UCHAR verb; while ((verb = *(*ptr)++) != isc_dyn_end) { switch (verb) { case isc_dyn_def_file: DYN_define_file(gbl, ptr, shadow_number, &start, 157); break; default: DYN_unsupported_verb(); } } } bool is_it_user_name(Global* gbl, const Firebird::MetaName& role_name, thread_db* tdbb) { /************************************** * * i s _ i t _ u s e r _ n a m e * ************************************** * * Functional description * * if role_name is user name returns true. Otherwise returns false. * **************************************/ USHORT request_id; SET_TDBB(tdbb); bool found = false; try { // If there is a user with privilege or a grantor on a relation we // can infer there is a user with this name request_id = drq_get_user_priv; AutoCacheRequest request(tdbb, request_id, DYN_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) PRIV IN RDB$USER_PRIVILEGES WITH (PRIV.RDB$USER EQ role_name.c_str() AND PRIV.RDB$USER_TYPE = obj_user) OR (PRIV.RDB$GRANTOR EQ role_name.c_str() AND PRIV.RDB$OBJECT_TYPE = obj_relation) { found = true; } END_FOR if (found) return found; // We can infer that 'role_name' is a user name if it owns any relations // Note we can only get here if a user creates a table and revokes all // his privileges on the table request_id = drq_get_rel_owner; request.reset(tdbb, request_id, DYN_REQUESTS); FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction) REL IN RDB$RELATIONS WITH REL.RDB$OWNER_NAME EQ role_name.c_str() { found = true; } END_FOR } catch (const Firebird::Exception& ex) { Firebird::stuff_exception(tdbb->tdbb_status_vector, ex); ERR_punt(); } return found; }