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 2008/10/30 19:09:29 UTC

svn commit: r709219 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/sql/dictionary/ engine/org/apache/derby/impl/sql/compile/ engine/org/apache/derby/loc/ shared/org/apache/derby/shared/common/reference/ testing/org/apache/derbyTesting/fun...

Author: rhillegas
Date: Thu Oct 30 11:09:26 2008
New Revision: 709219

URL: http://svn.apache.org/viewvc?rev=709219&view=rev
Log:
DERBY-481: Forbid generation clauses which reference generated columns.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/TableDescriptor.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AlterTableNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTableNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultColumnList.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableElementList.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/GeneratedColumnsTest.java

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/TableDescriptor.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/TableDescriptor.java?rev=709219&r1=709218&r2=709219&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/TableDescriptor.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/dictionary/TableDescriptor.java Thu Oct 30 11:09:26 2008
@@ -411,6 +411,25 @@
 	}
 
 	/**
+	 * Given a list of columns in the table, construct a bit  map of those
+	 * columns' ids.
+	 */
+	public FormatableBitSet makeColumnMap( ColumnDescriptorList cdl )
+	{
+		FormatableBitSet    result = new FormatableBitSet( columnDescriptorList.size() + 1 );
+        int                         count = cdl.size();
+
+        for ( int i = 0; i < count; i++ )
+        {
+            ColumnDescriptor    cd = cdl.elementAt( i );
+
+            result.set( cd.getPosition() );
+        }
+
+        return result;
+	}
+
+	/**
 	 * Gets the highest column id in the table. For now this is the same as
 	 * the number of columns. However, in the future, after we implement
 	 * ALTER TABLE DROP COLUMN, this correspondence won't hold any longer.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AlterTableNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AlterTableNode.java?rev=709219&r1=709218&r2=709219&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AlterTableNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/AlterTableNode.java Thu Oct 30 11:09:26 2008
@@ -397,7 +397,8 @@
 			 * the check constraints and generation clauses.
 			 */
 			if  (numCheckConstraints > 0) { tableElementList.bindAndValidateCheckConstraints(fromList); }
-			if  (numGenerationClauses > 0) { tableElementList.bindAndValidateGenerationClauses(fromList); }
+			if  (numGenerationClauses > 0)
+            { tableElementList.bindAndValidateGenerationClauses(fromList, baseTable.makeColumnMap( baseTable.getGeneratedColumns() ) ); }
 		}
 
 		//Check if we are in alter table to update the statistics. If yes, then

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTableNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTableNode.java?rev=709219&r1=709218&r2=709219&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTableNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/CreateTableNode.java Thu Oct 30 11:09:26 2008
@@ -25,6 +25,7 @@
 import org.apache.derby.iapi.reference.SQLState;
 import org.apache.derby.iapi.reference.Limits;
 
+import org.apache.derby.iapi.services.io.FormatableBitSet;
 import org.apache.derby.iapi.services.property.PropertyUtil;
 import org.apache.derby.iapi.services.sanity.SanityManager;
 
@@ -438,7 +439,7 @@
 			 * the check constraints and generation clauses.
 			 */
 			if  (numCheckConstraints > 0) { tableElementList.bindAndValidateCheckConstraints(fromList); }
-			if  (numGenerationClauses > 0) { tableElementList.bindAndValidateGenerationClauses(fromList); }
+			if  (numGenerationClauses > 0) { tableElementList.bindAndValidateGenerationClauses(fromList, new FormatableBitSet() ); }
 		}
 	}
 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultColumnList.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultColumnList.java?rev=709219&r1=709218&r2=709219&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultColumnList.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultColumnList.java Thu Oct 30 11:09:26 2008
@@ -3015,6 +3015,28 @@
 	}
 
 	/**
+	 * Get the position of first result column with the given name.
+	 *
+	 * @param name       Name of the column
+	 * @param basis		0 (for 0-based ids) or 1 (for 1-based ids)
+	 */
+	public int getPosition( String name, int basis )
+	{
+		int size = size();
+		for (int index = 0; index < size; index++)
+		{
+			ResultColumn rc = (ResultColumn) elementAt(index);
+
+			if ( name.equals( rc.getName() ) )
+			{
+				return index + basis;
+			}
+		}
+
+        return -1;
+	}
+
+	/**
 	 * Record the top level ColumnReferences in the specified array
 	 * and table map
 	 * This is useful when checking for uniqueness conditions.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableElementList.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableElementList.java?rev=709219&r1=709218&r2=709219&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableElementList.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/TableElementList.java Thu Oct 30 11:09:26 2008
@@ -21,6 +21,7 @@
 
 package	org.apache.derby.impl.sql.compile;
 
+import org.apache.derby.iapi.services.io.FormatableBitSet;
 import org.apache.derby.iapi.services.sanity.SanityManager;
 
 import org.apache.derby.iapi.error.StandardException;
@@ -656,16 +657,20 @@
 	 * the specified FromList.  
 	 *
 	 * @param fromList		The FromList in question.
+	 * @param generatedColumns Bitmap of generated columns in the table. Vacuous for CREATE TABLE, but may be non-trivial for ALTER TABLE. This routine may set bits for new generated columns.
 	 *
 	 * @exception StandardException		Thrown on error
 	 */
-	void bindAndValidateGenerationClauses(FromList fromList)
+	void bindAndValidateGenerationClauses(FromList fromList, FormatableBitSet generatedColumns )
 		throws StandardException
 	{
 		CompilerContext cc;
 		FromBaseTable				table = (FromBaseTable) fromList.elementAt(0);
+        int                                 columnCount = table.getResultColumns().size();
 		int						  size = size();
 
+        generatedColumns.grow( columnCount + 1 );
+        
 		cc = getCompilerContext();
 
 		Vector aggregateVector = new Vector();
@@ -689,7 +694,7 @@
 				continue;
 			}
 
-		    generationClauseNode = cdn.getGenerationClauseNode();
+            generationClauseNode = cdn.getGenerationClauseNode();
 
 			// bind the check condition
 			// verify that it evaluates to a boolean
@@ -763,6 +768,9 @@
 			int		numReferenced = rcl.countReferencedColumns();
 			int[]	generationClauseColumnReferences = new int[numReferenced];
 
+            int     position = rcl.getPosition( cdn.getColumnName(), 1 );
+            generatedColumns.set( position );
+        
 			rcl.recordColumnReferences(generationClauseColumnReferences, 1);
 
             DefaultInfoImpl dii = new DefaultInfoImpl
@@ -774,6 +782,36 @@
 			 */
 			rcl.clearColumnReferences();
 		}
+
+        //
+        // Now verify that none of the generated columns reference other
+        // generated columns.
+        //
+		for (int index = 0; index < size; index++)
+		{
+			ColumnDefinitionNode cdn;
+			TableElementNode element = (TableElementNode) elementAt(index);
+
+			if (! (element instanceof ColumnDefinitionNode)) { continue; }
+
+			cdn = (ColumnDefinitionNode) element;
+
+			if (!cdn.hasGenerationClause()) { continue; }
+
+            int[]   referencedColumns = cdn.getDefaultInfo().getReferencedColumnIDs();
+            int     count = referencedColumns.length;
+
+            for ( int i = 0; i < count; i++ )
+            {
+                int         referencedColumnID = referencedColumns[ i ];
+                if ( generatedColumns.isSet( referencedColumnID ) )
+                {
+                    throw StandardException.newException(SQLState.LANG_CANT_REFERENCE_GENERATED_COLUMN, cdn.getColumnName());
+                }
+            }   // end of loop through referenced columns
+
+        }       // end of loop through generated columns
+
 	}
 
 	/**

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=709219&r1=709218&r2=709219&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 Thu Oct 30 11:09:26 2008
@@ -1982,6 +1982,12 @@
             </msg>
 
             <msg>
+                <name>42XA4</name>
+                <text>The generation clause for column '{0}' references other generated columns. This is not allowed.</text>
+                <arg>columnName</arg>
+            </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>
                 <arg>className</arg>

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=709219&r1=709218&r2=709219&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 Thu Oct 30 11:09:26 2008
@@ -897,6 +897,7 @@
     String LANG_AGGREGATE_IN_GENERATION_CLAUSE                  = "42XA1";
     String LANG_NON_DETERMINISTIC_GENERATION_CLAUSE                  = "42XA2";
     String LANG_CANT_OVERRIDE_GENERATION_CLAUSE                  = "42XA3";
+    String LANG_CANT_REFERENCE_GENERATED_COLUMN                  = "42XA4";
 	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/GeneratedColumnsTest.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GeneratedColumnsTest.java?rev=709219&r1=709218&r2=709219&view=diff
==============================================================================
--- db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GeneratedColumnsTest.java (original)
+++ db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/lang/GeneratedColumnsTest.java Thu Oct 30 11:09:26 2008
@@ -59,9 +59,12 @@
     private static  final   String  REDUNDANT_CLAUSE = "42613";
     private static  final   String  UNSTABLE_RESULTS = "42XA2";
     private static  final   String  CANT_OVERRIDE_GENERATION_CLAUSE = "42XA3";
+    private static  final   String  CANT_REFERENCE_GENERATED_COLUMN = "42XA4";
     private static  final   String  CONSTRAINT_VIOLATION = "23513";
     private static  final   String  FOREIGN_KEY_VIOLATION = "23503";
     private static  final   String  ILLEGAL_DUPLICATE = "23505";
+    private static  final   String  MISPLACED_SELECT = "42X01";
+    private static  final   String  COLUMN_OUT_OF_SCOPE = "42X04";
 
     ///////////////////////////////////////////////////////////////////////////////////
     //
@@ -1302,6 +1305,87 @@
 
     }
     
+    /**
+     * <p>
+     * Verify that generated columns can't refer to one another.
+     * </p>
+     */
+    public  void    test_012_referencedColumns()
+        throws Exception
+    {
+        Connection  conn = getConnection();
+
+        expectCompilationError
+            (
+             CANT_REFERENCE_GENERATED_COLUMN,
+             "create table t_recurse_1( a int, b int generated always as ( -a ), c int generated always as ( b*b) )"
+             );
+        expectCompilationError
+            (
+             CANT_REFERENCE_GENERATED_COLUMN,
+             "create table t_recurse_1( a int, b int generated always as ( -b ) )"
+             );
+        
+        goodStatement
+            (
+             conn,
+             "create table t_recurse_1( a int, b int generated always as ( -a ) )"
+             );
+        expectCompilationError
+            (
+             CANT_REFERENCE_GENERATED_COLUMN,
+             "alter table t_recurse_1 add column c int generated always as ( b*b )"
+             );
+        expectCompilationError
+            (
+             CANT_REFERENCE_GENERATED_COLUMN,
+             "alter table t_recurse_1 add column c int generated always as ( -c )"
+             );
+    }
+
+    /**
+     * <p>
+     * Various miscellaneous illegal create/alter table statements.
+     * </p>
+     */
+    public  void    test_013_badReferences()
+        throws Exception
+    {
+        Connection  conn = getConnection();
+
+        goodStatement
+            (
+             conn,
+             "create table t_br_1( a int )"
+             );
+        goodStatement
+            (
+             conn,
+             "create table t_br_3( a int )"
+             );
+        
+        expectCompilationError
+            (
+             COLUMN_OUT_OF_SCOPE,
+             "create table t_br_2( a int, b int generated always as ( t_br_1.a ) )"
+             );
+        expectCompilationError
+            (
+             MISPLACED_SELECT,
+             "create table t_br_2( a int, b int generated always as ( select a from t_br_1 ) )"
+             );
+        expectCompilationError
+            (
+             COLUMN_OUT_OF_SCOPE,
+             "alter table t_br_3 add column b int generated always as ( t_br_1.a )"
+             );
+        expectCompilationError
+            (
+             MISPLACED_SELECT,
+             "alter table t_br_3 add column b int generated always as ( select a from t_br_1 )"
+             );
+    }
+
     ///////////////////////////////////////////////////////////////////////////////////
     //
     // MINIONS