8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-26 08:03:03 +01:00
firebird-mirror/src/dsql/StmtNodes.cpp
asfernandes 534b8c020e Misc
2009-12-25 19:29:58 +00:00

1082 lines
26 KiB
C++

/*
* 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): ______________________________________.
* Adriano dos Santos Fernandes - refactored from pass1.cpp, gen.cpp, cmp.cpp, par.cpp and exe.cpp
*/
#include "firebird.h"
#include "../jrd/common.h"
#include "../dsql/StmtNodes.h"
#include "../dsql/node.h"
#include "../jrd/jrd.h"
#include "../jrd/blr.h"
#include "../jrd/exe.h"
#include "../jrd/tra.h"
#include "../jrd/cmp_proto.h"
#include "../jrd/dfw_proto.h"
#include "../jrd/evl_proto.h"
#include "../jrd/exe_proto.h"
#include "../jrd/par_proto.h"
#include "../jrd/tra_proto.h"
#include "../dsql/ddl_proto.h"
#include "../jrd/vio_proto.h"
#include "../dsql/errd_proto.h"
#include "../dsql/gen_proto.h"
#include "../dsql/make_proto.h"
#include "../dsql/pass1_proto.h"
using namespace Firebird;
using namespace Jrd;
#include "gen/blrtable.h"
namespace Jrd {
template <typename T>
class RegisterNode
{
public:
explicit RegisterNode(UCHAR blr)
{
PAR_register(blr, T::parse);
}
};
//--------------------
DmlNode* DmlNode::pass2(thread_db* tdbb, CompilerScratch* csb, jrd_nod* aNode)
{
node = aNode;
return pass2(tdbb, csb);
}
//--------------------
static RegisterNode<IfNode> regIfNode(blr_if);
DmlNode* IfNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/)
{
IfNode* node = FB_NEW(pool) IfNode(pool);
node->condition = PAR_parse_node(tdbb, csb, TYPE_BOOL);
node->trueAction = PAR_parse_node(tdbb, csb, STATEMENT);
if (csb->csb_blr_reader.peekByte() == (UCHAR) blr_end)
csb->csb_blr_reader.getByte(); // skip blr_end
else
node->falseAction = PAR_parse_node(tdbb, csb, STATEMENT);
return node;
}
IfNode* IfNode::internalDsqlPass()
{
IfNode* node = FB_NEW(getPool()) IfNode(getPool());
node->dsqlScratch = dsqlScratch;
node->dsqlCondition = PASS1_node(dsqlScratch, dsqlCondition);
node->dsqlTrueAction = PASS1_statement(dsqlScratch, dsqlTrueAction);
node->dsqlFalseAction = PASS1_statement(dsqlScratch, dsqlFalseAction);
return node;
}
void IfNode::print(string& text, Array<dsql_nod*>& nodes) const
{
text = "IfNode";
nodes.add(dsqlCondition);
nodes.add(dsqlTrueAction);
if (dsqlFalseAction)
nodes.add(dsqlFalseAction);
}
void IfNode::genBlr()
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
stuff(statement, blr_if);
GEN_expr(dsqlScratch, dsqlCondition);
GEN_statement(dsqlScratch, dsqlTrueAction);
if (dsqlFalseAction)
GEN_statement(dsqlScratch, dsqlFalseAction);
else
stuff(statement, blr_end);
}
IfNode* IfNode::pass1(thread_db* tdbb, CompilerScratch* csb)
{
condition = CMP_pass1(tdbb, csb, condition);
trueAction = CMP_pass1(tdbb, csb, trueAction);
falseAction = CMP_pass1(tdbb, csb, falseAction);
return this;
}
IfNode* IfNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{
condition = CMP_pass2(tdbb, csb, condition, node);
trueAction = CMP_pass2(tdbb, csb, trueAction, node);
falseAction = CMP_pass2(tdbb, csb, falseAction, node);
return this;
}
jrd_nod* IfNode::execute(thread_db* tdbb, jrd_req* request) const
{
if (request->req_operation == jrd_req::req_evaluate)
{
if (EVL_boolean(tdbb, condition))
{
request->req_operation = jrd_req::req_evaluate;
return trueAction;
}
if (falseAction)
{
request->req_operation = jrd_req::req_evaluate;
return falseAction;
}
request->req_operation = jrd_req::req_return;
}
return node->nod_parent;
}
//--------------------
static RegisterNode<InAutonomousTransactionNode> regInAutonomousTransactionNode(blr_auto_trans);
DmlNode* InAutonomousTransactionNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb,
UCHAR /*blrOp*/)
{
InAutonomousTransactionNode* node = FB_NEW(pool) InAutonomousTransactionNode(pool);
if (csb->csb_blr_reader.getByte() != 0) // Reserved for future improvements. Should be 0 for now.
PAR_syntax_error(csb, "0");
node->action = PAR_parse_node(tdbb, csb, STATEMENT);
return node;
}
InAutonomousTransactionNode* InAutonomousTransactionNode::internalDsqlPass()
{
const bool autoTrans = dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK;
dsqlScratch->flags |= DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK;
InAutonomousTransactionNode* node = FB_NEW(getPool()) InAutonomousTransactionNode(getPool());
node->dsqlScratch = dsqlScratch;
node->dsqlAction = PASS1_statement(dsqlScratch, dsqlAction);
if (!autoTrans)
dsqlScratch->flags &= ~DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK;
return node;
}
void InAutonomousTransactionNode::print(string& text, Array<dsql_nod*>& nodes) const
{
text = "InAutonomousTransactionNode";
nodes.add(dsqlAction);
}
void InAutonomousTransactionNode::genBlr()
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
stuff(statement, blr_auto_trans);
stuff(statement, 0); // to extend syntax in the future
GEN_statement(dsqlScratch, dsqlAction);
}
InAutonomousTransactionNode* InAutonomousTransactionNode::pass1(thread_db* tdbb, CompilerScratch* csb)
{
action = CMP_pass1(tdbb, csb, action);
return this;
}
InAutonomousTransactionNode* InAutonomousTransactionNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{
savNumberOffset = CMP_impure(csb, sizeof(SLONG));
action = CMP_pass2(tdbb, csb, action, node);
return this;
}
jrd_nod* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request) const
{
SLONG* savNumber = (SLONG*) ((char*) request + savNumberOffset);
if (request->req_operation == jrd_req::req_evaluate)
{
fb_assert(tdbb->getTransaction() == request->req_transaction);
request->req_auto_trans.push(request->req_transaction);
request->req_transaction = TRA_start(tdbb, request->req_transaction->tra_flags,
request->req_transaction->tra_lock_timeout,
request->req_transaction);
tdbb->setTransaction(request->req_transaction);
VIO_start_save_point(tdbb, request->req_transaction);
*savNumber = request->req_transaction->tra_save_point->sav_number;
if (!(tdbb->getAttachment()->att_flags & ATT_no_db_triggers))
{
// run ON TRANSACTION START triggers
EXE_execute_db_triggers(tdbb, request->req_transaction, jrd_req::req_trigger_trans_start);
}
return action;
}
jrd_tra* transaction = request->req_transaction;
fb_assert(transaction);
fb_assert(transaction != tdbb->getDatabase()->dbb_sys_trans);
switch (request->req_operation)
{
case jrd_req::req_return:
if (!(tdbb->getAttachment()->att_flags & ATT_no_db_triggers))
{
// run ON TRANSACTION COMMIT triggers
EXE_execute_db_triggers(tdbb, transaction, jrd_req::req_trigger_trans_commit);
}
if (transaction->tra_save_point &&
!(transaction->tra_save_point->sav_flags & SAV_user) &&
!transaction->tra_save_point->sav_verb_count)
{
VIO_verb_cleanup(tdbb, transaction);
}
{ // scope
AutoSetRestore2<jrd_req*, thread_db> autoNullifyRequest(
tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL);
TRA_commit(tdbb, transaction, false);
} // end scope
break;
case jrd_req::req_unwind:
if (request->req_flags & (req_leave | req_continue_loop))
{
try
{
if (!(tdbb->getAttachment()->att_flags & ATT_no_db_triggers))
{
// run ON TRANSACTION COMMIT triggers
EXE_execute_db_triggers(tdbb, transaction,
jrd_req::req_trigger_trans_commit);
}
if (transaction->tra_save_point &&
!(transaction->tra_save_point->sav_flags & SAV_user) &&
!transaction->tra_save_point->sav_verb_count)
{
VIO_verb_cleanup(tdbb, transaction);
}
AutoSetRestore2<jrd_req*, thread_db> autoNullifyRequest(
tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL);
TRA_commit(tdbb, transaction, false);
}
catch (...)
{
request->req_flags &= ~(req_leave | req_continue_loop);
throw;
}
}
else
{
ThreadStatusGuard temp_status(tdbb);
if (!(tdbb->getAttachment()->att_flags & ATT_no_db_triggers))
{
try
{
// run ON TRANSACTION ROLLBACK triggers
EXE_execute_db_triggers(tdbb, transaction,
jrd_req::req_trigger_trans_rollback);
}
catch (const Exception&)
{
if (tdbb->getDatabase()->dbb_flags & DBB_bugcheck)
{
throw;
}
}
}
try
{
AutoSetRestore2<jrd_req*, thread_db> autoNullifyRequest(
tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL);
// undo all savepoints up to our one
for (const Savepoint* save_point = transaction->tra_save_point;
save_point && *savNumber <= save_point->sav_number;
save_point = transaction->tra_save_point)
{
++transaction->tra_save_point->sav_verb_count;
VIO_verb_cleanup(tdbb, transaction);
}
TRA_rollback(tdbb, transaction, false, false);
}
catch (const Exception&)
{
if (tdbb->getDatabase()->dbb_flags & DBB_bugcheck)
{
throw;
}
}
}
break;
default:
fb_assert(false);
}
request->req_transaction = request->req_auto_trans.pop();
tdbb->setTransaction(request->req_transaction);
return node->nod_parent;
}
//--------------------
ExecBlockNode* ExecBlockNode::internalDsqlPass()
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
statement->setBlockNode(this);
if (returns.hasData())
statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK);
else
statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK);
dsqlScratch->flags |= DsqlCompilerScratch::FLAG_BLOCK;
ExecBlockNode* node = FB_NEW(getPool()) ExecBlockNode(getPool());
node->dsqlScratch = dsqlScratch;
node->legacyParameters = PASS1_node_psql(dsqlScratch, legacyParameters, false);
node->returns = returns;
node->localDeclList = localDeclList;
node->body = body;
const size_t count = (node->legacyParameters ? node->legacyParameters->nod_count : 0) +
node->returns.getCount() +
(node->localDeclList ? node->localDeclList->nod_count : 0);
if (count)
{
StrArray names(*getDefaultMemoryPool(), count);
PASS1_check_unique_fields_names(names, node->legacyParameters);
// Hand-made PASS1_check_unique_fields_names for array of ParameterClause
for (size_t i = 0; i < returns.getCount(); ++i)
{
ParameterClause& parameter = returns[i];
size_t pos;
if (!names.find(parameter.name.c_str(), pos))
names.insert(pos, parameter.name.c_str());
else
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str(parameter.name));
}
}
PASS1_check_unique_fields_names(names, node->localDeclList);
}
return node;
}
void ExecBlockNode::print(string& text, Array<dsql_nod*>& nodes) const
{
text = "ExecBlockNode\n";
text += " Returns:\n";
for (size_t i = 0; i < returns.getCount(); ++i)
{
const ParameterClause& parameter = returns[i];
string s;
parameter.print(s);
text += " " + s + "\n";
}
nodes.add(legacyParameters);
nodes.add(localDeclList);
nodes.add(body);
}
void ExecBlockNode::genBlr()
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
// Update blockNode, because we have a reference to the original unprocessed node.
statement->setBlockNode(this);
statement->beginDebug();
USHORT inputs = 0, outputs = 0, locals = 0;
// now do the input parameters
if (legacyParameters)
{
dsql_nod* parameters = legacyParameters;
dsql_nod** ptr = parameters->nod_arg;
for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++)
{
dsql_nod* parameter = (*ptr)->nod_arg[Dsql::e_prm_val_fld];
dsql_fld* field = (dsql_fld*) parameter->nod_arg[Dsql::e_dfl_field];
// parameter = (*ptr)->nod_arg[Dsql::e_prm_val_val]; USELESS
DDL_resolve_intl_type(dsqlScratch, field,
reinterpret_cast<const dsql_str*>(parameter->nod_arg[Dsql::e_dfl_collate]));
variables.add(MAKE_variable(field, field->fld_name.c_str(),
VAR_input, 0, (USHORT) (2 * inputs), locals));
// ASF: do not increment locals here - CORE-2341
inputs++;
}
}
const unsigned returnsPos = variables.getCount();
// now do the output parameters
for (size_t i = 0; i < returns.getCount(); ++i)
{
ParameterClause& parameter = returns[i];
parameter.resolve(dsqlScratch);
dsql_nod* var = MAKE_variable(parameter.legacyField,
parameter.name.c_str(), VAR_output, 1, (USHORT) (2 * outputs), locals++);
variables.add(var);
outputVariables.add(var);
++outputs;
}
statement->append_uchar(blr_begin);
if (inputs)
{
revertParametersOrder(statement->getSendMsg()->msg_parameters);
GEN_port(dsqlScratch, statement->getSendMsg());
}
else
statement->setSendMsg(NULL);
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
{
dsql_par* param = MAKE_parameter(statement->getReceiveMsg(), true, true,
(i - outputVariables.begin()) + 1, *i);
param->par_node = *i;
MAKE_desc(dsqlScratch, &param->par_desc, *i, NULL);
param->par_desc.dsc_flags |= DSC_nullable;
}
// Set up parameter to handle EOF
dsql_par* param = MAKE_parameter(statement->getReceiveMsg(), false, false, 0, NULL);
statement->setEof(param);
param->par_desc.dsc_dtype = dtype_short;
param->par_desc.dsc_scale = 0;
param->par_desc.dsc_length = sizeof(SSHORT);
revertParametersOrder(statement->getReceiveMsg()->msg_parameters);
GEN_port(dsqlScratch, statement->getReceiveMsg());
if (inputs)
{
statement->append_uchar(blr_receive);
statement->append_uchar(0);
}
statement->append_uchar(blr_begin);
for (unsigned i = 0; i < returnsPos; ++i)
{
const dsql_nod* parameter = variables[i];
const dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
const dsql_fld* field = variable->var_field;
if (field->fld_full_domain || field->fld_not_nullable)
{
// ASF: Validation of execute block input parameters is different than procedure
// parameters, because we can't generate messages using the domains due to the
// connection charset influence. So to validate, we cast them and assign to null.
statement->append_uchar(blr_assignment);
statement->append_uchar(blr_cast);
DDL_put_field_dtype(dsqlScratch, field, true);
statement->append_uchar(blr_parameter2);
statement->append_uchar(0);
statement->append_ushort(variable->var_msg_item);
statement->append_ushort(variable->var_msg_item + 1);
statement->append_uchar(blr_null);
}
}
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
{
dsql_nod* parameter = *i;
dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
DDL_put_local_variable(dsqlScratch, variable, 0, NULL);
}
dsqlScratch->setPsql(true);
DDL_put_local_variables(dsqlScratch, localDeclList, locals, variables);
dsqlScratch->loopLevel = 0;
dsql_nod* stmtNode = PASS1_statement(dsqlScratch, body);
GEN_hidden_variables(dsqlScratch, false);
statement->append_uchar(blr_stall);
// Put a label before body of procedure, so that
// any exit statement can get out
statement->append_uchar(blr_label);
statement->append_uchar(0);
GEN_statement(dsqlScratch, stmtNode);
if (outputs)
statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK);
else
statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK);
statement->append_uchar(blr_end);
GEN_return(dsqlScratch, outputVariables, true, true);
statement->append_uchar(blr_end);
statement->endDebug();
}
void ExecBlockNode::genReturn()
{
GEN_return(dsqlScratch, outputVariables, true, false);
}
dsql_nod* ExecBlockNode::resolveVariable(const dsql_str* varName)
{
// try to resolve variable name against input and output parameters and local variables
return PASS1_resolve_variable_name(variables, varName);
}
// Revert parameters order for EXECUTE BLOCK statement
void ExecBlockNode::revertParametersOrder(Array<dsql_par*>& parameters)
{
int start = 0;
int end = int(parameters.getCount()) - 1;
while (start < end)
{
dsql_par* temp = parameters[start];
parameters[start] = parameters[end];
parameters[end] = temp;
++start;
--end;
}
}
//--------------------
void ExitNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
text = "ExitNode";
}
void ExitNode::genBlr()
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
stuff(statement, blr_leave);
stuff(statement, 0);
}
//--------------------
static RegisterNode<PostEventNode> regPostEventNode1(blr_post);
static RegisterNode<PostEventNode> regPostEventNode2(blr_post_arg);
DmlNode* PostEventNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR blrOp)
{
PostEventNode* node = FB_NEW(pool) PostEventNode(pool);
node->event = PAR_parse_node(tdbb, csb, VALUE);
if (blrOp == blr_post_arg)
node->argument = PAR_parse_node(tdbb, csb, VALUE);
return node;
}
PostEventNode* PostEventNode::internalDsqlPass()
{
PostEventNode* node = FB_NEW(getPool()) PostEventNode(getPool());
node->dsqlScratch = dsqlScratch;
node->dsqlEvent = PASS1_node(dsqlScratch, dsqlEvent);
node->dsqlArgument = PASS1_node(dsqlScratch, dsqlArgument);
return node;
}
void PostEventNode::print(string& text, Array<dsql_nod*>& nodes) const
{
text = "PostEventNode";
nodes.add(dsqlEvent);
if (dsqlArgument)
nodes.add(dsqlArgument);
}
void PostEventNode::genBlr()
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
if (dsqlArgument)
{
stuff(statement, blr_post_arg);
GEN_expr(dsqlScratch, dsqlEvent);
GEN_expr(dsqlScratch, dsqlArgument);
}
else
{
stuff(statement, blr_post);
GEN_expr(dsqlScratch, dsqlEvent);
}
}
PostEventNode* PostEventNode::pass1(thread_db* tdbb, CompilerScratch* csb)
{
event = CMP_pass1(tdbb, csb, event);
argument = CMP_pass1(tdbb, csb, argument);
return this;
}
PostEventNode* PostEventNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{
event = CMP_pass2(tdbb, csb, event, node);
argument = CMP_pass2(tdbb, csb, argument, node);
return this;
}
jrd_nod* PostEventNode::execute(thread_db* tdbb, jrd_req* request) const
{
jrd_tra* transaction = request->req_transaction;
DeferredWork* work = DFW_post_work(transaction, dfw_post_event, EVL_expr(tdbb, event), 0);
if (argument)
DFW_post_work_arg(transaction, work, EVL_expr(tdbb, argument), 0);
// For an autocommit transaction, events can be posted without any updates.
if (transaction->tra_flags & TRA_autocommit)
transaction->tra_flags |= TRA_perform_autocommit;
if (request->req_operation == jrd_req::req_evaluate)
request->req_operation = jrd_req::req_return;
return node->nod_parent;
}
//--------------------
static RegisterNode<SavepointNode> regSavepointNode(blr_user_savepoint);
DmlNode* SavepointNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/)
{
SavepointNode* node = FB_NEW(pool) SavepointNode(pool);
node->command = (Command) csb->csb_blr_reader.getByte();
PAR_name(csb, node->name);
return node;
}
SavepointNode* SavepointNode::internalDsqlPass()
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
// ASF: It should never enter in this IF, because the grammar does not allow it.
if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) // blocks, procedures and triggers
{
const char* cmd = NULL;
switch (command)
{
//case CMD_NOTHING:
case CMD_SET:
cmd = "SAVEPOINT";
break;
case CMD_RELEASE:
cmd = "RELEASE";
break;
case CMD_RELEASE_ONLY:
cmd = "RELEASE ONLY";
break;
case CMD_ROLLBACK:
cmd = "ROLLBACK";
break;
default:
cmd = "UNKNOWN";
fb_assert(false);
}
ERRD_post(
Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Token unknown
Arg::Gds(isc_token_err) <<
Arg::Gds(isc_random) << Arg::Str(cmd));
}
statement->setType(DsqlCompiledStatement::TYPE_SAVEPOINT);
return this;
}
void SavepointNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
text = "SavepointNode";
}
void SavepointNode::genBlr()
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
stuff(statement, blr_user_savepoint);
stuff(statement, (UCHAR) command);
stuff_cstring(statement, name.c_str());
}
SavepointNode* SavepointNode::pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
{
return this;
}
SavepointNode* SavepointNode::pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
{
return this;
}
jrd_nod* SavepointNode::execute(thread_db* tdbb, jrd_req* request) const
{
Database* dbb = request->req_attachment->att_database;
jrd_tra* transaction = request->req_transaction;
if (request->req_operation == jrd_req::req_evaluate && transaction != dbb->dbb_sys_trans)
{
// Skip the savepoint created by EXE_start
Savepoint* savepoint = transaction->tra_save_point->sav_next;
Savepoint* previous = transaction->tra_save_point;
// Find savepoint
bool found = false;
while (true)
{
if (!savepoint || !(savepoint->sav_flags & SAV_user))
break;
if (!strcmp(name.c_str(), savepoint->sav_name))
{
found = true;
break;
}
previous = savepoint;
savepoint = savepoint->sav_next;
}
if (!found && command != CMD_SET)
ERR_post(Arg::Gds(isc_invalid_savepoint) << Arg::Str(name));
switch (command)
{
case CMD_SET:
// Release the savepoint
if (found)
{
Savepoint* const current = transaction->tra_save_point;
transaction->tra_save_point = savepoint;
EXE_verb_cleanup(tdbb, transaction);
previous->sav_next = transaction->tra_save_point;
transaction->tra_save_point = current;
}
// Use the savepoint created by EXE_start
transaction->tra_save_point->sav_flags |= SAV_user;
strcpy(transaction->tra_save_point->sav_name, name.c_str());
break;
case CMD_RELEASE_ONLY:
{
// Release the savepoint
Savepoint* const current = transaction->tra_save_point;
transaction->tra_save_point = savepoint;
EXE_verb_cleanup(tdbb, transaction);
previous->sav_next = transaction->tra_save_point;
transaction->tra_save_point = current;
break;
}
case CMD_RELEASE:
{
const SLONG sav_number = savepoint->sav_number;
// Release the savepoint and all subsequent ones
while (transaction->tra_save_point &&
transaction->tra_save_point->sav_number >= sav_number)
{
EXE_verb_cleanup(tdbb, transaction);
}
// Restore the savepoint initially created by EXE_start
VIO_start_save_point(tdbb, transaction);
break;
}
case CMD_ROLLBACK:
{
const SLONG sav_number = savepoint->sav_number;
// Undo the savepoint
while (transaction->tra_save_point &&
transaction->tra_save_point->sav_number >= sav_number)
{
transaction->tra_save_point->sav_verb_count++;
EXE_verb_cleanup(tdbb, transaction);
}
// Now set the savepoint again to allow to return to it later
VIO_start_save_point(tdbb, transaction);
transaction->tra_save_point->sav_flags |= SAV_user;
strcpy(transaction->tra_save_point->sav_name, name.c_str());
break;
}
default:
BUGCHECK(232);
break;
}
}
request->req_operation = jrd_req::req_return;
return node->nod_parent;
}
//--------------------
static RegisterNode<SuspendNode> regSuspendNode(blr_send);
DmlNode* SuspendNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/)
{
SuspendNode* node = FB_NEW(pool) SuspendNode(pool);
USHORT n = csb->csb_blr_reader.getByte();
node->message = csb->csb_rpt[n].csb_message;
node->statement = PAR_parse_node(tdbb, csb, STATEMENT);
return node;
}
SuspendNode* SuspendNode::internalDsqlPass()
{
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
if (dsqlScratch->flags & (DsqlCompilerScratch::FLAG_TRIGGER | DsqlCompilerScratch::FLAG_FUNCTION))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Token unknown
Arg::Gds(isc_token_err) <<
Arg::Gds(isc_random) << Arg::Str("SUSPEND"));
}
if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) // autonomous transaction
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_dsql_unsupported_in_auto_trans) << Arg::Str("SUSPEND"));
}
statement->addFlags(DsqlCompiledStatement::FLAG_SELECTABLE);
blockNode = statement->getBlockNode();
return this;
}
void SuspendNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
{
text = "SuspendNode";
}
void SuspendNode::genBlr()
{
if (blockNode)
blockNode->genReturn();
}
SuspendNode* SuspendNode::pass1(thread_db* tdbb, CompilerScratch* csb)
{
statement = CMP_pass1(tdbb, csb, statement);
return this;
}
SuspendNode* SuspendNode::pass2(thread_db* tdbb, CompilerScratch* csb)
{
statement = CMP_pass2(tdbb, csb, statement, node);
return this;
}
// Execute a SEND statement.
jrd_nod* SuspendNode::execute(thread_db* /*tdbb*/, jrd_req* request) const
{
switch (request->req_operation)
{
case jrd_req::req_evaluate:
return statement;
case jrd_req::req_return:
request->req_operation = jrd_req::req_send;
request->req_message = message;
request->req_flags |= req_stall;
return node;
case jrd_req::req_proceed:
request->req_operation = jrd_req::req_return;
return node->nod_parent;
default:
return node->nod_parent;
}
}
//--------------------
ReturnNode* ReturnNode::internalDsqlPass()
{
DsqlCompiledStatement* const statement = dsqlScratch->getStatement();
if (!(dsqlScratch->flags & DsqlCompilerScratch::FLAG_FUNCTION))
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
// Token unknown
Arg::Gds(isc_token_err) <<
Arg::Gds(isc_random) << Arg::Str("RETURN"));
}
if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) // autonomous transaction
{
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
Arg::Gds(isc_dsql_unsupported_in_auto_trans) << Arg::Str("RETURN"));
}
ReturnNode* node = FB_NEW(getPool()) ReturnNode(getPool());
node->dsqlScratch = dsqlScratch;
node->blockNode = statement->getBlockNode();
node->value = PASS1_node(dsqlScratch, value);
return node;
}
void ReturnNode::print(string& text, Array<dsql_nod*>& nodes) const
{
text = "ReturnNode";
nodes.add(value);
}
void ReturnNode::genBlr()
{
DsqlCompiledStatement* const statement = dsqlScratch->getStatement();
statement->append_uchar(blr_assignment);
GEN_expr(dsqlScratch, value);
statement->append_uchar(blr_variable);
statement->append_ushort(0);
blockNode->genReturn();
statement->append_uchar(blr_leave);
statement->append_uchar(0);
}
} // namespace Jrd