mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 22:03:04 +01:00
5162 lines
135 KiB
Plaintext
5162 lines
135 KiB
Plaintext
/*
|
|
* PROGRAM: JRD Access Method
|
|
* MODULE: met.epp
|
|
* DESCRIPTION: Meta data handler
|
|
*
|
|
* 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.6.25 Claudio Valderrama: Finish MET_lookup_generator_id() by
|
|
* assigning it a number in the compiled requests table.
|
|
*
|
|
* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
|
|
* conditionals, as the engine now fully supports
|
|
* readonly databases.
|
|
* 2001.10.03 Claudio Valderrama: MET_relation_owns_trigger() determines if
|
|
* there's a row in rdb$triggers with the given relation's and trigger's names.
|
|
* 2001.10.04 Claudio Valderrama: MET_relation_default_class() determines if the
|
|
* given security class name is the default security class for a relation.
|
|
* Modify MET_lookup_field() so it can verify the field's security class, too.
|
|
* 2002-02-24 Sean Leyne - Code Cleanup of old Win 3.1 port (WINDOWS_ONLY)
|
|
* 2002-09-16 Nickolay Samofatov - Deferred trigger compilation changes
|
|
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
|
|
* 2004.01.16 Vlad Horsun: added support for default parameters
|
|
*/
|
|
// This MUST be at the top of the file
|
|
#ifdef DARWIN
|
|
#define _STLP_CCTYPE
|
|
#endif
|
|
|
|
|
|
#include "firebird.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "../jrd/common.h"
|
|
#include <stdarg.h>
|
|
|
|
#include "../jrd/ibase.h"
|
|
#include "../jrd/jrd.h"
|
|
#include "../jrd/val.h"
|
|
#include "../jrd/irq.h"
|
|
#include "../jrd/tra.h"
|
|
#include "../jrd/lck.h"
|
|
#include "../jrd/ods.h"
|
|
#include "../jrd/btr.h"
|
|
#include "../jrd/req.h"
|
|
#include "../jrd/exe.h"
|
|
#include "../jrd/scl.h"
|
|
#include "../jrd/blb.h"
|
|
#include "../jrd/met.h"
|
|
#include "../jrd/os/pio.h"
|
|
#include "../jrd/sdw.h"
|
|
#include "../jrd/flags.h"
|
|
#include "../jrd/lls.h"
|
|
#include "../jrd/intl.h"
|
|
#include "../jrd/align.h"
|
|
#include "../jrd/flu.h"
|
|
#include "../jrd/blob_filter.h"
|
|
#include "../intl/charsets.h"
|
|
#include "../jrd/gdsassert.h"
|
|
#include "../jrd/blb_proto.h"
|
|
#include "../jrd/cmp_proto.h"
|
|
#include "../jrd/dfw_proto.h"
|
|
#include "../jrd/dsc_proto.h"
|
|
#include "../jrd/err_proto.h"
|
|
#include "../jrd/exe_proto.h"
|
|
#include "../jrd/ext_proto.h"
|
|
#include "../jrd/flu_proto.h"
|
|
#include "../jrd/gds_proto.h"
|
|
#include "../jrd/idx_proto.h"
|
|
#include "../jrd/ini_proto.h"
|
|
|
|
#include "../jrd/lck_proto.h"
|
|
#include "../jrd/met_proto.h"
|
|
#include "../jrd/mov_proto.h"
|
|
#include "../jrd/par_proto.h"
|
|
#include "../jrd/pcmet_proto.h"
|
|
#include "../jrd/os/pio_proto.h"
|
|
#include "../jrd/scl_proto.h"
|
|
#include "../jrd/sdw_proto.h"
|
|
#include "../jrd/thread_proto.h"
|
|
#include "../common/utils_proto.h"
|
|
|
|
#include "../../src/jrd/DebugInterface.h"
|
|
#include "../common/classes/MsgPrint.h"
|
|
|
|
|
|
#ifdef HAVE_CTYPE_H
|
|
#include <ctype.h>
|
|
#endif
|
|
|
|
/* Pick up relation ids */
|
|
#include "../jrd/ini.h"
|
|
|
|
#define BLR_BYTE *(csb->csb_running)++
|
|
const char BLANK = '\040';
|
|
|
|
DATABASE DB = FILENAME "ODS.RDB";
|
|
|
|
using namespace Jrd;
|
|
using namespace Firebird;
|
|
|
|
static int blocking_ast_dsql_cache(void* ast_object);
|
|
static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, int type, const Firebird::MetaName& name);
|
|
static int blocking_ast_procedure(void*);
|
|
static int blocking_ast_relation(void*);
|
|
static int partners_ast_relation(void*);
|
|
static void get_trigger(thread_db*, jrd_rel*, bid*, bid*, trig_vec**, const TEXT*, UCHAR, bool, USHORT);
|
|
static bool get_type(thread_db*, USHORT*, const UCHAR*, const TEXT*);
|
|
static void lookup_view_contexts(thread_db*, jrd_rel*);
|
|
static void make_relation_scope_name(const TEXT*, const USHORT, Firebird::string& str);
|
|
static jrd_nod* parse_field_blr(thread_db* tdbb, bid* blob_id, const Firebird::MetaName name = Firebird::MetaName());
|
|
static jrd_nod* parse_param_blr(thread_db*, jrd_prc*, bid*, CompilerScratch*);
|
|
static jrd_nod* parse_procedure_blr(thread_db*, jrd_prc*, bid*, CompilerScratch*);
|
|
static void par_messages(thread_db*, const UCHAR*, USHORT, jrd_prc*, CompilerScratch*);
|
|
static bool resolve_charset_and_collation(thread_db*, USHORT*, const UCHAR*, const UCHAR*);
|
|
static void save_trigger_data(thread_db*, trig_vec**, jrd_rel*, jrd_req*, blb*, bid*,
|
|
const TEXT*, UCHAR, bool, USHORT);
|
|
static void store_dependencies(thread_db*, CompilerScratch*, const jrd_rel*,
|
|
const Firebird::MetaName&, int, jrd_tra*);
|
|
static bool verify_TRG_ignore_perm(thread_db*, const Firebird::MetaName&);
|
|
|
|
|
|
|
|
// Decompile all triggers from vector
|
|
static void release_cached_triggers(thread_db* tdbb, trig_vec* vector)
|
|
{
|
|
if (!vector)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < vector->getCount(); i++)
|
|
{
|
|
(*vector)[i].release(tdbb);
|
|
}
|
|
}
|
|
|
|
|
|
static void inc_int_use_count(jrd_req* req)
|
|
{
|
|
// Increment int_use_count for all procedures in resource list of request
|
|
ResourceList& list = req->req_resources;
|
|
size_t i;
|
|
for (list.find(Resource(Resource::rsc_procedure, 0, NULL, NULL, NULL), i);
|
|
i < list.getCount(); i++)
|
|
{
|
|
Resource& resource = list[i];
|
|
if (resource.rsc_type != Resource::rsc_procedure)
|
|
break;
|
|
fb_assert(resource.rsc_prc->prc_int_use_count >= 0);
|
|
++resource.rsc_prc->prc_int_use_count;
|
|
}
|
|
}
|
|
|
|
|
|
// Increment int_use_count for all procedures used by triggers
|
|
static void post_used_procedures(trig_vec* vector)
|
|
{
|
|
if (!vector)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (size_t i = 0; i < vector->getCount(); i++)
|
|
{
|
|
jrd_req* r = (*vector)[i].request;
|
|
if (r && !CMP_clone_is_active(r))
|
|
{
|
|
inc_int_use_count(r);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void MET_get_domain(thread_db* tdbb, const Firebird::MetaName& name, dsc* desc, FieldInfo* fieldInfo)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ d o m a i n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get domain descriptor and informations.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
bool found = false;
|
|
|
|
jrd_req* handle = CMP_find_request(tdbb, irq_l_domain, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle)
|
|
FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ name.c_str()
|
|
|
|
if (!REQUEST(irq_l_domain))
|
|
REQUEST(irq_l_domain) = handle;
|
|
|
|
if (DSC_make_descriptor(desc,
|
|
FLD.RDB$FIELD_TYPE,
|
|
FLD.RDB$FIELD_SCALE,
|
|
FLD.RDB$FIELD_LENGTH,
|
|
FLD.RDB$FIELD_SUB_TYPE,
|
|
FLD.RDB$CHARACTER_SET_ID,
|
|
FLD.RDB$COLLATION_ID))
|
|
{
|
|
found = true;
|
|
|
|
if (fieldInfo)
|
|
{
|
|
fieldInfo->nullable = FLD.RDB$NULL_FLAG.NULL || FLD.RDB$NULL_FLAG == 0;
|
|
|
|
MemoryPool* csb_pool = dbb->createPool();
|
|
Jrd::ContextPoolHolder context(tdbb, csb_pool);
|
|
try
|
|
{
|
|
if (FLD.RDB$DEFAULT_VALUE.NULL)
|
|
fieldInfo->defaultValue = NULL;
|
|
else
|
|
fieldInfo->defaultValue = parse_field_blr(tdbb, &FLD.RDB$DEFAULT_VALUE);
|
|
|
|
if (FLD.RDB$VALIDATION_BLR.NULL)
|
|
fieldInfo->validation = NULL;
|
|
else
|
|
fieldInfo->validation = parse_field_blr(tdbb, &FLD.RDB$VALIDATION_BLR, name);
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{
|
|
dbb->deletePool(csb_pool);
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_domain))
|
|
REQUEST(irq_l_domain) = handle;
|
|
|
|
if (!found)
|
|
{
|
|
ERR_post(Arg::Gds(isc_domnotdef) << Arg::Str(name));
|
|
}
|
|
}
|
|
|
|
|
|
Firebird::MetaName MET_get_relation_field(thread_db* tdbb,
|
|
const Firebird::MetaName& relationName,
|
|
const Firebird::MetaName& fieldName,
|
|
dsc* desc,
|
|
FieldInfo* fieldInfo)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ r e l a t i o n _ f i e l d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get relation field descriptor and informations.
|
|
* Returns field source name.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
bool found = false;
|
|
Firebird::MetaName sourceName;
|
|
|
|
jrd_req* handle = CMP_find_request(tdbb, irq_l_relfield, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle)
|
|
RFL IN RDB$RELATION_FIELDS CROSS
|
|
FLD IN RDB$FIELDS WITH
|
|
RFL.RDB$RELATION_NAME EQ relationName.c_str() AND
|
|
RFL.RDB$FIELD_NAME EQ fieldName.c_str() AND
|
|
FLD.RDB$FIELD_NAME EQ RFL.RDB$FIELD_SOURCE
|
|
|
|
if (!REQUEST(irq_l_relfield))
|
|
REQUEST(irq_l_relfield) = handle;
|
|
|
|
if (DSC_make_descriptor(desc,
|
|
FLD.RDB$FIELD_TYPE,
|
|
FLD.RDB$FIELD_SCALE,
|
|
FLD.RDB$FIELD_LENGTH,
|
|
FLD.RDB$FIELD_SUB_TYPE,
|
|
FLD.RDB$CHARACTER_SET_ID,
|
|
FLD.RDB$COLLATION_ID))
|
|
{
|
|
found = true;
|
|
sourceName = RFL.RDB$FIELD_SOURCE;
|
|
|
|
if (fieldInfo)
|
|
{
|
|
fieldInfo->nullable = RFL.RDB$NULL_FLAG.NULL ?
|
|
(FLD.RDB$NULL_FLAG.NULL || FLD.RDB$NULL_FLAG == 0) : RFL.RDB$NULL_FLAG == 0;
|
|
|
|
MemoryPool* csb_pool = dbb->createPool();
|
|
Jrd::ContextPoolHolder context(tdbb, csb_pool);
|
|
try
|
|
{
|
|
bid* defaultId = NULL;
|
|
|
|
if (!RFL.RDB$DEFAULT_VALUE.NULL)
|
|
defaultId = &RFL.RDB$DEFAULT_VALUE;
|
|
else if (!FLD.RDB$DEFAULT_VALUE.NULL)
|
|
defaultId = &FLD.RDB$DEFAULT_VALUE;
|
|
|
|
if (defaultId)
|
|
fieldInfo->defaultValue = parse_field_blr(tdbb, defaultId);
|
|
else
|
|
fieldInfo->defaultValue = NULL;
|
|
|
|
if (!FLD.RDB$VALIDATION_BLR.NULL)
|
|
fieldInfo->validation = parse_field_blr(tdbb, &FLD.RDB$VALIDATION_BLR, RFL.RDB$FIELD_SOURCE);
|
|
else
|
|
fieldInfo->validation = NULL;
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{
|
|
dbb->deletePool(csb_pool);
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_relfield))
|
|
REQUEST(irq_l_relfield) = handle;
|
|
|
|
if (!found)
|
|
{
|
|
ERR_post(Arg::Gds(isc_dyn_column_does_not_exist) << Arg::Str(fieldName) <<
|
|
Arg::Str(relationName));
|
|
}
|
|
|
|
return sourceName;
|
|
}
|
|
|
|
|
|
void MET_update_partners(thread_db* tdbb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ u p d a t e _ p a r t n e r s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Mark all relations to update their links to FK partners
|
|
* Called when any index is deleted because engine don't know
|
|
* was it used in any FK or not
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
vec<jrd_rel*>* relations = dbb->dbb_relations;
|
|
|
|
vec<jrd_rel*>::iterator ptr = relations->begin();
|
|
for (const vec<jrd_rel*>::const_iterator end = relations->end(); ptr < end; ++ptr)
|
|
{
|
|
jrd_rel* relation = *ptr;
|
|
if (!relation) {
|
|
continue;
|
|
}
|
|
|
|
// signal other processes
|
|
LCK_lock(tdbb, relation->rel_partners_lock, LCK_EX, LCK_WAIT);
|
|
LCK_release(tdbb, relation->rel_partners_lock);
|
|
relation->rel_flags |= REL_check_partners;
|
|
}
|
|
}
|
|
|
|
|
|
void adjust_dependencies(jrd_prc* procedure)
|
|
{
|
|
if (procedure->prc_int_use_count == -1) {
|
|
// Already processed
|
|
return;
|
|
}
|
|
procedure->prc_int_use_count = -1; // Mark as undeletable
|
|
if (procedure->prc_request) {
|
|
// Loop over procedures from resource list of prc_request
|
|
ResourceList& list = procedure->prc_request->req_resources;
|
|
size_t i;
|
|
for (list.find(Resource(Resource::rsc_procedure, 0, NULL, NULL, NULL), i);
|
|
i < list.getCount(); i++)
|
|
{
|
|
Resource& resource = list[i];
|
|
if (resource.rsc_type != Resource::rsc_procedure)
|
|
break;
|
|
procedure = resource.rsc_prc;
|
|
if (procedure->prc_int_use_count == procedure->prc_use_count) {
|
|
// Mark it and all dependent procedures as undeletable
|
|
adjust_dependencies(procedure);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef DEV_BUILD
|
|
void MET_verify_cache(thread_db* tdbb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ v e r i f y _ c a c h e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Check if all links between procedures are properly counted
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
vec<jrd_prc*>* procedures = dbb->dbb_procedures;
|
|
if (procedures) {
|
|
jrd_prc* procedure;
|
|
vec<jrd_prc*>::iterator ptr, end;
|
|
|
|
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ++ptr)
|
|
{
|
|
if ( (procedure = *ptr) && procedure->prc_request /*&&
|
|
!(procedure->prc_flags & PRC_obsolete)*/ )
|
|
{
|
|
fb_assert(procedure->prc_int_use_count == 0);
|
|
}
|
|
}
|
|
|
|
/* Walk procedures and calculate internal dependencies */
|
|
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = *ptr) && procedure->prc_request /*&&
|
|
!(procedure->prc_flags & PRC_obsolete)*/ )
|
|
{
|
|
inc_int_use_count(procedure->prc_request);
|
|
}
|
|
}
|
|
|
|
/* Walk procedures again and check dependencies */
|
|
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = *ptr) && procedure->prc_request && /*
|
|
!(procedure->prc_flags & PRC_obsolete) && */
|
|
procedure->prc_use_count < procedure->prc_int_use_count)
|
|
{
|
|
char buffer[1024], *buf = buffer;
|
|
buf += sprintf(buf, "Procedure %d:%s is not properly counted (use count=%d, prc use=%d). Used by: \n",
|
|
procedure->prc_id, procedure->prc_name.c_str(), procedure->prc_use_count, procedure->prc_int_use_count);
|
|
vec<jrd_prc*>::const_iterator ptr2 = procedures->begin();
|
|
for (const vec<jrd_prc*>::const_iterator end2 = procedures->end();
|
|
ptr2 < end2; ++ptr2)
|
|
{
|
|
const jrd_prc* prc = *ptr2;
|
|
if (prc && prc->prc_request /*&& !(prc->prc_flags & PRC_obsolete)*/ )
|
|
{
|
|
// Loop over procedures from resource list of prc_request
|
|
const ResourceList& list = prc->prc_request->req_resources;
|
|
size_t i;
|
|
for (list.find(Resource(Resource::rsc_procedure, 0, NULL, NULL, NULL), i);
|
|
i < list.getCount(); i++)
|
|
{
|
|
const Resource& resource = list[i];
|
|
if (resource.rsc_type != Resource::rsc_procedure)
|
|
break;
|
|
if (resource.rsc_prc == procedure) {
|
|
buf += sprintf(buf, "%d:%s\n", prc->prc_id, prc->prc_name.c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
gds__log(buffer);
|
|
fb_assert(false);
|
|
}
|
|
}
|
|
|
|
/* Fix back int_use_count */
|
|
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = *ptr) )
|
|
{
|
|
procedure->prc_int_use_count = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
void MET_clear_cache(thread_db* tdbb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ c l e a r _ c a c h e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Remove all unused objects from metadata cache to
|
|
* release resources they use
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
#ifdef DEV_BUILD
|
|
MET_verify_cache(tdbb);
|
|
#endif
|
|
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
vec<jrd_rel*>* relations = dbb->dbb_relations;
|
|
{ // scope
|
|
vec<jrd_rel*>::iterator ptr, end;
|
|
for (ptr = relations->begin(), end = relations->end(); ptr < end; ++ptr)
|
|
{
|
|
jrd_rel* relation = *ptr;
|
|
if (!relation)
|
|
continue;
|
|
release_cached_triggers(tdbb, relation->rel_pre_store);
|
|
release_cached_triggers(tdbb, relation->rel_post_store);
|
|
release_cached_triggers(tdbb, relation->rel_pre_erase);
|
|
release_cached_triggers(tdbb, relation->rel_post_erase);
|
|
release_cached_triggers(tdbb, relation->rel_pre_modify);
|
|
release_cached_triggers(tdbb, relation->rel_post_modify);
|
|
}
|
|
} // scope
|
|
|
|
vec<jrd_prc*>* procedures = dbb->dbb_procedures;
|
|
if (procedures)
|
|
{
|
|
jrd_prc* procedure;
|
|
vec<jrd_prc*>::iterator ptr, end;
|
|
/* Walk procedures and calculate internal dependencies */
|
|
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ++ptr)
|
|
{
|
|
if ( (procedure = *ptr) && procedure->prc_request &&
|
|
!(procedure->prc_flags & PRC_obsolete) )
|
|
{
|
|
inc_int_use_count(procedure->prc_request);
|
|
}
|
|
}
|
|
|
|
// Walk procedures again and adjust dependencies for procedures
|
|
// which will not be removed.
|
|
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = *ptr) && procedure->prc_request &&
|
|
!(procedure->prc_flags & PRC_obsolete) &&
|
|
procedure->prc_use_count != procedure->prc_int_use_count )
|
|
{
|
|
adjust_dependencies(procedure);
|
|
}
|
|
}
|
|
|
|
/* Deallocate all used requests */
|
|
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = *ptr) )
|
|
{
|
|
|
|
if ( procedure->prc_request && !(procedure->prc_flags & PRC_obsolete) &&
|
|
procedure->prc_int_use_count >= 0 &&
|
|
procedure->prc_use_count == procedure->prc_int_use_count )
|
|
{
|
|
CMP_release(tdbb, procedure->prc_request);
|
|
procedure->prc_request = NULL;
|
|
LCK_release(tdbb, procedure->prc_existence_lock);
|
|
procedure->prc_existence_lock = NULL;
|
|
procedure->prc_flags |= PRC_obsolete;
|
|
}
|
|
// Leave it in state 0 to avoid extra pass next time to clear it
|
|
// Note: we need to adjust prc_int_use_count for all procedures
|
|
// in cache because any of them may have been affected from
|
|
// dependencies earlier. Even procedures that were not scanned yet !
|
|
procedure->prc_int_use_count = 0;
|
|
}
|
|
}
|
|
|
|
/* Remove deallocated procedures from cache */
|
|
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = *ptr) && (procedure->prc_flags & PRC_obsolete) )
|
|
{
|
|
MET_remove_procedure(tdbb, procedure->prc_id, procedure);
|
|
}
|
|
}
|
|
|
|
}
|
|
#ifdef DEV_BUILD
|
|
MET_verify_cache(tdbb);
|
|
#endif
|
|
}
|
|
|
|
|
|
bool MET_procedure_in_use(thread_db* tdbb, jrd_prc* proc)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ p r o c e d u r e _ i n _ u s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Determine if procedure is used by any user requests or transactions.
|
|
* Return false if procedure is used only inside cache or not used at all.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
#ifdef DEV_BUILD
|
|
MET_verify_cache(tdbb);
|
|
#endif
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
// This should not really happen
|
|
vec<jrd_prc*>* procedures = dbb->dbb_procedures;
|
|
if (!procedures) {
|
|
return false;
|
|
}
|
|
|
|
vec<jrd_rel*>* relations = dbb->dbb_relations;
|
|
{ // scope
|
|
vec<jrd_rel*>::iterator ptr, end;
|
|
for (ptr = relations->begin(), end = relations->end(); ptr < end; ++ptr)
|
|
{
|
|
jrd_rel* relation = *ptr;
|
|
if (!relation) {
|
|
continue;
|
|
}
|
|
post_used_procedures(relation->rel_pre_store);
|
|
post_used_procedures(relation->rel_post_store);
|
|
post_used_procedures(relation->rel_pre_erase);
|
|
post_used_procedures(relation->rel_post_erase);
|
|
post_used_procedures(relation->rel_pre_modify);
|
|
post_used_procedures(relation->rel_post_modify);
|
|
}
|
|
} // scope
|
|
|
|
jrd_prc* procedure;
|
|
vec<jrd_prc*>::iterator ptr, end;
|
|
/* Walk procedures and calculate internal dependencies */
|
|
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ++ptr)
|
|
{
|
|
if ((procedure = *ptr) && procedure->prc_request && !(procedure->prc_flags & PRC_obsolete))
|
|
{
|
|
inc_int_use_count(procedure->prc_request);
|
|
}
|
|
}
|
|
|
|
// Walk procedures again and adjust dependencies for procedures
|
|
// which will not be removed.
|
|
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = *ptr) && procedure->prc_request &&
|
|
!(procedure->prc_flags & PRC_obsolete) &&
|
|
procedure->prc_use_count != procedure->prc_int_use_count && procedure != proc )
|
|
{
|
|
adjust_dependencies(procedure);
|
|
}
|
|
}
|
|
|
|
const bool result = proc->prc_use_count != proc->prc_int_use_count;
|
|
|
|
/* Fix back int_use_count */
|
|
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = *ptr) )
|
|
{
|
|
procedure->prc_int_use_count = 0;
|
|
}
|
|
}
|
|
#ifdef DEV_BUILD
|
|
MET_verify_cache(tdbb);
|
|
#endif
|
|
return result;
|
|
}
|
|
|
|
|
|
void MET_activate_shadow(thread_db* tdbb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ a c t i v a t e _ s h a d o w
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Activate the current database, which presumably
|
|
* was formerly a shadow, by deleting all records
|
|
* corresponding to the shadow that this database
|
|
* represents.
|
|
* Get rid of write ahead log for the activated shadow.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
/* Erase any secondary files of the primary database of the
|
|
shadow being activated. */
|
|
|
|
jrd_req* handle = NULL;
|
|
FOR(REQUEST_HANDLE handle) X IN RDB$FILES
|
|
WITH X.RDB$SHADOW_NUMBER NOT MISSING
|
|
AND X.RDB$SHADOW_NUMBER EQ 0
|
|
ERASE X;
|
|
END_FOR;
|
|
|
|
CMP_release(tdbb, handle);
|
|
|
|
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
|
|
const char* dbb_file_name = pageSpace->file->fil_string;
|
|
|
|
/* go through files looking for any that expand to the current database name */
|
|
SCHAR expanded_name[MAXPATHLEN];
|
|
jrd_req* handle2 = handle = NULL;
|
|
FOR(REQUEST_HANDLE handle) X IN RDB$FILES
|
|
WITH X.RDB$SHADOW_NUMBER NOT MISSING
|
|
AND X.RDB$SHADOW_NUMBER NE 0
|
|
|
|
PIO_expand(X.RDB$FILE_NAME, (USHORT)strlen(X.RDB$FILE_NAME),
|
|
expanded_name, sizeof(expanded_name));
|
|
|
|
if (!strcmp(expanded_name, dbb_file_name)) {
|
|
FOR(REQUEST_HANDLE handle2) Y IN RDB$FILES
|
|
WITH X.RDB$SHADOW_NUMBER EQ Y.RDB$SHADOW_NUMBER
|
|
MODIFY Y
|
|
Y.RDB$SHADOW_NUMBER = 0;
|
|
END_MODIFY;
|
|
END_FOR;
|
|
ERASE X;
|
|
}
|
|
END_FOR;
|
|
|
|
if (handle2)
|
|
{
|
|
CMP_release(tdbb, handle2);
|
|
}
|
|
CMP_release(tdbb, handle);
|
|
}
|
|
|
|
|
|
ULONG MET_align(Database* dbb, const dsc* desc, ULONG value)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ a l i g n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Align value (presumed offset) on appropriate border
|
|
* and return.
|
|
*
|
|
**************************************/
|
|
USHORT alignment = desc->dsc_length;
|
|
switch (desc->dsc_dtype)
|
|
{
|
|
case dtype_text:
|
|
case dtype_cstring:
|
|
return value;
|
|
|
|
case dtype_varying:
|
|
alignment = sizeof(USHORT);
|
|
break;
|
|
}
|
|
|
|
alignment = MIN(alignment, dbb->dbb_ods_version >= ODS_VERSION11 ? FORMAT_ALIGNMENT : FB_ALIGNMENT);
|
|
|
|
return FB_ALIGN(value, alignment);
|
|
}
|
|
|
|
|
|
DeferredWork* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const dsc* field_source)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ c h a n g e _ f i e l d s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Somebody is modifying RDB$FIELDS.
|
|
* Find all relations affected and schedule a format update.
|
|
* Find all procedures and triggers and schedule a BLR validate.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
dsc relation_name;
|
|
DeferredWork* dw = 0;
|
|
jrd_req* request = CMP_find_request(tdbb, irq_m_fields, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATION_FIELDS WITH
|
|
X.RDB$FIELD_SOURCE EQ field_source->dsc_address
|
|
|
|
if (!REQUEST(irq_m_fields))
|
|
REQUEST(irq_m_fields) = request;
|
|
|
|
relation_name.dsc_dtype = dtype_text;
|
|
INTL_ASSIGN_DSC(&relation_name, CS_METADATA, COLLATE_NONE);
|
|
relation_name.dsc_length = sizeof(X.RDB$RELATION_NAME);
|
|
relation_name.dsc_address = (UCHAR *) X.RDB$RELATION_NAME;
|
|
SCL_check_relation(tdbb, &relation_name, SCL_control);
|
|
dw = DFW_post_work(transaction, dfw_update_format, &relation_name, 0);
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_m_fields))
|
|
REQUEST(irq_m_fields) = request;
|
|
|
|
if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1)
|
|
{
|
|
request = CMP_find_request(tdbb, irq_m_fields2, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
DEP IN RDB$DEPENDENCIES CROSS
|
|
PRC IN RDB$PROCEDURES
|
|
WITH DEP.RDB$DEPENDED_ON_NAME EQ field_source->dsc_address AND
|
|
DEP.RDB$DEPENDED_ON_TYPE EQ obj_field AND
|
|
DEP.RDB$DEPENDENT_TYPE EQ obj_procedure AND
|
|
DEP.RDB$DEPENDENT_NAME EQ PRC.RDB$PROCEDURE_NAME
|
|
|
|
if (!REQUEST(irq_m_fields2))
|
|
REQUEST(irq_m_fields2) = request;
|
|
|
|
Firebird::MetaName proc_name(PRC.RDB$PROCEDURE_NAME);
|
|
|
|
dsc desc;
|
|
desc.dsc_dtype = dtype_text;
|
|
INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE);
|
|
desc.dsc_length = proc_name.length();
|
|
desc.dsc_address = (UCHAR*) proc_name.c_str();
|
|
|
|
DeferredWork* dw2 =
|
|
DFW_post_work(transaction, dfw_modify_procedure, &desc, PRC.RDB$PROCEDURE_ID);
|
|
DFW_post_work_arg(transaction, dw2, NULL, 0)->dfw_type = dfw_arg_check_blr;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_m_fields2))
|
|
REQUEST(irq_m_fields2) = request;
|
|
|
|
request = CMP_find_request(tdbb, irq_m_fields3, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
DEP IN RDB$DEPENDENCIES CROSS
|
|
TRG IN RDB$TRIGGERS
|
|
WITH DEP.RDB$DEPENDED_ON_NAME EQ field_source->dsc_address AND
|
|
DEP.RDB$DEPENDED_ON_TYPE EQ obj_field AND
|
|
DEP.RDB$DEPENDENT_TYPE EQ obj_trigger AND
|
|
DEP.RDB$DEPENDENT_NAME EQ TRG.RDB$TRIGGER_NAME
|
|
|
|
if (!REQUEST(irq_m_fields3))
|
|
REQUEST(irq_m_fields3) = request;
|
|
|
|
Firebird::MetaName trigger_name(TRG.RDB$TRIGGER_NAME);
|
|
Firebird::MetaName trigger_relation_name(TRG.RDB$RELATION_NAME);
|
|
|
|
dsc desc;
|
|
desc.dsc_dtype = dtype_text;
|
|
INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE);
|
|
desc.dsc_length = trigger_name.length();
|
|
desc.dsc_address = (UCHAR*) trigger_name.c_str();
|
|
|
|
DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_trigger, &desc, 0);
|
|
DFW_post_work_arg(transaction, dw2, NULL,
|
|
TRG.RDB$TRIGGER_TYPE)->dfw_type = dfw_arg_trg_type;
|
|
|
|
desc.dsc_length = trigger_relation_name.length();
|
|
desc.dsc_address = (UCHAR*) trigger_relation_name.c_str();
|
|
DFW_post_work_arg(transaction, dw2, &desc, 0)->dfw_type = dfw_arg_check_blr;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_m_fields3))
|
|
REQUEST(irq_m_fields3) = request;
|
|
|
|
request = CMP_find_request(tdbb, irq_m_fields4, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
RFL IN RDB$RELATION_FIELDS CROSS
|
|
DEP IN RDB$DEPENDENCIES CROSS
|
|
PRC IN RDB$PROCEDURES
|
|
WITH RFL.RDB$FIELD_SOURCE EQ field_source->dsc_address AND
|
|
DEP.RDB$DEPENDED_ON_NAME EQ RFL.RDB$RELATION_NAME AND
|
|
DEP.RDB$FIELD_NAME EQ RFL.RDB$FIELD_NAME AND
|
|
DEP.RDB$DEPENDED_ON_TYPE EQ obj_relation AND
|
|
DEP.RDB$DEPENDENT_TYPE EQ obj_procedure AND
|
|
DEP.RDB$DEPENDENT_NAME EQ PRC.RDB$PROCEDURE_NAME
|
|
|
|
if (!REQUEST(irq_m_fields4))
|
|
REQUEST(irq_m_fields4) = request;
|
|
|
|
Firebird::MetaName proc_name(PRC.RDB$PROCEDURE_NAME);
|
|
|
|
dsc desc;
|
|
desc.dsc_dtype = dtype_text;
|
|
INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE);
|
|
desc.dsc_length = proc_name.length();
|
|
desc.dsc_address = (UCHAR*) proc_name.c_str();
|
|
|
|
DeferredWork* dw2 =
|
|
DFW_post_work(transaction, dfw_modify_procedure, &desc, PRC.RDB$PROCEDURE_ID);
|
|
DFW_post_work_arg(transaction, dw2, NULL, 0)->dfw_type = dfw_arg_check_blr;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_m_fields4))
|
|
REQUEST(irq_m_fields4) = request;
|
|
|
|
request = CMP_find_request(tdbb, irq_m_fields5, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
RFL IN RDB$RELATION_FIELDS CROSS
|
|
DEP IN RDB$DEPENDENCIES CROSS
|
|
TRG IN RDB$TRIGGERS
|
|
WITH RFL.RDB$FIELD_SOURCE EQ field_source->dsc_address AND
|
|
DEP.RDB$DEPENDED_ON_NAME EQ RFL.RDB$RELATION_NAME AND
|
|
DEP.RDB$FIELD_NAME EQ RFL.RDB$FIELD_NAME AND
|
|
DEP.RDB$DEPENDED_ON_TYPE EQ obj_relation AND
|
|
DEP.RDB$DEPENDENT_TYPE EQ obj_trigger AND
|
|
DEP.RDB$DEPENDENT_NAME EQ TRG.RDB$TRIGGER_NAME
|
|
|
|
if (!REQUEST(irq_m_fields5))
|
|
REQUEST(irq_m_fields5) = request;
|
|
|
|
Firebird::MetaName trigger_name(TRG.RDB$TRIGGER_NAME);
|
|
Firebird::MetaName trigger_relation_name(TRG.RDB$RELATION_NAME);
|
|
|
|
dsc desc;
|
|
desc.dsc_dtype = dtype_text;
|
|
INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE);
|
|
desc.dsc_length = trigger_name.length();
|
|
desc.dsc_address = (UCHAR*) trigger_name.c_str();
|
|
|
|
DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_trigger, &desc, 0);
|
|
DFW_post_work_arg(transaction, dw2, NULL,
|
|
TRG.RDB$TRIGGER_TYPE)->dfw_type = dfw_arg_trg_type;
|
|
|
|
desc.dsc_length = trigger_relation_name.length();
|
|
desc.dsc_address = (UCHAR*) trigger_relation_name.c_str();
|
|
DFW_post_work_arg(transaction, dw2, &desc, 0)->dfw_type = dfw_arg_check_blr;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_m_fields5))
|
|
REQUEST(irq_m_fields5) = request;
|
|
}
|
|
|
|
return dw;
|
|
}
|
|
|
|
|
|
Format* MET_current(thread_db* tdbb, jrd_rel* relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ c u r r e n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get the current format for a relation. The current format is the
|
|
* format in which new records are to be stored.
|
|
*
|
|
**************************************/
|
|
|
|
if (relation->rel_current_format)
|
|
return relation->rel_current_format;
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
return relation->rel_current_format = MET_format(tdbb, relation, relation->rel_current_fmt);
|
|
}
|
|
|
|
|
|
void MET_delete_dependencies(thread_db* tdbb,
|
|
const Firebird::MetaName& object_name,
|
|
int dependency_type,
|
|
jrd_tra* transaction)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ d e l e t e _ d e p e n d e n c i e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Delete all dependencies for the specified
|
|
* object of given type.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
// if (!object_name)
|
|
// return;
|
|
//
|
|
jrd_req* request = CMP_find_request(tdbb, irq_d_deps, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
DEP IN RDB$DEPENDENCIES
|
|
WITH DEP.RDB$DEPENDENT_NAME = object_name.c_str()
|
|
AND DEP.RDB$DEPENDENT_TYPE = dependency_type
|
|
|
|
if (!REQUEST(irq_d_deps))
|
|
REQUEST(irq_d_deps) = request;
|
|
|
|
ERASE DEP;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_d_deps))
|
|
REQUEST(irq_d_deps) = request;
|
|
}
|
|
|
|
|
|
void MET_delete_shadow(thread_db* tdbb, USHORT shadow_number)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ d e l e t e _ s h a d o w
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* When any of the shadows in RDB$FILES for a particular
|
|
* shadow are deleted, stop shadowing to that file and
|
|
* remove all other files from the same shadow.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* handle = NULL;
|
|
|
|
FOR(REQUEST_HANDLE handle)
|
|
X IN RDB$FILES WITH X.RDB$SHADOW_NUMBER EQ shadow_number
|
|
ERASE X;
|
|
END_FOR;
|
|
|
|
CMP_release(tdbb, handle);
|
|
|
|
for (Shadow* shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next) {
|
|
if (shadow->sdw_number == shadow_number) {
|
|
shadow->sdw_flags |= SDW_shutdown;
|
|
}
|
|
}
|
|
|
|
/* notify other processes to check for shadow deletion */
|
|
if (SDW_lck_update(tdbb, 0)) {
|
|
SDW_notify(tdbb);
|
|
}
|
|
}
|
|
|
|
|
|
bool MET_dsql_cache_use(thread_db* tdbb, int type, const Firebird::MetaName& name)
|
|
{
|
|
DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, name);
|
|
|
|
const bool obsolete = item->obsolete;
|
|
|
|
if (!item->locked)
|
|
{
|
|
// lock to be notified by others when we should mark as obsolete
|
|
LCK_lock(tdbb, item->lock, LCK_SR, LCK_WAIT);
|
|
item->locked = true;
|
|
}
|
|
|
|
item->obsolete = false;
|
|
|
|
return obsolete;
|
|
}
|
|
|
|
|
|
void MET_dsql_cache_release(thread_db* tdbb, int type, const Firebird::MetaName& name)
|
|
{
|
|
DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, name);
|
|
|
|
// release lock
|
|
LCK_release(tdbb, item->lock);
|
|
|
|
// notify others through AST to mark as obsolete
|
|
Database* const dbb = tdbb->getDatabase();
|
|
const size_t key_length = item->lock->lck_length;
|
|
Firebird::AutoPtr<Lock> temp_lock(FB_NEW_RPT(*tdbb->getDefaultPool(), key_length) Lock());
|
|
temp_lock->lck_dbb = dbb;
|
|
temp_lock->lck_parent = dbb->dbb_lock;
|
|
temp_lock->lck_type = LCK_dsql_cache;
|
|
temp_lock->lck_owner_handle = LCK_get_owner_handle(tdbb, temp_lock->lck_type);
|
|
temp_lock->lck_length = key_length;
|
|
memcpy(temp_lock->lck_key.lck_string, item->lock->lck_key.lck_string, key_length);
|
|
|
|
if (LCK_lock(tdbb, temp_lock, LCK_EX, LCK_WAIT))
|
|
LCK_release(tdbb, temp_lock);
|
|
|
|
item->locked = false;
|
|
item->obsolete = false;
|
|
}
|
|
|
|
|
|
void MET_error(const TEXT* string, ...)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Post an error in a metadata update
|
|
* Oh, wow.
|
|
*
|
|
**************************************/
|
|
TEXT s[128];
|
|
va_list ptr;
|
|
|
|
va_start(ptr, string);
|
|
VSNPRINTF(s, sizeof(s), string, ptr);
|
|
va_end(ptr);
|
|
|
|
ERR_post(Arg::Gds(isc_no_meta_update) <<
|
|
Arg::Gds(isc_random) << Arg::Str(s));
|
|
}
|
|
|
|
Format* MET_format(thread_db* tdbb, jrd_rel* relation, USHORT number)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ f o r m a t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup a format for given relation.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
Format* format;
|
|
vec<Format*>* formats = relation->rel_formats;
|
|
if (formats && (number < formats->count()) && (format = (*formats)[number]))
|
|
{
|
|
return format;
|
|
}
|
|
|
|
format = NULL;
|
|
jrd_req* request = CMP_find_request(tdbb, irq_r_format, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$FORMATS WITH X.RDB$RELATION_ID EQ relation->rel_id AND
|
|
X.RDB$FORMAT EQ number
|
|
|
|
if (!REQUEST(irq_r_format))
|
|
{
|
|
REQUEST(irq_r_format) = request;
|
|
}
|
|
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, &X.RDB$DESCRIPTOR);
|
|
|
|
if (sizeof(Ods::Descriptor) == sizeof(struct dsc) || dbb->dbb_ods_version < ODS_VERSION11)
|
|
{
|
|
// For ODS10 and earlier read descriptors in their in-memory representation
|
|
// This makes 64-bit and 32-bit ODS10 different for the same architecture.
|
|
|
|
const USHORT count = blob->blb_length / sizeof(struct dsc);
|
|
format = Format::newFormat(*dbb->dbb_permanent, count);
|
|
BLB_get_data(tdbb, blob, (UCHAR*) &(format->fmt_desc[0]), blob->blb_length);
|
|
|
|
for (Format::fmt_desc_const_iterator desc = format->fmt_desc.end() - 1;
|
|
desc >= format->fmt_desc.begin();
|
|
--desc)
|
|
{
|
|
if (desc->dsc_address)
|
|
{
|
|
format->fmt_length = (IPTR) desc->dsc_address + desc->dsc_length;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
// Use generic representation of formats with 32-bit offsets
|
|
|
|
const USHORT count = blob->blb_length / sizeof(Ods::Descriptor);
|
|
format = Format::newFormat(*dbb->dbb_permanent, count);
|
|
Firebird::Array<Ods::Descriptor> odsDescs;
|
|
Ods::Descriptor *odsDesc = odsDescs.getBuffer(count);
|
|
BLB_get_data(tdbb, blob, (UCHAR*) odsDesc, blob->blb_length);
|
|
|
|
for (Format::fmt_desc_iterator desc = format->fmt_desc.begin();
|
|
desc < format->fmt_desc.end(); ++desc, ++odsDesc)
|
|
{
|
|
*desc = *odsDesc;
|
|
if (odsDesc->dsc_offset)
|
|
format->fmt_length = odsDesc->dsc_offset + desc->dsc_length;
|
|
}
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_format)) {
|
|
REQUEST(irq_r_format) = request;
|
|
}
|
|
|
|
if (!format) {
|
|
format = Format::newFormat(*dbb->dbb_permanent);
|
|
}
|
|
|
|
format->fmt_version = number;
|
|
|
|
/* Link the format block into the world */
|
|
|
|
formats = relation->rel_formats =
|
|
vec<Format*>::newVector(*dbb->dbb_permanent, relation->rel_formats, number + 1);
|
|
(*formats)[number] = format;
|
|
|
|
return format;
|
|
}
|
|
|
|
|
|
bool MET_get_char_coll_subtype(thread_db* tdbb, USHORT* id, const UCHAR* name, USHORT length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ c h a r _ c o l l _ s u b t y p e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Character types can be specified as either:
|
|
* a) A POSIX style locale name "<collation>.<characterset>"
|
|
* or
|
|
* b) A simple <characterset> name (using default collation)
|
|
* c) A simple <collation> name (use charset for collation)
|
|
*
|
|
* Given an ASCII7 string which could be any of the above, try to
|
|
* resolve the name in the order a, b, c
|
|
* a) is only tried iff the name contains a period.
|
|
* (in which case b) and c) are not tried).
|
|
*
|
|
* Return:
|
|
* 1 if no errors (and *id is set).
|
|
* 0 if the name could not be resolved.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
fb_assert(id != NULL);
|
|
fb_assert(name != NULL);
|
|
|
|
const UCHAR* const end_name = name + length;
|
|
|
|
/* Force key to uppercase, following C locale rules for uppercasing */
|
|
/* At the same time, search for the first period in the string (if any) */
|
|
UCHAR buffer[32]; /* BASED ON RDB$COLLATION_NAME */
|
|
UCHAR* p = buffer;
|
|
UCHAR* period = NULL;
|
|
for (; name < end_name && p < buffer + sizeof(buffer) - 1; p++, name++) {
|
|
*p = UPPER7(*name);
|
|
if ((*p == '.') && !period) {
|
|
period = p;
|
|
}
|
|
}
|
|
*p = 0;
|
|
|
|
/* Is there a period, separating collation name from character set? */
|
|
if (period) {
|
|
*period = 0;
|
|
return resolve_charset_and_collation(tdbb, id, period + 1, buffer);
|
|
}
|
|
|
|
/* Is it a character set name (implying charset default collation sequence) */
|
|
|
|
bool res = resolve_charset_and_collation(tdbb, id, buffer, NULL);
|
|
if (!res) {
|
|
/* Is it a collation name (implying implementation-default character set) */
|
|
res = resolve_charset_and_collation(tdbb, id, NULL, buffer);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
|
|
bool MET_get_char_coll_subtype_info(thread_db* tdbb, USHORT id, SubtypeInfo* info)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ c h a r _ c o l l _ s u b t y p e _ i n f o
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get charset and collation informations
|
|
* for a subtype ID.
|
|
*
|
|
**************************************/
|
|
fb_assert(info != NULL);
|
|
|
|
const USHORT charset_id = id & 0x00FF;
|
|
const USHORT collation_id = id >> 8;
|
|
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_subtype, IRQ_REQUESTS);
|
|
bool found = false;
|
|
|
|
if (dbb->dbb_ods_version >= ODS_VERSION11)
|
|
{
|
|
FOR(REQUEST_HANDLE request) FIRST 1
|
|
CL IN RDB$COLLATIONS CROSS
|
|
CS IN RDB$CHARACTER_SETS
|
|
WITH CL.RDB$CHARACTER_SET_ID EQ charset_id AND
|
|
CL.RDB$COLLATION_ID EQ collation_id AND
|
|
CS.RDB$CHARACTER_SET_ID EQ CL.RDB$CHARACTER_SET_ID
|
|
|
|
found = true;
|
|
|
|
info->charsetName = CS.RDB$CHARACTER_SET_NAME;
|
|
info->collationName = CL.RDB$COLLATION_NAME;
|
|
|
|
if (CL.RDB$BASE_COLLATION_NAME.NULL)
|
|
info->baseCollationName = info->collationName;
|
|
else
|
|
info->baseCollationName = CL.RDB$BASE_COLLATION_NAME;
|
|
|
|
if (CL.RDB$SPECIFIC_ATTRIBUTES.NULL)
|
|
info->specificAttributes.clear();
|
|
else
|
|
{
|
|
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, &CL.RDB$SPECIFIC_ATTRIBUTES);
|
|
const SLONG length = blob->blb_length;
|
|
|
|
// ASF: Here info->specificAttributes is in UNICODE_FSS charset.
|
|
// It will be converted to the collation charset in intl.cpp
|
|
BLB_get_data(tdbb, blob, info->specificAttributes.getBuffer(length), length);
|
|
}
|
|
|
|
info->attributes = (USHORT)CL.RDB$COLLATION_ATTRIBUTES;
|
|
info->ignoreAttributes = CL.RDB$COLLATION_ATTRIBUTES.NULL;
|
|
END_FOR;
|
|
}
|
|
else
|
|
{
|
|
FOR(REQUEST_HANDLE request) FIRST 1
|
|
CL IN RDB$COLLATIONS CROSS
|
|
CS IN RDB$CHARACTER_SETS
|
|
WITH CL.RDB$CHARACTER_SET_ID EQ charset_id AND
|
|
CL.RDB$COLLATION_ID EQ collation_id AND
|
|
CS.RDB$CHARACTER_SET_ID EQ CL.RDB$CHARACTER_SET_ID
|
|
|
|
found = true;
|
|
|
|
info->charsetName = CS.RDB$CHARACTER_SET_NAME;
|
|
info->collationName = CL.RDB$COLLATION_NAME;
|
|
info->baseCollationName = info->collationName;
|
|
info->specificAttributes.clear();
|
|
info->attributes = 0;
|
|
info->ignoreAttributes = true;
|
|
END_FOR;
|
|
}
|
|
|
|
if (!REQUEST(irq_l_subtype))
|
|
REQUEST(irq_l_subtype) = request;
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
jrd_nod* MET_get_dependencies(thread_db* tdbb,
|
|
jrd_rel* relation,
|
|
const UCHAR* blob,
|
|
CompilerScratch* view_csb,
|
|
bid* blob_id,
|
|
jrd_req** request,
|
|
CompilerScratch** csb_ptr,
|
|
Firebird::MetaName& object_name,
|
|
int type,
|
|
USHORT flags,
|
|
jrd_tra* transaction,
|
|
Firebird::MetaName domain_validation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ d e p e n d e n c i e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get dependencies for an object by parsing
|
|
* the blr used in its definition.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5, domain_validation);
|
|
csb->csb_g_flags |= (csb_get_dependencies | flags);
|
|
|
|
jrd_nod* node;
|
|
if (blob)
|
|
{
|
|
node = PAR_blr( tdbb, relation, blob, view_csb, &csb, request,
|
|
(type == obj_trigger && relation != NULL), 0);
|
|
}
|
|
else
|
|
{
|
|
node = MET_parse_blob(tdbb, relation, blob_id, &csb, request,
|
|
(type == obj_trigger && relation != NULL));
|
|
}
|
|
|
|
if (type == obj_computed)
|
|
{
|
|
jrd_req* handle = NULL;
|
|
FOR(REQUEST_HANDLE handle)
|
|
RLF IN RDB$RELATION_FIELDS CROSS
|
|
FLD IN RDB$FIELDS WITH
|
|
RLF.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND
|
|
RLF.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND
|
|
RLF.RDB$FIELD_NAME EQ object_name.c_str()
|
|
object_name = FLD.RDB$FIELD_NAME;
|
|
END_FOR;
|
|
CMP_release(tdbb, handle);
|
|
}
|
|
|
|
store_dependencies(tdbb, csb, relation, object_name, type, transaction);
|
|
|
|
if (csb_ptr)
|
|
{
|
|
*csb_ptr = csb;
|
|
}
|
|
else
|
|
{
|
|
delete csb;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
jrd_fld* MET_get_field(jrd_rel* relation, USHORT id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ f i e l d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get the field block for a field if possible. If not,
|
|
* return NULL;
|
|
*
|
|
**************************************/
|
|
vec<jrd_fld*>* vector;
|
|
|
|
if (!relation || !(vector = relation->rel_fields) || id >= vector->count())
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return (*vector)[id];
|
|
}
|
|
|
|
|
|
void MET_get_shadow_files(thread_db* tdbb, bool delete_files)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ s h a d o w _ f i l e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Check the shadows found in the database against
|
|
* our in-memory list: if any new shadow files have
|
|
* been defined since the last time we looked, start
|
|
* shadowing to them; if any have been deleted, stop
|
|
* shadowing to them.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* handle = NULL;
|
|
FOR(REQUEST_HANDLE handle) X IN RDB$FILES
|
|
WITH X.RDB$SHADOW_NUMBER NOT MISSING
|
|
AND X.RDB$SHADOW_NUMBER NE 0
|
|
AND X.RDB$FILE_SEQUENCE EQ 0
|
|
if ((X.RDB$FILE_FLAGS & FILE_shadow) && !(X.RDB$FILE_FLAGS & FILE_inactive))
|
|
{
|
|
const USHORT file_flags = X.RDB$FILE_FLAGS;
|
|
SDW_start(tdbb, X.RDB$FILE_NAME, X.RDB$SHADOW_NUMBER, file_flags, delete_files);
|
|
|
|
/* if the shadow exists, mark the appropriate shadow
|
|
block as found for the purposes of this routine;
|
|
if the shadow was conditional and is no longer, note it */
|
|
|
|
for (Shadow* shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next)
|
|
{
|
|
if ((shadow->sdw_number == X.RDB$SHADOW_NUMBER) && !(shadow->sdw_flags & SDW_IGNORE))
|
|
{
|
|
shadow->sdw_flags |= SDW_found;
|
|
if (!(file_flags & FILE_conditional)) {
|
|
shadow->sdw_flags &= ~SDW_conditional;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
END_FOR;
|
|
|
|
CMP_release(tdbb, handle);
|
|
|
|
/* if any current shadows were not defined in database, mark
|
|
them to be shutdown since they don't exist anymore */
|
|
|
|
for (Shadow* shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next) {
|
|
if (!(shadow->sdw_flags & SDW_found)) {
|
|
shadow->sdw_flags |= SDW_shutdown;
|
|
}
|
|
else {
|
|
shadow->sdw_flags &= ~SDW_found;
|
|
}
|
|
}
|
|
|
|
SDW_check(tdbb);
|
|
}
|
|
|
|
|
|
void MET_load_db_triggers(thread_db* tdbb, int type)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o a d _ d b _ t r i g g e r s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Load database triggers from RDB$TRIGGERS.
|
|
*
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
Database* dbb = tdbb->getDatabase();
|
|
CHECK_DBB(dbb);
|
|
|
|
if ((tdbb->getAttachment()->att_flags & ATT_no_db_triggers) ||
|
|
tdbb->getDatabase()->dbb_triggers[type] != NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
tdbb->getDatabase()->dbb_triggers[type] = FB_NEW(*tdbb->getDatabase()->dbb_permanent)
|
|
trig_vec(*tdbb->getDatabase()->dbb_permanent);
|
|
|
|
jrd_req* trigger_request = NULL;
|
|
int encoded_type = type | TRIGGER_TYPE_DB;
|
|
|
|
FOR(REQUEST_HANDLE trigger_request)
|
|
TRG IN RDB$TRIGGERS
|
|
WITH TRG.RDB$RELATION_NAME MISSING AND
|
|
TRG.RDB$TRIGGER_TYPE EQ encoded_type AND
|
|
TRG.RDB$TRIGGER_INACTIVE EQ 0
|
|
SORTED BY TRG.RDB$TRIGGER_SEQUENCE
|
|
|
|
MET_load_trigger(tdbb, NULL, TRG.RDB$TRIGGER_NAME, &tdbb->getDatabase()->dbb_triggers[type]);
|
|
|
|
END_FOR;
|
|
|
|
CMP_release(tdbb, trigger_request);
|
|
}
|
|
|
|
|
|
void MET_load_trigger(thread_db* tdbb,
|
|
jrd_rel* relation,
|
|
const Firebird::MetaName& trigger_name,
|
|
trig_vec** triggers)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o a d _ t r i g g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Load triggers from RDB$TRIGGERS. If a requested,
|
|
* also load triggers from RDB$RELATIONS.
|
|
*
|
|
**************************************/
|
|
TEXT errmsg[MAX_ERRMSG_LEN + 1];
|
|
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
CHECK_DBB(dbb);
|
|
|
|
if (relation)
|
|
{
|
|
if (relation->rel_flags & REL_sys_trigs_being_loaded)
|
|
return;
|
|
|
|
// No need to load table triggers for ReadOnly databases,
|
|
// since INSERT/DELETE/UPDATE statements are not going to be allowed
|
|
if (dbb->dbb_flags & DBB_read_only)
|
|
return;
|
|
}
|
|
|
|
bid debug_blob_id;
|
|
debug_blob_id.clear();
|
|
if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1)
|
|
{
|
|
jrd_req* debug_info_req = CMP_find_request(tdbb, irq_l_trg_dbg, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE debug_info_req)
|
|
TRG IN RDB$TRIGGERS
|
|
WITH TRG.RDB$TRIGGER_NAME EQ trigger_name.c_str()
|
|
|
|
if (!REQUEST(irq_l_trg_dbg))
|
|
REQUEST(irq_l_trg_dbg) = debug_info_req;
|
|
|
|
if (!TRG.RDB$DEBUG_INFO.NULL)
|
|
debug_blob_id = TRG.RDB$DEBUG_INFO;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_trg_dbg))
|
|
REQUEST(irq_l_trg_dbg) = debug_info_req;
|
|
}
|
|
|
|
// Scan RDB$TRIGGERS next
|
|
|
|
jrd_req* trigger_request = CMP_find_request(tdbb, irq_s_triggers, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE trigger_request)
|
|
TRG IN RDB$TRIGGERS
|
|
WITH TRG.RDB$TRIGGER_NAME EQ trigger_name.c_str()
|
|
if (!REQUEST(irq_s_triggers))
|
|
REQUEST(irq_s_triggers) = trigger_request;
|
|
|
|
/* check if the trigger is to be fired without any permissions
|
|
checks. Verify such a claim */
|
|
USHORT trig_flags = (USHORT) TRG.RDB$FLAGS;
|
|
|
|
/* if there is an ignore permission flag, see if it is legit */
|
|
if ((TRG.RDB$FLAGS & TRG_ignore_perm) && !verify_TRG_ignore_perm(tdbb, trigger_name))
|
|
{
|
|
fb_msg_format(NULL, JRD_BUGCHK, 304, sizeof(errmsg),
|
|
errmsg, MsgFormat::SafeArg() << trigger_name.c_str());
|
|
ERR_log(JRD_BUGCHK, 304, errmsg);
|
|
|
|
trig_flags &= ~TRG_ignore_perm;
|
|
}
|
|
|
|
if (TRG.RDB$RELATION_NAME.NULL)
|
|
{
|
|
if ((TRG.RDB$TRIGGER_TYPE & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB)
|
|
{
|
|
// this is a database trigger
|
|
get_trigger(tdbb,
|
|
relation,
|
|
&TRG.RDB$TRIGGER_BLR,
|
|
&debug_blob_id,
|
|
triggers,
|
|
TRG.RDB$TRIGGER_NAME,
|
|
(UCHAR) (TRG.RDB$TRIGGER_TYPE & ~TRIGGER_TYPE_DB),
|
|
(bool) TRG.RDB$SYSTEM_FLAG,
|
|
trig_flags);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// dimitr: support for the universal triggers
|
|
int trigger_action, slot_index = 0;
|
|
while ((trigger_action = TRIGGER_ACTION_SLOT(TRG.RDB$TRIGGER_TYPE, ++slot_index)) > 0)
|
|
{
|
|
get_trigger(tdbb,
|
|
relation,
|
|
&TRG.RDB$TRIGGER_BLR,
|
|
&debug_blob_id,
|
|
triggers + trigger_action,
|
|
TRG.RDB$TRIGGER_NAME,
|
|
(UCHAR) trigger_action,
|
|
(bool) TRG.RDB$SYSTEM_FLAG,
|
|
trig_flags);
|
|
}
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_s_triggers))
|
|
REQUEST(irq_s_triggers) = trigger_request;
|
|
}
|
|
|
|
|
|
|
|
void MET_lookup_cnstrt_for_index(thread_db* tdbb,
|
|
Firebird::MetaName& constraint_name,
|
|
const Firebird::MetaName& index_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ c n s t r t _ f o r _ i n d e x
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup constraint name from index name, if one exists.
|
|
* Calling routine must pass a buffer of at least 32 bytes.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
constraint_name = "";
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_cnstrt, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATION_CONSTRAINTS WITH X.RDB$INDEX_NAME EQ index_name.c_str()
|
|
|
|
if (!REQUEST(irq_l_cnstrt))
|
|
REQUEST(irq_l_cnstrt) = request;
|
|
|
|
constraint_name = X.RDB$CONSTRAINT_NAME;
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_cnstrt))
|
|
REQUEST(irq_l_cnstrt) = request;
|
|
}
|
|
|
|
|
|
void MET_lookup_cnstrt_for_trigger(thread_db* tdbb,
|
|
Firebird::MetaName& constraint_name,
|
|
Firebird::MetaName& relation_name,
|
|
const Firebird::MetaName& trigger_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ c n s t r t _ f o r _ t r i g g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup constraint name from trigger name, if one exists.
|
|
* Calling routine must pass a buffer of at least 32 bytes.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
constraint_name = "";
|
|
relation_name = "";
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_check, IRQ_REQUESTS);
|
|
jrd_req* request2 = CMP_find_request(tdbb, irq_l_check2, IRQ_REQUESTS);
|
|
|
|
/* utilize two requests rather than one so that we
|
|
guarantee we always return the name of the relation
|
|
that the trigger is defined on, even if we don't
|
|
have a check constraint defined for that trigger */
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
Y IN RDB$TRIGGERS WITH
|
|
Y.RDB$TRIGGER_NAME EQ trigger_name.c_str()
|
|
|
|
if (!REQUEST(irq_l_check))
|
|
REQUEST(irq_l_check) = request;
|
|
|
|
FOR(REQUEST_HANDLE request2)
|
|
X IN RDB$CHECK_CONSTRAINTS WITH
|
|
X.RDB$TRIGGER_NAME EQ Y.RDB$TRIGGER_NAME
|
|
|
|
if (!REQUEST(irq_l_check2))
|
|
REQUEST(irq_l_check2) = request2;
|
|
|
|
constraint_name = X.RDB$CONSTRAINT_NAME;
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_check2))
|
|
REQUEST(irq_l_check2) = request2;
|
|
|
|
relation_name = Y.RDB$RELATION_NAME;
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_check))
|
|
REQUEST(irq_l_check) = request;
|
|
}
|
|
|
|
|
|
void MET_lookup_exception(thread_db* tdbb,
|
|
SLONG number,
|
|
Firebird::MetaName& name,
|
|
TEXT* message,
|
|
size_t messageSize)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ e x c e p t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup exception by number and return its name and message.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
/* We need to look up exception in RDB$EXCEPTIONS */
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_exception, IRQ_REQUESTS);
|
|
|
|
name = "";
|
|
if (message && messageSize)
|
|
{
|
|
*message = 0;
|
|
}
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$EXCEPTIONS WITH X.RDB$EXCEPTION_NUMBER = number
|
|
|
|
if (!REQUEST(irq_l_exception))
|
|
{
|
|
REQUEST(irq_l_exception) = request;
|
|
}
|
|
|
|
if (!X.RDB$EXCEPTION_NAME.NULL)
|
|
{
|
|
name = X.RDB$EXCEPTION_NAME;
|
|
}
|
|
if (!X.RDB$MESSAGE.NULL && message)
|
|
{
|
|
Firebird::string tmp(X.RDB$MESSAGE);
|
|
tmp.copyTo(message, messageSize);
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_exception))
|
|
{
|
|
REQUEST(irq_l_exception) = request;
|
|
}
|
|
}
|
|
|
|
|
|
SLONG MET_lookup_exception_number(thread_db* tdbb, const Firebird::MetaName& name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ e x c e p t i o n _ n u m b e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup exception by name and return its number.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
/* We need to look up exception in RDB$EXCEPTIONS */
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_except_no, IRQ_REQUESTS);
|
|
|
|
SLONG number = 0;
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$EXCEPTIONS WITH X.RDB$EXCEPTION_NAME = name.c_str()
|
|
|
|
if (!REQUEST(irq_l_except_no))
|
|
REQUEST(irq_l_except_no) = request;
|
|
|
|
number = X.RDB$EXCEPTION_NUMBER;
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_except_no))
|
|
REQUEST(irq_l_except_no) = request;
|
|
|
|
return number;
|
|
}
|
|
|
|
|
|
int MET_lookup_field(thread_db* tdbb,
|
|
jrd_rel* relation,
|
|
const Firebird::MetaName& name,
|
|
const Firebird::MetaName* security_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ f i e l d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up a field name.
|
|
* Additionally, if security_name is a not null pointer,
|
|
* it's used to include the condition that it should match
|
|
* the field's security class name, too.
|
|
*
|
|
* if the field is not found return -1
|
|
*
|
|
*****************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
/* Start by checking field names that we already know */
|
|
vec<jrd_fld*>* vector = relation->rel_fields;
|
|
|
|
if (vector)
|
|
{
|
|
int id = 0;
|
|
vec<jrd_fld*>::iterator fieldIter = vector->begin();
|
|
|
|
for (const vec<jrd_fld*>::const_iterator end = vector->end(); fieldIter < end;
|
|
++fieldIter, ++id)
|
|
{
|
|
if (*fieldIter)
|
|
{
|
|
jrd_fld* field = *fieldIter;
|
|
if (field->fld_name == name)
|
|
{
|
|
if (!security_name)
|
|
{
|
|
return id;
|
|
}
|
|
if (field->fld_security_name == *security_name)
|
|
{
|
|
return id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Not found. Next, try system relations directly */
|
|
|
|
int id = -1;
|
|
|
|
if (relation->rel_flags & REL_deleted)
|
|
{
|
|
return id;
|
|
}
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_field, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATION_FIELDS WITH
|
|
X.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND
|
|
X.RDB$FIELD_ID NOT MISSING AND
|
|
X.RDB$FIELD_NAME EQ name.c_str()
|
|
|
|
if (!REQUEST(irq_l_field)) {
|
|
REQUEST(irq_l_field) = request;
|
|
}
|
|
|
|
if (!security_name) {
|
|
id = X.RDB$FIELD_ID;
|
|
}
|
|
else {
|
|
if ((!X.RDB$SECURITY_CLASS.NULL) && (*security_name == X.RDB$SECURITY_CLASS))
|
|
{
|
|
id = X.RDB$FIELD_ID;
|
|
}
|
|
}
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_field)) {
|
|
REQUEST(irq_l_field) = request;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
BlobFilter* MET_lookup_filter(thread_db* tdbb, SSHORT from, SSHORT to)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ f i l t e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
FPTR_BFILTER_CALLBACK filter = NULL;
|
|
BlobFilter* blf = NULL;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_r_filters, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$FILTERS WITH X.RDB$INPUT_SUB_TYPE EQ from AND
|
|
X.RDB$OUTPUT_SUB_TYPE EQ to
|
|
|
|
if (!REQUEST(irq_r_filters))
|
|
REQUEST(irq_r_filters) = request;
|
|
filter = (FPTR_BFILTER_CALLBACK)
|
|
Module::lookup(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT, dbb->dbb_modules);
|
|
if (filter)
|
|
{
|
|
blf = FB_NEW(*dbb->dbb_permanent) BlobFilter(*dbb->dbb_permanent);
|
|
blf->blf_next = NULL;
|
|
blf->blf_from = from;
|
|
blf->blf_to = to;
|
|
blf->blf_filter = filter;
|
|
blf->blf_exception_message.printf(EXCEPTION_MESSAGE,
|
|
X.RDB$FUNCTION_NAME, X.RDB$ENTRYPOINT, X.RDB$MODULE_NAME);
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_filters))
|
|
REQUEST(irq_r_filters) = request;
|
|
|
|
return blf;
|
|
}
|
|
|
|
|
|
SLONG MET_lookup_generator(thread_db* tdbb, const TEXT* name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ g e n e r a t o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup generator (aka gen_id). If we can't find it, make a new one.
|
|
* CVC: I don't see how this function "makes" a new generator; it simply
|
|
* returns -1 if the name is not found.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
if (!strcmp(name, "RDB$GENERATORS"))
|
|
return 0;
|
|
|
|
SLONG gen_id = -1;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_r_gen_id, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$GENERATORS WITH X.RDB$GENERATOR_NAME EQ name
|
|
|
|
if (!REQUEST(irq_r_gen_id))
|
|
REQUEST(irq_r_gen_id) = request;
|
|
|
|
gen_id = X.RDB$GENERATOR_ID;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_gen_id))
|
|
REQUEST(irq_r_gen_id) = request;
|
|
|
|
return gen_id;
|
|
}
|
|
|
|
void MET_lookup_generator_id (thread_db* tdbb, SLONG gen_id, Firebird::MetaName& name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ g e n e r a t o r _ i d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup generator (aka gen_id) by ID. It will load
|
|
* the name in the third parameter.
|
|
*
|
|
**************************************/
|
|
SET_TDBB (tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
if (!gen_id) {
|
|
name = "RDB$GENERATORS";
|
|
return;
|
|
}
|
|
|
|
name = "";
|
|
|
|
jrd_req* request = CMP_find_request (tdbb, irq_r_gen_id_num, IRQ_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE request)
|
|
X IN RDB$GENERATORS WITH X.RDB$GENERATOR_ID EQ gen_id
|
|
|
|
if (!REQUEST (irq_r_gen_id_num))
|
|
REQUEST (irq_r_gen_id_num) = request;
|
|
name = X.RDB$GENERATOR_NAME;
|
|
END_FOR;
|
|
|
|
if (!REQUEST (irq_r_gen_id_num))
|
|
REQUEST (irq_r_gen_id_num) = request;
|
|
}
|
|
|
|
void MET_lookup_index(thread_db* tdbb,
|
|
Firebird::MetaName& index_name,
|
|
const Firebird::MetaName& relation_name,
|
|
USHORT number)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ i n d e x
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup index name from relation and index number.
|
|
* Calling routine must pass a buffer of at least 32 bytes.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
index_name = "";
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_index, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$INDICES WITH X.RDB$RELATION_NAME EQ relation_name.c_str()
|
|
AND X.RDB$INDEX_ID EQ number
|
|
|
|
if (!REQUEST(irq_l_index))
|
|
REQUEST(irq_l_index) = request;
|
|
|
|
index_name = X.RDB$INDEX_NAME;
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_index))
|
|
REQUEST(irq_l_index) = request;
|
|
}
|
|
|
|
|
|
SLONG MET_lookup_index_name(thread_db* tdbb,
|
|
const Firebird::MetaName& index_name,
|
|
SLONG* relation_id, SSHORT* status)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ i n d e x _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup index id from index name.
|
|
*
|
|
**************************************/
|
|
SLONG id = -1;
|
|
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_index_name, IRQ_REQUESTS);
|
|
|
|
*status = MET_object_unknown;
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$INDICES WITH
|
|
X.RDB$INDEX_NAME EQ index_name.c_str()
|
|
|
|
if (!REQUEST(irq_l_index_name))
|
|
REQUEST(irq_l_index_name) = request;
|
|
|
|
if (X.RDB$INDEX_INACTIVE == 0) {
|
|
*status = MET_object_active;
|
|
}
|
|
else {
|
|
*status = MET_object_inactive;
|
|
}
|
|
|
|
id = X.RDB$INDEX_ID - 1;
|
|
const jrd_rel* relation = MET_lookup_relation(tdbb, X.RDB$RELATION_NAME);
|
|
*relation_id = relation->rel_id;
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_index_name))
|
|
REQUEST(irq_l_index_name) = request;
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
bool MET_lookup_partner(thread_db* tdbb,
|
|
jrd_rel* relation,
|
|
index_desc* idx,
|
|
const TEXT* index_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ p a r t n e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Find partner index participating in a
|
|
* foreign key relationship.
|
|
*
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
Database::CheckoutLockGuard guard(dbb, dbb->dbb_meta_mutex);
|
|
|
|
if (relation->rel_flags & REL_check_partners)
|
|
{
|
|
/* Prepare for rescan of foreign references on other relations'
|
|
primary keys and release stale vectors. */
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_foreign1, IRQ_REQUESTS);
|
|
frgn* references = &relation->rel_foreign_refs;
|
|
int index_number = 0;
|
|
|
|
if (references->frgn_reference_ids) {
|
|
delete references->frgn_reference_ids;
|
|
references->frgn_reference_ids = NULL;
|
|
}
|
|
if (references->frgn_relations) {
|
|
delete references->frgn_relations;
|
|
references->frgn_relations = NULL;
|
|
}
|
|
if (references->frgn_indexes) {
|
|
delete references->frgn_indexes;
|
|
references->frgn_indexes = NULL;
|
|
}
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
IDX IN RDB$INDICES CROSS
|
|
RC IN RDB$RELATION_CONSTRAINTS
|
|
OVER RDB$INDEX_NAME CROSS
|
|
IND IN RDB$INDICES WITH
|
|
RC.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND
|
|
IDX.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND
|
|
IND.RDB$INDEX_NAME EQ IDX.RDB$FOREIGN_KEY AND
|
|
IND.RDB$UNIQUE_FLAG = 1
|
|
|
|
if (!REQUEST(irq_foreign1))
|
|
{
|
|
REQUEST(irq_foreign1) = request;
|
|
}
|
|
|
|
const jrd_rel* partner_relation = MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME);
|
|
|
|
if (partner_relation && !IDX.RDB$INDEX_INACTIVE && !IND.RDB$INDEX_INACTIVE)
|
|
{
|
|
// This seems a good candidate for vcl.
|
|
references->frgn_reference_ids =
|
|
vec<int>::newVector(*dbb->dbb_permanent, references->frgn_reference_ids,
|
|
index_number + 1);
|
|
|
|
(*references->frgn_reference_ids)[index_number] = IDX.RDB$INDEX_ID - 1;
|
|
|
|
references->frgn_relations =
|
|
vec<int>::newVector(*dbb->dbb_permanent, references->frgn_relations,
|
|
index_number + 1);
|
|
|
|
(*references->frgn_relations)[index_number] = partner_relation->rel_id;
|
|
|
|
references->frgn_indexes =
|
|
vec<int>::newVector(*dbb->dbb_permanent, references->frgn_indexes,
|
|
index_number + 1);
|
|
|
|
(*references->frgn_indexes)[index_number] = IND.RDB$INDEX_ID - 1;
|
|
|
|
index_number++;
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_foreign1))
|
|
{
|
|
REQUEST(irq_foreign1) = request;
|
|
}
|
|
|
|
/* Prepare for rescan of primary dependencies on relation's primary
|
|
key and stale vectors. */
|
|
|
|
request = CMP_find_request(tdbb, irq_foreign2, IRQ_REQUESTS);
|
|
prim* dependencies = &relation->rel_primary_dpnds;
|
|
index_number = 0;
|
|
|
|
if (dependencies->prim_reference_ids) {
|
|
delete dependencies->prim_reference_ids;
|
|
dependencies->prim_reference_ids = NULL;
|
|
}
|
|
if (dependencies->prim_relations) {
|
|
delete dependencies->prim_relations;
|
|
dependencies->prim_relations = NULL;
|
|
}
|
|
if (dependencies->prim_indexes) {
|
|
delete dependencies->prim_indexes;
|
|
dependencies->prim_indexes = NULL;
|
|
}
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
IDX IN RDB$INDICES CROSS
|
|
IND IN RDB$INDICES WITH
|
|
IDX.RDB$UNIQUE_FLAG = 1 AND
|
|
IDX.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND
|
|
IND.RDB$FOREIGN_KEY EQ IDX.RDB$INDEX_NAME
|
|
|
|
if (!REQUEST(irq_foreign2)) {
|
|
REQUEST(irq_foreign2) = request;
|
|
}
|
|
|
|
const jrd_rel* partner_relation = MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME);
|
|
|
|
if (partner_relation && !IDX.RDB$INDEX_INACTIVE && !IND.RDB$INDEX_INACTIVE)
|
|
{
|
|
dependencies->prim_reference_ids =
|
|
vec<int>::newVector(*dbb->dbb_permanent, dependencies->prim_reference_ids,
|
|
index_number + 1);
|
|
|
|
(*dependencies->prim_reference_ids)[index_number] = IDX.RDB$INDEX_ID - 1;
|
|
|
|
dependencies->prim_relations =
|
|
vec<int>::newVector(*dbb->dbb_permanent, dependencies->prim_relations,
|
|
index_number + 1);
|
|
|
|
(*dependencies->prim_relations)[index_number] = partner_relation->rel_id;
|
|
|
|
dependencies->prim_indexes =
|
|
vec<int>::newVector(*dbb->dbb_permanent, dependencies->prim_indexes,
|
|
index_number + 1);
|
|
|
|
(*dependencies->prim_indexes)[index_number] = IND.RDB$INDEX_ID - 1;
|
|
|
|
index_number++;
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_foreign2))
|
|
REQUEST(irq_foreign2) = request;
|
|
|
|
LCK_lock(tdbb, relation->rel_partners_lock, LCK_SR, LCK_WAIT);
|
|
relation->rel_flags &= ~REL_check_partners;
|
|
}
|
|
|
|
if (idx->idx_flags & idx_foreign)
|
|
{
|
|
if (index_name)
|
|
{
|
|
/* Since primary key index names aren't being cached, do a long
|
|
hard lookup. This is only called during index create for foreign
|
|
keys. */
|
|
|
|
bool found = false;
|
|
jrd_req* request = NULL;
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
IDX IN RDB$INDICES CROSS
|
|
IND IN RDB$INDICES WITH
|
|
IDX.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND
|
|
(IDX.RDB$INDEX_ID EQ idx->idx_id + 1 OR
|
|
IDX.RDB$INDEX_NAME EQ index_name) AND
|
|
IND.RDB$INDEX_NAME EQ IDX.RDB$FOREIGN_KEY AND
|
|
IND.RDB$UNIQUE_FLAG = 1
|
|
|
|
const jrd_rel* partner_relation = MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME);
|
|
|
|
if (partner_relation && !IDX.RDB$INDEX_INACTIVE && !IND.RDB$INDEX_INACTIVE)
|
|
{
|
|
idx->idx_primary_relation = partner_relation->rel_id;
|
|
idx->idx_primary_index = IND.RDB$INDEX_ID - 1;
|
|
found = true;
|
|
}
|
|
END_FOR;
|
|
|
|
CMP_release(tdbb, request);
|
|
return found;
|
|
}
|
|
|
|
frgn* references = &relation->rel_foreign_refs;
|
|
if (references->frgn_reference_ids)
|
|
{
|
|
for (int index_number = 0;
|
|
index_number < (int) references->frgn_reference_ids->count();
|
|
index_number++)
|
|
{
|
|
if (idx->idx_id == (*references->frgn_reference_ids)[index_number])
|
|
{
|
|
idx->idx_primary_relation = (*references->frgn_relations)[index_number];
|
|
idx->idx_primary_index = (*references->frgn_indexes)[index_number];
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
else if (idx->idx_flags & (idx_primary | idx_unique))
|
|
{
|
|
const prim* dependencies = &relation->rel_primary_dpnds;
|
|
if (dependencies->prim_reference_ids)
|
|
{
|
|
for (int index_number = 0;
|
|
index_number < (int) dependencies->prim_reference_ids->count();
|
|
index_number++)
|
|
{
|
|
if (idx->idx_id == (*dependencies->prim_reference_ids)[index_number])
|
|
{
|
|
idx->idx_foreign_primaries = relation->rel_primary_dpnds.prim_reference_ids;
|
|
idx->idx_foreign_relations = relation->rel_primary_dpnds.prim_relations;
|
|
idx->idx_foreign_indexes = relation->rel_primary_dpnds.prim_indexes;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
jrd_prc* MET_lookup_procedure(thread_db* tdbb, const Firebird::MetaName& name, bool noscan)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup procedure by name. Name passed in is
|
|
* ASCIZ name.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
jrd_prc* check_procedure = NULL;
|
|
|
|
/* See if we already know the procedure by name */
|
|
vec<jrd_prc*>* procedures = dbb->dbb_procedures;
|
|
if (procedures) {
|
|
vec<jrd_prc*>::iterator ptr = procedures->begin();
|
|
for (const vec<jrd_prc*>::const_iterator end = procedures->end(); ptr < end; ++ptr)
|
|
{
|
|
jrd_prc* procedure = *ptr;
|
|
if (procedure && !(procedure->prc_flags & PRC_obsolete) &&
|
|
((procedure->prc_flags & PRC_scanned) || noscan) &&
|
|
!(procedure->prc_flags & PRC_being_scanned) &&
|
|
!(procedure->prc_flags & PRC_being_altered))
|
|
{
|
|
if (procedure->prc_name == name)
|
|
{
|
|
if (procedure->prc_flags & PRC_check_existence)
|
|
{
|
|
check_procedure = procedure;
|
|
LCK_lock(tdbb, check_procedure->prc_existence_lock, LCK_SR, LCK_WAIT);
|
|
break;
|
|
}
|
|
|
|
return procedure;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We need to look up the procedure name in RDB$PROCEDURES */
|
|
|
|
jrd_prc* procedure = NULL;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_procedure, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_NAME EQ name.c_str()
|
|
|
|
if (!REQUEST(irq_l_procedure))
|
|
REQUEST(irq_l_procedure) = request;
|
|
|
|
procedure = MET_procedure(tdbb, P.RDB$PROCEDURE_ID, noscan, 0);
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_procedure))
|
|
REQUEST(irq_l_procedure) = request;
|
|
|
|
if (check_procedure) {
|
|
check_procedure->prc_flags &= ~PRC_check_existence;
|
|
if (check_procedure != procedure) {
|
|
LCK_release(tdbb, check_procedure->prc_existence_lock);
|
|
check_procedure->prc_flags |= PRC_obsolete;
|
|
}
|
|
}
|
|
|
|
return procedure;
|
|
}
|
|
|
|
|
|
jrd_prc* MET_lookup_procedure_id(thread_db* tdbb, SSHORT id,
|
|
bool return_deleted, bool noscan, USHORT flags)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ p r o c e d u r e _ i d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup procedure by id.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
jrd_prc* check_procedure = NULL;
|
|
|
|
jrd_prc* procedure;
|
|
|
|
vec<jrd_prc*>* procedures = dbb->dbb_procedures;
|
|
if (procedures && id < (SSHORT) procedures->count() && (procedure = (*procedures)[id]) &&
|
|
procedure->prc_id == id &&
|
|
!(procedure->prc_flags & PRC_being_scanned) &&
|
|
((procedure->prc_flags & PRC_scanned) || noscan) &&
|
|
!(procedure->prc_flags & PRC_being_altered) &&
|
|
(!(procedure->prc_flags & PRC_obsolete) || return_deleted))
|
|
{
|
|
if (procedure->prc_flags & PRC_check_existence) {
|
|
check_procedure = procedure;
|
|
LCK_lock(tdbb, check_procedure->prc_existence_lock, LCK_SR, LCK_WAIT);
|
|
}
|
|
else {
|
|
return procedure;
|
|
}
|
|
}
|
|
|
|
/* We need to look up the procedure name in RDB$PROCEDURES */
|
|
|
|
procedure = NULL;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_proc_id, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ id
|
|
|
|
if (!REQUEST(irq_l_proc_id))
|
|
REQUEST(irq_l_proc_id) = request;
|
|
|
|
procedure = MET_procedure(tdbb, P.RDB$PROCEDURE_ID, noscan, flags);
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_proc_id))
|
|
REQUEST(irq_l_proc_id) = request;
|
|
|
|
if (check_procedure) {
|
|
check_procedure->prc_flags &= ~PRC_check_existence;
|
|
if (check_procedure != procedure) {
|
|
LCK_release(tdbb, check_procedure->prc_existence_lock);
|
|
check_procedure->prc_flags |= PRC_obsolete;
|
|
}
|
|
}
|
|
|
|
return procedure;
|
|
}
|
|
|
|
|
|
jrd_rel* MET_lookup_relation(thread_db* tdbb, const Firebird::MetaName& name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup relation by name. Name passed in is
|
|
* ASCIZ name.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
/* See if we already know the relation by name */
|
|
|
|
vec<jrd_rel*>* relations = dbb->dbb_relations;
|
|
jrd_rel* check_relation = NULL;
|
|
|
|
vec<jrd_rel*>::iterator ptr = relations->begin();
|
|
for (const vec<jrd_rel*>::const_iterator end = relations->end(); ptr < end; ++ptr)
|
|
{
|
|
jrd_rel* relation = *ptr;
|
|
if (relation && !(relation->rel_flags & REL_deleted))
|
|
{
|
|
/* dimitr: for non-system relations we should also check
|
|
REL_scanned and REL_being_scanned flags. Look
|
|
at MET_lookup_procedure for example. */
|
|
if (!(relation->rel_flags & REL_system) &&
|
|
(!(relation->rel_flags & REL_scanned) || (relation->rel_flags & REL_being_scanned)))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (relation->rel_name == name)
|
|
{
|
|
if (relation->rel_flags & REL_check_existence)
|
|
{
|
|
check_relation = relation;
|
|
LCK_lock(tdbb, check_relation->rel_existence_lock, LCK_SR, LCK_WAIT);
|
|
break;
|
|
}
|
|
|
|
return relation;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We need to look up the relation name in RDB$RELATIONS */
|
|
|
|
jrd_rel* relation = NULL;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_relation, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ name.c_str()
|
|
|
|
if (!REQUEST(irq_l_relation))
|
|
{
|
|
REQUEST(irq_l_relation) = request;
|
|
}
|
|
|
|
relation = MET_relation(tdbb, X.RDB$RELATION_ID);
|
|
if (relation->rel_name.length() == 0) {
|
|
relation->rel_name = name;
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_relation))
|
|
{
|
|
REQUEST(irq_l_relation) = request;
|
|
}
|
|
|
|
if (check_relation) {
|
|
check_relation->rel_flags &= ~REL_check_existence;
|
|
if (check_relation != relation) {
|
|
LCK_release(tdbb, check_relation->rel_existence_lock);
|
|
LCK_release(tdbb, check_relation->rel_partners_lock);
|
|
check_relation->rel_flags &= ~REL_check_partners;
|
|
check_relation->rel_flags |= REL_deleted;
|
|
}
|
|
}
|
|
|
|
return relation;
|
|
}
|
|
|
|
|
|
jrd_rel* MET_lookup_relation_id(thread_db* tdbb, SLONG id, bool return_deleted)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ r e l a t i o n _ i d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup relation by id. Make sure it really exists.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
/* System relations are above suspicion */
|
|
|
|
if (id < (int) rel_MAX)
|
|
{
|
|
fb_assert(id < MAX_USHORT);
|
|
return MET_relation(tdbb, (USHORT) id);
|
|
}
|
|
|
|
jrd_rel* check_relation = NULL;
|
|
jrd_rel* relation;
|
|
vec<jrd_rel*>* vector = dbb->dbb_relations;
|
|
if (vector && (id < (SLONG) vector->count()) && (relation = (*vector)[id]))
|
|
{
|
|
if (relation->rel_flags & REL_deleted)
|
|
{
|
|
return return_deleted ? relation : NULL;
|
|
}
|
|
|
|
if (relation->rel_flags & REL_check_existence)
|
|
{
|
|
check_relation = relation;
|
|
LCK_lock(tdbb, check_relation->rel_existence_lock, LCK_SR, LCK_WAIT);
|
|
}
|
|
else
|
|
{
|
|
return relation;
|
|
}
|
|
}
|
|
|
|
/* We need to look up the relation id in RDB$RELATIONS */
|
|
|
|
relation = NULL;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_l_rel_id, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATIONS WITH X.RDB$RELATION_ID EQ id
|
|
|
|
if (!REQUEST(irq_l_rel_id))
|
|
REQUEST(irq_l_rel_id) = request;
|
|
|
|
relation = MET_relation(tdbb, X.RDB$RELATION_ID);
|
|
if (relation->rel_name.length() == 0) {
|
|
relation->rel_name = X.RDB$RELATION_NAME;
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_rel_id))
|
|
REQUEST(irq_l_rel_id) = request;
|
|
|
|
if (check_relation) {
|
|
check_relation->rel_flags &= ~REL_check_existence;
|
|
if (check_relation != relation) {
|
|
LCK_release(tdbb, check_relation->rel_existence_lock);
|
|
LCK_release(tdbb, check_relation->rel_partners_lock);
|
|
check_relation->rel_flags &= ~REL_check_partners;
|
|
check_relation->rel_flags |= REL_deleted;
|
|
}
|
|
}
|
|
|
|
return relation;
|
|
}
|
|
|
|
|
|
jrd_nod* MET_parse_blob(thread_db* tdbb,
|
|
jrd_rel* relation,
|
|
bid* blob_id,
|
|
CompilerScratch** csb_ptr,
|
|
jrd_req** request_ptr,
|
|
const bool trigger)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ p a r s e _ b l o b
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse blr, returning a compiler scratch block with the results.
|
|
*
|
|
* if ignore_perm is true then, the request generated must be set to
|
|
* ignore all permissions checks. In this case, we call PAR_blr
|
|
* passing it the csb_ignore_perm flag to generate a request
|
|
* which must go through without checking any permissions.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id);
|
|
const SLONG length = blob->blb_length + 10;
|
|
Firebird::HalfStaticArray<UCHAR, 512> tmp;
|
|
UCHAR* temp = tmp.getBuffer(length);
|
|
BLB_get_data(tdbb, blob, temp, length);
|
|
|
|
jrd_nod* node = PAR_blr(tdbb, relation, temp, NULL, csb_ptr, request_ptr, trigger, 0);
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
void MET_parse_sys_trigger(thread_db* tdbb, jrd_rel* relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ p a r s e _ s y s _ t r i g g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse the blr for a system relation's triggers.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
relation->rel_flags &= ~REL_sys_triggers;
|
|
|
|
// Release any triggers in case of a rescan
|
|
|
|
if (relation->rel_pre_store)
|
|
MET_release_triggers(tdbb, &relation->rel_pre_store);
|
|
if (relation->rel_post_store)
|
|
MET_release_triggers(tdbb, &relation->rel_post_store);
|
|
if (relation->rel_pre_erase)
|
|
MET_release_triggers(tdbb, &relation->rel_pre_erase);
|
|
if (relation->rel_post_erase)
|
|
MET_release_triggers(tdbb, &relation->rel_post_erase);
|
|
if (relation->rel_pre_modify)
|
|
MET_release_triggers(tdbb, &relation->rel_pre_modify);
|
|
if (relation->rel_post_modify)
|
|
MET_release_triggers(tdbb, &relation->rel_post_modify);
|
|
|
|
// No need to load triggers for ReadOnly databases, since
|
|
// INSERT/DELETE/UPDATE statements are not going to be allowed
|
|
|
|
if (dbb->dbb_flags & DBB_read_only)
|
|
{
|
|
return;
|
|
}
|
|
|
|
relation->rel_flags |= REL_sys_trigs_being_loaded;
|
|
|
|
jrd_req* trigger_request = CMP_find_request(tdbb, irq_s_triggers2, IRQ_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE trigger_request)
|
|
TRG IN RDB$TRIGGERS
|
|
WITH TRG.RDB$RELATION_NAME = relation->rel_name.c_str()
|
|
AND TRG.RDB$SYSTEM_FLAG = 1
|
|
|
|
if (!REQUEST (irq_s_triggers2))
|
|
REQUEST (irq_s_triggers2) = trigger_request;
|
|
|
|
const UCHAR type = (UCHAR) TRG.RDB$TRIGGER_TYPE;
|
|
const USHORT trig_flags = TRG.RDB$FLAGS;
|
|
const TEXT* name = TRG.RDB$TRIGGER_NAME;
|
|
|
|
trig_vec** ptr;
|
|
|
|
switch (type)
|
|
{
|
|
case 1:
|
|
ptr = &relation->rel_pre_store;
|
|
break;
|
|
case 2:
|
|
ptr = &relation->rel_post_store;
|
|
break;
|
|
case 3:
|
|
ptr = &relation->rel_pre_modify;
|
|
break;
|
|
case 4:
|
|
ptr = &relation->rel_post_modify;
|
|
break;
|
|
case 5:
|
|
ptr = &relation->rel_pre_erase;
|
|
break;
|
|
case 6:
|
|
ptr = &relation->rel_post_erase;
|
|
break;
|
|
default:
|
|
ptr = NULL;
|
|
break;
|
|
}
|
|
|
|
if (ptr)
|
|
{
|
|
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, &TRG.RDB$TRIGGER_BLR);
|
|
const SLONG length = blob->blb_length + 10;
|
|
Firebird::HalfStaticArray<UCHAR, 128> blr;
|
|
BLB_get_data(tdbb, blob, blr.getBuffer(length), length);
|
|
|
|
USHORT par_flags = (USHORT) ((trig_flags & TRG_ignore_perm) ? csb_ignore_perm : 0);
|
|
if (type & 1)
|
|
par_flags |= csb_pre_trigger;
|
|
else
|
|
par_flags |= csb_post_trigger;
|
|
|
|
jrd_req* request = NULL;
|
|
{
|
|
Jrd::ContextPoolHolder context(tdbb, dbb->createPool());
|
|
PAR_blr(tdbb, relation, blr.begin(), NULL, NULL, &request, true, par_flags);
|
|
}
|
|
|
|
request->req_trg_name = name;
|
|
|
|
request->req_flags |= req_sys_trigger;
|
|
if (trig_flags & TRG_ignore_perm)
|
|
{
|
|
request->req_flags |= req_ignore_perm;
|
|
}
|
|
|
|
save_trigger_data(tdbb, ptr, relation, request, NULL, NULL, NULL, type, true, 0);
|
|
}
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST (irq_s_triggers2))
|
|
REQUEST (irq_s_triggers2) = trigger_request;
|
|
|
|
relation->rel_flags &= ~REL_sys_trigs_being_loaded;
|
|
}
|
|
|
|
|
|
void MET_post_existence(thread_db* tdbb, jrd_rel* relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ p o s t _ e x i s t e n c e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Post an interest in the existence of a relation.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
relation->rel_use_count++;
|
|
|
|
if (!MET_lookup_relation_id(tdbb, relation->rel_id, false))
|
|
{
|
|
relation->rel_use_count--;
|
|
ERR_post(Arg::Gds(isc_relnotdef) << Arg::Str(relation->rel_name));
|
|
}
|
|
}
|
|
|
|
|
|
void MET_prepare(thread_db* tdbb, jrd_tra* transaction, USHORT length, const UCHAR* msg)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ p r e p a r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Post a transaction description to RDB$TRANSACTIONS.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_s_trans, IRQ_REQUESTS);
|
|
|
|
STORE(REQUEST_HANDLE request) X IN RDB$TRANSACTIONS
|
|
X.RDB$TRANSACTION_ID = transaction->tra_number;
|
|
X.RDB$TRANSACTION_STATE = RDB$TRANSACTIONS.RDB$TRANSACTION_STATE.LIMBO;
|
|
blb* blob = BLB_create(tdbb, dbb->dbb_sys_trans, &X.RDB$TRANSACTION_DESCRIPTION);
|
|
BLB_put_segment(tdbb, blob, msg, length);
|
|
BLB_close(tdbb, blob);
|
|
END_STORE;
|
|
|
|
if (!REQUEST(irq_s_trans))
|
|
REQUEST(irq_s_trans) = request;
|
|
}
|
|
|
|
|
|
jrd_prc* MET_procedure(thread_db* tdbb, int id, bool noscan, USHORT flags)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Find or create a procedure block for a given procedure id.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
vec<jrd_prc*>* vector = dbb->dbb_procedures;
|
|
if (!vector)
|
|
{
|
|
vector = dbb->dbb_procedures = vec<jrd_prc*>::newVector(*dbb->dbb_permanent, id + 10);
|
|
}
|
|
else if (id >= (int) vector->count())
|
|
{
|
|
vector->resize(id + 10);
|
|
}
|
|
|
|
Database::CheckoutLockGuard guard(dbb, dbb->dbb_meta_mutex);
|
|
|
|
jrd_prc* procedure = (*vector)[id];
|
|
|
|
if (procedure && !(procedure->prc_flags & PRC_obsolete))
|
|
{
|
|
// Make sure PRC_being_scanned and PRC_scanned are not set at the same time
|
|
|
|
fb_assert(!(procedure->prc_flags & PRC_being_scanned) || !(procedure->prc_flags & PRC_scanned));
|
|
|
|
/* To avoid scanning recursive procedure's blr recursively let's
|
|
make use of PRC_being_scanned bit. Because this bit is set
|
|
later in the code, it is not set when we are here first time.
|
|
If (in case of rec. procedure) we get here second time it is
|
|
already set and we return half baked procedure.
|
|
In case of superserver this code is under the rec. mutex
|
|
protection, thus the only guy (thread) who can get here and see
|
|
PRC_being_scanned bit set is the guy which started procedure scan
|
|
and currently holds the mutex.
|
|
In case of classic, there is always only one guy and if it
|
|
sees PRC_being_scanned bit set it is safe to assume it is here
|
|
second time.
|
|
|
|
If procedure has already been scanned - return. This condition
|
|
is for those threads that did not find procedure in cache and
|
|
came here to get it from disk. But only one was able to lock
|
|
the mutex and do the scanning, others were waiting. As soon as
|
|
the first thread releases the mutex another thread gets in and
|
|
it would be just unfair to make it do the work again.
|
|
*/
|
|
|
|
if ((procedure->prc_flags & PRC_being_scanned) || (procedure->prc_flags & PRC_scanned))
|
|
{
|
|
return procedure;
|
|
}
|
|
}
|
|
|
|
if (!procedure)
|
|
{
|
|
procedure = FB_NEW(*dbb->dbb_permanent) jrd_prc(*dbb->dbb_permanent);
|
|
}
|
|
|
|
try {
|
|
|
|
procedure->prc_flags |= (PRC_being_scanned | flags);
|
|
procedure->prc_flags &= ~PRC_obsolete;
|
|
|
|
procedure->prc_id = id;
|
|
(*vector)[id] = procedure;
|
|
|
|
if (!procedure->prc_existence_lock) {
|
|
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, 0) Lock;
|
|
procedure->prc_existence_lock = lock;
|
|
lock->lck_parent = dbb->dbb_lock;
|
|
lock->lck_dbb = dbb;
|
|
lock->lck_key.lck_long = procedure->prc_id;
|
|
lock->lck_length = sizeof(lock->lck_key.lck_long);
|
|
lock->lck_type = LCK_prc_exist;
|
|
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
|
|
lock->lck_object = procedure;
|
|
lock->lck_ast = blocking_ast_procedure;
|
|
}
|
|
|
|
LCK_lock(tdbb, procedure->prc_existence_lock, LCK_SR, LCK_WAIT);
|
|
|
|
if (!noscan)
|
|
{
|
|
SSHORT valid_blr = TRUE;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_r_procedure, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ procedure->prc_id
|
|
|
|
if (!REQUEST(irq_r_procedure))
|
|
{
|
|
REQUEST(irq_r_procedure) = request;
|
|
}
|
|
|
|
if (procedure->prc_name.length() == 0)
|
|
{
|
|
procedure->prc_name = P.RDB$PROCEDURE_NAME;
|
|
}
|
|
procedure->prc_id = P.RDB$PROCEDURE_ID;
|
|
if (!P.RDB$SECURITY_CLASS.NULL)
|
|
{
|
|
procedure->prc_security_name = P.RDB$SECURITY_CLASS;
|
|
}
|
|
if (P.RDB$SYSTEM_FLAG.NULL || !P.RDB$SYSTEM_FLAG)
|
|
{
|
|
procedure->prc_flags &= ~PRC_system;
|
|
}
|
|
else
|
|
{
|
|
procedure->prc_flags |= PRC_system;
|
|
}
|
|
if ( (procedure->prc_inputs = P.RDB$PROCEDURE_INPUTS) )
|
|
{
|
|
procedure->prc_input_fields =
|
|
vec<Parameter*>::newVector(*dbb->dbb_permanent, procedure->prc_input_fields,
|
|
P.RDB$PROCEDURE_INPUTS);
|
|
}
|
|
if ( (procedure->prc_outputs = P.RDB$PROCEDURE_OUTPUTS) )
|
|
{
|
|
procedure->prc_output_fields =
|
|
vec<Parameter*>::newVector(*dbb->dbb_permanent, procedure->prc_output_fields,
|
|
P.RDB$PROCEDURE_OUTPUTS);
|
|
}
|
|
|
|
vec<Parameter*>* paramVector = 0;
|
|
procedure->prc_defaults = 0;
|
|
|
|
jrd_req* request2 = CMP_find_request(tdbb, irq_r_params, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request2)
|
|
PA IN RDB$PROCEDURE_PARAMETERS CROSS
|
|
F IN RDB$FIELDS WITH F.RDB$FIELD_NAME = PA.RDB$FIELD_SOURCE
|
|
AND PA.RDB$PROCEDURE_NAME = P.RDB$PROCEDURE_NAME
|
|
|
|
if (!REQUEST(irq_r_params))
|
|
{
|
|
REQUEST(irq_r_params) = request2;
|
|
}
|
|
|
|
SSHORT pa_collation_id_null = true;
|
|
SSHORT pa_collation_id = 0;
|
|
SSHORT pa_default_value_null = true;
|
|
bid pa_default_value;
|
|
|
|
if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1)
|
|
{
|
|
jrd_req* request3 = CMP_find_request(tdbb, irq_r_params2, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request3)
|
|
PA2 IN RDB$PROCEDURE_PARAMETERS WITH
|
|
PA2.RDB$PROCEDURE_NAME EQ PA.RDB$PROCEDURE_NAME AND
|
|
PA2.RDB$PARAMETER_NAME EQ PA.RDB$PARAMETER_NAME
|
|
|
|
if (!REQUEST(irq_r_params2))
|
|
REQUEST(irq_r_params2) = request3;
|
|
|
|
pa_collation_id_null = PA2.RDB$COLLATION_ID.NULL;
|
|
pa_collation_id = PA2.RDB$COLLATION_ID;
|
|
pa_default_value_null = PA2.RDB$DEFAULT_VALUE.NULL;
|
|
pa_default_value = PA2.RDB$DEFAULT_VALUE;
|
|
END_FOR
|
|
|
|
if (!REQUEST(irq_r_params2))
|
|
REQUEST(irq_r_params2) = request3;
|
|
}
|
|
|
|
if (PA.RDB$PARAMETER_TYPE) {
|
|
paramVector = procedure->prc_output_fields;
|
|
}
|
|
else {
|
|
paramVector = procedure->prc_input_fields;
|
|
}
|
|
|
|
// should be error if field already exists
|
|
Parameter* parameter = FB_NEW(*dbb->dbb_permanent) Parameter(*dbb->dbb_permanent);
|
|
parameter->prm_number = PA.RDB$PARAMETER_NUMBER;
|
|
(*paramVector)[parameter->prm_number] = parameter;
|
|
parameter->prm_name = PA.RDB$PARAMETER_NAME;
|
|
|
|
DSC_make_descriptor(¶meter->prm_desc, F.RDB$FIELD_TYPE,
|
|
F.RDB$FIELD_SCALE, F.RDB$FIELD_LENGTH,
|
|
F.RDB$FIELD_SUB_TYPE, F.RDB$CHARACTER_SET_ID,
|
|
(pa_collation_id_null ? F.RDB$COLLATION_ID : pa_collation_id));
|
|
|
|
if (PA.RDB$PARAMETER_TYPE == 0 &&
|
|
(!pa_default_value_null ||
|
|
(fb_utils::implicit_domain(F.RDB$FIELD_NAME) && !F.RDB$DEFAULT_VALUE.NULL)))
|
|
{
|
|
procedure->prc_defaults++;
|
|
Jrd::ContextPoolHolder context(tdbb, dbb->createPool());
|
|
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5);
|
|
|
|
if (!pa_default_value_null)
|
|
{
|
|
parameter->prm_default_value =
|
|
parse_param_blr(tdbb, procedure, &pa_default_value, csb);
|
|
}
|
|
else
|
|
{
|
|
parameter->prm_default_value =
|
|
parse_param_blr(tdbb, procedure, &F.RDB$DEFAULT_VALUE, csb);
|
|
}
|
|
|
|
delete csb;
|
|
}
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_params)) {
|
|
REQUEST(irq_r_params) = request2;
|
|
}
|
|
|
|
if ((paramVector = procedure->prc_output_fields) && (*paramVector)[0])
|
|
{
|
|
Format* format = procedure->prc_format =
|
|
Format::newFormat(*dbb->dbb_permanent, procedure->prc_outputs);
|
|
ULONG length = FLAG_BYTES(format->fmt_count);
|
|
Format::fmt_desc_iterator desc = format->fmt_desc.begin();
|
|
vec<Parameter*>::iterator ptr, end;
|
|
for (ptr = paramVector->begin(), end = paramVector->end(); ptr < end; ++ptr, ++desc)
|
|
{
|
|
Parameter* parameter = *ptr;
|
|
// check for parameter to be null, this can only happen if the
|
|
// parameter numbers get out of sync. This was added to fix bug
|
|
// 10534. -Shaunak Mistry 12-May-99
|
|
if (parameter)
|
|
{
|
|
*desc = parameter->prm_desc;
|
|
length = MET_align(dbb, &(*desc), length);
|
|
desc->dsc_address = (UCHAR *) (IPTR) length;
|
|
length += desc->dsc_length;
|
|
}
|
|
}
|
|
format->fmt_length = (USHORT) length;
|
|
}
|
|
|
|
prc_t prc_type = prc_legacy;
|
|
|
|
MemoryPool* csb_pool = dbb->createPool();
|
|
|
|
{
|
|
Jrd::ContextPoolHolder context(tdbb, csb_pool);
|
|
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5);
|
|
|
|
if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1)
|
|
{
|
|
jrd_req* request4 = CMP_find_request(tdbb, irq_p_type, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request4)
|
|
PT IN RDB$PROCEDURES
|
|
WITH PT.RDB$PROCEDURE_ID EQ procedure->prc_id
|
|
|
|
if (!REQUEST(irq_p_type))
|
|
{
|
|
REQUEST(irq_p_type) = request4;
|
|
}
|
|
|
|
if (!PT.RDB$PROCEDURE_TYPE.NULL)
|
|
prc_type = (prc_t) PT.RDB$PROCEDURE_TYPE;
|
|
|
|
if (!PT.RDB$DEBUG_INFO.NULL)
|
|
DBG_parse_debug_info(tdbb, &PT.RDB$DEBUG_INFO, csb->csb_dbg_info);
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_p_type)) {
|
|
REQUEST(irq_p_type) = request4;
|
|
}
|
|
}
|
|
|
|
procedure->prc_type = prc_type;
|
|
|
|
try
|
|
{
|
|
parse_procedure_blr(tdbb, procedure, &P.RDB$PROCEDURE_BLR, csb);
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{
|
|
delete csb;
|
|
|
|
if (procedure->prc_request) {
|
|
CMP_release(tdbb, procedure->prc_request);
|
|
procedure->prc_request = NULL;
|
|
}
|
|
else {
|
|
dbb->deletePool(csb_pool);
|
|
}
|
|
|
|
ERR_post(Arg::Gds(isc_bad_proc_BLR) << Arg::Str(procedure->prc_name));
|
|
}
|
|
|
|
procedure->prc_request->req_procedure = procedure;
|
|
for (size_t i = 0; i < csb->csb_rpt.getCount(); i++)
|
|
{
|
|
jrd_nod* node = csb->csb_rpt[i].csb_message;
|
|
if (node)
|
|
{
|
|
/*
|
|
if ((int) (IPTR) node->nod_arg[e_msg_number] == 0)
|
|
{
|
|
// Never used.
|
|
procedure->prc_input_msg = node;
|
|
}
|
|
else
|
|
*/
|
|
if ((int) (IPTR) node->nod_arg[e_msg_number] == 1)
|
|
{
|
|
procedure->prc_output_msg = node;
|
|
}
|
|
}
|
|
}
|
|
delete csb;
|
|
}
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_procedure)) {
|
|
REQUEST(irq_r_procedure) = request;
|
|
}
|
|
|
|
procedure->prc_flags |= PRC_scanned;
|
|
|
|
if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1 &&
|
|
!(dbb->dbb_flags & DBB_read_only) && !valid_blr)
|
|
{
|
|
// if the BLR was marked as invalid but the procedure was compiled,
|
|
// mark the BLR as valid
|
|
|
|
jrd_req* request5 = NULL;
|
|
|
|
FOR(REQUEST_HANDLE request5)
|
|
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ procedure->prc_id
|
|
|
|
MODIFY P USING
|
|
P.RDB$VALID_BLR = TRUE;
|
|
P.RDB$VALID_BLR.NULL = FALSE;
|
|
END_MODIFY;
|
|
END_FOR;
|
|
|
|
CMP_release(tdbb, request5);
|
|
}
|
|
} // if !noscan
|
|
|
|
// Make sure that it is really being scanned
|
|
fb_assert(procedure->prc_flags & PRC_being_scanned);
|
|
|
|
procedure->prc_flags &= ~PRC_being_scanned;
|
|
|
|
} // try
|
|
catch (const Firebird::Exception&) {
|
|
procedure->prc_flags &= ~(PRC_being_scanned | PRC_scanned);
|
|
if (procedure->prc_existence_lock)
|
|
{
|
|
LCK_release(tdbb, procedure->prc_existence_lock);
|
|
procedure->prc_existence_lock = NULL;
|
|
}
|
|
throw;
|
|
}
|
|
|
|
return procedure;
|
|
}
|
|
|
|
|
|
jrd_rel* MET_relation(thread_db* tdbb, USHORT id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Find or create a relation block for a given relation id.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
CHECK_DBB(dbb);
|
|
|
|
vec<jrd_rel*>* vector = dbb->dbb_relations;
|
|
|
|
if (!vector)
|
|
{
|
|
vector = dbb->dbb_relations = vec<jrd_rel*>::newVector(*dbb->dbb_permanent, id + 10);
|
|
}
|
|
else if (id >= vector->count())
|
|
{
|
|
vector->resize(id + 10);
|
|
}
|
|
|
|
jrd_rel* relation = (*vector)[id];
|
|
if (relation) {
|
|
return relation;
|
|
}
|
|
|
|
const USHORT major_version = dbb->dbb_ods_version;
|
|
const USHORT minor_original = dbb->dbb_minor_original;
|
|
|
|
/* From ODS 9 onwards, the first 128 relation IDS have been
|
|
reserved for system relations */
|
|
USHORT max_sys_rel;
|
|
if (ENCODE_ODS(major_version, minor_original) < ODS_9_0) {
|
|
max_sys_rel = USER_REL_INIT_ID_ODS8 - 1;
|
|
}
|
|
else {
|
|
max_sys_rel = USER_DEF_REL_INIT_ID - 1;
|
|
}
|
|
|
|
relation = FB_NEW(*dbb->dbb_permanent) jrd_rel(*dbb->dbb_permanent);
|
|
(*vector)[id] = relation;
|
|
relation->rel_id = id;
|
|
|
|
{ // Scope block.
|
|
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, 0) Lock;
|
|
relation->rel_partners_lock = lock;
|
|
lock->lck_parent = dbb->dbb_lock;
|
|
lock->lck_dbb = dbb;
|
|
lock->lck_key.lck_long = relation->rel_id;
|
|
lock->lck_length = sizeof(lock->lck_key.lck_long);
|
|
lock->lck_type = LCK_rel_partners;
|
|
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
|
|
lock->lck_object = relation;
|
|
lock->lck_ast = partners_ast_relation;
|
|
}
|
|
|
|
// This should check system flag instead.
|
|
if (relation->rel_id <= max_sys_rel) {
|
|
return relation;
|
|
}
|
|
|
|
{ // Scope block.
|
|
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, 0) Lock;
|
|
relation->rel_existence_lock = lock;
|
|
lock->lck_parent = dbb->dbb_lock;
|
|
lock->lck_dbb = dbb;
|
|
lock->lck_key.lck_long = relation->rel_id;
|
|
lock->lck_length = sizeof(lock->lck_key.lck_long);
|
|
lock->lck_type = LCK_rel_exist;
|
|
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
|
|
lock->lck_object = relation;
|
|
lock->lck_ast = blocking_ast_relation;
|
|
}
|
|
|
|
relation->rel_flags |= (REL_check_existence | REL_check_partners);
|
|
return relation;
|
|
}
|
|
|
|
|
|
bool MET_relation_default_class(thread_db* tdbb, const Firebird::MetaName& relation_name,
|
|
const Firebird::MetaName& default_security_class_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r e l a t i o n _ d e f a u l t _ c l a s s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Checks that a given security class is the default for
|
|
* a given relation, returning TRUE if there's a match.
|
|
* It can be made obsolete in the future if jrd_rel struct
|
|
* gets another field, although metadata loading order
|
|
* would not be safe when compared with this function.
|
|
*
|
|
**************************************/
|
|
SET_TDBB (tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
CHECK_DBB (dbb);
|
|
|
|
bool found = false;
|
|
jrd_req* request = CMP_find_request (tdbb, irq_l_relation_defsec, IRQ_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE request)
|
|
REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ relation_name.c_str()
|
|
|
|
if (!REQUEST (irq_l_relation_defsec))
|
|
REQUEST (irq_l_relation_defsec) = request;
|
|
|
|
if (!REL.RDB$DEFAULT_CLASS.NULL) {
|
|
if (default_security_class_name == REL.RDB$DEFAULT_CLASS)
|
|
{
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST (irq_l_relation_defsec))
|
|
REQUEST (irq_l_relation_defsec) = request;
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
void MET_release_existence(thread_db* tdbb, jrd_rel* relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r e l e a s e _ e x i s t e n c e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Release interest in relation. If no remaining interest
|
|
* and we're blocking the drop of the relation then release
|
|
* existence lock and mark deleted.
|
|
*
|
|
**************************************/
|
|
if (relation->rel_use_count) {
|
|
relation->rel_use_count--;
|
|
}
|
|
|
|
if (!relation->rel_use_count) {
|
|
if (relation->rel_flags & REL_blocking) {
|
|
LCK_re_post(tdbb, relation->rel_existence_lock);
|
|
}
|
|
|
|
if (relation->rel_file) {
|
|
// close external file
|
|
EXT_fini(relation, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void MET_remove_procedure(thread_db* tdbb, int id, jrd_prc* procedure)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r e m o v e _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Remove a procedure from cache
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
vec<jrd_prc*>* pvector = dbb->dbb_procedures;
|
|
if (!pvector) {
|
|
return;
|
|
}
|
|
|
|
if (!procedure) {
|
|
/** If we are in here then dfw.epp/modify_procedure() called us **/
|
|
if (!(procedure = (*pvector)[id]))
|
|
return;
|
|
}
|
|
|
|
/* MET_procedure locks it. Lets unlock it now to avoid troubles later */
|
|
if (procedure->prc_existence_lock)
|
|
{
|
|
LCK_release(tdbb, procedure->prc_existence_lock);
|
|
}
|
|
|
|
/* Procedure that is being altered may have references
|
|
to it by other procedures via pointer to current meta
|
|
data structure, so don't loose the structure or the pointer. */
|
|
if ((procedure == (*pvector)[id]) && !(procedure->prc_flags & PRC_being_altered))
|
|
{
|
|
(*pvector)[id] = NULL;
|
|
}
|
|
|
|
/* deallocate all structure which were allocated. The procedure
|
|
* blr is originally read into a new pool from which all request
|
|
* are allocated. That will not be freed up.
|
|
*/
|
|
|
|
if (procedure->prc_existence_lock) {
|
|
delete procedure->prc_existence_lock;
|
|
procedure->prc_existence_lock = NULL;
|
|
}
|
|
|
|
/* deallocate input param structures */
|
|
vec<Parameter*>* vector;
|
|
if ((procedure->prc_inputs) && (vector = procedure->prc_input_fields)) {
|
|
for (int i = 0; i < procedure->prc_inputs; i++)
|
|
{
|
|
if ((*vector)[i])
|
|
{
|
|
delete (*vector)[i];
|
|
}
|
|
}
|
|
delete vector;
|
|
procedure->prc_inputs = 0;
|
|
procedure->prc_input_fields = NULL;
|
|
}
|
|
|
|
/* deallocate output param structures */
|
|
|
|
if ((procedure->prc_outputs) && (vector = procedure->prc_output_fields)) {
|
|
for (int i = 0; i < procedure->prc_outputs; i++)
|
|
{
|
|
if ((*vector)[i])
|
|
{
|
|
delete (*vector)[i];
|
|
}
|
|
}
|
|
delete vector;
|
|
procedure->prc_outputs = 0;
|
|
procedure->prc_output_fields = NULL;
|
|
}
|
|
|
|
if (!procedure->prc_use_count)
|
|
{
|
|
if (procedure->prc_format)
|
|
{
|
|
delete procedure->prc_format;
|
|
procedure->prc_format = NULL;
|
|
}
|
|
}
|
|
|
|
if (!(procedure->prc_flags & PRC_being_altered) && !procedure->prc_use_count)
|
|
{
|
|
delete procedure;
|
|
}
|
|
else
|
|
{
|
|
// Fully clear procedure block. Some pieces of code check for empty
|
|
// procedure name and ID, this is why we do it.
|
|
procedure->prc_name = "";
|
|
procedure->prc_security_name = "";
|
|
procedure->prc_defaults = 0;
|
|
procedure->prc_id = 0;
|
|
}
|
|
}
|
|
|
|
|
|
void MET_revoke(thread_db* tdbb,
|
|
jrd_tra* transaction,
|
|
const TEXT* relation,
|
|
const TEXT* revokee,
|
|
const TEXT* privilege)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r e v o k e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Execute a recursive revoke. This is called only when
|
|
* a revoked privilege had the grant option.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
/* See if the revokee still has the privilege. If so, there's
|
|
nothing to do */
|
|
|
|
USHORT count = 0;
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_revoke1, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
FIRST 1 P IN RDB$USER_PRIVILEGES WITH
|
|
P.RDB$RELATION_NAME EQ relation AND
|
|
P.RDB$PRIVILEGE EQ privilege AND
|
|
P.RDB$USER EQ revokee
|
|
|
|
if (!REQUEST(irq_revoke1))
|
|
REQUEST(irq_revoke1) = request;
|
|
++count;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_revoke1))
|
|
REQUEST(irq_revoke1) = request;
|
|
|
|
if (count)
|
|
return;
|
|
|
|
request = CMP_find_request(tdbb, irq_revoke2, IRQ_REQUESTS);
|
|
|
|
/* User lost privilege. Take it away from anybody he/she gave
|
|
it to. */
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
P IN RDB$USER_PRIVILEGES WITH
|
|
P.RDB$RELATION_NAME EQ relation AND
|
|
P.RDB$PRIVILEGE EQ privilege AND
|
|
P.RDB$GRANTOR EQ revokee
|
|
|
|
if (!REQUEST(irq_revoke2))
|
|
REQUEST(irq_revoke2) = request;
|
|
ERASE P;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_revoke2))
|
|
REQUEST(irq_revoke2) = request;
|
|
}
|
|
|
|
|
|
void MET_scan_relation(thread_db* tdbb, jrd_rel* relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ s c a n _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Scan a relation for view RecordSelExpr, computed by expressions, missing
|
|
* expressions, and validation expressions.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
trig_vec* triggers[TRIGGER_MAX];
|
|
Database* dbb = tdbb->getDatabase();
|
|
Jrd::ContextPoolHolder context(tdbb, dbb->dbb_permanent);
|
|
bool dependencies = false;
|
|
bool sys_triggers = false;
|
|
|
|
blb* blob = NULL;
|
|
jrd_req* request = NULL;
|
|
|
|
jrd_tra* depTrans = (tdbb->getTransaction() ? tdbb->getTransaction() : dbb->dbb_sys_trans);
|
|
|
|
/* If anything errors, catch it to reset the scan flag. This will
|
|
make sure that the error will be caught if the operation is tried
|
|
again. */
|
|
|
|
try {
|
|
|
|
Database::CheckoutLockGuard guard(dbb, dbb->dbb_meta_mutex);
|
|
|
|
if (relation->rel_flags & REL_scanned || relation->rel_flags & REL_deleted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
relation->rel_flags |= REL_being_scanned;
|
|
dependencies = (relation->rel_flags & REL_get_dependencies) ? true : false;
|
|
sys_triggers = (relation->rel_flags & REL_sys_triggers) ? true : false;
|
|
relation->rel_flags &= ~(REL_get_dependencies | REL_sys_triggers);
|
|
|
|
for (USHORT itr = 0; itr < TRIGGER_MAX; ++itr) {
|
|
triggers[itr] = NULL;
|
|
}
|
|
|
|
/* Since this can be called recursively, find an inactive clone of the request */
|
|
|
|
request = CMP_find_request(tdbb, irq_r_fields, IRQ_REQUESTS);
|
|
CompilerScratch* csb = NULL;
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
REL IN RDB$RELATIONS WITH REL.RDB$RELATION_ID EQ relation->rel_id
|
|
/* Pick up relation level stuff */
|
|
if (!REQUEST(irq_r_fields)) {
|
|
REQUEST(irq_r_fields) = request;
|
|
}
|
|
relation->rel_current_fmt = REL.RDB$FORMAT;
|
|
vec<jrd_fld*>* vector = relation->rel_fields =
|
|
vec<jrd_fld*>::newVector(*dbb->dbb_permanent, relation->rel_fields, REL.RDB$FIELD_ID + 1);
|
|
if (!REL.RDB$SECURITY_CLASS.NULL) {
|
|
relation->rel_security_name = REL.RDB$SECURITY_CLASS;
|
|
}
|
|
|
|
relation->rel_name = REL.RDB$RELATION_NAME;
|
|
relation->rel_owner_name = REL.RDB$OWNER_NAME;
|
|
|
|
if (REL.RDB$FLAGS & REL_sql)
|
|
{
|
|
relation->rel_flags |= REL_sql_relation;
|
|
}
|
|
|
|
if (!REL.RDB$VIEW_BLR.isEmpty())
|
|
{
|
|
/* parse the view blr, getting dependencies on relations, etc. at the same time */
|
|
|
|
if (dependencies)
|
|
{
|
|
Firebird::MetaName depName(REL.RDB$RELATION_NAME);
|
|
relation->rel_view_rse =
|
|
(RecordSelExpr*) MET_get_dependencies(tdbb, relation, NULL, NULL,
|
|
&REL.RDB$VIEW_BLR, NULL,
|
|
&csb, depName, obj_view, 0, depTrans);
|
|
}
|
|
else
|
|
{
|
|
relation->rel_view_rse =
|
|
(RecordSelExpr*) MET_parse_blob(tdbb, relation,
|
|
&REL.RDB$VIEW_BLR, &csb, NULL, false);
|
|
}
|
|
|
|
/* retrieve the view context names */
|
|
|
|
lookup_view_contexts(tdbb, relation);
|
|
}
|
|
|
|
relation->rel_flags |= REL_scanned;
|
|
if (REL.RDB$EXTERNAL_FILE[0])
|
|
{
|
|
EXT_file(relation, REL.RDB$EXTERNAL_FILE, &REL.RDB$EXTERNAL_DESCRIPTION);
|
|
}
|
|
|
|
/* Pick up field specific stuff */
|
|
|
|
blob = BLB_open(tdbb, dbb->dbb_sys_trans, &REL.RDB$RUNTIME);
|
|
Firebird::HalfStaticArray<UCHAR, 256> temp;
|
|
UCHAR* const buffer = temp.getBuffer(blob->blb_max_segment + 1);
|
|
|
|
jrd_fld* field = NULL;
|
|
ArrayField* array = 0;
|
|
USHORT view_context = 0;
|
|
USHORT field_id = 0;
|
|
for (;;)
|
|
{
|
|
USHORT length = BLB_get_segment(tdbb, blob, buffer, blob->blb_max_segment);
|
|
if (blob->blb_flags & BLB_eof)
|
|
{
|
|
break;
|
|
}
|
|
USHORT n;
|
|
buffer[length] = 0;
|
|
UCHAR* p = (UCHAR*) &n;
|
|
const UCHAR* q = buffer + 1;
|
|
while (q < buffer + 1 + sizeof(SSHORT))
|
|
{
|
|
*p++ = *q++;
|
|
}
|
|
p = buffer + 1;
|
|
--length;
|
|
switch ((RSR_T) buffer[0])
|
|
{
|
|
case RSR_field_id:
|
|
if (field && field->fld_security_name.length() == 0 && !REL.RDB$DEFAULT_CLASS.NULL)
|
|
{
|
|
field->fld_security_name = REL.RDB$DEFAULT_CLASS;
|
|
}
|
|
field_id = n;
|
|
field = (*vector)[field_id];
|
|
|
|
if (field) {
|
|
field->fld_computation = NULL;
|
|
field->fld_missing_value = NULL;
|
|
field->fld_default_value = NULL;
|
|
field->fld_validation = NULL;
|
|
field->fld_not_null = NULL;
|
|
}
|
|
|
|
array = NULL;
|
|
break;
|
|
|
|
case RSR_field_name:
|
|
if (field) {
|
|
/* The field exists. If its name hasn't changed, then
|
|
there's no need to copy anything. */
|
|
|
|
if (field->fld_name == reinterpret_cast<char*>(p))
|
|
break;
|
|
|
|
field->fld_name = reinterpret_cast<char*>(p);
|
|
}
|
|
else {
|
|
field = FB_NEW(*dbb->dbb_permanent) jrd_fld(*dbb->dbb_permanent);
|
|
(*vector)[field_id] = field;
|
|
field->fld_name = reinterpret_cast<char*>(p);
|
|
}
|
|
|
|
// CVC: Be paranoid and allow the possible trigger(s) to have a
|
|
// not null security class to work on, even if we only take it
|
|
// from the relation itself.
|
|
if (field->fld_security_name.length() == 0 && !REL.RDB$DEFAULT_CLASS.NULL)
|
|
{
|
|
field->fld_security_name = REL.RDB$DEFAULT_CLASS;
|
|
}
|
|
|
|
break;
|
|
|
|
case RSR_view_context:
|
|
view_context = n;
|
|
break;
|
|
|
|
case RSR_base_field:
|
|
if (dependencies) {
|
|
csb->csb_g_flags |= csb_get_dependencies;
|
|
field->fld_source = PAR_make_field(tdbb, csb, view_context, (TEXT*) p);
|
|
const Firebird::MetaName depName(REL.RDB$RELATION_NAME);
|
|
store_dependencies(tdbb, csb, 0, depName, obj_view, depTrans);
|
|
}
|
|
else {
|
|
field->fld_source = PAR_make_field(tdbb, csb, view_context, (TEXT*) p);
|
|
}
|
|
break;
|
|
|
|
case RSR_computed_blr:
|
|
field->fld_computation = (dependencies) ?
|
|
MET_get_dependencies(tdbb, relation, p, csb, NULL, NULL, NULL, field->fld_name,
|
|
obj_computed, 0, depTrans) :
|
|
PAR_blr(tdbb, relation, p, csb, NULL, NULL, false, 0);
|
|
break;
|
|
|
|
case RSR_missing_value:
|
|
field->fld_missing_value = PAR_blr(tdbb, relation, p, csb, NULL, NULL, false, 0);
|
|
break;
|
|
|
|
case RSR_default_value:
|
|
field->fld_default_value = PAR_blr(tdbb, relation, p, csb, NULL, NULL, false, 0);
|
|
break;
|
|
|
|
case RSR_validation_blr:
|
|
// AB: 2005-04-25 bug SF#1168898
|
|
// Ignore validation for VIEWs, because fields (domains) which are
|
|
// defined with CHECK constraints and have sub-selects should at least
|
|
// be parsed without view-context information. With view-context
|
|
// information the context-numbers are wrong.
|
|
// Because a VIEW can't have a validation section i ignored the whole call.
|
|
if (!csb)
|
|
{
|
|
field->fld_validation =
|
|
PAR_blr(tdbb, relation, p, csb, NULL, NULL, false, csb_validation);
|
|
}
|
|
break;
|
|
|
|
case RSR_field_not_null:
|
|
field->fld_not_null =
|
|
PAR_blr(tdbb, relation, p, csb, NULL, NULL, false, csb_validation);
|
|
break;
|
|
|
|
case RSR_security_class:
|
|
field->fld_security_name = (const TEXT*) p;
|
|
break;
|
|
|
|
case RSR_trigger_name:
|
|
MET_load_trigger(tdbb, relation, (const TEXT*) p, triggers);
|
|
break;
|
|
|
|
case RSR_dimensions:
|
|
field->fld_array = array = FB_NEW_RPT(*dbb->dbb_permanent, n) ArrayField();
|
|
array->arr_desc.iad_dimensions = n;
|
|
break;
|
|
|
|
case RSR_array_desc:
|
|
if (array)
|
|
memcpy(&array->arr_desc, p, length);
|
|
break;
|
|
|
|
default: /* Shut up compiler warning */
|
|
break;
|
|
}
|
|
}
|
|
BLB_close(tdbb, blob);
|
|
blob = 0;
|
|
if (field && field->fld_security_name.length() == 0 && !REL.RDB$DEFAULT_CLASS.NULL)
|
|
{
|
|
field->fld_security_name = REL.RDB$DEFAULT_CLASS;
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_fields))
|
|
REQUEST(irq_r_fields) = request;
|
|
|
|
delete csb;
|
|
|
|
if (ENCODE_ODS(dbb->dbb_ods_version, dbb->dbb_minor_original) >= ODS_11_1)
|
|
{
|
|
jrd_req* sub_request = CMP_find_request(tdbb, irq_r_type, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE sub_request)
|
|
REL IN RDB$RELATIONS WITH REL.RDB$RELATION_ID EQ relation->rel_id
|
|
|
|
if (!REQUEST(irq_r_type))
|
|
REQUEST(irq_r_type) = sub_request;
|
|
|
|
if (!REL.RDB$RELATION_TYPE.NULL)
|
|
{
|
|
switch (REL.RDB$RELATION_TYPE)
|
|
{
|
|
case rel_persistent:
|
|
break;
|
|
case rel_external:
|
|
fb_assert(relation->rel_file);
|
|
break;
|
|
case rel_view:
|
|
fb_assert(relation->rel_view_rse);
|
|
break;
|
|
case rel_virtual:
|
|
relation->rel_flags |= REL_virtual;
|
|
break;
|
|
case rel_global_temp_preserve:
|
|
relation->rel_flags |= REL_temp_conn;
|
|
break;
|
|
case rel_global_temp_delete:
|
|
relation->rel_flags |= REL_temp_tran;
|
|
break;
|
|
default:
|
|
fb_assert(false);
|
|
}
|
|
}
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_type))
|
|
REQUEST(irq_r_type) = sub_request;
|
|
}
|
|
|
|
/* release any triggers in case of a rescan, but not if the rescan
|
|
hapenned while system triggers were being loaded. */
|
|
|
|
if (!(relation->rel_flags & REL_sys_trigs_being_loaded)) {
|
|
/* if we are scanning a system relation during loading the system
|
|
triggers, (during parsing its blr actually), we must not release the
|
|
existing system triggers; because we have already set the
|
|
relation->rel_flag to not have REL_sys_trig, so these
|
|
system triggers will not get loaded again. This fixes bug 8149. */
|
|
|
|
/* We have just loaded the triggers onto the local vector triggers.
|
|
Its now time to place them at their rightful place ie the relation
|
|
block.
|
|
*/
|
|
trig_vec* tmp_vector;
|
|
|
|
tmp_vector = relation->rel_pre_store;
|
|
relation->rel_pre_store = triggers[TRIGGER_PRE_STORE];
|
|
MET_release_triggers(tdbb, &tmp_vector);
|
|
|
|
tmp_vector = relation->rel_post_store;
|
|
relation->rel_post_store = triggers[TRIGGER_POST_STORE];
|
|
MET_release_triggers(tdbb, &tmp_vector);
|
|
|
|
tmp_vector = relation->rel_pre_erase;
|
|
relation->rel_pre_erase = triggers[TRIGGER_PRE_ERASE];
|
|
MET_release_triggers(tdbb, &tmp_vector);
|
|
|
|
tmp_vector = relation->rel_post_erase;
|
|
relation->rel_post_erase = triggers[TRIGGER_POST_ERASE];
|
|
MET_release_triggers(tdbb, &tmp_vector);
|
|
|
|
tmp_vector = relation->rel_pre_modify;
|
|
relation->rel_pre_modify = triggers[TRIGGER_PRE_MODIFY];
|
|
MET_release_triggers(tdbb, &tmp_vector);
|
|
|
|
tmp_vector = relation->rel_post_modify;
|
|
relation->rel_post_modify = triggers[TRIGGER_POST_MODIFY];
|
|
MET_release_triggers(tdbb, &tmp_vector);
|
|
}
|
|
|
|
relation->rel_flags &= ~REL_being_scanned;
|
|
|
|
relation->rel_current_format = NULL;
|
|
|
|
} // try
|
|
catch (const Firebird::Exception&) {
|
|
relation->rel_flags &= ~(REL_being_scanned | REL_scanned);
|
|
if (dependencies) {
|
|
relation->rel_flags |= REL_get_dependencies;
|
|
}
|
|
if (sys_triggers) {
|
|
relation->rel_flags |= REL_sys_triggers;
|
|
}
|
|
if (blob)
|
|
BLB_close(tdbb, blob);
|
|
|
|
// Some functions inside FOR loop may throw, in which case request
|
|
// remained active forever. AP: 13-may-05.
|
|
if (request && request->req_flags & req_active)
|
|
{
|
|
EXE_unwind(tdbb, request);
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
void MET_trigger_msg(thread_db* tdbb, Firebird::string& msg, const Firebird::MetaName& name, USHORT number)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ t r i g g e r _ m s g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up trigger message using trigger and abort code.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_s_msgs, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
MSG IN RDB$TRIGGER_MESSAGES WITH
|
|
MSG.RDB$TRIGGER_NAME EQ name.c_str() AND
|
|
MSG.RDB$MESSAGE_NUMBER EQ number
|
|
|
|
if (!REQUEST(irq_s_msgs))
|
|
REQUEST(irq_s_msgs) = request;
|
|
msg = MSG.RDB$MESSAGE;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_s_msgs))
|
|
REQUEST(irq_s_msgs) = request;
|
|
|
|
msg.rtrim();
|
|
}
|
|
|
|
|
|
void MET_update_shadow(thread_db* tdbb, Shadow* shadow, USHORT file_flags)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ u p d a t e _ s h a d o w
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Update the stored file flags for the specified shadow.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* handle = NULL;
|
|
FOR(REQUEST_HANDLE handle)
|
|
FIL IN RDB$FILES WITH FIL.RDB$SHADOW_NUMBER EQ shadow->sdw_number
|
|
MODIFY FIL USING
|
|
FIL.RDB$FILE_FLAGS = file_flags;
|
|
END_MODIFY;
|
|
END_FOR;
|
|
CMP_release(tdbb, handle);
|
|
}
|
|
|
|
|
|
void MET_update_transaction(thread_db* tdbb, jrd_tra* transaction, const bool do_commit)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ u p d a t e _ t r a n s a c t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Update a record in RDB$TRANSACTIONS. If do_commit is true, this is a
|
|
* commit; otherwise it is a ROLLBACK.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_m_trans, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$TRANSACTIONS
|
|
WITH X.RDB$TRANSACTION_ID EQ transaction->tra_number
|
|
|
|
if (!REQUEST(irq_m_trans))
|
|
REQUEST(irq_m_trans) = request;
|
|
if (do_commit && (transaction->tra_flags & TRA_prepare2))
|
|
{
|
|
ERASE X
|
|
}
|
|
else
|
|
{
|
|
MODIFY X
|
|
X.RDB$TRANSACTION_STATE = (do_commit) ?
|
|
RDB$TRANSACTIONS.RDB$TRANSACTION_STATE.COMMITTED :
|
|
RDB$TRANSACTIONS.RDB$TRANSACTION_STATE.ROLLED_BACK;
|
|
END_MODIFY;
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_m_trans))
|
|
REQUEST(irq_m_trans) = request;
|
|
}
|
|
|
|
|
|
static int blocking_ast_dsql_cache(void* ast_object)
|
|
{
|
|
/**************************************
|
|
*
|
|
* b l o c k i n g _ a s t _ d s q l _ c a c h e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Someone is trying to drop an item from the DSQL cache.
|
|
* Mark the symbol as obsolete and release the lock.
|
|
*
|
|
**************************************/
|
|
DSqlCacheItem* item = static_cast<DSqlCacheItem*>(ast_object);
|
|
|
|
try
|
|
{
|
|
Database* dbb = item->lock->lck_dbb;
|
|
|
|
Database::SyncGuard dsGuard(dbb, true);
|
|
|
|
ThreadContextHolder tdbb;
|
|
tdbb->setDatabase(dbb);
|
|
tdbb->setAttachment(item->lock->lck_attachment);
|
|
|
|
Jrd::ContextPoolHolder context(tdbb, 0);
|
|
|
|
item->obsolete = true;
|
|
item->locked = false;
|
|
LCK_release(tdbb, item->lock);
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{} // no-op
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, int type, const Firebird::MetaName& name)
|
|
{
|
|
Database* dbb = tdbb->getDatabase();
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
Firebird::string key((char*) &type, sizeof(type));
|
|
key.append(name.c_str());
|
|
|
|
DSqlCacheItem* item = attachment->att_dsql_cache.put(key);
|
|
if (item)
|
|
{
|
|
item->obsolete = false;
|
|
item->locked = false;
|
|
item->lock = FB_NEW_RPT(*dbb->dbb_permanent, key.length()) Lock();
|
|
|
|
item->lock->lck_type = LCK_dsql_cache;
|
|
item->lock->lck_owner_handle = LCK_get_owner_handle(tdbb, item->lock->lck_type);
|
|
item->lock->lck_parent = dbb->dbb_lock;
|
|
item->lock->lck_dbb = dbb;
|
|
item->lock->lck_object = item;
|
|
item->lock->lck_ast = blocking_ast_dsql_cache;
|
|
item->lock->lck_length = key.length();
|
|
memcpy(item->lock->lck_key.lck_string, key.c_str(), key.length());
|
|
}
|
|
else
|
|
{
|
|
item = attachment->att_dsql_cache.get(key);
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
|
|
static int blocking_ast_procedure(void* ast_object)
|
|
{
|
|
/**************************************
|
|
*
|
|
* b l o c k i n g _ a s t _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Someone is trying to drop a proceedure. If there
|
|
* are outstanding interests in the existence of
|
|
* the relation then just mark as blocking and return.
|
|
* Otherwise, mark the procedure block as questionable
|
|
* and release the procedure existence lock.
|
|
*
|
|
**************************************/
|
|
jrd_prc* procedure = static_cast<jrd_prc*>(ast_object);
|
|
|
|
try
|
|
{
|
|
Database* dbb = procedure->prc_existence_lock->lck_dbb;
|
|
|
|
Database::SyncGuard dsGuard(dbb, true);
|
|
|
|
ThreadContextHolder tdbb;
|
|
tdbb->setDatabase(dbb);
|
|
tdbb->setAttachment(procedure->prc_existence_lock->lck_attachment);
|
|
|
|
Jrd::ContextPoolHolder context(tdbb, 0);
|
|
|
|
if (procedure->prc_existence_lock) {
|
|
LCK_release(tdbb, procedure->prc_existence_lock);
|
|
}
|
|
procedure->prc_flags |= PRC_obsolete;
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{} // no-op
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int blocking_ast_relation(void* ast_object)
|
|
{
|
|
/**************************************
|
|
*
|
|
* b l o c k i n g _ a s t _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Someone is trying to drop a relation. If there
|
|
* are outstanding interests in the existence of
|
|
* the relation then just mark as blocking and return.
|
|
* Otherwise, mark the relation block as questionable
|
|
* and release the relation existence lock.
|
|
*
|
|
**************************************/
|
|
jrd_rel* relation = static_cast<jrd_rel*>(ast_object);
|
|
|
|
try
|
|
{
|
|
Database* dbb = relation->rel_existence_lock->lck_dbb;
|
|
|
|
Database::SyncGuard dsGuard(dbb, true);
|
|
|
|
ThreadContextHolder tdbb;
|
|
tdbb->setDatabase(dbb);
|
|
tdbb->setAttachment(relation->rel_existence_lock->lck_attachment);
|
|
|
|
Jrd::ContextPoolHolder context(tdbb, 0);
|
|
|
|
if (relation->rel_use_count) {
|
|
relation->rel_flags |= REL_blocking;
|
|
}
|
|
else {
|
|
relation->rel_flags &= ~REL_blocking;
|
|
relation->rel_flags |= REL_check_existence;
|
|
if (relation->rel_existence_lock)
|
|
LCK_release(tdbb, relation->rel_existence_lock);
|
|
}
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{} // no-op
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int partners_ast_relation(void* ast_object)
|
|
{
|
|
jrd_rel* relation = static_cast<jrd_rel*>(ast_object);
|
|
|
|
try
|
|
{
|
|
Database* dbb = relation->rel_partners_lock->lck_dbb;
|
|
|
|
Database::SyncGuard dsGuard(dbb, true);
|
|
|
|
ThreadContextHolder tdbb;
|
|
tdbb->setDatabase(dbb);
|
|
tdbb->setAttachment(relation->rel_partners_lock->lck_attachment);
|
|
|
|
Jrd::ContextPoolHolder context(tdbb, 0);
|
|
|
|
LCK_release(tdbb, relation->rel_partners_lock);
|
|
relation->rel_flags |= REL_check_partners;
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{} // no-op
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void get_trigger(thread_db* tdbb, jrd_rel* relation,
|
|
bid* blob_id, bid* debug_blob_id, trig_vec** ptr,
|
|
const TEXT* name, UCHAR type,
|
|
bool sys_trigger, USHORT flags)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e t _ t r i g g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get trigger.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
if (blob_id->isEmpty())
|
|
return;
|
|
|
|
Database* dbb = tdbb->getDatabase();
|
|
blb* blrBlob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id);
|
|
save_trigger_data(tdbb, ptr, relation, NULL, blrBlob, debug_blob_id,
|
|
name, type, sys_trigger, flags);
|
|
}
|
|
|
|
|
|
static bool get_type(thread_db* tdbb, USHORT* id, const UCHAR* name, const TEXT* field)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e t _ t y p e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Resoved a symbolic name in RDB$TYPES. Returned the value
|
|
* defined for the name in (*id). Don't touch (*id) if you
|
|
* don't find the name.
|
|
*
|
|
* Return (1) if found, (0) otherwise.
|
|
*
|
|
**************************************/
|
|
UCHAR buffer[32]; /* BASED ON RDB$TYPE_NAME */
|
|
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
fb_assert(id != NULL);
|
|
fb_assert(name != NULL);
|
|
fb_assert(field != NULL);
|
|
|
|
/* Force key to uppercase, following C locale rules for uppercase */
|
|
UCHAR* p;
|
|
for (p = buffer; *name && p < buffer + sizeof(buffer) - 1; p++, name++)
|
|
{
|
|
*p = UPPER7(*name);
|
|
}
|
|
*p = 0;
|
|
|
|
/* Try for exact name match */
|
|
|
|
bool found = false;
|
|
|
|
jrd_req* handle = NULL;
|
|
FOR(REQUEST_HANDLE handle)
|
|
FIRST 1 T IN RDB$TYPES WITH
|
|
T.RDB$FIELD_NAME EQ field AND
|
|
T.RDB$TYPE_NAME EQ buffer
|
|
|
|
found = true;
|
|
*id = T.RDB$TYPE;
|
|
END_FOR;
|
|
CMP_release(tdbb, handle);
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
static void lookup_view_contexts( thread_db* tdbb, jrd_rel* view)
|
|
{
|
|
/**************************************
|
|
*
|
|
* l o o k u p _ v i e w _ c o n t e x t s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup view contexts and store in a linked
|
|
* list on the relation block.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
jrd_req* request = CMP_find_request(tdbb, irq_view_context, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
V IN RDB$VIEW_RELATIONS WITH
|
|
V.RDB$VIEW_NAME EQ view->rel_name.c_str()
|
|
SORTED BY V.RDB$VIEW_CONTEXT
|
|
|
|
if (!REQUEST(irq_view_context))
|
|
REQUEST(irq_view_context) = request;
|
|
|
|
// trim trailing spaces
|
|
fb_utils::exact_name_limit(V.RDB$CONTEXT_NAME, sizeof(V.RDB$CONTEXT_NAME));
|
|
|
|
ViewContext* view_context = FB_NEW(*dbb->dbb_permanent)
|
|
ViewContext(*dbb->dbb_permanent,
|
|
V.RDB$CONTEXT_NAME, V.RDB$RELATION_NAME, V.RDB$VIEW_CONTEXT);
|
|
|
|
view->rel_view_contexts.add(view_context);
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_view_context))
|
|
REQUEST(irq_view_context) = request;
|
|
}
|
|
|
|
|
|
static void make_relation_scope_name(const TEXT* rel_name,
|
|
const USHORT rel_flags, Firebird::string& str)
|
|
{
|
|
/**************************************
|
|
*
|
|
* m a k e _ r e l a t i o n _ s c o p e _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Make string with relation name and type
|
|
* of its temporary scope
|
|
*
|
|
**************************************/
|
|
const char *scope = NULL;
|
|
if (rel_flags & REL_temp_conn)
|
|
scope = REL_SCOPE_GTT_PRESERVE;
|
|
else if (rel_flags & REL_temp_tran)
|
|
scope = REL_SCOPE_GTT_DELETE;
|
|
else
|
|
scope = REL_SCOPE_PERSISTENT;
|
|
|
|
str.printf(scope, rel_name);
|
|
}
|
|
|
|
|
|
// ***********************************************************
|
|
// * p a r s e _ f i e l d _ b l r
|
|
// ***********************************************************
|
|
// Parses default BLR and validation BLR for a field.
|
|
static jrd_nod* parse_field_blr(thread_db* tdbb, bid* blob_id, const Firebird::MetaName name)
|
|
{
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5, name);
|
|
|
|
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id);
|
|
SLONG length = blob->blb_length + 10;
|
|
Firebird::HalfStaticArray<UCHAR, 512> temp;
|
|
|
|
BLB_get_data(tdbb, blob, temp.getBuffer(length), length);
|
|
csb->csb_blr = temp.begin();
|
|
|
|
jrd_nod* node = PAR_blr(tdbb, NULL, temp.begin(), NULL, &csb, NULL, false, 0);
|
|
|
|
csb->csb_blr = 0;
|
|
delete csb;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static jrd_nod* parse_param_blr(thread_db* tdbb,
|
|
jrd_prc* procedure, bid* blob_id, CompilerScratch* csb)
|
|
{
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id);
|
|
SLONG length = blob->blb_length + 10;
|
|
Firebird::HalfStaticArray<UCHAR, 512> temp;
|
|
|
|
BLB_get_data(tdbb, blob, temp.getBuffer(length), length);
|
|
csb->csb_blr = temp.begin();
|
|
|
|
jrd_nod* node = PAR_blr(tdbb, NULL, temp.begin(), NULL, &csb, &procedure->prc_request, false, 0);
|
|
csb->csb_blr = 0;
|
|
|
|
return node;
|
|
}
|
|
|
|
static jrd_nod* parse_procedure_blr(thread_db* tdbb,
|
|
jrd_prc* procedure,
|
|
bid* blob_id,
|
|
CompilerScratch* csb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r s e _ p r o c e d u r e _ b l r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse blr, returning a compiler scratch block with the results.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id);
|
|
const SLONG length = blob->blb_length + 10;
|
|
Firebird::HalfStaticArray<UCHAR, 512> tmp;
|
|
UCHAR* temp = tmp.getBuffer(length);
|
|
csb->csb_blr = temp;
|
|
BLB_get_data(tdbb, blob, temp, length);
|
|
// CVC: Why don't we check this function's return value!
|
|
// Trashed memory may be a cause of failure here
|
|
par_messages(tdbb, csb->csb_blr, (USHORT) length, procedure, csb);
|
|
|
|
return PAR_blr(tdbb, NULL, csb->csb_blr, NULL, &csb, &procedure->prc_request, false, 0);
|
|
}
|
|
|
|
|
|
static void par_messages(thread_db* tdbb,
|
|
const UCHAR* blr,
|
|
USHORT blr_length,
|
|
jrd_prc* procedure,
|
|
CompilerScratch* csb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ m e s s a g e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse the messages of a blr request. For specified message, allocate
|
|
* a format (Format) block.
|
|
*
|
|
**************************************/
|
|
csb->csb_running = blr;
|
|
const SSHORT version = BLR_BYTE;
|
|
if (version != blr_version4 && version != blr_version5) {
|
|
ERR_post(Arg::Gds(isc_metadata_corrupt) <<
|
|
Arg::Gds(isc_wroblrver) << Arg::Num(blr_version4) << Arg::Num(version));
|
|
}
|
|
|
|
if (BLR_BYTE != blr_begin) {
|
|
ERR_post(Arg::Gds(isc_metadata_corrupt));
|
|
}
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
while (BLR_BYTE == blr_message) {
|
|
const USHORT msg_number = BLR_BYTE;
|
|
USHORT count = BLR_BYTE;
|
|
count += (BLR_BYTE) << 8;
|
|
Format* format = Format::newFormat(*tdbb->getDefaultPool(), count);
|
|
|
|
ULONG offset = 0;
|
|
for (Format::fmt_desc_iterator desc = format->fmt_desc.begin(); count; --count, ++desc)
|
|
{
|
|
const USHORT align = PAR_desc(tdbb, csb, &*desc);
|
|
if (align) {
|
|
offset = FB_ALIGN(offset, align);
|
|
}
|
|
desc->dsc_address = (UCHAR *) (IPTR) offset;
|
|
offset += desc->dsc_length;
|
|
}
|
|
|
|
if (offset > MAX_FORMAT_SIZE) {
|
|
ERR_post(Arg::Gds(isc_imp_exc) <<
|
|
Arg::Gds(isc_blktoobig));
|
|
}
|
|
|
|
format->fmt_length = (USHORT) offset;
|
|
|
|
switch (msg_number)
|
|
{
|
|
case 0:
|
|
procedure->prc_input_fmt = format;
|
|
break;
|
|
case 1:
|
|
procedure->prc_output_fmt = format;
|
|
break;
|
|
default:
|
|
delete format;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void MET_release_trigger(thread_db* tdbb, trig_vec** vector_ptr,
|
|
const Firebird::MetaName& name)
|
|
{
|
|
/***********************************************
|
|
*
|
|
* M E T _ r e l e a s e _ t r i g g e r
|
|
*
|
|
***********************************************
|
|
*
|
|
* Functional description
|
|
* Release a specified trigger.
|
|
* If trigger are still active let someone
|
|
* else do the work.
|
|
*
|
|
**************************************/
|
|
if (!*vector_ptr)
|
|
return;
|
|
|
|
trig_vec& vector = **vector_ptr;
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
for (size_t i = 0; i < vector.getCount(); ++i)
|
|
{
|
|
if (vector[i].name == name)
|
|
{
|
|
jrd_req* r = vector[i].request;
|
|
if (r)
|
|
{
|
|
if (CMP_clone_is_active(r))
|
|
break;
|
|
CMP_release(tdbb, r);
|
|
}
|
|
vector.remove(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void MET_release_triggers( thread_db* tdbb, trig_vec** vector_ptr)
|
|
{
|
|
/***********************************************
|
|
*
|
|
* M E T _ r e l e a s e _ t r i g g e r s
|
|
*
|
|
***********************************************
|
|
*
|
|
* Functional description
|
|
* Release a possibly null vector of triggers.
|
|
* If triggers are still active let someone
|
|
* else do the work.
|
|
*
|
|
**************************************/
|
|
trig_vec* vector = *vector_ptr;
|
|
if (!vector)
|
|
{
|
|
return;
|
|
}
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
*vector_ptr = NULL;
|
|
size_t i;
|
|
|
|
for (i = 0; i < vector->getCount(); i++)
|
|
{
|
|
jrd_req* r = (*vector)[i].request;
|
|
if (r && CMP_clone_is_active(r))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < vector->getCount(); i++)
|
|
{
|
|
jrd_req* r = (*vector)[i].request;
|
|
if (r)
|
|
{
|
|
CMP_release(tdbb, r);
|
|
}
|
|
}
|
|
|
|
delete vector;
|
|
}
|
|
|
|
|
|
static bool resolve_charset_and_collation(thread_db* tdbb,
|
|
USHORT* id,
|
|
const UCHAR* charset,
|
|
const UCHAR* collation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* r e s o l v e _ c h a r s e t _ a n d _ c o l l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Given ASCII7 name of charset & collation
|
|
* resolve the specification to a Character set id.
|
|
* This character set id is also the id of the text_object
|
|
* that implements the C locale for the Character set.
|
|
*
|
|
* Inputs:
|
|
* (charset)
|
|
* ASCII7z name of character set.
|
|
* NULL (implying unspecified) means use the character set
|
|
* for defined for (collation).
|
|
*
|
|
* (collation)
|
|
* ASCII7z name of collation.
|
|
* NULL means use the default collation for (charset).
|
|
*
|
|
* Outputs:
|
|
* (*id)
|
|
* Set to character set specified by this name (low byte)
|
|
* Set to collation specified by this name (high byte).
|
|
*
|
|
* Return:
|
|
* true if no errors (and *id is set).
|
|
* false if either name not found.
|
|
* or if names found, but the collation isn't for the specified
|
|
* character set.
|
|
*
|
|
**************************************/
|
|
bool found = false;
|
|
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
fb_assert(id != NULL);
|
|
|
|
jrd_req* handle = NULL;
|
|
|
|
if (collation == NULL)
|
|
{
|
|
if (charset == NULL)
|
|
charset = (const UCHAR*) DEFAULT_CHARACTER_SET_NAME;
|
|
|
|
USHORT charset_id = 0;
|
|
if (get_type(tdbb, &charset_id, charset, "RDB$CHARACTER_SET_NAME"))
|
|
{
|
|
*id = charset_id;
|
|
return true;
|
|
}
|
|
|
|
/* Charset name not found in the alias table - before giving up
|
|
try the character_set table */
|
|
|
|
FOR(REQUEST_HANDLE handle)
|
|
FIRST 1 CS IN RDB$CHARACTER_SETS
|
|
WITH CS.RDB$CHARACTER_SET_NAME EQ charset
|
|
|
|
found = true;
|
|
*id = CS.RDB$CHARACTER_SET_ID;
|
|
|
|
END_FOR;
|
|
CMP_release(tdbb, handle);
|
|
|
|
return found;
|
|
}
|
|
|
|
if (charset == NULL)
|
|
{
|
|
FOR(REQUEST_HANDLE handle)
|
|
FIRST 1 COL IN RDB$COLLATIONS
|
|
WITH COL.RDB$COLLATION_NAME EQ collation
|
|
|
|
found = true;
|
|
*id = COL.RDB$CHARACTER_SET_ID | (COL.RDB$COLLATION_ID << 8);
|
|
|
|
END_FOR;
|
|
CMP_release(tdbb, handle);
|
|
|
|
return found;
|
|
}
|
|
|
|
FOR(REQUEST_HANDLE handle)
|
|
FIRST 1 CS IN RDB$CHARACTER_SETS CROSS
|
|
COL IN RDB$COLLATIONS OVER RDB$CHARACTER_SET_ID CROSS
|
|
AL1 IN RDB$TYPES
|
|
WITH AL1.RDB$FIELD_NAME EQ "RDB$CHARACTER_SET_NAME"
|
|
AND AL1.RDB$TYPE_NAME EQ charset
|
|
AND COL.RDB$COLLATION_NAME EQ collation
|
|
AND AL1.RDB$TYPE EQ CS.RDB$CHARACTER_SET_ID
|
|
|
|
found = true;
|
|
*id = CS.RDB$CHARACTER_SET_ID | (COL.RDB$COLLATION_ID << 8);
|
|
|
|
END_FOR;
|
|
CMP_release(tdbb, handle);
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
static void save_trigger_data(thread_db* tdbb, trig_vec** ptr, jrd_rel* relation,
|
|
jrd_req* request, blb* blrBlob, bid* dbgBlobID,
|
|
const TEXT* name, UCHAR type,
|
|
bool sys_trigger, USHORT flags)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s a v e _ t r i g g e r _ d a t a
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Save trigger data to passed vector
|
|
*
|
|
**************************************/
|
|
trig_vec* vector = *ptr;
|
|
|
|
if (!vector) {
|
|
vector = FB_NEW(*tdbb->getDatabase()->dbb_permanent)
|
|
trig_vec(*tdbb->getDatabase()->dbb_permanent);
|
|
*ptr = vector;
|
|
}
|
|
|
|
Trigger& t = vector->add();
|
|
if (blrBlob)
|
|
{
|
|
const SLONG length = blrBlob->blb_length + 10;
|
|
UCHAR* ptr2 = t.blr.getBuffer(length);
|
|
BLB_get_data(tdbb, blrBlob, ptr2, length);
|
|
}
|
|
if (dbgBlobID)
|
|
t.dbg_blob_id = *dbgBlobID;
|
|
if (name) {
|
|
t.name = name;
|
|
}
|
|
t.type = type;
|
|
t.flags = flags;
|
|
t.compile_in_progress = false;
|
|
t.sys_trigger = sys_trigger;
|
|
t.request = request;
|
|
t.relation = relation;
|
|
}
|
|
|
|
|
|
const Trigger* findTrigger(trig_vec* triggers, const Firebird::MetaName& trig_name)
|
|
{
|
|
if (triggers)
|
|
{
|
|
for (trig_vec::iterator t = triggers->begin(); t != triggers->end(); ++t)
|
|
{
|
|
if (t->name.compare(trig_name) == 0)
|
|
return &(*t);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void store_dependencies(thread_db* tdbb,
|
|
CompilerScratch* csb,
|
|
const jrd_rel* dep_rel,
|
|
const Firebird::MetaName& object_name,
|
|
int dependency_type,
|
|
jrd_tra* transaction)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s t o r e _ d e p e n d e n c i e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Store records in RDB$DEPENDENCIES
|
|
* corresponding to the objects found during
|
|
* compilation of blr for a trigger, view, etc.
|
|
*
|
|
**************************************/
|
|
Firebird::MetaName name;
|
|
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
const Trigger* t = 0;
|
|
const bool checkTableScope =
|
|
(dependency_type == obj_computed) ||
|
|
(dependency_type == obj_trigger) && (dep_rel != 0) &&
|
|
(
|
|
(t = findTrigger(dep_rel->rel_pre_erase, object_name)) ||
|
|
(t = findTrigger(dep_rel->rel_pre_modify, object_name)) ||
|
|
(t = findTrigger(dep_rel->rel_pre_store, object_name)) ||
|
|
(t = findTrigger(dep_rel->rel_post_erase, object_name)) ||
|
|
(t = findTrigger(dep_rel->rel_post_modify, object_name)) ||
|
|
(t = findTrigger(dep_rel->rel_post_store, object_name))
|
|
) && t && (t->sys_trigger);
|
|
|
|
while (csb->csb_dependencies.hasData())
|
|
{
|
|
jrd_nod* node = csb->csb_dependencies.pop();
|
|
if (!node->nod_arg[e_dep_object])
|
|
continue;
|
|
|
|
int dpdo_type = (int) (IPTR) node->nod_arg[e_dep_object_type];
|
|
jrd_rel* relation = NULL;
|
|
jrd_prc* procedure = NULL;
|
|
const Firebird::MetaName* dpdo_name = 0;
|
|
SubtypeInfo info;
|
|
|
|
switch (dpdo_type)
|
|
{
|
|
case obj_relation:
|
|
relation = (jrd_rel*) node->nod_arg[e_dep_object];
|
|
dpdo_name = &relation->rel_name;
|
|
|
|
fb_assert(dep_rel || !checkTableScope);
|
|
|
|
if (checkTableScope &&
|
|
( (dep_rel->rel_flags & (REL_temp_tran | REL_temp_conn)) !=
|
|
(relation->rel_flags & (REL_temp_tran | REL_temp_conn)) ))
|
|
{
|
|
if ( !( // master is ON COMMIT PRESERVE, detail is ON COMMIT DELETE
|
|
(dep_rel->rel_flags & REL_temp_tran) && (relation->rel_flags & REL_temp_conn) ||
|
|
// computed field of a view
|
|
(dependency_type == obj_computed) && (dep_rel->rel_view_rse != NULL)
|
|
))
|
|
{
|
|
Firebird::string sMaster, sChild;
|
|
|
|
make_relation_scope_name(relation->rel_name.c_str(),
|
|
relation->rel_flags, sMaster);
|
|
make_relation_scope_name(dep_rel->rel_name.c_str(),
|
|
dep_rel->rel_flags, sChild);
|
|
|
|
ERR_post(Arg::Gds(isc_no_meta_update) <<
|
|
Arg::Gds(isc_met_wrong_gtt_scope) << Arg::Str(sChild) <<
|
|
Arg::Str(sMaster));
|
|
}
|
|
}
|
|
|
|
MET_scan_relation(tdbb, relation);
|
|
if (relation->rel_view_rse) {
|
|
dpdo_type = obj_view;
|
|
}
|
|
break;
|
|
case obj_procedure:
|
|
procedure = (jrd_prc*) node->nod_arg[e_dep_object];
|
|
dpdo_name = &procedure->prc_name;
|
|
break;
|
|
case obj_collation:
|
|
{
|
|
const USHORT number = (IPTR) node->nod_arg[e_dep_object];
|
|
MET_get_char_coll_subtype_info(tdbb, number, &info);
|
|
dpdo_name = &info.collationName;
|
|
}
|
|
break;
|
|
case obj_exception:
|
|
{
|
|
const SLONG number = (IPTR) node->nod_arg[e_dep_object];
|
|
MET_lookup_exception (tdbb, number, name, 0, 0);
|
|
dpdo_name = &name;
|
|
}
|
|
break;
|
|
case obj_field:
|
|
dpdo_name = (Firebird::MetaName*) node->nod_arg[e_dep_object];
|
|
break;
|
|
// CVC: Here I'm going to track those pesky things named generators and UDFs.
|
|
case obj_generator:
|
|
{
|
|
const SLONG number = (IPTR) node->nod_arg[e_dep_object];
|
|
MET_lookup_generator_id (tdbb, number, name);
|
|
dpdo_name = &name;
|
|
}
|
|
break;
|
|
case obj_udf:
|
|
{
|
|
UserFunction* udf = (UserFunction*) node->nod_arg[e_dep_object];
|
|
dpdo_name = &udf->fun_name;
|
|
}
|
|
break;
|
|
case obj_index:
|
|
name = (TEXT*) node->nod_arg[e_dep_object];
|
|
dpdo_name = &name;
|
|
break;
|
|
}
|
|
|
|
jrd_nod* field_node = node->nod_arg[e_dep_field];
|
|
|
|
Firebird::MetaName field_name;
|
|
if (field_node)
|
|
{
|
|
if (field_node->nod_type == nod_field)
|
|
{
|
|
const SSHORT fld_id = (SSHORT) (IPTR) field_node->nod_arg[0];
|
|
if (relation)
|
|
{
|
|
const jrd_fld* field = MET_get_field(relation, fld_id);
|
|
if (field)
|
|
{
|
|
field_name = field->fld_name;
|
|
}
|
|
}
|
|
else if (procedure)
|
|
{
|
|
const Parameter* param = (*procedure->prc_output_fields)[fld_id];
|
|
// CVC: Setting the field var here didn't make sense alone,
|
|
// so I thought the missing code was to try to extract
|
|
// the field name that's in this case an output var from a proc.
|
|
if (param)
|
|
{
|
|
field_name = param->prm_name;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
field_name = (TEXT *) field_node->nod_arg[0];
|
|
}
|
|
}
|
|
|
|
if (field_name.length() > 0)
|
|
{
|
|
jrd_req* request = CMP_find_request(tdbb, irq_c_deps_f, IRQ_REQUESTS);
|
|
bool found = false;
|
|
fb_assert(dpdo_name);
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) X IN RDB$DEPENDENCIES WITH
|
|
X.RDB$DEPENDENT_NAME = object_name.c_str() AND
|
|
X.RDB$DEPENDED_ON_NAME = dpdo_name->c_str() AND
|
|
X.RDB$DEPENDED_ON_TYPE = dpdo_type AND
|
|
X.RDB$FIELD_NAME = field_name.c_str() AND
|
|
X.RDB$DEPENDENT_TYPE = dependency_type
|
|
|
|
if (!REQUEST(irq_c_deps_f))
|
|
REQUEST(irq_c_deps_f) = request;
|
|
found = true;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_c_deps_f))
|
|
REQUEST(irq_c_deps_f) = request;
|
|
|
|
if (found) {
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
jrd_req* request = CMP_find_request(tdbb, irq_c_deps, IRQ_REQUESTS);
|
|
bool found = false;
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) X IN RDB$DEPENDENCIES WITH
|
|
X.RDB$DEPENDENT_NAME = object_name.c_str() AND
|
|
X.RDB$DEPENDED_ON_NAME = dpdo_name->c_str() AND
|
|
X.RDB$DEPENDED_ON_TYPE = dpdo_type AND
|
|
X.RDB$FIELD_NAME MISSING AND
|
|
X.RDB$DEPENDENT_TYPE = dependency_type
|
|
|
|
if (!REQUEST(irq_c_deps))
|
|
REQUEST(irq_c_deps) = request;
|
|
found = true;
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_c_deps))
|
|
REQUEST(irq_c_deps) = request;
|
|
|
|
if (found) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_s_deps, IRQ_REQUESTS);
|
|
|
|
fb_assert(dpdo_name);
|
|
STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) DEP IN RDB$DEPENDENCIES
|
|
strcpy(DEP.RDB$DEPENDENT_NAME, object_name.c_str());
|
|
DEP.RDB$DEPENDED_ON_TYPE = dpdo_type;
|
|
strcpy(DEP.RDB$DEPENDED_ON_NAME, dpdo_name->c_str());
|
|
if (field_name.length() > 0)
|
|
{
|
|
DEP.RDB$FIELD_NAME.NULL = FALSE;
|
|
strcpy(DEP.RDB$FIELD_NAME, field_name.c_str());
|
|
}
|
|
else {
|
|
DEP.RDB$FIELD_NAME.NULL = TRUE;
|
|
}
|
|
DEP.RDB$DEPENDENT_TYPE = dependency_type;
|
|
END_STORE;
|
|
|
|
if (!REQUEST(irq_s_deps))
|
|
REQUEST(irq_s_deps) = request;
|
|
}
|
|
}
|
|
|
|
|
|
static bool verify_TRG_ignore_perm(thread_db* tdbb, const Firebird::MetaName& trig_name)
|
|
{
|
|
/*****************************************************
|
|
*
|
|
* v e r i f y _ T R G _ i g n o r e _ p e r m
|
|
*
|
|
*****************************************************
|
|
*
|
|
* Functional description
|
|
* Return true if this trigger can go through without any permission
|
|
* checks. Currently, the only class of triggers that can go
|
|
* through without permission checks are
|
|
* (a) two system triggers (RDB$TRIGGERS_34 and RDB$TRIGGERS_35)
|
|
* (b) those defined for referential integrity actions such as,
|
|
* set null, set default, and cascade.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
// See if this is a system trigger, with the flag set as TRG_ignore_perm
|
|
|
|
if (INI_get_trig_flags(trig_name.c_str()) & TRG_ignore_perm)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
// See if this is a RI trigger
|
|
|
|
jrd_req* request = CMP_find_request(tdbb, irq_c_trg_perm, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
CHK IN RDB$CHECK_CONSTRAINTS CROSS
|
|
REF IN RDB$REF_CONSTRAINTS WITH
|
|
CHK.RDB$TRIGGER_NAME EQ trig_name.c_str() AND
|
|
REF.RDB$CONSTRAINT_NAME = CHK.RDB$CONSTRAINT_NAME
|
|
|
|
if (!REQUEST(irq_c_trg_perm))
|
|
{
|
|
REQUEST(irq_c_trg_perm) = request;
|
|
}
|
|
|
|
EXE_unwind(tdbb, request);
|
|
fb_utils::exact_name_limit(REF.RDB$UPDATE_RULE, sizeof(REF.RDB$UPDATE_RULE));
|
|
fb_utils::exact_name_limit(REF.RDB$DELETE_RULE, sizeof(REF.RDB$DELETE_RULE));
|
|
|
|
if (!strcmp(REF.RDB$UPDATE_RULE, RI_ACTION_CASCADE) ||
|
|
!strcmp(REF.RDB$UPDATE_RULE, RI_ACTION_NULL) ||
|
|
!strcmp(REF.RDB$UPDATE_RULE, RI_ACTION_DEFAULT) ||
|
|
!strcmp(REF.RDB$DELETE_RULE, RI_ACTION_CASCADE) ||
|
|
!strcmp(REF.RDB$DELETE_RULE, RI_ACTION_NULL) ||
|
|
!strcmp(REF.RDB$DELETE_RULE, RI_ACTION_DEFAULT))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_c_trg_perm))
|
|
{
|
|
REQUEST(irq_c_trg_perm) = request;
|
|
}
|
|
|
|
return false;
|
|
}
|