--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.execute.MultiProbeTableScanResultSet\r
+\r
+ Licensed to the Apache Software Foundation (ASF) under one or more\r
+ contributor license agreements. See the NOTICE file distributed with\r
+ this work for additional information regarding copyright ownership.\r
+ The ASF licenses this file to you under the Apache License, Version 2.0\r
+ (the "License"); you may not use this file except in compliance with\r
+ the License. You may obtain a copy of the License at\r
+\r
+ http://www.apache.org/licenses/LICENSE-2.0\r
+\r
+ Unless required by applicable law or agreed to in writing, software\r
+ distributed under the License is distributed on an "AS IS" BASIS,\r
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ See the License for the specific language governing permissions and\r
+ limitations under the License.\r
+\r
+ */\r
+\r
+package org.apache.derby.impl.sql.execute;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+\r
+import org.apache.derby.iapi.store.access.Qualifier;\r
+import org.apache.derby.iapi.store.access.StaticCompiledOpenConglomInfo;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.compile.RowOrdering;\r
+import org.apache.derby.iapi.sql.execute.CursorResultSet;\r
+import org.apache.derby.iapi.sql.execute.ExecRow;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+// These are for javadoc "@see" tags.\r
+import org.apache.derby.iapi.sql.execute.NoPutResultSet;\r
+import org.apache.derby.iapi.sql.execute.ResultSetFactory;\r
+\r
+/**\r
+ * Result set that fetches rows from a scan by "probing" the underlying\r
+ * table with a given list of values. Repeated calls to getNextRowCore()\r
+ * will first return all rows matching probeValues[0], then all rows matching\r
+ * probeValues[1], and so on (duplicate probe values are ignored). Once all\r
+ * matching rows for all values in probeValues have been returned, the call\r
+ * to getNextRowCore() will return null, thereby ending the scan. The\r
+ * expectation is that this kind of result set only ever appears beneath\r
+ * some other top-level result set (esp. IndexRowToBaseRowResultSet), in\r
+ * which case all result sets higher up in the result set tree will just\r
+ * see a stream of rows satisfying the list of probe values.\r
+ *\r
+ * Currently this type of result is used for evaluation of IN lists, where\r
+ * the user wants to retrieve all rows for which some target column has a\r
+ * value that equals one of values in the IN list. In that case the IN list\r
+ * values are represented by the probeValues array.\r
+ *\r
+ * Most of the work for this class is inherited from TableScanResultSet. \r
+ * This class overrides four public methods and two protected methods\r
+ * from TableScanResultSet. In all cases the methods here set probing\r
+ * state and then call the corresponding methods on "super".\r
+ */\r
+class MultiProbeTableScanResultSet extends TableScanResultSet\r
+ implements CursorResultSet\r
+{\r
+ /** The values with which we will probe the table. */\r
+ protected DataValueDescriptor [] probeValues;\r
+ /**\r
+ * The values with which we will probe the table, as they were passed to\r
+ * the constructor. We need to keep them unchanged in case the result set\r
+ * is reused when a statement is re-executed (see DERBY-827).\r
+ */\r
+ protected DataValueDescriptor [] origProbeValues;\r
+\r
+ /**\r
+ * 0-based position of the <b>next</b> value to lookup w.r.t. the probe\r
+ * values list.\r
+ */\r
+ protected int probeValIndex;\r
+\r
+ /**\r
+ * Indicator as to which type of sort we need: ASCENDING, DESCENDING,\r
+ * or NONE (NONE is represented by "RowOrdering.DONTCARE" and is used\r
+ * for cases where all necessary sorting occurred at compilation time).\r
+ */\r
+ private int sortRequired;\r
+\r
+ /**\r
+ * Constructor. Just save off the relevant probing state and pass\r
+ * everything else up to TableScanResultSet.\r
+ * \r
+ * @see ResultSetFactory#getMultiProbeTableScanResultSet\r
+ * @exception StandardException thrown on failure to open\r
+ */\r
+ MultiProbeTableScanResultSet(long conglomId,\r
+ StaticCompiledOpenConglomInfo scoci, Activation activation, \r
+ GeneratedMethod resultRowAllocator, \r
+ int resultSetNumber,\r
+ GeneratedMethod startKeyGetter, int startSearchOperator,\r
+ GeneratedMethod stopKeyGetter, int stopSearchOperator,\r
+ boolean sameStartStopPosition,\r
+ Qualifier[][] qualifiers,\r
+ DataValueDescriptor [] probingVals,\r
+ int sortRequired,\r
+ String tableName,\r
+ String userSuppliedOptimizerOverrides,\r
+ String indexName,\r
+ boolean isConstraint,\r
+ boolean forUpdate,\r
+ int colRefItem,\r
+ int indexColItem,\r
+ int lockMode,\r
+ boolean tableLocked,\r
+ int isolationLevel,\r
+ boolean oneRowScan,\r
+ double optimizerEstimatedRowCount,\r
+ double optimizerEstimatedCost)\r
+ throws StandardException\r
+ {\r
+ /* Note: We use '1' as rows per read because we do not currently\r
+ * allow bulk fetching when multi-probing. If that changes in\r
+ * the future then we will need to update rowsPerRead accordingly.\r
+ */\r
+ super(conglomId,\r
+ scoci,\r
+ activation,\r
+ resultRowAllocator,\r
+ resultSetNumber,\r
+ startKeyGetter,\r
+ startSearchOperator,\r
+ stopKeyGetter,\r
+ stopSearchOperator,\r
+ sameStartStopPosition,\r
+ qualifiers,\r
+ tableName,\r
+ userSuppliedOptimizerOverrides,\r
+ indexName,\r
+ isConstraint,\r
+ forUpdate,\r
+ colRefItem,\r
+ indexColItem,\r
+ lockMode,\r
+ tableLocked,\r
+ isolationLevel,\r
+ 1, // rowsPerRead\r
+ oneRowScan,\r
+ optimizerEstimatedRowCount,\r
+ optimizerEstimatedCost);\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ SanityManager.ASSERT(\r
+ (probingVals != null) && (probingVals.length > 0),\r
+ "No probe values found for multi-probe scan.");\r
+ }\r
+\r
+ this.origProbeValues = probingVals;\r
+ this.sortRequired = sortRequired;\r
+ }\r
+\r
+ /**\r
+ * @see NoPutResultSet#openCore\r
+ */\r
+ public void openCore() throws StandardException\r
+ {\r
+ /* If the probe values are not already sorted then sort them now. This\r
+ * allows us to skip over duplicate probe values (otherwise we could\r
+ * end up with duplicate rows in the result set).\r
+ *\r
+ * Note: If all of the probe values were provided as constants then we\r
+ * sorted them at compile time (during preprocessing) so we don't have\r
+ * to do it now. But if one or more was specified as a param then we\r
+ * have to do the sort here, at execution time, because this is the\r
+ * only point at which we know what values the parameters have.\r
+ */\r
+ if (sortRequired == RowOrdering.DONTCARE)\r
+ {\r
+ /* DONTCARE really means that the values are already sorted\r
+ * in ascending order, and that's good enough.\r
+ */\r
+ probeValues = origProbeValues;\r
+ }\r
+ else\r
+ {\r
+ /* RESOLVE: For some reason sorting the probeValues array\r
+ * directly leads to incorrect parameter value assignment when\r
+ * executing a prepared statement multiple times. Need to figure\r
+ * out why (maybe related to DERBY-827?). In the meantime, if\r
+ * we're going to sort the values we use clones. This is not\r
+ * ideal, but it works for now.\r
+ */\r
+ DataValueDescriptor [] pVals =\r
+ new DataValueDescriptor[origProbeValues.length];\r
+\r
+ for (int i = 0; i < pVals.length; i++)\r
+ pVals[i] = origProbeValues[i].getClone();\r
+\r
+ if (sortRequired == RowOrdering.ASCENDING)\r
+ java.util.Arrays.sort(pVals);\r
+ else\r
+ {\r
+ // Sort the values in DESCENDING order.\r
+ java.util.Arrays.sort(\r
+ pVals, java.util.Collections.reverseOrder());\r
+ }\r
+\r
+ probeValues = pVals;\r
+ }\r
+\r
+ probeValIndex = 0;\r
+ super.openCore();\r
+ }\r
+\r
+ /**\r
+ * Open the scan controller\r
+ *\r
+ * @param tc transaction controller; will open one if null.\r
+ * @exception StandardException thrown on failure to open\r
+ */\r
+ protected void openScanController(TransactionController tc)\r
+ throws StandardException\r
+ {\r
+ /* If we're opening the scan controller for the first time then\r
+ * we want to use the first value in the (now sorted) list as\r
+ * the start/stop key. That's what we pass in here.\r
+ */\r
+ openScanController(tc, probeValues[0]);\r
+\r
+ /* probeValIndex should be the index of the *next* value to\r
+ * use. Since we just positioned ourselves at the 0th probe\r
+ * value with the above call, the next value we want is the\r
+ * one at index "1".\r
+ */\r
+ probeValIndex = 1;\r
+ }\r
+\r
+ /**\r
+ * @see NoPutResultSet#reopenCore\r
+ */\r
+ public void reopenCore() throws StandardException\r
+ {\r
+ reopenCore(false);\r
+ }\r
+\r
+ /**\r
+ * There are two scenarios for which we reopen this kind of scan:\r
+ *\r
+ * A - The first is for join processing. In this case we have\r
+ * a(nother) row from some outer table and we want to reopen this\r
+ * scan to look for rows matching the new outer row.\r
+ *\r
+ * B - The second is for multi-probing. Here we want to reopen\r
+ * the scan on this table to look for rows matching the next value\r
+ * in the probe list.\r
+ *\r
+ * If we are reopening the scan for scenario A (join processing)\r
+ * then we need to reset our position within the probe list. \r
+ * If we are reopening the scan for scenario B then we do *not*\r
+ * want to reset our position within the probe list because that\r
+ * position tells us where to find the next probe value.\r
+ *\r
+ * That said, this method does the work of reopenCore() using\r
+ * the received boolean to determine which of the two scenarios\r
+ * we are in. Note that if our current position (i.e. the value\r
+ * of probeValIndex) is beyond the length of the probe list then\r
+ * we know that we are reopening the scan for scenario A. Or put\r
+ * another away, we should never get here for scenario B if\r
+ * probeValIndex is greater than or equal to the length of the\r
+ * probe list. The reason is that the call to reopenCore() for\r
+ * scenario B will only ever happen when moreInListVals() returns\r
+ * true--and in that case we know that probeValIndex will be less\r
+ * than the length of the probeValues. But the opposite is not\r
+ * true: i.e. it is *not* safe to say that a probeValIndex which\r
+ * is less than the length of probe list is always for scenario\r
+ * B. That's not true because it's possible that the join to\r
+ * which this scan belongs is a "oneRowRightSide" join, meaning\r
+ * that this, the "right" side scan, will be "interrupted" after\r
+ * we return a single row for the current outer row. If we then\r
+ * come back with a new outer row we need to reset our position--\r
+ * even though probeValIndex will be less than probeValues.length\r
+ * in that case. DERBY-3603.\r
+ */\r
+ private void reopenCore(boolean forNextProbe) throws StandardException\r
+ {\r
+ if (!forNextProbe)\r
+ probeValIndex = 0;\r
+\r
+ super.reopenCore();\r
+ }\r
+\r
+ /**\r
+ * Reopen the scan controller\r
+ *\r
+ * @exception StandardException thrown on failure to open\r
+ */\r
+ protected void reopenScanController() throws StandardException\r
+ {\r
+ /* If we're looking for the first value in the probe list, then\r
+ * reset the row scan count. Otherwise leave it unchanged since\r
+ * we're just continuing an already-opened scan. Note that we\r
+ * have to do this check *before* we call getNextProbeValue()\r
+ * because that method will increment probeValIndex.\r
+ */\r
+ if (probeValIndex == 0)\r
+ rowsThisScan = 0;\r
+\r
+ DataValueDescriptor pv = null;\r
+ if (moreInListVals())\r
+ {\r
+ pv = getNextProbeValue();\r
+ if (pv == null)\r
+ {\r
+ /* We'll get here when we've exhausted the probe list. In\r
+ * that case leave the scan as it is, which effectively\r
+ * means we are done.\r
+ */\r
+ return;\r
+ }\r
+ }\r
+\r
+ reopenScanController(pv);\r
+ }\r
+\r
+ /**\r
+ * Return the next row (if any) from the scan (if open).\r
+ *\r
+ * More specifically we do the following:\r
+ *\r
+ * 1 - See if we have a row to read from the current scan position.\r
+ * If so, return that row (done).\r
+ *\r
+ * 2 - If there are no more rows to read from the current scan\r
+ * position AND if there are more probe values to look at,\r
+ * then a) reposition the scan using the next probe value\r
+ * as the start/stop key and b) go back to step 1. Otherwise\r
+ * proceed to step 3.\r
+ * \r
+ * 3 - Return null (no more rows).\r
+ *\r
+ * Note that step 1 is important for cases where multiple rows in this\r
+ * table match a single probe value. In such a scenario we have to\r
+ * be sure that we do *not* move on to the next probe value until\r
+ * we have returned all of the rows for the _current_ probe value.\r
+ *\r
+ * @exception StandardException thrown on failure to get next row\r
+ */\r
+ public ExecRow getNextRowCore() throws StandardException\r
+ {\r
+ checkCancellationFlag();\r
+\r
+ // Step 1.\r
+ ExecRow result = super.getNextRowCore();\r
+\r
+ // Steps 2, 1, 2, 1, 2, ...\r
+ while ((result == null) && moreInListVals())\r
+ {\r
+ /* Repositioning the scan (if needed) is simply a matter of\r
+ * reopening the core scan again. As part of that method we will\r
+ * figure out what the next probe value should be (and thus\r
+ * where to position the scan).\r
+ */\r
+ reopenCore(true);\r
+ result = super.getNextRowCore();\r
+ }\r
+\r
+ // Step 3: result will be null if there are no more rows.\r
+ return result;\r
+ }\r
+\r
+ /**\r
+ * @see NoPutResultSet#close\r
+ */\r
+ public void close() throws StandardException\r
+ {\r
+ /* We'll let TableScanResultSet track the time it takes to close up,\r
+ * so no timing here.\r
+ */\r
+ super.close();\r
+\r
+ /* Note: We can't set probeValues == null here because we may end\r
+ * up reopening this scan again later, in which case we'll need the\r
+ * list of probe values.\r
+ */\r
+ }\r
+\r
+ /**\r
+ * Figure out whether or not we can (re-)position the scan\r
+ * controller based on the next value in probeValues. This\r
+ * will return false when we have exhausted the probe list\r
+ * (i.e. when we've gone through all of the values).\r
+ */\r
+ private boolean moreInListVals()\r
+ {\r
+ return (probeValIndex < probeValues.length);\r
+ }\r
+\r
+ /**\r
+ * Return the next non-duplicate value from the probe list.\r
+ * Assumption is that the list is sorted so that duplicates\r
+ * appear next to each other, and that probeValIndex is the\r
+ * index of the next value. If we've exhausted the probe list\r
+ * then just return null.\r
+ */\r
+ private DataValueDescriptor getNextProbeValue()\r
+ throws StandardException\r
+ {\r
+ int ctr = probeValIndex;\r
+\r
+ // Skip over duplicate values.\r
+ while ((ctr > 0) && (ctr < probeValues.length) &&\r
+ probeValues[probeValIndex-1].equals(probeValues[ctr]))\r
+ {\r
+ ctr++;\r
+ }\r
+\r
+ probeValIndex = ctr;\r
+ if (probeValIndex < probeValues.length)\r
+ return probeValues[probeValIndex++];\r
+\r
+ return null;\r
+ }\r
+}\r