/* If any underlying ResultSetNode is a SelectNode, then we
* need to do a full bind(), including the expressions
* (since the fromList may include a FromSubquery).
*/
DataDictionary dataDictionary = getDataDictionary();
super.bindResultSetsWithTables(dataDictionary);
/*
** Get the TableDescriptor for the table we are inserting into
*/
verifyTargetTable();
// Check the validity of the targetProperties, if they exist
if (targetProperties != null)
{
verifyTargetProperties(dataDictionary);
}
/*
** Get the resultColumnList representing the columns in the base
** table or VTI.
*/
getResultColumnList();
/* If we have a target column list, then it must have the same # of
* entries as the result set's RCL.
*/
if (targetColumnList != null)
{
/*
* Normalize synonym qualifers for column references.
*/
if (synonymTableName != null)
{
normalizeSynonymColumns ( targetColumnList, targetTableName );
}
/* Bind the target column list */
getCompilerContext().pushCurrentPrivType( getPrivType());
if (targetTableDescriptor != null)
{
targetColumnList.bindResultColumnsByName(targetTableDescriptor,
(DMLStatementNode) this);
}
else
{
targetColumnList.bindResultColumnsByName(targetVTI.getResultColumns(), targetVTI,
this);
}
getCompilerContext().popCurrentPrivType();
}
/* Verify that all underlying ResultSets reclaimed their FromList */
if (SanityManager.DEBUG)
{
SanityManager.ASSERT(fromList.size() == 0,
"fromList.size() is expected to be 0, not " +
fromList.size() +
" on return from RS.bindExpressions()");
}
/* Replace any DEFAULTs with the associated tree */
resultSet.replaceDefaults(targetTableDescriptor, targetColumnList);
/* Bind the expressions now that the result columns are bound
* NOTE: This will be the 2nd time for those underlying ResultSets
* that have tables (no harm done), but it is necessary for those
* that do not have tables. It's too hard/not work the effort to
* avoid the redundancy.
*/
super.bindExpressions();
/*
** If the result set is a union, it could be a table constructor.
** Bind any nulls in the result columns of the table constructor
** to the types of the table being inserted into.
**
** The types of ? parameters in row constructors and table constructors
** in an INSERT statement come from the result columns.
**
** If there is a target column list, use that instead of the result
** columns for the whole table, since the columns in the result set
** correspond to the target column list.
*/
if (targetColumnList != null)
{
if (resultSet.getResultColumns().size() > targetColumnList.size())
throw StandardException.newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED);
resultSet.bindUntypedNullsToResultColumns(targetColumnList);
resultSet.setTableConstructorTypes(targetColumnList);
}
else
{
if (resultSet.getResultColumns().size() > resultColumnList.size())
throw StandardException.newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED);
resultSet.bindUntypedNullsToResultColumns(resultColumnList);
resultSet.setTableConstructorTypes(resultColumnList);
}
/* Bind the columns of the result set to their expressions */
resultSet.bindResultColumns(fromList);
int resCols = resultSet.getResultColumns().size();
DataDictionary dd = getDataDictionary();
if (targetColumnList != null)
{
if (targetColumnList.size() != resCols)
throw StandardException.newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED);
}
else
{
if (targetTableDescriptor != null &&
targetTableDescriptor.getNumberOfColumns() != resCols)
throw StandardException.newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED);
}
/* See if the ResultSet's RCL needs to be ordered to match the target
* list, or "enhanced" to accommodate defaults. It can only need to
* be ordered if there is a target column list. It needs to be
* enhanced if there are fewer source columns than there are columns
* in the table.
*/
boolean inOrder = true;
int numTableColumns = resultColumnList.size();
/* colMap[] will be the size of the target list, which could be larger
* than the current size of the source list. In that case, the source
* list will be "enhanced" to include defaults.
*/
int[] colMap = new int[numTableColumns];
// set the fields to an unused value
for (int i = 0; i < colMap.length; i++)
{
colMap[i] = -1;
}
/* Create the source/target list mapping */
if (targetColumnList != null)
{
/*
** There is a target column list, so the result columns might
** need to be ordered. Step through the target column list
** and remember the position in the target table of each column.
** Remember if any of the columns are out of order.
*/
int targetSize = targetColumnList.size();
for (int index = 0; index < targetSize; index++)
{
int position =
((ResultColumn) (targetColumnList.elementAt(index))).
columnDescriptor.getPosition();
if (index != position-1)
{
inOrder = false;
}
// position is 1-base; colMap indexes and entries are 0-based.
colMap[position-1] = index;
}
}
else
{
/*
** There is no target column list, so the result columns in the
** source are presumed to be in the same order as the target
** table.
*/
for (int position = 0;
position < resultSet.getResultColumns().size();
position++)
{
colMap[position] = position;
}
}
// colmap[x] == y means that column x in the target table
// maps to column y in the source result set.
// colmap[x] == -1 means that column x in the target table
// maps to its default value.
// both colmap indexes and values are 0-based.
/* if the list is in order and complete, we don't have to change
* the tree. If it is not, then we call RSN.enhanceRCLForInsert()
* which will either
* (reorder and/or "enhance" the source RCL within the same RSN) or
* (generate and return a PRN with a new reordered/enhanced RCL above
* the existing RSN). This way, RSN's that understand how to do projections
* can avoid the additional PRN while those that do not will get one.
*/
/* NOTE - javascope gives confusing branch coverage info here. By
* breaking apart the following if condition, I have verified that
* we test all cases. (Jerry 7/17/97)
*/
if (! inOrder || resultSet.resultColumns.size() < numTableColumns)
{
// one thing we do know is that all of the resultsets underneath
// us have their resultColumn names filled in with the names of
// the target table columns. That makes generating the mapping
// "easier" -- we simply generate the names of the target table columns
// that are included. For the missing columns, we generate default
// value expressions.
resultSet = resultSet.enhanceRCLForInsert(numTableColumns, colMap,
dataDictionary,
targetTableDescriptor, targetVTI);
}
if (resultSet instanceof UnionNode)
{
// If we are inserting a number of rows in VALUES clause, we need to
// examine each row for 'autoincrement'.
resultColumnList.checkAutoincrementUnion(resultSet);
}
else resultColumnList.checkAutoincrement(resultSet.getResultColumns());
resultColumnList.checkStorableExpressions(resultSet.getResultColumns());
/* Insert a NormalizeResultSetNode above the source if the source
* and target column types and lengths do not match.
*/
if (! resultColumnList.columnTypesAndLengthsMatch(
resultSet.getResultColumns()))
{
resultSet = resultSet.genNormalizeResultSetNode(resultSet, false);
resultColumnList.copyTypesAndLengthsToSource(resultSet.getResultColumns());
}
if (targetTableDescriptor != null)
{
/* Get and bind all constraints on the table */
ResultColumnList sourceRCL = resultSet.getResultColumns();
sourceRCL.copyResultColumnNames(resultColumnList);
checkConstraints = bindConstraints(dataDictionary,
getNodeFactory(),
targetTableDescriptor,
null,
sourceRCL,
(int[]) null,
(FormatableBitSet) null,
false,
true); /* we always include
* triggers in core language */
/* Do we need to do a deferred mode insert */
/*
** Deferred if:
** If the target table is also a source table
** Self-referencing foreign key constraint
** trigger
*/
if (resultSet.referencesTarget(
targetTableDescriptor.getName(), true) ||
requiresDeferredProcessing())
{
deferred = true;
/* Disallow bulk insert replace when target table
* is also a source table.
*/
if (bulkInsertReplace &&
resultSet.referencesTarget(
targetTableDescriptor.getName(), true))
{
throw StandardException.newException(SQLState.LANG_INVALID_BULK_INSERT_REPLACE,
targetTableDescriptor.getQualifiedName());
}
}
/* Get the list of indexes on the table being inserted into */
getAffectedIndexes(targetTableDescriptor);
TransactionController tc =
getLanguageConnectionContext().getTransactionCompile();
autoincRowLocation =
dd.computeAutoincRowLocations(tc, targetTableDescriptor);
if (isPrivilegeCollectionRequired())
{
getCompilerContext().pushCurrentPrivType(getPrivType());
getCompilerContext().addRequiredTablePriv(targetTableDescriptor);