| /* 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.ParserDQL.CompileContext; |
| import org.hsqldb.error.Error; |
| import org.hsqldb.error.ErrorCode; |
| import org.hsqldb.navigator.RangeIterator; |
| import org.hsqldb.navigator.RowSetNavigator; |
| import org.hsqldb.navigator.RowSetNavigatorClient; |
| import org.hsqldb.persist.PersistentStore; |
| import org.hsqldb.result.Result; |
| import org.hsqldb.result.ResultConstants; |
| import org.hsqldb.result.ResultMetaData; |
| import org.hsqldb.types.Type; |
| |
| /** |
| * Implementation of Statement for INSERT statements.<p> |
| * |
| * @author Fred Toussi (fredt@users dot sourceforge.net) |
| * @version 1.9.0 |
| * @since 1.9.0 |
| */ |
| public class StatementInsert extends StatementDML { |
| |
| int generatedType; |
| ResultMetaData generatedInputMetaData; |
| boolean isSimpleInsert; |
| int overrideUserValue = -1; |
| |
| /** |
| * Instantiate this as an INSERT_VALUES statement. |
| */ |
| StatementInsert(Session session, Table targetTable, int[] columnMap, |
| Expression insertExpression, boolean[] checkColumns, |
| CompileContext compileContext) { |
| |
| super(StatementTypes.INSERT, StatementTypes.X_SQL_DATA_CHANGE, |
| session.getCurrentSchemaHsqlName()); |
| |
| this.targetTable = targetTable; |
| this.baseTable = targetTable.isTriggerInsertable() ? targetTable |
| : targetTable |
| .getBaseTable(); |
| this.insertColumnMap = columnMap; |
| this.insertCheckColumns = checkColumns; |
| this.insertExpression = insertExpression; |
| |
| setDatabseObjects(session, compileContext); |
| checkAccessRights(session); |
| setupChecks(); |
| |
| isSimpleInsert = insertExpression != null |
| && insertExpression.nodes.length == 1 |
| && updatableTableCheck == null; |
| } |
| |
| /** |
| * Instantiate this as an INSERT_SELECT statement. |
| */ |
| StatementInsert(Session session, Table targetTable, int[] columnMap, |
| boolean[] checkColumns, QueryExpression queryExpression, |
| CompileContext compileContext, int override) { |
| |
| super(StatementTypes.INSERT, StatementTypes.X_SQL_DATA_CHANGE, |
| session.getCurrentSchemaHsqlName()); |
| |
| this.targetTable = targetTable; |
| this.baseTable = targetTable.isTriggerInsertable() ? targetTable |
| : targetTable |
| .getBaseTable(); |
| this.insertColumnMap = columnMap; |
| this.insertCheckColumns = checkColumns; |
| this.queryExpression = queryExpression; |
| this.overrideUserValue = override; |
| |
| setDatabseObjects(session, compileContext); |
| checkAccessRights(session); |
| setupChecks(); |
| } |
| |
| /** |
| * Executes an INSERT_SELECT or INSERT_VALUESstatement. It is assumed that |
| * the argument is of the correct type. |
| * |
| * @return the result of executing the statement |
| */ |
| Result getResult(Session session) { |
| |
| Result resultOut = null; |
| RowSetNavigator generatedNavigator = null; |
| PersistentStore store = baseTable.getRowStore(session); |
| |
| if (generatedIndexes != null) { |
| resultOut = Result.newUpdateCountResult(generatedResultMetaData, |
| 0); |
| generatedNavigator = resultOut.getChainedResult().getNavigator(); |
| } |
| |
| if (isSimpleInsert) { |
| Type[] colTypes = baseTable.getColumnTypes(); |
| Object[] data = getInsertData(session, colTypes, |
| insertExpression.nodes[0].nodes); |
| |
| return insertSingleRow(session, store, data); |
| } |
| |
| RowSetNavigator newDataNavigator = queryExpression == null |
| ? getInsertValuesNavigator(session) |
| : getInsertSelectNavigator(session); |
| |
| if (newDataNavigator.getSize() > 0) { |
| insertRowSet(session, generatedNavigator, newDataNavigator); |
| } |
| |
| if (baseTable.triggerLists[Trigger.INSERT_AFTER].length > 0) { |
| baseTable.fireTriggers(session, Trigger.INSERT_AFTER, |
| newDataNavigator); |
| } |
| |
| if (resultOut == null) { |
| resultOut = new Result(ResultConstants.UPDATECOUNT, |
| newDataNavigator.getSize()); |
| } else { |
| resultOut.setUpdateCount(newDataNavigator.getSize()); |
| } |
| |
| return resultOut; |
| } |
| |
| RowSetNavigator getInsertSelectNavigator(Session session) { |
| |
| Type[] colTypes = baseTable.getColumnTypes(); |
| int[] columnMap = insertColumnMap; |
| |
| // |
| Result result = queryExpression.getResult(session, 0); |
| RowSetNavigator nav = result.initialiseNavigator(); |
| Type[] sourceTypes = result.metaData.columnTypes; |
| RowSetNavigatorClient newData = new RowSetNavigatorClient(2); |
| |
| while (nav.hasNext()) { |
| Object[] data = baseTable.getNewRowData(session); |
| Object[] sourceData = (Object[]) nav.getNext(); |
| |
| for (int i = 0; i < columnMap.length; i++) { |
| int j = columnMap[i]; |
| |
| if (j == this.overrideUserValue) { |
| continue; |
| } |
| |
| Type sourceType = sourceTypes[i]; |
| |
| data[j] = colTypes[j].convertToType(session, sourceData[i], |
| sourceType); |
| } |
| |
| newData.add(data); |
| } |
| |
| return newData; |
| } |
| |
| RowSetNavigator getInsertValuesNavigator(Session session) { |
| |
| Type[] colTypes = baseTable.getColumnTypes(); |
| |
| // |
| Expression[] list = insertExpression.nodes; |
| RowSetNavigatorClient newData = new RowSetNavigatorClient(list.length); |
| |
| for (int j = 0; j < list.length; j++) { |
| Expression[] rowArgs = list[j].nodes; |
| Object[] data = getInsertData(session, colTypes, rowArgs); |
| |
| newData.add(data); |
| } |
| |
| return newData; |
| } |
| |
| /** |
| * @todo - fredt - this does not work with different prepare calls |
| * with the same SQL statement, but different generated column requests |
| * To fix, add comment encapsulating the generated column list to SQL |
| * to differentiate between the two invocations |
| */ |
| public void setGeneratedColumnInfo(int generate, ResultMetaData meta) { |
| |
| // can support INSERT_SELECT also |
| if (type != StatementTypes.INSERT) { |
| return; |
| } |
| |
| int colIndex = baseTable.getIdentityColumnIndex(); |
| |
| if (colIndex == -1) { |
| return; |
| } |
| |
| generatedType = generate; |
| generatedInputMetaData = meta; |
| |
| switch (generate) { |
| |
| case ResultConstants.RETURN_NO_GENERATED_KEYS : |
| return; |
| |
| case ResultConstants.RETURN_GENERATED_KEYS_COL_INDEXES : |
| int[] columnIndexes = meta.getGeneratedColumnIndexes(); |
| |
| if (columnIndexes.length != 1) { |
| return; |
| } |
| |
| if (columnIndexes[0] != colIndex) { |
| return; |
| } |
| |
| // fall through |
| case ResultConstants.RETURN_GENERATED_KEYS : |
| generatedIndexes = new int[]{ colIndex }; |
| break; |
| |
| case ResultConstants.RETURN_GENERATED_KEYS_COL_NAMES : |
| String[] columnNames = meta.getGeneratedColumnNames(); |
| |
| if (columnNames.length != 1) { |
| return; |
| } |
| |
| if (baseTable.findColumn(columnNames[0]) != colIndex) { |
| return; |
| } |
| |
| generatedIndexes = new int[]{ colIndex }; |
| break; |
| } |
| |
| generatedResultMetaData = |
| ResultMetaData.newResultMetaData(generatedIndexes.length); |
| |
| for (int i = 0; i < generatedIndexes.length; i++) { |
| ColumnSchema column = baseTable.getColumn(generatedIndexes[i]); |
| |
| generatedResultMetaData.columns[i] = column; |
| } |
| |
| generatedResultMetaData.prepareData(); |
| |
| isSimpleInsert = false; |
| } |
| } |