--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.OrderByList\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.compile;\r
+\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+import org.apache.derby.iapi.sql.compile.CostEstimate;\r
+import org.apache.derby.iapi.sql.compile.RequiredRowOrdering;\r
+import org.apache.derby.iapi.sql.compile.RowOrdering;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.impl.sql.compile.ActivationClassBuilder;\r
+\r
+import org.apache.derby.iapi.sql.Activation;\r
+import org.apache.derby.iapi.sql.ResultSet;\r
+\r
+import org.apache.derby.iapi.sql.conn.LanguageConnectionContext;\r
+\r
+import org.apache.derby.iapi.services.compiler.MethodBuilder;\r
+\r
+import org.apache.derby.iapi.services.loader.GeneratedMethod;\r
+\r
+import org.apache.derby.iapi.store.access.ColumnOrdering;\r
+import org.apache.derby.iapi.store.access.SortCostController;\r
+import org.apache.derby.iapi.store.access.TransactionController;\r
+\r
+import org.apache.derby.iapi.types.DataValueDescriptor;\r
+\r
+import org.apache.derby.iapi.reference.ClassName;\r
+import org.apache.derby.iapi.reference.Limits;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.util.JBitSet;\r
+import org.apache.derby.iapi.services.classfile.VMOpcode;\r
+\r
+import java.util.Properties;\r
+\r
+/**\r
+ * An OrderByList is an ordered list of columns in the ORDER BY clause.\r
+ * That is, the order of columns in this list is significant - the\r
+ * first column in the list is the most significant in the ordering,\r
+ * and the last column in the list is the least significant.\r
+ *\r
+ */\r
+public class OrderByList extends OrderedColumnList\r
+ implements RequiredRowOrdering {\r
+\r
+ private boolean allAscending = true;\r
+ private boolean alwaysSort;\r
+ private ResultSetNode resultToSort;\r
+ private SortCostController scc;\r
+ private Object[] resultRow;\r
+ private ColumnOrdering[] columnOrdering;\r
+ private int estimatedRowSize;\r
+ private boolean sortNeeded = true;\r
+\r
+ /**\r
+ Add a column to the list\r
+ \r
+ @param column The column to add to the list\r
+ */\r
+ public void addOrderByColumn(OrderByColumn column) \r
+ {\r
+ addElement(column);\r
+\r
+ if (! column.isAscending())\r
+ allAscending = false;\r
+ }\r
+\r
+ /**\r
+ * Are all columns in the list ascending.\r
+ *\r
+ * @return Whether or not all columns in the list ascending.\r
+ */\r
+ boolean allAscending()\r
+ {\r
+ return allAscending;\r
+ }\r
+\r
+ /**\r
+ Get a column from the list\r
+ \r
+ @param position The column to get from the list\r
+ */\r
+ public OrderByColumn getOrderByColumn(int position) {\r
+ if (SanityManager.DEBUG)\r
+ SanityManager.ASSERT(position >=0 && position < size());\r
+ return (OrderByColumn) elementAt(position);\r
+ }\r
+\r
+ /**\r
+ Print the list.\r
+ \r
+ @param depth The depth at which to indent the sub-nodes\r
+ */\r
+ public void printSubNodes(int depth) {\r
+\r
+ if (SanityManager.DEBUG) \r
+ {\r
+ for (int index = 0; index < size(); index++)\r
+ {\r
+ ( (OrderByColumn) (elementAt(index)) ).treePrint(depth);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ Bind the update columns by their names to the target resultset\r
+ of the cursor specification.\r
+\r
+ @param target The underlying result set\r
+ \r
+ @exception StandardException Thrown on error\r
+ */\r
+ public void bindOrderByColumns(ResultSetNode target)\r
+ throws StandardException {\r
+\r
+ /* Remember the target for use in optimization */\r
+ resultToSort = target;\r
+\r
+ int size = size();\r
+\r
+ /* Only 1012 columns allowed in ORDER BY clause */\r
+ if (size > Limits.DB2_MAX_ELEMENTS_IN_ORDER_BY)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_TOO_MANY_ELEMENTS);\r
+ }\r
+\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ OrderByColumn obc = (OrderByColumn) elementAt(index);\r
+ obc.bindOrderByColumn(target, this);\r
+\r
+ /*\r
+ ** Always sort if we are ordering on an expression, and not\r
+ ** just a column.\r
+ */\r
+ if ( !\r
+ (obc.getResultColumn().getExpression() instanceof ColumnReference))\r
+ {\r
+ alwaysSort = true;\r
+ }\r
+ }\r
+ }\r
+ \r
+ /**\r
+ * Adjust addedColumnOffset values due to removal of a duplicate column\r
+ *\r
+ * This routine is called by bind processing when it identifies and\r
+ * removes a column from the result column list which was pulled up due\r
+ * to its presence in the ORDER BY clause, but which was later found to\r
+ * be a duplicate. The OrderByColumn instance for the removed column\r
+ * has been adjusted to point to the true column in the result column\r
+ * list and its addedColumnOffset has been reset to -1. This routine\r
+ * finds any other OrderByColumn instances which had an offset greater\r
+ * than that of the column that has been deleted, and decrements their\r
+ * addedColumOffset to account for the deleted column's removal.\r
+ *\r
+ * @param gap column which has been removed from the result column list\r
+ */\r
+ void closeGap(int gap)\r
+ {\r
+ for (int index = 0; index < size(); index++)\r
+ {\r
+ OrderByColumn obc = (OrderByColumn) elementAt(index);\r
+ obc.collapseAddedColumnGap(gap);\r
+ }\r
+ }\r
+\r
+ /**\r
+ Pull up Order By columns by their names to the target resultset\r
+ of the cursor specification.\r
+\r
+ @param target The underlying result set\r
+ \r
+ */\r
+ public void pullUpOrderByColumns(ResultSetNode target)\r
+ throws StandardException {\r
+\r
+ /* Remember the target for use in optimization */\r
+ resultToSort = target;\r
+\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ OrderByColumn obc = (OrderByColumn) elementAt(index);\r
+ obc.pullUpOrderByColumn(target);\r
+ }\r
+\r
+ }\r
+\r
+ /**\r
+ * Is this order by list an in order prefix of the specified RCL.\r
+ * This is useful when deciding if an order by list can be eliminated\r
+ * due to a sort from an underlying distinct or union.\r
+ *\r
+ * @param sourceRCL The source RCL.\r
+ *\r
+ * @return Whether or not this order by list an in order prefix of the specified RCL.\r
+ */\r
+ boolean isInOrderPrefix(ResultColumnList sourceRCL)\r
+ {\r
+ boolean inOrderPrefix = true;\r
+ int rclSize = sourceRCL.size();\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ if (size() > sourceRCL.size())\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "size() (" + size() + \r
+ ") expected to be <= sourceRCL.size() (" +\r
+ sourceRCL.size() + ")");\r
+ }\r
+ }\r
+\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ if (((OrderByColumn) elementAt(index)).getResultColumn() !=\r
+ (ResultColumn) sourceRCL.elementAt(index))\r
+ {\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+ }\r
+\r
+ /**\r
+ * Order by columns now point to the PRN above the node of interest.\r
+ * We need them to point to the RCL under that one. This is useful\r
+ * when combining sorts where we need to reorder the sorting\r
+ * columns.\r
+ */\r
+ void resetToSourceRCs()\r
+ {\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ OrderByColumn obc = (OrderByColumn) elementAt(index);\r
+ obc.resetToSourceRC();\r
+ }\r
+ }\r
+\r
+ /**\r
+ * Build a new RCL with the same RCs as the passed in RCL\r
+ * but in an order that matches the ordering columns.\r
+ *\r
+ * @param resultColumns The RCL to reorder.\r
+ * \r
+ * @exception StandardException Thrown on error\r
+ */\r
+ ResultColumnList reorderRCL(ResultColumnList resultColumns)\r
+ throws StandardException\r
+ {\r
+ ResultColumnList newRCL = (ResultColumnList) getNodeFactory().getNode(\r
+ C_NodeTypes.RESULT_COLUMN_LIST,\r
+ getContextManager());\r
+\r
+ /* The new RCL starts with the ordering columns */\r
+ int size = size();\r
+ for (int index = 0; index < size; index++)\r
+ {\r
+ OrderByColumn obc = (OrderByColumn) elementAt(index);\r
+ newRCL.addElement(obc.getResultColumn());\r
+ resultColumns.removeElement(obc.getResultColumn());\r
+ }\r
+\r
+ /* And ends with the non-ordering columns */\r
+ newRCL.destructiveAppend(resultColumns);\r
+ newRCL.resetVirtualColumnIds();\r
+ newRCL.copyOrderBySelect(resultColumns);\r
+ return newRCL;\r
+ }\r
+\r
+ /**\r
+ Remove any constant columns from this order by list.\r
+ Constant columns are ones where all of the column references\r
+ are equal to constant expressions according to the given\r
+ predicate list.\r
+ */\r
+ void removeConstantColumns(PredicateList whereClause)\r
+ {\r
+ /* Walk the list backwards so we can remove elements safely */\r
+ for (int loc = size() - 1;\r
+ loc >= 0;\r
+ loc--)\r
+ {\r
+ OrderByColumn obc = (OrderByColumn) elementAt(loc);\r
+\r
+ if (obc.constantColumn(whereClause))\r
+ {\r
+ removeElementAt(loc);\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ Remove any duplicate columns from this order by list.\r
+ For example, one may "ORDER BY 1, 1, 2" can be reduced\r
+ to "ORDER BY 1, 2".\r
+ Beetle 5401.\r
+ */\r
+ void removeDupColumns()\r
+ {\r
+ /* Walk the list backwards so we can remove elements safely */\r
+ for (int loc = size() - 1; loc > 0; loc--)\r
+ {\r
+ OrderByColumn obc = (OrderByColumn) elementAt(loc);\r
+ int colPosition = obc.getColumnPosition();\r
+\r
+ for (int inner = 0; inner < loc; inner++)\r
+ {\r
+ OrderByColumn prev_obc = (OrderByColumn) elementAt(inner);\r
+ if (colPosition == prev_obc.getColumnPosition())\r
+ {\r
+ removeElementAt(loc);\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ /**\r
+ generate the sort result set operating over the source\r
+ expression.\r
+\r
+ @param acb the tool for building the class\r
+ @param mb the method the generated code is to go into\r
+ @exception StandardException thrown on failure\r
+ */\r
+ public void generate(ActivationClassBuilder acb, \r
+ MethodBuilder mb,\r
+ ResultSetNode child)\r
+ throws StandardException \r
+ {\r
+ /*\r
+ ** If sorting is not required, don't generate a sort result set -\r
+ ** just return the child result set.\r
+ */\r
+ if ( ! sortNeeded) {\r
+ child.generate(acb, mb);\r
+ return;\r
+ }\r
+\r
+ /* Get the next ResultSet#, so we can number this ResultSetNode, its\r
+ * ResultColumnList and ResultSet.\r
+ *\r
+ * REMIND: to do this properly (if order bys can live throughout\r
+ * the tree) there ought to be an OrderByNode that holds its own\r
+ * ResultColumnList that is a lsit of virtual column nodes pointing\r
+ * to the source's result columns. But since we know it is outermost,\r
+ * we just gloss over that and get ourselves a resultSetNumber\r
+ * directly.\r
+ */\r
+ CompilerContext cc = getCompilerContext();\r
+\r
+\r
+ /*\r
+ create the orderItem and stuff it in.\r
+ */\r
+ int orderItem = acb.addItem(acb.getColumnOrdering(this));\r
+\r
+\r
+ /* Generate the SortResultSet:\r
+ * arg1: childExpress - Expression for childResultSet\r
+ * arg2: distinct - always false, we have a separate node\r
+ * for distincts\r
+ * arg3: isInSortedOrder - is the source result set in sorted order\r
+ * arg4: orderItem - entry in saved objects for the ordering\r
+ * arg5: rowAllocator - method to construct rows for fetching\r
+ * from the sort\r
+ * arg6: row size\r
+ * arg7: resultSetNumber\r
+ * arg8: estimated row count\r
+ * arg9: estimated cost\r
+ */\r
+\r
+ acb.pushGetResultSetFactoryExpression(mb);\r
+\r
+ child.generate(acb, mb);\r
+\r
+ int resultSetNumber = cc.getNextResultSetNumber();\r
+\r
+ // is a distinct query\r
+ mb.push(false);\r
+\r
+ // not in sorted order\r
+ mb.push(false);\r
+\r
+ mb.push(orderItem);\r
+\r
+ // row allocator\r
+ child.getResultColumns().generateHolder(acb, mb);\r
+\r
+ mb.push(child.getResultColumns().getTotalColumnSize());\r
+\r
+ mb.push(resultSetNumber);\r
+\r
+ // Get the cost estimate for the child\r
+ // RESOLVE - we will eventually include the cost of the sort\r
+ CostEstimate costEstimate = child.getFinalCostEstimate(); \r
+\r
+ mb.push(costEstimate.rowCount());\r
+ mb.push(costEstimate.getEstimatedCost());\r
+\r
+ mb.callMethod(VMOpcode.INVOKEINTERFACE, (String) null, "getSortResultSet",\r
+ ClassName.NoPutResultSet, 9);\r
+\r
+ }\r
+\r
+ /* RequiredRowOrdering interface */\r
+\r
+ /**\r
+ * @see RequiredRowOrdering#sortRequired\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public int sortRequired(RowOrdering rowOrdering) throws StandardException\r
+ {\r
+ return sortRequired(rowOrdering, (JBitSet) null);\r
+ }\r
+\r
+ /**\r
+ * @see RequiredRowOrdering#sortRequired\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public int sortRequired(RowOrdering rowOrdering, JBitSet tableMap)\r
+ throws StandardException\r
+ {\r
+ /*\r
+ ** Currently, all indexes are ordered ascending, so a descending\r
+ ** ORDER BY always requires a sort.\r
+ */\r
+ if (alwaysSort)\r
+ {\r
+ return RequiredRowOrdering.SORT_REQUIRED;\r
+ }\r
+\r
+ /*\r
+ ** Step through the columns in this list, and ask the\r
+ ** row ordering whether it is ordered on each column.\r
+ */\r
+ int position = 0;\r
+ int size = size();\r
+ for (int loc = 0; loc < size; loc++)\r
+ {\r
+ OrderByColumn obc = getOrderByColumn(loc);\r
+\r
+ // ResultColumn rc = obc.getResultColumn();\r
+\r
+ /*\r
+ ** This presumes that the OrderByColumn refers directly to\r
+ ** the base column, i.e. there is no intervening VirtualColumnNode.\r
+ */\r
+ // ValueNode expr = obc.getNonRedundantExpression();\r
+ ValueNode expr = obc.getResultColumn().getExpression();\r
+\r
+ if ( ! (expr instanceof ColumnReference))\r
+ {\r
+ return RequiredRowOrdering.SORT_REQUIRED;\r
+ }\r
+\r
+ ColumnReference cr = (ColumnReference) expr;\r
+\r
+ /*\r
+ ** Check whether the table referred to is in the table map (if any).\r
+ ** If it isn't, we may have an ordering that does not require\r
+ ** sorting for the tables in a partial join order. Look for\r
+ ** columns beyond this column to see whether a referenced table\r
+ ** is found - if so, sorting is required (for example, in a\r
+ ** case like ORDER BY S.A, T.B, S.C, sorting is required).\r
+ */\r
+ if (tableMap != null)\r
+ {\r
+ if ( ! tableMap.get(cr.getTableNumber()))\r
+ {\r
+ /* Table not in partial join order */\r
+ for (int remainingPosition = loc + 1;\r
+ remainingPosition < size();\r
+ remainingPosition++)\r
+ {\r
+ OrderByColumn remainingobc = getOrderByColumn(loc);\r
+\r
+ ResultColumn remainingrc =\r
+ remainingobc.getResultColumn();\r
+\r
+ ValueNode remainingexpr = remainingrc.getExpression();\r
+\r
+ if (remainingexpr instanceof ColumnReference)\r
+ {\r
+ ColumnReference remainingcr =\r
+ (ColumnReference) remainingexpr;\r
+ if (tableMap.get(remainingcr.getTableNumber()))\r
+ {\r
+ return RequiredRowOrdering.SORT_REQUIRED;\r
+ }\r
+ }\r
+ }\r
+\r
+ return RequiredRowOrdering.NOTHING_REQUIRED;\r
+ }\r
+ }\r
+\r
+ if ( ! rowOrdering.alwaysOrdered(cr.getTableNumber()))\r
+ {\r
+ /*\r
+ ** Check whether the ordering is ordered on this column in\r
+ ** this position.\r
+ */\r
+ if ( ! rowOrdering.orderedOnColumn(\r
+ obc.isAscending() ?\r
+ RowOrdering.ASCENDING : RowOrdering.DESCENDING,\r
+ position,\r
+ cr.getTableNumber(),\r
+ cr.getColumnNumber()\r
+ ))\r
+ {\r
+ return RequiredRowOrdering.SORT_REQUIRED;\r
+ }\r
+\r
+ /*\r
+ ** The position to ask about is for the columns in tables\r
+ ** that are *not* always ordered. The always-ordered tables\r
+ ** are not counted as part of the list of ordered columns\r
+ */\r
+ position++;\r
+ }\r
+ }\r
+\r
+ return RequiredRowOrdering.NOTHING_REQUIRED;\r
+ }\r
+\r
+ /**\r
+ * @see RequiredRowOrdering#estimateCost\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ public void estimateCost(double estimatedInputRows,\r
+ RowOrdering rowOrdering,\r
+ CostEstimate resultCost)\r
+ throws StandardException\r
+ {\r
+ /*\r
+ ** Do a bunch of set-up the first time: get the SortCostController,\r
+ ** the template row, the ColumnOrdering array, and the estimated\r
+ ** row size.\r
+ */\r
+ if (scc == null)\r
+ {\r
+ scc = getCompilerContext().getSortCostController();\r
+\r
+ resultRow =\r
+ resultToSort.getResultColumns().buildEmptyRow().getRowArray();\r
+ columnOrdering = getColumnOrdering();\r
+ estimatedRowSize =\r
+ resultToSort.getResultColumns().getTotalColumnSize();\r
+ }\r
+\r
+ long inputRows = (long) estimatedInputRows;\r
+ long exportRows = inputRows;\r
+ double sortCost;\r
+\r
+ sortCost = scc.getSortCost(\r
+ (DataValueDescriptor[]) resultRow,\r
+ columnOrdering,\r
+ false,\r
+ inputRows,\r
+ exportRows,\r
+ estimatedRowSize\r
+ );\r
+\r
+ resultCost.setCost(sortCost, estimatedInputRows, estimatedInputRows);\r
+ }\r
+\r
+ /** @see RequiredRowOrdering#sortNeeded */\r
+ public void sortNeeded()\r
+ {\r
+ sortNeeded = true;\r
+ }\r
+\r
+ /** @see RequiredRowOrdering#sortNotNeeded */\r
+ public void sortNotNeeded()\r
+ {\r
+ sortNeeded = false;\r
+ }\r
+\r
+ /**\r
+ * Remap all ColumnReferences in this tree to be clones of the\r
+ * underlying expression.\r
+ *\r
+ * @exception StandardException Thrown on error\r
+ */\r
+ void remapColumnReferencesToExpressions() throws StandardException\r
+ {\r
+ }\r
+\r
+ /**\r
+ * Get whether or not a sort is needed.\r
+ *\r
+ * @return Whether or not a sort is needed.\r
+ */\r
+ public boolean getSortNeeded()\r
+ {\r
+ return sortNeeded;\r
+ }\r
+\r
+ /**\r
+ * Determine whether or not this RequiredRowOrdering has a\r
+ * DESCENDING requirement for the column referenced by the\r
+ * received ColumnReference.\r
+ */\r
+ boolean requiresDescending(ColumnReference cRef, int numOptimizables)\r
+ throws StandardException\r
+ {\r
+ int size = size();\r
+\r
+ /* Start by getting the table number and column position for\r
+ * the table to which the ColumnReference points.\r
+ */\r
+ JBitSet tNum = new JBitSet(numOptimizables);\r
+ BaseTableNumbersVisitor btnVis = new BaseTableNumbersVisitor(tNum);\r
+\r
+ cRef.accept(btnVis);\r
+ int crTableNumber = tNum.getFirstSetBit();\r
+ int crColPosition = btnVis.getColumnNumber();\r
+\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ /* We assume that we only ever get here if the column\r
+ * reference points to a specific column in a specific\r
+ * table...\r
+ */\r
+ if ((crTableNumber < 0) || (crColPosition < 0))\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "Failed to find table/column number for column '" +\r
+ cRef.getColumnName() + "' when checking for an " +\r
+ "ORDER BY requirement.");\r
+ }\r
+\r
+ /* Since we started with a single ColumnReference there\r
+ * should be exactly one table number.\r
+ */\r
+ if (!tNum.hasSingleBitSet())\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "Expected ColumnReference '" + cRef.getColumnName() +\r
+ "' to reference exactly one table, but tables found " +\r
+ "were: " + tNum);\r
+ }\r
+ }\r
+\r
+ /* Walk through the various ORDER BY elements to see if\r
+ * any of them point to the same table and column that\r
+ * we found above.\r
+ */\r
+ for (int loc = 0; loc < size; loc++)\r
+ {\r
+ OrderByColumn obc = getOrderByColumn(loc);\r
+ ResultColumn rcOrderBy = obc.getResultColumn();\r
+\r
+ btnVis.reset();\r
+ rcOrderBy.accept(btnVis);\r
+ int obTableNumber = tNum.getFirstSetBit();\r
+ int obColPosition = btnVis.getColumnNumber();\r
+\r
+ /* ORDER BY target should always have a table number and\r
+ * a column position. It may not necessarily be a base\r
+ * table, but there should be some FromTable for which\r
+ * we have a ResultColumnList, and the ORDER BY should\r
+ * reference one of the columns in that list (otherwise\r
+ * we shouldn't have made it this far).\r
+ */\r
+ if (SanityManager.DEBUG)\r
+ {\r
+ /* Since we started with a single ResultColumn there\r
+ * should exactly one table number.\r
+ */\r
+ if (!tNum.hasSingleBitSet())\r
+ {\r
+ SanityManager.THROWASSERT("Expected ResultColumn '" +\r
+ rcOrderBy.getColumnName() + "' to reference " +\r
+ "exactly one table, but found: " + tNum);\r
+ }\r
+\r
+ if (obColPosition < 0)\r
+ {\r
+ SanityManager.THROWASSERT(\r
+ "Failed to find orderBy column number " +\r
+ "for ORDER BY check on column '" + \r
+ cRef.getColumnName() + "'.");\r
+ }\r
+ }\r
+\r
+ if (crTableNumber != obTableNumber)\r
+ continue;\r
+\r
+ /* They point to the same base table, so check the\r
+ * column positions.\r
+ */\r
+\r
+ if (crColPosition == obColPosition)\r
+ {\r
+ /* This ORDER BY element points to the same table\r
+ * and column as the received ColumnReference. So\r
+ * return whether or not this ORDER BY element is\r
+ * descending.\r
+ */\r
+ return !obc.isAscending();\r
+ }\r
+ }\r
+\r
+ /* None of the ORDER BY elements referenced the same table\r
+ * and column as the received ColumnReference, so there\r
+ * is no descending requirement for the ColumnReference's\r
+ * source (at least not from this OrderByList).\r
+ */\r
+ return false;\r
+ }\r
+}\r