blob: 56eb0e826476c5fde9250d5541915fa1c04c7794 [file] [log] [blame]
/* Copyright (c) 2001-2010, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb;
import org.hsqldb.HsqlNameManager.HsqlName;
import org.hsqldb.ParserDQL.CompileContext;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.HashSet;
import org.hsqldb.lib.OrderedHashSet;
import org.hsqldb.navigator.RangeIterator;
import org.hsqldb.navigator.RowIterator;
import org.hsqldb.navigator.RowSetNavigator;
import org.hsqldb.navigator.RowSetNavigatorClient;
import org.hsqldb.navigator.RowSetNavigatorDataChange;
import org.hsqldb.persist.PersistentStore;
import org.hsqldb.result.Result;
import org.hsqldb.result.ResultConstants;
import org.hsqldb.types.Type;
/**
* Implementation of Statement for DML statements.<p>
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 2.0.0
* @since 1.9.0
*/
// support for MERGE statement originally contributed by Justin Spadea (jzs9783@users dot sourceforge.net)
public class StatementDML extends StatementDMQL {
Expression[] targets;
Expression updatableTableCheck;
RangeVariable checkRangeVariable;
boolean isTruncate;
public StatementDML(int type, int group, HsqlName schemaName) {
super(type, group, schemaName);
}
/**
* Instantiate this as a DELETE statement
*/
StatementDML(Session session, Table targetTable,
RangeVariable[] rangeVars, CompileContext compileContext,
boolean restartIdentity, int type) {
super(StatementTypes.DELETE_WHERE, StatementTypes.X_SQL_DATA_CHANGE,
session.getCurrentSchemaHsqlName());
this.targetTable = targetTable;
this.baseTable = targetTable.getBaseTable() == null ? targetTable
: targetTable
.getBaseTable();
this.targetRangeVariables = rangeVars;
this.restartIdentity = restartIdentity;
setDatabseObjects(session, compileContext);
checkAccessRights(session);
if (type == StatementTypes.TRUNCATE) {
isTruncate = true;
}
targetRangeVariables[0].addAllColumns();
}
/**
* Instantiate this as an UPDATE statement.
*/
StatementDML(Session session, Expression[] targets, Table targetTable,
RangeVariable rangeVars[], int[] updateColumnMap,
Expression[] colExpressions, boolean[] checkColumns,
CompileContext compileContext) {
super(StatementTypes.UPDATE_WHERE, StatementTypes.X_SQL_DATA_CHANGE,
session.getCurrentSchemaHsqlName());
this.targets = targets;
this.targetTable = targetTable;
this.baseTable = targetTable.getBaseTable() == null ? targetTable
: targetTable
.getBaseTable();
this.updateColumnMap = updateColumnMap;
this.updateExpressions = colExpressions;
this.updateCheckColumns = checkColumns;
this.targetRangeVariables = rangeVars;
setDatabseObjects(session, compileContext);
checkAccessRights(session);
setupChecks();
targetRangeVariables[0].addAllColumns();
}
/**
* Instantiate this as a MERGE statement.
*/
StatementDML(Session session, Expression[] targets,
RangeVariable[] targetRangeVars, int[] insertColMap,
int[] updateColMap, boolean[] checkColumns,
Expression mergeCondition, Expression insertExpr,
Expression[] updateExpr, CompileContext compileContext) {
super(StatementTypes.MERGE, StatementTypes.X_SQL_DATA_CHANGE,
session.getCurrentSchemaHsqlName());
this.targets = targets;
this.sourceTable = targetRangeVars[0].rangeTable;
this.targetTable = targetRangeVars[1].rangeTable;
this.baseTable = targetTable.getBaseTable() == null ? targetTable
: targetTable
.getBaseTable();
this.insertCheckColumns = checkColumns;
this.insertColumnMap = insertColMap;
this.updateColumnMap = updateColMap;
this.insertExpression = insertExpr;
this.updateExpressions = updateExpr;
this.targetRangeVariables = targetRangeVars;
this.condition = mergeCondition;
setDatabseObjects(session, compileContext);
checkAccessRights(session);
setupChecks();
}
/**
* Instantiate this as a CURSOR operation statement.
*/
StatementDML() {
super(StatementTypes.UPDATE_CURSOR, StatementTypes.X_SQL_DATA_CHANGE,
null);
}
void setupChecks() {
if (targetTable != baseTable) {
QuerySpecification select =
((TableDerived) targetTable).getQueryExpression()
.getMainSelect();
this.updatableTableCheck = select.checkQueryCondition;
this.checkRangeVariable = select.rangeVariables[0];
}
}
Result getResult(Session session) {
Result result = null;
switch (type) {
case StatementTypes.UPDATE_WHERE :
result = executeUpdateStatement(session);
break;
case StatementTypes.MERGE :
result = executeMergeStatement(session);
break;
case StatementTypes.DELETE_WHERE :
if (isTruncate) {
result = executeDeleteTruncateStatement(session);
} else {
result = executeDeleteStatement(session);
}
break;
default :
throw Error.runtimeError(ErrorCode.U_S0500, "StatementDML");
}
return result;
}
// this fk references -> other : other read lock
void collectTableNamesForRead(OrderedHashSet set) {
if (baseTable.isView()) {
getTriggerTableNames(set, false);
} else if (!baseTable.isTemp()) {
for (int i = 0; i < baseTable.fkConstraints.length; i++) {
Constraint constraint = baseTable.fkConstraints[i];
if (type == StatementTypes.UPDATE_WHERE
|| type == StatementTypes.MERGE) {
if (ArrayUtil.haveCommonElement(constraint.getRefColumns(),
updateColumnMap)) {
set.add(
baseTable.fkConstraints[i].getMain().getName());
}
} else if (type == StatementTypes.INSERT) {
set.add(baseTable.fkConstraints[i].getMain().getName());
}
}
if (type == StatementTypes.UPDATE_WHERE
|| type == StatementTypes.MERGE) {
baseTable.collectFKReadLocks(updateColumnMap, set);
} else if (type == StatementTypes.DELETE_WHERE) {
baseTable.collectFKReadLocks(null, set);
}
getTriggerTableNames(set, false);
}
for (int i = 0; i < rangeVariables.length; i++) {
Table rangeTable = rangeVariables[i].rangeTable;
HsqlName name = rangeTable.getName();
if (rangeTable.isReadOnly() || rangeTable.isTemp()) {
continue;
}
if (name.schema == SqlInvariants.SYSTEM_SCHEMA_HSQLNAME) {
continue;
}
set.add(name);
}
for (int i = 0; i < subqueries.length; i++) {
if (subqueries[i].queryExpression != null) {
subqueries[i].queryExpression.getBaseTableNames(set);
}
}
for (int i = 0; i < routines.length; i++) {
set.addAll(routines[i].getTableNamesForRead());
}
}
void collectTableNamesForWrite(OrderedHashSet set) {
// other fk references this : if constraint trigger action : other write lock
if (baseTable.isView()) {
getTriggerTableNames(set, true);
} else if (!baseTable.isTemp()) {
set.add(baseTable.getName());
if (type == StatementTypes.UPDATE_WHERE
|| type == StatementTypes.MERGE) {
baseTable.collectFKWriteLocks(updateColumnMap, set);
} else if (type == StatementTypes.DELETE_WHERE) {
baseTable.collectFKWriteLocks(null, set);
}
getTriggerTableNames(set, true);
}
}
void getTriggerTableNames(OrderedHashSet set, boolean write) {
for (int i = 0; i < baseTable.triggerList.length; i++) {
TriggerDef td = baseTable.triggerList[i];
switch (type) {
case StatementTypes.INSERT :
if (td.getStatementType() == StatementTypes.INSERT) {
break;
}
continue;
case StatementTypes.UPDATE_WHERE :
if (td.getStatementType() == StatementTypes.UPDATE_WHERE) {
break;
}
continue;
case StatementTypes.DELETE_WHERE :
if (td.getStatementType() == StatementTypes.DELETE_WHERE) {
break;
}
continue;
case StatementTypes.MERGE :
if (td.getStatementType() == StatementTypes.INSERT
|| td.getStatementType()
== StatementTypes.UPDATE_WHERE) {
break;
}
continue;
default :
throw Error.runtimeError(ErrorCode.U_S0500,
"StatementDML");
}
if (td.routine != null) {
if (write) {
set.addAll(td.routine.getTableNamesForWrite());
} else {
set.addAll(td.routine.getTableNamesForRead());
}
}
}
}
/**
* Executes an UPDATE statement.
*
* @return Result object
*/
Result executeUpdateStatement(Session session) {
int count = 0;
Expression[] colExpressions = updateExpressions;
RowSetNavigatorDataChange rowset = new RowSetNavigatorDataChange();
Type[] colTypes = baseTable.getColumnTypes();
RangeIterator it = RangeVariable.getIterator(session,
targetRangeVariables);
while (it.next()) {
session.sessionData.startRowProcessing();
Row row = it.getCurrentRow();
Object[] data = row.getData();
Object[] newData = getUpdatedData(session, targets, baseTable,
updateColumnMap, colExpressions,
colTypes, data);
if (updatableTableCheck != null) {
it.setCurrent(newData);
boolean check = updatableTableCheck.testCondition(session);
if (!check) {
it.release();
throw Error.error(ErrorCode.X_44000);
}
}
rowset.addRow(session, row, newData, colTypes, updateColumnMap);
}
it.release();
/* debug 190
if (rowset.size() == 0) {
System.out.println(targetTable.getName().name + " zero update: session "
+ session.getId());
} else if (rowset.size() >1) {
System.out.println("multiple update: session "
+ session.getId() + ", " + rowset.size());
}
//* debug 190 */
rowset.beforeFirst();
count = update(session, baseTable, rowset);
if (count == 1) {
return Result.updateOneResult;
} else if (count == 0) {
return Result.updateZeroResult;
}
return new Result(ResultConstants.UPDATECOUNT, count);
}
static Object[] getUpdatedData(Session session, Expression[] targets,
Table targetTable, int[] columnMap,
Expression[] colExpressions,
Type[] colTypes, Object[] oldData) {
Object[] data = targetTable.getEmptyRowData();
System.arraycopy(oldData, 0, data, 0, data.length);
for (int i = 0, ix = 0; i < columnMap.length; ) {
Expression expr = colExpressions[ix++];
if (expr.getType() == OpTypes.ROW) {
Object[] values = expr.getRowValue(session);
for (int j = 0; j < values.length; j++, i++) {
int colIndex = columnMap[i];
Expression e = expr.nodes[j];
// transitional - still supporting null for identity generation
if (targetTable.identityColumn == colIndex) {
if (e.getType() == OpTypes.VALUE
&& e.valueData == null) {
continue;
}
}
if (e.getType() == OpTypes.DEFAULT) {
if (targetTable.identityColumn == colIndex) {
continue;
}
data[colIndex] =
targetTable.colDefaults[colIndex].getValue(
session);
continue;
}
data[colIndex] = colTypes[colIndex].convertToType(session,
values[j], e.dataType);
}
} else if (expr.getType() == OpTypes.ROW_SUBQUERY) {
Object[] values = expr.getRowValue(session);
for (int j = 0; j < values.length; j++, i++) {
int colIndex = columnMap[i];
Type colType =
expr.subQuery.queryExpression.getMetaData()
.columnTypes[j];
data[colIndex] = colTypes[colIndex].convertToType(session,
values[j], colType);
}
} else {
int colIndex = columnMap[i];
if (expr.getType() == OpTypes.DEFAULT) {
if (targetTable.identityColumn == colIndex) {
i++;
continue;
}
data[colIndex] =
targetTable.colDefaults[colIndex].getValue(session);
i++;
continue;
}
Object value = expr.getValue(session);
if (targets[i].getType() == OpTypes.ARRAY_ACCESS) {
data[colIndex] =
((ExpressionAccessor) targets[i]).getUpdatedArray(
session, (Object[]) data[colIndex], value, true);
} else {
data[colIndex] = colTypes[colIndex].convertToType(session,
value, expr.dataType);
}
i++;
}
}
return data;
}
/**
* Executes a MERGE statement.
*
* @return Result object
*/
Result executeMergeStatement(Session session) {
Type[] colTypes = baseTable.getColumnTypes();
Result resultOut = null;
RowSetNavigator generatedNavigator = null;
if (generatedIndexes != null) {
resultOut = Result.newUpdateCountResult(generatedResultMetaData,
0);
generatedNavigator = resultOut.getChainedResult().getNavigator();
}
int count = 0;
// data generated for non-matching rows
RowSetNavigatorClient newData = new RowSetNavigatorClient(8);
// rowset for update operation
RowSetNavigatorDataChange updateRowSet =
new RowSetNavigatorDataChange();
RangeVariable[] joinRangeIterators = targetRangeVariables;
// populate insert and update lists
RangeIterator[] rangeIterators =
new RangeIterator[joinRangeIterators.length];
for (int i = 0; i < joinRangeIterators.length; i++) {
rangeIterators[i] = joinRangeIterators[i].getIterator(session);
}
for (int currentIndex = 0; currentIndex >= 0; ) {
RangeIterator it = rangeIterators[currentIndex];
boolean beforeFirst = it.isBeforeFirst();
if (it.next()) {
if (currentIndex < joinRangeIterators.length - 1) {
currentIndex++;
continue;
}
} else {
if (currentIndex == 1 && beforeFirst
&& insertExpression != null) {
Object[] data =
getInsertData(session, colTypes,
insertExpression.nodes[0].nodes);
if (data != null) {
newData.add(data);
}
}
it.reset();
currentIndex--;
continue;
}
// row matches!
if (updateExpressions.length != 0) {
Row row = it.getCurrentRow(); // this is always the second iterator
Object[] data = getUpdatedData(session, targets, baseTable,
updateColumnMap,
updateExpressions, colTypes,
row.getData());
try {
updateRowSet.addRow(session, row, data, colTypes,
updateColumnMap);
} catch (HsqlException e) {
for (int i = 0; i < joinRangeIterators.length; i++) {
rangeIterators[i].reset();
}
throw Error.error(ErrorCode.X_21000);
}
}
}
for (int i = 0; i < joinRangeIterators.length; i++) {
rangeIterators[i].reset();
}
// run the transaction as a whole, updating and inserting where needed
// update any matched rows
if (updateExpressions.length != 0) {
count = update(session, baseTable, updateRowSet);
}
// insert any non-matched rows
if (newData.getSize() > 0) {
insertRowSet(session, generatedNavigator, newData);
count += newData.getSize();
}
if (insertExpression != null
&& baseTable.triggerLists[Trigger.INSERT_AFTER].length > 0) {
baseTable.fireTriggers(session, Trigger.INSERT_AFTER, newData);
}
if (resultOut == null) {
if (count == 1) {
return Result.updateOneResult;
}
return new Result(ResultConstants.UPDATECOUNT, count);
} else {
resultOut.setUpdateCount(count);
return resultOut;
}
}
void insertRowSet(Session session, RowSetNavigator generatedNavigator,
RowSetNavigator newData) {
PersistentStore store = baseTable.getRowStore(session);
RangeIterator checkIterator = null;
if (updatableTableCheck != null) {
checkIterator = checkRangeVariable.getIterator(session);
}
newData.beforeFirst();
if (baseTable.triggerLists[Trigger.INSERT_BEFORE_ROW].length > 0) {
while (newData.hasNext()) {
Object[] data = (Object[]) newData.getNext();
baseTable.fireTriggers(session, Trigger.INSERT_BEFORE_ROW,
null, data, null);
}
newData.beforeFirst();
}
while (newData.hasNext()) {
Object[] data = (Object[]) newData.getNext();
baseTable.insertSingleRow(session, store, data, null);
if (checkIterator != null) {
checkIterator.setCurrent(data);
boolean check = updatableTableCheck.testCondition(session);
if (!check) {
throw Error.error(ErrorCode.X_44000);
}
}
if (generatedNavigator != null) {
Object[] generatedValues = getGeneratedColumns(data);
generatedNavigator.add(generatedValues);
}
}
newData.beforeFirst();
while (newData.hasNext()) {
Object[] data = (Object[]) newData.getNext();
performIntegrityChecks(session, baseTable, null, data, null);
}
newData.beforeFirst();
if (baseTable.triggerLists[Trigger.INSERT_AFTER_ROW].length > 0) {
while (newData.hasNext()) {
Object[] data = (Object[]) newData.getNext();
baseTable.fireTriggers(session, Trigger.INSERT_AFTER_ROW,
null, data, null);
}
newData.beforeFirst();
}
}
Result insertSingleRow(Session session, PersistentStore store,
Object[] data) {
if (baseTable.triggerLists[Trigger.INSERT_BEFORE_ROW].length > 0) {
baseTable.fireTriggers(session, Trigger.INSERT_BEFORE_ROW, null,
data, null);
}
baseTable.insertSingleRow(session, store, data, null);
performIntegrityChecks(session, baseTable, null, data, null);
if (session.database.isReferentialIntegrity()) {
for (int i = 0, size = baseTable.fkConstraints.length; i < size;
i++) {
baseTable.fkConstraints[i].checkInsert(session, baseTable,
data, true);
}
}
if (baseTable.triggerLists[Trigger.INSERT_AFTER_ROW].length > 0) {
baseTable.fireTriggers(session, Trigger.INSERT_AFTER_ROW, null,
data, null);
}
if (baseTable.triggerLists[Trigger.INSERT_AFTER].length > 0) {
baseTable.fireTriggers(session, Trigger.INSERT_AFTER,
(RowSetNavigator) null);
}
return Result.updateOneResult;
}
Object[] getInsertData(Session session, Type[] colTypes,
Expression[] rowArgs) {
Object[] data = baseTable.getNewRowData(session);
session.sessionData.startRowProcessing();
for (int i = 0; i < rowArgs.length; i++) {
Expression e = rowArgs[i];
int colIndex = insertColumnMap[i];
if (e.opType == OpTypes.DEFAULT) {
if (baseTable.identityColumn == colIndex) {
continue;
}
if (baseTable.colDefaults[colIndex] != null) {
data[colIndex] =
baseTable.colDefaults[colIndex].getValue(session);
continue;
}
continue;
}
Object value = e.getValue(session);
Type type = colTypes[colIndex];
if (colTypes[colIndex] != e.dataType) {
value = type.convertToType(session, value, e.dataType);
}
data[colIndex] = value;
}
return data;
}
/**
* Highest level multiple row update method.<p>
*
* Following clauses from SQL Standard section 11.8 are enforced 9) Let ISS
* be the innermost SQL-statement being executed. 10) If evaluation of these
* General Rules during the execution of ISS would cause an update of some
* site to a value that is distinct from the value to which that site was
* previously updated during the execution of ISS, then an exception
* condition is raised: triggered data change violation. 11) If evaluation
* of these General Rules during the execution of ISS would cause deletion
* of a row containing a site that is identified for replacement in that
* row, then an exception condition is raised: triggered data change
* violation.
*
* @param session Session
* @param table Table
* @param updateList RowSetNavigatorDataChange
* @return int
*/
int update(Session session, Table table,
RowSetNavigatorDataChange navigator) {
int rowCount = navigator.getSize();
// set identity column where null and check columns
for (int i = 0; i < rowCount; i++) {
navigator.next();
Object[] data = navigator.getCurrentChangedData();
/**
* @todo 1.9.0 - make optional using database property -
* this means the identity column can be set to null to force
* creation of a new identity value
*/
table.setIdentityColumn(session, data);
table.setGeneratedColumns(session, data);
}
navigator.beforeFirst();
if (table.fkMainConstraints.length > 0) {
HashSet path = session.sessionContext.getConstraintPath();
for (int i = 0; i < rowCount; i++) {
Row row = navigator.getNextRow();
Object[] data = navigator.getCurrentChangedData();
performReferentialActions(session, table, navigator, row,
data, this.updateColumnMap, path);
path.clear();
}
navigator.beforeFirst();
}
for (int i = 0; i < navigator.getSize(); i++) {
Row row = navigator.getNextRow();
Object[] data = navigator.getCurrentChangedData();
int[] changedColumns = navigator.getCurrentChangedColumns();
Table currentTable = ((Table) row.getTable());
if (currentTable.triggerLists[Trigger.UPDATE_BEFORE_ROW].length
> 0) {
currentTable.fireTriggers(session, Trigger.UPDATE_BEFORE_ROW,
row.getData(), data, changedColumns);
currentTable.enforceRowConstraints(session, data);
}
}
if (table.isView) {
return rowCount;
}
navigator.beforeFirst();
for (int i = 0; i < navigator.getSize(); i++) {
Row row = navigator.getNextRow();
Table currentTable = ((Table) row.getTable());
int[] changedColumns = navigator.getCurrentChangedColumns();
session.addDeleteAction(currentTable, row, changedColumns);
}
navigator.beforeFirst();
for (int i = 0; i < navigator.getSize(); i++) {
Row row = navigator.getNextRow();
Object[] data = navigator.getCurrentChangedData();
Table currentTable = ((Table) row.getTable());
int[] changedColumns = navigator.getCurrentChangedColumns();
PersistentStore store = currentTable.getRowStore(session);
if (data == null) {
continue;
}
Row newRow = currentTable.insertSingleRow(session, store, data,
changedColumns);
// newRow.rowAction.updatedAction = row.rowAction;
}
navigator.beforeFirst();
OrderedHashSet extraUpdateTables = null;
boolean hasAfterRowTriggers =
table.triggerLists[Trigger.UPDATE_AFTER_ROW].length > 0;
for (int i = 0; i < navigator.getSize(); i++) {
Row row = navigator.getNextRow();
Table currentTable = ((Table) row.getTable());
Object[] changedData = navigator.getCurrentChangedData();
int[] changedColumns = navigator.getCurrentChangedColumns();
performIntegrityChecks(session, currentTable, row.getData(),
changedData, changedColumns);
if (currentTable != table) {
if (extraUpdateTables == null) {
extraUpdateTables = new OrderedHashSet();
}
extraUpdateTables.add(currentTable);
if (currentTable.triggerLists[Trigger.UPDATE_AFTER_ROW].length
> 0) {
hasAfterRowTriggers = true;
}
}
}
navigator.beforeFirst();
if (hasAfterRowTriggers) {
for (int i = 0; i < navigator.getSize(); i++) {
Row row = navigator.getNextRow();
Object[] changedData = navigator.getCurrentChangedData();
int[] changedColumns = navigator.getCurrentChangedColumns();
Table currentTable = ((Table) row.getTable());
currentTable.fireTriggers(session, Trigger.UPDATE_AFTER_ROW,
row.getData(), changedData,
changedColumns);
}
navigator.beforeFirst();
}
baseTable.fireTriggers(session, Trigger.UPDATE_AFTER, navigator);
if (extraUpdateTables != null) {
for (int i = 0; i < extraUpdateTables.size(); i++) {
Table currentTable = (Table) extraUpdateTables.get(i);
currentTable.fireTriggers(session, Trigger.UPDATE_AFTER,
navigator);
}
}
return rowCount;
}
/**
* Executes a DELETE statement.
*
* @return the result of executing the statement
*/
Result executeDeleteStatement(Session session) {
int count = 0;
RangeIterator it = RangeVariable.getIterator(session,
targetRangeVariables);
RowSetNavigatorDataChange navigator = new RowSetNavigatorDataChange();
while (it.next()) {
Row currentRow = it.getCurrentRow();
navigator.addRow(currentRow);
}
it.release();
if (navigator.getSize() > 0) {
count = delete(session, baseTable, navigator);
} else {
return Result.updateZeroResult;
}
if (count == 1) {
return Result.updateOneResult;
}
return new Result(ResultConstants.UPDATECOUNT, count);
}
Result executeDeleteTruncateStatement(Session session) {
PersistentStore store = targetTable.getRowStore(session);
RowIterator it = targetTable.getPrimaryIndex().firstRow(store);
try {
while (it.hasNext()) {
Row row = it.getNextRow();
session.addDeleteAction((Table) row.getTable(), row, null);
}
if (restartIdentity && targetTable.identitySequence != null) {
targetTable.identitySequence.reset();
}
} finally {
it.release();
}
return Result.updateOneResult;
}
/**
* Highest level multiple row delete method. Corresponds to an SQL
* DELETE.
*/
int delete(Session session, Table table,
RowSetNavigatorDataChange navigator) {
int rowCount = navigator.getSize();
navigator.beforeFirst();
if (table.fkMainConstraints.length > 0) {
HashSet path = session.sessionContext.getConstraintPath();
for (int i = 0; i < rowCount; i++) {
navigator.next();
Row row = navigator.getCurrentRow();
performReferentialActions(session, table, navigator, row,
null, null, path);
path.clear();
}
navigator.beforeFirst();
}
while (navigator.hasNext()) {
navigator.next();
Row row = navigator.getCurrentRow();
Object[] changedData = navigator.getCurrentChangedData();
int[] changedColumns = navigator.getCurrentChangedColumns();
Table currentTable = ((Table) row.getTable());
if (changedData == null) {
currentTable.fireTriggers(session, Trigger.DELETE_BEFORE_ROW,
row.getData(), null, null);
} else {
currentTable.fireTriggers(session, Trigger.UPDATE_BEFORE_ROW,
row.getData(), changedData,
changedColumns);
}
}
if (table.isView) {
return rowCount;
}
navigator.beforeFirst();
boolean hasUpdate = false;
for (int i = 0; i < navigator.getSize(); i++) {
Row row = navigator.getNextRow();
Object[] data = navigator.getCurrentChangedData();
Table currentTable = ((Table) row.getTable());
session.addDeleteAction(currentTable, row, null);
if (data != null) {
hasUpdate = true;
}
}
navigator.beforeFirst();
if (hasUpdate) {
for (int i = 0; i < navigator.getSize(); i++) {
Row row = navigator.getNextRow();
Object[] data = navigator.getCurrentChangedData();
Table currentTable = ((Table) row.getTable());
int[] changedColumns = navigator.getCurrentChangedColumns();
PersistentStore store = currentTable.getRowStore(session);
if (data == null) {
continue;
}
Row newRow = currentTable.insertSingleRow(session, store,
data, changedColumns);
// newRow.rowAction.updatedAction = row.rowAction;
}
navigator.beforeFirst();
}
OrderedHashSet extraUpdateTables = null;
OrderedHashSet extraDeleteTables = null;
boolean hasAfterRowTriggers =
table.triggerLists[Trigger.DELETE_AFTER_ROW].length > 0;
if (rowCount != navigator.getSize()) {
while (navigator.hasNext()) {
navigator.next();
Row row = navigator.getCurrentRow();
Object[] changedData = navigator.getCurrentChangedData();
int[] changedColumns = navigator.getCurrentChangedColumns();
Table currentTable = ((Table) row.getTable());
if (changedData != null) {
performIntegrityChecks(session, currentTable,
row.getData(), changedData,
changedColumns);
}
if (currentTable != table) {
if (changedData == null) {
if (currentTable.triggerLists[Trigger.DELETE_AFTER_ROW]
.length > 0) {
hasAfterRowTriggers = true;
}
if (extraDeleteTables == null) {
extraDeleteTables = new OrderedHashSet();
}
extraDeleteTables.add(currentTable);
} else {
if (currentTable.triggerLists[Trigger.UPDATE_AFTER_ROW]
.length > 0) {
hasAfterRowTriggers = true;
}
if (extraUpdateTables == null) {
extraUpdateTables = new OrderedHashSet();
}
extraUpdateTables.add(currentTable);
}
}
}
navigator.beforeFirst();
}
if (hasAfterRowTriggers) {
while (navigator.hasNext()) {
navigator.next();
Row row = navigator.getCurrentRow();
Object[] changedData = navigator.getCurrentChangedData();
Table currentTable = ((Table) row.getTable());
if (changedData == null) {
currentTable.fireTriggers(session,
Trigger.DELETE_AFTER_ROW,
row.getData(), null, null);
} else {
currentTable.fireTriggers(session,
Trigger.UPDATE_AFTER_ROW,
row.getData(), changedData,
null);
}
}
navigator.beforeFirst();
}
table.fireTriggers(session, Trigger.DELETE_AFTER, navigator);
if (extraUpdateTables != null) {
for (int i = 0; i < extraUpdateTables.size(); i++) {
Table currentTable = (Table) extraUpdateTables.get(i);
currentTable.fireTriggers(session, Trigger.UPDATE_AFTER,
navigator);
}
}
if (extraDeleteTables != null) {
for (int i = 0; i < extraDeleteTables.size(); i++) {
Table currentTable = (Table) extraDeleteTables.get(i);
currentTable.fireTriggers(session, Trigger.DELETE_AFTER,
navigator);
}
}
return rowCount;
}
static void performIntegrityChecks(Session session, Table table,
Object[] oldData, Object[] newData,
int[] updatedColumns) {
if (newData == null) {
return;
}
for (int i = 0, size = table.checkConstraints.length; i < size; i++) {
table.checkConstraints[i].checkInsert(session, table, newData,
oldData == null);
}
if (!session.database.isReferentialIntegrity()) {
return;
}
for (int i = 0, size = table.fkConstraints.length; i < size; i++) {
boolean check = oldData == null;
Constraint c = table.fkConstraints[i];
if (!check) {
check = ArrayUtil.haveCommonElement(c.getRefColumns(),
updatedColumns);
}
if (check) {
c.checkInsert(session, table, newData, oldData == null);
}
}
}
static void performReferentialActions(Session session, Table table,
RowSetNavigatorDataChange navigator,
Row row, Object[] data,
int[] changedCols, HashSet path) {
if (!session.database.isReferentialIntegrity()) {
return;
}
boolean delete = data == null;
for (int i = 0, size = table.fkMainConstraints.length; i < size; i++) {
Constraint c = table.fkMainConstraints[i];
int action = delete ? c.core.deleteAction
: c.core.updateAction;
if (!delete) {
if (!ArrayUtil.haveCommonElement(changedCols,
c.core.mainCols)) {
continue;
}
if (c.core.mainIndex.compareRowNonUnique(
session, row.getData(), data, c.core.mainCols) == 0) {
continue;
}
}
RowIterator refiterator = c.findFkRef(session, row.getData());
if (!refiterator.hasNext()) {
refiterator.release();
continue;
}
while (refiterator.hasNext()) {
Row refRow = refiterator.getNextRow();
Object[] refData = null;
/** @todo use MATCH */
if (c.core.refIndex.compareRowNonUnique(
session, refRow.getData(), row.getData(),
c.core.mainCols) != 0) {
break;
}
if (delete && refRow.getId() == row.getId()) {
continue;
}
switch (action) {
case SchemaObject.ReferentialAction.CASCADE : {
if (delete) {
if (navigator.addRow(refRow)) {
performReferentialActions(session,
c.core.refTable,
navigator, refRow,
null, null, path);
}
continue;
}
refData = c.core.refTable.getEmptyRowData();
System.arraycopy(refRow.getData(), 0, refData, 0,
refData.length);
for (int j = 0; j < c.core.refCols.length; j++) {
refData[c.core.refCols[j]] =
data[c.core.mainCols[j]];
}
break;
}
case SchemaObject.ReferentialAction.SET_NULL : {
refData = c.core.refTable.getEmptyRowData();
System.arraycopy(refRow.getData(), 0, refData, 0,
refData.length);
for (int j = 0; j < c.core.refCols.length; j++) {
refData[c.core.refCols[j]] = null;
}
break;
}
case SchemaObject.ReferentialAction.SET_DEFAULT : {
refData = c.core.refTable.getEmptyRowData();
System.arraycopy(refRow.getData(), 0, refData, 0,
refData.length);
for (int j = 0; j < c.core.refCols.length; j++) {
ColumnSchema col =
c.core.refTable.getColumn(c.core.refCols[j]);
refData[c.core.refCols[j]] =
col.getDefaultValue(session);
}
break;
}
case SchemaObject.ReferentialAction.NO_ACTION :
case SchemaObject.ReferentialAction.RESTRICT : {
if (navigator.containsDeletedRow(refRow)) {
continue;
}
int errorCode = c.core.deleteAction
== SchemaObject.ReferentialAction
.NO_ACTION ? ErrorCode.X_23504
: ErrorCode.X_23001;
String[] info = new String[] {
c.core.refName.name, c.core.refTable.getName().name
};
refiterator.release();
throw Error.error(null, errorCode,
ErrorCode.CONSTRAINT, info);
}
default :
continue;
}
refData = navigator.addRow(session, refRow, refData,
table.getColumnTypes(),
c.core.refCols);
if (!path.add(c)) {
continue;
}
performReferentialActions(session, c.core.refTable, navigator,
refRow, refData, c.core.refCols,
path);
path.remove(c);
}
refiterator.release();
}
}
}