0001: /*
0002: Derby - Class org.apache.derby.impl.sql.compile.ResultColumnList
0003:
0004: Licensed to the Apache Software Foundation (ASF) under one or more
0005: contributor license agreements. See the NOTICE file distributed with
0006: this work for additional information regarding copyright ownership.
0007: The ASF licenses this file to you under the Apache License, Version 2.0
0008: (the "License"); you may not use this file except in compliance with
0009: the License. You may obtain a copy of the License at
0010:
0011: http://www.apache.org/licenses/LICENSE-2.0
0012:
0013: Unless required by applicable law or agreed to in writing, software
0014: distributed under the License is distributed on an "AS IS" BASIS,
0015: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0016: See the License for the specific language governing permissions and
0017: limitations under the License.
0018:
0019: */
0020:
0021: package org.apache.derby.impl.sql.compile;
0022:
0023: import org.apache.derby.iapi.services.context.ContextManager;
0024: import org.apache.derby.iapi.services.compiler.MethodBuilder;
0025: import org.apache.derby.iapi.services.compiler.LocalField;
0026:
0027: import org.apache.derby.iapi.services.sanity.SanityManager;
0028: import org.apache.derby.iapi.services.monitor.Monitor;
0029: import org.apache.derby.iapi.services.loader.ClassFactory;
0030: import org.apache.derby.iapi.services.io.Storable;
0031: import org.apache.derby.iapi.services.context.ContextManager;
0032:
0033: import org.apache.derby.iapi.error.StandardException;
0034:
0035: import org.apache.derby.iapi.sql.compile.NodeFactory;
0036: import org.apache.derby.iapi.sql.compile.CompilerContext;
0037: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
0038:
0039: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
0040:
0041: import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
0042: import org.apache.derby.iapi.sql.dictionary.ColumnDescriptorList;
0043: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
0044: import org.apache.derby.iapi.sql.dictionary.DataDictionaryContext;
0045: import org.apache.derby.iapi.sql.dictionary.DefaultDescriptor;
0046: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
0047: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
0048:
0049: import org.apache.derby.iapi.types.TypeId;
0050: import org.apache.derby.iapi.services.classfile.VMOpcode;
0051: import org.apache.derby.iapi.util.JBitSet;
0052:
0053: import org.apache.derby.iapi.reference.SQLState;
0054:
0055: import org.apache.derby.iapi.reference.SQLState;
0056:
0057: import org.apache.derby.iapi.sql.Activation;
0058:
0059: import org.apache.derby.iapi.sql.ResultColumnDescriptor;
0060: import org.apache.derby.iapi.sql.Row;
0061: import org.apache.derby.iapi.types.TypeId;
0062:
0063: import org.apache.derby.iapi.sql.execute.ExecRow;
0064: import org.apache.derby.iapi.sql.execute.ExecIndexRow;
0065: import org.apache.derby.iapi.sql.execute.ExecutionFactory;
0066: import org.apache.derby.iapi.sql.execute.ExecutionContext;
0067:
0068: import org.apache.derby.iapi.types.DataTypeDescriptor;
0069: import org.apache.derby.iapi.types.DataValueDescriptor;
0070: import org.apache.derby.iapi.types.RowLocation;
0071:
0072: import org.apache.derby.iapi.store.access.ConglomerateController;
0073: import org.apache.derby.iapi.store.access.StoreCostController;
0074: import org.apache.derby.iapi.store.access.TransactionController;
0075:
0076: import org.apache.derby.iapi.services.loader.GeneratedMethod;
0077:
0078: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
0079: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
0080:
0081: import org.apache.derby.iapi.services.io.FormatableBitSet;
0082: import org.apache.derby.iapi.reference.ClassName;
0083:
0084: import org.apache.derby.catalog.types.DefaultInfoImpl;
0085:
0086: import java.lang.reflect.Modifier;
0087:
0088: import org.apache.derby.iapi.util.ReuseFactory;
0089: import org.apache.derby.iapi.services.classfile.VMOpcode;
0090:
0091: import java.sql.ResultSetMetaData;
0092: import java.sql.SQLException;
0093: import java.sql.Types;
0094:
0095: import java.util.Hashtable;
0096: import java.util.Vector;
0097:
0098: /**
0099: * A ResultColumnList is the target list of a SELECT, INSERT, or UPDATE.
0100: *
0101: * @see ResultColumn
0102: */
0103:
0104: public class ResultColumnList extends QueryTreeNodeVector {
0105: /* Is this the ResultColumnList for an index row? */
0106: protected boolean indexRow;
0107: protected long conglomerateId;
0108:
0109: int orderBySelect = 0; // the number of result columns pulled up
0110: // from ORDERBY list
0111: /*
0112: * A comment on 'orderBySelect'. When we encounter a SELECT .. ORDER BY
0113: * statement, the columns (or expressions) in the ORDER BY clause may
0114: * or may not have been explicitly mentioned in the SELECT column list.
0115: * If the columns were NOT explicitly mentioned in the SELECT column
0116: * list, then the parsing of the ORDER BY clause implicitly generates
0117: * them into the result column list, because we'll need to have those
0118: * columns present at execution time in order to sort by them. Those
0119: * generated columns are added to the *end* of the ResultColumnList, and
0120: * we keep track of the *number* of those columns in 'orderBySelect',
0121: * so we can tell whether we are looking at a generated column by seeing
0122: * whether its position in the ResultColumnList is in the last
0123: * 'orderBySelect' number of columns. If the SELECT .. ORDER BY
0124: * statement uses the "*" token to select all the columns from a table,
0125: * then during ORDER BY parsing we redundantly generate the columns
0126: * mentioned in the ORDER BY clause into the ResultColumnlist, but then
0127: * later in getOrderByColumn we determine that these are duplicates and
0128: * we take them back out again.
0129: */
0130:
0131: /*
0132: ** Is this ResultColumnList for a FromBaseTable for an index
0133: ** that is to be updated?
0134: */
0135: protected boolean forUpdate;
0136:
0137: // Is a count mismatch allowed - see set/get methods for details.
0138: private boolean countMismatchAllowed;
0139:
0140: // Number of RCs in this RCL at "init" time, before additional
0141: // ones were added internally.
0142: private int initialListSize = 0;
0143:
0144: public ResultColumnList() {
0145: }
0146:
0147: /**
0148: * Add a ResultColumn (at this point, ResultColumn or
0149: * AllResultColumn) to the list
0150: *
0151: * @param resultColumn The ResultColumn to add to the list
0152: */
0153:
0154: public void addResultColumn(ResultColumn resultColumn) {
0155: /* Vectors are 0-based, ResultColumns are 1-based */
0156: resultColumn.setVirtualColumnId(size() + 1);
0157: addElement(resultColumn);
0158: }
0159:
0160: /**
0161: * Append a given ResultColumnList to this one, resetting the virtual
0162: * column ids in the appended portion.
0163: *
0164: * @param resultColumns The ResultColumnList to be appended
0165: * @param destructiveCopy Whether or not this is a descructive copy
0166: * from resultColumns
0167: */
0168: public void appendResultColumns(ResultColumnList resultColumns,
0169: boolean destructiveCopy) {
0170: int oldSize = size();
0171: int newID = oldSize + 1;
0172:
0173: /*
0174: ** Set the virtual column ids in the list being appended.
0175: ** Vectors are zero-based, and virtual column ids are one-based,
0176: ** so the new virtual column ids start at the original size
0177: ** of this list, plus one.
0178: */
0179: int otherSize = resultColumns.size();
0180: for (int index = 0; index < otherSize; index++) {
0181: /* ResultColumns are 1-based */
0182: ((ResultColumn) resultColumns.elementAt(index))
0183: .setVirtualColumnId(newID);
0184: newID++;
0185: }
0186:
0187: if (destructiveCopy) {
0188: destructiveAppend(resultColumns);
0189: } else {
0190: nondestructiveAppend(resultColumns);
0191: }
0192: }
0193:
0194: /**
0195: * Get a ResultColumn from a column position (1-based) in the list
0196: *
0197: * @param position The ResultColumn to get from the list (1-based)
0198: *
0199: * @return the column at that position.
0200: */
0201:
0202: public ResultColumn getResultColumn(int position) {
0203: /*
0204: ** First see if it falls in position x. If not,
0205: ** search the whole shebang
0206: */
0207: if (position <= size()) {
0208: // this wraps the cast needed,
0209: // and the 0-based nature of the Vectors.
0210: ResultColumn rc = (ResultColumn) elementAt(position - 1);
0211: if (rc.getColumnPosition() == position) {
0212: return rc;
0213: }
0214: }
0215:
0216: /*
0217: ** Check each column
0218: */
0219: int size = size();
0220: for (int index = 0; index < size; index++) {
0221: ResultColumn rc = (ResultColumn) elementAt(index);
0222: if (rc.getColumnPosition() == position) {
0223: return rc;
0224: }
0225: }
0226: return null;
0227: }
0228:
0229: /**
0230: * Take a column position and a ResultSetNode and find the ResultColumn
0231: * in this RCL whose source result set is the same as the received
0232: * RSN and whose column position is the same as the received column
0233: * position.
0234: *
0235: * @param colNum The column position (w.r.t rsn) for which we're searching
0236: * @param rsn The result set node for which we're searching.
0237: * @return The ResultColumn in this RCL whose source is column colNum
0238: * in result set rsn. That ResultColumn's position w.r.t to this RCL
0239: * is also returned via the whichRC parameter. If no match is found,
0240: * return null and leave whichRC untouched.
0241: */
0242: public ResultColumn getResultColumn(int colNum, ResultSetNode rsn,
0243: int[] whichRC) throws StandardException {
0244: if (colNum == -1)
0245: return null;
0246:
0247: ResultColumn rc = null;
0248: ColumnReference colRef = null;
0249: int[] crColNum = new int[] { -1 };
0250:
0251: for (int index = size() - 1; index >= 0; index--) {
0252: rc = (ResultColumn) elementAt(index);
0253: if (!(rc.getExpression() instanceof ColumnReference)) {
0254: // If the rc's expression isn't a column reference then
0255: // it can't be pointing to rsn, so just skip it.
0256: continue;
0257: }
0258:
0259: colRef = (ColumnReference) rc.getExpression();
0260: if ((rsn == colRef.getSourceResultSet(crColNum))
0261: && (crColNum[0] == colNum)) {
0262: // Found a match.
0263: whichRC[0] = index + 1;
0264: return rc;
0265: }
0266: }
0267:
0268: return null;
0269: }
0270:
0271: /**
0272: * Get a ResultColumn from a column position (1-based) in the list,
0273: * null if out of range (for order by).
0274: *
0275: * @param position The ResultColumn to get from the list (1-based)
0276: *
0277: * @return the column at that position, null if out of range
0278: */
0279: public ResultColumn getOrderByColumn(int position) {
0280: // this wraps the cast needed, and the 0-based nature of the Vectors.
0281: if (position == 0)
0282: return null;
0283:
0284: return getResultColumn(position);
0285: }
0286:
0287: /**
0288: * Get a ResultColumn that matches the specified columnName and
0289: * mark the ResultColumn as being referenced.
0290: *
0291: * @param columnName The ResultColumn to get from the list
0292: *
0293: * @return the column that matches that name.
0294: */
0295:
0296: public ResultColumn getResultColumn(String columnName) {
0297: int size = size();
0298: for (int index = 0; index < size; index++) {
0299: ResultColumn resultColumn = (ResultColumn) elementAt(index);
0300: if (columnName.equals(resultColumn.getName())) {
0301: /* Mark ResultColumn as referenced and return it */
0302: resultColumn.setReferenced();
0303: return resultColumn;
0304: }
0305: }
0306: return null;
0307: }
0308:
0309: /**
0310: * Get a ResultColumn that matches the specified columnName and
0311: * mark the ResultColumn as being referenced.
0312: *
0313: * @param columnsTableName Qualifying name for the column
0314: * @param columnName The ResultColumn to get from the list
0315: *
0316: * @return the column that matches that name.
0317: */
0318:
0319: public ResultColumn getResultColumn(String columnsTableName,
0320: String columnName) {
0321: int size = size();
0322: for (int index = 0; index < size; index++) {
0323: ResultColumn resultColumn = (ResultColumn) elementAt(index);
0324:
0325: /* If the column's table name is non-null, then we have found a match
0326: * only if the RC's table name is non-null and the same as the
0327: * the CR's table name.
0328: */
0329: if (columnsTableName != null) {
0330: if (resultColumn.getTableName() == null) {
0331: continue;
0332: }
0333:
0334: if (!columnsTableName.equals(resultColumn
0335: .getTableName())) {
0336: continue;
0337: }
0338: }
0339: if (columnName.equals(resultColumn.getName())) {
0340: /* Mark ResultColumn as referenced and return it */
0341: resultColumn.setReferenced();
0342: return resultColumn;
0343: }
0344: }
0345: return null;
0346: }
0347:
0348: /**
0349: * Get a ResultColumn that matches the specified columnName and
0350: * mark the ResultColumn as being referenced.
0351: * NOTE - this flavor enforces no ambiguity (at most 1 match)
0352: * Only FromSubquery needs to call this flavor since
0353: * it can have ambiguous references in its own list.
0354: *
0355: * @param cr The ColumnReference to resolve
0356: * @param exposedTableName Exposed table name for FromTable
0357: *
0358: * @return the column that matches that name.
0359: *
0360: * @exception StandardException Thrown on error
0361: */
0362:
0363: public ResultColumn getAtMostOneResultColumn(ColumnReference cr,
0364: String exposedTableName) throws StandardException {
0365: int size = size();
0366: ResultColumn retRC = null;
0367: String columnName = cr.getColumnName();
0368:
0369: for (int index = 0; index < size; index++) {
0370: ResultColumn resultColumn = (ResultColumn) elementAt(index);
0371:
0372: if (columnName.equals(resultColumn.getName())) {
0373: /* We should get at most 1 match */
0374: if (retRC != null) {
0375: throw StandardException
0376: .newException(
0377: SQLState.LANG_AMBIGUOUS_COLUMN_NAME_IN_TABLE,
0378: columnName, exposedTableName);
0379: }
0380: /* Mark ResultColumn as referenced and return it */
0381: resultColumn.setReferenced();
0382: retRC = resultColumn;
0383: }
0384: }
0385: return retRC;
0386: }
0387:
0388: /**
0389: * For order by, get a ResultColumn that matches the specified
0390: * columnName.
0391: *
0392: * @param columnName The ResultColumn to get from the list
0393: * @param tableName The table name on the OrderByColumn, if any
0394: * @param tableNumber The tableNumber corresponding to the FromTable with the
0395: * exposed name of tableName, if tableName != null.
0396: *
0397: * @return the column that matches that name.
0398: * @exception StandardException thrown on ambiguity
0399: */
0400: public ResultColumn getOrderByColumn(String columnName,
0401: TableName tableName, int tableNumber)
0402: throws StandardException {
0403: int size = size();
0404: ResultColumn retVal = null, resultColumn;
0405:
0406: for (int index = 0; index < size; index++) {
0407: resultColumn = (ResultColumn) elementAt(index);
0408:
0409: /* The order by column is qualified, then it is okay to consider
0410: * this RC if:
0411: * o The RC is qualified and the qualifiers on the order by column
0412: * and the RC are equal().
0413: * o The RC is not qualified, but its expression is a ColumnReference
0414: * from the same table (as determined by the tableNumbers).
0415: */
0416: if (tableName != null) {
0417: ValueNode rcExpr = resultColumn.getExpression();
0418: if (!(rcExpr instanceof ColumnReference))
0419: continue;
0420:
0421: ColumnReference cr = (ColumnReference) rcExpr;
0422: if ((!tableName.equals(cr.getTableNameNode()))
0423: && tableNumber != cr.getTableNumber())
0424: continue;
0425: }
0426:
0427: /* We finally got past the qualifiers, now see if the column
0428: * names are equal. If they are, then we appear to have found
0429: * our order by column. If we find our order by column multiple
0430: * times, make sure that they are truly duplicates, otherwise
0431: * we have an ambiguous situation. For example, the query
0432: * SELECT b+c AS a, d+e AS a FROM t ORDER BY a
0433: * is ambiguous because we don't know which "a" is meant. But
0434: * SELECT t.a, t.* FROM t ORDER BY a
0435: * is not ambiguous, even though column "a" is selected twice.
0436: * If we find our ORDER BY column at the end of the
0437: * SELECT column list, in the last 'orderBySelect' number
0438: * of columns, then this column was not explicitly mentioned
0439: * by the user in their SELECT column list, but was implicitly
0440: * added by the parsing of the ORDER BY clause, and it
0441: * should be removed from the ResultColumnList and returned
0442: * to the caller.
0443: */
0444: if (columnName.equals(resultColumn.getName())) {
0445: if (retVal == null) {
0446: retVal = resultColumn;
0447: } else if (!retVal.isEquivalent(resultColumn)) {
0448: throw StandardException
0449: .newException(
0450: SQLState.LANG_DUPLICATE_COLUMN_FOR_ORDER_BY,
0451: columnName);
0452: } else if (index >= size - orderBySelect) {// remove the column due to pullup of orderby item
0453: removeElement(resultColumn);
0454: decOrderBySelect();
0455: break;
0456: }
0457: }
0458: }
0459: return retVal;
0460: }
0461:
0462: /**
0463: * For order by, get a ResultColumn that matches the specified
0464: * columnName.
0465: *
0466: * @param columnName The ResultColumn to get from the list
0467: * @param tableName The table name on the OrderByColumn, if any
0468: *
0469: * @return the column that matches that name.
0470: * @exception StandardException thrown on ambiguity
0471: */
0472: public ResultColumn getOrderByColumn(String columnName,
0473: TableName tableName) throws StandardException {
0474: int size = size();
0475: ResultColumn retVal = null, resultColumn;
0476:
0477: for (int index = 0; index < size; index++) {
0478: resultColumn = (ResultColumn) elementAt(index);
0479:
0480: // We may be checking on "ORDER BY T.A" against "SELECT *".
0481: // exposedName will not be null and "*" will not have an expression
0482: // or tablename.
0483: // We may be checking on "ORDER BY T.A" against "SELECT T.B, T.A".
0484: if (tableName != null) {
0485: ValueNode rcExpr = resultColumn.getExpression();
0486: if (rcExpr == null
0487: || !(rcExpr instanceof ColumnReference)) {
0488: continue;
0489: }
0490: ColumnReference cr = (ColumnReference) rcExpr;
0491: if (!tableName.equals(cr.getTableNameNode()))
0492: continue;
0493: }
0494:
0495: /* We finally got past the qualifiers, now see if the column
0496: * names are equal.
0497: */
0498: if (columnName.equals(resultColumn.getName())) {
0499: if (retVal == null) {
0500: retVal = resultColumn;
0501: } else if (!retVal.isEquivalent(resultColumn)) {
0502: throw StandardException
0503: .newException(
0504: SQLState.LANG_DUPLICATE_COLUMN_FOR_ORDER_BY,
0505: columnName);
0506: } else if (index >= size - orderBySelect) {// remove the column due to pullup of orderby item
0507: removeElement(resultColumn);
0508: decOrderBySelect();
0509: break;
0510: }
0511: }
0512: }
0513: return retVal;
0514: }
0515:
0516: /**
0517: * Copy the result column names from the given ResultColumnList
0518: * to this ResultColumnList. This is useful for insert-select,
0519: * where the columns being inserted into may be different from
0520: * the columns being selected from. The result column list for
0521: * an insert is supposed to have the column names being inserted
0522: * into.
0523: *
0524: * @param nameList The ResultColumnList from which to copy
0525: * the column names
0526: */
0527:
0528: void copyResultColumnNames(ResultColumnList nameList) {
0529: /* List checking is done during bind(). Lists should be the
0530: * same size when we are called.
0531: */
0532: if (SanityManager.DEBUG) {
0533: if ((!countMismatchAllowed) && size() != nameList.size()) {
0534: SanityManager
0535: .THROWASSERT("The size of the 2 lists is expected to be the same. size() = "
0536: + size()
0537: + ", nameList.size() = "
0538: + nameList.size());
0539: }
0540: }
0541:
0542: int size = (countMismatchAllowed) ? nameList.size() : size();
0543: for (int index = 0; index < size; index++) {
0544: ResultColumn this ResultColumn = (ResultColumn) elementAt(index);
0545: ResultColumn nameListResultColumn = (ResultColumn) nameList
0546: .elementAt(index);
0547: this ResultColumn.setName(nameListResultColumn.getName());
0548: this ResultColumn.setNameGenerated(nameListResultColumn
0549: .isNameGenerated());
0550: }
0551: }
0552:
0553: /**
0554: * This class needs a treePrint method, even though it is not a
0555: * descendant of QueryTreeNode, because its members contain tree
0556: * nodes, and these have to be printed and indented properly.
0557: *
0558: * @param depth The depth at which to indent the sub-nodes
0559: */
0560:
0561: public void treePrint(int depth) {
0562: if (SanityManager.DEBUG) {
0563: for (int index = 0; index < size(); index++) {
0564: ((ResultColumn) elementAt(index)).treePrint(depth);
0565: }
0566: }
0567: }
0568:
0569: /**
0570: * Bind the expressions in this ResultColumnList. This means binding
0571: * the expression under each ResultColumn node.
0572: *
0573: * @param fromList The FROM list for the query this
0574: * expression is in, for binding columns.
0575: * @param subqueryList The subquery list being built as we find SubqueryNodes
0576: * @param aggregateVector The aggregate vector being built as we find AggregateNodes
0577: *
0578: * @exception StandardException Thrown on error
0579: */
0580: public void bindExpressions(FromList fromList,
0581: SubqueryList subqueryList, Vector aggregateVector)
0582: throws StandardException {
0583: /* First we expand the *'s in the result column list */
0584: expandAllsAndNameColumns(fromList);
0585:
0586: /* Now we bind each result column */
0587: int size = size();
0588: for (int index = 0; index < size; index++) {
0589: ValueNode vn = (ValueNode) elementAt(index);
0590: vn = ((ResultColumn) vn).bindExpression(fromList,
0591: subqueryList, aggregateVector);
0592: setElementAt(vn, index);
0593: }
0594: }
0595:
0596: /**
0597: * Bind the result columns to the expressions that live under them.
0598: * All this does is copy the datatype information to from each expression
0599: * to each result column. This is useful for SELECT statements, where
0600: * the result type of each column is the type of the column's expression.
0601: *
0602: * @exception StandardException Thrown on error
0603: */
0604: public void bindResultColumnsToExpressions()
0605: throws StandardException {
0606: int size = size();
0607: for (int index = 0; index < size; index++) {
0608: ((ResultColumn) elementAt(index))
0609: .bindResultColumnToExpression();
0610: }
0611: }
0612:
0613: /**
0614: * Bind the result columns by their names. This is useful for GRANT and REVOKE statements
0615: * like "GRANT SELECT ON t(c1,c1,c3) TO george", where the user specified a column list.
0616: * This method does not check for duplicate column names.
0617: *
0618: * @param targetTableDescriptor The descriptor for the table
0619: *
0620: * @exception StandardException Thrown on error
0621: */
0622: public void bindResultColumnsByName(
0623: TableDescriptor targetTableDescriptor)
0624: throws StandardException {
0625: int size = size();
0626:
0627: for (int index = 0; index < size; index++) {
0628: ResultColumn rc = (ResultColumn) elementAt(index);
0629:
0630: rc.bindResultColumnByName(targetTableDescriptor, index + 1);
0631: }
0632: } // end of bindResultColumnsByName( TableDescriptor)
0633:
0634: /**
0635: * Bind the result columns by their names. This is useful for update, grant, and revoke
0636: * statements, and for INSERT statements like "insert into t (a, b, c)
0637: * values (1, 2, 3)" where the user specified a column list.
0638: * If the statment is an insert or update verify that the result column list does not contain any duplicates.
0639: * NOTE: We pass the ResultColumns position in the ResultColumnList so
0640: * that the VirtualColumnId gets set.
0641: *
0642: * @param targetTableDescriptor The descriptor for the table being
0643: * updated or inserted into
0644: * @param statement DMLStatementNode containing this list, null if no duplicate checking is to be done
0645: *
0646: * @return A FormatableBitSet representing the set of columns with respect to the table
0647: *
0648: * @exception StandardException Thrown on error
0649: */
0650: public FormatableBitSet bindResultColumnsByName(
0651: TableDescriptor targetTableDescriptor,
0652: DMLStatementNode statement) throws StandardException {
0653: int size = size();
0654: FormatableBitSet columnBitSet = new FormatableBitSet(
0655: targetTableDescriptor.getNumberOfColumns());
0656:
0657: for (int index = 0; index < size; index++) {
0658: ResultColumn rc = (ResultColumn) elementAt(index);
0659:
0660: rc.bindResultColumnByName(targetTableDescriptor, index + 1);
0661: int colIdx = rc.getColumnPosition() - 1;
0662: if (SanityManager.DEBUG)
0663: SanityManager.ASSERT(colIdx >= 0
0664: && colIdx < targetTableDescriptor
0665: .getNumberOfColumns(),
0666: "Invalid column position found for "
0667: + rc.getName());
0668: /* Verify that this column's name is unique within the list if requested */
0669: if (statement != null && columnBitSet.isSet(colIdx)) {
0670: String colName = rc.getName();
0671:
0672: if (statement instanceof UpdateNode) {
0673: throw StandardException.newException(
0674: SQLState.LANG_DUPLICATE_COLUMN_NAME_UPDATE,
0675: colName);
0676: } else {
0677: throw StandardException.newException(
0678: SQLState.LANG_DUPLICATE_COLUMN_NAME_INSERT,
0679: colName);
0680: }
0681: }
0682: columnBitSet.set(colIdx);
0683: }
0684: return columnBitSet;
0685: }
0686:
0687: /**
0688: * Bind the result columns by their names. This is useful for update
0689: * VTI statements, and for INSERT statements like "insert into new t() (a, b, c)
0690: * values (1, 2, 3)" where the user specified a column list.
0691: * Also, verify that the result column list does not contain any duplicates.
0692: * NOTE: We pass the ResultColumns position in the ResultColumnList so
0693: * that the VirtualColumnId gets set.
0694: *
0695: * @param fullRCL The full RCL for the target table
0696: * @param statement DMLStatementNode containing this list
0697: *
0698: * @exception StandardException Thrown on error
0699: */
0700: public void bindResultColumnsByName(ResultColumnList fullRCL,
0701: FromVTI targetVTI, DMLStatementNode statement)
0702: throws StandardException {
0703: int size = size();
0704: Hashtable ht = new Hashtable(size + 2, (float) .999);
0705:
0706: for (int index = 0; index < size; index++) {
0707: ResultColumn matchRC;
0708: ResultColumn rc = (ResultColumn) elementAt(index);
0709:
0710: /* Verify that this column's name is unique within the list */
0711: String colName = rc.getName();
0712:
0713: Object object = ht.put(colName, colName);
0714:
0715: if (object != null && ((String) object).equals(colName)) {
0716: if (SanityManager.DEBUG) {
0717: SanityManager
0718: .ASSERT(
0719: (statement instanceof UpdateNode)
0720: || (statement instanceof InsertNode),
0721: "statement is expected to be instanceof UpdateNode or InsertNode");
0722: }
0723: if (statement instanceof UpdateNode) {
0724: throw StandardException.newException(
0725: SQLState.LANG_DUPLICATE_COLUMN_NAME_UPDATE,
0726: colName);
0727: } else {
0728: throw StandardException.newException(
0729: SQLState.LANG_DUPLICATE_COLUMN_NAME_INSERT,
0730: colName);
0731: }
0732: }
0733:
0734: matchRC = fullRCL.getResultColumn(null, rc.getName());
0735: if (matchRC == null) {
0736: throw StandardException.newException(
0737: SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE, rc
0738: .getName(), targetVTI
0739: .getNewInvocation().getJavaClassName());
0740: }
0741:
0742: /* We have a match. We need to create a dummy ColumnDescriptor
0743: * since calling code expects one to get column info.
0744: */
0745: ColumnDescriptor cd = new ColumnDescriptor(rc.getName(),
0746: matchRC.getVirtualColumnId(), matchRC.getType(),
0747: null, null, (TableDescriptor) null, null, 0, 0);
0748: rc.setColumnDescriptor(null, cd);
0749: rc.setVirtualColumnId(index + 1);
0750: }
0751: }
0752:
0753: /**
0754: * Bind the result columns by ordinal position. This is useful for
0755: * INSERT statements like "insert into t values (1, 2, 3)", where the
0756: * user did not specify a column list.
0757: *
0758: * @param targetTableDescriptor The descriptor for the table being
0759: * inserted into
0760: *
0761: * @exception StandardException Thrown on error
0762: */
0763: public void bindResultColumnsByPosition(
0764: TableDescriptor targetTableDescriptor)
0765: throws StandardException {
0766: int size = size();
0767:
0768: for (int index = 0; index < size; index++) {
0769: /*
0770: ** Add one to the iterator index, because iterator indexes start at zero,
0771: ** and column numbers start at one.
0772: */
0773: ((ResultColumn) elementAt(index))
0774: .bindResultColumnByPosition(targetTableDescriptor,
0775: index + 1);
0776: }
0777: }
0778:
0779: /**
0780: * Preprocess the expression trees under the RCL.
0781: * We do a number of transformations
0782: * here (including subqueries, IN lists, LIKE and BETWEEN) plus
0783: * subquery flattening.
0784: * NOTE: This is done before the outer ResultSetNode is preprocessed.
0785: *
0786: * @param numTables Number of tables in the DML Statement
0787: * @param outerFromList FromList from outer query block
0788: * @param outerSubqueryList SubqueryList from outer query block
0789: * @param outerPredicateList PredicateList from outer query block
0790: *
0791: * @exception StandardException Thrown on error
0792: */
0793: public void preprocess(int numTables, FromList outerFromList,
0794: SubqueryList outerSubqueryList,
0795: PredicateList outerPredicateList) throws StandardException {
0796: int size = size();
0797:
0798: for (int index = 0; index < size; index++) {
0799: ResultColumn resultColumn = (ResultColumn) elementAt(index);
0800: setElementAt(resultColumn.preprocess(numTables,
0801: outerFromList, outerSubqueryList,
0802: outerPredicateList), index);
0803: }
0804: }
0805:
0806: /**
0807: Verify that all the result columns have expressions that
0808: are storable for them. Check versus the given ResultColumnList.
0809:
0810: @exception StandardException Thrown on error
0811: */
0812: void checkStorableExpressions(ResultColumnList toStore)
0813: throws StandardException {
0814: int size = size();
0815:
0816: for (int index = 0; index < size; index++) {
0817: ResultColumn otherRC = (ResultColumn) toStore
0818: .elementAt(index);
0819:
0820: ((ResultColumn) elementAt(index))
0821: .checkStorableExpression(otherRC);
0822: }
0823: }
0824:
0825: /**
0826: Return an array holding the 0 based heap offsets of
0827: the StreamStorable columns in this ResultColumnList.
0828: This returns null if this list does not contain any
0829: StreamStorableColumns. The list this returns does not
0830: contain duplicates. This should only be used for
0831: a resultColumnList the refers to a single heap
0832: such as the target for an Insert, Update or Delete.
0833: @param heapColCount the number of heap columns
0834: @exception StandardException Thrown on error
0835: */
0836: public int[] getStreamStorableColIds(int heapColCount)
0837: throws StandardException {
0838: //@#$
0839: //System.out.println("getStreamStorableColids");
0840:
0841: int ssCount = 0;
0842: boolean[] isSS = new boolean[heapColCount];//Should be table length.
0843: int size = size();
0844:
0845: for (int index = 0; index < size; index++) {
0846: ResultColumn rc = (ResultColumn) elementAt(index);
0847:
0848: if (rc.getTypeId().streamStorable()) {
0849: //System.out.println(" streamStorable=true");
0850: ColumnDescriptor cd = rc.getTableColumnDescriptor();
0851: isSS[cd.getPosition() - 1] = true;
0852: }
0853: }
0854:
0855: for (int ix = 0; ix < isSS.length; ix++)
0856: if (isSS[ix])
0857: ssCount++;
0858:
0859: if (ssCount == 0)
0860: return null;
0861:
0862: int[] result = new int[ssCount];
0863: int resultOffset = 0;
0864: for (int heapOffset = 0; heapOffset < isSS.length; heapOffset++) {
0865: if (isSS[heapOffset])
0866: result[resultOffset++] = heapOffset;
0867: }
0868:
0869: return result;
0870: }
0871:
0872: /**
0873: Verify that all the result columns have expressions that
0874: are storable for them. Check versus the expressions under the
0875: ResultColumns.
0876:
0877: @exception StandardException Thrown on error
0878: */
0879: void checkStorableExpressions() throws StandardException {
0880: int size = size();
0881:
0882: for (int index = 0; index < size; index++) {
0883: ((ResultColumn) elementAt(index)).checkStorableExpression();
0884: }
0885: }
0886:
0887: /**
0888: * Generate the code to place the columns' values into
0889: * a row variable named "r". This wrapper is here
0890: * rather than in ResultColumn, because that class does
0891: * not know about the position of the columns in the list.
0892: *
0893: * @exception StandardException Thrown on error
0894: */
0895: public void generate(ActivationClassBuilder acb, MethodBuilder mb)
0896: throws StandardException {
0897: generateCore(acb, mb, false);
0898: }
0899:
0900: /**
0901: * Generate the code to place the columns' values into
0902: * a row variable named "r". This wrapper is here
0903: * rather than in ResultColumn, because that class does
0904: * not know about the position of the columns in the list.
0905: *
0906: * @exception StandardException Thrown on error
0907: */
0908: void generateNulls(ActivationClassBuilder acb, MethodBuilder mb)
0909: throws StandardException {
0910: generateCore(acb, mb, true);
0911: }
0912:
0913: /**
0914: * Generate the code to place the columns' values into
0915: * a row variable named "r". This wrapper is here
0916: * rather than in ResultColumn, because that class does
0917: * not know about the position of the columns in the list.
0918: *
0919: * This is the method that does the work.
0920: */
0921: void generateCore(ExpressionClassBuilder acb, MethodBuilder mb,
0922: boolean genNulls) throws StandardException {
0923: // generate the function and initializer:
0924: // private ExecRow fieldX;
0925: // In the constructor:
0926: // fieldX = getExecutionFactory().getValueRow(# cols);
0927: // private ExecRow exprN()
0928: // {
0929: // fieldX.setColumn(1, col(1).generateColumn(ps)));
0930: // ... and so on for each column ...
0931: // return fieldX;
0932: // }
0933: // static Method exprN = method pointer to exprN;
0934:
0935: // this sets up the method and the static field.
0936: MethodBuilder userExprFun = acb.newUserExprFun();
0937:
0938: /* Declare the field */
0939: LocalField field = acb.newFieldDeclaration(Modifier.PRIVATE,
0940: ClassName.ExecRow);
0941:
0942: // Generate the code to create the row in the constructor
0943: genCreateRow(acb, field, "getValueRow", ClassName.ExecRow,
0944: size());
0945:
0946: ResultColumn rc;
0947: int size = size();
0948:
0949: MethodBuilder cb = acb.getConstructor();
0950:
0951: for (int index = 0; index < size; index++) {
0952: // generate statements of the form
0953: // fieldX.setColumn(columnNumber, (DataValueDescriptor) columnExpr);
0954: // and add them to exprFun.
0955: rc = (ResultColumn) elementAt(index);
0956:
0957: /* If we are not generating nulls, then we can skip this RC if
0958: * it is simply propagating a column from the source result set.
0959: */
0960: if (!genNulls) {
0961: ValueNode sourceExpr = rc.getExpression();
0962:
0963: if (sourceExpr instanceof VirtualColumnNode
0964: && !(((VirtualColumnNode) sourceExpr)
0965: .getCorrelated())) {
0966: continue;
0967: }
0968:
0969: if (sourceExpr instanceof ColumnReference
0970: && !(((ColumnReference) sourceExpr)
0971: .getCorrelated())) {
0972: continue;
0973: }
0974: }
0975:
0976: // row add is 1-based, and iterator index is 0-based
0977: if (SanityManager.DEBUG) {
0978: if (index + 1 != rc.getVirtualColumnId()) {
0979: SanityManager
0980: .THROWASSERT("VirtualColumnId ("
0981: + rc.getVirtualColumnId()
0982: + ") does not agree with position within Vector ("
0983: + (index + 1) + ")");
0984: }
0985: }
0986:
0987: // we need the expressions to be Columns exactly.
0988:
0989: /* SPECIAL CASE: Expression is a non-null constant.
0990: * Generate the setColumn() call in the constructor
0991: * so that it will only be executed once per instantiation.
0992: *
0993: * Increase the statement counter in constructor. Code size in
0994: * constructor can become too big (more than 64K) for Java compiler
0995: * to handle (beetle 4293). We set constant columns in other
0996: * methods if constructor has too many statements already.
0997: */
0998: if ((!genNulls)
0999: && (rc.getExpression() instanceof ConstantNode)
1000: && !((ConstantNode) rc.getExpression()).isNull()
1001: && !cb.statementNumHitLimit(1)) {
1002:
1003: cb.getField(field); // instance
1004: cb.push(index + 1); // first arg;
1005:
1006: rc.generateExpression(acb, cb);
1007: cb.cast(ClassName.DataValueDescriptor); // second arg
1008: cb.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row,
1009: "setColumn", "void", 2);
1010: continue;
1011: }
1012:
1013: userExprFun.getField(field); // instance
1014: userExprFun.push(index + 1); // arg1
1015:
1016: /* We want to reuse the null values instead of doing a new each time
1017: * if the caller said to generate nulls or the underlying expression
1018: * is a typed null value.
1019: */
1020: boolean needDVDCast = true;
1021: if (rc.isAutoincrementGenerated()) {
1022: // (com.ibm.db2j.impl... DataValueDescriptor)
1023: // this.getSetAutoincValue(column_number)
1024:
1025: userExprFun.pushThis();
1026:
1027: userExprFun.push(rc.getColumnPosition());
1028: userExprFun.push(rc.getTableColumnDescriptor()
1029: .getAutoincInc());
1030:
1031: userExprFun.callMethod(VMOpcode.INVOKEVIRTUAL,
1032: ClassName.BaseActivation,
1033: "getSetAutoincrementValue",
1034: ClassName.DataValueDescriptor, 2);
1035: needDVDCast = false;
1036:
1037: } else if (genNulls
1038: || ((rc.getExpression() instanceof ConstantNode) && ((ConstantNode) rc
1039: .getExpression()).isNull())) {
1040: userExprFun.getField(field);
1041: userExprFun.push(index + 1);
1042: userExprFun.callMethod(VMOpcode.INVOKEINTERFACE,
1043: ClassName.Row, "getColumn",
1044: ClassName.DataValueDescriptor, 1); // the express
1045:
1046: acb.generateNullWithExpress(userExprFun, rc
1047: .getTypeCompiler());
1048:
1049: } else {
1050: rc.generateExpression(acb, userExprFun);
1051: }
1052: if (needDVDCast)
1053: userExprFun.cast(ClassName.DataValueDescriptor);
1054:
1055: userExprFun.callMethod(VMOpcode.INVOKEINTERFACE,
1056: ClassName.Row, "setColumn", "void", 2);
1057: }
1058: userExprFun.getField(field);
1059: userExprFun.methodReturn();
1060:
1061: // we are now done modifying userExprFun
1062: userExprFun.complete();
1063:
1064: // what we return is the access of the field, i.e. the pointer to the method.
1065: acb.pushMethodReference(mb, userExprFun);
1066: }
1067:
1068: /**
1069: * Build an empty row with the size and shape of the ResultColumnList.
1070: *
1071: * @return an empty row of the correct size and shape.
1072: * @exception StandardException Thrown on error
1073: */
1074: public ExecRow buildEmptyRow() throws StandardException {
1075: int columnCount = size();
1076: ExecRow row = getExecutionFactory().getValueRow(columnCount);
1077: int position = 1;
1078:
1079: for (int index = 0; index < columnCount; index++) {
1080: ResultColumn rc = (ResultColumn) elementAt(index);
1081: DataTypeDescriptor dataType = rc.getTypeServices();
1082: DataValueDescriptor dataValue = dataType.getNull();
1083:
1084: row.setColumn(position++, dataValue);
1085: }
1086:
1087: return row;
1088: }
1089:
1090: /**
1091: * Build an empty index row for the given conglomerate.
1092: *
1093: * @return an empty row of the correct size and shape.
1094: * @exception StandardException Thrown on error
1095: */
1096: public ExecRow buildEmptyIndexRow(TableDescriptor td,
1097: ConglomerateDescriptor cd, StoreCostController scc,
1098: DataDictionary dd) throws StandardException {
1099: ResultColumn rc;
1100:
1101: if (SanityManager.DEBUG) {
1102: if (!cd.isIndex()) {
1103: SanityManager
1104: .THROWASSERT("ConglomerateDescriptor expected to be for index: "
1105: + cd);
1106: }
1107: }
1108:
1109: int[] baseCols = cd.getIndexDescriptor().baseColumnPositions();
1110: ExecRow row = getExecutionFactory().getValueRow(
1111: baseCols.length + 1);
1112:
1113: for (int i = 0; i < baseCols.length; i++) {
1114: ColumnDescriptor coldes = td
1115: .getColumnDescriptor(baseCols[i]);
1116: DataTypeDescriptor dataType = coldes.getType();
1117:
1118: // rc = getResultColumn(baseCols[i]);
1119: // rc = (ResultColumn) at(baseCols[i] - 1);
1120: // dataType = rc.getTypeServices();
1121: DataValueDescriptor dataValue = dataType.getNull();
1122:
1123: row.setColumn(i + 1, dataValue);
1124: }
1125:
1126: RowLocation rlTemplate = scc.newRowLocationTemplate();
1127:
1128: row.setColumn(baseCols.length + 1, rlTemplate);
1129:
1130: return row;
1131: }
1132:
1133: /**
1134: Generates a row with the size and shape of the ResultColumnList.
1135:
1136: Some structures, like FromBaseTable and DistinctNode,
1137: need to generate rowAllocator functions to get a row
1138: the size and shape of their ResultColumnList.
1139:
1140: We return the method pointer, which is a field access
1141: in the generated class.
1142:
1143: @exception StandardException
1144: */
1145: void generateHolder(ExpressionClassBuilder acb, MethodBuilder mb)
1146: throws StandardException {
1147: generateHolder(acb, mb, (FormatableBitSet) null,
1148: (FormatableBitSet) null);
1149: }
1150:
1151: /**
1152: Generates a row with the size and shape of the ResultColumnList.
1153:
1154: Some structures, like FromBaseTable and DistinctNode,
1155: need to generate rowAllocator functions to get a row
1156: the size and shape of their ResultColumnList.
1157:
1158: We return the method pointer, which is a field access
1159: in the generated class.
1160:
1161: @exception StandardException
1162: */
1163: void generateHolder(ExpressionClassBuilder acb, MethodBuilder mb,
1164: FormatableBitSet referencedCols,
1165: FormatableBitSet propagatedCols) throws StandardException {
1166:
1167: // what we return is a pointer to the method.
1168: acb.pushMethodReference(mb, generateHolderMethod(acb,
1169: referencedCols, propagatedCols));
1170: }
1171:
1172: MethodBuilder generateHolderMethod(ExpressionClassBuilder acb,
1173: FormatableBitSet referencedCols,
1174: FormatableBitSet propagatedCols) throws StandardException {
1175: int numCols;
1176: String rowAllocatorMethod;
1177: String rowAllocatorType;
1178: int highestColumnNumber = -1;
1179:
1180: if (referencedCols != null) {
1181: // Find the number of the last column referenced in the table
1182: for (int i = referencedCols.anySetBit(); i != -1; i = referencedCols
1183: .anySetBit(i)) {
1184: highestColumnNumber = i;
1185: }
1186: } else {
1187: highestColumnNumber = size() - 1;
1188: }
1189:
1190: // Within the constructor:
1191: // fieldX = getExecutionFactory().getValueRow(# cols);
1192: // The body of the new method:
1193: // {
1194: // fieldX.setColumn(1, col(1).generateColumn(ps)));
1195: // ... and so on for each column ...
1196: // return fieldX;
1197: // }
1198: // static Method exprN = method pointer to exprN;
1199:
1200: // this sets up the method and the static field
1201: MethodBuilder exprFun = acb.newExprFun();
1202:
1203: // Allocate the right type of row, depending on
1204: // whether we're scanning an index or a heap.
1205: if (indexRow) {
1206: rowAllocatorMethod = "getIndexableRow";
1207: rowAllocatorType = ClassName.ExecIndexRow;
1208: } else {
1209: rowAllocatorMethod = "getValueRow";
1210: rowAllocatorType = ClassName.ExecRow;
1211: }
1212: numCols = size();
1213:
1214: /* Declare the field */
1215: LocalField lf = acb.newFieldDeclaration(Modifier.PRIVATE,
1216: ClassName.ExecRow);
1217: // Generate the code to create the row in the constructor
1218: genCreateRow(acb, lf, rowAllocatorMethod, rowAllocatorType,
1219: highestColumnNumber + 1);
1220:
1221: // now we fill in the body of the function
1222:
1223: int colNum;
1224:
1225: // If there is a referenced column map, the first column to fill
1226: // in is the first one in the bit map - otherwise, it is
1227: // column 0.
1228: if (referencedCols != null)
1229: colNum = referencedCols.anySetBit();
1230: else
1231: colNum = 0;
1232:
1233: for (int index = 0; index < numCols; index++) {
1234: ResultColumn rc = ((ResultColumn) elementAt(index));
1235:
1236: /* Special code generation for RID since expression is CurrentRowLocationNode.
1237: * Really need yet another node type that does its own code generation.
1238: */
1239: if (rc.getExpression() instanceof CurrentRowLocationNode) {
1240: ConglomerateController cc = null;
1241: int savedItem;
1242: RowLocation rl;
1243:
1244: cc = getLanguageConnectionContext()
1245: .getTransactionCompile()
1246: .openConglomerate(
1247: conglomerateId,
1248: false,
1249: 0,
1250: TransactionController.MODE_RECORD,
1251: TransactionController.ISOLATION_READ_COMMITTED);
1252: try {
1253: rl = cc.newRowLocationTemplate();
1254: } finally {
1255: if (cc != null) {
1256: cc.close();
1257: }
1258: }
1259:
1260: savedItem = acb.addItem(rl);
1261:
1262: // get the RowLocation template
1263: exprFun.getField(lf); // instance for setColumn
1264: exprFun.push(highestColumnNumber + 1); // first arg
1265:
1266: exprFun.pushThis(); // instance for getRowLocationTemplate
1267: exprFun.push(savedItem); // first arg
1268: exprFun.callMethod(VMOpcode.INVOKEINTERFACE,
1269: ClassName.Activation, "getRowLocationTemplate",
1270: ClassName.RowLocation, 1);
1271:
1272: exprFun.upCast(ClassName.DataValueDescriptor);
1273: exprFun.callMethod(VMOpcode.INVOKEINTERFACE,
1274: ClassName.Row, "setColumn", "void", 2);
1275: continue;
1276: }
1277:
1278: /* Skip over those columns whose source is the immediate
1279: * child result set. (No need to generate a wrapper
1280: * for a SQL NULL when we are smart enough not to pass
1281: * that wrapper to the store.)
1282: * NOTE: Believe it or not, we have to check for the case
1283: * where referencedCols is not null, but no bits are set.
1284: * This can happen when we need to get all of the columns
1285: * from the heap due to a check constraint.
1286: */
1287: if (propagatedCols != null
1288: && propagatedCols.getNumBitsSet() != 0) {
1289: /* We can skip this RC if it is simply propagating
1290: * a column from the source result set.
1291: */
1292: ValueNode sourceExpr = rc.getExpression();
1293:
1294: if (sourceExpr instanceof VirtualColumnNode) {
1295: // There is a referenced columns bit set, so use
1296: // it to figure out what the next column number is.
1297: // colNum = referencedCols.anySetBit(colNum);
1298: continue;
1299: }
1300: }
1301:
1302: // generate the column space creation call
1303: // generate statements of the form
1304: // r.setColumn(columnNumber, columnShape);
1305: //
1306: // This assumes that there are no "holes" in the column positions,
1307: // and that column positions reflect the stored format/order
1308: exprFun.getField(lf); // instance
1309: exprFun.push(colNum + 1); // first arg
1310: rc.generateHolder(acb, exprFun);
1311:
1312: exprFun.callMethod(VMOpcode.INVOKEINTERFACE, ClassName.Row,
1313: "setColumn", "void", 2);
1314:
1315: // If there is a bit map of referenced columns, use it to
1316: // figure out what the next column is, otherwise just go
1317: // to the next column.
1318: if (referencedCols != null)
1319: colNum = referencedCols.anySetBit(colNum);
1320: else
1321: colNum++;
1322: }
1323:
1324: // generate:
1325: // return fieldX;
1326: // and add to the end of exprFun's body.
1327: exprFun.getField(lf);
1328: exprFun.methodReturn();
1329:
1330: // we are done putting stuff in exprFun:
1331: exprFun.complete();
1332:
1333: return exprFun;
1334: }
1335:
1336: /**
1337: * Generate the code to create an empty row in the constructor.
1338: *
1339: * @param acb The ACB.
1340: * @param field The field for the new row.
1341: * @param rowAllocatorMethod The method to call.
1342: * @param rowAllocatorType The row type.
1343: * @param numCols The number of columns in the row.
1344: *
1345: * @exception StandardException Thrown on error
1346: */
1347: private void genCreateRow(ExpressionClassBuilder acb,
1348: LocalField field, String rowAllocatorMethod,
1349: String rowAllocatorType, int numCols)
1350: throws StandardException {
1351: // Create the row in the constructor
1352: // fieldX = getExecutionFactory().getValueRow(# cols);
1353:
1354: MethodBuilder cb = acb.getConstructor();
1355:
1356: acb.pushGetExecutionFactoryExpression(cb); // instance
1357: cb.push(numCols);
1358: cb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
1359: rowAllocatorMethod, rowAllocatorType, 1);
1360: cb.setField(field);
1361: /* Increase the statement counter in constructor. Code size in
1362: * constructor can become too big (more than 64K) for Java compiler
1363: * to handle (beetle 4293). We set constant columns in other
1364: * methods if constructor has too many statements already.
1365: */
1366: cb.statementNumHitLimit(1); // ignore return value
1367: }
1368:
1369: /**
1370: * Make a ResultDescription for use in a ResultSet.
1371: * This is useful when generating/executing a NormalizeResultSet, since
1372: * it can appear anywhere in the tree.
1373: *
1374: * @return A ResultDescription for this ResultSetNode.
1375: */
1376:
1377: public ResultColumnDescriptor[] makeResultDescriptors() {
1378: ExecutionContext ec = (ExecutionContext) getContextManager()
1379: .getContext(ExecutionContext.CONTEXT_ID);
1380: return makeResultDescriptors(ec);
1381: }
1382:
1383: ResultColumnDescriptor[] makeResultDescriptors(ExecutionContext ec) {
1384: ResultColumnDescriptor colDescs[] = new ResultColumnDescriptor[size()];
1385: int size = size();
1386:
1387: for (int index = 0; index < size; index++) {
1388: // the ResultColumn nodes are descriptors, so take 'em...
1389: colDescs[index] = ec
1390: .getExecutionFactory()
1391: .getResultColumnDescriptor(
1392: ((ResultColumnDescriptor) elementAt(index)));
1393: }
1394:
1395: return colDescs;
1396: }
1397:
1398: /**
1399: * Expand any *'s in the ResultColumnList. In addition, we will guarantee that
1400: * each ResultColumn has a name. (All generated names will be unique across the
1401: * entire statement.)
1402: *
1403: *
1404: * @exception StandardException Thrown on error
1405: */
1406:
1407: public void expandAllsAndNameColumns(FromList fromList)
1408: throws StandardException {
1409: boolean expanded = false;
1410: ResultColumnList allExpansion;
1411: TableName fullTableName;
1412:
1413: /* First walk result column list looking for *'s to expand */
1414: for (int index = 0; index < size(); index++) {
1415: ResultColumn rc = (ResultColumn) elementAt(index);
1416: if (rc instanceof AllResultColumn) {
1417: expanded = true;
1418:
1419: //fullTableName = ((AllResultColumn) rc).getFullTableName();
1420: TableName temp = rc.getTableNameObject();
1421: if (temp != null) {
1422: String sName = temp.getSchemaName();
1423: String tName = temp.getTableName();
1424: fullTableName = makeTableName(sName, tName);
1425: } else
1426: fullTableName = null;
1427: allExpansion = fromList.expandAll(fullTableName);
1428:
1429: /* Make sure that every column has a name */
1430: allExpansion.nameAllResultColumns();
1431:
1432: /* Make sure that every RC and expression is marked as being in
1433: * the SELECT list.
1434: */
1435: allExpansion.setClause(ValueNode.IN_SELECT_LIST);
1436:
1437: /* Replace the AllResultColumn with the expanded list.
1438: * We will update the VirtualColumnIds once below.
1439: */
1440: removeElementAt(index);
1441: for (int inner = 0; inner < allExpansion.size(); inner++) {
1442: insertElementAt(allExpansion.elementAt(inner),
1443: index + inner);
1444: }
1445:
1446: // If the rc was a "*", we need to set the initial list size
1447: // to the number of columns that are actually returned to
1448: // the user.
1449: markInitialSize();
1450: } else {
1451: /* Make sure that every column has a name */
1452: rc.guaranteeColumnName();
1453: }
1454: }
1455:
1456: /* Go back and update the VirtualColumnIds if we expanded any *'s */
1457: if (expanded) {
1458: int size = size();
1459:
1460: for (int index = 0; index < size; index++) {
1461: /* Vectors are 0-based, VirtualColumnIds are 1-based. */
1462: ((ResultColumn) elementAt(index))
1463: .setVirtualColumnId(index + 1);
1464: }
1465: }
1466: }
1467:
1468: /**
1469: * Generate (unique across the entire statement) column names for those
1470: * ResultColumns in this list which are not named.
1471: *
1472: * @exception StandardException Thrown on error
1473: */
1474: public void nameAllResultColumns() throws StandardException {
1475: int size = size();
1476:
1477: for (int index = 0; index < size; index++) {
1478: ResultColumn resultColumn = (ResultColumn) elementAt(index);
1479:
1480: resultColumn.guaranteeColumnName();
1481: }
1482: }
1483:
1484: /**
1485: * Copy the types and lengths for this RCL (the target)
1486: * to another RCL (the source).
1487: * This is useful when adding a NormalizeResultSetNode.
1488: *
1489: * @param sourceRCL The source RCL
1490: */
1491: public void copyTypesAndLengthsToSource(ResultColumnList sourceRCL)
1492: throws StandardException {
1493: /* Source and target can have different lengths. */
1494: int size = (size() > sourceRCL.size()) ? size() : sourceRCL
1495: .size();
1496: for (int index = 0; index < size; index++) {
1497: ResultColumn sourceRC = (ResultColumn) sourceRCL
1498: .elementAt(index);
1499: ResultColumn resultColumn = (ResultColumn) elementAt(index);
1500: sourceRC.setType(resultColumn.getTypeServices());
1501: sourceRC.getExpression().setType(
1502: resultColumn.getTypeServices());
1503: }
1504: }
1505:
1506: /*
1507: ** Check whether the column lengths and types of the result columns
1508: ** match the expressions under those columns. This is useful for
1509: ** INSERT and UPDATE statements. For SELECT statements this method
1510: ** should always return true. There is no need to call this for a
1511: ** DELETE statement.
1512: ** NOTE: We skip over generated columns since they won't have a
1513: ** column descriptor.
1514: **
1515: ** @return true means all the columns match their expressions,
1516: ** false means at least one column does not match its
1517: ** expression
1518: */
1519:
1520: boolean columnTypesAndLengthsMatch() throws StandardException {
1521: int size = size();
1522:
1523: for (int index = 0; index < size; index++) {
1524: ResultColumn resultColumn = (ResultColumn) elementAt(index);
1525:
1526: /* Skip over generated columns */
1527: if (resultColumn.isGenerated()) {
1528: continue;
1529: }
1530:
1531: if (!resultColumn.columnTypeAndLengthMatch())
1532: return false;
1533: }
1534:
1535: return true;
1536: }
1537:
1538: boolean columnTypesAndLengthsMatch(ResultColumnList otherRCL)
1539: throws StandardException {
1540: boolean retval = true;
1541:
1542: /* We check every RC, even after finding 1 that requires
1543: * normalization, because the conversion of constants to
1544: * the appropriate type occurs under this loop.
1545: */
1546: int size = size();
1547: for (int index = 0; index < size; index++) {
1548: ResultColumn resultColumn = (ResultColumn) elementAt(index);
1549:
1550: ResultColumn otherResultColumn = (ResultColumn) otherRCL
1551: .elementAt(index);
1552:
1553: /* Skip over generated columns */
1554: if (resultColumn.isGenerated()
1555: || otherResultColumn.isGenerated()) {
1556: continue;
1557: }
1558:
1559: if (!resultColumn
1560: .columnTypeAndLengthMatch(otherResultColumn)) {
1561: retval = false;
1562: }
1563: }
1564:
1565: return retval;
1566: }
1567:
1568: /**
1569: * Determine whether this RCL is a No-Op projection of the given RCL.
1570: * It only makes sense to do this if the given RCL is from the child
1571: * result set of the ProjectRestrict that this RCL is from.
1572: *
1573: * @param childRCL The ResultColumnList of the child result set.
1574: *
1575: * @return true if this RCL is a No-Op projection of the given RCL.
1576: */
1577: public boolean nopProjection(ResultColumnList childRCL) {
1578: /*
1579: ** This RCL is a useless projection if each column in the child
1580: ** if the same as the column in this RCL. This is impossible
1581: ** if the two RCLs have different numbers of columns.
1582: */
1583: if (this .size() != childRCL.size()) {
1584: return false;
1585: }
1586:
1587: /*
1588: ** The two lists have the same numbers of elements. Are the lists
1589: ** identical? In other words, is the expression in every ResultColumn
1590: ** in the PRN's RCL a ColumnReference that points to the corresponding
1591: ** column in the child?
1592: */
1593: int size = size();
1594: for (int index = 0; index < size; index++) {
1595: ResultColumn this Column = (ResultColumn) elementAt(index);
1596: ResultColumn referencedColumn = null;
1597:
1598: /*
1599: ** A No-Op projection can point to a VirtualColumnNode or a
1600: ** ColumnReference.
1601: */
1602: if (this Column.getExpression() instanceof VirtualColumnNode) {
1603: referencedColumn = ((VirtualColumnNode) (this Column
1604: .getExpression())).getSourceColumn();
1605: } else if (this Column.getExpression() instanceof ColumnReference) {
1606: referencedColumn = ((ColumnReference) (this Column
1607: .getExpression())).getSource();
1608: } else {
1609: return false;
1610: }
1611:
1612: ResultColumn childColumn = (ResultColumn) childRCL
1613: .elementAt(index);
1614:
1615: if (referencedColumn != childColumn) {
1616: return false;
1617: }
1618: }
1619:
1620: return true;
1621: }
1622:
1623: /**
1624: * Create a shallow copy of a ResultColumnList and its ResultColumns.
1625: * (All other pointers are preserved.)
1626: * Useful for building new ResultSetNodes during preprocessing.
1627: *
1628: * @return None.
1629: *
1630: * @exception StandardException Thrown on error
1631: */
1632: public ResultColumnList copyListAndObjects()
1633: throws StandardException {
1634: ResultColumn newResultColumn;
1635: ResultColumn origResultColumn;
1636: ResultColumnList newList;
1637:
1638: /* Create the new ResultColumnList */
1639: newList = (ResultColumnList) getNodeFactory().getNode(
1640: C_NodeTypes.RESULT_COLUMN_LIST, getContextManager());
1641:
1642: /* Walk the current list - for each ResultColumn in the list, make a copy
1643: * and add it to the new list.
1644: */
1645: int size = size();
1646:
1647: for (int index = 0; index < size; index++) {
1648: origResultColumn = (ResultColumn) elementAt(index);
1649:
1650: newResultColumn = origResultColumn.cloneMe();
1651:
1652: newList.addResultColumn(newResultColumn);
1653: }
1654:
1655: return newList;
1656: }
1657:
1658: /**
1659: * Walk the list and replace ResultColumn.expression with a new
1660: * VirtualColumnNode. This is useful when propagating a ResultColumnList
1661: * up the query tree.
1662: * NOTE: This flavor marks all of the underlying RCs as referenced.
1663: *
1664: * @param sourceResultSet ResultSetNode that is source of value
1665: *
1666: * @exception StandardException Thrown on error
1667: */
1668: public void genVirtualColumnNodes(ResultSetNode sourceResultSet,
1669: ResultColumnList sourceResultColumnList)
1670: throws StandardException {
1671: genVirtualColumnNodes(sourceResultSet, sourceResultColumnList,
1672: true);
1673: }
1674:
1675: /**
1676: * Walk the list and replace ResultColumn.expression with a new
1677: * VirtualColumnNode. This is useful when propagating a ResultColumnList
1678: * up the query tree.
1679: *
1680: * @param sourceResultSet ResultSetNode that is source of value
1681: * @param markReferenced Whether or not to mark the underlying RCs
1682: * as referenced
1683: *
1684: * @exception StandardException Thrown on error
1685: */
1686: public void genVirtualColumnNodes(ResultSetNode sourceResultSet,
1687: ResultColumnList sourceResultColumnList,
1688: boolean markReferenced) throws StandardException {
1689: int size = size();
1690:
1691: for (int index = 0; index < size; index++) {
1692: ResultColumn resultColumn = (ResultColumn) elementAt(index);
1693:
1694: /* dts = resultColumn.getExpression().getTypeServices(); */
1695: DataTypeDescriptor dts = resultColumn.getTypeServices();
1696:
1697: /* Vectors are 0-based, VirtualColumnIds are 1-based */
1698: resultColumn.expression = (ValueNode) getNodeFactory()
1699: .getNode(C_NodeTypes.VIRTUAL_COLUMN_NODE,
1700: sourceResultSet,
1701: sourceResultColumnList.elementAt(index),
1702: ReuseFactory.getInteger(index + 1),
1703: getContextManager());
1704:
1705: /* Mark the ResultColumn as being referenced */
1706: if (markReferenced) {
1707: resultColumn.setReferenced();
1708: }
1709: }
1710: }
1711:
1712: /**
1713: * Walk the list and adjust the virtualColumnIds in the ResultColumns
1714: * by the specified amount. If ResultColumn.expression is a VirtualColumnNode,
1715: * then we adjust the columnId there as well.
1716: *
1717: * @param adjust The size of the increment.
1718: */
1719: public void adjustVirtualColumnIds(int adjust) {
1720: int size = size();
1721:
1722: for (int index = 0; index < size; index++) {
1723: ResultColumn resultColumn = (ResultColumn) elementAt(index);
1724: resultColumn.adjustVirtualColumnId(adjust);
1725: if (SanityManager.DEBUG) {
1726: if (!(resultColumn.getExpression() instanceof VirtualColumnNode)) {
1727: SanityManager
1728: .THROWASSERT("resultColumn.getExpression() is expected to be "
1729: + "instanceof VirtualColumnNode"
1730: + " not "
1731: + resultColumn.getExpression()
1732: .getClass().getName());
1733: }
1734: }
1735:
1736: ((VirtualColumnNode) resultColumn.getExpression()).columnId += adjust;
1737: }
1738: }
1739:
1740: /**
1741: * Project out any unreferenced ResultColumns from the list and
1742: * reset the virtual column ids in the referenced ResultColumns.
1743: * If all ResultColumns are projected out, then the list is not empty.
1744: *
1745: * @exception StandardException Thrown on error
1746: */
1747: public void doProjection() throws StandardException {
1748: int numDeleted = 0;
1749: int size = size();
1750: ResultColumnList deletedRCL = new ResultColumnList();
1751: for (int index = 0; index < size; index++) {
1752: ResultColumn resultColumn = (ResultColumn) elementAt(index);
1753:
1754: /* RC's for FromBaseTables are marked as referenced during binding.
1755: * For other nodes, namely JoinNodes, we need to go 1 level
1756: * down the RC/VCN chain to see if the RC is referenced. This is
1757: * because we propagate the referencing info from the bottom up.
1758: */
1759: if ((!resultColumn.isReferenced())
1760: && (resultColumn.getExpression() instanceof VirtualColumnNode)
1761: && !(((VirtualColumnNode) resultColumn
1762: .getExpression()).getSourceColumn()
1763: .isReferenced())) {
1764: // Remember the RC to delete when done
1765: deletedRCL.addElement(resultColumn);
1766:
1767: /* Remember how many we have deleted and decrement the
1768: * VirtualColumnIds for all nodes which appear after us
1769: * in the list.
1770: */
1771: numDeleted++;
1772: } else {
1773: /* Decrement the VirtualColumnId for each node in the list
1774: * after the 1st deleted one.
1775: */
1776: if (numDeleted >= 1)
1777: resultColumn.adjustVirtualColumnId(-numDeleted);
1778: /* Make sure that the RC is marked as referenced! */
1779: resultColumn.setReferenced();
1780: }
1781: }
1782:
1783: // Go back and delete the RCs to be delete from the list
1784: for (int index = 0; index < deletedRCL.size(); index++) {
1785: removeElement((ResultColumn) deletedRCL.elementAt(index));
1786: }
1787: }
1788:
1789: /**
1790: * Check the uniqueness of the column names within a column list.
1791: *
1792: * @param errForGenCols Raise an error for any generated column names.
1793: *
1794: * @return String The first duplicate column name, if any.
1795: */
1796: public String verifyUniqueNames(boolean errForGenCols)
1797: throws StandardException {
1798: int size = size();
1799: Hashtable ht = new Hashtable(size + 2, (float) .999);
1800: ResultColumn rc;
1801:
1802: for (int index = 0; index < size; index++) {
1803: rc = (ResultColumn) elementAt(index);
1804: if (errForGenCols && rc.isNameGenerated())
1805: throw StandardException
1806: .newException(SQLState.LANG_DB2_VIEW_REQUIRES_COLUMN_NAMES);
1807: /* Verify that this column's name is unique within the list */
1808: String colName = ((ResultColumn) elementAt(index))
1809: .getName();
1810:
1811: Object object = ht.put(colName, colName);
1812:
1813: if (object != null && ((String) object).equals(colName)) {
1814: return colName;
1815: }
1816: }
1817:
1818: /* No duplicate column names */
1819: return null;
1820: }
1821:
1822: /**
1823: * Validate the derived column list (DCL) and propagate the info
1824: * from the list to the final ResultColumnList.
1825: *
1826: * @param derivedRCL The derived column list
1827: * @param tableName The table name for the FromTable
1828: *
1829: * @exception StandardException Thrown on error
1830: */
1831: public void propagateDCLInfo(ResultColumnList derivedRCL,
1832: String tableName) throws StandardException {
1833: String duplicateColName;
1834:
1835: /* Do both lists, if supplied by user, have the same degree? */
1836: if (derivedRCL.size() != size()
1837: && !derivedRCL.getCountMismatchAllowed()) {
1838: throw StandardException.newException(
1839: SQLState.LANG_DERIVED_COLUMN_LIST_MISMATCH,
1840: tableName);
1841: }
1842:
1843: /* Check the uniqueness of the column names within the derived list */
1844: duplicateColName = derivedRCL.verifyUniqueNames(false);
1845: if (duplicateColName != null) {
1846: throw StandardException.newException(
1847: SQLState.LANG_DUPLICATE_COLUMN_NAME_DERIVED,
1848: duplicateColName);
1849: }
1850:
1851: /* We can finally copy the derived names into the final list */
1852: copyResultColumnNames(derivedRCL);
1853: }
1854:
1855: /**
1856: * Look for and reject ? parameters under ResultColumns. This is done for
1857: * SELECT statements.
1858: *
1859: * @exception StandardException Thrown if a ? parameter found directly
1860: * under a ResultColumn
1861: */
1862:
1863: void rejectParameters() throws StandardException {
1864: int size = size();
1865:
1866: for (int index = 0; index < size; index++) {
1867: ResultColumn rc = (ResultColumn) elementAt(index);
1868: rc.rejectParameter();
1869: }
1870: }
1871:
1872: /**
1873: * Check for (and reject) XML values directly under the ResultColumns.
1874: * This is done for SELECT/VALUES statements. We reject values
1875: * in this case because JDBC does not define an XML type/binding
1876: * and thus there's no standard way to pass such a type back
1877: * to a JDBC application.
1878: *
1879: * Note that we DO allow an XML column in a top-level RCL
1880: * IF that column was added to the RCL by _us_ instead of
1881: * by the user. For example, if we have a table:
1882: *
1883: * create table t1 (i int, x xml)
1884: *
1885: * and the user query is:
1886: *
1887: * select i from t1 order by x
1888: *
1889: * the "x" column will be added (internally) to the RCL
1890: * as part of ORDER BY processing--and so we need to
1891: * allow that XML column to be bound without throwing
1892: * an error. If, as in this case, the XML column reference
1893: * is invalid (we can't use ORDER BY on an XML column because
1894: * XML values aren't ordered), a more appropriate error
1895: * message should be returned to the user in later processing.
1896: * If we didn't allow for this, the user would get an
1897: * error saying that XML columns are not valid as part
1898: * of the result set--but as far as s/he knows, there
1899: * isn't such a column: only "i" is supposed to be returned
1900: * (the RC for "x" was added to the RCL by _us_ as part of
1901: * ORDER BY processing).
1902: *
1903: * ASSUMPTION: Any RCs that are generated internally and
1904: * added to this RCL (before this RCL is bound) are added
1905: * at the _end_ of the list. If that's true, then any
1906: * RC with an index greater than the size of the initial
1907: * (user-specified) list must have been added internally
1908: * and will not be returned to the user.
1909: *
1910: * @exception StandardException Thrown if an XML value found
1911: * directly under a ResultColumn
1912: */
1913: void rejectXMLValues() throws StandardException {
1914: int sz = size();
1915: ResultColumn rc = null;
1916: for (int i = 1; i <= sz; i++) {
1917:
1918: if (i > initialListSize)
1919: // this RC was generated internally and will not
1920: // be returned to the user, so don't throw error.
1921: continue;
1922:
1923: rc = getResultColumn(i);
1924: if ((rc != null) && (rc.getType() != null)
1925: && rc.getType().getTypeId().isXMLTypeId()) { // Disallow it.
1926: throw StandardException
1927: .newException(SQLState.LANG_ATTEMPT_TO_SELECT_XML);
1928: }
1929:
1930: }
1931: }
1932:
1933: /**
1934: * Set the resultSetNumber in all of the ResultColumns.
1935: *
1936: * @param resultSetNumber The resultSetNumber
1937: */
1938: public void setResultSetNumber(int resultSetNumber) {
1939: int size = size();
1940:
1941: for (int index = 0; index < size; index++) {
1942: ((ResultColumn) elementAt(index))
1943: .setResultSetNumber(resultSetNumber);
1944: }
1945: }
1946:
1947: /**
1948: * Mark all of the ResultColumns as redundant.
1949: * Useful when chopping a ResultSetNode out of a tree when there are
1950: * still references to its RCL.
1951: */
1952: public void setRedundant() {
1953: int size = size();
1954:
1955: for (int index = 0; index < size; index++) {
1956: ((ResultColumn) elementAt(index)).setRedundant();
1957: }
1958: }
1959:
1960: /**
1961: * Verify that all of the columns in the SET clause of a positioned update
1962: * appear in the cursor's FOR UPDATE OF list.
1963: *
1964: * @param ucl The cursor's FOR UPDATE OF list. (May be null.)
1965: * @param cursorName The cursor's name.
1966: *
1967: * @exception StandardException Thrown on error
1968: */
1969: public void checkColumnUpdateability(String[] ucl, String cursorName)
1970: throws StandardException {
1971: int size = size();
1972:
1973: for (int index = 0; index < size; index++) {
1974: ResultColumn resultColumn = (ResultColumn) elementAt(index);
1975:
1976: if (resultColumn.updated()
1977: && !resultColumn.foundInList(ucl)) {
1978: throw StandardException.newException(
1979: SQLState.LANG_COLUMN_NOT_UPDATABLE_IN_CURSOR,
1980: resultColumn.getName(), cursorName);
1981: }
1982: }
1983: }
1984:
1985: /**
1986: * Set up the result expressions for a UNION, INTERSECT, or EXCEPT:
1987: * o Verify union type compatiblity
1988: * o Get dominant type for result (type + max length + nullability)
1989: * o Create a new ColumnReference with dominant type and name of from this
1990: * RCL and make that the new expression.
1991: * o Set the type info for in the ResultColumn to the dominant type
1992: *
1993: * NOTE - We are assuming that caller has generated a new RCL for the UNION
1994: * with the same names as the left side's RCL and copies of the expressions.
1995: *
1996: * @param otherRCL RCL from other side of the UNION.
1997: * @param tableNumber The tableNumber for the UNION.
1998: * @param level The nesting level for the UNION.
1999: * @param operatorName "UNION", "INTERSECT", or "EXCEPT"
2000: *
2001: * @exception StandardException Thrown on error
2002: */
2003: public void setUnionResultExpression(ResultColumnList otherRCL,
2004: int tableNumber, int level, String operatorName)
2005: throws StandardException {
2006: TableName dummyTN;
2007:
2008: if (SanityManager.DEBUG) {
2009: if (size() != otherRCL.size()) {
2010: SanityManager.THROWASSERT("size() = (" + size()
2011: + ") is expected to equal otherRCL.size ("
2012: + otherRCL.size() + ")");
2013: }
2014: }
2015:
2016: /* Make a dummy TableName to be shared by all new CRs */
2017: dummyTN = (TableName) getNodeFactory()
2018: .getNode(C_NodeTypes.TABLE_NAME, null, null,
2019: getContextManager());
2020:
2021: ContextManager cm = getContextManager();
2022:
2023: int size = size();
2024: for (int index = 0; index < size; index++) {
2025: boolean nullableResult;
2026: ColumnReference newCR;
2027: ResultColumn this RC = (ResultColumn) elementAt(index);
2028: ResultColumn otherRC = (ResultColumn) otherRCL
2029: .elementAt(index);
2030: ValueNode this Expr = this RC.getExpression();
2031: ValueNode otherExpr = otherRC.getExpression();
2032:
2033: // If there is one row that is not 'autoincrement', the Union should
2034: // not be 'autoincrement'.
2035: if (!otherRC.isAutoincrementGenerated()
2036: && this RC.isAutoincrementGenerated()) {
2037: this RC.resetAutoincrementGenerated();
2038: }
2039: /*
2040: ** If there are ? parameters in the ResultColumnList of a row
2041: ** in a table constructor, their types will not be set. Just skip
2042: ** these - their types will be set later. Each ? parameter will
2043: ** get the type of the first non-? in its column, so it can't
2044: ** affect the final dominant type. It's possible that all the
2045: ** rows for a particular column will have ? parameters - this is
2046: ** an error condition that will be caught later.
2047: */
2048: TypeId this TypeId = this Expr.getTypeId();
2049: if (this TypeId == null)
2050: continue;
2051:
2052: TypeId otherTypeId = otherExpr.getTypeId();
2053: if (otherTypeId == null)
2054: continue;
2055:
2056: /*
2057: ** Check type compatability. We want to make sure that
2058: ** the types are assignable in either direction
2059: ** and they are comparable.
2060: */
2061: ClassFactory cf = getClassFactory();
2062: if (!this Expr.getTypeCompiler().storable(otherTypeId, cf)
2063: && !otherExpr.getTypeCompiler().storable(
2064: this TypeId, cf)) {
2065: throw StandardException.newException(
2066: SQLState.LANG_NOT_UNION_COMPATIBLE, this TypeId
2067: .getSQLTypeName(), otherTypeId
2068: .getSQLTypeName(), operatorName);
2069: }
2070:
2071: DataTypeDescriptor resultType = this Expr.getTypeServices()
2072: .getDominantType(otherExpr.getTypeServices(), cf);
2073:
2074: newCR = (ColumnReference) getNodeFactory().getNode(
2075: C_NodeTypes.COLUMN_REFERENCE, this RC.getName(),
2076: dummyTN, getContextManager());
2077: newCR.setType(resultType);
2078: /* Set the tableNumber and nesting levels in newCR.
2079: * If thisExpr is not a CR, then newCR cannot be
2080: * correlated, hence source and nesting levels are
2081: * the same.
2082: */
2083: if (this Expr instanceof ColumnReference) {
2084: newCR.copyFields((ColumnReference) this Expr);
2085: } else {
2086: newCR.setNestingLevel(level);
2087: newCR.setSourceLevel(level);
2088: }
2089: newCR.setTableNumber(tableNumber);
2090: this RC.setExpression(newCR);
2091: this RC.setType(this RC.getTypeServices().getDominantType(
2092: otherRC.getTypeServices(), cf));
2093:
2094: /* DB2 requires both sides of union to have same name for the result to
2095: * have that name. Otherwise, leave it or set it to a generated name */
2096: if (this RC.getName() != null && !this RC.isNameGenerated()
2097: && otherRC.getName() != null) {
2098: /* Result name needs to be changed */
2099: if (otherRC.isNameGenerated()) {
2100: this RC.setName(otherRC.getName());
2101: this RC.setNameGenerated(true);
2102: } else if (!this RC.getName().equals(otherRC.getName())) {
2103: /* Both sides have user specified names that don't match */
2104: this RC.setName(null);
2105: this RC.guaranteeColumnName();
2106: this RC.setNameGenerated(true);
2107: }
2108: }
2109: }
2110: }
2111:
2112: /**
2113: * If the resultset is a UnionNode (e.g., multi-rows in VALUES clause), we recursively call itself.
2114: * checkAutoincrement() will set ColumnDescriptor for autoincrement columns.
2115: * This way, all ColumnDescriptor of all rows will be set properly.
2116: */
2117: public void checkAutoincrementUnion(ResultSetNode rs)
2118: throws StandardException {
2119: ResultSetNode lrs = ((TableOperatorNode) rs).getLeftResultSet();
2120: ResultSetNode rrs = ((TableOperatorNode) rs)
2121: .getRightResultSet();
2122:
2123: if (lrs instanceof UnionNode) {
2124: this .checkAutoincrementUnion(lrs);
2125: } else {
2126: this .checkAutoincrement(lrs.getResultColumns());
2127: }
2128:
2129: if (rrs instanceof UnionNode) {
2130: this .checkAutoincrementUnion(rrs);
2131: } else {
2132: this .checkAutoincrement(rrs.getResultColumns());
2133: }
2134: }
2135:
2136: /**
2137: * Do the 2 RCLs have the same type & length.
2138: * This is useful for UNIONs when deciding whether a NormalizeResultSet is required.
2139: *
2140: * @param otherRCL The other RCL.
2141: *
2142: * @return boolean Whether or not there is an exact UNION type match on the 2 RCLs.
2143: */
2144: public boolean isExactTypeAndLengthMatch(ResultColumnList otherRCL)
2145: throws StandardException {
2146: int size = size();
2147: for (int index = 0; index < size; index++) {
2148: ResultColumn this RC = (ResultColumn) elementAt(index);
2149: ResultColumn otherRC = (ResultColumn) otherRCL
2150: .elementAt(index);
2151:
2152: if (!this RC.getTypeServices().isExactTypeAndLengthMatch(
2153: otherRC.getTypeServices())) {
2154: return false;
2155: }
2156: }
2157:
2158: return true;
2159: }
2160:
2161: /**
2162: * Does the column list contain any of the given column positions
2163: * that are updated? Implements same named routine in UpdateList.
2164: *
2165: * @param columns An array of column positions
2166: *
2167: * @return True if this column list contains any of the given columns
2168: */
2169: public boolean updateOverlaps(int[] columns) {
2170: int size = size();
2171:
2172: for (int index = 0; index < size; index++) {
2173: ResultColumn rc = (ResultColumn) elementAt(index);
2174:
2175: if (!rc.updated())
2176: continue;
2177:
2178: int column = rc.getColumnPosition();
2179:
2180: for (int i = 0; i < columns.length; i++) {
2181: if (columns[i] == column)
2182: return true;
2183: }
2184: }
2185:
2186: return false;
2187: }
2188:
2189: /**
2190: * Return an array that contains references to the columns in this list
2191: * sorted by position.
2192: *
2193: * @return The sorted array.
2194: */
2195: ResultColumn[] getSortedByPosition() {
2196: int size = size();
2197: ResultColumn[] result;
2198:
2199: /*
2200: ** Form an array of the original ResultColumns
2201: */
2202: result = new ResultColumn[size];
2203:
2204: /*
2205: ** Put the ResultColumns in the array
2206: */
2207: for (int index = 0; index < size; index++) {
2208: result[index] = (ResultColumn) elementAt(index);
2209: }
2210:
2211: /*
2212: ** Sort the array by column position
2213: */
2214: java.util.Arrays.sort(result);
2215: return result;
2216: }
2217:
2218: /**
2219: * Return an array of all my column positions, sorted in
2220: * ascending order.
2221: *
2222: * @return a sorted array
2223: */
2224: public int[] sortMe() {
2225: ResultColumn[] sortedResultColumns = getSortedByPosition();
2226: int[] sortedColumnIds = new int[sortedResultColumns.length];
2227: for (int ix = 0; ix < sortedResultColumns.length; ix++) {
2228: sortedColumnIds[ix] = sortedResultColumns[ix]
2229: .getColumnPosition();
2230: }
2231: return sortedColumnIds;
2232: }
2233:
2234: /**
2235: * Expand this ResultColumnList by adding all columns from the given
2236: * table that are not in this list. The result is sorted by column
2237: * position.
2238: *
2239: * @param td The TableDescriptor for the table in question
2240: * @param tableName The name of the table as given in the query
2241: *
2242: * @return A new ResultColumnList expanded to include all columns in
2243: * the given table.
2244: *
2245: * @exception StandardException Thrown on error
2246: */
2247: public ResultColumnList expandToAll(TableDescriptor td,
2248: TableName tableName) throws StandardException {
2249: ResultColumn rc;
2250: ColumnDescriptor cd;
2251: ResultColumnList retval;
2252: ResultColumn[] originalRCS;
2253: int posn;
2254:
2255: /* Get a new ResultColumnList */
2256: retval = (ResultColumnList) getNodeFactory().getNode(
2257: C_NodeTypes.RESULT_COLUMN_LIST, getContextManager());
2258:
2259: /*
2260: ** Form a sorted array of the ResultColumns
2261: */
2262: originalRCS = getSortedByPosition();
2263:
2264: posn = 0;
2265:
2266: /* Iterate through the ColumnDescriptors for the given table */
2267: ColumnDescriptorList cdl = td.getColumnDescriptorList();
2268: int cdlSize = cdl.size();
2269:
2270: for (int index = 0; index < cdlSize; index++) {
2271: cd = (ColumnDescriptor) cdl.elementAt(index);
2272:
2273: if ((posn < originalRCS.length)
2274: && (cd.getPosition() == originalRCS[posn]
2275: .getColumnPosition())) {
2276: rc = originalRCS[posn];
2277: posn++;
2278: } else {
2279: /* Build a ResultColumn/ColumnReference pair for the column */
2280: rc = makeColumnReferenceFromName(tableName, cd
2281: .getColumnName());
2282:
2283: /* Bind the new ResultColumn */
2284: rc.bindResultColumnByPosition(td, cd.getPosition());
2285: }
2286:
2287: /* Add the ResultColumn to the list */
2288: retval.addResultColumn(rc);
2289: }
2290:
2291: if (SanityManager.DEBUG)
2292: SanityManager
2293: .ASSERT(posn == originalRCS.length,
2294: "ResultColumns in original list not added to expanded ResultColumnList");
2295:
2296: return retval;
2297: }
2298:
2299: /**
2300: * Bind any untyped null nodes to the types in the given ResultColumnList.
2301: * Nodes that don't know their type may pass down nulls to
2302: * children nodes. In the case of something like a union, it knows
2303: * to try its right and left result sets against each other.
2304: * But if a null reaches us, it means we have a null type that
2305: * we don't know how to handle.
2306: *
2307: * @param bindingRCL The ResultColumnList with the types to bind to.
2308: *
2309: * @exception StandardException Thrown on error
2310: */
2311: public void bindUntypedNullsToResultColumns(
2312: ResultColumnList bindingRCL) throws StandardException {
2313: if (bindingRCL == null) {
2314: throw StandardException
2315: .newException(SQLState.LANG_NULL_IN_VALUES_CLAUSE);
2316: }
2317:
2318: if (SanityManager.DEBUG)
2319: SanityManager
2320: .ASSERT(bindingRCL.size() >= this .size(),
2321: "More columns in result column list than in base table");
2322:
2323: int size = size();
2324: for (int index = 0; index < size; index++) {
2325: ResultColumn bindingRC = (ResultColumn) bindingRCL
2326: .elementAt(index);
2327: ResultColumn this RC = (ResultColumn) elementAt(index);
2328:
2329: this RC.typeUntypedNullExpression(bindingRC);
2330: }
2331: }
2332:
2333: /**
2334: * Mark all the columns in this list as updated by an update statement.
2335: */
2336: void markUpdated() {
2337: int size = size();
2338:
2339: for (int index = 0; index < size; index++) {
2340: ((ResultColumn) elementAt(index)).markUpdated();
2341: }
2342: }
2343:
2344: /**
2345: * Mark all the (base) columns in this list as updatable by a positioned update
2346: * statement. This is necessary
2347: * for positioned update statements, because we expand the column list
2348: * to include all the columns in the base table, and we need to be able
2349: * to tell which ones the user is really trying to update so we can
2350: * determine correctly whether all the updated columns are in the
2351: * "for update" list.
2352: */
2353: void markUpdatableByCursor() {
2354: int size = size();
2355:
2356: for (int index = 0; index < size; index++) {
2357: //determine if the column is a base column and not a derived column
2358: if (((ResultColumn) elementAt(index)).getSourceTableName() != null)
2359: ((ResultColumn) elementAt(index))
2360: .markUpdatableByCursor();
2361: }
2362: }
2363:
2364: /**
2365: * @see QueryTreeNode#disablePrivilegeCollection
2366: */
2367: public void disablePrivilegeCollection() {
2368: super .disablePrivilegeCollection();
2369:
2370: int size = size();
2371: for (int index = 0; index < size; index++)
2372: ((ResultColumn) elementAt(index))
2373: .disablePrivilegeCollection();
2374: }
2375:
2376: /**
2377: * Verify that all of the column names in this list are contained
2378: * within the ColumnDefinitionNodes within the TableElementList.
2379: *
2380: *
2381: * @return String The 1st column name, if any, that is not in the list.
2382: */
2383: public String verifyCreateConstraintColumnList(TableElementList tel) {
2384: int size = size();
2385:
2386: for (int index = 0; index < size; index++) {
2387: String colName = ((ResultColumn) elementAt(index))
2388: .getName();
2389:
2390: if (!tel.containsColumnName(colName)) {
2391: return colName;
2392: }
2393: }
2394: return null;
2395: }
2396:
2397: /**
2398: * Export the result column names to the passed in String[].
2399: *
2400: * @param columnNames String[] to hold the column names.
2401: */
2402: public void exportNames(String[] columnNames) {
2403: if (SanityManager.DEBUG) {
2404: if (size() != columnNames.length) {
2405: SanityManager.THROWASSERT("size() (" + size()
2406: + ") is expected to equal columnNames.length ("
2407: + columnNames.length + ")");
2408: }
2409: }
2410:
2411: int size = size();
2412:
2413: for (int index = 0; index < size; index++) {
2414: columnNames[index] = ((ResultColumn) elementAt(index))
2415: .getName();
2416: }
2417: }
2418:
2419: /**
2420: * Count the number of RCs with simple ColumnReferences.
2421: * (RC.expression instanceof ColumnReference) This is useful
2422: * for ensuring that the number of columns in the group by list
2423: * equals the number of grouping columns.
2424: *
2425: * @return int The number of simple ColumnReferences.
2426: */
2427: public int countNumberOfSimpleColumnReferences() {
2428: int numCRs = 0;
2429:
2430: int size = size();
2431:
2432: for (int index = 0; index < size; index++) {
2433: if (((ResultColumn) elementAt(index)).getExpression() instanceof ColumnReference) {
2434: numCRs++;
2435: }
2436: }
2437:
2438: return numCRs;
2439: }
2440:
2441: /**
2442: * Given a ResultColumn at the next deepest level in the tree,
2443: * search this RCL for its parent ResultColumn.
2444: *
2445: * @param childRC The child ResultColumn
2446: *
2447: * @return ResultColumn The parent ResultColumn
2448: */
2449: public ResultColumn findParentResultColumn(ResultColumn childRC) {
2450: ResultColumn parentRC = null;
2451:
2452: int size = size();
2453:
2454: for (int index = 0; index < size; index++) {
2455: ResultColumn rc = (ResultColumn) elementAt(index);
2456:
2457: if (rc.getExpression() instanceof ColumnReference) {
2458: ColumnReference cr = (ColumnReference) rc
2459: .getExpression();
2460:
2461: if (cr.getSource() == childRC) {
2462: parentRC = rc;
2463: break;
2464: }
2465: } else if (rc.getExpression() instanceof VirtualColumnNode) {
2466: VirtualColumnNode vcn = (VirtualColumnNode) rc
2467: .getExpression();
2468:
2469: if (vcn.getSourceColumn() == childRC) {
2470: parentRC = rc;
2471: break;
2472: }
2473: }
2474:
2475: }
2476:
2477: return parentRC;
2478: }
2479:
2480: public void setClause(int clause) {
2481: int size = size();
2482:
2483: for (int index = 0; index < size; index++) {
2484: ResultColumn rc = (ResultColumn) elementAt(index);
2485: rc.setClause(clause);
2486: }
2487: }
2488:
2489: /**
2490: * Mark as updatable all the columns in this result column list
2491: * that match the columns in the given update column list.
2492: *
2493: * @param updateColumns A ResultColumnList representing the columns
2494: * to be updated.
2495: */
2496: void markUpdated(ResultColumnList updateColumns) {
2497: ResultColumn updateColumn;
2498: ResultColumn resultColumn;
2499:
2500: int size = updateColumns.size();
2501:
2502: for (int index = 0; index < size; index++) {
2503: updateColumn = (ResultColumn) updateColumns
2504: .elementAt(index);
2505:
2506: resultColumn = getResultColumn(updateColumn.getName());
2507:
2508: /*
2509: ** This ResultColumnList may not be bound yet - for update
2510: ** statements, we mark the updated columns *before* we bind
2511: ** the RCL. This ordering is important because we add columns
2512: ** to the RCL after marking the update columns and before
2513: ** binding.
2514: **
2515: ** So, it can happen that there is an invalid column name in
2516: ** the list. This condition will cause an exception when the
2517: ** RCL is bound. Just ignore it for now.
2518: */
2519: if (resultColumn != null) {
2520: resultColumn.markUpdated();
2521: }
2522: }
2523: }
2524:
2525: /**
2526: * Mark all the columns in the select sql that this result column list represents
2527: * as updatable if they match the columns in the given update column list.
2528: *
2529: * @param updateColumns A Vector representing the columns
2530: * to be updated.
2531: */
2532: void markColumnsInSelectListUpdatableByCursor(Vector updateColumns) {
2533: commonCodeForUpdatableByCursor(updateColumns, true);
2534: }
2535:
2536: /**
2537: * dealingWithSelectResultColumnList true means we are dealing with
2538: * ResultColumnList for a select sql. When dealing with ResultColumnList for
2539: * select sql, it is possible that not all the updatable columns are
2540: * projected in the select column list and hence it is possible that we may
2541: * not find the column to be updated in the ResultColumnList and that is why
2542: * special handling is required when dealingWithSelectResultColumnList is true.
2543: * eg select c11, c13 from t1 for update of c11, c12
2544: * In the eg above, we will find updatable column c11 in the select column
2545: * list but we will not find updatable column c12 in the select column list
2546: */
2547: private void commonCodeForUpdatableByCursor(Vector updateColumns,
2548: boolean dealingWithSelectResultColumnList) {
2549: /*
2550: ** If there is no update column list, or the list is empty, then it means that
2551: ** all the columns which have a base table associated with them are updatable.
2552: */
2553: if ((updateColumns == null) || (updateColumns.size() == 0)) {
2554: markUpdatableByCursor();
2555: } else {
2556: int ucSize = updateColumns.size();
2557: ResultColumn resultColumn;
2558: String columnName;
2559:
2560: for (int index = 0; index < ucSize; index++) {
2561: columnName = (String) updateColumns.elementAt(index);
2562:
2563: resultColumn = getResultColumn(columnName);
2564: if (SanityManager.DEBUG) {
2565: if (resultColumn == null
2566: && !dealingWithSelectResultColumnList) {
2567: SanityManager
2568: .THROWASSERT("No result column found with name "
2569: + columnName);
2570: }
2571: }
2572: //Following if means the column specified in FOR UPDATE clause is not
2573: //part of the select list
2574: if (resultColumn == null
2575: && dealingWithSelectResultColumnList)
2576: continue;
2577: resultColumn.markUpdatableByCursor();
2578: }
2579: }
2580: }
2581:
2582: /**
2583: * Mark as updatable all the columns in this result column list
2584: * that match the columns in the given update column list
2585: *
2586: * @param updateColumns A Vector representing the columns
2587: * to be updated.
2588: */
2589: void markUpdatableByCursor(Vector updateColumns) {
2590: commonCodeForUpdatableByCursor(updateColumns, false);
2591: }
2592:
2593: /**
2594: * Returns true if the given column position is for a column that will
2595: * be or could be updated by the positioned update of a cursor.
2596: *
2597: * @param columnPosition The position of the column in question
2598: *
2599: * @return true if the column is updatable
2600: */
2601: boolean updatableByCursor(int columnPosition) {
2602: return getResultColumn(columnPosition).updatableByCursor();
2603: }
2604:
2605: /**
2606: * Return whether or not this RCL can be flattened out of a tree.
2607: * It can only be flattened if the expressions are all cloneable.
2608: *
2609: * @return boolean Whether or not this RCL can be flattened out of a tree.
2610: */
2611: public boolean isCloneable() {
2612: boolean retcode = true;
2613: int size = size();
2614:
2615: for (int index = 0; index < size; index++) {
2616: ResultColumn rc = (ResultColumn) elementAt(index);
2617:
2618: if (!rc.getExpression().isCloneable()) {
2619: retcode = false;
2620: break;
2621: }
2622: }
2623:
2624: return retcode;
2625: }
2626:
2627: /**
2628: * Remap all ColumnReferences in this tree to be clones of the
2629: * underlying expression.
2630: *
2631: * @exception StandardException Thrown on error
2632: */
2633: public void remapColumnReferencesToExpressions()
2634: throws StandardException {
2635: int size = size();
2636: for (int index = 0; index < size; index++) {
2637: ResultColumn rc = (ResultColumn) elementAt(index);
2638:
2639: // The expression may be null if this column is an identity
2640: // column generated always. If the expression is not null, it
2641: // is a ColumnReference; we call through to the ColumnReference
2642: // to give it a chance to remap itself from the outer query
2643: // node to this one.
2644: if (rc.getExpression() != null)
2645: rc.setExpression(rc.getExpression()
2646: .remapColumnReferencesToExpressions());
2647: }
2648: }
2649:
2650: /*
2651: ** Indicate that the conglomerate is an index, so we need to generate a
2652: ** RowLocation as the last column of the result set.
2653: **
2654: ** @param cid The conglomerate id of the index
2655: */
2656: void setIndexRow(long cid, boolean forUpdate) {
2657: indexRow = true;
2658: conglomerateId = cid;
2659: this .forUpdate = forUpdate;
2660: }
2661:
2662: /* Debugging methods */
2663:
2664: /**
2665: * Verify that all ResultColumns and their expressions have type information
2666: * and that the type information between the respective RCs and
2667: * expressions matches.
2668: *
2669: * @return boolean Whether or not the type information is consistent
2670: */
2671: public boolean hasConsistentTypeInfo() throws StandardException {
2672: boolean isConsistent = true;
2673:
2674: if (SanityManager.DEBUG) {
2675: int size = size();
2676: for (int index = 0; index < size; index++) {
2677: ResultColumn rc = (ResultColumn) elementAt(index);
2678: ValueNode expr = rc.getExpression();
2679: DataTypeDescriptor rcDTS = rc.getTypeServices();
2680: DataTypeDescriptor exDTS = expr.getTypeServices();
2681:
2682: if (rcDTS == null || exDTS == null) {
2683: isConsistent = false;
2684: break;
2685: }
2686:
2687: if (rcDTS.getClass().getName() != exDTS.getClass()
2688: .getName()) {
2689: isConsistent = false;
2690: break;
2691: }
2692: }
2693: }
2694:
2695: return isConsistent;
2696: }
2697:
2698: /**
2699: * Return whether or not this RCL contains an AllResultColumn.
2700: * This is useful when dealing with SELECT * views which
2701: * reference tables that may have had columns added to them via
2702: * ALTER TABLE since the view was created.
2703: *
2704: * @return Whether or not this RCL contains an AllResultColumn.
2705: */
2706: public boolean containsAllResultColumn() {
2707: boolean containsAllResultColumn = false;
2708:
2709: int size = size();
2710: for (int index = 0; index < size; index++) {
2711: if (elementAt(index) instanceof AllResultColumn) {
2712: containsAllResultColumn = true;
2713: break;
2714: }
2715: }
2716:
2717: return containsAllResultColumn;
2718: }
2719:
2720: /**
2721: * Count the number of RCs in the list that are referenced.
2722: *
2723: * @return The number of RCs in the list that are referenced.
2724: */
2725: public int countReferencedColumns() {
2726: int numReferenced = 0;
2727:
2728: int size = size();
2729: for (int index = 0; index < size; index++) {
2730: ResultColumn rc = (ResultColumn) elementAt(index);
2731: if (rc.isReferenced()) {
2732: numReferenced++;
2733: }
2734: }
2735: return numReferenced;
2736: }
2737:
2738: /**
2739: * Record the column ids of the referenced columns in the specified array.
2740: *
2741: * @param idArray int[] for column ids
2742: * @param basis 0 (for 0-based ids) or 1 (for 1-based ids)
2743: */
2744: public void recordColumnReferences(int[] idArray, int basis) {
2745: int currArrayElement = 0;
2746: int size = size();
2747: for (int index = 0; index < size; index++) {
2748: ResultColumn rc = (ResultColumn) elementAt(index);
2749:
2750: if (rc.isReferenced()) {
2751: idArray[currArrayElement++] = index + basis;
2752: }
2753: }
2754: }
2755:
2756: /**
2757: * Record the top level ColumnReferences in the specified array
2758: * and table map
2759: * This is useful when checking for uniqueness conditions.
2760: * NOTE: All top level CRs assumed to be from the same table.
2761: * The size of the array is expected to be the # of columns
2762: * in the table of interest + 1, so we use 1-base column #s.
2763: *
2764: * @param colArray1 boolean[] for columns
2765: * @param tableColMap JBitSet[] for tables
2766: * @param tableNumber Table number of column references
2767: */
2768: public void recordColumnReferences(boolean[] colArray1,
2769: JBitSet[] tableColMap, int tableNumber) {
2770: int size = size();
2771: for (int index = 0; index < size; index++) {
2772: int columnNumber;
2773: ResultColumn rc = (ResultColumn) elementAt(index);
2774:
2775: if (!(rc.getExpression() instanceof ColumnReference)) {
2776: continue;
2777: }
2778:
2779: columnNumber = ((ColumnReference) rc.getExpression())
2780: .getColumnNumber();
2781: colArray1[columnNumber] = true;
2782: tableColMap[tableNumber].set(columnNumber);
2783: }
2784: }
2785:
2786: /**
2787: * Return whether or not all of the RCs in the list whose
2788: * expressions are ColumnReferences are
2789: * from the same table. One place this
2790: * is useful for distinct elimination based on the existence
2791: * of a uniqueness condition.
2792: *
2793: * @return -1 if all of the top level CRs in the RCL
2794: * are not ColumnReferences from the same table,
2795: * else the tableNumber
2796: */
2797: int allTopCRsFromSameTable() {
2798: int tableNumber = -1;
2799:
2800: int size = size();
2801: for (int index = 0; index < size; index++) {
2802: ResultColumn rc = (ResultColumn) elementAt(index);
2803: ValueNode vn = rc.getExpression();
2804: if (!(vn instanceof ColumnReference)) {
2805: continue;
2806: }
2807:
2808: // Remember the tableNumber from the first CR
2809: ColumnReference cr = (ColumnReference) vn;
2810: if (tableNumber == -1) {
2811: tableNumber = cr.getTableNumber();
2812: } else if (tableNumber != cr.getTableNumber()) {
2813: return -1;
2814: }
2815: }
2816: return tableNumber;
2817: }
2818:
2819: /**
2820: * Clear the column references from the RCL. (Restore RCL back to a state
2821: * where none of the RCs are marked as referenced.)
2822: */
2823: public void clearColumnReferences() {
2824: int size = size();
2825: for (int index = 0; index < size; index++) {
2826: ResultColumn rc = (ResultColumn) elementAt(index);
2827:
2828: if (rc.isReferenced()) {
2829: rc.setUnreferenced();
2830: }
2831: }
2832: }
2833:
2834: /**
2835: * Copy the referenced RCs from this list to the supplied target list.
2836: *
2837: * @param targetList The list to copy to
2838: */
2839: public void copyReferencedColumnsToNewList(
2840: ResultColumnList targetList) {
2841: int size = size();
2842: for (int index = 0; index < size; index++) {
2843: ResultColumn rc = (ResultColumn) elementAt(index);
2844:
2845: if (rc.isReferenced()) {
2846: targetList.addElement(rc);
2847: }
2848: }
2849: }
2850:
2851: /**
2852: * Copy the RCs from this list to the supplied target list.
2853: *
2854: * @param targetList The list to copy to,
2855: * @param copyList 1 based bitMap we copy columns associated with set bits.
2856: */
2857: public void copyColumnsToNewList(ResultColumnList targetList,
2858: FormatableBitSet copyList) {
2859: int size = size();
2860: for (int index = 0; index < size; index++) {
2861: ResultColumn rc = (ResultColumn) elementAt(index);
2862: if (copyList.isSet(rc.getColumnPosition())) {
2863: targetList.addElement(rc);
2864: }
2865: }
2866: }
2867:
2868: /**
2869: * Get a FormatableBitSet of the columns referenced in this rcl
2870: *
2871: * @return the FormatableBitSet
2872: */
2873: public FormatableBitSet getColumnReferenceMap() {
2874: FormatableBitSet colMap = new FormatableBitSet(size());
2875: int size = size();
2876: for (int index = 0; index < size; index++) {
2877: ResultColumn rc = (ResultColumn) elementAt(index);
2878: if (rc.isReferenced()) {
2879: colMap.set(index);
2880: }
2881: }
2882: return colMap;
2883: }
2884:
2885: /**
2886: * Or in any isReferenced booleans from the virtual column chain. That is the isReferenced bits on each
2887: * ResultColumn on the list will be set if the ResultColumn is referenced or if any VirtualColumnNode in its
2888: * expression chain refers to a referenced column.
2889: */
2890: void pullVirtualIsReferenced() {
2891: int size = size();
2892: for (int index = 0; index < size; index++) {
2893: ResultColumn rc = (ResultColumn) elementAt(index);
2894: rc.pullVirtualIsReferenced();
2895: }
2896: } // end of pullVirtualIsReferenced
2897:
2898: public void clearTableNames() {
2899: int size = size();
2900: for (int index = 0; index < size; index++) {
2901: ResultColumn rc = (ResultColumn) elementAt(index);
2902: rc.clearTableName();
2903: }
2904: }
2905:
2906: /**
2907: * Set the value of whether or not a count mismatch is allowed between
2908: * this RCL, as a derived column list, and an underlying RCL. This is allowed
2909: * for SELECT * views when an underlying table has had columns added to it
2910: * via ALTER TABLE.
2911: *
2912: * @param allowed Whether or not a mismatch is allowed.
2913: */
2914: protected void setCountMismatchAllowed(boolean allowed) {
2915: countMismatchAllowed = allowed;
2916: }
2917:
2918: /**
2919: * Return whether or not a count mismatch is allowed between this RCL,
2920: * as a derived column list, and an underlying RCL. This is allowed
2921: * for SELECT * views when an underlying table has had columns added to it
2922: * via ALTER TABLE.
2923: *
2924: * return Whether or not a mismatch is allowed.
2925: */
2926:
2927: protected boolean getCountMismatchAllowed() {
2928: return countMismatchAllowed;
2929: }
2930:
2931: /**
2932: * Get the size of all the columns added
2933: * together. Does <B>NOT</B> include the
2934: * column overhead that the store requires.
2935: * Also, will be a very rough estimate for
2936: * user types.
2937: *
2938: * @return the size
2939: */
2940: public int getTotalColumnSize() {
2941: int colSize = 0;
2942: int size = size();
2943: for (int index = 0; index < size; index++) {
2944: colSize += ((ResultColumn) elementAt(index))
2945: .getMaximumColumnSize();
2946: }
2947: return colSize;
2948: }
2949:
2950: /**
2951: * Generate an RCL to match the contents of a ResultSetMetaData.
2952: * This is useful when dealing with VTIs.
2953: *
2954: * @param rsmd The ResultSetMetaData.
2955: * @param tableName The TableName for the BCNs.
2956: * @param javaClassName The name of the VTI
2957: *
2958: * @exception StandardException Thrown on error
2959: */
2960: public void createListFromResultSetMetaData(ResultSetMetaData rsmd,
2961: TableName tableName, String javaClassName)
2962: throws StandardException {
2963: try {
2964: // JDBC columns #s are 1-based
2965: // Check to make sure # of columns >= 1
2966: int numColumns = rsmd.getColumnCount();
2967:
2968: if (numColumns <= 0) {
2969: throw StandardException.newException(
2970: SQLState.LANG_INVALID_V_T_I_COLUMN_COUNT,
2971: javaClassName, String.valueOf(numColumns));
2972: }
2973:
2974: for (int index = 1; index <= numColumns; index++) {
2975: boolean nullableResult = (rsmd.isNullable(index) != ResultSetMetaData.columnNoNulls);
2976:
2977: TypeId cti;
2978:
2979: int jdbcColumnType = rsmd.getColumnType(index);
2980:
2981: switch (jdbcColumnType) {
2982: case org.apache.derby.iapi.reference.JDBC20Translation.SQL_TYPES_JAVA_OBJECT:
2983: case Types.OTHER: {
2984: cti = TypeId.getUserDefinedTypeId(rsmd
2985: .getColumnTypeName(index), false);
2986: break;
2987: }
2988: default: {
2989: cti = TypeId.getBuiltInTypeId(jdbcColumnType);
2990: break;
2991: }
2992: }
2993:
2994: // Handle the case where a VTI returns a bad column type
2995: if (cti == null) {
2996: throw StandardException.newException(
2997: SQLState.LANG_BAD_J_D_B_C_TYPE_INFO,
2998: Integer.toString(index));
2999: }
3000:
3001: // Get the maximum byte storage for this column
3002: int maxWidth;
3003:
3004: /* Get maximum byte storage from rsmd for variable
3005: * width types, set it to MAXINT for the long types,
3006: * otherwise get it from the TypeId
3007: */
3008: if (cti.variableLength()) {
3009: maxWidth = rsmd.getColumnDisplaySize(index);
3010: } else if (jdbcColumnType == Types.LONGVARCHAR
3011: || jdbcColumnType == Types.LONGVARBINARY) {
3012: maxWidth = Integer.MAX_VALUE;
3013: } else {
3014: maxWidth = 0;
3015: }
3016:
3017: int precision = cti.isDecimalTypeId() ? rsmd
3018: .getPrecision(index) : 0;
3019: int scale = cti.isDecimalTypeId() ? rsmd
3020: .getScale(index) : 0;
3021: DataTypeDescriptor dts = new DataTypeDescriptor(cti,
3022: precision, scale, nullableResult, maxWidth);
3023: ValueNode bcn = (ValueNode) getNodeFactory().getNode(
3024: C_NodeTypes.BASE_COLUMN_NODE,
3025: rsmd.getColumnName(index), tableName, dts,
3026: getContextManager());
3027: ResultColumn rc = (ResultColumn) getNodeFactory()
3028: .getNode(C_NodeTypes.RESULT_COLUMN,
3029: rsmd.getColumnName(index), bcn,
3030: getContextManager());
3031: rc.setType(dts);
3032: addResultColumn(rc);
3033: }
3034: } catch (Throwable t) {
3035: if (t instanceof StandardException) {
3036: throw (StandardException) t;
3037: } else {
3038: throw StandardException.unexpectedUserException(t);
3039: }
3040: }
3041: }
3042:
3043: /**
3044: * Add an RC to the end of the list for the RID from an index.
3045: * NOTE: RC.expression is a CurrentRowLocationNode. This was previously only used
3046: * for non-select DML. We test for this node when generating the holder above
3047: * and generate the expected code. (We really should create yet another new node
3048: * type with its own code generation.)
3049: *
3050: * @exception StandardException Thrown on error
3051: */
3052: public void addRCForRID() throws StandardException {
3053: ResultColumn rowLocationColumn;
3054: CurrentRowLocationNode rowLocationNode;
3055:
3056: /* Generate the RowLocation column */
3057: rowLocationNode = (CurrentRowLocationNode) getNodeFactory()
3058: .getNode(C_NodeTypes.CURRENT_ROW_LOCATION_NODE,
3059: getContextManager());
3060: rowLocationColumn = (ResultColumn) getNodeFactory().getNode(
3061: C_NodeTypes.RESULT_COLUMN, "", rowLocationNode,
3062: getContextManager());
3063: rowLocationColumn.markGenerated();
3064:
3065: /* Append to the ResultColumnList */
3066: addResultColumn(rowLocationColumn);
3067: }
3068:
3069: /**
3070: * Walk the list and mark all RCs as unreferenced. This is useful
3071: * when recalculating which RCs are referenced at what level like
3072: * when deciding which columns need to be returned from a non-matching
3073: * index scan (as opposed to those returned from the base table).
3074: *
3075: * @exception StandardException Thrown on error
3076: */
3077: public void markAllUnreferenced() throws StandardException {
3078: int size = size();
3079:
3080: for (int index = 0; index < size; index++) {
3081: ResultColumn resultColumn = (ResultColumn) elementAt(index);
3082: resultColumn.setUnreferenced();
3083: }
3084: }
3085:
3086: /**
3087: * Determine if all of the RC.expressions are columns in the source result set.
3088: * This is useful for determining if we need to do reflection
3089: * at execution time.
3090: *
3091: * @param sourceRS The source ResultSet.
3092: *
3093: * @return Whether or not all of the RC.expressions are columns in the source result set.
3094: */
3095: boolean allExpressionsAreColumns(ResultSetNode sourceRS) {
3096: int size = size();
3097:
3098: for (int index = 0; index < size; index++) {
3099: ResultColumn resultColumn;
3100: ValueNode expr;
3101:
3102: resultColumn = (ResultColumn) elementAt(index);
3103: expr = resultColumn.getExpression();
3104: if (!(expr instanceof VirtualColumnNode)
3105: && !(expr instanceof ColumnReference)) {
3106: return false;
3107: }
3108:
3109: /* If the expression is a VirtualColumnNode, make sure that the column
3110: * is coming from the source result set, ie, that it is not a correlated
3111: * column.
3112: */
3113: if (expr instanceof VirtualColumnNode) {
3114: VirtualColumnNode vcn = (VirtualColumnNode) expr;
3115: if (vcn.getSourceResultSet() != sourceRS) {
3116: vcn.setCorrelated();
3117: return false;
3118: }
3119: }
3120:
3121: /* Make sure this is not a correlated CR */
3122: if (expr instanceof ColumnReference) {
3123: ColumnReference cr = (ColumnReference) expr;
3124: if (cr.getCorrelated()) {
3125: return false;
3126: }
3127: }
3128: }
3129: return true;
3130: }
3131:
3132: /**
3133: * Map the source columns to these columns. Build an array to represent the mapping.
3134: * For each RC, if the expression is simply a VCN or a CR then set the array element to be
3135: * the virtual column number of the source RC. Otherwise, set the array element to
3136: * -1.
3137: * This is useful for determining if we need to do reflection
3138: * at execution time.
3139: *
3140: * @return Array representiong mapping of RCs to source RCs.
3141: */
3142: int[] mapSourceColumns() {
3143: int[] mapArray = new int[size()];
3144: ResultColumn resultColumn;
3145:
3146: int size = size();
3147:
3148: for (int index = 0; index < size; index++) {
3149: resultColumn = (ResultColumn) elementAt(index);
3150: if (resultColumn.getExpression() instanceof VirtualColumnNode) {
3151: VirtualColumnNode vcn = (VirtualColumnNode) resultColumn
3152: .getExpression();
3153:
3154: // Can't deal with correlated VCNs
3155: if (vcn.getCorrelated()) {
3156: mapArray[index] = -1;
3157: } else {
3158: // Virtual column #s are 1-based
3159: mapArray[index] = vcn.getSourceColumn()
3160: .getVirtualColumnId();
3161: }
3162: } else if (resultColumn.getExpression() instanceof ColumnReference) {
3163: ColumnReference cr = (ColumnReference) resultColumn
3164: .getExpression();
3165:
3166: // Can't deal with correlated CRs
3167: if (cr.getCorrelated()) {
3168: mapArray[index] = -1;
3169: } else {
3170: // Virtual column #s are 1-based
3171: mapArray[index] = cr.getSource()
3172: .getVirtualColumnId();
3173: }
3174: } else {
3175: mapArray[index] = -1;
3176: }
3177: }
3178:
3179: return mapArray;
3180: }
3181:
3182: /** Set the nullability of every ResultColumn in this list */
3183: public void setNullability(boolean nullability) {
3184: int size = size();
3185:
3186: for (int index = 0; index < size; index++) {
3187: ResultColumn resultColumn = (ResultColumn) elementAt(index);
3188: resultColumn.setNullability(nullability);
3189: }
3190: }
3191:
3192: /**
3193: * Generate a FormatableBitSet representing the columns that are referenced in this RCL.
3194: * The caller decides if they want this FormatableBitSet if every RC is referenced.
3195: *
3196: * @param positionedUpdate Whether or not the scan that the RCL
3197: * belongs to is for update w/o a column list
3198: * @param always Whether or not caller always wants a non-null FormatableBitSet if
3199: * all RCs are referenced.
3200: * @param onlyBCNs If true, only set bit if expression is a BaseColumnNode,
3201: * otherwise set bit for all referenced RCs.
3202: *
3203: * @return The FormatableBitSet representing the referenced RCs.
3204: */
3205:
3206: FormatableBitSet getReferencedFormatableBitSet(
3207: boolean positionedUpdate, boolean always, boolean onlyBCNs) {
3208: int index;
3209: int colsAdded = 0;
3210: int size = size();
3211:
3212: FormatableBitSet newReferencedCols = new FormatableBitSet(size);
3213:
3214: /*
3215: ** For an updatable cursor, we need
3216: ** all columns.
3217: */
3218: if (positionedUpdate) {
3219: if (always) {
3220: /* Set all bits in the bit map */
3221: for (index = 0; index < size; index++) {
3222: newReferencedCols.set(index);
3223: }
3224:
3225: return newReferencedCols;
3226: } else {
3227: return null;
3228: }
3229: }
3230:
3231: for (index = 0; index < size; index++) {
3232: ResultColumn oldCol = (ResultColumn) elementAt(index);
3233: if (oldCol.isReferenced()) {
3234: /* Skip RCs whose expression is not a BCN
3235: * when requested to do so.
3236: */
3237: if (onlyBCNs
3238: && !(oldCol.getExpression() instanceof BaseColumnNode)) {
3239: continue;
3240: }
3241: newReferencedCols.set(index);
3242: colsAdded++;
3243: }
3244: }
3245:
3246: /* Return the FormatableBitSet if not all RCs are referenced or if
3247: * the caller always wants the FormatableBitSet returned.
3248: */
3249: if (colsAdded != index || always) {
3250: return newReferencedCols;
3251: } else {
3252: return null;
3253: }
3254: }
3255:
3256: /**
3257: * Create a new, compacted RCL based on the referenced RCs
3258: * in this list. If the RCL being compacted is for an
3259: * updatable scan, then we simply return this.
3260: *
3261: * The caller tells us whether or not they want a new list
3262: * if there is no compaction because all RCs are referenced.
3263: * This is useful in the case where the caller needs a new
3264: * RCL for existing RCs so that it can augment the new list.
3265: *
3266: * @param positionedUpdate Whether or not the scan that the RCL
3267: * belongs to is for update w/o a column list
3268: * @param always Whether or not caller always wants a new RCL
3269: *
3270: * @return The compacted RCL if compaction occurred, otherwise return this RCL.
3271: *
3272: * @exception StandardException Thrown on error
3273: */
3274: ResultColumnList compactColumns(boolean positionedUpdate,
3275: boolean always) throws StandardException {
3276: int index;
3277: int colsAdded = 0;
3278:
3279: /*
3280: ** For an updatable cursor, we need
3281: ** all columns.
3282: */
3283: if (positionedUpdate) {
3284: return this ;
3285: }
3286:
3287: ResultColumnList newCols = (ResultColumnList) getNodeFactory()
3288: .getNode(C_NodeTypes.RESULT_COLUMN_LIST,
3289: getContextManager());
3290:
3291: int size = size();
3292: for (index = 0; index < size; index++) {
3293: ResultColumn oldCol = (ResultColumn) elementAt(index);
3294: if (oldCol.isReferenced()) {
3295: newCols.addResultColumn(oldCol);
3296: colsAdded++;
3297: }
3298: }
3299:
3300: /* Return new RCL if we found unreferenced columns or if
3301: * the caller always wants a new list.
3302: */
3303: if (colsAdded != index || always) {
3304: return newCols;
3305: } else {
3306: return this ;
3307: }
3308: }
3309:
3310: /**
3311: * Remove the columns which are join columns (in the
3312: * joinColumns RCL) from this list. This is useful
3313: * for a JOIN with a USING clause.
3314: *
3315: * @param joinColumns The list of join columns
3316: */
3317: void removeJoinColumns(ResultColumnList joinColumns) {
3318: int jcSize = joinColumns.size();
3319: for (int index = 0; index < jcSize; index++) {
3320: ResultColumn joinRC = (ResultColumn) joinColumns
3321: .elementAt(index);
3322: String columnName = joinRC.getName();
3323:
3324: // columnName should always be non-null
3325: if (SanityManager.DEBUG) {
3326: SanityManager.ASSERT(columnName != null,
3327: "columnName should be non-null");
3328: }
3329:
3330: ResultColumn rightRC = getResultColumn(columnName);
3331:
3332: // Remove the RC from this list.
3333: if (rightRC != null) {
3334: removeElement(rightRC);
3335: }
3336: }
3337: }
3338:
3339: /**
3340: * Get the join columns from this list.
3341: * This is useful for a join with a USING clause.
3342: * (ANSI specifies that the join columns appear 1st.)
3343: *
3344: * @param joinColumns A list of the join columns.
3345: *
3346: * @return A list of the join columns from this list
3347: */
3348: ResultColumnList getJoinColumns(ResultColumnList joinColumns) {
3349: ResultColumnList newRCL = new ResultColumnList();
3350:
3351: /* Find all of the join columns and put them 1st on the
3352: * new RCL.
3353: */
3354: int jcSize = joinColumns.size();
3355: for (int index = 0; index < jcSize; index++) {
3356: ResultColumn joinRC = (ResultColumn) joinColumns
3357: .elementAt(index);
3358: String columnName = joinRC.getName();
3359:
3360: // columnName should always be non-null
3361: if (SanityManager.DEBUG) {
3362: SanityManager.ASSERT(columnName != null,
3363: "columnName should be non-null");
3364: }
3365:
3366: ResultColumn xferRC = getResultColumn(columnName);
3367:
3368: // Add the RC to the new list.
3369: newRCL.addElement(xferRC);
3370: }
3371: return newRCL;
3372: }
3373:
3374: /**
3375: * Reset the virtual column ids for all of the
3376: * underlying RCs. (Virtual column ids are 1-based.)
3377: */
3378: void resetVirtualColumnIds() {
3379: int size = size();
3380:
3381: for (int index = 0; index < size; index++) {
3382: /* ResultColumns are 1-based */
3383: ((ResultColumn) elementAt(index))
3384: .setVirtualColumnId(index + 1);
3385: }
3386: }
3387:
3388: /**
3389: * Return whether or not the same result row can be used for all
3390: * rows returned by the associated ResultSet. This is possible
3391: * if all entries in the list are constants or AggregateNodes.
3392: *
3393: * @return Whether or not the same result row can be used for all
3394: * rows returned by the associated ResultSet.
3395: */
3396: boolean reusableResult() {
3397: int size = size();
3398:
3399: for (int index = 0; index < size; index++) {
3400: ResultColumn rc = (ResultColumn) elementAt(index);
3401:
3402: if ((rc.getExpression() instanceof ConstantNode)
3403: || (rc.getExpression() instanceof AggregateNode)) {
3404: continue;
3405: }
3406: return false;
3407: }
3408: return true;
3409: }
3410:
3411: /**
3412: * Get an array of column positions (1-based) for all the columns
3413: * in this RCL. Assumes that all the columns are in the passed-in
3414: * table
3415: *
3416: * @return the array of strings
3417: *
3418: * @exception throws StandardException on error
3419: */
3420: public int[] getColumnPositions(TableDescriptor td)
3421: throws StandardException {
3422: int size = size();
3423: int[] myPositions = new int[size];
3424: String columnName;
3425: ColumnDescriptor cd;
3426:
3427: for (int index = 0; index < size; index++) {
3428: ResultColumn resultColumn = (ResultColumn) elementAt(index);
3429: columnName = resultColumn.getName();
3430: cd = td.getColumnDescriptor(columnName);
3431:
3432: if (cd == null) {
3433: throw StandardException.newException(
3434: SQLState.LANG_COLUMN_NOT_FOUND_IN_TABLE,
3435: columnName, td.getQualifiedName());
3436: }
3437:
3438: myPositions[index] = cd.getPosition();
3439: }
3440:
3441: return myPositions;
3442: }
3443:
3444: /**
3445: * Get an array of strings for all the columns
3446: * in this RCL.
3447: *
3448: * @return the array of strings
3449: */
3450: public String[] getColumnNames() {
3451: String strings[] = new String[size()];
3452:
3453: int size = size();
3454:
3455: for (int index = 0; index < size; index++) {
3456: ResultColumn resultColumn = (ResultColumn) elementAt(index);
3457: strings[index] = resultColumn.getName();
3458: }
3459: return strings;
3460: }
3461:
3462: /**
3463: * Replace any DEFAULTs with the associated tree for the default.
3464: *
3465: * @param ttd The TableDescriptor for the target table.
3466: * @param tcl The RCL for the target table.
3467: *
3468: * @exception StandardException Thrown on error
3469: */
3470: void replaceDefaults(TableDescriptor ttd, ResultColumnList tcl)
3471: throws StandardException {
3472: int size = size();
3473:
3474: for (int index = 0; index < size; index++) {
3475: ResultColumn rc = (ResultColumn) elementAt(index);
3476:
3477: if (rc.isDefaultColumn()) {
3478: // DefaultNode defaultNode = (DefaultNode) rc.getExpression();
3479: // Get ColumnDescriptor by name or by position?
3480: ColumnDescriptor cd;
3481: if (tcl == null) {
3482: cd = ttd.getColumnDescriptor(index + 1);
3483: } else {
3484: ResultColumn trc = (ResultColumn) tcl
3485: .elementAt(index);
3486: cd = ttd.getColumnDescriptor(trc.getName());
3487: }
3488:
3489: // Too many RCs if no ColumnDescriptor
3490: if (cd == null) {
3491: throw StandardException.newException(
3492: SQLState.LANG_TOO_MANY_RESULT_COLUMNS, ttd
3493: .getQualifiedName());
3494: }
3495:
3496: if (cd.isAutoincrement()) {
3497: rc.setAutoincrementGenerated();
3498: } // end of if ()
3499:
3500: DefaultInfoImpl defaultInfo = (DefaultInfoImpl) cd
3501: .getDefaultInfo();
3502: if (defaultInfo != null) {
3503: /* Query is dependent on the DefaultDescriptor */
3504: DefaultDescriptor defaultDescriptor = cd
3505: .getDefaultDescriptor(getDataDictionary());
3506: getCompilerContext().createDependency(
3507: defaultDescriptor);
3508:
3509: rc.setExpression(DefaultNode.parseDefault(
3510: defaultInfo.getDefaultText(),
3511: getLanguageConnectionContext(),
3512: getCompilerContext()));
3513:
3514: } else {
3515: rc
3516: .setExpression((ValueNode) getNodeFactory()
3517: .getNode(
3518: C_NodeTypes.UNTYPED_NULL_CONSTANT_NODE,
3519: getContextManager()));
3520: }
3521: rc.setDefaultColumn(false);
3522: }
3523: }
3524: }
3525:
3526: /**
3527: * Walk the RCL and check for DEFAULTs. DEFAULTs
3528: * are invalid at the time that this method is called,
3529: * so we throw an exception if found.
3530: * NOTE: The grammar allows:
3531: * VALUES DEFAULT;
3532: *
3533: * @exception StandardException Thrown on error
3534: */
3535: void checkForInvalidDefaults() throws StandardException {
3536: int size = size();
3537:
3538: for (int index = 0; index < size; index++) {
3539: ResultColumn rc = (ResultColumn) elementAt(index);
3540:
3541: if (rc.isAutoincrementGenerated())
3542: continue;
3543:
3544: if (rc.isDefaultColumn()) {
3545: throw StandardException
3546: .newException(SQLState.LANG_INVALID_USE_OF_DEFAULT);
3547: }
3548: }
3549: }
3550:
3551: /**
3552: * Verify that all of the RCs in this list are comparable.
3553: *
3554: * @exception StandardException Thrown on error
3555: */
3556: void verifyAllOrderable() throws StandardException {
3557: int size = size();
3558:
3559: for (int index = 0; index < size; index++) {
3560: ResultColumn rc = (ResultColumn) elementAt(index);
3561: rc.verifyOrderable();
3562: }
3563: }
3564:
3565: /**
3566: * Build this ResultColumnList from a table description and
3567: * an array of column IDs.
3568: *
3569: * @param table describes the table
3570: * @param columnIDs column positions in that table (1-based)
3571: *
3572: * @exception StandardException Thrown on error
3573: */
3574: public void populate(TableDescriptor table, int[] columnIDs)
3575: throws StandardException {
3576: if (columnIDs == null) {
3577: return;
3578: }
3579:
3580: int count = columnIDs.length;
3581: TableName tableName = makeTableName(table.getSchemaName(),
3582: table.getName());
3583: String columnName;
3584: int columnPosition;
3585: ResultColumn rc;
3586:
3587: for (int i = 0; i < count; i++) {
3588: columnPosition = columnIDs[i];
3589: columnName = table.getColumnDescriptor(columnPosition)
3590: .getColumnName();
3591:
3592: rc = makeColumnFromName(columnName);
3593:
3594: addResultColumn(rc);
3595: }
3596:
3597: }
3598:
3599: private ResultColumn makeColumnFromName(String columnName)
3600: throws StandardException {
3601: ResultColumn resultColumn = (ResultColumn) getNodeFactory()
3602: .getNode(C_NodeTypes.RESULT_COLUMN, columnName, null,
3603: getContextManager());
3604:
3605: return resultColumn;
3606: }
3607:
3608: private ResultColumn makeColumnReferenceFromName(
3609: TableName tableName, String columnName)
3610: throws StandardException {
3611: ContextManager cm = getContextManager();
3612: NodeFactory nodeFactory = getNodeFactory();
3613:
3614: ResultColumn rc = (ResultColumn) nodeFactory.getNode(
3615: C_NodeTypes.RESULT_COLUMN, null, nodeFactory.getNode(
3616: C_NodeTypes.COLUMN_REFERENCE, columnName,
3617: tableName, cm), cm);
3618:
3619: return rc;
3620: }
3621:
3622: /**
3623: * check if any autoincrement columns exist in the result column list.
3624: * called from insert or update where you cannot insert/update the value
3625: * of an autoincrement column.
3626: *
3627: * @exception StandardException If the column is an ai column
3628: */
3629: public void checkAutoincrement(ResultColumnList sourceRSRCL)
3630: throws StandardException {
3631: int size = size();
3632:
3633: for (int index = 0; index < size; index++) {
3634: ResultColumn rc = (ResultColumn) elementAt(index);
3635: ResultColumn sourceRC = (ResultColumn) ((sourceRSRCL == null) ? null
3636: : sourceRSRCL.elementAt(index));
3637: ColumnDescriptor cd = rc.getTableColumnDescriptor();
3638:
3639: if ((cd != null) && (cd.isAutoincrement())) {
3640: if ((sourceRC != null)
3641: && (sourceRC.isAutoincrementGenerated())) {
3642: sourceRC.setColumnDescriptor(cd
3643: .getTableDescriptor(), cd);
3644:
3645: } else {
3646: if (cd.isAutoincAlways())
3647: throw StandardException.newException(
3648: SQLState.LANG_AI_CANNOT_MODIFY_AI, rc
3649: .getName());
3650: }
3651: }
3652: }
3653: }
3654:
3655: public void incOrderBySelect() {
3656: orderBySelect++;
3657: }
3658:
3659: public void decOrderBySelect() {
3660: orderBySelect--;
3661: }
3662:
3663: public int getOrderBySelect() {
3664: return orderBySelect;
3665: }
3666:
3667: public void copyOrderBySelect(ResultColumnList src) {
3668: orderBySelect = src.orderBySelect;
3669: }
3670:
3671: /* ****
3672: * Take note of the size of this RCL _before_ we start
3673: * processing/binding it. This is so that, at bind time,
3674: * we can tell if any columns in the RCL were added
3675: * internally by us (i.e. they were not specified by the
3676: * user and thus will not be returned to the user).
3677: */
3678: protected void markInitialSize() {
3679: initialListSize = size();
3680: }
3681: }
|