You are viewing a plain text version of this content. The canonical link for it is here.
Posted to derby-commits@db.apache.org by rh...@apache.org on 2014/02/07 23:12:24 UTC

svn commit: r1565830 - in /db/derby/code/trunk/java: engine/org/apache/derby/impl/sql/compile/ engine/org/apache/derby/loc/ shared/org/apache/derby/shared/common/reference/ testing/org/apache/derbyTesting/functionTests/tests/lang/

Author: rhillegas
Date: Fri Feb  7 22:12:23 2014
New Revision: 1565830

URL: http://svn.apache.org/r1565830
Log:
DERBY-3155: Forbid synonyms in MERGE statement; tests passed cleanly for me on derby-3155-21-ac-cleanupAndForbidSynonyms.diff.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ColumnReference.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MatchingClauseNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MergeNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableName.java
    db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
    db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
    db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/MergeStatementTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ColumnReference.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ColumnReference.java?rev=1565830&r1=1565829&r2=1565830&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ColumnReference.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ColumnReference.java Fri Feb  7 22:12:23 2014
@@ -1241,20 +1241,31 @@ public class ColumnReference extends Val
     void    setMergeTableID( int mergeTableID )
     {
         // Changing the association of a column means we are confused. Shouldn't happen.
-        if ( _mergeTableID != MERGE_UNKNOWN )
+        if ( (_mergeTableID != MERGE_UNKNOWN) && (_mergeTableID != mergeTableID)  )
         {
             if (SanityManager.DEBUG)
             {
                 SanityManager.ASSERT
                     (
                      (_mergeTableID == mergeTableID),
-                     "MERGE statement can't re-associate column " + debugName()
+                     "MERGE statement can't re-associate column " + getSQLColumnName() +
+                     " from " + prettyPrintMergeTableID( _mergeTableID ) +
+                     " to " + prettyPrintMergeTableID( mergeTableID )
                      );
             }
         }
 
         _mergeTableID = mergeTableID;
     }
+    private String  prettyPrintMergeTableID( int mergeTableID )
+    {
+        switch ( mergeTableID )
+        {
+        case MERGE_SOURCE: return "SOURCE";
+        case MERGE_TARGET: return "TARGET";
+        default: return "UNKNOWN";
+        }
+    }
 
     /** Get the MERGE table (SOURCE or TARGET) associated with this column */
     int getMergeTableID()
@@ -1262,13 +1273,6 @@ public class ColumnReference extends Val
         return _mergeTableID;
     }
 
-    /** Get the name of this column (for debugging printout) */
-    String  debugName()
-    {
-        if ( _qualifiedTableName == null ) { return _columnName; }
-        else { return _qualifiedTableName + "." + _columnName; }
-    }
-
 	/**
 	 * Helper class to keep track of remap data when a ColumnReference
 	 * is remapped multiple times.  This allows the CR to be UN-

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MatchingClauseNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MatchingClauseNode.java?rev=1565830&r1=1565829&r2=1565830&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MatchingClauseNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MatchingClauseNode.java Fri Feb  7 22:12:23 2014
@@ -294,11 +294,13 @@ public class MatchingClauseNode extends 
         ResultColumnList    setClauses = realiasSetClauses( targetTable );
         bindSetClauses( fullFromList, targetTable, setClauses );
 
+        TableName   tableName = targetTable.getTableNameField();
+        FromList    selectFromList = fullFromList;
+        
         SelectNode  selectNode = new SelectNode
             (
-             //             _updateColumns,
              setClauses,
-             fullFromList,
+             selectFromList,
              null,      // where clause
              null,      // group by list
              null,      // having clause
@@ -306,7 +308,7 @@ public class MatchingClauseNode extends 
              null,      // optimizer plan override
              getContextManager()
              );
-        _dml = new UpdateNode( targetTable.getTableNameField(), selectNode, this, getContextManager() );
+        _dml = new UpdateNode( tableName, selectNode, this, getContextManager() );
 
         _dml.bindStatement();
 
@@ -320,6 +322,7 @@ public class MatchingClauseNode extends 
         // the full row is the before image, the after image, and a row location
         int     rowSize = fullUpdateRow.size() / 2;
 
+        // split the row into before and after images
         for ( int i = 0; i < rowSize; i++ )
         {
             ResultColumn    origBeforeRC = fullUpdateRow.elementAt( i );
@@ -351,10 +354,11 @@ public class MatchingClauseNode extends 
         for ( int i = 0; i < _updateColumns.size(); i++ )
         {
             ResultColumn    setRC = _updateColumns.elementAt( i );
+            TableName   tableName = targetTable.getTableName();
             ColumnReference newTargetColumn = new ColumnReference
                 (
                  setRC.getReference().getColumnName(),
-                 targetTable.getTableName(),
+                 tableName,
                  getContextManager()
                  );
             newTargetColumn.setMergeTableID( ColumnReference.MERGE_TARGET );
@@ -436,7 +440,7 @@ public class MatchingClauseNode extends 
         TableDescriptor td = targetTable.getTableDescriptor();
         HashSet<String> changedColumns = getChangedColumnNames();
         HashSet<String> changedGeneratedColumns = getChangedGeneratedColumnNames( td, changedColumns );
-
+        
         _thenColumns = fullRow.copyListAndObjects();
 
         //
@@ -1108,7 +1112,7 @@ public class MatchingClauseNode extends 
             
             if (SanityManager.DEBUG)
             {
-                SanityManager.THROWASSERT( "Can't find select list column corresponding to " + bufferedCR.debugName() );
+                SanityManager.THROWASSERT( "Can't find select list column corresponding to " + bufferedCR.getSQLColumnName() );
             }
         }
         else if ( bufferedExpression instanceof CurrentRowLocationNode )
@@ -1134,7 +1138,7 @@ public class MatchingClauseNode extends 
             SanityManager.ASSERT
                 (
                  ( (mergeTableID == ColumnReference.MERGE_SOURCE) || (mergeTableID == ColumnReference.MERGE_TARGET) ),
-                 "Column " + cr.debugName() + " has illegal MERGE table id: " + mergeTableID
+                 "Column " + cr.getSQLColumnName() + " has illegal MERGE table id: " + mergeTableID
                  );
         }
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MergeNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MergeNode.java?rev=1565830&r1=1565829&r2=1565830&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MergeNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/MergeNode.java Fri Feb  7 22:12:23 2014
@@ -214,34 +214,23 @@ public final class MergeNode extends DML
             throw StandardException.newException( SQLState.LANG_SAME_EXPOSED_NAME );
         }
 
+        // synonyms not allowed
+        forbidSynonyms( dd );
+
         FromList    dfl = new FromList( getContextManager() );
-        dfl.addFromTable( _sourceTable );
-        dfl.addFromTable( _targetTable );
+        FromTable   dflSource = cloneFromTable( _sourceTable );
+        FromBaseTable   dflTarget = (FromBaseTable) cloneFromTable( _targetTable );
+        dfl.addFromTable( dflSource );
+        dfl.addFromTable( dflTarget );
         dfl.bindTables( dd, new FromList( getOptimizerFactory().doJoinOrderOptimization(), getContextManager() ) );
 
-        //
-        // Bind the WHEN [ NOT ] MATCHED clauses.
-        //
-        FromList    dummyFromList = new FromList( getContextManager() );
-        FromBaseTable   dummyTargetTable = new FromBaseTable
-            (
-             _targetTable.getTableNameField(),
-             _targetTable.correlationName,
-             null,
-             null,
-             getContextManager()
-             );
-        FromTable       dummySourceTable = cloneSourceTable();
-        
-        dummyFromList.addFromTable( dummySourceTable );
-        dummyFromList.addFromTable( dummyTargetTable );
-        dummyFromList.bindTables( dd, new FromList( getOptimizerFactory().doJoinOrderOptimization(), getContextManager() ) );
-
         // target table must be a base table
-        if ( !targetIsBaseTable( _targetTable ) ) { notBaseTable(); }
+        if ( !targetIsBaseTable( dflTarget ) ) { notBaseTable(); }
 
         for ( MatchingClauseNode mcn : _matchingClauses )
         {
+            FromList    dummyFromList = cloneFromList( dd, dflTarget );
+            FromBaseTable   dummyTargetTable = (FromBaseTable) dummyFromList.elementAt( TARGET_TABLE_INDEX );
             mcn.bind( dd, this, dummyFromList, dummyTargetTable );
         }
         
@@ -255,12 +244,54 @@ public final class MergeNode extends DML
         }
 	}
 
+    /** Create a FromList for binding a WHEN [ NOT ] MATCHED clause */
+    private FromList    cloneFromList( DataDictionary dd, FromBaseTable targetTable )
+        throws StandardException
+    {
+        FromList    dummyFromList = new FromList( getContextManager() );
+        FromBaseTable   dummyTargetTable = new FromBaseTable
+            (
+             targetTable.getTableNameField(),
+             targetTable.correlationName,
+             null,
+             null,
+             getContextManager()
+             );
+        FromTable       dummySourceTable = cloneFromTable( _sourceTable );
+        
+        dummyFromList.addFromTable( dummySourceTable );
+        dummyFromList.addFromTable( dummyTargetTable );
+        dummyFromList.bindTables( dd, new FromList( getOptimizerFactory().doJoinOrderOptimization(), getContextManager() ) );
+
+        return dummyFromList;
+    }
+
     /** Get the exposed name of a FromTable */
     private String  getExposedName( FromTable ft ) throws StandardException
     {
         return ft.getTableName().getTableName();
     }
 
+    /** Neither the source nor the target table may be a synonym */
+    private void    forbidSynonyms( DataDictionary dd )    throws StandardException
+    {
+        forbidSynonyms( dd, _targetTable.getTableNameField().cloneMe() );
+        if ( _sourceTable instanceof FromBaseTable )
+        {
+            forbidSynonyms( dd, ((FromBaseTable)_sourceTable).getTableNameField().cloneMe() );
+        }
+    }
+    private void    forbidSynonyms( DataDictionary dd, TableName tableName ) throws StandardException
+    {
+        tableName.bind( dd );
+
+        TableName   synonym = resolveTableToSynonym( tableName );
+        if ( synonym != null )
+        {
+            throw StandardException.newException( SQLState.LANG_NO_SYNONYMS_IN_MERGE );
+        }
+    }
+
     /**
      * Bind the driving left join select.
      * Stuffs the left join SelectNode into the resultSet variable.
@@ -406,7 +437,7 @@ public final class MergeNode extends DML
     {
         // tell the target table to generate a row location column
         _targetTable.setRowLocationColumnName( TARGET_ROW_LOCATION_NAME );
-        
+
         TableName   fromTableName = _targetTable.getTableName();
         ColumnReference cr = new ColumnReference
                 ( TARGET_ROW_LOCATION_NAME, fromTableName, getContextManager() );
@@ -458,12 +489,12 @@ public final class MergeNode extends DML
         }
     }
 
-    /** Clone the source table for binding the MATCHED clauses */
-    private FromTable   cloneSourceTable() throws StandardException
+    /** Clone a FromTable to avoid binding the original */
+    private FromTable   cloneFromTable( FromTable fromTable ) throws StandardException
     {
-        if ( _sourceTable instanceof FromVTI )
+        if ( fromTable instanceof FromVTI )
         {
-            FromVTI source = (FromVTI) _sourceTable;
+            FromVTI source = (FromVTI) fromTable;
 
             return new FromVTI
                 (
@@ -475,9 +506,9 @@ public final class MergeNode extends DML
                  getContextManager()
                  );
         }
-        else if ( _sourceTable instanceof FromBaseTable )
+        else if ( fromTable instanceof FromBaseTable )
         {
-            FromBaseTable   source = (FromBaseTable) _sourceTable;
+            FromBaseTable   source = (FromBaseTable) fromTable;
             return new FromBaseTable
                 (
                  source.tableName,
@@ -508,11 +539,12 @@ public final class MergeNode extends DML
         throws StandardException
     {
         String[]    columnNames = getColumns( getExposedName( fromTable ), drivingColumnMap );
-        
+        TableName   tableName = fromTable.getTableName();
+
         for ( int i = 0; i < columnNames.length; i++ )
         {
             ColumnReference cr = new ColumnReference
-                ( columnNames[ i ], fromTable.getTableName(), getContextManager() );
+                ( columnNames[ i ], tableName, getContextManager() );
             cr.setMergeTableID( mergeTableID );
             ResultColumn    rc = new ResultColumn( (String) null, cr, getContextManager() );
             selectList.addResultColumn( rc );

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableName.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableName.java?rev=1565830&r1=1565829&r2=1565830&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableName.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableName.java Fri Feb  7 22:12:23 2014
@@ -227,6 +227,12 @@ public class TableName extends QueryTree
 		}
 	}
 
+    /** Clone this TableName */
+    public  TableName   cloneMe()
+    {
+        return new TableName( schemaName, tableName, getContextManager() );
+    }
+
 	///////////////////////////////////////////////////////////////////////
 	//
 	//	BIND METHODS

Modified: db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml?rev=1565830&r1=1565829&r2=1565830&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/loc/messages.xml Fri Feb  7 22:12:23 2014
@@ -2291,6 +2291,11 @@ Guide.
                 <text>Subqueries are not allowed in the WHEN [ NOT ] MATCHED clauses of MERGE statements.</text>
             </msg>
 
+	        <msg>
+                <name>42XAP</name>
+                <text>Synonyms are not allowed as the source or target tables of MERGE statements.</text>
+            </msg>
+
             <msg>
                 <name>42Y00</name>
                 <text>Class '{0}' does not implement org.apache.derby.iapi.db.AggregateDefinition and thus cannot be used as an aggregate expression.</text>

Modified: db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java?rev=1565830&r1=1565829&r2=1565830&view=diff
==============================================================================
--- db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java (original)
+++ db/derby/code/trunk/java/shared/org/apache/derby/shared/common/reference/SQLState.java Fri Feb  7 22:12:23 2014
@@ -951,6 +951,7 @@ public interface SQLState {
     String LANG_SAME_EXPOSED_NAME                                       = "42XAM";    
     String LANG_NOT_NULL_CHARACTERISTICS                               = "42XAN";
     String LANG_NO_SUBQUERIES_IN_MATCHED_CLAUSE         = "42XAO";
+    String LANG_NO_SYNONYMS_IN_MERGE                            = "42XAP";
     String LANG_INVALID_USER_AGGREGATE_DEFINITION2                     = "42Y00";
 	String LANG_INVALID_CHECK_CONSTRAINT                               = "42Y01";
     // String LANG_NO_ALTER_TABLE_COMPRESS_ON_TARGET_TABLE             = "42Y02";

Modified: db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/MergeStatementTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/MergeStatementTest.java?rev=1565830&r1=1565829&r2=1565830&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/MergeStatementTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/MergeStatementTest.java Fri Feb  7 22:12:23 2014
@@ -72,6 +72,7 @@ public class MergeStatementTest extends 
     private static  final   String      NO_ROWS_AFFECTED = "02000";
     private static  final   String      NO_DML_IN_BEFORE_TRIGGERS = "42Z9D";
     private static  final   String      NO_SUBQUERIES_IN_MATCHED_CLAUSE = "42XAO";
+    private static  final   String      NO_SYNONYMS_IN_MERGE = "42XAP";
 
     private static  final   String[]    TRIGGER_HISTORY_COLUMNS = new String[] { "ACTION", "ACTION_VALUE" };
 
@@ -4965,6 +4966,75 @@ public class MergeStatementTest extends 
         goodStatement( aliceConnection, "drop table t1_033" );
     }
     
+    /**
+     * <p>
+     * Synonyms not allowed as source or target tables in MERGE statements.
+     * </p>
+     */
+    public  void    test_034_noSynonyms()
+        throws Exception
+    {
+        Connection  dboConnection = openUserConnection( TEST_DBO );
+
+        //
+        // create schema
+        //
+        goodStatement
+            (
+             dboConnection,
+             "create table t1_034( x int, y int )"
+             );
+        goodStatement
+            (
+             dboConnection,
+             "create table t2_034( x int, y int )"
+             );
+        goodStatement
+            (
+             dboConnection,
+             "create synonym syn_t1_034 for t1_034"
+             );
+        goodStatement
+            (
+             dboConnection,
+             "create synonym syn_t2_034 for t2_034"
+             );
+
+        // verify that synonyms are forbidden
+        expectCompilationError
+            ( dboConnection, NO_SYNONYMS_IN_MERGE,
+              "merge into syn_t1_034\n" +
+              "using t2_034 on syn_t1_034.x  = t2_034.x\n" +
+              "when matched then update set syn_t1_034.y = t2_034.y\n"
+              );
+        expectCompilationError
+            ( dboConnection, NO_SYNONYMS_IN_MERGE,
+              "merge into syn_t1_034 a\n" +
+              "using t2_034 on a.x  = t2_034.x\n" +
+              "when matched then update set a.y = t2_034.y\n"
+              );
+        expectCompilationError
+            ( dboConnection, NO_SYNONYMS_IN_MERGE,
+              "merge into t1_034\n" +
+              "using syn_t2_034 on t1_034.x = syn_t2_034.x\n" +
+              "when matched then update set t1_034.y = syn_t2_034.y\n"
+              );
+        expectCompilationError
+            ( dboConnection, NO_SYNONYMS_IN_MERGE,
+              "merge into t1_034\n" +
+              "using syn_t2_034 a on t1_034.x = a.x\n" +
+              "when matched then update set t1_034.y = a.y\n"
+              );
+
+        //
+        // drop schema
+        //
+        goodStatement( dboConnection, "drop synonym syn_t2_034" );
+        goodStatement( dboConnection, "drop synonym syn_t1_034" );
+        goodStatement( dboConnection, "drop table t2_034" );
+        goodStatement( dboConnection, "drop table t1_034" );
+    }
+    
     ///////////////////////////////////////////////////////////////////////////////////
     //
     // ROUTINES