[ 
https://issues.apache.org/jira/browse/DERBY-3155?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Rick Hillegas updated DERBY-3155:
---------------------------------

    Attachment: derby-3155-07-ad-insertAction.diff

Attaching derby-3155-07-ad-insertAction.diff. This patch adds support for the 
INSERT action of MERGE statements. I am running tests now.

The patch includes tests for the following use-cases:

i) INSERT actions with DEFAULT expressions for identity, generated, and default 
columns

ii) INSERT actions on tables with CHECK, UNIQUE, PRIMARY KEY, and FOREIGN KEY 
constraints.

iii) INSERT actions in MERGE statements fired by triggers.

iv) INSERT actions which fire triggers.

v) MERGE statements with INSERT and DELETE actions operating together.

The following behaviors have not been implemented/tested yet:

a) UPDATE actions

b) Permissions checking

c) Dependency checking and statement invalidation

So far the bulk of the complexity remains concentrated in compilation. 


Touches the following files:

---------------

M       java/engine/org/apache/derby/iapi/sql/execute/NoPutResultSet.java
M       
java/engine/org/apache/derby/impl/sql/execute/TemporaryRowHolderResultSet.java

Replaced a magic number with a named constant. Temporary tables don't have 
result set numbers assigned to them. But now they are being used to drive the 
INSERT/UPDATE/DELETE actions of MERGE statements. In order to get CHECK 
constraints to work properly, the row being checked is stuffed into slot 0 of 
the result set row array. That is usually the result set number corresponding 
to the driving result set of an INSERT/UPDATE/DELETE statement. And it was, 
coincidentally, the magic result set number of temporary tables. This decision 
may need to be re-visited if we invent other situations for which temporary 
tables need result set numbers.

---------------

M       java/engine/org/apache/derby/iapi/sql/dictionary/TableDescriptor.java

Improved a method header comment.

---------------

M       java/engine/org/apache/derby/impl/sql/compile/ResultSetNode.java

Added a new way to determine the data type of a ResultColumn. There is now a 
path through the pinball machine where the old technique didn't work.

---------------

M       java/engine/org/apache/derby/impl/sql/compile/MergeNode.java
M       java/engine/org/apache/derby/impl/sql/compile/MatchingClauseNode.java
M       java/engine/org/apache/derby/impl/sql/compile/InsertNode.java

Compilation support for the INSERT action of a MERGE statement. The bulk of the 
new complexity has to do with generating the rows which are stuffed into a 
temporary table at execution time. There is considerable complexity involved in 
handling the DEFAULT keyword, which can appear as the value to be inserted into 
an identity, generated, or default column, as for instance, in this situation:

{
  create table t1_007
  (
      c1 int generated always as identity,
      c2 int,
      c3 int generated always as ( c1 + c2 ),
      c1_4 int,
      c5 int default 1000
  );

...

  merge into t1_007
  using t2_007
  on t1_007.c2 = t2_007.c2
  when not matched and t2_007.c5 = 'three'
    then insert ( c1, c2, c3, c1_4, c5 ) values ( default, 100 * t2_007.c2, 
default, t2_007.c3, default );
}

For an ordinary INSERT statement, much of that complexity is handled by the 
compilation of the driving SELECT. For a MERGE statement, that SELECT is a 
dummy statement which is not optimized or generated. That, in turn, 
short-circuits some of the complex handling of the DEFAULT keyword. So that 
logic had to be reproduced in MatchingClauseNode.

There is already too much complexity in the handling of these special columns 
and now I have made the situation even more complicated. In particular, I don't 
understand why GENERATED BY DEFAULT AS IDENTITY and GENERATED ALWAYS AS 
IDENTITY take such different trajectories through the pinball machine. And I 
don't understand why the special columns are compiled with the driving SELECT 
rather than with the INSERT/UPDATE action itself. This is a source of 
brittleness which we should be aware of. However, I thought that cleaning this 
up was outside the scope of this JIRA.

---------------

M       java/engine/org/apache/derby/impl/sql/compile/DeleteNode.java
M       java/engine/org/apache/derby/impl/sql/compile/UpdateNode.java
M       
java/engine/org/apache/derby/impl/sql/execute/WriteCursorConstantAction.java
M       java/engine/org/apache/derby/impl/sql/execute/DMLVTIResultSet.java
M       java/engine/org/apache/derby/impl/sql/execute/UpdateVTIResultSet.java
M       
java/engine/org/apache/derby/impl/sql/execute/CallStatementResultSet.java
M       
java/engine/org/apache/derby/impl/sql/execute/UpdatableVTIConstantAction.java
M       java/engine/org/apache/derby/impl/sql/execute/InsertVTIResultSet.java
M       java/engine/org/apache/derby/impl/sql/execute/DeleteResultSet.java
M       java/engine/org/apache/derby/impl/sql/execute/UpdateResultSet.java
M       java/engine/org/apache/derby/impl/sql/execute/MiscResultSet.java
M       java/engine/org/apache/derby/impl/sql/execute/NoRowsResultSetImpl.java
M       java/engine/org/apache/derby/impl/sql/execute/DeleteConstantAction.java
M       java/engine/org/apache/derby/impl/sql/execute/DeleteVTIResultSet.java
M       
java/engine/org/apache/derby/impl/sql/execute/GenericConstantActionFactory.java
M       java/engine/org/apache/derby/impl/sql/execute/UpdateConstantAction.java

Added a small amount of logic so that INSERT/UPDATE/DELETE actions know at 
execution time whether they are running under a MERGE statement. If so, then we 
don't close the Activation at the end of the action. The Activation is closed 
by the MergeResultSet at the very end after all of the actions have run. This, 
in turn, makes it possible to run multiple INSERT/DELETE/UPDATE actions in a 
single MERGE statement.

---------------

M       java/engine/org/apache/derby/impl/sql/compile/DMLModStatementNode.java

Added some special processing for generation clauses invoked by actions of 
MERGE statements. This is more of the fallout from the fact that the dummy 
SELECTs are not optimized and generated.

---------------

M       java/engine/org/apache/derby/impl/sql/compile/ResultColumnList.java

Abstracted some logic into a separate method so that it could be called when 
generating the temporary row for an INSERT action of a MERGE statement.

---------------

M       java/engine/org/apache/derby/impl/sql/execute/InsertResultSet.java
M       java/engine/org/apache/derby/impl/sql/execute/MergeResultSet.java
M       java/engine/org/apache/derby/impl/sql/execute/InsertConstantAction.java
M       
java/engine/org/apache/derby/impl/sql/execute/MatchingClauseConstantAction.java

Changes to support INSERT actions under MERGE statements.

---------------

M       
java/testing/org/apache/derbyTesting/functionTests/tests/lang/MergeStatementTest.java

Tests for the new behavior.


> Support for SQL:2003 MERGE statement
> ------------------------------------
>
>                 Key: DERBY-3155
>                 URL: https://issues.apache.org/jira/browse/DERBY-3155
>             Project: Derby
>          Issue Type: Improvement
>          Components: SQL
>            Reporter: Trejkaz
>            Assignee: Rick Hillegas
>              Labels: derby_triage10_10
>         Attachments: MergeStatement.html, MergeStatement.html, 
> MergeStatement.html, derby-3155-01-ac-grammar.diff, 
> derby-3155-02-ag-fixParserWarning.diff, 
> derby-3155-03-ae-backingStoreHashtableWithRowLocation.diff, 
> derby-3155-03-af-backingStoreHashtableWithRowLocation.diff, 
> derby-3155-03-ag-backingStoreHashtableWithRowLocation.diff, 
> derby-3155-03-ah-backingStoreHashtableWithRowLocation.diff, 
> derby-3155-04-ae-deleteAction.diff, derby-3155-04-af-deleteAction.diff, 
> derby-3155-05-aa-triggerTransitionTableAsTarget.diff, 
> derby-3155-06-aa-triggerTransitionTableAsSource.diff, 
> derby-3155-07-ad-insertAction.diff
>
>
> A relatively common piece of logic in a database application is to check for 
> a row's existence and then either update or insert depending on its existence.
> SQL:2003 added a MERGE statement to perform this operation.  It looks like 
> this:
>     MERGE INTO table_name USING table_name ON (condition)
>     WHEN MATCHED THEN UPDATE SET column1 = value1 [, column2 = value2 ...]
>     WHEN NOT MATCHED THEN INSERT column1 [, column2 ...] VALUES (value1 [, 
> value2 ...]) 
> At the moment, the only workaround for this would be to write a stored 
> procedure to do the same operation, or to implement the logic client-side.



--
This message was sent by Atlassian JIRA
(v6.1#6144)

Reply via email to