mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-23 04:43:03 +01:00
Implemented improvement CORE-1928 : Allow EXECUTE STATEMENT to inherit access privileges of caller stored procedure or trigger.
Allow to specify EXECUTE STATEMENToptional clauses in any (not fixed) order. Use INTERNAL_PROVIDER for access to the current database by another user too.
This commit is contained in:
parent
d331961962
commit
5d9c430674
@ -72,6 +72,7 @@ static void gen_for_select(CompiledStatement*, const dsql_nod*);
|
||||
static void gen_gen_id(CompiledStatement*, const dsql_nod*);
|
||||
static void gen_join_rse(CompiledStatement*, const dsql_nod*);
|
||||
static void gen_map(CompiledStatement*, dsql_map*);
|
||||
static inline void gen_optional_expr(CompiledStatement*, dsql_nod*);
|
||||
static void gen_parameter(CompiledStatement*, const dsql_par*);
|
||||
static void gen_plan(CompiledStatement*, const dsql_nod*);
|
||||
static void gen_relation(CompiledStatement*, dsql_ctx*);
|
||||
@ -1101,11 +1102,12 @@ void GEN_statement( CompiledStatement* statement, dsql_nod* node)
|
||||
case nod_exec_stmt:
|
||||
{ // scope
|
||||
const bool old_syntax =
|
||||
(node->nod_arg[e_exec_stmt_inputs] == NULL) &&
|
||||
(node->nod_arg[e_exec_stmt_data_src]->nod_type == nod_null) &&
|
||||
(node->nod_arg[e_exec_stmt_user]->nod_type == nod_null) &&
|
||||
(node->nod_arg[e_exec_stmt_pwd]->nod_type == nod_null) &&
|
||||
(node->nod_arg[e_exec_stmt_tran]->nod_flags == NOD_TRAN_COMMON);
|
||||
!node->nod_arg[e_exec_stmt_inputs] &&
|
||||
!node->nod_arg[e_exec_stmt_data_src] &&
|
||||
!node->nod_arg[e_exec_stmt_user] &&
|
||||
!node->nod_arg[e_exec_stmt_pwd] &&
|
||||
!node->nod_arg[e_exec_stmt_tran] &&
|
||||
!node->nod_arg[e_exec_stmt_privs];
|
||||
|
||||
const bool old_exec_into = old_syntax && node->nod_arg[e_exec_stmt_outputs];
|
||||
|
||||
@ -1144,13 +1146,22 @@ void GEN_statement( CompiledStatement* statement, dsql_nod* node)
|
||||
if (!old_syntax)
|
||||
{
|
||||
// external data source, user and password
|
||||
GEN_expr(statement, node->nod_arg[e_exec_stmt_data_src]);
|
||||
GEN_expr(statement, node->nod_arg[e_exec_stmt_user]);
|
||||
GEN_expr(statement, node->nod_arg[e_exec_stmt_pwd]);
|
||||
gen_optional_expr(statement, node->nod_arg[e_exec_stmt_data_src]);
|
||||
gen_optional_expr(statement, node->nod_arg[e_exec_stmt_user]);
|
||||
gen_optional_expr(statement, node->nod_arg[e_exec_stmt_pwd]);
|
||||
|
||||
// statement's transaction behavior
|
||||
stuff(statement, (UCHAR) node->nod_arg[e_exec_stmt_tran]->nod_flags);
|
||||
const dsql_nod* opt = node->nod_arg[e_exec_stmt_tran];
|
||||
stuff(statement, (UCHAR) opt ? opt->nod_flags : NOD_TRAN_DEFAULT);
|
||||
stuff(statement, 0); // transaction parameters equal to current transaction
|
||||
|
||||
// inherit caller's privileges ?
|
||||
if (node->nod_arg[e_exec_stmt_privs]) {
|
||||
stuff(statement, 1);
|
||||
}
|
||||
else {
|
||||
stuff(statement, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// singleton flag and proc block body
|
||||
@ -2024,6 +2035,13 @@ static void gen_map( CompiledStatement* statement, dsql_map* map)
|
||||
}
|
||||
}
|
||||
|
||||
static void gen_optional_expr(CompiledStatement* statement, dsql_nod* node)
|
||||
{
|
||||
if (node)
|
||||
GEN_expr(statement, node);
|
||||
else
|
||||
stuff(statement, blr_null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
|
@ -95,6 +95,7 @@ static const TOK tokens[] =
|
||||
{BOTH, "BOTH", 2, false},
|
||||
{KW_BREAK, "BREAK", 2, true},
|
||||
{BY, "BY", 1, false},
|
||||
{CALLER, "CALLER", 2, true},
|
||||
{CASCADE, "CASCADE", 1, true},
|
||||
{CASE, "CASE", 2, false},
|
||||
{CAST, "CAST", 1, false},
|
||||
|
@ -366,6 +366,10 @@ enum nod_t
|
||||
nod_del_user,
|
||||
nod_exec_stmt,
|
||||
nod_exec_stmt_inputs, // 290
|
||||
nod_exec_stmt_datasrc,
|
||||
nod_exec_stmt_user,
|
||||
nod_exec_stmt_pwd,
|
||||
nod_exec_stmt_privs,
|
||||
nod_tran_params,
|
||||
nod_named_param,
|
||||
nod_dfl_collate,
|
||||
@ -436,11 +440,13 @@ enum node_args {
|
||||
e_exec_stmt_inputs,
|
||||
e_exec_stmt_outputs,
|
||||
e_exec_stmt_proc_block,
|
||||
e_exec_stmt_label,
|
||||
e_exec_stmt_options,
|
||||
e_exec_stmt_data_src,
|
||||
e_exec_stmt_user,
|
||||
e_exec_stmt_pwd,
|
||||
e_exec_stmt_tran,
|
||||
e_exec_stmt_label,
|
||||
e_exec_stmt_privs,
|
||||
e_exec_stmt_count,
|
||||
|
||||
e_exec_stmt_inputs_sql = 0, // nod_exec_stmt_inputs
|
||||
@ -1090,7 +1096,8 @@ enum nod_flags_vals {
|
||||
|
||||
NOD_TRAN_AUTONOMOUS = 1, // nod_exec_stmt
|
||||
NOD_TRAN_COMMON = 2,
|
||||
NOD_TRAN_2PC = 3
|
||||
NOD_TRAN_2PC = 3,
|
||||
NOD_TRAN_DEFAULT = NOD_TRAN_COMMON
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -551,6 +551,7 @@ inline void check_copy_incr(char*& to, const char ch, const char* const string)
|
||||
%token SIMILAR
|
||||
%token UUID_TO_CHAR
|
||||
// new execute statement
|
||||
%token CALLER
|
||||
%token COMMON
|
||||
%token DATA
|
||||
%token SOURCE
|
||||
@ -1751,32 +1752,29 @@ for_select : label_opt FOR select INTO variable_list cursor_def DO proc_block
|
||||
// ;
|
||||
|
||||
exec_sql
|
||||
: EXECUTE STATEMENT exec_stmt_inputs
|
||||
ext_datasrc_opt ext_user_opt ext_pwd_opt ext_tran_opt
|
||||
: EXECUTE STATEMENT exec_stmt_inputs exec_stmt_options
|
||||
{
|
||||
$$ = make_node (nod_exec_stmt, int (e_exec_stmt_count),
|
||||
($3)->nod_arg[0], ($3)->nod_arg[1], NULL, NULL, $4, $5, $6, $7, NULL);
|
||||
($3)->nod_arg[0], ($3)->nod_arg[1], NULL, NULL, NULL, make_list($4), NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
;
|
||||
|
||||
exec_into
|
||||
: EXECUTE STATEMENT exec_stmt_inputs
|
||||
ext_datasrc_opt ext_user_opt ext_pwd_opt ext_tran_opt
|
||||
: EXECUTE STATEMENT exec_stmt_inputs exec_stmt_options
|
||||
INTO variable_list
|
||||
{
|
||||
$$ = make_node (nod_exec_stmt, int (e_exec_stmt_count),
|
||||
($3)->nod_arg[0], ($3)->nod_arg[1], make_list ($9), NULL, $4, $5, $6, $7, NULL);
|
||||
($3)->nod_arg[0], ($3)->nod_arg[1], make_list($6), NULL, NULL, make_list($4), NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
;
|
||||
|
||||
for_exec_into
|
||||
: label_opt FOR EXECUTE STATEMENT exec_stmt_inputs
|
||||
ext_datasrc_opt ext_user_opt ext_pwd_opt ext_tran_opt
|
||||
: label_opt FOR EXECUTE STATEMENT exec_stmt_inputs exec_stmt_options
|
||||
INTO variable_list
|
||||
DO proc_block
|
||||
{
|
||||
$$ = make_node (nod_exec_stmt, int (e_exec_stmt_count),
|
||||
($5)->nod_arg[0], ($5)->nod_arg[1], make_list ($11), $13, $6, $7, $8, $9, $1);
|
||||
($5)->nod_arg[0], ($5)->nod_arg[1], make_list($8), $10, $1, make_list($6), NULL, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
;
|
||||
|
||||
@ -1811,38 +1809,55 @@ not_named_param
|
||||
{ $$ = make_node (nod_named_param, e_named_param_count, NULL, $1); }
|
||||
;
|
||||
|
||||
ext_datasrc_opt
|
||||
exec_stmt_options
|
||||
: exec_stmt_options_list
|
||||
|
|
||||
{ $$ = NULL; }
|
||||
;
|
||||
|
||||
exec_stmt_options_list
|
||||
: exec_stmt_options_list exec_stmt_option
|
||||
{ $$ = make_node (nod_list, 2, $1, $2); }
|
||||
| exec_stmt_option
|
||||
;
|
||||
|
||||
exec_stmt_option
|
||||
: ext_datasrc
|
||||
| ext_user
|
||||
| ext_pwd
|
||||
| ext_tran
|
||||
| ext_privs
|
||||
;
|
||||
|
||||
ext_datasrc
|
||||
: ON EXTERNAL DATA SOURCE value
|
||||
{ $$ = $5; }
|
||||
{ $$ = make_node (nod_exec_stmt_datasrc, 1, $5); }
|
||||
| ON EXTERNAL value
|
||||
{ $$ = $3; }
|
||||
|
|
||||
{ $$ = make_node (nod_null, 0, NULL); }
|
||||
{ $$ = make_node (nod_exec_stmt_datasrc, 1, $3); }
|
||||
;
|
||||
|
||||
ext_user_opt
|
||||
ext_user
|
||||
: AS USER value
|
||||
{ $$ = $3; }
|
||||
|
|
||||
{ $$ = make_node (nod_null, 0, NULL); }
|
||||
{ $$ = make_node (nod_exec_stmt_user, 1, $3); }
|
||||
;
|
||||
|
||||
ext_pwd_opt
|
||||
ext_pwd
|
||||
: PASSWORD value
|
||||
{ $$ = $2; }
|
||||
|
|
||||
{ $$ = make_node (nod_null, 0, NULL); }
|
||||
{ $$ = make_node (nod_exec_stmt_pwd, 1, $2); }
|
||||
;
|
||||
|
||||
ext_tran_opt
|
||||
ext_tran
|
||||
: WITH AUTONOMOUS TRANSACTION
|
||||
{ $$ = make_flag_node(nod_tran_params, NOD_TRAN_AUTONOMOUS, 1, NULL); }
|
||||
| WITH COMMON TRANSACTION
|
||||
{ $$ = make_flag_node(nod_tran_params, NOD_TRAN_COMMON, 1, NULL); }
|
||||
// | WITH TWO_PHASE TRANSACTION
|
||||
// { $$ = make_flag_node(nod_tran_params, NOD_TRAN_2PC, 1, NULL); }
|
||||
|
|
||||
{ $$ = make_flag_node(nod_tran_params, NOD_TRAN_COMMON, 1, NULL); }
|
||||
;
|
||||
|
||||
ext_privs
|
||||
: WITH CALLER PRIVILEGES
|
||||
{ $$ = make_node (nod_exec_stmt_privs, 1, NULL); }
|
||||
;
|
||||
|
||||
if_then_else : IF '(' search_condition ')' THEN proc_block ELSE proc_block
|
||||
@ -5003,7 +5018,8 @@ non_reserved_word :
|
||||
| MAPPING
|
||||
| OS_NAME
|
||||
| UUID_TO_CHAR
|
||||
| COMMON // new execute statement
|
||||
| CALLER // new execute statement
|
||||
| COMMON
|
||||
| DATA
|
||||
| SOURCE
|
||||
| TWO_PHASE
|
||||
|
@ -1110,6 +1110,9 @@ dsql_nod* PASS1_node(CompiledStatement* statement, dsql_nod* input)
|
||||
case nod_tran_params:
|
||||
return input;
|
||||
|
||||
case nod_exec_stmt_privs:
|
||||
return input;
|
||||
|
||||
case nod_named_param:
|
||||
node = MAKE_node(input->nod_type, input->nod_count);
|
||||
node->nod_arg[e_named_param_name] = input->nod_arg[e_named_param_name];
|
||||
@ -1780,17 +1783,66 @@ dsql_nod* PASS1_statement(CompiledStatement* statement, dsql_nod* input)
|
||||
statement->req_labels.pop();
|
||||
}
|
||||
|
||||
node->nod_arg[e_exec_stmt_data_src] =
|
||||
PASS1_node(statement, input->nod_arg[e_exec_stmt_data_src]);
|
||||
// process various optional arguments
|
||||
if (input->nod_arg[e_exec_stmt_options])
|
||||
{
|
||||
dsql_nod *list = input->nod_arg[e_exec_stmt_options];
|
||||
fb_assert(list->nod_type == nod_list);
|
||||
|
||||
node->nod_arg[e_exec_stmt_user] =
|
||||
PASS1_node(statement, input->nod_arg[e_exec_stmt_user]);
|
||||
dsql_nod **ptr = list->nod_arg;
|
||||
const dsql_nod *const *end = list->nod_arg + list->nod_count;
|
||||
for (; ptr < end; ptr++)
|
||||
{
|
||||
char *dupClause = NULL;
|
||||
dsql_nod *opt = *ptr;
|
||||
switch (opt->nod_type)
|
||||
{
|
||||
case nod_exec_stmt_datasrc:
|
||||
if (node->nod_arg[e_exec_stmt_data_src])
|
||||
dupClause = "EXTERNAL DATA SOURCE";
|
||||
else
|
||||
node->nod_arg[e_exec_stmt_data_src] = PASS1_node(statement, opt->nod_arg[0]);
|
||||
break;
|
||||
|
||||
node->nod_arg[e_exec_stmt_pwd] =
|
||||
PASS1_node(statement, input->nod_arg[e_exec_stmt_pwd]);
|
||||
case nod_exec_stmt_user:
|
||||
if (node->nod_arg[e_exec_stmt_user])
|
||||
dupClause = "USER";
|
||||
else
|
||||
node->nod_arg[e_exec_stmt_user] = PASS1_node(statement, opt->nod_arg[0]);
|
||||
break;
|
||||
|
||||
node->nod_arg[e_exec_stmt_tran] =
|
||||
PASS1_node(statement, input->nod_arg[e_exec_stmt_tran]);
|
||||
case nod_exec_stmt_pwd:
|
||||
if (node->nod_arg[e_exec_stmt_pwd])
|
||||
dupClause = "PASSWORD";
|
||||
else
|
||||
node->nod_arg[e_exec_stmt_pwd] = PASS1_node(statement, opt->nod_arg[0]);
|
||||
break;
|
||||
|
||||
case nod_tran_params:
|
||||
if (node->nod_arg[e_exec_stmt_tran])
|
||||
dupClause = "TRANSACTION";
|
||||
else
|
||||
node->nod_arg[e_exec_stmt_tran] = PASS1_node(statement, opt);
|
||||
break;
|
||||
|
||||
case nod_exec_stmt_privs:
|
||||
if (node->nod_arg[e_exec_stmt_privs])
|
||||
dupClause = "CALLER PRIVILEGES";
|
||||
else
|
||||
node->nod_arg[e_exec_stmt_privs] = PASS1_node(statement, opt);
|
||||
break;
|
||||
|
||||
default:
|
||||
fb_assert(false);
|
||||
}
|
||||
|
||||
if (dupClause) {
|
||||
ERRD_post(isc_sqlerr, isc_arg_number, (SLONG) -637,
|
||||
isc_arg_gds, isc_dsql_duplicate_spec,
|
||||
isc_arg_string, dupClause, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pass1_savepoint(statement, node);
|
||||
|
||||
|
@ -399,6 +399,7 @@ void CMP_verify_access(thread_db* tdbb, jrd_req* request)
|
||||
* resources it used indirectecty via procedures or triggers
|
||||
*
|
||||
**************************************/
|
||||
|
||||
ExternalAccessList external;
|
||||
build_external_access(tdbb, external, request);
|
||||
|
||||
@ -445,12 +446,28 @@ void CMP_verify_access(thread_db* tdbb, jrd_req* request)
|
||||
}
|
||||
}
|
||||
|
||||
// Inherit privileges of caller stored procedure or trigger if and only if
|
||||
// this request is called immediately by caller (check for empty req_caller).
|
||||
// Currently (in v2.5) this rule will work for EXECUTE STATEMENT only, as
|
||||
// tra_callback_count incremented only by it.
|
||||
// When external SP's will be introduced we need to decide if they also can
|
||||
// inherit caller's privileges
|
||||
jrd_tra* transaction = tdbb->getTransaction();
|
||||
const jrd_req *exec_stmt_caller =
|
||||
(transaction && transaction->tra_callback_count && !request->req_caller) ?
|
||||
transaction->tra_callback_caller : NULL;
|
||||
|
||||
for (const AccessItem* access = request->req_access.begin(); access < request->req_access.end();
|
||||
access++)
|
||||
{
|
||||
const SecurityClass* sec_class = SCL_get_class(tdbb, access->acc_security_name.c_str());
|
||||
SCL_check_access(sec_class, access->acc_view_id, NULL, NULL, access->acc_mask,
|
||||
access->acc_type, access->acc_name, access->acc_r_name);
|
||||
|
||||
Firebird::MetaName trgName(exec_stmt_caller ? exec_stmt_caller->req_trg_name : NULL);
|
||||
Firebird::MetaName prcName(exec_stmt_caller && exec_stmt_caller->req_procedure ?
|
||||
exec_stmt_caller->req_procedure->prc_name : NULL);
|
||||
|
||||
SCL_check_access(sec_class, access->acc_view_id, trgName, prcName,
|
||||
access->acc_mask, access->acc_type, access->acc_name, access->acc_r_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1646,6 +1646,9 @@ static jrd_nod* execute_statement(thread_db* tdbb, jrd_req* request, jrd_nod* no
|
||||
const jrd_nod* tra_node = node->nod_arg[node->nod_count + e_exec_stmt_extra_tran];
|
||||
const EDS::TraScope tra_scope = tra_node ? (EDS::TraScope)(IPTR) tra_node : EDS::traCommon;
|
||||
|
||||
const jrd_nod* privs_node = node->nod_arg[node->nod_count + e_exec_stmt_extra_privs];
|
||||
const bool caller_privs = (privs_node != NULL);
|
||||
|
||||
Firebird::string sSql;
|
||||
get_string(tdbb, request, node->nod_arg[e_exec_stmt_stmt_sql], sSql);
|
||||
|
||||
@ -1662,10 +1665,10 @@ static jrd_nod* execute_statement(thread_db* tdbb, jrd_req* request, jrd_nod* no
|
||||
|
||||
stmt = conn->createStatement(sSql);
|
||||
|
||||
EDS::Transaction* tran = EDS::Transaction::getTransaction(tdbb,
|
||||
stmt->getConnection(), tra_scope);
|
||||
EDS::Transaction* tran = EDS::Transaction::getTransaction(tdbb, stmt->getConnection(), tra_scope);
|
||||
|
||||
stmt->bindToRequest(request, stmt_ptr);
|
||||
stmt->setCallerPrivileges(caller_privs);
|
||||
|
||||
const Firebird::string* const * inp_names = inputs_names ? inputs_names->begin() : NULL;
|
||||
stmt->prepare(tdbb, tran, sSql, inputs_names != NULL);
|
||||
|
@ -553,7 +553,8 @@ const int e_exec_stmt_extra_inputs = 0;
|
||||
const int e_exec_stmt_extra_input_names = 1;
|
||||
const int e_exec_stmt_extra_outputs = 2;
|
||||
const int e_exec_stmt_extra_tran = 3;
|
||||
const int e_exec_stmt_extra_count = 4;
|
||||
const int e_exec_stmt_extra_privs = 4;
|
||||
const int e_exec_stmt_extra_count = 5;
|
||||
|
||||
// Request resources
|
||||
|
||||
|
@ -39,6 +39,7 @@
|
||||
#include "../exe_proto.h"
|
||||
#include "../err_proto.h"
|
||||
#include "../evl_proto.h"
|
||||
#include "../intl_proto.h"
|
||||
#include "../mov_proto.h"
|
||||
|
||||
using namespace Jrd;
|
||||
@ -102,10 +103,8 @@ Connection* Manager::getConnection(thread_db *tdbb, const string &dataSource,
|
||||
|
||||
if (dataSource.isEmpty())
|
||||
{
|
||||
if (user.isEmpty() || user == tdbb->getAttachment()->att_user->usr_user_name)
|
||||
prvName = INTERNAL_PROVIDER_NAME;
|
||||
else
|
||||
prvName = FIREBIRD_PROVIDER_NAME;
|
||||
prvName = INTERNAL_PROVIDER_NAME;
|
||||
dbName = tdbb->getDatabase()->dbb_database_name.c_str();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -221,6 +220,7 @@ Connection::Connection(Provider &prov) :
|
||||
PermanentStorage(prov.getPool()),
|
||||
m_provider(prov),
|
||||
m_dbName(getPool()),
|
||||
m_dpb(getPool(), ClumpletReader::Tagged, MAX_DPB_SIZE),
|
||||
m_transactions(getPool()),
|
||||
m_statements(getPool()),
|
||||
m_freeStatements(NULL),
|
||||
@ -247,6 +247,47 @@ Connection::~Connection()
|
||||
{
|
||||
}
|
||||
|
||||
void Connection::generateDPB(thread_db *tdbb, ClumpletWriter &dpb,
|
||||
const string &dbName, const string &user, const string &pwd) const
|
||||
{
|
||||
dpb.reset(isc_dpb_version1);
|
||||
|
||||
Firebird::string &attUser = tdbb->getAttachment()->att_user->usr_user_name;
|
||||
|
||||
if ((m_provider.getFlags() & prvTrustedAuth) &&
|
||||
(user.isEmpty() || user == attUser) && pwd.isEmpty())
|
||||
{
|
||||
dpb.insertString(isc_dpb_trusted_auth, attUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!user.isEmpty()) {
|
||||
dpb.insertString(isc_dpb_user_name, user);
|
||||
}
|
||||
if (!pwd.isEmpty()) {
|
||||
dpb.insertString(isc_dpb_password, pwd);
|
||||
}
|
||||
}
|
||||
|
||||
CharSet* const cs = INTL_charset_lookup(tdbb, tdbb->getAttachment()->att_charset);
|
||||
if (cs) {
|
||||
dpb.insertString(isc_dpb_lc_ctype, cs->getName());
|
||||
}
|
||||
}
|
||||
|
||||
bool Connection::isSameDatabase(thread_db *tdbb, const string &dbName,
|
||||
const string &user, const string &pwd) const
|
||||
{
|
||||
if (m_dbName != dbName)
|
||||
return false;
|
||||
|
||||
ClumpletWriter dpb(ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
|
||||
generateDPB(tdbb, dpb, dbName, user, pwd);
|
||||
|
||||
return m_dpb.simpleCompare(dpb);
|
||||
}
|
||||
|
||||
|
||||
Transaction* Connection::createTransaction()
|
||||
{
|
||||
Transaction* tran = doCreateTransaction();
|
||||
@ -602,6 +643,8 @@ Statement::Statement(Connection &conn) :
|
||||
m_stmt_selectable(false),
|
||||
m_inputs(0),
|
||||
m_outputs(0),
|
||||
m_callerPrivileges(false),
|
||||
m_preparedByReq(NULL),
|
||||
m_sqlParamNames(getPool()),
|
||||
m_sqlParamsMap(getPool()),
|
||||
m_in_buffer(getPool()),
|
||||
@ -627,12 +670,14 @@ void Statement::prepare(thread_db *tdbb, Transaction *tran, const string& sql, b
|
||||
fb_assert(!m_active);
|
||||
|
||||
// already prepared the same non-empty statement
|
||||
if (isAllocated() && (m_sql == sql) && (m_sql != ""))
|
||||
if (isAllocated() && (m_sql == sql) && (m_sql != "") &&
|
||||
m_preparedByReq == (m_callerPrivileges ? tdbb->getRequest() : NULL))
|
||||
return;
|
||||
|
||||
m_error = false;
|
||||
m_transaction = tran;
|
||||
m_sql = "";
|
||||
m_preparedByReq = NULL;
|
||||
|
||||
m_in_buffer.clear();
|
||||
m_out_buffer.clear();
|
||||
@ -662,6 +707,7 @@ void Statement::prepare(thread_db *tdbb, Transaction *tran, const string& sql, b
|
||||
|
||||
m_sql = sql;
|
||||
m_sql.trim();
|
||||
m_preparedByReq = m_callerPrivileges ? tdbb->getRequest() : NULL;
|
||||
}
|
||||
|
||||
void Statement::execute(thread_db* tdbb, Transaction* tran, int in_count,
|
||||
|
@ -162,7 +162,7 @@ public:
|
||||
virtual bool isConnected() const = 0;
|
||||
|
||||
virtual bool isSameDatabase(Jrd::thread_db *tdbb, const Firebird::string &dbName,
|
||||
const Firebird::string &user, const Firebird::string &pwd) const = 0;
|
||||
const Firebird::string &user, const Firebird::string &pwd) const;
|
||||
|
||||
// Search for existing transaction of given scope, may return NULL.
|
||||
Transaction* findTransaction(Jrd::thread_db *tdbb, TraScope traScope) const;
|
||||
@ -192,6 +192,10 @@ public:
|
||||
virtual Blob* createBlob() = 0;
|
||||
|
||||
protected:
|
||||
void generateDPB(Jrd::thread_db *tdbb, Firebird::ClumpletWriter &dpb,
|
||||
const Firebird::string &dbName, const Firebird::string &user,
|
||||
const Firebird::string &pwd) const;
|
||||
|
||||
virtual Transaction* doCreateTransaction() = 0;
|
||||
virtual Statement* doCreateStatement() = 0;
|
||||
void clearStatements(Jrd::thread_db *tdbb);
|
||||
@ -201,6 +205,7 @@ protected:
|
||||
|
||||
Provider &m_provider;
|
||||
Firebird::string m_dbName;
|
||||
Firebird::ClumpletWriter m_dpb;
|
||||
|
||||
Firebird::Array<Transaction*> m_transactions;
|
||||
Firebird::Array<Statement*> m_statements;
|
||||
@ -293,6 +298,8 @@ public:
|
||||
|
||||
const Firebird::string& getSql() const { return m_sql; }
|
||||
|
||||
void setCallerPrivileges(bool use) { m_callerPrivileges = use; }
|
||||
|
||||
bool isActive() const { return m_active; }
|
||||
|
||||
bool isAllocated() const { return m_allocated; }
|
||||
@ -366,6 +373,9 @@ protected:
|
||||
int m_inputs;
|
||||
int m_outputs;
|
||||
|
||||
bool m_callerPrivileges;
|
||||
Jrd::jrd_req* m_preparedByReq;
|
||||
|
||||
// set in preprocess
|
||||
ParamNames m_sqlParamNames;
|
||||
ParamNames m_sqlParamsMap;
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "../align.h"
|
||||
#include "../exe.h"
|
||||
#include "../jrd.h"
|
||||
#include "../tra.h"
|
||||
#include "../dsc.h"
|
||||
#include "../../dsql/dsql.h"
|
||||
#include "../../dsql/sqlda_pub.h"
|
||||
@ -114,7 +115,33 @@ void InternalConnection::attach(thread_db *tdbb, const Firebird::string &dbName,
|
||||
const Firebird::string &user, const Firebird::string &pwd)
|
||||
{
|
||||
fb_assert(!m_attachment);
|
||||
m_attachment = tdbb->getAttachment();
|
||||
Database* dbb = tdbb->getDatabase();
|
||||
fb_assert(dbName.isEmpty() || dbName == dbb->dbb_database_name.c_str());
|
||||
|
||||
Attachment* attachment = tdbb->getAttachment();
|
||||
if (user.isEmpty() || user == attachment->att_user->usr_user_name)
|
||||
{
|
||||
m_isCurrent = true;
|
||||
m_attachment = attachment;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_isCurrent = false;
|
||||
m_dbName = dbb->dbb_database_name.c_str();
|
||||
generateDPB(tdbb, m_dpb, m_dbName, user, pwd);
|
||||
|
||||
ISC_STATUS_ARRAY status = {0};
|
||||
|
||||
{
|
||||
EngineCallbackGuard guard(tdbb, *this);
|
||||
jrd8_attach_database(status, m_dbName.c_str(), &m_attachment,
|
||||
m_dpb.getBufferLength(), m_dpb.getBuffer());
|
||||
}
|
||||
if (status[1]) {
|
||||
raise(status, tdbb, "attach");
|
||||
}
|
||||
}
|
||||
|
||||
m_sqlDialect = (m_attachment->att_database->dbb_flags & DBB_DB_SQL_dialect_3) ?
|
||||
SQL_DIALECT_V6 : SQL_DIALECT_V5;
|
||||
}
|
||||
@ -124,18 +151,47 @@ void InternalConnection::detach(thread_db *tdbb)
|
||||
clearStatements(tdbb);
|
||||
|
||||
fb_assert(m_attachment);
|
||||
m_attachment = 0;
|
||||
if (m_isCurrent)
|
||||
{
|
||||
m_attachment = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
ISC_STATUS_ARRAY status = {0};
|
||||
|
||||
{
|
||||
Attachment* att = m_attachment;
|
||||
m_attachment = NULL;
|
||||
|
||||
EngineCallbackGuard guard(tdbb, *this);
|
||||
jrd8_detach_database(status, &att);
|
||||
|
||||
m_attachment = att;
|
||||
}
|
||||
if (status[1]) {
|
||||
raise(status, tdbb, "dettach");
|
||||
}
|
||||
}
|
||||
fb_assert(!m_attachment)
|
||||
}
|
||||
|
||||
// this internal connection instance is available for the current execution context if it
|
||||
// a) is current conenction and current thread's attachment is equal to
|
||||
// this attachment, or
|
||||
// b) is not current conenction
|
||||
bool InternalConnection::isAvailable(thread_db *tdbb, TraScope traScope) const
|
||||
{
|
||||
return (tdbb->getAttachment() == m_attachment);
|
||||
return !m_isCurrent ||
|
||||
m_isCurrent && (tdbb->getAttachment() == m_attachment);
|
||||
}
|
||||
|
||||
bool InternalConnection::isSameDatabase(thread_db *tdbb, const Firebird::string &dbName,
|
||||
const Firebird::string &user, const Firebird::string &pwd) const
|
||||
{
|
||||
return dbName.isEmpty() && user.isEmpty() && (tdbb->getAttachment() == m_attachment);
|
||||
if (m_isCurrent)
|
||||
return (tdbb->getAttachment() == m_attachment);
|
||||
else
|
||||
return Connection::isSameDatabase(tdbb, dbName, user, pwd);
|
||||
}
|
||||
|
||||
Transaction* InternalConnection::doCreateTransaction()
|
||||
@ -160,7 +216,7 @@ void InternalTransaction::doStart(ISC_STATUS* status, thread_db *tdbb, ClumpletW
|
||||
{
|
||||
fb_assert(!m_transaction);
|
||||
|
||||
if (m_scope == traCommon) {
|
||||
if (m_scope == traCommon && m_IntConnection.isCurrent()) {
|
||||
m_transaction = tdbb->getTransaction();
|
||||
}
|
||||
else
|
||||
@ -184,7 +240,7 @@ void InternalTransaction::doCommit(ISC_STATUS* status, thread_db *tdbb, bool ret
|
||||
{
|
||||
fb_assert(m_transaction);
|
||||
|
||||
if (m_scope == traCommon) {
|
||||
if (m_scope == traCommon && m_IntConnection.isCurrent()) {
|
||||
m_transaction = 0;
|
||||
}
|
||||
else
|
||||
@ -201,7 +257,7 @@ void InternalTransaction::doRollback(ISC_STATUS* status, thread_db *tdbb, bool r
|
||||
{
|
||||
fb_assert(m_transaction);
|
||||
|
||||
if (m_scope == traCommon) {
|
||||
if (m_scope == traCommon && m_IntConnection.isCurrent()) {
|
||||
m_transaction = 0;
|
||||
}
|
||||
else
|
||||
@ -253,8 +309,14 @@ void InternalStatement::doPrepare(thread_db *tdbb, const string &sql)
|
||||
|
||||
{
|
||||
EngineCallbackGuard guard(tdbb, *this);
|
||||
|
||||
jrd_req* save_caller = tran->tra_callback_caller;
|
||||
tran->tra_callback_caller = m_callerPrivileges ? tdbb->getRequest() : NULL;
|
||||
|
||||
jrd8_prepare(status, &tran, &m_request, sql.length(), sql.c_str(),
|
||||
m_connection.getSqlDialect(), 0, NULL, 0, NULL);
|
||||
|
||||
tran->tra_callback_caller = save_caller;
|
||||
}
|
||||
if (status[1]) {
|
||||
raise(status, tdbb, "jrd8_prepare", &sql);
|
||||
|
@ -56,7 +56,8 @@ protected:
|
||||
|
||||
explicit InternalConnection(InternalProvider &prov) :
|
||||
Connection(prov),
|
||||
m_attachment(0)
|
||||
m_attachment(0),
|
||||
m_isCurrent(false)
|
||||
{}
|
||||
|
||||
virtual ~InternalConnection();
|
||||
@ -75,6 +76,8 @@ public:
|
||||
virtual bool isSameDatabase(Jrd::thread_db *tdbb, const Firebird::string &dbName,
|
||||
const Firebird::string &user, const Firebird::string &pwd) const;
|
||||
|
||||
bool isCurrent() const { return m_isCurrent; }
|
||||
|
||||
Jrd::Attachment* getJrdAtt()
|
||||
{ return m_attachment; }
|
||||
|
||||
@ -85,6 +88,7 @@ protected:
|
||||
virtual Statement* doCreateStatement();
|
||||
|
||||
Jrd::Attachment* m_attachment;
|
||||
bool m_isCurrent;
|
||||
};
|
||||
|
||||
|
||||
|
@ -93,8 +93,7 @@ Connection* IscProvider::doCreateConnection()
|
||||
IscConnection::IscConnection(IscProvider &prov) :
|
||||
Connection(prov),
|
||||
m_iscProvider(prov),
|
||||
m_handle(0),
|
||||
m_dpb(getPool(), ClumpletReader::Tagged, MAX_DPB_SIZE)
|
||||
m_handle(0)
|
||||
{
|
||||
}
|
||||
|
||||
@ -102,34 +101,6 @@ IscConnection::~IscConnection()
|
||||
{
|
||||
}
|
||||
|
||||
void IscConnection::generateDPB(thread_db *tdbb, ClumpletWriter &dpb,
|
||||
const string &dbName, const string &user, const string &pwd) const
|
||||
{
|
||||
dpb.reset(isc_dpb_version1);
|
||||
|
||||
Firebird::string &attUser = tdbb->getAttachment()->att_user->usr_user_name;
|
||||
|
||||
if ((m_provider.getFlags() & prvTrustedAuth) &&
|
||||
(user.isEmpty() || user == attUser) && pwd.isEmpty())
|
||||
{
|
||||
dpb.insertString(isc_dpb_trusted_auth, attUser);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!user.isEmpty()) {
|
||||
dpb.insertString(isc_dpb_user_name, user);
|
||||
}
|
||||
if (!pwd.isEmpty()) {
|
||||
dpb.insertString(isc_dpb_password, pwd);
|
||||
}
|
||||
}
|
||||
|
||||
CharSet* const cs = INTL_charset_lookup(tdbb, tdbb->getAttachment()->att_charset);
|
||||
if (cs) {
|
||||
dpb.insertString(isc_dpb_lc_ctype, cs->getName());
|
||||
}
|
||||
}
|
||||
|
||||
void IscConnection::attach(thread_db *tdbb, const string &dbName, const string &user,
|
||||
const string &pwd)
|
||||
{
|
||||
@ -203,7 +174,7 @@ void IscConnection::detach(thread_db *tdbb)
|
||||
}
|
||||
}
|
||||
|
||||
// connection is available for the current execution context if it
|
||||
// this ISC connection instance is available for the current execution context if it
|
||||
// a) have no active statements or support many active statements
|
||||
// and
|
||||
// b) have no active transactions or have active transaction of given
|
||||
@ -224,18 +195,6 @@ bool IscConnection::isAvailable(thread_db *tdbb, TraScope traScope) const
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IscConnection::isSameDatabase(thread_db *tdbb, const string &dbName,
|
||||
const string &user, const string &pwd) const
|
||||
{
|
||||
if (m_dbName != dbName)
|
||||
return false;
|
||||
|
||||
ClumpletWriter dpb(ClumpletReader::Tagged, MAX_DPB_SIZE, isc_dpb_version1);
|
||||
generateDPB(tdbb, dpb, dbName, user, pwd);
|
||||
|
||||
return m_dpb.simpleCompare(dpb);
|
||||
}
|
||||
|
||||
Blob* IscConnection::createBlob()
|
||||
{
|
||||
return new IscBlob(*this);
|
||||
|
@ -516,22 +516,14 @@ public:
|
||||
virtual bool isConnected() const
|
||||
{ return (m_handle != 0); }
|
||||
|
||||
virtual bool isSameDatabase(Jrd::thread_db *tdbb, const Firebird::string &dbName,
|
||||
const Firebird::string &user, const Firebird::string &pwd) const;
|
||||
|
||||
virtual Blob* createBlob();
|
||||
|
||||
protected:
|
||||
virtual Transaction* doCreateTransaction();
|
||||
virtual Statement* doCreateStatement();
|
||||
|
||||
void generateDPB(Jrd::thread_db *tdbb, Firebird::ClumpletWriter &dpb,
|
||||
const Firebird::string &dbName, const Firebird::string &user,
|
||||
const Firebird::string &pwd) const;
|
||||
|
||||
IscProvider &m_iscProvider;
|
||||
FB_API_HANDLE m_handle;
|
||||
Firebird::ClumpletWriter m_dpb;
|
||||
};
|
||||
|
||||
|
||||
|
@ -2868,14 +2868,15 @@ jrd_nod* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb, USHORT expected,
|
||||
*arg++ = PAR_parse_node(tdbb, csb, VALUE); // e_exec_stmt_data_src
|
||||
*arg++ = PAR_parse_node(tdbb, csb, VALUE); // e_exec_stmt_user
|
||||
*arg++ = PAR_parse_node(tdbb, csb, VALUE); // e_exec_stmt_password
|
||||
const UCHAR tra_mode = BLR_BYTE; // e_exec_stmt_tran
|
||||
const UCHAR tra_mode = BLR_BYTE; // e_exec_stmt_tran
|
||||
if (BLR_BYTE != 0) {
|
||||
// external transaction parameters is not implemented currently
|
||||
PAR_syntax_error(csb, "external transaction parameters");
|
||||
}
|
||||
const UCHAR use_caller_privs = BLR_BYTE; // e_exec_stmt_extra_privs
|
||||
|
||||
if (BLR_BYTE) // singleton flag
|
||||
*arg++ = 0; // e_exec_stmt_proc_block
|
||||
*arg++ = 0; // e_exec_stmt_proc_block
|
||||
else
|
||||
*arg++ = PAR_parse_node(tdbb, csb, STATEMENT); // e_exec_stmt_proc_block
|
||||
|
||||
@ -2910,6 +2911,7 @@ jrd_nod* PAR_parse_node(thread_db* tdbb, CompilerScratch* csb, USHORT expected,
|
||||
*arg++ = (jrd_nod*) paramNames; // e_exec_stmt_extra_input_names
|
||||
*arg++ = (jrd_nod*)(IPTR) outputs; // e_exec_stmt_extra_outputs
|
||||
*arg++ = (jrd_nod*)(IPTR) tra_mode; // e_exec_stmt_extra_tran
|
||||
*arg++ = (jrd_nod*)(IPTR) use_caller_privs; // e_exec_stmt_extra_privs
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -196,6 +196,7 @@ public:
|
||||
RuntimeStatistics tra_stats;
|
||||
Firebird::Array<dsql_req*> tra_open_cursors;
|
||||
jrd_tra* const tra_outer; // outer transaction of an autonomous transaction
|
||||
jrd_req* tra_callback_caller; // caller request for execute statement
|
||||
Firebird::Array<UCHAR> tra_transactions;
|
||||
|
||||
EDS::Transaction *tra_ext_common;
|
||||
|
Loading…
Reference in New Issue
Block a user