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

Allow only one of WHEN MATCHED and WHEN NOT MATCHED in MERGE as the SQL spec

This commit is contained in:
asfernandes 2006-09-17 01:41:24 +00:00
parent b4b17a5b73
commit f2e24565f0
2 changed files with 112 additions and 85 deletions

View File

@ -3417,6 +3417,10 @@ merge_when_clause
{ $$ = make_node(nod_merge_when, e_mrg_when_count, $1, $2); }
| merge_when_not_matched_clause merge_when_matched_clause
{ $$ = make_node(nod_merge_when, e_mrg_when_count, $2, $1); }
| merge_when_matched_clause
{ $$ = make_node(nod_merge_when, e_mrg_when_count, $1, NULL); }
| merge_when_not_matched_clause
{ $$ = make_node(nod_merge_when, e_mrg_when_count, NULL, $1); }
;
merge_when_matched_clause

View File

@ -6284,104 +6284,127 @@ static dsql_nod* pass1_merge(dsql_req* request, dsql_nod* input, bool proc_flag)
source = for_select->nod_arg[e_flp_select]->nod_arg[e_select_expr]->nod_arg[e_join_left_rel];
target = for_select->nod_arg[e_flp_select]->nod_arg[e_select_expr]->nod_arg[e_join_rght_rel];
// get the assignments of the UPDATE statement
dsql_nod* list =
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched]->nod_arg[e_mrg_update_statement];
fb_assert(list->nod_type == nod_list);
dsql_nod* modify = NULL;
Firebird::Array<dsql_nod*> org_values, new_values;
int i;
// separate the new and org values to process in correct contexts
for (i = 0; i < list->nod_count; ++i)
if (input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched])
{
const dsql_nod* const assign = list->nod_arg[i];
fb_assert(assign->nod_type == nod_assign);
org_values.add(assign->nod_arg[0]);
new_values.add(assign->nod_arg[1]);
// get the assignments of the UPDATE statement
dsql_nod* list =
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched]->nod_arg[e_mrg_update_statement];
fb_assert(list->nod_type == nod_list);
Firebird::Array<dsql_nod*> org_values, new_values;
int i;
// separate the new and org values to process in correct contexts
for (i = 0; i < list->nod_count; ++i)
{
const dsql_nod* const assign = list->nod_arg[i];
fb_assert(assign->nod_type == nod_assign);
org_values.add(assign->nod_arg[0]);
new_values.add(assign->nod_arg[1]);
}
// build the MODIFY node
modify = MAKE_node(nod_modify_current, e_mdc_count);
dsql_ctx* context = get_context(target);
dsql_nod** ptr;
modify->nod_arg[e_mdc_context] = (dsql_nod*) context;
// push the USING context
request->req_context->push(get_context(source));
request->req_scope_level++;
// process old context values
request->req_context->push(context);
request->req_scope_level++;
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
*ptr = PASS1_node(request, *ptr, false);
request->req_scope_level--;
request->req_context->pop();
// pop the USING context
request->req_scope_level--;
request->req_context->pop();
// process relation
modify->nod_arg[e_mdc_update] = pass1_relation(request, input->nod_arg[e_mrg_relation]);
// process new context values
for (ptr = new_values.begin(); ptr < new_values.end(); ++ptr)
*ptr = PASS1_node(request, *ptr, false);
request->req_context->pop();
// recreate list of assignments
modify->nod_arg[e_mdc_statement] = list =
MAKE_node(nod_list, list->nod_count);
for (i = 0; i < list->nod_count; ++i)
{
dsql_nod* assign = MAKE_node(nod_assign, 2);
assign->nod_arg[0] = org_values[i];
assign->nod_arg[1] = new_values[i];
list->nod_arg[i] = assign;
}
// We do not allow cases like UPDATE T SET f1 = v1, f2 = v2, f1 = v3...
field_appears_once(modify->nod_arg[e_mdc_statement],
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched]->nod_arg[e_mrg_update_statement],
false, "MERGE");
}
// build the MODIFY node
dsql_nod* modify = MAKE_node(nod_modify_current, e_mdc_count);
dsql_ctx* context = get_context(target);
dsql_nod** ptr;
dsql_nod* insert = NULL;
modify->nod_arg[e_mdc_context] = (dsql_nod*) context;
// push the USING context
request->req_context->push(get_context(source));
request->req_scope_level++;
// process old context values
request->req_context->push(context);
request->req_scope_level++;
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
*ptr = PASS1_node(request, *ptr, false);
request->req_scope_level--;
request->req_context->pop();
// pop the USING context
request->req_scope_level--;
request->req_context->pop();
// process relation
modify->nod_arg[e_mdc_update] = pass1_relation(request, input->nod_arg[e_mrg_relation]);
// process new context values
for (ptr = new_values.begin(); ptr < new_values.end(); ++ptr)
*ptr = PASS1_node(request, *ptr, false);
request->req_context->pop();
// recreate list of assignments
modify->nod_arg[e_mdc_statement] = list =
MAKE_node(nod_list, list->nod_count);
for (i = 0; i < list->nod_count; ++i)
if (input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched])
{
dsql_nod* assign = MAKE_node(nod_assign, 2);
assign->nod_arg[0] = org_values[i];
assign->nod_arg[1] = new_values[i];
list->nod_arg[i] = assign;
// push the USING context
request->req_context->push(get_context(source));
request->req_scope_level++;
// the INSERT relation should be processed in a higher level than the source
request->req_scope_level++;
// build the INSERT node
insert = MAKE_node(nod_insert, e_ins_count);
insert->nod_arg[e_ins_relation] = input->nod_arg[e_mrg_relation];
insert->nod_arg[e_ins_fields] =
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched]->nod_arg[e_mrg_insert_fields];
insert->nod_arg[e_ins_values] =
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched]->nod_arg[e_mrg_insert_values];
insert = pass1_insert(request, insert, proc_flag);
// restore the scope level
request->req_scope_level--;
// pop the USING context
request->req_scope_level--;
request->req_context->pop();
}
// We do not allow cases like UPDATE T SET f1 = v1, f2 = v2, f1 = v3...
field_appears_once(modify->nod_arg[e_mdc_statement],
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched]->nod_arg[e_mrg_update_statement],
false, "MERGE");
// push the USING context
request->req_context->push(get_context(source));
request->req_scope_level++;
// the INSERT relation should be processed in a higher level than the source
request->req_scope_level++;
// build the INSERT node
dsql_nod* insert = MAKE_node(nod_insert, e_ins_count);
insert->nod_arg[e_ins_relation] = input->nod_arg[e_mrg_relation];
insert->nod_arg[e_ins_fields] =
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched]->nod_arg[e_mrg_insert_fields];
insert->nod_arg[e_ins_values] =
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched]->nod_arg[e_mrg_insert_values];
insert = pass1_insert(request, insert, proc_flag);
// restore the scope level
request->req_scope_level--;
// pop the USING context
request->req_scope_level--;
request->req_context->pop();
// build a IF (target.RDB$DB_KEY IS NULL)
dsql_nod* action = MAKE_node(nod_if, e_if_count);
action->nod_arg[e_if_condition] = MAKE_node(nod_missing, 1);
action->nod_arg[e_if_condition]->nod_arg[0] = MAKE_node(nod_dbkey, 1);
action->nod_arg[e_if_condition]->nod_arg[0]->nod_arg[0] = target;
action->nod_arg[e_if_true] = insert; // then INSERT
action->nod_arg[e_if_false] = modify; // else UPDATE
if (insert)
{
action->nod_arg[e_if_true] = insert; // then INSERT
action->nod_arg[e_if_false] = modify; // else UPDATE
}
else
{
// negate the condition -> IF (target.RDB$DB_KEY IS NOT NULL)
dsql_nod* not_node = MAKE_node(nod_not, 1);
not_node->nod_arg[0] = action->nod_arg[e_if_condition];
action->nod_arg[e_if_condition] = not_node;
action->nod_arg[e_if_true] = modify; // then UPDATE
}
// insert the IF inside the FOR SELECT
for_select->nod_arg[e_flp_action] = action;