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

Improvement CORE-2005 - Support SQL 2008 syntax for MERGE statement with DELETE extension

This commit is contained in:
asfernandes 2010-03-23 16:06:16 +00:00
parent 4903e17853
commit e0762f5f95
3 changed files with 104 additions and 33 deletions

View File

@ -284,6 +284,7 @@ enum nod_t
nod_merge,
nod_merge_when,
nod_merge_update,
nod_merge_delete,
nod_merge_insert,
nod_sys_function,
nod_similar,
@ -478,10 +479,15 @@ enum node_args {
e_mrg_when_not_matched,
e_mrg_when_count,
e_mrg_update_statement = 0, // nod_merge_update
e_mrg_update_condition = 0, // nod_merge_update
e_mrg_update_statement,
e_mrg_update_count,
e_mrg_insert_fields = 0, // nod_merge_insert
e_mrg_delete_condition = 0, // nod_merge_delete
e_mrg_delete_count,
e_mrg_insert_condition = 0, // nod_merge_insert
e_mrg_insert_fields,
e_mrg_insert_values,
e_mrg_insert_count,

View File

@ -4519,7 +4519,7 @@ insert : INSERT INTO simple_table_name ins_column_parens_opt
// MERGE statement
merge
: MERGE INTO table_name USING table_reference ON search_condition
: MERGE INTO table_name USING table_reference ON search_condition
merge_when_clause
{
$$ = make_node(nod_merge, e_mrg_count, $3, $5, $7, $8);
@ -4527,34 +4527,42 @@ merge
;
merge_when_clause
: merge_when_matched_clause merge_when_not_matched_clause
: merge_when_matched_clause merge_when_not_matched_clause
{ $$ = make_node(nod_merge_when, e_mrg_when_count, $1, $2); }
| merge_when_not_matched_clause merge_when_matched_clause
| merge_when_not_matched_clause merge_when_matched_clause
{ $$ = make_node(nod_merge_when, e_mrg_when_count, $2, $1); }
| merge_when_matched_clause
| merge_when_matched_clause
{ $$ = make_node(nod_merge_when, e_mrg_when_count, $1, NULL); }
| merge_when_not_matched_clause
| merge_when_not_matched_clause
{ $$ = make_node(nod_merge_when, e_mrg_when_count, NULL, $1); }
;
merge_when_matched_clause
: WHEN MATCHED THEN merge_update_specification
{ $$ = $4; }
: WHEN MATCHED merge_update_specification
{ $$ = $3; }
;
merge_when_not_matched_clause
: WHEN NOT MATCHED THEN merge_insert_specification
{ $$ = $5; }
: WHEN NOT MATCHED merge_insert_specification
{ $$ = $4; }
;
merge_update_specification
: UPDATE SET assignments
{ $$ = make_node(nod_merge_update, e_mrg_update_count, make_list($3)); }
: THEN UPDATE SET assignments
{ $$ = make_node(nod_merge_update, e_mrg_update_count, NULL, make_list($4)); }
| AND search_condition THEN UPDATE SET assignments
{ $$ = make_node(nod_merge_update, e_mrg_update_count, $2, make_list($6)); }
| THEN KW_DELETE
{ $$ = make_node(nod_merge_delete, e_mrg_delete_count, NULL); }
| AND search_condition THEN KW_DELETE
{ $$ = make_node(nod_merge_delete, e_mrg_delete_count, $2); }
;
merge_insert_specification
: INSERT ins_column_parens_opt VALUES '(' value_list ')'
{ $$ = make_node(nod_merge_insert, e_mrg_insert_count, make_list($2), make_list($5)); }
: THEN INSERT ins_column_parens_opt VALUES '(' value_list ')'
{ $$ = make_node(nod_merge_insert, e_mrg_insert_count, NULL, make_list($3), make_list($6)); }
| AND search_condition THEN INSERT ins_column_parens_opt VALUES '(' value_list ')'
{ $$ = make_node(nod_merge_insert, e_mrg_insert_count, $2, make_list($5), make_list($8)); }
;

View File

@ -6297,13 +6297,16 @@ static dsql_nod* pass1_merge(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
source = forNode->dsqlSelect->nod_arg[e_select_expr]->nod_arg[0]->nod_arg[e_join_left_rel];
target = forNode->dsqlSelect->nod_arg[e_select_expr]->nod_arg[0]->nod_arg[e_join_rght_rel];
dsql_nod* modify = NULL;
dsql_nod* update = NULL;
if (input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched])
fb_assert(input->nod_arg[e_mrg_when]->nod_type == nod_merge_when);
dsql_nod* whenNode = input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched];
dsql_nod* updateCondition = NULL;
if (whenNode && whenNode->nod_type == nod_merge_update)
{
// get the assignments of the UPDATE dsqlScratch
dsql_nod* list =
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched]->nod_arg[e_mrg_update_statement];
dsql_nod* list = whenNode->nod_arg[e_mrg_update_statement];
fb_assert(list->nod_type == nod_list);
Firebird::Array<dsql_nod*> org_values, new_values;
@ -6318,11 +6321,11 @@ static dsql_nod* pass1_merge(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
}
// build the MODIFY node
modify = MAKE_node(nod_modify_current, e_mdc_count);
update = 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;
update->nod_arg[e_mdc_context] = (dsql_nod*) context;
dsqlScratch->scopeLevel++; // go to the same level of source and target contexts
dsqlScratch->context->push(get_context(source)); // push the USING context
@ -6331,13 +6334,16 @@ static dsql_nod* pass1_merge(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
*ptr = PASS1_node_psql(dsqlScratch, *ptr, false);
updateCondition = PASS1_node_psql(dsqlScratch,
whenNode->nod_arg[e_mrg_update_condition], false);
// and pop the contexts
dsqlScratch->context->pop();
dsqlScratch->context->pop();
dsqlScratch->scopeLevel--;
// process relation
modify->nod_arg[e_mdc_update] = pass1_relation(dsqlScratch, input->nod_arg[e_mrg_relation]);
update->nod_arg[e_mdc_update] = pass1_relation(dsqlScratch, input->nod_arg[e_mrg_relation]);
// process new context values
for (ptr = new_values.begin(); ptr < new_values.end(); ++ptr)
@ -6346,7 +6352,7 @@ static dsql_nod* pass1_merge(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
dsqlScratch->context->pop();
// recreate list of assignments
modify->nod_arg[e_mdc_statement] = list = MAKE_node(nod_list, list->nod_count);
update->nod_arg[e_mdc_statement] = list = MAKE_node(nod_list, list->nod_count);
for (int i = 0; i < list->nod_count; ++i)
{
@ -6360,28 +6366,63 @@ static dsql_nod* pass1_merge(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
}
// We do not allow cases like UPDATE 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");
field_appears_once(update->nod_arg[e_mdc_statement],
whenNode->nod_arg[e_mrg_update_statement], false, "MERGE");
}
else if (whenNode && whenNode->nod_type == nod_merge_delete)
{
// build the DELETE node
update = MAKE_node(nod_erase_current, e_erc_count);
dsql_ctx* context = get_context(target);
update->nod_arg[e_erc_context] = (dsql_nod*) context;
if (whenNode->nod_arg[e_mrg_delete_condition])
{
dsqlScratch->scopeLevel++; // go to the same level of source and target contexts
dsqlScratch->context->push(get_context(source)); // push the USING context
dsqlScratch->context->push(context); // process old context values
updateCondition = PASS1_node_psql(dsqlScratch,
whenNode->nod_arg[e_mrg_delete_condition], false);
// and pop the contexts
dsqlScratch->context->pop();
dsqlScratch->context->pop();
dsqlScratch->scopeLevel--;
}
}
if (updateCondition)
{
IfNode* testNode = FB_NEW(dsqlScratch->getStatement()->getPool()) IfNode(
dsqlScratch->getStatement()->getPool(), dsqlScratch);
testNode->dsqlCondition = updateCondition;
testNode->dsqlTrueAction = update;
update = MAKE_node(nod_class_stmtnode, 1);
update->nod_arg[0] = (dsql_nod*) testNode;
}
whenNode = input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched];
dsql_nod* insert = NULL;
if (input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_not_matched])
if (whenNode)
{
dsqlScratch->scopeLevel++; // go to the same level of the source context
dsqlScratch->context->push(get_context(source)); // push the USING context
dsql_nod* insertCondition = PASS1_node_psql(dsqlScratch,
whenNode->nod_arg[e_mrg_insert_condition], false);
// the INSERT relation should be processed in a higher level than the source context
dsqlScratch->scopeLevel++;
// 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->nod_arg[e_ins_fields] = whenNode->nod_arg[e_mrg_insert_fields];
insert->nod_arg[e_ins_values] = whenNode->nod_arg[e_mrg_insert_values];
insert = pass1_insert(dsqlScratch, insert, false);
// restore the scope level
@ -6390,6 +6431,18 @@ static dsql_nod* pass1_merge(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
// pop the USING context
dsqlScratch->scopeLevel--;
dsqlScratch->context->pop();
if (insertCondition)
{
IfNode* testNode = FB_NEW(dsqlScratch->getStatement()->getPool()) IfNode(
dsqlScratch->getStatement()->getPool(), dsqlScratch);
testNode->dsqlCondition = insertCondition;
testNode->dsqlTrueAction = insert;
insert = MAKE_node(nod_class_stmtnode, 1);
insert->nod_arg[0] = (dsql_nod*) testNode;
}
}
// build a IF (target.RDB$DB_KEY IS NULL)
@ -6403,7 +6456,7 @@ static dsql_nod* pass1_merge(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
if (insert)
{
action->dsqlTrueAction = insert; // then INSERT
action->dsqlFalseAction = modify; // else UPDATE
action->dsqlFalseAction = update; // else UPDATE/DELETE
}
else
{
@ -6412,7 +6465,7 @@ static dsql_nod* pass1_merge(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
not_node->nod_arg[0] = action->dsqlCondition;
action->dsqlCondition = not_node;
action->dsqlTrueAction = modify; // then UPDATE
action->dsqlTrueAction = update; // then UPDATE/DELETE
}
// insert the IF inside the FOR SELECT
@ -10786,6 +10839,10 @@ void DSQL_pretty(const dsql_nod* node, int column)
verb = "merge_update";
break;
case nod_merge_delete:
verb = "merge_delete";
break;
case nod_merge_insert:
verb = "merge_insert";
break;