mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-29 06:43:03 +01:00
dc7aad88ed
2) Re-enabled checkouts in PIO routines, this resolves CORE-4179. Windows build may be broken, please validate. 3) Implemented faster reaction on asynchronous cancellation/shutdown requests. 4) Avoided locking dbb_sync in AST. This change also solves races during massive attachment shutdown.
5267 lines
139 KiB
Plaintext
5267 lines
139 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
|
|
*/
|
|
|
|
#include "firebird.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdarg.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 "../dsql/StmtNodes.h"
|
|
#include "../intl/charsets.h"
|
|
#include "../common/gdsassert.h"
|
|
#include "../jrd/blb_proto.h"
|
|
#include "../jrd/cmp_proto.h"
|
|
#include "../jrd/dfw_proto.h"
|
|
#include "../common/dsc_proto.h"
|
|
#include "../jrd/err_proto.h"
|
|
#include "../jrd/evl_proto.h"
|
|
#include "../jrd/exe_proto.h"
|
|
#include "../jrd/ext_proto.h"
|
|
#include "../jrd/flu_proto.h"
|
|
#include "../yvalve/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 "../common/utils_proto.h"
|
|
|
|
#include "../jrd/PreparedStatement.h"
|
|
#include "../jrd/RecordSourceNodes.h"
|
|
#include "../jrd/DebugInterface.h"
|
|
#include "../common/classes/MsgPrint.h"
|
|
#include "../jrd/Function.h"
|
|
|
|
|
|
#ifdef HAVE_CTYPE_H
|
|
#include <ctype.h>
|
|
#endif
|
|
|
|
// Pick up relation ids
|
|
#include "../jrd/ini.h"
|
|
|
|
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 QualifiedName& name);
|
|
static int blocking_ast_procedure(void*);
|
|
static int blocking_ast_relation(void*);
|
|
static int partners_ast_relation(void*);
|
|
static int rescan_ast_relation(void*);
|
|
static ULONG get_rel_flags_from_FLAGS(USHORT);
|
|
static void get_trigger(thread_db*, jrd_rel*, bid*, bid*, trig_vec**, const TEXT*, FB_UINT64, bool,
|
|
USHORT, const MetaName&, const string&, const bid*);
|
|
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, string& str);
|
|
static ValueExprNode* parse_field_default_blr(thread_db* tdbb, bid* blob_id);
|
|
static BoolExprNode* parse_field_validation_blr(thread_db* tdbb, bid* blob_id, const MetaName name);
|
|
static bool resolve_charset_and_collation(thread_db*, USHORT*, const UCHAR*, const UCHAR*);
|
|
static void save_trigger_data(thread_db*, trig_vec**, jrd_rel*, JrdStatement*, blb*, blb*,
|
|
const TEXT*, FB_UINT64, bool, USHORT, const MetaName&, const string&,
|
|
const bid*);
|
|
static void scan_partners(thread_db*, jrd_rel*);
|
|
static void store_dependencies(thread_db*, CompilerScratch*, const jrd_rel*,
|
|
const MetaName&, int, jrd_tra*);
|
|
static bool verify_TRG_ignore_perm(thread_db*, const MetaName&);
|
|
|
|
|
|
// Decompile all triggers from vector
|
|
static void release_cached_triggers(thread_db* tdbb, trig_vec* vector)
|
|
{
|
|
if (!vector)
|
|
return;
|
|
|
|
for (FB_SIZE_T i = 0; i < vector->getCount(); i++)
|
|
(*vector)[i].release(tdbb);
|
|
}
|
|
|
|
|
|
static void inc_int_use_count(JrdStatement* statement)
|
|
{
|
|
// Handle sub-statements
|
|
for (JrdStatement** subStatement = statement->subStatements.begin();
|
|
subStatement != statement->subStatements.end();
|
|
++subStatement)
|
|
{
|
|
inc_int_use_count(*subStatement);
|
|
}
|
|
|
|
// Increment int_use_count for all procedures in resource list of request
|
|
ResourceList& list = statement->resources;
|
|
FB_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;
|
|
//// FIXME: CORE-4271: fb_assert(resource.rsc_routine->intUseCount >= 0);
|
|
++resource.rsc_routine->intUseCount;
|
|
}
|
|
|
|
for (list.find(Resource(Resource::rsc_function, 0, NULL, NULL, NULL), i);
|
|
i < list.getCount(); i++)
|
|
{
|
|
Resource& resource = list[i];
|
|
if (resource.rsc_type != Resource::rsc_function)
|
|
break;
|
|
//// FIXME: CORE-4271: fb_assert(resource.rsc_routine->intUseCount >= 0);
|
|
++resource.rsc_routine->intUseCount;
|
|
}
|
|
}
|
|
|
|
|
|
// Increment int_use_count for all procedures used by triggers
|
|
static void post_used_procedures(trig_vec* vector)
|
|
{
|
|
if (!vector)
|
|
return;
|
|
|
|
for (FB_SIZE_T i = 0; i < vector->getCount(); i++)
|
|
{
|
|
JrdStatement* stmt = (*vector)[i].statement;
|
|
if (stmt && !stmt->isActive())
|
|
inc_int_use_count(stmt);
|
|
}
|
|
}
|
|
|
|
|
|
void MET_get_domain(thread_db* tdbb, MemoryPool& csbPool, const 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
bool found = false;
|
|
|
|
AutoCacheRequest handle(tdbb, irq_l_domain, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE handle)
|
|
FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ name.c_str()
|
|
{
|
|
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;
|
|
|
|
Jrd::ContextPoolHolder context(tdbb, &csbPool);
|
|
|
|
if (FLD.RDB$DEFAULT_VALUE.NULL)
|
|
fieldInfo->defaultValue = NULL;
|
|
else
|
|
fieldInfo->defaultValue = parse_field_default_blr(tdbb, &FLD.RDB$DEFAULT_VALUE);
|
|
|
|
if (FLD.RDB$VALIDATION_BLR.NULL)
|
|
fieldInfo->validationExpr = NULL;
|
|
else
|
|
{
|
|
fieldInfo->validationExpr = parse_field_validation_blr(tdbb,
|
|
&FLD.RDB$VALIDATION_BLR, name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
if (!found)
|
|
{
|
|
ERR_post(Arg::Gds(isc_domnotdef) << Arg::Str(name));
|
|
}
|
|
}
|
|
|
|
|
|
MetaName MET_get_relation_field(thread_db* tdbb, MemoryPool& csbPool, const MetaName& relationName,
|
|
const 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
bool found = false;
|
|
MetaName sourceName;
|
|
|
|
AutoCacheRequest handle(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 (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,
|
|
(RFL.RDB$COLLATION_ID.NULL ? FLD.RDB$COLLATION_ID : RFL.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;
|
|
|
|
Jrd::ContextPoolHolder context(tdbb, &csbPool);
|
|
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_default_blr(tdbb, defaultId);
|
|
else
|
|
fieldInfo->defaultValue = NULL;
|
|
|
|
if (FLD.RDB$VALIDATION_BLR.NULL)
|
|
fieldInfo->validationExpr = NULL;
|
|
else
|
|
{
|
|
fieldInfo->validationExpr = parse_field_validation_blr(tdbb,
|
|
&FLD.RDB$VALIDATION_BLR, RFL.RDB$FIELD_SOURCE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
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);
|
|
Attachment* const attachment = tdbb->getAttachment();
|
|
|
|
vec<jrd_rel*>* relations = attachment->att_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;
|
|
}
|
|
}
|
|
|
|
|
|
static void adjust_dependencies(Routine* routine)
|
|
{
|
|
if (routine->intUseCount == -1)
|
|
{
|
|
// Already processed
|
|
return;
|
|
}
|
|
|
|
routine->intUseCount = -1; // Mark as undeletable
|
|
|
|
if (routine->getStatement())
|
|
{
|
|
// Loop over procedures from resource list of request
|
|
ResourceList& list = routine->getStatement()->resources;
|
|
FB_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;
|
|
|
|
routine = resource.rsc_routine;
|
|
|
|
if (routine->intUseCount == routine->useCount)
|
|
{
|
|
// Mark it and all dependent procedures as undeletable
|
|
adjust_dependencies(routine);
|
|
}
|
|
}
|
|
|
|
for (list.find(Resource(Resource::rsc_function, 0, NULL, NULL, NULL), i);
|
|
i < list.getCount(); i++)
|
|
{
|
|
Resource& resource = list[i];
|
|
|
|
if (resource.rsc_type != Resource::rsc_function)
|
|
break;
|
|
|
|
routine = resource.rsc_routine;
|
|
|
|
if (routine->intUseCount == routine->useCount)
|
|
{
|
|
// Mark it and all dependent functions as undeletable
|
|
adjust_dependencies(routine);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#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 routines are properly counted
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Attachment* att = tdbb->getAttachment();
|
|
if (!att)
|
|
return;
|
|
|
|
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
|
{
|
|
Routine* routine = *iter;
|
|
|
|
if (routine && routine->getStatement() /*&&
|
|
!(routine->flags & Routine::FLAG_OBSOLETE)*/ )
|
|
{
|
|
fb_assert(routine->intUseCount == 0);
|
|
}
|
|
}
|
|
|
|
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
|
{
|
|
Routine* routine = *iter;
|
|
|
|
if (routine && routine->getStatement() /*&&
|
|
!(routine->flags & Routine::FLAG_OBSOLETE)*/ )
|
|
{
|
|
fb_assert(routine->intUseCount == 0);
|
|
}
|
|
}
|
|
|
|
// Walk procedures and calculate internal dependencies
|
|
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
|
{
|
|
jrd_prc* routine = *iter;
|
|
|
|
if (routine && routine->getStatement() /*&&
|
|
!(routine->flags & Routine::FLAG_OBSOLETE)*/ )
|
|
{
|
|
inc_int_use_count(routine->getStatement());
|
|
}
|
|
}
|
|
|
|
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
|
{
|
|
Routine* routine = *iter;
|
|
|
|
if (routine && routine->getStatement() /*&&
|
|
!(routine->flags & Routine::FLAG_OBSOLETE)*/ )
|
|
{
|
|
inc_int_use_count(routine->getStatement());
|
|
}
|
|
}
|
|
|
|
// Walk procedures again and check dependencies
|
|
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
|
{
|
|
Routine* routine = *iter;
|
|
|
|
if (routine && routine->getStatement() && /*
|
|
!(routine->flags & Routine::FLAG_OBSOLETE) && */
|
|
routine->useCount < routine->intUseCount)
|
|
{
|
|
char buffer[1024], *buf = buffer;
|
|
buf += sprintf(buf, "Procedure %d:%s is not properly counted (use count=%d, prc use=%d). Used by: \n",
|
|
routine->getId(), routine->getName().toString().c_str(),
|
|
routine->useCount, routine->intUseCount);
|
|
|
|
for (jrd_prc** iter2 = att->att_procedures.begin(); iter2 != att->att_procedures.end(); ++iter2)
|
|
{
|
|
Routine* routine2 = *iter2;
|
|
|
|
if (routine2 && routine2->getStatement() /*&& !(routine2->flags & Routine::FLAG_OBSOLETE)*/ )
|
|
{
|
|
// Loop over procedures from resource list of request
|
|
const ResourceList& list = routine2->getStatement()->resources;
|
|
FB_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_routine == routine)
|
|
{
|
|
// Do not enable this code in production builds unless
|
|
// the possible B.O. is fixed here.
|
|
buf += sprintf(buf, "%d:%s\n", routine2->getId(),
|
|
routine2->getName().toString().c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (Function** iter2 = att->att_functions.begin(); iter2 != att->att_functions.end(); ++iter2)
|
|
{
|
|
Routine* routine2 = *iter2;
|
|
|
|
if (routine2 && routine2->getStatement() /*&& !(routine2->flags & Routine::FLAG_OBSOLETE)*/ )
|
|
{
|
|
// Loop over procedures from resource list of request
|
|
const ResourceList& list = routine2->getStatement()->resources;
|
|
FB_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_routine == routine)
|
|
{
|
|
// Do not enable this code in production builds unless
|
|
// the possible B.O. is fixed here.
|
|
buf += sprintf(buf, "%d:%s\n", routine2->getId(),
|
|
routine2->getName().toString().c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gds__log(buffer);
|
|
fb_assert(false);
|
|
}
|
|
}
|
|
|
|
// Walk functions again and check dependencies
|
|
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
|
{
|
|
Routine* routine = *iter;
|
|
|
|
if (routine && routine->getStatement() && /*
|
|
!(routine->flags & Routine::FLAG_OBSOLETE) && */
|
|
routine->useCount < routine->intUseCount)
|
|
{
|
|
char buffer[1024], *buf = buffer;
|
|
buf += sprintf(buf, "Function %d:%s is not properly counted (use count=%d, func use=%d). Used by: \n",
|
|
routine->getId(), routine->getName().toString().c_str(),
|
|
routine->useCount, routine->intUseCount);
|
|
|
|
for (jrd_prc** iter2 = att->att_procedures.begin(); iter2 != att->att_procedures.end(); ++iter2)
|
|
{
|
|
Routine* routine2 = *iter2;
|
|
|
|
if (routine2 && routine2->getStatement() /*&& !(routine2->flags & Routine::FLAG_OBSOLETE)*/ )
|
|
{
|
|
// Loop over procedures from resource list of request
|
|
const ResourceList& list = routine2->getStatement()->resources;
|
|
FB_SIZE_T i;
|
|
|
|
for (list.find(Resource(Resource::rsc_function, 0, NULL, NULL, NULL), i);
|
|
i < list.getCount(); i++)
|
|
{
|
|
const Resource& resource = list[i];
|
|
if (resource.rsc_type != Resource::rsc_function)
|
|
break;
|
|
|
|
if (resource.rsc_routine == routine)
|
|
{
|
|
// Do not enable this code in production builds unless
|
|
// the possible B.O. is fixed here.
|
|
buf += sprintf(buf, "%d:%s\n", routine2->getId(),
|
|
routine2->getName().toString().c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (Function** iter2 = att->att_functions.begin(); iter2 != att->att_functions.end(); ++iter2)
|
|
{
|
|
Routine* routine2 = *iter2;
|
|
|
|
if (routine2 && routine2->getStatement() /*&& !(routine2->flags & Routine::FLAG_OBSOLETE)*/ )
|
|
{
|
|
// Loop over procedures from resource list of request
|
|
const ResourceList& list = routine2->getStatement()->resources;
|
|
FB_SIZE_T i;
|
|
|
|
for (list.find(Resource(Resource::rsc_function, 0, NULL, NULL, NULL), i);
|
|
i < list.getCount(); i++)
|
|
{
|
|
const Resource& resource = list[i];
|
|
if (resource.rsc_type != Resource::rsc_function)
|
|
break;
|
|
|
|
if (resource.rsc_routine == routine)
|
|
{
|
|
// Do not enable this code in production builds unless
|
|
// the possible B.O. is fixed here.
|
|
buf += sprintf(buf, "%d:%s\n", routine2->getId(),
|
|
routine2->getName().toString().c_str());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
gds__log(buffer);
|
|
fb_assert(false);
|
|
}
|
|
}
|
|
|
|
// Fix back int_use_count
|
|
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
|
{
|
|
Routine* routine = *iter;
|
|
|
|
if (routine)
|
|
routine->intUseCount = 0;
|
|
}
|
|
|
|
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
|
{
|
|
Routine* routine = *iter;
|
|
|
|
if (routine)
|
|
routine->intUseCount = 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
|
|
|
|
Attachment* att = tdbb->getAttachment();
|
|
|
|
for (unsigned i = 0; i < DB_TRIGGER_MAX; i++)
|
|
release_cached_triggers(tdbb, att->att_triggers[i]);
|
|
|
|
vec<jrd_rel*>* relations = att->att_relations;
|
|
if (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
|
|
|
|
// Walk routines and calculate internal dependencies.
|
|
|
|
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
|
{
|
|
Routine* const routine = *iter;
|
|
|
|
if (routine && routine->getStatement() &&
|
|
!(routine->flags & Routine::FLAG_OBSOLETE) )
|
|
{
|
|
inc_int_use_count(routine->getStatement());
|
|
}
|
|
}
|
|
|
|
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
|
{
|
|
Function* const routine = *iter;
|
|
|
|
if (routine && routine->getStatement() &&
|
|
!(routine->flags & Routine::FLAG_OBSOLETE) )
|
|
{
|
|
inc_int_use_count(routine->getStatement());
|
|
}
|
|
}
|
|
|
|
// Walk routines again and adjust dependencies for routines which will not be removed.
|
|
|
|
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
|
{
|
|
Routine* const routine = *iter;
|
|
|
|
if (routine && routine->getStatement() &&
|
|
!(routine->flags & Routine::FLAG_OBSOLETE) &&
|
|
routine->useCount != routine->intUseCount )
|
|
{
|
|
adjust_dependencies(routine);
|
|
}
|
|
}
|
|
|
|
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
|
{
|
|
Function* const routine = *iter;
|
|
|
|
if (routine && routine->getStatement() &&
|
|
!(routine->flags & Routine::FLAG_OBSOLETE) &&
|
|
routine->useCount != routine->intUseCount )
|
|
{
|
|
adjust_dependencies(routine);
|
|
}
|
|
}
|
|
|
|
// Deallocate all used requests.
|
|
|
|
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
|
{
|
|
Routine* const routine = *iter;
|
|
|
|
if (routine)
|
|
{
|
|
if (routine->getStatement() && !(routine->flags & Routine::FLAG_OBSOLETE) &&
|
|
routine->intUseCount >= 0 &&
|
|
routine->useCount == routine->intUseCount)
|
|
{
|
|
routine->releaseStatement(tdbb);
|
|
|
|
if (routine->existenceLock)
|
|
LCK_release(tdbb, routine->existenceLock);
|
|
routine->existenceLock = NULL;
|
|
routine->flags |= Routine::FLAG_OBSOLETE;
|
|
}
|
|
|
|
// Leave it in state 0 to avoid extra pass next time to clear it
|
|
// Note: we need to adjust intUseCount for all routines
|
|
// in cache because any of them may have been affected from
|
|
// dependencies earlier. Even routines that were not scanned yet !
|
|
routine->intUseCount = 0;
|
|
}
|
|
}
|
|
|
|
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
|
{
|
|
Function* const routine = *iter;
|
|
|
|
if (routine)
|
|
{
|
|
if (routine->getStatement() && !(routine->flags & Routine::FLAG_OBSOLETE) &&
|
|
routine->intUseCount >= 0 &&
|
|
routine->useCount == routine->intUseCount)
|
|
{
|
|
routine->releaseStatement(tdbb);
|
|
|
|
if (routine->existenceLock)
|
|
LCK_release(tdbb, routine->existenceLock);
|
|
routine->existenceLock = NULL;
|
|
routine->flags |= Routine::FLAG_OBSOLETE;
|
|
}
|
|
|
|
// Leave it in state 0 to avoid extra pass next time to clear it
|
|
// Note: we need to adjust intUseCount for all routines
|
|
// in cache because any of them may have been affected from
|
|
// dependencies earlier. Even routines that were not scanned yet !
|
|
routine->intUseCount = 0;
|
|
}
|
|
}
|
|
|
|
#ifdef DEV_BUILD
|
|
MET_verify_cache(tdbb);
|
|
#endif
|
|
}
|
|
|
|
|
|
bool MET_routine_in_use(thread_db* tdbb, Routine* routine)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r o u t i n e _ i n _ u s e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Determine if routine is used by any user requests or transactions.
|
|
* Return false if routine is used only inside cache or not used at all.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
#ifdef DEV_BUILD
|
|
MET_verify_cache(tdbb);
|
|
#endif
|
|
|
|
Attachment* const att = tdbb->getAttachment();
|
|
|
|
vec<jrd_rel*>* relations = att->att_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
|
|
|
|
// Walk routines and calculate internal dependencies
|
|
|
|
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
|
{
|
|
jrd_prc* procedure = *iter;
|
|
|
|
if (procedure && procedure->getStatement() &&
|
|
!(procedure->flags & Routine::FLAG_OBSOLETE))
|
|
{
|
|
inc_int_use_count(procedure->getStatement());
|
|
}
|
|
}
|
|
|
|
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
|
{
|
|
Function* function = *iter;
|
|
|
|
if (function && function->getStatement() &&
|
|
!(function->flags & Routine::FLAG_OBSOLETE))
|
|
{
|
|
inc_int_use_count(function->getStatement());
|
|
}
|
|
}
|
|
|
|
// Walk routines again and adjust dependencies for routines
|
|
// which will not be removed.
|
|
|
|
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
|
{
|
|
jrd_prc* procedure = *iter;
|
|
|
|
if (procedure && procedure->getStatement() &&
|
|
!(procedure->flags & Routine::FLAG_OBSOLETE) &&
|
|
procedure->useCount != procedure->intUseCount && procedure != routine)
|
|
{
|
|
adjust_dependencies(procedure);
|
|
}
|
|
}
|
|
|
|
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
|
{
|
|
Function* function = *iter;
|
|
|
|
if (function && function->getStatement() &&
|
|
!(function->flags & Routine::FLAG_OBSOLETE) &&
|
|
function->useCount != function->intUseCount && function != routine)
|
|
{
|
|
adjust_dependencies(function);
|
|
}
|
|
}
|
|
|
|
const bool result = routine->useCount != routine->intUseCount;
|
|
|
|
// Fix back intUseCount
|
|
|
|
for (jrd_prc** iter = att->att_procedures.begin(); iter != att->att_procedures.end(); ++iter)
|
|
{
|
|
jrd_prc* procedure = *iter;
|
|
|
|
if (procedure)
|
|
procedure->intUseCount = 0;
|
|
}
|
|
|
|
for (Function** iter = att->att_functions.begin(); iter != att->att_functions.end(); ++iter)
|
|
{
|
|
Function* function = *iter;
|
|
|
|
if (function)
|
|
function->intUseCount = 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
// Erase any secondary files of the primary database of the shadow being activated.
|
|
|
|
AutoRequest handle;
|
|
|
|
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
|
|
|
|
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];
|
|
AutoRequest handle2;
|
|
handle.reset();
|
|
|
|
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
|
|
}
|
|
|
|
|
|
ULONG MET_align(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, FORMAT_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.
|
|
* Find all functions and schedule a BLR validate.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
dsc relation_name;
|
|
DeferredWork* dw = NULL;
|
|
AutoCacheRequest 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
|
|
{
|
|
relation_name.makeText(sizeof(X.RDB$RELATION_NAME), CS_METADATA,
|
|
(UCHAR*) X.RDB$RELATION_NAME);
|
|
SCL_check_relation(tdbb, &relation_name, SCL_alter);
|
|
dw = DFW_post_work(transaction, dfw_update_format, &relation_name, 0);
|
|
|
|
AutoCacheRequest request2(tdbb, irq_m_fields4, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request2)
|
|
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 AND
|
|
PRC.RDB$PACKAGE_NAME MISSING
|
|
{
|
|
MetaName proc_name(PRC.RDB$PROCEDURE_NAME);
|
|
|
|
dsc desc;
|
|
desc.makeText(proc_name.length(), CS_METADATA, (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_arg_check_blr);
|
|
}
|
|
END_FOR
|
|
|
|
request2.reset(tdbb, irq_m_fields5, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request2)
|
|
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
|
|
{
|
|
MetaName trigger_name(TRG.RDB$TRIGGER_NAME);
|
|
MetaName trigger_relation_name(TRG.RDB$RELATION_NAME);
|
|
|
|
dsc desc;
|
|
desc.makeText(trigger_name.length(), CS_METADATA, (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_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_arg_check_blr);
|
|
}
|
|
END_FOR
|
|
|
|
request2.reset(tdbb, irq_m_fields8, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request2)
|
|
RFL IN RDB$RELATION_FIELDS CROSS
|
|
DEP IN RDB$DEPENDENCIES CROSS
|
|
FUN IN RDB$FUNCTIONS
|
|
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_udf AND
|
|
DEP.RDB$DEPENDENT_NAME EQ FUN.RDB$FUNCTION_NAME AND
|
|
FUN.RDB$PACKAGE_NAME MISSING
|
|
{
|
|
MetaName name(FUN.RDB$FUNCTION_NAME);
|
|
|
|
dsc desc;
|
|
desc.makeText(name.length(), CS_METADATA, (UCHAR*) name.c_str());
|
|
|
|
DeferredWork* dw2 =
|
|
DFW_post_work(transaction, dfw_modify_function, &desc, FUN.RDB$FUNCTION_ID);
|
|
DFW_post_work_arg(transaction, dw2, NULL, 0, dfw_arg_check_blr);
|
|
}
|
|
END_FOR
|
|
}
|
|
END_FOR
|
|
|
|
request.reset(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 AND
|
|
PRC.RDB$PACKAGE_NAME MISSING
|
|
{
|
|
MetaName proc_name(PRC.RDB$PROCEDURE_NAME);
|
|
|
|
dsc desc;
|
|
desc.makeText(proc_name.length(), CS_METADATA, (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_arg_check_blr);
|
|
}
|
|
END_FOR
|
|
|
|
request.reset(tdbb, irq_m_fields6, 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_package_header OR
|
|
DEP.RDB$DEPENDENT_TYPE EQ obj_package_body) AND
|
|
DEP.RDB$DEPENDENT_NAME EQ PRC.RDB$PACKAGE_NAME
|
|
{
|
|
MetaName proc_name(PRC.RDB$PROCEDURE_NAME);
|
|
|
|
dsc desc;
|
|
desc.makeText(proc_name.length(), CS_METADATA, (UCHAR*) proc_name.c_str());
|
|
|
|
DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_procedure, &desc,
|
|
PRC.RDB$PROCEDURE_ID, PRC.RDB$PACKAGE_NAME);
|
|
DFW_post_work_arg(transaction, dw2, NULL, 0, dfw_arg_check_blr);
|
|
}
|
|
END_FOR
|
|
|
|
request.reset(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
|
|
{
|
|
MetaName trigger_name(TRG.RDB$TRIGGER_NAME);
|
|
MetaName trigger_relation_name(TRG.RDB$RELATION_NAME);
|
|
|
|
dsc desc;
|
|
desc.makeText(trigger_name.length(), CS_METADATA, (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_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_arg_check_blr);
|
|
}
|
|
END_FOR
|
|
|
|
request.reset(tdbb, irq_m_fields7, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
DEP IN RDB$DEPENDENCIES CROSS
|
|
FUN IN RDB$FUNCTIONS
|
|
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_udf AND
|
|
DEP.RDB$DEPENDENT_NAME EQ FUN.RDB$FUNCTION_NAME AND
|
|
FUN.RDB$PACKAGE_NAME MISSING
|
|
{
|
|
MetaName name(FUN.RDB$FUNCTION_NAME);
|
|
|
|
dsc desc;
|
|
desc.makeText(name.length(), CS_METADATA, (UCHAR*) name.c_str());
|
|
|
|
DeferredWork* dw2 =
|
|
DFW_post_work(transaction, dfw_modify_function, &desc, FUN.RDB$FUNCTION_ID);
|
|
DFW_post_work_arg(transaction, dw2, NULL, 0, dfw_arg_check_blr);
|
|
}
|
|
END_FOR
|
|
|
|
request.reset(tdbb, irq_m_fields9, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
DEP IN RDB$DEPENDENCIES CROSS
|
|
FUN IN RDB$FUNCTIONS
|
|
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_package_header OR
|
|
DEP.RDB$DEPENDENT_TYPE EQ obj_package_body) AND
|
|
DEP.RDB$DEPENDENT_NAME EQ FUN.RDB$PACKAGE_NAME
|
|
{
|
|
MetaName name(FUN.RDB$FUNCTION_NAME);
|
|
|
|
dsc desc;
|
|
desc.makeText(name.length(), CS_METADATA, (UCHAR*) name.c_str());
|
|
|
|
DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_function, &desc,
|
|
FUN.RDB$FUNCTION_ID, FUN.RDB$PACKAGE_NAME);
|
|
DFW_post_work_arg(transaction, dw2, NULL, 0, dfw_arg_check_blr);
|
|
}
|
|
END_FOR
|
|
|
|
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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
if (!(relation->rel_flags & REL_scanned))
|
|
{
|
|
AutoCacheRequest request(tdbb, irq_l_curr_format, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
REL IN RDB$RELATIONS
|
|
WITH REL.RDB$RELATION_ID EQ relation->rel_id
|
|
{
|
|
relation->rel_current_fmt = REL.RDB$FORMAT;
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
// Usually, format numbers start with one and they are present in RDB$FORMATS.
|
|
// However, system tables have zero as their initial format and they don't have
|
|
// any related records in RDB$FORMATS, instead their rel_formats[0] is initialized
|
|
// directly (see ini.epp). Every other case of zero format number found for an already
|
|
// scanned table must be catched here and investigated.
|
|
fb_assert(relation->rel_current_fmt || relation->isSystem());
|
|
|
|
relation->rel_current_format = MET_format(tdbb, relation, relation->rel_current_fmt);
|
|
|
|
return relation->rel_current_format;
|
|
}
|
|
|
|
|
|
void MET_delete_dependencies(thread_db* tdbb,
|
|
const 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);
|
|
|
|
// if (!object_name)
|
|
// return;
|
|
//
|
|
AutoCacheRequest 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
|
|
{
|
|
ERASE DEP;
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
|
|
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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
AutoRequest handle;
|
|
|
|
FOR(REQUEST_HANDLE handle)
|
|
X IN RDB$FILES WITH X.RDB$SHADOW_NUMBER EQ shadow_number
|
|
ERASE X;
|
|
END_FOR
|
|
|
|
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 MetaName& name, const MetaName& package)
|
|
{
|
|
DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, QualifiedName(name, package));
|
|
|
|
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 MetaName& name, const MetaName& package)
|
|
{
|
|
DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, QualifiedName(name, package));
|
|
|
|
// release lock
|
|
LCK_release(tdbb, item->lock);
|
|
|
|
// notify others through AST to mark as obsolete
|
|
const USHORT key_length = item->lock->lck_length;
|
|
AutoPtr<Lock> temp_lock(FB_NEW_RPT(*tdbb->getDefaultPool(), key_length)
|
|
Lock(tdbb, key_length, LCK_dsql_cache));
|
|
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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
Format* format;
|
|
vec<Format*>* formats = relation->rel_formats;
|
|
if (formats && (number < formats->count()) && (format = (*formats)[number]))
|
|
{
|
|
return format;
|
|
}
|
|
|
|
format = NULL;
|
|
AutoCacheRequest 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
|
|
{
|
|
blb* blob = blb::open(tdbb, attachment->getSysTransaction(), &X.RDB$DESCRIPTOR);
|
|
|
|
// Use generic representation of formats with 32-bit offsets
|
|
|
|
HalfStaticArray<UCHAR, BUFFER_MEDIUM> buffer;
|
|
blob->BLB_get_data(tdbb, buffer.getBuffer(blob->blb_length), blob->blb_length);
|
|
unsigned bufferPos = 2;
|
|
USHORT count = buffer[0] | (buffer[1] << 8);
|
|
|
|
format = Format::newFormat(*relation->rel_pool, count);
|
|
|
|
Array<Ods::Descriptor> odsDescs;
|
|
Ods::Descriptor* odsDesc = odsDescs.getBuffer(count);
|
|
memcpy(odsDesc, buffer.begin() + bufferPos, count * sizeof(Ods::Descriptor));
|
|
|
|
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;
|
|
}
|
|
|
|
const UCHAR* p = buffer.begin() + bufferPos + count * sizeof(Ods::Descriptor);
|
|
count = p[0] | (p[1] << 8);
|
|
p += 2;
|
|
|
|
while (count-- > 0)
|
|
{
|
|
USHORT offset = p[0] | (p[1] << 8);
|
|
p += 2;
|
|
|
|
const Ods::Descriptor* odsDflDesc = (Ods::Descriptor*) p;
|
|
p = (UCHAR*) (odsDflDesc + 1);
|
|
|
|
dsc desc = *odsDflDesc;
|
|
desc.dsc_address = const_cast<UCHAR*>(p);
|
|
EVL_make_value(tdbb, &desc, &format->fmt_defaults[offset], relation->rel_pool);
|
|
|
|
p += desc.dsc_length;
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
if (!format)
|
|
format = Format::newFormat(*relation->rel_pool);
|
|
|
|
format->fmt_version = number;
|
|
|
|
// Link the format block into the world
|
|
|
|
formats = relation->rel_formats =
|
|
vec<Format*>::newVector(*relation->rel_pool, 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[MAX_SQL_IDENTIFIER_SIZE]; // 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
AutoCacheRequest request(tdbb, irq_l_subtype, IRQ_REQUESTS);
|
|
bool found = false;
|
|
|
|
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, attachment->getSysTransaction(), &CL.RDB$SPECIFIC_ATTRIBUTES);
|
|
const ULONG length = blob->blb_length;
|
|
|
|
// ASF: Here info->specificAttributes is in UNICODE_FSS charset.
|
|
// It will be converted to the collation charset in intl.cpp
|
|
blob->BLB_get_data(tdbb, info->specificAttributes.getBuffer(length), length);
|
|
}
|
|
|
|
info->attributes = (USHORT)CL.RDB$COLLATION_ATTRIBUTES;
|
|
info->ignoreAttributes = CL.RDB$COLLATION_ATTRIBUTES.NULL;
|
|
}
|
|
END_FOR
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
DmlNode* MET_get_dependencies(thread_db* tdbb,
|
|
jrd_rel* relation,
|
|
const UCHAR* blob,
|
|
const ULONG blob_length,
|
|
CompilerScratch* view_csb,
|
|
bid* blob_id,
|
|
JrdStatement** statementPtr,
|
|
CompilerScratch** csb_ptr,
|
|
const MetaName& object_name,
|
|
int type,
|
|
USHORT flags,
|
|
jrd_tra* transaction,
|
|
const 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
fb_assert(domain_validation.isEmpty() || object_name == domain_validation); // for now
|
|
|
|
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5, domain_validation);
|
|
csb->csb_g_flags |= (csb_get_dependencies | flags);
|
|
|
|
DmlNode* node;
|
|
|
|
if (blob)
|
|
{
|
|
node = PAR_blr(tdbb, relation, blob, blob_length, view_csb, &csb, statementPtr,
|
|
(type == obj_trigger && relation != NULL), 0);
|
|
}
|
|
else
|
|
{
|
|
node = MET_parse_blob(tdbb, relation, blob_id, &csb, statementPtr,
|
|
(type == obj_trigger && relation != NULL), type == obj_validation);
|
|
}
|
|
|
|
if (type == obj_computed)
|
|
{
|
|
MetaName domainName;
|
|
|
|
AutoRequest handle;
|
|
|
|
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()
|
|
{
|
|
domainName = FLD.RDB$FIELD_NAME;
|
|
}
|
|
END_FOR
|
|
|
|
MET_delete_dependencies(tdbb, domainName, type, transaction);
|
|
store_dependencies(tdbb, csb, relation, domainName, type, transaction);
|
|
}
|
|
else
|
|
{
|
|
MET_delete_dependencies(tdbb, object_name, type, transaction);
|
|
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(const 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
AutoRequest handle;
|
|
|
|
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
|
|
|
|
// 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
Database* dbb = tdbb->getDatabase();
|
|
CHECK_DBB(dbb);
|
|
|
|
if ((attachment->att_flags & ATT_no_db_triggers) ||
|
|
attachment->att_triggers[type] != NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
attachment->att_triggers[type] = FB_NEW_POOL(*attachment->att_pool)
|
|
trig_vec(*attachment->att_pool);
|
|
|
|
AutoRequest trigger_request;
|
|
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, &attachment->att_triggers[type]);
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
|
|
// Load DDL triggers from RDB$TRIGGERS.
|
|
void MET_load_ddl_triggers(thread_db* tdbb)
|
|
{
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
Database* dbb = tdbb->getDatabase();
|
|
CHECK_DBB(dbb);
|
|
|
|
if ((attachment->att_flags & ATT_no_db_triggers) ||
|
|
attachment->att_ddl_triggers != NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
attachment->att_ddl_triggers = FB_NEW_POOL(*attachment->att_pool)
|
|
trig_vec(*attachment->att_pool);
|
|
|
|
AutoRequest trigger_request;
|
|
|
|
FOR(REQUEST_HANDLE trigger_request)
|
|
TRG IN RDB$TRIGGERS
|
|
WITH TRG.RDB$RELATION_NAME MISSING AND
|
|
TRG.RDB$TRIGGER_INACTIVE EQ 0
|
|
SORTED BY TRG.RDB$TRIGGER_SEQUENCE
|
|
{
|
|
if ((TRG.RDB$TRIGGER_TYPE & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL)
|
|
{
|
|
MET_load_trigger(tdbb, NULL, TRG.RDB$TRIGGER_NAME,
|
|
&attachment->att_ddl_triggers);
|
|
}
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
|
|
void MET_load_trigger(thread_db* tdbb,
|
|
jrd_rel* relation,
|
|
const 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
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
|
|
// hvlad: GTT with ON COMMIT DELETE ROWS clause is writable
|
|
|
|
if (dbb->readOnly() && !(relation->rel_flags & REL_temp_tran))
|
|
return;
|
|
}
|
|
|
|
// Scan RDB$TRIGGERS next
|
|
|
|
AutoCacheRequest request(tdbb, irq_s_triggers, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
TRG IN RDB$TRIGGERS
|
|
WITH TRG.RDB$TRIGGER_NAME EQ trigger_name.c_str() AND
|
|
(TRG.RDB$TRIGGER_INACTIVE MISSING OR TRG.RDB$TRIGGER_INACTIVE EQ 0)
|
|
{
|
|
// 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;
|
|
}
|
|
|
|
bid debug_blob_id;
|
|
debug_blob_id.clear();
|
|
|
|
bid extBodyId;
|
|
extBodyId.clear();
|
|
|
|
if (!TRG.RDB$DEBUG_INFO.NULL) // ODS_11_1
|
|
debug_blob_id = TRG.RDB$DEBUG_INFO;
|
|
|
|
MetaName engine;
|
|
string entryPoint;
|
|
|
|
if (!TRG.RDB$ENGINE_NAME.NULL) // ODS_12_0
|
|
{
|
|
engine = TRG.RDB$ENGINE_NAME;
|
|
extBodyId = TRG.RDB$TRIGGER_SOURCE;
|
|
}
|
|
|
|
if (!TRG.RDB$ENTRYPOINT.NULL) // ODS_12_0
|
|
entryPoint = TRG.RDB$ENTRYPOINT;
|
|
|
|
if (TRG.RDB$RELATION_NAME.NULL)
|
|
{
|
|
if ((TRG.RDB$TRIGGER_TYPE & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB ||
|
|
(TRG.RDB$TRIGGER_TYPE & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL)
|
|
{
|
|
// this is a database trigger
|
|
get_trigger(tdbb,
|
|
relation,
|
|
&TRG.RDB$TRIGGER_BLR,
|
|
&debug_blob_id,
|
|
triggers,
|
|
TRG.RDB$TRIGGER_NAME,
|
|
TRG.RDB$TRIGGER_TYPE & ~TRIGGER_TYPE_MASK,
|
|
(bool) TRG.RDB$SYSTEM_FLAG,
|
|
trig_flags,
|
|
engine,
|
|
entryPoint,
|
|
&extBodyId);
|
|
}
|
|
}
|
|
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,
|
|
engine,
|
|
entryPoint,
|
|
&extBodyId);
|
|
}
|
|
}
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
|
|
|
|
void MET_lookup_cnstrt_for_index(thread_db* tdbb,
|
|
MetaName& constraint_name,
|
|
const 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
constraint_name = "";
|
|
AutoCacheRequest 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()
|
|
{
|
|
constraint_name = X.RDB$CONSTRAINT_NAME;
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
|
|
void MET_lookup_cnstrt_for_trigger(thread_db* tdbb,
|
|
MetaName& constraint_name,
|
|
MetaName& relation_name,
|
|
const 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
constraint_name = "";
|
|
relation_name = "";
|
|
AutoCacheRequest request(tdbb, irq_l_check, IRQ_REQUESTS);
|
|
AutoCacheRequest request2(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()
|
|
{
|
|
FOR(REQUEST_HANDLE request2)
|
|
X IN RDB$CHECK_CONSTRAINTS WITH
|
|
X.RDB$TRIGGER_NAME EQ Y.RDB$TRIGGER_NAME
|
|
{
|
|
constraint_name = X.RDB$CONSTRAINT_NAME;
|
|
}
|
|
END_FOR
|
|
|
|
relation_name = Y.RDB$RELATION_NAME;
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
|
|
void MET_lookup_exception(thread_db* tdbb,
|
|
SLONG number,
|
|
MetaName& name,
|
|
string* message)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
// We need to look up exception in RDB$EXCEPTIONS
|
|
|
|
AutoCacheRequest request(tdbb, irq_l_exception, IRQ_REQUESTS);
|
|
|
|
name = "";
|
|
if (message)
|
|
*message = "";
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$EXCEPTIONS WITH X.RDB$EXCEPTION_NUMBER = number
|
|
{
|
|
if (!X.RDB$EXCEPTION_NAME.NULL)
|
|
name = X.RDB$EXCEPTION_NAME;
|
|
|
|
if (!X.RDB$MESSAGE.NULL && message)
|
|
*message = X.RDB$MESSAGE;
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
|
|
bool MET_load_exception(thread_db* tdbb, ExceptionItem& item)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o a d _ e x c e p t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup exception by name and fill the passed instance.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
// We need to look up exception in RDB$EXCEPTIONS
|
|
|
|
AutoCacheRequest request(tdbb, irq_l_except_no, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$EXCEPTIONS WITH X.RDB$EXCEPTION_NAME = item.name.c_str()
|
|
{
|
|
item.type = ExceptionItem::XCP_CODE;
|
|
item.code = X.RDB$EXCEPTION_NUMBER;
|
|
item.secName = X.RDB$SECURITY_CLASS;
|
|
|
|
return true;
|
|
}
|
|
END_FOR
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
int MET_lookup_field(thread_db* tdbb, jrd_rel* relation, const MetaName& name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ f i e l d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up a field name.
|
|
*
|
|
* if the field is not found return -1
|
|
*
|
|
*****************************************/
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
// 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)
|
|
{
|
|
return id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Not found. Next, try system relations directly
|
|
|
|
int id = -1;
|
|
|
|
if (relation->rel_flags & REL_deleted)
|
|
return id;
|
|
|
|
AutoCacheRequest 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()
|
|
{
|
|
id = X.RDB$FIELD_ID;
|
|
}
|
|
END_FOR
|
|
|
|
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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
FPTR_BFILTER_CALLBACK filter = NULL;
|
|
BlobFilter* blf = NULL;
|
|
|
|
AutoCacheRequest 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
|
|
{
|
|
filter = (FPTR_BFILTER_CALLBACK)
|
|
Module::lookup(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT, dbb->dbb_modules);
|
|
if (filter)
|
|
{
|
|
blf = FB_NEW_POOL(*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
|
|
|
|
return blf;
|
|
}
|
|
|
|
|
|
bool MET_load_generator(thread_db* tdbb, GeneratorItem& item, bool* sysGen, SLONG* step)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o a d _ g e n e r a t o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup generator ID by its name and load its metadata into the passed object.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
if (item.name == MASTER_GENERATOR)
|
|
{
|
|
item.id = 0;
|
|
if (sysGen)
|
|
*sysGen = true;
|
|
if (step)
|
|
*step = 1;
|
|
return true;
|
|
}
|
|
|
|
AutoCacheRequest request(tdbb, irq_r_gen_id, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$GENERATORS WITH X.RDB$GENERATOR_NAME EQ item.name.c_str()
|
|
{
|
|
item.id = X.RDB$GENERATOR_ID;
|
|
item.secName = X.RDB$SECURITY_CLASS;
|
|
if (sysGen)
|
|
*sysGen = (X.RDB$SYSTEM_FLAG == fb_sysflag_system);
|
|
if (step)
|
|
*step = X.RDB$GENERATOR_INCREMENT;
|
|
|
|
return true;
|
|
}
|
|
END_FOR
|
|
|
|
return false;
|
|
}
|
|
|
|
SLONG MET_lookup_generator(thread_db* tdbb, const MetaName& name, bool* sysGen, SLONG* step)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ g e n e r a t o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup generator ID by its name.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
if (name == MASTER_GENERATOR)
|
|
{
|
|
if (sysGen)
|
|
*sysGen = true;
|
|
if (step)
|
|
*step = 1;
|
|
return 0;
|
|
}
|
|
|
|
AutoCacheRequest request(tdbb, irq_l_gen_id, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$GENERATORS WITH X.RDB$GENERATOR_NAME EQ name.c_str()
|
|
{
|
|
if (sysGen)
|
|
*sysGen = (X.RDB$SYSTEM_FLAG == fb_sysflag_system);
|
|
if (step)
|
|
*step = X.RDB$GENERATOR_INCREMENT;
|
|
|
|
return X.RDB$GENERATOR_ID;
|
|
}
|
|
END_FOR
|
|
|
|
return -1;
|
|
}
|
|
|
|
bool MET_lookup_generator_id(thread_db* tdbb, SLONG gen_id, MetaName& name, bool* sysGen)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
fb_assert(gen_id != 0);
|
|
|
|
name = "";
|
|
|
|
AutoCacheRequest 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 (sysGen)
|
|
*sysGen = (X.RDB$SYSTEM_FLAG == fb_sysflag_system);
|
|
|
|
name = X.RDB$GENERATOR_NAME;
|
|
}
|
|
END_FOR
|
|
|
|
return name.hasData();
|
|
}
|
|
|
|
void MET_update_generator_increment(thread_db* tdbb, SLONG gen_id, SLONG step)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ u p d a t e _ g e n e r a t o r _ i n c r e m e n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Update the step in a generator searched by ID.
|
|
* This function is for legacy code "SET GENERATOR TO value" only!
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
AutoCacheRequest request(tdbb, irq_upd_gen_id_increm, IRQ_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE request)
|
|
X IN RDB$GENERATORS WITH X.RDB$GENERATOR_ID EQ gen_id
|
|
|
|
// We never accept changing the step in sys gens.
|
|
if (X.RDB$SYSTEM_FLAG == fb_sysflag_system)
|
|
return;
|
|
|
|
MODIFY X
|
|
X.RDB$GENERATOR_INCREMENT = step;
|
|
END_MODIFY
|
|
END_FOR
|
|
}
|
|
|
|
|
|
void MET_lookup_index(thread_db* tdbb,
|
|
MetaName& index_name,
|
|
const 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
index_name = "";
|
|
|
|
AutoCacheRequest 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
|
|
{
|
|
index_name = X.RDB$INDEX_NAME;
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
|
|
SLONG MET_lookup_index_name(thread_db* tdbb,
|
|
const MetaName& index_name,
|
|
SLONG* relation_id, IndexStatus* 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
AutoCacheRequest 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 (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
|
|
|
|
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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
if (relation->rel_flags & REL_check_partners)
|
|
scan_partners(tdbb, relation);
|
|
|
|
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;
|
|
AutoRequest request;
|
|
|
|
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
|
|
{
|
|
//// ASF: Hack fix for CORE-4304, until nasty interactions between dfw and met are not resolved.
|
|
const jrd_rel* partner_relation = relation->rel_name == IND.RDB$RELATION_NAME ?
|
|
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;
|
|
fb_assert(idx->idx_primary_index != idx_invalid);
|
|
found = true;
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
return found;
|
|
}
|
|
|
|
frgn* references = &relation->rel_foreign_refs;
|
|
if (references->frgn_reference_ids)
|
|
{
|
|
for (unsigned int index_number = 0;
|
|
index_number < 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 (unsigned int index_number = 0;
|
|
index_number < 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 QualifiedName& 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
jrd_prc* check_procedure = NULL;
|
|
|
|
// See if we already know the procedure by name
|
|
for (jrd_prc** iter = attachment->att_procedures.begin(); iter != attachment->att_procedures.end(); ++iter)
|
|
{
|
|
jrd_prc* procedure = *iter;
|
|
|
|
if (procedure && !(procedure->flags & Routine::FLAG_OBSOLETE) &&
|
|
((procedure->flags & Routine::FLAG_SCANNED) || noscan) &&
|
|
!(procedure->flags & Routine::FLAG_BEING_SCANNED) &&
|
|
!(procedure->flags & Routine::FLAG_BEING_ALTERED))
|
|
{
|
|
if (procedure->getName() == name)
|
|
{
|
|
if (procedure->flags & Routine::FLAG_CHECK_EXISTENCE)
|
|
{
|
|
check_procedure = procedure;
|
|
LCK_lock(tdbb, check_procedure->existenceLock, LCK_SR, LCK_WAIT);
|
|
break;
|
|
}
|
|
|
|
return procedure;
|
|
}
|
|
}
|
|
}
|
|
|
|
// We need to look up the procedure name in RDB$PROCEDURES
|
|
|
|
jrd_prc* procedure = NULL;
|
|
|
|
AutoCacheRequest request(tdbb, irq_l_procedure, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
P IN RDB$PROCEDURES
|
|
WITH P.RDB$PROCEDURE_NAME EQ name.identifier.c_str() AND
|
|
P.RDB$PACKAGE_NAME EQUIV NULLIF(name.package.c_str(), '')
|
|
{
|
|
procedure = MET_procedure(tdbb, P.RDB$PROCEDURE_ID, noscan, 0);
|
|
}
|
|
END_FOR
|
|
|
|
if (check_procedure)
|
|
{
|
|
check_procedure->flags &= ~Routine::FLAG_CHECK_EXISTENCE;
|
|
if (check_procedure != procedure)
|
|
{
|
|
LCK_release(tdbb, check_procedure->existenceLock);
|
|
check_procedure->flags |= Routine::FLAG_OBSOLETE;
|
|
}
|
|
}
|
|
|
|
return procedure;
|
|
}
|
|
|
|
|
|
jrd_prc* MET_lookup_procedure_id(thread_db* tdbb, USHORT 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
jrd_prc* check_procedure = NULL;
|
|
|
|
jrd_prc* procedure;
|
|
|
|
if (id < (USHORT) attachment->att_procedures.getCount() && (procedure = attachment->att_procedures[id]) &&
|
|
procedure->getId() == id &&
|
|
!(procedure->flags & Routine::FLAG_BEING_SCANNED) &&
|
|
((procedure->flags & Routine::FLAG_SCANNED) || noscan) &&
|
|
!(procedure->flags & Routine::FLAG_BEING_ALTERED) &&
|
|
(!(procedure->flags & Routine::FLAG_OBSOLETE) || return_deleted))
|
|
{
|
|
if (procedure->flags & Routine::FLAG_CHECK_EXISTENCE)
|
|
{
|
|
check_procedure = procedure;
|
|
LCK_lock(tdbb, check_procedure->existenceLock, LCK_SR, LCK_WAIT);
|
|
}
|
|
else {
|
|
return procedure;
|
|
}
|
|
}
|
|
|
|
// We need to look up the procedure name in RDB$PROCEDURES
|
|
|
|
procedure = NULL;
|
|
|
|
AutoCacheRequest request(tdbb, irq_l_proc_id, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ id
|
|
{
|
|
procedure = MET_procedure(tdbb, P.RDB$PROCEDURE_ID, noscan, flags);
|
|
}
|
|
END_FOR
|
|
|
|
if (check_procedure)
|
|
{
|
|
check_procedure->flags &= ~Routine::FLAG_CHECK_EXISTENCE;
|
|
if (check_procedure != procedure)
|
|
{
|
|
LCK_release(tdbb, check_procedure->existenceLock);
|
|
check_procedure->flags |= Routine::FLAG_OBSOLETE;
|
|
}
|
|
}
|
|
|
|
return procedure;
|
|
}
|
|
|
|
|
|
jrd_rel* MET_lookup_relation(thread_db* tdbb, const 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
// See if we already know the relation by name
|
|
|
|
vec<jrd_rel*>* relations = attachment->att_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* const relation = *ptr;
|
|
|
|
if (relation)
|
|
{
|
|
if (relation->rel_flags & REL_deleting)
|
|
CheckoutLockGuard guard(tdbb, relation->rel_drop_mutex, FB_FUNCTION);
|
|
|
|
if (!(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;
|
|
|
|
AutoCacheRequest request(tdbb, irq_l_relation, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ name.c_str()
|
|
{
|
|
relation = MET_relation(tdbb, X.RDB$RELATION_ID);
|
|
if (relation->rel_name.length() == 0) {
|
|
relation->rel_name = name;
|
|
}
|
|
|
|
relation->rel_flags |= get_rel_flags_from_FLAGS(X.RDB$FLAGS);
|
|
|
|
if (!X.RDB$RELATION_TYPE.NULL)
|
|
{
|
|
relation->rel_flags |= MET_get_rel_flags_from_TYPE(X.RDB$RELATION_TYPE);
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
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);
|
|
LCK_release(tdbb, check_relation->rel_rescan_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);
|
|
Attachment* const attachment = tdbb->getAttachment();
|
|
|
|
// 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 = attachment->att_relations;
|
|
if (vector && (id < (SLONG) vector->count()) && (relation = (*vector)[id]))
|
|
{
|
|
if (relation->rel_flags & REL_deleting)
|
|
CheckoutLockGuard guard(tdbb, relation->rel_drop_mutex, FB_FUNCTION);
|
|
|
|
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;
|
|
|
|
AutoCacheRequest request(tdbb, irq_l_rel_id, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATIONS WITH X.RDB$RELATION_ID EQ id
|
|
{
|
|
relation = MET_relation(tdbb, X.RDB$RELATION_ID);
|
|
if (relation->rel_name.length() == 0) {
|
|
relation->rel_name = X.RDB$RELATION_NAME;
|
|
}
|
|
|
|
relation->rel_flags |= get_rel_flags_from_FLAGS(X.RDB$FLAGS);
|
|
|
|
if (!X.RDB$RELATION_TYPE.NULL)
|
|
{
|
|
relation->rel_flags |= MET_get_rel_flags_from_TYPE(X.RDB$RELATION_TYPE);
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
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);
|
|
LCK_release(tdbb, check_relation->rel_rescan_lock);
|
|
check_relation->rel_flags &= ~REL_check_partners;
|
|
check_relation->rel_flags |= REL_deleted;
|
|
}
|
|
}
|
|
|
|
return relation;
|
|
}
|
|
|
|
|
|
DmlNode* MET_parse_blob(thread_db* tdbb,
|
|
jrd_rel* relation,
|
|
bid* blob_id,
|
|
CompilerScratch** csb_ptr,
|
|
JrdStatement** statementPtr,
|
|
const bool trigger,
|
|
bool validationExpr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
blb* blob = blb::open(tdbb, attachment->getSysTransaction(), blob_id);
|
|
ULONG length = blob->blb_length + 10;
|
|
HalfStaticArray<UCHAR, 512> tmp;
|
|
UCHAR* temp = tmp.getBuffer(length);
|
|
length = blob->BLB_get_data(tdbb, temp, length);
|
|
|
|
DmlNode* node = NULL;
|
|
|
|
if (validationExpr)
|
|
{
|
|
// The set of MET parse functions needs a rework.
|
|
// For now, our caller chain is not interested in the returned node.
|
|
PAR_validation_blr(tdbb, relation, temp, length, NULL, csb_ptr, 0);
|
|
}
|
|
else
|
|
node = PAR_blr(tdbb, relation, temp, length, NULL, csb_ptr, statementPtr, 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
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
|
|
// hvlad: GTT with ON COMMIT DELETE ROWS clause is writable
|
|
|
|
if (dbb->readOnly() && !(relation->rel_flags & REL_temp_tran))
|
|
return;
|
|
|
|
relation->rel_flags |= REL_sys_trigs_being_loaded;
|
|
|
|
AutoCacheRequest request(tdbb, irq_s_triggers2, IRQ_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE request)
|
|
TRG IN RDB$TRIGGERS
|
|
WITH TRG.RDB$RELATION_NAME = relation->rel_name.c_str()
|
|
AND TRG.RDB$SYSTEM_FLAG = 1
|
|
{
|
|
const FB_UINT64 type = 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, attachment->getSysTransaction(), &TRG.RDB$TRIGGER_BLR);
|
|
ULONG length = blob->blb_length + 10;
|
|
HalfStaticArray<UCHAR, 128> blr;
|
|
length = blob->BLB_get_data(tdbb, 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;
|
|
|
|
JrdStatement* statement = NULL;
|
|
|
|
{
|
|
Jrd::ContextPoolHolder context(tdbb, attachment->createPool());
|
|
PAR_blr(tdbb, relation, blr.begin(), length, NULL, NULL, &statement, true, par_flags);
|
|
}
|
|
|
|
statement->triggerName = name;
|
|
|
|
statement->flags |= JrdStatement::FLAG_SYS_TRIGGER;
|
|
if (trig_flags & TRG_ignore_perm)
|
|
statement->flags |= JrdStatement::FLAG_IGNORE_PERM;
|
|
|
|
save_trigger_data(tdbb, ptr, relation, statement, NULL, NULL, NULL, type, true, 0, "",
|
|
"", NULL);
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
AutoCacheRequest 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, attachment->getSysTransaction(), &X.RDB$TRANSACTION_DESCRIPTION);
|
|
blob->BLB_put_segment(tdbb, msg, length);
|
|
blob->BLB_close(tdbb);
|
|
}
|
|
END_STORE
|
|
}
|
|
|
|
|
|
jrd_prc* MET_procedure(thread_db* tdbb, USHORT 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
Database* dbb = tdbb->getDatabase();
|
|
|
|
if (id >= attachment->att_procedures.getCount())
|
|
attachment->att_procedures.resize(id + 10);
|
|
|
|
jrd_prc* procedure = attachment->att_procedures[id];
|
|
|
|
if (procedure && !(procedure->flags & Routine::FLAG_OBSOLETE))
|
|
{
|
|
// Make sure PRC_being_scanned and PRC_scanned are not set at the same time
|
|
|
|
fb_assert(!(procedure->flags & Routine::FLAG_BEING_SCANNED) ||
|
|
!(procedure->flags & Routine::FLAG_SCANNED));
|
|
|
|
/* To avoid scanning recursive procedure's blr recursively let's
|
|
make use of Routine::FLAG_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
|
|
FLAG_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 FLAG_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->flags & Routine::FLAG_BEING_SCANNED) ||
|
|
(procedure->flags & Routine::FLAG_SCANNED))
|
|
{
|
|
return procedure;
|
|
}
|
|
}
|
|
|
|
if (!procedure)
|
|
{
|
|
procedure = FB_NEW_POOL(*attachment->att_pool) jrd_prc(*attachment->att_pool);
|
|
}
|
|
|
|
try {
|
|
|
|
procedure->flags |= (Routine::FLAG_BEING_SCANNED | flags);
|
|
procedure->flags &= ~Routine::FLAG_OBSOLETE;
|
|
|
|
procedure->setId(id);
|
|
attachment->att_procedures[id] = procedure;
|
|
|
|
if (!procedure->existenceLock)
|
|
{
|
|
Lock* lock = FB_NEW_RPT(*attachment->att_pool, 0)
|
|
Lock(tdbb, sizeof(SLONG), LCK_prc_exist, procedure, blocking_ast_procedure);
|
|
procedure->existenceLock = lock;
|
|
lock->lck_key.lck_long = procedure->getId();
|
|
}
|
|
|
|
LCK_lock(tdbb, procedure->existenceLock, LCK_SR, LCK_WAIT);
|
|
|
|
if (!noscan)
|
|
{
|
|
AutoCacheRequest request(tdbb, irq_r_procedure, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ procedure->getId()
|
|
{
|
|
if (procedure->getName().toString().length() == 0)
|
|
{
|
|
procedure->setName(QualifiedName(P.RDB$PROCEDURE_NAME,
|
|
(P.RDB$PACKAGE_NAME.NULL ? "" : P.RDB$PACKAGE_NAME)));
|
|
}
|
|
|
|
procedure->setId(P.RDB$PROCEDURE_ID);
|
|
|
|
if (!P.RDB$SECURITY_CLASS.NULL)
|
|
procedure->setSecurityName(P.RDB$SECURITY_CLASS);
|
|
else if (!P.RDB$PACKAGE_NAME.NULL)
|
|
{
|
|
AutoCacheRequest requestHandle(tdbb, irq_l_procedure_pkg_class, IRQ_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE requestHandle)
|
|
PKG IN RDB$PACKAGES
|
|
WITH PKG.RDB$PACKAGE_NAME EQ P.RDB$PACKAGE_NAME
|
|
{
|
|
if (!PKG.RDB$SECURITY_CLASS.NULL)
|
|
procedure->setSecurityName(PKG.RDB$SECURITY_CLASS);
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
procedure->setImplemented(true);
|
|
procedure->getInputFields().resize(P.RDB$PROCEDURE_INPUTS);
|
|
procedure->getOutputFields().resize(P.RDB$PROCEDURE_OUTPUTS);
|
|
procedure->setDefaultCount(0);
|
|
|
|
AutoCacheRequest request2(tdbb, irq_r_params, IRQ_REQUESTS);
|
|
|
|
const MetaName packageName(P.RDB$PACKAGE_NAME.NULL ? NULL : P.RDB$PACKAGE_NAME);
|
|
|
|
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 AND
|
|
PA.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '')
|
|
{
|
|
const SSHORT pa_collation_id_null = PA.RDB$COLLATION_ID.NULL;
|
|
const SSHORT pa_collation_id = PA.RDB$COLLATION_ID;
|
|
const SSHORT pa_default_value_null = PA.RDB$DEFAULT_VALUE.NULL;
|
|
bid pa_default_value = pa_default_value_null ? F.RDB$DEFAULT_VALUE : PA.RDB$DEFAULT_VALUE;
|
|
|
|
Array<NestConst<Parameter> >& paramArray = PA.RDB$PARAMETER_TYPE ?
|
|
procedure->getOutputFields() : procedure->getInputFields();
|
|
|
|
// should be error if field already exists
|
|
Parameter* parameter = FB_NEW_POOL(*attachment->att_pool) Parameter(*attachment->att_pool);
|
|
parameter->prm_number = PA.RDB$PARAMETER_NUMBER;
|
|
paramArray[parameter->prm_number] = parameter;
|
|
parameter->prm_name = PA.RDB$PARAMETER_NAME;
|
|
parameter->prm_nullable = PA.RDB$NULL_FLAG.NULL || PA.RDB$NULL_FLAG == 0; // ODS_11_1
|
|
parameter->prm_mechanism = PA.RDB$PARAMETER_MECHANISM.NULL ? // ODS_11_1
|
|
prm_mech_normal : (prm_mech_t) PA.RDB$PARAMETER_MECHANISM;
|
|
|
|
if (!PA.RDB$FIELD_SOURCE.NULL)
|
|
parameter->prm_field_source = PA.RDB$FIELD_SOURCE;
|
|
|
|
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->setDefaultCount(procedure->getDefaultCount() + 1);
|
|
MemoryPool* pool = attachment->createPool();
|
|
Jrd::ContextPoolHolder context(tdbb, pool);
|
|
|
|
try
|
|
{
|
|
parameter->prm_default_value = static_cast<ValueExprNode*>(
|
|
MET_parse_blob(tdbb, NULL, &pa_default_value, NULL, NULL, false, false));
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
// Here we lose pools created for previous defaults.
|
|
// Probably we should use common pool for defaults and procedure itself.
|
|
|
|
attachment->deletePool(pool);
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
const bool external = !P.RDB$ENGINE_NAME.NULL; // ODS_12_0
|
|
|
|
Array<NestConst<Parameter> >& paramArray = procedure->getOutputFields();
|
|
|
|
if (paramArray.hasData() && paramArray[0])
|
|
{
|
|
Format* format = Format::newFormat(
|
|
*attachment->att_pool, procedure->getOutputFields().getCount());
|
|
procedure->prc_record_format = format;
|
|
ULONG length = FLAG_BYTES(format->fmt_count);
|
|
Format::fmt_desc_iterator desc = format->fmt_desc.begin();
|
|
Array<NestConst<Parameter> >::iterator ptr, end;
|
|
for (ptr = paramArray.begin(), end = paramArray.end(); ptr < end; ++ptr, ++desc)
|
|
{
|
|
const 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(&(*desc), length);
|
|
desc->dsc_address = (UCHAR *) (IPTR) length;
|
|
length += desc->dsc_length;
|
|
}
|
|
}
|
|
|
|
format->fmt_length = length;
|
|
}
|
|
|
|
procedure->prc_type = P.RDB$PROCEDURE_TYPE.NULL ?
|
|
prc_legacy : (prc_t) P.RDB$PROCEDURE_TYPE;
|
|
|
|
if (external || !P.RDB$PROCEDURE_BLR.NULL)
|
|
{
|
|
MemoryPool* const csb_pool = attachment->createPool();
|
|
Jrd::ContextPoolHolder context(tdbb, csb_pool);
|
|
|
|
try
|
|
{
|
|
AutoPtr<CompilerScratch> csb(CompilerScratch::newCsb(*csb_pool, 5));
|
|
|
|
if (external)
|
|
{
|
|
HalfStaticArray<char, 512> body;
|
|
|
|
if (!P.RDB$PROCEDURE_SOURCE.NULL)
|
|
{
|
|
blb* blob = blb::open(tdbb, attachment->getSysTransaction(),
|
|
&P.RDB$PROCEDURE_SOURCE);
|
|
ULONG len = blob->BLB_get_data(tdbb,
|
|
(UCHAR*) body.getBuffer(blob->blb_length + 1), blob->blb_length + 1);
|
|
body.begin()[MIN(blob->blb_length, len)] = '\0';
|
|
}
|
|
else
|
|
body.getBuffer(1)[0] = '\0';
|
|
|
|
dbb->dbb_extManager.makeProcedure(tdbb, csb, procedure, P.RDB$ENGINE_NAME,
|
|
(P.RDB$ENTRYPOINT.NULL ? "" : P.RDB$ENTRYPOINT), body.begin());
|
|
}
|
|
else
|
|
{
|
|
if (!P.RDB$DEBUG_INFO.NULL)
|
|
DBG_parse_debug_info(tdbb, &P.RDB$DEBUG_INFO, *csb->csb_dbg_info);
|
|
|
|
try
|
|
{
|
|
procedure->parseBlr(tdbb, csb, &P.RDB$PROCEDURE_BLR);
|
|
}
|
|
catch (const Exception& ex)
|
|
{
|
|
StaticStatusVector temp_status;
|
|
ex.stuffException(temp_status);
|
|
const string name = procedure->getName().toString();
|
|
(Arg::Gds(isc_bad_proc_BLR) << Arg::Str(name)
|
|
<< Arg::StatusVector(temp_status.begin())).raise();
|
|
}
|
|
}
|
|
}
|
|
catch (const Exception&)
|
|
{
|
|
if (procedure->getStatement())
|
|
procedure->releaseStatement(tdbb);
|
|
else
|
|
attachment->deletePool(csb_pool);
|
|
|
|
throw;
|
|
}
|
|
|
|
procedure->getStatement()->procedure = procedure;
|
|
}
|
|
else
|
|
{
|
|
RefPtr<MsgMetadata> inputMetadata(REF_NO_INCR,
|
|
Routine::createMetadata(procedure->getInputFields()));
|
|
procedure->setInputFormat(
|
|
Routine::createFormat(procedure->getPool(), inputMetadata, false));
|
|
|
|
RefPtr<MsgMetadata> outputMetadata(REF_NO_INCR,
|
|
Routine::createMetadata(procedure->getOutputFields()));
|
|
procedure->setOutputFormat(
|
|
Routine::createFormat(procedure->getPool(), outputMetadata, true));
|
|
|
|
procedure->setImplemented(false);
|
|
}
|
|
|
|
procedure->flags |= Routine::FLAG_SCANNED;
|
|
|
|
if (!dbb->readOnly() &&
|
|
!P.RDB$PROCEDURE_BLR.NULL &&
|
|
!P.RDB$VALID_BLR.NULL && P.RDB$VALID_BLR == FALSE)
|
|
{
|
|
// If the BLR was marked as invalid but the procedure was compiled,
|
|
// mark the BLR as valid.
|
|
|
|
MODIFY P USING
|
|
P.RDB$VALID_BLR = TRUE;
|
|
P.RDB$VALID_BLR.NULL = FALSE;
|
|
END_MODIFY
|
|
}
|
|
}
|
|
END_FOR
|
|
} // if !noscan
|
|
|
|
// Make sure that it is really being scanned
|
|
fb_assert(procedure->flags & Routine::FLAG_BEING_SCANNED);
|
|
|
|
procedure->flags &= ~Routine::FLAG_BEING_SCANNED;
|
|
|
|
} // try
|
|
catch (const Exception&)
|
|
{
|
|
procedure->flags &= ~(Routine::FLAG_BEING_SCANNED | Routine::FLAG_SCANNED);
|
|
|
|
if (procedure->getExternal())
|
|
{
|
|
delete procedure->getExternal();
|
|
procedure->setExternal(NULL);
|
|
}
|
|
|
|
if (procedure->existenceLock)
|
|
{
|
|
LCK_release(tdbb, procedure->existenceLock);
|
|
delete procedure->existenceLock;
|
|
procedure->existenceLock = 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);
|
|
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
vec<jrd_rel*>* vector = attachment->att_relations;
|
|
MemoryPool* pool = attachment->att_pool;
|
|
|
|
if (!vector)
|
|
vector = attachment->att_relations = vec<jrd_rel*>::newVector(*pool, id + 10);
|
|
else if (id >= vector->count())
|
|
vector->resize(id + 10);
|
|
|
|
jrd_rel* relation = (*vector)[id];
|
|
if (relation)
|
|
return relation;
|
|
|
|
// From ODS 9 onwards, the first 128 relation IDS have been
|
|
// reserved for system relations
|
|
const USHORT max_sys_rel = USER_DEF_REL_INIT_ID - 1;
|
|
|
|
relation = FB_NEW_POOL(*pool) jrd_rel(*pool);
|
|
(*vector)[id] = relation;
|
|
relation->rel_id = id;
|
|
|
|
{ // Scope block.
|
|
Lock* lock = FB_NEW_RPT(*pool, 0)
|
|
Lock(tdbb, sizeof(SLONG), LCK_rel_partners, relation, partners_ast_relation);
|
|
relation->rel_partners_lock = lock;
|
|
lock->lck_key.lck_long = relation->rel_id;
|
|
}
|
|
|
|
{ // Scope block.
|
|
Lock* lock = FB_NEW_RPT(*pool, 0)
|
|
Lock(tdbb, sizeof(SLONG), LCK_rel_rescan, relation, rescan_ast_relation);
|
|
relation->rel_rescan_lock = lock;
|
|
lock->lck_key.lck_long = relation->rel_id;
|
|
}
|
|
|
|
// This should check system flag instead.
|
|
if (relation->rel_id <= max_sys_rel)
|
|
return relation;
|
|
|
|
{ // Scope block.
|
|
Lock* lock = FB_NEW_RPT(*pool, 0)
|
|
Lock(tdbb, sizeof(SLONG), LCK_rel_exist, relation, blocking_ast_relation);
|
|
relation->rel_existence_lock = lock;
|
|
lock->lck_key.lck_long = relation->rel_id;
|
|
}
|
|
|
|
relation->rel_flags |= (REL_check_existence | REL_check_partners);
|
|
return relation;
|
|
}
|
|
|
|
|
|
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_revoke(thread_db* tdbb, jrd_tra* transaction, const MetaName& relation,
|
|
const MetaName& revokee, const string& 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);
|
|
|
|
// See if the revokee still has the privilege. If so, there's nothing to do
|
|
|
|
USHORT count = 0;
|
|
|
|
AutoCacheRequest 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.c_str() AND
|
|
P.RDB$PRIVILEGE EQ privilege.c_str() AND
|
|
P.RDB$USER EQ revokee.c_str()
|
|
{
|
|
++count;
|
|
}
|
|
END_FOR
|
|
|
|
if (count)
|
|
return;
|
|
|
|
request.reset(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.c_str() AND
|
|
P.RDB$PRIVILEGE EQ privilege.c_str() AND
|
|
P.RDB$GRANTOR EQ revokee.c_str()
|
|
{
|
|
ERASE P;
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
|
|
void MET_scan_partners(thread_db* tdbb, jrd_rel* relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ s c a n _ p a r t n e r s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Scan of foreign references on other relations' primary keys and
|
|
* scan of primary dependencies on relation's primary key.
|
|
*
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
if (relation->rel_flags & REL_check_partners)
|
|
scan_partners(tdbb, relation);
|
|
}
|
|
|
|
|
|
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];
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
Database* dbb = tdbb->getDatabase();
|
|
Jrd::ContextPoolHolder context(tdbb, attachment->att_pool);
|
|
bool dependencies = false;
|
|
bool sys_triggers = false;
|
|
|
|
blb* blob = NULL;
|
|
|
|
jrd_tra* depTrans = tdbb->getTransaction() ?
|
|
tdbb->getTransaction() : attachment->getSysTransaction();
|
|
|
|
// 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 {
|
|
|
|
if (relation->rel_flags & (REL_scanned | 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
|
|
|
|
AutoCacheRequest 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
|
|
relation->rel_current_fmt = REL.RDB$FORMAT;
|
|
vec<jrd_fld*>* vector = relation->rel_fields =
|
|
vec<jrd_fld*>::newVector(*relation->rel_pool, 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$VIEW_BLR.isEmpty())
|
|
{
|
|
// parse the view blr, getting dependencies on relations, etc. at the same time
|
|
|
|
DmlNode* rseNode;
|
|
|
|
if (dependencies)
|
|
{
|
|
const MetaName depName(REL.RDB$RELATION_NAME);
|
|
rseNode = MET_get_dependencies(tdbb, relation, NULL, 0, NULL, &REL.RDB$VIEW_BLR,
|
|
NULL, &csb, depName, obj_view, 0, depTrans);
|
|
}
|
|
else
|
|
rseNode = MET_parse_blob(tdbb, relation, &REL.RDB$VIEW_BLR, &csb, NULL, false, false);
|
|
|
|
if (rseNode)
|
|
{
|
|
fb_assert(rseNode->kind == DmlNode::KIND_REC_SOURCE);
|
|
relation->rel_view_rse = static_cast<RseNode*>(rseNode);
|
|
fb_assert(relation->rel_view_rse->type == RseNode::TYPE);
|
|
}
|
|
else
|
|
relation->rel_view_rse = NULL;
|
|
|
|
// 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);
|
|
}
|
|
|
|
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);
|
|
fb_assert(relation->rel_flags & REL_jrd_view);
|
|
relation->rel_flags |= REL_jrd_view;
|
|
break;
|
|
case rel_virtual:
|
|
fb_assert(relation->rel_flags & REL_virtual);
|
|
relation->rel_flags |= REL_virtual;
|
|
break;
|
|
case rel_global_temp_preserve:
|
|
fb_assert(relation->rel_flags & REL_temp_conn);
|
|
relation->rel_flags |= REL_temp_conn;
|
|
break;
|
|
case rel_global_temp_delete:
|
|
fb_assert(relation->rel_flags & REL_temp_tran);
|
|
relation->rel_flags |= REL_temp_tran;
|
|
break;
|
|
default:
|
|
fb_assert(false);
|
|
}
|
|
}
|
|
|
|
// Pick up field specific stuff
|
|
|
|
blob = blb::open(tdbb, attachment->getSysTransaction(), &REL.RDB$RUNTIME);
|
|
HalfStaticArray<UCHAR, 256> temp;
|
|
UCHAR* const buffer = temp.getBuffer(blob->getMaxSegment() + 1U);
|
|
|
|
jrd_fld* field = NULL;
|
|
ArrayField* array = 0;
|
|
USHORT view_context = 0;
|
|
USHORT field_id = 0;
|
|
for (;;)
|
|
{
|
|
USHORT length = blob->BLB_get_segment(tdbb, buffer, blob->getMaxSegment());
|
|
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_POOL(*relation->rel_pool) jrd_fld(*relation->rel_pool);
|
|
(*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 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:
|
|
{
|
|
DmlNode* nod = dependencies ?
|
|
MET_get_dependencies(tdbb, relation, p, length, csb, NULL, NULL, NULL,
|
|
field->fld_name, obj_computed, 0, depTrans) :
|
|
PAR_blr(tdbb, relation, p, length, csb, NULL, NULL, false, 0);
|
|
|
|
field->fld_computation = static_cast<ValueExprNode*>(nod);
|
|
}
|
|
break;
|
|
|
|
case RSR_missing_value:
|
|
field->fld_missing_value = static_cast<ValueExprNode*>(
|
|
PAR_blr(tdbb, relation, p, length, csb, NULL, NULL, false, 0));
|
|
break;
|
|
|
|
case RSR_default_value:
|
|
field->fld_default_value = static_cast<ValueExprNode*>(
|
|
PAR_blr(tdbb, relation, p, length, 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_validation_blr(tdbb, relation, p, length, csb,
|
|
NULL, csb_validation);
|
|
}
|
|
break;
|
|
|
|
case RSR_field_not_null:
|
|
field->fld_not_null = PAR_validation_blr(tdbb, relation, p, length, csb,
|
|
NULL, 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(*relation->rel_pool, n) ArrayField();
|
|
array->arr_desc.iad_dimensions = n;
|
|
break;
|
|
|
|
case RSR_array_desc:
|
|
if (array)
|
|
memcpy(&array->arr_desc, p, length);
|
|
break;
|
|
|
|
case RSR_field_generator_name:
|
|
field->fld_generator_name = (const TEXT*) p;
|
|
break;
|
|
|
|
default: // Shut up compiler warning
|
|
break;
|
|
}
|
|
}
|
|
blob->BLB_close(tdbb);
|
|
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
|
|
|
|
delete csb;
|
|
|
|
// 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);
|
|
}
|
|
|
|
LCK_lock(tdbb, relation->rel_rescan_lock, LCK_SR, LCK_WAIT);
|
|
relation->rel_flags &= ~REL_being_scanned;
|
|
|
|
relation->rel_current_format = NULL;
|
|
|
|
} // try
|
|
catch (const 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)
|
|
blob->BLB_close(tdbb);
|
|
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
void MET_trigger_msg(thread_db* tdbb, string& msg, const 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
AutoCacheRequest 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
|
|
{
|
|
msg = MSG.RDB$MESSAGE;
|
|
}
|
|
END_FOR
|
|
|
|
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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
AutoRequest handle;
|
|
|
|
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
|
|
}
|
|
|
|
|
|
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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
AutoCacheRequest 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 (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
|
|
}
|
|
|
|
|
|
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* const item = static_cast<DSqlCacheItem*>(ast_object);
|
|
|
|
try
|
|
{
|
|
Database* const dbb = item->lock->lck_dbb;
|
|
|
|
AsyncContextHolder tdbb(dbb, FB_FUNCTION, item->lock);
|
|
|
|
item->obsolete = true;
|
|
item->locked = false;
|
|
LCK_release(tdbb, item->lock);
|
|
}
|
|
catch (const Exception&)
|
|
{} // no-op
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, int type, const QualifiedName& name)
|
|
{
|
|
Database* dbb = tdbb->getDatabase();
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
string key((char*) &type, sizeof(type));
|
|
int len = name.identifier.length();
|
|
key.append((char*) &len, sizeof(len));
|
|
key.append(name.identifier.c_str(), len);
|
|
|
|
len = name.package.length();
|
|
key.append((char*) &len, sizeof(len));
|
|
key.append(name.package.c_str(), len);
|
|
|
|
DSqlCacheItem* item = attachment->att_dsql_cache.put(key);
|
|
if (item)
|
|
{
|
|
item->obsolete = false;
|
|
item->locked = false;
|
|
item->lock = FB_NEW_RPT(*attachment->att_pool, key.length())
|
|
Lock(tdbb, key.length(), LCK_dsql_cache, item, blocking_ast_dsql_cache);
|
|
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* const procedure = static_cast<jrd_prc*>(ast_object);
|
|
|
|
try
|
|
{
|
|
if (procedure->existenceLock)
|
|
{
|
|
Database* const dbb = procedure->existenceLock->lck_dbb;
|
|
|
|
AsyncContextHolder tdbb(dbb, FB_FUNCTION, procedure->existenceLock);
|
|
|
|
LCK_release(tdbb, procedure->existenceLock);
|
|
}
|
|
procedure->flags |= Routine::FLAG_OBSOLETE;
|
|
}
|
|
catch (const 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* const relation = static_cast<jrd_rel*>(ast_object);
|
|
|
|
try
|
|
{
|
|
if (relation->rel_existence_lock)
|
|
{
|
|
Database* const dbb = relation->rel_existence_lock->lck_dbb;
|
|
|
|
AsyncContextHolder tdbb(dbb, FB_FUNCTION, relation->rel_existence_lock);
|
|
|
|
if (relation->rel_use_count)
|
|
relation->rel_flags |= REL_blocking;
|
|
else
|
|
{
|
|
relation->rel_flags &= ~REL_blocking;
|
|
relation->rel_flags |= REL_check_existence;
|
|
LCK_release(tdbb, relation->rel_existence_lock);
|
|
}
|
|
}
|
|
}
|
|
catch (const Exception&)
|
|
{} // no-op
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int partners_ast_relation(void* ast_object)
|
|
{
|
|
jrd_rel* const relation = static_cast<jrd_rel*>(ast_object);
|
|
|
|
try
|
|
{
|
|
Database* const dbb = relation->rel_partners_lock->lck_dbb;
|
|
|
|
AsyncContextHolder tdbb(dbb, FB_FUNCTION, relation->rel_partners_lock);
|
|
|
|
LCK_release(tdbb, relation->rel_partners_lock);
|
|
relation->rel_flags |= REL_check_partners;
|
|
}
|
|
catch (const Exception&)
|
|
{} // no-op
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int rescan_ast_relation(void* ast_object)
|
|
{
|
|
jrd_rel* const relation = static_cast<jrd_rel*>(ast_object);
|
|
|
|
try
|
|
{
|
|
Database* const dbb = relation->rel_rescan_lock->lck_dbb;
|
|
|
|
AsyncContextHolder tdbb(dbb, FB_FUNCTION, relation->rel_rescan_lock);
|
|
|
|
LCK_release(tdbb, relation->rel_rescan_lock);
|
|
relation->rel_flags &= ~REL_scanned;
|
|
}
|
|
catch (const Firebird::Exception&)
|
|
{} // no-op
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static ULONG get_rel_flags_from_FLAGS(USHORT flags)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e t _ r e l _ f l a g s _ f r o m _ F L A G S
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get rel_flags from RDB$FLAGS
|
|
*
|
|
**************************************/
|
|
ULONG ret = 0;
|
|
|
|
if (flags & REL_sql) {
|
|
ret |= REL_sql_relation;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
ULONG MET_get_rel_flags_from_TYPE(USHORT type)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _g e t _ r e l _ f l a g s _ f r o m _ T Y P E
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get rel_flags from RDB$RELATION_TYPE
|
|
*
|
|
**************************************/
|
|
ULONG ret = 0;
|
|
|
|
switch (type)
|
|
{
|
|
case rel_persistent:
|
|
break;
|
|
case rel_external:
|
|
break;
|
|
case rel_view:
|
|
ret |= REL_jrd_view;
|
|
break;
|
|
case rel_virtual:
|
|
ret |= REL_virtual;
|
|
break;
|
|
case rel_global_temp_preserve:
|
|
ret |= REL_temp_conn;
|
|
break;
|
|
case rel_global_temp_delete:
|
|
ret |= REL_temp_tran;
|
|
break;
|
|
default:
|
|
fb_assert(false);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
static void get_trigger(thread_db* tdbb, jrd_rel* relation,
|
|
bid* blob_id, bid* debug_blob_id, trig_vec** ptr,
|
|
const TEXT* name, FB_UINT64 type,
|
|
bool sys_trigger, USHORT flags,
|
|
const MetaName& engine, const string& entryPoint,
|
|
const bid* body)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e t _ t r i g g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get trigger.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
if (blob_id->isEmpty() && (engine.isEmpty() || entryPoint.isEmpty()))
|
|
return;
|
|
|
|
blb* blrBlob = NULL;
|
|
blb* debugInfoBlob = NULL;
|
|
|
|
if (!blob_id->isEmpty())
|
|
blrBlob = blb::open(tdbb, attachment->getSysTransaction(), blob_id);
|
|
|
|
if (!debug_blob_id->isEmpty())
|
|
debugInfoBlob = blb::open(tdbb, attachment->getSysTransaction(), debug_blob_id);
|
|
|
|
save_trigger_data(tdbb, ptr, relation, NULL, blrBlob, debugInfoBlob,
|
|
name, type, sys_trigger, flags, engine, entryPoint, body);
|
|
}
|
|
|
|
|
|
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[MAX_SQL_IDENTIFIER_SIZE]; // BASED ON RDB$TYPE_NAME
|
|
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
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;
|
|
|
|
AutoRequest handle;
|
|
|
|
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
|
|
|
|
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 sorted
|
|
* array on the relation block.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
Database* dbb = tdbb->getDatabase();
|
|
AutoCacheRequest 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
|
|
{
|
|
// trim trailing spaces
|
|
fb_utils::exact_name_limit(V.RDB$CONTEXT_NAME, sizeof(V.RDB$CONTEXT_NAME));
|
|
|
|
ViewContext* view_context = FB_NEW_POOL(*view->rel_pool)
|
|
ViewContext(*view->rel_pool,
|
|
V.RDB$CONTEXT_NAME, V.RDB$RELATION_NAME, V.RDB$VIEW_CONTEXT,
|
|
(V.RDB$CONTEXT_TYPE.NULL ? VCT_TABLE : ViewContextType(V.RDB$CONTEXT_TYPE)));
|
|
|
|
view->rel_view_contexts.add(view_context);
|
|
}
|
|
END_FOR
|
|
}
|
|
|
|
|
|
static void make_relation_scope_name(const TEXT* rel_name, const USHORT rel_flags,
|
|
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);
|
|
}
|
|
|
|
|
|
// Parses default BLR for a field.
|
|
static ValueExprNode* parse_field_default_blr(thread_db* tdbb, bid* blob_id)
|
|
{
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5);
|
|
|
|
blb* blob = blb::open(tdbb, attachment->getSysTransaction(), blob_id);
|
|
ULONG length = blob->blb_length + 10;
|
|
HalfStaticArray<UCHAR, 512> temp;
|
|
|
|
length = blob->BLB_get_data(tdbb, temp.getBuffer(length), length);
|
|
|
|
DmlNode* node = PAR_blr(tdbb, NULL, temp.begin(), length, NULL, &csb, NULL, false, 0);
|
|
|
|
csb->csb_blr_reader = BlrReader();
|
|
delete csb;
|
|
|
|
return static_cast<ValueExprNode*>(node);
|
|
}
|
|
|
|
|
|
// Parses validation BLR for a field.
|
|
static BoolExprNode* parse_field_validation_blr(thread_db* tdbb, bid* blob_id, const MetaName name)
|
|
{
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5, name);
|
|
|
|
blb* blob = blb::open(tdbb, attachment->getSysTransaction(), blob_id);
|
|
ULONG length = blob->blb_length + 10;
|
|
HalfStaticArray<UCHAR, 512> temp;
|
|
|
|
length = blob->BLB_get_data(tdbb, temp.getBuffer(length), length);
|
|
|
|
BoolExprNode* expr = PAR_validation_blr(tdbb, NULL,
|
|
temp.begin(), length, NULL, &csb, 0);
|
|
|
|
csb->csb_blr_reader = BlrReader();
|
|
delete csb;
|
|
|
|
return expr;
|
|
}
|
|
|
|
|
|
void MET_release_trigger(thread_db* tdbb, trig_vec** vector_ptr, const 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 (FB_SIZE_T i = 0; i < vector.getCount(); ++i)
|
|
{
|
|
if (vector[i].name == name)
|
|
{
|
|
JrdStatement* stmt = vector[i].statement;
|
|
if (stmt)
|
|
{
|
|
if (stmt->isActive())
|
|
break;
|
|
stmt->release(tdbb);
|
|
}
|
|
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;
|
|
|
|
for (FB_SIZE_T i = 0; i < vector->getCount(); i++)
|
|
{
|
|
JrdStatement* stmt = (*vector)[i].statement;
|
|
if (stmt && stmt->isActive())
|
|
return;
|
|
}
|
|
|
|
for (FB_SIZE_T i = 0; i < vector->getCount(); i++)
|
|
{
|
|
JrdStatement* stmt = (*vector)[i].statement;
|
|
if (stmt)
|
|
stmt->release(tdbb);
|
|
|
|
delete (*vector)[i].extTrigger;
|
|
}
|
|
|
|
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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
fb_assert(id != NULL);
|
|
|
|
AutoRequest handle;
|
|
|
|
if (!collation)
|
|
{
|
|
if (charset == NULL)
|
|
charset = (const UCHAR*) DEFAULT_CHARACTER_SET_NAME;
|
|
|
|
if (attachment->att_charset_ids.get((const TEXT*) charset, *id))
|
|
return true;
|
|
|
|
USHORT charset_id = 0;
|
|
if (get_type(tdbb, &charset_id, charset, "RDB$CHARACTER_SET_NAME"))
|
|
{
|
|
attachment->att_charset_ids.put((const TEXT*) charset, charset_id);
|
|
*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;
|
|
attachment->att_charset_ids.put((const TEXT*) charset, CS.RDB$CHARACTER_SET_ID);
|
|
*id = CS.RDB$CHARACTER_SET_ID;
|
|
}
|
|
END_FOR
|
|
|
|
return found;
|
|
}
|
|
|
|
if (!charset)
|
|
{
|
|
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
|
|
|
|
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;
|
|
attachment->att_charset_ids.put((const TEXT*) charset, CS.RDB$CHARACTER_SET_ID);
|
|
*id = CS.RDB$CHARACTER_SET_ID | (COL.RDB$COLLATION_ID << 8);
|
|
}
|
|
END_FOR
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
static void save_trigger_data(thread_db* tdbb, trig_vec** ptr, jrd_rel* relation,
|
|
JrdStatement* statement, blb* blrBlob, blb* debugInfoBlob,
|
|
const TEXT* name, FB_UINT64 type,
|
|
bool sys_trigger, USHORT flags,
|
|
const MetaName& engine, const string& entryPoint,
|
|
const bid* body)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s a v e _ t r i g g e r _ d a t a
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Save trigger data to passed vector
|
|
*
|
|
**************************************/
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
trig_vec* vector = *ptr;
|
|
|
|
if (!vector)
|
|
{
|
|
MemoryPool* pool = relation ? relation->rel_pool : attachment->att_pool;
|
|
vector = FB_NEW_POOL(*pool) trig_vec(*pool);
|
|
*ptr = vector;
|
|
}
|
|
|
|
Trigger& t = vector->add();
|
|
|
|
if (blrBlob)
|
|
{
|
|
const ULONG length = blrBlob->blb_length + 10;
|
|
UCHAR* const data = t.blr.getBuffer(length);
|
|
t.blr.resize(blrBlob->BLB_get_data(tdbb, data, length));
|
|
}
|
|
|
|
if (debugInfoBlob)
|
|
{
|
|
const ULONG length = debugInfoBlob->blb_length + 10;
|
|
UCHAR* const data = t.debugInfo.getBuffer(length);
|
|
t.debugInfo.resize(debugInfoBlob->BLB_get_data(tdbb, data, length));
|
|
}
|
|
|
|
if (name)
|
|
t.name = name;
|
|
|
|
if (body)
|
|
{
|
|
blb* bodyBlob = blb::open(tdbb, attachment->getSysTransaction(), body);
|
|
|
|
HalfStaticArray<char, 512> temp;
|
|
ULONG length = bodyBlob->BLB_get_data(tdbb, (UCHAR*) temp.getBuffer(bodyBlob->blb_length),
|
|
bodyBlob->blb_length);
|
|
|
|
t.extBody.assign(temp.begin(), length);
|
|
}
|
|
|
|
t.type = type;
|
|
t.flags = flags;
|
|
t.compile_in_progress = false;
|
|
t.sys_trigger = sys_trigger;
|
|
t.statement = statement;
|
|
t.relation = relation;
|
|
t.engine = engine;
|
|
t.entryPoint = entryPoint;
|
|
}
|
|
|
|
|
|
const Trigger* findTrigger(trig_vec* triggers, const 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;
|
|
}
|
|
|
|
|
|
void scan_partners(thread_db* tdbb, jrd_rel* relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s c a n _ p a r t n e r s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Scan of foreign references on other relations' primary keys and
|
|
* scan of primary dependencies on relation's primary key.
|
|
*
|
|
**************************************/
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
AutoCacheRequest 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
|
|
{
|
|
//// ASF: Hack fix for CORE-4304, until nasty interactions between dfw and met are not resolved.
|
|
const jrd_rel* partner_relation = relation->rel_name == IND.RDB$RELATION_NAME ?
|
|
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(*relation->rel_pool, references->frgn_reference_ids,
|
|
index_number + 1);
|
|
|
|
(*references->frgn_reference_ids)[index_number] = IDX.RDB$INDEX_ID - 1;
|
|
|
|
references->frgn_relations =
|
|
vec<int>::newVector(*relation->rel_pool, references->frgn_relations,
|
|
index_number + 1);
|
|
|
|
(*references->frgn_relations)[index_number] = partner_relation->rel_id;
|
|
|
|
references->frgn_indexes =
|
|
vec<int>::newVector(*relation->rel_pool, references->frgn_indexes,
|
|
index_number + 1);
|
|
|
|
(*references->frgn_indexes)[index_number] = IND.RDB$INDEX_ID - 1;
|
|
|
|
index_number++;
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
// Prepare for rescan of primary dependencies on relation's primary key and stale vectors.
|
|
|
|
request.reset(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
|
|
{
|
|
//// ASF: Hack fix for CORE-4304, until nasty interactions between dfw and met are not resolved.
|
|
const jrd_rel* partner_relation = relation->rel_name == IND.RDB$RELATION_NAME ?
|
|
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(*relation->rel_pool, dependencies->prim_reference_ids,
|
|
index_number + 1);
|
|
|
|
(*dependencies->prim_reference_ids)[index_number] = IDX.RDB$INDEX_ID - 1;
|
|
|
|
dependencies->prim_relations =
|
|
vec<int>::newVector(*relation->rel_pool, dependencies->prim_relations,
|
|
index_number + 1);
|
|
|
|
(*dependencies->prim_relations)[index_number] = partner_relation->rel_id;
|
|
|
|
dependencies->prim_indexes =
|
|
vec<int>::newVector(*relation->rel_pool, dependencies->prim_indexes,
|
|
index_number + 1);
|
|
|
|
(*dependencies->prim_indexes)[index_number] = IND.RDB$INDEX_ID - 1;
|
|
|
|
index_number++;
|
|
}
|
|
}
|
|
END_FOR
|
|
|
|
LCK_lock(tdbb, relation->rel_partners_lock, LCK_SR, LCK_WAIT);
|
|
relation->rel_flags &= ~REL_check_partners;
|
|
}
|
|
|
|
|
|
static void store_dependencies(thread_db* tdbb,
|
|
CompilerScratch* csb,
|
|
const jrd_rel* dep_rel,
|
|
const 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.
|
|
*
|
|
**************************************/
|
|
MetaName name;
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
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())
|
|
{
|
|
CompilerScratch::Dependency dependency = csb->csb_dependencies.pop();
|
|
|
|
if (!dependency.relation && !dependency.function && !dependency.procedure &&
|
|
!dependency.name && !dependency.number)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int dpdo_type = dependency.objType;
|
|
jrd_rel* relation = NULL;
|
|
const jrd_prc* procedure = NULL;
|
|
const MetaName* dpdo_name = NULL;
|
|
MetaName packageName;
|
|
SubtypeInfo info;
|
|
|
|
switch (dpdo_type)
|
|
{
|
|
case obj_relation:
|
|
relation = dependency.relation;
|
|
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)
|
|
))
|
|
{
|
|
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 = dependency.procedure;
|
|
dpdo_name = &procedure->getName().identifier;
|
|
packageName = procedure->getName().package;
|
|
break;
|
|
case obj_collation:
|
|
{
|
|
const USHORT number = dependency.number;
|
|
MET_get_char_coll_subtype_info(tdbb, number, &info);
|
|
dpdo_name = &info.collationName;
|
|
}
|
|
break;
|
|
case obj_exception:
|
|
{
|
|
const SLONG number = dependency.number;
|
|
MET_lookup_exception(tdbb, number, name, NULL);
|
|
dpdo_name = &name;
|
|
}
|
|
break;
|
|
case obj_field:
|
|
dpdo_name = dependency.name;
|
|
break;
|
|
case obj_generator:
|
|
{
|
|
// CVC: Here I'm going to track those pesky things named generators and UDFs.
|
|
// But don't track sys gens.
|
|
bool sysGen = false;
|
|
const SLONG number = dependency.number;
|
|
if (number == 0 || !MET_lookup_generator_id(tdbb, number, name, &sysGen) || sysGen)
|
|
continue;
|
|
dpdo_name = &name;
|
|
}
|
|
break;
|
|
case obj_udf:
|
|
{
|
|
const Function* const udf = dependency.function;
|
|
dpdo_name = &udf->getName().identifier;
|
|
packageName = udf->getName().package;
|
|
}
|
|
break;
|
|
case obj_index:
|
|
name = *dependency.name;
|
|
dpdo_name = &name;
|
|
break;
|
|
}
|
|
|
|
MetaName field_name;
|
|
|
|
if (dependency.subNumber || dependency.subName)
|
|
{
|
|
if (dependency.subNumber)
|
|
{
|
|
const SSHORT fld_id = (SSHORT) dependency.subNumber;
|
|
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->getOutputFields()[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 = *dependency.subName;
|
|
}
|
|
|
|
if (field_name.hasData())
|
|
{
|
|
AutoCacheRequest 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
|
|
{
|
|
found = true;
|
|
}
|
|
END_FOR
|
|
|
|
if (found)
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
AutoCacheRequest 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 AND
|
|
X.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '')
|
|
{
|
|
found = true;
|
|
}
|
|
END_FOR
|
|
|
|
if (found)
|
|
continue;
|
|
}
|
|
|
|
AutoCacheRequest 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.hasData())
|
|
{
|
|
DEP.RDB$FIELD_NAME.NULL = FALSE;
|
|
strcpy(DEP.RDB$FIELD_NAME, field_name.c_str());
|
|
}
|
|
else
|
|
DEP.RDB$FIELD_NAME.NULL = TRUE;
|
|
|
|
if (packageName.hasData())
|
|
{
|
|
DEP.RDB$PACKAGE_NAME.NULL = FALSE;
|
|
strcpy(DEP.RDB$PACKAGE_NAME, packageName.c_str());
|
|
}
|
|
else
|
|
DEP.RDB$PACKAGE_NAME.NULL = TRUE;
|
|
|
|
DEP.RDB$DEPENDENT_TYPE = dependency_type;
|
|
}
|
|
END_STORE
|
|
}
|
|
}
|
|
|
|
|
|
static bool verify_TRG_ignore_perm(thread_db* tdbb, const 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);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
|
|
// 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
|
|
|
|
AutoCacheRequest 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
|
|
{
|
|
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
|
|
|
|
return false;
|
|
}
|
|
|
|
int MET_get_linger(thread_db* tdbb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ l i n g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Return linger value for current database
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
Attachment* attachment = tdbb->getAttachment();
|
|
int rc = 0;
|
|
|
|
AutoCacheRequest request(tdbb, irq_linger, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
DAT IN RDB$DATABASE
|
|
{
|
|
if (!DAT.RDB$LINGER.NULL)
|
|
rc = DAT.RDB$LINGER;
|
|
}
|
|
END_FOR
|
|
|
|
return rc;
|
|
}
|