Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / derby-10.3.2.1 / java / engine / org / apache / derby / impl / sql / compile / OrderByColumn.java
diff --git a/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/sql/compile/OrderByColumn.java b/JMCR-Stable/real-world application/derby-10.3.2.1/java/engine/org/apache/derby/impl/sql/compile/OrderByColumn.java
new file mode 100644 (file)
index 0000000..b537adc
--- /dev/null
@@ -0,0 +1,453 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.compile.OrderByColumn\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.types.TypeId;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+\r
+import org.apache.derby.iapi.sql.compile.NodeFactory;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+\r
+import org.apache.derby.iapi.util.ReuseFactory;\r
+\r
+/**\r
+ * An OrderByColumn is a column in the ORDER BY clause.  An OrderByColumn\r
+ * can be ordered ascending or descending.\r
+ *\r
+ * We need to make sure that the named columns are\r
+ * columns in that query, and that positions are within range.\r
+ *\r
+ */\r
+public class OrderByColumn extends OrderedColumn {\r
+\r
+       private ResultColumn    resultCol;\r
+       private boolean                 ascending = true;\r
+       private ValueNode expression;\r
+       private OrderByList     list;\r
+    /**\r
+     * If this sort key is added to the result column list then it is at result column position\r
+     * 1 + resultColumnList.size() - resultColumnList.getOrderBySelect() + addedColumnOffset\r
+     * If the sort key is already in the result column list then addedColumnOffset < 0.\r
+     */\r
+    private int addedColumnOffset = -1;\r
+\r
+\r
+       /**\r
+        * Initializer.\r
+        *\r
+        * @param expression            Expression of this column\r
+        */\r
+       public void init(Object expression)\r
+       {\r
+               this.expression = (ValueNode)expression;\r
+       }\r
+       \r
+       /**\r
+        * Convert this object to a String.  See comments in QueryTreeNode.java\r
+        * for how this should be done for tree printing.\r
+        *\r
+        * @return      This object as a String\r
+        */\r
+       public String toString() {\r
+               if (SanityManager.DEBUG) {\r
+                       return expression.toString();\r
+               } else {\r
+                       return "";\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Mark the column as descending order\r
+        */\r
+       public void setDescending() {\r
+               ascending = false;\r
+       }\r
+\r
+       /**\r
+        * Get the column order.  Overrides \r
+        * OrderedColumn.isAscending.\r
+        *\r
+        * @return true if ascending, false if descending\r
+        */\r
+       public boolean isAscending() {\r
+               return ascending;\r
+       }\r
+\r
+       /**\r
+        * Get the underlying ResultColumn.\r
+        *\r
+        * @return The underlying ResultColumn.\r
+        */\r
+       ResultColumn getResultColumn()\r
+       {\r
+               return resultCol;\r
+       }\r
+\r
+       /**\r
+        * Get the underlying expression, skipping over ResultColumns that\r
+        * are marked redundant.\r
+        */\r
+       ValueNode getNonRedundantExpression()\r
+       {\r
+               ResultColumn    rc;\r
+               ValueNode               value;\r
+               ColumnReference colref = null;\r
+\r
+               for (rc = resultCol; rc.isRedundant(); rc = colref.getSource())\r
+               {\r
+                       value = rc.getExpression();\r
+\r
+                       if (value instanceof ColumnReference)\r
+                       {\r
+                               colref = (ColumnReference) value;\r
+                       }\r
+                       else\r
+                       {\r
+                               if (SanityManager.DEBUG)\r
+                               {\r
+                                       SanityManager.THROWASSERT(\r
+                                               "value should be a ColumnReference, but is a " +\r
+                                               value.getClass().getName());\r
+                               }\r
+                       }\r
+               }\r
+\r
+               return rc.getExpression();\r
+       }\r
+\r
+       /**\r
+        * Bind this column.\r
+        *\r
+        * During binding, we may discover that this order by column was pulled\r
+        * up into the result column list, but is now a duplicate, because the\r
+        * actual result column was expanded into the result column list when "*"\r
+        * expressions were replaced with the list of the table's columns. In such\r
+        * a situation, we will end up calling back to the OrderByList to\r
+        * adjust the addedColumnOffset values of the columns; the "oblist"\r
+        * parameter exists to allow that callback to be performed.\r
+        *\r
+        * @param target        The result set being selected from\r
+        * @param oblist    OrderByList which contains this column\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        * @exception StandardException         Thrown when column not found\r
+        */\r
+       public void bindOrderByColumn(ResultSetNode target, OrderByList oblist)\r
+                               throws StandardException \r
+       {\r
+               this.list = oblist;\r
+\r
+               if(expression instanceof ColumnReference){\r
+               \r
+                       ColumnReference cr = (ColumnReference) expression;\r
+                       \r
+                       resultCol = resolveColumnReference(target,\r
+                                                          cr);\r
+                       \r
+                       columnPosition = resultCol.getColumnPosition();\r
+\r
+                       if (addedColumnOffset >= 0 &&\r
+                                       target instanceof SelectNode &&\r
+                                       ( (SelectNode)target ).hasDistinct())\r
+                               throw StandardException.newException(SQLState.LANG_DISTINCT_ORDER_BY, cr.columnName);\r
+               }else if(isReferedColByNum(expression)){\r
+                       \r
+                       ResultColumnList targetCols = target.getResultColumns();\r
+                       columnPosition = ((Integer)expression.getConstantValueAsObject()).intValue();\r
+                       resultCol = targetCols.getOrderByColumn(columnPosition);\r
+\r
+                       /* Column is out of range if either a) resultCol is null, OR\r
+                        * b) resultCol points to a column that is not visible to the\r
+                        * user (i.e. it was generated internally).\r
+                        */\r
+                       if ((resultCol == null) ||\r
+                               (resultCol.getColumnPosition() > targetCols.visibleSize()))\r
+                       {\r
+                               throw StandardException.newException(SQLState.LANG_COLUMN_OUT_OF_RANGE, \r
+                                                                    String.valueOf(columnPosition));\r
+                       }\r
+\r
+               }else{\r
+            if( SanityManager.DEBUG)\r
+                SanityManager.ASSERT( addedColumnOffset >= 0,\r
+                                      "Order by expression was not pulled into the result column list");\r
+            resolveAddedColumn(target);\r
+               if (resultCol == null)\r
+                       throw StandardException.newException(SQLState.LANG_UNION_ORDER_BY);\r
+               }\r
+\r
+               // Verify that the column is orderable\r
+               resultCol.verifyOrderable();\r
+       }\r
+\r
+    /**\r
+     * Assuming this OrderByColumn was "pulled" into the received target's\r
+     * ResultColumnList (because it wasn't there to begin with), use\r
+     * this.addedColumnOffset to figure out which of the target's result\r
+     * columns is the one corresponding to "this".\r
+     *\r
+     * The desired position is w.r.t. the original, user-specified result\r
+     * column list--which is what "visibleSize()" gives us.  I.e. To get\r
+     * this OrderByColumn's position in target's RCL, first subtract out\r
+     * all columns which were "pulled" into the RCL for GROUP BY or ORDER\r
+     * BY, then add "this.addedColumnOffset". As an example, if the query\r
+     * was:\r
+     *\r
+     *   select sum(j) as s from t1 group by i, k order by k, sum(k)\r
+     *\r
+     * then we will internally add columns "K" and "SUM(K)" to the RCL for\r
+     * ORDER BY, *AND* we will add a generated column "I" to the RCL for\r
+     * GROUP BY.  Thus we end up with four result columns:\r
+     *\r
+     *          (1)        (2)  (3)   (4)\r
+     *  select sum(j) as s, K, SUM(K), I from t1 ...\r
+     *\r
+     * So when we get here and we want to find out which column "this"\r
+     * corresponds to, we begin by taking the total number of VISIBLE\r
+     * columns, which is 1 (i.e. 4 total columns minus 1 GROUP BY column\r
+     * minus 2 ORDER BY columns).  Then we add this.addedColumnOffset in\r
+     * order to find the target column position.  Since addedColumnOffset\r
+     * is 0-based, an addedColumnOffset value of "0" means we want the\r
+     * the first ORDER BY column added to target's RCL, "1" means we want\r
+     * the second ORDER BY column added, etc.  So if we assume that\r
+     * this.addedColumnOffset is "1" in this example then we add that\r
+     * to the RCL's "visible size". And finally, we add 1 more to account\r
+     * for fact that addedColumnOffset is 0-based while column positions\r
+     * are 1-based. This gives:\r
+     *\r
+     *  position = 1 + 1 + 1 = 3\r
+     *\r
+     * which points to SUM(K) in the RCL.  Thus an addedColumnOffset\r
+     * value of "1" resolves to column SUM(K) in target's RCL; similarly,\r
+     * an addedColumnOffset value of "0" resolves to "K". DERBY-3303.\r
+     */\r
+    private void resolveAddedColumn(ResultSetNode target)\r
+    {\r
+        ResultColumnList targetCols = target.getResultColumns();\r
+        columnPosition = targetCols.visibleSize() + addedColumnOffset + 1;\r
+        resultCol = targetCols.getResultColumn( columnPosition);\r
+    }\r
+\r
+       /**\r
+        * Pull up this orderby column if it doesn't appear in the resultset\r
+        *\r
+        * @param target        The result set being selected from\r
+        *\r
+        */\r
+       public void pullUpOrderByColumn(ResultSetNode target)\r
+                               throws StandardException \r
+       {\r
+        ResultColumnList targetCols = target.getResultColumns();\r
+\r
+        if(expression instanceof ColumnReference){\r
+\r
+                       ColumnReference cr = (ColumnReference) expression;\r
+\r
+                       resultCol = targetCols.findResultColumnForOrderBy(\r
+                    cr.getColumnName(), cr.getTableNameNode());\r
+\r
+                       if(resultCol == null){\r
+                               resultCol = (ResultColumn) getNodeFactory().getNode(C_NodeTypes.RESULT_COLUMN,\r
+                                                                                   cr.getColumnName(),\r
+                                                                                   cr,\r
+                                                                                   getContextManager());\r
+                               targetCols.addResultColumn(resultCol);\r
+                addedColumnOffset = targetCols.getOrderBySelect();\r
+                               targetCols.incOrderBySelect();\r
+                       }\r
+                       \r
+               }else if(!isReferedColByNum(expression)){\r
+                       resultCol = (ResultColumn) getNodeFactory().getNode(C_NodeTypes.RESULT_COLUMN,\r
+                                                                           null,\r
+                                                                           expression,\r
+                                                                           getContextManager());\r
+                       targetCols.addResultColumn(resultCol);\r
+            addedColumnOffset = targetCols.getOrderBySelect();\r
+                       targetCols.incOrderBySelect();\r
+               }\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 resetToSourceRC()\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       if (! (resultCol.getExpression() instanceof VirtualColumnNode))\r
+                       {\r
+                               SanityManager.THROWASSERT(\r
+                                       "resultCol.getExpression() expected to be instanceof VirtualColumnNode " +\r
+                                       ", not " + resultCol.getExpression().getClass().getName());\r
+                       }\r
+               }\r
+\r
+               resultCol = resultCol.getExpression().getSourceResultColumn();\r
+       }\r
+\r
+       /**\r
+        * Is this OrderByColumn constant, according to the given predicate list?\r
+        * A constant column is one where all the column references it uses are\r
+        * compared equal to constants.\r
+        */\r
+       boolean constantColumn(PredicateList whereClause)\r
+       {\r
+               ValueNode sourceExpr = resultCol.getExpression();\r
+\r
+               return sourceExpr.constantExpression(whereClause);\r
+       }\r
+\r
+       /**\r
+        * Remap all the column references under this OrderByColumn to their\r
+        * expressions.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       void remapColumnReferencesToExpressions() throws StandardException\r
+       {\r
+               resultCol.setExpression(\r
+                       resultCol.getExpression().remapColumnReferencesToExpressions());\r
+       }\r
+\r
+       private static boolean isReferedColByNum(ValueNode expression) \r
+       throws StandardException{\r
+               \r
+               if(!expression.isConstantExpression()){\r
+                       return false;\r
+               }\r
+               \r
+               return expression.getConstantValueAsObject() instanceof Integer;\r
+       }\r
+\r
+       \r
+       private ResultColumn resolveColumnReference(ResultSetNode target,\r
+                                                          ColumnReference cr)\r
+       throws StandardException{\r
+               \r
+               ResultColumn resultCol = null;\r
+               \r
+               int                                     sourceTableNumber = -1;\r
+               \r
+               //bug 5716 - for db2 compatibility - no qualified names allowed in order by clause when union/union all operator is used \r
+\r
+               if (target instanceof SetOperatorNode && cr.getTableName() != null){\r
+                       String fullName = cr.getSQLColumnName();\r
+                       throw StandardException.newException(SQLState.LANG_QUALIFIED_COLUMN_NAME_NOT_ALLOWED, fullName);\r
+               }\r
+\r
+               if(cr.getTableNameNode() != null){\r
+                       TableName tableNameNode = cr.getTableNameNode();\r
+\r
+                       FromTable fromTable = target.getFromTableByName(tableNameNode.getTableName(),\r
+                                                                       (tableNameNode.hasSchema() ?\r
+                                                                        tableNameNode.getSchemaName():null),\r
+                                                                       true);\r
+                       if(fromTable == null){\r
+                               fromTable = target.getFromTableByName(tableNameNode.getTableName(),\r
+                                                                     (tableNameNode.hasSchema() ?\r
+                                                                      tableNameNode.getSchemaName():null),\r
+                                                                     false);\r
+                               if(fromTable == null){\r
+                                       String fullName = cr.getTableNameNode().toString();\r
+                                       throw StandardException.newException(SQLState.LANG_EXPOSED_NAME_NOT_FOUND, fullName);\r
+                               }\r
+                       }\r
+\r
+                       /* HACK - if the target is a UnionNode, then we have to\r
+                        * have special code to get the sourceTableNumber.  This is\r
+                        * because of the gyrations we go to with building the RCLs\r
+                        * for a UnionNode.\r
+                        */\r
+                       if (target instanceof SetOperatorNode)\r
+                       {\r
+                               sourceTableNumber = ((FromTable) target).getTableNumber();\r
+                       }\r
+                       else\r
+                       {\r
+                               sourceTableNumber = fromTable.getTableNumber();\r
+                       }\r
+                       \r
+               }\r
+\r
+               ResultColumnList        targetCols = target.getResultColumns();\r
+\r
+               resultCol = targetCols.getOrderByColumnToBind(cr.getColumnName(),\r
+                                                       cr.getTableNameNode(),\r
+                                                       sourceTableNumber,\r
+                                                       this);\r
+        /* Search targetCols before using addedColumnOffset because select list wildcards, '*',\r
+         * are expanded after pullUpOrderByColumn is called. A simple column reference in the\r
+         * order by clause may be found in the user specified select list now even though it was\r
+         * not found when pullUpOrderByColumn was called.\r
+         */\r
+        if( resultCol == null && addedColumnOffset >= 0)\r
+            resolveAddedColumn(target);\r
+                                                       \r
+               if (resultCol == null || resultCol.isNameGenerated()){\r
+                       String errString = cr.columnName;\r
+                       throw StandardException.newException(SQLState.LANG_ORDER_BY_COLUMN_NOT_FOUND, errString);\r
+               }\r
+\r
+               return resultCol;\r
+\r
+       }\r
+\r
+       /**\r
+        * Reset addedColumnOffset to indicate that column is no longer added\r
+        *\r
+        * An added column is one which was artificially added to the result\r
+        * column list due to its presence in the ORDER BY clause, as opposed to\r
+        * having been explicitly selected by the user. Since * is not expanded\r
+        * until after the ORDER BY columns have been pulled up, we may add a\r
+        * column, then later decide it is a duplicate of an explicitly selected\r
+        * column. In that case, this method is called, and it does the following:\r
+        * - resets addedColumnOffset to -1 to indicate this is not an added col\r
+        * - calls back to the OrderByList to adjust any other added cols\r
+        */\r
+       void clearAddedColumnOffset()\r
+       {\r
+               list.closeGap(addedColumnOffset);\r
+               addedColumnOffset = -1;\r
+       }\r
+       /**\r
+        * Adjust addedColumnOffset to reflect that a column has been removed\r
+        *\r
+        * This routine is called when a previously-added result column has been\r
+        * removed due to being detected as a duplicate. If that added column had\r
+        * a lower offset than our column, we decrement our offset to reflect that\r
+        * we have just been moved down one slot in the result column list.\r
+        *\r
+        * @param gap   offset of the column which has just been removed from list\r
+        */\r
+       void collapseAddedColumnGap(int gap)\r
+       {\r
+               if (addedColumnOffset > gap)\r
+                       addedColumnOffset--;\r
+       }\r
+}\r