0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.sql.compile.ResultSetNode
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to you under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.sql.compile;
0023:
0024: import org.apache.derby.iapi.services.context.ContextManager;
0025:
0026: import org.apache.derby.iapi.error.StandardException;
0027: import org.apache.derby.iapi.sql.compile.CompilerContext;
0028: import org.apache.derby.iapi.sql.compile.CostEstimate;
0029: import org.apache.derby.iapi.sql.compile.OptimizerFactory;
0030: import org.apache.derby.iapi.sql.compile.Optimizer;
0031: import org.apache.derby.iapi.sql.compile.OptimizableList;
0032: import org.apache.derby.iapi.sql.compile.OptimizablePredicateList;
0033: import org.apache.derby.iapi.sql.compile.Parser;
0034: import org.apache.derby.iapi.sql.compile.Visitable;
0035: import org.apache.derby.iapi.sql.compile.Visitor;
0036: import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;
0037: import org.apache.derby.iapi.sql.compile.RowOrdering;
0038: import org.apache.derby.iapi.sql.compile.C_NodeTypes;
0039:
0040: import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;
0041:
0042: import org.apache.derby.iapi.sql.dictionary.ColumnDescriptor;
0043: import org.apache.derby.iapi.sql.dictionary.DataDictionary;
0044: import org.apache.derby.iapi.sql.dictionary.DefaultDescriptor;
0045: import org.apache.derby.iapi.sql.dictionary.TableDescriptor;
0046:
0047: import org.apache.derby.iapi.sql.execute.ExecutionContext;
0048:
0049: import org.apache.derby.iapi.sql.Activation;
0050: import org.apache.derby.iapi.types.DataTypeDescriptor;
0051: import org.apache.derby.iapi.sql.ResultColumnDescriptor;
0052: import org.apache.derby.iapi.sql.ResultDescription;
0053: import org.apache.derby.iapi.sql.ResultSet;
0054:
0055: import org.apache.derby.iapi.types.TypeId;
0056:
0057: import org.apache.derby.iapi.store.access.TransactionController;
0058:
0059: import org.apache.derby.iapi.services.loader.GeneratedMethod;
0060:
0061: import org.apache.derby.iapi.services.sanity.SanityManager;
0062: import org.apache.derby.iapi.reference.ClassName;
0063:
0064: import org.apache.derby.iapi.services.compiler.MethodBuilder;
0065:
0066: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
0067: import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;
0068:
0069: import org.apache.derby.iapi.util.JBitSet;
0070: import org.apache.derby.iapi.services.classfile.VMOpcode;
0071:
0072: import org.apache.derby.catalog.types.DefaultInfoImpl;
0073:
0074: import java.util.Properties;
0075: import java.util.Vector;
0076: import java.util.Set;
0077:
0078: /**
0079: * A ResultSetNode represents a result set, that is, a set of rows. It is
0080: * analogous to a ResultSet in the LanguageModuleExternalInterface. In fact,
0081: * code generation for a a ResultSetNode will create a "new" call to a
0082: * constructor for a ResultSet.
0083: *
0084: * @author Jeff Lichtman
0085: */
0086:
0087: public abstract class ResultSetNode extends QueryTreeNode {
0088: int resultSetNumber;
0089: /* Bit map of referenced tables under this ResultSetNode */
0090: JBitSet referencedTableMap;
0091: ResultColumnList resultColumns;
0092: boolean statementResultSet;
0093: boolean cursorTargetTable;
0094: boolean insertSource;
0095:
0096: CostEstimate costEstimate;
0097: CostEstimate scratchCostEstimate;
0098: Optimizer optimizer;
0099:
0100: // Final cost estimate for this result set node, which is the estimate
0101: // for this node with respect to the best join order for the top-level
0102: // query. Subclasses will set this value where appropriate.
0103: CostEstimate finalCostEstimate;
0104:
0105: /**
0106: * Convert this object to a String. See comments in QueryTreeNode.java
0107: * for how this should be done for tree printing.
0108: *
0109: * @return This object as a String
0110: */
0111:
0112: public String toString() {
0113: if (SanityManager.DEBUG) {
0114: return "resultSetNumber: "
0115: + resultSetNumber
0116: + "\n"
0117: + "referencedTableMap: "
0118: + (referencedTableMap != null ? referencedTableMap
0119: .toString() : "null") + "\n"
0120: + "statementResultSet: " + statementResultSet
0121: + "\n" + super .toString();
0122: } else {
0123: return "";
0124: }
0125: }
0126:
0127: /**
0128: * Prints the sub-nodes of this object. See QueryTreeNode.java for
0129: * how tree printing is supposed to work.
0130: *
0131: * @param depth The depth of this node in the tree
0132: */
0133:
0134: public void printSubNodes(int depth) {
0135: if (SanityManager.DEBUG) {
0136: super .printSubNodes(depth);
0137:
0138: if (resultColumns != null) {
0139: printLabel(depth, "resultColumns: ");
0140: resultColumns.treePrint(depth);
0141: }
0142: }
0143: }
0144:
0145: /**
0146: * Get the resultSetNumber in this ResultSetNode. Expected to be set during
0147: * generate().
0148: *
0149: * @return int The resultSetNumber.
0150: */
0151:
0152: public int getResultSetNumber() {
0153: return resultSetNumber;
0154: }
0155:
0156: /**
0157: * Get the CostEstimate for this ResultSetNode.
0158: *
0159: * @return The CostEstimate for this ResultSetNode.
0160: */
0161: public CostEstimate getCostEstimate() {
0162: if (SanityManager.DEBUG) {
0163: if (costEstimate == null) {
0164: SanityManager
0165: .THROWASSERT("costEstimate is not expected to be null for "
0166: + getClass().getName());
0167: }
0168: }
0169: return costEstimate;
0170: }
0171:
0172: /**
0173: * Get the final CostEstimate for this ResultSetNode.
0174: *
0175: * @return The final CostEstimate for this ResultSetNode.
0176: */
0177: public CostEstimate getFinalCostEstimate() throws StandardException {
0178: if (SanityManager.DEBUG) {
0179: if (finalCostEstimate == null) {
0180: SanityManager
0181: .THROWASSERT("finalCostEstimate is not expected to be null for "
0182: + getClass().getName());
0183: }
0184: }
0185: return finalCostEstimate;
0186: }
0187:
0188: /**
0189: * Assign the next resultSetNumber to the resultSetNumber in this ResultSetNode.
0190: * Expected to be done during generate().
0191: *
0192: * @exception StandardException Thrown on error
0193: */
0194:
0195: public void assignResultSetNumber() throws StandardException {
0196: resultSetNumber = getCompilerContext().getNextResultSetNumber();
0197: resultColumns.setResultSetNumber(resultSetNumber);
0198: }
0199:
0200: /**
0201: * Bind the non VTI tables in this ResultSetNode. This includes getting their
0202: * descriptors from the data dictionary and numbering them.
0203: *
0204: * @param dataDictionary The DataDictionary to use for binding
0205: * @param fromListParam FromList to use/append to.
0206: *
0207: * @return ResultSetNode
0208: *
0209: * @exception StandardException Thrown on error
0210: */
0211:
0212: public ResultSetNode bindNonVTITables(
0213: DataDictionary dataDictionary, FromList fromListParam)
0214: throws StandardException {
0215: return this ;
0216: }
0217:
0218: /**
0219: * Bind the VTI tables in this ResultSetNode. This includes getting their
0220: * descriptors from the data dictionary and numbering them.
0221: *
0222: * @param fromListParam FromList to use/append to.
0223: *
0224: * @return ResultSetNode
0225: *
0226: * @exception StandardException Thrown on error
0227: */
0228:
0229: public ResultSetNode bindVTITables(FromList fromListParam)
0230: throws StandardException {
0231: return this ;
0232: }
0233:
0234: /**
0235: * Bind the expressions in this ResultSetNode. This means binding the
0236: * sub-expressions, as well as figuring out what the return type is for
0237: * each expression.
0238: *
0239: * @param fromListParam FromList to use/append to.
0240: *
0241: * @exception StandardException Thrown on error
0242: */
0243: public void bindExpressions(FromList fromListParam)
0244: throws StandardException {
0245: if (SanityManager.DEBUG)
0246: SanityManager.ASSERT(false,
0247: "bindExpressions() is not expected to be called for "
0248: + this .getClass().toString());
0249: }
0250:
0251: /**
0252: * Bind the expressions in this ResultSetNode if it has tables. This means binding the
0253: * sub-expressions, as well as figuring out what the return type is for
0254: * each expression.
0255: *
0256: * @param fromListParam FromList to use/append to.
0257: *
0258: * @exception StandardException Thrown on error
0259: */
0260: public void bindExpressionsWithTables(FromList fromListParam)
0261: throws StandardException {
0262: if (SanityManager.DEBUG)
0263: SanityManager.ASSERT(false,
0264: "bindExpressionsWithTables() is not expected to be called for "
0265: + this .getClass().toString());
0266: }
0267:
0268: /**
0269: * Bind the expressions in the target list. This means binding the
0270: * sub-expressions, as well as figuring out what the return type is
0271: * for each expression. This is useful for EXISTS subqueries, where we
0272: * need to validate the target list before blowing it away and replacing
0273: * it with a SELECT true.
0274: *
0275: * @exception StandardException Thrown on error
0276: */
0277:
0278: public void bindTargetExpressions(FromList fromListParam)
0279: throws StandardException {
0280: if (SanityManager.DEBUG)
0281: SanityManager.ASSERT(false,
0282: "bindTargetExpressions() is not expected to be called for "
0283: + this .getClass().toString());
0284: }
0285:
0286: /**
0287: * Set the type of each parameter in the result column list for this table constructor.
0288: *
0289: * @param typeColumns The ResultColumnList containing the desired result
0290: * types.
0291: *
0292: * @exception StandardException Thrown on error
0293: */
0294: void setTableConstructorTypes(ResultColumnList typeColumns)
0295: throws StandardException {
0296: if (SanityManager.DEBUG)
0297: SanityManager
0298: .ASSERT(resultColumns.size() <= typeColumns.size(),
0299: "More columns in ResultColumnList than in base table");
0300:
0301: /* Look for ? parameters in the result column list */
0302: int rclSize = resultColumns.size();
0303: for (int index = 0; index < rclSize; index++) {
0304: ResultColumn rc = (ResultColumn) resultColumns
0305: .elementAt(index);
0306:
0307: ValueNode re = rc.getExpression();
0308:
0309: if (re.requiresTypeFromContext()) {
0310: ResultColumn typeCol = (ResultColumn) typeColumns
0311: .elementAt(index);
0312:
0313: /*
0314: ** We found a ? - set its type to the type of the
0315: ** corresponding column of the target table.
0316: */
0317: re.setType(typeCol.getTypeServices());
0318: } else if (re instanceof CharConstantNode) {
0319: // Character constants are of type CHAR (fixed length string).
0320: // This causes a problem (beetle 5160) when multiple row values are provided
0321: // as constants for insertion into a variable length string column.
0322: //
0323: // This issue is the query expression
0324: // VALUES 'abc', 'defghi'
0325: // has type of CHAR(6), ie. the length of largest row value for that column.
0326: // This is from the UNION defined behaviour.
0327: // This causes strings with less than the maximum length to be blank padded
0328: // to that length (CHAR semantics). Thus if this VALUES clause is used to
0329: // insert into a variable length string column, then these blank padded values
0330: // are inserted, which is not what is required ...
0331: //
0332: // BECAUSE, when the VALUES is used as a table constructor SQL standard says the
0333: // types of the table constructor's columns are set by the table's column types.
0334: // Thus, in this case, each of those string constants should be of type VARCHAR
0335: // (or the matching string type for the table).
0336: //
0337: //
0338: // This is only an issue for fixed length character (CHAR, BIT) string or
0339: // binary consraints being inserted into variable length types.
0340: // This is because any other type's fundemental literal value is not affected
0341: // by its data type. E.g. Numeric types such as INT, REAL, BIGINT, DECIMAL etc.
0342: // do not have their value modifed by the union since even if the type is promoted
0343: // to a higher type, its fundemental value remains unchanged.
0344: // values (1.2, 34.4567, 234.47) will be promoted to
0345: // values (1.2000, 34.4567, 234.4700)
0346: // but their numeric value remains the same.
0347: //
0348: //
0349: //
0350: // The fix is to change the base type of the table constructor's value to
0351: // match the column type. Its length can be left as-is, because there is
0352: // still a normailzation step when the value is inserted into the table.
0353: // That will set the correct length and perform truncation checks etc.
0354:
0355: ResultColumn typeCol = (ResultColumn) typeColumns
0356: .elementAt(index);
0357:
0358: TypeId colTypeId = typeCol.getTypeId();
0359:
0360: if (colTypeId.isStringTypeId()) {
0361:
0362: if (colTypeId.getJDBCTypeId() != java.sql.Types.CHAR) {
0363:
0364: int maxWidth = re.getTypeServices()
0365: .getMaximumWidth();
0366:
0367: re.setType(new DataTypeDescriptor(colTypeId,
0368: true, maxWidth));
0369: }
0370: } else if (colTypeId.isBitTypeId()) {
0371: if (colTypeId.getJDBCTypeId() == java.sql.Types.VARBINARY) {
0372: // then we're trying to cast a char literal into a
0373: // variable bit column. We can't change the base
0374: // type of the table constructor's value from char
0375: // to bit, so instead, we just change the base type
0376: // of that value from char to varchar--that way,
0377: // no padding will be added when we convert to
0378: // bits later on (Beetle 5306).
0379: TypeId tId = TypeId
0380: .getBuiltInTypeId(java.sql.Types.VARCHAR);
0381: re.setType(new DataTypeDescriptor(tId, true));
0382: typeColumns.setElementAt(typeCol, index);
0383: } else if (colTypeId.getJDBCTypeId() == java.sql.Types.LONGVARBINARY) {
0384: TypeId tId = TypeId
0385: .getBuiltInTypeId(java.sql.Types.LONGVARCHAR);
0386: re.setType(new DataTypeDescriptor(tId, true));
0387: typeColumns.setElementAt(typeCol, index);
0388: }
0389: }
0390:
0391: } else if (re instanceof BitConstantNode) {
0392: ResultColumn typeCol = (ResultColumn) typeColumns
0393: .elementAt(index);
0394:
0395: TypeId colTypeId = typeCol.getTypeId();
0396:
0397: if (colTypeId.isBitTypeId()) {
0398:
0399: // NOTE: Don't bother doing this if the column type is BLOB,
0400: // as we don't allow bit literals to be inserted into BLOB
0401: // columns (they have to be explicitly casted first); beetle 5266.
0402: if ((colTypeId.getJDBCTypeId() != java.sql.Types.BINARY)
0403: && (colTypeId.getJDBCTypeId() != java.sql.Types.BLOB)) {
0404:
0405: int maxWidth = re.getTypeServices()
0406: .getMaximumWidth();
0407:
0408: re.setType(new DataTypeDescriptor(colTypeId,
0409: true, maxWidth));
0410: }
0411: } else if (colTypeId.isStringTypeId()) {
0412: if (colTypeId.getJDBCTypeId() == java.sql.Types.VARCHAR) {
0413: // then we're trying to cast a bit literal into a
0414: // variable char column. We can't change the base
0415: // type of the table constructor's value from bit
0416: // to char, so instead, we just change the base
0417: // type of that value from bit to varbit--that way,
0418: // no padding will be added when we convert to
0419: // char later on.
0420: TypeId tId = TypeId
0421: .getBuiltInTypeId(java.sql.Types.VARBINARY);
0422: re.setType(new DataTypeDescriptor(tId, true));
0423: typeColumns.setElementAt(typeCol, index);
0424: } else if (colTypeId.getJDBCTypeId() == java.sql.Types.LONGVARCHAR) {
0425: TypeId tId = TypeId
0426: .getBuiltInTypeId(java.sql.Types.LONGVARBINARY);
0427: re.setType(new DataTypeDescriptor(tId, true));
0428: typeColumns.setElementAt(typeCol, index);
0429: }
0430: }
0431: }
0432: }
0433: }
0434:
0435: /**
0436: * Remember that this node is the source result set for an INSERT.
0437: */
0438: public void setInsertSource() {
0439: insertSource = true;
0440: }
0441:
0442: /**
0443: * Verify that a SELECT * is valid for this type of subquery.
0444: *
0445: * @param outerFromList The FromList from the outer query block(s)
0446: * @param subqueryType The subquery type
0447: *
0448: * @exception StandardException Thrown on error
0449: */
0450: public void verifySelectStarSubquery(FromList outerFromList,
0451: int subqueryType) throws StandardException {
0452: if (SanityManager.DEBUG)
0453: SanityManager.ASSERT(false,
0454: "verifySelectStarSubquery() is not expected to be called for "
0455: + this .getClass().toString());
0456: }
0457:
0458: /**
0459: * Expand "*" into a ResultColumnList with all of the columns
0460: * in the table's result list.
0461: *
0462: * @param allTableName The qualifier on the "*"
0463: *
0464: * @return ResultColumnList The expanded list
0465: *
0466: * @exception StandardException Thrown on error
0467: */
0468: public ResultColumnList getAllResultColumns(TableName allTableName)
0469: throws StandardException {
0470: if (SanityManager.DEBUG)
0471: SanityManager
0472: .THROWASSERT("getAllResultColumns() not expected to be called for "
0473: + this .getClass().getName() + this );
0474: return null;
0475: }
0476:
0477: /**
0478: * Try to find a ResultColumn in the table represented by this FromTable
0479: * that matches the name in the given ColumnReference.
0480: *
0481: * @param columnReference The columnReference whose name we're looking
0482: * for in the given table.
0483: *
0484: * @return A ResultColumn whose expression is the ColumnNode
0485: * that matches the ColumnReference.
0486: * Returns null if there is no match.
0487: *
0488: * @exception StandardException Thrown on error
0489: */
0490:
0491: public ResultColumn getMatchingColumn(
0492: ColumnReference columnReference) throws StandardException {
0493: if (SanityManager.DEBUG)
0494: SanityManager
0495: .THROWASSERT("getMatchingColumn() not expected to be called for "
0496: + this );
0497: return null;
0498: }
0499:
0500: /**
0501: * Set the result column for the subquery to a boolean true,
0502: * Useful for transformations such as
0503: * changing:
0504: * where exists (select ... from ...)
0505: * to:
0506: * where (select true from ...)
0507: *
0508: * NOTE: No transformation is performed if the ResultColumn.expression is
0509: * already the correct boolean constant.
0510: *
0511: * @param onlyConvertAlls Boolean, whether or not to just convert *'s
0512: *
0513: * @exception StandardException Thrown on error
0514: */
0515: public void setResultToBooleanTrueNode(boolean onlyConvertAlls)
0516: throws StandardException {
0517: BooleanConstantNode booleanNode;
0518: ResultColumn resultColumn;
0519:
0520: /* We need to be able to handle both ResultColumn and AllResultColumn
0521: * since they are peers.
0522: */
0523: if (resultColumns.elementAt(0) instanceof AllResultColumn) {
0524: resultColumn = (ResultColumn) getNodeFactory().getNode(
0525: C_NodeTypes.RESULT_COLUMN, "", null,
0526: getContextManager());
0527: } else if (onlyConvertAlls) {
0528: return;
0529: } else {
0530: resultColumn = (ResultColumn) resultColumns.elementAt(0);
0531:
0532: /* Nothing to do if query is already select TRUE ... */
0533: if (resultColumn.getExpression().isBooleanTrue()) {
0534: return;
0535: }
0536: }
0537:
0538: booleanNode = (BooleanConstantNode) getNodeFactory().getNode(
0539: C_NodeTypes.BOOLEAN_CONSTANT_NODE, Boolean.TRUE,
0540: getContextManager());
0541:
0542: resultColumn.setExpression(booleanNode);
0543: resultColumn.setType(booleanNode.getTypeServices());
0544: /* VirtualColumnIds are 1-based, RCLs are 0-based */
0545: resultColumn.setVirtualColumnId(1);
0546: resultColumns.setElementAt(resultColumn, 0);
0547: }
0548:
0549: /**
0550: * Get the FromList. Create and return an empty FromList. (Subclasses
0551: * which actuall have FromLists will override this.) This is useful because
0552: * there is a FromList parameter to bindExpressions() which is used as
0553: * the common FromList to bind against, allowing us to support
0554: * correlation columns under unions in subqueries.
0555: *
0556: * @return FromList
0557: * @exception StandardException Thrown on error
0558: */
0559: public FromList getFromList() throws StandardException {
0560: return (FromList) getNodeFactory().getNode(
0561: C_NodeTypes.FROM_LIST,
0562: getNodeFactory().doJoinOrderOptimization(),
0563: getContextManager());
0564: }
0565:
0566: /**
0567: * @see QueryTreeNode#disablePrivilegeCollection
0568: */
0569: public void disablePrivilegeCollection() {
0570: super .disablePrivilegeCollection();
0571: if (resultColumns != null)
0572: resultColumns.disablePrivilegeCollection();
0573: }
0574:
0575: /**
0576: * Bind the result columns of this ResultSetNode when there is no
0577: * base table to bind them to. This is useful for SELECT statements,
0578: * where the result columns get their types from the expressions that
0579: * live under them.
0580: *
0581: * @param fromListParam FromList to use/append to.
0582: *
0583: * @exception StandardException Thrown on error
0584: */
0585:
0586: public void bindResultColumns(FromList fromListParam)
0587: throws StandardException {
0588: resultColumns.bindResultColumnsToExpressions();
0589: }
0590:
0591: /**
0592: * Bind the result columns for this ResultSetNode to a base table.
0593: * This is useful for INSERT and UPDATE statements, where the
0594: * result columns get their types from the table being updated or
0595: * inserted into.
0596: * If a result column list is specified, then the verification that the
0597: * result column list does not contain any duplicates will be done when
0598: * binding them by name.
0599: *
0600: * @param targetTableDescriptor The TableDescriptor for the table being
0601: * updated or inserted into
0602: * @param targetColumnList For INSERT statements, the user
0603: * does not have to supply column
0604: * names (for example, "insert into t
0605: * values (1,2,3)". When this
0606: * parameter is null, it means that
0607: * the user did not supply column
0608: * names, and so the binding should
0609: * be done based on order. When it
0610: * is not null, it means do the binding
0611: * by name, not position.
0612: * @param statement Calling DMLStatementNode (Insert or Update)
0613: * @param fromListParam FromList to use/append to.
0614: *
0615: * @exception StandardException Thrown on error
0616: */
0617:
0618: public void bindResultColumns(
0619: TableDescriptor targetTableDescriptor, FromVTI targetVTI,
0620: ResultColumnList targetColumnList,
0621: DMLStatementNode statement, FromList fromListParam)
0622: throws StandardException {
0623: /* For insert select, we need to expand any *'s in the
0624: * select before binding the result columns
0625: */
0626: if (this instanceof SelectNode) {
0627: resultColumns
0628: .expandAllsAndNameColumns(((SelectNode) this ).fromList);
0629: }
0630:
0631: /* If specified, copy the result column names down to the
0632: * source's target list.
0633: */
0634: if (targetColumnList != null) {
0635: resultColumns.copyResultColumnNames(targetColumnList);
0636: }
0637:
0638: if (targetColumnList != null) {
0639: if (targetTableDescriptor != null) {
0640: resultColumns.bindResultColumnsByName(
0641: targetTableDescriptor,
0642: (DMLStatementNode) statement);
0643: } else {
0644: resultColumns.bindResultColumnsByName(targetVTI
0645: .getResultColumns(), targetVTI, statement);
0646: }
0647: } else
0648: resultColumns
0649: .bindResultColumnsByPosition(targetTableDescriptor);
0650: }
0651:
0652: /**
0653: * Bind untyped nulls to the types in the given ResultColumnList.
0654: * This is used for binding the nulls in row constructors and
0655: * table constructors. In all other cases (as of the time of
0656: * this writing), we do nothing.
0657: *
0658: * @param rcl The ResultColumnList with the types to bind nulls to
0659: *
0660: * @exception StandardException Thrown on error
0661: */
0662: public void bindUntypedNullsToResultColumns(ResultColumnList rcl)
0663: throws StandardException {
0664: return;
0665: }
0666:
0667: /**
0668: * Preprocess a ResultSetNode - this currently means:
0669: * o Generating a referenced table map for each ResultSetNode.
0670: * o Putting the WHERE and HAVING clauses in conjunctive normal form (CNF).
0671: * o Converting the WHERE and HAVING clauses into PredicateLists and
0672: * classifying them.
0673: * o Ensuring that a ProjectRestrictNode is generated on top of every
0674: * FromBaseTable and generated in place of every FromSubquery.
0675: * o Pushing single table predicates down to the new ProjectRestrictNodes.
0676: *
0677: * @param numTables The number of tables in the DML Statement
0678: * @param gbl The group by list, if any
0679: * @param fromList The from list, if any
0680: *
0681: * @return ResultSetNode at top of preprocessed tree.
0682: *
0683: * @exception StandardException Thrown on error
0684: */
0685:
0686: public ResultSetNode preprocess(int numTables, GroupByList gbl,
0687: FromList fromList) throws StandardException {
0688: if (SanityManager.DEBUG)
0689: SanityManager
0690: .THROWASSERT("preprocess() not expected to be called for "
0691: + getClass().toString());
0692: return null;
0693: }
0694:
0695: /**
0696: * Find the unreferenced result columns and project them out.
0697: */
0698: void projectResultColumns() throws StandardException {
0699: // It is only necessary for joins
0700: }
0701:
0702: /**
0703: * Ensure that the top of the RSN tree has a PredicateList.
0704: *
0705: * @param numTables The number of tables in the query.
0706: * @return ResultSetNode A RSN tree with a node which has a PredicateList on top.
0707: *
0708: * @exception StandardException Thrown on error
0709: */
0710: public ResultSetNode ensurePredicateList(int numTables)
0711: throws StandardException {
0712: if (SanityManager.DEBUG)
0713: SanityManager
0714: .THROWASSERT("ensurePredicateList() not expected to be called for "
0715: + getClass().toString());
0716: return null;
0717: }
0718:
0719: /**
0720: * Add a new predicate to the list. This is useful when doing subquery
0721: * transformations, when we build a new predicate with the left side of
0722: * the subquery operator and the subquery's result column.
0723: *
0724: * @param predicate The predicate to add
0725: *
0726: * @return ResultSetNode The new top of the tree.
0727: *
0728: * @exception StandardException Thrown on error
0729: */
0730: public ResultSetNode addNewPredicate(Predicate predicate)
0731: throws StandardException {
0732: if (SanityManager.DEBUG)
0733: SanityManager
0734: .THROWASSERT("addNewPredicate() not expected to be called for "
0735: + getClass().toString());
0736: return null;
0737: }
0738:
0739: /**
0740: * Evaluate whether or not the subquery in a FromSubquery is flattenable.
0741: * Currently, a FSqry is flattenable if all of the following are true:
0742: * o Subquery is a SelectNode. (ie, not a RowResultSetNode or a UnionNode)
0743: * o It contains no top level subqueries. (RESOLVE - we can relax this)
0744: * o It does not contain a group by or having clause
0745: * o It does not contain aggregates.
0746: *
0747: * @param fromList The outer from list
0748: *
0749: * @return boolean Whether or not the FromSubquery is flattenable.
0750: */
0751: public boolean flattenableInFromSubquery(FromList fromList) {
0752: if (SanityManager.DEBUG)
0753: SanityManager
0754: .THROWASSERT("flattenableInFromSubquery() not expected to be called for "
0755: + getClass().toString());
0756: return false;
0757: }
0758:
0759: /**
0760: * Get a parent ProjectRestrictNode above us.
0761: * This is useful when we need to preserve the
0762: * user specified column order when reordering the
0763: * columns in the distinct when we combine
0764: * an order by with a distinct.
0765: *
0766: * @return A parent ProjectRestrictNode to do column reordering
0767: *
0768: * @exception StandardException Thrown on error
0769: */
0770: ResultSetNode genProjectRestrictForReordering()
0771: throws StandardException {
0772: ResultColumnList prRCList;
0773:
0774: /* We get a shallow copy of the ResultColumnList and its
0775: * ResultColumns. (Copy maintains ResultColumn.expression for now.)
0776: */
0777: prRCList = resultColumns;
0778: resultColumns = resultColumns.copyListAndObjects();
0779:
0780: /* Replace ResultColumn.expression with new VirtualColumnNodes
0781: * in the ProjectRestrictNode's ResultColumnList. (VirtualColumnNodes include
0782: * pointers to source ResultSetNode, this, and source ResultColumn.)
0783: * NOTE: We don't want to mark the underlying RCs as referenced, otherwise
0784: * we won't be able to project out any of them.
0785: */
0786: prRCList.genVirtualColumnNodes(this , resultColumns, false);
0787:
0788: /* Finally, we create the new ProjectRestrictNode */
0789: return (ResultSetNode) getNodeFactory().getNode(
0790: C_NodeTypes.PROJECT_RESTRICT_NODE, this , prRCList,
0791: null, /* Restriction */
0792: null, /* Restriction as PredicateList */
0793: null, /* Project subquery list */
0794: null, /* Restrict subquery list */
0795: null, getContextManager());
0796: }
0797:
0798: /**
0799: * Optimize a ResultSetNode. This means choosing the best access
0800: * path for each table under the ResultSetNode, among other things.
0801: *
0802: * The only RSNs that need to implement their own optimize() are a
0803: * SelectNode and those RSNs that can appear above a SelectNode in the
0804: * query tree. Currently, a ProjectRestrictNode is the only RSN that
0805: * can appear above a SelectNode.
0806: *
0807: * @param dataDictionary The DataDictionary to use for optimization
0808: * @param predicates The PredicateList to apply.
0809: * @param outerRows The number of outer joining rows
0810: *
0811: * @return ResultSetNode The top of the optimized query tree
0812: *
0813: * @exception StandardException Thrown on error
0814: */
0815:
0816: public ResultSetNode optimize(DataDictionary dataDictionary,
0817: PredicateList predicates, double outerRows)
0818: throws StandardException {
0819: if (SanityManager.DEBUG)
0820: SanityManager.ASSERT(false,
0821: "optimize() is not expected to be called for "
0822: + this .getClass().toString());
0823: return null;
0824: }
0825:
0826: /**
0827: * Modify the access paths according to the decisions the optimizer
0828: * made. This can include adding project/restrict nodes,
0829: * index-to-base-row nodes, etc.
0830: *
0831: * @return The modified query tree
0832: *
0833: * @exception StandardException Thrown on error
0834: */
0835: public ResultSetNode modifyAccessPaths() throws StandardException {
0836: /* Default behavior is to do nothing */
0837: return this ;
0838: }
0839:
0840: /**
0841: * Modify the access paths according to the decisions the optimizer
0842: * made. This can include adding project/restrict nodes,
0843: * index-to-base-row nodes, etc.
0844: *
0845: * @param predList A list of optimizable predicates that should
0846: * be pushed to this ResultSetNode, as determined by optimizer.
0847: * @return The modified query tree
0848: * @exception StandardException Thrown on error
0849: */
0850: public ResultSetNode modifyAccessPaths(PredicateList predList)
0851: throws StandardException {
0852: // Default behavior is to call the no-arg version of this method.
0853: return modifyAccessPaths();
0854: }
0855:
0856: ResultColumnDescriptor[] makeResultDescriptors(ExecutionContext ec) {
0857: return resultColumns.makeResultDescriptors(ec);
0858: }
0859:
0860: /*
0861: ** Check whether the column lengths and types of the result columns
0862: ** match the expressions under those columns. This is useful for
0863: ** INSERT and UPDATE statements. For SELECT statements this method
0864: ** should always return true. There is no need to call this for a
0865: ** DELETE statement.
0866: **
0867: ** @return true means all the columns match their expressions,
0868: ** false means at least one column does not match its
0869: ** expression
0870: */
0871:
0872: boolean columnTypesAndLengthsMatch() throws StandardException {
0873: return resultColumns.columnTypesAndLengthsMatch();
0874: }
0875:
0876: /**
0877: * Set the resultColumns in this ResultSetNode
0878: *
0879: * @param newRCL The new ResultColumnList for this ResultSetNode
0880: */
0881: public void setResultColumns(ResultColumnList newRCL) {
0882: resultColumns = newRCL;
0883: }
0884:
0885: /**
0886: * Get the resultColumns for this ResultSetNode
0887: *
0888: * @return ResultColumnList for this ResultSetNode
0889: */
0890: public ResultColumnList getResultColumns() {
0891: return resultColumns;
0892: }
0893:
0894: /**
0895: * Set the referencedTableMap in this ResultSetNode
0896: *
0897: * @param newRTM The new referencedTableMap for this ResultSetNode
0898: */
0899: public void setReferencedTableMap(JBitSet newRTM) {
0900: referencedTableMap = newRTM;
0901: }
0902:
0903: /**
0904: * Get the referencedTableMap for this ResultSetNode
0905: *
0906: * @return JBitSet Referenced table map for this ResultSetNode
0907: */
0908: public JBitSet getReferencedTableMap() {
0909: return referencedTableMap;
0910: }
0911:
0912: /**
0913: * Fill the referencedTableMap with this ResultSetNode.
0914: *
0915: * @param passedMap The table map to fill in.
0916: */
0917: public void fillInReferencedTableMap(JBitSet passedMap) {
0918: }
0919:
0920: /**
0921: * Check for (and reject) ? parameters directly under the ResultColumns.
0922: * This is done for SELECT statements.
0923: *
0924: * @exception StandardException Thrown if a ? parameter found
0925: * directly under a ResultColumn
0926: */
0927:
0928: public void rejectParameters() throws StandardException {
0929: /* Okay if no resultColumns yet - means no parameters there */
0930: if (resultColumns != null) {
0931: resultColumns.rejectParameters();
0932: }
0933: }
0934:
0935: /**
0936: * Check for (and reject) XML values directly under the ResultColumns.
0937: * This is done for SELECT/VALUES statements. We reject values
0938: * in this case because JDBC does not define an XML type/binding
0939: * and thus there's no standard way to pass such a type back
0940: * to a JDBC application.
0941: *
0942: * @exception StandardException Thrown if an XML value found
0943: * directly under a ResultColumn
0944: */
0945: public void rejectXMLValues() throws StandardException {
0946: if (resultColumns != null) {
0947: resultColumns.rejectXMLValues();
0948: }
0949: }
0950:
0951: /**
0952: * Rename generated result column names as '1', '2' etc... These will be the result
0953: * column names seen by JDBC clients.
0954: */
0955: public void renameGeneratedResultNames() throws StandardException {
0956: for (int i = 0; i < resultColumns.size(); i++) {
0957: ResultColumn rc = (ResultColumn) resultColumns.elementAt(i);
0958: if (rc.isNameGenerated())
0959: rc.setName(Integer.toString(i + 1));
0960: }
0961: }
0962:
0963: /**
0964: This method is overridden to allow a resultset node to know
0965: if it is the one controlling the statement -- i.e., it is
0966: the outermost result set node for the statement.
0967: */
0968: public void markStatementResultSet() {
0969: statementResultSet = true;
0970: }
0971:
0972: /**
0973: * This ResultSet is the source for an Insert. The target RCL
0974: * is in a different order and/or a superset of this RCL. In most cases
0975: * we will reorder and/or add defaults to the current RCL so that is
0976: * matches the target RCL. Those RSNs whose generate() method does
0977: * not handle projects will insert a PRN, with a new RCL which matches
0978: * the target RCL, above the current RSN.
0979: * NOTE - The new or enhanced RCL will be fully bound.
0980: *
0981: * @param numTargetColumns # of columns in target RCL
0982: * @param colMap int array representation of correspondence between
0983: * RCLs - colmap[i] = -1 -> missing in current RCL
0984: * colmap[i] = j -> targetRCL(i) <-> thisRCL(j+1)
0985: * @param dataDictionary DataDictionary to use
0986: * @param targetTD TableDescriptor for target if the target is not a VTI, null if a VTI
0987: * @param targetVTI Target description if it is a VTI, null if not a VTI
0988: *
0989: * @return ResultSetNode The new top of the tree
0990: *
0991: * @exception StandardException Thrown on error
0992: */
0993: public ResultSetNode enhanceRCLForInsert(int numTargetColumns,
0994: int[] colMap, DataDictionary dataDictionary,
0995: TableDescriptor targetTD, FromVTI targetVTI)
0996: throws StandardException {
0997: // our newResultCols are put into the bound form straight away.
0998: ResultColumnList newResultCols = (ResultColumnList) getNodeFactory()
0999: .getNode(C_NodeTypes.RESULT_COLUMN_LIST,
1000: getContextManager());
1001: int numResultSetColumns = resultColumns.size();
1002:
1003: /* Create a massaged version of the source RCL.
1004: * (Much simpler to build new list and then assign to source,
1005: * rather than massage the source list in place.)
1006: */
1007: for (int index = 0; index < numTargetColumns; index++) {
1008: ResultColumn newResultColumn = null;
1009: ColumnReference newColumnReference;
1010:
1011: if (colMap[index] != -1) {
1012: // getResultColumn uses 1-based positioning, so offset the colMap entry appropriately
1013: newResultColumn = resultColumns
1014: .getResultColumn(colMap[index] + 1);
1015: } else {
1016: newResultColumn = genNewRCForInsert(targetTD,
1017: targetVTI, index + 1, dataDictionary);
1018: }
1019:
1020: newResultCols.addResultColumn(newResultColumn);
1021: }
1022:
1023: /* Set the source RCL to the massaged version */
1024: resultColumns = newResultCols;
1025:
1026: return this ;
1027: }
1028:
1029: /**
1030: * Generate the RC/expression for an unspecified column in an insert.
1031: * Use the default if one exists.
1032: *
1033: * @param targetTD Target TableDescriptor if the target is not a VTI, null if a VTI.
1034: * @param targetVTI Target description if it is a VTI, null if not a VTI
1035: * @param columnNumber The column number
1036: * @param dataDictionary The DataDictionary
1037: * @return The RC/expression for the unspecified column.
1038: *
1039: * @exception StandardException Thrown on error
1040: */
1041: ResultColumn genNewRCForInsert(TableDescriptor targetTD,
1042: FromVTI targetVTI, int columnNumber,
1043: DataDictionary dataDictionary) throws StandardException {
1044: ResultColumn newResultColumn = null;
1045:
1046: // the i-th column's value was not specified, so create an
1047: // expression containing its default value (null for now)
1048: // REVISIT: will we store trailing nulls?
1049:
1050: if (targetVTI != null) {
1051: newResultColumn = targetVTI.getResultColumns()
1052: .getResultColumn(columnNumber);
1053: newResultColumn = newResultColumn.cloneMe();
1054: newResultColumn.setExpressionToNullNode();
1055: } else {
1056: // column position is 1-based, index is 0-based.
1057: ColumnDescriptor colDesc = targetTD
1058: .getColumnDescriptor(columnNumber);
1059: DataTypeDescriptor colType = colDesc.getType();
1060:
1061: // Check for defaults
1062: DefaultInfoImpl defaultInfo = (DefaultInfoImpl) colDesc
1063: .getDefaultInfo();
1064:
1065: //Column has constant default value ,
1066: //if it have defaultInfo and not be autoincrement.
1067: if (defaultInfo != null && !colDesc.isAutoincrement()) {
1068: //RESOLVEPARAMETER - skip the tree if we have the value
1069: /*
1070: if (defaultInfo.getDefaultValue() != null)
1071: {
1072: }
1073: else
1074: */
1075: {
1076: // Generate the tree for the default
1077: String defaultText = defaultInfo.getDefaultText();
1078: ValueNode defaultTree = parseDefault(defaultText);
1079: defaultTree = defaultTree.bindExpression(
1080: getFromList(), (SubqueryList) null,
1081: (Vector) null);
1082: newResultColumn = (ResultColumn) getNodeFactory()
1083: .getNode(C_NodeTypes.RESULT_COLUMN,
1084: defaultTree.getTypeServices(),
1085: defaultTree, getContextManager());
1086:
1087: DefaultDescriptor defaultDescriptor = colDesc
1088: .getDefaultDescriptor(dataDictionary);
1089: if (SanityManager.DEBUG) {
1090: SanityManager
1091: .ASSERT(defaultDescriptor != null,
1092: "defaultDescriptor expected to be non-null");
1093: }
1094: getCompilerContext().createDependency(
1095: defaultDescriptor);
1096: }
1097: } else if (colDesc.isAutoincrement()) {
1098: newResultColumn = (ResultColumn) getNodeFactory()
1099: .getNode(C_NodeTypes.RESULT_COLUMN, colDesc,
1100: null, getContextManager());
1101: newResultColumn.setAutoincrementGenerated();
1102: } else {
1103: newResultColumn = (ResultColumn) getNodeFactory()
1104: .getNode(
1105: C_NodeTypes.RESULT_COLUMN,
1106: colType,
1107: getNullNode(colType.getTypeId(),
1108: getContextManager()),
1109: getContextManager());
1110: }
1111: }
1112:
1113: // Mark the new RC as generated for an unmatched column in an insert
1114: newResultColumn.markGeneratedForUnmatchedColumnInInsert();
1115:
1116: return newResultColumn;
1117: }
1118:
1119: /**
1120: * Parse a default and turn it into a query tree.
1121: *
1122: * @param defaultText Text of Default.
1123: *
1124: * @return The parsed default as a query tree.
1125: *
1126: * @exception StandardException Thrown on failure
1127: */
1128: public ValueNode parseDefault(String defaultText)
1129: throws StandardException {
1130: Parser p;
1131: ValueNode defaultTree;
1132: LanguageConnectionContext lcc = getLanguageConnectionContext();
1133: CompilerContext compilerContext = getCompilerContext();
1134:
1135: /* Get a Statement to pass to the parser */
1136:
1137: /* We're all set up to parse. We have to build a compilable SQL statement
1138: * before we can parse - So, we goober up a VALUES defaultText.
1139: */
1140: String values = "VALUES " + defaultText;
1141:
1142: /*
1143: ** Get a new compiler context, so the parsing of the select statement
1144: ** doesn't mess up anything in the current context (it could clobber
1145: ** the ParameterValueSet, for example).
1146: */
1147: CompilerContext newCC = lcc.pushCompilerContext();
1148:
1149: p = newCC.getParser();
1150:
1151: /* Finally, we can call the parser */
1152: // Since this is always nested inside another SQL statement, so topLevel flag
1153: // should be false
1154: QueryTreeNode qt = p.parseStatement(values);
1155: if (SanityManager.DEBUG) {
1156: if (!(qt instanceof CursorNode)) {
1157: SanityManager
1158: .THROWASSERT("qt expected to be instanceof CursorNode, not "
1159: + qt.getClass().getName());
1160: }
1161: CursorNode cn = (CursorNode) qt;
1162: if (!(cn.getResultSetNode() instanceof RowResultSetNode)) {
1163: SanityManager
1164: .THROWASSERT("cn.getResultSetNode() expected to be instanceof RowResultSetNode, not "
1165: + cn.getResultSetNode().getClass()
1166: .getName());
1167: }
1168: }
1169:
1170: defaultTree = ((ResultColumn) ((CursorNode) qt)
1171: .getResultSetNode().getResultColumns().elementAt(0))
1172: .getExpression();
1173:
1174: lcc.popCompilerContext(newCC);
1175:
1176: return defaultTree;
1177: }
1178:
1179: /**
1180: * Make a ResultDescription for use in a ResultSet.
1181: * This is useful when generating/executing a NormalizeResultSet, since
1182: * it can appear anywhere in the tree.
1183: *
1184: * @return A ResultDescription for this ResultSetNode.
1185: */
1186:
1187: public ResultDescription makeResultDescription() {
1188: ExecutionContext ec = (ExecutionContext) getContextManager()
1189: .getContext(ExecutionContext.CONTEXT_ID);
1190: ResultColumnDescriptor[] colDescs = makeResultDescriptors(ec);
1191:
1192: return ec.getExecutionFactory().getResultDescription(colDescs,
1193: null);
1194: }
1195:
1196: /**
1197: Determine if this result set is updatable or not, for a cursor
1198: (i.e., is it a cursor-updatable select). This returns false
1199: and we expect selectnode to refine it for further checking.
1200: *
1201: * @exception StandardException Thrown on error
1202: */
1203: boolean isUpdatableCursor(DataDictionary dd)
1204: throws StandardException {
1205: if (SanityManager.DEBUG)
1206: SanityManager.DEBUG("DumpUpdateCheck",
1207: "cursor is not a select result set");
1208: return false;
1209: }
1210:
1211: /**
1212: return the target table of an updatable cursor result set.
1213: since this is not updatable, just return null.
1214: */
1215: FromTable getCursorTargetTable() {
1216: return null;
1217: }
1218:
1219: /**
1220: Mark this ResultSetNode as the target table of an updatable
1221: cursor. Most types of ResultSetNode can't be target tables.
1222: @return true if the target table supports positioned updates.
1223: */
1224: public boolean markAsCursorTargetTable() {
1225: return false;
1226: }
1227:
1228: /**
1229: Mark this ResultSetNode as *not* the target table of an updatable
1230: cursor.
1231: */
1232: void notCursorTargetTable() {
1233: cursorTargetTable = false;
1234: }
1235:
1236: /**
1237: * Put a ProjectRestrictNode on top of this ResultSetNode.
1238: * ColumnReferences must continue to point to the same ResultColumn, so
1239: * that ResultColumn must percolate up to the new PRN. However,
1240: * that ResultColumn will point to a new expression, a VirtualColumnNode,
1241: * which points to the FromTable and the ResultColumn that is the source for
1242: * the ColumnReference.
1243: * (The new PRN will have the original of the ResultColumnList and
1244: * the ResultColumns from that list. The FromTable will get shallow copies
1245: * of the ResultColumnList and its ResultColumns. ResultColumn.expression
1246: * will remain at the FromTable, with the PRN getting a new
1247: * VirtualColumnNode for each ResultColumn.expression.)
1248: *
1249: * This is useful for UNIONs, where we want to generate a DistinctNode above
1250: * the UnionNode to eliminate the duplicates, because DistinctNodes expect
1251: * their immediate child to be a PRN.
1252: *
1253: * @return The generated ProjectRestrictNode atop the original ResultSetNode.
1254: *
1255: * @exception StandardException Thrown on error
1256: */
1257:
1258: public ResultSetNode genProjectRestrict() throws StandardException {
1259: ResultColumnList prRCList;
1260:
1261: /* We get a shallow copy of the ResultColumnList and its
1262: * ResultColumns. (Copy maintains ResultColumn.expression for now.)
1263: */
1264: prRCList = resultColumns;
1265: resultColumns = resultColumns.copyListAndObjects();
1266:
1267: /* Replace ResultColumn.expression with new VirtualColumnNodes
1268: * in the ProjectRestrictNode's ResultColumnList. (VirtualColumnNodes include
1269: * pointers to source ResultSetNode, this, and source ResultColumn.)
1270: */
1271: prRCList.genVirtualColumnNodes(this , resultColumns);
1272:
1273: /* Finally, we create the new ProjectRestrictNode */
1274: return (ResultSetNode) getNodeFactory().getNode(
1275: C_NodeTypes.PROJECT_RESTRICT_NODE, this , prRCList,
1276: null, /* Restriction */
1277: null, /* Restriction as PredicateList */
1278: null, /* Project subquery list */
1279: null, /* Restrict subquery list */
1280: null, getContextManager());
1281: }
1282:
1283: /**
1284: * Put a ProjectRestrictNode on top of each FromTable in the FromList.
1285: * ColumnReferences must continue to point to the same ResultColumn, so
1286: * that ResultColumn must percolate up to the new PRN. However,
1287: * that ResultColumn will point to a new expression, a VirtualColumnNode,
1288: * which points to the FromTable and the ResultColumn that is the source for
1289: * the ColumnReference.
1290: * (The new PRN will have the original of the ResultColumnList and
1291: * the ResultColumns from that list. The FromTable will get shallow copies
1292: * of the ResultColumnList and its ResultColumns. ResultColumn.expression
1293: * will remain at the FromTable, with the PRN getting a new
1294: * VirtualColumnNode for each ResultColumn.expression.)
1295: * We then project out the non-referenced columns. If there are no referenced
1296: * columns, then the PRN's ResultColumnList will consist of a single ResultColumn
1297: * whose expression is 1.
1298: *
1299: * @param numTables Number of tables in the DML Statement
1300: *
1301: * @return The generated ProjectRestrictNode atop the original FromTable.
1302: *
1303: * @exception StandardException Thrown on error
1304: */
1305:
1306: protected ResultSetNode genProjectRestrict(int numTables)
1307: throws StandardException {
1308: return genProjectRestrict();
1309: }
1310:
1311: /**
1312: * Put a NormalizeResultSetNode on top of the specified ResultSetNode.
1313: * ColumnReferences must continue to point to the same ResultColumn, so
1314: * that ResultColumn must percolate up to the new PRN. However,
1315: * that ResultColumn will point to a new expression, a VirtualColumnNode,
1316: * which points to the FromTable and the ResultColumn that is the source for
1317: * the ColumnReference.
1318: * (The new NRSN will have the original of the ResultColumnList and
1319: * the ResultColumns from that list. The FromTable will get shallow copies
1320: * of the ResultColumnList and its ResultColumns. ResultColumn.expression
1321: * will remain at the FromTable, with the PRN getting a new
1322: * VirtualColumnNode for each ResultColumn.expression.)
1323: *
1324: * This is useful for UNIONs, where we want to generate a DistinctNode above
1325: * the UnionNode to eliminate the duplicates, because the type going into the
1326: * sort has to agree with what the sort expects.
1327: * (insert into t1 (smallintcol) values 1 union all values 2;
1328: *
1329: * @param normalizeChild Child result set for new NRSN.
1330: * @param forUpdate If the normalize result set is being used as a
1331: * child for an update statement, then this is true.
1332: *
1333: * @return The generated NormalizeResultSetNode atop the original UnionNode.
1334: *
1335: * @exception StandardException Thrown on error
1336: * @see NormalizeResultSetNode#init
1337: */
1338:
1339: public NormalizeResultSetNode genNormalizeResultSetNode(
1340: ResultSetNode normalizeChild, boolean forUpdate)
1341: throws StandardException {
1342: NormalizeResultSetNode nrsn;
1343: ResultColumnList prRCList;
1344:
1345: /* We get a shallow copy of the ResultColumnList and its
1346: * ResultColumns. (Copy maintains ResultColumn.expression for now.)
1347: */
1348: prRCList = resultColumns;
1349: resultColumns = resultColumns.copyListAndObjects();
1350:
1351: /* Replace ResultColumn.expression with new VirtualColumnNodes
1352: * in the NormalizeResultSetNode's ResultColumnList. (VirtualColumnNodes include
1353: * pointers to source ResultSetNode, this, and source ResultColumn.)
1354: */
1355: prRCList.genVirtualColumnNodes(this , resultColumns);
1356:
1357: /* Finally, we create the new NormalizeResultSetNode */
1358: nrsn = (NormalizeResultSetNode) getNodeFactory().getNode(
1359: C_NodeTypes.NORMALIZE_RESULT_SET_NODE, normalizeChild,
1360: prRCList, null, new Boolean(forUpdate),
1361: getContextManager());
1362: // Propagate the referenced table map if it's already been created
1363: if (normalizeChild.getReferencedTableMap() != null) {
1364: nrsn.setReferencedTableMap((JBitSet) normalizeChild
1365: .getReferencedTableMap().clone());
1366: }
1367: return nrsn;
1368: }
1369:
1370: /**
1371: * Generate the code for a NormalizeResultSet.
1372: The call must push two items before calling this method
1373: <OL>
1374: <LI> pushGetResultSetFactoryExpression
1375: <LI> the expression to normalize
1376: </OL>
1377: *
1378: * @param acb The ActivationClassBuilder
1379: * @param mb The method to put the generated code in
1380: * @param resultSetNumber The result set number for the NRS
1381: * @param resultDescription The ERD for the ResultSet
1382: *
1383: *
1384: * @exception StandardException Thrown on error
1385: */
1386: public void generateNormalizationResultSet(
1387: ActivationClassBuilder acb, MethodBuilder mb,
1388: int resultSetNumber, ResultDescription resultDescription)
1389: throws StandardException {
1390: int erdNumber = acb.addItem(resultDescription);
1391:
1392: // instance and first arg are pushed by caller
1393:
1394: mb.push(resultSetNumber);
1395: mb.push(erdNumber);
1396: mb.push(getCostEstimate().rowCount());
1397: mb.push(getCostEstimate().getEstimatedCost());
1398: mb.push(false);
1399:
1400: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
1401: "getNormalizeResultSet", ClassName.NoPutResultSet, 6);
1402: }
1403:
1404: /**
1405: * The optimizer's decision on the access path for a result set
1406: * may require the generation of extra result sets. For example,
1407: * if it chooses an index for a FromBaseTable, we need an IndexToBaseRowNode
1408: * above the FromBaseTable (and the FromBaseTable has to change its
1409: * column list to match the index.
1410: *
1411: * This method in the parent class does not generate any extra result sets.
1412: * It may be overridden in child classes.
1413: *
1414: * @return A ResultSetNode tree modified to do any extra processing for
1415: * the chosen access path
1416: *
1417: * @exception StandardException Thrown on error
1418: */
1419: public ResultSetNode changeAccessPath() throws StandardException {
1420: return this ;
1421: }
1422:
1423: /**
1424: * Search to see if a query references the specifed table name.
1425: *
1426: * @param name Table name (String) to search for.
1427: * @param baseTable Whether or not name is for a base table
1428: *
1429: * @return true if found, else false
1430: *
1431: * @exception StandardException Thrown on error
1432: */
1433: public boolean referencesTarget(String name, boolean baseTable)
1434: throws StandardException {
1435: return false;
1436: }
1437:
1438: /**
1439: * Return whether or not this ResultSetNode contains a subquery with a
1440: * reference to the specified target.
1441: *
1442: * @param name The table name.
1443: *
1444: * @return boolean Whether or not a reference to the table was found.
1445: *
1446: * @exception StandardException Thrown on error
1447: */
1448: boolean subqueryReferencesTarget(String name, boolean baseTable)
1449: throws StandardException {
1450: return false;
1451: }
1452:
1453: /**
1454: * Return whether or not the underlying ResultSet tree will return
1455: * a single row, at most.
1456: * This is important for join nodes where we can save the extra next
1457: * on the right side if we know that it will return at most 1 row.
1458: *
1459: * @return Whether or not the underlying ResultSet tree will return a single row.
1460: * @exception StandardException Thrown on error
1461: */
1462: public boolean isOneRowResultSet() throws StandardException {
1463: // Default is false
1464: return false;
1465: }
1466:
1467: /**
1468: * Return whether or not the underlying ResultSet tree is for a NOT EXISTS
1469: * join.
1470: *
1471: * @return Whether or not the underlying ResultSet tree if for NOT EXISTS.
1472: */
1473: public boolean isNotExists() {
1474: // Default is false
1475: return false;
1476: }
1477:
1478: /**
1479: * Get an optimizer to use for this ResultSetNode. Only get it once -
1480: * subsequent calls return the same optimizer.
1481: *
1482: * @exception StandardException Thrown on error
1483: */
1484: protected Optimizer getOptimizer(OptimizableList optList,
1485: OptimizablePredicateList predList,
1486: DataDictionary dataDictionary,
1487: RequiredRowOrdering requiredRowOrdering)
1488: throws StandardException {
1489: if (optimizer == null) {
1490: /* Get an optimizer. */
1491: OptimizerFactory optimizerFactory = getLanguageConnectionContext()
1492: .getOptimizerFactory();
1493:
1494: optimizer = optimizerFactory.getOptimizer(optList,
1495: predList, dataDictionary, requiredRowOrdering,
1496: getCompilerContext().getNumTables(),
1497: getLanguageConnectionContext());
1498: }
1499:
1500: optimizer.prepForNextRound();
1501: return optimizer;
1502: }
1503:
1504: /**
1505: * Get the optimizer for this result set.
1506: *
1507: * @return If this.optimizer has has already been created by the
1508: * getOptimizer() method above, then return it; otherwise,
1509: * return null.
1510: */
1511: protected OptimizerImpl getOptimizerImpl() {
1512: // Note that the optimizer might be null because it's possible that
1513: // we'll get here before any calls to getOptimizer() were made, which
1514: // can happen if we're trying to save a "best path" but we haven't
1515: // actually found one yet. In that case we just return the "null"
1516: // value; the caller must check for it and behave appropriately.
1517: // Ex. see TableOperatorNode.addOrLoadBestPlanMapping().
1518: return (OptimizerImpl) optimizer;
1519: }
1520:
1521: /**
1522: * Get a cost estimate to use for this ResultSetNode.
1523: *
1524: * @exception StandardException Thrown on error
1525: */
1526: protected CostEstimate getNewCostEstimate()
1527: throws StandardException {
1528: OptimizerFactory optimizerFactory = getLanguageConnectionContext()
1529: .getOptimizerFactory();
1530: return optimizerFactory.getCostEstimate();
1531: }
1532:
1533: /**
1534: * Accept a visitor, and call v.visit()
1535: * on child nodes as necessary.
1536: *
1537: * @param v the visitor
1538: *
1539: * @exception StandardException on error
1540: */
1541: public Visitable accept(Visitor v) throws StandardException {
1542: Visitable returnNode = v.visit(this );
1543:
1544: if (v.skipChildren(this )) {
1545: return returnNode;
1546: }
1547:
1548: if (resultColumns != null && !v.stopTraversal()) {
1549: resultColumns = (ResultColumnList) resultColumns.accept(v);
1550: }
1551: return returnNode;
1552: }
1553:
1554: /**
1555: * Consider materialization for this ResultSet tree if it is valid and cost effective
1556: * (It is not valid if incorrect results would be returned.)
1557: *
1558: * @return Top of the new/same ResultSet tree.
1559: *
1560: * @exception StandardException Thrown on error
1561: */
1562: public ResultSetNode considerMaterialization(JBitSet outerTables)
1563: throws StandardException {
1564: return this ;
1565: }
1566:
1567: /**
1568: * Return whether or not to materialize this ResultSet tree.
1569: *
1570: * @return Whether or not to materialize this ResultSet tree.
1571: * would return valid results.
1572: *
1573: * @exception StandardException Thrown on error
1574: */
1575: public boolean performMaterialization(JBitSet outerTables)
1576: throws StandardException {
1577: return false;
1578: }
1579:
1580: /**
1581: * Determine whether or not the specified name is an exposed name in
1582: * the current query block.
1583: *
1584: * @param name The specified name to search for as an exposed name.
1585: * @param schemaName Schema name, if non-null.
1586: * @param exactMatch Whether or not we need an exact match on specified schema and table
1587: * names or match on table id.
1588: *
1589: * @return The FromTable, if any, with the exposed name.
1590: *
1591: * @exception StandardException Thrown on error
1592: */
1593: protected FromTable getFromTableByName(String name,
1594: String schemaName, boolean exactMatch)
1595: throws StandardException {
1596: if (SanityManager.DEBUG) {
1597: SanityManager
1598: .THROWASSERT("getFromTableByName() not expected to be called for "
1599: + getClass().getName());
1600: }
1601: return null;
1602: }
1603:
1604: /**
1605: * Decrement (query block) level (0-based) for
1606: * all of the tables in this ResultSet tree.
1607: * This is useful when flattening a subquery.
1608: *
1609: * @param decrement The amount to decrement by.
1610: */
1611: abstract void decrementLevel(int decrement);
1612:
1613: /**
1614: * Push the order by list down from the cursor node
1615: * into its child result set so that the optimizer
1616: * has all of the information that it needs to
1617: * consider sort avoidance.
1618: *
1619: * @param orderByList The order by list
1620: */
1621: void pushOrderByList(OrderByList orderByList) {
1622: if (SanityManager.DEBUG) {
1623: SanityManager
1624: .THROWASSERT("pushOrderByList() not expected to be called for "
1625: + getClass().getName());
1626: }
1627: }
1628:
1629: /**
1630: * General logic shared by Core compilation and by the Replication Filter
1631: * compiler. A couple ResultSets (the ones used by PREPARE SELECT FILTER)
1632: * implement this method.
1633: *
1634: * @param acb The ExpressionClassBuilder for the class being built
1635: * @param mb The method the expression will go into
1636: *
1637: *
1638: * @exception StandardException Thrown on error
1639: */
1640:
1641: public void generateResultSet(ExpressionClassBuilder acb,
1642: MethodBuilder mb) throws StandardException {
1643: System.out.println("I am a " + getClass());
1644: if (SanityManager.DEBUG)
1645: SanityManager.NOTREACHED();
1646: return;
1647: }
1648:
1649: /**
1650: * Get the lock mode for the target of an update statement
1651: * (a delete or update). The update mode will always be row for
1652: * CurrentOfNodes. It will be table if there is no where clause.
1653: *
1654: * @see TransactionController
1655: *
1656: * @return The lock mode
1657: */
1658: public int updateTargetLockMode() {
1659: return TransactionController.MODE_TABLE;
1660: }
1661:
1662: /**
1663: * Mark this node and its children as not being a flattenable join.
1664: */
1665: void notFlattenableJoin() {
1666: }
1667:
1668: /**
1669: * Return whether or not the underlying ResultSet tree
1670: * is ordered on the specified columns.
1671: * RESOLVE - This method currently only considers the outermost table
1672: * of the query block.
1673: *
1674: * @param crs The specified ColumnReference[]
1675: * @param permuteOrdering Whether or not the order of the CRs in the array can be permuted
1676: * @param fbtVector Vector that is to be filled with the FromBaseTable
1677: *
1678: * @return Whether the underlying ResultSet tree
1679: * is ordered on the specified column.
1680: *
1681: * @exception StandardException Thrown on error
1682: */
1683: boolean isOrderedOn(ColumnReference[] crs, boolean permuteOrdering,
1684: Vector fbtVector) throws StandardException {
1685: return false;
1686: }
1687:
1688: /**
1689: * Return whether or not this ResultSet tree is guaranteed to return
1690: * at most 1 row based on heuristics. (A RowResultSetNode and a
1691: * SELECT with a non-grouped aggregate will return at most 1 row.)
1692: *
1693: * @return Whether or not this ResultSet tree is guaranteed to return
1694: * at most 1 row based on heuristics.
1695: */
1696: boolean returnsAtMostOneRow() {
1697: return false;
1698: }
1699:
1700: /**
1701: * Replace any DEFAULTs with the associated tree for the default.
1702: *
1703: * @param ttd The TableDescriptor for the target table.
1704: * @param tcl The RCL for the target table.
1705: *
1706: * @exception StandardException Thrown on error
1707: */
1708: void replaceDefaults(TableDescriptor ttd, ResultColumnList tcl)
1709: throws StandardException {
1710: // Only subclasses with something to do override this.
1711: }
1712:
1713: /**
1714: * Is it possible to do a distinct scan on this ResultSet tree.
1715: * (See SelectNode for the criteria.)
1716: *
1717: * @param distinctColumns the set of distinct columns
1718: * @return Whether or not it is possible to do a distinct scan on this ResultSet tree.
1719: */
1720: boolean isPossibleDistinctScan(Set distinctColumns) {
1721: return false;
1722: }
1723:
1724: /**
1725: * Mark the underlying scan as a distinct scan.
1726: */
1727: void markForDistinctScan() {
1728: if (SanityManager.DEBUG) {
1729: SanityManager
1730: .THROWASSERT("markForDistinctScan() not expected to be called for "
1731: + getClass().getName());
1732: }
1733: }
1734:
1735: /**
1736: * Notify the underlying result set tree that the result is
1737: * ordering dependent. (For example, no bulk fetch on an index
1738: * if under an IndexRowToBaseRow.)
1739: */
1740: void markOrderingDependent() {
1741: if (SanityManager.DEBUG) {
1742: SanityManager
1743: .THROWASSERT("markOrderingDependent() not expected to be called for "
1744: + getClass().getName());
1745: }
1746: }
1747:
1748: /**
1749: * Count the number of distinct aggregates in the list.
1750: * By 'distinct' we mean aggregates of the form:
1751: * <UL><I>SELECT MAX(DISTINCT x) FROM T<\I><\UL>
1752: *
1753: * @return number of aggregates
1754: */
1755: protected static final int numDistinctAggregates(
1756: Vector aggregateVector) {
1757: int count = 0;
1758: int size = aggregateVector.size();
1759:
1760: for (int index = 0; index < size; index++) {
1761: count += (((AggregateNode) aggregateVector.elementAt(index))
1762: .isDistinct() == true) ? 1 : 0;
1763: }
1764:
1765: return count;
1766: }
1767:
1768: // It may be we have a SELECT view underneath a LOJ.
1769: // Return null for now.. we don't do any optimization.
1770: public JBitSet LOJgetReferencedTables(int numTables)
1771: throws StandardException {
1772: if (this instanceof FromTable) {
1773: if (((FromTable) this ).tableNumber != -1) {
1774: JBitSet map = new JBitSet(numTables);
1775: map.set(((FromTable) this).tableNumber);
1776: return map;
1777: }
1778: }
1779:
1780: return null;
1781: }
1782:
1783: }
|