mirror of
https://github.com/FirebirdSQL/firebird.git
synced 2025-01-24 23:23:03 +01:00
4570 lines
112 KiB
Plaintext
4570 lines
112 KiB
Plaintext
/*
|
|
* PROGRAM: JRD Access Method
|
|
* MODULE: met.e
|
|
* DESCRIPTION: Meta data handler
|
|
*
|
|
* The contents of this file are subject to the Interbase Public
|
|
* License Version 1.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy
|
|
* of the License at http://www.Inprise.com/IPL.html
|
|
*
|
|
* Software distributed under the License is distributed on an
|
|
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
|
|
* or implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code was created by Inprise Corporation
|
|
* and its predecessors. Portions created by Inprise Corporation are
|
|
* Copyright (C) Inprise Corporation.
|
|
*
|
|
* All Rights Reserved.
|
|
* Contributor(s): ______________________________________.
|
|
*
|
|
* 2001.6.25 Claudio Valderrama: Finish MET_lookup_generator_id() by
|
|
* assigning it a number in the compiled requests table.
|
|
*
|
|
* 2001.07.06 Sean Leyne - Code Cleanup, removed "#ifdef READONLY_DATABASE"
|
|
* conditionals, as the engine now fully supports
|
|
* readonly databases.
|
|
* 2001.10.03 Claudio Valderrama: MET_relation_owns_trigger() determines if
|
|
* there's a row in rdb$triggers with the given relation's and trigger's names.
|
|
* 2001.10.04 Claudio Valderrama: MET_relation_default_class() determines if the
|
|
* given security class name is the default security class for a relation.
|
|
* Modify MET_lookup_field() so it can verify the field's security class, too.
|
|
* 2002-02-24 Sean Leyne - Code Cleanup of old Win 3.1 port (WINDOWS_ONLY)
|
|
* 2002-09-16 Nickolay Samofatov - Deferred trigger compilation changes
|
|
* 2002.10.29 Sean Leyne - Removed obsolete "Netware" port
|
|
*
|
|
*/
|
|
/*
|
|
$Id: met.epp,v 1.50 2003-02-18 10:00:19 eku Exp $
|
|
*/
|
|
// This MUST be at the top of the file
|
|
#ifdef DARWIN
|
|
#define _STLP_CCTYPE
|
|
#endif
|
|
|
|
|
|
#ifdef SHLIB_DEFS
|
|
#define LOCAL_SHLIB_DEFS
|
|
#endif
|
|
|
|
#include "firebird.h"
|
|
#include "../jrd/ib_stdio.h"
|
|
#include <string.h>
|
|
#include "../jrd/common.h"
|
|
#include <stdarg.h>
|
|
|
|
#include "../jrd/constants.h"
|
|
#include "../jrd/gds.h"
|
|
#include "../jrd/jrd.h"
|
|
#include "../jrd/val.h"
|
|
#include "../jrd/irq.h"
|
|
#include "../jrd/tra.h"
|
|
#include "../jrd/lck.h"
|
|
#include "../jrd/ods.h"
|
|
#include "../jrd/btr.h"
|
|
#include "../jrd/req.h"
|
|
#include "../jrd/exe.h"
|
|
#include "../jrd/scl.h"
|
|
#include "../jrd/blb.h"
|
|
#include "../jrd/met.h"
|
|
#include "../jrd/pio.h"
|
|
#include "../jrd/sdw.h"
|
|
#include "../jrd/flags.h"
|
|
#include "../jrd/all.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/all_proto.h"
|
|
#include "../jrd/blb_proto.h"
|
|
#include "../jrd/cmp_proto.h"
|
|
#include "../jrd/dfw_proto.h"
|
|
#include "../jrd/dsc_proto.h"
|
|
#include "../jrd/err_proto.h"
|
|
#include "../jrd/exe_proto.h"
|
|
#include "../jrd/ext_proto.h"
|
|
#include "../jrd/flu_proto.h"
|
|
#include "../jrd/gds_proto.h"
|
|
#include "../jrd/idx_proto.h"
|
|
#include "../jrd/ini_proto.h"
|
|
|
|
#include "../jrd/lck_proto.h"
|
|
#include "../jrd/met_proto.h"
|
|
#include "../jrd/mov_proto.h"
|
|
#include "../jrd/par_proto.h"
|
|
#include "../jrd/pcmet_proto.h"
|
|
#include "../jrd/pio_proto.h"
|
|
#include "../jrd/scl_proto.h"
|
|
#include "../jrd/sdw_proto.h"
|
|
#include "../jrd/thd_proto.h"
|
|
#include "../jrd/sch_proto.h"
|
|
#include "../jrd/iberr.h"
|
|
|
|
#ifdef HAVE_CTYPE_H
|
|
#include <ctype.h>
|
|
#endif
|
|
|
|
/* Pick up relation ids */
|
|
|
|
#define RELATION(name,id,ods) id,
|
|
#define FIELD(symbol,name,id,update,ods,new_id,new_ods)
|
|
#define END_RELATION
|
|
|
|
typedef ENUM rids {
|
|
#include "../jrd/relations.h"
|
|
rel_MAX
|
|
} RIDS;
|
|
|
|
#undef RELATION
|
|
#undef FIELD
|
|
#undef END_RELATION
|
|
|
|
#define BLR_BYTE *((*csb)->csb_running)++
|
|
#define NULL_BLOB(id) (!id.gds_quad_high && !id.gds_quad_low)
|
|
|
|
#define BLANK '\040'
|
|
|
|
DATABASE DB = FILENAME "ODS.RDB";
|
|
|
|
static void blocking_ast_procedure(JRD_PRC);
|
|
static void blocking_ast_relation(JRD_REL);
|
|
static void get_trigger(TDBB, JRD_REL, SLONG[2], TRIG_VEC *, TEXT *, BOOLEAN, USHORT);
|
|
static BOOLEAN get_type(TDBB, SSHORT *, UCHAR *, UCHAR *);
|
|
static void lookup_view_contexts(TDBB, JRD_REL);
|
|
static void name_copy(TEXT *, TEXT *);
|
|
static USHORT name_length(const TEXT *);
|
|
static JRD_NOD parse_procedure_blr(TDBB, JRD_PRC, SLONG[2], CSB *);
|
|
static BOOLEAN par_messages(TDBB, UCHAR *, USHORT, JRD_PRC, CSB *);
|
|
static BOOLEAN resolve_charset_and_collation(TDBB, SSHORT *, UCHAR *,
|
|
UCHAR *);
|
|
static STR save_name(TDBB, const TEXT*);
|
|
static void save_trigger_data(TDBB, TRIG_VEC*, JRD_REL, JRD_REQ, STR, TEXT*, BOOLEAN, USHORT);
|
|
static void store_dependencies(TDBB, CSB, const TEXT*, USHORT);
|
|
static void terminate(TEXT*, USHORT);
|
|
static BOOLEAN verify_TRG_ignore_perm(TDBB, TEXT*);
|
|
|
|
|
|
#ifdef SHLIB_DEFS
|
|
#define strlen (*_libgds_strlen)
|
|
#define strcmp (*_libgds_strcmp)
|
|
#define strcpy (*_libgds_strcpy)
|
|
#define vsprintf (*_libgds_vsprintf)
|
|
#define memcmp (*_libgds_memcmp)
|
|
#define strncmp (*_libgds_strncmp)
|
|
#define memset (*_libgds_memset)
|
|
#define _iob (*_libgds__iob)
|
|
#define ib_fprintf (*_libgds_fprintf)
|
|
|
|
extern int strlen();
|
|
extern int strcmp();
|
|
extern char *strcpy();
|
|
extern int vsprintf();
|
|
extern int memcmp();
|
|
extern int strncmp();
|
|
extern void *memset();
|
|
extern IB_FILE _iob[];
|
|
extern int ib_fprintf();
|
|
#endif
|
|
|
|
|
|
// Decompile all triggers from vector
|
|
void release_cached_triggers(TDBB tdbb, TRIG_VEC vector)
|
|
{
|
|
trig_vec::iterator ptr, end;
|
|
|
|
if (!vector) return;
|
|
|
|
for (ptr = vector->begin(), end = vector->end(); ptr < end; ptr++)
|
|
ptr->release(tdbb);
|
|
}
|
|
|
|
void MET_update_partners(TDBB 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
|
|
*
|
|
**************************************/
|
|
|
|
DBB dbb;
|
|
VEC relations;
|
|
JRD_REL relation;
|
|
vec::iterator ptr, end;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
relations = dbb->dbb_relations;
|
|
|
|
for (ptr = relations->begin(), end = relations->end(); ptr < end; ptr++)
|
|
{
|
|
relation = JRD_REL(*ptr);
|
|
if (!relation) continue;
|
|
relation->rel_flags |= REL_check_partners;
|
|
}
|
|
}
|
|
|
|
void adjust_dependencies(JRD_PRC procedure) {
|
|
if (procedure->prc_int_use_count==-1)
|
|
// Already processed
|
|
return;
|
|
procedure->prc_int_use_count=-1; // Mark as undeletable
|
|
for (RSC resource = procedure->prc_request->req_resources; resource;
|
|
resource = resource->rsc_next)
|
|
{
|
|
if (resource->rsc_type == rsc_procedure) {
|
|
procedure = resource->rsc_prc;
|
|
if (procedure->prc_int_use_count==procedure->prc_use_count) {
|
|
// Mark it and all dependent procedures as undeletable
|
|
adjust_dependencies(procedure);
|
|
}
|
|
else
|
|
procedure->prc_int_use_count=-1;
|
|
}
|
|
}
|
|
}
|
|
|
|
BOOLEAN MET_clear_cache(TDBB tdbb, JRD_PRC proc)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ c l e a r _ c a c h e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Try to release all resources locked by cached triggers
|
|
* do not remove proc procedure from cache because it will be
|
|
* handled by the caller
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
VEC relations;
|
|
JRD_REL relation;
|
|
vec::iterator ptr, end;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
relations = dbb->dbb_relations;
|
|
|
|
for (ptr = relations->begin(), end = relations->end(); ptr < end; ptr++)
|
|
{
|
|
relation = JRD_REL(*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);
|
|
}
|
|
|
|
VEC procedures;
|
|
JRD_PRC procedure;
|
|
BOOLEAN result = TRUE;
|
|
|
|
if ( (procedures = dbb->dbb_procedures) ) {
|
|
/* Walk procedures and calculate internal dependencies */
|
|
for (ptr = procedures->begin(), end = procedures->end();
|
|
ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = JRD_PRC(*ptr)) && procedure->prc_request &&
|
|
!(procedure->prc_flags & PRC_obsolete) )
|
|
{
|
|
for (RSC resource = procedure->prc_request->req_resources; resource;
|
|
resource = resource->rsc_next)
|
|
{
|
|
if (resource->rsc_type == rsc_procedure)
|
|
resource->rsc_prc->prc_int_use_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Walk procedures again and adjust dependencies for procedures which will not be removed */
|
|
for (ptr = procedures->begin(), end = procedures->end();
|
|
ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = JRD_PRC(*ptr)) && procedure->prc_request &&
|
|
!(procedure->prc_flags & PRC_obsolete) &&
|
|
procedure->prc_use_count != procedure->prc_int_use_count && procedure != proc )
|
|
{
|
|
adjust_dependencies(procedure);
|
|
}
|
|
}
|
|
|
|
if (proc) {
|
|
result = proc->prc_use_count == proc->prc_int_use_count;
|
|
if (proc->prc_request)
|
|
adjust_dependencies(proc);
|
|
}
|
|
|
|
/* Deallocate all used requests */
|
|
for (ptr = procedures->begin(), end = procedures->end();
|
|
ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = JRD_PRC(*ptr)) && procedure->prc_request &&
|
|
!(procedure->prc_flags & PRC_obsolete) && (procedure != proc) )
|
|
{
|
|
|
|
if ( procedure->prc_int_use_count >= 0 &&
|
|
procedure->prc_use_count == procedure->prc_int_use_count )
|
|
{
|
|
CMP_release(tdbb, procedure->prc_request);
|
|
procedure->prc_request = NULL;
|
|
LCK_release(tdbb, procedure->prc_existence_lock);
|
|
procedure->prc_existence_lock = NULL;
|
|
procedure->prc_flags |= PRC_obsolete;
|
|
} else
|
|
// Leave it in state 0 to avoid extra pass next time to clear it
|
|
procedure->prc_int_use_count = 0;
|
|
}
|
|
}
|
|
|
|
/* Remove deallocated procedures from cache */
|
|
for (ptr = procedures->begin(), end = procedures->end();
|
|
ptr < end; ptr++)
|
|
{
|
|
if ( (procedure = JRD_PRC(*ptr)) &&
|
|
(procedure->prc_flags & PRC_obsolete) && (procedure != proc) )
|
|
{
|
|
procedure->prc_flags &= ~PRC_being_altered; // Just a safety sake
|
|
MET_remove_procedure(tdbb, procedure->prc_id, procedure);
|
|
}
|
|
}
|
|
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
void MET_activate_shadow( TDBB tdbb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ a c t i v a t e _ s h a d o w
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Activate the current database, which presumably
|
|
* was formerly a shadow, by deleting all records
|
|
* corresponding to the shadow that this database
|
|
* represents.
|
|
* Get rid of write ahead log for the activated shadow.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK handle, handle2;
|
|
SCHAR expanded_name[MAXPATHLEN], *dbb_file_name;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
/* Erase any secondary files of the primary database of the
|
|
shadow being activated. */
|
|
|
|
handle = NULL;
|
|
FOR(REQUEST_HANDLE handle) X IN RDB$FILES
|
|
WITH X.RDB$SHADOW_NUMBER NOT MISSING
|
|
AND X.RDB$SHADOW_NUMBER EQ 0
|
|
ERASE X;
|
|
END_FOR;
|
|
|
|
CMP_release(tdbb, (JRD_REQ)handle);
|
|
|
|
dbb_file_name = dbb->dbb_file->fil_string;
|
|
|
|
/* go through files looking for any that expand to the current database name */
|
|
|
|
handle = handle2 = NULL;
|
|
FOR(REQUEST_HANDLE handle) X IN RDB$FILES
|
|
WITH X.RDB$SHADOW_NUMBER NOT MISSING
|
|
AND X.RDB$SHADOW_NUMBER NE 0
|
|
|
|
PIO_expand(X.RDB$FILE_NAME, (USHORT)strlen(X.RDB$FILE_NAME),
|
|
expanded_name);
|
|
|
|
if (!strcmp(expanded_name, dbb_file_name)) {
|
|
FOR(REQUEST_HANDLE handle2) Y IN RDB$FILES
|
|
WITH X.RDB$SHADOW_NUMBER EQ Y.RDB$SHADOW_NUMBER
|
|
MODIFY Y
|
|
Y.RDB$SHADOW_NUMBER = 0;
|
|
END_MODIFY;
|
|
END_FOR;
|
|
ERASE X;
|
|
}
|
|
END_FOR;
|
|
|
|
if (handle2)
|
|
{
|
|
CMP_release(tdbb, (JRD_REQ)handle2);
|
|
}
|
|
CMP_release(tdbb, (JRD_REQ)handle);
|
|
|
|
/* Get rid of WAL after activation. For V4.0, we are not allowing
|
|
WAL and Shadowing to be configured together. So the following
|
|
(commented out) code should be re-visited when we do allow
|
|
such configuration. */
|
|
|
|
/***********************
|
|
handle = 0;
|
|
FOR(REQUEST_HANDLE handle) X IN RDB$LOG_FILES
|
|
ERASE X;
|
|
END_FOR;
|
|
CMP_release(tdbb, handle);
|
|
AIL_modify_log();
|
|
************************/
|
|
}
|
|
|
|
|
|
ULONG MET_align(DSC * desc, USHORT value)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ a l i g n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Align value (presumed offset) on appropriate border
|
|
* and return.
|
|
*
|
|
**************************************/
|
|
USHORT alignment;
|
|
|
|
alignment = desc->dsc_length;
|
|
switch (desc->dsc_dtype) {
|
|
case dtype_text:
|
|
case dtype_cstring:
|
|
return value;
|
|
|
|
case dtype_varying:
|
|
alignment = sizeof(USHORT);
|
|
break;
|
|
}
|
|
|
|
alignment = MIN(alignment, ALIGNMENT);
|
|
|
|
return FB_ALIGN(value, alignment);
|
|
}
|
|
|
|
|
|
void MET_change_fields( TDBB tdbb, JRD_TRA transaction, DSC * field_source)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ c h a n g e _ f i e l d s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Somebody is modifying RDB$FIELDS. Find all relations affected
|
|
* and schedule a format update.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
DSC relation_name;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_m_fields, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATION_FIELDS WITH
|
|
X.RDB$FIELD_SOURCE EQ field_source->dsc_address
|
|
|
|
if (!REQUEST(irq_m_fields)) REQUEST(irq_m_fields) = request;
|
|
|
|
relation_name.dsc_dtype = dtype_text;
|
|
INTL_ASSIGN_DSC(&relation_name, CS_METADATA, COLLATE_NONE);
|
|
relation_name.dsc_length = sizeof(X.RDB$RELATION_NAME);
|
|
relation_name.dsc_address = (UCHAR *) X.RDB$RELATION_NAME;
|
|
SCL_check_relation(&relation_name, SCL_control);
|
|
DFW_post_work(transaction, dfw_update_format, &relation_name, 0);
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_m_fields))
|
|
REQUEST(irq_m_fields) = request;
|
|
}
|
|
|
|
|
|
FMT MET_current(TDBB tdbb, JRD_REL relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ c u r r e n t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get the current format for a relation. The current format is the
|
|
* format in which new records are to be stored.
|
|
*
|
|
**************************************/
|
|
|
|
if (relation->rel_current_format)
|
|
return relation->rel_current_format;
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
return relation->rel_current_format =
|
|
MET_format(tdbb, relation, relation->rel_current_fmt);
|
|
}
|
|
|
|
|
|
void MET_delete_dependencies(TDBB tdbb,
|
|
TEXT* object_name,
|
|
USHORT dependency_type)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ d e l e t e _ d e p e n d e n c i e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Delete all dependencies for the specified
|
|
* object of given type.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
if (!object_name)
|
|
return;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_d_deps, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
DEP IN RDB$DEPENDENCIES
|
|
WITH DEP.RDB$DEPENDENT_NAME = object_name
|
|
AND DEP.RDB$DEPENDENT_TYPE = dependency_type
|
|
|
|
if (!REQUEST(irq_d_deps)) REQUEST(irq_d_deps) = request;
|
|
|
|
ERASE DEP;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_d_deps))
|
|
REQUEST(irq_d_deps) = request;
|
|
}
|
|
|
|
|
|
void MET_delete_shadow( TDBB tdbb, USHORT shadow_number)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ d e l e t e _ s h a d o w
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* When any of the shadows in RDB$FILES for a particular
|
|
* shadow are deleted, stop shadowing to that file and
|
|
* remove all other files from the same shadow.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK handle;
|
|
SDW shadow;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
handle = NULL;
|
|
|
|
FOR(REQUEST_HANDLE handle)
|
|
X IN RDB$FILES WITH X.RDB$SHADOW_NUMBER EQ shadow_number
|
|
ERASE X;
|
|
END_FOR;
|
|
|
|
CMP_release(tdbb, (JRD_REQ)handle);
|
|
|
|
for (shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next)
|
|
if (shadow->sdw_number == shadow_number)
|
|
shadow->sdw_flags |= SDW_shutdown;
|
|
|
|
/* notify other processes to check for shadow deletion */
|
|
if (SDW_lck_update((SLONG) 0))
|
|
SDW_notify();
|
|
}
|
|
|
|
|
|
void MET_error( TEXT * string, ...)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ e r r o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Post an error in a metadata update
|
|
* Oh, wow.
|
|
*
|
|
**************************************/
|
|
TEXT s[128];
|
|
va_list ptr;
|
|
|
|
VA_START(ptr, string);
|
|
vsprintf(s, string, ptr);
|
|
|
|
ERR_post(gds_no_meta_update, gds_arg_gds, gds_random, gds_arg_string,
|
|
ERR_cstring(s), 0);
|
|
}
|
|
|
|
|
|
SCHAR *MET_exact_name( TEXT * str)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ e x a c t _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Trim off trailing spaces from a metadata name.
|
|
* eg: insert a null after the last non-blank character.
|
|
*
|
|
* SQL delimited identifier may have blank as part of the name
|
|
*
|
|
* Parameters: str - the string to terminate
|
|
* Returns: str
|
|
*
|
|
**************************************/
|
|
TEXT *p, *q;
|
|
|
|
q = str - 1;
|
|
for (p = str; *p; p++) {
|
|
if (*p != BLANK)
|
|
q = p;
|
|
}
|
|
|
|
*(q + 1) = '\0';
|
|
return str;
|
|
}
|
|
|
|
|
|
FMT MET_format(TDBB tdbb, JRD_REL relation, USHORT number)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ f o r m a t
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup a format for given relation.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
VEC formats;
|
|
FMT format;
|
|
BLB blob;
|
|
fmt::fmt_desc_iterator desc;
|
|
BLK request;
|
|
USHORT count;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
if ((formats = relation->rel_formats) &&
|
|
(number < formats->count()) &&
|
|
(format = (FMT) (*formats)[number]))
|
|
{
|
|
return format;
|
|
}
|
|
|
|
format = NULL;
|
|
request = (BLK) CMP_find_request(tdbb, irq_r_format, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$FORMATS WITH X.RDB$RELATION_ID EQ relation->rel_id AND
|
|
X.RDB$FORMAT EQ number
|
|
|
|
if (!REQUEST(irq_r_format))
|
|
{
|
|
REQUEST(irq_r_format) = request;
|
|
}
|
|
blob = BLB_open(tdbb, dbb->dbb_sys_trans, (BID)&X.RDB$DESCRIPTOR);
|
|
count = blob->blb_length / sizeof(struct dsc);
|
|
format = fmt::newFmt(*dbb->dbb_permanent, count);
|
|
format->fmt_count = count;
|
|
// BLB_get_data(tdbb, blob, (UCHAR*) &*(format->fmt_desc.begin()), blob->blb_length);
|
|
BLB_get_data(tdbb, blob, (UCHAR*) &(format->fmt_desc[0]), blob->blb_length);
|
|
|
|
for (desc = format->fmt_desc.end() - 1;
|
|
desc >= format->fmt_desc.begin();
|
|
--desc)
|
|
{
|
|
if (desc->dsc_address)
|
|
{
|
|
format->fmt_length =
|
|
(ULONG) desc->dsc_address + desc->dsc_length;
|
|
break;
|
|
}
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_format)) {
|
|
REQUEST(irq_r_format) = request;
|
|
}
|
|
|
|
if (!format) {
|
|
format = fmt::newFmt(*dbb->dbb_permanent);
|
|
}
|
|
|
|
format->fmt_version = number;
|
|
|
|
/* Link the format block into the world */
|
|
|
|
formats = relation->rel_formats;
|
|
if (!formats) {
|
|
formats = vec::newVector(*dbb->dbb_permanent, number+1);
|
|
relation->rel_formats = formats;
|
|
} else {
|
|
formats->resize(number+1);
|
|
}
|
|
(*formats)[number] = (BLK) format;
|
|
|
|
return format;
|
|
}
|
|
|
|
|
|
BOOLEAN MET_get_char_subtype(TDBB tdbb,
|
|
SSHORT * id, UCHAR * name, USHORT length)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ c h a r _ s u b t y p e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Character types can be specified as either:
|
|
* a) A POSIX style locale name "<collation>.<characterset>"
|
|
* or
|
|
* b) A simple <characterset> name (using default collation)
|
|
* c) A simple <collation> name (use charset for collation)
|
|
*
|
|
* Given an ASCII7 string which could be any of the above, try to
|
|
* resolve the name in the order a, b, c
|
|
* a) is only tried iff the name contains a period.
|
|
* (in which case b) and c) are not tried).
|
|
*
|
|
* Return:
|
|
* 1 if no errors (and *id is set).
|
|
* 0 if the name could not be resolved.
|
|
*
|
|
**************************************/
|
|
UCHAR buffer[32]; /* BASED ON RDB$COLLATION_NAME */
|
|
UCHAR *p;
|
|
UCHAR *period = NULL;
|
|
UCHAR *end_name;
|
|
BOOLEAN res;
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
assert(id != NULL);
|
|
assert(name != NULL);
|
|
|
|
end_name = name + length;
|
|
|
|
/* Force key to uppercase, following C locale rules for uppercasing */
|
|
/* At the same time, search for the first period in the string (if any) */
|
|
|
|
for (p = buffer;
|
|
name < end_name && p < buffer + sizeof(buffer) - 1; p++, name++) {
|
|
*p = UPPER7(*name);
|
|
if ((*p == '.') && !period)
|
|
period = p;
|
|
}
|
|
*p = 0;
|
|
|
|
/* Is there a period, separating collation name from character set? */
|
|
if (period) {
|
|
*period = 0;
|
|
return resolve_charset_and_collation(tdbb, id, period + 1, buffer);
|
|
}
|
|
|
|
else {
|
|
/* Is it a character set name (implying charset default collation sequence) */
|
|
|
|
res = resolve_charset_and_collation(tdbb, id, buffer, NULL);
|
|
if (!res) {
|
|
/* Is it a collation name (implying implementation-default character set) */
|
|
res = resolve_charset_and_collation(tdbb, id, NULL, buffer);
|
|
}
|
|
return res;
|
|
}
|
|
}
|
|
|
|
|
|
JRD_NOD MET_get_dependencies(TDBB tdbb,
|
|
JRD_REL relation,
|
|
TEXT* blob,
|
|
CSB view_csb,
|
|
SLONG blob_id[2],
|
|
JRD_REQ* request,
|
|
CSB* csb_ptr,
|
|
const TEXT* object_name,
|
|
USHORT type)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ d e p e n d e n c i e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get dependencies for an object by parsing
|
|
* the blr used in its definition.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
CSB csb_;
|
|
JRD_NOD node;
|
|
BLK handle;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
csb_ = Csb::newCsb(*tdbb->tdbb_default, 5);
|
|
csb_->csb_rpt.resize(5); // vec always allocates one too many
|
|
csb_->csb_count = 5;
|
|
csb_->csb_g_flags |= csb_get_dependencies;
|
|
|
|
if (blob)
|
|
{
|
|
node = PAR_blr( tdbb,
|
|
relation,
|
|
(UCHAR*)blob,
|
|
view_csb,
|
|
&csb_,
|
|
request,
|
|
(BOOLEAN)((type == obj_trigger) ? TRUE : FALSE),
|
|
0);
|
|
}
|
|
else
|
|
{
|
|
node =
|
|
MET_parse_blob( tdbb,
|
|
relation,
|
|
blob_id,
|
|
&csb_,
|
|
request,
|
|
(BOOLEAN)((type == obj_trigger) ? TRUE : FALSE),
|
|
FALSE);
|
|
}
|
|
|
|
if (type == (USHORT) obj_computed)
|
|
{
|
|
handle = NULL;
|
|
FOR(REQUEST_HANDLE handle)
|
|
RLF IN RDB$RELATION_FIELDS CROSS
|
|
FLD IN RDB$FIELDS WITH
|
|
RLF.RDB$FIELD_SOURCE EQ FLD.RDB$FIELD_NAME AND
|
|
RLF.RDB$RELATION_NAME EQ relation->rel_name AND
|
|
RLF.RDB$FIELD_NAME EQ object_name
|
|
object_name = FLD.RDB$FIELD_NAME;
|
|
END_FOR;
|
|
CMP_release(tdbb, (JRD_REQ)handle);
|
|
}
|
|
|
|
store_dependencies(tdbb, csb_, object_name, type);
|
|
|
|
if (csb_ptr)
|
|
{
|
|
*csb_ptr = csb_;
|
|
}
|
|
else
|
|
{
|
|
delete csb_;
|
|
}
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
JRD_FLD MET_get_field(JRD_REL relation, USHORT id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ f i e l d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get the field block for a field if possible. If not,
|
|
* return NULL;
|
|
*
|
|
**************************************/
|
|
VEC vector;
|
|
|
|
if (!relation ||
|
|
!(vector = relation->rel_fields) ||
|
|
id >= vector->count()) return NULL;
|
|
|
|
return (JRD_FLD) (*vector)[id];
|
|
}
|
|
|
|
|
|
void MET_get_shadow_files( TDBB tdbb, USHORT deleteVar)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ s h a d o w _ f i l e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Check the shadows found in the database against
|
|
* our in-memory list: if any new shadow files have
|
|
* been defined since the last time we looked, start
|
|
* shadowing to them; if any have been deleted, stop
|
|
* shadowing to them.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK handle;
|
|
USHORT file_flags;
|
|
SDW shadow;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
handle = NULL;
|
|
FOR(REQUEST_HANDLE handle) X IN RDB$FILES
|
|
WITH X.RDB$SHADOW_NUMBER NOT MISSING
|
|
AND X.RDB$SHADOW_NUMBER NE 0
|
|
AND X.RDB$FILE_SEQUENCE EQ 0
|
|
if ((X.RDB$FILE_FLAGS & FILE_shadow) &&
|
|
!(X.RDB$FILE_FLAGS & FILE_inactive))
|
|
{
|
|
file_flags = X.RDB$FILE_FLAGS;
|
|
SDW_start(X.RDB$FILE_NAME, X.RDB$SHADOW_NUMBER, file_flags,
|
|
deleteVar);
|
|
|
|
/* if the shadow exists, mark the appropriate shadow
|
|
block as found for the purposes of this routine;
|
|
if the shadow was conditional and is no longer, note it */
|
|
|
|
for (shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next)
|
|
{
|
|
if ((shadow->sdw_number == X.RDB$SHADOW_NUMBER) &&
|
|
!(shadow->sdw_flags & SDW_IGNORE))
|
|
{
|
|
shadow->sdw_flags |= SDW_found;
|
|
if (!(file_flags & FILE_conditional))
|
|
shadow->sdw_flags &= ~SDW_conditional;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
END_FOR;
|
|
|
|
CMP_release(tdbb, (JRD_REQ)handle);
|
|
|
|
/* if any current shadows were not defined in database, mark
|
|
them to be shutdown since they don't exist anymore */
|
|
|
|
for (shadow = dbb->dbb_shadow; shadow; shadow = shadow->sdw_next)
|
|
if (!(shadow->sdw_flags & SDW_found))
|
|
shadow->sdw_flags |= SDW_shutdown;
|
|
else
|
|
shadow->sdw_flags &= ~SDW_found;
|
|
|
|
SDW_check();
|
|
}
|
|
|
|
|
|
int MET_get_walinfo(
|
|
TDBB tdbb,
|
|
LGFILE ** logfiles_, ULONG * number, LGFILE ** over_flow)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ g e t _ w a l i n f o
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get WAL information from RDB$LOG_FILES
|
|
* Initialize over_flow pointer if over flow file is specified.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK handle;
|
|
SSHORT num = 0;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
handle = NULL;
|
|
|
|
FOR(REQUEST_HANDLE handle)
|
|
LOG IN RDB$LOG_FILES SORTED BY LOG.RDB$FILE_SEQUENCE
|
|
logfiles_[num] = FB_NEW_RPT(*dbb->dbb_permanent,
|
|
LGFILE_SIZE + MAXPATHLEN) logfiles();
|
|
strcpy(logfiles_[num]->lg_name, LOG.RDB$FILE_NAME);
|
|
logfiles_[num]->lg_size = LOG.RDB$FILE_LENGTH;
|
|
logfiles_[num]->lg_partitions = LOG.RDB$FILE_PARTITIONS;
|
|
logfiles_[num]->lg_flags = LOG.RDB$FILE_FLAGS;
|
|
logfiles_[num]->lg_sequence = LOG.RDB$FILE_SEQUENCE;
|
|
|
|
num++;
|
|
END_FOR;
|
|
|
|
if (handle)
|
|
{
|
|
CMP_release(tdbb, (JRD_REQ)handle);
|
|
}
|
|
|
|
if (num)
|
|
{
|
|
*over_flow = logfiles_[num - 1];
|
|
|
|
/* The overflow file will be passed separately */
|
|
|
|
if (over_flow[0]->lg_flags & LOG_overflow)
|
|
num--;
|
|
else
|
|
*over_flow = (LGFILE *) 0;
|
|
}
|
|
|
|
*number = num;
|
|
|
|
return num;
|
|
}
|
|
|
|
|
|
void MET_load_trigger(
|
|
TDBB tdbb,
|
|
JRD_REL relation, TEXT * trigger_name, TRIG_VEC* triggers)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o a d _ t r i g g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Load triggers from RDB$TRIGGERS. If a requested,
|
|
* also load triggers from RDB$RELATIONS.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK trigger_request;
|
|
USHORT trig_flags;
|
|
TEXT errmsg[MAX_ERRMSG_LEN + 1];
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
CHECK_DBB(dbb);
|
|
|
|
if (relation->rel_flags & REL_sys_trigs_being_loaded)
|
|
return;
|
|
|
|
/* No need to load triggers for ReadOnly databases,
|
|
since INSERT/DELETE/UPDATE statements are not going to be allowed */
|
|
if (dbb->dbb_flags & DBB_read_only)
|
|
return;
|
|
|
|
/* Scan RDB$TRIGGERS next */
|
|
|
|
trigger_request =
|
|
(BLK) CMP_find_request(tdbb, irq_s_triggers, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE trigger_request)
|
|
TRG IN RDB$TRIGGERS WITH TRG.RDB$RELATION_NAME =
|
|
relation->rel_name AND TRG.
|
|
RDB$TRIGGER_NAME EQ trigger_name if (!REQUEST(irq_s_triggers))
|
|
REQUEST(irq_s_triggers) = trigger_request;
|
|
|
|
if (TRG.RDB$TRIGGER_TYPE > 0 && TRG.RDB$TRIGGER_TYPE < TRIGGER_COMBINED_MAX)
|
|
{
|
|
/* check if the trigger is to be fired without any permissions
|
|
checks. Verify such a claim */
|
|
trig_flags = (USHORT) TRG.RDB$FLAGS;
|
|
|
|
/* if there is an ignore permission flag, see if it is legit */
|
|
if ((TRG.RDB$FLAGS & TRG_ignore_perm) &&
|
|
!verify_TRG_ignore_perm(tdbb, trigger_name))
|
|
{
|
|
gds__msg_format(NULL_PTR, JRD_BUGCHK, 304, sizeof(errmsg),
|
|
errmsg, trigger_name, (TEXT*)NULL_PTR, (TEXT*)NULL_PTR,
|
|
(TEXT*)NULL_PTR, (TEXT*)NULL_PTR);
|
|
ERR_log(JRD_BUGCHK, 304, errmsg);
|
|
|
|
trig_flags &= ~TRG_ignore_perm;
|
|
}
|
|
|
|
// 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,
|
|
(SLONG*)&TRG.RDB$TRIGGER_BLR,
|
|
triggers + trigger_action,
|
|
TRG.RDB$TRIGGER_NAME,
|
|
(BOOLEAN)TRG.RDB$SYSTEM_FLAG,
|
|
trig_flags);
|
|
}
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_s_triggers))
|
|
REQUEST(irq_s_triggers) = trigger_request;
|
|
}
|
|
|
|
|
|
|
|
void DLL_EXPORT MET_lookup_cnstrt_for_index(TDBB tdbb,
|
|
TEXT* constraint_name,
|
|
const TEXT* index_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ c n s t r t _ f o r _ i n d e x
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup constraint name from index name, if one exists.
|
|
* Calling routine must pass a buffer of at least 32 bytes.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
constraint_name[0] = 0;
|
|
request = (BLK) CMP_find_request(tdbb, irq_l_cnstrt, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATION_CONSTRAINTS WITH X.RDB$INDEX_NAME EQ index_name
|
|
|
|
if (!REQUEST(irq_l_cnstrt))
|
|
REQUEST(irq_l_cnstrt) = request;
|
|
|
|
X.RDB$CONSTRAINT_NAME[name_length(X.RDB$CONSTRAINT_NAME)] = 0;
|
|
strcpy(constraint_name, X.RDB$CONSTRAINT_NAME);
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_cnstrt))
|
|
REQUEST(irq_l_cnstrt) = request;
|
|
}
|
|
|
|
|
|
void MET_lookup_cnstrt_for_trigger(
|
|
TDBB tdbb,
|
|
TEXT * constraint_name,
|
|
TEXT * relation_name, TEXT * trigger_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ c n s t r t _ f o r _ t r i g g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup constraint name from trigger name, if one exists.
|
|
* Calling routine must pass a buffer of at least 32 bytes.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request, request2;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
constraint_name[0] = 0;
|
|
relation_name[0] = 0;
|
|
request = (BLK) CMP_find_request(tdbb, irq_l_check, IRQ_REQUESTS);
|
|
request2 = (BLK) CMP_find_request(tdbb, irq_l_check2, IRQ_REQUESTS);
|
|
|
|
/* utilize two requests rather than one so that we
|
|
guarantee we always return the name of the relation
|
|
that the trigger is defined on, even if we don't
|
|
have a check constraint defined for that trigger */
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
Y IN RDB$TRIGGERS WITH
|
|
Y.RDB$TRIGGER_NAME EQ trigger_name
|
|
|
|
if (!REQUEST(irq_l_check)) REQUEST(irq_l_check) = request;
|
|
|
|
FOR(REQUEST_HANDLE request2)
|
|
X IN RDB$CHECK_CONSTRAINTS WITH
|
|
X.RDB$TRIGGER_NAME EQ Y.RDB$TRIGGER_NAME
|
|
|
|
if (!REQUEST(irq_l_check2)) REQUEST(irq_l_check2) = request2;
|
|
|
|
X.RDB$CONSTRAINT_NAME[name_length(X.RDB$CONSTRAINT_NAME)] = 0;
|
|
strcpy(constraint_name, X.RDB$CONSTRAINT_NAME);
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_check2))
|
|
REQUEST(irq_l_check2) = request2;
|
|
|
|
Y.RDB$RELATION_NAME[name_length(Y.RDB$RELATION_NAME)] = 0;
|
|
strcpy(relation_name, Y.RDB$RELATION_NAME);
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_check))
|
|
REQUEST(irq_l_check) = request;
|
|
}
|
|
|
|
|
|
void MET_lookup_exception(TDBB tdbb,
|
|
SLONG number,
|
|
TEXT* name,
|
|
TEXT* message)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ e x c e p t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup exception by number and return its name and message.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
//SCHAR *p;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
/* We need to look up exception in RDB$EXCEPTIONS */
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_l_exception, IRQ_REQUESTS);
|
|
|
|
*name = 0;
|
|
if (message)
|
|
{
|
|
*message = 0;
|
|
}
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$EXCEPTIONS WITH X.RDB$EXCEPTION_NUMBER = number
|
|
|
|
if (!REQUEST(irq_l_exception))
|
|
{
|
|
REQUEST(irq_l_exception) = request;
|
|
}
|
|
|
|
if (!X.RDB$EXCEPTION_NAME.NULL)
|
|
{
|
|
name_copy(name, X.RDB$EXCEPTION_NAME);
|
|
}
|
|
if (!X.RDB$MESSAGE.NULL && message)
|
|
{
|
|
name_copy(message, X.RDB$MESSAGE);
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_exception))
|
|
{
|
|
REQUEST(irq_l_exception) = request;
|
|
}
|
|
}
|
|
|
|
|
|
SLONG MET_lookup_exception_number(TDBB tdbb, TEXT * name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ e x c e p t i o n _ n u m b e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup exception by name and return its number.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
SLONG number;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
/* We need to look up exception in RDB$EXCEPTIONS */
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_l_except_no, IRQ_REQUESTS);
|
|
|
|
number = 0;
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$EXCEPTIONS WITH X.RDB$EXCEPTION_NAME = name
|
|
|
|
if (!REQUEST(irq_l_except_no)) REQUEST(irq_l_except_no) = request;
|
|
|
|
number = X.RDB$EXCEPTION_NUMBER;
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_except_no))
|
|
REQUEST(irq_l_except_no) = request;
|
|
|
|
return number;
|
|
}
|
|
|
|
|
|
int MET_lookup_field(TDBB tdbb, JRD_REL relation, const TEXT* name, const TEXT* security_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ f i e l d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up a field name.
|
|
* Additinally, if security_name is a not null pointer,
|
|
* it's used to include the condition that it should match
|
|
* the field's security class name, too.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
VEC vector;
|
|
vec::iterator fieldIter, end;
|
|
const TEXT* p;
|
|
const TEXT* q;
|
|
USHORT id;
|
|
UCHAR length;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
/* Start by checking field names that we already know */
|
|
|
|
if ( (vector = relation->rel_fields) ) {
|
|
length = strlen(name);
|
|
for (fieldIter = vector->begin(), id = 0, end = vector->end();
|
|
fieldIter < end; fieldIter++, id++) {
|
|
if (*fieldIter) {
|
|
JRD_FLD field = (JRD_FLD) *fieldIter;
|
|
|
|
if (field->fld_length == length && (p = field->fld_name)) {
|
|
q = name;
|
|
while (*p++ == *q) { // Check equal till end of string
|
|
if (!*q++) { // Found end of string and so are equal
|
|
if (!security_name) {
|
|
return id;
|
|
}
|
|
|
|
USHORT nl2 = name_length (security_name);
|
|
if (field->fld_security_name) {
|
|
USHORT nl = name_length(field->fld_security_name);
|
|
if (nl == nl2 && !strncmp(field->fld_security_name, security_name, nl)) {
|
|
return id;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Not found. Next, try system relations directly */
|
|
|
|
id = -1U;
|
|
|
|
if (!relation->rel_name)
|
|
{
|
|
return id;
|
|
}
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_l_field, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATION_FIELDS WITH
|
|
X.RDB$RELATION_NAME EQ relation->rel_name AND
|
|
X.RDB$FIELD_NAME EQ name
|
|
|
|
if (!REQUEST(irq_l_field)) {
|
|
REQUEST(irq_l_field) = request;
|
|
}
|
|
|
|
if (!security_name) {
|
|
id = X.RDB$FIELD_ID;
|
|
}
|
|
else {
|
|
USHORT nl, nl2;
|
|
nl2 = name_length (security_name);
|
|
if (!X.RDB$SECURITY_CLASS.NULL
|
|
&& (nl = name_length (X.RDB$SECURITY_CLASS)) == nl2
|
|
&& !strncmp (X.RDB$SECURITY_CLASS, security_name, nl)) {
|
|
|
|
id = X.RDB$FIELD_ID;
|
|
}
|
|
}
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_field)) {
|
|
REQUEST(irq_l_field) = request;
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
BLF MET_lookup_filter(TDBB tdbb, SSHORT from, SSHORT to)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ f i l t e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
PTR filter;
|
|
MOD module;
|
|
LLS stack;
|
|
BLF blf_;
|
|
STR exception_msg;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
filter = NULL;
|
|
blf_ = NULL;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_r_filters, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$FILTERS WITH X.RDB$INPUT_SUB_TYPE EQ from AND
|
|
X.RDB$OUTPUT_SUB_TYPE EQ to
|
|
|
|
if (!REQUEST(irq_r_filters))
|
|
REQUEST(irq_r_filters) = request;
|
|
MET_exact_name(X.RDB$MODULE_NAME);
|
|
MET_exact_name(X.RDB$ENTRYPOINT);
|
|
filter = (STATUS (*)(USHORT, CTL))
|
|
ISC_lookup_entrypoint(X.RDB$MODULE_NAME, X.RDB$ENTRYPOINT,
|
|
ISC_EXT_LIB_PATH_ENV);
|
|
if (filter)
|
|
{
|
|
blf_ = FB_NEW(*dbb->dbb_permanent) blf();
|
|
blf_->blf_next = NULL;
|
|
blf_->blf_from = from;
|
|
blf_->blf_to = to;
|
|
blf_->blf_filter = filter;
|
|
exception_msg =
|
|
FB_NEW_RPT(*dbb->dbb_permanent,
|
|
strlen(EXCEPTION_MESSAGE) +
|
|
strlen(X.RDB$FUNCTION_NAME) +
|
|
strlen(X.RDB$ENTRYPOINT) +
|
|
strlen(X.RDB$MODULE_NAME) + 1) str();
|
|
sprintf((char*)exception_msg->str_data, EXCEPTION_MESSAGE,
|
|
X.RDB$FUNCTION_NAME, X.RDB$ENTRYPOINT, X.RDB$MODULE_NAME);
|
|
blf_->blf_exception_message = exception_msg;
|
|
}
|
|
if ( (module = FLU_lookup_module(X.RDB$MODULE_NAME)) )
|
|
{
|
|
/* Register interest in the module by database. */
|
|
|
|
for (stack = dbb->dbb_modules; stack; stack = stack->lls_next)
|
|
if (module == (MOD) stack->lls_object)
|
|
break;
|
|
|
|
/* If the module was already registered with this database
|
|
then decrement the use count that was incremented in
|
|
ISC_lookup_entrypoint() above. Otherwise push it onto
|
|
the stack of registered modules. */
|
|
|
|
if (stack)
|
|
FLU_unregister_module(module);
|
|
else
|
|
{
|
|
JrdMemoryPool *old_pool;
|
|
|
|
old_pool = tdbb->tdbb_default;
|
|
tdbb->tdbb_default = dbb->dbb_permanent;
|
|
LLS_PUSH(module, &dbb->dbb_modules);
|
|
tdbb->tdbb_default = old_pool;
|
|
}
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_filters))
|
|
REQUEST(irq_r_filters) = request;
|
|
|
|
return blf_;
|
|
}
|
|
|
|
|
|
SLONG MET_lookup_generator(TDBB tdbb, TEXT * name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ g e n e r a t o r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup generator (aka gen_id). If we can't find it, make a new one.
|
|
* CVC: I don't see how this function "makes" a new generator; it simply
|
|
* returns -1 if the name is not found.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
SLONG gen_id;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
if (!strcmp(name, "RDB$GENERATORS"))
|
|
return 0;
|
|
|
|
gen_id = -1;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_r_gen_id, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$GENERATORS WITH X.RDB$GENERATOR_NAME EQ name
|
|
|
|
if (!REQUEST(irq_r_gen_id))
|
|
REQUEST(irq_r_gen_id) = request;
|
|
|
|
gen_id = X.RDB$GENERATOR_ID;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_gen_id))
|
|
REQUEST(irq_r_gen_id) = request;
|
|
|
|
return gen_id;
|
|
}
|
|
|
|
void MET_lookup_generator_id (TDBB tdbb, SLONG gen_id, TEXT *name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ g e n e r a t o r _ i d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup generator (aka gen_id) by ID. It will load
|
|
* the name in the third parameter.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
|
|
SET_TDBB (tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
if (!gen_id) {
|
|
strcpy (name, "RDB$GENERATORS");
|
|
return;
|
|
}
|
|
|
|
*name = 0;
|
|
|
|
request = (BLK) CMP_find_request (tdbb, irq_r_gen_id_num, IRQ_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE request)
|
|
X IN RDB$GENERATORS WITH X.RDB$GENERATOR_ID EQ gen_id
|
|
|
|
if (!REQUEST (irq_r_gen_id_num))
|
|
REQUEST (irq_r_gen_id_num) = request;
|
|
name_copy (name, X.RDB$GENERATOR_NAME);
|
|
END_FOR;
|
|
|
|
if (!REQUEST (irq_r_gen_id_num))
|
|
REQUEST (irq_r_gen_id_num) = request;
|
|
}
|
|
|
|
void DLL_EXPORT MET_lookup_index(TDBB tdbb,
|
|
TEXT * index_name,
|
|
TEXT * relation_name, USHORT number)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ i n d e x
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup index name from relation and index number.
|
|
* Calling routine must pass a buffer of at least 32 bytes.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
index_name[0] = 0;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_l_index, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$INDICES WITH X.RDB$RELATION_NAME EQ relation_name
|
|
AND X.RDB$INDEX_ID EQ number
|
|
|
|
if (!REQUEST(irq_l_index))
|
|
REQUEST(irq_l_index) = request;
|
|
|
|
X.RDB$INDEX_NAME[name_length(X.RDB$INDEX_NAME)] = 0;
|
|
strcpy(index_name, X.RDB$INDEX_NAME);
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_index))
|
|
REQUEST(irq_l_index) = request;
|
|
}
|
|
|
|
|
|
SLONG MET_lookup_index_name(TDBB tdbb,
|
|
TEXT * index_name,
|
|
SLONG * relation_id, SSHORT * status)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ i n d e x _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup index id from index name.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
SLONG id = -1;
|
|
JRD_REL relation;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_l_index_name, IRQ_REQUESTS);
|
|
|
|
*status = MET_object_unknown;
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$INDICES WITH
|
|
X.RDB$INDEX_NAME EQ index_name
|
|
|
|
if (!REQUEST(irq_l_index_name))
|
|
REQUEST(irq_l_index_name) = request;
|
|
|
|
if (X.RDB$INDEX_INACTIVE == 0)
|
|
*status = MET_object_active;
|
|
else
|
|
*status = MET_object_inactive;
|
|
|
|
id = X.RDB$INDEX_ID - 1;
|
|
relation = MET_lookup_relation(tdbb, X.RDB$RELATION_NAME);
|
|
*relation_id = relation->rel_id;
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_index_name))
|
|
REQUEST(irq_l_index_name) = request;
|
|
|
|
return id;
|
|
}
|
|
|
|
|
|
int MET_lookup_partner(
|
|
TDBB tdbb, JRD_REL relation, IDX * idx, UCHAR * index_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ p a r t n e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Find partner index participating in a
|
|
* foreign key relationship.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
JRD_REL partner_relation;
|
|
int index_number, found;
|
|
PRIM dependencies;
|
|
FRGN references;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
if (relation->rel_flags & REL_check_partners)
|
|
{
|
|
/* Prepare for rescan of foreign references on other relations'
|
|
primary keys and release stale vectors. */
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_foreign1, IRQ_REQUESTS);
|
|
references = &relation->rel_foreign_refs;
|
|
index_number = 0;
|
|
|
|
if (references->frgn_reference_ids) {
|
|
delete references->frgn_reference_ids;
|
|
references->frgn_reference_ids = (VEC) NULL_PTR;
|
|
}
|
|
if (references->frgn_relations) {
|
|
delete references->frgn_relations;
|
|
references->frgn_relations = (VEC) NULL_PTR;
|
|
}
|
|
if (references->frgn_indexes) {
|
|
delete references->frgn_indexes;
|
|
references->frgn_indexes = (VEC) NULL_PTR;
|
|
}
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
IDX IN RDB$INDICES CROSS
|
|
RC IN RDB$RELATION_CONSTRAINTS
|
|
OVER RDB$INDEX_NAME CROSS
|
|
IND IN RDB$INDICES WITH
|
|
RC.RDB$CONSTRAINT_TYPE EQ "FOREIGN KEY" AND
|
|
IDX.RDB$RELATION_NAME EQ relation->rel_name AND
|
|
IND.RDB$INDEX_NAME EQ IDX.RDB$FOREIGN_KEY AND
|
|
IND.RDB$UNIQUE_FLAG NOT MISSING
|
|
|
|
if (!REQUEST(irq_foreign1))
|
|
{
|
|
REQUEST(irq_foreign1) = request;
|
|
}
|
|
|
|
partner_relation =
|
|
MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME);
|
|
|
|
if (partner_relation)
|
|
{
|
|
if (!references->frgn_reference_ids) {
|
|
references->frgn_reference_ids =
|
|
vec::newVector(*dbb->dbb_permanent,
|
|
(USHORT)index_number + 1);
|
|
} else {
|
|
references->frgn_reference_ids->resize((USHORT)index_number + 1);
|
|
}
|
|
|
|
(*references->frgn_reference_ids)[index_number] =
|
|
(BLK) (IDX.RDB$INDEX_ID - 1);
|
|
|
|
if (!references->frgn_relations) {
|
|
references->frgn_relations =
|
|
vec::newVector(*dbb->dbb_permanent,
|
|
(USHORT)index_number + 1);
|
|
} else {
|
|
references->frgn_relations->resize((USHORT)index_number + 1);
|
|
}
|
|
|
|
(*references->frgn_relations)[index_number] =
|
|
(BLK) partner_relation->rel_id;
|
|
|
|
if (!references->frgn_indexes) {
|
|
references->frgn_indexes =
|
|
vec::newVector(*dbb->dbb_permanent,
|
|
(USHORT)index_number + 1);
|
|
} else {
|
|
references->frgn_indexes->resize((USHORT)index_number + 1);
|
|
}
|
|
|
|
(*references->frgn_indexes)[index_number] =
|
|
(BLK) (IND.RDB$INDEX_ID - 1);
|
|
|
|
index_number++;
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_foreign1))
|
|
{
|
|
REQUEST(irq_foreign1) = request;
|
|
}
|
|
|
|
/* Prepare for rescan of primary dependencies on relation's primary
|
|
key and stale vectors. */
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_foreign2, IRQ_REQUESTS);
|
|
dependencies = &relation->rel_primary_dpnds;
|
|
index_number = 0;
|
|
|
|
if (dependencies->prim_reference_ids) {
|
|
delete dependencies->prim_reference_ids;
|
|
dependencies->prim_reference_ids = (VEC) NULL_PTR;
|
|
}
|
|
if (dependencies->prim_relations) {
|
|
delete dependencies->prim_relations;
|
|
dependencies->prim_relations = (VEC) NULL_PTR;
|
|
}
|
|
if (dependencies->prim_indexes) {
|
|
delete dependencies->prim_indexes;
|
|
dependencies->prim_indexes = (VEC) NULL_PTR;
|
|
}
|
|
|
|
/*
|
|
** ============================================================
|
|
** ==
|
|
** == since UNIQUE constraint also could be used as primary key
|
|
** == therefore we change:
|
|
** ==
|
|
** == IDX.RDB$INDEX_NAME STARTING WITH "RDB$PRIMARY"
|
|
** ==
|
|
** == to
|
|
** ==
|
|
** == IDX.RDB$UNIQUE_FLAG = 1
|
|
** ==
|
|
** ============================================================
|
|
*/
|
|
|
|
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 AND
|
|
IND.RDB$FOREIGN_KEY EQ IDX.RDB$INDEX_NAME
|
|
|
|
if (!REQUEST(irq_foreign2)) {
|
|
REQUEST(irq_foreign2) = request;
|
|
}
|
|
partner_relation = MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME);
|
|
if (partner_relation)
|
|
{
|
|
if (!dependencies->prim_reference_ids) {
|
|
dependencies->prim_reference_ids =
|
|
vec::newVector(*dbb->dbb_permanent,
|
|
(USHORT)index_number+1);
|
|
} else {
|
|
dependencies->prim_reference_ids->resize((USHORT)index_number+1);
|
|
}
|
|
|
|
(*dependencies->prim_reference_ids)[index_number] =
|
|
(BLK) (IDX.RDB$INDEX_ID - 1);
|
|
|
|
if (!dependencies->prim_relations) {
|
|
dependencies->prim_relations =
|
|
vec::newVector(*dbb->dbb_permanent,
|
|
(USHORT)index_number+1);
|
|
} else {
|
|
dependencies->prim_relations->resize((USHORT)index_number+1);
|
|
}
|
|
|
|
(*dependencies->prim_relations)[index_number] =
|
|
(BLK) partner_relation->rel_id;
|
|
|
|
if (!dependencies->prim_indexes) {
|
|
dependencies->prim_indexes =
|
|
vec::newVector(*dbb->dbb_permanent,
|
|
(USHORT)index_number+1);
|
|
} else {
|
|
dependencies->prim_indexes->resize((USHORT)index_number+1);
|
|
}
|
|
|
|
(*dependencies->prim_indexes)[index_number] =
|
|
(BLK) (IND.RDB$INDEX_ID - 1);
|
|
|
|
index_number++;
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_foreign2))
|
|
REQUEST(irq_foreign2) = request;
|
|
|
|
relation->rel_flags &= ~REL_check_partners;
|
|
}
|
|
|
|
if (idx->idx_flags & idx_foreign)
|
|
{
|
|
if (*index_name)
|
|
{
|
|
/* Since primary key index names aren't being cached, do a long
|
|
hard lookup. This is only called during index create for foreign
|
|
keys. */
|
|
|
|
found = FALSE;
|
|
request = (BLK) NULL_PTR;
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
IDX IN RDB$INDICES CROSS
|
|
IND IN RDB$INDICES WITH
|
|
IDX.RDB$RELATION_NAME EQ relation->rel_name AND
|
|
(IDX.RDB$INDEX_ID EQ idx->idx_id + 1 OR
|
|
IDX.RDB$INDEX_NAME EQ index_name) AND
|
|
IND.RDB$INDEX_NAME EQ IDX.RDB$FOREIGN_KEY AND
|
|
IND.RDB$UNIQUE_FLAG NOT MISSING
|
|
|
|
partner_relation =
|
|
MET_lookup_relation(tdbb, IND.RDB$RELATION_NAME);
|
|
|
|
if (partner_relation)
|
|
{
|
|
idx->idx_primary_relation = partner_relation->rel_id;
|
|
idx->idx_primary_index = IND.RDB$INDEX_ID - 1;
|
|
found = TRUE;
|
|
}
|
|
END_FOR;
|
|
|
|
CMP_release(tdbb, (JRD_REQ)request);
|
|
return found;
|
|
}
|
|
|
|
references = &relation->rel_foreign_refs;
|
|
if (references->frgn_reference_ids)
|
|
{
|
|
for (index_number = 0;
|
|
index_number < references->frgn_reference_ids->count();
|
|
index_number++)
|
|
{
|
|
if (idx->idx_id == (UCHAR) (*references->frgn_reference_ids)
|
|
[index_number])
|
|
{
|
|
idx->idx_primary_relation =
|
|
(USHORT) (*references->frgn_relations)[index_number];
|
|
idx->idx_primary_index =
|
|
(UCHAR) (*references->frgn_indexes)[index_number];
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
else if (idx->idx_flags & (idx_primary | idx_unique))
|
|
{
|
|
dependencies = &relation->rel_primary_dpnds;
|
|
if (dependencies->prim_reference_ids)
|
|
{
|
|
for (index_number = 0;
|
|
index_number < dependencies->prim_reference_ids->count();
|
|
index_number++)
|
|
{
|
|
if (idx->idx_id == (UCHAR) (*dependencies->prim_reference_ids)
|
|
[index_number])
|
|
{
|
|
idx->idx_foreign_primaries =
|
|
relation->rel_primary_dpnds.prim_reference_ids;
|
|
idx->idx_foreign_relations =
|
|
relation->rel_primary_dpnds.prim_relations;
|
|
idx->idx_foreign_indexes =
|
|
relation->rel_primary_dpnds.prim_indexes;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
JRD_PRC MET_lookup_procedure(TDBB tdbb, SCHAR * name, BOOLEAN noscan)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup procedure by name. Name passed in is
|
|
* ASCIZ name.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
JRD_PRC procedure;
|
|
vec::iterator ptr, end;
|
|
VEC procedures;
|
|
SCHAR *p, *q;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
/* See if we already know the procedure by name */
|
|
|
|
if ( (procedures = dbb->dbb_procedures) )
|
|
for (ptr = procedures->begin(), end = procedures->end();
|
|
ptr < end; ptr++)
|
|
{
|
|
if ((procedure = JRD_PRC(*ptr))&& !(procedure->prc_flags & PRC_obsolete)
|
|
&& ((procedure->prc_flags & PRC_scanned) || noscan)
|
|
&& !(procedure->prc_flags & PRC_being_scanned)
|
|
&& !(procedure->prc_flags & PRC_being_altered)
|
|
&& procedure->prc_name)
|
|
{
|
|
p = (SCHAR*)procedure->prc_name->str_data;
|
|
for (q = name; *p == *q; p++, q++)
|
|
{
|
|
if (*p == 0)
|
|
return procedure;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* We need to look up the procedure name in RDB$PROCEDURES */
|
|
|
|
procedure = NULL;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_l_procedure, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_NAME EQ name
|
|
|
|
if (!REQUEST(irq_l_procedure))
|
|
REQUEST(irq_l_procedure) = request;
|
|
|
|
procedure = MET_procedure(tdbb, P.RDB$PROCEDURE_ID, noscan, 0);
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_procedure))
|
|
REQUEST(irq_l_procedure) = request;
|
|
|
|
return procedure;
|
|
}
|
|
|
|
|
|
JRD_PRC MET_lookup_procedure_id(TDBB tdbb, SSHORT id,
|
|
BOOLEAN return_deleted, BOOLEAN noscan, USHORT flags)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ p r o c e d u r e _ i d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup procedure by id.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
JRD_PRC procedure;
|
|
VEC procedures;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
if ((procedures = dbb->dbb_procedures)
|
|
&& id < procedures->count()
|
|
&& (procedure = (JRD_PRC)(*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))
|
|
{
|
|
return procedure;
|
|
}
|
|
|
|
/* We need to look up the procedure name in RDB$PROCEDURES */
|
|
|
|
procedure = NULL;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_l_proc_id, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ id
|
|
|
|
if (!REQUEST(irq_l_proc_id))
|
|
REQUEST(irq_l_proc_id) = request;
|
|
|
|
procedure = MET_procedure(tdbb, P.RDB$PROCEDURE_ID, noscan, flags);
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_proc_id))
|
|
REQUEST(irq_l_proc_id) = request;
|
|
|
|
return procedure;
|
|
}
|
|
|
|
|
|
JRD_REL MET_lookup_relation(TDBB tdbb, const char* name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup relation by name. Name passed in is
|
|
* ASCIZ name.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
VEC relations;
|
|
JRD_REL relation;
|
|
JRD_REL check_relation;
|
|
vec::iterator ptr, end;
|
|
SCHAR* p;
|
|
const SCHAR* q;
|
|
UCHAR length;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
/* See if we already know the relation by name */
|
|
|
|
relations = dbb->dbb_relations;
|
|
check_relation = NULL;
|
|
length = strlen(name);
|
|
|
|
for (ptr = relations->begin(), end = relations->end(); ptr < end; ptr++)
|
|
{
|
|
if ((relation = JRD_REL(*ptr)) &&
|
|
(relation->rel_length == length) &&
|
|
(!(relation->rel_flags & REL_deleted)) &&
|
|
(p = relation->rel_name))
|
|
{
|
|
for (q = name; *p == *q; p++, q++)
|
|
{
|
|
if (*p == 0)
|
|
{
|
|
if (relation->rel_flags & REL_check_existence)
|
|
{
|
|
check_relation = relation;
|
|
LCK_lock(tdbb, check_relation->rel_existence_lock,
|
|
LCK_SR, TRUE);
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
return relation;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (check_relation)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* We need to look up the relation name in RDB$RELATIONS */
|
|
|
|
relation = NULL;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_l_relation, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATIONS WITH X.RDB$RELATION_NAME EQ name
|
|
|
|
if (!REQUEST(irq_l_relation))
|
|
{
|
|
REQUEST(irq_l_relation) = request;
|
|
}
|
|
|
|
relation = MET_relation(tdbb, X.RDB$RELATION_ID);
|
|
if (!relation->rel_name)
|
|
{
|
|
relation->rel_name = MET_save_name(tdbb, name);
|
|
relation->rel_length = strlen(relation->rel_name);
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_relation))
|
|
{
|
|
REQUEST(irq_l_relation) = request;
|
|
}
|
|
|
|
if (check_relation) {
|
|
check_relation->rel_flags &= ~REL_check_existence;
|
|
if (check_relation != relation) {
|
|
LCK_release(tdbb, check_relation->rel_existence_lock);
|
|
check_relation->rel_flags |= REL_deleted;
|
|
}
|
|
}
|
|
|
|
return relation;
|
|
}
|
|
|
|
|
|
JRD_REL MET_lookup_relation_id(TDBB tdbb, SLONG id, BOOLEAN return_deleted)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ l o o k u p _ r e l a t i o n _ i d
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup relation by id. Make sure it really exists.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
JRD_REL relation, check_relation;
|
|
VEC vector;
|
|
BLK request;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
/* System relations are above suspicion */
|
|
|
|
if (id < (int) rel_MAX)
|
|
{
|
|
assert((USHORT)id < MAX_USHORT);
|
|
return MET_relation(tdbb, (USHORT) id);
|
|
}
|
|
|
|
check_relation = NULL;
|
|
|
|
if ((vector = dbb->dbb_relations) &&
|
|
(id < vector->count()) && (relation = (JRD_REL) (*vector)[id]))
|
|
{
|
|
if (relation->rel_flags & REL_deleted)
|
|
{
|
|
if (return_deleted)
|
|
return relation;
|
|
return NULL;
|
|
}
|
|
else if (relation->rel_flags & REL_check_existence)
|
|
{
|
|
check_relation = relation;
|
|
LCK_lock(tdbb, check_relation->rel_existence_lock, LCK_SR, TRUE);
|
|
}
|
|
else
|
|
{
|
|
return relation;
|
|
}
|
|
}
|
|
|
|
/* We need to look up the relation id in RDB$RELATIONS */
|
|
|
|
relation = NULL;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_l_rel_id, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$RELATIONS WITH X.RDB$RELATION_ID EQ id
|
|
|
|
if (!REQUEST(irq_l_rel_id)) REQUEST(irq_l_rel_id) = request;
|
|
relation = MET_relation(tdbb, X.RDB$RELATION_ID);
|
|
if (!relation->rel_name) {
|
|
relation->rel_name = MET_save_name(tdbb, X.RDB$RELATION_NAME);
|
|
relation->rel_length = strlen(relation->rel_name);
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_l_rel_id))
|
|
REQUEST(irq_l_rel_id) = request;
|
|
|
|
if (check_relation) {
|
|
check_relation->rel_flags &= ~REL_check_existence;
|
|
if (check_relation != relation) {
|
|
LCK_release(tdbb, check_relation->rel_existence_lock);
|
|
check_relation->rel_flags |= REL_deleted;
|
|
}
|
|
}
|
|
|
|
return relation;
|
|
}
|
|
|
|
|
|
JRD_NOD MET_parse_blob(TDBB tdbb,
|
|
JRD_REL relation,
|
|
SLONG blob_id[2],
|
|
CSB* csb_ptr,
|
|
JRD_REQ* request_ptr,
|
|
BOOLEAN trigger,
|
|
BOOLEAN ignore_perm)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLB blob;
|
|
JRD_NOD node;
|
|
STR temp;
|
|
SLONG length;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
blob = BLB_open(tdbb, dbb->dbb_sys_trans, (BID)blob_id);
|
|
length = blob->blb_length + 10;
|
|
temp = FB_NEW_RPT(*tdbb->tdbb_default, length) str();
|
|
BLB_get_data(tdbb, blob, temp->str_data, length);
|
|
|
|
node = PAR_blr( tdbb,
|
|
relation,
|
|
temp->str_data,
|
|
(CSB)NULL_PTR,
|
|
csb_ptr,
|
|
request_ptr,
|
|
trigger,
|
|
(USHORT)(ignore_perm ? csb_ignore_perm : 0));
|
|
|
|
delete temp;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
void MET_parse_sys_trigger(TDBB tdbb, JRD_REL relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ p a r s e _ s y s _ t r i g g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse the blr for a system relation's triggers.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK* trigger; /* should really be "TRG**" */
|
|
const UCHAR* blr;
|
|
UCHAR type;
|
|
const TEXT* name;
|
|
TRIG_VEC* ptr;
|
|
JRD_REQ request;
|
|
JrdMemoryPool *old_pool;
|
|
USHORT trig_flags;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
relation->rel_flags &= ~REL_sys_triggers;
|
|
|
|
/* release any triggers in case of a rescan */
|
|
|
|
if (relation->rel_pre_store)
|
|
MET_release_triggers(tdbb, &relation->rel_pre_store);
|
|
if (relation->rel_post_store)
|
|
MET_release_triggers(tdbb, &relation->rel_post_store);
|
|
if (relation->rel_pre_erase)
|
|
MET_release_triggers(tdbb, &relation->rel_pre_erase);
|
|
if (relation->rel_post_erase)
|
|
MET_release_triggers(tdbb, &relation->rel_post_erase);
|
|
if (relation->rel_pre_modify)
|
|
MET_release_triggers(tdbb, &relation->rel_pre_modify);
|
|
if (relation->rel_post_modify)
|
|
MET_release_triggers(tdbb, &relation->rel_post_modify);
|
|
|
|
/* No need to load triggers for ReadOnly databases, since
|
|
INSERT/DELETE/UPDATE statements are not going to be allowed */
|
|
|
|
if (dbb->dbb_flags & DBB_read_only)
|
|
{
|
|
return;
|
|
}
|
|
|
|
relation->rel_flags |= REL_sys_trigs_being_loaded;
|
|
|
|
trigger = NULL;
|
|
/* TMN: The cast "(struct jrd_trg*)" is a bad lie. Fix later
|
|
* and remove this comment when it's done.
|
|
*/
|
|
while ( (trigger =
|
|
(BLK*)INI_lookup_sys_trigger(relation,
|
|
(struct jrd_trg*)trigger,
|
|
&blr,
|
|
&type,
|
|
&name,
|
|
&trig_flags)) )
|
|
{
|
|
switch (type) {
|
|
case 1:
|
|
ptr = &relation->rel_pre_store;
|
|
break;
|
|
case 2:
|
|
ptr = &relation->rel_post_store;
|
|
break;
|
|
case 3:
|
|
ptr = &relation->rel_pre_modify;
|
|
break;
|
|
case 4:
|
|
ptr = &relation->rel_post_modify;
|
|
break;
|
|
case 5:
|
|
ptr = &relation->rel_pre_erase;
|
|
break;
|
|
case 6:
|
|
ptr = &relation->rel_post_erase;
|
|
break;
|
|
default:
|
|
ptr = NULL;
|
|
break;
|
|
}
|
|
|
|
if (ptr)
|
|
{
|
|
const USHORT par_flags = (USHORT)
|
|
((trig_flags & TRG_ignore_perm) ? csb_ignore_perm : 0);
|
|
|
|
old_pool = tdbb->tdbb_default;
|
|
tdbb->tdbb_default = JrdMemoryPool::createPool();
|
|
PAR_blr(tdbb,
|
|
relation,
|
|
const_cast<UCHAR*>(blr),
|
|
(CSB) NULL_PTR,
|
|
(CSB*) NULL_PTR,
|
|
&request,
|
|
TRUE,
|
|
par_flags);
|
|
|
|
tdbb->tdbb_default = old_pool;
|
|
|
|
request->req_trg_name = const_cast<TEXT*>(name);
|
|
|
|
request->req_flags |= req_sys_trigger;
|
|
if (trig_flags & TRG_ignore_perm)
|
|
{
|
|
request->req_flags |= req_ignore_perm;
|
|
}
|
|
|
|
save_trigger_data(tdbb, ptr, relation, request, NULL, NULL, TRUE, 0);
|
|
}
|
|
}
|
|
|
|
relation->rel_flags &= ~REL_sys_trigs_being_loaded;
|
|
}
|
|
|
|
|
|
int MET_post_existence( TDBB tdbb, JRD_REL relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ p o s t _ e x i s t e n c e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Post an interest in the existence of a relation.
|
|
*
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
if (++relation->rel_use_count == 1 &&
|
|
!MET_lookup_relation_id(tdbb, relation->rel_id, FALSE))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void MET_prepare( TDBB tdbb, JRD_TRA transaction, USHORT length, UCHAR * msg)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ p r e p a r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Post a transaction description to RDB$TRANSACTIONS.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
BLB blob;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_s_trans, IRQ_REQUESTS);
|
|
|
|
STORE(REQUEST_HANDLE request) X IN RDB$TRANSACTIONS
|
|
X.RDB$TRANSACTION_ID = transaction->tra_number;
|
|
X.RDB$TRANSACTION_STATE =
|
|
RDB$TRANSACTIONS.RDB$TRANSACTION_STATE.LIMBO;
|
|
blob =
|
|
BLB_create(tdbb, dbb->dbb_sys_trans,
|
|
(BID)&X.RDB$TRANSACTION_DESCRIPTION);
|
|
BLB_put_segment(tdbb, blob, msg, length);
|
|
BLB_close(tdbb, blob);
|
|
END_STORE;
|
|
|
|
if (!REQUEST(irq_s_trans))
|
|
REQUEST(irq_s_trans) = request;
|
|
}
|
|
|
|
|
|
JRD_PRC MET_procedure(TDBB tdbb, int id, BOOLEAN noscan, USHORT flags)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Find or create a procedure block for a given procedure id.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
JRD_PRC procedure;
|
|
VEC vector;
|
|
LCK lock;
|
|
BLK request, request2;
|
|
vec::iterator ptr, end;
|
|
CSB csb_;
|
|
PRM parameter;
|
|
JrdMemoryPool *old_pool;
|
|
JRD_NOD node;
|
|
FMT format;
|
|
fmt::fmt_desc_iterator desc;
|
|
SSHORT i;
|
|
USHORT length;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
if (!(vector = dbb->dbb_procedures))
|
|
{
|
|
vector = dbb->dbb_procedures = vec::newVector(*dbb->dbb_permanent, id + 10);
|
|
}
|
|
else if (id >= vector->count())
|
|
{
|
|
vector->resize(id + 10);
|
|
}
|
|
|
|
#ifdef SUPERSERVER
|
|
|
|
if (!(dbb->dbb_flags & DBB_sp_rec_mutex_init))
|
|
{
|
|
THD_rec_mutex_init(&dbb->dbb_sp_rec_mutex);
|
|
dbb->dbb_flags |= DBB_sp_rec_mutex_init;
|
|
}
|
|
THREAD_EXIT;
|
|
|
|
if (THD_rec_mutex_lock(&dbb->dbb_sp_rec_mutex))
|
|
{
|
|
THREAD_ENTER;
|
|
return NULL;
|
|
}
|
|
|
|
THREAD_ENTER;
|
|
|
|
#endif /* SUPERSERVER */
|
|
|
|
procedure = (JRD_PRC) (*vector)[id];
|
|
if (procedure)
|
|
{
|
|
/* Make sure PRC_being_scanned and PRC_scanned
|
|
are not set at the same time
|
|
*/
|
|
assert(!(procedure->prc_flags & PRC_being_scanned) ||
|
|
!(procedure->prc_flags & PRC_scanned));
|
|
|
|
/* To avoid scanning recursive procedure's blr recursively let's
|
|
make use of PRC_being_scanned bit. Because this bit is set
|
|
later in the code, it is not set when we are here first time.
|
|
If (in case of rec. procedure) we get here second time it is
|
|
already set and we return half baked procedure.
|
|
In case of superserver this code is under the rec. mutex
|
|
protection, thus the only guy (thread) who can get here and see
|
|
PRC_being_scanned bit set is the guy which started procedure scan
|
|
and currently holds the mutex.
|
|
In case of classic, there is always only one guy and if it
|
|
sees PRC_being_scanned bit set it is safe to assume it is here
|
|
second time.
|
|
|
|
If procedure has already been scanned - return. This condition
|
|
is for those threads that did not find procedure in cach and
|
|
came here to get it from disk. But only one was able to lock
|
|
the mutex and do the scanning, others were waiting. As soon as
|
|
the first thread releases the mutex another thread gets in and
|
|
it would be just unfair to make it do the work again
|
|
*/
|
|
if ((procedure->prc_flags & PRC_being_scanned) ||
|
|
(procedure->prc_flags & PRC_scanned))
|
|
{
|
|
|
|
#ifdef SUPERSERVER
|
|
THD_rec_mutex_unlock(&dbb->dbb_sp_rec_mutex);
|
|
#endif
|
|
return procedure;
|
|
}
|
|
}
|
|
|
|
if (!procedure)
|
|
{
|
|
procedure = FB_NEW_RPT(*dbb->dbb_permanent, 0) jrd_prc;
|
|
}
|
|
|
|
try {
|
|
|
|
procedure->prc_flags |= (PRC_being_scanned | flags);
|
|
procedure->prc_id = id;
|
|
(*vector)[id] = (BLK) procedure;
|
|
|
|
if (!procedure->prc_existence_lock) {
|
|
procedure->prc_existence_lock = lock = FB_NEW_RPT(*dbb->dbb_permanent, 0) lck;
|
|
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 = (BLK) procedure;
|
|
/* TMN: Beware - this is a hairy AND UGLY cast */
|
|
lock->lck_ast = reinterpret_cast<lck_ast_t>(blocking_ast_procedure);
|
|
|
|
LCK_lock(tdbb, procedure->prc_existence_lock, LCK_SR, TRUE);
|
|
}
|
|
|
|
if (!noscan) {
|
|
request = (BLK) CMP_find_request(tdbb, irq_r_procedure, IRQ_REQUESTS);
|
|
request2 = (BLK) CMP_find_request(tdbb, irq_r_params, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
P IN RDB$PROCEDURES WITH P.RDB$PROCEDURE_ID EQ procedure->prc_id
|
|
|
|
if (!REQUEST(irq_r_procedure))
|
|
{
|
|
REQUEST(irq_r_procedure) = request;
|
|
}
|
|
|
|
if (!procedure->prc_name)
|
|
{
|
|
procedure->prc_name = save_name(tdbb, P.RDB$PROCEDURE_NAME);
|
|
}
|
|
procedure->prc_id = P.RDB$PROCEDURE_ID;
|
|
procedure->prc_use_count = 0;
|
|
procedure->prc_int_use_count = 0;
|
|
if (!P.RDB$SECURITY_CLASS.NULL)
|
|
{
|
|
procedure->prc_security_name =
|
|
save_name(tdbb, P.RDB$SECURITY_CLASS);
|
|
}
|
|
if (P.RDB$SYSTEM_FLAG.NULL || !P.RDB$SYSTEM_FLAG)
|
|
{
|
|
procedure->prc_flags &= ~PRC_system;
|
|
}
|
|
else
|
|
{
|
|
procedure->prc_flags |= PRC_system;
|
|
}
|
|
if ( (procedure->prc_inputs = P.RDB$PROCEDURE_INPUTS) )
|
|
{
|
|
if (!procedure->prc_input_fields) {
|
|
procedure->prc_input_fields =
|
|
vec::newVector(*dbb->dbb_permanent,
|
|
P.RDB$PROCEDURE_INPUTS + 1);
|
|
} else {
|
|
procedure->prc_input_fields->resize(P.RDB$PROCEDURE_INPUTS + 1);
|
|
}
|
|
}
|
|
if ( (procedure->prc_outputs = P.RDB$PROCEDURE_OUTPUTS) )
|
|
{
|
|
if (!procedure->prc_output_fields) {
|
|
procedure->prc_output_fields =
|
|
vec::newVector(*dbb->dbb_permanent,
|
|
P.RDB$PROCEDURE_OUTPUTS + 1);
|
|
} else {
|
|
procedure->prc_output_fields->resize(P.RDB$PROCEDURE_OUTPUTS + 1);
|
|
}
|
|
}
|
|
|
|
FOR(REQUEST_HANDLE request2) PA IN RDB$PROCEDURE_PARAMETERS CROSS
|
|
F IN RDB$FIELDS WITH F.RDB$FIELD_NAME = PA.RDB$FIELD_SOURCE
|
|
AND PA.RDB$PROCEDURE_NAME = P.RDB$PROCEDURE_NAME
|
|
|
|
if (!REQUEST(irq_r_params))
|
|
{
|
|
REQUEST(irq_r_params) = request2;
|
|
}
|
|
|
|
if (PA.RDB$PARAMETER_TYPE) {
|
|
vector = procedure->prc_output_fields;
|
|
} else {
|
|
vector = procedure->prc_input_fields;
|
|
}
|
|
|
|
/* should be error if field already exists */
|
|
parameter =
|
|
FB_NEW_RPT(*dbb->dbb_permanent, name_length(PA.RDB$PARAMETER_NAME)) prm();
|
|
parameter->prm_number = PA.RDB$PARAMETER_NUMBER;
|
|
(*vector)[parameter->prm_number] = (BLK) parameter;
|
|
name_copy(parameter->prm_string, PA.RDB$PARAMETER_NAME);
|
|
parameter->prm_name = parameter->prm_string;
|
|
DSC_make_descriptor(¶meter->prm_desc, F.RDB$FIELD_TYPE,
|
|
F.RDB$FIELD_SCALE, F.RDB$FIELD_LENGTH,
|
|
F.RDB$FIELD_SUB_TYPE, F.RDB$CHARACTER_SET_ID,
|
|
F.RDB$COLLATION_ID);
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_params)) {
|
|
REQUEST(irq_r_params) = request2;
|
|
}
|
|
|
|
if ((vector = procedure->prc_output_fields) && (*vector)[0])
|
|
{
|
|
format = procedure->prc_format =
|
|
fmt::newFmt(*dbb->dbb_permanent, procedure->prc_outputs);
|
|
format->fmt_count = procedure->prc_outputs;
|
|
length = FLAG_BYTES(format->fmt_count);
|
|
desc = format->fmt_desc.begin();
|
|
for (ptr = vector->begin(), end = vector->end();
|
|
ptr < end; ptr++, desc++)
|
|
{
|
|
parameter = (PRM) * ptr;
|
|
/* check for parameter to be null, this can only happen if the
|
|
* parameter numbers get out of sync. This was added to fix bug
|
|
* 10534. -Shaunak Mistry 12-May-99
|
|
*/
|
|
if (parameter)
|
|
{
|
|
*desc = parameter->prm_desc;
|
|
length = MET_align(&*desc, length);
|
|
desc->dsc_address = (UCHAR *) length;
|
|
length += desc->dsc_length;
|
|
}
|
|
}
|
|
format->fmt_length = length;
|
|
}
|
|
|
|
old_pool = tdbb->tdbb_default;
|
|
tdbb->tdbb_default = JrdMemoryPool::createPool();
|
|
csb_ = Csb::newCsb(*tdbb->tdbb_default, 5);
|
|
csb_->csb_rpt.resize(5); // vec always allocates one too many
|
|
csb_->csb_count = 5;
|
|
parse_procedure_blr(tdbb, procedure, (SLONG*)&P.RDB$PROCEDURE_BLR, &csb_);
|
|
procedure->prc_request->req_procedure = procedure;
|
|
for (i = 0; i < csb_->csb_count; i++)
|
|
{
|
|
if ( (node = csb_->csb_rpt[i].csb_message) )
|
|
{
|
|
if ((int) node->nod_arg[e_msg_number] == 0)
|
|
{
|
|
procedure->prc_input_msg = node;
|
|
} else if ((int) node->nod_arg[e_msg_number] == 1)
|
|
{
|
|
procedure->prc_output_msg = node;
|
|
}
|
|
}
|
|
}
|
|
delete csb_;
|
|
tdbb->tdbb_default = old_pool;
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_r_procedure)) {
|
|
REQUEST(irq_r_procedure) = request;
|
|
}
|
|
|
|
procedure->prc_flags |= PRC_scanned;
|
|
|
|
} /* if !noscan */
|
|
|
|
/* Make sure that it is really being Scanned ! */
|
|
assert(procedure->prc_flags & PRC_being_scanned);
|
|
|
|
procedure->prc_flags &= ~PRC_being_scanned;
|
|
|
|
#ifdef SUPERSERVER
|
|
THD_rec_mutex_unlock(&dbb->dbb_sp_rec_mutex);
|
|
#endif
|
|
|
|
} // try
|
|
catch (const std::exception&) {
|
|
procedure->prc_flags &= ~(PRC_being_scanned | PRC_scanned);
|
|
#ifdef SUPERSERVER
|
|
THD_rec_mutex_unlock(&dbb->dbb_sp_rec_mutex);
|
|
#endif
|
|
if (procedure->prc_existence_lock)
|
|
{
|
|
LCK_release(tdbb, procedure->prc_existence_lock);
|
|
procedure->prc_existence_lock = NULL;
|
|
}
|
|
ERR_punt();
|
|
}
|
|
|
|
return procedure;
|
|
}
|
|
|
|
|
|
JRD_REL MET_relation(TDBB tdbb, USHORT id)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Find or create a relation block for a given relation id.
|
|
*
|
|
**************************************/
|
|
|
|
JRD_REL relation;
|
|
LCK lock;
|
|
USHORT major_version, minor_original, max_sys_rel;
|
|
|
|
SET_TDBB(tdbb);
|
|
DBB dbb = tdbb->tdbb_database;
|
|
CHECK_DBB(dbb);
|
|
|
|
VEC vector = dbb->dbb_relations;
|
|
|
|
if (!vector)
|
|
{
|
|
vector = dbb->dbb_relations = vec::newVector(*dbb->dbb_permanent, id + 10);
|
|
}
|
|
else if (id >= vector->count())
|
|
{
|
|
vector->resize(id + 10);
|
|
}
|
|
|
|
if ( (relation = (JRD_REL) (*vector)[id]) )
|
|
return relation;
|
|
|
|
major_version = (SSHORT) dbb->dbb_ods_version;
|
|
minor_original = (SSHORT) dbb->dbb_minor_original;
|
|
|
|
/* From ODS 9 onwards, the first 128 relation IDS have been
|
|
reserved for system relations */
|
|
if (ENCODE_ODS(major_version, minor_original) < ODS_9_0)
|
|
max_sys_rel = (USHORT) USER_REL_INIT_ID_ODS8 - 1;
|
|
else
|
|
max_sys_rel = (USHORT) USER_DEF_REL_INIT_ID - 1;
|
|
|
|
relation = FB_NEW(*dbb->dbb_permanent) jrd_rel();
|
|
(*vector)[id] = (BLK) relation;
|
|
relation->rel_id = id;
|
|
|
|
if (relation->rel_id <= max_sys_rel)
|
|
return relation;
|
|
|
|
relation->rel_existence_lock = lock = FB_NEW_RPT(*dbb->dbb_permanent, 0) lck;
|
|
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 = (BLK) relation;
|
|
/* TMN: Beware - this is a hairy AND UGLY cast */
|
|
lock->lck_ast = reinterpret_cast<lck_ast_t>(blocking_ast_relation);
|
|
|
|
relation->rel_flags |= (REL_check_existence | REL_check_partners);
|
|
return relation;
|
|
}
|
|
|
|
|
|
BOOLEAN MET_relation_owns_trigger (TDBB tdbb, const TEXT *relation_name,
|
|
const TEXT *trigger_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r e l a t i o n _ o w n s _ t r i g g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Checks that a given trigger is defined for a
|
|
* given relation, returning TRUE if there's a match.
|
|
* It's almost a subset of MET_load_trigger().
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK trigger_request;
|
|
BOOLEAN found = FALSE;
|
|
|
|
SET_TDBB (tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
CHECK_DBB (dbb);
|
|
|
|
/* No need to load triggers for ReadOnly databases,
|
|
since INSERT/DELETE/UPDATE statements are not going to be allowed;
|
|
but we do not care of this flag here. We do not load, we only check. */
|
|
/*if (dbb->dbb_flags & DBB_read_only)
|
|
return;*/
|
|
|
|
/* Scan RDB$TRIGGERS next */
|
|
|
|
/* CVC: Notice that we'll use the request irq_s_triggers2 that was found
|
|
to be unused at this time. */
|
|
|
|
trigger_request = (BLK) CMP_find_request (tdbb, irq_s_triggers2, IRQ_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE trigger_request)
|
|
TRG IN RDB$TRIGGERS WITH TRG.RDB$RELATION_NAME = relation_name AND
|
|
TRG.RDB$TRIGGER_NAME EQ trigger_name
|
|
|
|
if (!REQUEST (irq_s_triggers2))
|
|
REQUEST (irq_s_triggers2) = trigger_request;
|
|
|
|
found = TRUE;
|
|
|
|
/* Notice we do not care whether the trigger is valid or not. We assume
|
|
the caller wants to verify already validated entities.
|
|
if (TRG.RDB$TRIGGER_TYPE > 0 && TRG.RDB$TRIGGER_TYPE < TRIGGER_MAX)
|
|
*/
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST (irq_s_triggers2))
|
|
REQUEST (irq_s_triggers2) = trigger_request;
|
|
|
|
return found;
|
|
}
|
|
|
|
BOOLEAN MET_relation_default_class (TDBB tdbb, const TEXT *relation_name,
|
|
const TEXT *default_security_class_name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r e l a t i o n _ d e f a u l t _ c l a s s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Checks that a given security class is the default for
|
|
* a given relation, returning TRUE if there's a match.
|
|
* It can be made obsolete in the future if JRD_REL struct
|
|
* gets another field, although metadata loading order
|
|
* would not be safe when compared with this function.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
BOOLEAN found = FALSE;
|
|
|
|
SET_TDBB (tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
CHECK_DBB (dbb);
|
|
|
|
request = (BLK) CMP_find_request (tdbb, irq_l_relation_defsec, IRQ_REQUESTS);
|
|
|
|
FOR (REQUEST_HANDLE request)
|
|
REL IN RDB$RELATIONS WITH REL.RDB$RELATION_NAME EQ relation_name
|
|
|
|
if (!REQUEST (irq_l_relation_defsec))
|
|
REQUEST (irq_l_relation_defsec) = request;
|
|
|
|
if (!REL.RDB$DEFAULT_CLASS.NULL) {
|
|
USHORT nl = name_length (REL.RDB$DEFAULT_CLASS),
|
|
nl2 = name_length (default_security_class_name);
|
|
if (nl == nl2 && !strncmp (REL.RDB$DEFAULT_CLASS, default_security_class_name, nl)) {
|
|
found = TRUE;
|
|
}
|
|
}
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST (irq_l_relation_defsec))
|
|
REQUEST (irq_l_relation_defsec) = request;
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
void MET_release_existence( JRD_REL relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r e l e a s e _ e x i s t e n c e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Release interest in relation. If no remaining interest
|
|
* and we're blocking the drop of the relation then release
|
|
* existence lock and mark deleted.
|
|
*
|
|
**************************************/
|
|
if (relation->rel_use_count) relation->rel_use_count--;
|
|
|
|
if (!relation->rel_use_count)
|
|
if (relation->rel_flags & REL_blocking)
|
|
LCK_re_post(relation->rel_existence_lock);
|
|
}
|
|
|
|
|
|
void MET_remove_procedure( TDBB tdbb, int id, JRD_PRC procedure)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r e m o v e _ p r o c e d u r e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Remove a procedure from cache
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
VEC vector;
|
|
SSHORT i;
|
|
USHORT save_proc_flags;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
if (!(vector = dbb->dbb_procedures))
|
|
return;
|
|
|
|
if (!procedure) {
|
|
/** If we are in here then dfw.e/modify_procedure() called us **/
|
|
if (!(procedure = (JRD_PRC) (*vector)[id]))
|
|
return;
|
|
}
|
|
|
|
/* MET_procedure locks it. Lets unlock it now to avoid troubles later */
|
|
if (procedure->prc_existence_lock)
|
|
{
|
|
LCK_release(tdbb, procedure->prc_existence_lock);
|
|
}
|
|
|
|
/* Procedure that is being altered may have references
|
|
to it by other procedures via pointer to current meta
|
|
data structure, so don't loose the structure or the pointer. */
|
|
if ((procedure == (JRD_PRC) (*vector)[id]) && !(procedure->prc_flags & PRC_being_altered))
|
|
(*vector)[id] = (BLK) NULL_PTR;
|
|
|
|
/* deallocate all structure which were allocated. The procedure
|
|
* blr is originally read into a new pool from which all request
|
|
* are allocated. That will not be freed up.
|
|
*/
|
|
|
|
if (procedure->prc_existence_lock)
|
|
delete procedure->prc_existence_lock;
|
|
|
|
if (procedure->prc_name)
|
|
delete procedure->prc_name;
|
|
if (procedure->prc_security_name)
|
|
delete procedure->prc_security_name;
|
|
|
|
/* deallocate input param structures */
|
|
|
|
if ((procedure->prc_inputs) && (vector = procedure->prc_input_fields)) {
|
|
for (i = 0; i < procedure->prc_inputs; i++)
|
|
{
|
|
if ((*vector)[i])
|
|
{
|
|
delete (*vector)[i];
|
|
}
|
|
}
|
|
delete vector;
|
|
}
|
|
|
|
/* deallocate output param structures */
|
|
|
|
if ((procedure->prc_outputs) && (vector = procedure->prc_output_fields)) {
|
|
for (i = 0; i < procedure->prc_outputs; i++)
|
|
{
|
|
if ((*vector)[i])
|
|
{
|
|
delete (*vector)[i];
|
|
}
|
|
}
|
|
delete vector;
|
|
}
|
|
|
|
if (procedure->prc_format)
|
|
delete procedure->prc_format;
|
|
|
|
if (!(procedure->prc_flags & PRC_being_altered))
|
|
{
|
|
delete procedure;
|
|
}
|
|
else
|
|
{
|
|
save_proc_flags = procedure->prc_flags;
|
|
memset(((SCHAR *)&procedure->prc_id), 0,
|
|
sizeof(struct jrd_prc) - ((SCHAR *)&procedure->prc_id-(SCHAR *)procedure));
|
|
procedure->prc_flags = save_proc_flags;
|
|
}
|
|
}
|
|
|
|
|
|
void MET_revoke(
|
|
TDBB tdbb,
|
|
JRD_TRA transaction,
|
|
TEXT * relation, TEXT * revokee, TEXT * privilege)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ r e v o k e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Execute a recursive revoke. This is called only when
|
|
* a revoked privilege had the grant option.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
USHORT count;
|
|
BLK request;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
/* See if the revokee still has the privilege. If so, there's
|
|
nothing to do */
|
|
|
|
count = 0;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_revoke1, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
FIRST 1 P IN RDB$USER_PRIVILEGES WITH
|
|
P.RDB$RELATION_NAME EQ relation AND
|
|
P.RDB$PRIVILEGE EQ privilege AND
|
|
P.RDB$USER EQ revokee
|
|
|
|
if (!REQUEST(irq_revoke1))
|
|
REQUEST(irq_revoke1) = request;
|
|
++count;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_revoke1))
|
|
REQUEST(irq_revoke1) = request;
|
|
|
|
if (count)
|
|
return;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_revoke2, IRQ_REQUESTS);
|
|
|
|
/* User lost privilege. Take it away from anybody he/she gave
|
|
it to. */
|
|
|
|
FOR(REQUEST_HANDLE request TRANSACTION_HANDLE transaction)
|
|
P IN RDB$USER_PRIVILEGES WITH
|
|
P.RDB$RELATION_NAME EQ relation AND
|
|
P.RDB$PRIVILEGE EQ privilege AND
|
|
P.RDB$GRANTOR EQ revokee
|
|
|
|
if (!REQUEST(irq_revoke2))
|
|
REQUEST(irq_revoke2) = request;
|
|
ERASE P;
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_revoke2))
|
|
REQUEST(irq_revoke2) = request;
|
|
}
|
|
|
|
|
|
TEXT* MET_save_name(TDBB tdbb, const TEXT* name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ s a v e _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Immortalize a field or relation name in a permant string block.
|
|
* Oh, wow.
|
|
*
|
|
**************************************/
|
|
STR string;
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
string = save_name(tdbb, name);
|
|
return (TEXT*) string->str_data;
|
|
}
|
|
|
|
|
|
void MET_scan_relation( TDBB tdbb, JRD_REL relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ s c a n _ r e l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Scan a relation for view rse, computed by expressions, missing
|
|
* expressions, and validation expressions.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
USHORT n, length, view_context, field_id;
|
|
ARR array;
|
|
TRIG_VEC triggers[TRIGGER_MAX], tmp_vector;
|
|
VEC vector;
|
|
JRD_FLD field;
|
|
BLK request;
|
|
CSB csb_;
|
|
BLB blob;
|
|
UCHAR *p, *q, *buffer, temp[256];
|
|
STR name, string;
|
|
JrdMemoryPool* old_pool;
|
|
volatile BOOLEAN dependencies, sys_triggers;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
#ifdef SUPERSERVER
|
|
if (!(dbb->dbb_flags & DBB_sp_rec_mutex_init)) {
|
|
THD_rec_mutex_init(&dbb->dbb_sp_rec_mutex);
|
|
dbb->dbb_flags |= DBB_sp_rec_mutex_init;
|
|
}
|
|
THREAD_EXIT;
|
|
if (THD_rec_mutex_lock(&dbb->dbb_sp_rec_mutex)) {
|
|
THREAD_ENTER;
|
|
return;
|
|
}
|
|
THREAD_ENTER;
|
|
#endif /* SUPERSERVER */
|
|
|
|
if (relation->rel_flags & REL_scanned
|
|
|| relation->rel_flags & REL_deleted) {
|
|
#ifdef SUPERSERVER
|
|
THD_rec_mutex_unlock(&dbb->dbb_sp_rec_mutex);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
relation->rel_flags |= REL_being_scanned;
|
|
dependencies =
|
|
(relation->rel_flags & REL_get_dependencies) ? TRUE : FALSE;
|
|
sys_triggers = (relation->rel_flags & REL_sys_triggers) ? TRUE : FALSE;
|
|
relation->rel_flags &= ~(REL_get_dependencies | REL_sys_triggers);
|
|
for (n = 0; n < TRIGGER_MAX; n++)
|
|
triggers[n] = (TRIG_VEC) NULL_PTR;
|
|
old_pool = tdbb->tdbb_default;
|
|
tdbb->tdbb_default = dbb->dbb_permanent;
|
|
|
|
/* 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 {
|
|
|
|
/* Since this can be called recursively, find an inactive clone of the request */
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_r_fields, IRQ_REQUESTS);
|
|
csb_ = NULL;
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
REL IN RDB$RELATIONS WITH REL.RDB$RELATION_ID EQ relation->rel_id
|
|
/* Pick up relation level stuff */
|
|
if (!REQUEST(irq_r_fields)) {
|
|
REQUEST(irq_r_fields) = request;
|
|
}
|
|
relation->rel_current_fmt = REL.RDB$FORMAT;
|
|
vector = relation->rel_fields;
|
|
if (!vector) {
|
|
vector = vec::newVector(*dbb->dbb_permanent, REL.RDB$FIELD_ID + 1);
|
|
relation->rel_fields = vector;
|
|
} else {
|
|
vector->resize(REL.RDB$FIELD_ID + 1);
|
|
}
|
|
if (!REL.RDB$SECURITY_CLASS.NULL) {
|
|
relation->rel_security_name =
|
|
MET_save_name(tdbb, REL.RDB$SECURITY_CLASS);
|
|
}
|
|
if (!relation->rel_name) {
|
|
relation->rel_name = MET_save_name(tdbb, REL.RDB$RELATION_NAME);
|
|
relation->rel_length = strlen(relation->rel_name);
|
|
}
|
|
if (!relation->rel_owner_name)
|
|
relation->rel_owner_name =
|
|
MET_save_name(tdbb, REL.RDB$OWNER_NAME);
|
|
if (REL.RDB$FLAGS & REL_sql)
|
|
{
|
|
relation->rel_flags |= REL_sql_relation;
|
|
}
|
|
if (!NULL_BLOB(REL.RDB$VIEW_BLR))
|
|
{
|
|
/* parse the view blr, getting dependencies on relations, etc. at the same time */
|
|
|
|
if (dependencies)
|
|
{
|
|
relation->rel_view_rse =
|
|
(RSE) MET_get_dependencies( tdbb,
|
|
relation,
|
|
(TEXT*)NULL_PTR,
|
|
(CSB) NULL_PTR,
|
|
(SLONG*)&REL.RDB$VIEW_BLR,
|
|
(JRD_REQ*) NULL_PTR,
|
|
&csb_,
|
|
REL.RDB$RELATION_NAME,
|
|
obj_view);
|
|
}
|
|
else
|
|
{
|
|
relation->rel_view_rse =
|
|
(RSE) MET_parse_blob(tdbb,
|
|
relation,
|
|
(SLONG*)&REL.RDB$VIEW_BLR,
|
|
&csb_,
|
|
(JRD_REQ*) NULL_PTR,
|
|
FALSE,
|
|
FALSE);
|
|
}
|
|
|
|
/* retrieve the view context names */
|
|
|
|
lookup_view_contexts(tdbb, relation);
|
|
}
|
|
|
|
relation->rel_flags |= REL_scanned;
|
|
if (REL.RDB$EXTERNAL_FILE[0])
|
|
{
|
|
EXT_file(relation,
|
|
REL.RDB$EXTERNAL_FILE,
|
|
(SLONG*)&REL.RDB$EXTERNAL_DESCRIPTION);
|
|
}
|
|
|
|
/* Pick up field specific stuff */
|
|
|
|
blob = BLB_open(tdbb, dbb->dbb_sys_trans, (BID)&REL.RDB$RUNTIME);
|
|
if (blob->blb_max_segment < sizeof(temp))
|
|
{
|
|
buffer = temp;
|
|
}
|
|
else
|
|
{
|
|
string = FB_NEW_RPT(*tdbb->tdbb_default, blob->blb_max_segment) str();
|
|
buffer = string->str_data;
|
|
}
|
|
|
|
field = NULL;
|
|
for (;;)
|
|
{
|
|
length =
|
|
BLB_get_segment(tdbb, blob, buffer, blob->blb_max_segment);
|
|
if (blob->blb_flags & BLB_eof)
|
|
{
|
|
break;
|
|
}
|
|
buffer[length] = 0;
|
|
p = (UCHAR *) & n;
|
|
q = buffer + 1;
|
|
while (q < buffer + 1 + sizeof(SSHORT))
|
|
{
|
|
*p++ = *q++;
|
|
}
|
|
p = buffer + 1;
|
|
--length;
|
|
switch ((RSR_T) buffer[0]) {
|
|
case RSR_field_id:
|
|
if (field && !field->fld_security_name
|
|
&& !REL.RDB$DEFAULT_CLASS.NULL) field->fld_security_name =
|
|
MET_save_name(tdbb, REL.RDB$DEFAULT_CLASS);
|
|
field_id = n;
|
|
field = (JRD_FLD) (*vector)[field_id];
|
|
array = NULL;
|
|
break;
|
|
|
|
case RSR_field_name:
|
|
if (field) {
|
|
/* The field exists. If its name hasn't changed, then
|
|
there's no need to copy anything. */
|
|
|
|
if (!strcmp((char*)p, (char*)field->fld_name))
|
|
break;
|
|
|
|
name = FB_NEW_RPT(*dbb->dbb_permanent, length) str();
|
|
field->fld_name = (TEXT *) name->str_data;
|
|
}
|
|
else {
|
|
field = FB_NEW_RPT(*dbb->dbb_permanent, length) jrd_fld();
|
|
(*vector)[field_id] = (BLK) field;
|
|
field->fld_name = (TEXT *) field->fld_string;
|
|
}
|
|
/*
|
|
* TMN: This const_cast equivalent is safe since
|
|
* we just allocated the struct.
|
|
*/
|
|
strcpy((char*)field->fld_name, (char*)p);
|
|
field->fld_length = strlen(field->fld_name);
|
|
|
|
// CVC: Be paranoid and allow the possible trigger(s) to have a
|
|
// not null security class to work on, even if we only take it
|
|
// from the relation itself.
|
|
if (!field->fld_security_name && !REL.RDB$DEFAULT_CLASS.NULL) {
|
|
field->fld_security_name = MET_save_name (tdbb, REL.RDB$DEFAULT_CLASS);
|
|
}
|
|
|
|
break;
|
|
|
|
case RSR_view_context:
|
|
view_context = n;
|
|
break;
|
|
|
|
case RSR_base_field:
|
|
field->fld_source =
|
|
PAR_make_field(tdbb, csb_, view_context, (TEXT*)p);
|
|
break;
|
|
|
|
case RSR_computed_blr:
|
|
field->fld_computation = (dependencies) ?
|
|
MET_get_dependencies(tdbb, relation,
|
|
(TEXT*) p,
|
|
csb_,
|
|
(SLONG*) NULL_PTR,
|
|
(JRD_REQ*) NULL_PTR,
|
|
(CSB*) NULL_PTR,
|
|
field->fld_name,
|
|
obj_computed) :
|
|
PAR_blr(tdbb, relation, p, csb_, (CSB*) NULL_PTR, (JRD_REQ*) NULL_PTR, FALSE,
|
|
0);
|
|
break;
|
|
|
|
case RSR_missing_value:
|
|
field->fld_missing_value =
|
|
PAR_blr(tdbb, relation, p, csb_, (CSB*) NULL_PTR, (JRD_REQ*) NULL_PTR, FALSE,
|
|
0);
|
|
break;
|
|
|
|
case RSR_default_value:
|
|
field->fld_default_value =
|
|
PAR_blr(tdbb, relation, p, csb_, (CSB*) NULL_PTR, (JRD_REQ*) NULL_PTR, FALSE,
|
|
0);
|
|
break;
|
|
|
|
case RSR_validation_blr:
|
|
field->fld_validation =
|
|
PAR_blr(tdbb, relation, p, csb_, (CSB*) NULL_PTR, (JRD_REQ*) NULL_PTR, FALSE,
|
|
csb_validation);
|
|
break;
|
|
|
|
case RSR_field_not_null:
|
|
field->fld_not_null =
|
|
PAR_blr(tdbb, relation, p, csb_, (CSB*) NULL_PTR, (JRD_REQ*) NULL_PTR, FALSE,
|
|
csb_validation);
|
|
break;
|
|
|
|
case RSR_security_class:
|
|
field->fld_security_name = MET_save_name(tdbb, (TEXT*) p);
|
|
break;
|
|
|
|
case RSR_trigger_name:
|
|
MET_load_trigger(tdbb, relation, (TEXT*) p, triggers);
|
|
break;
|
|
|
|
case RSR_dimensions:
|
|
field->fld_array = array = FB_NEW_RPT(*dbb->dbb_permanent, n) arr();
|
|
array->arr_desc.ads_dimensions = n;
|
|
break;
|
|
|
|
case RSR_array_desc:
|
|
if (array)
|
|
MOVE_FAST(p, &array->arr_desc, length);
|
|
break;
|
|
default: /* Shut up compiler warning */
|
|
break;
|
|
}
|
|
}
|
|
if (field && !field->fld_security_name && !REL.RDB$DEFAULT_CLASS.NULL)
|
|
{
|
|
field->fld_security_name =
|
|
MET_save_name(tdbb, REL.RDB$DEFAULT_CLASS);
|
|
}
|
|
if (buffer != temp)
|
|
{
|
|
delete string;
|
|
}
|
|
END_FOR;
|
|
|
|
if (csb_)
|
|
{
|
|
delete csb_;
|
|
}
|
|
|
|
/* release any triggers in case of a rescan, but not if the rescan
|
|
hapenned while system triggers were being loaded. */
|
|
|
|
if (!(relation->rel_flags & REL_sys_trigs_being_loaded)) {
|
|
/* if we are scanning a system relation during loading the system
|
|
triggers, (during parsing its blr actually), we must not release the
|
|
existing system triggers; because we have already set the
|
|
relation->rel_flag to not have REL_sys_trig, so these
|
|
system triggers will not get loaded again. This fixes bug 8149. */
|
|
|
|
/* We have just loaded the triggers onto the local vector triggers.
|
|
Its now time to place them at their rightful place ie the relation
|
|
block.
|
|
*/
|
|
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;
|
|
#ifdef SUPERSERVER
|
|
THD_rec_mutex_unlock(&dbb->dbb_sp_rec_mutex);
|
|
#endif
|
|
relation->rel_current_format = NULL;
|
|
tdbb->tdbb_default = old_pool;
|
|
|
|
} // try
|
|
catch (const std::exception&) {
|
|
relation->rel_flags &= ~(REL_being_scanned | REL_scanned);
|
|
if (dependencies) {
|
|
relation->rel_flags |= REL_get_dependencies;
|
|
}
|
|
if (sys_triggers) {
|
|
relation->rel_flags |= REL_sys_triggers;
|
|
}
|
|
#ifdef SUPERSERVER
|
|
THD_rec_mutex_unlock(&dbb->dbb_sp_rec_mutex);
|
|
#endif
|
|
tdbb->tdbb_default = old_pool;
|
|
ERR_punt();
|
|
}
|
|
}
|
|
|
|
|
|
const TEXT* MET_trigger_msg(TDBB tdbb, const TEXT* name, USHORT number)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ t r i g g e r _ m s g
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Look up trigger message using trigger and abort code.
|
|
*
|
|
**************************************/
|
|
|
|
SET_TDBB(tdbb);
|
|
DBB dbb = tdbb->tdbb_database;
|
|
|
|
const TEXT* msg = 0;
|
|
BLK request = (BLK) CMP_find_request(tdbb, irq_s_msgs, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
MSG IN RDB$TRIGGER_MESSAGES WITH
|
|
MSG.RDB$TRIGGER_NAME EQ name AND
|
|
MSG.RDB$MESSAGE_NUMBER EQ number
|
|
|
|
if (!REQUEST(irq_s_msgs))
|
|
REQUEST(irq_s_msgs) = request;
|
|
msg = ERR_cstring(MSG.RDB$MESSAGE);
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_s_msgs))
|
|
REQUEST(irq_s_msgs) = request;
|
|
|
|
return msg;
|
|
}
|
|
|
|
|
|
void MET_update_shadow( TDBB tdbb, SDW shadow, USHORT file_flags)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ u p d a t e _ s h a d o w
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Update the stored file flags for the specified shadow.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK handle;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
handle = NULL;
|
|
FOR(REQUEST_HANDLE handle)
|
|
FIL IN RDB$FILES WITH FIL.RDB$SHADOW_NUMBER EQ shadow->sdw_number
|
|
MODIFY FIL USING
|
|
FIL.RDB$FILE_FLAGS = file_flags;
|
|
END_MODIFY;
|
|
END_FOR;
|
|
CMP_release(tdbb, (JRD_REQ)handle);
|
|
}
|
|
|
|
|
|
void MET_update_transaction( TDBB tdbb, JRD_TRA transaction, USHORT flag)
|
|
{
|
|
/**************************************
|
|
*
|
|
* M E T _ u p d a t e _ t r a n s a c t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Update a record in RDB$TRANSACTIONS. If flag is TRUE, this is a
|
|
* commit; otherwise it is a ROLLBACK.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_m_trans, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
X IN RDB$TRANSACTIONS
|
|
WITH X.RDB$TRANSACTION_ID EQ transaction->tra_number
|
|
|
|
if (!REQUEST(irq_m_trans))
|
|
REQUEST(irq_m_trans) = request;
|
|
if (flag && (transaction->tra_flags & TRA_prepare2))
|
|
{
|
|
ERASE X
|
|
}
|
|
else
|
|
{
|
|
MODIFY X
|
|
X.RDB$TRANSACTION_STATE = (flag) ?
|
|
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 void blocking_ast_procedure( JRD_PRC procedure)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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.
|
|
*
|
|
**************************************/
|
|
struct tdbb thd_context, *tdbb;
|
|
|
|
/* Since this routine will be called asynchronously, we must establish
|
|
a thread context. */
|
|
|
|
SET_THREAD_DATA;
|
|
|
|
tdbb->tdbb_database = procedure->prc_existence_lock->lck_dbb;
|
|
tdbb->tdbb_attachment = procedure->prc_existence_lock->lck_attachment;
|
|
tdbb->tdbb_quantum = QUANTUM;
|
|
tdbb->tdbb_request = NULL;
|
|
tdbb->tdbb_transaction = NULL;
|
|
tdbb->tdbb_default = NULL;
|
|
|
|
if (procedure->prc_existence_lock)
|
|
LCK_release(tdbb, procedure->prc_existence_lock);
|
|
procedure->prc_flags |= PRC_obsolete;
|
|
|
|
/* Restore the prior thread context */
|
|
|
|
RESTORE_THREAD_DATA;
|
|
}
|
|
|
|
|
|
static void blocking_ast_relation( JRD_REL relation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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.
|
|
*
|
|
**************************************/
|
|
struct tdbb thd_context, *tdbb;
|
|
|
|
/* Since this routine will be called asynchronously, we must establish
|
|
a thread context. */
|
|
|
|
SET_THREAD_DATA;
|
|
|
|
tdbb->tdbb_database = relation->rel_existence_lock->lck_dbb;
|
|
tdbb->tdbb_attachment = relation->rel_existence_lock->lck_attachment;
|
|
tdbb->tdbb_quantum = QUANTUM;
|
|
tdbb->tdbb_request = NULL;
|
|
tdbb->tdbb_transaction = NULL;
|
|
tdbb->tdbb_default = NULL;
|
|
|
|
if (relation->rel_use_count)
|
|
relation->rel_flags |= REL_blocking;
|
|
else {
|
|
relation->rel_flags &= ~REL_blocking;
|
|
relation->rel_flags |= (REL_check_existence | REL_check_partners);
|
|
if (relation->rel_existence_lock)
|
|
LCK_release(tdbb, relation->rel_existence_lock);
|
|
}
|
|
|
|
/* Restore the prior thread context */
|
|
|
|
RESTORE_THREAD_DATA;
|
|
}
|
|
|
|
|
|
static void get_trigger(
|
|
TDBB tdbb,
|
|
JRD_REL relation,
|
|
SLONG blob_id[2],
|
|
TRIG_VEC* ptr,
|
|
TEXT * name, BOOLEAN sys_trigger, USHORT flags)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e t _ t r i g g e r
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Get trigger.
|
|
*
|
|
**************************************/
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
if (!blob_id[0] && !blob_id[1])
|
|
return;
|
|
|
|
DBB dbb = tdbb->tdbb_database;
|
|
BLB blob = BLB_open(tdbb, dbb->dbb_sys_trans, (BID)blob_id);
|
|
SLONG length = blob->blb_length + 10;
|
|
STR blr = FB_NEW_RPT(*dbb->dbb_permanent,length) str();
|
|
BLB_get_data(tdbb, blob, blr->str_data, length);
|
|
|
|
save_trigger_data(tdbb, ptr, relation, NULL, blr, name, sys_trigger, flags);
|
|
}
|
|
|
|
|
|
static BOOLEAN get_type( TDBB tdbb, SSHORT * id, UCHAR * name, UCHAR * field)
|
|
{
|
|
/**************************************
|
|
*
|
|
* g e t _ t y p e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Resoved a symbolic name in RDB$TYPES. Returned the value
|
|
* defined for the name in (*id). Don't touch (*id) if you
|
|
* don't find the name.
|
|
*
|
|
* Return (1) if found, (0) otherwise.
|
|
*
|
|
**************************************/
|
|
UCHAR buffer[32]; /* BASED ON RDB$TYPE_NAME */
|
|
BOOLEAN found;
|
|
UCHAR *p;
|
|
BLK handle;
|
|
DBB dbb;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
assert(id != NULL);
|
|
assert(name != NULL);
|
|
assert(field != NULL);
|
|
|
|
/* Force key to uppercase, following C locale rules for uppercase */
|
|
|
|
for (p = buffer; *name && p < buffer + sizeof(buffer) - 1; p++, name++)
|
|
{
|
|
*p = UPPER7(*name);
|
|
}
|
|
*p = 0;
|
|
|
|
/* Try for exact name match */
|
|
|
|
found = FALSE;
|
|
|
|
handle = NULL;
|
|
FOR(REQUEST_HANDLE handle)
|
|
FIRST 1 T IN RDB$TYPES WITH
|
|
T.RDB$FIELD_NAME EQ field AND
|
|
T.RDB$TYPE_NAME EQ buffer
|
|
|
|
found = TRUE;
|
|
*id = T.RDB$TYPE;
|
|
END_FOR;
|
|
CMP_release(tdbb, (JRD_REQ)handle);
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
static void lookup_view_contexts( TDBB tdbb, JRD_REL view)
|
|
{
|
|
/**************************************
|
|
*
|
|
* l o o k u p _ v i e w _ c o n t e x t s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Lookup view contexts and store in a linked
|
|
* list on the relation block.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
SSHORT length;
|
|
STR alias;
|
|
VCX view_context, *vcx_ptr;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
request = (BLK) CMP_find_request(tdbb, irq_view_context, IRQ_REQUESTS);
|
|
|
|
vcx_ptr = &view->rel_view_contexts;
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
V IN RDB$VIEW_RELATIONS WITH
|
|
V.RDB$VIEW_NAME EQ view->rel_name
|
|
SORTED BY V.RDB$VIEW_CONTEXT
|
|
|
|
if (!REQUEST(irq_view_context))
|
|
REQUEST(irq_view_context) = request;
|
|
|
|
/* allocate a view context block and link it in
|
|
to the relation block's linked list */
|
|
|
|
view_context = FB_NEW(*tdbb->tdbb_default) vcx();
|
|
*vcx_ptr = view_context;
|
|
vcx_ptr = &view_context->vcx_next;
|
|
|
|
view_context->vcx_context = V.RDB$VIEW_CONTEXT;
|
|
|
|
/* allocate a string block for the context name */
|
|
|
|
length = name_length(V.RDB$CONTEXT_NAME);
|
|
alias = FB_NEW_RPT(*tdbb->tdbb_default, length + 1) str();
|
|
V.RDB$CONTEXT_NAME[length] = 0;
|
|
strcpy((char*)alias->str_data, V.RDB$CONTEXT_NAME);
|
|
alias->str_length = length;
|
|
|
|
view_context->vcx_context_name = alias;
|
|
|
|
/* allocate a string block for the relation name */
|
|
|
|
length = name_length(V.RDB$RELATION_NAME);
|
|
alias = FB_NEW_RPT(*tdbb->tdbb_default, length + 1) str();
|
|
V.RDB$RELATION_NAME[length] = 0;
|
|
strcpy((char*)alias->str_data, V.RDB$RELATION_NAME);
|
|
alias->str_length = length;
|
|
|
|
view_context->vcx_relation_name = alias;
|
|
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_view_context))
|
|
REQUEST(irq_view_context) = request;
|
|
}
|
|
|
|
|
|
static void name_copy( TEXT * to, TEXT * from)
|
|
{
|
|
/**************************************
|
|
*
|
|
* n a m e _ c o p y
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Copy a system name, stripping trailing blanks.
|
|
*
|
|
**************************************/
|
|
USHORT length;
|
|
|
|
length = name_length(from);
|
|
memcpy(to, from, length);
|
|
to[length] = '\0';
|
|
}
|
|
|
|
|
|
static USHORT name_length(const TEXT* name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* n a m e _ l e n g t h
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Compute effective length of system relation name.
|
|
* SQL delimited identifier may contain blanks.
|
|
*
|
|
**************************************/
|
|
const TEXT* p;
|
|
const TEXT* q;
|
|
|
|
q = name - 1;
|
|
for (p = name; *p; p++)
|
|
{
|
|
if (*p != BLANK)
|
|
{
|
|
q = p;
|
|
}
|
|
}
|
|
|
|
return (q + 1) - name;
|
|
}
|
|
|
|
|
|
static JRD_NOD parse_procedure_blr(
|
|
TDBB tdbb,
|
|
JRD_PRC procedure, SLONG blob_id[2], CSB * csb_ptr)
|
|
{
|
|
/**************************************
|
|
*
|
|
* 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.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLB blob;
|
|
JRD_NOD node;
|
|
STR temp;
|
|
SLONG length;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
blob = BLB_open(tdbb, dbb->dbb_sys_trans, (BID)blob_id);
|
|
length = blob->blb_length + 10;
|
|
temp = FB_NEW_RPT(*tdbb->tdbb_default, length) str();
|
|
BLB_get_data(tdbb, blob, temp->str_data, length);
|
|
(*csb_ptr)->csb_blr = temp->str_data;
|
|
par_messages(tdbb, temp->str_data, (USHORT) length, procedure, csb_ptr);
|
|
node =
|
|
PAR_blr(tdbb, (JRD_REL) NULL_PTR, temp->str_data, (CSB) NULL_PTR, csb_ptr,
|
|
&procedure->prc_request, FALSE, 0);
|
|
delete temp;
|
|
|
|
return node;
|
|
}
|
|
|
|
|
|
static BOOLEAN par_messages(TDBB tdbb,
|
|
UCHAR* blr,
|
|
USHORT blr_length,
|
|
JRD_PRC procedure,
|
|
CSB* csb)
|
|
{
|
|
/**************************************
|
|
*
|
|
* p a r _ m e s s a g e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Parse the messages of a blr request. For specified message, allocate
|
|
* a format (fmt) block.
|
|
*
|
|
**************************************/
|
|
fmt::fmt_desc_iterator desc;
|
|
FMT format;
|
|
USHORT count, offset, align, msg_number;
|
|
SSHORT version;
|
|
|
|
(*csb)->csb_running = blr;
|
|
version = BLR_BYTE;
|
|
if (version != blr_version4 && version != blr_version5)
|
|
return FALSE;
|
|
|
|
if (BLR_BYTE != blr_begin)
|
|
return FALSE;
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
while (BLR_BYTE == blr_message) {
|
|
msg_number = BLR_BYTE;
|
|
count = BLR_BYTE;
|
|
count += (BLR_BYTE) << 8;
|
|
format = fmt::newFmt(*tdbb->tdbb_default, count);
|
|
format->fmt_count = count;
|
|
offset = 0;
|
|
for (desc = format->fmt_desc.begin(); count; --count, ++desc) {
|
|
align = PAR_desc(csb, &*desc);
|
|
if (align)
|
|
offset = FB_ALIGN(offset, align);
|
|
desc->dsc_address = (UCHAR *) offset;
|
|
offset += desc->dsc_length;
|
|
}
|
|
format->fmt_length = offset;
|
|
if (msg_number == 0)
|
|
{
|
|
procedure->prc_input_fmt = format;
|
|
}
|
|
else if (msg_number == 1)
|
|
{
|
|
procedure->prc_output_fmt = format;
|
|
}
|
|
else
|
|
{
|
|
delete format;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
void MET_release_triggers( TDBB tdbb, TRIG_VEC* vector_ptr)
|
|
{
|
|
/***********************************************
|
|
*
|
|
* M E T _ r e l e a s e _ t r i g g e r s
|
|
*
|
|
***********************************************
|
|
*
|
|
* Functional description
|
|
* Release a possibly null vector of triggers.
|
|
* If triggers are still active let someone
|
|
* else do the work.
|
|
*
|
|
**************************************/
|
|
TRIG_VEC vector;
|
|
trig_vec::iterator ptr, end;
|
|
|
|
|
|
if (!(vector = *vector_ptr))
|
|
return;
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
*vector_ptr = NULL;
|
|
|
|
for (ptr = vector->begin(), end = vector->end(); ptr < end; ptr++)
|
|
{
|
|
if (ptr->request && CMP_clone_active(ptr->request))
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
for (ptr = vector->begin(), end = vector->end(); ptr < end; ptr++)
|
|
{
|
|
if (ptr->request)
|
|
CMP_release(tdbb, ptr->request);
|
|
else
|
|
if (ptr->name)
|
|
delete ptr->name;
|
|
if (ptr->blr)
|
|
delete ptr->blr;
|
|
}
|
|
|
|
delete vector;
|
|
}
|
|
|
|
|
|
static BOOLEAN resolve_charset_and_collation(
|
|
TDBB tdbb,
|
|
SSHORT * id,
|
|
UCHAR * charset,
|
|
UCHAR * collation)
|
|
{
|
|
/**************************************
|
|
*
|
|
* r e s o l v e _ c h a r s e t _ a n d _ c o l l a t i o n
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Given ASCII7 name of charset & collation
|
|
* resolve the specification to a Character set id.
|
|
* This character set id is also the id of the text_object
|
|
* that implements the C locale for the Character set.
|
|
*
|
|
* Inputs:
|
|
* (charset)
|
|
* ASCII7z name of character set.
|
|
* NULL (implying unspecified) means use the character set
|
|
* for defined for (collation).
|
|
*
|
|
* (collation)
|
|
* ASCII7z name of collation.
|
|
* NULL means use the default collation for (charset).
|
|
*
|
|
* Outputs:
|
|
* (*id)
|
|
* Set to character set specified by this name.
|
|
*
|
|
* Return:
|
|
* 1 if no errors (and *id is set).
|
|
* 0 if either name not found.
|
|
* or if names found, but the collation isn't for the specified
|
|
* character set.
|
|
*
|
|
**************************************/
|
|
BOOLEAN found = FALSE;
|
|
BLK handle;
|
|
DBB dbb;
|
|
SSHORT charset_id;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
assert(id != NULL);
|
|
|
|
handle = NULL;
|
|
|
|
if (collation == NULL)
|
|
{
|
|
if (charset == NULL)
|
|
charset = (UCHAR *) DEFAULT_CHARACTER_SET_NAME;
|
|
|
|
charset_id = 0;
|
|
// const CAST
|
|
if (get_type(tdbb, &charset_id, charset, (UCHAR*) "RDB$CHARACTER_SET_NAME"))
|
|
{
|
|
*id = charset_id;
|
|
return TRUE;
|
|
}
|
|
|
|
/* Charset name not found in the alias table - before giving up
|
|
try the character_set table */
|
|
|
|
FOR(REQUEST_HANDLE handle)
|
|
FIRST 1 CS IN RDB$CHARACTER_SETS
|
|
WITH CS.RDB$CHARACTER_SET_NAME EQ charset
|
|
|
|
found = TRUE;
|
|
*id = CS.RDB$CHARACTER_SET_ID;
|
|
|
|
END_FOR;
|
|
CMP_release(tdbb, (JRD_REQ)handle);
|
|
|
|
return found;
|
|
}
|
|
else if (charset == NULL)
|
|
{
|
|
FOR(REQUEST_HANDLE handle)
|
|
FIRST 1 COL IN RDB$COLLATIONS
|
|
WITH COL.RDB$COLLATION_NAME EQ collation
|
|
|
|
found = TRUE;
|
|
*id = COL.RDB$CHARACTER_SET_ID;
|
|
|
|
END_FOR;
|
|
CMP_release(tdbb, (JRD_REQ)handle);
|
|
|
|
return found;
|
|
}
|
|
|
|
FOR(REQUEST_HANDLE handle)
|
|
FIRST 1 CS IN RDB$CHARACTER_SETS CROSS
|
|
COL IN RDB$COLLATIONS OVER RDB$CHARACTER_SET_ID CROSS
|
|
AL1 IN RDB$TYPES
|
|
WITH AL1.RDB$FIELD_NAME EQ "RDB$CHARACTER_SET_NAME"
|
|
AND AL1.RDB$TYPE_NAME EQ charset
|
|
AND COL.RDB$COLLATION_NAME EQ collation
|
|
AND AL1.RDB$TYPE EQ CS.RDB$CHARACTER_SET_ID
|
|
|
|
found = TRUE;
|
|
*id = CS.RDB$CHARACTER_SET_ID;
|
|
|
|
END_FOR;
|
|
CMP_release(tdbb, (JRD_REQ)handle);
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
static STR save_name(TDBB tdbb, const TEXT* name)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s a v e _ n a m e
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* save a name in a string.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
STR string;
|
|
TEXT* p;
|
|
USHORT l;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
l = name_length(name);
|
|
string = FB_NEW_RPT(*dbb->dbb_permanent, l) str();
|
|
string->str_length = l;
|
|
p = (TEXT *) string->str_data;
|
|
|
|
if (l)
|
|
{
|
|
do {
|
|
*p++ = *name++;
|
|
} while (--l);
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
|
|
static void save_trigger_data(TDBB tdbb, TRIG_VEC* ptr, JRD_REL relation, JRD_REQ request, STR blr,
|
|
TEXT* name, BOOLEAN sys_trigger, USHORT flags)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s a v e _ t r i g g e r _ d a t a
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Save trigger data to passed vector
|
|
*
|
|
**************************************/
|
|
USHORT n = (*ptr) ? (*ptr)->size() : 0;
|
|
TRIG_VEC vector = *ptr;
|
|
|
|
if (!vector) {
|
|
vector = FB_NEW(*tdbb->tdbb_database->dbb_permanent)
|
|
trig_vec(n+1, *tdbb->tdbb_database->dbb_permanent);
|
|
*ptr = vector;
|
|
} else {
|
|
vector->resize(n + 1);
|
|
}
|
|
|
|
trig& t = (*vector)[n];
|
|
t.blr = blr;
|
|
if (name)
|
|
t.name = save_name(tdbb, name);
|
|
else
|
|
t.name = NULL;
|
|
t.flags = flags;
|
|
t.compile_in_progress = FALSE;
|
|
t.sys_trigger = sys_trigger;
|
|
t.request = request;
|
|
t.relation = relation;
|
|
}
|
|
|
|
|
|
static void store_dependencies(TDBB tdbb,
|
|
CSB csb,
|
|
const TEXT* object_name,
|
|
USHORT dependency_type)
|
|
{
|
|
/**************************************
|
|
*
|
|
* s t o r e _ d e p e n d e n c i e s
|
|
*
|
|
**************************************
|
|
*
|
|
* Functional description
|
|
* Store records in RDB$DEPENDENCIES
|
|
* corresponding to the objects found during
|
|
* compilation of blr for a trigger, view, etc.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
JRD_REL relation;
|
|
JRD_PRC procedure;
|
|
JRD_FLD field;
|
|
JRD_NOD node;
|
|
JRD_NOD field_node;
|
|
SSHORT fld_id;
|
|
SSHORT dpdo_type;
|
|
SLONG number;
|
|
BOOLEAN found;
|
|
const TEXT* field_name;
|
|
TEXT* dpdo_name;
|
|
TEXT name[32];
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
while (csb->csb_dependencies)
|
|
{
|
|
node = (JRD_NOD) LLS_POP(&csb->csb_dependencies);
|
|
if (!node->nod_arg[e_dep_object])
|
|
continue;
|
|
dpdo_type = (SSHORT) node->nod_arg[e_dep_object_type];
|
|
relation = NULL;
|
|
procedure = NULL;
|
|
switch (dpdo_type) {
|
|
case obj_relation:
|
|
relation = (JRD_REL) node->nod_arg[e_dep_object];
|
|
dpdo_name = relation->rel_name;
|
|
break;
|
|
case obj_procedure:
|
|
procedure = (JRD_PRC) node->nod_arg [e_dep_object];
|
|
dpdo_name = (TEXT*) procedure->prc_name->str_data;
|
|
break;
|
|
case obj_exception:
|
|
number = (SLONG) node->nod_arg [e_dep_object];
|
|
MET_lookup_exception (tdbb, number, name, NULL);
|
|
dpdo_name = name;
|
|
break;
|
|
/* CVC: Here I'm going to track those pesky things named generators and UDFs. */
|
|
case obj_generator:
|
|
number = (SLONG) node->nod_arg [e_dep_object];
|
|
MET_lookup_generator_id (tdbb, number, name);
|
|
dpdo_name = name;
|
|
break;
|
|
case obj_udf: {
|
|
FUN udf = (FUN) node->nod_arg [e_dep_object];
|
|
dpdo_name = udf->fun_symbol->sym_string;
|
|
}
|
|
break;
|
|
}
|
|
|
|
field_node = node->nod_arg[e_dep_field];
|
|
|
|
field_name = NULL;
|
|
if (field_node)
|
|
{
|
|
if (field_node->nod_type == nod_field)
|
|
{
|
|
fld_id = (SSHORT) field_node->nod_arg[0];
|
|
if (relation)
|
|
{
|
|
if ( (field = MET_get_field(relation, fld_id)) )
|
|
{
|
|
field_name = field->fld_name;
|
|
}
|
|
else if (procedure)
|
|
{
|
|
field =
|
|
(JRD_FLD) (*procedure->prc_output_fields)[fld_id];
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
field_name = (TEXT *) field_node->nod_arg[0];
|
|
}
|
|
}
|
|
|
|
if (field_name)
|
|
{
|
|
request =
|
|
(BLK) CMP_find_request(tdbb, irq_c_deps_f, IRQ_REQUESTS);
|
|
found = FALSE;
|
|
FOR(REQUEST_HANDLE request) X IN RDB$DEPENDENCIES WITH
|
|
X.RDB$DEPENDENT_NAME = object_name AND
|
|
X.RDB$DEPENDED_ON_NAME = dpdo_name AND
|
|
X.RDB$DEPENDED_ON_TYPE = dpdo_type AND
|
|
X.RDB$FIELD_NAME = field_name AND
|
|
X.RDB$DEPENDENT_TYPE = dependency_type
|
|
|
|
if (!REQUEST(irq_c_deps_f))
|
|
REQUEST(irq_c_deps_f) = request;
|
|
found = TRUE;
|
|
END_FOR;
|
|
|
|
if (found)
|
|
continue;
|
|
if (!REQUEST(irq_c_deps_f))
|
|
REQUEST(irq_c_deps_f) = request;
|
|
}
|
|
else
|
|
{
|
|
request = (BLK) CMP_find_request(tdbb, irq_c_deps, IRQ_REQUESTS);
|
|
found = FALSE;
|
|
FOR(REQUEST_HANDLE request) X IN RDB$DEPENDENCIES WITH
|
|
X.RDB$DEPENDENT_NAME = object_name AND
|
|
X.RDB$DEPENDED_ON_NAME = dpdo_name AND
|
|
X.RDB$DEPENDED_ON_TYPE = dpdo_type AND
|
|
X.RDB$FIELD_NAME MISSING AND
|
|
X.RDB$DEPENDENT_TYPE = dependency_type
|
|
|
|
if (!REQUEST(irq_c_deps))
|
|
REQUEST(irq_c_deps) = request;
|
|
found = TRUE;
|
|
|
|
END_FOR;
|
|
|
|
if (found)
|
|
continue;
|
|
if (!REQUEST(irq_c_deps))
|
|
REQUEST(irq_c_deps) = request;
|
|
}
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_s_deps, IRQ_REQUESTS);
|
|
|
|
STORE(REQUEST_HANDLE request) DEP IN RDB$DEPENDENCIES
|
|
strcpy(DEP.RDB$DEPENDENT_NAME, object_name);
|
|
DEP.RDB$DEPENDED_ON_TYPE = dpdo_type;
|
|
strcpy(DEP.RDB$DEPENDED_ON_NAME, dpdo_name);
|
|
if (field_name)
|
|
{
|
|
DEP.RDB$FIELD_NAME.NULL = FALSE;
|
|
strcpy(DEP.RDB$FIELD_NAME, field_name);
|
|
}
|
|
else
|
|
DEP.RDB$FIELD_NAME.NULL = TRUE;
|
|
DEP.RDB$DEPENDENT_TYPE = dependency_type;
|
|
END_STORE;
|
|
|
|
if (!REQUEST(irq_s_deps))
|
|
REQUEST(irq_s_deps) = request;
|
|
}
|
|
}
|
|
|
|
|
|
static void terminate(TEXT * str, USHORT len)
|
|
{
|
|
/**************************************
|
|
*
|
|
* t e r m i n a t e
|
|
*
|
|
****************************************
|
|
*
|
|
* Functional description
|
|
* Truncates the rightmost contiguous spaces on a string.
|
|
*
|
|
**************************************************************/
|
|
SSHORT i;
|
|
assert(len > 0);
|
|
|
|
for (i = len - 1; i >= 0 && ((isspace(str[i])) || (str[i] == 0)); i--)
|
|
/* do nothing */ ;
|
|
str[i + 1] = 0;
|
|
}
|
|
|
|
|
|
static BOOLEAN verify_TRG_ignore_perm(TDBB tdbb, TEXT* trig_name)
|
|
{
|
|
/*****************************************************
|
|
*
|
|
* v e r i f y _ T R G _ i g n o r e _ p e r m
|
|
*
|
|
*****************************************************
|
|
*
|
|
* Functional description
|
|
* Return TRUE if this trigger can go through without any permission
|
|
* checks. Currently, the only class of triggers that can go
|
|
* through without permission checks are
|
|
* (a) two system triggers (RDB$TRIGGERS_34 and RDB$TRIGGERS_35)
|
|
* (b) those defined for referential integrity actions such as,
|
|
* set null, set default, and cascade.
|
|
*
|
|
**************************************/
|
|
DBB dbb;
|
|
BLK request;
|
|
|
|
SET_TDBB(tdbb);
|
|
dbb = tdbb->tdbb_database;
|
|
|
|
/* see if this is a system trigger, with the flag set as TRG_ignore_perm */
|
|
if (INI_get_trig_flags(trig_name) & (USHORT) TRG_ignore_perm)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
request = (BLK) CMP_find_request(tdbb, irq_c_trg_perm, IRQ_REQUESTS);
|
|
|
|
FOR(REQUEST_HANDLE request)
|
|
CHK IN RDB$CHECK_CONSTRAINTS CROSS
|
|
REF IN RDB$REF_CONSTRAINTS WITH
|
|
CHK.RDB$TRIGGER_NAME EQ trig_name AND
|
|
REF.RDB$CONSTRAINT_NAME = CHK.RDB$CONSTRAINT_NAME
|
|
|
|
if (!REQUEST(irq_c_trg_perm))
|
|
{
|
|
REQUEST(irq_c_trg_perm) = request;
|
|
}
|
|
|
|
EXE_unwind(tdbb, (JRD_REQ)request);
|
|
terminate(REF.RDB$UPDATE_RULE, sizeof(REF.RDB$UPDATE_RULE));
|
|
terminate(REF.RDB$DELETE_RULE, sizeof(REF.RDB$DELETE_RULE));
|
|
|
|
if (!strcmp(REF.RDB$UPDATE_RULE, RI_ACTION_CASCADE) ||
|
|
!strcmp(REF.RDB$UPDATE_RULE, RI_ACTION_NULL) ||
|
|
!strcmp(REF.RDB$UPDATE_RULE, RI_ACTION_DEFAULT) ||
|
|
!strcmp(REF.RDB$DELETE_RULE, RI_ACTION_CASCADE) ||
|
|
!strcmp(REF.RDB$DELETE_RULE, RI_ACTION_NULL) ||
|
|
!strcmp(REF.RDB$DELETE_RULE, RI_ACTION_DEFAULT))
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
END_FOR;
|
|
|
|
if (!REQUEST(irq_c_trg_perm))
|
|
{
|
|
REQUEST(irq_c_trg_perm) = request;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|