001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.IndexToBaseRowNode
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import org.apache.derby.iapi.services.context.ContextManager;
025:
026: import org.apache.derby.iapi.sql.compile.AccessPath;
027: import org.apache.derby.iapi.sql.compile.CostEstimate;
028: import org.apache.derby.iapi.sql.compile.Optimizable;
029:
030: import org.apache.derby.iapi.sql.dictionary.ConglomerateDescriptor;
031:
032: import org.apache.derby.impl.sql.compile.ActivationClassBuilder;
033:
034: import org.apache.derby.iapi.error.StandardException;
035:
036: import org.apache.derby.iapi.services.compiler.MethodBuilder;
037:
038: import org.apache.derby.iapi.sql.execute.NoPutResultSet;
039:
040: import org.apache.derby.iapi.sql.Activation;
041: import org.apache.derby.iapi.sql.ResultSet;
042:
043: import org.apache.derby.iapi.services.loader.GeneratedMethod;
044:
045: import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;
046:
047: import org.apache.derby.iapi.services.io.FormatableBitSet;
048:
049: import org.apache.derby.catalog.types.ReferencedColumnsDescriptorImpl;
050: import org.apache.derby.iapi.reference.ClassName;
051: import org.apache.derby.iapi.services.classfile.VMOpcode;
052:
053: import java.util.Properties;
054: import java.util.Vector;
055:
056: /**
057: * This node type translates an index row to a base row. It takes a
058: * FromBaseTable as its source ResultSetNode, and generates an
059: * IndexRowToBaseRowResultSet that takes a TableScanResultSet on an
060: * index conglomerate as its source.
061: */
062: public class IndexToBaseRowNode extends FromTable {
063: protected FromBaseTable source;
064: protected ConglomerateDescriptor baseCD;
065: protected boolean cursorTargetTable;
066: protected PredicateList restrictionList;
067: protected boolean forUpdate;
068: private FormatableBitSet heapReferencedCols;
069: private FormatableBitSet indexReferencedCols;
070:
071: public void init(Object source, Object baseCD,
072: Object resultColumns, Object cursorTargetTable,
073: Object heapReferencedCols, Object indexReferencedCols,
074: Object restrictionList, Object forUpdate,
075: Object tableProperties) {
076: super .init(null, tableProperties);
077: this .source = (FromBaseTable) source;
078: this .baseCD = (ConglomerateDescriptor) baseCD;
079: this .resultColumns = (ResultColumnList) resultColumns;
080: this .cursorTargetTable = ((Boolean) cursorTargetTable)
081: .booleanValue();
082: this .restrictionList = (PredicateList) restrictionList;
083: this .forUpdate = ((Boolean) forUpdate).booleanValue();
084: this .heapReferencedCols = (FormatableBitSet) heapReferencedCols;
085: this .indexReferencedCols = (FormatableBitSet) indexReferencedCols;
086: }
087:
088: /** @see Optimizable#forUpdate */
089: public boolean forUpdate() {
090: return source.forUpdate();
091: }
092:
093: /** @see Optimizable#getTrulyTheBestAccessPath */
094: public AccessPath getTrulyTheBestAccessPath() {
095: // Get AccessPath comes from base table.
096: return ((Optimizable) source).getTrulyTheBestAccessPath();
097: }
098:
099: public CostEstimate getCostEstimate() {
100: return source.getTrulyTheBestAccessPath().getCostEstimate();
101: }
102:
103: public CostEstimate getFinalCostEstimate() {
104: return source.getFinalCostEstimate();
105: }
106:
107: /**
108: * Return whether or not the underlying ResultSet tree
109: * is ordered on the specified columns.
110: * RESOLVE - This method currently only considers the outermost table
111: * of the query block.
112: *
113: * @param crs The specified ColumnReference[]
114: * @param permuteOrdering Whether or not the order of the CRs in the array can be permuted
115: * @param fbtVector Vector that is to be filled with the FromBaseTable
116: *
117: * @return Whether the underlying ResultSet tree
118: * is ordered on the specified column.
119: *
120: * @exception StandardException Thrown on error
121: */
122: boolean isOrderedOn(ColumnReference[] crs, boolean permuteOrdering,
123: Vector fbtVector) throws StandardException {
124: return source.isOrderedOn(crs, permuteOrdering, fbtVector);
125: }
126:
127: /**
128: * Generation of an IndexToBaseRowNode creates an
129: * IndexRowToBaseRowResultSet, which uses the RowLocation in the last
130: * column of an index row to get the row from the base conglomerate (heap).
131: *
132: * @param acb The ActivationClassBuilder for the class being built
133: * @param mb the method for the method to be built
134: *
135: * @exception StandardException Thrown on error
136: */
137: public void generate(ActivationClassBuilder acb, MethodBuilder mb)
138: throws StandardException {
139: ValueNode restriction = null;
140:
141: /*
142: ** Get the next ResultSet #, so that we can number this ResultSetNode,
143: ** its ResultColumnList and ResultSet.
144: */
145: assignResultSetNumber();
146:
147: // Get the CostEstimate info for the underlying scan
148: costEstimate = getFinalCostEstimate();
149:
150: /* Put the predicates back into the tree */
151: if (restrictionList != null) {
152: restriction = restrictionList.restorePredicates();
153: /* Allow the restrictionList to get garbage collected now
154: * that we're done with it.
155: */
156: restrictionList = null;
157: }
158:
159: // for the restriction, we generate an exprFun
160: // that evaluates the expression of the clause
161: // against the current row of the child's result.
162: // if the restriction is empty, simply pass null
163: // to optimize for run time performance.
164:
165: // generate the function and initializer:
166: // Note: Boolean lets us return nulls (boolean would not)
167: // private Boolean exprN()
168: // {
169: // return <<restriction.generate(ps)>>;
170: // }
171: // static Method exprN = method pointer to exprN;
172:
173: int heapColRefItem = -1;
174: int indexColRefItem = -1;
175: if (heapReferencedCols != null) {
176: heapColRefItem = acb.addItem(heapReferencedCols);
177: }
178: if (indexReferencedCols != null) {
179: indexColRefItem = acb.addItem(indexReferencedCols);
180: }
181:
182: /* Create the ReferencedColumnsDescriptorImpl which tells which columns
183: * come from the index.
184: */
185: int indexColMapItem = acb
186: .addItem(new ReferencedColumnsDescriptorImpl(
187: getIndexColMapping()));
188: long heapConglomNumber = baseCD.getConglomerateNumber();
189: StaticCompiledOpenConglomInfo scoci = getLanguageConnectionContext()
190: .getTransactionCompile().getStaticCompiledConglomInfo(
191: heapConglomNumber);
192:
193: acb.pushGetResultSetFactoryExpression(mb);
194:
195: mb.push(heapConglomNumber);
196: mb.push(acb.addItem(scoci));
197: source.generate(acb, mb);
198:
199: mb.upCast(ClassName.NoPutResultSet);
200:
201: resultColumns.generateHolder(acb, mb, heapReferencedCols,
202: indexReferencedCols);
203: mb.push(resultSetNumber);
204: mb.push(source.getBaseTableName());
205: mb.push(heapColRefItem);
206: mb.push(indexColRefItem);
207: mb.push(indexColMapItem);
208:
209: // if there is no restriction, we just want to pass null.
210: if (restriction == null) {
211: mb.pushNull(ClassName.GeneratedMethod);
212: } else {
213: // this sets up the method and the static field.
214: // generates:
215: // Object userExprFun { }
216: MethodBuilder userExprFun = acb.newUserExprFun();
217:
218: // restriction knows it is returning its value;
219:
220: /* generates:
221: * return <restriction.generate(acb)>;
222: * and adds it to userExprFun
223: * NOTE: The explicit cast to DataValueDescriptor is required
224: * since the restriction may simply be a boolean column or subquery
225: * which returns a boolean. For example:
226: * where booleanColumn
227: */
228: restriction.generate(acb, userExprFun);
229: userExprFun.methodReturn();
230:
231: // we are done modifying userExprFun, complete it.
232: userExprFun.complete();
233:
234: // restriction is used in the final result set as an access of the new static
235: // field holding a reference to this new method.
236: // generates:
237: // ActivationClass.userExprFun
238: // which is the static field that "points" to the userExprFun
239: // that evaluates the where clause.
240: acb.pushMethodReference(mb, userExprFun);
241: }
242:
243: mb.push(forUpdate);
244: mb.push(costEstimate.rowCount());
245: mb.push(costEstimate.getEstimatedCost());
246:
247: mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null,
248: "getIndexRowToBaseRowResultSet",
249: ClassName.NoPutResultSet, 13);
250:
251: /* The IndexRowToBaseRowResultSet generator is what we return */
252:
253: /*
254: ** Remember if this result set is the cursor target table, so we
255: ** can know which table to use when doing positioned update and delete.
256: */
257: if (cursorTargetTable) {
258: acb.rememberCursorTarget(mb);
259: }
260: }
261:
262: /**
263: * Return whether or not the underlying ResultSet tree will return
264: * a single row, at most.
265: * This is important for join nodes where we can save the extra next
266: * on the right side if we know that it will return at most 1 row.
267: *
268: * @return Whether or not the underlying ResultSet tree will return a single row.
269: * @exception StandardException Thrown on error
270: */
271: public boolean isOneRowResultSet() throws StandardException {
272: // Default is false
273: return source.isOneRowResultSet();
274: }
275:
276: /**
277: * Return whether or not the underlying FBT is for NOT EXISTS.
278: *
279: * @return Whether or not the underlying FBT is for NOT EXISTS.
280: */
281: public boolean isNotExists() {
282: return source.isNotExists();
283: }
284:
285: /**
286: * Decrement (query block) level (0-based) for this FromTable.
287: * This is useful when flattening a subquery.
288: *
289: * @param decrement The amount to decrement by.
290: */
291: void decrementLevel(int decrement) {
292: source.decrementLevel(decrement);
293: }
294:
295: /**
296: * Get the lock mode for the target of an update statement
297: * (a delete or update). The update mode will always be row for
298: * CurrentOfNodes. It will be table if there is no where clause.
299: *
300: * @return The lock mode
301: */
302: public int updateTargetLockMode() {
303: return source.updateTargetLockMode();
304: }
305:
306: /**
307: * Notify the underlying result set tree that the result is
308: * ordering dependent. (For example, no bulk fetch on an index
309: * if under an IndexRowToBaseRow.)
310: */
311: void markOrderingDependent() {
312: /* NOTE: We use a different method to tell a FBT that
313: * it cannot do a bulk fetch as the ordering issues are
314: * specific to a FBT being under an IRTBR as opposed to a
315: * FBT being under a PRN, etc.
316: */
317: source.disableBulkFetch();
318: }
319:
320: /**
321: * Fill in the column mapping for those columns coming from the index.
322: *
323: * @return The int[] with the mapping.
324: */
325: private int[] getIndexColMapping() {
326: int rclSize = resultColumns.size();
327: int[] indexColMapping = new int[rclSize];
328:
329: for (int index = 0; index < rclSize; index++) {
330: ResultColumn rc = (ResultColumn) resultColumns
331: .elementAt(index);
332: if (indexReferencedCols != null
333: && rc.getExpression() instanceof VirtualColumnNode) {
334: // Column is coming from index
335: VirtualColumnNode vcn = (VirtualColumnNode) rc
336: .getExpression();
337: indexColMapping[index] = vcn.getSourceColumn()
338: .getVirtualColumnId() - 1;
339: } else {
340: // Column is not coming from index
341: indexColMapping[index] = -1;
342: }
343: }
344:
345: return indexColMapping;
346: }
347:
348: }
|