8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 23:23:04 +01:00

Fixed CORE-2041 - update or insert with gen_id() with wrong generator value

This commit is contained in:
asfernandes 2008-08-15 16:32:42 +00:00
parent 28209cb815
commit 5d4084d30c
7 changed files with 125 additions and 34 deletions

View File

@ -2640,14 +2640,18 @@ static void define_procedure(CompiledStatement* statement, NOD_TYPE op)
put_local_variables(statement, procedure_node->nod_arg[e_prc_dcls], locals);
statement->req_loop_level = 0;
statement->req_cursor_number = 0;
dsql_nod* stmtNode = PASS1_statement(statement, procedure_node->nod_arg[e_prc_body]);
GEN_hidden_variables(statement);
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);
statement->req_loop_level = 0;
statement->req_cursor_number = 0;
GEN_statement(statement, PASS1_statement(statement, procedure_node->nod_arg[e_prc_body]));
GEN_statement(statement, stmtNode);
statement->req_type = REQ_DDL;
statement->append_uchar(blr_end);
GEN_return(statement, procedure_node->nod_arg[e_prc_outputs], true);
@ -2789,13 +2793,17 @@ void DDL_gen_block(CompiledStatement* statement, dsql_nod* node)
put_local_variables(statement, node->nod_arg[e_exe_blk_dcls], locals);
statement->req_loop_level = 0;
dsql_nod* stmtNode = PASS1_statement(statement, node->nod_arg[e_exe_blk_body]);
GEN_hidden_variables(statement);
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);
statement->req_loop_level = 0;
GEN_statement(statement, PASS1_statement(statement, node->nod_arg[e_exe_blk_body]));
GEN_statement(statement, stmtNode);
if (outputs)
statement->req_type = REQ_SELECT_BLOCK;
else
@ -3238,6 +3246,9 @@ static void define_trigger(CompiledStatement* statement, NOD_TYPE op)
trigger_node->nod_arg[e_trg_actions]->nod_arg[e_trg_act_dcls], 0);
statement->req_scope_level++;
statement->req_loop_level = 0;
statement->req_cursor_number = 0;
actions = PASS1_statement(statement, actions);
// dimitr: I see no reason to deny EXIT command in triggers,
// hence I've added zero label at the beginning.
// My first suspicion regarding an obvious conflict
@ -3247,9 +3258,8 @@ static void define_trigger(CompiledStatement* statement, NOD_TYPE op)
// Hopefully, system triggers are never recompiled.
statement->append_uchar(blr_label);
statement->append_uchar(0);
statement->req_loop_level = 0;
statement->req_cursor_number = 0;
GEN_statement(statement, PASS1_statement(statement, actions));
GEN_hidden_variables(statement);
GEN_statement(statement, actions);
statement->req_scope_level--;
statement->append_uchar(blr_end);
statement->end_blr();
@ -6209,6 +6219,8 @@ static void put_local_variable( CompiledStatement* statement, dsql_var* variable
statement->put_debug_variable(variable->var_variable_number,
variable->var_name);
++statement->req_hidden_vars_number;
}

View File

@ -433,6 +433,7 @@ public:
req_dt_context(p),
req_labels(p),
req_cursors(p),
req_hidden_vars(p),
req_curr_ctes(p),
req_ctes(p),
req_cte_aliases(p)
@ -539,6 +540,8 @@ public:
USHORT req_client_dialect; //!< dialect passed into the API call
USHORT req_in_outer_join; //!< processing inside outer-join part
dsql_str* req_alias_relation_prefix; //!< prefix for every relation-alias.
DsqlNodStack req_hidden_vars; // hidden variables
USHORT req_hidden_vars_number; // next hidden variable number
DsqlNodStack req_curr_ctes; // current processing CTE's
class dsql_ctx* req_recursive_ctx; // context of recursive CTE

View File

@ -67,7 +67,6 @@ static void gen_cast(CompiledStatement*, const dsql_nod*);
static void gen_coalesce(CompiledStatement*, const dsql_nod*);
static void gen_constant(CompiledStatement*, const dsc*, bool);
static void gen_constant(CompiledStatement*, dsql_nod*, bool);
static void gen_descriptor(CompiledStatement*, const dsc*, bool);
static void gen_error_condition(CompiledStatement*, const dsql_nod*);
static void gen_exec_stmt(CompiledStatement* statement, const dsql_nod* node);
static void gen_field(CompiledStatement*, const dsql_ctx*, const dsql_fld*, dsql_nod*);
@ -103,6 +102,29 @@ const bool NEGATE_VALUE = true;
const bool USE_VALUE = false;
void GEN_hidden_variables(CompiledStatement* statement)
{
/**************************************
*
* G E N _ h i d d e n _ v a r i a b l e s
*
**************************************
*
* Function
* Emit BLR for hidden variables.
*
**************************************/
for (DsqlNodStack::const_iterator i(statement->req_hidden_vars);
i.hasData(); ++i)
{
dsql_var* var = ((dsql_var*) i.object()->nod_arg[e_var_variable]);
statement->append_uchar(blr_dcl_variable);
statement->append_ushort(var->var_variable_number);
GEN_descriptor(statement, &i.object()->nod_desc, true);
}
}
/**
GEN_expr
@ -694,7 +716,7 @@ void GEN_port(CompiledStatement* statement, dsql_msg* message)
parameter->par_desc.dsc_address = (UCHAR*)(IPTR) offset;
offset += parameter->par_desc.dsc_length;
// if (statement->req_blr_string)
gen_descriptor(statement, &parameter->par_desc, false);
GEN_descriptor(statement, &parameter->par_desc, false);
}
if (offset > MAX_FORMAT_SIZE) {
@ -758,6 +780,8 @@ void GEN_request( CompiledStatement* statement, dsql_nod* node)
{
stuff(statement, blr_begin);
GEN_hidden_variables(statement);
switch (statement->req_type)
{
case REQ_SELECT:
@ -1033,12 +1057,14 @@ void GEN_statement( CompiledStatement* statement, dsql_nod* node)
return;
case nod_list:
if (!(node->nod_flags & NOD_SIMPLE_LIST))
stuff(statement, blr_begin);
for (ptr = node->nod_arg, end = ptr + node->nod_count; ptr < end;
ptr++)
{
GEN_statement(statement, *ptr);
}
if (!(node->nod_flags & NOD_SIMPLE_LIST))
stuff(statement, blr_end);
return;
@ -1382,7 +1408,7 @@ static void gen_coalesce( CompiledStatement* statement, const dsql_nod* node)
// blr_value_if is used for building the coalesce function
dsql_nod* list = node->nod_arg[0];
stuff(statement, blr_cast);
gen_descriptor(statement, &node->nod_desc, true);
GEN_descriptor(statement, &node->nod_desc, true);
dsql_nod* const* ptr = list->nod_arg;
for (const dsql_nod* const* const end = ptr + (list->nod_count - 1);
ptr < end; ptr++)
@ -1427,7 +1453,7 @@ static void gen_constant( CompiledStatement* statement, const dsc* desc, bool ne
switch (desc->dsc_dtype) {
case dtype_short:
gen_descriptor(statement, desc, true);
GEN_descriptor(statement, desc, true);
value = *(SSHORT *) p;
if (negate_value)
value = -value;
@ -1435,7 +1461,7 @@ static void gen_constant( CompiledStatement* statement, const dsc* desc, bool ne
break;
case dtype_long:
gen_descriptor(statement, desc, true);
GEN_descriptor(statement, desc, true);
value = *(SLONG *) p;
if (negate_value)
value = -value;
@ -1446,7 +1472,7 @@ static void gen_constant( CompiledStatement* statement, const dsc* desc, bool ne
case dtype_sql_time:
case dtype_sql_date:
gen_descriptor(statement, desc, true);
GEN_descriptor(statement, desc, true);
value = *(SLONG *) p;
stuff_word(statement, value);
stuff_word(statement, value >> 16);
@ -1457,7 +1483,7 @@ static void gen_constant( CompiledStatement* statement, const dsc* desc, bool ne
/* this is used for approximate/large numeric literal
which is transmitted to the engine as a string.
*/
gen_descriptor(statement, desc, true);
GEN_descriptor(statement, desc, true);
// Length of string literal, cast because it could be > 127 bytes.
const USHORT l = (USHORT)(UCHAR) desc->dsc_scale;
if (negate_value) {
@ -1521,7 +1547,7 @@ static void gen_constant( CompiledStatement* statement, const dsc* desc, bool ne
case dtype_blob:
case dtype_array:
case dtype_timestamp:
gen_descriptor(statement, desc, true);
GEN_descriptor(statement, desc, true);
value = *(SLONG *) p;
stuff_word(statement, value);
stuff_word(statement, value >> 16);
@ -1534,7 +1560,7 @@ static void gen_constant( CompiledStatement* statement, const dsc* desc, bool ne
{
const USHORT length = desc->dsc_length;
gen_descriptor(statement, desc, true);
GEN_descriptor(statement, desc, true);
if (length)
statement->append_raw_string(p, length);
}
@ -1571,7 +1597,7 @@ static void gen_constant( CompiledStatement* statement, dsql_nod* node, bool neg
/**
gen_descriptor
GEN_descriptor
@brief Generate a blr descriptor from an internal descriptor.
@ -1581,7 +1607,7 @@ static void gen_constant( CompiledStatement* statement, dsql_nod* node, bool neg
@param texttype
**/
static void gen_descriptor( CompiledStatement* statement, const dsc* desc, bool texttype)
void GEN_descriptor( CompiledStatement* statement, const dsc* desc, bool texttype)
{
switch (desc->dsc_dtype) {
case dtype_text:
@ -2417,7 +2443,7 @@ static void gen_searched_case( CompiledStatement* statement, const dsql_nod* nod
// blr_value_if is used for building the case expression
stuff(statement, blr_cast);
gen_descriptor(statement, &node->nod_desc, true);
GEN_descriptor(statement, &node->nod_desc, true);
const SSHORT count =
node->nod_arg[e_searched_case_search_conditions]->nod_count;
dsql_nod* boolean_list = node->nod_arg[e_searched_case_search_conditions];
@ -2637,7 +2663,7 @@ static void gen_simple_case( CompiledStatement* statement, const dsql_nod* node)
// blr_value_if is used for building the case expression
stuff(statement, blr_cast);
gen_descriptor(statement, &node->nod_desc, true);
GEN_descriptor(statement, &node->nod_desc, true);
SSHORT count = node->nod_arg[e_simple_case_when_operands]->nod_count;
dsql_nod* when_list = node->nod_arg[e_simple_case_when_operands];
dsql_nod* results_list = node->nod_arg[e_simple_case_results];

View File

@ -24,7 +24,9 @@
#ifndef DSQL_GEN_PROTO_H
#define DSQL_GEN_PROTO_H
void GEN_descriptor(Jrd::CompiledStatement* statement, const dsc* desc, bool texttype);
void GEN_expr(Jrd::CompiledStatement*, Jrd::dsql_nod*);
void GEN_hidden_variables(Jrd::CompiledStatement* statement);
void GEN_port(Jrd::CompiledStatement*, Jrd::dsql_msg*);
void GEN_request(Jrd::CompiledStatement*, Jrd::dsql_nod*);
void GEN_return(Jrd::CompiledStatement*, const Jrd::dsql_nod*, bool);

View File

@ -1836,6 +1836,7 @@ dsql_nod* MAKE_variable(dsql_fld* field, const TEXT* name, const dsql_var_type t
strcpy(variable->var_name, name);
//variable->var_flags = 0;
variable->var_type = type;
if (field)
MAKE_desc_from_field(&node->nod_desc, field);
return node;
@ -2006,6 +2007,7 @@ static void make_parameter_names(dsql_par* parameter, const dsql_nod* item)
case nod_variable:
{
dsql_var* variable = (dsql_var*) item->nod_arg[e_var_variable];
if (variable->var_field)
name_alias = variable->var_field->fld_name.c_str();
break;
}

View File

@ -1043,6 +1043,7 @@ enum nod_flags_vals {
NOD_UNION_ALL = 1, // nod_list
NOD_UNION_RECURSIVE = 2,
NOD_SIMPLE_LIST = 4, // no need to enclose with blr_begin ... blr_end
NOD_READ_ONLY = 1, // nod_access
NOD_READ_WRITE = 2,

View File

@ -241,6 +241,7 @@ static bool pass1_found_aggregate(const dsql_nod*, USHORT, USHORT, bool);
static bool pass1_found_field(const dsql_nod*, USHORT, USHORT, bool*);
static bool pass1_found_sub_select(const dsql_nod*);
static dsql_nod* pass1_group_by_list(CompiledStatement*, dsql_nod*, dsql_nod*);
static dsql_nod* pass1_hidden_variable(CompiledStatement* statement, dsql_nod* value);
static dsql_nod* pass1_insert(CompiledStatement*, dsql_nod*, bool);
static dsql_nod* pass1_join(CompiledStatement*, dsql_nod*);
static dsql_nod* pass1_label(CompiledStatement*, dsql_nod*);
@ -6155,6 +6156,30 @@ static dsql_nod* pass1_group_by_list(CompiledStatement* statement, dsql_nod* inp
}
// Create (if necessary) a hidden variable to store a temporary value.
static dsql_nod* pass1_hidden_variable(CompiledStatement* statement, dsql_nod* value)
{
// For some node types, it's better to not create temporary value.
switch (value->nod_type)
{
case nod_constant:
case nod_dbkey:
case nod_field:
case nod_parameter:
case nod_user_name:
case nod_variable:
return NULL;
}
dsql_nod* var = MAKE_variable(NULL, "", VAR_local, 0, 0, statement->req_hidden_vars_number++);
MAKE_desc(statement, &var->nod_desc, value, NULL);
statement->req_hidden_vars.push(var);
return var;
}
/**
pass1_insert
@ -9286,6 +9311,8 @@ static dsql_nod* pass1_update_or_insert(CompiledStatement* statement, dsql_nod*
dsql_nod* match = NULL;
USHORT match_count = 0;
DsqlNodStack varStack;
DsqlNodStack stack;
dsql_nod** field_ptr = fields->nod_arg;
dsql_nod** value_ptr = values->nod_arg;
@ -9296,12 +9323,12 @@ static dsql_nod* pass1_update_or_insert(CompiledStatement* statement, dsql_nod*
DEV_BLKCHK(*field_ptr, dsql_type_nod);
DEV_BLKCHK(*value_ptr, dsql_type_nod);
dsql_nod* temp = MAKE_node(nod_assign, e_asgn_count);
temp->nod_arg[e_asgn_value] = *value_ptr;
temp->nod_arg[e_asgn_field] = *field_ptr;
stack.push(temp);
dsql_nod* assign = MAKE_node(nod_assign, e_asgn_count);
assign->nod_arg[e_asgn_value] = *value_ptr;
assign->nod_arg[e_asgn_field] = *field_ptr;
stack.push(assign);
temp = *value_ptr;
dsql_nod* temp = *value_ptr;
dsql_nod* temp2 = insert->nod_arg[e_sto_statement]->nod_arg[field_ptr - fields->nod_arg]->nod_arg[1];
set_parameter_type(statement, temp, temp2, false);
@ -9337,9 +9364,25 @@ static dsql_nod* pass1_update_or_insert(CompiledStatement* statement, dsql_nod*
{
++match_count;
dsql_nod*& expr = insert->nod_arg[e_sto_statement]->nod_arg[
field_ptr - fields->nod_arg]->nod_arg[0];
dsql_nod* var = pass1_hidden_variable(statement, expr);
if (var)
{
temp = MAKE_node(nod_assign, e_asgn_count);
temp->nod_arg[e_asgn_value] = expr;
temp->nod_arg[e_asgn_field] = var;
varStack.push(temp);
assign->nod_arg[e_asgn_value] = expr = var;
}
else
var = *value_ptr;
dsql_nod* eql = MAKE_node(equality_type, 2);
eql->nod_arg[0] = *field_ptr;
eql->nod_arg[1] = *value_ptr;
eql->nod_arg[1] = var;
if (match)
{
@ -9424,9 +9467,11 @@ static dsql_nod* pass1_update_or_insert(CompiledStatement* statement, dsql_nod*
if_nod->nod_arg[e_if_true] = insert;
// build the UPDATE / IF nodes
dsql_nod* list = MAKE_node(nod_list, 2);
list->nod_arg[0] = update;
list->nod_arg[1] = if_nod;
dsql_nod* list = MAKE_node(nod_list, 3);
list->nod_arg[0] = MAKE_list(varStack);
list->nod_arg[0]->nod_flags |= NOD_SIMPLE_LIST;
list->nod_arg[1] = update;
list->nod_arg[2] = if_nod;
// if RETURNING is present, req_type is already REQ_EXEC_PROCEDURE
if (!input->nod_arg[e_upi_return])