use of org.apache.derby.iapi.sql.compile.ScopeFilter in project derby by apache.
the class DeleteNode method bindStatement.
/**
* Bind this DeleteNode. This means looking up tables and columns and
* getting their types, and figuring out the result types of all
* expressions, as well as doing view resolution, permissions checking,
* etc.
* <p>
* If any indexes need to be updated, we add all the columns in the
* base table to the result column list, so that we can use the column
* values as look-up keys for the index rows to be deleted. Binding a
* delete will also massage the tree so that the ResultSetNode has
* column containing the RowLocation of the base row.
*
* @exception StandardException Thrown on error
*/
@Override
public void bindStatement() throws StandardException {
// We just need select privilege on the where clause tables
getCompilerContext().pushCurrentPrivType(Authorizer.SELECT_PRIV);
try {
FromList fromList = new FromList(getOptimizerFactory().doJoinOrderOptimization(), getContextManager());
ResultColumn rowLocationColumn = null;
CurrentRowLocationNode rowLocationNode;
TableName cursorTargetTableName = null;
CurrentOfNode currentOfNode = null;
//
// Don't add privilege requirements for the UDT types of columns.
// The compiler will attempt to add these when generating the full column list during
// binding of the tables.
//
IgnoreFilter ignorePermissions = new IgnoreFilter();
getCompilerContext().addPrivilegeFilter(ignorePermissions);
DataDictionary dataDictionary = getDataDictionary();
// for DELETE clause of a MERGE statement, the tables have already been bound
if (!inMatchingClause()) {
super.bindTables(dataDictionary);
}
// for positioned delete, get the cursor's target table.
if (SanityManager.DEBUG)
SanityManager.ASSERT(resultSet != null && resultSet instanceof SelectNode, "Delete must have a select result set");
SelectNode sel = (SelectNode) resultSet;
targetTable = (FromTable) sel.fromList.elementAt(0);
if (targetTable instanceof CurrentOfNode) {
currentOfNode = (CurrentOfNode) targetTable;
cursorTargetTableName = inMatchingClause() ? targetTableName : currentOfNode.getBaseCursorTargetTableName();
// instead of an assert, we might say the cursor is not updatable.
if (SanityManager.DEBUG)
SanityManager.ASSERT(cursorTargetTableName != null);
}
if (targetTable instanceof FromVTI) {
targetVTI = (FromVTI) targetTable;
targetVTI.setTarget();
} else {
// we get it from the cursor supplying the position.
if (targetTableName == null) {
// verify we have current of
if (SanityManager.DEBUG)
SanityManager.ASSERT(cursorTargetTableName != null);
targetTableName = cursorTargetTableName;
} else // the named table is the same as the cursor's target (base table name).
if (cursorTargetTableName != null) {
// be the same as a base name in the cursor.
if (!targetTableName.equals(cursorTargetTableName)) {
throw StandardException.newException(SQLState.LANG_CURSOR_DELETE_MISMATCH, targetTableName, currentOfNode.getCursorName());
}
}
}
// descriptor must exist, tables already bound.
verifyTargetTable();
/* Generate a select list for the ResultSetNode - CurrentRowLocation(). */
if (SanityManager.DEBUG) {
SanityManager.ASSERT((resultSet.getResultColumns() == null), "resultColumns is expected to be null until bind time");
}
if (targetTable instanceof FromVTI) {
getResultColumnList();
resultColumnList = targetTable.getResultColumnsForList(null, resultColumnList, null);
/* Set the new result column list in the result set */
resultSet.setResultColumns(resultColumnList);
} else {
/*
** Start off assuming no columns from the base table
** are needed in the rcl.
*/
resultColumnList = new ResultColumnList(getContextManager());
FromBaseTable fbt = getResultColumnList(resultColumnList);
readColsBitSet = getReadMap(dataDictionary, targetTableDescriptor);
resultColumnList = fbt.addColsToList(resultColumnList, readColsBitSet);
/*
** If all bits are set, then behave as if we chose all
** in the first place
*/
int i = 1;
int size = targetTableDescriptor.getMaxColumnID();
for (; i <= size; i++) {
if (!readColsBitSet.get(i)) {
break;
}
}
if (i > size) {
readColsBitSet = null;
}
/* Generate the RowLocation column */
rowLocationNode = new CurrentRowLocationNode(getContextManager());
rowLocationColumn = new ResultColumn(COLUMNNAME, rowLocationNode, getContextManager());
rowLocationColumn.markGenerated();
/* Append to the ResultColumnList */
resultColumnList.addResultColumn(rowLocationColumn);
/* Force the added columns to take on the table's correlation name, if any */
correlateAddedColumns(resultColumnList, targetTable);
/* Add the new result columns to the driving result set */
ResultColumnList originalRCL = resultSet.getResultColumns();
if (originalRCL != null) {
originalRCL.appendResultColumns(resultColumnList, false);
resultColumnList = originalRCL;
}
resultSet.setResultColumns(resultColumnList);
}
// done excluding column types from privilege checking
getCompilerContext().removePrivilegeFilter(ignorePermissions);
/* Bind the expressions before the ResultColumns are bound */
// only add privileges when we're inside the WHERE clause
ScopeFilter scopeFilter = new ScopeFilter(getCompilerContext(), CompilerContext.WHERE_SCOPE, 1);
getCompilerContext().addPrivilegeFilter(scopeFilter);
super.bindExpressions();
getCompilerContext().removePrivilegeFilter(scopeFilter);
/* Bind untyped nulls directly under the result columns */
resultSet.getResultColumns().bindUntypedNullsToResultColumns(resultColumnList);
if (!(targetTable instanceof FromVTI)) {
/* Bind the new ResultColumn */
rowLocationColumn.bindResultColumnToExpression();
bindConstraints(dataDictionary, getOptimizerFactory(), targetTableDescriptor, null, resultColumnList, (int[]) null, readColsBitSet, // we alway include triggers in core language
true, // dummy
new boolean[1]);
/* If the target table is also a source table, then
* the delete will have to be in deferred mode
* For deletes, this means that the target table appears in a
* subquery. Also, self-referencing foreign key deletes
* are deferred. And triggers cause the delete to be deferred.
*/
if (resultSet.subqueryReferencesTarget(targetTableDescriptor.getName(), true) || requiresDeferredProcessing()) {
deferred = true;
}
} else {
deferred = VTIDeferModPolicy.deferIt(DeferModification.DELETE_STATEMENT, targetVTI, null, sel.getWhereClause());
}
/* 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()");
}
// the ref action dependent tables and bind them.
if (fkTableNames != null) {
String currentTargetTableName = targetTableDescriptor.getSchemaName() + "." + targetTableDescriptor.getName();
if (!isDependentTable) {
// graph node
dependentTables = new HashSet<String>();
}
/*Check whether the current target has already been explored.
*If we are seeing the same table name which we binded earlier
*means we have cyclic references.
*/
if (dependentTables.add(currentTargetTableName)) {
cascadeDelete = true;
int noDependents = fkTableNames.length;
dependentNodes = new StatementNode[noDependents];
for (int i = 0; i < noDependents; i++) {
dependentNodes[i] = getDependentTableNode(fkSchemaNames[i], fkTableNames[i], fkRefActions[i], fkColDescriptors[i]);
dependentNodes[i].bindStatement();
}
}
} else {
// case where current dependent table does not have dependent tables
if (isDependentTable) {
String currentTargetTableName = targetTableDescriptor.getSchemaName() + "." + targetTableDescriptor.getName();
dependentTables.add(currentTargetTableName);
}
}
// add need for DELETE privilege on the target table
getCompilerContext().pushCurrentPrivType(getPrivType());
getCompilerContext().addRequiredTablePriv(targetTableDescriptor);
getCompilerContext().popCurrentPrivType();
} finally {
getCompilerContext().popCurrentPrivType();
}
}
Aggregations