From: <asf...@us...> - 2011-10-23 01:31:19
|
Revision: 53586 http://firebird.svn.sourceforge.net/firebird/?rev=53586&view=rev Author: asfernandes Date: 2011-10-23 01:31:11 +0000 (Sun, 23 Oct 2011) Log Message: ----------- Improvement CORE-3639 - Allow the use of multiple WHEN MATCHED / NOT MATCHED clauses in MERGE, as per the SQL 2008 specification. Also updated MERGE and RETURNING docs, and fixed a bug with MERGE WHEN MATCHED DELETE and RETURNING. Modified Paths: -------------- firebird/trunk/doc/sql.extensions/README.merge.txt firebird/trunk/doc/sql.extensions/README.returning firebird/trunk/src/dsql/StmtNodes.cpp firebird/trunk/src/dsql/StmtNodes.h firebird/trunk/src/dsql/parse.y Modified: firebird/trunk/doc/sql.extensions/README.merge.txt =================================================================== --- firebird/trunk/doc/sql.extensions/README.merge.txt 2011-10-22 03:23:18 UTC (rev 53585) +++ firebird/trunk/doc/sql.extensions/README.merge.txt 2011-10-23 01:31:11 UTC (rev 53586) @@ -15,21 +15,24 @@ INTO <table or view> [ [AS] <correlation name> ] USING <table or view or derived table> [ [AS] <correlation name> ] ON <condition> - [ <merge when matched> ] - [ <merge when not matched> ] + <merge when>... + <returning clause> + <merge when> ::= + <merge when matched> | + <merge when not matched> + <merge when matched> ::= - WHEN MATCHED THEN + WHEN MATCHED [ AND <condition> ] THEN UPDATE SET <assignment list> <merge when not matched> ::= - WHEN NOT MATCHED THEN + WHEN NOT MATCHED [ AND <condition> ] THEN INSERT [ <left paren> <column list> <right paren> ] VALUES <left paren> <value list> <right paren> Syntax rules: - 1. At least one of <merge when matched> and <merge when not matched> should be specified - and each one should not be specified more than one time. + 1. At least one of <merge when matched> and <merge when not matched> should be specified. Scope: DSQL, PSQL @@ -52,4 +55,9 @@ UPDATE is called when a record exist in the left table (INTO), otherwise INSERT is called. + As soon is decided if the source matched or not a record in the target, the set of the + correspondent (WHEN MATCHED / WHEN NOT MATCHED) statements are evaluated in the order specified, + to check its optional conditions. The first statement which have its condition evaluated to true + is the one which will be executed, and the subsequent ones will be ignored. + If no record is returned in the join, INSERT is not called. Modified: firebird/trunk/doc/sql.extensions/README.returning =================================================================== --- firebird/trunk/doc/sql.extensions/README.returning 2011-10-22 03:23:18 UTC (rev 53585) +++ firebird/trunk/doc/sql.extensions/README.returning 2011-10-23 01:31:11 UTC (rev 53586) @@ -4,7 +4,7 @@ Function: Allow to return the column values actually stored in the table as a result of the "INSERT", - "UPDATE OR INSERT", UPDATE and DELETE statements. + "UPDATE OR INSERT", UPDATE, DELETE and MERGE statements. The most common usage is to retrieve the value of the primary key generated inside a BEFORE-trigger. Authors: @@ -41,12 +41,15 @@ so the existing connectivity drivers should support this feature automagically. 4. Any explicit record change (update or delete) performed by AFTER-triggers is ignored by the RETURNING clause. - 5. OLD and NEW could be used in RETURNING clause of UPDATE and INSERT OR UPDATE statements. + 5. OLD and NEW could be used in RETURNING clause of UPDATE, INSERT OR UPDATE and MERGE statements. 6. In UPDATE and INSERT OR UPDATE statements, unqualified or qualified by table name/alias fields are resolved to NEW. + 7. In MERGE WHEN MATCHED UPDATE and MERGE WHEN NOT MATCHED statements, unqualified or qualified + by table name/alias fields are resolved to NEW. In MERGE WHEN MATCHED DELETE they are + resolved to OLD. Limitations: - 1. The modify statement (INSERT, UPDATE, DELETE) should operate in no more than one record + 1. The modify statement (INSERT, UPDATE, DELETE, MERGE) should operate in no more than one record (i.e. should be singleton). 2. The statement always return one row in DSQL, even if no record is inserted/updated/deleted. This may be changed in the future (i.e. return empty resultset). Modified: firebird/trunk/src/dsql/StmtNodes.cpp =================================================================== --- firebird/trunk/src/dsql/StmtNodes.cpp 2011-10-22 03:23:18 UTC (rev 53585) +++ firebird/trunk/src/dsql/StmtNodes.cpp 2011-10-23 01:31:11 UTC (rev 53586) @@ -128,12 +128,12 @@ // Play with contexts for RETURNING purposes. // Its assumed that oldContext is already on the stack. // Changes oldContext name to "OLD". - ReturningProcessor(DsqlCompilerScratch* aScratch, dsql_ctx* oldContext, dsql_ctx* modContext) + ReturningProcessor(DsqlCompilerScratch* aScratch, dsql_ctx* aOldContext, dsql_ctx* modContext) : scratch(aScratch), - autoAlias(&oldContext->ctx_alias, OLD_CONTEXT), - autoInternalAlias(&oldContext->ctx_internal_alias, oldContext->ctx_alias), - autoFlags(&oldContext->ctx_flags, oldContext->ctx_flags | CTX_system | CTX_returning), - hasModContext(modContext != NULL) + oldContext(aOldContext), + oldAlias(oldContext->ctx_alias), + oldInternalAlias(oldContext->ctx_internal_alias), + autoFlags(&oldContext->ctx_flags, oldContext->ctx_flags | CTX_system | CTX_returning) { // Clone the modify/old context and push with name "NEW" in a greater scope level. @@ -141,21 +141,26 @@ if (modContext) { - // push the modify context in the same scope level + // Push the modify context in the same scope level. scratch->context->push(modContext); *newContext = *modContext; newContext->ctx_flags |= CTX_system; } else { + // Create the target (= OLD) context and push it on the stack. + dsql_ctx* targetContext = FB_NEW(scratch->getPool()) dsql_ctx(scratch->getPool()); + *targetContext = *oldContext; + targetContext->ctx_flags &= ~CTX_system; // resolve unqualified fields + scratch->context->push(targetContext); + // This is NEW in the context of a DELETE. Mark it as NULL. *newContext = *oldContext; newContext->ctx_flags |= CTX_null; - - // Remove the system flag, so unqualified fields could be resolved to this context. - oldContext->ctx_flags &= ~CTX_system; } + oldContext->ctx_alias = oldContext->ctx_internal_alias = OLD_CONTEXT; + newContext->ctx_alias = newContext->ctx_internal_alias = MAKE_cstring(NEW_CONTEXT)->str_data; newContext->ctx_flags |= CTX_returning; @@ -164,10 +169,12 @@ ~ReturningProcessor() { + oldContext->ctx_alias = oldAlias; + oldContext->ctx_internal_alias = oldInternalAlias; + // Restore the context stack. scratch->context->pop(); - if (hasModContext) - scratch->context->pop(); + scratch->context->pop(); } // Process the RETURNING clause. @@ -208,8 +215,8 @@ private: DsqlCompilerScratch* scratch; - AutoSetRestore<string> autoAlias; - AutoSetRestore<string> autoInternalAlias; + dsql_ctx* oldContext; + string oldAlias, oldInternalAlias; AutoSetRestore<USHORT> autoFlags; bool hasModContext; }; @@ -4752,8 +4759,6 @@ dsql_nod* source = dsqlUsing; // USING dsql_nod* target = dsqlRelation; // INTO - dsql_nod* updDelCondition = dsqlWhenMatchedCondition; - dsql_nod* insCondition = dsqlWhenNotMatchedCondition; // Build a join between USING and INTO tables. RseNode* join = FB_NEW(pool) RseNode(pool); @@ -4763,7 +4768,7 @@ join->dsqlFrom->nod_arg[0] = source; // Left join if WHEN NOT MATCHED is present. - if (dsqlWhenNotMatchedPresent) + if (dsqlWhenNotMatched.hasData()) join->rse_jointype = blr_left; join->dsqlFrom->nod_arg[1] = target; @@ -4777,13 +4782,40 @@ dsql_nod* querySpecNod = MAKE_node(Dsql::nod_class_exprnode, 1); querySpecNod->nod_arg[0] = reinterpret_cast<dsql_nod*>(querySpec); - if (updDelCondition || insCondition) + dsql_nod* matchedConditions = NULL; + dsql_nod* notMatchedConditions = NULL; + + for (Matched* matched = dsqlWhenMatched.begin(); matched != dsqlWhenMatched.end(); ++matched) { + if (matched->condition) + matchedConditions = PASS1_compose(matchedConditions, matched->condition, blr_or); + else + { + matchedConditions = NULL; + break; + } + } + + for (NotMatched* notMatched = dsqlWhenNotMatched.begin(); + notMatched != dsqlWhenNotMatched.end(); + ++notMatched) + { + if (notMatched->condition) + notMatchedConditions = PASS1_compose(notMatchedConditions, notMatched->condition, blr_or); + else + { + notMatchedConditions = NULL; + break; + } + } + + if (matchedConditions || notMatchedConditions) + { const char* targetName = ExprNode::as<RelationSourceNode>(target)->alias.nullStr(); if (!targetName) targetName = ExprNode::as<RelationSourceNode>(target)->dsqlName.c_str(); - if (dsqlWhenMatchedPresent) // WHEN MATCHED + if (dsqlWhenMatched.hasData()) // WHEN MATCHED { MissingBoolNode* missingNode = FB_NEW(pool) MissingBoolNode( pool, MAKE_node(Dsql::nod_class_exprnode, 1)); @@ -4796,14 +4828,14 @@ querySpec->dsqlWhere = MAKE_node(Dsql::nod_class_exprnode, 1); querySpec->dsqlWhere->nod_arg[0] = reinterpret_cast<dsql_nod*>(notNode); + + if (matchedConditions) + querySpec->dsqlWhere = PASS1_compose(querySpec->dsqlWhere, matchedConditions, blr_and); } - if (updDelCondition) - querySpec->dsqlWhere = PASS1_compose(querySpec->dsqlWhere, updDelCondition, blr_and); - dsql_nod* temp = NULL; - if (dsqlWhenNotMatchedPresent) // WHEN NOT MATCHED + if (dsqlWhenNotMatched.hasData()) // WHEN NOT MATCHED { MissingBoolNode* missingNode = FB_NEW(pool) MissingBoolNode( pool, MAKE_node(Dsql::nod_class_exprnode, 1)); @@ -4813,8 +4845,8 @@ temp = MAKE_node(Dsql::nod_class_exprnode, 1); temp->nod_arg[0] = reinterpret_cast<dsql_nod*>(missingNode); - if (insCondition) - temp = PASS1_compose(temp, insCondition, blr_and); + if (notMatchedConditions) + temp = PASS1_compose(temp, notMatchedConditions, blr_and); querySpec->dsqlWhere = PASS1_compose(querySpec->dsqlWhere, temp, blr_or); } @@ -4845,113 +4877,124 @@ DsqlContextStack usingCtxs; dsqlGetContexts(usingCtxs, source); - StmtNode* update = NULL; - StmtNode* matchedRet = NULL; + StmtNode* processedRet = NULL; StmtNode* nullRet = NULL; - if (dsqlWhenMatchedPresent && dsqlWhenMatchedAssignments) + StmtNode* update = NULL; + IfNode* lastIf = NULL; + + for (Matched* matched = dsqlWhenMatched.begin(); matched != dsqlWhenMatched.end(); ++matched) { - // Get the assignments of the UPDATE dsqlScratch. - CompoundStmtNode* stmts = dsqlWhenMatchedAssignments; - Array<dsql_nod*> org_values, new_values; + IfNode* thisIf = FB_NEW(pool) IfNode(pool); - // Separate the new and org values to process in correct contexts. - for (size_t i = 0; i < stmts->statements.getCount(); ++i) + if (matched->assignments) { - const AssignmentNode* const assign = stmts->statements[i]->as<AssignmentNode>(); - fb_assert(assign); - org_values.add(assign->dsqlAsgnFrom); - new_values.add(assign->dsqlAsgnTo); - } + // Get the assignments of the UPDATE dsqlScratch. + CompoundStmtNode* stmts = matched->assignments; + Array<dsql_nod*> orgValues, newValues; - // Build the MODIFY node. - ModifyNode* modify = FB_NEW(pool) ModifyNode(pool); + // Separate the new and org values to process in correct contexts. + for (size_t i = 0; i < stmts->statements.getCount(); ++i) + { + const AssignmentNode* const assign = stmts->statements[i]->as<AssignmentNode>(); + fb_assert(assign); + orgValues.add(assign->dsqlAsgnFrom); + newValues.add(assign->dsqlAsgnTo); + } - dsql_ctx* const old_context = dsqlGetContext(target); - dsql_nod** ptr; + // Build the MODIFY node. + ModifyNode* modify = FB_NEW(pool) ModifyNode(pool); + thisIf->trueAction = modify; - modify->dsqlContext = old_context; + dsql_ctx* const oldContext = dsqlGetContext(target); - ++dsqlScratch->scopeLevel; // Go to the same level of source and target contexts. + modify->dsqlContext = oldContext; - for (DsqlContextStack::iterator itr(usingCtxs); itr.hasData(); ++itr) - dsqlScratch->context->push(itr.object()); // push the USING contexts + ++dsqlScratch->scopeLevel; // Go to the same level of source and target contexts. - dsqlScratch->context->push(old_context); // process old context values + for (DsqlContextStack::iterator itr(usingCtxs); itr.hasData(); ++itr) + dsqlScratch->context->push(itr.object()); // push the USING contexts - for (ptr = org_values.begin(); ptr != org_values.end(); ++ptr) - *ptr = PASS1_node_psql(dsqlScratch, *ptr, false); + dsqlScratch->context->push(oldContext); // process old context values - // And pop the contexts. - dsqlScratch->context->pop(); - dsqlScratch->context->pop(); - --dsqlScratch->scopeLevel; + if (matched->condition) + thisIf->dsqlCondition = PASS1_node_psql(dsqlScratch, matched->condition, false); - // Process relation. - modify->dsqlRelation = PASS1_relation(dsqlScratch, dsqlRelation); - dsql_ctx* mod_context = dsqlGetContext(modify->dsqlRelation); + dsql_nod** ptr; - // Process new context values. - for (ptr = new_values.begin(); ptr != new_values.end(); ++ptr) - *ptr = PASS1_node_psql(dsqlScratch, *ptr, false); + for (ptr = orgValues.begin(); ptr != orgValues.end(); ++ptr) + *ptr = PASS1_node_psql(dsqlScratch, *ptr, false); - dsqlScratch->context->pop(); + // And pop the contexts. + dsqlScratch->context->pop(); + dsqlScratch->context->pop(); + --dsqlScratch->scopeLevel; - if (dsqlReturning) - { - // Repush the source contexts. - ++dsqlScratch->scopeLevel; // Go to the same level of source and target contexts. + // Process relation. + modify->dsqlRelation = PASS1_relation(dsqlScratch, dsqlRelation); + dsql_ctx* modContext = dsqlGetContext(modify->dsqlRelation); - for (DsqlContextStack::iterator itr(usingCtxs); itr.hasData(); ++itr) - dsqlScratch->context->push(itr.object()); // push the USING contexts + // Process new context values. + for (ptr = newValues.begin(); ptr != newValues.end(); ++ptr) + *ptr = PASS1_node_psql(dsqlScratch, *ptr, false); - dsqlScratch->context->push(old_context); // process old context values + dsqlScratch->context->pop(); - mod_context->ctx_scope_level = old_context->ctx_scope_level; + if (dsqlReturning) + { + StmtNode* updRet = ReturningProcessor::clone(dsqlScratch, dsqlReturning, processedRet); - matchedRet = modify->statement2 = ReturningProcessor( - dsqlScratch, old_context, mod_context).process(dsqlReturning, NULL); + // Repush the source contexts. + ++dsqlScratch->scopeLevel; // Go to the same level of source and target contexts. - nullRet = dsqlNullifyReturning(dsqlScratch, modify, false); + for (DsqlContextStack::iterator itr(usingCtxs); itr.hasData(); ++itr) + dsqlScratch->context->push(itr.object()); // push the USING contexts - // And pop them. - dsqlScratch->context->pop(); - dsqlScratch->context->pop(); - --dsqlScratch->scopeLevel; - } + dsqlScratch->context->push(oldContext); // process old context values - // Recreate the list of assignments. + modContext->ctx_scope_level = oldContext->ctx_scope_level; - CompoundStmtNode* assignStatements = FB_NEW(pool) CompoundStmtNode(pool); - modify->statement = assignStatements; + processedRet = modify->statement2 = ReturningProcessor( + dsqlScratch, oldContext, modContext).process(dsqlReturning, updRet); - assignStatements->statements.resize(stmts->statements.getCount()); + nullRet = dsqlNullifyReturning(dsqlScratch, modify, false); - for (size_t i = 0; i < assignStatements->statements.getCount(); ++i) - { - if (!PASS1_set_parameter_type(dsqlScratch, org_values[i], new_values[i], false)) - PASS1_set_parameter_type(dsqlScratch, new_values[i], org_values[i], false); + // And pop them. + dsqlScratch->context->pop(); + dsqlScratch->context->pop(); + --dsqlScratch->scopeLevel; + } - AssignmentNode* assign = FB_NEW(pool) AssignmentNode(pool); - assign->dsqlAsgnFrom = org_values[i]; - assign->dsqlAsgnTo = new_values[i]; - assignStatements->statements[i] = assign; - } + // Recreate the list of assignments. - // We do not allow cases like UPDATE SET f1 = v1, f2 = v2, f1 = v3... - dsqlFieldAppearsOnce(new_values, "MERGE"); + CompoundStmtNode* assignStatements = FB_NEW(pool) CompoundStmtNode(pool); + modify->statement = assignStatements; - update = modify; - } - else if (dsqlWhenMatchedPresent && !dsqlWhenMatchedAssignments) - { - // build the DELETE node - EraseNode* erase = FB_NEW(pool) EraseNode(pool); - dsql_ctx* context = dsqlGetContext(target); - erase->dsqlContext = context; + assignStatements->statements.resize(stmts->statements.getCount()); - if (dsqlReturning) + for (size_t i = 0; i < assignStatements->statements.getCount(); ++i) + { + if (!PASS1_set_parameter_type(dsqlScratch, orgValues[i], newValues[i], false)) + PASS1_set_parameter_type(dsqlScratch, newValues[i], orgValues[i], false); + + AssignmentNode* assign = FB_NEW(pool) AssignmentNode(pool); + assign->dsqlAsgnFrom = orgValues[i]; + assign->dsqlAsgnTo = newValues[i]; + assignStatements->statements[i] = assign; + } + + // We do not allow cases like UPDATE SET f1 = v1, f2 = v2, f1 = v3... + dsqlFieldAppearsOnce(newValues, "MERGE"); + } + else { + // Build the DELETE node. + EraseNode* erase = FB_NEW(pool) EraseNode(pool); + thisIf->trueAction = erase; + + dsql_ctx* context = dsqlGetContext(target); + erase->dsqlContext = context; + ++dsqlScratch->scopeLevel; // Go to the same level of source and target contexts. for (DsqlContextStack::iterator itr(usingCtxs); itr.hasData(); ++itr) @@ -4959,24 +5002,46 @@ dsqlScratch->context->push(context); // process old context values - matchedRet = erase->statement = ReturningProcessor( - dsqlScratch, context, NULL).process(dsqlReturning, NULL); + if (matched->condition) + thisIf->dsqlCondition = PASS1_node_psql(dsqlScratch, matched->condition, false); - nullRet = dsqlNullifyReturning(dsqlScratch, erase, false); + if (dsqlReturning) + { + StmtNode* delRet = ReturningProcessor::clone(dsqlScratch, dsqlReturning, processedRet); + processedRet = erase->statement = ReturningProcessor( + dsqlScratch, context, NULL).process(dsqlReturning, delRet); + + nullRet = dsqlNullifyReturning(dsqlScratch, erase, false); + } + // And pop the contexts. dsqlScratch->context->pop(); dsqlScratch->context->pop(); --dsqlScratch->scopeLevel; } - update = erase; + if (lastIf) + lastIf->falseAction = thisIf->dsqlCondition ? thisIf : thisIf->trueAction; + else + update = thisIf->dsqlCondition ? thisIf : thisIf->trueAction; + + lastIf = thisIf; + + // If a statement executes unconditionally, no other will ever execute. + if (!thisIf->dsqlCondition) + break; } StmtNode* insert = NULL; + lastIf = NULL; - if (dsqlWhenNotMatchedPresent) + for (NotMatched* notMatched = dsqlWhenNotMatched.begin(); + notMatched != dsqlWhenNotMatched.end(); + ++notMatched) { + IfNode* thisIf = FB_NEW(pool) IfNode(pool); + ++dsqlScratch->scopeLevel; // Go to the same level of the source context. for (DsqlContextStack::iterator itr(usingCtxs); itr.hasData(); ++itr) @@ -4988,29 +5053,32 @@ // Build the INSERT node. StoreNode* store = FB_NEW(pool) StoreNode(pool); store->dsqlRelation = dsqlRelation; - store->dsqlFields = dsqlWhenNotMatchedFields; - store->dsqlValues = dsqlWhenNotMatchedValues; + store->dsqlFields = notMatched->fields; + store->dsqlValues = notMatched->values; - store = store->internalDsqlPass(dsqlScratch, false)->as<StoreNode>(); + thisIf->trueAction = store = store->internalDsqlPass(dsqlScratch, false)->as<StoreNode>(); fb_assert(store); + if (notMatched->condition) + thisIf->dsqlCondition = PASS1_node_psql(dsqlScratch, notMatched->condition, false); + // Restore the scope level. --dsqlScratch->scopeLevel; - StmtNode* insRet = ReturningProcessor::clone(dsqlScratch, dsqlReturning, matchedRet); - if (dsqlReturning) { - dsql_ctx* const old_context = dsqlGetContext(target); - dsqlScratch->context->push(old_context); + StmtNode* insRet = ReturningProcessor::clone(dsqlScratch, dsqlReturning, processedRet); + dsql_ctx* const oldContext = dsqlGetContext(target); + dsqlScratch->context->push(oldContext); + dsql_ctx* context = dsqlGetContext(store->dsqlRelation); - context->ctx_scope_level = old_context->ctx_scope_level; + context->ctx_scope_level = oldContext->ctx_scope_level; - store->statement2 = ReturningProcessor( - dsqlScratch, old_context, context).process(dsqlReturning, insRet); + processedRet = store->statement2 = ReturningProcessor( + dsqlScratch, oldContext, context).process(dsqlReturning, insRet); - if (!matchedRet) + if (!processedRet) nullRet = dsqlNullifyReturning(dsqlScratch, store, false); dsqlScratch->context->pop(); @@ -5020,7 +5088,16 @@ dsqlScratch->context->pop(); --dsqlScratch->scopeLevel; - insert = store; + if (lastIf) + lastIf->falseAction = thisIf->dsqlCondition ? thisIf : thisIf->trueAction; + else + insert = thisIf->dsqlCondition ? thisIf : thisIf->trueAction; + + lastIf = thisIf; + + // If a statement executes unconditionally, no other will ever execute. + if (!thisIf->dsqlCondition) + break; } MissingBoolNode* missingNode = FB_NEW(pool) MissingBoolNode( Modified: firebird/trunk/src/dsql/StmtNodes.h =================================================================== --- firebird/trunk/src/dsql/StmtNodes.h 2011-10-22 03:23:18 UTC (rev 53585) +++ firebird/trunk/src/dsql/StmtNodes.h 2011-10-23 01:31:11 UTC (rev 53586) @@ -937,18 +937,39 @@ class MergeNode : public TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_MERGE> { public: + struct Matched + { + Matched() + : assignments(NULL), + condition(NULL) + { + } + + CompoundStmtNode* assignments; + dsql_nod* condition; + }; + + struct NotMatched + { + NotMatched() + : fields(NULL), + values(NULL), + condition(NULL) + { + } + + dsql_nod* fields; + dsql_nod* values; + dsql_nod* condition; + }; + explicit MergeNode(MemoryPool& pool, dsql_nod* val = NULL) : TypedNode<DsqlOnlyStmtNode, StmtNode::TYPE_MERGE>(pool), dsqlRelation(NULL), dsqlUsing(NULL), dsqlCondition(NULL), - dsqlWhenMatchedPresent(false), - dsqlWhenMatchedAssignments(NULL), - dsqlWhenMatchedCondition(NULL), - dsqlWhenNotMatchedPresent(false), - dsqlWhenNotMatchedFields(NULL), - dsqlWhenNotMatchedValues(NULL), - dsqlWhenNotMatchedCondition(NULL), + dsqlWhenMatched(pool), + dsqlWhenNotMatched(pool), dsqlReturning(NULL) { } @@ -961,13 +982,8 @@ dsql_nod* dsqlRelation; dsql_nod* dsqlUsing; dsql_nod* dsqlCondition; - bool dsqlWhenMatchedPresent; - CompoundStmtNode* dsqlWhenMatchedAssignments; - dsql_nod* dsqlWhenMatchedCondition; - bool dsqlWhenNotMatchedPresent; - dsql_nod* dsqlWhenNotMatchedFields; - dsql_nod* dsqlWhenNotMatchedValues; - dsql_nod* dsqlWhenNotMatchedCondition; + Firebird::Array<Matched> dsqlWhenMatched; + Firebird::Array<NotMatched> dsqlWhenNotMatched; ReturningClause* dsqlReturning; }; Modified: firebird/trunk/src/dsql/parse.y =================================================================== --- firebird/trunk/src/dsql/parse.y 2011-10-22 03:23:18 UTC (rev 53585) +++ firebird/trunk/src/dsql/parse.y 2011-10-23 01:31:11 UTC (rev 53586) @@ -4789,47 +4789,62 @@ ; merge_when_clause($mergeNode) - : merge_when_matched_clause($mergeNode) merge_when_not_matched_clause($mergeNode) - | merge_when_not_matched_clause($mergeNode) merge_when_matched_clause($mergeNode) - | merge_when_matched_clause($mergeNode) + : merge_when_matched_clause($mergeNode) | merge_when_not_matched_clause($mergeNode) + | merge_when_clause merge_when_matched_clause($mergeNode) + | merge_when_clause merge_when_not_matched_clause($mergeNode) ; merge_when_matched_clause($mergeNode) : WHEN MATCHED merge_update_specification($mergeNode) - { $mergeNode->dsqlWhenMatchedPresent = true; } ; merge_when_not_matched_clause($mergeNode) : WHEN NOT MATCHED merge_insert_specification($mergeNode) - { $mergeNode->dsqlWhenNotMatchedPresent = true; } ; merge_update_specification($mergeNode) : THEN UPDATE SET assignments - { $mergeNode->dsqlWhenMatchedAssignments = $4; } + { + MergeNode::Matched when; + when.assignments = $4; + $mergeNode->dsqlWhenMatched.add(when); + } | AND search_condition THEN UPDATE SET assignments { - $mergeNode->dsqlWhenMatchedAssignments = $6; - $mergeNode->dsqlWhenMatchedCondition = $2; + MergeNode::Matched when; + when.assignments = $6; + when.condition = $2; + $mergeNode->dsqlWhenMatched.add(when); } | THEN KW_DELETE - // Nothing to do here. + { + MergeNode::Matched when; + $mergeNode->dsqlWhenMatched.add(when); + } | AND search_condition THEN KW_DELETE - { $mergeNode->dsqlWhenMatchedCondition = $2; } + { + MergeNode::Matched when; + when.condition = $2; + $mergeNode->dsqlWhenMatched.add(when); + } ; merge_insert_specification($mergeNode) : THEN INSERT ins_column_parens_opt VALUES '(' value_list ')' { - $mergeNode->dsqlWhenNotMatchedFields = make_list($3); - $mergeNode->dsqlWhenNotMatchedValues = make_list($6); + MergeNode::NotMatched when; + when.fields = make_list($3); + when.values = make_list($6); + $mergeNode->dsqlWhenNotMatched.add(when); } | AND search_condition THEN INSERT ins_column_parens_opt VALUES '(' value_list ')' { - $mergeNode->dsqlWhenNotMatchedFields = make_list($5); - $mergeNode->dsqlWhenNotMatchedValues = make_list($8); - $mergeNode->dsqlWhenNotMatchedCondition = $2; + MergeNode::NotMatched when; + when.fields = make_list($5); + when.values = make_list($8); + when.condition = $2; + $mergeNode->dsqlWhenNotMatched.add(when); } ; This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |