8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 06:03:02 +01:00

Metadata cache support for PSQL functions.

This commit is contained in:
dimitr 2009-12-24 14:15:21 +00:00
parent b2cd45ffe4
commit 6e1f8db753
6 changed files with 510 additions and 48 deletions

View File

@ -630,3 +630,15 @@ dsc* Function::execute(thread_db* tdbb, jrd_nod* args, impure_value* value)
return &value->vlu_desc;
}
USHORT Function::incrementAlterCount()
{
if (fun_alter_count == Function::MAX_ALTER_COUNT)
{
status_exception::raise(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_udf_name) << Arg::Str(fun_name.toString()) <<
Arg::Gds(isc_version_err));
}
return ++fun_alter_count;
}

View File

@ -34,7 +34,7 @@ namespace Jrd
class Function : public pool_alloc<type_fun>
{
static const USHORT MAX_ALTER = 64; // Number of times an in-cache function can be altered
static const USHORT MAX_ALTER_COUNT = 64; // Number of times an in-cache function can be altered
static const char* const EXCEPTION_MESSAGE;
public:
@ -49,8 +49,12 @@ namespace Jrd
return (fun_use_count != 0);
}
void releaseLocks(thread_db* tdbb);
USHORT incrementAlterCount();
dsc* execute(thread_db* tdbb, jrd_nod* args, impure_value* value);
void releaseLocks(thread_db* tdbb);
void remove(thread_db* tdbb);
void parseBlr(thread_db* tdbb, bid* blob_id, CompilerScratch* csb);
private:
explicit Function(MemoryPool& p)
@ -58,9 +62,6 @@ namespace Jrd
fun_legacy(true), fun_invariant(false)
{}
void remove(thread_db* tdbb);
void parseBlr(thread_db* tdbb, bid* blob_id, CompilerScratch* csb);
static Function* loadMetadata(thread_db* tdbb, USHORT id, bool noscan, USHORT flags);
static int blockingAst(void*);

View File

@ -120,6 +120,7 @@
#include "../jrd/trig.h"
#include "../jrd/IntlManager.h"
#include "../jrd/UserManagement.h"
#include "../jrd/Function.h"
#include "../common/utils_proto.h"
#include "../common/classes/Hash.h"
@ -367,7 +368,9 @@ static bool delete_collation(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool delete_exception(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool delete_generator(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool modify_generator(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool delete_udf(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool create_function(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool delete_function(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool modify_function(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool create_field(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool delete_field(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
static bool modify_field(thread_db*, SSHORT, DeferredWork*, jrd_tra*);
@ -397,6 +400,7 @@ static void check_system_generator(const TEXT*, const dfw_t);
static bool formatsAreEqual(const Format*, const Format*);
static bool find_depend_in_dfw(thread_db*, TEXT*, USHORT, USHORT, jrd_tra*);
static void get_array_desc(thread_db*, const TEXT*, Ods::InternalArrayDesc*);
static void get_function_dependencies(DeferredWork*, bool, jrd_tra*);
static void get_procedure_dependencies(DeferredWork*, bool, jrd_tra*);
static void get_trigger_dependencies(DeferredWork*, bool, jrd_tra*);
static void load_trigs(thread_db*, jrd_rel*, trig_vec**);
@ -463,7 +467,9 @@ static const deferred_task task_table[] =
{ dfw_delete_exception, delete_exception },
{ dfw_delete_generator, delete_generator },
{ dfw_modify_generator, modify_generator },
{ dfw_delete_udf, delete_udf },
{ dfw_create_function, create_function },
{ dfw_delete_function, delete_function },
{ dfw_modify_function, modify_function },
{ dfw_add_difference, add_difference },
{ dfw_delete_difference, delete_difference },
{ dfw_begin_backup, begin_backup },
@ -2216,6 +2222,44 @@ static bool modify_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_
}
static bool create_function(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
{
/**************************************
*
* c r e a t e _ f u n c t i o n
*
**************************************
*
* Functional description
* Create a new function.
*
**************************************/
SET_TDBB(tdbb);
switch (phase)
{
case 1:
case 2:
return true;
case 3:
{
const bool compile = !work->findArg(dfw_arg_check_blr);
get_function_dependencies(work, compile, transaction);
if (!Function::lookup(tdbb, QualifiedName(work->dfw_name, work->dfw_package), compile))
{
return false;
}
}
break;
}
return false;
}
static bool create_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
{
/**************************************
@ -3182,42 +3226,6 @@ static bool modify_generator(thread_db* tdbb, SSHORT phase, DeferredWork* work,
}
static bool delete_udf(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
{
/**************************************
*
* d e l e t e _ u d f
*
**************************************
*
* Functional description
* Check if it is allowable to delete
* an udf, and if so, clean up after it.
* CVC: This function was modelled after delete_exception.
*
**************************************/
SET_TDBB(tdbb);
switch (phase)
{
case 0:
return false;
case 1:
check_dependencies(tdbb, work->dfw_name.c_str(), NULL, NULL, obj_udf, transaction);
return true;
case 2:
return true;
case 3:
return true;
case 4:
break;
}
return false;
}
static bool create_field(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
{
/**************************************
@ -3494,6 +3502,138 @@ static void check_partners(thread_db* tdbb, const USHORT rel_id)
}
static bool delete_function(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
{
/**************************************
*
* d e l e t e _ f u n c t i o n
*
**************************************
*
* Functional description
* Check if it is allowed to delete
* a function, and if so, clean up after it.
*
**************************************/
SET_TDBB(tdbb);
Database* const dbb = tdbb->getDatabase();
Function* function = NULL;
switch (phase)
{
case 0:
function = Function::lookup(tdbb, work->dfw_id, false, true, 0);
if (!function)
{
return false;
}
if (function->fun_existence_lock)
{
LCK_convert(tdbb, function->fun_existence_lock, LCK_SR, transaction->getLockWait());
}
return false;
case 1:
check_dependencies(tdbb, work->dfw_name.c_str(), NULL, work->dfw_package.c_str(),
obj_udf, transaction);
return true;
case 2:
function = Function::lookup(tdbb, work->dfw_id, false, true, 0);
if (!function)
{
return false;
}
if (function->fun_existence_lock)
{
if (!LCK_convert(tdbb, function->fun_existence_lock, LCK_EX, transaction->getLockWait()))
{
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_obj_in_use) << Arg::Str(work->dfw_name));
}
}
// If we are in a multi-client server, someone else may have marked
// function obsolete. Unmark and we will remark it later.
function->fun_flags &= ~FUN_obsolete;
return true;
case 3:
return true;
case 4:
{
function = Function::lookup(tdbb, work->dfw_id, true, true, 0);
if (!function)
{
return false;
}
const QualifiedName name(work->dfw_name, work->dfw_package);
// Do not allow to drop function used by user requests
if (function->isUsed())
{
/*
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_obj_in_use) << Arg::Str(name.toString()));
*/
gds__log("Deleting function %s which is currently in use by active user requests",
name.toString().c_str());
if (work->dfw_package.isEmpty())
{
MET_delete_dependencies(tdbb, work->dfw_name, obj_udf, transaction);
}
if (function->fun_existence_lock)
{
LCK_release(tdbb, function->fun_existence_lock);
}
dbb->dbb_functions[function->fun_id] = NULL;
return false;
}
const USHORT old_flags = function->fun_flags;
function->fun_flags |= FUN_obsolete;
if (function->fun_request)
{
if (CMP_clone_is_active(function->fun_request))
{
function->fun_flags = old_flags;
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_obj_in_use) << Arg::Str(name.toString()));
}
CMP_release(tdbb, function->fun_request);
function->fun_request = NULL;
}
// delete dependency lists
if (work->dfw_package.isEmpty())
{
MET_delete_dependencies(tdbb, work->dfw_name, obj_udf, transaction);
}
if (function->fun_existence_lock)
{
LCK_release(tdbb, function->fun_existence_lock);
}
}
break;
} // switch
return false;
}
static bool delete_index(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
{
/**************************************
@ -4337,6 +4477,9 @@ static bool find_depend_in_dfw(thread_db* tdbb,
case obj_package_body:
dfw_type = dfw_drop_package_body;
break;
case obj_udf:
dfw_type = dfw_delete_function;
break;
default:
fb_assert(false);
break;
@ -4346,18 +4489,21 @@ static bool find_depend_in_dfw(thread_db* tdbb,
for (const DeferredWork* work = transaction->tra_deferred_job->work; work; work = work->getNext())
{
if ((work->dfw_type == dfw_type ||
(work->dfw_type == dfw_modify_procedure && dfw_type == dfw_delete_procedure)) &&
(work->dfw_type == dfw_modify_procedure && dfw_type == dfw_delete_procedure) ||
(work->dfw_type == dfw_modify_function && dfw_type == dfw_delete_function)) &&
work->dfw_name == object_name && work->dfw_package.isEmpty() &&
(!rel_id || rel_id == work->dfw_id))
{
if (work->dfw_type == dfw_modify_procedure)
if (work->dfw_type == dfw_modify_procedure || work->dfw_type == dfw_modify_function)
{
// Don't consider that procedure is in DFW if we are only checking the BLR
if (!work->findArg(dfw_arg_check_blr))
return true;
}
else
{
return true;
}
}
if (work->dfw_type == dfw_type && dfw_type == dfw_delete_expression_index)
@ -4484,6 +4630,83 @@ static void get_array_desc(thread_db* tdbb, const TEXT* field_name, Ods::Interna
}
static void get_function_dependencies(DeferredWork* work, bool compile, jrd_tra* transaction)
{
/**************************************
*
* g e t _ f u n c t i o n _ d e p e n d e n c i e s
*
**************************************
*
* Functional description
* Get relations and fields on which this
* function depends, either when it's being
* created or when it's modified.
*
**************************************/
thread_db* const tdbb = JRD_get_thread_data();
Database* const dbb = tdbb->getDatabase();
if (compile)
{
compile = !(tdbb->getAttachment()->att_flags & ATT_gbak_attachment);
}
bool found = false;
bid blob_id;
blob_id.clear();
jrd_req* handle = CMP_find_request(tdbb, irq_c_fun_dpd, IRQ_REQUESTS);
FOR(REQUEST_HANDLE handle)
X IN RDB$FUNCTIONS WITH
X.RDB$FUNCTION_NAME EQ work->dfw_name.c_str() AND
X.RDB$PACKAGE_NAME EQUIV NULLIF(work->dfw_package.c_str(), '')
if (!REQUEST(irq_c_fun_dpd))
REQUEST(irq_c_fun_dpd) = handle;
blob_id = X.RDB$FUNCTION_BLR;
if (Function::lookup(tdbb, QualifiedName(work->dfw_name, work->dfw_package), !compile))
{
found = true;
}
END_FOR;
if (!REQUEST(irq_c_fun_dpd))
REQUEST(irq_c_fun_dpd) = handle;
// get any dependencies now by parsing the blr
if (found && !blob_id.isEmpty())
{
jrd_req* request = NULL;
MemoryPool* const new_pool = dbb->createPool();
Jrd::ContextPoolHolder context(tdbb, new_pool);
Firebird::MetaName depName(work->dfw_package.isEmpty() ?
work->dfw_name : work->dfw_package);
MET_get_dependencies(tdbb, NULL, NULL, 0, NULL, &blob_id, compile ? &request : NULL,
NULL, depName,
(work->dfw_package.isEmpty() ? obj_udf : obj_package_body),
0, transaction);
if (request)
{
CMP_release(tdbb, request);
}
else
{
dbb->deletePool(new_pool);
}
}
}
static void get_procedure_dependencies(DeferredWork* work, bool compile, jrd_tra* transaction)
{
/**************************************
@ -5279,6 +5502,205 @@ static bool make_version(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_
}
static bool modify_function(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
{
/**************************************
*
* m o d i f y _ f u n c t i o n
*
**************************************
*
* Functional description
* Perform required actions when modifying function.
*
**************************************/
SET_TDBB(tdbb);
Database* const dbb = tdbb->getDatabase();
const QualifiedName name(work->dfw_name, work->dfw_package);
Function* function = NULL;
switch (phase)
{
case 0:
{
function = Function::lookup(tdbb, work->dfw_id, false, true, 0);
if (!function)
{
return false;
}
if (function->fun_existence_lock)
{
LCK_convert(tdbb, function->fun_existence_lock, LCK_SR, transaction->getLockWait());
}
return false;
}
case 1:
return true;
case 2:
return true;
case 3:
function = Function::lookup(tdbb, work->dfw_id, false, true, 0);
if (!function)
{
return false;
}
if (function->fun_existence_lock)
{
// Let function be deleted if only this transaction is using it
if (!LCK_convert(tdbb, function->fun_existence_lock, LCK_EX, transaction->getLockWait()))
{
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_obj_in_use) << Arg::Str(name.toString()));
}
}
// If we are in a multi-client server, someone else may have marked
// function obsolete. Unmark and we will remark it later.
function->fun_flags &= ~FUN_obsolete;
return true;
case 4:
function = Function::lookup(tdbb, work->dfw_id, false, true, 0);
if (!function)
{
return false;
}
{ // guard scope
Database::CheckoutLockGuard guard(dbb, dbb->dbb_meta_mutex);
// Do not allow to modify function used by user requests
if (function->isUsed())
{
/*
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_obj_in_use) << Arg::Str(name.toString()));
*/
gds__log("Modifying function %s which is currently in use by active user requests",
name.toString().c_str());
const USHORT alter_count = function->incrementAlterCount();
if (function->fun_existence_lock)
{
LCK_release(tdbb, function->fun_existence_lock);
}
dbb->dbb_functions[function->fun_id] = NULL;
if (!(function = Function::lookup(tdbb, work->dfw_id, false,
true, FUN_being_altered)))
{
return false;
}
function->fun_alter_count = alter_count;
}
function->fun_flags |= FUN_being_altered;
if (function->fun_request)
{
if (CMP_clone_is_active(function->fun_request))
{
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_obj_in_use) << Arg::Str(name.toString()));
}
// release the request
CMP_release(tdbb, function->fun_request);
function->fun_request = NULL;
}
// delete dependency lists
if (work->dfw_package.isEmpty())
{
MET_delete_dependencies(tdbb, work->dfw_name, obj_udf, transaction);
}
// The function has just been scanned by Function::lookup()
// and its FUN_scanned flag is set. We are going to reread it
// from file (create all new dependencies) and do not want this
// flag to be set. That is why we do not add FUN_obsolete and
// FUN_being_altered flags, we set only these two flags.
function->fun_flags = (FUN_obsolete | FUN_being_altered);
if (function->fun_existence_lock)
{
LCK_release(tdbb, function->fun_existence_lock);
}
// remove function from cache
function->remove(tdbb);
// Now handle the new definition
const bool compile = !work->findArg(dfw_arg_check_blr);
get_function_dependencies(work, compile, transaction);
function->fun_flags &= ~(FUN_obsolete | FUN_being_altered);
}
return true;
case 5:
if (work->findArg(dfw_arg_check_blr))
{
jrd_req* request = CMP_find_request(tdbb, irq_fun_validate, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
FUN IN RDB$FUNCTIONS WITH
FUN.RDB$FUNCTION_ID EQ work->dfw_id
if (!REQUEST(irq_fun_validate))
REQUEST(irq_fun_validate) = request;
bool valid_blr = false;
MemoryPool* const csb_pool = dbb->createPool();
Jrd::ContextPoolHolder context(tdbb, csb_pool);
CompilerScratch* const csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5);
try
{
function->parseBlr(tdbb, &FUN.RDB$FUNCTION_BLR, csb);
valid_blr = true;
}
catch (const Firebird::Exception&)
{
fb_utils::init_status(tdbb->tdbb_status_vector);
}
dbb->deletePool(csb_pool);
MODIFY FUN USING
FUN.RDB$VALID_BLR = valid_blr ? TRUE : FALSE;
FUN.RDB$VALID_BLR.NULL = FALSE;
END_MODIFY;
END_FOR;
if (!REQUEST(irq_fun_validate))
REQUEST(irq_fun_validate) = request;
}
break;
}
return false;
}
static bool modify_procedure(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_tra* transaction)
{
/**************************************

View File

@ -155,6 +155,8 @@ enum irq_type_t
irq_f_security, // verify security for function
irq_l_arg_fld, // lookup argument's domain
irq_func_ret_fld, // lookup argument's domain
irq_fun_validate, // function blr validate
irq_c_fun_dpd, // get function dependencies
irq_MAX
};

View File

@ -429,7 +429,9 @@ enum dfw_t {
//dfw_unlink_file,
dfw_delete_generator,
dfw_modify_generator,
dfw_delete_udf,
dfw_create_function,
dfw_modify_function,
dfw_delete_function,
dfw_add_difference,
dfw_delete_difference,
dfw_begin_backup,

View File

@ -1297,7 +1297,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
SCL_check_function(tdbb, &desc, SCL_delete);
}
DFW_post_work(transaction, dfw_delete_udf, &desc, 0);
DFW_post_work(transaction, dfw_delete_function, &desc, 0);
break;
case rel_indices:
@ -2296,7 +2296,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j
EVL_field(0, org_rpb->rpb_record, f_prc_id, &desc2);
const USHORT id = MOV_get_long(&desc2, 0);
DFW_post_work(transaction, dfw_modify_procedure, &desc1, id, package_name);
DFW_post_work(transaction, dfw_modify_function, &desc1, id, package_name);
} // scope
break;
@ -2770,6 +2770,29 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction)
set_system_flag(tdbb, rpb, f_prc_sys_flag, 0);
break;
case rel_funs:
EVL_field(0, rpb->rpb_record, f_fun_name, &desc);
{ // scope
SqlIdentifier package_name;
if (EVL_field(0, rpb->rpb_record, f_fun_pkg_name, &desc2))
MOV_get_metadata_str(&desc2, package_name, sizeof(package_name));
else
package_name[0] = '\0';
EVL_field(0, rpb->rpb_record, f_fun_id, &desc2);
const USHORT id = MOV_get_long(&desc2, 0);
work = DFW_post_work(transaction, dfw_create_function, &desc, id, package_name);
bool check_blr = true;
if (EVL_field(0, rpb->rpb_record, f_fun_valid_blr, &desc2))
check_blr = MOV_get_long(&desc2, 0) != 0;
if (check_blr)
DFW_post_work_arg(transaction, work, NULL, 0, dfw_arg_check_blr);
} // scope
set_system_flag(tdbb, rpb, f_fun_sys_flag, 0);
break;
case rel_indices:
EVL_field(0, rpb->rpb_record, f_idx_relation, &desc);
SCL_check_relation(tdbb, &desc, SCL_control);