001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.InsertNode
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import org.apache.derby.iapi.services.context.ContextManager;
025:
026: import org.apache.derby.iapi.services.loader.GeneratedMethod;
027:
028: import org.apache.derby.iapi.services.compiler.JavaFactory;
029: import org.apache.derby.iapi.services.compiler.MethodBuilder;
030:
031: import org.apache.derby.iapi.reference.SQLState;
032: import org.apache.derby.iapi.error.StandardException;
033:
034: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
035:
036: import org.apache.derby.iapi.sql.conn.Authorizer;
037:
038: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
039: import org.apache.derby.iapi.sql.compile.Visitable;
040: import org.apache.derby.iapi.sql.compile.Visitor;
041: import org.apache.derby.iapi.sql.compile.CompilerContext;
042:
043: import org.apache.derby.iapi.reference.ClassName;
044:
045: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
046: import org.apache.derby.iapi.sql.dictionary.ConstraintDescriptor;
047: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
048: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
049: import org.apache.derby.iapi.sql.dictionary.IndexLister;
050: import org.apache.derby.iapi.sql.dictionary.IndexRowGenerator;
051: import org.apache.derby.iapi.types.TypeId;
052:
053: import org.apache.derby.iapi.sql.ResultSet;
054: import org.apache.derby.iapi.sql.Activation;
055: import org.apache.derby.iapi.sql.StatementType;
056:
057: import org.apache.derby.iapi.sql.execute.ConstantAction;
058:
059: import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
060: import org.apache.derby.iapi.store.access.TransactionController;
061: import org.apache.derby.iapi.types.RowLocation;
062:
063: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
064: import org.apache.derby.iapi.services.compiler.MethodBuilder;
065:
066: import org.apache.derby.iapi.services.sanity.SanityManager;
067:
068: import org.apache.derby.vti.DeferModification;
069:
070: import org.apache.derby.iapi.services.classfile.VMOpcode;
071: import org.apache.derby.iapi.util.StringUtil;
072:
073: import org.apache.derby.catalog.UUID;
074:
075: import org.apache.derby.impl.sql.execute.FKInfo;
076:
077: import java.util.Properties;
078:
079: import org.apache.derby.iapi.services.io.FormatableBitSet;
080: import org.apache.derby.iapi.util.ReuseFactory;
081:
082: /**
083: * An InsertNode is the top node in a query tree for an
084: * insert statement.
085: * <p>
086: * After parsing, the node contains
087: * targetTableName: the target table for the insert
088: * collist: a list of column names, if specified
089: * queryexpr: the expression being inserted, either
090: * a values clause or a select form; both
091: * of these are represented via the SelectNode,
092: * potentially with a TableOperatorNode such as
093: * UnionNode above it.
094: * <p>
095: * After binding, the node has had the target table's
096: * descriptor located and inserted, and the queryexpr
097: * and collist have been massaged so that they are identical
098: * to the table layout. This involves adding any default
099: * values for missing columns, and reordering the columns
100: * to match the table's ordering of them.
101: * <p>
102: * After optimizing, ...
103: */
104: public final class InsertNode extends DMLModStatementNode {
105: public ResultColumnList targetColumnList;
106: public boolean deferred;
107: public ValueNode checkConstraints;
108: public Properties targetProperties;
109: public FKInfo fkInfo;
110: protected boolean bulkInsert;
111: private boolean bulkInsertReplace;
112:
113: protected RowLocation[] autoincRowLocation;
114:
115: /**
116: * Initializer for an InsertNode.
117: *
118: * @param targetName The name of the table/VTI to insert into
119: * @param insertColumns A ResultColumnList with the names of the
120: * columns to insert into. May be null if the
121: * user did not specify the columns - in this
122: * case, the binding phase will have to figure
123: * it out.
124: * @param queryExpression The query expression that will generate
125: * the rows to insert into the given table
126: * @param targetProperties The properties specified on the target table
127: */
128:
129: public void init(Object targetName, Object insertColumns,
130: Object queryExpression, Object targetProperties) {
131: /* statementType gets set in super() before we've validated
132: * any properties, so we've kludged the code to get the
133: * right statementType for a bulk insert replace.
134: */
135: super
136: .init(
137: queryExpression,
138: ReuseFactory
139: .getInteger(getStatementType((Properties) targetProperties)));
140: setTarget((QueryTreeNode) targetName);
141: targetColumnList = (ResultColumnList) insertColumns;
142: this .targetProperties = (Properties) targetProperties;
143:
144: /* Remember that the query expression is the source to an INSERT */
145: getResultSetNode().setInsertSource();
146: }
147:
148: /**
149: * Convert this object to a String. See comments in QueryTreeNode.java
150: * for how this should be done for tree printing.
151: *
152: * @return This object as a String
153: */
154:
155: public String toString() {
156: if (SanityManager.DEBUG) {
157: try {
158: return ((targetTableName != null) ? targetTableName
159: : targetVTI.getTableName()).toString()
160: + "\n"
161: + targetProperties
162: + "\n"
163: + super .toString();
164: } catch (org.apache.derby.iapi.error.StandardException e) {
165: return "tableName: <not_known>\n" + targetProperties
166: + "\n" + super .toString();
167: }
168: } else {
169: return "";
170: }
171: }
172:
173: public String statementToString() {
174: return "INSERT";
175: }
176:
177: /**
178: * Prints the sub-nodes of this object. See QueryTreeNode.java for
179: * how tree printing is supposed to work.
180: *
181: * @param depth The depth of this node in the tree
182: */
183:
184: public void printSubNodes(int depth) {
185: if (SanityManager.DEBUG) {
186: super .printSubNodes(depth);
187:
188: if (targetTableName != null) {
189: printLabel(depth, "targetTableName: ");
190: targetTableName.treePrint(depth + 1);
191: }
192:
193: if (targetColumnList != null) {
194: printLabel(depth, "targetColumnList: ");
195: targetColumnList.treePrint(depth + 1);
196: }
197:
198: /* RESOLVE - need to print out targetTableDescriptor */
199: }
200: }
201:
202: /**
203: * Bind this InsertNode. This means looking up tables and columns and
204: * getting their types, and figuring out the result types of all
205: * expressions, as well as doing view resolution, permissions checking,
206: * etc.
207: * <p>
208: * Binding an insert will also massage the tree so that
209: * the collist and select column order/number are the
210: * same as the layout of the table in the store.
211: *
212: * @return The bound query tree
213: *
214: * @exception StandardException Thrown on error
215: */
216:
217: public QueryTreeNode bind() throws StandardException {
218: // We just need select privilege on the expressions
219: getCompilerContext()
220: .pushCurrentPrivType(Authorizer.SELECT_PRIV);
221:
222: FromList fromList = (FromList) getNodeFactory().getNode(
223: C_NodeTypes.FROM_LIST,
224: getNodeFactory().doJoinOrderOptimization(),
225: getContextManager());
226:
227: /* If any underlying ResultSetNode is a SelectNode, then we
228: * need to do a full bind(), including the expressions
229: * (since the fromList may include a FromSubquery).
230: */
231: DataDictionary dataDictionary = getDataDictionary();
232: super .bindResultSetsWithTables(dataDictionary);
233:
234: /*
235: ** Get the TableDescriptor for the table we are inserting into
236: */
237: verifyTargetTable();
238:
239: // Check the validity of the targetProperties, if they exist
240: if (targetProperties != null) {
241: verifyTargetProperties(dataDictionary);
242: }
243:
244: /*
245: ** Get the resultColumnList representing the columns in the base
246: ** table or VTI.
247: */
248: getResultColumnList();
249:
250: /* If we have a target column list, then it must have the same # of
251: * entries as the result set's RCL.
252: */
253: if (targetColumnList != null) {
254: /*
255: * Normalize synonym qualifers for column references.
256: */
257: if (synonymTableName != null) {
258: normalizeSynonymColumns(targetColumnList,
259: targetTableName);
260: }
261:
262: /* Bind the target column list */
263: getCompilerContext().pushCurrentPrivType(getPrivType());
264: if (targetTableDescriptor != null) {
265: targetColumnList.bindResultColumnsByName(
266: targetTableDescriptor, (DMLStatementNode) this );
267: } else {
268: targetColumnList.bindResultColumnsByName(targetVTI
269: .getResultColumns(), targetVTI, this );
270: }
271: getCompilerContext().popCurrentPrivType();
272: }
273:
274: /* Verify that all underlying ResultSets reclaimed their FromList */
275: if (SanityManager.DEBUG) {
276: SanityManager.ASSERT(fromList.size() == 0,
277: "fromList.size() is expected to be 0, not "
278: + fromList.size()
279: + " on return from RS.bindExpressions()");
280: }
281:
282: /* Replace any DEFAULTs with the associated tree */
283: resultSet.replaceDefaults(targetTableDescriptor,
284: targetColumnList);
285:
286: /* Bind the expressions now that the result columns are bound
287: * NOTE: This will be the 2nd time for those underlying ResultSets
288: * that have tables (no harm done), but it is necessary for those
289: * that do not have tables. It's too hard/not work the effort to
290: * avoid the redundancy.
291: */
292: super .bindExpressions();
293:
294: /*
295: ** If the result set is a union, it could be a table constructor.
296: ** Bind any nulls in the result columns of the table constructor
297: ** to the types of the table being inserted into.
298: **
299: ** The types of ? parameters in row constructors and table constructors
300: ** in an INSERT statement come from the result columns.
301: **
302: ** If there is a target column list, use that instead of the result
303: ** columns for the whole table, since the columns in the result set
304: ** correspond to the target column list.
305: */
306: if (targetColumnList != null) {
307: if (resultSet.getResultColumns().size() > targetColumnList
308: .size())
309: throw StandardException
310: .newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED);
311: resultSet.bindUntypedNullsToResultColumns(targetColumnList);
312: resultSet.setTableConstructorTypes(targetColumnList);
313: } else {
314: if (resultSet.getResultColumns().size() > resultColumnList
315: .size())
316: throw StandardException
317: .newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED);
318: resultSet.bindUntypedNullsToResultColumns(resultColumnList);
319: resultSet.setTableConstructorTypes(resultColumnList);
320: }
321:
322: /* Bind the columns of the result set to their expressions */
323: resultSet.bindResultColumns(fromList);
324:
325: int resCols = resultSet.getResultColumns().size();
326: DataDictionary dd = getDataDictionary();
327: if (targetColumnList != null) {
328: if (targetColumnList.size() != resCols)
329: throw StandardException
330: .newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED);
331: } else {
332: if (targetTableDescriptor != null
333: && targetTableDescriptor.getNumberOfColumns() != resCols)
334: throw StandardException
335: .newException(SQLState.LANG_DB2_INVALID_COLS_SPECIFIED);
336: }
337:
338: /* See if the ResultSet's RCL needs to be ordered to match the target
339: * list, or "enhanced" to accommodate defaults. It can only need to
340: * be ordered if there is a target column list. It needs to be
341: * enhanced if there are fewer source columns than there are columns
342: * in the table.
343: */
344: boolean inOrder = true;
345: int numTableColumns = resultColumnList.size();
346:
347: /* colMap[] will be the size of the target list, which could be larger
348: * than the current size of the source list. In that case, the source
349: * list will be "enhanced" to include defaults.
350: */
351: int[] colMap = new int[numTableColumns];
352:
353: // set the fields to an unused value
354: for (int i = 0; i < colMap.length; i++) {
355: colMap[i] = -1;
356: }
357:
358: /* Create the source/target list mapping */
359: if (targetColumnList != null) {
360: /*
361: ** There is a target column list, so the result columns might
362: ** need to be ordered. Step through the target column list
363: ** and remember the position in the target table of each column.
364: ** Remember if any of the columns are out of order.
365: */
366: int targetSize = targetColumnList.size();
367: for (int index = 0; index < targetSize; index++) {
368: int position = ((ResultColumn) (targetColumnList
369: .elementAt(index))).columnDescriptor
370: .getPosition();
371:
372: if (index != position - 1) {
373: inOrder = false;
374: }
375:
376: // position is 1-base; colMap indexes and entries are 0-based.
377: colMap[position - 1] = index;
378: }
379: } else {
380: /*
381: ** There is no target column list, so the result columns in the
382: ** source are presumed to be in the same order as the target
383: ** table.
384: */
385: for (int position = 0; position < resultSet
386: .getResultColumns().size(); position++) {
387: colMap[position] = position;
388: }
389: }
390:
391: // colmap[x] == y means that column x in the target table
392: // maps to column y in the source result set.
393: // colmap[x] == -1 means that column x in the target table
394: // maps to its default value.
395: // both colmap indexes and values are 0-based.
396:
397: /* if the list is in order and complete, we don't have to change
398: * the tree. If it is not, then we call RSN.enhanceRCLForInsert()
399: * which will either
400: * (reorder and/or "enhance" the source RCL within the same RSN) or
401: * (generate and return a PRN with a new reordered/enhanced RCL above
402: * the existing RSN). This way, RSN's that understand how to do projections
403: * can avoid the additional PRN while those that do not will get one.
404: */
405: /* NOTE - javascope gives confusing branch coverage info here. By
406: * breaking apart the following if condition, I have verified that
407: * we test all cases. (Jerry 7/17/97)
408: */
409: if (!inOrder
410: || resultSet.resultColumns.size() < numTableColumns) {
411: // one thing we do know is that all of the resultsets underneath
412: // us have their resultColumn names filled in with the names of
413: // the target table columns. That makes generating the mapping
414: // "easier" -- we simply generate the names of the target table columns
415: // that are included. For the missing columns, we generate default
416: // value expressions.
417:
418: resultSet = resultSet.enhanceRCLForInsert(numTableColumns,
419: colMap, dataDictionary, targetTableDescriptor,
420: targetVTI);
421: }
422:
423: if (resultSet instanceof UnionNode) {
424: // If we are inserting a number of rows in VALUES clause, we need to
425: // examine each row for 'autoincrement'.
426: resultColumnList.checkAutoincrementUnion(resultSet);
427: } else
428: resultColumnList.checkAutoincrement(resultSet
429: .getResultColumns());
430: resultColumnList.checkStorableExpressions(resultSet
431: .getResultColumns());
432: /* Insert a NormalizeResultSetNode above the source if the source
433: * and target column types and lengths do not match.
434: */
435: if (!resultColumnList.columnTypesAndLengthsMatch(resultSet
436: .getResultColumns())) {
437: resultSet = resultSet.genNormalizeResultSetNode(resultSet,
438: false);
439: resultColumnList.copyTypesAndLengthsToSource(resultSet
440: .getResultColumns());
441: }
442:
443: if (targetTableDescriptor != null) {
444: /* Get and bind all constraints on the table */
445: ResultColumnList sourceRCL = resultSet.getResultColumns();
446: sourceRCL.copyResultColumnNames(resultColumnList);
447: checkConstraints = bindConstraints(dataDictionary,
448: getNodeFactory(), targetTableDescriptor, null,
449: sourceRCL, (int[]) null, (FormatableBitSet) null,
450: false, true); /* we always include
451: * triggers in core language */
452:
453: /* Do we need to do a deferred mode insert */
454: /*
455: ** Deferred if:
456: ** If the target table is also a source table
457: ** Self-referencing foreign key constraint
458: ** trigger
459: */
460: if (resultSet.referencesTarget(targetTableDescriptor
461: .getName(), true)
462: || requiresDeferredProcessing()) {
463: deferred = true;
464:
465: /* Disallow bulk insert replace when target table
466: * is also a source table.
467: */
468: if (bulkInsertReplace
469: && resultSet.referencesTarget(
470: targetTableDescriptor.getName(), true)) {
471: throw StandardException.newException(
472: SQLState.LANG_INVALID_BULK_INSERT_REPLACE,
473: targetTableDescriptor.getQualifiedName());
474: }
475: }
476:
477: /* Get the list of indexes on the table being inserted into */
478: getAffectedIndexes(targetTableDescriptor);
479: TransactionController tc = getLanguageConnectionContext()
480: .getTransactionCompile();
481:
482: autoincRowLocation = dd.computeAutoincRowLocations(tc,
483: targetTableDescriptor);
484:
485: if (isPrivilegeCollectionRequired()) {
486: getCompilerContext().pushCurrentPrivType(getPrivType());
487: getCompilerContext().addRequiredTablePriv(
488: targetTableDescriptor);
489: getCompilerContext().popCurrentPrivType();
490: }
491:
492: } else {
493: deferred = VTIDeferModPolicy.deferIt(
494: DeferModification.INSERT_STATEMENT, targetVTI,
495: null, resultSet);
496: }
497:
498: getCompilerContext().popCurrentPrivType();
499: return this ;
500: }
501:
502: int getPrivType() {
503: return Authorizer.INSERT_PRIV;
504: }
505:
506: /**
507: * Return true if the node references SESSION schema tables (temporary or permanent)
508: *
509: * @return true if references SESSION schema tables, else false
510: *
511: * @exception StandardException Thrown on error
512: */
513: public boolean referencesSessionSchema() throws StandardException {
514: boolean returnValue = false;
515:
516: //If this node references a SESSION schema table, then return true.
517: if (targetTableDescriptor != null)
518: returnValue = isSessionSchema(targetTableDescriptor
519: .getSchemaDescriptor());
520:
521: if (returnValue == false)
522: returnValue = resultSet.referencesSessionSchema();
523:
524: return returnValue;
525: }
526:
527: /**
528: * Verify that the target properties that we are interested in
529: * all hold valid values.
530: * NOTE: Any target property which is valid but cannot be supported
531: * due to a target database, etc. will be turned off quietly.
532: *
533: * @param dd The DataDictionary
534: *
535: * @exception StandardException Thrown on error
536: */
537: private void verifyTargetProperties(DataDictionary dd)
538: throws StandardException {
539: // The only property that we're currently interested in is insertMode
540: String insertMode = targetProperties.getProperty("insertMode");
541: if (insertMode != null) {
542: String upperValue = StringUtil.SQLToUpperCase(insertMode);
543: if (!upperValue.equals("BULKINSERT")
544: && !upperValue.equals("REPLACE")) {
545: throw StandardException.newException(
546: SQLState.LANG_INVALID_INSERT_MODE, insertMode,
547: targetTableName);
548: } else {
549: /* Turn off bulkInsert if it is on and we can't support it. */
550: if (!verifyBulkInsert(dd, upperValue)) {
551: targetProperties.remove("insertMode");
552: } else {
553: /* Now we know we're doing bulk insert */
554: bulkInsert = true;
555:
556: if (upperValue.equals("REPLACE")) {
557: bulkInsertReplace = true;
558: }
559:
560: // Validate the bulkFetch property if specified
561: String bulkFetchStr = targetProperties
562: .getProperty("bulkFetch");
563: if (bulkFetchStr != null) {
564: int bulkFetch = getIntProperty(bulkFetchStr,
565: "bulkFetch");
566:
567: // verify that the specified value is valid
568: if (bulkFetch <= 0) {
569: throw StandardException
570: .newException(
571: SQLState.LANG_INVALID_BULK_FETCH_VALUE,
572: String.valueOf(bulkFetch));
573: }
574: }
575: }
576: }
577: }
578: }
579:
580: /**
581: * Do the bind time checks to see if bulkInsert is allowed on
582: * this table. bulkInsert is disallowed at bind time for:
583: * o target databases
584: * o (tables with triggers?)
585: * (It is disallowed at execution time if the table has at
586: * least 1 row in it or if it is a deferred mode insert.)
587: *
588: * @param dd The DataDictionary
589: * @param mode The insert mode
590: *
591: * @return Whether or not bulkInsert is allowed.
592: *
593: * @exception StandardException Thrown on error
594: */
595: private boolean verifyBulkInsert(DataDictionary dd, String mode)
596: throws StandardException {
597: return true;
598: }
599:
600: /**
601: * Compile constants that Execution will use
602: *
603: * @exception StandardException Thrown on failure
604: */
605: public ConstantAction makeConstantAction() throws StandardException {
606:
607: /* Different constant actions for base tables and updatable VTIs */
608: if (targetTableDescriptor != null) {
609: // Base table
610:
611: long heapConglomId = targetTableDescriptor
612: .getHeapConglomerateId();
613: TransactionController tc = getLanguageConnectionContext()
614: .getTransactionCompile();
615: int numIndexes = (targetTableDescriptor != null) ? indexConglomerateNumbers.length
616: : 0;
617: StaticCompiledOpenConglomInfo[] indexSCOCIs = new StaticCompiledOpenConglomInfo[numIndexes];
618:
619: for (int index = 0; index < numIndexes; index++) {
620: indexSCOCIs[index] = tc
621: .getStaticCompiledConglomInfo(indexConglomerateNumbers[index]);
622: }
623:
624: /*
625: ** If we're doing bulk insert, do table locking regardless of
626: ** what the optimizer decided. This is because bulk insert is
627: ** generally done with a large number of rows into an empty table.
628: ** We also do table locking if the table's lock granularity is
629: ** set to table.
630: */
631: if (bulkInsert
632: || targetTableDescriptor.getLockGranularity() == TableDescriptor.TABLE_LOCK_GRANULARITY) {
633: lockMode = TransactionController.MODE_TABLE;
634: }
635:
636: return getGenericConstantActionFactory()
637: .getInsertConstantAction(
638: targetTableDescriptor,
639: heapConglomId,
640: tc
641: .getStaticCompiledConglomInfo(heapConglomId),
642: indicesToMaintain,
643: indexConglomerateNumbers,
644: indexSCOCIs,
645: indexNames,
646: deferred,
647: false,
648: targetTableDescriptor.getUUID(),
649: lockMode,
650: null,
651: null,
652: targetProperties,
653: getFKInfo(),
654: getTriggerInfo(),
655: resultColumnList
656: .getStreamStorableColIds(targetTableDescriptor
657: .getNumberOfColumns()),
658: getIndexedCols(), (UUID) null, null, null,
659: resultSet.isOneRowResultSet(),
660: autoincRowLocation);
661: } else {
662: /* Return constant action for VTI
663: * NOTE: ConstantAction responsible for preserving instantiated
664: * VTIs for in-memory queries and for only preserving VTIs
665: * that implement Serializable for SPSs.
666: */
667: return getGenericConstantActionFactory()
668: .getUpdatableVTIConstantAction(
669: DeferModification.INSERT_STATEMENT,
670: deferred);
671: }
672: }
673:
674: /**
675: * Create a boolean[] to track the (0-based) columns which are indexed.
676: *
677: * @return A boolean[] to track the (0-based) columns which are indexed.
678: *
679: * @exception StandardException Thrown on failure
680: */
681: public boolean[] getIndexedCols() throws StandardException {
682: /* Create a boolean[] to track the (0-based) columns which are indexed */
683: boolean[] indexedCols = new boolean[targetTableDescriptor
684: .getNumberOfColumns()];
685: for (int index = 0; index < indicesToMaintain.length; index++) {
686: int[] colIds = indicesToMaintain[index]
687: .getIndexDescriptor().baseColumnPositions();
688:
689: for (int index2 = 0; index2 < colIds.length; index2++) {
690: indexedCols[colIds[index2] - 1] = true;
691: }
692: }
693:
694: return indexedCols;
695: }
696:
697: /**
698: * Code generation for insert
699: * creates an expression for:
700: * ResultSetFactory.getInsertResultSet(resultSet.generate(ps), this )
701: *
702: * @param acb The ActivationClassBuilder for the class being built
703: * @param mb the method for the execute() method to be built
704: *
705: * @exception StandardException Thrown on error
706: */
707: public void generate(ActivationClassBuilder acb, MethodBuilder mb)
708: throws StandardException {
709: //If the DML is on the temporary table, generate the code to mark temporary table as modified in the current UOW
710: generateCodeForTemporaryTable(acb, mb);
711:
712: /* generate the parameters */
713: generateParameterValueSet(acb);
714: // Base table
715: if (targetTableDescriptor != null) {
716: /*
717: ** Generate the insert result set, giving it either the original
718: ** source or the normalize result set, the constant action,
719: ** and "this".
720: */
721:
722: acb.pushGetResultSetFactoryExpression(mb);
723:
724: // arg 1
725: resultSet.generate(acb, mb);
726:
727: // arg 2 generate code to evaluate CHECK CONSTRAINTS
728: generateCheckConstraints(checkConstraints, acb, mb);
729:
730: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
731: "getInsertResultSet", ClassName.ResultSet, 2);
732: } else {
733: /* Generate code for the VTI
734: * NOTE: we need to create a dummy cost estimate for the
735: * targetVTI since we never optimized it.
736: * RESOLVEVTI - we will have to optimize it in order to
737: * push predicates into the VTI.
738: */
739: targetVTI
740: .assignCostEstimate(resultSet.getNewCostEstimate());
741:
742: /*
743: ** Generate the insert VTI result set, giving it either the original
744: ** source or the normalize result set, the constant action,
745: */
746: acb.pushGetResultSetFactoryExpression(mb);
747:
748: // arg 1
749: resultSet.generate(acb, mb);
750:
751: // arg 2
752: targetVTI.generate(acb, mb);
753:
754: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
755: "getInsertVTIResultSet", ClassName.ResultSet, 2);
756: }
757: }
758:
759: /**
760: * Return the type of statement, something from
761: * StatementType.
762: *
763: * @return the type of statement
764: */
765: protected final int getStatementType() {
766: return StatementType.INSERT;
767: }
768:
769: /**
770: * Return the statement type, where it is dependent on
771: * the targetProperties. (insertMode = replace causes
772: * statement type to be BULK_INSERT_REPLACE.
773: *
774: * @return the type of statement
775: */
776: static final int getStatementType(Properties targetProperties) {
777: int retval = StatementType.INSERT;
778:
779: // The only property that we're currently interested in is insertMode
780: String insertMode = (targetProperties == null) ? null
781: : targetProperties.getProperty("insertMode");
782: if (insertMode != null) {
783: String upperValue = StringUtil.SQLToUpperCase(insertMode);
784: if (upperValue.equals("REPLACE")) {
785: retval = StatementType.BULK_INSERT_REPLACE;
786: }
787: }
788: return retval;
789: }
790:
791: /**
792: * Get the list of indexes on the table being inserted into. This
793: * is used by INSERT. This is an optimized version of what
794: * UPDATE and DELETE use.
795: *
796: * @param td TableDescriptor for the table being inserted into
797: * or deleted from
798: *
799: * @exception StandardException Thrown on error
800: */
801: private void getAffectedIndexes(TableDescriptor td)
802: throws StandardException {
803: IndexLister indexLister = td.getIndexLister();
804:
805: indicesToMaintain = indexLister.getDistinctIndexRowGenerators();
806: indexConglomerateNumbers = indexLister
807: .getDistinctIndexConglomerateNumbers();
808: indexNames = indexLister.getDistinctIndexNames();
809:
810: /* Add dependencies on all indexes in the list */
811: ConglomerateDescriptor[] cds = td.getConglomerateDescriptors();
812: CompilerContext cc = getCompilerContext();
813:
814: for (int index = 0; index < cds.length; index++) {
815: cc.createDependency(cds[index]);
816: }
817: }
818:
819: } // end of class InsertNode
|