Adding JMCR-Stable version
[Benchmarks_CSolver.git] / JMCR-Stable / real-world application / MyDerby-10.3 / java / engine / org / apache / derby / impl / sql / compile / AggregateNode.java
diff --git a/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java b/JMCR-Stable/real-world application/MyDerby-10.3/java/engine/org/apache/derby/impl/sql/compile/AggregateNode.java
new file mode 100644 (file)
index 0000000..8da417a
--- /dev/null
@@ -0,0 +1,635 @@
+/*\r
+\r
+   Derby - Class org.apache.derby.impl.sql.compile.AggregateNode\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.services.compiler.MethodBuilder;\r
+\r
+import org.apache.derby.iapi.services.sanity.SanityManager;\r
+import org.apache.derby.iapi.services.loader.ClassInspector;\r
+import org.apache.derby.iapi.services.loader.ClassFactory;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+\r
+import org.apache.derby.iapi.sql.compile.CompilerContext;\r
+import org.apache.derby.iapi.sql.compile.C_NodeTypes;\r
+\r
+import org.apache.derby.iapi.types.DataTypeDescriptor;\r
+import org.apache.derby.iapi.types.TypeId;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.iapi.sql.dictionary.DataDictionary;\r
+import org.apache.derby.iapi.sql.execute.ExecAggregator;\r
+\r
+import org.apache.derby.iapi.error.StandardException;\r
+import org.apache.derby.iapi.reference.SQLState;\r
+\r
+import org.apache.derby.impl.sql.compile.ActivationClassBuilder;\r
+import org.apache.derby.impl.sql.compile.ExpressionClassBuilder;\r
+\r
+import org.apache.derby.catalog.AliasInfo;\r
+import org.apache.derby.catalog.TypeDescriptor;\r
+\r
+import org.apache.derby.impl.sql.compile.CountAggregateDefinition;\r
+import org.apache.derby.impl.sql.compile.MaxMinAggregateDefinition;\r
+import org.apache.derby.impl.sql.compile.SumAvgAggregateDefinition;\r
+\r
+import java.util.Vector;\r
+\r
+/**\r
+ * An Aggregate Node is a node that reprsents a set function/aggregate.\r
+ * It used for all system aggregates as well as user defined aggregates.\r
+ *\r
+ */\r
+\r
+public class AggregateNode extends UnaryOperatorNode\r
+{\r
+       private boolean                                 distinct;\r
+\r
+       private AggregateDefinition             uad;\r
+       private StringBuffer                    aggregatorClassName;\r
+       private String                                  aggregateDefinitionClassName;\r
+       private Class                                   aggregateDefinitionClass;\r
+       private ClassInspector                  classInspector;\r
+       private String                                  aggregateName;\r
+\r
+       /*\r
+       ** We wind up pushing all aggregates into a different\r
+       ** resultColumnList.  When we do this (in \r
+       ** replaceAggregateWithColumnReference), we return a\r
+       ** column reference and create a new result column.\r
+       ** This is used to store that result column.\r
+       */\r
+       private ResultColumn                    generatedRC;\r
+       private ColumnReference                 generatedRef;\r
+\r
+       /**\r
+        * Intializer.  Used for user defined and internally defined aggregates.\r
+        * Called when binding a StaticMethodNode that we realize is an aggregate.\r
+        *\r
+        * @param operand       the value expression for the aggregate\r
+        * @param uadClass      the class name for user aggregate definition for the aggregate\r
+        *                                      or the Class for the internal aggregate type.\r
+        * @param distinct      boolean indicating whether this is distinct\r
+        *                                      or not.\r
+        * @param aggregateName the name of the aggregate from the user's perspective,\r
+        *                                      e.g. MAX\r
+        *\r
+        * @exception StandardException on error\r
+        */\r
+       public void init\r
+       (\r
+               Object  operand,\r
+               Object          uadClass,\r
+               Object          distinct,\r
+               Object          aggregateName\r
+       ) throws StandardException\r
+       {\r
+               super.init(operand);\r
+               this.aggregateName = (String) aggregateName;\r
+\r
+               if (uadClass instanceof String)\r
+               {\r
+                       this.aggregateDefinitionClassName = (String) uadClass;\r
+                       this.distinct = ((Boolean) distinct).booleanValue();\r
+               }\r
+               else\r
+               {\r
+                       this.aggregateDefinitionClass = (Class) uadClass;\r
+                       this.aggregateDefinitionClassName =\r
+                                                                               aggregateDefinitionClass.getName();\r
+\r
+                       // Distinct is meaningless for min and max\r
+                       if (!aggregateDefinitionClass.equals(MaxMinAggregateDefinition.class))\r
+                       {\r
+                               this.distinct = ((Boolean) distinct).booleanValue();\r
+                       }\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Replace aggregates in the expression tree with a ColumnReference to\r
+        * that aggregate, append the aggregate to the supplied RCL (assumed to\r
+        * be from the child ResultSetNode) and return the ColumnReference.\r
+        * This is useful for pushing aggregates in the Having clause down to\r
+        * the user's select at parse time.  It is also used for moving around \r
+        * Aggregates in the select list when creating the Group By node.  In \r
+        * that case it is called <B> after </B> bind time, so we need to create\r
+        * the column differently.\r
+        *\r
+        * @param rcl   The RCL to append to.\r
+        * @param tableNumber   The tableNumber for the new ColumnReference\r
+        *\r
+        * @return ValueNode    The (potentially) modified tree.\r
+        *\r
+        * @exception StandardException                 Thrown on error\r
+        */\r
+       public ValueNode replaceAggregatesWithColumnReferences(ResultColumnList rcl, int tableNumber)\r
+               throws StandardException\r
+       {\r
+\r
+               /*\r
+               ** This call is idempotent.  Do\r
+               ** the right thing if we have already\r
+               ** replaced ourselves.\r
+               */\r
+               if (generatedRef == null)\r
+               {\r
+                       String                                  generatedColName;\r
+                       CompilerContext                 cc = getCompilerContext();\r
+                       generatedColName ="SQLCol" + cc.getNextColumnNumber();\r
+                       generatedRC = (ResultColumn) getNodeFactory().getNode(\r
+                                                                                       C_NodeTypes.RESULT_COLUMN,\r
+                                                                                       generatedColName,\r
+                                                                                       this,\r
+                                                                                       getContextManager());\r
+                       generatedRC.markGenerated();\r
+       \r
+                       /*\r
+                       ** Parse time.  \r
+                       */\r
+                       if (getTypeServices() == null)\r
+                       {\r
+                               generatedRef = (ColumnReference) getNodeFactory().getNode(\r
+                                                                                               C_NodeTypes.COLUMN_REFERENCE,\r
+                                                                                               generatedColName,\r
+                                                                                               null,\r
+                                                                                               getContextManager());\r
+                       }\r
+                       else\r
+                       {\r
+                               generatedRef = (ColumnReference) getNodeFactory().getNode(\r
+                                                                                               C_NodeTypes.COLUMN_REFERENCE,\r
+                                                                                               generatedRC.getName(),\r
+                                                                                               null,\r
+                                                                                               getContextManager());\r
+                               generatedRef.setType(this.getTypeServices());\r
+                       }\r
+                       // RESOLVE - unknown nesting level, but not correlated, so nesting levels must be 0\r
+                       generatedRef.setNestingLevel(0);\r
+                       generatedRef.setSourceLevel(0);\r
+                       if (tableNumber != -1)\r
+                       {\r
+                               generatedRef.setTableNumber(tableNumber);\r
+                       }\r
+\r
+                       rcl.addResultColumn(generatedRC);\r
+\r
+                       /* \r
+                       ** Mark the ColumnReference as being generated to replace\r
+                       ** an aggregate\r
+                       */\r
+                       generatedRef.markGeneratedToReplaceAggregate();\r
+               }\r
+               else\r
+               {\r
+                       rcl.addResultColumn(generatedRC);\r
+               }\r
+\r
+               return generatedRef;\r
+       }\r
+\r
+       /**\r
+        * Get the AggregateDefinition.\r
+        *\r
+        * @return The AggregateDefinition\r
+        */\r
+       AggregateDefinition getAggregateDefinition()\r
+       {\r
+               return uad;\r
+       }\r
+\r
+       /**\r
+        * Get the generated ResultColumn where this\r
+        * aggregate now resides after a call to \r
+        * replaceAggregatesWithColumnReference().\r
+        *\r
+        * @return the result column\r
+        */\r
+       public ResultColumn getGeneratedRC()\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       SanityManager.ASSERT(generatedRC != null, \r
+                               "generatedRC is null.  replaceAggregateWithColumnReference() "+\r
+                               "has not been called on this AggergateNode.  Make sure "+\r
+                               "the node is under a ResultColumn as expected.");\r
+               }\r
+                                       \r
+               return generatedRC;\r
+       }\r
+\r
+       /**\r
+        * Get the generated ColumnReference to this\r
+        * aggregate after the parent called\r
+        * replaceAggregatesWithColumnReference().\r
+        *\r
+        * @return the column reference\r
+        */\r
+       public ColumnReference getGeneratedRef()\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       SanityManager.ASSERT(generatedRef != null, \r
+                               "generatedRef is null.  replaceAggregateWithColumnReference() "+\r
+                               "has not been called on this AggergateNode.  Make sure "+\r
+                               "the node is under a ResultColumn as expected.");\r
+               }\r
+               return generatedRef;\r
+       }\r
+\r
+       /**\r
+        * Bind this operator.  Determine the type of the subexpression,\r
+        * and pass that into the UserAggregate.\r
+        *\r
+        * @param fromList                      The query's FROM list\r
+        * @param subqueryList          The subquery list being built as we find SubqueryNodes\r
+        * @param aggregateVector       The aggregate list being built as we find AggregateNodes\r
+        *\r
+        * @return      The new top of the expression tree.\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public ValueNode bindExpression(\r
+                                       FromList                        fromList,\r
+                                       SubqueryList            subqueryList,\r
+                                       Vector                          aggregateVector)\r
+                       throws StandardException\r
+       {\r
+               DataTypeDescriptor      dts = null;\r
+               ClassFactory            cf;\r
+\r
+               cf = getClassFactory();\r
+               classInspector = cf.getClassInspector();\r
+\r
+               instantiateAggDef();\r
+\r
+               /* Add ourselves to the aggregateVector before we do anything else */\r
+               aggregateVector.addElement(this);\r
+\r
+        // operand being null means a count(*)\r
+               if (operand != null)\r
+               {\r
+            bindOperand(fromList, subqueryList, aggregateVector);\r
+            \r
+                       /*\r
+                       ** Make sure that we don't have an aggregate \r
+                       ** IMMEDIATELY below us.  Don't search below\r
+                       ** any ResultSetNodes.\r
+                       */\r
+                       HasNodeVisitor visitor = new HasNodeVisitor(this.getClass(), ResultSetNode.class);\r
+                       operand.accept(visitor);\r
+                       if (visitor.hasNode())\r
+                       {\r
+                               throw StandardException.newException(SQLState.LANG_USER_AGGREGATE_CONTAINS_AGGREGATE, \r
+                                               aggregateName);\r
+                       }\r
+\r
+                       /*\r
+                       ** Check the type of the operand.  Make sure that the user\r
+                       ** defined aggregate can handle the operand datatype.\r
+                       */\r
+                       dts = operand.getTypeServices();\r
+\r
+                       /* Convert count(nonNullableColumn) to count(*) */\r
+                       if (uad instanceof CountAggregateDefinition &&\r
+                               !dts.isNullable())\r
+                       {\r
+                               setOperator(aggregateName);\r
+                               setMethodName(aggregateName);\r
+                       }\r
+\r
+                       /*\r
+                       ** If we have a distinct, then the value expression\r
+                       ** MUST implement Orderable because we are going\r
+                       ** to process it using it as part of a sort.\r
+                       */\r
+                       if (distinct)\r
+                       {\r
+                               /*\r
+                               ** For now, we check to see if orderable() returns\r
+                               ** true for this type.  In the future we may need\r
+                               ** to check to see if the type implements Orderable\r
+                               **\r
+                               */\r
+                               if (!operand.getTypeId().orderable(cf))\r
+                               {\r
+                                       throw StandardException.newException(SQLState.LANG_COLUMN_NOT_ORDERABLE_DURING_EXECUTION, \r
+                                                       dts.getTypeId().getSQLTypeName());\r
+                               }\r
+\r
+                       }\r
+\r
+                       /*\r
+                       ** Don't allow an untyped null\r
+                       */\r
+                       if (operand instanceof UntypedNullConstantNode)\r
+                       {\r
+                               throw StandardException.newException(SQLState.LANG_USER_AGGREGATE_BAD_TYPE_NULL, aggregateName);\r
+                       }\r
+               }\r
+\r
+               /*\r
+               ** Ask the aggregate definition whether it can handle\r
+               ** the input datatype.\r
+               */\r
+        aggregatorClassName = new StringBuffer();\r
+        DataTypeDescriptor resultType = uad.getAggregator(dts, aggregatorClassName);\r
+\r
+               if (resultType == null)\r
+               {\r
+                       throw StandardException.newException(SQLState.LANG_USER_AGGREGATE_BAD_TYPE, \r
+                                               aggregateName, \r
+                                               operand.getTypeId().getSQLTypeName());\r
+               }\r
+\r
+               checkAggregatorClassName(aggregatorClassName.toString());\r
+\r
+               setType(resultType);\r
+\r
+               return this;\r
+       }\r
+\r
+       /*\r
+       ** Make sure the aggregator class is ok\r
+       */\r
+       private void checkAggregatorClassName(String className) throws StandardException\r
+       {\r
+               verifyClassExist(className);\r
+\r
+               if (!classInspector.assignableTo(className, "org.apache.derby.iapi.sql.execute.ExecAggregator"))\r
+               {\r
+                       throw StandardException.newException(SQLState.LANG_BAD_AGGREGATOR_CLASS2, \r
+                                                                                                       className, \r
+                                                                                                       aggregateName,\r
+                                                                                                       operand.getTypeId().getSQLTypeName());\r
+               }\r
+       }\r
+\r
+               \r
+       /*\r
+       ** Instantiate the aggregate definition.\r
+       */\r
+       private void instantiateAggDef() throws StandardException\r
+       {\r
+               Class theClass = aggregateDefinitionClass;\r
+\r
+               // get the class\r
+               if (theClass == null)\r
+               {\r
+                       String aggClassName = aggregateDefinitionClassName;\r
+                       verifyClassExist(aggClassName);\r
+\r
+                       try\r
+                       {\r
+                               theClass = classInspector.getClass(aggClassName);\r
+                       }\r
+                       catch (Throwable t)\r
+                       {\r
+                               throw StandardException.unexpectedUserException(t);\r
+                       }\r
+               }\r
+\r
+               // get an instance\r
+               Object instance = null;\r
+               try\r
+               {\r
+                       instance = theClass.newInstance();\r
+                       //Added by Jeff Huang\r
+                       //TODO: FIXIT\r
+               }\r
+               catch (Throwable t)\r
+               {\r
+                       throw StandardException.unexpectedUserException(t);\r
+               }\r
+\r
+               if (!(instance instanceof AggregateDefinition))\r
+               {\r
+                       throw StandardException.newException(SQLState.LANG_INVALID_USER_AGGREGATE_DEFINITION2, aggregateDefinitionClassName);\r
+               }\r
+\r
+               if (instance instanceof MaxMinAggregateDefinition)\r
+               {\r
+                       MaxMinAggregateDefinition temp = (MaxMinAggregateDefinition)instance;\r
+                       if (aggregateName.equals("MAX"))\r
+                               temp.setMaxOrMin(true);\r
+                       else\r
+                               temp.setMaxOrMin(false);\r
+               }\r
+\r
+               if (instance instanceof SumAvgAggregateDefinition)\r
+               {\r
+                       SumAvgAggregateDefinition temp1 = (SumAvgAggregateDefinition)instance;\r
+                       if (aggregateName.equals("SUM"))\r
+                               temp1.setSumOrAvg(true);\r
+                       else\r
+                               temp1.setSumOrAvg(false);\r
+               }\r
+\r
+               this.uad = (AggregateDefinition)instance;\r
+       \r
+               setOperator(aggregateName);\r
+               setMethodName(aggregateDefinitionClassName);\r
+\r
+       }\r
+\r
+       /**\r
+        * Indicate whether this aggregate is distinct or not.\r
+        *\r
+        * @return      true/false\r
+        */\r
+       public boolean isDistinct()\r
+       {\r
+               return distinct;\r
+       }\r
+\r
+       /**\r
+        * Get the class that implements that aggregator for this\r
+        * node.\r
+        *\r
+        * @return the class name\r
+        */\r
+       public String   getAggregatorClassName()\r
+       {\r
+               return aggregatorClassName.toString();\r
+       }\r
+\r
+       /**\r
+        * Get the class that implements that aggregator for this\r
+        * node.\r
+        *\r
+        * @return the class name\r
+        */\r
+       public String   getAggregateName()\r
+       {\r
+               return aggregateName;\r
+       }\r
+\r
+       /**\r
+        * Get the result column that has a new aggregator.\r
+        * This aggregator will be fed into the sorter.\r
+        *\r
+        * @param dd    the data dictionary\r
+        *\r
+        * @return the result column.  WARNING: it still needs to be bound\r
+        *\r
+        * @exception StandardException on error\r
+        */\r
+       public ResultColumn     getNewAggregatorResultColumn(DataDictionary     dd)\r
+               throws StandardException\r
+       {\r
+               String  className = aggregatorClassName.toString();\r
+\r
+               TypeId compTypeId = TypeId.getSQLTypeForJavaType(className);\r
+\r
+               /*\r
+               ** Create a null of the right type.  The proper aggregators\r
+               ** are created dynamically by the SortObservers\r
+               */\r
+               ConstantNode nullNode = getNullNode(\r
+                               compTypeId,\r
+                               getContextManager(),\r
+                               getTypeServices().getCollationType(),\r
+                               getTypeServices().getCollationDerivation()\r
+                               ); // no params\r
+\r
+               nullNode.bindExpression(\r
+                                               null,   // from\r
+                                               null,   // subquery\r
+                                               null);  // aggregate\r
+\r
+               /*\r
+               ** Create a result column with this new node below\r
+               ** it.\r
+               */\r
+               return (ResultColumn) getNodeFactory().getNode(\r
+                                                                       C_NodeTypes.RESULT_COLUMN,\r
+                                                                       aggregateName,\r
+                                                                       nullNode, \r
+                                                                       getContextManager());\r
+       }\r
+\r
+\r
+       /**\r
+        * Get the aggregate expression in a new result\r
+        * column.\r
+        *\r
+        * @param dd the data dictionary\r
+        *\r
+        * @return the result column.  WARNING: it still needs to be bound\r
+        *\r
+        * @exception StandardException on error\r
+        */\r
+       public ResultColumn     getNewExpressionResultColumn(DataDictionary     dd)\r
+               throws StandardException\r
+       {\r
+               ValueNode               node;\r
+               /*\r
+               ** Create a result column with the aggrergate operand\r
+               ** it.  If there is no operand, then we have a COUNT(*),\r
+               ** so we'll have to create a new null node and put\r
+               ** that in place.\r
+               */\r
+               node = (operand == null) ?\r
+                       this.getNewNullResultExpression() :\r
+                       operand;\r
+\r
+               return (ResultColumn) getNodeFactory().getNode(\r
+                                                               C_NodeTypes.RESULT_COLUMN,\r
+                                                               "##aggregate expression",\r
+                                                               node,\r
+                                                               getContextManager());\r
+       }\r
+\r
+       /**\r
+        * Get the null aggregate result expression\r
+        * column.\r
+        *\r
+        * @return the value node\r
+        *\r
+        * @exception StandardException on error\r
+        */\r
+       public ValueNode        getNewNullResultExpression()\r
+               throws StandardException\r
+       {\r
+               /*\r
+               ** Create a result column with the aggrergate operand\r
+               ** it.\r
+               */\r
+               return getNullNode(this.getTypeId(),\r
+                                                       getContextManager(), this.getTypeServices().getCollationType(),\r
+                                                       this.getTypeServices().getCollationDerivation());\r
+       }\r
+\r
+       /**\r
+        * Do code generation for this unary operator.  Should\r
+        * never be called for an aggregate -- it should be converted\r
+        * into something else by code generation time.\r
+        *\r
+        * @param acb   The ExpressionClassBuilder for the class we're generating\r
+        * @param mb    The method the code to place the code\r
+        *\r
+        * @exception StandardException         Thrown on error\r
+        */\r
+       public void generateExpression(ExpressionClassBuilder acb,\r
+                                                                                       MethodBuilder mb)\r
+               throws StandardException\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       SanityManager.THROWASSERT("generateExpression() should never "+\r
+                                       "be called on an AggregateNode.  "+\r
+                                       "replaceAggregatesWithColumnReferences should have " +\r
+                                       "been called prior to generateExpression");\r
+               }\r
+       }\r
+\r
+       /**\r
+        * Print a string ref of this node.\r
+        *\r
+        * @return a string representation of this node \r
+        */\r
+       public String toString()\r
+       {\r
+               if (SanityManager.DEBUG)\r
+               {\r
+                       return "Aggregate: "+aggregateName+\r
+                               "\ndistinct: "+distinct+\r
+                               super.toString();\r
+               }\r
+               else\r
+               {\r
+                       return "";\r
+               }\r
+       }\r
+\r
+       public boolean isConstant()\r
+       {\r
+               return false;\r
+       }\r
+       \r
+       public boolean constantExpression(PredicateList where)\r
+       {\r
+               return false;\r
+       }\r
+}\r