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

2052 lines
53 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 EXE_cleanup_rpb() as directed
2002-06-30 11:58:20 +02:00
* 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 "../common/common.h"
2001-05-23 15:26:42 +02:00
#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 "../dsql/ExprNodes.h"
#include "../dsql/StmtNodes.h"
2001-05-23 15:26:42 +02:00
#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"
2010-10-12 10:02:57 +02:00
#include "../yvalve/gds_proto.h"
2001-05-23 15:26:42 +02:00
#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"
2010-10-12 10:02:57 +02:00
#include "../common/isc_s_proto.h"
2001-05-23 15:26:42 +02:00
2003-04-22 10:45:52 +02:00
#include "../dsql/dsql_proto.h"
#include "../jrd/rpb_chain.h"
#include "../jrd/RecordSourceNodes.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);
}
void StatusXcp::as_sqlstate(char* sqlstate) const
{
fb_sqlstate(sqlstate, status);
}
2008-12-20 09:12:19 +01:00
static void execute_looper(thread_db*, jrd_req*, jrd_tra*, jrd_req::req_s);
2009-04-28 15:08:04 +02:00
static void looper_seh(thread_db*, jrd_req*);
static void release_blobs(thread_db*, jrd_req*);
static void release_proc_save_points(jrd_req*);
static void trigger_failure(thread_db*, jrd_req*);
static void stuff_stack_trace(const jrd_req*);
2001-05-23 15:26:42 +02:00
2004-11-07 15:30:38 +01:00
const size_t MAX_STACK_TRACE = 2048;
// Perform an assignment.
void EXE_assignment(thread_db* tdbb, const jrd_nod* node)
2001-05-23 15:26:42 +02:00
{
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, request, node->nod_arg[e_asgn_from]->asValue());
EXE_assignment(tdbb, node->nod_arg[e_asgn_to]->asValue(), 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;
}
// Perform an assignment.
void EXE_assignment(thread_db* tdbb, const ValueExprNode* source, const ValueExprNode* target)
{
SET_TDBB(tdbb);
jrd_req* request = tdbb->getRequest();
// Get descriptors of src field/parameter/variable, etc.
request->req_flags &= ~req_null;
dsc* from_desc = EVL_expr(tdbb, request, source);
EXE_assignment(tdbb, target, from_desc, (request->req_flags & req_null), NULL, NULL);
request->req_operation = jrd_req::req_return;
}
// Perform an assignment.
void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bool from_null,
const jrd_nod* missing_node, const jrd_nod* missing2_node)
{
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, request, missing_node->asValue());
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;
USHORT* impure_flags = NULL;
2010-11-02 18:05:01 +01:00
const ParameterNode* toParam;
const VariableNode* toVar;
const FieldNode* toField;
2010-11-02 18:05:01 +01:00
if ((toParam = ExprNode::as<ParameterNode>(to)))
{
if (toParam->argInfo)
{
EVL_validate(tdbb,
Item(Item::TYPE_PARAMETER, (IPTR) toParam->message->nod_arg[e_msg_number],
toParam->argNumber),
toParam->argInfo, from_desc, null == -1);
}
impure_flags = request->getImpure<USHORT>(
(IPTR) toParam->message->nod_arg[e_msg_impure_flags] +
(sizeof(USHORT) * toParam->argNumber));
}
2010-11-02 18:05:01 +01:00
else if ((toVar = ExprNode::as<VariableNode>(to)))
{
2010-11-02 18:05:01 +01:00
if (toVar->varInfo)
{
2010-11-02 18:05:01 +01:00
EVL_validate(tdbb, Item(Item::TYPE_VARIABLE, toVar->varId),
toVar->varInfo, from_desc, null == -1);
}
2010-11-02 18:05:01 +01:00
impure_flags = &request->getImpure<impure_value>(
2010-11-02 18:05:01 +01:00
toVar->varDecl->nod_impure)->vlu_flags;
}
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
if (toParam && toParam->argIndicator)
2001-12-24 03:51:06 +01:00
{
dsc* indicator = EVL_assign_to(tdbb, toParam->argIndicator);
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;
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);
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-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, request, missing2_node->asValue())))
MOV_move(tdbb, missing, to_desc);
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
if ((toField = ExprNode::as<FieldNode>(to)))
2001-12-24 03:51:06 +01:00
{
Record* record = request->req_rpb[toField->fieldStream].rpb_record;
if (null)
SET_NULL(record, toField->fieldId);
else
CLEAR_NULL(record, toField->fieldId);
2001-05-23 15:26:42 +02:00
}
else if (toParam && toParam->argFlag)
2001-12-24 03:51:06 +01:00
{
to_desc = EVL_assign_to(tdbb, toParam->argFlag);
2001-05-23 15:26:42 +02:00
// 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;
2008-12-20 09:12:19 +01:00
switch (to_desc->dsc_dtype)
{
case dtype_text:
minlen = 1;
break;
case dtype_cstring:
minlen = 2;
break;
case dtype_varying:
minlen = 3;
break;
}
2008-12-20 09:12:19 +01:00
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);
if (null && toParam->argIndicator)
2009-11-23 06:24:29 +01:00
{
to_desc = EVL_assign_to(tdbb, toParam->argIndicator);
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
{
EXE_execute_triggers(tdbb, &tdbb->getDatabase()->dbb_triggers[type],
NULL, NULL, trigger_action, StmtNode::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;
}
}
EXE_execute_triggers(tdbb, &triggersPtr, NULL, NULL, jrd_req::req_trigger_ddl,
StmtNode::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;
}
}
}
void EXE_receive(thread_db* tdbb,
jrd_req* request,
USHORT msg,
ULONG 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->getStatement()->procedure &&
request->getStatement()->procedure->getExternal();
if (external)
{
execute_looper(tdbb, request, transaction, jrd_req::req_sync);
}
else
{
if (StmtNode::is<StallNode>(request->req_message))
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
memcpy(buffer, request->getImpure<UCHAR>(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
}
}
// Release a request instance.
void EXE_release(thread_db* tdbb, jrd_req* request)
{
DEV_BLKCHK(request, type_req);
SET_TDBB(tdbb);
EXE_unwind(tdbb, request);
delete request->inputParams;
request->inputParams = NULL;
delete request->outputParams;
request->outputParams = NULL;
if (request->req_attachment)
{
size_t pos;
if (request->req_attachment->att_requests.find(request, pos))
request->req_attachment->att_requests.remove(pos);
request->req_attachment = NULL;
}
}
void EXE_send(thread_db* tdbb, jrd_req* request, USHORT msg, ULONG 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
const jrd_nod* message;
const 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 JrdStatement* statement = request->getStatement();
const bool external = statement->procedure && statement->procedure->getExternal();
2001-05-23 15:26:42 +02:00
if (external)
2009-01-14 09:22:32 +01:00
{
fb_assert(statement->topNode->nod_type == nod_list);
message = statement->topNode->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_class_stmtnode_jrd:
2009-01-14 09:22:32 +01:00
{
const SelectNode* selectNode = StmtNode::as<SelectNode>(node);
if (selectNode)
{
const NestConst<jrd_nod>* ptr = selectNode->statements.begin();
for (const NestConst<jrd_nod>* end = selectNode->statements.end();
ptr != end;
++ptr)
2009-11-23 06:24:29 +01:00
{
const ReceiveNode* receiveNode = StmtNode::as<ReceiveNode>(ptr->getObject());
message = receiveNode->message;
if ((USHORT)(IPTR) message->nod_arg[e_msg_number] == msg)
{
request->req_next = *ptr;
break;
}
}
break;
2009-01-14 09:22:32 +01:00
}
// fall into
2001-05-23 15:26:42 +02:00
}
default:
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
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
memcpy(request->getImpure<UCHAR>(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)
{
const UCHAR* p = request->getImpure<UCHAR>(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 = request->getImpure<Jrd::bid>(
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);
Jrd::Attachment* attachment = tdbb->getAttachment();
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
JrdStatement* statement = request->getStatement();
const jrd_prc* proc = statement->procedure;
if (proc && proc->isUndefined())
{
status_exception::raise(
Arg::Gds(isc_proc_pack_not_implemented) <<
Arg::Str(proc->getName().identifier) << Arg::Str(proc->getName().package));
}
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, statement->resources);
2001-05-23 15:26:42 +02:00
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);
request->req_flags &= req_in_use;
2001-05-23 15:26:42 +02:00
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.
const ULONG* const* ptr, * const* end;
for (ptr = statement->invariants.begin(), end = statement->invariants.end();
ptr < end; ++ptr)
{
impure_value* impure = request->getImpure<impure_value>(**ptr);
impure->vlu_flags = 0;
2001-05-23 15:26:42 +02:00
}
if (statement->sqlText)
2009-02-05 21:41:47 +01:00
tdbb->bumpStats(RuntimeStatistics::STMT_EXECUTES);
// Start a save point if not in middle of one
if (transaction && (transaction != attachment->getSysTransaction())) {
2002-11-03 18:29:51 +01:00
VIO_start_save_point(tdbb, transaction);
}
request->req_src_line = 0;
request->req_src_column = 0;
2010-04-04 10:52:10 +02:00
looper_seh(tdbb, request);
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
if (transaction && (transaction != attachment->getSysTransaction()) &&
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
* Unwind a request, maybe active, maybe not.
2001-05-23 15:26:42 +02:00
*
**************************************/
DEV_BLKCHK(request, type_req);
SET_TDBB(tdbb);
2008-12-05 02:20:14 +01:00
if (request->req_flags & req_active)
{
const JrdStatement* statement = request->getStatement();
if (statement->fors.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 (const RecordSource* const* ptr = statement->fors.begin();
ptr != statement->fors.end(); ++ptr)
{
(*ptr)->close(tdbb);
}
2006-03-10 18:46:18 +01:00
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;
request->req_sorts.unlinkAll();
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
}
// 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.
void EXE_cleanup_rpb(thread_db* tdbb, record_param* rpb)
2002-06-30 11:58:20 +02:00
{
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
}
}
}
}
// Perform operation's pre-triggers, storing active rpb in chain.
void EXE_PreModifyEraseTriggers(thread_db* tdbb,
2008-12-05 02:20:14 +01:00
trig_vec** trigs,
StmtNode::WhichTrigger which_trig,
2008-12-05 02:20:14 +01:00
record_param* rpb,
record_param* rec,
jrd_req::req_ta op)
{
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 != StmtNode::POST_TRIG)
{
try
{
EXE_execute_triggers(tdbb, trigs, rpb, rec, op, StmtNode::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
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);
Jrd::Attachment* attachment = tdbb->getAttachment();
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
{
if (transaction && (transaction != attachment->getSysTransaction()))
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)
{
if (transaction && (transaction != attachment->getSysTransaction()) &&
transaction->tra_save_point && !transaction->tra_save_point->sav_verb_count)
2003-12-31 06:36:12 +01:00
{
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
}
void EXE_execute_triggers(thread_db* tdbb,
trig_vec** triggers,
record_param* old_rpb,
record_param* new_rpb,
jrd_req::req_ta trigger_action, StmtNode::WhichTrigger 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);
jrd_req* const request = tdbb->getRequest();
jrd_tra* const transaction = request ? request->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 =
request ? request->req_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 = ptr->statement->findRequest(tdbb);
trigger->req_rpb[0].rpb_record = old_rec ? old_rec : null_rec;
2010-08-05 02:44:58 +02:00
if (trigger->req_rpb.getCount() > 1)
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 == StmtNode::PRE_TRIG &&
trigger_action == jrd_req::req_trigger_insert))
{
if (which_trig == StmtNode::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);
}
2010-08-05 02:44:58 +02:00
else if (trigger->req_rpb.getCount() > 1)
trigger->req_rpb[1].rpb_number.setValid(false);
2009-02-02 04:35:52 +01:00
trigger->req_timestamp = timestamp;
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 stuff_stack_trace(const jrd_req* request)
{
Firebird::string sTrace;
bool isEmpty = true;
for (const jrd_req* req = request; req; req = req->req_caller)
{
const JrdStatement* statement = req->getStatement();
Firebird::string name;
if (statement->triggerName.length())
2009-11-23 06:24:29 +01:00
{
name = "At trigger '";
name += statement->triggerName.c_str();
}
else if (statement->procedure)
2009-11-23 06:24:29 +01:00
{
name = "At procedure '";
name += statement->procedure->getName().toString().c_str();
}
else if (statement->function)
{
name = "At function '";
name += statement->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
const jrd_nod* EXE_looper(thread_db* tdbb, jrd_req* request, const jrd_nod* in_node, bool stmtExpr)
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.
*
**************************************/
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);
Jrd::Attachment* attachment = tdbb->getAttachment();
jrd_tra* sysTransaction = attachment->getSysTransaction();
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
StmtNode::ExeState exeState(tdbb);
Jrd::ContextPoolHolder context(tdbb, request->req_pool);
2001-12-28 07:31:38 +01:00
tdbb->setRequest(request);
jrd_tra* old_transaction = tdbb->getTransaction();
tdbb->setTransaction(transaction);
if (!stmtExpr)
{
fb_assert(request->req_caller == NULL);
request->req_caller = exeState.oldRequest;
}
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;
const jrd_nod* node = in_node;
2001-05-23 15:26:42 +02:00
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
const JrdStatement* statement = request->getStatement();
bool runExternal = statement->procedure && statement->procedure->getExternal();
while (runExternal ||
(node && !(request->req_flags & req_stall) &&
(!stmtExpr || request->req_operation == jrd_req::req_evaluate)))
2001-12-29 12:41:29 +01:00
{
try {
if (statement->procedure && statement->procedure->getExternal())
{
if (runExternal)
runExternal = false;
else
break;
switch (request->req_operation)
{
case jrd_req::req_evaluate:
request->req_message = statement->topNode->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(statement->topNode->nod_type == nod_list);
2010-08-12 02:59:48 +02:00
const jrd_nod* outMsgNode = statement->topNode->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 = request->getImpure<UCHAR>(outMsgNode->nod_impure);
if (!request->resultSet)
{
// input message
2010-08-12 02:59:48 +02:00
const jrd_nod* inMsgNode = statement->topNode->nod_arg[e_extproc_input_message];
fb_assert(inMsgNode->nod_type == nod_message);
fb_assert(request->req_message == inMsgNode);
2010-08-12 02:59:48 +02:00
const jrd_nod* list = statement->topNode->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 = request->getImpure<USHORT>(
(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 = request->getImpure<USHORT>(
(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 = request->getImpure<UCHAR>(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,
statement->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,
statement->procedure->prc_output_fields);
request->outputParams->setNull();
}
request->resultSet = statement->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;
2010-08-12 02:59:48 +02:00
const jrd_nod* list = statement->topNode->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)
{
const jrd_nod* const* 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 = request->getImpure<impure_value>(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_list:
2001-12-28 07:31:38 +01:00
{
impure_state* impure = request->getImpure<impure_state>(node->nod_impure);
switch (request->req_operation)
2009-01-14 10:19:00 +01:00
{
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_message:
if (request->req_operation == jrd_req::req_evaluate)
{
const Format* format = (Format*) node->nod_arg[e_msg_format];
USHORT* impure_flags = request->getImpure<USHORT>(
(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_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)
{
const jrd_nod* var_node = node->nod_arg[e_init_var_variable];
DSC* to_desc = &request->getImpure<impure_value>(var_node->nod_impure)->vlu_desc;
to_desc->dsc_flags |= DSC_null;
MapFieldInfo::ValueType fieldInfo;
if (itemInfo->fullDomain &&
request->getStatement()->mapFieldInfo.get(itemInfo->field, fieldInfo) &&
fieldInfo.defaultValue)
{
dsc* value = EVL_expr(tdbb, request, fieldInfo.defaultValue->asValue());
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_stmtnode_jrd:
node = reinterpret_cast<const StmtNode*>(node->nod_arg[0])->execute(
tdbb, request, &exeState);
2008-01-16 09:54:50 +01:00
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.
if (exeState.catchDisabled)
2009-01-14 10:19:00 +01:00
{
if (transaction != sysTransaction)
2009-11-23 06:24:29 +01:00
{
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
if (transaction != sysTransaction && transaction->tra_save_point)
{
++transaction->tra_save_point->sav_verb_count;
EXE_verb_cleanup(tdbb, transaction);
}
exeState.errorPending = true;
exeState.catchDisabled = true;
request->req_operation = jrd_req::req_unwind;
request->req_label = 0;
if (!(tdbb->tdbb_flags & TDBB_stack_trace_done) && !(tdbb->tdbb_flags & TDBB_sys_error))
2009-11-23 06:24:29 +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();
2001-05-23 15:26:42 +02:00
fb_assert(request->req_auto_trans.getCount() == 0);
2001-05-23 15:26:42 +02:00
// 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 (!stmtExpr && !node)
2008-11-30 03:23:01 +01:00
{
// Close active cursors
for (const Cursor* const* ptr = request->req_cursors.begin();
ptr < request->req_cursors.end(); ++ptr)
{
if (*ptr)
(*ptr)->close(tdbb);
}
request->req_flags &= ~(req_active | req_reserved);
request->req_timestamp.invalidate();
release_blobs(tdbb, request);
2001-05-23 15:26:42 +02:00
}
end:
request->req_next = node;
tdbb->setTransaction(old_transaction);
tdbb->setRequest(exeState.oldRequest);
2001-05-23 15:26:42 +02:00
if (!stmtExpr)
2009-01-14 10:19:00 +01:00
{
fb_assert(request->req_caller == exeState.oldRequest);
request->req_caller = NULL;
}
// In the case of a pending error condition (one which did not
// result in a exception to the top of looper), we need to
// delete the last savepoint
2001-05-23 15:26:42 +02:00
if (exeState.errorPending)
{
if (transaction != sysTransaction)
2009-01-14 10:19:00 +01:00
{
for (const Savepoint* save_point = transaction->tra_save_point;
((save_point) && (save_point_number <= save_point->sav_number));
save_point = transaction->tra_save_point)
2009-01-14 10:19:00 +01:00
{
++transaction->tra_save_point->sav_verb_count;
EXE_verb_cleanup(tdbb, transaction);
}
}
ERR_punt();
2001-05-23 15:26:42 +02:00
}
// 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
if (request->req_flags & req_abort) {
ERR_post(Arg::Gds(isc_req_sync));
2001-05-23 15:26:42 +02:00
}
return node;
2001-05-23 15:26:42 +02:00
}
// Start looper under Windows SEH (Structured Exception Handling) control
static void looper_seh(thread_db* tdbb, jrd_req* request)
{
#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)
2001-05-23 15:26:42 +02:00
EXE_looper(tdbb, request, request->getStatement()->topNode);
2001-05-23 15:26:42 +02:00
#ifdef WIN_NT
END_CHECK_FOR_EXCEPTIONS(NULL);
#endif
2001-05-23 15:26:42 +02:00
}
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 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->getStatement()->triggerName, trigger->req_label);
if (msg.hasData())
2001-12-29 12:41:29 +01:00
{
if (trigger->getStatement()->flags & JrdStatement::FLAG_SYS_TRIGGER)
2001-12-29 12:41:29 +01:00
{
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
}
// Execute a list of validation expressions.
void EXE_validate(thread_db* tdbb, const jrd_nod* list)
2001-05-23 15:26:42 +02:00
{
SET_TDBB(tdbb);
BLKCHK(list, type_nod);
const jrd_nod* const* 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();
if ((*ptr1)->nod_arg[e_val_stmt])
EXE_looper(tdbb, request, (*ptr1)->nod_arg[e_val_stmt], true);
BoolExprNode* boolExpr = reinterpret_cast<BoolExprNode*>(
(*ptr1)->nod_arg[e_val_boolean]->nod_arg[0]);
if (!boolExpr->execute(tdbb, request) && !(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
const jrd_nod* node = (*ptr1)->nod_arg[e_val_value];
const dsc* desc = EVL_expr(tdbb, request, node->asValue());
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
}
const TEXT* name = NULL;
const FieldNode* fieldNode = ExprNode::as<FieldNode>(node);
2003-12-31 06:36:12 +01:00
if (fieldNode)
{
const jrd_rel* relation = request->req_rpb[fieldNode->fieldStream].rpb_relation;
const vec<jrd_fld*>* vector = relation->rel_fields;
const jrd_fld* field;
if (vector && fieldNode->fieldId < vector->count() &&
(field = (*vector)[fieldNode->fieldId]))
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
}
}