8
0
mirror of https://github.com/FirebirdSQL/firebird.git synced 2025-01-23 22:43:04 +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,
nod_merge_when, nod_merge_when,
nod_merge_update, nod_merge_update,
nod_merge_delete,
nod_merge_insert, nod_merge_insert,
nod_sys_function, nod_sys_function,
nod_similar, nod_similar,
@ -478,10 +479,15 @@ enum node_args {
e_mrg_when_not_matched, e_mrg_when_not_matched,
e_mrg_when_count, 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_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_values,
e_mrg_insert_count, e_mrg_insert_count,

View File

@ -4519,7 +4519,7 @@ insert : INSERT INTO simple_table_name ins_column_parens_opt
// MERGE statement // MERGE statement
merge merge
: MERGE INTO table_name USING table_reference ON search_condition : MERGE INTO table_name USING table_reference ON search_condition
merge_when_clause merge_when_clause
{ {
$$ = make_node(nod_merge, e_mrg_count, $3, $5, $7, $8); $$ = make_node(nod_merge, e_mrg_count, $3, $5, $7, $8);
@ -4527,34 +4527,42 @@ merge
; ;
merge_when_clause 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); } { $$ = 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); } { $$ = 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); } { $$ = 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); } { $$ = make_node(nod_merge_when, e_mrg_when_count, NULL, $1); }
; ;
merge_when_matched_clause merge_when_matched_clause
: WHEN MATCHED THEN merge_update_specification : WHEN MATCHED merge_update_specification
{ $$ = $4; } { $$ = $3; }
; ;
merge_when_not_matched_clause merge_when_not_matched_clause
: WHEN NOT MATCHED THEN merge_insert_specification : WHEN NOT MATCHED merge_insert_specification
{ $$ = $5; } { $$ = $4; }
; ;
merge_update_specification merge_update_specification
: UPDATE SET assignments : THEN UPDATE SET assignments
{ $$ = make_node(nod_merge_update, e_mrg_update_count, make_list($3)); } { $$ = 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 merge_insert_specification
: INSERT ins_column_parens_opt VALUES '(' value_list ')' : THEN INSERT ins_column_parens_opt VALUES '(' value_list ')'
{ $$ = make_node(nod_merge_insert, e_mrg_insert_count, make_list($2), make_list($5)); } { $$ = 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]; 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]; 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 // get the assignments of the UPDATE dsqlScratch
dsql_nod* list = dsql_nod* list = whenNode->nod_arg[e_mrg_update_statement];
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); fb_assert(list->nod_type == nod_list);
Firebird::Array<dsql_nod*> org_values, new_values; 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 // 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_ctx* context = get_context(target);
dsql_nod** ptr; 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->scopeLevel++; // go to the same level of source and target contexts
dsqlScratch->context->push(get_context(source)); // push the USING context 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) for (ptr = org_values.begin(); ptr < org_values.end(); ++ptr)
*ptr = PASS1_node_psql(dsqlScratch, *ptr, false); *ptr = PASS1_node_psql(dsqlScratch, *ptr, false);
updateCondition = PASS1_node_psql(dsqlScratch,
whenNode->nod_arg[e_mrg_update_condition], false);
// and pop the contexts // and pop the contexts
dsqlScratch->context->pop(); dsqlScratch->context->pop();
dsqlScratch->context->pop(); dsqlScratch->context->pop();
dsqlScratch->scopeLevel--; dsqlScratch->scopeLevel--;
// process relation // 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 // process new context values
for (ptr = new_values.begin(); ptr < new_values.end(); ++ptr) 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(); dsqlScratch->context->pop();
// recreate list of assignments // 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) 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... // We do not allow cases like UPDATE SET f1 = v1, f2 = v2, f1 = v3...
field_appears_once(modify->nod_arg[e_mdc_statement], field_appears_once(update->nod_arg[e_mdc_statement],
input->nod_arg[e_mrg_when]->nod_arg[e_mrg_when_matched]->nod_arg[e_mrg_update_statement], whenNode->nod_arg[e_mrg_update_statement], false, "MERGE");
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; 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->scopeLevel++; // go to the same level of the source context
dsqlScratch->context->push(get_context(source)); // push the USING 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 // the INSERT relation should be processed in a higher level than the source context
dsqlScratch->scopeLevel++; dsqlScratch->scopeLevel++;
// build the INSERT node // build the INSERT node
insert = MAKE_node(nod_insert, e_ins_count); 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_relation] = input->nod_arg[e_mrg_relation];
insert->nod_arg[e_ins_fields] = insert->nod_arg[e_ins_fields] = whenNode->nod_arg[e_mrg_insert_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] = whenNode->nod_arg[e_mrg_insert_values];
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(dsqlScratch, insert, false); insert = pass1_insert(dsqlScratch, insert, false);
// restore the scope level // restore the scope level
@ -6390,6 +6431,18 @@ static dsql_nod* pass1_merge(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
// pop the USING context // pop the USING context
dsqlScratch->scopeLevel--; dsqlScratch->scopeLevel--;
dsqlScratch->context->pop(); 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) // 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) if (insert)
{ {
action->dsqlTrueAction = insert; // then INSERT action->dsqlTrueAction = insert; // then INSERT
action->dsqlFalseAction = modify; // else UPDATE action->dsqlFalseAction = update; // else UPDATE/DELETE
} }
else else
{ {
@ -6412,7 +6465,7 @@ static dsql_nod* pass1_merge(DsqlCompilerScratch* dsqlScratch, dsql_nod* input)
not_node->nod_arg[0] = action->dsqlCondition; not_node->nod_arg[0] = action->dsqlCondition;
action->dsqlCondition = not_node; action->dsqlCondition = not_node;
action->dsqlTrueAction = modify; // then UPDATE action->dsqlTrueAction = update; // then UPDATE/DELETE
} }
// insert the IF inside the FOR SELECT // insert the IF inside the FOR SELECT
@ -10786,6 +10839,10 @@ void DSQL_pretty(const dsql_nod* node, int column)
verb = "merge_update"; verb = "merge_update";
break; break;
case nod_merge_delete:
verb = "merge_delete";
break;
case nod_merge_insert: case nod_merge_insert:
verb = "merge_insert"; verb = "merge_insert";
break; break;