--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.InsertNode\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.compile;\r
+\r
+import org.apache.derby.iapi.services.context.ContextManager;\r
+\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+\r
+import org.apache.derby.iapi.services.compiler.JavaFactory;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+\r
+import org.apache.derby.iapi.sql.conn.Authorizer;\r
+\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+import org.apache.derby.iapi.sql.compile.Visitable;\r
+import org.apache.derby.iapi.sql.compile.Visitor;\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+\r
+import org.apache.derby.iapi.reference.ClassName;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.dictionary.TableDescriptor;\r
+import org.apache.derby.iapi.sql.dictionary.IndexLister;\r
+import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;\r
+import org.apache.derby.iapi.types.TypeId;\r
+\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.StatementType;\r
+\r
+import org.apache.derby.iapi.sql.execute.ConstantAction;\r
+\r
+import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+import org.apache.derby.iapi.types.RowLocation;\r
+\r
+import org.apache.derby.impl.sql.compile.ActivationClassBuilder;\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.vti.DeferModification;\r
+\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+import org.apache.derby.iapi.util.StringUtil;\r
+\r
+import org.apache.derby.catalog.UUID;\r
+\r
+import org.apache.derby.impl.sql.execute.FKInfo;\r
+\r
+import java.util.Properties;\r
+\r
+import org.apache.derby.iapi.services.io.FormatableBitSet;\r
+import org.apache.derby.iapi.util.ReuseFactory;\r
+\r
+/**\r
+ * An InsertNode is the top node in a query tree for an\r
+ * insert statement.\r
+ * <p>\r
+ * After parsing, the node contains\r
+ * targetTableName: the target table for the insert\r
+ * collist: a list of column names, if specified\r
+ * queryexpr: the expression being inserted, either\r
+ * a values clause or a select form; both\r
+ * of these are represented via the SelectNode,\r
+ * potentially with a TableOperatorNode such as\r
+ * UnionNode above it.\r
+ * <p>\r
+ * After binding, the node has had the target table's\r
+ * descriptor located and inserted, and the queryexpr\r
+ * and collist have been massaged so that they are identical\r
+ * to the table layout. This involves adding any default\r
+ * values for missing columns, and reordering the columns\r
+ * to match the table's ordering of them.\r
+ * <p>\r
+ * After optimizing, ...\r
+ */\r
+public final class InsertNode extends DMLModStatementNode\r
+{\r
+ public ResultColumnList targetColumnList;\r
+ public boolean deferred;\r
+ public ValueNode checkConstraints;\r
+ public Properties targetProperties;\r
+ public FKInfo fkInfo;\r
+ protected boolean bulkInsert;\r
+ private boolean bulkInsertReplace;\r
+ \r
+ protected RowLocation[] autoincRowLocation;\r
+ /**\r
+ * Initializer for an InsertNode.\r
+ *\r
+ * @param targetName The name of the table/VTI to insert into\r
+ * @param insertColumns A ResultColumnList with the names of the\r
+ * columns to insert into. May be null if the\r
+ * user did not specify the columns - in this\r
+ * case, the binding phase will have to figure\r
+ * it out.\r
+ * @param queryExpression The query expression that will generate\r
+ * the rows to insert into the given table\r
+ * @param targetProperties The properties specified on the target table\r
+ */\r
+\r
+ public void init(\r
+ Object targetName,\r
+ Object insertColumns,\r
+ Object queryExpression,\r
+ Object targetProperties)\r
+ {\r
+ /* statementType gets set in super() before we've validated\r
+ * any properties, so we've kludged the code to get the\r
+ * right statementType for a bulk insert replace.\r
+ */\r
+ super.init(\r
+ queryExpression,\r
+ ReuseFactory.getInteger(getStatementType(\r
+ (Properties) targetProperties))\r
+ );\r
+ setTarget((QueryTreeNode) targetName);\r
+ targetColumnList = (ResultColumnList) insertColumns;\r
+ this.targetProperties = (Properties) targetProperties;\r
+\r
+ /* Remember that the query expression is the source to an INSERT */\r
+ getResultSetNode().setInsertSource();\r
+ }\r
+\r
+ /**\r
+ * Convert this object to a String. See comments in QueryTreeNode.java\r
+ * for how this should be done for tree printing.\r
+ *\r
+ * @return This object as a String\r
+ */\r
+\r
+ public String toString()\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ try {\r
+ return ( (targetTableName!=null) ? targetTableName : targetVTI.getTableName() ).toString() + "\n"\r
+ + targetProperties + "\n"\r
+ + super.toString();\r
+ } catch (org.apache.derby.iapi.error.StandardException e) {\r
+ return "tableName: <not_known>\n"\r
+ + targetProperties + "\n"\r
+ + super.toString();\r
+ }\r
+ }\r
+ else\r
+ {\r
+ return "";\r
+ }\r
+ }\r
+\r
+ public String statementToString()\r
+ {\r
+ return "INSERT";\r
+ }\r
+\r
+ /**\r
+ * Prints the sub-nodes of this object. See QueryTreeNode.java for\r
+ * how tree printing is supposed to work.\r
+ *\r
+ * @param depth The depth of this node in the tree\r
+ */\r
+\r
+ public void printSubNodes(int depth)\r
+ {\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ super.printSubNodes(depth);\r
+\r
+ if (targetTableName != null)\r
+ {\r
+ printLabel(depth, "targetTableName: ");\r
+ targetTableName.treePrint(depth + 1);\r
+ }\r
+\r
+ if (targetColumnList != null)\r
+ {\r
+ printLabel(depth, "targetColumnList: ");\r
+ targetColumnList.treePrint(depth + 1);\r
+ }\r
+\r
+ /* RESOLVE - need to print out targetTableDescriptor */\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Bind this InsertNode. This means looking up tables and columns and\r
+ * getting their types, and figuring out the result types of all\r
+ * expressions, as well as doing view resolution, permissions checking,\r
+ * etc.\r
+ * <p>\r
+ * Binding an insert will also massage the tree so that\r
+ * the collist and select column order/number are the\r
+ * same as the layout of the table in the store. \r
+ *\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+\r
+ public void bindStatement() throws StandardException\r
+ {\r
+ // We just need select privilege on the expressions\r
+ getCompilerContext().pushCurrentPrivType( Authorizer.SELECT_PRIV);\r
+\r
+ FromList fromList = (FromList) getNodeFactory().getNode(\r
+ C_NodeTypes.FROM_LIST,\r
+ getNodeFactory().doJoinOrderOptimization(),\r
+ getContextManager());\r
+\r
+ /* If any underlying ResultSetNode is a SelectNode, then we\r
+ * need to do a full bind(), including the expressions\r
+ * (since the fromList may include a FromSubquery).\r
+ */\r
+ DataDictionary dataDictionary = getDataDictionary();\r
+ super.bindResultSetsWithTables(dataDictionary);\r
+\r
+ /*\r
+ ** Get the TableDescriptor for the table we are inserting into\r
+ */\r
+ verifyTargetTable();\r
+\r
+ // Check the validity of the targetProperties, if they exist\r
+ if (targetProperties != null)\r
+ {\r
+ verifyTargetProperties(dataDictionary);\r
+ }\r
+\r
+ /*\r
+ ** Get the resultColumnList representing the columns in the base\r
+ ** table or VTI.\r
+ */\r
+ getResultColumnList();\r
+\r
+ /* If we have a target column list, then it must have the same # of\r
+ * entries as the result set's RCL.\r
+ */\r
+ if (targetColumnList != null)\r
+ {\r
+ /*\r
+ * Normalize synonym qualifers for column references.\r
+ */\r
+ if (synonymTableName != null)\r
+ {\r
+ normalizeSynonymColumns ( targetColumnList, targetTableName );\r
+ }\r
+ \r
+ /* Bind the target column list */\r
+ getCompilerContext().pushCurrentPrivType( getPrivType());\r
+ if (targetTableDescriptor != null)\r
+ {\r
+ targetColumnList.bindResultColumnsByName(targetTableDescriptor,\r
+ (DMLStatementNode) this);\r
+ }\r
+ else\r
+ {\r
+ targetColumnList.bindResultColumnsByName(targetVTI.getResultColumns(), targetVTI,\r
+ this);\r
+ }\r
+ getCompilerContext().popCurrentPrivType();\r
+ }\r
+\r
+ /* Verify that all underlying ResultSets reclaimed their FromList */\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(fromList.size() == 0,\r
+ "fromList.size() is expected to be 0, not " + \r
+ fromList.size() +\r
+ " on return from RS.bindExpressions()");\r
+ }\r
+\r
+ /* Replace any DEFAULTs with the associated tree */\r
+ resultSet.replaceDefaults(targetTableDescriptor, targetColumnList);\r
+\r
+ /* Bind the expressions now that the result columns are bound \r
+ * NOTE: This will be the 2nd time for those underlying ResultSets\r
+ * that have tables (no harm done), but it is necessary for those\r
+ * that do not have tables. It's too hard/not work the effort to\r
+ * avoid the redundancy.\r
+ */\r
+ super.bindExpressions();\r
+\r
+ /*\r
+ ** If the result set is a union, it could be a table constructor.\r
+ ** Bind any nulls in the result columns of the table constructor\r
+ ** to the types of the table being inserted into.\r
+ **\r
+ ** The types of ? parameters in row constructors and table constructors\r
+ ** in an INSERT statement come from the result columns.\r
+ **\r
+ ** If there is a target column list, use that instead of the result\r
+ ** columns for the whole table, since the columns in the result set\r
+ ** correspond to the target column list.\r
+ */\r
+ if (targetColumnList != null)\r
+ {\r
+ if (resultSet.getResultColumns().visibleSize() > targetColumnList.size())\r
+ throw StandardException.newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED); \r
+ resultSet.bindUntypedNullsToResultColumns(targetColumnList);\r
+ resultSet.setTableConstructorTypes(targetColumnList);\r
+ }\r
+ else\r
+ {\r
+ if (resultSet.getResultColumns().visibleSize() > resultColumnList.size())\r
+ throw StandardException.newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED); \r
+ resultSet.bindUntypedNullsToResultColumns(resultColumnList);\r
+ resultSet.setTableConstructorTypes(resultColumnList);\r
+ }\r
+\r
+ /* Bind the columns of the result set to their expressions */\r
+ resultSet.bindResultColumns(fromList);\r
+\r
+ int resCols = resultSet.getResultColumns().visibleSize();\r
+ DataDictionary dd = getDataDictionary();\r
+ if (targetColumnList != null)\r
+ {\r
+ if (targetColumnList.size() != resCols)\r
+ throw StandardException.newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED); \r
+ }\r
+ else \r
+ {\r
+ if (targetTableDescriptor != null &&\r
+ targetTableDescriptor.getNumberOfColumns() != resCols)\r
+ throw StandardException.newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED); \r
+ }\r
+\r
+ /* See if the ResultSet's RCL needs to be ordered to match the target\r
+ * list, or "enhanced" to accommodate defaults. It can only need to\r
+ * be ordered if there is a target column list. It needs to be\r
+ * enhanced if there are fewer source columns than there are columns\r
+ * in the table.\r
+ */\r
+ boolean inOrder = true;\r
+ int numTableColumns = resultColumnList.size();\r
+\r
+ /* colMap[] will be the size of the target list, which could be larger\r
+ * than the current size of the source list. In that case, the source\r
+ * list will be "enhanced" to include defaults.\r
+ */\r
+ int[] colMap = new int[numTableColumns];\r
+\r
+ // set the fields to an unused value\r
+ for (int i = 0; i < colMap.length; i++) \r
+ {\r
+ colMap[i] = -1;\r
+ }\r
+\r
+ /* Create the source/target list mapping */\r
+ if (targetColumnList != null)\r
+ {\r
+ /*\r
+ ** There is a target column list, so the result columns might\r
+ ** need to be ordered. Step through the target column list\r
+ ** and remember the position in the target table of each column.\r
+ ** Remember if any of the columns are out of order.\r
+ */\r
+ int targetSize = targetColumnList.size();\r
+ for (int index = 0; index < targetSize; index++)\r
+ {\r
+ int position =\r
+ ((ResultColumn) (targetColumnList.elementAt(index))).\r
+ columnDescriptor.getPosition();\r
+\r
+ if (index != position-1)\r
+ {\r
+ inOrder = false;\r
+ }\r
+\r
+ // position is 1-base; colMap indexes and entries are 0-based.\r
+ colMap[position-1] = index;\r
+ }\r
+ }\r
+ else\r
+ {\r
+ /*\r
+ ** There is no target column list, so the result columns in the\r
+ ** source are presumed to be in the same order as the target\r
+ ** table.\r
+ */\r
+ for (int position = 0;\r
+ position < resultSet.getResultColumns().visibleSize();\r
+ position++)\r
+ {\r
+ colMap[position] = position;\r
+ }\r
+ }\r
+\r
+ enhanceAndCheckForAutoincrement(resultSet, inOrder,\r
+ numTableColumns, colMap, dataDictionary,\r
+ targetTableDescriptor, targetVTI);\r
+\r
+ resultColumnList.checkStorableExpressions(resultSet.getResultColumns());\r
+ /* Insert a NormalizeResultSetNode above the source if the source\r
+ * and target column types and lengths do not match.\r
+ */\r
+ if (! resultColumnList.columnTypesAndLengthsMatch(\r
+ resultSet.getResultColumns()))\r
+ {\r
+ resultSet = resultSet.genNormalizeResultSetNode(resultSet, false);\r
+ resultColumnList.copyTypesAndLengthsToSource(resultSet.getResultColumns());\r
+ }\r
+\r
+ if (targetTableDescriptor != null)\r
+ {\r
+ /* Get and bind all constraints on the table */\r
+ ResultColumnList sourceRCL = resultSet.getResultColumns();\r
+ sourceRCL.copyResultColumnNames(resultColumnList);\r
+ checkConstraints = bindConstraints(dataDictionary,\r
+ getNodeFactory(),\r
+ targetTableDescriptor,\r
+ null,\r
+ sourceRCL,\r
+ (int[]) null,\r
+ (FormatableBitSet) null,\r
+ false,\r
+ true); /* we always include\r
+ * triggers in core language */\r
+ \r
+ /* Do we need to do a deferred mode insert */\r
+ /* \r
+ ** Deferred if:\r
+ ** If the target table is also a source table\r
+ ** Self-referencing foreign key constraint \r
+ ** trigger\r
+ */\r
+ if (resultSet.referencesTarget(\r
+ targetTableDescriptor.getName(), true) ||\r
+ requiresDeferredProcessing())\r
+ {\r
+ deferred = true;\r
+\r
+ /* Disallow bulk insert replace when target table\r
+ * is also a source table.\r
+ */\r
+ if (bulkInsertReplace &&\r
+ resultSet.referencesTarget(\r
+ targetTableDescriptor.getName(), true))\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_INVALID_BULK_INSERT_REPLACE, \r
+ targetTableDescriptor.getQualifiedName());\r
+ }\r
+ }\r
+\r
+ /* Get the list of indexes on the table being inserted into */\r
+ getAffectedIndexes(targetTableDescriptor);\r
+ TransactionController tc = \r
+ getLanguageConnectionContext().getTransactionCompile();\r
+\r
+ autoincRowLocation = \r
+ dd.computeAutoincRowLocations(tc, targetTableDescriptor);\r
+\r
+ if (isPrivilegeCollectionRequired())\r
+ {\r
+ getCompilerContext().pushCurrentPrivType(getPrivType());\r
+ getCompilerContext().addRequiredTablePriv(targetTableDescriptor);\r
+ getCompilerContext().popCurrentPrivType(); \r
+ }\r
+\r
+ }\r
+ else\r
+ {\r
+ deferred = VTIDeferModPolicy.deferIt( DeferModification.INSERT_STATEMENT,\r
+ targetVTI,\r
+ null,\r
+ resultSet);\r
+ }\r
+ \r
+ getCompilerContext().popCurrentPrivType();\r
+ }\r
+\r
+ /**\r
+ * Process ResultSet column lists for projection and autoincrement.\r
+ *\r
+ * This method recursively descends the result set node tree. When\r
+ * it finds a simple result set, it processes any autoincrement\r
+ * columns in that rs by calling checkAutoIncrement. When it finds\r
+ * a compound result set, like a Union or a PRN, it recursively\r
+ * descends to the child(ren) nodes. Union nodes can arise due to\r
+ * multi-rows in VALUES clause), PRN nodes can arise when the set\r
+ * of columns being inserted is a subset of the set of columns in \r
+ * the table.\r
+ *\r
+ * In addition to checking for autoincrement columns in the result set,\r
+ * we may need to enhance and re-order the column list to match the\r
+ * column list of the table we are inserting into. This work is handled\r
+ * by ResultsetNode.enhanceRCLForInsert.\r
+ *\r
+ * Note that, at the leaf level, we need to enhance the RCL first, then\r
+ * check for autoincrement columns. At the non-leaf levels, we have\r
+ * to enhance the RCL, but we don't have to check for autoincrement\r
+ * columns, since they only occur at the leaf level.\r
+ *\r
+ * This way, all ColumnDescriptor of all rows will be set properly.\r
+ *\r
+ * @param resultSet current node in the result set tree\r
+ * @param inOrder FALSE if the column list needs reordering\r
+ * @param numTableColumns # of columns in target RCL\r
+ * @param colMap correspondence between RCLs\r
+ * @param dataDictionary DataDictionary to use\r
+ * @param targetTableDescriptor Table Descriptor for target\r
+ * @param targetVTI Target description if it is a VTI\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ private void enhanceAndCheckForAutoincrement(ResultSetNode resultSet, \r
+ boolean inOrder, int numTableColumns, int []colMap, \r
+ DataDictionary dataDictionary,\r
+ TableDescriptor targetTableDescriptor,\r
+ FromVTI targetVTI)\r
+ throws StandardException\r
+ {\r
+ /*\r
+ * Some implementation notes:\r
+ * \r
+ * colmap[x] == y means that column x in the target table\r
+ * maps to column y in the source result set.\r
+ * colmap[x] == -1 means that column x in the target table\r
+ * maps to its default value.\r
+ * both colmap indexes and values are 0-based.\r
+ *\r
+ * if the list is in order and complete, we don't have to change\r
+ * the tree. If it is not, then we call RSN.enhanceRCLForInsert() \r
+ * which will reorder ("enhance") the source RCL within the same RSN)\r
+ *\r
+ * one thing we do know is that all of the resultsets underneath\r
+ * us have their resultColumn names filled in with the names of\r
+ * the target table columns. That makes generating the mapping\r
+ * "easier" -- we simply generate the names of the target table columns\r
+ * that are included. For the missing columns, we generate default\r
+ * value expressions.\r
+ */\r
+\r
+ if (resultSet instanceof SingleChildResultSetNode)\r
+ {\r
+ enhanceAndCheckForAutoincrement(\r
+ ((SingleChildResultSetNode)resultSet).getChildResult(),\r
+ inOrder, numTableColumns, colMap, dataDictionary,\r
+ targetTableDescriptor, targetVTI);\r
+ if (! inOrder || resultSet.resultColumns.size() < numTableColumns)\r
+ resultSet.enhanceRCLForInsert(\r
+ numTableColumns, colMap, dataDictionary,\r
+ targetTableDescriptor, targetVTI);\r
+ }\r
+ else if (resultSet instanceof UnionNode)\r
+ {\r
+ enhanceAndCheckForAutoincrement(\r
+ ((TableOperatorNode)resultSet).getLeftResultSet(),\r
+ inOrder, numTableColumns, colMap, dataDictionary,\r
+ targetTableDescriptor, targetVTI);\r
+ enhanceAndCheckForAutoincrement(\r
+ ((TableOperatorNode)resultSet).getRightResultSet(),\r
+ inOrder, numTableColumns, colMap, dataDictionary,\r
+ targetTableDescriptor, targetVTI);\r
+ if (! inOrder || resultSet.resultColumns.size() < numTableColumns)\r
+ resultSet.enhanceRCLForInsert(\r
+ numTableColumns, colMap, dataDictionary,\r
+ targetTableDescriptor, targetVTI);\r
+ }\r
+ else\r
+ {\r
+ if (! inOrder || resultSet.resultColumns.size() < numTableColumns)\r
+ resultSet.enhanceRCLForInsert(\r
+ numTableColumns, colMap, dataDictionary,\r
+ targetTableDescriptor, targetVTI);\r
+ resultColumnList.checkAutoincrement(resultSet.getResultColumns());\r
+ }\r
+ }\r
+\r
+ int getPrivType()\r
+ {\r
+ return Authorizer.INSERT_PRIV;\r
+ }\r
+\r
+ /**\r
+ * Return true if the node references SESSION schema tables (temporary or permanent)\r
+ *\r
+ * @return true if references SESSION schema tables, else false\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public boolean referencesSessionSchema()\r
+ throws StandardException\r
+ {\r
+ boolean returnValue = false;\r
+\r
+ //If this node references a SESSION schema table, then return true. \r
+ if (targetTableDescriptor != null)\r
+ returnValue = isSessionSchema(targetTableDescriptor.getSchemaDescriptor());\r
+\r
+ if (returnValue == false)\r
+ returnValue = resultSet.referencesSessionSchema();\r
+\r
+ return returnValue;\r
+ }\r
+\r
+ /**\r
+ * Verify that the target properties that we are interested in\r
+ * all hold valid values.\r
+ * NOTE: Any target property which is valid but cannot be supported\r
+ * due to a target database, etc. will be turned off quietly.\r
+ *\r
+ * @param dd The DataDictionary\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ private void verifyTargetProperties(DataDictionary dd)\r
+ throws StandardException\r
+ {\r
+ // The only property that we're currently interested in is insertMode\r
+ String insertMode = targetProperties.getProperty("insertMode");\r
+ if (insertMode != null)\r
+ {\r
+ String upperValue = StringUtil.SQLToUpperCase(insertMode);\r
+ if (! upperValue.equals("BULKINSERT") &&\r
+ ! upperValue.equals("REPLACE"))\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_INVALID_INSERT_MODE, \r
+ insertMode,\r
+ targetTableName);\r
+ }\r
+ else\r
+ {\r
+ /* Turn off bulkInsert if it is on and we can't support it. */\r
+ if (! verifyBulkInsert(dd, upperValue))\r
+ {\r
+ targetProperties.remove("insertMode");\r
+ }\r
+ else\r
+ {\r
+ /* Now we know we're doing bulk insert */\r
+ bulkInsert = true;\r
+\r
+ if (upperValue.equals("REPLACE"))\r
+ {\r
+ bulkInsertReplace = true;\r
+ }\r
+\r
+ // Validate the bulkFetch property if specified\r
+ String bulkFetchStr = targetProperties.getProperty("bulkFetch");\r
+ if (bulkFetchStr != null)\r
+ {\r
+ int bulkFetch = getIntProperty(bulkFetchStr, "bulkFetch");\r
+\r
+ // verify that the specified value is valid\r
+ if (bulkFetch <= 0)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_INVALID_BULK_FETCH_VALUE,\r
+ String.valueOf(bulkFetch));\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Do the bind time checks to see if bulkInsert is allowed on\r
+ * this table. bulkInsert is disallowed at bind time for:\r
+ * o target databases\r
+ * o (tables with triggers?)\r
+ * (It is disallowed at execution time if the table has at\r
+ * least 1 row in it or if it is a deferred mode insert.)\r
+ *\r
+ * @param dd The DataDictionary\r
+ * @param mode The insert mode\r
+ *\r
+ * @return Whether or not bulkInsert is allowed.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ private boolean verifyBulkInsert(DataDictionary dd, String mode)\r
+ throws StandardException\r
+ {\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Compile constants that Execution will use\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ */\r
+ public ConstantAction makeConstantAction() throws StandardException\r
+ {\r
+\r
+ /* Different constant actions for base tables and updatable VTIs */\r
+ if (targetTableDescriptor != null)\r
+ {\r
+ // Base table\r
+\r
+ long heapConglomId = targetTableDescriptor.getHeapConglomerateId();\r
+ TransactionController tc = \r
+ getLanguageConnectionContext().getTransactionCompile();\r
+ int numIndexes = (targetTableDescriptor != null) ?\r
+ indexConglomerateNumbers.length : 0;\r
+ StaticCompiledOpenConglomInfo[] indexSCOCIs = \r
+ new StaticCompiledOpenConglomInfo[numIndexes];\r
+\r
+ for (int index = 0; index < numIndexes; index++)\r
+ {\r
+ indexSCOCIs[index] = tc.getStaticCompiledConglomInfo(indexConglomerateNumbers[index]);\r
+ }\r
+\r
+ /*\r
+ ** If we're doing bulk insert, do table locking regardless of\r
+ ** what the optimizer decided. This is because bulk insert is\r
+ ** generally done with a large number of rows into an empty table.\r
+ ** We also do table locking if the table's lock granularity is\r
+ ** set to table.\r
+ */\r
+ if (bulkInsert ||\r
+ targetTableDescriptor.getLockGranularity() == TableDescriptor.TABLE_LOCK_GRANULARITY)\r
+ {\r
+ lockMode = TransactionController.MODE_TABLE;\r
+ }\r
+\r
+ return getGenericConstantActionFactory().getInsertConstantAction\r
+ ( targetTableDescriptor,\r
+ heapConglomId,\r
+ tc.getStaticCompiledConglomInfo(heapConglomId),\r
+ indicesToMaintain,\r
+ indexConglomerateNumbers,\r
+ indexSCOCIs,\r
+ indexNames,\r
+ deferred,\r
+ false,\r
+ targetTableDescriptor.getUUID(),\r
+ lockMode,\r
+ null, null, \r
+ targetProperties,\r
+ getFKInfo(),\r
+ getTriggerInfo(),\r
+ resultColumnList.getStreamStorableColIds(targetTableDescriptor.getNumberOfColumns()),\r
+ getIndexedCols(),\r
+ (UUID) null,\r
+ null,\r
+ null,\r
+ resultSet.isOneRowResultSet(), \r
+ autoincRowLocation\r
+ );\r
+ }\r
+ else\r
+ {\r
+ /* Return constant action for VTI\r
+ * NOTE: ConstantAction responsible for preserving instantiated\r
+ * VTIs for in-memory queries and for only preserving VTIs\r
+ * that implement Serializable for SPSs.\r
+ */\r
+ return getGenericConstantActionFactory().getUpdatableVTIConstantAction( DeferModification.INSERT_STATEMENT,\r
+ deferred);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Create a boolean[] to track the (0-based) columns which are indexed.\r
+ *\r
+ * @return A boolean[] to track the (0-based) columns which are indexed.\r
+ *\r
+ * @exception StandardException Thrown on failure\r
+ */\r
+ public boolean[] getIndexedCols() throws StandardException\r
+ {\r
+ /* Create a boolean[] to track the (0-based) columns which are indexed */\r
+ boolean[] indexedCols = new boolean[targetTableDescriptor.getNumberOfColumns()];\r
+ for (int index = 0; index < indicesToMaintain.length; index++)\r
+ {\r
+ int[] colIds = indicesToMaintain[index].getIndexDescriptor().baseColumnPositions();\r
+\r
+ for (int index2 = 0; index2 < colIds.length; index2++)\r
+ {\r
+ indexedCols[colIds[index2] - 1] = true;\r
+ }\r
+ }\r
+\r
+ return indexedCols;\r
+ }\r
+\r
+ /**\r
+ * Code generation for insert\r
+ * creates an expression for:\r
+ * ResultSetFactory.getInsertResultSet(resultSet.generate(ps), this )\r
+ *\r
+ * @param acb The ActivationClassBuilder for the class being built\r
+ * @param mb the method for the execute() method to be built\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void generate(ActivationClassBuilder acb,\r
+ MethodBuilder mb)\r
+ throws StandardException\r
+ {\r
+ // If the DML is on the temporary table, generate the code to\r
+ // mark temporary table as modified in the current UOW. After\r
+ // DERBY-827 this must be done in execute() since\r
+ // fillResultSet() will only be called once.\r
+ generateCodeForTemporaryTable(acb, acb.getExecuteMethod());\r
+\r
+ /* generate the parameters */\r
+ generateParameterValueSet(acb);\r
+ // Base table\r
+ if (targetTableDescriptor != null)\r
+ {\r
+ /*\r
+ ** Generate the insert result set, giving it either the original\r
+ ** source or the normalize result set, the constant action,\r
+ ** and "this".\r
+ */\r
+\r
+ acb.pushGetResultSetFactoryExpression(mb);\r
+\r
+ // arg 1\r
+ resultSet.generate(acb, mb);\r
+\r
+ // arg 2 generate code to evaluate CHECK CONSTRAINTS\r
+ generateCheckConstraints( checkConstraints, acb, mb );\r
+\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getInsertResultSet", ClassName.ResultSet, 2);\r
+ }\r
+ else\r
+ {\r
+ /* Generate code for the VTI\r
+ * NOTE: we need to create a dummy cost estimate for the\r
+ * targetVTI since we never optimized it.\r
+ * RESOLVEVTI - we will have to optimize it in order to \r
+ * push predicates into the VTI.\r
+ */\r
+ targetVTI.assignCostEstimate(resultSet.getNewCostEstimate());\r
+\r
+ /*\r
+ ** Generate the insert VTI result set, giving it either the original\r
+ ** source or the normalize result set, the constant action,\r
+ */\r
+ acb.pushGetResultSetFactoryExpression(mb);\r
+\r
+ // arg 1\r
+ resultSet.generate(acb, mb);\r
+\r
+ // arg 2\r
+ targetVTI.generate(acb, mb);\r
+\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getInsertVTIResultSet", ClassName.ResultSet, 2);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Return the type of statement, something from\r
+ * StatementType.\r
+ *\r
+ * @return the type of statement\r
+ */\r
+ protected final int getStatementType()\r
+ {\r
+ return StatementType.INSERT;\r
+ }\r
+\r
+ /**\r
+ * Return the statement type, where it is dependent on\r
+ * the targetProperties. (insertMode = replace causes\r
+ * statement type to be BULK_INSERT_REPLACE.\r
+ *\r
+ * @return the type of statement\r
+ */\r
+ static final int getStatementType(Properties targetProperties)\r
+ {\r
+ int retval = StatementType.INSERT;\r
+\r
+ // The only property that we're currently interested in is insertMode\r
+ String insertMode = (targetProperties == null) ? null : targetProperties.getProperty("insertMode");\r
+ if (insertMode != null)\r
+ {\r
+ String upperValue = StringUtil.SQLToUpperCase(insertMode);\r
+ if (upperValue.equals("REPLACE"))\r
+ {\r
+ retval = StatementType.BULK_INSERT_REPLACE;\r
+ }\r
+ }\r
+ return retval;\r
+ }\r
+\r
+ /**\r
+ * Get the list of indexes on the table being inserted into. This\r
+ * is used by INSERT. This is an optimized version of what\r
+ * UPDATE and DELETE use. \r
+ *\r
+ * @param td TableDescriptor for the table being inserted into\r
+ * or deleted from\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ private void getAffectedIndexes\r
+ (\r
+ TableDescriptor td\r
+ ) \r
+ throws StandardException\r
+ {\r
+ IndexLister indexLister = td.getIndexLister( );\r
+\r
+ indicesToMaintain = indexLister.getDistinctIndexRowGenerators();\r
+ indexConglomerateNumbers = indexLister.getDistinctIndexConglomerateNumbers();\r
+ indexNames = indexLister.getDistinctIndexNames();\r
+\r
+ /* Add dependencies on all indexes in the list */\r
+ ConglomerateDescriptor[] cds = td.getConglomerateDescriptors();\r
+ CompilerContext cc = getCompilerContext();\r
+\r
+ for (int index = 0; index < cds.length; index++)\r
+ {\r
+ cc.createDependency(cds[index]);\r
+ }\r
+ }\r
+ \r
+} // end of class InsertNode\r