8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-30 19:23:03 +01:00
firebird-mirror/src/jrd/met.epp

5455 lines
142 KiB
Plaintext
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
* MODULE: met.epp
2001-05-23 15:26:42 +02:00
* 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): ______________________________________.
*
2002-07-01 18:59:09 +02:00
* 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-30 07:40:58 +01:00
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
2005-05-28 00:45:31 +02:00
* 2004.01.16 Vlad Horsun: added support for default parameters
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
2004-04-29 00:43:34 +02:00
#include <stdio.h>
2001-05-23 15:26:42 +02:00
#include <string.h>
#include "../jrd/common.h"
#include <stdarg.h>
2003-11-08 17:40:17 +01:00
#include "../jrd/ibase.h"
2001-05-23 15:26:42 +02:00
#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"
2003-07-14 12:35:49 +02:00
#include "../jrd/os/pio.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/sdw.h"
#include "../jrd/flags.h"
#include "../jrd/lls.h"
#include "../jrd/intl.h"
#include "../jrd/align.h"
#include "../jrd/flu.h"
#include "../jrd/blob_filter.h"
#include "../intl/charsets.h"
#include "../jrd/gdsassert.h"
#include "../jrd/blb_proto.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/dfw_proto.h"
#include "../jrd/dsc_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/exe_proto.h"
#include "../jrd/ext_proto.h"
#include "../jrd/flu_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/idx_proto.h"
#include "../jrd/ini_proto.h"
#include "../jrd/lck_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/par_proto.h"
#include "../jrd/pcmet_proto.h"
2003-07-14 12:35:49 +02:00
#include "../jrd/os/pio_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/scl_proto.h"
#include "../jrd/sdw_proto.h"
#include "../jrd/thread_proto.h"
2003-12-31 06:36:12 +01:00
#include "../common/utils_proto.h"
#include "../jrd/PreparedStatement.h"
#include "../jrd/DebugInterface.h"
#include "../common/classes/MsgPrint.h"
2001-05-23 15:26:42 +02:00
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif
2009-11-21 04:23:47 +01:00
// Pick up relation ids
#include "../jrd/ini.h"
2001-05-23 15:26:42 +02:00
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 void get_trigger(thread_db*, jrd_rel*, bid*, bid*, trig_vec**, const TEXT*, FB_UINT64, bool,
USHORT, const Firebird::MetaName&, const Firebird::string&, const bid*);
2006-04-05 18:34:18 +02:00
static bool get_type(thread_db*, USHORT*, const UCHAR*, const TEXT*);
static void lookup_view_contexts(thread_db*, jrd_rel*);
2006-05-24 22:43:07 +02:00
static void make_relation_scope_name(const TEXT*, const USHORT, Firebird::string& str);
2008-01-16 10:29:37 +01:00
static jrd_nod* parse_field_blr(thread_db* tdbb, bid* blob_id, const Firebird::MetaName name = Firebird::MetaName());
static jrd_nod* parse_param_blr(thread_db*, jrd_prc*, bid*, CompilerScratch*);
static jrd_nod* parse_procedure_blr(thread_db*, jrd_prc*, bid*, CompilerScratch*, bool);
2009-04-28 15:08:04 +02:00
static void par_messages(thread_db*, const UCHAR* const, USHORT, jrd_prc*, CompilerScratch*);
2008-12-18 11:57:12 +01:00
static bool resolve_charset_and_collation(thread_db*, USHORT*, const UCHAR*, const UCHAR*);
static void save_trigger_data(thread_db*, trig_vec**, jrd_rel*, jrd_req*, blb*, bid*,
const TEXT*, FB_UINT64, bool, USHORT, const Firebird::MetaName&, const Firebird::string&,
const bid*);
static void store_dependencies(thread_db*, CompilerScratch*, const jrd_rel*,
const Firebird::MetaName&, int, jrd_tra*);
static bool verify_TRG_ignore_perm(thread_db*, const Firebird::MetaName&);
2001-05-23 15:26:42 +02:00
// Decompile all triggers from vector
static void release_cached_triggers(thread_db* tdbb, trig_vec* vector)
{
2005-05-28 00:45:31 +02:00
if (!vector)
2004-04-19 17:29:29 +02:00
{
2003-12-22 11:00:59 +01:00
return;
}
2005-05-28 00:45:31 +02:00
for (size_t i = 0; i < vector->getCount(); i++)
2004-04-19 17:29:29 +02:00
{
(*vector)[i].release(tdbb);
}
}
2004-05-09 07:48:33 +02:00
static void inc_int_use_count(jrd_req* req)
{
// Increment int_use_count for all procedures in resource list of request
2004-05-09 07:48:33 +02:00
ResourceList& list = req->req_resources;
size_t i;
for (list.find(Resource(Resource::rsc_procedure, 0, NULL, NULL, NULL), i);
2008-12-18 11:57:12 +01:00
i < list.getCount(); i++)
{
2004-05-09 07:48:33 +02:00
Resource& resource = list[i];
2005-05-28 00:45:31 +02:00
if (resource.rsc_type != Resource::rsc_procedure)
break;
fb_assert(resource.rsc_prc->prc_int_use_count >= 0);
++resource.rsc_prc->prc_int_use_count;
}
}
2004-05-09 07:48:33 +02:00
// Increment int_use_count for all procedures used by triggers
2004-04-19 17:29:29 +02:00
static void post_used_procedures(trig_vec* vector)
{
2005-05-28 00:45:31 +02:00
if (!vector)
2004-04-19 17:29:29 +02:00
{
2003-12-22 11:00:59 +01:00
return;
}
2005-05-28 00:45:31 +02:00
for (size_t i = 0; i < vector->getCount(); i++)
2004-04-19 17:29:29 +02:00
{
jrd_req* r = (*vector)[i].request;
if (r && !CMP_clone_is_active(r))
{
inc_int_use_count(r);
}
}
}
2004-05-09 07:48:33 +02:00
void MET_get_domain(thread_db* tdbb, const Firebird::MetaName& name, dsc* desc, FieldInfo* fieldInfo)
{
/**************************************
*
* M E T _ g e t _ d o m a i n
*
**************************************
*
* Functional description
* Get domain descriptor and informations.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
bool found = false;
jrd_req* handle = CMP_find_request(tdbb, irq_l_domain, IRQ_REQUESTS);
FOR(REQUEST_HANDLE handle)
FLD IN RDB$FIELDS WITH FLD.RDB$FIELD_NAME EQ name.c_str()
if (!REQUEST(irq_l_domain))
REQUEST(irq_l_domain) = handle;
if (DSC_make_descriptor(desc,
FLD.RDB$FIELD_TYPE,
FLD.RDB$FIELD_SCALE,
FLD.RDB$FIELD_LENGTH,
FLD.RDB$FIELD_SUB_TYPE,
FLD.RDB$CHARACTER_SET_ID,
FLD.RDB$COLLATION_ID))
{
2008-01-16 10:29:37 +01:00
found = true;
if (fieldInfo)
{
fieldInfo->nullable = FLD.RDB$NULL_FLAG.NULL || FLD.RDB$NULL_FLAG == 0;
2008-01-29 11:11:52 +01:00
MemoryPool* csb_pool = dbb->createPool();
Jrd::ContextPoolHolder context(tdbb, csb_pool);
try
{
if (FLD.RDB$DEFAULT_VALUE.NULL)
fieldInfo->defaultValue = NULL;
else
2008-01-16 10:29:37 +01:00
fieldInfo->defaultValue = parse_field_blr(tdbb, &FLD.RDB$DEFAULT_VALUE);
2008-01-16 10:29:37 +01:00
if (FLD.RDB$VALIDATION_BLR.NULL)
fieldInfo->validation = NULL;
else
fieldInfo->validation = parse_field_blr(tdbb, &FLD.RDB$VALIDATION_BLR, name);
}
catch (const Firebird::Exception&)
{
2008-01-29 11:11:52 +01:00
dbb->deletePool(csb_pool);
2008-01-16 10:29:37 +01:00
throw;
}
}
}
2008-01-16 10:29:37 +01:00
END_FOR;
2008-01-16 10:29:37 +01:00
if (!REQUEST(irq_l_domain))
REQUEST(irq_l_domain) = handle;
2008-01-16 10:29:37 +01:00
if (!found)
{
ERR_post(Arg::Gds(isc_domnotdef) << Arg::Str(name));
2008-01-16 10:29:37 +01:00
}
}
Firebird::MetaName MET_get_relation_field(thread_db* tdbb,
const Firebird::MetaName& relationName,
const Firebird::MetaName& fieldName,
dsc* desc,
FieldInfo* fieldInfo)
2008-01-16 10:29:37 +01:00
{
/**************************************
*
* 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);
2008-01-16 13:22:11 +01:00
Database* dbb = tdbb->getDatabase();
2008-01-16 10:29:37 +01:00
bool found = false;
Firebird::MetaName sourceName;
2008-01-16 10:29:37 +01:00
jrd_req* handle = CMP_find_request(tdbb, irq_l_relfield, IRQ_REQUESTS);
2008-01-16 10:29:37 +01:00
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
2008-01-16 10:29:37 +01:00
if (!REQUEST(irq_l_relfield))
REQUEST(irq_l_relfield) = handle;
if (DSC_make_descriptor(desc,
FLD.RDB$FIELD_TYPE,
FLD.RDB$FIELD_SCALE,
FLD.RDB$FIELD_LENGTH,
FLD.RDB$FIELD_SUB_TYPE,
FLD.RDB$CHARACTER_SET_ID,
FLD.RDB$COLLATION_ID))
{
found = true;
sourceName = RFL.RDB$FIELD_SOURCE;
if (fieldInfo)
{
fieldInfo->nullable = RFL.RDB$NULL_FLAG.NULL ?
2008-12-18 11:57:12 +01:00
(FLD.RDB$NULL_FLAG.NULL || FLD.RDB$NULL_FLAG == 0) : RFL.RDB$NULL_FLAG == 0;
2008-01-16 10:29:37 +01:00
2008-01-29 11:11:52 +01:00
MemoryPool* csb_pool = dbb->createPool();
2008-01-16 10:29:37 +01:00
Jrd::ContextPoolHolder context(tdbb, csb_pool);
try
{
bid* defaultId = NULL;
if (!RFL.RDB$DEFAULT_VALUE.NULL)
defaultId = &RFL.RDB$DEFAULT_VALUE;
else if (!FLD.RDB$DEFAULT_VALUE.NULL)
defaultId = &FLD.RDB$DEFAULT_VALUE;
if (defaultId)
fieldInfo->defaultValue = parse_field_blr(tdbb, defaultId);
else
fieldInfo->defaultValue = NULL;
if (!FLD.RDB$VALIDATION_BLR.NULL)
fieldInfo->validation = parse_field_blr(tdbb, &FLD.RDB$VALIDATION_BLR, RFL.RDB$FIELD_SOURCE);
else
fieldInfo->validation = NULL;
}
catch (const Firebird::Exception&)
{
2008-01-29 11:11:52 +01:00
dbb->deletePool(csb_pool);
throw;
}
}
}
END_FOR;
2008-01-16 10:29:37 +01:00
if (!REQUEST(irq_l_relfield))
REQUEST(irq_l_relfield) = handle;
if (!found)
{
ERR_post(Arg::Gds(isc_dyn_column_does_not_exist) << Arg::Str(fieldName) <<
Arg::Str(relationName));
}
2008-01-16 10:29:37 +01:00
return sourceName;
}
void MET_update_partners(thread_db* tdbb)
{
/**************************************
*
* M E T _ u p d a t e _ p a r t n e r s
*
**************************************
*
* Functional description
* Mark all relations to update their links to FK partners
* Called when any index is deleted because engine don't know
* was it used in any FK or not
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
vec<jrd_rel*>* relations = dbb->dbb_relations;
vec<jrd_rel*>::iterator ptr = relations->begin();
2006-02-10 04:28:43 +01:00
for (const vec<jrd_rel*>::const_iterator end = relations->end(); ptr < end; ++ptr)
{
jrd_rel* relation = *ptr;
if (!relation) {
2003-12-22 11:00:59 +01:00
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;
}
}
2004-05-09 07:48:33 +02:00
void adjust_dependencies(jrd_prc* procedure)
{
2009-11-07 12:58:54 +01:00
if (procedure->prc_int_use_count == -1)
{
// Already processed
return;
}
2003-12-22 11:00:59 +01:00
procedure->prc_int_use_count = -1; // Mark as undeletable
2009-11-07 12:58:54 +01:00
if (procedure->prc_request)
{
// Loop over procedures from resource list of prc_request
2004-05-09 07:48:33 +02:00
ResourceList& list = procedure->prc_request->req_resources;
size_t i;
for (list.find(Resource(Resource::rsc_procedure, 0, NULL, NULL, NULL), i);
2008-12-18 11:57:12 +01:00
i < list.getCount(); i++)
{
2004-05-09 07:48:33 +02:00
Resource& resource = list[i];
2005-05-28 00:45:31 +02:00
if (resource.rsc_type != Resource::rsc_procedure)
break;
procedure = resource.rsc_prc;
2009-11-07 12:58:54 +01:00
if (procedure->prc_int_use_count == procedure->prc_use_count)
{
// Mark it and all dependent procedures as undeletable
2005-05-28 00:45:31 +02:00
adjust_dependencies(procedure);
}
}
}
}
2004-05-09 07:48:33 +02:00
#ifdef DEV_BUILD
void MET_verify_cache(thread_db* tdbb)
{
/**************************************
*
* M E T _ v e r i f y _ c a c h e
*
**************************************
*
* Functional description
* Check if all links between procedures are properly counted
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2005-05-28 00:45:31 +02:00
vec<jrd_prc*>* procedures = dbb->dbb_procedures;
2009-11-07 12:58:54 +01:00
if (procedures)
{
2003-12-22 11:00:59 +01:00
jrd_prc* procedure;
vec<jrd_prc*>::iterator ptr, end;
2005-05-28 00:45:31 +02:00
2008-12-18 11:57:12 +01:00
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ++ptr)
{
if ( (procedure = *ptr) && procedure->prc_request /*&&
2005-05-28 00:45:31 +02:00
!(procedure->prc_flags & PRC_obsolete)*/ )
{
2003-11-04 00:59:24 +01:00
fb_assert(procedure->prc_int_use_count == 0);
}
}
2005-05-28 00:45:31 +02:00
2009-11-21 04:23:47 +01:00
// Walk procedures and calculate internal dependencies
2008-12-18 11:57:12 +01:00
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
{
if ( (procedure = *ptr) && procedure->prc_request /*&&
2005-05-28 00:45:31 +02:00
!(procedure->prc_flags & PRC_obsolete)*/ )
{
inc_int_use_count(procedure->prc_request);
}
}
2005-05-28 00:45:31 +02:00
2009-11-21 04:23:47 +01:00
// Walk procedures again and check dependencies
2008-12-18 11:57:12 +01:00
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
{
if ( (procedure = *ptr) && procedure->prc_request && /*
!(procedure->prc_flags & PRC_obsolete) && */
procedure->prc_use_count < procedure->prc_int_use_count)
{
char buffer[1024], *buf = buffer;
2005-05-28 00:45:31 +02:00
buf += sprintf(buf, "Procedure %d:%s is not properly counted (use count=%d, prc use=%d). Used by: \n",
procedure->prc_id, procedure->prc_name.toString().c_str(), procedure->prc_use_count, procedure->prc_int_use_count);
vec<jrd_prc*>::const_iterator ptr2 = procedures->begin();
for (const vec<jrd_prc*>::const_iterator end2 = procedures->end();
2006-02-10 04:28:43 +01:00
ptr2 < end2; ++ptr2)
{
const jrd_prc* prc = *ptr2;
2008-12-18 11:57:12 +01:00
if (prc && prc->prc_request /*&& !(prc->prc_flags & PRC_obsolete)*/ )
{
// Loop over procedures from resource list of prc_request
const ResourceList& list = prc->prc_request->req_resources;
size_t i;
for (list.find(Resource(Resource::rsc_procedure, 0, NULL, NULL, NULL), i);
2005-05-28 00:45:31 +02:00
i < list.getCount(); i++)
{
const Resource& resource = list[i];
2005-05-28 00:45:31 +02:00
if (resource.rsc_type != Resource::rsc_procedure)
break;
if (resource.rsc_prc == procedure) {
buf += sprintf(buf, "%d:%s\n", prc->prc_id, prc->prc_name.toString().c_str());
}
}
}
}
gds__log(buffer);
2003-11-04 00:59:24 +01:00
fb_assert(false);
}
}
2005-05-28 00:45:31 +02:00
2009-11-21 04:23:47 +01:00
// Fix back int_use_count
2008-12-18 11:57:12 +01:00
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
{
if ( (procedure = *ptr) )
2005-05-28 00:45:31 +02:00
{
procedure->prc_int_use_count = 0;
}
2005-05-28 00:45:31 +02:00
}
}
}
#endif
2004-05-09 07:48:33 +02:00
2004-04-10 21:46:48 +02:00
void MET_clear_cache(thread_db* tdbb)
{
/**************************************
*
* M E T _ c l e a r _ c a c h e
*
**************************************
*
* Functional description
2005-05-28 00:45:31 +02:00
* Remove all unused objects from metadata cache to
2004-04-10 21:46:48 +02:00
* release resources they use
*
**************************************/
SET_TDBB(tdbb);
#ifdef DEV_BUILD
MET_verify_cache(tdbb);
#endif
2003-12-22 11:00:59 +01:00
Database* dbb = tdbb->getDatabase();
2003-12-22 11:00:59 +01:00
for (int i = 0; i < DB_TRIGGER_MAX; i++) {
release_cached_triggers(tdbb, dbb->dbb_triggers[i]);
}
vec<jrd_rel*>* relations = dbb->dbb_relations;
2009-03-15 21:09:15 +01:00
if (relations)
{ // scope
vec<jrd_rel*>::iterator ptr, end;
2006-02-10 04:28:43 +01:00
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
2005-05-28 00:45:31 +02:00
vec<jrd_prc*>* procedures = dbb->dbb_procedures;
if (procedures)
{
2003-12-22 11:00:59 +01:00
jrd_prc* procedure;
vec<jrd_prc*>::iterator ptr, end;
2009-11-21 04:23:47 +01:00
// Walk procedures and calculate internal dependencies
2008-12-18 11:57:12 +01:00
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ++ptr)
{
if ( (procedure = *ptr) && procedure->prc_request &&
2005-05-28 00:45:31 +02:00
!(procedure->prc_flags & PRC_obsolete) )
{
inc_int_use_count(procedure->prc_request);
}
}
2005-05-28 00:45:31 +02:00
// Walk procedures again and adjust dependencies for procedures
// which will not be removed.
2008-12-18 11:57:12 +01:00
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
{
if ( (procedure = *ptr) && procedure->prc_request &&
2008-12-18 11:57:12 +01:00
!(procedure->prc_flags & PRC_obsolete) &&
procedure->prc_use_count != procedure->prc_int_use_count )
{
adjust_dependencies(procedure);
}
}
2005-05-28 00:45:31 +02:00
2009-11-21 04:23:47 +01:00
// Deallocate all used requests
2008-12-18 11:57:12 +01:00
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
{
if ( (procedure = *ptr) )
{
2005-05-28 00:45:31 +02:00
2008-12-18 11:57:12 +01:00
if ( procedure->prc_request && !(procedure->prc_flags & PRC_obsolete) &&
2005-05-28 00:45:31 +02:00
procedure->prc_int_use_count >= 0 &&
procedure->prc_use_count == procedure->prc_int_use_count )
{
CMP_release(tdbb, procedure->prc_request);
procedure->prc_request = NULL;
if (procedure->prc_existence_lock) //// TODO: verify why this IF is necessary now
LCK_release(tdbb, procedure->prc_existence_lock);
procedure->prc_existence_lock = NULL;
procedure->prc_flags |= PRC_obsolete;
}
// Leave it in state 0 to avoid extra pass next time to clear it
// Note: we need to adjust prc_int_use_count for all procedures
// in cache because any of them may have been affected from
// dependencies earlier. Even procedures that were not scanned yet !
procedure->prc_int_use_count = 0;
}
}
2005-05-28 00:45:31 +02:00
2009-11-21 04:23:47 +01:00
// Remove deallocated procedures from cache
2008-12-18 11:57:12 +01:00
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
{
2008-12-18 11:57:12 +01:00
if ( (procedure = *ptr) && (procedure->prc_flags & PRC_obsolete) )
{
2005-05-28 00:45:31 +02:00
MET_remove_procedure(tdbb, procedure->prc_id, procedure);
}
}
2005-05-28 00:45:31 +02:00
}
#ifdef DEV_BUILD
MET_verify_cache(tdbb);
#endif
}
2004-05-09 07:48:33 +02:00
bool MET_procedure_in_use(thread_db* tdbb, jrd_prc* proc)
{
/**************************************
*
* M E T _ p r o c e d u r e _ i n _ u s e
*
**************************************
*
* Functional description
2005-05-28 00:45:31 +02:00
* Determine if procedure is used by any user requests or transactions.
* Return false if procedure is used only inside cache or not used at all.
*
**************************************/
SET_TDBB(tdbb);
#ifdef DEV_BUILD
MET_verify_cache(tdbb);
#endif
Database* dbb = tdbb->getDatabase();
2005-05-28 00:45:31 +02:00
// This should not really happen
vec<jrd_prc*>* procedures = dbb->dbb_procedures;
if (!procedures) {
return false;
}
vec<jrd_rel*>* relations = dbb->dbb_relations;
{ // scope
vec<jrd_rel*>::iterator ptr, end;
2006-02-10 04:28:43 +01:00
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
2003-12-22 11:00:59 +01:00
jrd_prc* procedure;
vec<jrd_prc*>::iterator ptr, end;
2009-11-21 04:23:47 +01:00
// Walk procedures and calculate internal dependencies
2008-12-18 11:57:12 +01:00
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ++ptr)
{
2008-12-18 11:57:12 +01:00
if ((procedure = *ptr) && procedure->prc_request && !(procedure->prc_flags & PRC_obsolete))
{
inc_int_use_count(procedure->prc_request);
}
}
2005-05-28 00:45:31 +02:00
// Walk procedures again and adjust dependencies for procedures
// which will not be removed.
2008-12-18 11:57:12 +01:00
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
{
if ( (procedure = *ptr) && procedure->prc_request &&
2008-12-18 11:57:12 +01:00
!(procedure->prc_flags & PRC_obsolete) &&
procedure->prc_use_count != procedure->prc_int_use_count && procedure != proc )
{
adjust_dependencies(procedure);
}
}
2005-05-28 00:45:31 +02:00
const bool result = proc->prc_use_count != proc->prc_int_use_count;
2005-05-28 00:45:31 +02:00
2009-11-21 04:23:47 +01:00
// Fix back int_use_count
2008-12-18 11:57:12 +01:00
for (ptr = procedures->begin(), end = procedures->end(); ptr < end; ptr++)
{
if ( (procedure = *ptr) )
2005-05-28 00:45:31 +02:00
{
procedure->prc_int_use_count = 0;
}
}
#ifdef DEV_BUILD
MET_verify_cache(tdbb);
#endif
return result;
}
2001-12-24 03:51:06 +01:00
void MET_activate_shadow(thread_db* tdbb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ a c t i v a t e _ s h a d o w
*
**************************************
*
* Functional description
* Activate the current database, which presumably
* was formerly a shadow, by deleting all records
* corresponding to the shadow that this database
* represents.
* Get rid of write ahead log for the activated shadow.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// Erase any secondary files of the primary database of the shadow being activated.
2001-05-23 15:26:42 +02:00
jrd_req* handle = NULL;
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE handle) X IN RDB$FILES
WITH X.RDB$SHADOW_NUMBER NOT MISSING
2005-05-28 00:45:31 +02:00
AND X.RDB$SHADOW_NUMBER EQ 0
2008-12-18 11:57:12 +01:00
ERASE X;
2001-05-23 15:26:42 +02:00
END_FOR;
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, handle);
2001-05-23 15:26:42 +02:00
2006-05-22 00:07:35 +02:00
PageSpace* pageSpace = dbb->dbb_page_manager.findPageSpace(DB_PAGE_SPACE);
const char* dbb_file_name = pageSpace->file->fil_string;
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// go through files looking for any that expand to the current database name
2003-12-22 11:00:59 +01:00
SCHAR expanded_name[MAXPATHLEN];
jrd_req* handle2 = handle = NULL;
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE handle) X IN RDB$FILES
WITH X.RDB$SHADOW_NUMBER NOT MISSING
AND X.RDB$SHADOW_NUMBER NE 0
2008-12-18 11:57:12 +01:00
PIO_expand(X.RDB$FILE_NAME, (USHORT)strlen(X.RDB$FILE_NAME),
expanded_name, sizeof(expanded_name));
2001-05-23 15:26:42 +02:00
2009-11-07 12:58:54 +01:00
if (!strcmp(expanded_name, dbb_file_name))
{
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE handle2) Y IN RDB$FILES
WITH X.RDB$SHADOW_NUMBER EQ Y.RDB$SHADOW_NUMBER
MODIFY Y
Y.RDB$SHADOW_NUMBER = 0;
END_MODIFY;
END_FOR;
ERASE X;
}
END_FOR;
if (handle2)
{
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, handle2);
2001-05-23 15:26:42 +02:00
}
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, handle);
2001-05-23 15:26:42 +02:00
}
2009-11-27 07:59:21 +01:00
ULONG MET_align(const dsc* desc, ULONG value)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ a l i g n
*
**************************************
*
* Functional description
* Align value (presumed offset) on appropriate border
* and return.
*
**************************************/
2003-12-22 11:00:59 +01:00
USHORT alignment = desc->dsc_length;
2009-01-20 09:33:59 +01:00
switch (desc->dsc_dtype)
{
2001-05-23 15:26:42 +02:00
case dtype_text:
case dtype_cstring:
return value;
case dtype_varying:
alignment = sizeof(USHORT);
break;
}
alignment = MIN(alignment, FORMAT_ALIGNMENT);
2001-05-23 15:26:42 +02:00
return FB_ALIGN(value, alignment);
}
2006-07-12 03:38:41 +02:00
DeferredWork* MET_change_fields(thread_db* tdbb, jrd_tra* transaction, const dsc* field_source)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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.
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
dsc relation_name;
DeferredWork* dw = 0;
jrd_req* request = CMP_find_request(tdbb, irq_m_fields, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
X IN RDB$RELATION_FIELDS WITH
2002-12-22 14:08:50 +01:00
X.RDB$FIELD_SOURCE EQ field_source->dsc_address
2001-05-23 15:26:42 +02:00
if (!REQUEST(irq_m_fields))
2003-12-22 11:00:59 +01:00
REQUEST(irq_m_fields) = request;
2001-05-23 15:26:42 +02:00
relation_name.dsc_dtype = dtype_text;
INTL_ASSIGN_DSC(&relation_name, CS_METADATA, COLLATE_NONE);
relation_name.dsc_length = sizeof(X.RDB$RELATION_NAME);
relation_name.dsc_address = (UCHAR *) X.RDB$RELATION_NAME;
SCL_check_relation(tdbb, &relation_name, SCL_control);
dw = DFW_post_work(transaction, dfw_update_format, &relation_name, 0);
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_m_fields))
REQUEST(irq_m_fields) = request;
request = CMP_find_request(tdbb, irq_m_fields2, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
DEP IN RDB$DEPENDENCIES CROSS
PRC IN RDB$PROCEDURES
WITH DEP.RDB$DEPENDED_ON_NAME EQ field_source->dsc_address AND
DEP.RDB$DEPENDED_ON_TYPE EQ obj_field AND
DEP.RDB$DEPENDENT_TYPE EQ obj_procedure AND
DEP.RDB$DEPENDENT_NAME EQ PRC.RDB$PROCEDURE_NAME AND
PRC.RDB$PACKAGE_NAME MISSING
if (!REQUEST(irq_m_fields2))
REQUEST(irq_m_fields2) = request;
Firebird::MetaName proc_name(PRC.RDB$PROCEDURE_NAME);
dsc desc;
desc.dsc_dtype = dtype_text;
INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE);
desc.dsc_length = proc_name.length();
desc.dsc_address = (UCHAR*) proc_name.c_str();
DeferredWork* dw2 =
DFW_post_work(transaction, dfw_modify_procedure, &desc, PRC.RDB$PROCEDURE_ID);
DFW_post_work_arg(transaction, dw2, NULL, 0, dfw_arg_check_blr);
END_FOR;
if (!REQUEST(irq_m_fields2))
REQUEST(irq_m_fields2) = request;
request = CMP_find_request(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
if (!REQUEST(irq_m_fields6))
REQUEST(irq_m_fields6) = request;
Firebird::MetaName proc_name(PRC.RDB$PROCEDURE_NAME);
dsc desc;
desc.dsc_dtype = dtype_text;
INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE);
desc.dsc_length = proc_name.length();
desc.dsc_address = (UCHAR*) proc_name.c_str();
DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_procedure, &desc,
PRC.RDB$PROCEDURE_ID, PRC.RDB$PACKAGE_NAME);
DFW_post_work_arg(transaction, dw2, NULL, 0, dfw_arg_check_blr);
END_FOR;
if (!REQUEST(irq_m_fields6))
REQUEST(irq_m_fields6) = request;
request = CMP_find_request(tdbb, irq_m_fields3, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
DEP IN RDB$DEPENDENCIES CROSS
TRG IN RDB$TRIGGERS
WITH DEP.RDB$DEPENDED_ON_NAME EQ field_source->dsc_address AND
DEP.RDB$DEPENDED_ON_TYPE EQ obj_field AND
DEP.RDB$DEPENDENT_TYPE EQ obj_trigger AND
DEP.RDB$DEPENDENT_NAME EQ TRG.RDB$TRIGGER_NAME
if (!REQUEST(irq_m_fields3))
REQUEST(irq_m_fields3) = request;
2008-01-16 10:29:37 +01:00
Firebird::MetaName trigger_name(TRG.RDB$TRIGGER_NAME);
Firebird::MetaName trigger_relation_name(TRG.RDB$RELATION_NAME);
2008-01-16 10:29:37 +01:00
dsc desc;
desc.dsc_dtype = dtype_text;
INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE);
desc.dsc_length = trigger_name.length();
desc.dsc_address = (UCHAR*) trigger_name.c_str();
DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_trigger, &desc, 0);
DFW_post_work_arg(transaction, dw2, NULL, TRG.RDB$TRIGGER_TYPE, dfw_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;
if (!REQUEST(irq_m_fields3))
REQUEST(irq_m_fields3) = request;
request = CMP_find_request(tdbb, irq_m_fields4, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
RFL IN RDB$RELATION_FIELDS CROSS
DEP IN RDB$DEPENDENCIES CROSS
PRC IN RDB$PROCEDURES
WITH RFL.RDB$FIELD_SOURCE EQ field_source->dsc_address AND
DEP.RDB$DEPENDED_ON_NAME EQ RFL.RDB$RELATION_NAME AND
DEP.RDB$FIELD_NAME EQ RFL.RDB$FIELD_NAME AND
DEP.RDB$DEPENDED_ON_TYPE EQ obj_relation AND
DEP.RDB$DEPENDENT_TYPE EQ obj_procedure AND
DEP.RDB$DEPENDENT_NAME EQ PRC.RDB$PROCEDURE_NAME AND
PRC.RDB$PACKAGE_NAME MISSING
2008-01-16 10:29:37 +01:00
if (!REQUEST(irq_m_fields4))
REQUEST(irq_m_fields4) = request;
Firebird::MetaName proc_name(PRC.RDB$PROCEDURE_NAME);
2008-01-16 10:29:37 +01:00
dsc desc;
desc.dsc_dtype = dtype_text;
INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE);
desc.dsc_length = proc_name.length();
desc.dsc_address = (UCHAR*) proc_name.c_str();
DeferredWork* dw2 =
DFW_post_work(transaction, dfw_modify_procedure, &desc, PRC.RDB$PROCEDURE_ID);
DFW_post_work_arg(transaction, dw2, NULL, 0, dfw_arg_check_blr);
END_FOR;
if (!REQUEST(irq_m_fields4))
REQUEST(irq_m_fields4) = request;
request = CMP_find_request(tdbb, irq_m_fields5, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
RFL IN RDB$RELATION_FIELDS CROSS
DEP IN RDB$DEPENDENCIES CROSS
TRG IN RDB$TRIGGERS
WITH RFL.RDB$FIELD_SOURCE EQ field_source->dsc_address AND
DEP.RDB$DEPENDED_ON_NAME EQ RFL.RDB$RELATION_NAME AND
DEP.RDB$FIELD_NAME EQ RFL.RDB$FIELD_NAME AND
DEP.RDB$DEPENDED_ON_TYPE EQ obj_relation AND
DEP.RDB$DEPENDENT_TYPE EQ obj_trigger AND
DEP.RDB$DEPENDENT_NAME EQ TRG.RDB$TRIGGER_NAME
2008-01-16 10:29:37 +01:00
if (!REQUEST(irq_m_fields5))
REQUEST(irq_m_fields5) = request;
Firebird::MetaName trigger_name(TRG.RDB$TRIGGER_NAME);
Firebird::MetaName trigger_relation_name(TRG.RDB$RELATION_NAME);
dsc desc;
desc.dsc_dtype = dtype_text;
INTL_ASSIGN_DSC(&desc, CS_METADATA, COLLATE_NONE);
desc.dsc_length = trigger_name.length();
desc.dsc_address = (UCHAR*) trigger_name.c_str();
DeferredWork* dw2 = DFW_post_work(transaction, dfw_modify_trigger, &desc, 0);
DFW_post_work_arg(transaction, dw2, NULL, TRG.RDB$TRIGGER_TYPE, dfw_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;
if (!REQUEST(irq_m_fields5))
REQUEST(irq_m_fields5) = request;
return dw;
2001-05-23 15:26:42 +02:00
}
2004-03-30 06:10:52 +02:00
Format* MET_current(thread_db* tdbb, jrd_rel* relation)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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);
2008-12-18 11:57:12 +01:00
return relation->rel_current_format = MET_format(tdbb, relation, relation->rel_current_fmt);
2001-05-23 15:26:42 +02:00
}
void MET_delete_dependencies(thread_db* tdbb,
const Firebird::MetaName& object_name,
int dependency_type,
jrd_tra* transaction)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ d e l e t e _ d e p e n d e n c i e s
*
**************************************
*
* Functional description
2005-05-28 00:45:31 +02:00
* Delete all dependencies for the specified
2001-05-23 15:26:42 +02:00
* object of given type.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
// if (!object_name)
// return;
//
jrd_req* request = CMP_find_request(tdbb, irq_d_deps, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
2001-05-23 15:26:42 +02:00
DEP IN RDB$DEPENDENCIES
WITH DEP.RDB$DEPENDENT_NAME = object_name.c_str()
2001-05-23 15:26:42 +02:00
AND DEP.RDB$DEPENDENT_TYPE = dependency_type
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_d_deps))
REQUEST(irq_d_deps) = request;
2001-05-23 15:26:42 +02:00
ERASE DEP;
END_FOR;
if (!REQUEST(irq_d_deps))
REQUEST(irq_d_deps) = request;
}
void MET_delete_shadow(thread_db* tdbb, USHORT shadow_number)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ d e l e t e _ s h a d o w
*
**************************************
*
2005-05-28 00:45:31 +02:00
* Functional description
2001-05-23 15:26:42 +02:00
* When any of the shadows in RDB$FILES for a particular
* shadow are deleted, stop shadowing to that file and
* remove all other files from the same shadow.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
jrd_req* handle = NULL;
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE handle)
2005-05-28 00:45:31 +02:00
X IN RDB$FILES WITH X.RDB$SHADOW_NUMBER EQ shadow_number
2008-12-18 11:57:12 +01:00
ERASE X;
2001-05-23 15:26:42 +02:00
END_FOR;
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, handle);
2001-05-23 15:26:42 +02:00
2009-11-07 12:58:54 +01:00
for (Shadow* shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next)
{
if (shadow->sdw_number == shadow_number) {
2001-05-23 15:26:42 +02:00
shadow->sdw_flags |= SDW_shutdown;
}
2003-12-22 11:00:59 +01:00
}
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// notify other processes to check for shadow deletion
if (SDW_lck_update(tdbb, 0)) {
SDW_notify(tdbb);
}
2001-05-23 15:26:42 +02:00
}
bool MET_dsql_cache_use(thread_db* tdbb, int type, const Firebird::MetaName& name, const Firebird::MetaName& package)
{
DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, QualifiedName(package, name));
2008-03-02 08:35:37 +01:00
const bool obsolete = item->obsolete;
if (!item->locked)
{
// lock to be notified by others when we should mark as obsolete
LCK_lock(tdbb, item->lock, LCK_SR, LCK_WAIT);
item->locked = true;
}
item->obsolete = false;
return obsolete;
}
void MET_dsql_cache_release(thread_db* tdbb, int type, const Firebird::MetaName& name,
const Firebird::MetaName& package)
{
DSqlCacheItem* item = get_dsql_cache_item(tdbb, type, QualifiedName(package, name));
// release lock
LCK_release(tdbb, item->lock);
// notify others through AST to mark as obsolete
Database* const dbb = tdbb->getDatabase();
const size_t key_length = item->lock->lck_length;
Firebird::AutoPtr<Lock> temp_lock(FB_NEW_RPT(*tdbb->getDefaultPool(), key_length) Lock());
temp_lock->lck_dbb = dbb;
temp_lock->lck_parent = dbb->dbb_lock;
temp_lock->lck_type = LCK_dsql_cache;
temp_lock->lck_owner_handle = LCK_get_owner_handle(tdbb, temp_lock->lck_type);
temp_lock->lck_length = key_length;
memcpy(temp_lock->lck_key.lck_string, item->lock->lck_key.lck_string, key_length);
if (LCK_lock(tdbb, temp_lock, LCK_EX, LCK_WAIT))
LCK_release(tdbb, temp_lock);
item->locked = false;
item->obsolete = false;
}
2003-12-22 11:00:59 +01:00
void MET_error(const TEXT* string, ...)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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);
2001-05-23 15:26:42 +02:00
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_random) << Arg::Str(s));
2001-05-23 15:26:42 +02:00
}
2004-03-30 06:10:52 +02:00
Format* MET_format(thread_db* tdbb, jrd_rel* relation, USHORT number)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ f o r m a t
*
**************************************
*
* Functional description
* Lookup a format for given relation.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2004-03-30 06:10:52 +02:00
Format* format;
vec<Format*>* formats = relation->rel_formats;
2008-12-18 11:57:12 +01:00
if (formats && (number < formats->count()) && (format = (*formats)[number]))
2001-05-23 15:26:42 +02:00
{
return format;
}
format = NULL;
jrd_req* request = CMP_find_request(tdbb, irq_r_format, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
X IN RDB$FORMATS WITH X.RDB$RELATION_ID EQ relation->rel_id AND
2005-05-28 00:45:31 +02:00
X.RDB$FORMAT EQ number
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_r_format))
REQUEST(irq_r_format) = request;
2009-11-26 01:20:59 +01:00
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, &X.RDB$DESCRIPTOR);
2005-05-28 00:45:31 +02:00
// Use generic representation of formats with 32-bit offsets
2005-05-28 00:45:31 +02:00
HalfStaticArray<UCHAR, BUFFER_MEDIUM> buffer;
BLB_get_data(tdbb, blob, buffer.getBuffer(blob->blb_length), blob->blb_length);
unsigned bufferPos = 2;
USHORT count = buffer[0] | (buffer[1] << 8);
2005-05-28 00:45:31 +02:00
format = Format::newFormat(*dbb->dbb_permanent, count);
Firebird::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;
}
2005-05-28 00:45:31 +02:00
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 = format->fmt_defaults[offset].vlu_desc;
desc = *odsDflDesc;
if (desc.dsc_dtype == dtype_text)
{
VaryingString* vluString = FB_NEW_RPT(*dbb->dbb_permanent,
desc.dsc_length) VaryingString();
vluString->str_length = desc.dsc_length;
format->fmt_defaults[offset].vlu_string = vluString;
desc.dsc_address = format->fmt_defaults[offset].vlu_string->str_data;
}
else
desc.dsc_address = (UCHAR*) &format->fmt_defaults[offset].vlu_misc;
memcpy(desc.dsc_address, p, desc.dsc_length);
p += desc.dsc_length;
2001-05-23 15:26:42 +02:00
}
END_FOR;
2009-11-26 01:20:59 +01:00
if (!REQUEST(irq_r_format))
2001-05-23 15:26:42 +02:00
REQUEST(irq_r_format) = request;
2009-11-26 01:20:59 +01:00
if (!format)
2004-03-30 06:10:52 +02:00
format = Format::newFormat(*dbb->dbb_permanent);
2001-05-23 15:26:42 +02:00
format->fmt_version = number;
2009-11-21 04:23:47 +01:00
// Link the format block into the world
2001-05-23 15:26:42 +02:00
formats = relation->rel_formats =
vec<Format*>::newVector(*dbb->dbb_permanent, relation->rel_formats, number + 1);
(*formats)[number] = format;
2001-05-23 15:26:42 +02:00
return format;
}
bool MET_get_char_coll_subtype(thread_db* tdbb, USHORT* id, const UCHAR* name, USHORT length)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2005-05-28 00:45:31 +02:00
* M E T _ g e t _ c h a r _ c o l l _ s u b t y p e
2001-05-23 15:26:42 +02:00
*
**************************************
*
* 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);
2003-11-04 00:59:24 +01:00
fb_assert(id != NULL);
fb_assert(name != NULL);
2001-05-23 15:26:42 +02:00
2003-12-22 11:00:59 +01:00
const UCHAR* const end_name = name + length;
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// 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
2003-12-22 11:00:59 +01:00
UCHAR* p = buffer;
UCHAR* period = NULL;
2009-11-07 12:58:54 +01:00
for (; name < end_name && p < buffer + sizeof(buffer) - 1; p++, name++)
{
2001-05-23 15:26:42 +02:00
*p = UPPER7(*name);
if ((*p == '.') && !period) {
2001-05-23 15:26:42 +02:00
period = p;
}
2001-05-23 15:26:42 +02:00
}
*p = 0;
2009-11-21 04:23:47 +01:00
// Is there a period, separating collation name from character set?
2009-11-07 12:58:54 +01:00
if (period)
{
2001-05-23 15:26:42 +02:00
*period = 0;
return resolve_charset_and_collation(tdbb, id, period + 1, buffer);
}
2009-11-21 04:23:47 +01:00
// Is it a character set name (implying charset default collation sequence)
2008-01-16 10:29:37 +01:00
bool res = resolve_charset_and_collation(tdbb, id, buffer, NULL);
2009-11-07 12:58:54 +01:00
if (!res)
{
2009-11-21 04:23:47 +01:00
// Is it a collation name (implying implementation-default character set)
2008-01-16 10:29:37 +01:00
res = resolve_charset_and_collation(tdbb, id, NULL, buffer);
2001-05-23 15:26:42 +02:00
}
2008-01-16 10:29:37 +01:00
return res;
2001-05-23 15:26:42 +02:00
}
2006-04-05 18:34:18 +02:00
bool MET_get_char_coll_subtype_info(thread_db* tdbb, USHORT id, SubtypeInfo* info)
2005-05-28 00:45:31 +02:00
{
/**************************************
*
* 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;
2005-05-28 00:45:31 +02:00
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2005-05-28 00:45:31 +02:00
jrd_req* request = CMP_find_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
2005-05-28 00:45:31 +02:00
found = true;
2005-05-28 00:45:31 +02:00
info->charsetName = CS.RDB$CHARACTER_SET_NAME;
info->collationName = CL.RDB$COLLATION_NAME;
2005-05-28 00:45:31 +02:00
if (CL.RDB$BASE_COLLATION_NAME.NULL)
info->baseCollationName = info->collationName;
else
info->baseCollationName = CL.RDB$BASE_COLLATION_NAME;
2005-05-28 00:45:31 +02:00
if (CL.RDB$SPECIFIC_ATTRIBUTES.NULL)
info->specificAttributes.clear();
else
{
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, &CL.RDB$SPECIFIC_ATTRIBUTES);
const SLONG length = blob->blb_length;
2005-05-28 00:45:31 +02:00
// ASF: Here info->specificAttributes is in UNICODE_FSS charset.
// It will be converted to the collation charset in intl.cpp
BLB_get_data(tdbb, blob, info->specificAttributes.getBuffer(length), length);
}
2005-05-28 00:45:31 +02:00
info->attributes = (USHORT)CL.RDB$COLLATION_ATTRIBUTES;
info->ignoreAttributes = CL.RDB$COLLATION_ATTRIBUTES.NULL;
END_FOR;
2005-05-28 00:45:31 +02:00
if (!REQUEST(irq_l_subtype))
REQUEST(irq_l_subtype) = request;
return found;
}
jrd_nod* MET_get_dependencies(thread_db* tdbb,
jrd_rel* relation,
const UCHAR* blob,
const ULONG blob_length,
CompilerScratch* view_csb,
bid* blob_id,
jrd_req** request,
CompilerScratch** csb_ptr,
Firebird::MetaName& object_name,
int type,
USHORT flags,
jrd_tra* transaction,
Firebird::MetaName domain_validation)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ g e t _ d e p e n d e n c i e s
*
**************************************
*
* Functional description
2005-05-28 00:45:31 +02:00
* Get dependencies for an object by parsing
2001-05-23 15:26:42 +02:00
* the blr used in its definition.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5, domain_validation);
csb->csb_g_flags |= (csb_get_dependencies | flags);
2001-05-23 15:26:42 +02:00
2003-12-22 11:00:59 +01:00
jrd_nod* node;
2001-05-23 15:26:42 +02:00
if (blob)
{
node = PAR_blr(tdbb, relation, blob, blob_length, view_csb, &csb, request,
(type == obj_trigger && relation != NULL), 0);
2001-05-23 15:26:42 +02:00
}
else
{
2008-12-18 11:57:12 +01:00
node = MET_parse_blob(tdbb, relation, blob_id, &csb, request,
(type == obj_trigger && relation != NULL));
2001-05-23 15:26:42 +02:00
}
2006-08-30 06:54:44 +02:00
if (type == obj_computed)
2001-05-23 15:26:42 +02:00
{
jrd_req* handle = NULL;
2001-05-23 15:26:42 +02:00
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()
2008-12-18 11:57:12 +01:00
object_name = FLD.RDB$FIELD_NAME;
2001-05-23 15:26:42 +02:00
END_FOR;
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, handle);
2001-05-23 15:26:42 +02:00
}
MET_delete_dependencies(tdbb, object_name, type, transaction);
store_dependencies(tdbb, csb, relation, object_name, type, transaction);
2001-05-23 15:26:42 +02:00
if (csb_ptr)
{
*csb_ptr = csb;
2001-05-23 15:26:42 +02:00
}
else
{
delete csb;
2001-05-23 15:26:42 +02:00
}
return node;
}
jrd_fld* MET_get_field(jrd_rel* relation, USHORT id)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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;
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!relation || !(vector = relation->rel_fields) || id >= vector->count())
2003-12-22 11:00:59 +01:00
{
return NULL;
}
2001-05-23 15:26:42 +02:00
return (*vector)[id];
2001-05-23 15:26:42 +02:00
}
void MET_get_shadow_files(thread_db* tdbb, bool delete_files)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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
2005-05-28 00:45:31 +02:00
* our in-memory list: if any new shadow files have
2001-05-23 15:26:42 +02:00
* been defined since the last time we looked, start
* shadowing to them; if any have been deleted, stop
* shadowing to them.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
jrd_req* handle = NULL;
2001-05-23 15:26:42 +02:00
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
2008-12-18 11:57:12 +01:00
if ((X.RDB$FILE_FLAGS & FILE_shadow) && !(X.RDB$FILE_FLAGS & FILE_inactive))
2001-05-23 15:26:42 +02:00
{
2003-12-22 11:00:59 +01:00
const USHORT file_flags = X.RDB$FILE_FLAGS;
SDW_start(tdbb, X.RDB$FILE_NAME, X.RDB$SHADOW_NUMBER, file_flags, delete_files);
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// 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
2001-05-23 15:26:42 +02:00
for (Shadow* shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next)
2001-05-23 15:26:42 +02:00
{
2008-12-18 11:57:12 +01:00
if ((shadow->sdw_number == X.RDB$SHADOW_NUMBER) && !(shadow->sdw_flags & SDW_IGNORE))
2001-05-23 15:26:42 +02:00
{
shadow->sdw_flags |= SDW_found;
if (!(file_flags & FILE_conditional)) {
2001-05-23 15:26:42 +02:00
shadow->sdw_flags &= ~SDW_conditional;
}
2001-05-23 15:26:42 +02:00
break;
}
}
}
END_FOR;
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, handle);
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// if any current shadows were not defined in database, mark
// them to be shutdown since they don't exist anymore
2001-05-23 15:26:42 +02:00
2009-11-07 12:58:54 +01:00
for (Shadow* shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next)
{
if (!(shadow->sdw_flags & SDW_found)) {
2001-05-23 15:26:42 +02:00
shadow->sdw_flags |= SDW_shutdown;
}
else {
2001-05-23 15:26:42 +02:00
shadow->sdw_flags &= ~SDW_found;
}
2003-12-22 11:00:59 +01:00
}
2001-05-23 15:26:42 +02:00
SDW_check(tdbb);
2001-05-23 15:26:42 +02:00
}
void MET_load_db_triggers(thread_db* tdbb, int type)
{
/**************************************
*
2006-11-06 02:07:31 +01:00
* M E T _ l o a d _ d b _ t r i g g e r s
*
**************************************
*
* Functional description
* Load database triggers from RDB$TRIGGERS.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
CHECK_DBB(dbb);
if ((tdbb->getAttachment()->att_flags & ATT_no_db_triggers) ||
tdbb->getDatabase()->dbb_triggers[type] != NULL)
{
return;
}
tdbb->getDatabase()->dbb_triggers[type] = FB_NEW(*tdbb->getDatabase()->dbb_permanent)
trig_vec(*tdbb->getDatabase()->dbb_permanent);
jrd_req* trigger_request = NULL;
int encoded_type = type | TRIGGER_TYPE_DB;
FOR(REQUEST_HANDLE trigger_request)
TRG IN RDB$TRIGGERS
WITH TRG.RDB$RELATION_NAME MISSING AND
TRG.RDB$TRIGGER_TYPE EQ encoded_type AND
TRG.RDB$TRIGGER_INACTIVE EQ 0
SORTED BY TRG.RDB$TRIGGER_SEQUENCE
2008-12-18 11:57:12 +01:00
MET_load_trigger(tdbb, NULL, TRG.RDB$TRIGGER_NAME, &tdbb->getDatabase()->dbb_triggers[type]);
END_FOR;
CMP_release(tdbb, trigger_request);
}
// Load DDL triggers from RDB$TRIGGERS.
void MET_load_ddl_triggers(thread_db* tdbb)
{
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
CHECK_DBB(dbb);
if ((tdbb->getAttachment()->att_flags & ATT_no_db_triggers) ||
tdbb->getDatabase()->dbb_ddl_triggers != NULL)
{
return;
}
tdbb->getDatabase()->dbb_ddl_triggers = FB_NEW(*tdbb->getDatabase()->dbb_permanent)
trig_vec(*tdbb->getDatabase()->dbb_permanent);
jrd_req* trigger_request = NULL;
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,
&tdbb->getDatabase()->dbb_ddl_triggers);
}
}
END_FOR;
CMP_release(tdbb, trigger_request);
}
void MET_load_trigger(thread_db* tdbb,
2005-05-28 00:45:31 +02:00
jrd_rel* relation,
const Firebird::MetaName& trigger_name,
trig_vec** triggers)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o a d _ t r i g g e r
*
**************************************
*
* Functional description
* Load triggers from RDB$TRIGGERS. If a requested,
* also load triggers from RDB$RELATIONS.
*
**************************************/
TEXT errmsg[MAX_ERRMSG_LEN + 1];
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
if (relation)
{
if (relation->rel_flags & REL_sys_trigs_being_loaded)
return;
// No need to load table triggers for ReadOnly databases,
// since INSERT/DELETE/UPDATE statements are not going to be allowed
if (dbb->dbb_flags & DBB_read_only)
return;
}
2001-05-23 15:26:42 +02:00
// Scan RDB$TRIGGERS next
2001-05-23 15:26:42 +02:00
jrd_req* trigger_request = CMP_find_request(tdbb, irq_s_triggers, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE trigger_request)
TRG IN RDB$TRIGGERS
WITH TRG.RDB$TRIGGER_NAME EQ trigger_name.c_str()
2003-12-22 11:00:59 +01:00
if (!REQUEST(irq_s_triggers))
2001-05-23 15:26:42 +02:00
REQUEST(irq_s_triggers) = trigger_request;
2009-11-21 04:23:47 +01:00
// check if the trigger is to be fired without any permissions
// checks. Verify such a claim
USHORT trig_flags = (USHORT) TRG.RDB$FLAGS;
2009-11-21 04:23:47 +01:00
// if there is an ignore permission flag, see if it is legit
2008-12-18 11:57:12 +01:00
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);
2001-05-23 15:26:42 +02:00
trig_flags &= ~TRG_ignore_perm;
}
2001-05-23 15:26:42 +02:00
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;
Firebird::MetaName engine;
Firebird::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);
2001-05-23 15:26:42 +02:00
}
}
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,
2003-12-03 09:19:24 +01:00
(bool) TRG.RDB$SYSTEM_FLAG,
trig_flags,
engine,
entryPoint,
&extBodyId);
}
2001-05-23 15:26:42 +02:00
}
END_FOR;
if (!REQUEST(irq_s_triggers))
REQUEST(irq_s_triggers) = trigger_request;
}
void MET_lookup_cnstrt_for_index(thread_db* tdbb,
Firebird::MetaName& constraint_name,
const Firebird::MetaName& index_name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ c n s t r t _ f o r _ i n d e x
*
**************************************
*
* Functional description
* Lookup constraint name from index name, if one exists.
* Calling routine must pass a buffer of at least 32 bytes.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
constraint_name = "";
jrd_req* request = CMP_find_request(tdbb, irq_l_cnstrt, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
X IN RDB$RELATION_CONSTRAINTS WITH X.RDB$INDEX_NAME EQ index_name.c_str()
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_cnstrt))
REQUEST(irq_l_cnstrt) = request;
2001-05-23 15:26:42 +02:00
constraint_name = X.RDB$CONSTRAINT_NAME;
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_l_cnstrt))
REQUEST(irq_l_cnstrt) = request;
}
void MET_lookup_cnstrt_for_trigger(thread_db* tdbb,
Firebird::MetaName& constraint_name,
Firebird::MetaName& relation_name,
const Firebird::MetaName& trigger_name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2005-05-28 00:45:31 +02:00
* 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
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Lookup constraint name from trigger name, if one exists.
* Calling routine must pass a buffer of at least 32 bytes.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
constraint_name = "";
relation_name = "";
jrd_req* request = CMP_find_request(tdbb, irq_l_check, IRQ_REQUESTS);
jrd_req* request2 = CMP_find_request(tdbb, irq_l_check2, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// 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
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
Y IN RDB$TRIGGERS WITH
Y.RDB$TRIGGER_NAME EQ trigger_name.c_str()
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_check))
REQUEST(irq_l_check) = request;
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request2)
X IN RDB$CHECK_CONSTRAINTS WITH
X.RDB$TRIGGER_NAME EQ Y.RDB$TRIGGER_NAME
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_check2))
REQUEST(irq_l_check2) = request2;
2001-05-23 15:26:42 +02:00
constraint_name = X.RDB$CONSTRAINT_NAME;
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_l_check2))
REQUEST(irq_l_check2) = request2;
relation_name = Y.RDB$RELATION_NAME;
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_l_check))
REQUEST(irq_l_check) = request;
}
void MET_lookup_exception(thread_db* tdbb,
SLONG number,
Firebird::MetaName& name,
TEXT* message,
size_t messageSize)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ e x c e p t i o n
*
**************************************
*
* Functional description
* Lookup exception by number and return its name and message.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// We need to look up exception in RDB$EXCEPTIONS
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_l_exception, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
name = "";
2008-05-06 11:00:59 +02:00
if (message && messageSize)
2001-05-23 15:26:42 +02:00
{
*message = 0;
}
FOR(REQUEST_HANDLE request)
X IN RDB$EXCEPTIONS WITH X.RDB$EXCEPTION_NUMBER = number
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_exception))
2001-05-23 15:26:42 +02:00
{
REQUEST(irq_l_exception) = request;
}
if (!X.RDB$EXCEPTION_NAME.NULL)
{
name = X.RDB$EXCEPTION_NAME;
2001-05-23 15:26:42 +02:00
}
if (!X.RDB$MESSAGE.NULL && message)
{
Firebird::string tmp(X.RDB$MESSAGE);
2008-05-06 11:00:59 +02:00
tmp.copyTo(message, messageSize);
2001-05-23 15:26:42 +02:00
}
END_FOR;
if (!REQUEST(irq_l_exception))
{
REQUEST(irq_l_exception) = request;
}
}
SLONG MET_lookup_exception_number(thread_db* tdbb, const Firebird::MetaName& name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ e x c e p t i o n _ n u m b e r
*
**************************************
*
* Functional description
* Lookup exception by name and return its number.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// We need to look up exception in RDB$EXCEPTIONS
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_l_except_no, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
2003-12-22 11:00:59 +01:00
SLONG number = 0;
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
X IN RDB$EXCEPTIONS WITH X.RDB$EXCEPTION_NAME = name.c_str()
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_except_no))
2003-12-22 11:00:59 +01:00
REQUEST(irq_l_except_no) = request;
2001-05-23 15:26:42 +02:00
number = X.RDB$EXCEPTION_NUMBER;
END_FOR;
if (!REQUEST(irq_l_except_no))
REQUEST(irq_l_except_no) = request;
return number;
}
2005-05-28 00:45:31 +02:00
int MET_lookup_field(thread_db* tdbb,
jrd_rel* relation,
const Firebird::MetaName& name,
const Firebird::MetaName* security_name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ f i e l d
*
**************************************
*
* Functional description
* Look up a field name.
2008-01-16 10:29:37 +01:00
* Additionally, if security_name is a not null pointer,
2002-07-01 18:59:09 +02:00
* it's used to include the condition that it should match
* the field's security class name, too.
2001-05-23 15:26:42 +02:00
*
2003-02-20 01:10:25 +01:00
* if the field is not found return -1
*
*****************************************/
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// 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();
2008-12-18 11:57:12 +01:00
for (const vec<jrd_fld*>::const_iterator end = vector->end(); fieldIter < end;
++fieldIter, ++id)
2003-12-22 11:00:59 +01:00
{
2005-05-28 00:45:31 +02:00
if (*fieldIter)
{
jrd_fld* field = *fieldIter;
if (field->fld_name == name)
{
2005-05-28 00:45:31 +02:00
if (!security_name)
{
return id;
}
2005-05-28 00:45:31 +02:00
if (field->fld_security_name == *security_name)
{
return id;
}
}
}
}
}
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// Not found. Next, try system relations directly
2001-05-23 15:26:42 +02:00
int id = -1;
2001-05-23 15:26:42 +02:00
if (relation->rel_flags & REL_deleted)
{
return id;
}
jrd_req* request = CMP_find_request(tdbb, irq_l_field, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
X IN RDB$RELATION_FIELDS WITH
X.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND
2005-09-14 14:02:13 +02:00
X.RDB$FIELD_ID NOT MISSING AND
2005-05-28 00:45:31 +02:00
X.RDB$FIELD_NAME EQ name.c_str()
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_field)) {
REQUEST(irq_l_field) = request;
2001-05-23 15:26:42 +02:00
}
2002-07-01 18:59:09 +02:00
2008-12-18 11:57:12 +01:00
if (!security_name) {
id = X.RDB$FIELD_ID;
}
2009-11-07 12:58:54 +01:00
else
{
2008-12-18 11:57:12 +01:00
if ((!X.RDB$SECURITY_CLASS.NULL) && (*security_name == X.RDB$SECURITY_CLASS))
{
2008-12-18 11:57:12 +01:00
id = X.RDB$FIELD_ID;
}
}
2002-07-01 18:59:09 +02:00
2001-05-23 15:26:42 +02:00
END_FOR;
2002-07-01 18:59:09 +02:00
if (!REQUEST(irq_l_field)) {
2001-05-23 15:26:42 +02:00
REQUEST(irq_l_field) = request;
}
return id;
}
BlobFilter* MET_lookup_filter(thread_db* tdbb, SSHORT from, SSHORT to)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ f i l t e r
*
**************************************
*
* Functional description
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2003-12-22 11:00:59 +01:00
FPTR_BFILTER_CALLBACK filter = NULL;
BlobFilter* blf = NULL;
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_r_filters, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
X IN RDB$FILTERS WITH X.RDB$INPUT_SUB_TYPE EQ from AND
2008-12-18 11:57:12 +01:00
X.RDB$OUTPUT_SUB_TYPE EQ to
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_r_filters))
REQUEST(irq_r_filters) = request;
filter = (FPTR_BFILTER_CALLBACK)
2008-01-29 11:11:52 +01:00
Module::lookup(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT, dbb->dbb_modules);
2001-05-23 15:26:42 +02:00
if (filter)
{
blf = FB_NEW(*dbb->dbb_permanent) BlobFilter(*dbb->dbb_permanent);
blf->blf_next = NULL;
blf->blf_from = from;
blf->blf_to = to;
blf->blf_filter = filter;
blf->blf_exception_message.printf(EXCEPTION_MESSAGE,
2001-05-23 15:26:42 +02:00
X.RDB$FUNCTION_NAME, X.RDB$ENTRYPOINT, X.RDB$MODULE_NAME);
}
END_FOR;
if (!REQUEST(irq_r_filters))
REQUEST(irq_r_filters) = request;
return blf;
2001-05-23 15:26:42 +02:00
}
SLONG MET_lookup_generator(thread_db* tdbb, const TEXT* name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ g e n e r a t o r
*
**************************************
*
* Functional description
* Lookup generator (aka gen_id). If we can't find it, make a new one.
2002-07-01 18:59:09 +02:00
* CVC: I don't see how this function "makes" a new generator; it simply
* returns -1 if the name is not found.
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
if (!strcmp(name, "RDB$GENERATORS"))
return 0;
2003-12-22 11:00:59 +01:00
SLONG gen_id = -1;
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_r_gen_id, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
X IN RDB$GENERATORS WITH X.RDB$GENERATOR_NAME EQ name
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_r_gen_id))
2001-05-23 15:26:42 +02:00
REQUEST(irq_r_gen_id) = request;
gen_id = X.RDB$GENERATOR_ID;
END_FOR;
if (!REQUEST(irq_r_gen_id))
REQUEST(irq_r_gen_id) = request;
return gen_id;
}
void MET_lookup_generator_id (thread_db* tdbb, SLONG gen_id, Firebird::MetaName& name)
2002-07-01 18:59:09 +02:00
{
/**************************************
*
* 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.
*
**************************************/
2008-12-18 11:57:12 +01:00
SET_TDBB (tdbb);
Database* dbb = tdbb->getDatabase();
2005-05-28 00:45:31 +02:00
2009-11-07 12:58:54 +01:00
if (!gen_id)
{
2008-12-18 11:57:12 +01:00
name = "RDB$GENERATORS";
return;
}
2002-07-01 18:59:09 +02:00
2008-12-18 11:57:12 +01:00
name = "";
2005-05-28 00:45:31 +02:00
jrd_req* request = CMP_find_request (tdbb, irq_r_gen_id_num, IRQ_REQUESTS);
2005-05-28 00:45:31 +02:00
2008-12-18 11:57:12 +01:00
FOR (REQUEST_HANDLE request)
X IN RDB$GENERATORS WITH X.RDB$GENERATOR_ID EQ gen_id
2002-07-01 18:59:09 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST (irq_r_gen_id_num))
REQUEST (irq_r_gen_id_num) = request;
name = X.RDB$GENERATOR_NAME;
END_FOR;
2002-07-01 18:59:09 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST (irq_r_gen_id_num))
REQUEST (irq_r_gen_id_num) = request;
2002-07-01 18:59:09 +02:00
}
2001-05-23 15:26:42 +02:00
void MET_lookup_index(thread_db* tdbb,
Firebird::MetaName& index_name,
const Firebird::MetaName& relation_name,
USHORT number)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ i n d e x
*
**************************************
*
* Functional description
* Lookup index name from relation and index number.
* Calling routine must pass a buffer of at least 32 bytes.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
index_name = "";
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_l_index, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
X IN RDB$INDICES WITH X.RDB$RELATION_NAME EQ relation_name.c_str()
2005-05-28 00:45:31 +02:00
AND X.RDB$INDEX_ID EQ number
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_index))
REQUEST(irq_l_index) = request;
2001-05-23 15:26:42 +02:00
index_name = X.RDB$INDEX_NAME;
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_l_index))
REQUEST(irq_l_index) = request;
}
SLONG MET_lookup_index_name(thread_db* tdbb,
const Firebird::MetaName& index_name,
2003-12-22 11:00:59 +01:00
SLONG* relation_id, SSHORT* status)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ i n d e x _ n a m e
*
**************************************
*
* Functional description
* Lookup index id from index name.
*
**************************************/
SLONG id = -1;
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_l_index_name, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
*status = MET_object_unknown;
FOR(REQUEST_HANDLE request)
X IN RDB$INDICES WITH
X.RDB$INDEX_NAME EQ index_name.c_str()
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_index_name))
REQUEST(irq_l_index_name) = request;
2001-05-23 15:26:42 +02:00
if (X.RDB$INDEX_INACTIVE == 0) {
2001-05-23 15:26:42 +02:00
*status = MET_object_active;
}
else {
2001-05-23 15:26:42 +02:00
*status = MET_object_inactive;
}
2001-05-23 15:26:42 +02:00
id = X.RDB$INDEX_ID - 1;
2003-12-22 11:00:59 +01:00
const jrd_rel* relation = MET_lookup_relation(tdbb, X.RDB$RELATION_NAME);
2001-05-23 15:26:42 +02:00
*relation_id = relation->rel_id;
END_FOR;
if (!REQUEST(irq_l_index_name))
REQUEST(irq_l_index_name) = request;
return id;
}
2009-11-21 06:20:18 +01:00
bool MET_lookup_partner(thread_db* tdbb, jrd_rel* relation, index_desc* idx, const TEXT* index_name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ p a r t n e r
*
**************************************
*
* Functional description
* Find partner index participating in a
* foreign key relationship.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
Database::CheckoutLockGuard guard(dbb, dbb->dbb_meta_mutex);
2001-05-23 15:26:42 +02:00
if (relation->rel_flags & REL_check_partners)
{
2009-11-21 04:23:47 +01:00
// Prepare for rescan of foreign references on other relations'
// primary keys and release stale vectors.
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_foreign1, IRQ_REQUESTS);
frgn* references = &relation->rel_foreign_refs;
int index_number = 0;
2001-05-23 15:26:42 +02:00
2009-11-07 12:58:54 +01:00
if (references->frgn_reference_ids)
{
2001-12-24 03:51:06 +01:00
delete references->frgn_reference_ids;
references->frgn_reference_ids = NULL;
2001-05-23 15:26:42 +02:00
}
2009-11-07 12:58:54 +01:00
if (references->frgn_relations)
{
2001-12-24 03:51:06 +01:00
delete references->frgn_relations;
references->frgn_relations = NULL;
2001-05-23 15:26:42 +02:00
}
2009-11-07 12:58:54 +01:00
if (references->frgn_indexes)
{
2001-12-24 03:51:06 +01:00
delete references->frgn_indexes;
references->frgn_indexes = NULL;
2001-05-23 15:26:42 +02:00
}
FOR(REQUEST_HANDLE request)
IDX IN RDB$INDICES CROSS
RC IN RDB$RELATION_CONSTRAINTS
OVER RDB$INDEX_NAME CROSS
2001-05-23 15:26:42 +02:00
IND IN RDB$INDICES WITH
RC.RDB$CONSTRAINT_TYPE EQ FOREIGN_KEY AND
IDX.RDB$RELATION_NAME EQ relation->rel_name.c_str() AND
2001-05-23 15:26:42 +02:00
IND.RDB$INDEX_NAME EQ IDX.RDB$FOREIGN_KEY AND
2005-12-12 14:58:19 +01:00
IND.RDB$UNIQUE_FLAG = 1
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_foreign1))
2001-05-23 15:26:42 +02:00
{
2008-12-18 11:57:12 +01:00
REQUEST(irq_foreign1) = request;
2001-05-23 15:26:42 +02:00
}
2008-12-18 11:57:12 +01:00
const jrd_rel* partner_relation = MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME);
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (partner_relation && !IDX.RDB$INDEX_INACTIVE && !IND.RDB$INDEX_INACTIVE)
2001-05-23 15:26:42 +02:00
{
// This seems a good candidate for vcl.
references->frgn_reference_ids =
vec<int>::newVector(*dbb->dbb_permanent, references->frgn_reference_ids,
index_number + 1);
2001-05-23 15:26:42 +02:00
(*references->frgn_reference_ids)[index_number] = IDX.RDB$INDEX_ID - 1;
2001-05-23 15:26:42 +02:00
references->frgn_relations =
vec<int>::newVector(*dbb->dbb_permanent, references->frgn_relations,
index_number + 1);
2001-12-24 03:51:06 +01:00
(*references->frgn_relations)[index_number] = partner_relation->rel_id;
2001-05-23 15:26:42 +02:00
2005-05-28 00:45:31 +02:00
references->frgn_indexes =
vec<int>::newVector(*dbb->dbb_permanent, references->frgn_indexes,
index_number + 1);
2001-05-23 15:26:42 +02:00
(*references->frgn_indexes)[index_number] = IND.RDB$INDEX_ID - 1;
2001-05-23 15:26:42 +02:00
index_number++;
}
END_FOR;
if (!REQUEST(irq_foreign1))
{
REQUEST(irq_foreign1) = request;
}
2009-11-21 04:23:47 +01:00
// Prepare for rescan of primary dependencies on relation's primary key and stale vectors.
2001-05-23 15:26:42 +02:00
request = CMP_find_request(tdbb, irq_foreign2, IRQ_REQUESTS);
prim* dependencies = &relation->rel_primary_dpnds;
2001-05-23 15:26:42 +02:00
index_number = 0;
2009-11-07 12:58:54 +01:00
if (dependencies->prim_reference_ids)
{
2001-12-24 03:51:06 +01:00
delete dependencies->prim_reference_ids;
dependencies->prim_reference_ids = NULL;
2001-05-23 15:26:42 +02:00
}
2009-11-07 12:58:54 +01:00
if (dependencies->prim_relations)
{
2001-12-24 03:51:06 +01:00
delete dependencies->prim_relations;
dependencies->prim_relations = NULL;
2001-05-23 15:26:42 +02:00
}
2009-11-07 12:58:54 +01:00
if (dependencies->prim_indexes)
{
2001-12-24 03:51:06 +01:00
delete dependencies->prim_indexes;
dependencies->prim_indexes = NULL;
2001-05-23 15:26:42 +02:00
}
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
2001-05-23 15:26:42 +02:00
IND.RDB$FOREIGN_KEY EQ IDX.RDB$INDEX_NAME
2001-12-24 03:51:06 +01:00
if (!REQUEST(irq_foreign2)) {
2001-05-23 15:26:42 +02:00
REQUEST(irq_foreign2) = request;
2001-12-24 03:51:06 +01:00
}
2005-12-12 14:58:19 +01:00
2008-12-18 11:57:12 +01:00
const jrd_rel* partner_relation = MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME);
2005-12-12 14:58:19 +01:00
2008-12-18 11:57:12 +01:00
if (partner_relation && !IDX.RDB$INDEX_INACTIVE && !IND.RDB$INDEX_INACTIVE)
2001-05-23 15:26:42 +02:00
{
dependencies->prim_reference_ids =
vec<int>::newVector(*dbb->dbb_permanent, dependencies->prim_reference_ids,
index_number + 1);
2001-05-23 15:26:42 +02:00
(*dependencies->prim_reference_ids)[index_number] = IDX.RDB$INDEX_ID - 1;
2001-05-23 15:26:42 +02:00
2005-05-28 00:45:31 +02:00
dependencies->prim_relations =
vec<int>::newVector(*dbb->dbb_permanent, dependencies->prim_relations,
2004-11-30 07:18:39 +01:00
index_number + 1);
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
(*dependencies->prim_relations)[index_number] = partner_relation->rel_id;
2001-05-23 15:26:42 +02:00
dependencies->prim_indexes =
vec<int>::newVector(*dbb->dbb_permanent, dependencies->prim_indexes,
2004-11-30 07:18:39 +01:00
index_number + 1);
2001-05-23 15:26:42 +02:00
(*dependencies->prim_indexes)[index_number] = IND.RDB$INDEX_ID - 1;
2001-05-23 15:26:42 +02:00
index_number++;
}
END_FOR;
if (!REQUEST(irq_foreign2))
REQUEST(irq_foreign2) = request;
LCK_lock(tdbb, relation->rel_partners_lock, LCK_SR, LCK_WAIT);
relation->rel_flags &= ~REL_check_partners;
2001-05-23 15:26:42 +02:00
}
if (idx->idx_flags & idx_foreign)
{
if (index_name)
2001-05-23 15:26:42 +02:00
{
2009-11-21 04:23:47 +01:00
// Since primary key index names aren't being cached, do a long
// hard lookup. This is only called during index create for foreign keys.
2001-05-23 15:26:42 +02:00
bool found = false;
jrd_req* request = NULL;
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
(IDX.RDB$INDEX_ID EQ idx->idx_id + 1 OR
2002-12-22 14:08:50 +01:00
IDX.RDB$INDEX_NAME EQ index_name) AND
2001-05-23 15:26:42 +02:00
IND.RDB$INDEX_NAME EQ IDX.RDB$FOREIGN_KEY AND
2005-12-12 14:58:19 +01:00
IND.RDB$UNIQUE_FLAG = 1
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
const jrd_rel* partner_relation = MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME);
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (partner_relation && !IDX.RDB$INDEX_INACTIVE && !IND.RDB$INDEX_INACTIVE)
2001-05-23 15:26:42 +02:00
{
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;
2001-05-23 15:26:42 +02:00
}
END_FOR;
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, request);
2001-05-23 15:26:42 +02:00
return found;
}
frgn* references = &relation->rel_foreign_refs;
2001-05-23 15:26:42 +02:00
if (references->frgn_reference_ids)
{
for (unsigned int index_number = 0;
index_number < references->frgn_reference_ids->count();
2008-12-18 11:57:12 +01:00
index_number++)
2001-05-23 15:26:42 +02:00
{
if (idx->idx_id == (*references->frgn_reference_ids)[index_number])
2001-05-23 15:26:42 +02:00
{
idx->idx_primary_relation = (*references->frgn_relations)[index_number];
idx->idx_primary_index = (*references->frgn_indexes)[index_number];
return true;
2001-05-23 15:26:42 +02:00
}
}
}
return false;
2001-05-23 15:26:42 +02:00
}
else if (idx->idx_flags & (idx_primary | idx_unique))
{
const prim* dependencies = &relation->rel_primary_dpnds;
2001-05-23 15:26:42 +02:00
if (dependencies->prim_reference_ids)
{
for (unsigned int index_number = 0;
index_number < dependencies->prim_reference_ids->count();
2001-05-23 15:26:42 +02:00
index_number++)
{
if (idx->idx_id == (*dependencies->prim_reference_ids)[index_number])
2001-05-23 15:26:42 +02:00
{
2008-12-18 11:57:12 +01:00
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;
2001-05-23 15:26:42 +02:00
}
}
}
return false;
2001-05-23 15:26:42 +02:00
}
return false;
2001-05-23 15:26:42 +02:00
}
jrd_prc* MET_lookup_procedure(thread_db* tdbb, const QualifiedName& name, bool noscan)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ p r o c e d u r e
*
**************************************
*
* Functional description
* Lookup procedure by name. Name passed in is
* ASCIZ name.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2003-12-22 11:00:59 +01:00
jrd_prc* check_procedure = NULL;
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// See if we already know the procedure by name
vec<jrd_prc*>* procedures = dbb->dbb_procedures;
2009-11-07 12:58:54 +01:00
if (procedures)
{
2008-12-18 11:57:12 +01:00
vec<jrd_prc*>::iterator ptr = procedures->begin();
for (const vec<jrd_prc*>::const_iterator end = procedures->end(); ptr < end; ++ptr)
2002-11-16 21:20:44 +01:00
{
jrd_prc* procedure = *ptr;
2008-12-18 11:57:12 +01:00
if (procedure && !(procedure->prc_flags & PRC_obsolete) &&
((procedure->prc_flags & PRC_scanned) || noscan) &&
!(procedure->prc_flags & PRC_being_scanned) &&
!(procedure->prc_flags & PRC_being_altered))
{
2005-05-28 00:45:31 +02:00
if (procedure->prc_name == name)
2002-11-16 21:20:44 +01:00
{
2005-05-28 00:45:31 +02:00
if (procedure->prc_flags & PRC_check_existence)
{
check_procedure = procedure;
2008-12-18 11:57:12 +01:00
LCK_lock(tdbb, check_procedure->prc_existence_lock, LCK_SR, LCK_WAIT);
break;
}
2008-01-16 10:29:37 +01:00
return procedure;
}
}
2001-05-23 15:26:42 +02:00
}
}
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// We need to look up the procedure name in RDB$PROCEDURES
2001-05-23 15:26:42 +02:00
jrd_prc* procedure = NULL;
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_l_procedure, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
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.qualifier.c_str(), '')
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_procedure))
REQUEST(irq_l_procedure) = request;
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
procedure = MET_procedure(tdbb, P.RDB$PROCEDURE_ID, noscan, 0);
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_l_procedure))
REQUEST(irq_l_procedure) = request;
2009-11-07 12:58:54 +01:00
if (check_procedure)
{
check_procedure->prc_flags &= ~PRC_check_existence;
2009-11-07 12:58:54 +01:00
if (check_procedure != procedure)
{
LCK_release(tdbb, check_procedure->prc_existence_lock);
check_procedure->prc_flags |= PRC_obsolete;
}
}
2001-05-23 15:26:42 +02:00
return procedure;
}
jrd_prc* MET_lookup_procedure_id(thread_db* tdbb, SSHORT id,
2009-11-21 06:20:18 +01:00
bool return_deleted, bool noscan, USHORT flags)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ p r o c e d u r e _ i d
*
**************************************
*
* Functional description
* Lookup procedure by id.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2003-12-22 11:00:59 +01:00
jrd_prc* check_procedure = NULL;
2001-05-23 15:26:42 +02:00
jrd_prc* procedure;
2005-05-28 00:45:31 +02:00
vec<jrd_prc*>* procedures = dbb->dbb_procedures;
2008-12-18 11:57:12 +01:00
if (procedures && id < (SSHORT) procedures->count() && (procedure = (*procedures)[id]) &&
procedure->prc_id == id &&
!(procedure->prc_flags & PRC_being_scanned) &&
((procedure->prc_flags & PRC_scanned) || noscan) &&
!(procedure->prc_flags & PRC_being_altered) &&
(!(procedure->prc_flags & PRC_obsolete) || return_deleted))
{
2009-11-07 12:58:54 +01:00
if (procedure->prc_flags & PRC_check_existence)
{
check_procedure = procedure;
LCK_lock(tdbb, check_procedure->prc_existence_lock, LCK_SR, LCK_WAIT);
}
else {
return procedure;
}
}
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// We need to look up the procedure name in RDB$PROCEDURES
2001-05-23 15:26:42 +02:00
procedure = NULL;
jrd_req* request = CMP_find_request(tdbb, irq_l_proc_id, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ id
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_proc_id))
REQUEST(irq_l_proc_id) = request;
2001-05-23 15:26:42 +02:00
procedure = MET_procedure(tdbb, P.RDB$PROCEDURE_ID, noscan, flags);
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_l_proc_id))
REQUEST(irq_l_proc_id) = request;
2009-11-07 12:58:54 +01:00
if (check_procedure)
{
check_procedure->prc_flags &= ~PRC_check_existence;
2009-11-07 12:58:54 +01:00
if (check_procedure != procedure)
{
LCK_release(tdbb, check_procedure->prc_existence_lock);
check_procedure->prc_flags |= PRC_obsolete;
}
}
2001-05-23 15:26:42 +02:00
return procedure;
}
jrd_rel* MET_lookup_relation(thread_db* tdbb, const Firebird::MetaName& name)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ r e l a t i o n
*
**************************************
*
* Functional description
* Lookup relation by name. Name passed in is
* ASCIZ name.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// See if we already know the relation by name
2001-05-23 15:26:42 +02:00
vec<jrd_rel*>* relations = dbb->dbb_relations;
jrd_rel* check_relation = NULL;
2001-05-23 15:26:42 +02:00
vec<jrd_rel*>::iterator ptr = relations->begin();
2006-02-10 04:28:43 +01:00
for (const vec<jrd_rel*>::const_iterator end = relations->end(); ptr < end; ++ptr)
2001-05-23 15:26:42 +02:00
{
jrd_rel* relation = *ptr;
if (relation && !(relation->rel_flags & REL_deleted))
2001-05-23 15:26:42 +02:00
{
2009-11-21 04:23:47 +01:00
// 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) &&
2008-12-18 11:57:12 +01:00
(!(relation->rel_flags & REL_scanned) || (relation->rel_flags & REL_being_scanned)))
{
2003-02-24 16:37:15 +01:00
continue;
}
if (relation->rel_name == name)
2001-05-23 15:26:42 +02:00
{
if (relation->rel_flags & REL_check_existence)
2001-05-23 15:26:42 +02:00
{
check_relation = relation;
LCK_lock(tdbb, check_relation->rel_existence_lock, LCK_SR, LCK_WAIT);
break;
}
2008-01-16 10:29:37 +01:00
return relation;
2001-05-23 15:26:42 +02:00
}
}
}
2009-11-21 04:23:47 +01:00
// We need to look up the relation name in RDB$RELATIONS
2001-05-23 15:26:42 +02:00
jrd_rel* relation = NULL;
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_l_relation, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ name.c_str()
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_relation))
2001-05-23 15:26:42 +02:00
{
REQUEST(irq_l_relation) = request;
}
relation = MET_relation(tdbb, X.RDB$RELATION_ID);
if (relation->rel_name.length() == 0) {
relation->rel_name = name;
}
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_l_relation))
{
REQUEST(irq_l_relation) = request;
}
2009-11-07 12:58:54 +01:00
if (check_relation)
{
check_relation->rel_flags &= ~REL_check_existence;
2009-11-07 12:58:54 +01:00
if (check_relation != relation)
{
2001-05-23 15:26:42 +02:00
LCK_release(tdbb, check_relation->rel_existence_lock);
LCK_release(tdbb, check_relation->rel_partners_lock);
check_relation->rel_flags &= ~REL_check_partners;
2001-05-23 15:26:42 +02:00
check_relation->rel_flags |= REL_deleted;
}
}
return relation;
}
jrd_rel* MET_lookup_relation_id(thread_db* tdbb, SLONG id, bool return_deleted)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ l o o k u p _ r e l a t i o n _ i d
*
**************************************
*
* Functional description
* Lookup relation by id. Make sure it really exists.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// System relations are above suspicion
2001-05-23 15:26:42 +02:00
if (id < (int) rel_MAX)
{
2004-11-01 04:18:42 +01:00
fb_assert(id < MAX_USHORT);
2001-05-23 15:26:42 +02:00
return MET_relation(tdbb, (USHORT) id);
}
jrd_rel* check_relation = NULL;
jrd_rel* relation;
vec<jrd_rel*>* vector = dbb->dbb_relations;
2008-12-18 11:57:12 +01:00
if (vector && (id < (SLONG) vector->count()) && (relation = (*vector)[id]))
2001-05-23 15:26:42 +02:00
{
if (relation->rel_flags & REL_deleted)
{
2008-01-16 10:29:37 +01:00
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);
2001-05-23 15:26:42 +02:00
}
else
{
2008-01-16 10:29:37 +01:00
return relation;
2001-05-23 15:26:42 +02:00
}
}
2009-11-21 04:23:47 +01:00
// We need to look up the relation id in RDB$RELATIONS
2001-05-23 15:26:42 +02:00
relation = NULL;
jrd_req* request = CMP_find_request(tdbb, irq_l_rel_id, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
X IN RDB$RELATIONS WITH X.RDB$RELATION_ID EQ id
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_l_rel_id))
REQUEST(irq_l_rel_id) = request;
2001-05-23 15:26:42 +02:00
relation = MET_relation(tdbb, X.RDB$RELATION_ID);
if (relation->rel_name.length() == 0) {
2008-07-01 03:12:02 +02:00
relation->rel_name = X.RDB$RELATION_NAME;
}
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_l_rel_id))
REQUEST(irq_l_rel_id) = request;
2009-11-07 12:58:54 +01:00
if (check_relation)
{
check_relation->rel_flags &= ~REL_check_existence;
2009-11-07 12:58:54 +01:00
if (check_relation != relation)
{
2001-05-23 15:26:42 +02:00
LCK_release(tdbb, check_relation->rel_existence_lock);
LCK_release(tdbb, check_relation->rel_partners_lock);
check_relation->rel_flags &= ~REL_check_partners;
2001-05-23 15:26:42 +02:00
check_relation->rel_flags |= REL_deleted;
}
}
return relation;
}
jrd_nod* MET_parse_blob(thread_db* tdbb,
2003-12-22 11:00:59 +01:00
jrd_rel* relation,
bid* blob_id,
CompilerScratch** csb_ptr,
jrd_req** request_ptr,
const bool trigger)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ p a r s e _ b l o b
*
**************************************
*
* Functional description
* Parse blr, returning a compiler scratch block with the results.
*
* if ignore_perm is true then, the request generated must be set to
* ignore all permissions checks. In this case, we call PAR_blr
* passing it the csb_ignore_perm flag to generate a request
* which must go through without checking any permissions.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id);
SLONG length = blob->blb_length + 10;
Firebird::HalfStaticArray<UCHAR, 512> tmp;
UCHAR* temp = tmp.getBuffer(length);
length = BLB_get_data(tdbb, blob, temp, length);
2001-05-23 15:26:42 +02:00
jrd_nod* node = PAR_blr(tdbb, relation, temp, length, NULL, csb_ptr, request_ptr, trigger, 0);
2001-05-23 15:26:42 +02:00
return node;
}
void MET_parse_sys_trigger(thread_db* tdbb, jrd_rel* relation)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ p a r s e _ s y s _ t r i g g e r
*
**************************************
*
* Functional description
* Parse the blr for a system relation's triggers.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
relation->rel_flags &= ~REL_sys_triggers;
// Release any triggers in case of a rescan
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
if (dbb->dbb_flags & DBB_read_only)
{
return;
}
relation->rel_flags |= REL_sys_trigs_being_loaded;
2008-12-18 11:57:12 +01:00
jrd_req* trigger_request = CMP_find_request(tdbb, irq_s_triggers2, IRQ_REQUESTS);
2005-05-28 00:45:31 +02:00
2008-12-18 11:57:12 +01:00
FOR (REQUEST_HANDLE trigger_request)
TRG IN RDB$TRIGGERS
WITH TRG.RDB$RELATION_NAME = relation->rel_name.c_str()
AND TRG.RDB$SYSTEM_FLAG = 1
2008-12-18 11:57:12 +01:00
if (!REQUEST (irq_s_triggers2))
REQUEST (irq_s_triggers2) = trigger_request;
const FB_UINT64 type = TRG.RDB$TRIGGER_TYPE;
const USHORT trig_flags = TRG.RDB$FLAGS;
const TEXT* name = TRG.RDB$TRIGGER_NAME;
2008-12-18 11:57:12 +01:00
trig_vec** ptr;
2009-01-20 09:33:59 +01:00
switch (type)
{
2001-05-23 15:26:42 +02:00
case 1:
ptr = &relation->rel_pre_store;
break;
case 2:
ptr = &relation->rel_post_store;
break;
case 3:
ptr = &relation->rel_pre_modify;
break;
case 4:
ptr = &relation->rel_post_modify;
break;
case 5:
ptr = &relation->rel_pre_erase;
break;
case 6:
ptr = &relation->rel_post_erase;
break;
default:
ptr = NULL;
break;
}
if (ptr)
{
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, &TRG.RDB$TRIGGER_BLR);
SLONG length = blob->blb_length + 10;
Firebird::HalfStaticArray<UCHAR, 128> blr;
length = BLB_get_data(tdbb, blob, blr.getBuffer(length), length);
2008-12-18 11:57:12 +01:00
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;
2008-12-18 11:57:12 +01:00
jrd_req* request = NULL;
{
2008-01-29 11:11:52 +01:00
Jrd::ContextPoolHolder context(tdbb, dbb->createPool());
PAR_blr(tdbb, relation, blr.begin(), length, NULL, NULL,
&request, true, par_flags);
}
2001-05-23 15:26:42 +02:00
request->req_trg_name = name;
2001-05-23 15:26:42 +02:00
request->req_flags |= req_sys_trigger;
if (trig_flags & TRG_ignore_perm)
{
request->req_flags |= req_ignore_perm;
}
save_trigger_data(tdbb, ptr, relation, request, NULL, NULL, NULL, type, true, 0, "", "",
NULL);
2001-05-23 15:26:42 +02:00
}
2008-12-18 11:57:12 +01:00
END_FOR;
2008-12-18 11:57:12 +01:00
if (!REQUEST (irq_s_triggers2))
REQUEST (irq_s_triggers2) = trigger_request;
2001-05-23 15:26:42 +02:00
relation->rel_flags &= ~REL_sys_trigs_being_loaded;
}
void MET_post_existence(thread_db* tdbb, jrd_rel* relation)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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));
}
2001-05-23 15:26:42 +02:00
}
void MET_prepare(thread_db* tdbb, jrd_tra* transaction, USHORT length, const UCHAR* msg)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2005-05-28 00:45:31 +02:00
* M E T _ p r e p a r e
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Post a transaction description to RDB$TRANSACTIONS.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_s_trans, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
STORE(REQUEST_HANDLE request) X IN RDB$TRANSACTIONS
X.RDB$TRANSACTION_ID = transaction->tra_number;
2008-12-18 11:57:12 +01:00
X.RDB$TRANSACTION_STATE = RDB$TRANSACTIONS.RDB$TRANSACTION_STATE.LIMBO;
blb* blob = BLB_create(tdbb, dbb->dbb_sys_trans, &X.RDB$TRANSACTION_DESCRIPTION);
2001-05-23 15:26:42 +02:00
BLB_put_segment(tdbb, blob, msg, length);
BLB_close(tdbb, blob);
END_STORE;
if (!REQUEST(irq_s_trans))
REQUEST(irq_s_trans) = request;
}
jrd_prc* MET_procedure(thread_db* tdbb, int id, bool noscan, USHORT flags)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ p r o c e d u r e
*
**************************************
*
* Functional description
* Find or create a procedure block for a given procedure id.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
vec<jrd_prc*>* vector = dbb->dbb_procedures;
if (!vector)
2001-05-23 15:26:42 +02:00
{
vector = dbb->dbb_procedures = vec<jrd_prc*>::newVector(*dbb->dbb_permanent, id + 10);
2001-05-23 15:26:42 +02:00
}
else if (id >= (int) vector->count())
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
vector->resize(id + 10);
2001-05-23 15:26:42 +02:00
}
Database::CheckoutLockGuard guard(dbb, dbb->dbb_meta_mutex);
2005-05-28 00:45:31 +02:00
jrd_prc* procedure = (*vector)[id];
if (procedure && !(procedure->prc_flags & PRC_obsolete))
2001-05-23 15:26:42 +02:00
{
// Make sure PRC_being_scanned and PRC_scanned are not set at the same time
2008-12-18 11:57:12 +01:00
fb_assert(!(procedure->prc_flags & PRC_being_scanned) || !(procedure->prc_flags & PRC_scanned));
2001-05-23 15:26:42 +02:00
/* To avoid scanning recursive procedure's blr recursively let's
make use of PRC_being_scanned bit. Because this bit is set
later in the code, it is not set when we are here first time.
2005-05-28 00:45:31 +02:00
If (in case of rec. procedure) we get here second time it is
2001-05-23 15:26:42 +02:00
already set and we return half baked procedure.
In case of superserver this code is under the rec. mutex
protection, thus the only guy (thread) who can get here and see
PRC_being_scanned bit set is the guy which started procedure scan
and currently holds the mutex.
2005-05-28 00:45:31 +02:00
In case of classic, there is always only one guy and if it
2001-05-23 15:26:42 +02:00
sees PRC_being_scanned bit set it is safe to assume it is here
second time.
If procedure has already been scanned - return. This condition
is for those threads that did not find procedure in cache and
2001-05-23 15:26:42 +02:00
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.
2001-05-23 15:26:42 +02:00
*/
2008-12-18 11:57:12 +01:00
if ((procedure->prc_flags & PRC_being_scanned) || (procedure->prc_flags & PRC_scanned))
2001-05-23 15:26:42 +02:00
{
return procedure;
}
}
if (!procedure)
2001-05-23 15:26:42 +02:00
{
procedure = FB_NEW(*dbb->dbb_permanent) jrd_prc(*dbb->dbb_permanent);
2001-05-23 15:26:42 +02:00
}
2001-12-24 03:51:06 +01:00
try {
2001-05-23 15:26:42 +02:00
procedure->prc_flags |= (PRC_being_scanned | flags);
procedure->prc_flags &= ~PRC_obsolete;
2005-05-28 00:45:31 +02:00
2001-05-23 15:26:42 +02:00
procedure->prc_id = id;
(*vector)[id] = procedure;
2001-05-23 15:26:42 +02:00
2009-11-07 12:58:54 +01:00
if (!procedure->prc_existence_lock)
{
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, 0) Lock;
procedure->prc_existence_lock = lock;
lock->lck_parent = dbb->dbb_lock;
lock->lck_dbb = dbb;
lock->lck_key.lck_long = procedure->prc_id;
lock->lck_length = sizeof(lock->lck_key.lck_long);
lock->lck_type = LCK_prc_exist;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
lock->lck_object = procedure;
lock->lck_ast = blocking_ast_procedure;
}
2001-05-23 15:26:42 +02:00
LCK_lock(tdbb, procedure->prc_existence_lock, LCK_SR, LCK_WAIT);
if (!noscan)
{
// CVC: Where is this set to false? I made it const to draw attention here.
const bool valid_blr = true;
2001-05-23 15:26:42 +02:00
2007-07-04 18:07:55 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_r_procedure, IRQ_REQUESTS);
FOR(REQUEST_HANDLE request)
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ procedure->prc_id
2001-05-23 15:26:42 +02:00
if (!REQUEST(irq_r_procedure))
2001-05-23 15:26:42 +02:00
{
REQUEST(irq_r_procedure) = request;
2001-05-23 15:26:42 +02:00
}
if (procedure->prc_name.toString().length() == 0)
{
procedure->prc_name = QualifiedName(P.RDB$PROCEDURE_NAME,
(P.RDB$PACKAGE_NAME.NULL ? "" : P.RDB$PACKAGE_NAME));
}
procedure->prc_id = P.RDB$PROCEDURE_ID;
if (!P.RDB$SECURITY_CLASS.NULL)
{
procedure->prc_security_name = 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->prc_security_name = PKG.RDB$SECURITY_CLASS;
}
END_FOR
}
if (P.RDB$SYSTEM_FLAG.NULL || !P.RDB$SYSTEM_FLAG)
{
procedure->prc_flags &= ~PRC_system;
}
else
{
procedure->prc_flags |= PRC_system;
}
if ( (procedure->prc_inputs = P.RDB$PROCEDURE_INPUTS) )
{
procedure->prc_input_fields =
vec<Parameter*>::newVector(*dbb->dbb_permanent, procedure->prc_input_fields,
P.RDB$PROCEDURE_INPUTS);
}
if ( (procedure->prc_outputs = P.RDB$PROCEDURE_OUTPUTS) )
{
procedure->prc_output_fields =
vec<Parameter*>::newVector(*dbb->dbb_permanent, procedure->prc_output_fields,
P.RDB$PROCEDURE_OUTPUTS);
2001-05-23 15:26:42 +02:00
}
vec<Parameter*>* paramVector = 0;
2004-01-16 13:59:16 +01:00
procedure->prc_defaults = 0;
2007-07-04 18:07:55 +02:00
jrd_req* request2 = CMP_find_request(tdbb, irq_r_params, IRQ_REQUESTS);
2009-10-30 11:50:59 +01:00
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
2009-10-30 11:50:59 +01:00
PA.RDB$PACKAGE_NAME EQUIV NULLIF(packageName.c_str(), '')
2001-05-23 15:26:42 +02:00
if (!REQUEST(irq_r_params))
REQUEST(irq_r_params) = request2;
2001-05-23 15:26:42 +02:00
2009-11-27 07:59:21 +01:00
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.RDB$DEFAULT_VALUE;
if (PA.RDB$PARAMETER_TYPE) {
paramVector = procedure->prc_output_fields;
}
else {
paramVector = procedure->prc_input_fields;
}
// should be error if field already exists
2008-12-18 11:57:12 +01:00
Parameter* parameter = FB_NEW(*dbb->dbb_permanent) Parameter(*dbb->dbb_permanent);
parameter->prm_number = PA.RDB$PARAMETER_NUMBER;
(*paramVector)[parameter->prm_number] = parameter;
parameter->prm_name = PA.RDB$PARAMETER_NAME;
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(&parameter->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));
2004-01-16 13:59:16 +01:00
if (PA.RDB$PARAMETER_TYPE == 0 &&
(!pa_default_value_null ||
2006-09-01 03:40:22 +02:00
(fb_utils::implicit_domain(F.RDB$FIELD_NAME) && !F.RDB$DEFAULT_VALUE.NULL)))
{
2004-01-16 13:59:16 +01:00
procedure->prc_defaults++;
2008-01-29 11:11:52 +01:00
Jrd::ContextPoolHolder context(tdbb, dbb->createPool());
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5);
2009-11-27 07:59:21 +01:00
// CVC: It seems we need another try/catch block here.
if (!pa_default_value_null)
{
parameter->prm_default_value =
parse_param_blr(tdbb, procedure, &pa_default_value, csb);
}
else
{
parameter->prm_default_value =
parse_param_blr(tdbb, procedure, &F.RDB$DEFAULT_VALUE, csb);
}
2005-05-28 00:45:31 +02:00
delete csb;
2004-01-16 13:59:16 +01:00
}
END_FOR;
2009-11-28 02:44:42 +01:00
if (!REQUEST(irq_r_params))
REQUEST(irq_r_params) = request2;
if ((paramVector = procedure->prc_output_fields) && (*paramVector)[0])
2001-05-23 15:26:42 +02:00
{
Format* format = procedure->prc_format =
2004-03-30 06:10:52 +02:00
Format::newFormat(*dbb->dbb_permanent, procedure->prc_outputs);
ULONG length = FLAG_BYTES(format->fmt_count);
Format::fmt_desc_iterator desc = format->fmt_desc.begin();
vec<Parameter*>::iterator ptr, end;
2008-12-18 11:57:12 +01:00
for (ptr = paramVector->begin(), end = paramVector->end(); ptr < end; ++ptr, ++desc)
2001-05-23 15:26:42 +02:00
{
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;
2009-11-27 07:59:21 +01:00
length = MET_align(&(*desc), length);
desc->dsc_address = (UCHAR *) (IPTR) length;
length += desc->dsc_length;
}
2001-05-23 15:26:42 +02:00
}
format->fmt_length = (USHORT) length;
2001-05-23 15:26:42 +02:00
}
prc_t prc_type = prc_legacy;
2009-11-27 07:59:21 +01:00
const bool external = !P.RDB$ENGINE_NAME.NULL; // ODS_12_0
2008-01-29 11:11:52 +01:00
MemoryPool* csb_pool = dbb->createPool();
2009-11-27 07:59:21 +01:00
{ // scope
Jrd::ContextPoolHolder context(tdbb, csb_pool);
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5);
jrd_req* request4 = CMP_find_request(tdbb, irq_p_type, IRQ_REQUESTS);
2007-07-04 18:07:55 +02:00
FOR(REQUEST_HANDLE request4)
PT IN RDB$PROCEDURES
WITH PT.RDB$PROCEDURE_ID EQ procedure->prc_id
if (!REQUEST(irq_p_type))
REQUEST(irq_p_type) = request4;
if (!PT.RDB$PROCEDURE_TYPE.NULL)
prc_type = (prc_t) PT.RDB$PROCEDURE_TYPE;
if (!PT.RDB$DEBUG_INFO.NULL)
DBG_parse_debug_info(tdbb, &PT.RDB$DEBUG_INFO, csb->csb_dbg_info);
END_FOR;
2009-11-28 02:44:42 +01:00
if (!REQUEST(irq_p_type))
REQUEST(irq_p_type) = request4;
procedure->prc_type = prc_type;
try
{
parse_procedure_blr(tdbb, procedure,
(P.RDB$PROCEDURE_BLR.NULL ? NULL : &P.RDB$PROCEDURE_BLR), csb, external);
}
2009-01-20 09:33:59 +01:00
catch (const Firebird::Exception&)
2006-11-03 10:42:42 +01:00
{
delete csb;
2009-11-07 12:58:54 +01:00
if (procedure->prc_request)
{
2006-11-03 10:42:42 +01:00
CMP_release(tdbb, procedure->prc_request);
procedure->prc_request = NULL;
}
else {
2008-01-29 11:11:52 +01:00
dbb->deletePool(csb_pool);
2006-11-03 10:42:42 +01:00
}
ERR_post(Arg::Gds(isc_bad_proc_BLR) << Arg::Str(procedure->prc_name.toString()));
2006-11-03 10:42:42 +01:00
}
procedure->prc_request->req_procedure = procedure;
for (size_t i = 0; i < csb->csb_rpt.getCount(); i++)
2001-05-23 15:26:42 +02:00
{
jrd_nod* node = csb->csb_rpt[i].csb_message;
if (node)
{
2008-02-14 13:21:35 +01:00
/*
if ((int) (IPTR) node->nod_arg[e_msg_number] == 0)
{
// Never used.
procedure->prc_input_msg = node;
}
2008-02-14 13:21:35 +01:00
else
*/
if ((int) (IPTR) node->nod_arg[e_msg_number] == 1)
{
procedure->prc_output_msg = node;
}
}
2001-05-23 15:26:42 +02:00
}
delete csb;
2009-11-27 07:59:21 +01:00
} // scope
if (external)
{
HalfStaticArray<char, 512> body;
if (!P.RDB$PROCEDURE_SOURCE.NULL)
{
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, &P.RDB$PROCEDURE_SOURCE);
ULONG len = BLB_get_data(tdbb, blob,
(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';
procedure->prc_external = dbb->dbb_extManager.makeProcedure(
tdbb, procedure, P.RDB$ENGINE_NAME,
(P.RDB$ENTRYPOINT.NULL ? "" : P.RDB$ENTRYPOINT), body.begin()); // ODS_12_0
}
END_FOR;
if (!REQUEST(irq_r_procedure)) {
REQUEST(irq_r_procedure) = request;
2001-05-23 15:26:42 +02:00
}
procedure->prc_flags |= PRC_scanned;
2001-05-23 15:26:42 +02:00
// CVC: This condition is always false because valid_blr is always true.
if (!(dbb->dbb_flags & DBB_read_only) && !valid_blr)
{
// if the BLR was marked as invalid but the procedure was compiled,
// mark the BLR as valid
2006-08-30 06:54:44 +02:00
jrd_req* request5 = NULL;
2006-08-30 06:54:44 +02:00
FOR(REQUEST_HANDLE request5)
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ procedure->prc_id
MODIFY P USING
P.RDB$VALID_BLR = TRUE;
P.RDB$VALID_BLR.NULL = FALSE;
END_MODIFY;
END_FOR;
2006-08-30 06:54:44 +02:00
CMP_release(tdbb, request5);
}
} // if !noscan
2005-05-28 00:45:31 +02:00
// Make sure that it is really being scanned
2003-11-04 00:59:24 +01:00
fb_assert(procedure->prc_flags & PRC_being_scanned);
2005-05-28 00:45:31 +02:00
2001-05-23 15:26:42 +02:00
procedure->prc_flags &= ~PRC_being_scanned;
2001-12-24 03:51:06 +01:00
} // try
2009-11-07 12:58:54 +01:00
catch (const Firebird::Exception&)
{
2001-12-24 03:51:06 +01:00
procedure->prc_flags &= ~(PRC_being_scanned | PRC_scanned);
if (procedure->prc_existence_lock)
{
LCK_release(tdbb, procedure->prc_existence_lock);
procedure->prc_existence_lock = NULL;
2001-12-24 03:51:06 +01:00
}
throw;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
return procedure;
}
jrd_rel* MET_relation(thread_db* tdbb, USHORT id)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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();
2001-05-23 15:26:42 +02:00
CHECK_DBB(dbb);
vec<jrd_rel*>* vector = dbb->dbb_relations;
2001-12-24 03:51:06 +01:00
if (!vector)
2001-05-23 15:26:42 +02:00
{
vector = dbb->dbb_relations = vec<jrd_rel*>::newVector(*dbb->dbb_permanent, id + 10);
2001-05-23 15:26:42 +02:00
}
2001-12-24 03:51:06 +01:00
else if (id >= vector->count())
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
vector->resize(id + 10);
2001-05-23 15:26:42 +02:00
}
jrd_rel* relation = (*vector)[id];
if (relation) {
2001-05-23 15:26:42 +02:00
return relation;
}
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// 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;
2001-05-23 15:26:42 +02:00
relation = FB_NEW(*dbb->dbb_permanent) jrd_rel(*dbb->dbb_permanent);
(*vector)[id] = relation;
2001-05-23 15:26:42 +02:00
relation->rel_id = id;
2005-07-08 05:25:31 +02:00
{ // Scope block.
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, 0) Lock;
relation->rel_partners_lock = lock;
lock->lck_parent = dbb->dbb_lock;
lock->lck_dbb = dbb;
lock->lck_key.lck_long = relation->rel_id;
lock->lck_length = sizeof(lock->lck_key.lck_long);
lock->lck_type = LCK_rel_partners;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
lock->lck_object = relation;
lock->lck_ast = partners_ast_relation;
}
// This should check system flag instead.
if (relation->rel_id <= max_sys_rel) {
2001-05-23 15:26:42 +02:00
return relation;
}
2001-05-23 15:26:42 +02:00
2005-07-08 05:25:31 +02:00
{ // Scope block.
Lock* lock = FB_NEW_RPT(*dbb->dbb_permanent, 0) Lock;
relation->rel_existence_lock = lock;
lock->lck_parent = dbb->dbb_lock;
lock->lck_dbb = dbb;
lock->lck_key.lck_long = relation->rel_id;
lock->lck_length = sizeof(lock->lck_key.lck_long);
lock->lck_type = LCK_rel_exist;
lock->lck_owner_handle = LCK_get_owner_handle(tdbb, lock->lck_type);
lock->lck_object = relation;
lock->lck_ast = blocking_ast_relation;
}
2001-05-23 15:26:42 +02:00
2008-03-04 14:13:19 +01:00
relation->rel_flags |= (REL_check_existence | REL_check_partners);
2001-05-23 15:26:42 +02:00
return relation;
}
bool MET_relation_default_class(thread_db* tdbb, const Firebird::MetaName& relation_name,
2008-12-18 11:57:12 +01:00
const Firebird::MetaName& default_security_class_name)
2002-07-01 18:59:09 +02:00
{
/**************************************
*
* M E T _ r e l a t i o n _ d e f a u l t _ c l a s s
*
**************************************
*
* Functional description
* Checks that a given security class is the default for
* a given relation, returning TRUE if there's a match.
2003-12-22 11:00:59 +01:00
* It can be made obsolete in the future if jrd_rel struct
2002-07-01 18:59:09 +02:00
* gets another field, although metadata loading order
* would not be safe when compared with this function.
*
**************************************/
2008-12-18 11:57:12 +01:00
SET_TDBB (tdbb);
Database* dbb = tdbb->getDatabase();
CHECK_DBB (dbb);
2005-05-28 00:45:31 +02:00
bool found = false;
2008-12-18 11:57:12 +01:00
jrd_req* request = CMP_find_request (tdbb, irq_l_relation_defsec, IRQ_REQUESTS);
2005-05-28 00:45:31 +02:00
2008-12-18 11:57:12 +01:00
FOR (REQUEST_HANDLE request)
REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ relation_name.c_str()
2002-07-01 18:59:09 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST (irq_l_relation_defsec))
REQUEST (irq_l_relation_defsec) = request;
2002-07-01 18:59:09 +02:00
2009-11-07 12:58:54 +01:00
if (!REL.RDB$DEFAULT_CLASS.NULL)
{
2008-12-18 11:57:12 +01:00
if (default_security_class_name == REL.RDB$DEFAULT_CLASS)
{
2008-12-18 11:57:12 +01:00
found = true;
}
}
2002-07-01 18:59:09 +02:00
2008-12-18 11:57:12 +01:00
END_FOR;
2002-07-01 18:59:09 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST (irq_l_relation_defsec))
REQUEST (irq_l_relation_defsec) = request;
2002-07-01 18:59:09 +02:00
2008-12-18 11:57:12 +01:00
return found;
2002-07-01 18:59:09 +02:00
}
void MET_release_existence(thread_db* tdbb, jrd_rel* relation)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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--;
}
2001-05-23 15:26:42 +02:00
2009-11-07 12:58:54 +01:00
if (!relation->rel_use_count)
{
if (relation->rel_flags & REL_blocking) {
LCK_re_post(tdbb, relation->rel_existence_lock);
}
2009-11-07 12:58:54 +01:00
if (relation->rel_file)
{
// close external file
EXT_fini(relation, true);
}
}
2001-05-23 15:26:42 +02:00
}
void MET_remove_procedure(thread_db* tdbb, int id, jrd_prc* procedure)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ r e m o v e _ p r o c e d u r e
*
**************************************
*
* Functional description
* Remove a procedure from cache
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
vec<jrd_prc*>* pvector = dbb->dbb_procedures;
if (!pvector) {
2002-09-28 22:58:40 +02:00
return;
}
2002-09-28 22:58:40 +02:00
2009-11-07 12:58:54 +01:00
if (!procedure)
{
2009-11-21 04:23:47 +01:00
// If we are in here then dfw.epp/modify_procedure() called us
if (!(procedure = (*pvector)[id]))
2001-05-23 15:26:42 +02:00
return;
}
2005-05-28 00:45:31 +02:00
2009-11-21 04:23:47 +01:00
// MET_procedure locks it. Lets unlock it now to avoid troubles later
if (procedure->prc_existence_lock)
{
LCK_release(tdbb, procedure->prc_existence_lock);
}
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// Procedure that is being altered may have references
// to it by other procedures via pointer to current meta
// data structure, so don't loose the structure or the pointer.
2008-12-18 11:57:12 +01:00
if ((procedure == (*pvector)[id]) && !(procedure->prc_flags & PRC_being_altered))
{
(*pvector)[id] = NULL;
}
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// deallocate all structure which were allocated. The procedure
// blr is originally read into a new pool from which all request
// are allocated. That will not be freed up.
2001-05-23 15:26:42 +02:00
2009-11-07 12:58:54 +01:00
if (procedure->prc_existence_lock)
{
2001-12-24 03:51:06 +01:00
delete procedure->prc_existence_lock;
procedure->prc_existence_lock = NULL;
}
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// deallocate input param structures
vec<Parameter*>* vector;
2009-11-07 12:58:54 +01:00
if ((procedure->prc_inputs) && (vector = procedure->prc_input_fields))
{
2006-08-30 06:54:44 +02:00
for (int i = 0; i < procedure->prc_inputs; i++)
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
if ((*vector)[i])
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
delete (*vector)[i];
2001-05-23 15:26:42 +02:00
}
}
2001-12-24 03:51:06 +01:00
delete vector;
procedure->prc_inputs = 0;
procedure->prc_input_fields = NULL;
2001-05-23 15:26:42 +02:00
}
2009-11-21 04:23:47 +01:00
// deallocate output param structures
2001-05-23 15:26:42 +02:00
2009-11-07 12:58:54 +01:00
if ((procedure->prc_outputs) && (vector = procedure->prc_output_fields))
{
2006-08-30 06:54:44 +02:00
for (int i = 0; i < procedure->prc_outputs; i++)
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
if ((*vector)[i])
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
delete (*vector)[i];
2001-05-23 15:26:42 +02:00
}
}
2001-12-24 03:51:06 +01:00
delete vector;
procedure->prc_outputs = 0;
procedure->prc_output_fields = NULL;
2001-05-23 15:26:42 +02:00
}
if (!procedure->prc_use_count)
{
if (procedure->prc_format)
{
delete procedure->prc_format;
procedure->prc_format = NULL;
}
}
2001-05-23 15:26:42 +02:00
if (!(procedure->prc_flags & PRC_being_altered) && !procedure->prc_use_count)
2001-05-23 15:26:42 +02:00
{
2001-12-24 03:51:06 +01:00
delete procedure;
2001-05-23 15:26:42 +02:00
}
else
{
2005-05-28 00:45:31 +02:00
// Fully clear procedure block. Some pieces of code check for empty
// procedure name and ID, this is why we do it.
procedure->prc_name = QualifiedName();
procedure->prc_security_name = "";
procedure->prc_defaults = 0;
procedure->prc_id = 0;
2001-05-23 15:26:42 +02:00
}
}
void MET_revoke(thread_db* tdbb,
2003-12-22 11:00:59 +01:00
jrd_tra* transaction,
const TEXT* relation,
const TEXT* revokee,
const TEXT* privilege)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ r e v o k e
*
**************************************
*
* Functional description
* Execute a recursive revoke. This is called only when
* a revoked privilege had the grant option.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// See if the revokee still has the privilege. If so, there's nothing to do
2001-05-23 15:26:42 +02:00
USHORT count = 0;
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_revoke1, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
FIRST 1 P IN RDB$USER_PRIVILEGES WITH
P.RDB$RELATION_NAME EQ relation AND
P.RDB$PRIVILEGE EQ privilege AND
2005-05-28 00:45:31 +02:00
P.RDB$USER EQ revokee
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_revoke1))
2001-05-23 15:26:42 +02:00
REQUEST(irq_revoke1) = request;
++count;
END_FOR;
if (!REQUEST(irq_revoke1))
REQUEST(irq_revoke1) = request;
if (count)
return;
request = CMP_find_request(tdbb, irq_revoke2, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// User lost privilege. Take it away from anybody he/she gave it to.
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
P IN RDB$USER_PRIVILEGES WITH
P.RDB$RELATION_NAME EQ relation AND
P.RDB$PRIVILEGE EQ privilege AND
2005-05-28 00:45:31 +02:00
P.RDB$GRANTOR EQ revokee
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_revoke2))
REQUEST(irq_revoke2) = request;
2001-05-23 15:26:42 +02:00
ERASE P;
END_FOR;
if (!REQUEST(irq_revoke2))
REQUEST(irq_revoke2) = request;
}
void MET_scan_relation(thread_db* tdbb, jrd_rel* relation)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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
2001-05-23 15:26:42 +02:00
* expressions, and validation expressions.
*
**************************************/
SET_TDBB(tdbb);
trig_vec* triggers[TRIGGER_MAX];
Database* dbb = tdbb->getDatabase();
Jrd::ContextPoolHolder context(tdbb, dbb->dbb_permanent);
bool dependencies = false;
bool sys_triggers = false;
2001-05-23 15:26:42 +02:00
blb* blob = NULL;
jrd_req* request = NULL;
jrd_tra* depTrans = (tdbb->getTransaction() ? tdbb->getTransaction() : dbb->dbb_sys_trans);
2009-11-21 04:23:47 +01:00
// If anything errors, catch it to reset the scan flag. This will
// make sure that the error will be caught if the operation is tried again.
try {
Database::CheckoutLockGuard guard(dbb, dbb->dbb_meta_mutex);
2001-05-23 15:26:42 +02:00
2008-02-06 01:43:54 +01:00
if (relation->rel_flags & REL_scanned || relation->rel_flags & REL_deleted)
2004-12-24 09:52:39 +01:00
{
2001-05-23 15:26:42 +02:00
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;
2001-05-23 15:26:42 +02:00
relation->rel_flags &= ~(REL_get_dependencies | REL_sys_triggers);
2008-02-06 01:43:54 +01:00
for (USHORT itr = 0; itr < TRIGGER_MAX; ++itr) {
triggers[itr] = NULL;
}
2001-12-24 03:51:06 +01:00
2009-11-21 04:23:47 +01:00
// Since this can be called recursively, find an inactive clone of the request
2001-05-23 15:26:42 +02:00
request = CMP_find_request(tdbb, irq_r_fields, IRQ_REQUESTS);
CompilerScratch* csb = NULL;
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
REL IN RDB$RELATIONS WITH REL.RDB$RELATION_ID EQ relation->rel_id
2009-11-21 04:23:47 +01:00
// Pick up relation level stuff
2001-12-24 03:51:06 +01:00
if (!REQUEST(irq_r_fields)) {
2005-04-28 08:12:08 +02:00
REQUEST(irq_r_fields) = request;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
relation->rel_current_fmt = REL.RDB$FORMAT;
vec<jrd_fld*>* vector = relation->rel_fields =
2008-12-18 11:57:12 +01:00
vec<jrd_fld*>::newVector(*dbb->dbb_permanent, relation->rel_fields, REL.RDB$FIELD_ID + 1);
2001-12-24 03:51:06 +01:00
if (!REL.RDB$SECURITY_CLASS.NULL) {
relation->rel_security_name = REL.RDB$SECURITY_CLASS;
2001-12-24 03:51:06 +01:00
}
relation->rel_name = REL.RDB$RELATION_NAME;
relation->rel_owner_name = REL.RDB$OWNER_NAME;
2001-05-23 15:26:42 +02:00
if (REL.RDB$FLAGS & REL_sql)
{
relation->rel_flags |= REL_sql_relation;
}
2006-05-22 00:07:35 +02:00
if (!REL.RDB$VIEW_BLR.isEmpty())
2001-05-23 15:26:42 +02:00
{
2009-11-21 04:23:47 +01:00
// parse the view blr, getting dependencies on relations, etc. at the same time
2001-05-23 15:26:42 +02:00
if (dependencies)
{
Firebird::MetaName depName(REL.RDB$RELATION_NAME);
2001-05-23 15:26:42 +02:00
relation->rel_view_rse =
(RecordSelExpr*) MET_get_dependencies(tdbb, relation, NULL, 0, NULL,
2005-05-28 00:45:31 +02:00
&REL.RDB$VIEW_BLR, NULL,
&csb, depName, obj_view, 0, depTrans);
2001-05-23 15:26:42 +02:00
}
else
{
relation->rel_view_rse =
(RecordSelExpr*) MET_parse_blob(tdbb, relation,
2008-12-18 11:57:12 +01:00
&REL.RDB$VIEW_BLR, &csb, NULL, false);
2001-05-23 15:26:42 +02:00
}
2009-11-21 04:23:47 +01:00
// retrieve the view context names
2001-05-23 15:26:42 +02:00
lookup_view_contexts(tdbb, relation);
}
relation->rel_flags |= REL_scanned;
if (REL.RDB$EXTERNAL_FILE[0])
{
2009-04-28 15:08:04 +02:00
EXT_file(relation, REL.RDB$EXTERNAL_FILE); //, &REL.RDB$EXTERNAL_DESCRIPTION);
2001-05-23 15:26:42 +02:00
}
2009-11-27 07:59:21 +01:00
if (!REL.RDB$RELATION_TYPE.NULL)
{
switch (REL.RDB$RELATION_TYPE)
{
case rel_persistent:
break;
case rel_external:
fb_assert(relation->rel_file);
break;
case rel_view:
fb_assert(relation->rel_view_rse);
break;
case rel_virtual:
relation->rel_flags |= REL_virtual;
break;
case rel_global_temp_preserve:
relation->rel_flags |= REL_temp_conn;
break;
case rel_global_temp_delete:
relation->rel_flags |= REL_temp_tran;
break;
default:
fb_assert(false);
}
}
2009-11-21 04:23:47 +01:00
// Pick up field specific stuff
2001-05-23 15:26:42 +02:00
blob = BLB_open(tdbb, dbb->dbb_sys_trans, &REL.RDB$RUNTIME);
2004-05-26 18:12:05 +02:00
Firebird::HalfStaticArray<UCHAR, 256> temp;
UCHAR* const buffer = temp.getBuffer(blob->blb_max_segment + 1);
2001-05-23 15:26:42 +02:00
jrd_fld* field = NULL;
ArrayField* array = 0;
USHORT view_context = 0;
USHORT field_id = 0;
2001-05-23 15:26:42 +02:00
for (;;)
{
2008-12-18 11:57:12 +01:00
USHORT length = BLB_get_segment(tdbb, blob, buffer, blob->blb_max_segment);
2001-05-23 15:26:42 +02:00
if (blob->blb_flags & BLB_eof)
{
break;
}
USHORT n;
2001-05-23 15:26:42 +02:00
buffer[length] = 0;
UCHAR* p = (UCHAR*) &n;
const UCHAR* q = buffer + 1;
2001-05-23 15:26:42 +02:00
while (q < buffer + 1 + sizeof(SSHORT))
{
*p++ = *q++;
}
p = buffer + 1;
--length;
2009-04-03 12:07:55 +02:00
switch ((rsr_t) buffer[0])
2009-01-20 09:33:59 +01:00
{
2001-05-23 15:26:42 +02:00
case RSR_field_id:
2008-12-18 11:57:12 +01:00
if (field && field->fld_security_name.length() == 0 && !REL.RDB$DEFAULT_CLASS.NULL)
{
field->fld_security_name = REL.RDB$DEFAULT_CLASS;
}
2001-05-23 15:26:42 +02:00
field_id = n;
field = (*vector)[field_id];
2009-11-07 12:58:54 +01:00
if (field)
{
2006-07-23 13:01:20 +02:00
field->fld_computation = NULL;
field->fld_missing_value = NULL;
field->fld_default_value = NULL;
field->fld_validation = NULL;
field->fld_not_null = NULL;
}
2001-05-23 15:26:42 +02:00
array = NULL;
break;
case RSR_field_name:
2009-11-07 12:58:54 +01:00
if (field)
{
2009-11-21 04:23:47 +01:00
// The field exists. If its name hasn't changed, then
// there's no need to copy anything.
2001-05-23 15:26:42 +02:00
if (field->fld_name == reinterpret_cast<char*>(p))
2001-05-23 15:26:42 +02:00
break;
field->fld_name = reinterpret_cast<char*>(p);
2001-05-23 15:26:42 +02:00
}
2009-11-07 12:58:54 +01:00
else
{
field = FB_NEW(*dbb->dbb_permanent) jrd_fld(*dbb->dbb_permanent);
(*vector)[field_id] = field;
field->fld_name = reinterpret_cast<char*>(p);
2001-05-23 15:26:42 +02:00
}
2002-07-01 18:59:09 +02:00
// 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.
2008-12-18 11:57:12 +01:00
if (field->fld_security_name.length() == 0 && !REL.RDB$DEFAULT_CLASS.NULL)
2005-05-22 05:11:41 +02:00
{
field->fld_security_name = REL.RDB$DEFAULT_CLASS;
}
2002-07-01 18:59:09 +02:00
2001-05-23 15:26:42 +02:00
break;
case RSR_view_context:
view_context = n;
break;
case RSR_base_field:
2009-11-07 12:58:54 +01:00
if (dependencies)
{
2006-01-13 10:33:40 +01:00
csb->csb_g_flags |= csb_get_dependencies;
2008-12-18 11:57:12 +01:00
field->fld_source = PAR_make_field(tdbb, csb, view_context, (TEXT*) p);
2007-08-26 11:05:29 +02:00
const Firebird::MetaName depName(REL.RDB$RELATION_NAME);
store_dependencies(tdbb, csb, 0, depName, obj_view, depTrans);
2006-01-13 10:33:40 +01:00
}
else {
2008-12-18 11:57:12 +01:00
field->fld_source = PAR_make_field(tdbb, csb, view_context, (TEXT*) p);
2006-01-13 10:33:40 +01:00
}
2001-05-23 15:26:42 +02:00
break;
case RSR_computed_blr:
2009-02-28 12:57:40 +01:00
field->fld_computation = 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);
2001-05-23 15:26:42 +02:00
break;
case RSR_missing_value:
field->fld_missing_value = PAR_blr(tdbb, relation, p, length, csb,
NULL, NULL, false, 0);
2001-05-23 15:26:42 +02:00
break;
case RSR_default_value:
field->fld_default_value = PAR_blr(tdbb, relation, p, length, csb,
NULL, NULL, false, 0);
2001-05-23 15:26:42 +02:00
break;
case RSR_validation_blr:
2005-04-25 08:54:45 +02:00
// 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
2005-05-28 00:45:31 +02:00
// be parsed without view-context information. With view-context
2005-04-25 08:54:45 +02:00
// information the context-numbers are wrong.
// Because a VIEW can't have a validation section i ignored the whole call.
2005-05-28 00:45:31 +02:00
if (!csb)
{
field->fld_validation = PAR_blr(tdbb, relation, p, length, csb,
NULL, NULL, false, csb_validation);
}
2001-05-23 15:26:42 +02:00
break;
case RSR_field_not_null:
field->fld_not_null = PAR_blr(tdbb, relation, p, length, csb, NULL,
NULL, false, csb_validation);
2001-05-23 15:26:42 +02:00
break;
case RSR_security_class:
field->fld_security_name = (const TEXT*) p;
2001-05-23 15:26:42 +02:00
break;
case RSR_trigger_name:
MET_load_trigger(tdbb, relation, (const TEXT*) p, triggers);
2001-05-23 15:26:42 +02:00
break;
case RSR_dimensions:
field->fld_array = array = FB_NEW_RPT(*dbb->dbb_permanent, n) ArrayField();
array->arr_desc.iad_dimensions = n;
2001-05-23 15:26:42 +02:00
break;
case RSR_array_desc:
if (array)
2008-02-03 11:41:44 +01:00
memcpy(&array->arr_desc, p, length);
2001-05-23 15:26:42 +02:00
break;
2009-11-21 04:23:47 +01:00
default: // Shut up compiler warning
break;
2001-05-23 15:26:42 +02:00
}
}
BLB_close(tdbb, blob);
blob = 0;
2009-11-27 07:59:21 +01:00
if (field && field->fld_security_name.length() == 0 && !REL.RDB$DEFAULT_CLASS.NULL)
2001-05-23 15:26:42 +02:00
{
field->fld_security_name = REL.RDB$DEFAULT_CLASS;
2001-05-23 15:26:42 +02:00
}
2009-11-27 07:59:21 +01:00
2001-05-23 15:26:42 +02:00
END_FOR;
2005-04-28 08:12:08 +02:00
if (!REQUEST(irq_r_fields))
REQUEST(irq_r_fields) = request;
delete csb;
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// release any triggers in case of a rescan, but not if the rescan
// hapenned while system triggers were being loaded.
2001-05-23 15:26:42 +02:00
2009-11-07 12:58:54 +01:00
if (!(relation->rel_flags & REL_sys_trigs_being_loaded))
{
2009-11-21 04:23:47 +01:00
// 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.
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// 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;
2005-05-28 00:45:31 +02:00
2001-05-23 15:26:42 +02:00
tmp_vector = relation->rel_pre_store;
relation->rel_pre_store = triggers[TRIGGER_PRE_STORE];
MET_release_triggers(tdbb, &tmp_vector);
tmp_vector = relation->rel_post_store;
relation->rel_post_store = triggers[TRIGGER_POST_STORE];
MET_release_triggers(tdbb, &tmp_vector);
tmp_vector = relation->rel_pre_erase;
relation->rel_pre_erase = triggers[TRIGGER_PRE_ERASE];
MET_release_triggers(tdbb, &tmp_vector);
tmp_vector = relation->rel_post_erase;
relation->rel_post_erase = triggers[TRIGGER_POST_ERASE];
MET_release_triggers(tdbb, &tmp_vector);
tmp_vector = relation->rel_pre_modify;
relation->rel_pre_modify = triggers[TRIGGER_PRE_MODIFY];
MET_release_triggers(tdbb, &tmp_vector);
tmp_vector = relation->rel_post_modify;
relation->rel_post_modify = triggers[TRIGGER_POST_MODIFY];
MET_release_triggers(tdbb, &tmp_vector);
}
relation->rel_flags &= ~REL_being_scanned;
2008-01-16 10:29:37 +01:00
2001-05-23 15:26:42 +02:00
relation->rel_current_format = NULL;
2001-12-24 03:51:06 +01:00
} // try
2009-11-07 12:58:54 +01:00
catch (const Firebird::Exception&)
{
2001-12-24 03:51:06 +01:00
relation->rel_flags &= ~(REL_being_scanned | REL_scanned);
if (dependencies) {
relation->rel_flags |= REL_get_dependencies;
}
if (sys_triggers) {
relation->rel_flags |= REL_sys_triggers;
}
if (blob)
BLB_close(tdbb, blob);
2008-01-16 10:29:37 +01:00
// Some functions inside FOR loop may throw, in which case request
// remained active forever. AP: 13-may-05.
if (request && request->req_flags & req_active)
{
EXE_unwind(tdbb, request);
}
throw;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
}
2009-11-21 06:20:18 +01:00
void MET_trigger_msg(thread_db* tdbb, Firebird::string& msg, const Firebird::MetaName& name,
USHORT number)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ t r i g g e r _ m s g
*
**************************************
*
* Functional description
* Look up trigger message using trigger and abort code.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_s_msgs, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
MSG IN RDB$TRIGGER_MESSAGES WITH
MSG.RDB$TRIGGER_NAME EQ name.c_str() AND
2005-05-28 00:45:31 +02:00
MSG.RDB$MESSAGE_NUMBER EQ number
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_s_msgs))
REQUEST(irq_s_msgs) = request;
msg = MSG.RDB$MESSAGE;
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_s_msgs))
REQUEST(irq_s_msgs) = request;
msg.rtrim();
2001-05-23 15:26:42 +02:00
}
void MET_update_shadow(thread_db* tdbb, Shadow* shadow, USHORT file_flags)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ u p d a t e _ s h a d o w
*
**************************************
*
* Functional description
* Update the stored file flags for the specified shadow.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
jrd_req* handle = NULL;
2001-05-23 15:26:42 +02:00
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;
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, handle);
2001-05-23 15:26:42 +02:00
}
void MET_update_transaction(thread_db* tdbb, jrd_tra* transaction, const bool do_commit)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* M E T _ u p d a t e _ t r a n s a c t i o n
*
**************************************
*
* Functional description
2003-12-22 11:00:59 +01:00
* Update a record in RDB$TRANSACTIONS. If do_commit is true, this is a
2001-05-23 15:26:42 +02:00
* commit; otherwise it is a ROLLBACK.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
jrd_req* request = CMP_find_request(tdbb, irq_m_trans, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
2005-05-28 00:45:31 +02:00
X IN RDB$TRANSACTIONS
2008-12-18 11:57:12 +01:00
WITH X.RDB$TRANSACTION_ID EQ transaction->tra_number
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_m_trans))
REQUEST(irq_m_trans) = request;
2003-12-22 11:00:59 +01:00
if (do_commit && (transaction->tra_flags & TRA_prepare2))
2001-05-23 15:26:42 +02:00
{
ERASE X
}
else
{
MODIFY X
2009-02-28 12:57:40 +01:00
X.RDB$TRANSACTION_STATE = do_commit ?
2001-05-23 15:26:42 +02:00
RDB$TRANSACTIONS.RDB$TRANSACTION_STATE.COMMITTED :
RDB$TRANSACTIONS.RDB$TRANSACTION_STATE.ROLLED_BACK;
END_MODIFY;
}
END_FOR;
if (!REQUEST(irq_m_trans))
REQUEST(irq_m_trans) = request;
}
static int blocking_ast_dsql_cache(void* ast_object)
{
/**************************************
*
* b l o c k i n g _ a s t _ d s q l _ c a c h e
*
**************************************
*
* Functional description
2008-03-02 08:35:37 +01:00
* Someone is trying to drop an item from the DSQL cache.
* Mark the symbol as obsolete and release the lock.
*
**************************************/
DSqlCacheItem* item = static_cast<DSqlCacheItem*>(ast_object);
try
{
Database* dbb = item->lock->lck_dbb;
Database::SyncGuard dsGuard(dbb, true);
ThreadContextHolder tdbb;
tdbb->setDatabase(dbb);
tdbb->setAttachment(item->lock->lck_attachment);
Jrd::ContextPoolHolder context(tdbb, 0);
item->obsolete = true;
item->locked = false;
LCK_release(tdbb, item->lock);
}
catch (const Firebird::Exception&)
{} // no-op
return 0;
}
static DSqlCacheItem* get_dsql_cache_item(thread_db* tdbb, int type, const QualifiedName& name)
{
Database* dbb = tdbb->getDatabase();
Jrd::Attachment* attachment = tdbb->getAttachment();
Firebird::string key((char*) &type, sizeof(type));
int len = name.identifier.length();
key.append((char*) &len, sizeof(len));
key.append(name.identifier.c_str());
len = name.qualifier.length();
key.append((char*) &len, sizeof(len));
key.append(name.qualifier.c_str());
DSqlCacheItem* item = attachment->att_dsql_cache.put(key);
if (item)
{
item->obsolete = false;
item->locked = false;
item->lock = FB_NEW_RPT(*dbb->dbb_permanent, key.length()) Lock();
item->lock->lck_type = LCK_dsql_cache;
item->lock->lck_owner_handle = LCK_get_owner_handle(tdbb, item->lock->lck_type);
item->lock->lck_parent = dbb->dbb_lock;
item->lock->lck_dbb = dbb;
item->lock->lck_object = item;
item->lock->lck_ast = blocking_ast_dsql_cache;
item->lock->lck_length = key.length();
memcpy(item->lock->lck_key.lck_string, key.c_str(), key.length());
}
else
{
item = attachment->att_dsql_cache.get(key);
}
return item;
}
2003-12-31 06:36:12 +01:00
static int blocking_ast_procedure(void* ast_object)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* b l o c k i n g _ a s t _ p r o c e d u r e
*
**************************************
*
* Functional description
* Someone is trying to drop a proceedure. If there
* are outstanding interests in the existence of
* the relation then just mark as blocking and return.
* Otherwise, mark the procedure block as questionable
* and release the procedure existence lock.
*
**************************************/
jrd_prc* procedure = static_cast<jrd_prc*>(ast_object);
2001-05-23 15:26:42 +02:00
try
{
Database* dbb = procedure->prc_existence_lock->lck_dbb;
2001-05-23 15:26:42 +02:00
Database::SyncGuard dsGuard(dbb, true);
2001-05-23 15:26:42 +02:00
ThreadContextHolder tdbb;
tdbb->setDatabase(dbb);
tdbb->setAttachment(procedure->prc_existence_lock->lck_attachment);
Jrd::ContextPoolHolder context(tdbb, 0);
2001-05-23 15:26:42 +02:00
if (procedure->prc_existence_lock) {
LCK_release(tdbb, procedure->prc_existence_lock);
}
procedure->prc_flags |= PRC_obsolete;
}
catch (const Firebird::Exception&)
{} // no-op
2001-05-23 15:26:42 +02:00
2003-05-19 08:57:08 +02:00
return 0;
2001-05-23 15:26:42 +02:00
}
static int blocking_ast_relation(void* ast_object)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* b l o c k i n g _ a s t _ r e l a t i o n
*
**************************************
*
* Functional description
* Someone is trying to drop a relation. If there
* are outstanding interests in the existence of
* the relation then just mark as blocking and return.
* Otherwise, mark the relation block as questionable
* and release the relation existence lock.
*
**************************************/
jrd_rel* relation = static_cast<jrd_rel*>(ast_object);
2001-05-23 15:26:42 +02:00
try
{
Database* dbb = relation->rel_existence_lock->lck_dbb;
2001-05-23 15:26:42 +02:00
Database::SyncGuard dsGuard(dbb, true);
2001-05-23 15:26:42 +02:00
ThreadContextHolder tdbb;
tdbb->setDatabase(dbb);
tdbb->setAttachment(relation->rel_existence_lock->lck_attachment);
Jrd::ContextPoolHolder context(tdbb, 0);
2001-05-23 15:26:42 +02:00
if (relation->rel_use_count) {
relation->rel_flags |= REL_blocking;
}
2009-11-07 12:58:54 +01:00
else
{
relation->rel_flags &= ~REL_blocking;
relation->rel_flags |= REL_check_existence;
if (relation->rel_existence_lock)
LCK_release(tdbb, relation->rel_existence_lock);
}
2001-05-23 15:26:42 +02:00
}
catch (const Firebird::Exception&)
{} // no-op
2001-05-23 15:26:42 +02:00
2005-05-28 00:45:31 +02:00
return 0;
2001-05-23 15:26:42 +02:00
}
static int partners_ast_relation(void* ast_object)
{
jrd_rel* relation = static_cast<jrd_rel*>(ast_object);
try
{
Database* dbb = relation->rel_partners_lock->lck_dbb;
Database::SyncGuard dsGuard(dbb, true);
ThreadContextHolder tdbb;
tdbb->setDatabase(dbb);
tdbb->setAttachment(relation->rel_partners_lock->lck_attachment);
Jrd::ContextPoolHolder context(tdbb, 0);
LCK_release(tdbb, relation->rel_partners_lock);
relation->rel_flags |= REL_check_partners;
}
catch (const Firebird::Exception&)
{} // no-op
return 0;
}
static void get_trigger(thread_db* tdbb, jrd_rel* relation,
bid* blob_id, bid* debug_blob_id, trig_vec** ptr,
const TEXT* name, FB_UINT64 type,
bool sys_trigger, USHORT flags,
const Firebird::MetaName& engine, const Firebird::string& entryPoint,
const bid* body)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* g e t _ t r i g g e r
*
**************************************
*
* Functional description
* Get trigger.
*
**************************************/
SET_TDBB(tdbb);
if (blob_id->isEmpty() && (engine.isEmpty() || entryPoint.isEmpty()))
2001-05-23 15:26:42 +02:00
return;
blb* blrBlob = NULL;
if (!blob_id->isEmpty())
blrBlob = BLB_open(tdbb, tdbb->getDatabase()->dbb_sys_trans, blob_id);
save_trigger_data(tdbb, ptr, relation, NULL, blrBlob, debug_blob_id,
name, type, sys_trigger, flags, engine, entryPoint, body);
2001-05-23 15:26:42 +02:00
}
2006-04-05 18:34:18 +02:00
static bool get_type(thread_db* tdbb, USHORT* id, const UCHAR* name, const TEXT* field)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2003-11-04 00:59:24 +01:00
fb_assert(id != NULL);
fb_assert(name != NULL);
fb_assert(field != NULL);
2001-05-23 15:26:42 +02:00
2009-11-21 04:23:47 +01:00
// Force key to uppercase, following C locale rules for uppercase
UCHAR* p;
2001-05-23 15:26:42 +02:00
for (p = buffer; *name && p < buffer + sizeof(buffer) - 1; p++, name++)
{
*p = UPPER7(*name);
}
*p = 0;
2009-11-21 04:23:47 +01:00
// Try for exact name match
2001-05-23 15:26:42 +02:00
bool found = false;
2001-05-23 15:26:42 +02:00
jrd_req* handle = NULL;
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE handle)
FIRST 1 T IN RDB$TYPES WITH
2002-12-22 14:08:50 +01:00
T.RDB$FIELD_NAME EQ field AND
T.RDB$TYPE_NAME EQ buffer
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
found = true;
2001-05-23 15:26:42 +02:00
*id = T.RDB$TYPE;
END_FOR;
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, handle);
2001-05-23 15:26:42 +02:00
return found;
}
static void lookup_view_contexts( thread_db* tdbb, jrd_rel* view)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* l o o k u p _ v i e w _ c o n t e x t s
*
**************************************
*
* Functional description
2005-05-28 00:45:31 +02:00
* Lookup view contexts and store in a linked
2001-05-23 15:26:42 +02:00
* list on the relation block.
2005-05-28 00:45:31 +02:00
*
2001-05-23 15:26:42 +02:00
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
jrd_req* request = CMP_find_request(tdbb, irq_view_context, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE request)
V IN RDB$VIEW_RELATIONS WITH
V.RDB$VIEW_NAME EQ view->rel_name.c_str()
2005-05-28 00:45:31 +02:00
SORTED BY V.RDB$VIEW_CONTEXT
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_view_context))
REQUEST(irq_view_context) = request;
2001-05-23 15:26:42 +02:00
// trim trailing spaces
2008-12-18 11:57:12 +01:00
fb_utils::exact_name_limit(V.RDB$CONTEXT_NAME, sizeof(V.RDB$CONTEXT_NAME));
2008-05-13 03:29:38 +02:00
ViewContext* view_context = FB_NEW(*dbb->dbb_permanent)
ViewContext(*dbb->dbb_permanent,
2008-12-18 11:57:12 +01:00
V.RDB$CONTEXT_NAME, V.RDB$RELATION_NAME, V.RDB$VIEW_CONTEXT);
2005-05-28 00:45:31 +02:00
view->rel_view_contexts.add(view_context);
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_view_context))
REQUEST(irq_view_context) = request;
}
2009-11-21 06:20:18 +01:00
static void make_relation_scope_name(const TEXT* rel_name, const USHORT rel_flags,
Firebird::string& str)
2006-05-24 22:43:07 +02:00
{
/**************************************
*
* 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
2006-05-24 22:43:07 +02:00
* 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);
}
2008-01-16 10:29:37 +01:00
// ***********************************************************
// * p a r s e _ f i e l d _ b l r
// ***********************************************************
// Parses default BLR and validation BLR for a field.
static jrd_nod* parse_field_blr(thread_db* tdbb, bid* blob_id, const Firebird::MetaName name)
{
SET_TDBB(tdbb);
2008-01-16 13:22:11 +01:00
Database* dbb = tdbb->getDatabase();
2008-01-16 10:29:37 +01:00
CompilerScratch* csb = CompilerScratch::newCsb(*tdbb->getDefaultPool(), 5, name);
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id);
SLONG length = blob->blb_length + 10;
Firebird::HalfStaticArray<UCHAR, 512> temp;
length = BLB_get_data(tdbb, blob, temp.getBuffer(length), length);
2008-01-16 10:29:37 +01:00
jrd_nod* node = PAR_blr(tdbb, NULL, temp.begin(), length, NULL, &csb, NULL, false, 0);
2008-01-16 10:29:37 +01:00
csb->csb_blr_reader = BlrReader();
2008-01-16 10:29:37 +01:00
delete csb;
return node;
}
2009-11-21 06:20:18 +01:00
static jrd_nod* parse_param_blr(thread_db* tdbb, jrd_prc* procedure, bid* blob_id,
CompilerScratch* csb)
2004-01-16 13:59:16 +01:00
{
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2004-01-16 13:59:16 +01:00
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id);
SLONG length = blob->blb_length + 10;
2008-01-16 10:29:37 +01:00
Firebird::HalfStaticArray<UCHAR, 512> temp;
2004-01-16 13:59:16 +01:00
length = BLB_get_data(tdbb, blob, temp.getBuffer(length), length);
2004-01-16 13:59:16 +01:00
jrd_nod* node = PAR_blr(tdbb, NULL, temp.begin(), length, NULL, &csb,
&procedure->prc_request, false, 0);
csb->csb_blr_reader = BlrReader();
2004-01-16 13:59:16 +01:00
return node;
}
2001-05-23 15:26:42 +02:00
// Generate BLR message for external procedures
static void gen_ext_message(Firebird::UCharBuffer& blr, UCHAR message,
const vec<Parameter*>* parameters)
{
size_t count = (parameters ? parameters->count() : 0);
2009-10-30 11:50:59 +01:00
count = count * 2 + message;
fb_assert(count < MAX_USHORT);
blr.add(blr_message); // e_extproc_input_message, e_extproc_output_message
blr.add(message);
2009-10-30 11:50:59 +01:00
blr.add(count); // parameters (with nulls)
blr.add(count >> 8);
dsc shortDesc;
shortDesc.makeShort(0);
if (!parameters)
return;
for (Parameter* const* i = parameters->begin(); i != parameters->end(); ++i)
{
const Parameter* parameter = *i;
if (!parameter->prm_nullable)
blr.add(blr_not_nullable);
if (parameter->prm_mechanism == prm_mech_type_of)
PreparedStatement::generateBlr(&parameter->prm_desc, blr);
else
{
fb_assert(parameter->prm_mechanism == prm_mech_normal);
bool isText = parameter->prm_desc.isText() ||
(parameter->prm_desc.isBlob() &&
parameter->prm_desc.getBlobSubType() == isc_blob_text);
if (isText)
blr.add(blr_domain_name2);
else
blr.add(blr_domain_name);
blr.add(blr_domain_full);
2009-10-30 11:50:59 +01:00
fb_assert(parameter->prm_field_source.length() <= MAX_UCHAR);
blr.add(parameter->prm_field_source.length());
blr.push(reinterpret_cast<const UCHAR*>(parameter->prm_field_source.c_str()),
parameter->prm_field_source.length());
if (isText)
{
blr.add(parameter->prm_desc.getTextType());
blr.add(parameter->prm_desc.getTextType() >> 8);
}
}
PreparedStatement::generateBlr(&shortDesc, blr); // null flag
}
}
static jrd_nod* parse_procedure_blr(thread_db* tdbb,
jrd_prc* procedure,
bid* blob_id,
CompilerScratch* csb,
bool external)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p a r s e _ p r o c e d u r e _ b l r
*
**************************************
*
* Functional description
* Parse blr, returning a compiler scratch block with the results.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
Firebird::UCharBuffer tmp;
if (external || !blob_id)
{
tmp.add(blr_version5);
tmp.add(blr_begin);
2005-05-28 00:45:31 +02:00
// ASF: Generate input messages even when number of parameters is 0
// because we'll use hardcoded message number (0 and 1) in execution.
gen_ext_message(tmp, 0, procedure->prc_input_fields); // input message
gen_ext_message(tmp, 1, procedure->prc_output_fields); // output message
dsc shortDesc;
shortDesc.makeShort(0);
PreparedStatement::generateBlr(&shortDesc, tmp); // end flag
tmp.add(blr_begin); // e_extproc_input_assign
2009-10-30 11:50:59 +01:00
fb_assert(procedure->prc_inputs < MAX_USHORT / 2);
for (USHORT i = 0; i < procedure->prc_inputs; ++i)
{
tmp.add(blr_assignment);
tmp.add(blr_parameter2);
tmp.add(0); // input message
tmp.add((i * 2));
tmp.add((i * 2) >> 8);
tmp.add((i * 2) + 1); // null indicator
tmp.add(((i * 2) + 1) >> 8);
tmp.add(blr_null);
}
tmp.add(blr_end);
tmp.add(blr_begin); // e_extproc_output_assign
2009-10-30 11:50:59 +01:00
fb_assert(procedure->prc_outputs < MAX_USHORT / 2);
for (USHORT i = 0; i < procedure->prc_outputs; ++i)
{
tmp.add(blr_assignment);
tmp.add(blr_parameter2);
tmp.add(1); // output message
tmp.add((i * 2));
tmp.add((i * 2) >> 8);
tmp.add((i * 2) + 1); // null indicator
tmp.add(((i * 2) + 1) >> 8);
tmp.add(blr_null);
}
tmp.add(blr_end);
tmp.add(blr_end);
tmp.add(blr_eoc);
}
else
{
blb* blob = BLB_open(tdbb, dbb->dbb_sys_trans, blob_id);
const SLONG length = blob->blb_length + 10;
UCHAR* temp = tmp.getBuffer(length);
const ULONG realLen = BLB_get_data(tdbb, blob, temp, length);
2009-11-01 11:58:16 +01:00
fb_assert(realLen >= ULONG(length) - 10 && realLen <= ULONG(MAX_USHORT));
tmp.resize(realLen);
}
par_messages(tdbb, tmp.begin(), (ULONG) tmp.getCount(), procedure, csb);
return PAR_blr(tdbb, NULL, tmp.begin(), (ULONG) tmp.getCount(), NULL, &csb,
&procedure->prc_request, false, 0);
2001-05-23 15:26:42 +02:00
}
static void par_messages(thread_db* tdbb,
2009-04-28 15:08:04 +02:00
const UCHAR* const blr,
USHORT blr_length,
jrd_prc* procedure,
CompilerScratch* csb)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* p a r _ m e s s a g e s
*
**************************************
*
* Functional description
* Parse the messages of a blr request. For specified message, allocate
2004-03-30 06:10:52 +02:00
* a format (Format) block.
2001-05-23 15:26:42 +02:00
*
**************************************/
2009-04-28 15:08:04 +02:00
if (blr_length < 2)
ERR_post(Arg::Gds(isc_metadata_corrupt));
blr_length -= 2;
csb->csb_blr_reader = BlrReader(blr, blr_length);
const SSHORT version = csb->csb_blr_reader.getByte();
2009-11-07 12:58:54 +01:00
if (version != blr_version4 && version != blr_version5)
{
ERR_post(Arg::Gds(isc_metadata_corrupt) <<
Arg::Gds(isc_wroblrver) << Arg::Num(blr_version4) << Arg::Num(version));
}
2001-05-23 15:26:42 +02:00
if (csb->csb_blr_reader.getByte() != blr_begin) {
ERR_post(Arg::Gds(isc_metadata_corrupt));
}
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
while (csb->csb_blr_reader.getByte() == blr_message)
2009-04-28 15:08:04 +02:00
{
if (blr_length < 4) // Covers the call above and the next three
ERR_post(Arg::Gds(isc_metadata_corrupt));
blr_length -= 4;
const USHORT msg_number = csb->csb_blr_reader.getByte();
USHORT count = csb->csb_blr_reader.getByte();
count += (csb->csb_blr_reader.getByte()) << 8;
Format* format = Format::newFormat(*tdbb->getDefaultPool(), count);
2006-07-20 11:11:44 +02:00
ULONG offset = 0;
2008-12-18 11:57:12 +01:00
for (Format::fmt_desc_iterator desc = format->fmt_desc.begin(); count; --count, ++desc)
{
2009-04-28 15:08:04 +02:00
// CVC: I can't find an elegant way to make PAR_desc aware of the BLR length, so
// for now I know that at least should consume one byte. I would have to change the
// current 6 calls to it. Maybe a new member in CompilerScratch may be the solution?
if (blr_length < 2)
ERR_post(Arg::Gds(isc_metadata_corrupt));
const UCHAR* const savePos = csb->csb_blr_reader.getPos();
2009-04-28 15:08:04 +02:00
const USHORT align = PAR_desc(tdbb, csb, &*desc);
if (align) {
2001-05-23 15:26:42 +02:00
offset = FB_ALIGN(offset, align);
}
desc->dsc_address = (UCHAR *) (IPTR) offset;
2001-05-23 15:26:42 +02:00
offset += desc->dsc_length;
2009-04-28 15:08:04 +02:00
// This solution only checks that PAR_desc doesn't run out of bounds, but it
// does after the disaster happened.
const int diff = csb->csb_blr_reader.getPos() - savePos;
2009-04-28 15:08:04 +02:00
if (diff > blr_length)
ERR_post(Arg::Gds(isc_metadata_corrupt));
blr_length -= diff;
2001-05-23 15:26:42 +02:00
}
2006-07-20 11:11:44 +02:00
2009-11-07 12:58:54 +01:00
if (offset > MAX_FORMAT_SIZE)
{
ERR_post(Arg::Gds(isc_imp_exc) <<
Arg::Gds(isc_blktoobig));
2006-07-20 11:11:44 +02:00
}
format->fmt_length = (USHORT) offset;
2009-01-20 09:33:59 +01:00
switch (msg_number)
2001-05-23 15:26:42 +02:00
{
2009-01-20 09:33:59 +01:00
case 0:
2001-05-23 15:26:42 +02:00
procedure->prc_input_fmt = format;
2009-01-20 09:33:59 +01:00
break;
case 1:
2001-05-23 15:26:42 +02:00
procedure->prc_output_fmt = format;
2009-01-20 09:33:59 +01:00
break;
default:
2001-12-24 03:51:06 +01:00
delete format;
2001-05-23 15:26:42 +02:00
}
}
}
2009-11-21 06:20:18 +01:00
void MET_release_trigger(thread_db* tdbb, trig_vec** vector_ptr, const Firebird::MetaName& name)
{
/***********************************************
*
* M E T _ r e l e a s e _ t r i g g e r
*
***********************************************
*
* Functional description
* Release a specified trigger.
* If trigger are still active let someone
* else do the work.
*
**************************************/
2006-11-11 01:52:31 +01:00
if (!*vector_ptr)
return;
2006-11-11 01:52:31 +01:00
trig_vec& vector = **vector_ptr;
2006-11-11 01:52:31 +01:00
SET_TDBB(tdbb);
2006-11-11 01:52:31 +01:00
for (size_t i = 0; i < vector.getCount(); ++i)
{
2006-11-11 01:52:31 +01:00
if (vector[i].name == name)
{
2006-11-11 01:52:31 +01:00
jrd_req* r = vector[i].request;
if (r)
2006-11-11 01:52:31 +01:00
{
if (CMP_clone_is_active(r))
break;
CMP_release(tdbb, r);
2006-11-11 01:52:31 +01:00
}
vector.remove(i);
break;
}
}
}
void MET_release_triggers( thread_db* tdbb, trig_vec** vector_ptr)
2001-05-23 15:26:42 +02:00
{
/***********************************************
*
* 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.
*
**************************************/
2003-12-22 11:00:59 +01:00
trig_vec* vector = *vector_ptr;
2005-05-28 00:45:31 +02:00
if (!vector)
2004-04-19 17:29:29 +02:00
{
2001-05-23 15:26:42 +02:00
return;
}
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
*vector_ptr = NULL;
2009-06-27 08:23:36 +02:00
for (size_t i = 0; i < vector->getCount(); i++)
2001-05-23 15:26:42 +02:00
{
2004-04-19 17:29:29 +02:00
jrd_req* r = (*vector)[i].request;
if (r && CMP_clone_is_active(r))
2001-05-23 15:26:42 +02:00
{
return;
}
}
2009-06-27 08:23:36 +02:00
for (size_t i = 0; i < vector->getCount(); i++)
2001-05-23 15:26:42 +02:00
{
2004-04-19 17:29:29 +02:00
jrd_req* r = (*vector)[i].request;
2005-05-28 00:45:31 +02:00
if (r)
2004-04-19 17:29:29 +02:00
{
CMP_release(tdbb, r);
}
delete (*vector)[i].extTrigger;
2001-05-23 15:26:42 +02:00
}
2001-12-24 03:51:06 +01:00
delete vector;
2001-05-23 15:26:42 +02:00
}
2008-12-18 11:57:12 +01:00
static bool resolve_charset_and_collation(thread_db* tdbb,
USHORT* id,
const UCHAR* charset,
const UCHAR* collation)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* 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:
2005-05-28 00:45:31 +02:00
* (*id)
* Set to character set specified by this name (low byte)
* Set to collation specified by this name (high byte).
2001-05-23 15:26:42 +02:00
*
* Return:
* true if no errors (and *id is set).
* false if either name not found.
2001-05-23 15:26:42 +02:00
* or if names found, but the collation isn't for the specified
* character set.
*
**************************************/
bool found = false;
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2003-11-04 00:59:24 +01:00
fb_assert(id != NULL);
2001-05-23 15:26:42 +02:00
jrd_req* handle = NULL;
2001-05-23 15:26:42 +02:00
if (collation == NULL)
{
if (charset == NULL)
charset = (const UCHAR*) DEFAULT_CHARACTER_SET_NAME;
2001-05-23 15:26:42 +02:00
if (dbb->dbb_charset_ids.get((const TEXT*) charset, *id))
return true;
2006-04-05 18:34:18 +02:00
USHORT charset_id = 0;
if (get_type(tdbb, &charset_id, charset, "RDB$CHARACTER_SET_NAME"))
2001-05-23 15:26:42 +02:00
{
dbb->dbb_charset_ids.put((const TEXT*) charset, charset_id);
2001-05-23 15:26:42 +02:00
*id = charset_id;
return true;
2001-05-23 15:26:42 +02:00
}
2009-11-21 04:23:47 +01:00
// Charset name not found in the alias table - before giving up
// try the character_set table
2001-05-23 15:26:42 +02:00
FOR(REQUEST_HANDLE handle)
FIRST 1 CS IN RDB$CHARACTER_SETS
2002-12-22 14:08:50 +01:00
WITH CS.RDB$CHARACTER_SET_NAME EQ charset
2001-05-23 15:26:42 +02:00
found = true;
dbb->dbb_charset_ids.put((const TEXT*) charset, CS.RDB$CHARACTER_SET_ID);
2001-05-23 15:26:42 +02:00
*id = CS.RDB$CHARACTER_SET_ID;
END_FOR;
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, handle);
2001-05-23 15:26:42 +02:00
return found;
}
2008-01-16 10:29:37 +01:00
if (charset == NULL)
2001-05-23 15:26:42 +02:00
{
FOR(REQUEST_HANDLE handle)
FIRST 1 COL IN RDB$COLLATIONS
2002-12-22 14:08:50 +01:00
WITH COL.RDB$COLLATION_NAME EQ collation
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
found = true;
2005-05-28 00:45:31 +02:00
*id = COL.RDB$CHARACTER_SET_ID | (COL.RDB$COLLATION_ID << 8);
2001-05-23 15:26:42 +02:00
END_FOR;
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, handle);
2001-05-23 15:26:42 +02:00
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"
2002-12-22 14:08:50 +01:00
AND AL1.RDB$TYPE_NAME EQ charset
AND COL.RDB$COLLATION_NAME EQ collation
2005-05-28 00:45:31 +02:00
AND AL1.RDB$TYPE EQ CS.RDB$CHARACTER_SET_ID
2001-05-23 15:26:42 +02:00
found = true;
dbb->dbb_charset_ids.put((const TEXT*) charset, CS.RDB$CHARACTER_SET_ID);
2005-05-28 00:45:31 +02:00
*id = CS.RDB$CHARACTER_SET_ID | (COL.RDB$COLLATION_ID << 8);
2001-05-23 15:26:42 +02:00
END_FOR;
2004-08-26 13:07:57 +02:00
CMP_release(tdbb, handle);
2001-05-23 15:26:42 +02:00
return found;
}
static void save_trigger_data(thread_db* tdbb, trig_vec** ptr, jrd_rel* relation,
jrd_req* request, blb* blrBlob, bid* dbgBlobID,
const TEXT* name, FB_UINT64 type,
bool sys_trigger, USHORT flags,
const Firebird::MetaName& engine, const Firebird::string& entryPoint,
const bid* body)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s a v e _ t r i g g e r _ d a t a
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Save trigger data to passed vector
2001-05-23 15:26:42 +02:00
*
**************************************/
trig_vec* vector = *ptr;
2001-12-24 03:51:06 +01:00
if (!vector)
{
vector = FB_NEW(*tdbb->getDatabase()->dbb_permanent)
trig_vec(*tdbb->getDatabase()->dbb_permanent);
2001-12-24 03:51:06 +01:00
*ptr = vector;
}
2001-05-23 15:26:42 +02:00
2004-04-19 17:29:29 +02:00
Trigger& t = vector->add();
2005-05-28 00:45:31 +02:00
if (blrBlob)
2004-04-19 17:29:29 +02:00
{
2004-05-09 07:48:33 +02:00
const SLONG length = blrBlob->blb_length + 10;
2004-05-12 21:39:17 +02:00
UCHAR* ptr2 = t.blr.getBuffer(length);
t.blr.resize(BLB_get_data(tdbb, blrBlob, ptr2, length));
2003-12-03 09:19:24 +01:00
}
if (dbgBlobID)
t.dbg_blob_id = *dbgBlobID;
if (name)
2004-04-19 17:29:29 +02:00
t.name = name;
if (body)
{
blb* bodyBlob = BLB_open(tdbb, tdbb->getDatabase()->dbb_sys_trans, body);
HalfStaticArray<char, 512> temp;
ULONG length = BLB_get_data(tdbb, bodyBlob, (UCHAR*) temp.getBuffer(bodyBlob->blb_length),
bodyBlob->blb_length);
t.extBody = string(temp.begin(), length);
2003-12-03 09:19:24 +01:00
}
t.type = type;
t.flags = flags;
t.compile_in_progress = false;
t.sys_trigger = sys_trigger;
t.request = request;
t.relation = relation;
t.engine = engine;
t.entryPoint = entryPoint;
2001-05-23 15:26:42 +02:00
}
2006-05-22 00:07:35 +02:00
const Trigger* findTrigger(trig_vec* triggers, const Firebird::MetaName& trig_name)
{
if (triggers)
{
2008-12-18 11:57:12 +01:00
for (trig_vec::iterator t = triggers->begin(); t != triggers->end(); ++t)
2006-05-22 00:07:35 +02:00
{
if (t->name.compare(trig_name) == 0)
return &(*t);
}
}
return NULL;
}
static void store_dependencies(thread_db* tdbb,
CompilerScratch* csb,
const jrd_rel* dep_rel,
2006-05-22 00:07:35 +02:00
const Firebird::MetaName& object_name,
int dependency_type,
jrd_tra* transaction)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s t o r e _ d e p e n d e n c i e s
*
**************************************
*
* Functional description
* Store records in RDB$DEPENDENCIES
2005-05-28 00:45:31 +02:00
* corresponding to the objects found during
2001-05-23 15:26:42 +02:00
* compilation of blr for a trigger, view, etc.
*
**************************************/
Firebird::MetaName name;
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2006-05-25 10:40:23 +02:00
const Trigger* t = 0;
const bool checkTableScope =
2006-05-22 00:07:35 +02:00
(dependency_type == obj_computed) ||
2008-12-18 11:57:12 +01:00
(dependency_type == obj_trigger) && (dep_rel != 0) &&
(
2006-05-22 00:07:35 +02:00
(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))
2006-05-22 00:07:35 +02:00
) && t && (t->sys_trigger);
while (csb->csb_dependencies.hasData())
2001-05-23 15:26:42 +02:00
{
jrd_nod* node = csb->csb_dependencies.pop();
2001-05-23 15:26:42 +02:00
if (!node->nod_arg[e_dep_object])
continue;
2005-05-28 00:45:31 +02:00
int dpdo_type = (int) (IPTR) node->nod_arg[e_dep_object_type];
jrd_rel* relation = NULL;
jrd_prc* procedure = NULL;
const Firebird::MetaName* dpdo_name = 0;
MetaName packageName;
SubtypeInfo info;
2009-01-20 09:33:59 +01:00
switch (dpdo_type)
{
case obj_relation:
2003-12-22 11:00:59 +01:00
relation = (jrd_rel*) node->nod_arg[e_dep_object];
dpdo_name = &relation->rel_name;
2006-05-22 00:07:35 +02:00
2008-12-18 11:57:12 +01:00
fb_assert(dep_rel || !checkTableScope);
2006-05-22 00:07:35 +02:00
if (checkTableScope &&
2006-06-01 06:45:48 +02:00
( (dep_rel->rel_flags & (REL_temp_tran | REL_temp_conn)) !=
(relation->rel_flags & (REL_temp_tran | REL_temp_conn)) ))
2006-05-22 00:07:35 +02:00
{
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)
))
2006-05-22 00:07:35 +02:00
{
2006-05-24 22:43:07 +02:00
Firebird::string sMaster, sChild;
make_relation_scope_name(relation->rel_name.c_str(),
2006-05-24 22:43:07 +02:00
relation->rel_flags, sMaster);
make_relation_scope_name(dep_rel->rel_name.c_str(),
2006-05-24 22:43:07 +02:00
dep_rel->rel_flags, sChild);
2006-05-22 00:07:35 +02:00
ERR_post(Arg::Gds(isc_no_meta_update) <<
Arg::Gds(isc_met_wrong_gtt_scope) << Arg::Str(sChild) <<
Arg::Str(sMaster));
2006-05-22 00:07:35 +02:00
}
}
MET_scan_relation(tdbb, relation);
if (relation->rel_view_rse) {
dpdo_type = obj_view;
}
2008-12-18 11:57:12 +01:00
break;
case obj_procedure:
procedure = (jrd_prc*) node->nod_arg[e_dep_object];
dpdo_name = &procedure->prc_name.identifier;
packageName = procedure->prc_name.qualifier;
2008-12-18 11:57:12 +01:00
break;
case obj_collation:
{
const USHORT number = (IPTR) node->nod_arg[e_dep_object];
MET_get_char_coll_subtype_info(tdbb, number, &info);
dpdo_name = &info.collationName;
}
2006-08-13 08:46:16 +02:00
break;
2008-12-18 11:57:12 +01:00
case obj_exception:
{
const SLONG number = (IPTR) node->nod_arg[e_dep_object];
MET_lookup_exception (tdbb, number, name, 0, 0);
dpdo_name = &name;
}
break;
2008-12-18 11:57:12 +01:00
case obj_field:
dpdo_name = (Firebird::MetaName*) node->nod_arg[e_dep_object];
break;
2008-12-18 11:57:12 +01:00
// CVC: Here I'm going to track those pesky things named generators and UDFs.
case obj_generator:
{
const SLONG number = (IPTR) node->nod_arg[e_dep_object];
MET_lookup_generator_id (tdbb, number, name);
dpdo_name = &name;
}
break;
2008-12-18 11:57:12 +01:00
case obj_udf:
{
UserFunction* udf = (UserFunction*) node->nod_arg[e_dep_object];
dpdo_name = &udf->fun_name.identifier;
packageName = udf->fun_name.qualifier;
}
break;
case obj_index:
name = (TEXT*) node->nod_arg[e_dep_object];
dpdo_name = &name;
break;
}
2002-07-01 18:59:09 +02:00
jrd_nod* field_node = node->nod_arg[e_dep_field];
2001-05-23 15:26:42 +02:00
Firebird::MetaName field_name;
2001-05-23 15:26:42 +02:00
if (field_node)
{
if (field_node->nod_type == nod_field)
{
const SSHORT fld_id = (SSHORT) (IPTR) field_node->nod_arg[0];
2001-05-23 15:26:42 +02:00
if (relation)
{
const jrd_fld* field = MET_get_field(relation, fld_id);
if (field)
2001-05-23 15:26:42 +02:00
{
field_name = field->fld_name;
}
}
else if (procedure)
{
const Parameter* param = (*procedure->prc_output_fields)[fld_id];
// CVC: Setting the field var here didn't make sense alone,
// so I thought the missing code was to try to extract
// the field name that's in this case an output var from a proc.
if (param)
2001-05-23 15:26:42 +02:00
{
field_name = param->prm_name;
2001-05-23 15:26:42 +02:00
}
}
}
else
{
field_name = (TEXT *) field_node->nod_arg[0];
}
}
if (field_name.length() > 0)
2001-05-23 15:26:42 +02:00
{
2008-12-18 11:57:12 +01:00
jrd_req* request = CMP_find_request(tdbb, irq_c_deps_f, IRQ_REQUESTS);
bool found = false;
fb_assert(dpdo_name);
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) X IN RDB$DEPENDENCIES WITH
X.RDB$DEPENDENT_NAME = object_name.c_str() AND
X.RDB$DEPENDED_ON_NAME = dpdo_name->c_str() AND
2001-05-23 15:26:42 +02:00
X.RDB$DEPENDED_ON_TYPE = dpdo_type AND
X.RDB$FIELD_NAME = field_name.c_str() AND
2001-05-23 15:26:42 +02:00
X.RDB$DEPENDENT_TYPE = dependency_type
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_c_deps_f))
REQUEST(irq_c_deps_f) = request;
found = true;
2001-05-23 15:26:42 +02:00
END_FOR;
2006-07-09 07:08:23 +02:00
if (!REQUEST(irq_c_deps_f))
REQUEST(irq_c_deps_f) = request;
if (found) {
2001-05-23 15:26:42 +02:00
continue;
}
2001-05-23 15:26:42 +02:00
}
else
{
jrd_req* request = CMP_find_request(tdbb, irq_c_deps, IRQ_REQUESTS);
bool found = false;
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) X IN RDB$DEPENDENCIES WITH
X.RDB$DEPENDENT_NAME = object_name.c_str() AND
X.RDB$DEPENDED_ON_NAME = dpdo_name->c_str() AND
2001-05-23 15:26:42 +02:00
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(), '')
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_c_deps))
2001-05-23 15:26:42 +02:00
REQUEST(irq_c_deps) = request;
found = true;
2001-05-23 15:26:42 +02:00
END_FOR;
2006-07-09 07:08:23 +02:00
if (!REQUEST(irq_c_deps))
REQUEST(irq_c_deps) = request;
if (found) {
2001-05-23 15:26:42 +02:00
continue;
}
2001-05-23 15:26:42 +02:00
}
jrd_req* request = CMP_find_request(tdbb, irq_s_deps, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
fb_assert(dpdo_name);
STORE(REQUEST_HANDLE request TRANSACTION_HANDLE transaction) DEP IN RDB$DEPENDENCIES
strcpy(DEP.RDB$DEPENDENT_NAME, object_name.c_str());
2001-05-23 15:26:42 +02:00
DEP.RDB$DEPENDED_ON_TYPE = dpdo_type;
strcpy(DEP.RDB$DEPENDED_ON_NAME, dpdo_name->c_str());
if (field_name.length() > 0)
2001-05-23 15:26:42 +02:00
{
DEP.RDB$FIELD_NAME.NULL = FALSE;
strcpy(DEP.RDB$FIELD_NAME, field_name.c_str());
2001-05-23 15:26:42 +02:00
}
else
2001-05-23 15:26:42 +02:00
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;
2001-05-23 15:26:42 +02:00
DEP.RDB$DEPENDENT_TYPE = dependency_type;
END_STORE;
if (!REQUEST(irq_s_deps))
REQUEST(irq_s_deps) = request;
}
}
static bool verify_TRG_ignore_perm(thread_db* tdbb, const Firebird::MetaName& trig_name)
2001-05-23 15:26:42 +02:00
{
/*****************************************************
*
* 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
2001-05-23 15:26:42 +02:00
* checks. Currently, the only class of triggers that can go
2005-05-28 00:45:31 +02:00
* through without permission checks are
2001-05-23 15:26:42 +02:00
* (a) two system triggers (RDB$TRIGGERS_34 and RDB$TRIGGERS_35)
2005-05-28 00:45:31 +02:00
* (b) those defined for referential integrity actions such as,
2001-05-23 15:26:42 +02:00
* set null, set default, and cascade.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
// 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)
2001-05-23 15:26:42 +02:00
{
return true;
2001-05-23 15:26:42 +02:00
}
// See if this is a RI trigger
jrd_req* request = CMP_find_request(tdbb, irq_c_trg_perm, IRQ_REQUESTS);
2001-05-23 15:26:42 +02:00
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
2001-05-23 15:26:42 +02:00
REF.RDB$CONSTRAINT_NAME = CHK.RDB$CONSTRAINT_NAME
2008-12-18 11:57:12 +01:00
if (!REQUEST(irq_c_trg_perm))
2001-05-23 15:26:42 +02:00
{
REQUEST(irq_c_trg_perm) = request;
}
2004-08-26 13:07:57 +02:00
EXE_unwind(tdbb, request);
fb_utils::exact_name_limit(REF.RDB$UPDATE_RULE, sizeof(REF.RDB$UPDATE_RULE));
fb_utils::exact_name_limit(REF.RDB$DELETE_RULE, sizeof(REF.RDB$DELETE_RULE));
2001-05-23 15:26:42 +02:00
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;
2001-05-23 15:26:42 +02:00
}
return false;
2001-05-23 15:26:42 +02:00
END_FOR;
if (!REQUEST(irq_c_trg_perm))
{
REQUEST(irq_c_trg_perm) = request;
}
return false;
}