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/28 14:04:54 UTC
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...
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>