mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-27 20:03:03 +01:00
1938 lines
48 KiB
Plaintext
1938 lines
48 KiB
Plaintext
/*
|
|
* PROGRAM: JRD Data Definition Utility
|
|
* MODULE: dyn.epp
|
|
* DESCRIPTION: Dynamic data definition
|
|
*
|
|
* The contents of this file are subject to the Interbase Public
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code was created by Inprise Corporation
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
* Copyright (C) Inprise Corporation.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*
|
|
* 2001.04.20 Claudio Valderrama: Fix bug in grant/revoke by making user
|
|
* case insensitive.
|
|
* 2001.05.24 Claudio Valderrama: Move DYN_delete_role to dyn_del.e.
|
|
* 2001.06.05 John Bellardo: Renamed the revoke static function to
|
|
* revoke_permission, because there is already
|
|
* a revoke(2) function in *nix.
|
|
* 2001.06.20 Claudio Valderrama: Enable calls to DYN_delete_generator.
|
|
* 2001.10.01 Claudio Valderrama: Enable explicit GRANT...to ROLE role_name.
|
|
* and complain if the grantee ROLE doesn't exist.
|
|
* 2001.10.06 Claudio Valderrama: Forbid "NONE" from role-related operations.
|
|
* Honor explicit USER keyword in GRANTs and REVOKEs. *
|
|
* 2002.08.10 Dmitry Yemanov: ALTER VIEW
|
|
* 2002.10.29 Nickolay Samofatov: Added support for savepoints
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "../jrd/common.h"
|
|
#include <stdarg.h>
|
|
#include "../jrd/jrd.h"
|
|
#include "../jrd/ods.h"
|
|
#include "../jrd/tra.h"
|
|
#include "../jrd/scl.h"
|
|
#include "../jrd/drq.h"
|
|
#include "../jrd/flags.h"
|
|
#include "../jrd/ibase.h"
|
|
#include "../jrd/lls.h"
|
|
#include "../jrd/all.h"
|
|
#include "../jrd/met.h"
|
|
#include "../jrd/btr.h"
|
|
#include "../jrd/intl.h"
|
|
#include "../jrd/dyn.h"
|
|
#include "../jrd/license.h"
|
|
#include "../jrd/all_proto.h"
|
|
#include "../jrd/blb_proto.h"
|
|
#include "../jrd/cmp_proto.h"
|
|
#include "../jrd/dpm_proto.h"
|
|
#include "../jrd/dyn_proto.h"
|
|
#include "../jrd/dyn_df_proto.h"
|
|
#include "../jrd/dyn_dl_proto.h"
|
|
#include "../jrd/dyn_md_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/thd.h"
|
|
#include "../jrd/vio_proto.h"
|
|
#include "../jrd/sch_proto.h"
|
|
#include "../jrd/thread_proto.h"
|
|
#include "../common/utils_proto.h"
|
|
|
|
DATABASE DB = STATIC "ODS.RDB";
|
|
|
|
using namespace Jrd;
|
|
|
|
|
|
static void grant(Global*, const UCHAR**);
|
|
static bool grantor_can_grant_role(thread_db*, Global*, const TEXT*, const TEXT*);
|
|
static bool grantor_can_grant(Global*, const TEXT*, const TEXT*, const TEXT*,
|
|
const TEXT*, bool);
|
|
static void revoke_permission(Global*, const UCHAR**);
|
|
static void store_privilege(Global*, const TEXT*, const TEXT*, const TEXT*,
|
|
const TEXT*, SSHORT, SSHORT, int);
|
|
static void set_field_class_name(Global*, const TEXT*, const TEXT*);
|
|
|
|
|
|
void DYN_ddl(Attachment* attachment, jrd_tra* transaction, USHORT length,
|
|
const UCHAR* ddl)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ d d l
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Do meta-data.
|
|
*
|
|
**************************************/
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
#if defined(V4_THREADING) || (defined(SUPERSERVER) && defined(WIN_NT))
|
|
Database* dbb = tdbb->tdbb_database;
|
|
#endif
|
|
|
|
const UCHAR* ptr = ddl;
|
|
|
|
if (*ptr++ != isc_dyn_version_1) {
|
|
ERR_post(isc_wrodynver, 0);
|
|
}
|
|
|
|
ISC_STATUS* status = tdbb->tdbb_status_vector;
|
|
*status++ = isc_arg_gds;
|
|
*status++ = 0;
|
|
*status = isc_arg_end;
|
|
|
|
Global gbl(transaction);
|
|
|
|
// Create a pool for DYN to operate in. It will be released when
|
|
// the routine exits.
|
|
JrdMemoryPool* tempPool = JrdMemoryPool::createPool();
|
|
Jrd::ContextPoolHolder context(tdbb, tempPool);
|
|
|
|
try {
|
|
|
|
#if defined(SUPERSERVER) && defined(WIN_NT)
|
|
THREAD_EXIT();
|
|
THD_MUTEX_LOCK(dbb->dbb_mutexes + DBB_MUTX_dyn);
|
|
THREAD_ENTER();
|
|
#else
|
|
#ifdef V4_THREADING
|
|
V4_JRD_MUTEX_LOCK(dbb->dbb_mutexes + DBB_MUTX_dyn);
|
|
#endif
|
|
#endif
|
|
|
|
VIO_start_save_point(tdbb, transaction);
|
|
transaction->tra_save_point->sav_verb_count++;
|
|
|
|
DYN_execute(&gbl, &ptr, NULL, NULL, NULL, NULL, NULL);
|
|
transaction->tra_save_point->sav_verb_count--;
|
|
VIO_verb_cleanup(tdbb, transaction);
|
|
|
|
#if defined(SUPERSERVER) && defined(WIN_NT)
|
|
THD_MUTEX_UNLOCK(dbb->dbb_mutexes + DBB_MUTX_dyn);
|
|
#else
|
|
#ifdef V4_THREADING
|
|
V4_JRD_MUTEX_UNLOCK(dbb->dbb_mutexes + DBB_MUTX_dyn);
|
|
#endif
|
|
#endif
|
|
|
|
}
|
|
catch (const std::exception& ex) {
|
|
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
const Savepoint* savepoint = transaction->tra_save_point;
|
|
if (savepoint && savepoint->sav_verb_count)
|
|
{
|
|
// An error during undo is very bad and has to be dealt with
|
|
// by aborting the transaction. The easiest way is to kill
|
|
// the application by calling bugcheck.
|
|
|
|
try {
|
|
VIO_verb_cleanup(tdbb, transaction);
|
|
}
|
|
catch (const std::exception&) {
|
|
BUGCHECK(290); /* msg 290 error during savepoint backout */
|
|
}
|
|
}
|
|
|
|
#if defined(SUPERSERVER) && defined(WIN_NT)
|
|
THD_MUTEX_UNLOCK(dbb->dbb_mutexes + DBB_MUTX_dyn);
|
|
#else
|
|
#ifdef V4_THREADING
|
|
V4_JRD_MUTEX_UNLOCK(dbb->dbb_mutexes + DBB_MUTX_dyn);
|
|
#endif
|
|
#endif
|
|
|
|
ERR_punt();
|
|
}
|
|
}
|
|
|
|
|
|
void DYN_error(bool status_flag,
|
|
USHORT number,
|
|
const TEXT* arg1,
|
|
const TEXT* arg2,
|
|
const TEXT* arg3,
|
|
const TEXT* arg4,
|
|
const TEXT* arg5)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* DDL failed.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
if (tdbb->tdbb_status_vector[1] == isc_no_meta_update)
|
|
return;
|
|
|
|
TEXT error_buffer[BUFFER_MEDIUM];
|
|
|
|
if (number)
|
|
gds__msg_format(NULL, DYN_MSG_FAC, number,
|
|
sizeof(TEXT) * BUFFER_MEDIUM, error_buffer, arg1,
|
|
arg2, arg3, arg4, arg5);
|
|
|
|
ISC_STATUS_ARRAY local_status;
|
|
ISC_STATUS* v1 = local_status;
|
|
|
|
*v1++ = isc_arg_gds;
|
|
*v1++ = isc_no_meta_update;
|
|
if (number) {
|
|
*v1++ = isc_arg_gds;
|
|
*v1++ = isc_random;
|
|
*v1++ = isc_arg_string;
|
|
*v1++ = (ISC_STATUS) ERR_cstring(error_buffer);
|
|
}
|
|
if (status_flag) {
|
|
const ISC_STATUS* v2 = tdbb->tdbb_status_vector;
|
|
|
|
/* check every other argument for end of vector */
|
|
|
|
ISC_STATUS arg;
|
|
ISC_STATUS* const end = local_status + sizeof(local_status) - 1;
|
|
while (v1 < end &&
|
|
((arg = *v2++) != isc_arg_cstring || v1 + 1 < end) &&
|
|
(*v1++ = arg)) {
|
|
*v1++ = *v2++;
|
|
if (arg == isc_arg_cstring)
|
|
*v1++ = *v2++;
|
|
}
|
|
}
|
|
*v1 = isc_arg_end;
|
|
ISC_STATUS* const end = v1 + 1;
|
|
|
|
v1 = local_status;
|
|
for (ISC_STATUS* dest = tdbb->tdbb_status_vector; v1 < end;)
|
|
*dest++ = *v1++;
|
|
}
|
|
|
|
|
|
void DYN_error_punt(bool status_flag,
|
|
USHORT number,
|
|
const TEXT* arg1,
|
|
const TEXT* arg2,
|
|
const TEXT* arg3,
|
|
const TEXT* arg4,
|
|
const TEXT* arg5)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ e r r o r _ p u n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* DDL failed.
|
|
*
|
|
**************************************/
|
|
|
|
DYN_error(status_flag, number, arg1, arg2, arg3, arg4, arg5);
|
|
ERR_punt();
|
|
}
|
|
|
|
|
|
bool DYN_is_it_sql_role(Global* gbl,
|
|
const TEXT* input_name,
|
|
TEXT* output_name,
|
|
thread_db* tdbb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ i s _ i t _ s q l _ r o l e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
* If input_name is found in RDB$ROLES, then returns true. Otherwise
|
|
* returns false.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
const USHORT major_version = dbb->dbb_ods_version;
|
|
const USHORT minor_original = dbb->dbb_minor_original;
|
|
bool found = false;
|
|
|
|
if (ENCODE_ODS(major_version, minor_original) < ODS_9_0)
|
|
return found;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_get_role_nm, DYN_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
X IN RDB$ROLES WITH
|
|
X.RDB$ROLE_NAME EQ input_name
|
|
if (!DYN_REQUEST(drq_get_role_nm))
|
|
DYN_REQUEST(drq_get_role_nm) = request;
|
|
|
|
found = true;
|
|
fb_utils::exact_name_limit(X.RDB$OWNER_NAME, sizeof(X.RDB$OWNER_NAME));
|
|
strcpy(output_name, X.RDB$OWNER_NAME);
|
|
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_get_role_nm))
|
|
DYN_REQUEST(drq_get_role_nm) = request;
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
void DYN_unsupported_verb(void)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ u n s u p p o r t e d _ v e r b
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* We encountered an unsupported dyn verb.
|
|
*
|
|
**************************************/
|
|
|
|
DYN_error_punt(false, 2, NULL, NULL, NULL, NULL, NULL); /* msg 2: "unsupported DYN verb" */
|
|
}
|
|
|
|
|
|
void DYN_execute(
|
|
Global* gbl,
|
|
const UCHAR** ptr,
|
|
const TEXT* relation_name,
|
|
TEXT* field_name,
|
|
TEXT* trigger_name,
|
|
TEXT* function_name, TEXT* procedure_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ e x e c u t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Execute a dynamic ddl statement.
|
|
*
|
|
**************************************/
|
|
UCHAR verb;
|
|
|
|
switch (verb = *(*ptr)++) {
|
|
case isc_dyn_begin:
|
|
while (**ptr != isc_dyn_end)
|
|
DYN_execute(gbl, ptr, relation_name, field_name,
|
|
trigger_name, function_name, procedure_name);
|
|
++(*ptr);
|
|
break;
|
|
|
|
/* Runtime security-related dynamic DDL should not require licensing.
|
|
A placeholder case statement for SQL 3 Roles is reserved below. */
|
|
|
|
case isc_dyn_grant:
|
|
grant(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_revoke:
|
|
revoke_permission(gbl, ptr);
|
|
break;
|
|
|
|
/***
|
|
case isc_dyn_def_role:
|
|
create_role (gbl, ptr);
|
|
break;
|
|
***/
|
|
default:
|
|
/* make sure that the license allows metadata operations */
|
|
|
|
switch (verb) {
|
|
case isc_dyn_mod_database:
|
|
DYN_modify_database(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_rel:
|
|
case isc_dyn_def_view:
|
|
DYN_define_relation(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_rel:
|
|
DYN_modify_relation(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_view:
|
|
DYN_modify_view(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_rel:
|
|
DYN_delete_relation(gbl, ptr, relation_name);
|
|
break;
|
|
|
|
case isc_dyn_def_security_class:
|
|
DYN_define_security_class(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_security_class:
|
|
DYN_delete_security_class(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_exception:
|
|
DYN_define_exception(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_exception:
|
|
DYN_modify_exception(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_del_exception:
|
|
DYN_delete_exception(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_filter:
|
|
DYN_define_filter(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_filter:
|
|
DYN_delete_filter(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_function:
|
|
DYN_define_function(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_function_arg:
|
|
DYN_define_function_arg(gbl, ptr, function_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_function:
|
|
DYN_delete_function(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_generator:
|
|
DYN_define_generator(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_sql_role:
|
|
DYN_define_role(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_del_sql_role:
|
|
DYN_delete_role(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_procedure:
|
|
DYN_define_procedure(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_mod_procedure:
|
|
DYN_modify_procedure(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_procedure:
|
|
DYN_delete_procedure(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_parameter:
|
|
DYN_define_parameter(gbl, ptr, procedure_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_parameter:
|
|
DYN_delete_parameter(gbl, ptr, procedure_name);
|
|
break;
|
|
|
|
case isc_dyn_def_shadow:
|
|
DYN_define_shadow(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_shadow:
|
|
DYN_delete_shadow(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_trigger:
|
|
DYN_define_trigger(gbl, ptr, relation_name, NULL, false);
|
|
break;
|
|
|
|
case isc_dyn_mod_trigger:
|
|
DYN_modify_trigger(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_trigger:
|
|
DYN_delete_trigger(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_trigger_msg:
|
|
DYN_define_trigger_msg(gbl, ptr, trigger_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_trigger_msg:
|
|
DYN_modify_trigger_msg(gbl, ptr, trigger_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_trigger_msg:
|
|
DYN_delete_trigger_msg(gbl, ptr, trigger_name);
|
|
break;
|
|
|
|
case isc_dyn_def_global_fld:
|
|
DYN_define_global_field(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_global_fld:
|
|
DYN_modify_global_field(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_global_fld:
|
|
DYN_delete_global_field(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_def_local_fld:
|
|
DYN_define_local_field(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_local_fld:
|
|
DYN_modify_local_field(gbl, ptr, relation_name, NULL);
|
|
break;
|
|
|
|
case isc_dyn_delete_local_fld:
|
|
DYN_delete_local_field(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_sql_fld:
|
|
DYN_modify_sql_field(gbl, ptr, relation_name, NULL);
|
|
break;
|
|
|
|
case isc_dyn_def_sql_fld:
|
|
DYN_define_sql_field(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_def_idx:
|
|
DYN_define_index(gbl, ptr, relation_name, verb, NULL, NULL, NULL,
|
|
NULL);
|
|
break;
|
|
|
|
case isc_dyn_rel_constraint:
|
|
DYN_define_constraint(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_rel_constraint:
|
|
DYN_delete_constraint(gbl, ptr, relation_name);
|
|
break;
|
|
|
|
case isc_dyn_mod_idx:
|
|
DYN_modify_index(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_delete_idx:
|
|
DYN_delete_index(gbl, ptr);
|
|
break;
|
|
|
|
case isc_dyn_view_relation:
|
|
DYN_define_view_relation(gbl, ptr, relation_name);
|
|
break;
|
|
|
|
case isc_dyn_def_dimension:
|
|
DYN_define_dimension(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_dimensions:
|
|
DYN_delete_dimensions(gbl, ptr, relation_name, field_name);
|
|
break;
|
|
|
|
case isc_dyn_delete_generator:
|
|
DYN_delete_generator(gbl, ptr);
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
SLONG DYN_get_number(const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ g e t _ n u m b e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Pick up a number and possible clear a null flag.
|
|
*
|
|
**************************************/
|
|
const UCHAR* p = *ptr;
|
|
USHORT length = *p++;
|
|
length |= (*p++) << 8;
|
|
*ptr = p + length;
|
|
|
|
return gds__vax_integer(p, length);
|
|
}
|
|
|
|
|
|
USHORT DYN_get_string(const TEXT** ptr, TEXT* field, USHORT size, bool err_flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ g e t _ s t r i n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Pick up a string, move to a target, and, if requested,
|
|
* set a null flag. Return length of string.
|
|
* If destination field size is too small, punt if err_flag is true,
|
|
* otherwise try to copy as much as possible and skip the rest.
|
|
* Strings need enough space for null pad.
|
|
*
|
|
**************************************/
|
|
USHORT e = 0;
|
|
const TEXT* p = *ptr;
|
|
USHORT length = (UCHAR) *p++;
|
|
length |= ((USHORT) ((UCHAR) (*p++))) << 8;
|
|
|
|
USHORT l = length;
|
|
if (l) {
|
|
if (length >= size) {
|
|
if (err_flag) {
|
|
DYN_error_punt(false, 159, NULL, NULL, NULL, NULL, NULL);
|
|
/* msg 159: Name longer than database field size */
|
|
}
|
|
else {
|
|
l = size - 1;
|
|
e = length - l;
|
|
}
|
|
}
|
|
do {
|
|
*field++ = *p++;
|
|
} while (--l);
|
|
for (; e; e--)
|
|
p++;
|
|
}
|
|
|
|
*field = 0;
|
|
*ptr = p;
|
|
|
|
return length;
|
|
}
|
|
|
|
|
|
TEXT* DYN_dup_string(MemoryPool& pool, const TEXT** ptr, bool trim, USHORT size)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ d u p _ s t r i n g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Duplicate a string from ptr.
|
|
* If size is > 0 and it is too small for string, punt.
|
|
* Returns newly allocated string.
|
|
*
|
|
**************************************/
|
|
const TEXT* p = *ptr;
|
|
USHORT length = (UCHAR) *p++;
|
|
length |= ((USHORT) ((UCHAR) (*p++))) << 8;
|
|
*ptr = p + length;
|
|
|
|
if (trim) {
|
|
while (length) {
|
|
if (p[length - 1] != ' ') {
|
|
break;
|
|
}
|
|
--length;
|
|
}
|
|
}
|
|
if (size > 0 && length >= size) {
|
|
DYN_error_punt(false, 159, NULL, NULL, NULL, NULL, NULL);
|
|
/* msg 159: Name longer than database field size */
|
|
}
|
|
|
|
return stringDup(pool, p, length);
|
|
}
|
|
|
|
|
|
USHORT DYN_put_blr_blob(Global* gbl, const UCHAR** ptr, bid* blob_id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ p u t _ b l r _ b l o b
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Write out a blr blob.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
const UCHAR *p = *ptr;
|
|
USHORT length = *p++;
|
|
length |= (*p++) << 8;
|
|
|
|
if (!length) {
|
|
*ptr = p;
|
|
return length;
|
|
}
|
|
|
|
try {
|
|
blb* blob = BLB_create(tdbb, gbl->gbl_transaction, blob_id);
|
|
BLB_put_segment(tdbb, blob, p, length);
|
|
BLB_close(tdbb, blob);
|
|
}
|
|
catch (const std::exception& ex) {
|
|
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_error_punt(true, 106, NULL, NULL, NULL, NULL, NULL);
|
|
/* msg 106: "Create metadata blob failed" */
|
|
}
|
|
|
|
*ptr = p + length;
|
|
|
|
return length;
|
|
}
|
|
|
|
USHORT DYN_put_text_blob(Global* gbl, const UCHAR** ptr, bid* blob_id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ p u t _ t e x t _ b l o b
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Write out a text blob.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
|
|
const UCHAR* p = *ptr;
|
|
USHORT length = *p++;
|
|
length |= (*p++) << 8;
|
|
|
|
if (!length) {
|
|
*ptr = p;
|
|
return length;
|
|
}
|
|
|
|
// make the code die at some place if DYN_error_punt doesn't jump far away.
|
|
const UCHAR* end = NULL;
|
|
|
|
try {
|
|
blb* blob = BLB_create(tdbb, gbl->gbl_transaction, blob_id);
|
|
|
|
for (end = p + length; p < end; p += TEXT_BLOB_LENGTH)
|
|
{
|
|
length = (p + TEXT_BLOB_LENGTH <= end) ? TEXT_BLOB_LENGTH : end - p;
|
|
BLB_put_segment(tdbb, blob, p, length);
|
|
}
|
|
BLB_close(tdbb, blob);
|
|
}
|
|
catch (const std::exception& ex) {
|
|
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_error_punt(true, 106, NULL, NULL, NULL, NULL, NULL);
|
|
/* msg 106: "Create metadata blob failed" */
|
|
}
|
|
|
|
*ptr = end;
|
|
|
|
return length;
|
|
}
|
|
|
|
void DYN_rundown_request(jrd_req* handle, SSHORT id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ r u n d o w n _ r e q u e s t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Unwind a request and save it
|
|
* in the dyn internal request list.
|
|
*
|
|
**************************************/
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
#pragma FB_COMPILER_MESSAGE("TMN: FIXME! We do not have a jmp_buf anymore!")
|
|
|
|
if (!handle) {
|
|
return;
|
|
}
|
|
|
|
EXE_unwind(tdbb, handle);
|
|
if (id >= 0 && !DYN_REQUEST(id)) {
|
|
DYN_REQUEST(id) = handle;
|
|
}
|
|
}
|
|
|
|
|
|
USHORT DYN_skip_attribute(const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* D Y N _ s k i p _ a t t r i b u t e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Skip over attribute returning length (excluding
|
|
* size of count bytes).
|
|
*
|
|
**************************************/
|
|
const UCHAR* p = *ptr;
|
|
USHORT length = *p++;
|
|
length |= (*p++) << 8;
|
|
*ptr = p + length;
|
|
|
|
return length;
|
|
}
|
|
|
|
|
|
static void grant( Global* gbl, const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g r a n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Execute SQL grant operation.
|
|
*
|
|
**************************************/
|
|
|
|
SSHORT id;
|
|
TEXT privileges[16];
|
|
SqlIdentifier object;
|
|
SqlIdentifier field;
|
|
SqlIdentifier user;
|
|
TEXT priv[2];
|
|
bool grant_role_stmt = false;
|
|
SqlIdentifier grantor;
|
|
TEXT* ptr1;
|
|
TEXT* ptr2;
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
const USHORT major_version = dbb->dbb_ods_version;
|
|
const USHORT minor_original = dbb->dbb_minor_original;
|
|
|
|
GET_STRING(ptr, privileges);
|
|
if (!strcmp(privileges, "A")) {
|
|
strcpy(privileges, ALL_PRIVILEGES);
|
|
}
|
|
|
|
object[0] = field[0] = user[0] = 0;
|
|
int options = 0;
|
|
SSHORT user_type = -1;
|
|
SSHORT obj_type = -1;
|
|
SqlIdentifier dummy_name;
|
|
|
|
UCHAR verb;
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
{
|
|
switch (verb) {
|
|
case isc_dyn_rel_name:
|
|
obj_type = obj_relation;
|
|
GET_STRING(ptr, object);
|
|
break;
|
|
|
|
case isc_dyn_prc_name:
|
|
obj_type = obj_procedure;
|
|
GET_STRING(ptr, object);
|
|
break;
|
|
|
|
case isc_dyn_fld_name:
|
|
GET_STRING(ptr, field);
|
|
break;
|
|
|
|
case isc_dyn_grant_user_group:
|
|
user_type = obj_user_group;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_user:
|
|
{
|
|
GET_STRING(ptr, user);
|
|
fb_utils::exact_name_limit(user, sizeof(user));
|
|
/* This test may become obsolete as we now allow explicit ROLE keyword. */
|
|
if (DYN_is_it_sql_role(gbl, user, dummy_name, tdbb)) {
|
|
user_type = obj_sql_role;
|
|
if (!strcmp(user, NULL_ROLE)) {
|
|
DYN_error_punt(false, 195, user, NULL, NULL, NULL, NULL);
|
|
/* msg 195: keyword NONE could not be used as SQL role name. */
|
|
}
|
|
}
|
|
else {
|
|
user_type = obj_user;
|
|
for (ptr1 = user; *ptr1; ptr1++) {
|
|
*ptr1 = UPPER7(*ptr1);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_grant_user_explicit:
|
|
GET_STRING(ptr, user);
|
|
fb_utils::exact_name_limit(user, sizeof(user));
|
|
user_type = obj_user;
|
|
for (ptr1 = user; *ptr1; ptr1++) {
|
|
*ptr1 = UPPER7(*ptr1);
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_grant_role:
|
|
user_type = obj_sql_role;
|
|
GET_STRING(ptr, user);
|
|
fb_utils::exact_name_limit(user, sizeof(user));
|
|
if (!DYN_is_it_sql_role(gbl, user, dummy_name, tdbb)) {
|
|
DYN_error_punt(false, 188, user, NULL, NULL, NULL, NULL);
|
|
/* msg 188: Role doesn't exist. */
|
|
}
|
|
if (!strcmp(user, NULL_ROLE)) {
|
|
DYN_error_punt(false, 195, user, NULL, NULL, NULL, NULL);
|
|
/* msg 195: keyword NONE could not be used as SQL role name. */
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_sql_role_name: /* role name in role_name_list */
|
|
if (ENCODE_ODS(major_version, minor_original) < ODS_9_0) {
|
|
DYN_error_punt(false, 196, NULL, NULL, NULL, NULL, NULL);
|
|
}
|
|
else {
|
|
obj_type = obj_sql_role;
|
|
GET_STRING(ptr, object);
|
|
grant_role_stmt = true;
|
|
fb_utils::exact_name_limit(object, sizeof(object));
|
|
if (!strcmp (object, NULL_ROLE)) {
|
|
DYN_error_punt(false, 195, object, NULL, NULL, NULL, NULL);
|
|
/* msg 195: keyword NONE could not be used as SQL role name. */
|
|
}
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_grant_proc:
|
|
user_type = obj_procedure;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_trig:
|
|
user_type = obj_trigger;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_view:
|
|
user_type = obj_view;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_options:
|
|
case isc_dyn_grant_admin_options:
|
|
options = DYN_get_number(ptr);
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
}
|
|
|
|
jrd_req* request = NULL;
|
|
|
|
try {
|
|
|
|
request = CMP_find_request(tdbb,
|
|
(USHORT)(field[0] ? drq_l_grant1 : drq_l_grant2),
|
|
DYN_REQUESTS);
|
|
for (const TEXT* pr = privileges; *pr; pr++)
|
|
{
|
|
bool duplicate = false;
|
|
priv[0] = *pr;
|
|
priv[1] = 0;
|
|
if (field[0])
|
|
{
|
|
id = drq_l_grant1;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES WITH
|
|
PRIV.RDB$RELATION_NAME EQ object AND
|
|
PRIV.RDB$OBJECT_TYPE = obj_type AND
|
|
PRIV.RDB$PRIVILEGE EQ priv AND
|
|
PRIV.RDB$USER = user AND
|
|
PRIV.RDB$USER_TYPE = user_type AND
|
|
PRIV.RDB$GRANTOR EQ UPPERCASE(RDB$USER_NAME) AND
|
|
PRIV.RDB$FIELD_NAME EQ field
|
|
if (!DYN_REQUEST(drq_l_grant1)) {
|
|
DYN_REQUEST(drq_l_grant1) = request;
|
|
}
|
|
|
|
if ((PRIV.RDB$GRANT_OPTION.NULL) ||
|
|
(PRIV.RDB$GRANT_OPTION) ||
|
|
(PRIV.RDB$GRANT_OPTION == options))
|
|
{
|
|
duplicate = true;
|
|
}
|
|
else
|
|
{
|
|
ERASE PRIV; /* has to be 0 and options == 1 */
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_l_grant1))
|
|
{
|
|
DYN_REQUEST(drq_l_grant1) = request;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
id = drq_l_grant2;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES WITH
|
|
PRIV.RDB$RELATION_NAME EQ object AND
|
|
PRIV.RDB$OBJECT_TYPE = obj_type AND
|
|
PRIV.RDB$PRIVILEGE EQ priv AND
|
|
PRIV.RDB$USER = user AND
|
|
PRIV.RDB$USER_TYPE = user_type AND
|
|
PRIV.RDB$GRANTOR EQ UPPERCASE(RDB$USER_NAME) AND
|
|
PRIV.RDB$FIELD_NAME MISSING
|
|
if (!DYN_REQUEST(drq_l_grant2))
|
|
DYN_REQUEST(drq_l_grant2) = request;
|
|
|
|
if ((PRIV.RDB$GRANT_OPTION.NULL) ||
|
|
(PRIV.RDB$GRANT_OPTION) ||
|
|
(PRIV.RDB$GRANT_OPTION == options))
|
|
{
|
|
duplicate = true;
|
|
}
|
|
else
|
|
{
|
|
ERASE PRIV; /* has to be 0 and options == 1 */
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_l_grant2))
|
|
{
|
|
DYN_REQUEST(drq_l_grant2) = request;
|
|
}
|
|
}
|
|
|
|
if (duplicate) {
|
|
continue;
|
|
}
|
|
|
|
if (grant_role_stmt)
|
|
{
|
|
if (ENCODE_ODS(major_version, minor_original) < ODS_9_0)
|
|
{
|
|
DYN_error_punt(false, 196, NULL, NULL, NULL, NULL, NULL);
|
|
}
|
|
id = drq_get_role_nm;
|
|
|
|
for (ptr1 = tdbb->tdbb_attachment->att_user->usr_user_name,
|
|
ptr2 = grantor; *ptr1; ptr1++, ptr2++)
|
|
{
|
|
*ptr2 = UPPER7(*ptr1);
|
|
}
|
|
*ptr2 = '\0';
|
|
|
|
if (!grantor_can_grant_role(tdbb, gbl, grantor, object)) {
|
|
// ERR_punt();
|
|
goto do_punt;
|
|
}
|
|
|
|
if (user_type == obj_sql_role) {
|
|
/****************************************************
|
|
**
|
|
** temporary restriction. This should be removed once
|
|
** GRANT role1 TO rolex is supported and this message
|
|
** could be reused for blocking cycles of role grants
|
|
**
|
|
*****************************************************/
|
|
DYN_error_punt(false, 192, user, NULL, NULL, NULL, NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* In the case where the object is a view, then the grantor must have
|
|
some kind of grant privileges on the base table(s)/view(s). If the
|
|
grantor is the owner of the view, then we have to explicitely check
|
|
this because the owner of a view by default has grant privileges on
|
|
his own view. If the grantor is not the owner of the view, then the
|
|
base table/view grant privilege checks were made when the grantor
|
|
got its grant privilege on the view and no further checks are
|
|
necessary. */
|
|
|
|
if (!obj_type)
|
|
{ /* relation or view because we cannot distinguish at this point. */
|
|
id = drq_gcg1;
|
|
if (!grantor_can_grant(gbl,
|
|
tdbb->tdbb_attachment->att_user->usr_user_name,
|
|
priv,
|
|
object,
|
|
field,
|
|
true))
|
|
{
|
|
// grantor_can_grant has moved the error string already.
|
|
// just punt back to the setjump
|
|
// ERR_punt();
|
|
goto do_punt;
|
|
}
|
|
}
|
|
}
|
|
|
|
id = drq_s_grant;
|
|
store_privilege(gbl, object, user, field, pr, user_type, obj_type,
|
|
options);
|
|
} // for (...)
|
|
|
|
} // try
|
|
catch (const std::exception& ex) {
|
|
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
/* we need to rundown as we have to set the env.
|
|
But in case the error is from store_priveledge we have already
|
|
unwound the request so passing that as null */
|
|
jrd_req* req1 = (id == drq_s_grant || id == drq_gcg1) ? NULL : request;
|
|
DYN_rundown_request(req1, -1);
|
|
|
|
switch (id)
|
|
{
|
|
case drq_l_grant1:
|
|
DYN_error_punt(true, 77, NULL, NULL, NULL, NULL, NULL);
|
|
// msg 77: "SELECT RDB$USER_PRIVILEGES failed in grant"
|
|
break;
|
|
case drq_l_grant2:
|
|
DYN_error_punt(true, 78, NULL, NULL, NULL, NULL, NULL);
|
|
// msg 78: "SELECT RDB$USER_PRIVILEGES failed in grant"
|
|
break;
|
|
case drq_s_grant:
|
|
case drq_gcg1:
|
|
ERR_punt();
|
|
// store_priviledge || grantor_can_grant error already handled,
|
|
// just bail out
|
|
break;
|
|
case drq_get_role_nm:
|
|
ERR_punt();
|
|
break;
|
|
default:
|
|
fb_assert(id == drq_gcg1);
|
|
DYN_error_punt(true, 78, NULL, NULL, NULL, NULL, NULL);
|
|
// msg 78: "SELECT RDB$USER_PRIVILEGES failed in grant"
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
do_punt: // ugly, rethink logic of this function
|
|
ERR_punt();
|
|
}
|
|
|
|
|
|
static bool grantor_can_grant( Global* gbl,
|
|
const TEXT* grantor,
|
|
const TEXT* privilege,
|
|
const TEXT* relation_name,
|
|
const TEXT* field_name,
|
|
bool top_level)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g r a n t o r _ c a n _ g r a n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* return: true is the grantor has grant privilege on the relation/field.
|
|
* false otherwise.
|
|
*
|
|
**************************************/
|
|
USHORT err_num;
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
/* Verify that the input relation exists. */
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_gcg4, DYN_REQUESTS);
|
|
|
|
|
|
try {
|
|
|
|
err_num = 182; /* for the longjump */
|
|
bool sql_relation = false;
|
|
bool relation_exists = false;
|
|
/* SELECT RDB$RELATIONS failed in grant */
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
REL IN RDB$RELATIONS WITH
|
|
REL.RDB$RELATION_NAME = relation_name
|
|
relation_exists = true;
|
|
if ((!REL.RDB$FLAGS.NULL) && (REL.RDB$FLAGS & REL_sql))
|
|
sql_relation = true;
|
|
if (!DYN_REQUEST(drq_gcg4))
|
|
DYN_REQUEST(drq_gcg4) = request;
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_gcg4))
|
|
DYN_REQUEST(drq_gcg4) = request;
|
|
if (!relation_exists) {
|
|
DYN_error(false, 175, relation_name, NULL, NULL, NULL, NULL);
|
|
/* table/view .. does not exist */
|
|
return false;
|
|
}
|
|
|
|
/* Verify the the input field exists. */
|
|
|
|
if (field_name[0])
|
|
{
|
|
err_num = 183;
|
|
bool field_exists = false;
|
|
|
|
/* SELECT RDB$RELATION_FIELDS failed in grant */
|
|
request = CMP_find_request(tdbb, drq_gcg5, DYN_REQUESTS);
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
G_FLD IN RDB$RELATION_FIELDS WITH
|
|
G_FLD.RDB$RELATION_NAME = relation_name AND
|
|
G_FLD.RDB$FIELD_NAME = field_name if (!DYN_REQUEST(drq_gcg5))
|
|
DYN_REQUEST(drq_gcg5) = request;
|
|
field_exists = true;
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_gcg5))
|
|
DYN_REQUEST(drq_gcg5) = request;
|
|
if (!field_exists) {
|
|
DYN_error(false, 176, field_name, relation_name, NULL, NULL,
|
|
NULL);
|
|
/* column .. does not exist in table/view .. */
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* If the current user is locksmith - allow all grants to occur */
|
|
|
|
if (tdbb->tdbb_attachment->att_user->usr_flags & USR_locksmith) {
|
|
return true;
|
|
}
|
|
|
|
/* If this is a non-sql table, then the owner will probably not have any
|
|
entries in the rdb$user_privileges table. Give the owner of a GDML
|
|
table all privileges. */
|
|
err_num = 184;
|
|
bool grantor_is_owner = false;
|
|
/* SELECT RDB$RELATIONS/RDB$OWNER_NAME failed in grant */
|
|
|
|
request = CMP_find_request(tdbb, drq_gcg2, DYN_REQUESTS);
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
REL IN RDB$RELATIONS WITH
|
|
REL.RDB$RELATION_NAME = relation_name AND
|
|
REL.RDB$OWNER_NAME = UPPERCASE(grantor)
|
|
if (!DYN_REQUEST(drq_gcg2))
|
|
DYN_REQUEST(drq_gcg2) = request;
|
|
grantor_is_owner = true;
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_gcg2))
|
|
DYN_REQUEST(drq_gcg2) = request;
|
|
if (!sql_relation && grantor_is_owner) {
|
|
return true;
|
|
}
|
|
|
|
// Remember the grant option for non field-specific user-privileges, and
|
|
// the grant option for the user-privileges for the input field.
|
|
// -1 = no privilege found (yet)
|
|
// 0 = privilege without grant option found
|
|
// 1 = privilege with grant option found
|
|
SSHORT go_rel = -1;
|
|
SSHORT go_fld = -1;
|
|
|
|
|
|
/* Verify that the grantor has the grant option for this relation/field
|
|
in the rdb$user_privileges. If not, then we don't need to look further. */
|
|
|
|
err_num = 185;
|
|
/* SELECT RDB$USER_PRIVILEGES failed in grant */
|
|
|
|
request = CMP_find_request(tdbb, drq_gcg1, DYN_REQUESTS);
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRV IN RDB$USER_PRIVILEGES WITH
|
|
PRV.RDB$USER = UPPERCASE(grantor) AND
|
|
PRV.RDB$USER_TYPE = obj_user AND
|
|
PRV.RDB$RELATION_NAME = relation_name AND
|
|
PRV.RDB$OBJECT_TYPE = obj_relation AND
|
|
PRV.RDB$PRIVILEGE = privilege if (!DYN_REQUEST(drq_gcg1))
|
|
DYN_REQUEST(drq_gcg1) = request;
|
|
|
|
if (PRV.RDB$FIELD_NAME.NULL) {
|
|
if (PRV.RDB$GRANT_OPTION.NULL || !PRV.RDB$GRANT_OPTION)
|
|
go_rel = 0;
|
|
else if (go_rel)
|
|
go_rel = 1;
|
|
}
|
|
else {
|
|
if (PRV.RDB$GRANT_OPTION.NULL || !PRV.RDB$GRANT_OPTION) {
|
|
fb_utils::exact_name_limit(PRV.RDB$FIELD_NAME, sizeof(PRV.RDB$FIELD_NAME));
|
|
if (field_name[0] && !strcmp(field_name, PRV.RDB$FIELD_NAME))
|
|
go_fld = 0;
|
|
}
|
|
else
|
|
{
|
|
fb_utils::exact_name_limit(PRV.RDB$FIELD_NAME, sizeof(PRV.RDB$FIELD_NAME));
|
|
if (field_name[0] && !strcmp(field_name, PRV.RDB$FIELD_NAME))
|
|
go_fld = 1;
|
|
}
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_gcg1))
|
|
{
|
|
DYN_REQUEST(drq_gcg1) = request;
|
|
}
|
|
|
|
if (field_name[0])
|
|
{
|
|
if (go_fld == 0)
|
|
{
|
|
DYN_error(false,
|
|
(USHORT)(top_level ? 167 : 168),
|
|
privilege,
|
|
field_name,
|
|
relation_name,
|
|
NULL,
|
|
NULL);
|
|
/* no grant option for privilege .. on column .. of [base] table/view .. */
|
|
return false;
|
|
}
|
|
else if (go_fld == -1)
|
|
{
|
|
if (go_rel == 0)
|
|
{
|
|
DYN_error(false,
|
|
(USHORT)(top_level ? 169 : 170),
|
|
privilege,
|
|
relation_name,
|
|
field_name,
|
|
NULL,
|
|
NULL);
|
|
/* no grant option for privilege .. on [base] table/view .. (for column ..) */
|
|
return false;
|
|
}
|
|
else if (go_rel == -1)
|
|
{
|
|
DYN_error(false,
|
|
(USHORT)(top_level ? 171 : 172),
|
|
privilege,
|
|
relation_name,
|
|
field_name,
|
|
NULL,
|
|
NULL);
|
|
/* no .. privilege with grant option on [base] table/view .. (for column ..) */
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (go_rel == 0)
|
|
{
|
|
DYN_error(false, 173, privilege, relation_name, NULL, NULL, NULL);
|
|
/* no grant option for privilege .. on table/view .. */
|
|
return false;
|
|
}
|
|
else if (go_rel == -1)
|
|
{
|
|
DYN_error(false, 174, privilege, relation_name, NULL, NULL, NULL);
|
|
/* no .. privilege with grant option on table/view .. */
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/* If the grantor is not the owner of the relation, then we don't need to
|
|
check the base table(s)/view(s) because that check was performed when
|
|
the grantor was given its privileges. */
|
|
|
|
if (!grantor_is_owner) {
|
|
return true;
|
|
}
|
|
|
|
/* Find all the base fields/relations and check for the correct
|
|
grant privileges on them. */
|
|
|
|
err_num = 186;
|
|
/* SELECT RDB$VIEW_RELATIONS/RDB$RELATION_FIELDS/... failed in grant */
|
|
|
|
request = CMP_find_request(tdbb, drq_gcg3, DYN_REQUESTS);
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
G_FLD IN RDB$RELATION_FIELDS CROSS
|
|
G_VIEW IN RDB$VIEW_RELATIONS WITH
|
|
G_FLD.RDB$RELATION_NAME = relation_name AND
|
|
G_FLD.RDB$BASE_FIELD NOT MISSING AND
|
|
G_VIEW.RDB$VIEW_NAME EQ G_FLD.RDB$RELATION_NAME AND
|
|
G_VIEW.RDB$VIEW_CONTEXT EQ G_FLD.RDB$VIEW_CONTEXT
|
|
if (!DYN_REQUEST(drq_gcg3)) {
|
|
DYN_REQUEST(drq_gcg3) = request;
|
|
}
|
|
fb_utils::exact_name_limit(G_FLD.RDB$BASE_FIELD, sizeof(G_FLD.RDB$BASE_FIELD));
|
|
fb_utils::exact_name_limit(G_FLD.RDB$FIELD_NAME, sizeof(G_FLD.RDB$FIELD_NAME));
|
|
fb_utils::exact_name_limit(G_VIEW.RDB$RELATION_NAME,
|
|
sizeof(G_VIEW.RDB$RELATION_NAME));
|
|
|
|
if (field_name[0])
|
|
{
|
|
if (!strcmp(field_name, G_FLD.RDB$FIELD_NAME))
|
|
if (!grantor_can_grant
|
|
(gbl, grantor, privilege, G_VIEW.RDB$RELATION_NAME,
|
|
G_FLD.RDB$BASE_FIELD, FALSE))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
if (!grantor_can_grant(gbl,
|
|
grantor,
|
|
privilege,
|
|
G_VIEW.RDB$RELATION_NAME,
|
|
G_FLD.RDB$BASE_FIELD,
|
|
false))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_gcg3))
|
|
DYN_REQUEST(drq_gcg3) = request;
|
|
|
|
// all done.
|
|
|
|
} // try
|
|
catch (const std::exception& ex) {
|
|
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, err_num, NULL, NULL, NULL, NULL, NULL);
|
|
/* msg 77: "SELECT RDB$USER_PRIVILEGES failed in grant" */
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
static bool grantor_can_grant_role( thread_db* tdbb,
|
|
Global* gbl,
|
|
const TEXT* grantor,
|
|
const TEXT* role_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g r a n t o r _ c a n _ g r a n t _ r o l e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
* return: true if the grantor has admin privilege on the role.
|
|
* false otherwise.
|
|
*
|
|
**************************************/
|
|
|
|
bool grantable = false;
|
|
SqlIdentifier owner;
|
|
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
if (tdbb->tdbb_attachment->att_user->usr_flags & USR_locksmith) {
|
|
return true;
|
|
}
|
|
|
|
// Fetch the name of the owner of the ROLE
|
|
if (DYN_is_it_sql_role(gbl, role_name, owner, tdbb))
|
|
{
|
|
// The owner of the ROLE can always grant membership
|
|
if (strcmp(owner, grantor) == 0) {
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/****************************************************
|
|
**
|
|
** role name not exist.
|
|
**
|
|
*****************************************************/
|
|
DYN_error(false, 188, role_name, NULL, NULL, NULL, NULL);
|
|
return false;
|
|
}
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_get_role_au, DYN_REQUESTS);
|
|
|
|
// The 'grantor' is not the owner of the ROLE, see if they
|
|
// have admin privilege on the role
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRV IN RDB$USER_PRIVILEGES WITH
|
|
PRV.RDB$USER = UPPERCASE(grantor) AND
|
|
PRV.RDB$USER_TYPE = obj_user AND
|
|
PRV.RDB$RELATION_NAME EQ role_name AND
|
|
PRV.RDB$OBJECT_TYPE = obj_sql_role AND
|
|
PRV.RDB$PRIVILEGE EQ "M"
|
|
if (!DYN_REQUEST(drq_get_role_au)) {
|
|
DYN_REQUEST(drq_get_role_au) = request;
|
|
}
|
|
|
|
if (PRV.RDB$GRANT_OPTION == 2) {
|
|
grantable = true;
|
|
}
|
|
else {
|
|
/****************************************************
|
|
**
|
|
** user have no admin option.
|
|
**
|
|
*****************************************************/
|
|
DYN_error(false, 189, grantor, role_name, NULL, NULL, NULL);
|
|
return false;
|
|
}
|
|
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_get_role_au)) {
|
|
DYN_REQUEST(drq_get_role_au) = request;
|
|
}
|
|
|
|
if (!grantable) {
|
|
/****************************************************
|
|
**
|
|
** user is not a member of the role.
|
|
**
|
|
*****************************************************/
|
|
DYN_error(false, 190, grantor, role_name, NULL, NULL, NULL);
|
|
return false;
|
|
}
|
|
|
|
return grantable;
|
|
}
|
|
|
|
|
|
static void revoke_permission(Global* gbl, const UCHAR** ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* r e v o k e _ p e r m i s s i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Revoke some privileges.
|
|
*
|
|
* Note: According to SQL II, section 11.37, pg 280,
|
|
* general rules 8 & 9,
|
|
* if the specified priviledge for the specified user
|
|
* does not exist, it is not an exception error condition
|
|
* but a completion condition. Since V4.0 does not support
|
|
* completion conditions (warnings) we do not return
|
|
* any indication that the revoke operation didn't do
|
|
* anything.
|
|
* 1994-August-2 Rich Damon & David Schnepper
|
|
*
|
|
**************************************/
|
|
UCHAR verb;
|
|
TEXT privileges[16];
|
|
SqlIdentifier object, field, user;
|
|
SqlIdentifier revoking_user_name, dummy_name;
|
|
TEXT* ptr1;
|
|
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
const USHORT major_version = dbb->dbb_ods_version;
|
|
const USHORT minor_original = dbb->dbb_minor_original;
|
|
|
|
/* Stash away a copy of the revoker's name, in uppercase form */
|
|
|
|
const UserId* revoking_user = tdbb->tdbb_attachment->att_user;
|
|
{ // this block to make sure there's no other bug due to wrong scope
|
|
TEXT* q = revoking_user_name;
|
|
for (const TEXT* p = revoking_user->usr_user_name; *p; p++, q++)
|
|
*q = UPPER7(*p);
|
|
*q = 0;
|
|
} // end scope-only block
|
|
|
|
GET_STRING(ptr, privileges);
|
|
if (!strcmp(privileges, "A")) {
|
|
strcpy(privileges, ALL_PRIVILEGES);
|
|
}
|
|
|
|
object[0] = field[0] = user[0] = 0;
|
|
|
|
int options = 0;
|
|
SSHORT user_type = -1;
|
|
SSHORT obj_type = -1;
|
|
|
|
while ((verb = *(*ptr)++) != isc_dyn_end)
|
|
switch (verb) {
|
|
case isc_dyn_rel_name:
|
|
obj_type = obj_relation;
|
|
GET_STRING(ptr, object);
|
|
break;
|
|
|
|
case isc_dyn_prc_name:
|
|
obj_type = obj_procedure;
|
|
GET_STRING(ptr, object);
|
|
break;
|
|
|
|
case isc_dyn_fld_name:
|
|
GET_STRING(ptr, field);
|
|
break;
|
|
|
|
case isc_dyn_grant_user_group:
|
|
user_type = obj_user_group;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_user:
|
|
GET_STRING(ptr, user);
|
|
fb_utils::exact_name_limit(user, sizeof(user));
|
|
/* This test may become obsolete as we now allow explicit ROLE keyword. */
|
|
if (DYN_is_it_sql_role(gbl, user, dummy_name, tdbb)) {
|
|
user_type = obj_sql_role;
|
|
if (!strcmp(user, NULL_ROLE)) {
|
|
DYN_error_punt(false, 195, user, NULL, NULL, NULL, NULL);
|
|
/* msg 195: keyword NONE could not be used as SQL role name. */
|
|
}
|
|
}
|
|
else {
|
|
user_type = obj_user;
|
|
for (ptr1 = user; *ptr1; ptr1++) {
|
|
*ptr1 = UPPER7(*ptr1);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_grant_user_explicit:
|
|
GET_STRING(ptr, user);
|
|
fb_utils::exact_name_limit(user, sizeof(user));
|
|
user_type = obj_user;
|
|
for (ptr1 = user; *ptr1; ptr1++) {
|
|
*ptr1 = UPPER7(*ptr1);
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_grant_role:
|
|
user_type = obj_sql_role;
|
|
GET_STRING(ptr, user);
|
|
fb_utils::exact_name_limit(user, sizeof(user));
|
|
if (!DYN_is_it_sql_role(gbl, user, dummy_name, tdbb)) {
|
|
DYN_error_punt(false, 188, user, NULL, NULL, NULL, NULL);
|
|
/* msg 188: Role doesn't exist. */
|
|
}
|
|
if (!strcmp(user, NULL_ROLE)) {
|
|
DYN_error_punt(false, 195, user, NULL, NULL, NULL, NULL);
|
|
/* msg 195: keyword NONE could not be used as SQL role name. */
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_sql_role_name: /* role name in role_name_list */
|
|
if (ENCODE_ODS(major_version, minor_original) < ODS_9_0) {
|
|
DYN_error_punt(false, 196, NULL, NULL, NULL, NULL, NULL);
|
|
}
|
|
else {
|
|
obj_type = obj_sql_role;
|
|
GET_STRING(ptr, object);
|
|
fb_utils::exact_name_limit(object, sizeof(object));
|
|
/* CVC: Make this a warning in the future.
|
|
if (!strcmp(object, NULL_ROLE))
|
|
DYN_error_punt(false, 195, object, NULL, NULL, NULL, NULL);
|
|
*/
|
|
/* msg 195: keyword NONE could not be used as SQL role name. */
|
|
}
|
|
break;
|
|
|
|
case isc_dyn_grant_proc:
|
|
user_type = obj_procedure;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_trig:
|
|
user_type = obj_trigger;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_view:
|
|
user_type = obj_view;
|
|
GET_STRING(ptr, user);
|
|
break;
|
|
|
|
case isc_dyn_grant_options:
|
|
case isc_dyn_grant_admin_options:
|
|
options = DYN_get_number(ptr);
|
|
break;
|
|
|
|
default:
|
|
DYN_unsupported_verb();
|
|
}
|
|
|
|
|
|
jrd_req* request = CMP_find_request(tdbb,
|
|
(USHORT)(field[0] ? drq_e_grant1 : drq_e_grant2),
|
|
DYN_REQUESTS);
|
|
USHORT id = field[0] ? drq_e_grant1 : drq_e_grant2;
|
|
|
|
try {
|
|
|
|
TEXT temp[2];
|
|
temp[1] = 0;
|
|
for (const TEXT* pr = privileges; (temp[0] = *pr); pr++)
|
|
{
|
|
bool grant_erased = false;
|
|
|
|
if (field[0])
|
|
{
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES WITH
|
|
PRIV.RDB$PRIVILEGE EQ temp AND
|
|
PRIV.RDB$RELATION_NAME EQ object AND
|
|
PRIV.RDB$OBJECT_TYPE = obj_type AND
|
|
PRIV.RDB$USER = user AND
|
|
PRIV.RDB$USER_TYPE = user_type AND
|
|
PRIV.RDB$FIELD_NAME EQ field
|
|
if (!DYN_REQUEST(drq_e_grant1))
|
|
DYN_REQUEST(drq_e_grant1) = request;
|
|
|
|
fb_utils::exact_name_limit(PRIV.RDB$GRANTOR, sizeof(PRIV.RDB$GRANTOR));
|
|
if ((revoking_user->usr_flags & USR_locksmith) ||
|
|
(!strcmp(PRIV.RDB$GRANTOR, revoking_user_name)))
|
|
{
|
|
ERASE PRIV;
|
|
grant_erased = true;
|
|
}
|
|
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_e_grant1)) {
|
|
DYN_REQUEST(drq_e_grant1) = request;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES WITH
|
|
PRIV.RDB$PRIVILEGE EQ temp AND
|
|
PRIV.RDB$RELATION_NAME EQ object AND
|
|
PRIV.RDB$OBJECT_TYPE = obj_type AND
|
|
PRIV.RDB$USER EQ user AND
|
|
PRIV.RDB$USER_TYPE = user_type
|
|
if (!DYN_REQUEST(drq_e_grant2))
|
|
DYN_REQUEST(drq_e_grant2) = request;
|
|
|
|
fb_utils::exact_name_limit(PRIV.RDB$GRANTOR, sizeof(PRIV.RDB$GRANTOR));
|
|
|
|
/* revoking a permission at the table level implies
|
|
revoking the perm. on all columns. So for all fields
|
|
in this table which have been granted the privilege, we
|
|
erase the entries from RDB$USER_PRIVILEGES. */
|
|
|
|
if ((revoking_user->usr_flags & USR_locksmith) ||
|
|
(!strcmp(PRIV.RDB$GRANTOR, revoking_user_name))) {
|
|
ERASE PRIV;
|
|
grant_erased = true;
|
|
}
|
|
END_FOR;
|
|
if (!DYN_REQUEST(drq_e_grant2))
|
|
DYN_REQUEST(drq_e_grant2) = request;
|
|
}
|
|
|
|
if (options && grant_erased) {
|
|
/* Add the privilege without the grant option
|
|
* There is a modify trigger on the rdb$user_privileges
|
|
* which disallows the table from being updated. It would
|
|
* have to be changed such that only the grant_option
|
|
* field can be updated.
|
|
*/
|
|
|
|
const USHORT old_id = id;
|
|
id = drq_s_grant;
|
|
|
|
store_privilege(gbl, object, user, field, pr, user_type, obj_type,
|
|
0);
|
|
|
|
id = old_id;
|
|
}
|
|
}
|
|
|
|
}
|
|
catch (const std::exception& ex) {
|
|
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
/* we need to rundown as we have to set the env.
|
|
But in case the error is from store_priveledge we have already
|
|
unwound the request so passing that as null */
|
|
DYN_rundown_request(((id == drq_s_grant) ? NULL : request),
|
|
-1);
|
|
if (id == drq_e_grant1)
|
|
{
|
|
DYN_error_punt(true, 111, NULL, NULL, NULL, NULL, NULL);
|
|
/* msg 111: "ERASE RDB$USER_PRIVILEGES failed in revoke(1)" */
|
|
}
|
|
else if (id == drq_e_grant2)
|
|
{
|
|
DYN_error_punt(true, 113, NULL, NULL, NULL, NULL, NULL);
|
|
/* msg 113: "ERASE RDB$USER_PRIVILEGES failed in revoke (3)" */
|
|
}
|
|
else
|
|
{
|
|
ERR_punt();
|
|
/* store_priviledge error already handled, just bail out */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void set_field_class_name(Global* gbl, const TEXT* relation, const TEXT* field)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s e t _ f i e l d _ c l a s s _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* For field level grants, be sure the
|
|
* field has a unique class name.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_s_f_class, DYN_REQUESTS);
|
|
jrd_req* request2 = NULL;
|
|
|
|
bool unique = false;
|
|
|
|
FOR (REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
RFR IN RDB$RELATION_FIELDS
|
|
WITH RFR.RDB$FIELD_NAME = field AND
|
|
RFR.RDB$RELATION_NAME = relation AND
|
|
RFR.RDB$SECURITY_CLASS MISSING
|
|
|
|
MODIFY RFR
|
|
while (!unique)
|
|
{
|
|
sprintf(RFR.RDB$SECURITY_CLASS, "%s%" QUADFORMAT "d", "SQL$GRANT",
|
|
DPM_gen_id(tdbb,
|
|
MET_lookup_generator(tdbb, "RDB$SECURITY_CLASS"), false, (SINT64) 1));
|
|
|
|
unique = true;
|
|
request2 = CMP_find_request(tdbb, drq_s_u_class, DYN_REQUESTS);
|
|
FOR (REQUEST_HANDLE request2 TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
RFR1 IN RDB$RELATION_FIELDS
|
|
WITH RFR1.RDB$SECURITY_CLASS = RFR.RDB$SECURITY_CLASS
|
|
unique = false;
|
|
END_FOR;
|
|
}
|
|
|
|
RFR.RDB$SECURITY_CLASS.NULL = FALSE;
|
|
END_MODIFY;
|
|
|
|
END_FOR;
|
|
|
|
if (!DYN_REQUEST(drq_s_f_class))
|
|
DYN_REQUEST(drq_s_f_class) = request;
|
|
|
|
|
|
if (request2 && !DYN_REQUEST(drq_s_u_class))
|
|
DYN_REQUEST(drq_s_u_class) = request2;
|
|
}
|
|
|
|
|
|
static void store_privilege(Global* gbl,
|
|
const TEXT* object,
|
|
const TEXT* user,
|
|
const TEXT* field,
|
|
const TEXT* privilege,
|
|
SSHORT user_type,
|
|
SSHORT obj_type,
|
|
int option)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s t o r e _ p r i v i l e g e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Does its own cleanup in case of error, so calling
|
|
* routine should not.
|
|
*
|
|
**************************************/
|
|
thread_db* tdbb = JRD_get_thread_data();
|
|
Database* dbb = tdbb->tdbb_database;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, drq_s_grant, DYN_REQUESTS);
|
|
|
|
/* need to unwind our own request here!! SM 27-Sep-96 */
|
|
|
|
try {
|
|
STORE(REQUEST_HANDLE request TRANSACTION_HANDLE gbl->gbl_transaction)
|
|
PRIV IN RDB$USER_PRIVILEGES PRIV.RDB$FIELD_NAME.NULL = TRUE;
|
|
strcpy(PRIV.RDB$RELATION_NAME, object);
|
|
strcpy(PRIV.RDB$USER, user);
|
|
PRIV.RDB$USER_TYPE = user_type;
|
|
PRIV.RDB$OBJECT_TYPE = obj_type;
|
|
if (field && field[0]) {
|
|
strcpy(PRIV.RDB$FIELD_NAME, field);
|
|
PRIV.RDB$FIELD_NAME.NULL = FALSE;
|
|
set_field_class_name(gbl, object, field);
|
|
}
|
|
PRIV.RDB$PRIVILEGE[0] = privilege[0];
|
|
PRIV.RDB$PRIVILEGE[1] = 0;
|
|
PRIV.RDB$GRANT_OPTION = option;
|
|
|
|
END_STORE;
|
|
|
|
if (!DYN_REQUEST(drq_s_grant)) {
|
|
DYN_REQUEST(drq_s_grant) = request;
|
|
}
|
|
}
|
|
catch (const std::exception& ex) {
|
|
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
|
|
DYN_rundown_request(request, -1);
|
|
DYN_error_punt(true, 79, NULL, NULL, NULL, NULL, NULL);
|
|
/* msg 79: "STORE RDB$USER_PRIVILEGES failed in grant" */
|
|
}
|
|
}
|
|
|