8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-27 05:23:02 +01:00
firebird-mirror/src/jrd/exe.cpp

3963 lines
104 KiB
C++
Raw Normal View History

2001-05-23 15:26:42 +02:00
/*
* PROGRAM: JRD Access Method
2003-10-08 10:42:48 +02:00
* MODULE: exe.cpp
2001-05-23 15:26:42 +02:00
* DESCRIPTION: Statement execution
*
* The contents of this file are subject to the Interbase Public
* License Version 1.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy
* of the License at http://www.Inprise.com/IPL.html
*
* Software distributed under the License is distributed on an
* "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
* or implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code was created by Inprise Corporation
* and its predecessors. Portions created by Inprise Corporation are
* Copyright (C) Inprise Corporation.
*
* All Rights Reserved.
* Contributor(s): ______________________________________.
2002-06-30 11:58:20 +02:00
*
* 2001.6.21 Claudio Valderrama: Allow inserting strings into blob fields.
* 2001.6.28 Claudio Valderrama: Move code to cleanup_rpb() as directed
* by Ann Harrison and cleanup of new record in store() routine.
* 2001.10.11 Claudio Valderrama: Fix SF Bug #436462: From now, we only
* count real store, modify and delete operations either in an external
* file or in a table. Counting on a view caused up to three operations
* being reported instead of one.
* 2001.12.03 Claudio Valderrama: new visit to the same issue: views need
* to count virtual operations, not real I/O on the underlying tables.
* 2002.09.28 Dmitry Yemanov: Reworked internal_info stuff, enhanced
* exception handling in SPs/triggers,
* implemented ROWS_AFFECTED system variable
*
2008-12-05 02:20:14 +01:00
* 2002.10.21 Nickolay Samofatov: Added support for explicit pessimistic locks
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "MPEXL" port
* 2002.10.28 Sean Leyne - Code cleanup, removed obsolete "DecOSF" port
* 2002.10.29 Nickolay Samofatov: Added support for savepoints
* 2002.10.30 Sean Leyne - Removed support for obsolete "PC_PLATFORM" define
* 2003.10.05 Dmitry Yemanov: Added support for explicit cursors in PSQL
2007-03-06 03:29:48 +01:00
* Adriano dos Santos Fernandes
*
2001-05-23 15:26:42 +02:00
*/
#include "firebird.h"
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
# include <time.h>
#else
# ifdef HAVE_SYS_TIME_H
# include <sys/time.h>
# else
# include <time.h>
# endif
#endif
2001-05-23 15:26:42 +02:00
#include "../jrd/common.h"
#include "../jrd/ibsetjmp.h"
#include "../common/classes/VaryStr.h"
2001-05-23 15:26:42 +02:00
#include <string.h>
#include "../jrd/jrd.h"
#include "../jrd/req.h"
#include "../jrd/val.h"
#include "../jrd/exe.h"
#include "../jrd/extds/ExtDS.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/tra.h"
#include "gen/iberror.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/ods.h"
#include "../jrd/btr.h"
#include "../jrd/rse.h"
#include "../jrd/lck.h"
#include "../jrd/intl.h"
#include "../jrd/sbm.h"
#include "../jrd/blb.h"
#include "../jrd/blr.h"
#include "../jrd/blb_proto.h"
#include "../jrd/btr_proto.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/dfw_proto.h"
#include "../jrd/dpm_proto.h"
#include "../jrd/err_proto.h"
#include "../jrd/evl_proto.h"
#include "../jrd/exe_proto.h"
#include "../jrd/ext_proto.h"
#include "../jrd/gds_proto.h"
#include "../jrd/idx_proto.h"
2005-05-28 00:45:31 +02:00
#include "../jrd/intl_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/jrd_proto.h"
#include "../jrd/lck_proto.h"
#include "../jrd/met_proto.h"
#include "../jrd/mov_proto.h"
#include "../jrd/opt_proto.h"
#include "../jrd/par_proto.h"
#include "../jrd/rlck_proto.h"
2001-05-23 15:26:42 +02:00
#include "../jrd/tra_proto.h"
#include "../jrd/vio_proto.h"
#include "../jrd/isc_s_proto.h"
#include "../jrd/execute_statement.h"
2003-04-22 10:45:52 +02:00
#include "../dsql/dsql_proto.h"
#include "../jrd/rpb_chain.h"
#include "../jrd/VirtualTable.h"
2009-02-01 23:10:12 +01:00
#include "../jrd/trace/TraceManager.h"
#include "../jrd/trace/TraceJrdHelpers.h"
#include "../dsql/Nodes.h"
#include "../jrd/ValuesImpl.h"
#include "../jrd/recsrc/RecordSource.h"
#include "../jrd/recsrc/Cursor.h"
#include "../jrd/Function.h"
using namespace Jrd;
using namespace Firebird;
2005-06-24 14:56:34 +02:00
// AffectedRows class implementation
AffectedRows::AffectedRows()
{
clear();
}
void AffectedRows::clear()
{
writeFlag = false;
fetchedRows = modifiedRows = 0;
}
void AffectedRows::bumpFetched()
{
fetchedRows++;
}
void AffectedRows::bumpModified(bool increment)
{
if (increment) {
modifiedRows++;
}
else {
writeFlag = true;
}
}
bool AffectedRows::isReadOnly() const
{
return !writeFlag;
}
bool AffectedRows::hasCursor() const
{
return (fetchedRows > 0);
}
int AffectedRows::getCount() const
{
return writeFlag ? modifiedRows : fetchedRows;
}
// StatusXcp class implementation
StatusXcp::StatusXcp()
{
clear();
}
void StatusXcp::clear()
{
fb_utils::init_status(status);
}
void StatusXcp::init(const ISC_STATUS* vector)
{
memcpy(status, vector, sizeof(ISC_STATUS_ARRAY));
}
void StatusXcp::copyTo(ISC_STATUS* vector) const
{
memcpy(vector, status, sizeof(ISC_STATUS_ARRAY));
}
2001-05-23 15:26:42 +02:00
bool StatusXcp::success() const
{
2003-11-02 13:05:38 +01:00
return (status[1] == FB_SUCCESS);
}
SLONG StatusXcp::as_gdscode() const
{
return status[1];
}
SLONG StatusXcp::as_sqlcode() const
{
return gds__sqlcode(status);
}
static void cleanup_rpb(thread_db*, record_param*);
static jrd_nod* erase(thread_db*, jrd_nod*, SSHORT);
2008-12-20 09:12:19 +01:00
static void execute_looper(thread_db*, jrd_req*, jrd_tra*, jrd_req::req_s);
static void exec_sql(thread_db*, jrd_req*, DSC *);
static void execute_procedure(thread_db*, jrd_nod*);
static jrd_nod* execute_statement(thread_db*, jrd_req*, jrd_nod*);
static void execute_triggers(thread_db*, trig_vec**, record_param*, record_param*,
2008-12-20 09:12:19 +01:00
jrd_req::req_ta, SSHORT);
static void get_string(thread_db*, jrd_req*, jrd_nod*, Firebird::string&);
2009-04-28 15:08:04 +02:00
static void looper_seh(thread_db*, jrd_req*);
static jrd_nod* modify(thread_db*, jrd_nod*, SSHORT);
static jrd_nod* receive_msg(thread_db*, jrd_nod*);
static void release_blobs(thread_db*, jrd_req*);
static void release_proc_save_points(jrd_req*);
static jrd_nod* selct(thread_db*, jrd_nod*);
static void set_error(thread_db*, const xcp_repeat*, jrd_nod*);
static jrd_nod* stall(thread_db*, jrd_nod*);
static jrd_nod* store(thread_db*, jrd_nod*, SSHORT);
static bool test_and_fixup_error(thread_db*, const PsqlException*, jrd_req*);
static void trigger_failure(thread_db*, jrd_req*);
static void validate(thread_db*, jrd_nod*);
inline void PreModifyEraseTriggers(thread_db*, trig_vec**, SSHORT, record_param*,
record_param*, jrd_req::req_ta);
static void stuff_stack_trace(const jrd_req*);
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// macro definitions
2001-05-23 15:26:42 +02:00
#if (defined SUPERSERVER) && (defined WIN_NT)
2004-05-03 23:43:56 +02:00
const int MAX_CLONES = 750;
#else
const int MAX_CLONES = 1000;
2001-05-23 15:26:42 +02:00
#endif
2004-05-03 23:43:56 +02:00
const int ALL_TRIGS = 0;
const int PRE_TRIG = 1;
const int POST_TRIG = 2;
2001-05-23 15:26:42 +02:00
2004-11-07 15:30:38 +01:00
const size_t MAX_STACK_TRACE = 2048;
void EXE_assignment(thread_db* tdbb, jrd_nod* node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X E _ a s s i g n m e n t
*
**************************************
*
* Functional description
* Perform an assignment
*
**************************************/
DEV_BLKCHK(node, type_nod);
SET_TDBB(tdbb);
jrd_req* request = tdbb->getRequest();
2001-05-23 15:26:42 +02:00
BLKCHK(node, type_nod);
2007-06-27 04:08:25 +02:00
// Get descriptors of src field/parameter/variable, etc.
request->req_flags &= ~req_null;
dsc* from_desc = EVL_expr(tdbb, node->nod_arg[e_asgn_from]);
EXE_assignment(tdbb, node->nod_arg[e_asgn_to], from_desc, (request->req_flags & req_null),
node->nod_arg[e_asgn_missing], node->nod_arg[e_asgn_missing2]);
request->req_operation = jrd_req::req_return;
}
void EXE_assignment(thread_db* tdbb, jrd_nod* to, dsc* from_desc, bool from_null,
jrd_nod* missing_node, jrd_nod* missing2_node)
{
/**************************************
*
2007-06-27 04:08:25 +02:00
* E X E _ a s s i g n m e n t
*
**************************************
*
* Functional description
* Perform an assignment
*
**************************************/
DEV_BLKCHK(node, type_nod);
SET_TDBB(tdbb);
jrd_req* request = tdbb->getRequest();
// Get descriptors of receiving and sending fields/parameters, variables, etc.
2001-05-23 15:26:42 +02:00
dsc* missing = NULL;
if (missing_node) {
missing = EVL_expr(tdbb, missing_node);
2001-12-24 03:51:06 +01:00
}
2007-06-27 04:08:25 +02:00
// Get descriptor of target field/parameter/variable, etc.
2001-12-24 03:51:06 +01:00
DSC* to_desc = EVL_assign_to(tdbb, to);
2001-05-23 15:26:42 +02:00
request->req_flags &= ~req_null;
// NS: If we are assigning to NULL, we finished.
// This functionality is currently used to allow calling UDF routines
// without assigning resulting value anywhere.
if (!to_desc)
return;
SSHORT null = from_null ? -1 : 0;
2001-12-24 03:51:06 +01:00
if (!null && missing && MOV_compare(missing, from_desc) == 0) {
2001-05-23 15:26:42 +02:00
null = -1;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
USHORT* impure_flags = NULL;
switch (to->nod_type)
{
case nod_variable:
if (to->nod_arg[e_var_info])
{
2008-12-05 02:20:14 +01:00
EVL_validate(tdbb,
Item(nod_variable, (IPTR) to->nod_arg[e_var_id]),
reinterpret_cast<const ItemInfo*>(to->nod_arg[e_var_info]),
from_desc, null == -1);
}
impure_flags = &((impure_value*) ((SCHAR *) request +
to->nod_arg[e_var_variable]->nod_impure))->vlu_flags;
break;
case nod_argument:
if (to->nod_arg[e_arg_info])
{
EVL_validate(tdbb,
Item(nod_argument, (IPTR) to->nod_arg[e_arg_message]->nod_arg[e_msg_number],
(IPTR) to->nod_arg[e_arg_number]),
reinterpret_cast<const ItemInfo*>(to->nod_arg[e_arg_info]),
from_desc, null == -1);
}
impure_flags = (USHORT*) ((UCHAR *) request +
(IPTR) to->nod_arg[e_arg_message]->nod_arg[e_msg_impure_flags] +
(sizeof(USHORT) * (IPTR) to->nod_arg[e_arg_number]));
break;
}
if (impure_flags != NULL)
*impure_flags |= VLU_checked;
// If the value is non-missing, move/convert it. Otherwise fill the
// field with appropriate nulls.
2004-01-13 10:52:19 +01:00
dsc temp;
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (!null)
{
// if necessary and appropriate, use the indicator variable
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (to->nod_type == nod_argument && to->nod_arg[e_arg_indicator])
{
2004-01-13 10:52:19 +01:00
dsc* indicator = EVL_assign_to(tdbb, to->nod_arg[e_arg_indicator]);
2001-12-24 03:51:06 +01:00
temp.dsc_dtype = dtype_short;
temp.dsc_length = sizeof(SSHORT);
temp.dsc_scale = 0;
2001-05-23 15:26:42 +02:00
temp.dsc_sub_type = 0;
2001-12-24 03:51:06 +01:00
SSHORT len;
2008-12-20 09:12:19 +01:00
if ((from_desc->dsc_dtype <= dtype_varying) && (to_desc->dsc_dtype <= dtype_varying) &&
2001-05-23 15:26:42 +02:00
(TEXT_LEN(from_desc) > TEXT_LEN(to_desc)))
2001-12-24 03:51:06 +01:00
{
2001-05-23 15:26:42 +02:00
len = TEXT_LEN(from_desc);
}
else {
2001-05-23 15:26:42 +02:00
len = 0;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
2003-10-08 10:42:48 +02:00
temp.dsc_address = (UCHAR *) &len;
MOV_move(tdbb, &temp, indicator);
2001-05-23 15:26:42 +02:00
2009-11-23 06:24:29 +01:00
if (len)
{
2001-05-23 15:26:42 +02:00
temp = *from_desc;
temp.dsc_length = TEXT_LEN(to_desc);
2001-12-24 03:51:06 +01:00
if (temp.dsc_dtype == dtype_cstring) {
2001-05-23 15:26:42 +02:00
temp.dsc_length += 1;
}
else if (temp.dsc_dtype == dtype_varying) {
2001-05-23 15:26:42 +02:00
temp.dsc_length += 2;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
from_desc = &temp;
}
}
// Validate range for datetime values
if (DTYPE_IS_DATE(from_desc->dsc_dtype))
{
switch (from_desc->dsc_dtype)
{
case dtype_sql_date:
if (!Firebird::TimeStamp::isValidDate(*(GDS_DATE*) from_desc->dsc_address))
{
ERR_post(Arg::Gds(isc_date_range_exceeded));
}
break;
case dtype_sql_time:
if (!Firebird::TimeStamp::isValidTime(*(GDS_TIME*) from_desc->dsc_address))
{
ERR_post(Arg::Gds(isc_time_range_exceeded));
}
break;
case dtype_timestamp:
if (!Firebird::TimeStamp::isValidTimeStamp(*(GDS_TIMESTAMP*) from_desc->dsc_address))
{
ERR_post(Arg::Gds(isc_datetime_range_exceeded));
}
break;
default:
fb_assert(false);
}
}
2008-12-20 09:12:19 +01:00
if (DTYPE_IS_BLOB_OR_QUAD(from_desc->dsc_dtype) || DTYPE_IS_BLOB_OR_QUAD(to_desc->dsc_dtype))
{
// ASF: Don't let MOV_move call BLB_move because MOV
// will not pass the destination field to BLB_move.
BLB_move(tdbb, from_desc, to_desc, to);
}
else if (!DSC_EQUIV(from_desc, to_desc, false))
{
MOV_move(tdbb, from_desc, to_desc);
}
else if (from_desc->dsc_dtype == dtype_short)
{
2009-01-14 09:22:32 +01:00
*((SSHORT*) to_desc->dsc_address) = *((SSHORT*) from_desc->dsc_address);
2004-01-13 10:52:19 +01:00
}
else if (from_desc->dsc_dtype == dtype_long)
{
2009-01-14 09:22:32 +01:00
*((SLONG*) to_desc->dsc_address) = *((SLONG*) from_desc->dsc_address);
2004-01-13 10:52:19 +01:00
}
else if (from_desc->dsc_dtype == dtype_int64)
{
2009-01-14 09:22:32 +01:00
*((SINT64*) to_desc->dsc_address) = *((SINT64*) from_desc->dsc_address);
2004-01-13 10:52:19 +01:00
}
else
{
2008-01-16 09:54:50 +01:00
memcpy(to_desc->dsc_address, from_desc->dsc_address, from_desc->dsc_length);
}
2008-01-16 09:54:50 +01:00
2001-05-23 15:26:42 +02:00
to_desc->dsc_flags &= ~DSC_null;
}
2001-12-24 03:51:06 +01:00
else
{
if (missing2_node && (missing = EVL_expr(tdbb, missing2_node)))
2009-01-14 09:22:32 +01:00
{
MOV_move(tdbb, missing, to_desc);
2001-05-23 15:26:42 +02:00
}
else
{
memset(to_desc->dsc_address, 0, to_desc->dsc_length);
}
2001-05-23 15:26:42 +02:00
to_desc->dsc_flags |= DSC_null;
}
// Handle the null flag as appropriate for fields and message arguments.
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (to->nod_type == nod_field)
{
const SSHORT id = (USHORT)(IPTR) to->nod_arg[e_fld_id];
2008-11-30 03:23:01 +01:00
Record* record = request->req_rpb[(int) (IPTR) to->nod_arg[e_fld_stream]].rpb_record;
2001-12-24 03:51:06 +01:00
if (null) {
2001-05-23 15:26:42 +02:00
SET_NULL(record, id);
2004-01-13 10:52:19 +01:00
}
else {
2001-05-23 15:26:42 +02:00
CLEAR_NULL(record, id);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
}
2001-12-24 03:51:06 +01:00
else if (to->nod_type == nod_argument && to->nod_arg[e_arg_flag])
{
2001-05-23 15:26:42 +02:00
to_desc = EVL_assign_to(tdbb, to->nod_arg[e_arg_flag]);
// If the null flag is a string with an effective length of one,
// then -1 will not fit. Therefore, store 1 instead.
2001-05-23 15:26:42 +02:00
2008-12-20 09:12:19 +01:00
if (null && to_desc->dsc_dtype <= dtype_varying)
2001-12-24 03:51:06 +01:00
{
2008-12-20 09:12:19 +01:00
USHORT minlen;
switch (to_desc->dsc_dtype)
{
case dtype_text:
minlen = 1;
break;
case dtype_cstring:
minlen = 2;
break;
case dtype_varying:
minlen = 3;
break;
}
if (to_desc->dsc_length <= minlen)
{
null = 1;
}
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
temp.dsc_dtype = dtype_short;
temp.dsc_length = sizeof(SSHORT);
temp.dsc_scale = 0;
temp.dsc_sub_type = 0;
2009-01-14 10:19:00 +01:00
temp.dsc_address = (UCHAR*) &null;
MOV_move(tdbb, &temp, to_desc);
2009-11-23 06:24:29 +01:00
if (null && to->nod_arg[e_arg_indicator])
{
2001-05-23 15:26:42 +02:00
to_desc = EVL_assign_to(tdbb, to->nod_arg[e_arg_indicator]);
MOV_move(tdbb, &temp, to_desc);
2001-05-23 15:26:42 +02:00
}
}
}
2008-12-20 09:12:19 +01:00
void EXE_execute_db_triggers(thread_db* tdbb, jrd_tra* transaction, jrd_req::req_ta trigger_action)
{
/**************************************
*
* E X E _ e x e c u t e _ d b _ t r i g g e r s
*
**************************************
*
* Functional description
* Execute database triggers
*
**************************************/
2006-11-10 09:12:07 +01:00
// do nothing if user doesn't want database triggers
if (tdbb->getAttachment()->att_flags & ATT_no_db_triggers)
return;
int type = 0;
switch (trigger_action)
{
case jrd_req::req_trigger_connect:
type = DB_TRIGGER_CONNECT;
break;
case jrd_req::req_trigger_disconnect:
type = DB_TRIGGER_DISCONNECT;
break;
case jrd_req::req_trigger_trans_start:
type = DB_TRIGGER_TRANS_START;
break;
case jrd_req::req_trigger_trans_commit:
type = DB_TRIGGER_TRANS_COMMIT;
break;
case jrd_req::req_trigger_trans_rollback:
type = DB_TRIGGER_TRANS_ROLLBACK;
break;
default:
fb_assert(false);
return;
}
if (tdbb->getDatabase()->dbb_triggers[type])
{
jrd_tra* old_transaction = tdbb->getTransaction();
tdbb->setTransaction(transaction);
try
{
execute_triggers(tdbb, &tdbb->getDatabase()->dbb_triggers[type],
NULL, NULL, trigger_action, ALL_TRIGS);
tdbb->setTransaction(old_transaction);
}
catch (...)
{
tdbb->setTransaction(old_transaction);
throw;
}
}
}
// Execute DDL triggers.
void EXE_execute_ddl_triggers(thread_db* tdbb, jrd_tra* transaction, bool preTriggers, int action)
{
// Our caller verifies (ATT_no_db_triggers) if DDL triggers should not run.
if (tdbb->getDatabase()->dbb_ddl_triggers)
{
2009-10-31 02:46:06 +01:00
jrd_tra* const oldTransaction = tdbb->getTransaction();
tdbb->setTransaction(transaction);
try
{
trig_vec triggers;
trig_vec* triggersPtr = &triggers;
for (trig_vec::iterator i = tdbb->getDatabase()->dbb_ddl_triggers->begin();
i != tdbb->getDatabase()->dbb_ddl_triggers->end();
++i)
{
if ((i->type & (1LL << action)) &&
((preTriggers && (i->type & 0x1) == 0) || (!preTriggers && (i->type & 0x1) == 0x1)))
{
triggers.add() = *i;
}
}
2009-11-23 06:24:29 +01:00
execute_triggers(tdbb, &triggersPtr, NULL, NULL, jrd_req::req_trigger_ddl, ALL_TRIGS);
2009-10-31 02:46:06 +01:00
tdbb->setTransaction(oldTransaction);
}
catch (...)
{
2009-10-31 02:46:06 +01:00
tdbb->setTransaction(oldTransaction);
throw;
}
}
}
jrd_req* EXE_find_request(thread_db* tdbb, jrd_req* request, bool validate)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X E _ f i n d _ r e q u e s t
*
**************************************
*
* Functional description
* Find an inactive incarnation of a trigger request. If necessary,
* clone it.
*
**************************************/
DEV_BLKCHK(request, type_req);
SET_TDBB(tdbb);
2008-05-05 06:39:09 +02:00
Database* const dbb = tdbb->getDatabase();
Jrd::Attachment* const attachment = tdbb->getAttachment();
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// I found a core file from my test runs that came from a NULL request -
// but have no idea what test was running. Let's bugcheck so we can figure it out
2001-05-23 15:26:42 +02:00
if (!request)
2009-11-22 04:56:20 +01:00
BUGCHECK(167); // msg 167 invalid SEND request
2001-05-23 15:26:42 +02:00
Database::CheckoutLockGuard guard(dbb, dbb->dbb_exe_clone_mutex);
2003-12-31 06:36:12 +01:00
jrd_req* clone = NULL;
USHORT count = 0;
2001-05-23 15:26:42 +02:00
if (!(request->req_flags & req_in_use))
clone = request;
2009-01-14 09:22:32 +01:00
else
{
2008-05-05 06:39:09 +02:00
if (request->req_attachment == attachment)
2001-05-23 15:26:42 +02:00
count++;
2009-11-22 04:56:20 +01:00
// Request exists and is in use. Search clones for one in use by
// this attachment. If not found, return first inactive request.
2001-05-23 15:26:42 +02:00
vec<jrd_req*>* vector = request->req_sub_requests;
2009-01-14 10:19:00 +01:00
const USHORT clones = vector ? (vector->count() - 1) : 0;
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
USHORT n;
2009-01-14 09:22:32 +01:00
for (n = 1; n <= clones; n++)
{
2003-12-31 06:36:12 +01:00
jrd_req* next = CMP_clone_request(tdbb, request, n, validate);
2009-11-23 06:24:29 +01:00
if (next->req_attachment == attachment)
{
if (!(next->req_flags & req_in_use))
{
2001-05-23 15:26:42 +02:00
clone = next;
break;
}
2008-01-16 09:54:50 +01:00
count++;
2001-05-23 15:26:42 +02:00
}
else if (!(next->req_flags & req_in_use) && !clone)
clone = next;
}
if (count > MAX_CLONES) {
ERR_post(Arg::Gds(isc_req_max_clones_exceeded));
2001-05-23 15:26:42 +02:00
}
if (!clone)
clone = CMP_clone_request(tdbb, request, n, validate);
}
2008-05-05 06:39:09 +02:00
clone->req_attachment = attachment;
clone->req_stats.reset();
clone->req_base_stats.reset();
2001-05-23 15:26:42 +02:00
clone->req_flags |= req_in_use;
2001-05-23 15:26:42 +02:00
return clone;
}
void EXE_receive(thread_db* tdbb,
jrd_req* request,
2001-05-23 15:26:42 +02:00
USHORT msg,
USHORT length,
UCHAR* buffer,
bool top_level)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X E _ r e c e i v e
*
**************************************
*
* Functional description
* Move a message from JRD to the host program. This corresponds to
2004-01-13 10:52:19 +01:00
* a JRD BLR/jrd_nod* send.
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
DEV_BLKCHK(request, type_req);
2004-10-03 14:10:19 +02:00
if (--tdbb->tdbb_quantum < 0)
JRD_reschedule(tdbb, 0, true);
2003-12-31 06:36:12 +01:00
jrd_tra* transaction = request->req_transaction;
2001-05-23 15:26:42 +02:00
if (!(request->req_flags & req_active)) {
ERR_post(Arg::Gds(isc_req_sync));
2001-05-23 15:26:42 +02:00
}
if (request->req_flags & req_proc_fetch)
{
/* request->req_proc_sav_point stores all the request savepoints.
When going to continue execution put request save point list
into transaction->tra_save_point so that it is used in looper.
When we come back to EXE_receive() restore
transaction->tra_save_point and merge all work done under
stored procedure savepoints into the current transaction
savepoint, which is the savepoint for fetch. */
Savepoint* const save_sav_point = transaction->tra_save_point;
2001-05-23 15:26:42 +02:00
transaction->tra_save_point = request->req_proc_sav_point;
request->req_proc_sav_point = save_sav_point;
if (!transaction->tra_save_point) {
2002-11-03 18:29:51 +01:00
VIO_start_save_point(tdbb, transaction);
2001-05-23 15:26:42 +02:00
}
}
2008-12-05 02:20:14 +01:00
try
{
2001-05-23 15:26:42 +02:00
const bool external = request->req_procedure && request->req_procedure->getExternal();
if (external)
{
execute_looper(tdbb, request, transaction, jrd_req::req_sync);
}
else
{
if (request->req_message->nod_type == nod_stall)
{
execute_looper(tdbb, request, transaction, jrd_req::req_sync);
}
2001-05-23 15:26:42 +02:00
if (!(request->req_flags & req_active) || request->req_operation != jrd_req::req_send)
{
ERR_post(Arg::Gds(isc_req_sync));
}
2001-05-23 15:26:42 +02:00
}
2003-12-31 06:36:12 +01:00
const jrd_nod* message = request->req_message;
2004-03-30 06:10:52 +02:00
const Format* format = (Format*) message->nod_arg[e_msg_format];
2001-05-23 15:26:42 +02:00
if (msg != (USHORT)(IPTR) message->nod_arg[e_msg_number])
ERR_post(Arg::Gds(isc_req_sync));
2001-05-23 15:26:42 +02:00
if (length != format->fmt_length)
2008-12-20 09:12:19 +01:00
ERR_post(Arg::Gds(isc_port_len) << Arg::Num(length) << Arg::Num(format->fmt_length));
2001-05-23 15:26:42 +02:00
2008-01-16 09:54:50 +01:00
memcpy(buffer, (SCHAR*) request + message->nod_impure, length);
2001-05-23 15:26:42 +02:00
// ASF: temporary blobs returned to the client should not be released
// with the request, but in the transaction end.
if (top_level)
{
for (int i = 0; i < format->fmt_count; ++i)
{
const DSC* desc = &format->fmt_desc[i];
if (desc->isBlob())
{
const bid* id = (bid*) (buffer + (ULONG)(IPTR)desc->dsc_address);
if (transaction->tra_blobs->locate(id->bid_temp_id()))
{
BlobIndex* current = &transaction->tra_blobs->current();
if (current->bli_request &&
current->bli_request->req_blobs.locate(id->bid_temp_id()))
{
current->bli_request->req_blobs.fastRemove();
current->bli_request = NULL;
}
}
}
}
}
if (!external)
execute_looper(tdbb, request, transaction, jrd_req::req_proceed);
2001-05-23 15:26:42 +02:00
} //try
catch (const Firebird::Exception&)
{
if (request->req_flags & req_proc_fetch)
{
Savepoint* const save_sav_point = transaction->tra_save_point;
transaction->tra_save_point = request->req_proc_sav_point;
request->req_proc_sav_point = save_sav_point;
release_proc_save_points(request);
}
2002-04-18 05:54:36 +02:00
throw;
}
2009-11-23 06:24:29 +01:00
if (request->req_flags & req_proc_fetch)
{
Savepoint* const save_sav_point = transaction->tra_save_point;
2001-05-23 15:26:42 +02:00
transaction->tra_save_point = request->req_proc_sav_point;
request->req_proc_sav_point = save_sav_point;
2008-12-20 09:12:19 +01:00
VIO_merge_proc_sav_points(tdbb, transaction, &request->req_proc_sav_point);
2001-05-23 15:26:42 +02:00
}
}
2009-11-25 09:38:52 +01:00
void EXE_send(thread_db* tdbb, jrd_req* request, USHORT msg, USHORT length, const UCHAR* buffer)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X E _ s e n d
*
**************************************
*
* Functional description
2008-12-05 02:20:14 +01:00
* Send a message from the host program to the engine.
2001-05-23 15:26:42 +02:00
* This corresponds to a blr_receive or blr_select statement.
*
**************************************/
SET_TDBB(tdbb);
DEV_BLKCHK(request, type_req);
2004-10-03 14:10:19 +02:00
if (--tdbb->tdbb_quantum < 0)
JRD_reschedule(tdbb, 0, true);
2001-05-23 15:26:42 +02:00
if (!(request->req_flags & req_active))
ERR_post(Arg::Gds(isc_req_sync));
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
jrd_nod* message;
jrd_nod* node;
if (request->req_operation != jrd_req::req_receive)
ERR_post(Arg::Gds(isc_req_sync));
node = request->req_message;
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
jrd_tra* transaction = request->req_transaction;
const bool external = request->req_procedure && request->req_procedure->getExternal();
2001-05-23 15:26:42 +02:00
if (external)
2009-01-14 09:22:32 +01:00
{
fb_assert(request->req_top_node->nod_type == nod_list);
message = request->req_top_node->nod_arg[e_extproc_input_message]; // input message
fb_assert(message->nod_type == nod_message);
}
else
{
switch (node->nod_type)
2003-12-31 06:36:12 +01:00
{
case nod_message:
message = node;
break;
case nod_select:
2009-01-14 09:22:32 +01:00
{
jrd_nod** ptr = node->nod_arg;
for (const jrd_nod* const* const end = ptr + node->nod_count; ptr < end; ptr++)
{
message = (*ptr)->nod_arg[e_send_message];
2009-11-23 06:24:29 +01:00
if ((USHORT)(IPTR) message->nod_arg[e_msg_number] == msg)
{
request->req_next = *ptr;
break;
}
2009-01-14 09:22:32 +01:00
}
2001-05-23 15:26:42 +02:00
}
break;
default:
2009-11-22 04:56:20 +01:00
BUGCHECK(167); // msg 167 invalid SEND request
2001-05-23 15:26:42 +02:00
}
2009-01-14 09:22:32 +01:00
}
2001-05-23 15:26:42 +02:00
2004-03-30 06:10:52 +02:00
const Format* format = (Format*) message->nod_arg[e_msg_format];
2001-05-23 15:26:42 +02:00
if (msg != (USHORT)(IPTR) message->nod_arg[e_msg_number])
ERR_post(Arg::Gds(isc_req_sync));
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
if (length != format->fmt_length) {
2008-12-20 09:12:19 +01:00
ERR_post(Arg::Gds(isc_port_len) << Arg::Num(length) << Arg::Num(format->fmt_length));
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
2008-01-16 09:54:50 +01:00
memcpy((SCHAR*) request + message->nod_impure, buffer, length);
2001-05-23 15:26:42 +02:00
2005-05-28 00:45:31 +02:00
for (USHORT i = 0; i < format->fmt_count; ++i)
{
const DSC* desc = &format->fmt_desc[i];
// ASF: I'll not test for dtype_cstring because usage is only internal
if (desc->dsc_dtype == dtype_text || desc->dsc_dtype == dtype_varying)
{
2005-12-02 17:48:47 +01:00
const UCHAR* p = (UCHAR*)request + message->nod_impure + (ULONG)(IPTR)desc->dsc_address;
2005-05-28 00:45:31 +02:00
USHORT len;
switch (desc->dsc_dtype)
{
case dtype_text:
len = desc->dsc_length;
break;
case dtype_varying:
len = reinterpret_cast<const vary*>(p)->vary_length;
p += sizeof(USHORT);
break;
}
CharSet* charSet = INTL_charset_lookup(tdbb, DSC_GET_CHARSET(desc));
2005-05-28 00:45:31 +02:00
if (!charSet->wellFormed(len, p))
ERR_post(Arg::Gds(isc_malformed_string));
2005-05-28 00:45:31 +02:00
}
else if (desc->isBlob())
{
2007-05-24 02:39:38 +02:00
if (desc->getCharSet() != CS_NONE && desc->getCharSet() != CS_BINARY)
{
const Jrd::bid* bid = (Jrd::bid*) ((UCHAR*)request +
message->nod_impure + (ULONG)(IPTR)desc->dsc_address);
if (!bid->isEmpty())
{
AutoBlb blob(tdbb, BLB_open(tdbb, transaction/*tdbb->getTransaction()*/, bid));
BLB_check_well_formed(tdbb, desc, blob.getBlb());
}
}
}
2005-05-28 00:45:31 +02:00
}
if (!external)
execute_looper(tdbb, request, transaction, jrd_req::req_proceed);
2001-05-23 15:26:42 +02:00
}
void EXE_start(thread_db* tdbb, jrd_req* request, jrd_tra* transaction)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X E _ s t a r t
*
**************************************
*
* Functional description
* Start an execution running.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
BLKCHK(request, type_req);
BLKCHK(transaction, type_tra);
if (request->req_flags & req_active)
2008-08-30 05:14:08 +02:00
ERR_post(Arg::Gds(isc_req_sync) << Arg::Gds(isc_reqinuse));
2001-05-23 15:26:42 +02:00
if (transaction->tra_flags & TRA_prepared)
ERR_post(Arg::Gds(isc_req_no_trans));
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
/* Post resources to transaction block. In particular, the interest locks
on relations/indices are copied to the transaction, which is very
important for (short-lived) dynamically compiled requests. This will
provide transaction stability by preventing a relation from being
dropped after it has been referenced from an active transaction. */
2001-05-23 15:26:42 +02:00
TRA_post_resources(tdbb, transaction, request->req_resources);
Lock* lock = transaction->tra_cancel_lock;
if (lock && lock->lck_logical == LCK_none)
LCK_lock(tdbb, lock, LCK_SR, LCK_WAIT);
TRA_attach_request(transaction, request);
2001-05-23 15:26:42 +02:00
request->req_flags &= REQ_FLAGS_INIT_MASK;
request->req_flags |= req_active;
request->req_flags &= ~req_reserved;
request->req_operation = jrd_req::req_evaluate;
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// set up to count records affected by request
2001-05-23 15:26:42 +02:00
request->req_records_selected = 0;
request->req_records_updated = 0;
request->req_records_inserted = 0;
request->req_records_deleted = 0;
request->req_records_affected.clear();
2009-11-22 04:56:20 +01:00
// CVC: set up to count virtual operations on SQL views.
2002-06-30 11:58:20 +02:00
request->req_view_flags = 0;
request->req_top_view_store = NULL;
request->req_top_view_modify = NULL;
request->req_top_view_erase = NULL;
// Store request start time for timestamp work
request->req_timestamp.validate();
2001-05-23 15:26:42 +02:00
// Set all invariants to not computed.
jrd_nod **ptr, **end;
2008-12-20 09:12:19 +01:00
for (ptr = request->req_invariants.begin(), end = request->req_invariants.end(); ptr < end; ++ptr)
{
impure_value* impure = (impure_value*) ((SCHAR *) request + (*ptr)->nod_impure);
impure->vlu_flags = 0;
2001-05-23 15:26:42 +02:00
}
2009-02-05 21:41:47 +01:00
if (request->req_sql_text)
{
tdbb->bumpStats(RuntimeStatistics::STMT_EXECUTES);
}
// Start a save point if not in middle of one
2002-11-03 18:29:51 +01:00
if (transaction && (transaction != dbb->dbb_sys_trans)) {
VIO_start_save_point(tdbb, transaction);
}
request->req_src_line = 0;
request->req_src_column = 0;
2009-04-28 15:08:04 +02:00
looper_seh(tdbb, request); //, request->req_top_node);
2001-05-23 15:26:42 +02:00
// If any requested modify/delete/insert ops have completed, forget them
2001-05-23 15:26:42 +02:00
2008-12-20 09:12:19 +01:00
if (transaction && (transaction != dbb->dbb_sys_trans) && transaction->tra_save_point &&
!(transaction->tra_save_point->sav_flags & SAV_user) &&
!transaction->tra_save_point->sav_verb_count)
{
// Forget about any undo for this verb
2001-05-23 15:26:42 +02:00
2002-11-03 18:29:51 +01:00
VIO_verb_cleanup(tdbb, transaction);
}
2001-05-23 15:26:42 +02:00
}
void EXE_unwind(thread_db* tdbb, jrd_req* request)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X E _ u n w i n d
*
**************************************
*
* Functional description
2005-01-21 11:45:01 +01:00
* Unwind a request, maybe active, maybe not. This is particularly
2001-05-23 15:26:42 +02:00
* simple since nothing really needs to be done.
*
**************************************/
DEV_BLKCHK(request, type_req);
SET_TDBB(tdbb);
2008-12-05 02:20:14 +01:00
if (request->req_flags & req_active)
{
2008-12-05 02:20:14 +01:00
if (request->req_fors.getCount() || request->req_exec_sta.getCount() || request->req_ext_stmt)
{
Jrd::ContextPoolHolder context(tdbb, request->req_pool);
jrd_req* old_request = tdbb->getRequest();
jrd_tra* old_transaction = tdbb->getTransaction();
try {
tdbb->setRequest(request);
tdbb->setTransaction(request->req_transaction);
2003-12-31 06:36:12 +01:00
for (Cursor* const* ptr = request->req_fors.begin();
2009-12-10 01:02:01 +01:00
ptr < request->req_fors.end(); ptr++)
{
(*ptr)->close(tdbb);
}
2006-03-10 18:46:18 +01:00
for (size_t i = 0; i < request->req_exec_sta.getCount(); ++i)
{
jrd_nod* node = request->req_exec_sta[i];
2008-12-20 09:12:19 +01:00
ExecuteStatement* impure =(ExecuteStatement*) ((char*) request + node->nod_impure);
impure->close(tdbb);
}
while (request->req_ext_stmt)
{
request->req_ext_stmt->close(tdbb);
}
}
2007-12-05 01:03:15 +01:00
catch (const Firebird::Exception&)
2006-03-10 18:46:18 +01:00
{
tdbb->setRequest(old_request);
tdbb->setTransaction(old_transaction);
throw;
2006-03-10 18:46:18 +01:00
}
tdbb->setRequest(old_request);
tdbb->setTransaction(old_transaction);
2006-03-10 18:46:18 +01:00
}
2001-05-23 15:26:42 +02:00
release_blobs(tdbb, request);
}
delete request->resultSet;
request->resultSet = NULL;
2001-05-23 15:26:42 +02:00
if (request->req_proc_sav_point && (request->req_flags & req_proc_fetch))
release_proc_save_points(request);
TRA_detach_request(request);
2001-05-23 15:26:42 +02:00
request->req_flags &= ~(req_active | req_proc_fetch | req_reserved);
request->req_flags |= req_abort | req_stall;
request->req_timestamp.invalidate();
2009-02-01 23:10:12 +01:00
request->req_proc_inputs = NULL;
request->req_proc_caller = NULL;
2001-05-23 15:26:42 +02:00
}
2009-11-22 04:56:20 +01:00
// CVC: Moved to its own routine, originally in store().
static void cleanup_rpb(thread_db* tdbb, record_param* rpb)
2002-06-30 11:58:20 +02:00
{
/**************************************
*
* c l e a n u p _ r p b
*
**************************************
*
* Functional description
* Perform cleaning of rpb, zeroing unassigned fields and
* the impure tail of varying fields that we don't want to carry
* when the RLE algorithm is applied.
*
**************************************/
Record* record = rpb->rpb_record;
2004-03-30 06:10:52 +02:00
const Format* format = record->rec_format;
2002-06-30 11:58:20 +02:00
2009-11-22 04:56:20 +01:00
SET_TDBB(tdbb); // Is it necessary?
2002-06-30 11:58:20 +02:00
2009-11-22 04:56:20 +01:00
/*
2002-06-30 11:58:20 +02:00
Starting from the format, walk through its
array of descriptors. If the descriptor has
no address, its a computed field and we shouldn't
try to fix it. Get a pointer to the actual data
and see if that field is null by indexing into
the null flags between the record header and the
record data.
2009-11-22 04:56:20 +01:00
*/
2002-06-30 11:58:20 +02:00
2003-12-31 06:36:12 +01:00
for (USHORT n = 0; n < format->fmt_count; n++)
2002-06-30 11:58:20 +02:00
{
2003-12-31 06:36:12 +01:00
const dsc* desc = &format->fmt_desc[n];
2002-06-30 11:58:20 +02:00
if (!desc->dsc_address)
continue;
2009-01-14 10:19:00 +01:00
UCHAR* const p = record->rec_data + (IPTR) desc->dsc_address;
2002-06-30 11:58:20 +02:00
if (TEST_NULL(record, n))
{
2003-12-31 06:36:12 +01:00
USHORT length = desc->dsc_length;
if (length) {
memset(p, 0, length);
2003-12-31 06:36:12 +01:00
}
2002-06-30 11:58:20 +02:00
}
else if (desc->dsc_dtype == dtype_varying)
{
vary* varying = reinterpret_cast<vary*>(p);
2003-12-31 06:36:12 +01:00
USHORT length = desc->dsc_length - sizeof(USHORT);
if (length > varying->vary_length)
2002-06-30 11:58:20 +02:00
{
2009-01-14 10:19:00 +01:00
char* trail = varying->vary_string + varying->vary_length;
length -= varying->vary_length;
2009-01-14 10:19:00 +01:00
memset(trail, 0, length);
2002-06-30 11:58:20 +02:00
}
}
}
}
inline void PreModifyEraseTriggers(thread_db* tdbb,
2008-12-05 02:20:14 +01:00
trig_vec** trigs,
SSHORT which_trig,
record_param* rpb,
record_param* rec,
jrd_req::req_ta op)
{
/******************************************************
*
* P r e M o d i f y E r a s e T r i g g e r s
*
******************************************************
*
* Functional description
2008-12-05 02:20:14 +01:00
* Perform operation's pre-triggers,
* storing active rpb in chain.
*
******************************************************/
if (!tdbb->getTransaction()->tra_rpblist)
{
2008-12-05 02:20:14 +01:00
tdbb->getTransaction()->tra_rpblist =
2008-12-20 09:12:19 +01:00
FB_NEW(*tdbb->getTransaction()->tra_pool) traRpbList(*tdbb->getTransaction()->tra_pool);
}
2008-12-20 09:12:19 +01:00
const int rpblevel = tdbb->getTransaction()->tra_rpblist->PushRpb(rpb);
if (*trigs && which_trig != POST_TRIG)
{
try
{
execute_triggers(tdbb, trigs, rpb, rec, op, PRE_TRIG);
}
catch (const Exception&)
{
tdbb->getTransaction()->tra_rpblist->PopRpb(rpb, rpblevel);
throw;
}
}
tdbb->getTransaction()->tra_rpblist->PopRpb(rpb, rpblevel);
}
2001-05-23 15:26:42 +02:00
static jrd_nod* erase(thread_db* tdbb, jrd_nod* node, SSHORT which_trig)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e r a s e
*
**************************************
*
* Functional description
* Perform erase operation.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
BLKCHK(node, type_nod);
jrd_req* request = tdbb->getRequest();
2003-12-31 06:36:12 +01:00
jrd_tra* transaction = request->req_transaction;
record_param* rpb = &request->req_rpb[(int) (IPTR) node->nod_arg[e_erase_stream]];
2003-12-31 06:36:12 +01:00
jrd_rel* relation = rpb->rpb_relation;
2001-05-23 15:26:42 +02:00
2008-12-20 09:12:19 +01:00
if (rpb->rpb_number.isBof() || (!relation->rel_view_rse && !rpb->rpb_number.isValid()))
{
ERR_post(Arg::Gds(isc_no_cur_rec));
}
2009-01-14 09:22:32 +01:00
switch (request->req_operation)
{
case jrd_req::req_evaluate:
2003-12-31 06:36:12 +01:00
{
2008-12-20 09:12:19 +01:00
request->req_records_affected.bumpModified(false);
if (!node->nod_arg[e_erase_statement])
break;
const Format* format = MET_current(tdbb, rpb->rpb_relation);
Record* record = VIO_record(tdbb, rpb, format, tdbb->getDefaultPool());
rpb->rpb_address = record->rec_data;
rpb->rpb_length = format->fmt_length;
rpb->rpb_format_number = format->fmt_version;
return node->nod_arg[e_erase_statement];
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
case jrd_req::req_return:
2001-05-23 15:26:42 +02:00
break;
default:
return node->nod_parent;
}
request->req_operation = jrd_req::req_return;
2008-01-16 09:54:50 +01:00
RLCK_reserve_relation(tdbb, transaction, relation, true);
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// If the stream was sorted, the various fields in the rpb are
// probably junk. Just to make sure that everything is cool,
// refetch and release the record.
2001-05-23 15:26:42 +02:00
2009-11-23 06:24:29 +01:00
if (rpb->rpb_stream_flags & RPB_s_refetch)
{
VIO_refetch_record(tdbb, rpb, transaction);
2001-05-23 15:26:42 +02:00
rpb->rpb_stream_flags &= ~RPB_s_refetch;
}
if (transaction != dbb->dbb_sys_trans)
++transaction->tra_save_point->sav_verb_count;
2009-11-22 04:56:20 +01:00
// Handle pre-operation trigger
2008-12-20 09:12:19 +01:00
PreModifyEraseTriggers(tdbb, &relation->rel_pre_erase, which_trig, rpb, NULL,
jrd_req::req_trigger_delete);
2001-05-23 15:26:42 +02:00
if (relation->rel_file) {
EXT_erase(rpb, transaction);
}
else if (relation->isVirtual()) {
VirtualTable::erase(tdbb, rpb);
}
else if (!relation->rel_view_rse) {
VIO_erase(tdbb, rpb, transaction);
}
2009-11-22 04:56:20 +01:00
// Handle post operation trigger
if (relation->rel_post_erase && which_trig != PRE_TRIG)
2001-12-24 03:51:06 +01:00
{
execute_triggers(tdbb, &relation->rel_post_erase, rpb, NULL, jrd_req::req_trigger_delete,
POST_TRIG);
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// call IDX_erase (which checks constraints) after all post erase triggers
// have fired. This is required for cascading referential integrity, which
// can be implemented as post_erase triggers
2001-05-23 15:26:42 +02:00
2008-12-20 09:12:19 +01:00
if (!relation->rel_file && !relation->rel_view_rse && !relation->isVirtual())
2001-12-24 03:51:06 +01:00
{
jrd_rel* bad_relation = 0;
2001-05-23 15:26:42 +02:00
USHORT bad_index;
2009-04-03 12:07:55 +02:00
const idx_e error_code = IDX_erase(tdbb, rpb, transaction, &bad_relation, &bad_index);
2001-12-24 03:51:06 +01:00
if (error_code) {
ERR_duplicate_error(error_code, bad_relation, bad_index);
}
2001-05-23 15:26:42 +02:00
}
2009-11-22 04:56:20 +01:00
// CVC: Increment the counter only if we called VIO/EXT_erase() and
// we were successful.
2009-11-23 06:24:29 +01:00
if (!(request->req_view_flags & req_first_erase_return))
{
2002-06-30 11:58:20 +02:00
request->req_view_flags |= req_first_erase_return;
if (relation->rel_view_rse) {
request->req_top_view_erase = relation;
}
}
2009-11-23 06:24:29 +01:00
if (relation == request->req_top_view_erase)
{
2002-06-30 11:58:20 +02:00
if (which_trig == ALL_TRIGS || which_trig == POST_TRIG) {
request->req_records_deleted++;
2005-06-24 14:56:34 +02:00
request->req_records_affected.bumpModified(true);
2002-06-30 11:58:20 +02:00
}
}
2009-11-23 06:24:29 +01:00
else if (relation->rel_file || !relation->rel_view_rse)
{
2002-06-30 11:58:20 +02:00
request->req_records_deleted++;
2005-06-24 14:56:34 +02:00
request->req_records_affected.bumpModified(true);
2002-06-30 11:58:20 +02:00
}
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
if (transaction != dbb->dbb_sys_trans) {
2001-05-23 15:26:42 +02:00
--transaction->tra_save_point->sav_verb_count;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
rpb->rpb_number.setValid(false);
2001-05-23 15:26:42 +02:00
return node->nod_parent;
}
2008-12-20 09:12:19 +01:00
static void execute_looper(thread_db* tdbb,
jrd_req* request,
2008-12-20 09:12:19 +01:00
jrd_tra* transaction, jrd_req::req_s next_state)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x e c u t e _ l o o p e r
*
**************************************
*
* Functional description
* Wrapper around looper. This will execute
* looper with the save point mechanism.
*
**************************************/
DEV_BLKCHK(request, type_req);
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// Start a save point
2001-05-23 15:26:42 +02:00
if (!(request->req_flags & req_proc_fetch) && request->req_transaction)
2008-03-04 07:02:32 +01:00
{
2001-05-23 15:26:42 +02:00
if (transaction && (transaction != dbb->dbb_sys_trans))
2002-11-03 18:29:51 +01:00
VIO_start_save_point(tdbb, transaction);
2008-03-04 07:02:32 +01:00
}
2001-05-23 15:26:42 +02:00
request->req_flags &= ~req_stall;
request->req_operation = next_state;
EXE_looper(tdbb, request, request->req_next);
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// If any requested modify/delete/insert ops have completed, forget them
2001-05-23 15:26:42 +02:00
2009-01-14 10:19:00 +01:00
if (!(request->req_flags & req_proc_fetch) && request->req_transaction)
{
2008-12-20 09:12:19 +01:00
if (transaction && (transaction != dbb->dbb_sys_trans) && transaction->tra_save_point &&
2003-12-31 06:36:12 +01:00
!transaction->tra_save_point->sav_verb_count)
{
2009-11-22 04:56:20 +01:00
// Forget about any undo for this verb
2001-05-23 15:26:42 +02:00
VIO_verb_cleanup(tdbb, transaction);
}
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
}
static void exec_sql(thread_db* tdbb, jrd_req* request, DSC* dsc)
2002-04-04 15:53:20 +02:00
{
/**************************************
*
* e x e c _ s q l
*
**************************************
*
* Functional description
* Execute a string as SQL operator.
*
**************************************/
SET_TDBB(tdbb);
ExecuteStatement::execute(tdbb, request, dsc);
2002-04-04 15:53:20 +02:00
}
static void execute_procedure(thread_db* tdbb, jrd_nod* node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x e c u t e _ p r o c e d u r e
*
**************************************
*
* Functional description
* Execute a stored procedure. Begin by
* assigning the input parameters. End
* by assigning the output parameters.
*
**************************************/
SET_TDBB(tdbb);
BLKCHK(node, type_nod);
jrd_req* request = tdbb->getRequest();
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
jrd_nod* temp = node->nod_arg[e_esp_inputs];
2009-11-23 06:24:29 +01:00
if (temp)
{
2004-01-13 10:52:19 +01:00
jrd_nod** ptr;
jrd_nod** end;
2008-12-20 09:12:19 +01:00
for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end; ptr++)
2003-12-31 06:36:12 +01:00
{
2001-05-23 15:26:42 +02:00
EXE_assignment(tdbb, *ptr);
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
}
const jrd_prc* procedure = (jrd_prc*) node->nod_arg[e_esp_procedure];
2009-01-14 10:19:00 +01:00
USHORT in_msg_length = 0;
2009-01-15 04:46:48 +01:00
UCHAR* in_msg = NULL;
2003-12-31 06:36:12 +01:00
jrd_nod* in_message = node->nod_arg[e_esp_in_msg];
2009-11-23 06:24:29 +01:00
if (in_message)
{
2004-03-30 06:10:52 +02:00
const Format* format = (Format*) in_message->nod_arg[e_msg_format];
2001-05-23 15:26:42 +02:00
in_msg_length = format->fmt_length;
2008-03-10 09:31:35 +01:00
in_msg = (UCHAR*) request + in_message->nod_impure;
2001-05-23 15:26:42 +02:00
}
2003-12-31 06:36:12 +01:00
const Format* format = NULL;
2009-01-14 10:19:00 +01:00
USHORT out_msg_length = 0;
2009-01-15 04:46:48 +01:00
UCHAR* out_msg = NULL;
2003-12-31 06:36:12 +01:00
jrd_nod* out_message = node->nod_arg[e_esp_out_msg];
2009-11-23 06:24:29 +01:00
if (out_message)
{
format = (Format*) out_message->nod_arg[e_msg_format];
2001-05-23 15:26:42 +02:00
out_msg_length = format->fmt_length;
2008-03-10 09:31:35 +01:00
out_msg = (UCHAR*) request + out_message->nod_impure;
2001-05-23 15:26:42 +02:00
}
jrd_req* proc_request = EXE_find_request(tdbb, procedure->getRequest(), false);
2001-05-23 15:26:42 +02:00
2009-02-01 23:10:12 +01:00
// trace procedure execution start
TraceProcExecute trace(tdbb, proc_request, request, node->nod_arg[e_esp_inputs]);
2008-03-10 09:31:35 +01:00
Firebird::Array<UCHAR> temp_buffer;
2008-12-05 02:20:14 +01:00
2009-11-23 06:24:29 +01:00
if (!out_message)
{
format = (Format*) procedure->prc_output_msg->nod_arg[e_msg_format];
2001-05-23 15:26:42 +02:00
out_msg_length = format->fmt_length;
out_msg = temp_buffer.getBuffer(out_msg_length + FB_DOUBLE_ALIGN - 1);
out_msg = (UCHAR*) FB_ALIGN((U_IPTR) out_msg, FB_DOUBLE_ALIGN);
2001-05-23 15:26:42 +02:00
}
2009-11-22 04:56:20 +01:00
// Catch errors so we can unwind cleanly
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
try {
// Save the old pool
Jrd::ContextPoolHolder context(tdbb, proc_request->req_pool);
2001-05-23 15:26:42 +02:00
jrd_tra* transaction = request->req_transaction;
const SLONG save_point_number = transaction->tra_save_point ?
transaction->tra_save_point->sav_number : 0;
2001-05-23 15:26:42 +02:00
proc_request->req_timestamp = request->req_timestamp;
EXE_start(tdbb, proc_request, transaction);
if (in_message) {
2009-10-30 11:50:59 +01:00
EXE_send(tdbb, proc_request, 0, in_msg_length, in_msg);
}
2001-05-23 15:26:42 +02:00
2009-10-30 11:50:59 +01:00
EXE_receive(tdbb, proc_request, 1, out_msg_length, out_msg);
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// Clean up all savepoints started during execution of the procedure
2001-05-23 15:26:42 +02:00
2009-11-23 06:24:29 +01:00
if (transaction != tdbb->getDatabase()->dbb_sys_trans)
{
for (const Savepoint* save_point = transaction->tra_save_point;
save_point && save_point_number < save_point->sav_number;
save_point = transaction->tra_save_point)
{
VIO_verb_cleanup(tdbb, transaction);
}
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
2001-12-24 03:51:06 +01:00
} // try
2009-04-04 18:39:31 +02:00
catch (const Firebird::Exception& ex)
2009-02-01 23:10:12 +01:00
{
const bool no_priv = (ex.stuff_exception(tdbb->tdbb_status_vector) == isc_no_priv);
trace.finish(false, no_priv ? res_unauthorized : res_failed);
tdbb->setRequest(request);
2001-12-24 03:51:06 +01:00
EXE_unwind(tdbb, proc_request);
proc_request->req_attachment = NULL;
2001-12-24 03:51:06 +01:00
proc_request->req_flags &= ~(req_in_use | req_proc_fetch);
proc_request->req_timestamp.invalidate();
throw;
2001-12-24 03:51:06 +01:00
}
2009-02-01 23:10:12 +01:00
// trace procedure execution finish
trace.finish(false, res_successful);
2001-05-23 15:26:42 +02:00
EXE_unwind(tdbb, proc_request);
tdbb->setRequest(request);
2001-12-28 07:31:38 +01:00
temp = node->nod_arg[e_esp_outputs];
2009-11-23 06:24:29 +01:00
if (temp)
{
2004-01-13 10:52:19 +01:00
jrd_nod** ptr;
jrd_nod** end;
2008-12-20 09:12:19 +01:00
for (ptr = temp->nod_arg, end = ptr + temp->nod_count; ptr < end; ptr++)
2003-12-31 06:36:12 +01:00
{
2001-05-23 15:26:42 +02:00
EXE_assignment(tdbb, *ptr);
2003-12-31 06:36:12 +01:00
}
2001-05-23 15:26:42 +02:00
}
2001-12-28 07:31:38 +01:00
2001-05-23 15:26:42 +02:00
proc_request->req_attachment = NULL;
proc_request->req_flags &= ~(req_in_use | req_proc_fetch);
proc_request->req_timestamp.invalidate();
2001-05-23 15:26:42 +02:00
}
static jrd_nod* execute_statement(thread_db* tdbb, jrd_req* request, jrd_nod* node)
{
SET_TDBB(tdbb);
BLKCHK(node, type_nod);
EDS::Statement** stmt_ptr = (EDS::Statement**) ((char*) request + node->nod_impure);
EDS::Statement* stmt = *stmt_ptr;
2008-04-12 13:56:32 +02:00
const int inputs = (SSHORT)(IPTR) node->nod_arg[node->nod_count + e_exec_stmt_extra_inputs];
const int outputs = (SSHORT)(IPTR) node->nod_arg[node->nod_count + e_exec_stmt_extra_outputs];
2008-12-05 02:20:14 +01:00
jrd_nod** node_inputs = inputs ?
node->nod_arg + e_exec_stmt_fixed_count + e_exec_stmt_extra_inputs : NULL;
2008-12-05 02:20:14 +01:00
jrd_nod** node_outputs = outputs ?
node->nod_arg + e_exec_stmt_fixed_count + inputs : NULL;
2008-12-05 02:20:14 +01:00
2008-04-10 04:35:30 +02:00
jrd_nod* node_proc_block = node->nod_arg[e_exec_stmt_proc_block];
if (request->req_operation == jrd_req::req_evaluate)
{
fb_assert(*stmt_ptr == 0);
2008-12-05 02:20:14 +01:00
const EDS::ParamNames* inputs_names =
(EDS::ParamNames*) node->nod_arg[node->nod_count + e_exec_stmt_extra_input_names];
2008-12-05 02:20:14 +01:00
const jrd_nod* tra_node = node->nod_arg[node->nod_count + e_exec_stmt_extra_tran];
const EDS::TraScope tra_scope = tra_node ? (EDS::TraScope)(IPTR) tra_node : EDS::traCommon;
const jrd_nod* privs_node = node->nod_arg[node->nod_count + e_exec_stmt_extra_privs];
const bool caller_privs = (privs_node != NULL);
Firebird::string sSql;
get_string(tdbb, request, node->nod_arg[e_exec_stmt_stmt_sql], sSql);
Firebird::string sDataSrc;
get_string(tdbb, request, node->nod_arg[e_exec_stmt_data_src], sDataSrc);
Firebird::string sUser;
get_string(tdbb, request, node->nod_arg[e_exec_stmt_user], sUser);
Firebird::string sPwd;
get_string(tdbb, request, node->nod_arg[e_exec_stmt_password], sPwd);
Firebird::string sRole;
get_string(tdbb, request, node->nod_arg[e_exec_stmt_role], sRole);
EDS::Connection* conn = EDS::Manager::getConnection(tdbb, sDataSrc, sUser, sPwd, sRole, tra_scope);
stmt = conn->createStatement(sSql);
EDS::Transaction* tran = EDS::Transaction::getTransaction(tdbb, stmt->getConnection(), tra_scope);
stmt->bindToRequest(request, stmt_ptr);
stmt->setCallerPrivileges(caller_privs);
2008-04-10 04:35:30 +02:00
const Firebird::string* const * inp_names = inputs_names ? inputs_names->begin() : NULL;
stmt->prepare(tdbb, tran, sSql, inputs_names != NULL);
if (stmt->isSelectable())
stmt->open(tdbb, tran, inputs, inp_names, node_inputs, !node_proc_block);
else
stmt->execute(tdbb, tran, inputs, inp_names, node_inputs, outputs, node_outputs);
request->req_operation = jrd_req::req_return;
} // jrd_req::req_evaluate
2009-01-14 10:19:00 +01:00
if (request->req_operation == jrd_req::req_return || request->req_operation == jrd_req::req_sync)
{
fb_assert(stmt);
if (stmt->isSelectable())
{
if (stmt->fetch(tdbb, outputs, node_outputs))
{
request->req_operation = jrd_req::req_evaluate;
return node_proc_block;
}
request->req_operation = jrd_req::req_return;
}
}
2008-12-05 02:20:14 +01:00
if (request->req_operation == jrd_req::req_unwind)
{
jrd_nod* parent = node->nod_parent;
if (parent && parent->nod_type == nod_label &&
(request->req_label == (USHORT)(IPTR) parent->nod_arg[e_lbl_label]) &&
(request->req_flags & req_continue_loop))
{
request->req_flags &= ~req_continue_loop;
request->req_operation = jrd_req::req_sync;
return node;
}
}
if (stmt)
stmt->close(tdbb);
return node->nod_parent;
}
static void execute_triggers(thread_db* tdbb,
trig_vec** triggers,
record_param* old_rpb,
record_param* new_rpb,
2008-12-20 09:12:19 +01:00
jrd_req::req_ta trigger_action, SSHORT which_trig)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* e x e c u t e _ t r i g g e r s
*
**************************************
*
* Functional description
* Execute group of triggers. Return pointer to failing trigger
* if any blow up.
*
**************************************/
if (!*triggers)
return;
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
2009-01-14 10:19:00 +01:00
jrd_tra* transaction =
(tdbb->getRequest() ? tdbb->getRequest()->req_transaction : tdbb->getTransaction());
trig_vec* vector = *triggers;
Record* const old_rec = old_rpb ? old_rpb->rpb_record : NULL;
Record* const new_rec = new_rpb ? new_rpb->rpb_record : NULL;
2001-05-23 15:26:42 +02:00
Record* null_rec = NULL;
if (!old_rec && !new_rec)
{
// this is a database trigger
}
else if (!old_rec || !new_rec)
{
const Record* record = old_rec ? old_rec : new_rec;
fb_assert(record && record->rec_format);
// copy the record
2008-12-20 09:12:19 +01:00
null_rec = FB_NEW_RPT(record->rec_pool, record->rec_length) Record(record->rec_pool);
null_rec->rec_length = record->rec_length;
null_rec->rec_format = record->rec_format;
// zero the record buffer
memset(null_rec->rec_data, 0, record->rec_length);
// initialize all fields to missing
const SSHORT n = (record->rec_format->fmt_count + 7) >> 3;
memset(null_rec->rec_data, 0xFF, n);
}
const Firebird::TimeStamp timestamp(Firebird::TimeStamp::getCurrentTimeStamp());
2001-12-28 07:31:38 +01:00
try
{
for (trig_vec::iterator ptr = vector->begin(); ptr != vector->end(); ++ptr)
2001-12-28 07:31:38 +01:00
{
ptr->compile(tdbb);
if (ptr->extTrigger)
{
//// TODO: trace stuff
ptr->extTrigger->execute(tdbb, (Firebird::ExternalTrigger::Action) trigger_action,
old_rpb, new_rpb);
}
else
{
jrd_req* trigger = EXE_find_request(tdbb, ptr->request, false);
trigger->req_rpb[0].rpb_record = old_rec ? old_rec : null_rec;
trigger->req_rpb[1].rpb_record = new_rec ? new_rec : null_rec;
if (old_rec && trigger_action != jrd_req::req_trigger_insert)
{
trigger->req_rpb[0].rpb_number = old_rpb->rpb_number;
trigger->req_rpb[0].rpb_number.setValid(true);
}
else
trigger->req_rpb[0].rpb_number.setValid(false);
if (new_rec && !(which_trig == PRE_TRIG && trigger_action == jrd_req::req_trigger_insert))
{
if (which_trig == PRE_TRIG && trigger_action == jrd_req::req_trigger_update)
new_rpb->rpb_number = old_rpb->rpb_number;
trigger->req_rpb[1].rpb_number = new_rpb->rpb_number;
trigger->req_rpb[1].rpb_number.setValid(true);
}
else
trigger->req_rpb[1].rpb_number.setValid(false);
2009-02-02 04:35:52 +01:00
if (tdbb->getRequest())
trigger->req_timestamp = tdbb->getRequest()->req_timestamp;
else
trigger->req_timestamp = timestamp;
2009-02-01 23:10:12 +01:00
trigger->req_trigger_action = trigger_action;
2009-02-01 23:10:12 +01:00
TraceTrigExecute trace(tdbb, trigger, which_trig);
2009-02-01 23:10:12 +01:00
EXE_start(tdbb, trigger, transaction);
2009-02-01 23:10:12 +01:00
trigger->req_attachment = NULL;
trigger->req_flags &= ~req_in_use;
trigger->req_timestamp.invalidate();
const bool ok = (trigger->req_operation != jrd_req::req_unwind);
trace.finish(ok ? res_successful : res_failed);
if (!ok)
{
trigger_failure(tdbb, trigger);
break;
}
2001-12-28 07:31:38 +01:00
}
2001-05-23 15:26:42 +02:00
}
delete null_rec;
2001-12-28 07:31:38 +01:00
if (vector != *triggers) {
MET_release_triggers(tdbb, &vector);
2001-12-28 07:31:38 +01:00
}
}
2009-10-22 01:48:07 +02:00
catch (const Firebird::Exception&)
2001-12-28 07:31:38 +01:00
{
delete null_rec;
2001-12-24 03:51:06 +01:00
if (vector != *triggers) {
MET_release_triggers(tdbb, &vector);
2001-12-24 03:51:06 +01:00
}
throw;
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
}
static void get_string(thread_db* tdbb, jrd_req* request, jrd_nod* node, Firebird::string& str)
{
MoveBuffer buffer;
UCHAR* p = NULL;
SSHORT len = 0;
const dsc* dsc = node ? EVL_expr(tdbb, node) : NULL;
if (dsc && !(request->req_flags & req_null)) {
len = MOV_make_string2(tdbb, dsc, dsc->getTextType(), &p, buffer);
}
str = Firebird::string((char*) p, len);
str.trim();
}
static void stuff_stack_trace(const jrd_req* request)
{
Firebird::string sTrace;
bool isEmpty = true;
for (const jrd_req* req = request; req; req = req->req_caller)
{
Firebird::string name;
2009-11-23 06:24:29 +01:00
if (req->req_trg_name.length())
{
name = "At trigger '";
name += req->req_trg_name.c_str();
}
2009-11-23 06:24:29 +01:00
else if (req->req_procedure)
{
name = "At procedure '";
name += req->req_procedure->getName().toString().c_str();
}
else if (req->req_function)
{
name = "At function '";
name += req->req_function->getName().toString().c_str();
}
if (! name.isEmpty())
{
name.trim();
if (sTrace.length() + name.length() + 2 > MAX_STACK_TRACE)
break;
2009-11-23 06:24:29 +01:00
if (isEmpty)
{
isEmpty = false;
sTrace += name + "'";
}
else {
sTrace += "\n" + name + "'";
}
if (req->req_src_line)
{
Firebird::string src_info;
src_info.printf(" line: %u, col: %u", req->req_src_line, req->req_src_column);
if (sTrace.length() + src_info.length() > MAX_STACK_TRACE)
break;
2006-07-06 04:54:59 +02:00
sTrace += src_info;
}
}
}
if (!isEmpty)
ERR_post_nothrow(Arg::Gds(isc_stack_trace) << Arg::Str(sTrace));
}
2004-08-21 11:29:46 +02:00
jrd_nod* EXE_looper(thread_db* tdbb, jrd_req* request, jrd_nod* in_node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* E X E _ l o o p e r
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Cycle thru request execution tree. Return next node for
* execution on stall or request complete.
*
**************************************/
2001-12-28 07:31:38 +01:00
SSHORT which_erase_trig = 0;
SSHORT which_sto_trig = 0;
SSHORT which_mod_trig = 0;
jrd_nod* top_node = 0;
jrd_nod* prev_node;
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
jrd_tra* transaction = request->req_transaction;
if (!transaction) {
ERR_post(Arg::Gds(isc_req_no_trans));
2001-12-28 07:31:38 +01:00
}
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
BLKCHK(in_node, type_nod);
2001-12-28 07:31:38 +01:00
// Save the old pool and request to restore on exit
2008-01-29 11:11:52 +01:00
MemoryPool* old_pool = tdbb->getDefaultPool();
Jrd::ContextPoolHolder context(tdbb, request->req_pool);
2001-12-28 07:31:38 +01:00
jrd_req* old_request = tdbb->getRequest();
tdbb->setRequest(request);
jrd_tra* old_transaction = tdbb->getTransaction();
tdbb->setTransaction(transaction);
if (in_node->nod_type != nod_stmt_expr)
{
fb_assert(request->req_caller == NULL);
request->req_caller = old_request;
}
else
request->req_operation = jrd_req::req_evaluate;
2001-12-28 07:31:38 +01:00
2003-12-31 06:36:12 +01:00
const SLONG save_point_number = (transaction->tra_save_point) ?
2001-05-23 15:26:42 +02:00
transaction->tra_save_point->sav_number : 0;
jrd_nod* node = in_node;
2001-05-23 15:26:42 +02:00
2001-12-29 12:41:29 +01:00
// Catch errors so we can unwind cleanly
2001-05-23 15:26:42 +02:00
2003-06-28 11:32:46 +02:00
bool error_pending = false;
bool catch_disabled = false;
tdbb->tdbb_flags &= ~(TDBB_stack_trace_done | TDBB_sys_error);
2001-05-23 15:26:42 +02:00
2001-12-29 12:41:29 +01:00
// Execute stuff until we drop
2001-05-23 15:26:42 +02:00
bool runExternal = request->req_procedure && request->req_procedure->getExternal();
while (runExternal || (node && !(request->req_flags & req_stall)))
2001-12-29 12:41:29 +01:00
{
try {
if (request->req_procedure && request->req_procedure->getExternal())
{
if (runExternal)
runExternal = false;
else
break;
switch (request->req_operation)
{
case jrd_req::req_evaluate:
request->req_message = request->req_top_node->nod_arg[0]; // input message
request->req_flags |= req_stall;
request->req_operation = jrd_req::req_receive;
break;
case jrd_req::req_sync:
{
fb_assert(request->req_top_node->nod_type == nod_list);
jrd_nod* outMsgNode = request->req_top_node->nod_arg[e_extproc_output_message];
fb_assert(outMsgNode->nod_type == nod_message);
const Format* outFormat = (Format*) outMsgNode->nod_arg[e_msg_format];
UCHAR* outMsg = (UCHAR*) request + outMsgNode->nod_impure;
if (!request->resultSet)
{
// input message
jrd_nod* inMsgNode = request->req_top_node->nod_arg[e_extproc_input_message];
fb_assert(inMsgNode->nod_type == nod_message);
fb_assert(request->req_message == inMsgNode);
jrd_nod* list = request->req_top_node->nod_arg[e_extproc_input_assign];
fb_assert(list->nod_type == nod_asn_list ||
(list->nod_type == nod_list && list->nod_count == 0));
const Format* format = (Format*) inMsgNode->nod_arg[e_msg_format];
// clear the flags from the input message
USHORT* impure_flags = (USHORT*) ((UCHAR*) request +
(IPTR) request->req_message->nod_arg[e_msg_impure_flags]);
memset(impure_flags, 0, sizeof(USHORT) * format->fmt_count);
// clear the flags from the output message
impure_flags = (USHORT*) ((UCHAR*) request +
(IPTR) outMsgNode->nod_arg[e_msg_impure_flags]);
memset(impure_flags, 0, sizeof(USHORT) * outFormat->fmt_count);
// validate input parameters
for (unsigned i = 0; i < list->nod_count; ++i)
EXE_assignment(tdbb, list->nod_arg[i]);
if (!request->inputParams)
{
UCHAR* inMsg = (UCHAR*) request + request->req_message->nod_impure;
2009-10-22 01:48:07 +02:00
request->inputParams = FB_NEW(*request->req_pool) ValuesImpl(
*request->req_pool, format, inMsg,
request->req_procedure->prc_input_fields);
}
if (!request->outputParams)
{
2009-10-22 01:48:07 +02:00
request->outputParams = FB_NEW(*request->req_pool) ValuesImpl(
*request->req_pool, outFormat, outMsg,
request->req_procedure->prc_output_fields);
request->outputParams->setNull();
}
request->resultSet = request->req_procedure->getExternal()->open(tdbb,
request->inputParams, request->outputParams);
}
request->req_message = outMsgNode;
bool result = request->resultSet->fetch(tdbb);
// end flag
const dsc& eofDesc = outFormat->fmt_desc[outFormat->fmt_count - 1];
fb_assert(eofDesc.dsc_dtype == dtype_short);
*((SSHORT*) (UCHAR*) (outMsg + (IPTR) eofDesc.dsc_address)) = (SSHORT) result;
jrd_nod* list = request->req_top_node->nod_arg[e_extproc_output_assign];
fb_assert(list->nod_type == nod_asn_list ||
(list->nod_type == nod_list && list->nod_count == 0));
if (result)
{
// validate output parameters
for (unsigned i = 0; i < list->nod_count; ++i)
EXE_assignment(tdbb, list->nod_arg[i]);
}
break;
}
default:
fb_assert(false);
break;
}
goto end;
}
2008-12-20 09:12:19 +01:00
if (request->req_operation == jrd_req::req_evaluate && (--tdbb->tdbb_quantum < 0))
2004-01-13 10:52:19 +01:00
{
2003-12-22 11:00:59 +01:00
JRD_reschedule(tdbb, 0, true);
2004-01-13 10:52:19 +01:00
}
2001-05-23 15:26:42 +02:00
2009-01-14 10:19:00 +01:00
switch (node->nod_type)
{
2001-05-23 15:26:42 +02:00
case nod_asn_list:
2009-11-23 06:24:29 +01:00
if (request->req_operation == jrd_req::req_evaluate)
{
jrd_nod** ptr = node->nod_arg;
2008-12-20 09:12:19 +01:00
for (const jrd_nod* const* const end = ptr + node->nod_count; ptr < end; ptr++)
2004-01-13 10:52:19 +01:00
{
2001-05-23 15:26:42 +02:00
EXE_assignment(tdbb, *ptr);
2004-01-13 10:52:19 +01:00
}
request->req_operation = jrd_req::req_return;
2001-05-23 15:26:42 +02:00
}
node = node->nod_parent;
break;
case nod_assignment:
if (request->req_operation == jrd_req::req_evaluate)
2001-05-23 15:26:42 +02:00
EXE_assignment(tdbb, node);
node = node->nod_parent;
break;
case nod_dcl_variable:
{
impure_value* variable = (impure_value*) ((SCHAR*) request + node->nod_impure);
variable->vlu_desc = *(DSC*) (node->nod_arg + e_dcl_desc);
2001-05-23 15:26:42 +02:00
variable->vlu_desc.dsc_flags = 0;
variable->vlu_desc.dsc_address = (UCHAR*) &variable->vlu_misc;
if (variable->vlu_desc.dsc_dtype <= dtype_varying && !variable->vlu_string)
{
2009-01-14 10:19:00 +01:00
const USHORT len = variable->vlu_desc.dsc_length;
variable->vlu_string = FB_NEW_RPT(*tdbb->getDefaultPool(), len) VaryingString();
variable->vlu_string->str_length = len;
variable->vlu_desc.dsc_address = variable->vlu_string->str_data;
2001-05-23 15:26:42 +02:00
}
request->req_operation = jrd_req::req_return;
2001-05-23 15:26:42 +02:00
node = node->nod_parent;
}
break;
case nod_erase:
if (request->req_operation == jrd_req::req_unwind) {
node = node->nod_parent;
}
else if ((request->req_operation == jrd_req::req_return) &&
2008-12-20 09:12:19 +01:00
(node->nod_arg[e_erase_sub_erase]))
2001-12-28 07:31:38 +01:00
{
2009-11-23 06:24:29 +01:00
if (!top_node)
{
2001-05-23 15:26:42 +02:00
top_node = node;
which_erase_trig = PRE_TRIG;
}
prev_node = node;
node = erase(tdbb, node, which_erase_trig);
2009-11-23 06:24:29 +01:00
if (which_erase_trig == PRE_TRIG)
{
2001-05-23 15:26:42 +02:00
node = prev_node->nod_arg[e_erase_sub_erase];
node->nod_parent = prev_node;
}
2009-11-23 06:24:29 +01:00
if (top_node == prev_node && which_erase_trig == POST_TRIG)
{
2001-05-23 15:26:42 +02:00
top_node = NULL;
which_erase_trig = ALL_TRIGS;
}
else
request->req_operation = jrd_req::req_evaluate;
2001-05-23 15:26:42 +02:00
}
2009-11-23 06:24:29 +01:00
else
{
2001-05-23 15:26:42 +02:00
prev_node = node;
node = erase(tdbb, node, ALL_TRIGS);
2008-12-20 09:12:19 +01:00
if (!(prev_node->nod_arg[e_erase_sub_erase]) && which_erase_trig == PRE_TRIG)
2001-12-28 07:31:38 +01:00
{
2001-05-23 15:26:42 +02:00
which_erase_trig = POST_TRIG;
2001-12-28 07:31:38 +01:00
}
2001-05-23 15:26:42 +02:00
}
break;
2008-12-05 02:20:14 +01:00
2001-05-23 15:26:42 +02:00
case nod_exec_proc:
2009-11-23 06:24:29 +01:00
if (request->req_operation == jrd_req::req_unwind)
{
2001-05-23 15:26:42 +02:00
node = node->nod_parent;
break;
}
execute_procedure(tdbb, node);
node = node->nod_parent;
request->req_operation = jrd_req::req_return;
2001-05-23 15:26:42 +02:00
break;
case nod_for:
2009-01-20 09:33:59 +01:00
{
Cursor* const rsb = (Cursor*) node->nod_arg[e_for_rsb];
2009-12-10 01:02:01 +01:00
switch (request->req_operation)
2001-05-23 15:26:42 +02:00
{
case jrd_req::req_evaluate:
rsb->open(tdbb);
request->req_records_affected.clear();
case jrd_req::req_return:
if (node->nod_arg[e_for_stall])
{
node = node->nod_arg[e_for_stall];
break;
}
case jrd_req::req_sync:
if (rsb->fetchNext(tdbb))
{
node = node->nod_arg[e_for_statement];
request->req_operation = jrd_req::req_evaluate;
break;
}
request->req_operation = jrd_req::req_return;
case jrd_req::req_unwind:
{
jrd_nod* parent = node->nod_parent;
if (parent && parent->nod_type == nod_label &&
(request->req_label == (USHORT)(IPTR) parent->nod_arg[e_lbl_label]) &&
(request->req_flags & req_continue_loop))
{
request->req_flags &= ~req_continue_loop;
request->req_operation = jrd_req::req_sync;
break;
}
// fall into
}
default:
rsb->close(tdbb);
node = node->nod_parent;
}
2001-05-23 15:26:42 +02:00
}
break;
case nod_dcl_cursor:
2009-11-23 06:24:29 +01:00
if (request->req_operation == jrd_req::req_evaluate)
{
const USHORT number = (USHORT) (IPTR) node->nod_arg[e_dcl_cursor_number];
// set up the cursors array...
if (number >= request->req_cursors.getCount())
{
request->req_cursors.grow(number + 1);
}
// and store cursor there
request->req_cursors[number] = (Cursor*) node->nod_arg[e_dcl_cursor_rsb];
request->req_operation = jrd_req::req_return;
}
node = node->nod_parent;
break;
case nod_cursor_stmt:
{
2006-07-01 10:50:21 +02:00
const UCHAR op = (UCHAR) (IPTR) node->nod_arg[e_cursor_stmt_op];
const USHORT number = (USHORT) (IPTR) node->nod_arg[e_cursor_stmt_number];
fb_assert(number < request->req_cursors.getCount());
Cursor* const rsb = request->req_cursors[number];
bool fetched = false;
2009-01-14 10:19:00 +01:00
switch (op)
{
2006-07-01 10:50:21 +02:00
case blr_cursor_open:
2009-11-23 06:24:29 +01:00
if (request->req_operation == jrd_req::req_evaluate)
{
rsb->open(tdbb);
2006-07-01 10:50:21 +02:00
request->req_operation = jrd_req::req_return;
}
2006-07-01 10:50:21 +02:00
node = node->nod_parent;
break;
2006-07-01 10:50:21 +02:00
case blr_cursor_close:
2009-11-23 06:24:29 +01:00
if (request->req_operation == jrd_req::req_evaluate)
{
rsb->close(tdbb);
2006-07-01 10:50:21 +02:00
request->req_operation = jrd_req::req_return;
}
2006-07-01 10:50:21 +02:00
node = node->nod_parent;
break;
2006-07-01 10:50:21 +02:00
case blr_cursor_fetch:
case blr_cursor_fetch_scroll:
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
2006-07-01 10:50:21 +02:00
case jrd_req::req_evaluate:
request->req_records_affected.clear();
if (op == blr_cursor_fetch)
{
fetched = rsb->fetchNext(tdbb);
2006-07-01 10:50:21 +02:00
}
else
{
fb_assert(op == blr_cursor_fetch_scroll);
const SLONG fetch_op = (IPTR) node->nod_arg[e_cursor_stmt_scroll_op];
2009-12-10 06:20:20 +01:00
const dsc* desc = EVL_expr(tdbb, node->nod_arg[e_cursor_stmt_scroll_val]);
const bool unknown = !desc || (request->req_flags & req_null);
const SINT64 offset = unknown ? 0 : MOV_get_int64(desc, 0);
switch (fetch_op)
{
case blr_scroll_forward:
fetched = rsb->fetchNext(tdbb);
break;
case blr_scroll_backward:
fetched = rsb->fetchPrior(tdbb);
break;
case blr_scroll_bof:
fetched = rsb->fetchFirst(tdbb);
break;
case blr_scroll_eof:
fetched = rsb->fetchLast(tdbb);
break;
case blr_scroll_absolute:
fetched = unknown ? false : rsb->fetchAbsolute(tdbb, offset);
break;
case blr_scroll_relative:
fetched = unknown ? false : rsb->fetchRelative(tdbb, offset);
break;
default:
fb_assert(false);
2009-12-15 00:55:56 +01:00
fetched = false;
}
}
if (fetched)
2006-07-01 10:50:21 +02:00
{
node = node->nod_arg[e_cursor_stmt_into];
request->req_operation = jrd_req::req_evaluate;
break;
}
2006-07-01 10:50:21 +02:00
request->req_operation = jrd_req::req_return;
2006-07-01 10:50:21 +02:00
default:
node = node->nod_parent;
}
2006-07-01 10:50:21 +02:00
break;
}
}
break;
2001-05-23 15:26:42 +02:00
case nod_abort:
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
case jrd_req::req_evaluate:
{
PsqlException* xcp_node = reinterpret_cast<PsqlException*>(node->nod_arg[e_xcp_desc]);
if (xcp_node)
{
2009-11-22 04:56:20 +01:00
// PsqlException is defined, so throw an exception
set_error(tdbb, &xcp_node->xcp_rpt[0], node->nod_arg[e_xcp_msg]);
}
else if (!request->req_last_xcp.success())
{
2009-11-22 04:56:20 +01:00
// PsqlException is undefined, but there was a known exception before,
// so re-initiate it
set_error(tdbb, NULL, NULL);
}
else
{
2009-11-22 04:56:20 +01:00
// PsqlException is undefined and there weren't any exceptions before,
// so just do nothing
request->req_operation = jrd_req::req_return;
}
}
2001-05-23 15:26:42 +02:00
default:
node = node->nod_parent;
}
break;
2001-05-23 15:26:42 +02:00
case nod_start_savepoint:
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
case jrd_req::req_evaluate:
2009-11-22 04:56:20 +01:00
// Start a save point
2001-05-23 15:26:42 +02:00
if (transaction != dbb->dbb_sys_trans)
2002-11-03 18:29:51 +01:00
VIO_start_save_point(tdbb, transaction);
2001-05-23 15:26:42 +02:00
default:
node = node->nod_parent;
request->req_operation = jrd_req::req_return;
2001-05-23 15:26:42 +02:00
}
break;
case nod_end_savepoint:
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
case jrd_req::req_evaluate:
case jrd_req::req_unwind:
2009-11-22 04:56:20 +01:00
// If any requested modify/delete/insert ops have completed, forget them
2009-11-23 06:24:29 +01:00
if (transaction != dbb->dbb_sys_trans)
{
2009-11-22 04:56:20 +01:00
// If an error is still pending when the savepoint is
// supposed to end, then the application didn't handle the
// error and the savepoint should be undone.
2001-12-29 12:41:29 +01:00
if (error_pending) {
2001-05-23 15:26:42 +02:00
++transaction->tra_save_point->sav_verb_count;
2001-12-29 12:41:29 +01:00
}
EXE_verb_cleanup(tdbb, transaction);
2001-05-23 15:26:42 +02:00
}
default:
node = node->nod_parent;
request->req_operation = jrd_req::req_return;
2001-05-23 15:26:42 +02:00
}
break;
case nod_handler:
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
case jrd_req::req_evaluate:
2001-05-23 15:26:42 +02:00
node = node->nod_arg[0];
break;
case jrd_req::req_unwind:
2001-05-23 15:26:42 +02:00
if (!request->req_label)
request->req_operation = jrd_req::req_return;
2001-05-23 15:26:42 +02:00
default:
node = node->nod_parent;
}
break;
case nod_block:
switch (request->req_operation)
{
SLONG count;
2008-12-05 02:20:14 +01:00
case jrd_req::req_evaluate:
2009-11-23 06:24:29 +01:00
if (transaction != dbb->dbb_sys_trans)
{
2002-11-03 18:29:51 +01:00
VIO_start_save_point(tdbb, transaction);
const Savepoint* save_point = transaction->tra_save_point;
2001-05-23 15:26:42 +02:00
count = save_point->sav_number;
2008-01-16 09:54:50 +01:00
memcpy((SCHAR*) request + node->nod_impure, &count, sizeof(SLONG));
2001-05-23 15:26:42 +02:00
}
node = node->nod_arg[e_blk_action];
break;
case jrd_req::req_unwind:
2001-05-23 15:26:42 +02:00
{
if (request->req_flags & (req_leave | req_continue_loop))
2001-12-29 12:41:29 +01:00
{
// Although the req_operation is set to req_unwind,
// it's not an error case if req_leave/req_continue_loop bit is set.
// req_leave/req_continue_loop bit indicates that we hit an EXIT or
// BREAK/LEAVE/CONTINUE statement in the SP/trigger code.
// Do not perform the error handling stuff.
if (transaction != dbb->dbb_sys_trans)
{
2008-01-16 09:54:50 +01:00
memcpy(&count, (SCHAR*) request + node->nod_impure, sizeof(SLONG));
for (const Savepoint* save_point = transaction->tra_save_point;
2008-12-20 09:12:19 +01:00
save_point && count <= save_point->sav_number;
save_point = transaction->tra_save_point)
2004-01-13 10:52:19 +01:00
{
EXE_verb_cleanup(tdbb, transaction);
2004-01-13 10:52:19 +01:00
}
}
2001-05-23 15:26:42 +02:00
node = node->nod_parent;
break;
}
2001-12-29 12:41:29 +01:00
if (transaction != dbb->dbb_sys_trans)
{
2008-01-16 09:54:50 +01:00
memcpy(&count, (SCHAR*) request + node->nod_impure, sizeof(SLONG));
2009-11-22 04:56:20 +01:00
// Since there occurred an error (req_unwind), undo all savepoints
// up to, but not including, the savepoint of this block. The
// savepoint of this block will be dealt with below.
for (const Savepoint* save_point = transaction->tra_save_point;
2008-12-20 09:12:19 +01:00
save_point && count < save_point->sav_number;
save_point = transaction->tra_save_point)
2004-01-13 10:52:19 +01:00
{
2001-05-23 15:26:42 +02:00
++transaction->tra_save_point->sav_verb_count;
EXE_verb_cleanup(tdbb, transaction);
2001-05-23 15:26:42 +02:00
}
}
2001-12-29 12:41:29 +01:00
jrd_nod* handlers = node->nod_arg[e_blk_handlers];
2001-12-29 12:41:29 +01:00
if (handlers)
{
2001-05-23 15:26:42 +02:00
node = node->nod_parent;
jrd_nod** ptr = handlers->nod_arg;
2004-01-13 10:52:19 +01:00
for (const jrd_nod* const* const end = ptr + handlers->nod_count;
ptr < end; ptr++)
2001-12-29 12:41:29 +01:00
{
const PsqlException* xcp_node =
reinterpret_cast<PsqlException*>((*ptr)->nod_arg[e_err_conditions]);
if (test_and_fixup_error(tdbb, xcp_node, request))
2001-12-29 12:41:29 +01:00
{
request->req_operation = jrd_req::req_evaluate;
2001-05-23 15:26:42 +02:00
node = (*ptr)->nod_arg[e_err_action];
2003-06-28 11:32:46 +02:00
error_pending = false;
2009-11-22 04:56:20 +01:00
// On entering looper old_request etc. are saved.
// On recursive calling we will loose the actual old
// request for that invocation of looper. Avoid this.
{
Jrd::ContextPoolHolder contextLooper(tdbb, old_pool);
tdbb->setRequest(old_request);
fb_assert(request->req_caller == old_request);
request->req_caller = NULL;
2009-11-22 04:56:20 +01:00
// Save the previous state of req_error_handler
// bit. We need to restore it later. This is
// necessary if the error handler is deeply nested.
const ULONG prev_req_error_handler =
request->req_flags & req_error_handler;
request->req_flags |= req_error_handler;
node = EXE_looper(tdbb, request, node);
request->req_flags &= ~req_error_handler;
request->req_flags |= prev_req_error_handler;
/* Note: Previously the above call
2009-11-22 04:56:20 +01:00
"node = looper (tdbb, request, node);"
never returned back till the node tree
was executed completely. Now that the looper
has changed its behaviour such that it
returns back after handling error. This
makes it necessary that the jmpbuf be reset
so that looper can proceede with the
processing of execution tree. If this is
not done then anymore errors will take the
engine out of looper there by abruptly
terminating the processing. */
catch_disabled = false;
tdbb->setRequest(request);
fb_assert(request->req_caller == NULL);
request->req_caller = old_request;
}
2009-11-22 04:56:20 +01:00
// The error is dealt with by the application, cleanup
// this block's savepoint.
2001-12-29 12:41:29 +01:00
if (transaction != dbb->dbb_sys_trans)
{
for (const Savepoint* save_point = transaction->tra_save_point;
2008-12-20 09:12:19 +01:00
save_point && count <= save_point->sav_number;
save_point = transaction->tra_save_point)
2001-12-29 12:41:29 +01:00
{
EXE_verb_cleanup(tdbb, transaction);
2001-12-29 12:41:29 +01:00
}
2001-05-23 15:26:42 +02:00
}
}
2001-12-29 12:41:29 +01:00
}
2001-05-23 15:26:42 +02:00
}
else
2001-12-29 12:41:29 +01:00
{
2001-05-23 15:26:42 +02:00
node = node->nod_parent;
2001-12-29 12:41:29 +01:00
}
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// If the application didn't have an error handler, then
// the error will still be pending. Undo the block by
// using its savepoint.
2001-05-23 15:26:42 +02:00
2009-01-14 10:19:00 +01:00
if (error_pending && transaction != dbb->dbb_sys_trans)
{
for (const Savepoint* save_point = transaction->tra_save_point;
2008-12-20 09:12:19 +01:00
save_point && count <= save_point->sav_number;
save_point = transaction->tra_save_point)
{
++transaction->tra_save_point->sav_verb_count;
EXE_verb_cleanup(tdbb, transaction);
}
2001-05-23 15:26:42 +02:00
}
}
break;
case jrd_req::req_return:
2009-01-14 10:19:00 +01:00
if (transaction != dbb->dbb_sys_trans)
{
2008-01-16 09:54:50 +01:00
memcpy(&count, (SCHAR*) request + node->nod_impure, sizeof(SLONG));
for (const Savepoint* save_point = transaction->tra_save_point;
2001-05-23 15:26:42 +02:00
save_point && count <= save_point->sav_number;
save_point = transaction->tra_save_point)
2004-01-13 10:52:19 +01:00
{
EXE_verb_cleanup(tdbb, transaction);
2004-01-13 10:52:19 +01:00
}
2001-05-23 15:26:42 +02:00
}
default:
node = node->nod_parent;
}
break;
case nod_error_handler:
if (request->req_flags & req_error_handler && !error_pending)
{
fb_assert(request->req_caller == old_request);
request->req_caller = NULL;
2001-05-23 15:26:42 +02:00
return node;
}
node = node->nod_parent;
node = node->nod_parent;
if (request->req_operation == jrd_req::req_unwind) {
2001-05-23 15:26:42 +02:00
node = node->nod_parent;
}
request->req_last_xcp.clear();
2001-05-23 15:26:42 +02:00
break;
case nod_label:
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
case jrd_req::req_evaluate:
2001-05-23 15:26:42 +02:00
node = node->nod_arg[e_lbl_statement];
break;
case jrd_req::req_unwind:
fb_assert(!(request->req_flags & req_continue_loop));
if ((request->req_label == (USHORT)(IPTR) node->nod_arg[e_lbl_label]) &&
2008-12-20 09:12:19 +01:00
(request->req_flags & (req_leave | req_error_handler)))
{
2001-05-23 15:26:42 +02:00
request->req_flags &= ~req_leave;
request->req_operation = jrd_req::req_return;
2001-05-23 15:26:42 +02:00
}
// fall into
2001-05-23 15:26:42 +02:00
default:
node = node->nod_parent;
}
break;
case nod_leave:
request->req_flags |= req_leave;
request->req_operation = jrd_req::req_unwind;
request->req_label = (USHORT)(IPTR) node->nod_arg[0];
2001-05-23 15:26:42 +02:00
node = node->nod_parent;
break;
case nod_continue_loop:
request->req_flags |= req_continue_loop;
request->req_operation = jrd_req::req_unwind;
request->req_label = (USHORT)(IPTR) node->nod_arg[0];
node = node->nod_parent;
break;
2001-05-23 15:26:42 +02:00
case nod_list:
2003-12-31 06:36:12 +01:00
{
2006-07-01 10:50:21 +02:00
impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure);
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
2006-07-01 10:50:21 +02:00
case jrd_req::req_evaluate:
impure->sta_state = 0;
case jrd_req::req_return:
case jrd_req::req_sync:
2009-11-23 06:24:29 +01:00
if (impure->sta_state < node->nod_count)
{
2006-07-01 10:50:21 +02:00
request->req_operation = jrd_req::req_evaluate;
node = node->nod_arg[impure->sta_state++];
break;
}
request->req_operation = jrd_req::req_return;
default:
node = node->nod_parent;
2001-05-23 15:26:42 +02:00
}
}
break;
case nod_loop:
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
case jrd_req::req_evaluate:
case jrd_req::req_return:
2001-05-23 15:26:42 +02:00
node = node->nod_arg[0];
request->req_operation = jrd_req::req_evaluate;
2001-05-23 15:26:42 +02:00
break;
case jrd_req::req_unwind:
{
jrd_nod* parent = node->nod_parent;
if (parent && parent->nod_type == nod_label &&
(request->req_label == (USHORT)(IPTR) parent->nod_arg[e_lbl_label]) &&
(request->req_flags & req_continue_loop))
{
request->req_flags &= ~req_continue_loop;
request->req_operation = jrd_req::req_evaluate;
node = node->nod_arg[0];
break;
}
// fall into
}
2001-05-23 15:26:42 +02:00
default:
node = node->nod_parent;
}
break;
case nod_modify:
2003-12-31 06:36:12 +01:00
{
2006-07-01 10:50:21 +02:00
impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure);
if (request->req_operation == jrd_req::req_unwind) {
node = node->nod_parent;
2001-05-23 15:26:42 +02:00
}
2006-07-01 10:50:21 +02:00
else if ((request->req_operation == jrd_req::req_return) &&
2008-12-20 09:12:19 +01:00
(!impure->sta_state) && (node->nod_arg[e_mod_sub_mod]))
{
2009-11-23 06:24:29 +01:00
if (!top_node)
{
2006-07-01 10:50:21 +02:00
top_node = node;
which_mod_trig = PRE_TRIG;
}
prev_node = node;
node = modify(tdbb, node, which_mod_trig);
2009-11-23 06:24:29 +01:00
if (which_mod_trig == PRE_TRIG)
{
2006-07-01 10:50:21 +02:00
node = prev_node->nod_arg[e_mod_sub_mod];
node->nod_parent = prev_node;
}
2009-11-23 06:24:29 +01:00
if (top_node == prev_node && which_mod_trig == POST_TRIG)
{
2006-07-01 10:50:21 +02:00
top_node = NULL;
which_mod_trig = ALL_TRIGS;
}
else {
request->req_operation = jrd_req::req_evaluate;
}
2001-05-23 15:26:42 +02:00
}
2009-11-23 06:24:29 +01:00
else
{
2006-07-01 10:50:21 +02:00
prev_node = node;
node = modify(tdbb, node, ALL_TRIGS);
2008-12-20 09:12:19 +01:00
if (!(prev_node->nod_arg[e_mod_sub_mod]) && which_mod_trig == PRE_TRIG)
2006-07-01 10:50:21 +02:00
{
which_mod_trig = POST_TRIG;
}
2001-12-28 07:31:38 +01:00
}
2001-05-23 15:26:42 +02:00
}
break;
case nod_nop:
request->req_operation = jrd_req::req_return;
2001-05-23 15:26:42 +02:00
node = node->nod_parent;
break;
case nod_receive:
node = receive_msg(tdbb, node);
break;
2002-04-04 15:53:20 +02:00
case nod_exec_sql:
2009-11-23 06:24:29 +01:00
if (request->req_operation == jrd_req::req_unwind)
{
2002-04-04 15:53:20 +02:00
node = node->nod_parent;
break;
}
exec_sql(tdbb, request, EVL_expr(tdbb, node->nod_arg[0]));
if (request->req_operation == jrd_req::req_evaluate)
request->req_operation = jrd_req::req_return;
2002-04-04 15:53:20 +02:00
node = node->nod_parent;
break;
2008-12-05 02:20:14 +01:00
case nod_exec_into:
{
2008-12-20 09:12:19 +01:00
ExecuteStatement* impure = (ExecuteStatement*) ((SCHAR*) request + node->nod_impure);
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
2004-10-23 02:54:26 +02:00
case jrd_req::req_evaluate:
impure->open(tdbb, node->nod_arg[0], node->nod_count - 2, !node->nod_arg[1]);
2008-12-20 09:12:19 +01:00
// fall into ???
2004-10-23 02:54:26 +02:00
case jrd_req::req_return:
case jrd_req::req_sync:
2009-11-23 06:24:29 +01:00
if (impure->fetch(tdbb, &node->nod_arg[2]))
{
2004-10-23 02:54:26 +02:00
request->req_operation = jrd_req::req_evaluate;
node = node->nod_arg[1];
break;
}
request->req_operation = jrd_req::req_return;
default:
// if have active opened request - close it
impure->close(tdbb);
2004-10-23 02:54:26 +02:00
node = node->nod_parent;
}
}
break;
case nod_exec_stmt:
node = execute_statement(tdbb, request, node);
break;
2001-05-23 15:26:42 +02:00
case nod_message:
if (request->req_operation == jrd_req::req_evaluate)
{
const Format* format = (Format*) node->nod_arg[e_msg_format];
2009-01-14 10:19:00 +01:00
USHORT* impure_flags =
(USHORT*) ((UCHAR*) request + (IPTR) node->nod_arg[e_msg_impure_flags]);
memset(impure_flags, 0, sizeof(USHORT) * format->fmt_count);
request->req_operation = jrd_req::req_return;
}
2001-05-23 15:26:42 +02:00
node = node->nod_parent;
break;
case nod_stall:
node = stall(tdbb, node);
break;
case nod_select:
node = selct(tdbb, node);
break;
case nod_store:
2003-12-31 06:36:12 +01:00
{
2006-07-01 10:50:21 +02:00
impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure);
if ((request->req_operation == jrd_req::req_return) &&
(!impure->sta_state) && (node->nod_arg[e_sto_sub_store]))
{
2009-11-23 06:24:29 +01:00
if (!top_node)
{
2006-07-01 10:50:21 +02:00
top_node = node;
which_sto_trig = PRE_TRIG;
}
prev_node = node;
node = store(tdbb, node, which_sto_trig);
2009-11-23 06:24:29 +01:00
if (which_sto_trig == PRE_TRIG)
{
2006-07-01 10:50:21 +02:00
node = prev_node->nod_arg[e_sto_sub_store];
node->nod_parent = prev_node;
}
2009-11-23 06:24:29 +01:00
if (top_node == prev_node && which_sto_trig == POST_TRIG)
{
2006-07-01 10:50:21 +02:00
top_node = NULL;
which_sto_trig = ALL_TRIGS;
}
else
request->req_operation = jrd_req::req_evaluate;
2001-05-23 15:26:42 +02:00
}
2009-11-23 06:24:29 +01:00
else
{
2006-07-01 10:50:21 +02:00
prev_node = node;
node = store(tdbb, node, ALL_TRIGS);
2008-12-20 09:12:19 +01:00
if (!(prev_node->nod_arg[e_sto_sub_store]) && which_sto_trig == PRE_TRIG)
2006-07-01 10:50:21 +02:00
which_sto_trig = POST_TRIG;
2001-05-23 15:26:42 +02:00
}
}
break;
case nod_set_generator:
2009-10-23 02:42:40 +02:00
case nod_set_generator2:
if (request->req_operation == jrd_req::req_evaluate)
{
SSHORT genId = (SSHORT)(IPTR) node->nod_arg[e_gen_id];
MetaName genName;
MET_lookup_generator_id(tdbb, genId, genName);
DdlNode::executeDdlTrigger(tdbb, transaction, DdlNode::DTW_BEFORE,
DDL_TRIGGER_ALTER_SEQUENCE, genName, *request->req_sql_text);
2004-01-13 10:52:19 +01:00
dsc* desc = EVL_expr(tdbb, node->nod_arg[e_gen_value]);
DPM_gen_id(tdbb, genId, true, MOV_get_int64(desc, 0));
DdlNode::executeDdlTrigger(tdbb, transaction, DdlNode::DTW_AFTER,
DDL_TRIGGER_ALTER_SEQUENCE, genName, *request->req_sql_text);
request->req_operation = jrd_req::req_return;
2001-05-23 15:26:42 +02:00
}
node = node->nod_parent;
break;
case nod_src_info:
2009-11-23 06:24:29 +01:00
if (request->req_operation == jrd_req::req_evaluate)
{
request->req_src_line = (USHORT) (IPTR) node->nod_arg[e_src_info_line];
request->req_src_column = (USHORT) (IPTR) node->nod_arg[e_src_info_col];
//request->req_operation = jrd_req::req_return;
node = node->nod_arg[e_src_info_node];
}
else
node = node->nod_parent;
break;
case nod_init_variable:
if (request->req_operation == jrd_req::req_evaluate)
{
2008-12-05 02:20:14 +01:00
const ItemInfo* itemInfo =
reinterpret_cast<const ItemInfo*>(node->nod_arg[e_init_var_info]);
if (itemInfo)
{
jrd_nod* var_node = node->nod_arg[e_init_var_variable];
DSC* to_desc = &((impure_value*) ((SCHAR *) request + var_node->nod_impure))->vlu_desc;
to_desc->dsc_flags |= DSC_null;
MapFieldInfo::ValueType fieldInfo;
if (itemInfo->fullDomain &&
request->req_map_field_info.get(itemInfo->field, fieldInfo) &&
fieldInfo.defaultValue)
{
dsc* value = EVL_expr(tdbb, fieldInfo.defaultValue);
if (value && !(request->req_flags & req_null))
{
to_desc->dsc_flags &= ~DSC_null;
MOV_move(tdbb, value, to_desc);
}
}
}
request->req_operation = jrd_req::req_return;
}
node = node->nod_parent;
break;
case nod_class_node_jrd:
node = reinterpret_cast<const StmtNode*>(node->nod_arg[0])->execute(tdbb, request);
2008-01-16 09:54:50 +01:00
break;
case nod_stmt_expr:
if (request->req_operation == jrd_req::req_evaluate)
node = node->nod_arg[e_stmt_expr_stmt];
else
node = NULL;
break;
2001-05-23 15:26:42 +02:00
default:
2009-11-22 04:56:20 +01:00
BUGCHECK(168); // msg 168 looper: action not yet implemented
2001-05-23 15:26:42 +02:00
}
} // try
2009-01-14 10:19:00 +01:00
catch (const Firebird::Exception& ex)
{
Firebird::stuff_exception(tdbb->tdbb_status_vector, ex);
request->adjustCallerStats();
// Skip this handling for errors coming from the nested looper calls,
// as they're already handled properly. The only need is to undo
// our own savepoints.
2009-01-14 10:19:00 +01:00
if (catch_disabled)
{
2009-11-23 06:24:29 +01:00
if (transaction != dbb->dbb_sys_trans)
{
for (const Savepoint* save_point = transaction->tra_save_point;
((save_point) && (save_point_number <= save_point->sav_number));
save_point = transaction->tra_save_point)
{
++transaction->tra_save_point->sav_verb_count;
EXE_verb_cleanup(tdbb, transaction);
}
}
ERR_punt();
}
// If the database is already bug-checked, then get out
if (dbb->dbb_flags & DBB_bugcheck) {
Firebird::status_exception::raise(tdbb->tdbb_status_vector);
}
// Since an error happened, the current savepoint needs to be undone
2008-12-20 09:12:19 +01:00
if (transaction != dbb->dbb_sys_trans && transaction->tra_save_point)
{
++transaction->tra_save_point->sav_verb_count;
EXE_verb_cleanup(tdbb, transaction);
}
2003-06-28 11:32:46 +02:00
error_pending = true;
catch_disabled = true;
request->req_operation = jrd_req::req_unwind;
request->req_label = 0;
2008-12-20 09:12:19 +01:00
if (!(tdbb->tdbb_flags & TDBB_stack_trace_done) && !(tdbb->tdbb_flags & TDBB_sys_error))
{
2008-12-05 02:20:14 +01:00
stuff_stack_trace(request);
tdbb->tdbb_flags |= TDBB_stack_trace_done;
}
2001-05-23 15:26:42 +02:00
}
} // while()
2001-05-23 15:26:42 +02:00
request->adjustCallerStats();
2009-04-04 18:39:31 +02:00
2008-01-16 09:54:50 +01:00
fb_assert(request->req_auto_trans.getCount() == 0);
// If there is no node, assume we have finished processing the
// request unless we are in the middle of processing an
// asynchronous message
2001-05-23 15:26:42 +02:00
if (in_node->nod_type != nod_stmt_expr && !node)
2001-12-29 12:41:29 +01:00
{
// Close active cursors
for (Cursor* const* ptr = request->req_cursors.begin();
2009-12-10 01:02:01 +01:00
ptr < request->req_cursors.end(); ++ptr)
2009-01-14 10:19:00 +01:00
{
if (*ptr)
{
(*ptr)->close(tdbb);
}
}
2001-05-23 15:26:42 +02:00
request->req_flags &= ~(req_active | req_reserved);
request->req_timestamp.invalidate();
2001-05-23 15:26:42 +02:00
release_blobs(tdbb, request);
}
end:
2001-05-23 15:26:42 +02:00
request->req_next = node;
tdbb->setTransaction(old_transaction);
tdbb->setRequest(old_request);
if (in_node->nod_type != nod_stmt_expr)
{
fb_assert(request->req_caller == old_request);
request->req_caller = NULL;
}
2001-05-23 15:26:42 +02:00
// In the case of a pending error condition (one which did not
2001-12-29 12:41:29 +01:00
// result in a exception to the top of looper), we need to
// delete the last savepoint
2009-01-14 10:19:00 +01:00
if (error_pending)
{
2009-11-23 06:24:29 +01:00
if (transaction != dbb->dbb_sys_trans)
{
for (const Savepoint* save_point = transaction->tra_save_point;
2003-12-31 06:36:12 +01:00
((save_point) && (save_point_number <= save_point->sav_number));
save_point = transaction->tra_save_point)
{
2002-11-03 18:29:51 +01:00
++transaction->tra_save_point->sav_verb_count;
EXE_verb_cleanup(tdbb, transaction);
2001-05-23 15:26:42 +02:00
}
}
ERR_punt();
}
// if the request was aborted, assume that we have already
// longjmp'ed to the top of looper, and therefore that the
// last savepoint has already been deleted
2001-12-24 03:51:06 +01:00
if (request->req_flags & req_abort) {
ERR_post(Arg::Gds(isc_req_sync));
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
return node;
}
2008-08-28 17:54:21 +02:00
// Start looper under Windows SEH (Structured Exception Handling) control
2009-04-28 15:08:04 +02:00
static void looper_seh(thread_db* tdbb, jrd_req* request)
2008-08-28 17:54:21 +02:00
{
#ifdef WIN_NT
START_CHECK_FOR_EXCEPTIONS(NULL);
#endif
// TODO:
// 1. Try to fix the problem with MSVC C++ runtime library, making
// even C++ exceptions that are implemented in terms of Win32 SEH
// getting catched by the SEH handler below.
// 2. Check if it really is correct that only Win32 catches CPU
// exceptions (such as SEH) here. Shouldn't any platform capable
// of handling signals use this stuff?
// (see jrd/ibsetjmp.h for implementation of these macros)
EXE_looper(tdbb, request, request->req_top_node);
2008-08-28 17:54:21 +02:00
#ifdef WIN_NT
END_CHECK_FOR_EXCEPTIONS(NULL);
#endif
}
static jrd_nod* modify(thread_db* tdbb, jrd_nod* node, SSHORT which_trig)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* m o d i f y
*
**************************************
*
* Functional description
* Execute a MODIFY statement.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
BLKCHK(node, type_nod);
jrd_req* request = tdbb->getRequest();
2003-12-31 06:36:12 +01:00
jrd_tra* transaction = request->req_transaction;
impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure);
2001-05-23 15:26:42 +02:00
const SSHORT org_stream = (USHORT)(IPTR) node->nod_arg[e_mod_org_stream];
record_param* org_rpb = &request->req_rpb[org_stream];
2003-12-31 06:36:12 +01:00
jrd_rel* relation = org_rpb->rpb_relation;
2001-05-23 15:26:42 +02:00
2008-12-20 09:12:19 +01:00
if (org_rpb->rpb_number.isBof() || (!relation->rel_view_rse && !org_rpb->rpb_number.isValid()))
{
ERR_post(Arg::Gds(isc_no_cur_rec));
}
const SSHORT new_stream = (USHORT)(IPTR) node->nod_arg[e_mod_new_stream];
record_param* new_rpb = &request->req_rpb[new_stream];
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// If the stream was sorted, the various fields in the rpb are
// probably junk. Just to make sure that everything is cool,
// refetch and release the record.
2001-05-23 15:26:42 +02:00
2009-11-23 06:24:29 +01:00
if (org_rpb->rpb_stream_flags & RPB_s_refetch)
{
VIO_refetch_record(tdbb, org_rpb, transaction);
org_rpb->rpb_stream_flags &= ~RPB_s_refetch;
}
2001-05-23 15:26:42 +02:00
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
case jrd_req::req_evaluate:
2005-06-24 14:56:34 +02:00
request->req_records_affected.bumpModified(false);
2001-05-23 15:26:42 +02:00
break;
case jrd_req::req_return:
2006-09-03 03:09:23 +02:00
if (impure->sta_state == 1)
{
2001-05-23 15:26:42 +02:00
impure->sta_state = 0;
Record* org_record = org_rpb->rpb_record;
const Record* new_record = new_rpb->rpb_record;
2008-01-16 09:54:50 +01:00
memcpy(org_record->rec_data, new_record->rec_data, new_record->rec_length);
request->req_operation = jrd_req::req_evaluate;
2001-05-23 15:26:42 +02:00
return node->nod_arg[e_mod_statement];
}
2006-09-03 03:09:23 +02:00
if (impure->sta_state == 0)
{
2009-11-22 04:56:20 +01:00
// CVC: This call made here to clear the record in each NULL field and
// varchar field whose tail may contain garbage.
2006-09-03 03:09:23 +02:00
cleanup_rpb(tdbb, new_rpb);
2002-06-30 11:58:20 +02:00
2006-09-03 03:09:23 +02:00
if (transaction != dbb->dbb_sys_trans)
++transaction->tra_save_point->sav_verb_count;
2001-05-23 15:26:42 +02:00
2008-12-20 09:12:19 +01:00
PreModifyEraseTriggers(tdbb, &relation->rel_pre_modify, which_trig, org_rpb, new_rpb,
jrd_req::req_trigger_update);
2001-05-23 15:26:42 +02:00
2006-09-03 03:09:23 +02:00
if (node->nod_arg[e_mod_validate]) {
validate(tdbb, node->nod_arg[e_mod_validate]);
}
2001-05-23 15:26:42 +02:00
2006-09-03 03:09:23 +02:00
if (relation->rel_file)
{
EXT_modify(org_rpb, new_rpb, transaction);
}
else if (relation->isVirtual()) {
VirtualTable::modify(tdbb, org_rpb, new_rpb);
2006-09-03 03:09:23 +02:00
}
else if (!relation->rel_view_rse)
{
USHORT bad_index;
jrd_rel* bad_relation = 0;
2001-05-23 15:26:42 +02:00
2006-09-03 03:09:23 +02:00
VIO_modify(tdbb, org_rpb, new_rpb, transaction);
2009-04-03 12:07:55 +02:00
const idx_e error_code =
2008-12-18 11:57:12 +01:00
IDX_modify(tdbb, org_rpb, new_rpb, transaction, &bad_relation, &bad_index);
2006-09-03 03:09:23 +02:00
if (error_code) {
ERR_duplicate_error(error_code, bad_relation, bad_index);
}
2001-12-24 03:51:06 +01:00
}
2001-05-23 15:26:42 +02:00
new_rpb->rpb_number = org_rpb->rpb_number;
new_rpb->rpb_number.setValid(true);
if (relation->rel_post_modify && which_trig != PRE_TRIG)
2006-09-03 03:09:23 +02:00
{
execute_triggers(tdbb, &relation->rel_post_modify, org_rpb, new_rpb,
jrd_req::req_trigger_update, POST_TRIG);
2006-09-03 03:09:23 +02:00
}
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// now call IDX_modify_check_constrints after all post modify triggers
// have fired. This is required for cascading referential integrity,
// which can be implemented as post_erase triggers
2001-05-23 15:26:42 +02:00
2008-12-18 11:57:12 +01:00
if (!relation->rel_file && !relation->rel_view_rse && !relation->isVirtual())
2006-09-03 03:09:23 +02:00
{
USHORT bad_index;
jrd_rel* bad_relation = 0;
2001-05-23 15:26:42 +02:00
2009-04-03 12:07:55 +02:00
const idx_e error_code =
2006-09-03 03:09:23 +02:00
IDX_modify_check_constraints(tdbb, org_rpb, new_rpb, transaction,
2008-12-20 09:12:19 +01:00
&bad_relation, &bad_index);
2006-09-03 03:09:23 +02:00
if (error_code) {
ERR_duplicate_error(error_code, bad_relation, bad_index);
}
}
2001-05-23 15:26:42 +02:00
2006-09-03 03:09:23 +02:00
if (transaction != dbb->dbb_sys_trans) {
--transaction->tra_save_point->sav_verb_count;
}
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// CVC: Increment the counter only if we called VIO/EXT_modify() and
// we were successful.
2009-11-23 06:24:29 +01:00
if (!(request->req_view_flags & req_first_modify_return))
{
2006-09-03 03:09:23 +02:00
request->req_view_flags |= req_first_modify_return;
if (relation->rel_view_rse) {
request->req_top_view_modify = relation;
}
2002-06-30 11:58:20 +02:00
}
2009-11-23 06:24:29 +01:00
if (relation == request->req_top_view_modify)
{
if (which_trig == ALL_TRIGS || which_trig == POST_TRIG)
{
2006-09-03 03:09:23 +02:00
request->req_records_updated++;
request->req_records_affected.bumpModified(true);
}
}
2009-11-23 06:24:29 +01:00
else if (relation->rel_file || !relation->rel_view_rse)
{
2002-06-30 11:58:20 +02:00
request->req_records_updated++;
2005-06-24 14:56:34 +02:00
request->req_records_affected.bumpModified(true);
2002-06-30 11:58:20 +02:00
}
2006-09-03 03:09:23 +02:00
2009-11-23 06:24:29 +01:00
if (node->nod_arg[e_mod_statement2])
{
2006-09-03 03:09:23 +02:00
impure->sta_state = 2;
request->req_operation = jrd_req::req_evaluate;
return node->nod_arg[e_mod_statement2];
}
2002-06-30 11:58:20 +02:00
}
2001-05-23 15:26:42 +02:00
2009-11-23 06:24:29 +01:00
if (which_trig != PRE_TRIG)
{
Record* org_record = org_rpb->rpb_record;
2001-05-23 15:26:42 +02:00
org_rpb->rpb_record = new_rpb->rpb_record;
new_rpb->rpb_record = org_record;
}
default:
return node->nod_parent;
}
impure->sta_state = 0;
2008-01-16 09:54:50 +01:00
RLCK_reserve_relation(tdbb, transaction, relation, true);
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// Fall thru on evaluate to set up for modify before executing sub-statement.
// This involves finding the appropriate format, making sure a record block
// exists for the stream and is big enough, and copying fields from the
// original record to the new record.
2001-05-23 15:26:42 +02:00
2004-03-30 06:10:52 +02:00
const Format* new_format = MET_current(tdbb, new_rpb->rpb_relation);
Record* new_record = VIO_record(tdbb, new_rpb, new_format, tdbb->getDefaultPool());
2001-05-23 15:26:42 +02:00
new_rpb->rpb_address = new_record->rec_data;
new_rpb->rpb_length = new_format->fmt_length;
new_rpb->rpb_format_number = new_format->fmt_version;
2004-03-30 06:10:52 +02:00
const Format* org_format;
Record* org_record = org_rpb->rpb_record;
2008-11-30 03:23:01 +01:00
if (!org_record)
{
org_record = VIO_record(tdbb, org_rpb, new_format, tdbb->getDefaultPool());
2001-05-23 15:26:42 +02:00
org_format = org_record->rec_format;
org_rpb->rpb_address = org_record->rec_data;
org_rpb->rpb_length = org_format->fmt_length;
org_rpb->rpb_format_number = org_format->fmt_version;
}
else
org_format = org_record->rec_format;
2009-11-22 04:56:20 +01:00
// Copy the original record to the new record. If the format hasn't changed,
// this is a simple move. If the format has changed, each field must be
// fetched and moved separately, remembering to set the missing flag.
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
if (new_format->fmt_version == org_format->fmt_version) {
2008-01-16 09:54:50 +01:00
memcpy(new_rpb->rpb_address, org_record->rec_data, new_rpb->rpb_length);
2003-12-31 06:36:12 +01:00
}
2009-01-14 10:19:00 +01:00
else
{
2001-05-23 15:26:42 +02:00
DSC org_desc, new_desc;
2009-01-14 10:19:00 +01:00
for (SSHORT i = 0; i < new_format->fmt_count; i++)
{
2009-11-22 04:56:20 +01:00
// In order to "map a null to a default" value (in EVL_field()),
// the relation block is referenced.
// Reference: Bug 10116, 10424
2001-05-23 15:26:42 +02:00
CLEAR_NULL(new_record, i);
2009-01-14 10:19:00 +01:00
if (EVL_field(new_rpb->rpb_relation, new_record, i, &new_desc))
{
2008-12-18 11:57:12 +01:00
if (EVL_field(org_rpb->rpb_relation, org_record, i, &org_desc))
2003-12-31 06:36:12 +01:00
{
MOV_move(tdbb, &org_desc, &new_desc);
2003-12-31 06:36:12 +01:00
}
2009-11-23 06:24:29 +01:00
else
{
2001-05-23 15:26:42 +02:00
SET_NULL(new_record, i);
2009-11-23 06:24:29 +01:00
if (new_desc.dsc_dtype)
{
2003-12-31 06:36:12 +01:00
UCHAR* p = new_desc.dsc_address;
USHORT n = new_desc.dsc_length;
memset(p, 0, n);
2001-05-23 15:26:42 +02:00
}
2009-11-22 04:56:20 +01:00
} // if (org_record)
} // if (new_record)
} // for (fmt_count)
2001-05-23 15:26:42 +02:00
}
new_rpb->rpb_number = org_rpb->rpb_number;
new_rpb->rpb_number.setValid(true);
2009-11-23 06:24:29 +01:00
if (node->nod_arg[e_mod_map_view])
{
2001-05-23 15:26:42 +02:00
impure->sta_state = 1;
return node->nod_arg[e_mod_map_view];
}
return node->nod_arg[e_mod_statement];
}
static jrd_nod* receive_msg(thread_db* tdbb, jrd_nod* node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* r e c e i v e _ m s g
*
**************************************
*
* Functional description
* Execute a RECEIVE statement. This can be entered either
* with "req_evaluate" (ordinary receive statement) or
* "req_proceed" (select statement). In the latter case,
* the statement isn't every formalled evaluated.
*
**************************************/
SET_TDBB(tdbb);
jrd_req* request = tdbb->getRequest();
2001-05-23 15:26:42 +02:00
BLKCHK(node, type_nod);
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
case jrd_req::req_evaluate:
request->req_operation = jrd_req::req_receive;
2001-05-23 15:26:42 +02:00
request->req_message = node->nod_arg[e_send_message];
request->req_flags |= req_stall;
return node;
case jrd_req::req_proceed:
request->req_operation = jrd_req::req_evaluate;
2001-05-23 15:26:42 +02:00
return (node->nod_arg[e_send_statement]);
default:
return (node->nod_parent);
}
}
static void release_blobs(thread_db* tdbb, jrd_req* request)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* r e l e a s e _ b l o b s
*
**************************************
*
* Functional description
* Release temporary blobs assigned by this request.
*
**************************************/
SET_TDBB(tdbb);
DEV_BLKCHK(request, type_req);
2003-12-31 06:36:12 +01:00
jrd_tra* transaction = request->req_transaction;
2009-01-14 10:19:00 +01:00
if (transaction)
{
2001-05-23 15:26:42 +02:00
DEV_BLKCHK(transaction, type_tra);
2009-11-22 04:56:20 +01:00
// Release blobs bound to this request
2001-05-23 15:26:42 +02:00
if (request->req_blobs.getFirst())
{
2008-12-05 02:20:14 +01:00
while (true)
2006-04-06 10:18:53 +02:00
{
const ULONG blob_temp_id = request->req_blobs.current();
if (transaction->tra_blobs->locate(blob_temp_id))
2008-01-16 09:54:50 +01:00
{
BlobIndex *current = &transaction->tra_blobs->current();
if (current->bli_materialized)
{
request->req_blobs.fastRemove();
current->bli_request = NULL;
}
else
{
2008-12-05 02:20:14 +01:00
// Blob was created by request, is accounted for internal needs,
2006-04-06 10:18:53 +02:00
// but is not materialized. Get rid of it.
BLB_cancel(tdbb, current->bli_blob_object);
2008-12-05 02:20:14 +01:00
// Since the routine above modifies req_blobs
2006-04-06 10:18:53 +02:00
// we need to reestablish accessor position
}
if (request->req_blobs.locate(Firebird::locGreat, blob_temp_id))
continue;
2008-01-16 09:54:50 +01:00
break;
}
2008-01-16 09:54:50 +01:00
// Blob accounting inconsistent, only detected in DEV_BUILD.
fb_assert(false);
2006-04-06 10:18:53 +02:00
if (!request->req_blobs.getNext())
break;
}
}
request->req_blobs.clear();
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// Release arrays assigned by this request
2001-05-23 15:26:42 +02:00
2009-01-14 10:19:00 +01:00
for (ArrayField** array = &transaction->tra_arrays; *array;)
{
2001-05-23 15:26:42 +02:00
DEV_BLKCHK(*array, type_arr);
if ((*array)->arr_request == request)
BLB_release_array(*array);
else
array = &(*array)->arr_next;
}
}
}
static void release_proc_save_points(jrd_req* request)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* r e l e a s e _ p r o c _ s a v e _ p o i n t s
*
**************************************
*
* Functional description
* Release temporary blobs assigned by this request.
*
**************************************/
Savepoint* sav_point = request->req_proc_sav_point;
2001-05-23 15:26:42 +02:00
2009-11-23 06:24:29 +01:00
if (request->req_transaction)
{
while (sav_point)
{
Savepoint* const temp_sav_point = sav_point->sav_next;
delete sav_point;
sav_point = temp_sav_point;
}
2001-05-23 15:26:42 +02:00
}
request->req_proc_sav_point = NULL;
}
static jrd_nod* selct(thread_db* tdbb, jrd_nod* node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s e l e c t
*
**************************************
*
* Functional description
* Execute a SELECT statement. This is more than a little
* obscure. We first set up the SELECT statement as the
* "message" and stall on receive (waiting for user send).
* EXE_send will then loop thru the sub-statements of select
* looking for the appropriate RECEIVE statement. When (or if)
* it finds it, it will set it up the next statement to be
* executed. The RECEIVE, then, will be entered with the
* operation "req_proceed."
*
**************************************/
SET_TDBB(tdbb);
jrd_req* request = tdbb->getRequest();
2001-05-23 15:26:42 +02:00
BLKCHK(node, type_nod);
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
case jrd_req::req_evaluate:
2001-05-23 15:26:42 +02:00
request->req_message = node;
request->req_operation = jrd_req::req_receive;
2001-05-23 15:26:42 +02:00
request->req_flags |= req_stall;
return node;
default:
return node->nod_parent;
}
}
static void set_error(thread_db* tdbb, const xcp_repeat* exception, jrd_nod* msg_node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s e t _ e r r o r
*
**************************************
*
* Functional description
* Set status vector according to specified error condition
* and jump to handle error accordingly.
*
**************************************/
Firebird::MetaName name, relation_name;
TEXT message[XCP_MESSAGE_LENGTH + 1];
MoveBuffer temp;
2001-05-23 15:26:42 +02:00
SET_TDBB(tdbb);
jrd_req* request = tdbb->getRequest();
2009-11-23 06:24:29 +01:00
if (!exception)
{
// retrieve the status vector and punt
request->req_last_xcp.copyTo(tdbb->tdbb_status_vector);
request->req_last_xcp.clear();
ERR_punt();
}
USHORT length = 0;
2008-12-05 02:20:14 +01:00
if (msg_node)
{
UCHAR* string = NULL;
// evaluate exception message and convert it to string
dsc* desc = EVL_expr(tdbb, msg_node);
if (desc && !(request->req_flags & req_null))
{
length = MOV_make_string2(tdbb, desc, CS_METADATA, &string, temp);
length = MIN(length, sizeof(message) - 1);
2002-10-19 11:05:37 +02:00
/* dimitr: or should we throw an error here, i.e.
replace the above assignment with the following lines:
2002-10-19 11:05:37 +02:00
if (length > sizeof(message) - 1)
2008-08-30 05:14:08 +02:00
ERR_post(Arg::Gds(isc_imp_exc) << Arg::Gds(isc_blktoobig));
*/
2002-10-19 11:05:37 +02:00
memcpy(message, string, length);
}
else
{
length = 0;
}
}
message[length] = 0;
string tempStr;
const TEXT* s;
switch (exception->xcp_type)
{
2001-05-23 15:26:42 +02:00
case xcp_sql_code:
ERR_post(Arg::Gds(isc_sqlerr) << Arg::Num(exception->xcp_code));
2001-05-23 15:26:42 +02:00
case xcp_gds_code:
2009-11-23 06:24:29 +01:00
if (exception->xcp_code == isc_check_constraint)
{
2008-12-20 09:12:19 +01:00
MET_lookup_cnstrt_for_trigger(tdbb, name, relation_name, request->req_trg_name);
ERR_post(Arg::Gds(exception->xcp_code) << Arg::Str(name) << Arg::Str(relation_name));
2001-05-23 15:26:42 +02:00
}
else
ERR_post(Arg::Gds(exception->xcp_code));
2001-05-23 15:26:42 +02:00
case xcp_xcp_code:
// CVC: If we have the exception name, use it instead of the number.
// Solves SF Bug #494981.
MET_lookup_exception(tdbb, exception->xcp_code, name, &tempStr);
2001-05-23 15:26:42 +02:00
if (message[0])
s = message;
else if (tempStr.hasData())
s = tempStr.c_str();
2001-05-23 15:26:42 +02:00
else
s = NULL;
if (s && name.length())
2008-07-01 03:12:02 +02:00
{
ERR_post(Arg::Gds(isc_except) << Arg::Num(exception->xcp_code) <<
Arg::Gds(isc_random) << Arg::Str(name) <<
Arg::Gds(isc_random) << Arg::Str(s));
2008-07-01 03:12:02 +02:00
}
else if (s)
2008-07-01 03:12:02 +02:00
{
ERR_post(Arg::Gds(isc_except) << Arg::Num(exception->xcp_code) <<
Arg::Gds(isc_random) << Arg::Str(s));
2008-07-01 03:12:02 +02:00
}
else if (name.length())
2008-07-01 03:12:02 +02:00
{
ERR_post(Arg::Gds(isc_except) << Arg::Num(exception->xcp_code) <<
Arg::Gds(isc_random) << Arg::Str(name));
2008-07-01 03:12:02 +02:00
}
2008-12-05 02:20:14 +01:00
else
ERR_post(Arg::Gds(isc_except) << Arg::Num(exception->xcp_code));
default:
fb_assert(false);
2001-05-23 15:26:42 +02:00
}
}
static jrd_nod* stall(thread_db* tdbb, jrd_nod* node)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
2008-12-05 02:20:14 +01:00
* s t a l l
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Execute a stall statement.
* This is like a blr_receive, except that there is no
* need for a gds__send () from the user (i.e. EXE_send () in the engine).
* A gds__receive () will unblock the user.
*
**************************************/
SET_TDBB(tdbb);
jrd_req* request = tdbb->getRequest();
2001-05-23 15:26:42 +02:00
BLKCHK(node, type_nod);
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
case jrd_req::req_sync:
2001-05-23 15:26:42 +02:00
return node->nod_parent;
case jrd_req::req_proceed:
request->req_operation = jrd_req::req_return;
2001-05-23 15:26:42 +02:00
return node->nod_parent;
default:
request->req_message = node;
request->req_operation = jrd_req::req_return;
2001-05-23 15:26:42 +02:00
request->req_flags |= req_stall;
return node;
}
}
static jrd_nod* store(thread_db* tdbb, jrd_nod* node, SSHORT which_trig)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* s t o r e
*
**************************************
*
* Functional description
* Execute a STORE statement.
*
**************************************/
SET_TDBB(tdbb);
Database* dbb = tdbb->getDatabase();
2001-05-23 15:26:42 +02:00
BLKCHK(node, type_nod);
jrd_req* request = tdbb->getRequest();
jrd_tra* transaction = request->req_transaction;
impure_state* impure = (impure_state*) ((SCHAR *) request + node->nod_impure);
SSHORT stream = (USHORT)(IPTR) node->nod_arg[e_sto_relation]->nod_arg[e_rel_stream];
record_param* rpb = &request->req_rpb[stream];
jrd_rel* relation = rpb->rpb_relation;
2001-05-23 15:26:42 +02:00
2009-01-14 10:19:00 +01:00
switch (request->req_operation)
{
case jrd_req::req_evaluate:
2008-12-20 09:12:19 +01:00
if (request->req_records_affected.isReadOnly() && !request->req_records_affected.hasCursor())
2005-06-24 14:56:34 +02:00
{
request->req_records_affected.clear();
}
request->req_records_affected.bumpModified(false);
2001-05-23 15:26:42 +02:00
impure->sta_state = 0;
2008-01-16 09:54:50 +01:00
RLCK_reserve_relation(tdbb, transaction, relation, true);
2001-05-23 15:26:42 +02:00
break;
case jrd_req::req_return:
2001-05-23 15:26:42 +02:00
if (impure->sta_state)
return node->nod_parent;
if (transaction != dbb->dbb_sys_trans)
++transaction->tra_save_point->sav_verb_count;
if (relation->rel_pre_store && which_trig != POST_TRIG)
2001-12-29 12:41:29 +01:00
{
execute_triggers(tdbb, &relation->rel_pre_store, NULL, rpb,
jrd_req::req_trigger_insert, PRE_TRIG);
2001-12-29 12:41:29 +01:00
}
2001-05-23 15:26:42 +02:00
2001-12-29 12:41:29 +01:00
if (node->nod_arg[e_sto_validate]) {
2001-05-23 15:26:42 +02:00
validate(tdbb, node->nod_arg[e_sto_validate]);
2001-12-29 12:41:29 +01:00
}
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// For optimum on-disk record compression, zero all unassigned
// fields. In addition, zero the tail of assigned varying fields
// so that previous remnants don't defeat compression efficiency.
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// CVC: The code that was here was moved to its own routine: cleanup_rpb()
// and replaced by the call shown below.
2002-06-30 11:58:20 +02:00
cleanup_rpb(tdbb, rpb);
2001-05-23 15:26:42 +02:00
2001-12-29 12:41:29 +01:00
if (relation->rel_file) {
2009-04-28 15:08:04 +02:00
EXT_store(tdbb, rpb);
2001-12-29 12:41:29 +01:00
}
else if (relation->isVirtual()) {
VirtualTable::store(tdbb, rpb);
}
2001-12-29 12:41:29 +01:00
else if (!relation->rel_view_rse)
{
USHORT bad_index;
jrd_rel* bad_relation = 0;
2001-05-23 15:26:42 +02:00
VIO_store(tdbb, rpb, transaction);
2009-04-03 12:07:55 +02:00
const idx_e error_code = IDX_store(tdbb, rpb, transaction, &bad_relation, &bad_index);
2001-12-29 12:41:29 +01:00
if (error_code) {
ERR_duplicate_error(error_code, bad_relation, bad_index);
}
2001-05-23 15:26:42 +02:00
}
rpb->rpb_number.setValid(true);
if (relation->rel_post_store && which_trig != PRE_TRIG)
2001-12-29 12:41:29 +01:00
{
execute_triggers(tdbb, &relation->rel_post_store, NULL, rpb,
jrd_req::req_trigger_insert, POST_TRIG);
2001-12-29 12:41:29 +01:00
}
2001-05-23 15:26:42 +02:00
2009-11-22 04:56:20 +01:00
// CVC: Increment the counter only if we called VIO/EXT_store() and we were successful.
2009-11-23 06:24:29 +01:00
if (!(request->req_view_flags & req_first_store_return))
{
2002-06-30 11:58:20 +02:00
request->req_view_flags |= req_first_store_return;
if (relation->rel_view_rse) {
request->req_top_view_store = relation;
}
}
2009-11-23 06:24:29 +01:00
if (relation == request->req_top_view_store)
{
if (which_trig == ALL_TRIGS || which_trig == POST_TRIG)
{
2002-06-30 11:58:20 +02:00
request->req_records_inserted++;
2005-06-24 14:56:34 +02:00
request->req_records_affected.bumpModified(true);
2002-06-30 11:58:20 +02:00
}
}
2009-11-23 06:24:29 +01:00
else if (relation->rel_file || !relation->rel_view_rse)
{
2002-06-30 11:58:20 +02:00
request->req_records_inserted++;
2005-06-24 14:56:34 +02:00
request->req_records_affected.bumpModified(true);
2002-06-30 11:58:20 +02:00
}
2001-05-23 15:26:42 +02:00
2001-12-29 12:41:29 +01:00
if (transaction != dbb->dbb_sys_trans) {
2001-05-23 15:26:42 +02:00
--transaction->tra_save_point->sav_verb_count;
2001-12-29 12:41:29 +01:00
}
2001-05-23 15:26:42 +02:00
2009-11-23 06:24:29 +01:00
if (node->nod_arg[e_sto_statement2])
{
2001-05-23 15:26:42 +02:00
impure->sta_state = 1;
request->req_operation = jrd_req::req_evaluate;
2001-05-23 15:26:42 +02:00
return node->nod_arg[e_sto_statement2];
}
default:
return node->nod_parent;
}
2009-11-22 04:56:20 +01:00
// Fall thru on evaluate to set up for store before executing sub-statement.
// This involves finding the appropriate format, making sure a record block
// exists for the stream and is big enough, and initialize all null flags
// to "missing."
2001-05-23 15:26:42 +02:00
2004-03-30 06:10:52 +02:00
const Format* format = MET_current(tdbb, relation);
Record* record = VIO_record(tdbb, rpb, format, tdbb->getDefaultPool());
2001-05-23 15:26:42 +02:00
rpb->rpb_address = record->rec_data;
rpb->rpb_length = format->fmt_length;
rpb->rpb_format_number = format->fmt_version;
2009-11-22 04:56:20 +01:00
// CVC: This small block added by Ann Harrison to
// start with a clean empty buffer and so avoid getting
// new record buffer with misleading information. Fixes
// bug with incorrect blob sharing during insertion in
// a stored procedure.
2002-06-30 11:58:20 +02:00
memset(record->rec_data, 0, rpb->rpb_length);
2002-06-30 11:58:20 +02:00
2009-11-22 04:56:20 +01:00
// Initialize all fields to missing
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
SSHORT n = (format->fmt_count + 7) >> 3;
2001-12-29 12:41:29 +01:00
if (n) {
memset(record->rec_data, 0xff, n);
2001-12-29 12:41:29 +01:00
}
2001-05-23 15:26:42 +02:00
return node->nod_arg[e_sto_statement];
}
2009-01-14 10:19:00 +01:00
static bool test_and_fixup_error(thread_db* tdbb, const PsqlException* conditions, jrd_req* request)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* t e s t _ a n d _ f i x u p _ e r r o r
2001-05-23 15:26:42 +02:00
*
**************************************
*
* Functional description
* Test for match of current state with list of error conditions.
* Fix type and code of the exception.
2001-05-23 15:26:42 +02:00
*
**************************************/
SET_TDBB(tdbb);
if (tdbb->tdbb_flags & TDBB_sys_error)
return false;
ISC_STATUS* status_vector = tdbb->tdbb_status_vector;
2003-12-31 06:36:12 +01:00
const SSHORT sqlcode = gds__sqlcode(status_vector);
bool found = false;
2001-05-23 15:26:42 +02:00
for (USHORT i = 0; i < conditions->xcp_count; i++)
2001-12-29 12:41:29 +01:00
{
switch (conditions->xcp_rpt[i].xcp_type)
{
2001-05-23 15:26:42 +02:00
case xcp_sql_code:
if (sqlcode == conditions->xcp_rpt[i].xcp_code)
{
found = true;
2001-05-23 15:26:42 +02:00
}
break;
case xcp_gds_code:
if (status_vector[1] == conditions->xcp_rpt[i].xcp_code)
{
found = true;
2001-05-23 15:26:42 +02:00
}
break;
case xcp_xcp_code:
{
// Look at set_error() routine to understand how the
// exception ID info is encoded inside the status vector.
if ((status_vector[1] == isc_except) &&
(status_vector[3] == conditions->xcp_rpt[i].xcp_code))
2001-12-29 12:41:29 +01:00
{
found = true;
2001-05-23 15:26:42 +02:00
}
}
2001-05-23 15:26:42 +02:00
break;
case xcp_default:
found = true;
break;
default:
fb_assert(false);
}
if (found)
{
request->req_last_xcp.init(status_vector);
fb_utils::init_status(status_vector);
break;
2001-05-23 15:26:42 +02:00
}
}
2001-05-23 15:26:42 +02:00
return found;
2001-05-23 15:26:42 +02:00
}
static void trigger_failure(thread_db* tdbb, jrd_req* trigger)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* t r i g g e r _ f a i l u r e
*
**************************************
*
* Functional description
* Trigger failed, report error.
*
**************************************/
SET_TDBB(tdbb);
EXE_unwind(tdbb, trigger);
2001-12-29 12:41:29 +01:00
2001-05-23 15:26:42 +02:00
trigger->req_attachment = NULL;
trigger->req_flags &= ~req_in_use;
trigger->req_timestamp.invalidate();
2001-12-29 12:41:29 +01:00
if (trigger->req_flags & req_leave)
{
2001-05-23 15:26:42 +02:00
trigger->req_flags &= ~req_leave;
string msg;
MET_trigger_msg(tdbb, msg, trigger->req_trg_name, trigger->req_label);
if (msg.hasData())
2001-12-29 12:41:29 +01:00
{
if (trigger->req_flags & req_sys_trigger)
{
2003-04-10 08:49:16 +02:00
ISC_STATUS code = PAR_symbol_to_gdscode(msg);
2001-05-23 15:26:42 +02:00
if (code)
2001-12-29 12:41:29 +01:00
{
ERR_post(Arg::Gds(isc_integ_fail) << Arg::Num(trigger->req_label) <<
Arg::Gds(code));
2001-12-29 12:41:29 +01:00
}
2001-05-23 15:26:42 +02:00
}
ERR_post(Arg::Gds(isc_integ_fail) << Arg::Num(trigger->req_label) <<
Arg::Gds(isc_random) << Arg::Str(msg));
2001-05-23 15:26:42 +02:00
}
else
2001-12-29 12:41:29 +01:00
{
ERR_post(Arg::Gds(isc_integ_fail) << Arg::Num(trigger->req_label));
2001-12-29 12:41:29 +01:00
}
2001-05-23 15:26:42 +02:00
}
else
2001-12-29 12:41:29 +01:00
{
2001-05-23 15:26:42 +02:00
ERR_punt();
2001-12-29 12:41:29 +01:00
}
2001-05-23 15:26:42 +02:00
}
static void validate(thread_db* tdbb, jrd_nod* list)
2001-05-23 15:26:42 +02:00
{
/**************************************
*
* v a l i d a t e
*
**************************************
*
* Functional description
* Execute a list of validation expressions.
*
**************************************/
SET_TDBB(tdbb);
BLKCHK(list, type_nod);
jrd_nod** ptr1 = list->nod_arg;
2008-12-20 09:12:19 +01:00
for (const jrd_nod* const* const end = ptr1 + list->nod_count; ptr1 < end; ptr1++)
2001-05-23 15:26:42 +02:00
{
jrd_req* request = tdbb->getRequest();
2008-12-20 09:12:19 +01:00
if (!EVL_boolean(tdbb, (*ptr1)->nod_arg[e_val_boolean]) && !(request->req_flags & req_null))
2001-05-23 15:26:42 +02:00
{
2009-11-22 04:56:20 +01:00
// Validation error -- report result
const char* value;
VaryStr<128> temp;
2001-05-23 15:26:42 +02:00
2003-12-31 06:36:12 +01:00
jrd_nod* node = (*ptr1)->nod_arg[e_val_value];
const dsc* desc = EVL_expr(tdbb, node);
const USHORT length = (desc && !(request->req_flags & req_null)) ?
MOV_make_string(desc, ttype_dynamic, &value,
&temp, sizeof(temp) - 1) : 0;
2001-05-23 15:26:42 +02:00
2005-03-11 09:34:20 +01:00
if (!desc || (request->req_flags & req_null))
2001-05-23 15:26:42 +02:00
{
value = NULL_STRING_MARK;
2001-05-23 15:26:42 +02:00
}
else if (!length)
{
value = "";
}
else
{
const_cast<char*>(value)[length] = 0; // safe cast - data is actually on the stack
2001-05-23 15:26:42 +02:00
}
2003-12-31 06:36:12 +01:00
const TEXT* name = 0;
2001-05-23 15:26:42 +02:00
if (node->nod_type == nod_field)
{
const USHORT stream = (USHORT)(IPTR) node->nod_arg[e_fld_stream];
const USHORT id = (USHORT)(IPTR) node->nod_arg[e_fld_id];
2003-12-31 06:36:12 +01:00
const jrd_rel* relation = request->req_rpb[stream].rpb_relation;
const jrd_fld* field;
const vec<jrd_fld*>* vector = relation->rel_fields;
2008-12-20 09:12:19 +01:00
if (vector && id < vector->count() && (field = (*vector)[id]))
2001-05-23 15:26:42 +02:00
{
name = field->fld_name.c_str();
2001-05-23 15:26:42 +02:00
}
}
if (!name)
{
name = UNKNOWN_STRING_MARK;
2001-05-23 15:26:42 +02:00
}
2008-12-20 09:12:19 +01:00
ERR_post(Arg::Gds(isc_not_valid) << Arg::Str(name) << Arg::Str(value));
2001-05-23 15:26:42 +02:00
}
}
}
void EXE_verb_cleanup(thread_db* tdbb, jrd_tra* transaction)
{
/**************************************
*
* E X E _ v e r b _ c l e a n u p
*
**************************************
*
* Functional description
* If an error happens during the backout of a savepoint, then the transaction
* must be marked 'dead' because that is the only way to clean up after a
* failed backout. The easiest way to do this is to kill the application
* by calling bugcheck.
*
**************************************/
try
{
VIO_verb_cleanup(tdbb, transaction);
}
catch (const Firebird::Exception&)
{
if (tdbb->getDatabase()->dbb_flags & DBB_bugcheck)
Firebird::status_exception::raise(tdbb->tdbb_status_vector);
BUGCHECK(290); // msg 290 error during savepoint backout
}
}
const Routine* jrd_req::getRoutine() const
{
fb_assert(!(req_procedure && req_function));
if (req_procedure)
return req_procedure;
2009-12-30 05:24:31 +01:00
return req_function;
}