db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rhille...@apache.org
Subject svn commit: r708561 - in /db/derby/code/trunk/java: engine/org/apache/derby/iapi/sql/ engine/org/apache/derby/iapi/sql/execute/ engine/org/apache/derby/impl/sql/ engine/org/apache/derby/impl/sql/compile/ engine/org/apache/derby/impl/sql/execute/ testin...
Date Tue, 28 Oct 2008 13:04:54 GMT
Author: rhillegas
Date: Tue Oct 28 06:04:53 2008
New Revision: 708561

URL: http://svn.apache.org/viewvc?rev=708561&view=rev
Log:
DERBY-481: Basic INSERT support for generated columns.

Modified:
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java
    db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DMLModStatementNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InsertNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultColumn.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/ResultSetNode.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericResultSetFactory.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/InsertResultSet.java
    db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NoRowsResultSetImpl.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/Activation.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java?rev=708561&r1=708560&r2=708561&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/Activation.java Tue Oct 28 06:04:53 2008
@@ -234,6 +234,11 @@
 	void setCurrentRow(ExecRow currentRow, int resultSetNumber);
 
 	/**
+	 * Get the current row at the given index.
+	 */
+	public Row getCurrentRow(int resultSetNumber);
+    
+	/**
 	 * Generated plans have a current row field for ease in defining
 	 * the methods and finding them dynamically. The interface is
 	 * used to set the row before a dynamic method that uses it is

Modified: db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java?rev=708561&r1=708560&r2=708561&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/iapi/sql/execute/ResultSetFactory.java Tue Oct 28 06:04:53 2008
@@ -110,11 +110,12 @@
 
 		@param source the result set from which to take rows to
 			be inserted into the target table.
+		@param generationClauses	The code to compute column generation clauses if any
 		@param checkGM	The code to enforce the check constraints, if any
 		@return the insert operation as a result set.
 		@exception StandardException thrown when unable to perform the insert
 	 */
-	ResultSet getInsertResultSet(NoPutResultSet source, 
+	ResultSet getInsertResultSet(NoPutResultSet source, GeneratedMethod generationClauses,
 								 GeneratedMethod checkGM)
         throws StandardException;
 
@@ -203,8 +204,7 @@
 		@return the update operation as a result set.
 		@exception StandardException thrown when unable to perform the update
 	 */
-	ResultSet getUpdateResultSet(NoPutResultSet source, 
-								 GeneratedMethod checkGM)
+	ResultSet getUpdateResultSet(NoPutResultSet source, GeneratedMethod checkGM)
         throws StandardException;
 
 	/**

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java?rev=708561&r1=708560&r2=708561&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/GenericActivationHolder.java Tue Oct 28 06:04:53 2008
@@ -45,6 +45,7 @@
 import org.apache.derby.iapi.sql.ParameterValueSet;
 import org.apache.derby.iapi.sql.ResultSet;
 import org.apache.derby.iapi.sql.ResultDescription;
+import org.apache.derby.iapi.sql.Row;
 import org.apache.derby.iapi.sql.Activation;
 import org.apache.derby.iapi.sql.execute.CursorResultSet;
 import org.apache.derby.iapi.sql.execute.TemporaryRowHolder;
@@ -371,6 +372,15 @@
 	}
 
 	/**
+	 * @see Activation#getCurrentRow
+	 *
+	 */
+	public Row getCurrentRow(int resultSetNumber)
+    {
+        return ac.getCurrentRow( resultSetNumber );
+    }
+    
+	/**
 	 * @see Activation#clearCurrentRow
 	 */
 	public void clearCurrentRow(int resultSetNumber) 

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DMLModStatementNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DMLModStatementNode.java?rev=708561&r1=708560&r2=708561&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DMLModStatementNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/DMLModStatementNode.java Tue Oct 28 06:04:53 2008
@@ -21,8 +21,10 @@
 
 package	org.apache.derby.impl.sql.compile;
 
+import java.lang.reflect.Modifier;
 import java.util.Enumeration;
 import java.util.Hashtable;
+import java.util.HashSet;
 import java.util.Vector;
 
 import org.apache.derby.catalog.UUID;
@@ -30,7 +32,9 @@
 import org.apache.derby.iapi.reference.ClassName;
 import org.apache.derby.iapi.reference.SQLState;
 import org.apache.derby.iapi.services.classfile.VMOpcode;
+import org.apache.derby.iapi.services.compiler.LocalField;
 import org.apache.derby.iapi.services.compiler.MethodBuilder;
+import org.apache.derby.iapi.services.context.ContextManager;
 import org.apache.derby.iapi.services.io.FormatableBitSet;
 import org.apache.derby.iapi.services.sanity.SanityManager;
 import org.apache.derby.iapi.sql.StatementType;
@@ -400,6 +404,216 @@
 		}
 	}
 
+    /**
+     * Do not allow generation clauses to be overriden. Throws an exception
+     * if the user attempts to override the value of a generated column.
+     * The only value allowed in a generated column is DEFAULT. If we find
+     * a generated column which is being explicitly set to DEFAULT in an INSERT, we remove
+     * it from the column lists--it will be added back in during the enhance
+     * phase. For an update, addedGeneratedColumns will be non-null and we will
+     * use this list to pass through the generated columns which have already
+     * been added to the update list.
+     */
+    void forbidGenerationOverrides( ResultColumnList targetRCL, boolean forUpdate, ColumnDescriptorList addedGeneratedColumns )
+        throws StandardException
+    {
+        int  count = targetRCL.size();
+
+        ResultColumnList    resultRCL = resultSet.getResultColumns();
+        
+        for ( int i = 0; i < count; i++ )
+        {
+            ResultColumn    rc = (ResultColumn) targetRCL.elementAt( i );
+
+            if ( rc.hasGenerationClause() )
+            {
+                ValueNode   resultExpression = ((ResultColumn) resultRCL.elementAt( i )).getExpression();
+
+                if ( !( resultExpression instanceof DefaultNode) )
+                {
+                    //
+                    // For updates, we may have added the generation clause
+                    // ourselves. Here we forgive ourselves for this pro-active behavior.
+                    //
+                    boolean allIsForgiven = false;
+                    
+                    if ( forUpdate )
+                    {
+                        String      columnName = rc.getTableColumnDescriptor().getColumnName();
+                        int         addedCount = addedGeneratedColumns.size();
+
+                        for ( int j = 0; j < addedCount; j++ )
+                        {
+                            String  addedColumnName = addedGeneratedColumns.elementAt( j ).getColumnName();
+
+                            if ( columnName.equals( addedColumnName ) )
+                            {
+                                allIsForgiven = true;
+                                break;
+                            }
+                        }
+                    }
+                    if ( allIsForgiven ) { continue; }
+                    
+                    throw StandardException.newException
+                        ( SQLState.LANG_CANT_OVERRIDE_GENERATION_CLAUSE, rc.getName() );
+                }
+                else
+                {
+                    // skip this step if we're working on an update statement.
+                    // for updates, the target list has already been enhanced.
+                    if ( forUpdate ) { continue; }
+                    
+                    // Prune the generated column and its default. They will be
+                    // added back in during the enhance phase.
+                    targetRCL.removeElementAt( i );
+                    resultRCL.removeElementAt( i );
+
+                    targetRCL.resetVirtualColumnIds();
+                    resultRCL.resetVirtualColumnIds();
+
+                    // account for the dropped entries
+                    count--;
+                    i--;
+                }
+            }
+
+        }
+    }
+    
+    /**
+     * Parse and bind the generating expressions of computed columns.
+     */
+	void parseAndBindGenerationClauses
+	(
+		DataDictionary		dataDictionary,
+		TableDescriptor		targetTableDescriptor,
+		ResultColumnList	sourceRCL,
+		ResultColumnList	targetRCL,
+        boolean                 forUpdate,
+        ResultSetNode       updateResultSet
+    )
+		throws StandardException
+	{
+        int  count = targetRCL.size();
+
+        for ( int i = 0; i < count; i++ )
+        {
+            ResultColumn    rc = (ResultColumn) targetRCL.elementAt( i );
+
+            //
+            // For updates, there are two copies of the column in the row: a
+            // before image and the actual value which will be set when we
+            // update the row. We only want to compile a generation clause for
+            // the value which will be updated.
+            //
+            if ( forUpdate && !rc.updated() ) { continue; }
+            
+            if ( rc.hasGenerationClause() )
+            {
+                ColumnDescriptor    colDesc = rc.getTableColumnDescriptor();
+                ValueNode   generationClause = parseGenerationClause( colDesc.getDefaultInfo().getDefaultText(), targetTableDescriptor );
+
+                bindRowScopedExpression( getNodeFactory(), getContextManager(), targetTableDescriptor, sourceRCL, generationClause );
+
+                ResultColumn    newRC =  (ResultColumn) getNodeFactory().getNode
+                    ( C_NodeTypes.RESULT_COLUMN, generationClause.getTypeServices(), generationClause, getContextManager());
+
+                // replace the result column in place
+                newRC.setVirtualColumnId( i + 1 ); // column ids are 1-based
+                newRC.setColumnDescriptor( targetTableDescriptor, colDesc );
+                targetRCL.setElementAt( newRC, i );
+
+                // if this is an update, then the result column may appear in the
+                // source list as well. replace it there too and perform a
+                // little extra binding so that check constraints will bind and
+                // generate correctly if they reference the generated column
+                if ( forUpdate )
+                {
+                    for ( int j = 0; j < sourceRCL.size(); j++ )
+                    {
+                        if ( rc == sourceRCL.elementAt( j ) )
+                        {
+                            newRC.setName( rc.getName() );
+                            newRC.setResultSetNumber( updateResultSet.getResultSetNumber() );
+                            
+                            sourceRCL.setElementAt( newRC, j );
+                            
+                        }
+                    }   // end of loop through sourceRCL
+                }   // end if this is an update statement
+            }  // end if this is a generated column
+            
+        }   // end of loop through targetRCL
+    }
+    
+ 	/**
+	  *	Parse the generation clause for a column.
+	  *
+	  *	@param	clauseText  Text of the generation clause
+	  *
+	  * @return	The parsed expression as a query tree.
+	  *
+	  * @exception StandardException		Thrown on failure
+	  */
+	public	ValueNode	parseGenerationClause
+	(
+     String				clauseText,
+     TableDescriptor    td
+    )
+		throws StandardException
+	{
+		Parser						p;
+		ValueNode					clauseTree;
+		LanguageConnectionContext	lcc = getLanguageConnectionContext();
+		CompilerContext 			compilerContext = getCompilerContext();
+
+		/* Get a Statement to pass to the parser */
+
+		/* We're all set up to parse. We have to build a compilable SQL statement
+		 * before we can parse -  So, we goober up a VALUES defaultText.
+		 */
+		String select = "SELECT " + clauseText + " FROM " + td.getQualifiedName();
+		
+		/*
+		** Get a new compiler context, so the parsing of the select statement
+		** doesn't mess up anything in the current context (it could clobber
+		** the ParameterValueSet, for example).
+		*/
+		CompilerContext newCC = lcc.pushCompilerContext();
+
+		p = newCC.getParser();
+				
+		/* Finally, we can call the parser */
+		// Since this is always nested inside another SQL statement, so topLevel flag
+		// should be false
+		StatementNode qt = p.parseStatement(select);
+		if (SanityManager.DEBUG)
+		{
+			if (! (qt instanceof CursorNode))
+			{
+				SanityManager.THROWASSERT(
+					"qt expected to be instanceof CursorNode, not " +
+					qt.getClass().getName());
+			}
+			CursorNode cn = (CursorNode) qt;
+			if (! (cn.getResultSetNode() instanceof SelectNode))
+			{
+				SanityManager.THROWASSERT(
+					"cn.getResultSetNode() expected to be instanceof SelectNode, not " +
+					cn.getResultSetNode().getClass().getName());
+			}
+		}
+
+		clauseTree = ((ResultColumn) 
+							((CursorNode) qt).getResultSetNode().getResultColumns().elementAt(0)).
+									getExpression();
+
+		lcc.popCompilerContext(newCC);
+
+		return	clauseTree;
+	}
+
 	/**
 	 * Gets and binds all the constraints for an INSERT/UPDATE/DELETE.
 	 * First finds the constraints that are relevant to this node.
@@ -477,9 +691,9 @@
 			checkConstraints = generateCheckTree(relevantCdl,
 														targetTableDescriptor);
 
-			if (checkConstraints != null)
+            if (checkConstraints != null)
 			{
-				bindCheckConstraint(nodeFactory, 
+				bindRowScopedExpression(nodeFactory, getContextManager(),
 								targetTableDescriptor,
 								sourceRCL,
 								checkConstraints);
@@ -494,44 +708,44 @@
 	}
 
 	/**
-	 * Binds an already parsed check constraint
+	 * Binds an already parsed expression that only involves columns in a single
+	 * row. E.g., a check constraint or a generation clause.
 	 *
 	 * @param nodeFactory			Where to get query tree nodes.
 	 * @param targetTableDescriptor	The TableDescriptor for the constrained table.
 	 * @param sourceRCL		Result columns.
-	 * @param checkConstraint		Parsed query tree for check constraint
+	 * @param expression		Parsed query tree for row scoped expression
 	 *
 	 * @exception StandardException		Thrown on failure
 	 */
-	void	bindCheckConstraint
+	void	bindRowScopedExpression
 	(
 		NodeFactory			nodeFactory,
+        ContextManager    contextManager,
 		TableDescriptor		targetTableDescriptor,
 		ResultColumnList	sourceRCL,
-		ValueNode			checkConstraint
+		ValueNode			expression
     )
 		throws StandardException
 	{
 
-		TableName	targetTableName =
-						makeTableName(targetTableDescriptor.getSchemaName(),
-									  targetTableDescriptor.getName());
-
+		TableName	targetTableName = makeTableName
+            (nodeFactory, contextManager, targetTableDescriptor.getSchemaName(), targetTableDescriptor.getName());
 
-		/* We now have the check constraints as a query tree.  Now, we prepare
+		/* We now have the expression as a query tree.  Now, we prepare
 		 * to bind that query tree to the source's RCL.  That way, the
-		 * generated code for the check constraints will be evaluated against the
+		 * generated code for the expression will be evaluated against the
 		 * source row to be inserted into the target table or
 		 * against the after portion of the source row for the update
 		 * into the target table.
 		 *		o  Goober up a new FromList which has a single table,
 		 *		   a goobered up FromBaseTable for the target table
 		 *		   which has the source's RCL as it RCL.
-		 *		   (This allows the ColumnReferences in the check constraint
+		 *		   (This allows the ColumnReferences in the expression
 		 *		   tree to be bound to the right RCs.)
 		 *
 	 	 * Note that in some circumstances we may not actually verify
-		 * the constraint against the source RCL but against a temp
+		 * the expression against the source RCL but against a temp
 		 * row source used for deferred processing because of a trigger.
 		 * In this case, the caller of bindConstraints (UpdateNode)
 		 * has chosen to pass in the correct RCL to bind against.
@@ -540,7 +754,7 @@
 			(FromList) nodeFactory.getNode(
 							C_NodeTypes.FROM_LIST,
 							nodeFactory.doJoinOrderOptimization(),
-							getContextManager());
+							contextManager);
 		FromBaseTable table = (FromBaseTable)
 			nodeFactory.getNode(
 				C_NodeTypes.FROM_BASE_TABLE,
@@ -548,12 +762,12 @@
 				null,
 				sourceRCL,
 				null,
-				getContextManager());
+				contextManager);
 		table.setTableNumber(0);
 		fakeFromList.addFromTable(table);
 
 		// Now we can do the bind.
-		checkConstraint = checkConstraint.bindExpression(
+		expression = expression.bindExpression(
 										fakeFromList,
 										(SubqueryList) null,
 										(Vector) null);
@@ -582,6 +796,24 @@
 		return (ccCDL.size() > 0);
 	}
 
+	/**
+	 * Determine whether or not there are generated columns in the
+	 * specified table.
+	 *
+	 * @param td	The TableDescriptor for the table
+	 *
+	 * @return Whether or not there are generated columns in the specified table.
+	 *
+	 * @exception StandardException		Thrown on failure
+	 */
+	protected boolean hasGenerationClauses(TableDescriptor td)
+		throws StandardException
+	{
+		ColumnDescriptorList list= td.getGeneratedColumns();
+
+		return (list.size() > 0);
+	}
+
 
 	/**
 	 * Get the ANDing of all appropriate check constraints as 1 giant query tree.
@@ -1304,6 +1536,128 @@
 		return userExprFun;
 	}
 
+	/**
+	  *	Generate the code to evaluate all of the generation clauses. If there
+	  *	are generation clauses, this routine builds an Activation method which
+	  *	evaluates the generation clauses and fills in the computed columns.
+      *
+	  * @exception StandardException		Thrown on error
+	  */
+	public	void	generateGenerationClauses
+	(
+        ResultColumnList            rcl,
+        int                                 resultSetNumber,
+		ExpressionClassBuilder	ecb,
+		MethodBuilder			mb
+    )
+							throws StandardException
+	{
+		ResultColumn rc; 
+		int size = rcl.size();
+        boolean hasGenerationClauses = false;
+
+		for (int index = 0; index < size; index++)
+		{
+		    // generate statements of the form
+			// fieldX.setColumn(columnNumber, (DataValueDescriptor) columnExpr);
+			// and add them to exprFun.
+			rc = (ResultColumn) rcl.elementAt(index);
+
+            //
+            // Generated columns should be populated after the base row because
+            // the generation clauses may refer to base columns that have to be filled
+            // in first.
+            //
+			if ( rc.hasGenerationClause() )
+            {
+                hasGenerationClauses = true;
+                continue;
+            }
+        }
+
+		// we generate an exprFun
+		// that evaluates the generation clauses
+		// against the current row of the child's result.
+		// if there are no generation clauses, simply pass null
+		// to optimize for run time performance.
+
+   		// generate the function and initializer:
+   		// private Integer exprN()
+   		// {
+        //   ...
+   		//   return 1 or NULL;
+   		// }
+   		// static Method exprN = method pointer to exprN;
+
+		// if there are not generation clauses, we just want to pass null.
+		if ( !hasGenerationClauses )
+		{
+		   	mb.pushNull(ClassName.GeneratedMethod);
+		}
+		else
+		{
+			MethodBuilder	userExprFun = generateGenerationClauses( rcl, resultSetNumber, ecb);
+
+	   		// generation clause evaluation is used in the final result set 
+			// as an access of the new static
+   			// field holding a reference to this new method.
+   			ecb.pushMethodReference(mb, userExprFun);
+		}
+	}
+
+	/**
+	  *	Generate a method to compute all of the generation clauses in a row.
+	  */
+	public	MethodBuilder	generateGenerationClauses
+	(
+        ResultColumnList            rcl,
+        int                                 rsNumber,
+		ExpressionClassBuilder	ecb
+    )
+		throws StandardException
+	{
+		// this sets up the method and the static field.
+		// generates:
+		// 	java.lang.Object userExprFun( ) { }
+		MethodBuilder userExprFun = ecb.newUserExprFun();
+		
+		/* Declare the field and load it with the current row */
+		LocalField field = ecb.newFieldDeclaration(Modifier.PRIVATE, ClassName.ExecRow);
+        userExprFun.pushThis();
+        userExprFun.push( rsNumber );
+        userExprFun.callMethod(VMOpcode.INVOKEVIRTUAL, ClassName.BaseActivation, "getCurrentRow", ClassName.Row, 1);
+        userExprFun.putField( field );
+
+		// loop through the result columns, computing generated columns
+        // as we go
+        int     size = rcl.size();
+        for ( int i = 0; i < size; i++ )
+        {
+            ResultColumn    rc = (ResultColumn) rcl.elementAt( i );
+
+            if ( !rc.hasGenerationClause() ) { continue; }
+
+            userExprFun.getField(field); // instance
+            userExprFun.push(i + 1); // arg1
+
+            rc.generateExpression(ecb, userExprFun);
+            userExprFun.cast(ClassName.DataValueDescriptor);
+                
+            userExprFun.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row, "setColumn", "void", 2);
+        }
+
+		/* generates:
+		 *    return;
+		 * And adds it to userExprFun
+		 */
+		userExprFun.methodReturn();
+		
+		// we are done modifying userExprFun, complete it.
+		userExprFun.complete();
+
+		return userExprFun;
+	}
+
   /**
    * Generate an optimized QueryTree from a bound QueryTree.  Actually,
    * it can annotate the tree in place rather than generate a new tree,

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InsertNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InsertNode.java?rev=708561&r1=708560&r2=708561&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InsertNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/InsertNode.java Tue Oct 28 06:04:53 2008
@@ -284,6 +284,9 @@
 														this);
 			}
 			getCompilerContext().popCurrentPrivType();
+
+            // don't allow overriding of generation clauses
+            forbidGenerationOverrides( targetColumnList, false, null );
 		}
 
 		/* Verify that all underlying ResultSets reclaimed their FromList */
@@ -413,7 +416,7 @@
 
 		enhanceAndCheckForAutoincrement(resultSet, inOrder,
 				numTableColumns, colMap, dataDictionary,
-				targetTableDescriptor, targetVTI);
+                targetTableDescriptor, targetVTI );
 
 		resultColumnList.checkStorableExpressions(resultSet.getResultColumns());
 		/* Insert a NormalizeResultSetNode above the source if the source
@@ -432,9 +435,14 @@
 
 		if (targetTableDescriptor != null)
 		{
-			/* Get and bind all constraints on the table */
 			ResultColumnList sourceRCL = resultSet.getResultColumns();
 			sourceRCL.copyResultColumnNames(resultColumnList);
+
+            /* bind all generation clauses for generated columns */
+            parseAndBindGenerationClauses
+                ( dataDictionary, targetTableDescriptor, sourceRCL, resultColumnList, false, null );
+            
+			/* Get and bind all constraints on the table */
 			checkConstraints = bindConstraints(dataDictionary,
 												getNodeFactory(),
 												targetTableDescriptor,
@@ -536,7 +544,7 @@
 			boolean inOrder, int numTableColumns, int []colMap, 
 			DataDictionary dataDictionary,
 			TableDescriptor targetTableDescriptor,
-			FromVTI targetVTI)
+            FromVTI targetVTI)
 		throws StandardException
 	{
 		/*
@@ -584,7 +592,7 @@
 			if (! inOrder || resultSet.resultColumns.size() < numTableColumns)
 				resultSet.enhanceRCLForInsert(
 						numTableColumns, colMap, dataDictionary,
-						targetTableDescriptor, targetVTI);
+						targetTableDescriptor,targetVTI);
 		}
 		else
 		{
@@ -807,7 +815,7 @@
 	/**
 	 * Code generation for insert
 	 * creates an expression for:
-	 *   ResultSetFactory.getInsertResultSet(resultSet.generate(ps), this )
+	 *   ResultSetFactory.getInsertResultSet(resultSet.generate(ps), generationClausesResult, checkConstrainResult, this )
 	 *
 	 * @param acb	The ActivationClassBuilder for the class being built
 	 * @param mb the method  for the execute() method to be built
@@ -840,10 +848,13 @@
 			// arg 1
 			resultSet.generate(acb, mb);
 
-			// arg 2 generate code to evaluate CHECK CONSTRAINTS
+			// arg 2 generate code to evaluate generation clauses
+			generateGenerationClauses( resultColumnList, resultSet.getResultSetNumber(), acb, mb );
+
+			// arg 3 generate code to evaluate CHECK CONSTRAINTS
 			generateCheckConstraints( checkConstraints, acb, mb );
 
-			mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getInsertResultSet", ClassName.ResultSet, 2);
+			mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getInsertResultSet", ClassName.ResultSet, 3);
 		}
 		else
 		{

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultColumn.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultColumn.java?rev=708561&r1=708560&r2=708561&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultColumn.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultColumn.java Tue Oct 28 06:04:53 2008
@@ -149,7 +149,7 @@
 		{
 			this.name = (String) arg1;
 			this.exposedName = this.name;
-			this.expression = (ValueNode) arg2;
+			setExpression( (ValueNode) arg2 );
 		}
 		else if (arg1 instanceof ColumnReference)
 		{
@@ -162,7 +162,7 @@
 				the reference has the right table name.
 		 	*/
 			this.reference = ref; 
-			this.expression = (ValueNode) arg2;
+			setExpression( (ValueNode) arg2 );
 		}
 		else if (arg1 instanceof ColumnDescriptor)
 		{
@@ -172,13 +172,13 @@
 			this.exposedName = name;
 			setType(coldes.getType());
 			this.columnDescriptor = coldes;
-			this.expression = (ValueNode) arg2;
+			setExpression( (ValueNode) arg2 );
 			this.autoincrement = coldes.isAutoincrement();
 		}
 		else
 		{
 			setType((DataTypeDescriptor) arg1);
-			this.expression = (ValueNode) arg2;
+			setExpression( (ValueNode) arg2 );
 			if (arg2 instanceof ColumnReference)
 			{
 				reference = (ColumnReference) arg2;
@@ -355,7 +355,7 @@
 	void setExpressionToNullNode()
 		throws StandardException
 	{
-		expression = getNullNode(getTypeServices());
+		setExpression( getNullNode(getTypeServices()) );
 	}
 
 	/**
@@ -585,8 +585,8 @@
 			}
 		}
 
-		expression = expression.bindExpression(fromList, subqueryList,
-									aggregateVector);
+		setExpression( expression.bindExpression(fromList, subqueryList,
+                                                 aggregateVector) );
 
 		if (expression instanceof ColumnReference)
 		{
@@ -690,7 +690,7 @@
 		if (isPrivilegeCollectionRequired())
 			getCompilerContext().addRequiredColumnPriv( columnDescriptor);
 	}
-	
+
 	/**
 	 * Change an untyped null to a typed null.
 	 *
@@ -714,7 +714,7 @@
         	//eg insert into table1 values(1,null)
         	//When this method is executed for the sql above, we don't know
         	//the type of the null at this point.
-            expression = getNullNode(bindingRC.getTypeServices());
+            setExpression( getNullNode(bindingRC.getTypeServices()) );
         else if( ( expression instanceof ColumnReference) && expression.getTypeServices() == null)
         {
             // The expression must be a reference to a null column in a values table.
@@ -822,9 +822,9 @@
 	{
 		if (expression == null)
 			return this;
-		expression = expression.preprocess(numTables, outerFromList,
+		setExpression( expression.preprocess(numTables, outerFromList,
 										   outerSubqueryList,
-										   outerPredicateList);
+                                           outerPredicateList) );
 		return this;
 	}
 
@@ -1421,6 +1421,7 @@
   		if (isGenerated()) {
   			newResultColumn.markGenerated();
   		}
+
   		return newResultColumn;
 	}
 
@@ -1512,7 +1513,7 @@
 	
 		if (expression != null && !v.stopTraversal())
 		{
-			expression = (ValueNode)expression.accept(v);
+			setExpression( (ValueNode)expression.accept(v) );
 		}
 		return returnNode;
 	}
@@ -1770,4 +1771,15 @@
 		}
 		return false;
 	}
+
+	/**
+	 * Return true if this result column represents a generated column.
+	 */
+	public boolean hasGenerationClause()
+	{
+        if ( (columnDescriptor != null) && columnDescriptor.hasGenerationClause() ) { return true; }
+        else { return false; }
+	}
+    
 }
+

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=708561&r1=708560&r2=708561&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 Tue Oct 28 06:04:53 2008
@@ -40,7 +40,10 @@
 import org.apache.derby.iapi.services.sanity.SanityManager;
 import org.apache.derby.iapi.sql.ResultColumnDescriptor;
 import org.apache.derby.iapi.sql.compile.C_NodeTypes;
+import org.apache.derby.iapi.sql.compile.CompilerContext;
+import org.apache.derby.iapi.sql.compile.Parser;
 import org.apache.derby.iapi.sql.compile.NodeFactory;
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
 import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
 import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
@@ -279,6 +282,7 @@
 		for (int index = 0; index < size; index++)
 		{
 			ResultColumn resultColumn = (ResultColumn) elementAt(index);
+
 			if (columnName.equals( resultColumn.getName()) )
 			{
 				/* Mark ResultColumn as referenced and return it */
@@ -608,7 +612,6 @@
 		return retVal;
 	}
 
-
 	/**
 	 * Copy the result column names from the given ResultColumnList
 	 * to this ResultColumnList.  This is useful for insert-select,
@@ -974,7 +977,7 @@
 
 			if (rc.getTypeId().streamStorable())
 			{
-				//System.out.println("    streamStorable=true");
+                //System.out.println("    streamStorable=true");
 				ColumnDescriptor cd = rc.getTableColumnDescriptor();
 				isSS[cd.getPosition()-1] = true;
 			}
@@ -1126,6 +1129,18 @@
 				}
 			}
 
+            //
+            // Generated columns should be populated after the base row because
+            // the generation clauses may refer to base columns that have to be filled
+            // in first. Population of generated columns is done in another
+            // method, which (like CHECK CONSTRAINTS) is explicitly called by
+            // InsertResultSet and UpdateResultSet.
+            //
+			if ( rc.hasGenerationClause() )
+            {
+                continue;
+            }
+            
 			// we need the expressions to be Columns exactly.
 
 			/* SPECIAL CASE:  Expression is a non-null constant.
@@ -1161,6 +1176,7 @@
 			 * is a typed null value.
 			 */
 			boolean needDVDCast = true;
+
 			if (rc.isAutoincrementGenerated())
 			{
 				// (com.ibm.db2j.impl... DataValueDescriptor)
@@ -1188,7 +1204,7 @@
 				acb.generateNullWithExpress(userExprFun, rc.getTypeCompiler(),
 						rc.getTypeServices().getCollationType());
 			}
-			else
+            else
 			{
 				rc.generateExpression(acb, userExprFun);
 			}
@@ -1197,6 +1213,7 @@
 
 			userExprFun.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row, "setColumn", "void", 2);
 		}
+
 		userExprFun.getField(field);
 		userExprFun.methodReturn();
 
@@ -4165,5 +4182,6 @@
 		 * Needed for ordering to work.
 		 */
 		resetVirtualColumnIds();
-	}	
+	}
+
 }

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultSetNode.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultSetNode.java?rev=708561&r1=708560&r2=708561&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultSetNode.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/compile/ResultSetNode.java Tue Oct 28 06:04:53 2008
@@ -1146,18 +1146,22 @@
                   else
                 */
                 {
-                    // Generate the tree for the default
-                    String defaultText = defaultInfo.getDefaultText();
-                    ValueNode defaultTree = parseDefault(defaultText);
-                    defaultTree = defaultTree.bindExpression(
-                        getFromList(),
-                        (SubqueryList) null,
-                        (Vector) null);
-                    newResultColumn = (ResultColumn) getNodeFactory().getNode(
-                        C_NodeTypes.RESULT_COLUMN,
-                        defaultTree.getTypeServices(),
-                        defaultTree,
-                        getContextManager());
+                    if ( colDesc.hasGenerationClause() )
+                    {
+                        // later on we will revisit the generated columns and bind
+                        // their generation clauses
+                        newResultColumn = createGeneratedColumn( targetTD, colDesc );
+                    }
+                    else
+                    {
+                        // Generate the tree for the default
+                        String defaultText = defaultInfo.getDefaultText();
+                        ValueNode defaultTree = parseDefault(defaultText);
+                        defaultTree = defaultTree.bindExpression
+                            (getFromList(), (SubqueryList) null, (Vector) null);
+                        newResultColumn = (ResultColumn) getNodeFactory().getNode
+                            ( C_NodeTypes.RESULT_COLUMN, defaultTree.getTypeServices(), defaultTree, getContextManager());
+                    }
 
                     DefaultDescriptor defaultDescriptor = colDesc.getDefaultDescriptor(dataDictionary);
                     if (SanityManager.DEBUG)
@@ -1195,6 +1199,25 @@
 	}
 
 	/**
+	  * Create a ResultColumn for a column with a generation clause.
+	  */
+    private  ResultColumn    createGeneratedColumn
+        (
+         TableDescriptor    targetTD,
+         ColumnDescriptor   colDesc
+         )
+        throws StandardException
+    {
+        ValueNode       dummy = (ValueNode) getNodeFactory().getNode
+            ( C_NodeTypes.UNTYPED_NULL_CONSTANT_NODE, getContextManager());
+        ResultColumn    newResultColumn = (ResultColumn) getNodeFactory().getNode
+            ( C_NodeTypes.RESULT_COLUMN, colDesc.getType(), dummy, getContextManager());
+        newResultColumn.setColumnDescriptor( targetTD, colDesc );
+
+        return newResultColumn;
+    }
+
+	/**
 	  *	Parse a default and turn it into a query tree.
 	  *
 	  *	@param	defaultText			Text of Default.

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java?rev=708561&r1=708560&r2=708561&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/BaseActivation.java Tue Oct 28 06:04:53 2008
@@ -35,7 +35,7 @@
 import org.apache.derby.iapi.services.uuid.UUIDFactory;
 import org.apache.derby.iapi.services.monitor.Monitor;
 import org.apache.derby.iapi.sql.depend.Provider;
-
+import org.apache.derby.iapi.sql.Row;
 import org.apache.derby.iapi.error.StandardException;
 import org.apache.derby.iapi.jdbc.ConnectionContext;
 import org.apache.derby.iapi.reference.Property;
@@ -1331,7 +1331,7 @@
 	 * Remember the row for the specified ResultSet.
 	 */
 	public void setCurrentRow(ExecRow currentRow, int resultSetNumber)
-	{ 
+	{
 		if (SanityManager.DEBUG) 
 		{
 			SanityManager.ASSERT(!isClosed(), "closed");
@@ -1373,6 +1373,17 @@
 	}
 
 	/**
+	 * Get the current row at the given index.
+	 */
+	public Row getCurrentRow(int resultSetNumber)
+	{
+        return row[resultSetNumber];
+	}
+
+	/**
+	 * Return the current SQL session context for all immediately
+	 * nested connections stemming from the call or function
+	 * invocation of the statement corresponding to this activation.
 	 * @see org.apache.derby.iapi.sql.Activation#getSQLSessionContextForChildren
 	 */
 	public SQLSessionContext getSQLSessionContextForChildren() {

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericResultSetFactory.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericResultSetFactory.java?rev=708561&r1=708560&r2=708561&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericResultSetFactory.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/GenericResultSetFactory.java Tue Oct 28 06:04:53 2008
@@ -67,13 +67,13 @@
 		@see ResultSetFactory#getInsertResultSet
 		@exception StandardException thrown on error
 	 */
-	public ResultSet getInsertResultSet(NoPutResultSet source, 
+	public ResultSet getInsertResultSet(NoPutResultSet source, GeneratedMethod generationClauses,
 										GeneratedMethod checkGM)
 		throws StandardException
 	{
 		Activation activation = source.getActivation();
 		getAuthorizer(activation).authorize(activation, Authorizer.SQL_WRITE_OP);
-		return new InsertResultSet(source, checkGM, activation );
+		return new InsertResultSet(source, generationClauses, checkGM, activation );
 	}
 
 	/**
@@ -139,8 +139,7 @@
 		@see ResultSetFactory#getUpdateResultSet
 		@exception StandardException thrown on error
 	 */
-	public ResultSet getUpdateResultSet(NoPutResultSet source,
-										GeneratedMethod checkGM)
+	public ResultSet getUpdateResultSet(NoPutResultSet source, GeneratedMethod checkGM)
 			throws StandardException
 	{
 		Activation activation = source.getActivation();

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/InsertResultSet.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/InsertResultSet.java?rev=708561&r1=708560&r2=708561&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/InsertResultSet.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/InsertResultSet.java Tue Oct 28 06:04:53 2008
@@ -88,6 +88,7 @@
 	private	NoPutResultSet			sourceResultSet;
 	NoPutResultSet			savedSource;
 	InsertConstantAction	constants;
+    private GeneratedMethod         generationClauses;
 	private GeneratedMethod			checkGM;
 	private long					heapConglom;
 
@@ -254,7 +255,12 @@
 			triggerActivator.notifyEvent(TriggerEvents.BEFORE_INSERT,
 											(CursorResultSet)null,
 											rowHolder.getResultSet());
-		} 
+		}
+
+        if ( generationClauses != null )
+        {
+            evaluateGenerationClauses( generationClauses, activation, sourceResultSet, execRow );
+        }
 
 		if (checkGM != null && !hasBeforeStatementTrigger)
 		{
@@ -300,6 +306,7 @@
 	 * @exception StandardException		Thrown on error
      */
     InsertResultSet(NoPutResultSet source, 
+						   GeneratedMethod generationClauses,
 						   GeneratedMethod checkGM,
 						   Activation activation)
 		throws StandardException
@@ -307,8 +314,9 @@
 		super(activation);
 		sourceResultSet = source;
 		constants = (InsertConstantAction) constantAction;
+        this.generationClauses = generationClauses;
 		this.checkGM = checkGM;
-		heapConglom = constants.conglomId; 
+		heapConglom = constants.conglomId;
 
         tc = activation.getTransactionController();
 		fkInfoArray = constants.getFKInfo();
@@ -444,10 +452,10 @@
 												(CursorResultSet)null,
 												tableScan);
 			
-				// if we have a check constraint, we have
+				// if we have a check constraint or generation clauses, we have
 				// to do it the hard way now before we get
 				// to our AFTER triggers.
-				if (checkGM != null)
+				if ((checkGM != null) || (generationClauses != null) )
 				{
 					tableScan = getTableScanResultSet(baseTableConglom); 
 
@@ -459,7 +467,7 @@
 							// we have to set the source row so the check constraint
 							// sees the correct row.
 							sourceResultSet.setCurrentRow(currRow);
-							evaluateCheckConstraints();
+ 							evaluateCheckConstraints();
 						}
 					} finally
 					{
@@ -972,6 +980,9 @@
 			if (activation.getAutoGeneratedKeysResultsetMode())
 				autoGeneratedKeysRowsHolder.insert(getCompactRow(row, columnIndexes));
 
+            // fill in columns that are computed from expressions on other columns
+            evaluateGenerationClauses( generationClauses, activation, sourceResultSet, row );
+                    
 			/*
 			** If we're doing a deferred insert, insert into the temporary
 			** conglomerate.  Otherwise, insert directly into the permanent

Modified: db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NoRowsResultSetImpl.java
URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NoRowsResultSetImpl.java?rev=708561&r1=708560&r2=708561&view=diff
==============================================================================
--- db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NoRowsResultSetImpl.java (original)
+++ db/derby/code/trunk/java/engine/org/apache/derby/impl/sql/execute/NoRowsResultSetImpl.java Tue Oct 28 06:04:53 2008
@@ -583,6 +583,46 @@
 	}
 
 	/**
+	  * Compute the generation clauses on the current row in order to fill in computed columns.
+	  */
+	public	void	evaluateGenerationClauses
+	(
+	  GeneratedMethod generationClauses,
+	  Activation activation,
+      NoPutResultSet    source,
+      ExecRow           newRow
+	)
+		throws StandardException
+	{
+		if (generationClauses != null)
+		{
+            ExecRow oldRow = (ExecRow) activation.getCurrentRow( source.resultSetNumber() );
+
+            //
+            // We may need to poke the current row into the Activation so that
+            // it is visible to the method which evaluates the generation
+            // clause. This is because the generation clause may refer to other
+            // columns in that row.
+            //
+            try {
+                source.setCurrentRow( newRow );
+                generationClauses.invoke(activation);
+            }
+            finally
+            {
+                //
+                // We restore the Activation to its state before we ran the generation
+                // clause. This may not be necessary but I don't understand all of
+                // the paths through the Insert and Update result sets. This
+                // defensive coding seems prudent to me.
+                //
+                if ( oldRow == null ) { source.clearCurrentRow(); }
+                else { source.setCurrentRow( oldRow ); }
+            }
+		}
+	}
+
+	/**
 	  *	Run check constraints against the current row. Raise an error if
 	  * a check constraint is violated.
 	  *

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=708561&r1=708560&r2=708561&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 Tue Oct 28 06:04:53 2008
@@ -377,113 +377,113 @@
              );
     }
 
-    //    /**
-    //     * <p>
-    //     * Verify basic insert behavior for generated columns.
-    //     * </p>
-    //     */
-    //    public  void    test_005_basicInsert()
-    //        throws Exception
-    //    {
-    //        Connection  conn = getConnection();
-    //
-    //        goodStatement
-    //            (
-    //             conn,
-    //             "create table t_insert_1( a int,  b int  default 1, c int )"
-    //             );
-    //        goodStatement
-    //            (
-    //             conn,
-    //             "create table t_insert_2( a int,  b int  generated always as( -a ) check ( b < 0 ), c int )"
-    //             );
-    //        goodStatement
-    //            (
-    //             conn,
-    //             "create unique index t_insert_2_b on t_insert_2( b )"
-    //             );
-    //
-    //        goodStatement
-    //            (
-    //             conn,
-    //             "insert into t_insert_1( a, c ) values ( 100, 1000 ), ( 200, 2000 ), ( 300, 3000 )"
-    //             );
-    //
-    //        // insert one row
-    //        goodStatement
-    //            (
-    //             conn,
-    //             "insert into t_insert_2( a, c ) values ( 2, 200 )"
-    //             );
-    //
-    //        // insert multiple rows
-    //        goodStatement
-    //            (
-    //             conn,
-    //             "insert into t_insert_2( a, c ) values ( 1, 100 ), ( 3, 300 ), ( 4, 400 ), ( 5, 500 )"
-    //             );
-    //
-    //        // insert by selecting from another table
-    //        goodStatement
-    //            (
-    //             conn,
-    //             "insert into t_insert_2( a, c ) select a, c from t_insert_1"
-    //             );
-    //
-    //        // insert using a default clause on the generated column
-    //        goodStatement
-    //            (
-    //             conn,
-    //             "insert into t_insert_2( a, b ) values ( 6, default )"
-    //             );
-    //
-    //        //
-    //        // Verify that all of the expected rows are in the table having the
-    //        // generated column.
-    //        //
-    //        assertResults
-    //            (
-    //             conn,
-    //             "select * from t_insert_2 order by a",
-    //             new String[][]
-    //             {
-    //                 { "1" ,         "-1" ,        "100" },
-    //                 { "2" ,         "-2" ,        "200" },
-    //                 { "3" ,         "-3" ,        "300" },
-    //                 { "4" ,         "-4" ,        "400" },
-    //                 { "5" ,         "-5" ,        "500" },
-    //                 { "6" ,         "-6" ,        null },
-    //                 { "100",        "-100" ,      "1000" },
-    //                 { "200" ,       "-200" ,      "2000" },
-    //                 { "300" ,       "-300" ,      "3000" },
-    //             },
-    //             false
-    //             );
-    //
-    //        // fails trying to override a generation clause
-    //        expectCompilationError
-    //            (
-    //             CANT_OVERRIDE_GENERATION_CLAUSE,
-    //             "insert into t_insert_2( a, b ) values ( 7, 70 )"
-    //             );
-    //        
-    //        // fails on a violation of the check constraint on the generated column
-    //        expectExecutionError
-    //            (
-    //             conn,
-    //             CONSTRAINT_VIOLATION,
-    //             "insert into t_insert_2( a ) values ( -8 )"
-    //             );
-    //        
-    //        // fails because it violates the unique index on the generated column
-    //        expectExecutionError
-    //            (
-    //             conn,
-    //             ILLEGAL_DUPLICATE,
-    //             "insert into t_insert_2( a ) values ( 2 )"
-    //             );
-    //        
-    //    }
+    /**
+     * <p>
+     * Verify basic insert behavior for generated columns.
+     * </p>
+     */
+    public  void    test_005_basicInsert()
+        throws Exception
+    {
+        Connection  conn = getConnection();
+        
+        goodStatement
+            (
+             conn,
+             "create table t_insert_1( a int,  b int  default 1, c int )"
+             );
+        goodStatement
+            (
+             conn,
+             "create table t_insert_2( a int,  b int  generated always as( -a ) check ( b < 0 ), c int )"
+             );
+        goodStatement
+            (
+             conn,
+             "create unique index t_insert_2_b on t_insert_2( b )"
+             );
+        
+        goodStatement
+            (
+             conn,
+             "insert into t_insert_1( a, c ) values ( 100, 1000 ), ( 200, 2000 ), ( 300, 3000 )"
+             );
+        
+        // insert one row
+        goodStatement
+            (
+             conn,
+             "insert into t_insert_2( a, c ) values ( 2, 200 )"
+             );
+        
+        // insert multiple rows
+        goodStatement
+            (
+             conn,
+             "insert into t_insert_2( a, c ) values ( 1, 100 ), ( 3, 300 ), ( 4, 400 ), ( 5, 500 )"
+             );
+        
+        // insert by selecting from another table
+        goodStatement
+            (
+             conn,
+             "insert into t_insert_2( a, c ) select a, c from t_insert_1"
+             );
+        
+        // insert using a default clause on the generated column
+        goodStatement
+            (
+             conn,
+             "insert into t_insert_2( a, b ) values ( 6, default )"
+             );
+        
+        //
+        // Verify that all of the expected rows are in the table having the
+        // generated column.
+        //
+        assertResults
+            (
+             conn,
+             "select * from t_insert_2 order by a",
+             new String[][]
+             {
+                 { "1" ,         "-1" ,        "100" },
+                 { "2" ,         "-2" ,        "200" },
+                 { "3" ,         "-3" ,        "300" },
+                 { "4" ,         "-4" ,        "400" },
+                 { "5" ,         "-5" ,        "500" },
+                 { "6" ,         "-6" ,        null },
+                 { "100",        "-100" ,      "1000" },
+                 { "200" ,       "-200" ,      "2000" },
+                 { "300" ,       "-300" ,      "3000" },
+             },
+             false
+             );
+        
+        // fails trying to override a generation clause
+        expectCompilationError
+            (
+             CANT_OVERRIDE_GENERATION_CLAUSE,
+             "insert into t_insert_2( a, b ) values ( 7, 70 )"
+             );
+        
+        // fails on a violation of the check constraint on the generated column
+        expectExecutionError
+            (
+             conn,
+             CONSTRAINT_VIOLATION,
+             "insert into t_insert_2( a ) values ( -8 )"
+             );
+        
+        // fails because it violates the unique index on the generated column
+        expectExecutionError
+            (
+             conn,
+             ILLEGAL_DUPLICATE,
+             "insert into t_insert_2( a ) values ( 2 )"
+             );
+        
+    }
 
     //    /**
     //     * <p>



Mime
View raw message