2008-05-19 15:47:48 +02:00
|
|
|
/*
|
|
|
|
* 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"
|
2010-01-08 15:13:12 +01:00
|
|
|
#include "../common/classes/BaseStream.h"
|
|
|
|
#include "../common/classes/MsgPrint.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../dsql/StmtNodes.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../dsql/node.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../jrd/blr.h"
|
|
|
|
#include "../jrd/tra.h"
|
2010-08-24 05:25:01 +02:00
|
|
|
#include "../jrd/RecordSourceNodes.h"
|
2010-02-22 17:00:49 +01:00
|
|
|
#include "../jrd/recsrc/Cursor.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../jrd/cmp_proto.h"
|
2009-10-24 19:45:33 +02:00
|
|
|
#include "../jrd/dfw_proto.h"
|
|
|
|
#include "../jrd/evl_proto.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../jrd/exe_proto.h"
|
2010-01-05 18:32:42 +01:00
|
|
|
#include "../jrd/met_proto.h"
|
|
|
|
#include "../jrd/mov_proto.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../jrd/par_proto.h"
|
|
|
|
#include "../jrd/tra_proto.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../dsql/ddl_proto.h"
|
2009-08-05 23:36:49 +02:00
|
|
|
#include "../jrd/vio_proto.h"
|
2009-10-24 19:45:33 +02:00
|
|
|
#include "../dsql/errd_proto.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../dsql/gen_proto.h"
|
2009-10-21 02:42:38 +02:00
|
|
|
#include "../dsql/make_proto.h"
|
2008-05-19 15:47:48 +02:00
|
|
|
#include "../dsql/pass1_proto.h"
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
using namespace Firebird;
|
2008-05-19 15:47:48 +02:00
|
|
|
using namespace Jrd;
|
|
|
|
|
|
|
|
#include "gen/blrtable.h"
|
|
|
|
|
|
|
|
|
|
|
|
namespace Jrd {
|
|
|
|
|
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
// Write out field data type.
|
|
|
|
// Taking special care to declare international text.
|
|
|
|
void BlockNode::putDtype(DsqlCompilerScratch* dsqlScratch, const dsql_fld* field, bool useSubType)
|
|
|
|
{
|
|
|
|
#ifdef DEV_BUILD
|
|
|
|
// Check if the field describes a known datatype
|
|
|
|
|
|
|
|
if (field->fld_dtype > FB_NELEM(blr_dtypes) || !blr_dtypes[field->fld_dtype])
|
|
|
|
{
|
|
|
|
SCHAR buffer[100];
|
|
|
|
|
2010-06-30 10:55:16 +02:00
|
|
|
sprintf(buffer, "Invalid dtype %d in BlockNode::putDtype", field->fld_dtype);
|
2010-06-15 18:07:58 +02:00
|
|
|
ERRD_bugcheck(buffer);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (field->fld_not_nullable)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_not_nullable);
|
2010-06-15 18:07:58 +02:00
|
|
|
|
|
|
|
if (field->fld_type_of_name.hasData())
|
|
|
|
{
|
|
|
|
if (field->fld_type_of_table)
|
|
|
|
{
|
|
|
|
if (field->fld_explicit_collation)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_column_name2);
|
|
|
|
dsqlScratch->appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
dsqlScratch->appendMetaString(field->fld_type_of_table->str_data);
|
|
|
|
dsqlScratch->appendMetaString(field->fld_type_of_name.c_str());
|
|
|
|
dsqlScratch->appendUShort(field->fld_ttype);
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_column_name);
|
|
|
|
dsqlScratch->appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
dsqlScratch->appendMetaString(field->fld_type_of_table->str_data);
|
|
|
|
dsqlScratch->appendMetaString(field->fld_type_of_name.c_str());
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (field->fld_explicit_collation)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_domain_name2);
|
|
|
|
dsqlScratch->appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
dsqlScratch->appendMetaString(field->fld_type_of_name.c_str());
|
|
|
|
dsqlScratch->appendUShort(field->fld_ttype);
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_domain_name);
|
|
|
|
dsqlScratch->appendUChar(field->fld_full_domain ? blr_domain_full : blr_domain_type_of);
|
|
|
|
dsqlScratch->appendMetaString(field->fld_type_of_name.c_str());
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (field->fld_dtype)
|
|
|
|
{
|
|
|
|
case dtype_cstring:
|
|
|
|
case dtype_text:
|
|
|
|
case dtype_varying:
|
|
|
|
case dtype_blob:
|
|
|
|
if (!useSubType)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_dtypes[field->fld_dtype]);
|
2010-06-15 18:07:58 +02:00
|
|
|
else if (field->fld_dtype == dtype_varying)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_varying2);
|
|
|
|
dsqlScratch->appendUShort(field->fld_ttype);
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
else if (field->fld_dtype == dtype_cstring)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_cstring2);
|
|
|
|
dsqlScratch->appendUShort(field->fld_ttype);
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
else if (field->fld_dtype == dtype_blob)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_blob2);
|
|
|
|
dsqlScratch->appendUShort(field->fld_sub_type);
|
|
|
|
dsqlScratch->appendUShort(field->fld_ttype);
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_text2);
|
|
|
|
dsqlScratch->appendUShort(field->fld_ttype);
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (field->fld_dtype == dtype_varying)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUShort(field->fld_length - sizeof(USHORT));
|
2010-06-15 18:07:58 +02:00
|
|
|
else if (field->fld_dtype != dtype_blob)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUShort(field->fld_length);
|
2010-06-15 18:07:58 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_dtypes[field->fld_dtype]);
|
2010-06-15 18:07:58 +02:00
|
|
|
if (DTYPE_IS_EXACT(field->fld_dtype) || (dtype_quad == field->fld_dtype))
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(field->fld_scale);
|
2010-06-15 18:07:58 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Emit dyn for the local variables declared in a procedure or trigger.
|
|
|
|
void BlockNode::putLocalVariables(DsqlCompilerScratch* dsqlScratch, const dsql_nod* parameters,
|
|
|
|
SSHORT locals)
|
|
|
|
{
|
|
|
|
if (!parameters)
|
|
|
|
return;
|
|
|
|
|
|
|
|
dsql_nod* const* ptr = parameters->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + parameters->nod_count; ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
dsql_nod* parameter = *ptr;
|
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->putDebugSrcInfo(parameter->nod_line, parameter->nod_column);
|
2010-06-15 18:07:58 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
if (parameter->nod_type == Dsql::nod_def_field)
|
2010-06-15 18:07:58 +02:00
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsql_fld* field = (dsql_fld*) parameter->nod_arg[Dsql::e_dfl_field];
|
2010-06-15 18:07:58 +02:00
|
|
|
const dsql_nod* const* rest = ptr;
|
|
|
|
while (++rest != end)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
if ((*rest)->nod_type == Dsql::nod_def_field)
|
2010-06-15 18:07:58 +02:00
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
const dsql_fld* rest_field = (dsql_fld*) (*rest)->nod_arg[Dsql::e_dfl_field];
|
2010-06-15 18:07:58 +02:00
|
|
|
if (field->fld_name == rest_field->fld_name)
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-637) <<
|
|
|
|
Arg::Gds(isc_dsql_duplicate_spec) << Arg::Str(field->fld_name));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod* var_node = MAKE_variable(field, field->fld_name.c_str(), VAR_local, 0, 0, locals);
|
|
|
|
variables.add(var_node);
|
|
|
|
|
2010-06-16 14:44:43 +02:00
|
|
|
dsql_var* variable = (dsql_var*) var_node->nod_arg[Dsql::e_var_variable];
|
2010-06-15 18:07:58 +02:00
|
|
|
putLocalVariable(dsqlScratch, variable, parameter,
|
2010-06-17 03:18:40 +02:00
|
|
|
reinterpret_cast<const dsql_str*>(parameter->nod_arg[Dsql::e_dfl_collate]));
|
2010-06-15 18:07:58 +02:00
|
|
|
|
|
|
|
// Some field attributes are calculated inside
|
|
|
|
// putLocalVariable(), so we reinitialize the
|
|
|
|
// descriptor
|
|
|
|
MAKE_desc_from_field(&var_node->nod_desc, field);
|
|
|
|
|
|
|
|
locals++;
|
|
|
|
}
|
2010-06-17 03:18:40 +02:00
|
|
|
else if (parameter->nod_type == Dsql::nod_cursor)
|
2010-06-15 18:07:58 +02:00
|
|
|
{
|
|
|
|
PASS1_statement(dsqlScratch, parameter);
|
|
|
|
GEN_statement(dsqlScratch, parameter);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write out local variable field data type.
|
|
|
|
void BlockNode::putLocalVariable(DsqlCompilerScratch* dsqlScratch, dsql_var* variable,
|
|
|
|
dsql_nod* hostParam, const dsql_str* collationName)
|
|
|
|
{
|
|
|
|
dsql_fld* field = variable->var_field;
|
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_dcl_variable);
|
|
|
|
dsqlScratch->appendUShort(variable->var_variable_number);
|
2010-06-15 18:07:58 +02:00
|
|
|
DDL_resolve_intl_type(dsqlScratch, field, collationName);
|
|
|
|
|
|
|
|
//const USHORT dtype = field->fld_dtype;
|
|
|
|
|
|
|
|
putDtype(dsqlScratch, field, true);
|
|
|
|
//field->fld_dtype = dtype;
|
|
|
|
|
|
|
|
// Check for a default value, borrowed from define_domain
|
2010-06-17 03:18:40 +02:00
|
|
|
dsql_nod* node = hostParam ? hostParam->nod_arg[Dsql::e_dfl_default] : NULL;
|
2010-06-15 18:07:58 +02:00
|
|
|
|
|
|
|
if (node || (!field->fld_full_domain && !field->fld_not_nullable))
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_assignment);
|
2010-06-15 18:07:58 +02:00
|
|
|
|
|
|
|
if (node)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
fb_assert(node->nod_type == Dsql::nod_def_default);
|
2010-06-15 18:07:58 +02:00
|
|
|
PsqlChanger psqlChanger(dsqlScratch, false);
|
2010-06-17 03:18:40 +02:00
|
|
|
node = PASS1_node(dsqlScratch, node->nod_arg[Dsql::e_dft_default]);
|
2010-06-15 18:07:58 +02:00
|
|
|
GEN_expr(dsqlScratch, node);
|
|
|
|
}
|
|
|
|
else
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_null); // Initialize variable to NULL
|
2010-06-15 18:07:58 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_variable);
|
|
|
|
dsqlScratch->appendUShort(variable->var_variable_number);
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_init_variable);
|
|
|
|
dsqlScratch->appendUShort(variable->var_variable_number);
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (variable->var_name[0]) // Not a function return value
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->putDebugVariable(variable->var_variable_number, variable->var_name);
|
2010-06-15 18:07:58 +02:00
|
|
|
|
|
|
|
++dsqlScratch->hiddenVarsNumber;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to resolve variable name against parameters and local variables.
|
|
|
|
dsql_nod* BlockNode::resolveVariable(const dsql_str* varName)
|
|
|
|
{
|
|
|
|
for (dsql_nod* const* i = variables.begin(); i != variables.end(); ++i)
|
|
|
|
{
|
|
|
|
dsql_nod* var_node = *i;
|
|
|
|
fb_assert(var_node->nod_type == Dsql::nod_variable);
|
|
|
|
|
|
|
|
if (var_node->nod_type == Dsql::nod_variable)
|
|
|
|
{
|
|
|
|
const dsql_var* variable = (dsql_var*) var_node->nod_arg[Dsql::e_var_variable];
|
|
|
|
DEV_BLKCHK(variable, dsql_type_var);
|
|
|
|
|
|
|
|
if (!strcmp(varName->str_data, variable->var_name))
|
|
|
|
return var_node;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Generate BLR for a return.
|
2010-06-17 03:18:40 +02:00
|
|
|
void BlockNode::genReturn(DsqlCompilerScratch* dsqlScratch, bool eosFlag)
|
2010-06-15 18:07:58 +02:00
|
|
|
{
|
|
|
|
if (hasEos && !eosFlag)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_begin);
|
2010-06-15 18:07:58 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_send);
|
|
|
|
dsqlScratch->appendUChar(1);
|
|
|
|
dsqlScratch->appendUChar(blr_begin);
|
2010-06-15 18:07:58 +02:00
|
|
|
|
|
|
|
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
|
|
|
|
{
|
|
|
|
const dsql_nod* parameter = *i;
|
|
|
|
const dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_assignment);
|
|
|
|
dsqlScratch->appendUChar(blr_variable);
|
|
|
|
dsqlScratch->appendUShort(variable->var_variable_number);
|
|
|
|
dsqlScratch->appendUChar(blr_parameter2);
|
|
|
|
dsqlScratch->appendUChar(variable->var_msg_number);
|
|
|
|
dsqlScratch->appendUShort(variable->var_msg_item);
|
|
|
|
dsqlScratch->appendUShort(variable->var_msg_item + 1);
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hasEos)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_assignment);
|
|
|
|
dsqlScratch->appendUChar(blr_literal);
|
|
|
|
dsqlScratch->appendUChar(blr_short);
|
|
|
|
dsqlScratch->appendUChar(0);
|
|
|
|
dsqlScratch->appendUShort((eosFlag ? 0 : 1));
|
|
|
|
dsqlScratch->appendUChar(blr_parameter);
|
|
|
|
dsqlScratch->appendUChar(1);
|
|
|
|
dsqlScratch->appendUShort(USHORT(2 * outputVariables.getCount()));
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_end);
|
2010-06-15 18:07:58 +02:00
|
|
|
|
|
|
|
if (hasEos && !eosFlag)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_stall);
|
|
|
|
dsqlScratch->appendUChar(blr_end);
|
2010-06-15 18:07:58 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2010-02-21 02:47:54 +01:00
|
|
|
DmlNode* DmlNode::pass1(thread_db* tdbb, CompilerScratch* csb, jrd_nod* aNode)
|
|
|
|
{
|
|
|
|
node = aNode;
|
|
|
|
return pass1(tdbb, csb);
|
|
|
|
}
|
|
|
|
|
2008-05-19 15:47:48 +02:00
|
|
|
DmlNode* DmlNode::pass2(thread_db* tdbb, CompilerScratch* csb, jrd_nod* aNode)
|
|
|
|
{
|
|
|
|
node = aNode;
|
|
|
|
return pass2(tdbb, csb);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2010-01-05 18:32:42 +01:00
|
|
|
StmtNode* SavepointEncloseNode::make(MemoryPool& pool, DsqlCompilerScratch* dsqlScratch, StmtNode* node)
|
|
|
|
{
|
|
|
|
if (dsqlScratch->errorHandlers)
|
|
|
|
{
|
|
|
|
node = FB_NEW(pool) SavepointEncloseNode(pool, node);
|
|
|
|
node->dsqlPass(dsqlScratch);
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SavepointEncloseNode::print(string& text, Array<dsql_nod*>& nodes) const
|
|
|
|
{
|
|
|
|
text = "SavepointEncloseNode\n";
|
|
|
|
string s;
|
|
|
|
stmt->print(s, nodes);
|
|
|
|
text += s;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SavepointEncloseNode::genBlr()
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_begin);
|
|
|
|
dsqlScratch->appendUChar(blr_start_savepoint);
|
2010-01-05 18:32:42 +01:00
|
|
|
stmt->genBlr();
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_end_savepoint);
|
|
|
|
dsqlScratch->appendUChar(blr_end);
|
2010-01-05 18:32:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2009-10-24 19:45:33 +02:00
|
|
|
static RegisterNode<IfNode> regIfNode(blr_if);
|
|
|
|
|
|
|
|
|
2009-11-01 11:58:16 +01:00
|
|
|
DmlNode* IfNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/)
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
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()
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
IfNode* node = FB_NEW(getPool()) IfNode(getPool(), dsqlScratch);
|
2009-12-20 22:01:10 +01:00
|
|
|
node->dsqlCondition = PASS1_node(dsqlScratch, dsqlCondition);
|
|
|
|
node->dsqlTrueAction = PASS1_statement(dsqlScratch, dsqlTrueAction);
|
|
|
|
node->dsqlFalseAction = PASS1_statement(dsqlScratch, dsqlFalseAction);
|
2009-10-24 19:45:33 +02:00
|
|
|
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()
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_if);
|
2009-12-20 22:01:10 +01:00
|
|
|
GEN_expr(dsqlScratch, dsqlCondition);
|
|
|
|
GEN_statement(dsqlScratch, dsqlTrueAction);
|
2009-10-24 19:45:33 +02:00
|
|
|
if (dsqlFalseAction)
|
2009-12-20 22:01:10 +01:00
|
|
|
GEN_statement(dsqlScratch, dsqlFalseAction);
|
2009-10-24 19:45:33 +02:00
|
|
|
else
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_end);
|
2009-10-24 19:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-09 17:48:51 +02:00
|
|
|
const jrd_nod* IfNode::execute(thread_db* tdbb, jrd_req* request) const
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
static RegisterNode<InAutonomousTransactionNode> regInAutonomousTransactionNode(blr_auto_trans);
|
2008-05-19 15:47:48 +02:00
|
|
|
|
|
|
|
|
2009-10-24 19:45:33 +02:00
|
|
|
DmlNode* InAutonomousTransactionNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb,
|
2009-11-01 11:58:16 +01:00
|
|
|
UCHAR /*blrOp*/)
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
|
|
|
InAutonomousTransactionNode* node = FB_NEW(pool) InAutonomousTransactionNode(pool);
|
|
|
|
|
2009-08-02 06:10:07 +02:00
|
|
|
if (csb->csb_blr_reader.getByte() != 0) // Reserved for future improvements. Should be 0 for now.
|
2008-05-19 15:47:48 +02:00
|
|
|
PAR_syntax_error(csb, "0");
|
|
|
|
|
|
|
|
node->action = PAR_parse_node(tdbb, csb, STATEMENT);
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
InAutonomousTransactionNode* InAutonomousTransactionNode::internalDsqlPass()
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
2009-12-20 23:42:43 +01:00
|
|
|
const bool autoTrans = dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK;
|
|
|
|
dsqlScratch->flags |= DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK;
|
2008-05-19 15:47:48 +02:00
|
|
|
|
|
|
|
InAutonomousTransactionNode* node = FB_NEW(getPool()) InAutonomousTransactionNode(getPool());
|
2009-12-20 22:01:10 +01:00
|
|
|
node->dsqlScratch = dsqlScratch;
|
|
|
|
node->dsqlAction = PASS1_statement(dsqlScratch, dsqlAction);
|
2008-05-19 15:47:48 +02:00
|
|
|
|
|
|
|
if (!autoTrans)
|
2009-12-20 23:42:43 +01:00
|
|
|
dsqlScratch->flags &= ~DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK;
|
2008-05-19 15:47:48 +02:00
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
void InAutonomousTransactionNode::print(string& text, Array<dsql_nod*>& nodes) const
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
2009-10-24 19:45:33 +02:00
|
|
|
text = "InAutonomousTransactionNode";
|
2008-05-19 15:47:48 +02:00
|
|
|
nodes.add(dsqlAction);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void InAutonomousTransactionNode::genBlr()
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_auto_trans);
|
|
|
|
dsqlScratch->appendUChar(0); // to extend syntax in the future
|
2009-12-20 22:01:10 +01:00
|
|
|
GEN_statement(dsqlScratch, dsqlAction);
|
2008-05-19 15:47:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
InAutonomousTransactionNode* InAutonomousTransactionNode::pass1(thread_db* tdbb, CompilerScratch* csb)
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
|
|
|
action = CMP_pass1(tdbb, csb, action);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-01-08 10:26:06 +01:00
|
|
|
InAutonomousTransactionNode* InAutonomousTransactionNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
2009-08-06 08:30:35 +02:00
|
|
|
savNumberOffset = CMP_impure(csb, sizeof(SLONG));
|
2008-05-19 15:47:48 +02:00
|
|
|
action = CMP_pass2(tdbb, csb, action, node);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-09 17:48:51 +02:00
|
|
|
const jrd_nod* InAutonomousTransactionNode::execute(thread_db* tdbb, jrd_req* request) const
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
2010-04-02 23:48:15 +02:00
|
|
|
Jrd::Attachment* attachment = request->req_attachment;
|
2010-04-05 23:20:08 +02:00
|
|
|
SLONG* savNumber = request->getImpure<SLONG>(savNumberOffset);
|
2009-08-06 08:30:35 +02:00
|
|
|
|
2008-05-19 15:47:48 +02:00
|
|
|
if (request->req_operation == jrd_req::req_evaluate)
|
|
|
|
{
|
|
|
|
fb_assert(tdbb->getTransaction() == request->req_transaction);
|
|
|
|
|
|
|
|
request->req_auto_trans.push(request->req_transaction);
|
2009-01-08 10:26:06 +01:00
|
|
|
request->req_transaction = TRA_start(tdbb, request->req_transaction->tra_flags,
|
|
|
|
request->req_transaction->tra_lock_timeout,
|
|
|
|
request->req_transaction);
|
2008-05-19 15:47:48 +02:00
|
|
|
tdbb->setTransaction(request->req_transaction);
|
|
|
|
|
2009-08-05 23:36:49 +02:00
|
|
|
VIO_start_save_point(tdbb, request->req_transaction);
|
2009-08-06 08:30:35 +02:00
|
|
|
*savNumber = request->req_transaction->tra_save_point->sav_number;
|
2009-08-05 23:36:49 +02:00
|
|
|
|
2010-04-02 23:48:15 +02:00
|
|
|
if (!(attachment->att_flags & ATT_no_db_triggers))
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
|
|
|
// run ON TRANSACTION START triggers
|
2009-01-08 10:26:06 +01:00
|
|
|
EXE_execute_db_triggers(tdbb, request->req_transaction, jrd_req::req_trigger_trans_start);
|
2008-05-19 15:47:48 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return action;
|
|
|
|
}
|
|
|
|
|
2009-08-05 23:36:49 +02:00
|
|
|
jrd_tra* transaction = request->req_transaction;
|
2010-04-02 23:48:15 +02:00
|
|
|
fb_assert(transaction && transaction != attachment->getSysTransaction());
|
2009-08-05 23:36:49 +02:00
|
|
|
|
2008-05-19 15:47:48 +02:00
|
|
|
switch (request->req_operation)
|
|
|
|
{
|
|
|
|
case jrd_req::req_return:
|
2010-04-02 23:48:15 +02:00
|
|
|
if (!(attachment->att_flags & ATT_no_db_triggers))
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
|
|
|
// run ON TRANSACTION COMMIT triggers
|
2009-08-05 23:36:49 +02:00
|
|
|
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);
|
2008-05-19 15:47:48 +02:00
|
|
|
}
|
2008-12-05 02:20:14 +01:00
|
|
|
|
2008-06-03 08:19:50 +02:00
|
|
|
{ // scope
|
2009-10-21 02:42:38 +02:00
|
|
|
AutoSetRestore2<jrd_req*, thread_db> autoNullifyRequest(
|
2008-06-03 08:19:50 +02:00
|
|
|
tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL);
|
2009-08-05 23:36:49 +02:00
|
|
|
TRA_commit(tdbb, transaction, false);
|
2008-06-03 08:19:50 +02:00
|
|
|
} // end scope
|
2008-05-19 15:47:48 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
case jrd_req::req_unwind:
|
2009-10-21 02:42:38 +02:00
|
|
|
if (request->req_flags & (req_leave | req_continue_loop))
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
2010-04-02 23:48:15 +02:00
|
|
|
if (!(attachment->att_flags & ATT_no_db_triggers))
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
|
|
|
// run ON TRANSACTION COMMIT triggers
|
2009-08-05 23:36:49 +02:00
|
|
|
EXE_execute_db_triggers(tdbb, transaction,
|
2009-01-08 10:26:06 +01:00
|
|
|
jrd_req::req_trigger_trans_commit);
|
2008-05-19 15:47:48 +02:00
|
|
|
}
|
2008-06-01 21:14:41 +02:00
|
|
|
|
2009-08-05 23:36:49 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
AutoSetRestore2<jrd_req*, thread_db> autoNullifyRequest(
|
2008-06-01 21:14:41 +02:00
|
|
|
tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL);
|
2009-08-05 23:36:49 +02:00
|
|
|
TRA_commit(tdbb, transaction, false);
|
2008-05-19 15:47:48 +02:00
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
request->req_flags &= ~(req_leave | req_continue_loop);
|
2008-05-19 15:47:48 +02:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ThreadStatusGuard temp_status(tdbb);
|
|
|
|
|
2010-04-02 23:48:15 +02:00
|
|
|
if (!(attachment->att_flags & ATT_no_db_triggers))
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
|
|
|
try
|
|
|
|
{
|
|
|
|
// run ON TRANSACTION ROLLBACK triggers
|
2009-08-05 23:36:49 +02:00
|
|
|
EXE_execute_db_triggers(tdbb, transaction,
|
2009-01-08 10:26:06 +01:00
|
|
|
jrd_req::req_trigger_trans_rollback);
|
2008-05-19 15:47:48 +02:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
catch (const Exception&)
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
|
|
|
if (tdbb->getDatabase()->dbb_flags & DBB_bugcheck)
|
|
|
|
{
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2009-10-21 02:42:38 +02:00
|
|
|
AutoSetRestore2<jrd_req*, thread_db> autoNullifyRequest(
|
2008-06-01 21:14:41 +02:00
|
|
|
tdbb, &thread_db::getRequest, &thread_db::setRequest, NULL);
|
2009-08-05 23:36:49 +02:00
|
|
|
|
|
|
|
// undo all savepoints up to our one
|
|
|
|
for (const Savepoint* save_point = transaction->tra_save_point;
|
2009-08-06 08:30:35 +02:00
|
|
|
save_point && *savNumber <= save_point->sav_number;
|
2009-08-05 23:36:49 +02:00
|
|
|
save_point = transaction->tra_save_point)
|
|
|
|
{
|
|
|
|
++transaction->tra_save_point->sav_verb_count;
|
|
|
|
VIO_verb_cleanup(tdbb, transaction);
|
|
|
|
}
|
|
|
|
|
|
|
|
TRA_rollback(tdbb, transaction, false, false);
|
2008-05-19 15:47:48 +02:00
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
catch (const Exception&)
|
2008-05-19 15:47:48 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
|
|
|
ExecBlockNode* ExecBlockNode::internalDsqlPass()
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
|
|
|
2009-12-22 16:36:10 +01:00
|
|
|
statement->setBlockNode(this);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
if (returns.hasData())
|
2009-12-23 01:57:08 +01:00
|
|
|
statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK);
|
2009-10-21 02:42:38 +02:00
|
|
|
else
|
2009-12-23 01:57:08 +01:00
|
|
|
statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK);
|
2009-10-30 11:43:42 +01:00
|
|
|
|
2009-12-20 23:42:43 +01:00
|
|
|
dsqlScratch->flags |= DsqlCompilerScratch::FLAG_BLOCK;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
ExecBlockNode* node = FB_NEW(getPool()) ExecBlockNode(getPool());
|
2009-12-20 22:01:10 +01:00
|
|
|
node->dsqlScratch = dsqlScratch;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
for (ParameterClause* param = parameters.begin(); param != parameters.end(); ++param)
|
|
|
|
{
|
|
|
|
PsqlChanger changer(dsqlScratch, false);
|
|
|
|
|
|
|
|
node->parameters.add(*param);
|
|
|
|
ParameterClause& newParam = node->parameters.back();
|
|
|
|
|
|
|
|
newParam.legacyParameter = PASS1_node(dsqlScratch, newParam.legacyParameter);
|
|
|
|
|
|
|
|
if (newParam.legacyDefault)
|
|
|
|
{
|
|
|
|
newParam.legacyDefault->nod_arg[Dsql::e_dft_default] =
|
|
|
|
PASS1_node(dsqlScratch, newParam.legacyDefault->nod_arg[Dsql::e_dft_default]);
|
|
|
|
}
|
|
|
|
|
|
|
|
newParam.resolve(dsqlScratch);
|
|
|
|
newParam.legacyField->fld_id = param - parameters.begin();
|
|
|
|
|
|
|
|
{ // scope
|
|
|
|
dsql_nod* temp = newParam.legacyParameter;
|
|
|
|
DEV_BLKCHK(temp, dsql_type_nod);
|
|
|
|
|
|
|
|
// Initialize this stack variable, and make it look like a node
|
|
|
|
AutoPtr<dsql_nod> desc_node(FB_NEW_RPT(*getDefaultMemoryPool(), 0) dsql_nod);
|
|
|
|
|
|
|
|
newParam.legacyField->fld_flags |= FLD_nullable;
|
|
|
|
MAKE_desc_from_field(&(desc_node->nod_desc), newParam.legacyField);
|
|
|
|
PASS1_set_parameter_type(dsqlScratch, temp, desc_node, false);
|
|
|
|
} // end scope
|
|
|
|
|
|
|
|
if (param != parameters.begin())
|
|
|
|
node->parameters.end()[-2].legacyField->fld_next = newParam.legacyField;
|
|
|
|
}
|
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
node->returns = returns;
|
2010-06-15 18:07:58 +02:00
|
|
|
|
|
|
|
for (size_t i = 0; i < node->returns.getCount(); ++i)
|
|
|
|
{
|
|
|
|
node->returns[i].resolve(dsqlScratch);
|
|
|
|
node->returns[i].legacyField->fld_id = i;
|
|
|
|
|
|
|
|
if (i != 0)
|
|
|
|
node->returns[i - 1].legacyField->fld_next = node->returns[i].legacyField;
|
|
|
|
}
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
node->localDeclList = localDeclList;
|
|
|
|
node->body = body;
|
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
const size_t count = node->parameters.getCount() + node->returns.getCount() +
|
2009-12-19 14:03:18 +01:00
|
|
|
(node->localDeclList ? node->localDeclList->nod_count : 0);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
if (count != 0)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
|
|
|
StrArray names(*getDefaultMemoryPool(), count);
|
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
// Hand-made PASS1_check_unique_fields_names for arrays of ParameterClause
|
2009-11-21 21:42:27 +01:00
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
Array<ParameterClause> params(parameters);
|
|
|
|
params.add(returns.begin(), returns.getCount());
|
|
|
|
|
|
|
|
for (size_t i = 0; i < params.getCount(); ++i)
|
2009-11-21 21:42:27 +01:00
|
|
|
{
|
2010-06-15 18:07:58 +02:00
|
|
|
ParameterClause& parameter = params[i];
|
2009-11-21 21:42:27 +01:00
|
|
|
|
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
PASS1_check_unique_fields_names(names, node->localDeclList);
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExecBlockNode::print(string& text, Array<dsql_nod*>& nodes) const
|
|
|
|
{
|
2009-11-21 21:42:27 +01:00
|
|
|
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";
|
|
|
|
}
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
nodes.add(localDeclList);
|
|
|
|
nodes.add(body);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExecBlockNode::genBlr()
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// Update blockNode, because we have a reference to the original unprocessed node.
|
2009-12-22 16:36:10 +01:00
|
|
|
statement->setBlockNode(this);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->beginDebug();
|
2009-10-21 02:42:38 +02:00
|
|
|
|
|
|
|
// now do the input parameters
|
2010-06-15 18:07:58 +02:00
|
|
|
for (size_t i = 0; i < parameters.getCount(); ++i)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2010-06-15 18:07:58 +02:00
|
|
|
ParameterClause& parameter = parameters[i];
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
dsql_nod* var = MAKE_variable(parameter.legacyField,
|
2010-06-17 03:18:40 +02:00
|
|
|
parameter.name.c_str(), VAR_input, 0, (USHORT) (2 * i), 0);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-15 18:07:58 +02:00
|
|
|
variables.add(var);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2009-11-22 11:08:13 +01:00
|
|
|
const unsigned returnsPos = variables.getCount();
|
2009-11-21 21:42:27 +01:00
|
|
|
|
2009-10-21 02:42:38 +02:00
|
|
|
// now do the output parameters
|
2009-11-21 21:42:27 +01:00
|
|
|
for (size_t i = 0; i < returns.getCount(); ++i)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-11-21 21:42:27 +01:00
|
|
|
ParameterClause& parameter = returns[i];
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
dsql_nod* var = MAKE_variable(parameter.legacyField,
|
2010-06-17 03:18:40 +02:00
|
|
|
parameter.name.c_str(), VAR_output, 1, (USHORT) (2 * i), i);
|
2009-11-21 21:42:27 +01:00
|
|
|
|
|
|
|
variables.add(var);
|
|
|
|
outputVariables.add(var);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_begin);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
if (parameters.hasData())
|
2009-11-17 09:18:54 +01:00
|
|
|
{
|
2009-12-22 16:36:10 +01:00
|
|
|
revertParametersOrder(statement->getSendMsg()->msg_parameters);
|
|
|
|
GEN_port(dsqlScratch, statement->getSendMsg());
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
else
|
2009-12-22 16:36:10 +01:00
|
|
|
statement->setSendMsg(NULL);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-22 16:36:10 +01:00
|
|
|
dsql_par* param = MAKE_parameter(statement->getReceiveMsg(), true, true,
|
2009-11-21 21:42:27 +01:00
|
|
|
(i - outputVariables.begin()) + 1, *i);
|
|
|
|
param->par_node = *i;
|
2009-12-20 22:01:10 +01:00
|
|
|
MAKE_desc(dsqlScratch, ¶m->par_desc, *i, NULL);
|
2009-11-21 21:42:27 +01:00
|
|
|
param->par_desc.dsc_flags |= DSC_nullable;
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set up parameter to handle EOF
|
2009-12-22 16:36:10 +01:00
|
|
|
dsql_par* param = MAKE_parameter(statement->getReceiveMsg(), false, false, 0, NULL);
|
|
|
|
statement->setEof(param);
|
2009-10-21 02:42:38 +02:00
|
|
|
param->par_desc.dsc_dtype = dtype_short;
|
|
|
|
param->par_desc.dsc_scale = 0;
|
|
|
|
param->par_desc.dsc_length = sizeof(SSHORT);
|
|
|
|
|
2009-12-22 16:36:10 +01:00
|
|
|
revertParametersOrder(statement->getReceiveMsg()->msg_parameters);
|
|
|
|
GEN_port(dsqlScratch, statement->getReceiveMsg());
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
if (parameters.hasData())
|
2009-11-17 09:18:54 +01:00
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_receive);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_begin);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
for (unsigned i = 0; i < returnsPos; ++i)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-11-21 21:42:27 +01:00
|
|
|
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;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
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.
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_assignment);
|
|
|
|
dsqlScratch->appendUChar(blr_cast);
|
2010-06-15 18:07:58 +02:00
|
|
|
BlockNode::putDtype(dsqlScratch, field, true);
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_parameter2);
|
|
|
|
dsqlScratch->appendUChar(0);
|
|
|
|
dsqlScratch->appendUShort(variable->var_msg_item);
|
|
|
|
dsqlScratch->appendUShort(variable->var_msg_item + 1);
|
|
|
|
dsqlScratch->appendUChar(blr_null);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-21 21:42:27 +01:00
|
|
|
for (Array<dsql_nod*>::const_iterator i = outputVariables.begin(); i != outputVariables.end(); ++i)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-11-21 21:42:27 +01:00
|
|
|
dsql_nod* parameter = *i;
|
|
|
|
dsql_var* variable = (dsql_var*) parameter->nod_arg[Dsql::e_var_variable];
|
2010-06-15 18:07:58 +02:00
|
|
|
putLocalVariable(dsqlScratch, variable, 0, NULL);
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->setPsql(true);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
putLocalVariables(dsqlScratch, localDeclList, USHORT(returns.getCount()));
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsqlScratch->loopLevel = 0;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
dsql_nod* stmtNode = PASS1_statement(dsqlScratch, body);
|
|
|
|
GEN_hidden_variables(dsqlScratch, false);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_stall);
|
2009-10-21 02:42:38 +02:00
|
|
|
// Put a label before body of procedure, so that
|
|
|
|
// any exit statement can get out
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_label);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-12-20 22:01:10 +01:00
|
|
|
GEN_statement(dsqlScratch, stmtNode);
|
2010-06-17 03:18:40 +02:00
|
|
|
if (returns.hasData())
|
2009-12-23 01:57:08 +01:00
|
|
|
statement->setType(DsqlCompiledStatement::TYPE_SELECT_BLOCK);
|
2009-10-21 02:42:38 +02:00
|
|
|
else
|
2009-12-23 01:57:08 +01:00
|
|
|
statement->setType(DsqlCompiledStatement::TYPE_EXEC_BLOCK);
|
2009-10-30 11:43:42 +01:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_end);
|
|
|
|
genReturn(dsqlScratch, true);
|
|
|
|
dsqlScratch->appendUChar(blr_end);
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->endDebug();
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Revert parameters order for EXECUTE BLOCK statement
|
2009-12-19 19:50:38 +01:00
|
|
|
void ExecBlockNode::revertParametersOrder(Array<dsql_par*>& parameters)
|
2009-10-21 02:42:38 +02:00
|
|
|
{
|
2009-12-19 19:50:38 +01:00
|
|
|
int start = 0;
|
|
|
|
int end = int(parameters.getCount()) - 1;
|
2009-10-21 02:42:38 +02:00
|
|
|
|
2009-12-19 19:50:38 +01:00
|
|
|
while (start < end)
|
|
|
|
{
|
|
|
|
dsql_par* temp = parameters[start];
|
|
|
|
parameters[start] = parameters[end];
|
|
|
|
parameters[end] = temp;
|
|
|
|
++start;
|
|
|
|
--end;
|
|
|
|
}
|
2009-10-21 02:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-24 19:45:33 +02:00
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2010-01-05 18:32:42 +01:00
|
|
|
static RegisterNode<ExceptionNode> regExceptionNode(blr_abort);
|
|
|
|
|
|
|
|
|
|
|
|
DmlNode* ExceptionNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb,
|
|
|
|
UCHAR /*blrOp*/)
|
|
|
|
{
|
|
|
|
ExceptionNode* node = FB_NEW(pool) ExceptionNode(pool);
|
2010-01-07 01:16:50 +01:00
|
|
|
const UCHAR type = csb->csb_blr_reader.peekByte();
|
2010-01-05 18:32:42 +01:00
|
|
|
const USHORT codeType = csb->csb_blr_reader.getByte();
|
|
|
|
|
|
|
|
// Don't create PsqlException if blr_raise is used.
|
|
|
|
if (codeType != blr_raise)
|
|
|
|
{
|
|
|
|
node->exception = FB_NEW_RPT(pool, 1) PsqlException();
|
|
|
|
node->exception->xcp_count = 1;
|
|
|
|
xcp_repeat& item = node->exception->xcp_rpt[0];
|
|
|
|
|
|
|
|
switch (codeType)
|
|
|
|
{
|
|
|
|
case blr_sql_code:
|
|
|
|
item.xcp_type = xcp_sql_code;
|
|
|
|
item.xcp_code = (SSHORT) csb->csb_blr_reader.getWord();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_gds_code:
|
|
|
|
{
|
|
|
|
string exName;
|
|
|
|
item.xcp_type = xcp_gds_code;
|
|
|
|
PAR_name(csb, exName);
|
|
|
|
exName.lower();
|
|
|
|
SLONG code_number = PAR_symbol_to_gdscode(exName);
|
|
|
|
if (code_number)
|
|
|
|
item.xcp_code = code_number;
|
|
|
|
else
|
|
|
|
PAR_error(csb, Arg::Gds(isc_codnotdef) << Arg::Str(exName));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case blr_exception:
|
|
|
|
case blr_exception_msg:
|
2010-01-07 01:16:50 +01:00
|
|
|
case blr_exception_params:
|
2010-01-05 18:32:42 +01:00
|
|
|
{
|
|
|
|
item.xcp_type = xcp_xcp_code;
|
|
|
|
PAR_name(csb, node->name);
|
|
|
|
if (!(item.xcp_code = MET_lookup_exception_number(tdbb, node->name)))
|
|
|
|
PAR_error(csb, Arg::Gds(isc_xcpnotdef) << Arg::Str(node->name));
|
2010-08-11 04:59:33 +02:00
|
|
|
|
|
|
|
CompilerScratch::Dependency dependency(obj_exception);
|
|
|
|
dependency.number = item.xcp_code;
|
|
|
|
csb->csb_dependencies.push(dependency);
|
2010-01-05 18:32:42 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-01-07 01:16:50 +01:00
|
|
|
if (type == blr_exception_params)
|
|
|
|
{
|
2010-01-15 05:57:57 +01:00
|
|
|
const USHORT count = csb->csb_blr_reader.getWord();
|
2010-01-07 01:16:50 +01:00
|
|
|
|
|
|
|
node->parameters = PAR_make_node(tdbb, count);
|
|
|
|
node->parameters->nod_type = nod_list;
|
|
|
|
node->parameters->nod_count = count;
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < count; ++i)
|
|
|
|
node->parameters->nod_arg[i] = PAR_parse_node(tdbb, csb, VALUE);
|
|
|
|
}
|
|
|
|
else if (type == blr_exception_msg)
|
2010-01-05 18:32:42 +01:00
|
|
|
node->messageExpr = PAR_parse_node(tdbb, csb, VALUE);
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
StmtNode* ExceptionNode::internalDsqlPass()
|
|
|
|
{
|
|
|
|
ExceptionNode* node = FB_NEW(getPool()) ExceptionNode(getPool());
|
|
|
|
node->dsqlScratch = dsqlScratch;
|
|
|
|
node->name = name;
|
2010-01-06 23:38:20 +01:00
|
|
|
node->dsqlMessageExpr = PASS1_node(dsqlScratch, dsqlMessageExpr);
|
2010-01-07 01:16:50 +01:00
|
|
|
node->dsqlParameters = PASS1_node(dsqlScratch, dsqlParameters);
|
2010-01-05 18:32:42 +01:00
|
|
|
|
|
|
|
return SavepointEncloseNode::make(getPool(), dsqlScratch, node);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExceptionNode::print(string& text, Array<dsql_nod*>& nodes) const
|
|
|
|
{
|
|
|
|
text.printf("ExceptionNode: Name: %s", name.c_str());
|
|
|
|
if (dsqlMessageExpr)
|
|
|
|
nodes.add(dsqlMessageExpr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExceptionNode::genBlr()
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_abort);
|
2010-01-05 18:32:42 +01:00
|
|
|
|
|
|
|
// If exception name is undefined, it means we have re-initiate semantics here,
|
|
|
|
// so blr_raise verb should be generated.
|
|
|
|
if (name.isEmpty())
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_raise);
|
2010-01-05 18:32:42 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-01-15 05:57:57 +01:00
|
|
|
// If exception value is defined, it means we have user-defined exception message
|
2010-01-07 01:16:50 +01:00
|
|
|
// here, so blr_exception_msg verb should be generated.
|
|
|
|
if (dsqlParameters)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_exception_params);
|
2010-01-07 01:16:50 +01:00
|
|
|
else if (dsqlMessageExpr)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_exception_msg);
|
2010-01-05 18:32:42 +01:00
|
|
|
else // Otherwise go usual way, i.e. generate blr_exception.
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_exception);
|
2010-01-05 18:32:42 +01:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendNullString(name.c_str());
|
2010-01-05 18:32:42 +01:00
|
|
|
|
2010-01-07 01:16:50 +01:00
|
|
|
// If exception parameters or value is defined, generate appropriate BLR verbs.
|
|
|
|
if (dsqlParameters)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUShort(dsqlParameters->nod_count);
|
2010-01-07 01:16:50 +01:00
|
|
|
|
|
|
|
dsql_nod** ptr = dsqlParameters->nod_arg;
|
|
|
|
const dsql_nod* const* end = ptr + dsqlParameters->nod_count;
|
|
|
|
while (ptr < end)
|
|
|
|
GEN_expr(dsqlScratch, *ptr++);
|
|
|
|
}
|
|
|
|
else if (dsqlMessageExpr)
|
2010-01-05 18:32:42 +01:00
|
|
|
GEN_expr(dsqlScratch, dsqlMessageExpr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ExceptionNode* ExceptionNode::pass1(thread_db* tdbb, CompilerScratch* csb)
|
|
|
|
{
|
|
|
|
messageExpr = CMP_pass1(tdbb, csb, messageExpr);
|
2010-01-07 01:16:50 +01:00
|
|
|
parameters = CMP_pass1(tdbb, csb, parameters);
|
2010-01-05 18:32:42 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ExceptionNode* ExceptionNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
|
|
|
{
|
|
|
|
messageExpr = CMP_pass2(tdbb, csb, messageExpr, node);
|
2010-01-07 01:16:50 +01:00
|
|
|
parameters = CMP_pass2(tdbb, csb, parameters, node);
|
2010-01-05 18:32:42 +01:00
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-09 17:48:51 +02:00
|
|
|
const jrd_nod* ExceptionNode::execute(thread_db* tdbb, jrd_req* request) const
|
2010-01-05 18:32:42 +01:00
|
|
|
{
|
|
|
|
if (request->req_operation == jrd_req::req_evaluate)
|
|
|
|
{
|
|
|
|
if (exception)
|
|
|
|
{
|
|
|
|
// PsqlException is defined, so throw an exception.
|
|
|
|
setError(tdbb);
|
|
|
|
}
|
|
|
|
else if (!request->req_last_xcp.success())
|
|
|
|
{
|
|
|
|
// PsqlException is undefined, but there was a known exception before,
|
|
|
|
// so re-initiate it.
|
|
|
|
setError(tdbb);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// PsqlException is undefined and there weren't any exceptions before,
|
|
|
|
// so just do nothing.
|
|
|
|
request->req_operation = jrd_req::req_return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return node->nod_parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Set status vector according to specified error condition and jump to handle error accordingly.
|
|
|
|
void ExceptionNode::setError(thread_db* tdbb) const
|
|
|
|
{
|
|
|
|
SET_TDBB(tdbb);
|
|
|
|
|
|
|
|
jrd_req* request = tdbb->getRequest();
|
|
|
|
|
|
|
|
if (!exception)
|
|
|
|
{
|
|
|
|
// Retrieve the status vector and punt.
|
|
|
|
request->req_last_xcp.copyTo(tdbb->tdbb_status_vector);
|
|
|
|
request->req_last_xcp.clear();
|
|
|
|
ERR_punt();
|
|
|
|
}
|
|
|
|
|
|
|
|
MetaName exName;
|
|
|
|
MetaName relationName;
|
|
|
|
TEXT message[XCP_MESSAGE_LENGTH + 1];
|
|
|
|
MoveBuffer temp;
|
|
|
|
USHORT length = 0;
|
|
|
|
|
|
|
|
if (messageExpr)
|
|
|
|
{
|
|
|
|
UCHAR* string = NULL;
|
|
|
|
|
|
|
|
// Evaluate exception message and convert it to string.
|
|
|
|
dsc* desc = EVL_expr(tdbb, messageExpr);
|
|
|
|
if (desc && !(request->req_flags & req_null))
|
|
|
|
{
|
|
|
|
length = MOV_make_string2(tdbb, desc, CS_METADATA, &string, temp);
|
|
|
|
length = MIN(length, sizeof(message) - 1);
|
|
|
|
|
|
|
|
/* dimitr: or should we throw an error here, i.e.
|
|
|
|
replace the above assignment with the following lines:
|
|
|
|
|
|
|
|
if (length > sizeof(message) - 1)
|
|
|
|
ERR_post(Arg::Gds(isc_imp_exc) << Arg::Gds(isc_blktoobig));
|
|
|
|
*/
|
|
|
|
|
|
|
|
memcpy(message, string, length);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
length = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
message[length] = 0;
|
|
|
|
|
|
|
|
SLONG xcpCode = exception->xcp_rpt[0].xcp_code;
|
|
|
|
|
|
|
|
switch (exception->xcp_rpt[0].xcp_type)
|
|
|
|
{
|
|
|
|
case xcp_sql_code:
|
|
|
|
ERR_post(Arg::Gds(isc_sqlerr) << Arg::Num(xcpCode));
|
|
|
|
|
|
|
|
case xcp_gds_code:
|
|
|
|
if (xcpCode == isc_check_constraint)
|
|
|
|
{
|
2010-04-19 00:19:11 +02:00
|
|
|
MET_lookup_cnstrt_for_trigger(tdbb, exName, relationName,
|
|
|
|
request->getStatement()->triggerName);
|
2010-01-05 18:32:42 +01:00
|
|
|
ERR_post(Arg::Gds(xcpCode) << Arg::Str(exName) << Arg::Str(relationName));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ERR_post(Arg::Gds(xcpCode));
|
|
|
|
|
|
|
|
case xcp_xcp_code:
|
|
|
|
{
|
|
|
|
string tempStr;
|
|
|
|
const TEXT* s;
|
|
|
|
|
|
|
|
// CVC: If we have the exception name, use it instead of the number.
|
|
|
|
// Solves SF Bug #494981.
|
|
|
|
MET_lookup_exception(tdbb, xcpCode, exName, &tempStr);
|
|
|
|
|
|
|
|
if (message[0])
|
|
|
|
s = message;
|
|
|
|
else if (tempStr.hasData())
|
|
|
|
s = tempStr.c_str();
|
|
|
|
else
|
|
|
|
s = NULL;
|
|
|
|
|
2010-01-07 01:16:50 +01:00
|
|
|
Arg::StatusVector status;
|
|
|
|
ISC_STATUS msgCode = parameters ? isc_formatted_exception : isc_random;
|
|
|
|
|
|
|
|
if (s && exName.hasData())
|
2010-01-05 18:32:42 +01:00
|
|
|
{
|
2010-01-07 01:16:50 +01:00
|
|
|
status << Arg::Gds(isc_except) << Arg::Num(xcpCode) <<
|
|
|
|
Arg::Gds(isc_random) << Arg::Str(exName) <<
|
2010-01-08 15:13:12 +01:00
|
|
|
Arg::Gds(msgCode);
|
2010-01-05 18:32:42 +01:00
|
|
|
}
|
|
|
|
else if (s)
|
2010-01-07 01:16:50 +01:00
|
|
|
status << Arg::Gds(isc_except) << Arg::Num(xcpCode) <<
|
2010-01-08 15:13:12 +01:00
|
|
|
Arg::Gds(msgCode);
|
2010-01-07 01:16:50 +01:00
|
|
|
else if (exName.hasData())
|
2010-01-05 18:32:42 +01:00
|
|
|
{
|
|
|
|
ERR_post(Arg::Gds(isc_except) << Arg::Num(xcpCode) <<
|
|
|
|
Arg::Gds(isc_random) << Arg::Str(exName));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
ERR_post(Arg::Gds(isc_except) << Arg::Num(xcpCode));
|
2010-01-07 01:16:50 +01:00
|
|
|
|
2010-01-08 15:13:12 +01:00
|
|
|
// Preallocate objects, because Arg::StatusVector store pointers.
|
|
|
|
string formattedMsg;
|
2010-01-07 01:16:50 +01:00
|
|
|
ObjectsArray<string> paramsStr;
|
|
|
|
|
|
|
|
if (parameters)
|
|
|
|
{
|
|
|
|
for (unsigned i = 0; i < parameters->nod_count; ++i)
|
|
|
|
{
|
|
|
|
const dsc* value = EVL_expr(tdbb, parameters->nod_arg[i]);
|
|
|
|
|
|
|
|
if (!value || (request->req_flags & req_null))
|
|
|
|
paramsStr.push(NULL_STRING_MARK);
|
|
|
|
else
|
|
|
|
paramsStr.push(MOV_make_string2(tdbb, value, ttype_metadata));
|
|
|
|
}
|
|
|
|
|
2010-01-08 15:13:12 +01:00
|
|
|
// And add the values to the args and status vector only after they are all created
|
|
|
|
// and will not move in paramsStr.
|
|
|
|
|
|
|
|
MsgFormat::SafeArg arg;
|
|
|
|
for (unsigned i = 0; i < parameters->nod_count; ++i)
|
|
|
|
arg << paramsStr[i].c_str();
|
|
|
|
|
|
|
|
MsgFormat::StringRefStream stream(formattedMsg);
|
|
|
|
MsgFormat::MsgPrint(stream, s, arg, true);
|
|
|
|
|
|
|
|
status << formattedMsg;
|
|
|
|
|
2010-01-07 01:16:50 +01:00
|
|
|
for (unsigned i = 0; i < parameters->nod_count; ++i)
|
|
|
|
status << paramsStr[i];
|
|
|
|
}
|
2010-01-08 15:13:12 +01:00
|
|
|
else
|
|
|
|
status << s; // add the exception text
|
2010-01-07 01:16:50 +01:00
|
|
|
|
|
|
|
ERR_post(status);
|
2010-01-05 18:32:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
fb_assert(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void ExitNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
text = "ExitNode";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ExitNode::genBlr()
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_leave);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-10-24 19:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2010-02-22 17:00:49 +01:00
|
|
|
static RegisterNode<ForNode> regForNode(blr_for);
|
|
|
|
|
|
|
|
DmlNode* ForNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR blrOp)
|
|
|
|
{
|
|
|
|
ForNode* node = FB_NEW(pool) ForNode(pool);
|
|
|
|
|
|
|
|
if (csb->csb_blr_reader.peekByte() == (UCHAR) blr_stall)
|
|
|
|
node->stall = PAR_parse_node(tdbb, csb, STATEMENT);
|
|
|
|
|
|
|
|
if (csb->csb_blr_reader.peekByte() == (UCHAR) blr_rse ||
|
|
|
|
csb->csb_blr_reader.peekByte() == (UCHAR) blr_singular ||
|
|
|
|
csb->csb_blr_reader.peekByte() == (UCHAR) blr_scrollable)
|
|
|
|
{
|
2010-08-24 05:25:01 +02:00
|
|
|
node->rse = RseNode::getFrom(PAR_parse_node(tdbb, csb, TYPE_RSE));
|
2010-02-22 17:00:49 +01:00
|
|
|
}
|
|
|
|
else
|
2010-08-24 05:25:01 +02:00
|
|
|
node->rse = RseNode::getFrom(PAR_rse(tdbb, csb, blrOp));
|
2010-02-22 17:00:49 +01:00
|
|
|
|
|
|
|
node->statement = PAR_parse_node(tdbb, csb, STATEMENT);
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
StmtNode* ForNode::internalDsqlPass()
|
|
|
|
{
|
|
|
|
ForNode* node = FB_NEW(getPool()) ForNode(getPool());
|
|
|
|
node->dsqlScratch = dsqlScratch;
|
|
|
|
|
|
|
|
node->dsqlCursor = dsqlCursor;
|
|
|
|
node->dsqlSelect = PASS1_statement(dsqlScratch, dsqlSelect);
|
|
|
|
|
|
|
|
if (dsqlCursor)
|
|
|
|
{
|
|
|
|
fb_assert(dsqlCursor->nod_flags > 0);
|
|
|
|
PASS1_cursor_name(dsqlScratch, (dsql_str*) dsqlCursor->nod_arg[Dsql::e_cur_name],
|
|
|
|
NOD_CURSOR_ALL, false);
|
|
|
|
dsqlCursor->nod_arg[Dsql::e_cur_rse] = node->dsqlSelect;
|
|
|
|
dsqlCursor->nod_arg[Dsql::e_cur_number] = (dsql_nod*) (IPTR) dsqlScratch->cursorNumber++;
|
|
|
|
dsqlScratch->cursors.push(dsqlCursor);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dsqlInto)
|
|
|
|
{
|
|
|
|
node->dsqlInto = MAKE_node(dsqlInto->nod_type, dsqlInto->nod_count);
|
|
|
|
const dsql_nod** ptr2 = const_cast<const dsql_nod**>(node->dsqlInto->nod_arg);
|
|
|
|
dsql_nod** ptr = dsqlInto->nod_arg;
|
|
|
|
for (const dsql_nod* const* const end = ptr + dsqlInto->nod_count; ptr < end; ptr++)
|
|
|
|
{
|
|
|
|
DEV_BLKCHK(*ptr, dsql_type_nod);
|
|
|
|
*ptr2++ = PASS1_node(dsqlScratch, *ptr);
|
|
|
|
DEV_BLKCHK(*(ptr2 - 1), dsql_type_nod);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dsqlAction)
|
|
|
|
{
|
|
|
|
// CVC: Let's add the ability to BREAK the for_select same as the while,
|
|
|
|
// but only if the command is FOR SELECT, otherwise we have singular SELECT
|
|
|
|
dsqlScratch->loopLevel++;
|
|
|
|
node->dsqlLabel = PASS1_label2(dsqlScratch, NULL, dsqlLabel);
|
|
|
|
node->dsqlAction = PASS1_statement(dsqlScratch, dsqlAction);
|
|
|
|
dsqlScratch->loopLevel--;
|
|
|
|
dsqlScratch->labels.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dsqlCursor)
|
|
|
|
{
|
|
|
|
dsqlScratch->cursorNumber--;
|
|
|
|
dsqlScratch->cursors.pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ForNode::print(string& text, Array<dsql_nod*>& nodes) const
|
|
|
|
{
|
|
|
|
text = "ForNode";
|
|
|
|
nodes.add(dsqlSelect);
|
|
|
|
nodes.add(dsqlInto);
|
|
|
|
nodes.add(dsqlCursor);
|
|
|
|
nodes.add(dsqlAction);
|
|
|
|
nodes.add(dsqlLabel);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ForNode::genBlr()
|
|
|
|
{
|
|
|
|
// CVC: Only put a label if this is not singular; otherwise,
|
|
|
|
// what loop is the user trying to abandon?
|
|
|
|
if (dsqlAction)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_label);
|
2010-06-22 02:53:35 +02:00
|
|
|
dsqlScratch->appendUChar((int)(IPTR) dsqlLabel->nod_arg[Dsql::e_label_number]);
|
2010-02-22 17:00:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Generate FOR loop
|
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_for);
|
2010-02-22 17:00:49 +01:00
|
|
|
|
2010-06-10 04:03:03 +02:00
|
|
|
if (!dsqlAction || dsqlForceSingular)
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_singular);
|
2010-02-22 17:00:49 +01:00
|
|
|
|
|
|
|
GEN_rse(dsqlScratch, dsqlSelect);
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_begin);
|
2010-02-22 17:00:49 +01:00
|
|
|
|
|
|
|
// Build body of FOR loop
|
|
|
|
|
|
|
|
dsql_nod* list = dsqlSelect->nod_arg[Dsql::e_rse_items];
|
|
|
|
|
|
|
|
if (dsqlInto)
|
|
|
|
{
|
|
|
|
if (list->nod_count != dsqlInto->nod_count)
|
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-313) <<
|
|
|
|
Arg::Gds(isc_dsql_count_mismatch));
|
|
|
|
}
|
|
|
|
|
|
|
|
dsql_nod** ptr = list->nod_arg;
|
|
|
|
dsql_nod** ptr_to = dsqlInto->nod_arg;
|
|
|
|
|
|
|
|
for (const dsql_nod* const* const end = ptr + list->nod_count; ptr < end; ptr++, ptr_to++)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_assignment);
|
2010-02-22 17:00:49 +01:00
|
|
|
GEN_expr(dsqlScratch, *ptr);
|
|
|
|
GEN_expr(dsqlScratch, *ptr_to);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dsqlAction)
|
|
|
|
GEN_statement(dsqlScratch, dsqlAction);
|
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_end);
|
2010-02-22 17:00:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
StmtNode* ForNode::pass1(thread_db* tdbb, CompilerScratch* csb)
|
|
|
|
{
|
|
|
|
stall = CMP_pass1(tdbb, csb, stall);
|
2010-08-24 05:25:01 +02:00
|
|
|
rse->pass1(tdbb, csb, csb->csb_view);
|
2010-02-22 17:00:49 +01:00
|
|
|
statement = CMP_pass1(tdbb, csb, statement);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
StmtNode* ForNode::pass2(thread_db* tdbb, CompilerScratch* csb)
|
|
|
|
{
|
|
|
|
stall = CMP_pass2(tdbb, csb, stall, node);
|
2010-08-24 05:25:01 +02:00
|
|
|
rse->pass2(tdbb, csb);
|
2010-02-22 17:00:49 +01:00
|
|
|
statement = CMP_pass2(tdbb, csb, statement, node);
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
2010-08-09 17:48:51 +02:00
|
|
|
const jrd_nod* ForNode::execute(thread_db* tdbb, jrd_req* request) const
|
2010-02-22 17:00:49 +01:00
|
|
|
{
|
|
|
|
switch (request->req_operation)
|
|
|
|
{
|
|
|
|
case jrd_req::req_evaluate:
|
|
|
|
cursor->open(tdbb);
|
|
|
|
request->req_records_affected.clear();
|
|
|
|
// fall into
|
|
|
|
|
|
|
|
case jrd_req::req_return:
|
|
|
|
if (stall)
|
|
|
|
return stall;
|
|
|
|
// fall into
|
|
|
|
|
|
|
|
case jrd_req::req_sync:
|
|
|
|
if (cursor->fetchNext(tdbb))
|
|
|
|
{
|
|
|
|
request->req_operation = jrd_req::req_evaluate;
|
|
|
|
return statement;
|
|
|
|
}
|
|
|
|
request->req_operation = jrd_req::req_return;
|
|
|
|
// fall into
|
|
|
|
|
|
|
|
case jrd_req::req_unwind:
|
|
|
|
{
|
2010-08-09 17:48:51 +02:00
|
|
|
const jrd_nod* parent = node->nod_parent;
|
2010-02-22 17:00:49 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fall into
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
cursor->close(tdbb);
|
|
|
|
return node->nod_parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb_assert(false);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
2009-10-24 19:45:33 +02:00
|
|
|
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());
|
2009-12-20 22:01:10 +01:00
|
|
|
node->dsqlScratch = dsqlScratch;
|
2009-10-24 19:45:33 +02:00
|
|
|
|
2009-12-20 22:01:10 +01:00
|
|
|
node->dsqlEvent = PASS1_node(dsqlScratch, dsqlEvent);
|
|
|
|
node->dsqlArgument = PASS1_node(dsqlScratch, dsqlArgument);
|
2009-10-24 19:45:33 +02:00
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
|
|
|
if (dsqlArgument)
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_post_arg);
|
2009-12-20 22:01:10 +01:00
|
|
|
GEN_expr(dsqlScratch, dsqlEvent);
|
|
|
|
GEN_expr(dsqlScratch, dsqlArgument);
|
2009-10-24 19:45:33 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_post);
|
2009-12-20 22:01:10 +01:00
|
|
|
GEN_expr(dsqlScratch, dsqlEvent);
|
2009-10-24 19:45:33 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-09 17:48:51 +02:00
|
|
|
const jrd_nod* PostEventNode::execute(thread_db* tdbb, jrd_req* request) const
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
2009-11-01 11:58:16 +01:00
|
|
|
DmlNode* SavepointNode::parse(thread_db* /*tdbb*/, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/)
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
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()
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
|
|
|
2009-10-24 19:45:33 +02:00
|
|
|
// ASF: It should never enter in this IF, because the grammar does not allow it.
|
2009-12-20 23:42:43 +01:00
|
|
|
if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_BLOCK) // blocks, procedures and triggers
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
2009-10-31 02:46:06 +01:00
|
|
|
const char* cmd = NULL;
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
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";
|
2009-10-31 02:46:06 +01:00
|
|
|
fb_assert(false);
|
2009-10-30 11:43:42 +01:00
|
|
|
}
|
|
|
|
|
2009-10-24 19:45:33 +02:00
|
|
|
ERRD_post(
|
|
|
|
Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Token unknown
|
|
|
|
Arg::Gds(isc_token_err) <<
|
2009-11-17 09:18:54 +01:00
|
|
|
Arg::Gds(isc_random) << Arg::Str(cmd));
|
2009-10-24 19:45:33 +02:00
|
|
|
}
|
|
|
|
|
2009-12-23 01:57:08 +01:00
|
|
|
statement->setType(DsqlCompiledStatement::TYPE_SAVEPOINT);
|
2009-10-24 19:45:33 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void SavepointNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
text = "SavepointNode";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SavepointNode::genBlr()
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_user_savepoint);
|
|
|
|
dsqlScratch->appendUChar((UCHAR) command);
|
|
|
|
dsqlScratch->appendNullString(name.c_str());
|
2009-10-24 19:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-01 11:58:16 +01:00
|
|
|
SavepointNode* SavepointNode::pass1(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-01 11:58:16 +01:00
|
|
|
SavepointNode* SavepointNode::pass2(thread_db* /*tdbb*/, CompilerScratch* /*csb*/)
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-08-09 17:48:51 +02:00
|
|
|
const jrd_nod* SavepointNode::execute(thread_db* tdbb, jrd_req* request) const
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
jrd_tra* transaction = request->req_transaction;
|
|
|
|
|
2010-04-02 23:48:15 +02:00
|
|
|
if (request->req_operation == jrd_req::req_evaluate &&
|
|
|
|
transaction != request->req_attachment->getSysTransaction())
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
|
2009-11-01 11:58:16 +01:00
|
|
|
DmlNode* SuspendNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, UCHAR /*blrOp*/)
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
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()
|
|
|
|
{
|
2009-12-20 22:01:10 +01:00
|
|
|
DsqlCompiledStatement* statement = dsqlScratch->getStatement();
|
|
|
|
|
2009-12-22 01:08:49 +01:00
|
|
|
if (dsqlScratch->flags & (DsqlCompilerScratch::FLAG_TRIGGER | DsqlCompilerScratch::FLAG_FUNCTION))
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) <<
|
|
|
|
// Token unknown
|
|
|
|
Arg::Gds(isc_token_err) <<
|
|
|
|
Arg::Gds(isc_random) << Arg::Str("SUSPEND"));
|
|
|
|
}
|
|
|
|
|
2009-12-20 23:42:43 +01:00
|
|
|
if (dsqlScratch->flags & DsqlCompilerScratch::FLAG_IN_AUTO_TRANS_BLOCK) // autonomous transaction
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) <<
|
|
|
|
Arg::Gds(isc_dsql_unsupported_in_auto_trans) << Arg::Str("SUSPEND"));
|
|
|
|
}
|
|
|
|
|
2009-12-24 13:56:31 +01:00
|
|
|
statement->addFlags(DsqlCompiledStatement::FLAG_SELECTABLE);
|
2009-10-24 19:45:33 +02:00
|
|
|
|
2009-12-22 16:36:10 +01:00
|
|
|
blockNode = statement->getBlockNode();
|
2009-10-24 19:45:33 +02:00
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-10-30 11:43:42 +01:00
|
|
|
void SuspendNode::print(string& text, Array<dsql_nod*>& /*nodes*/) const
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
text = "SuspendNode";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void SuspendNode::genBlr()
|
|
|
|
{
|
|
|
|
if (blockNode)
|
2010-06-17 03:18:40 +02:00
|
|
|
blockNode->genReturn(dsqlScratch);
|
2009-10-24 19:45:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
2010-08-09 17:48:51 +02:00
|
|
|
const jrd_nod* SuspendNode::execute(thread_db* /*tdbb*/, jrd_req* request) const
|
2009-10-24 19:45:33 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-21 18:23:07 +01:00
|
|
|
//--------------------
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2009-12-22 16:36:10 +01:00
|
|
|
node->blockNode = statement->getBlockNode();
|
2009-12-21 18:23:07 +01:00
|
|
|
node->value = PASS1_node(dsqlScratch, value);
|
|
|
|
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-12-22 01:08:49 +01:00
|
|
|
void ReturnNode::print(string& text, Array<dsql_nod*>& nodes) const
|
2009-12-21 18:23:07 +01:00
|
|
|
{
|
|
|
|
text = "ReturnNode";
|
2009-12-22 01:08:49 +01:00
|
|
|
nodes.add(value);
|
2009-12-21 18:23:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ReturnNode::genBlr()
|
|
|
|
{
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_assignment);
|
2009-12-21 18:23:07 +01:00
|
|
|
GEN_expr(dsqlScratch, value);
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_variable);
|
|
|
|
dsqlScratch->appendUShort(0);
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
blockNode->genReturn(dsqlScratch);
|
2009-12-21 18:23:07 +01:00
|
|
|
|
2010-06-17 03:18:40 +02:00
|
|
|
dsqlScratch->appendUChar(blr_leave);
|
|
|
|
dsqlScratch->appendUChar(0);
|
2009-12-21 18:23:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-05-19 15:47:48 +02:00
|
|
|
} // namespace Jrd
|