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:
parent
4903e17853
commit
e0762f5f95
@ -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,
|
||||||
|
|
||||||
|
@ -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)); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user