--- /dev/null
+/*\r
+\r
+ Derby - Class org.apache.derby.impl.sql.compile.VerifyAggregateExpressionsVisitor\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.Visitable; \r
+import org.apache.derby.iapi.sql.compile.Visitor;\r
+\r
+import org.apache.derby.iapi.reference.SQLState;\r
+import org.apache.derby.iapi.error.StandardException;\r
+\r
+/**\r
+ * If a RCL (SELECT list) contains an aggregate, then we must verify\r
+ * that the RCL (SELECT list) is valid. \r
+ * For ungrouped queries,\r
+ * the RCL must be composed entirely of valid aggregate expressions -\r
+ * in this case, no column references outside of an aggregate.\r
+ * For grouped aggregates,\r
+ * the RCL must be composed of grouping columns or valid aggregate\r
+ * expressions - in this case, the only column references allowed outside of\r
+ * an aggregate are grouping columns.\r
+ *\r
+ */\r
+public class VerifyAggregateExpressionsVisitor implements Visitor\r
+{\r
+ private GroupByList groupByList;\r
+\r
+ public VerifyAggregateExpressionsVisitor(GroupByList groupByList)\r
+ {\r
+ this.groupByList = groupByList;\r
+ }\r
+\r
+\r
+ ////////////////////////////////////////////////\r
+ //\r
+ // VISITOR INTERFACE\r
+ //\r
+ ////////////////////////////////////////////////\r
+\r
+ /**\r
+ * Verify that this expression is ok\r
+ * for an aggregate query. \r
+ *\r
+ * @param node the node to process\r
+ *\r
+ * @return me\r
+ *\r
+ * @exception StandardException on ColumnReference not\r
+ * in group by list, ValueNode or \r
+ * JavaValueNode that isn't under an\r
+ * aggregate\r
+ */\r
+ public Visitable visit(Visitable node)\r
+ throws StandardException\r
+ {\r
+ if (node instanceof ColumnReference)\r
+ {\r
+ ColumnReference cr = (ColumnReference)node;\r
+ \r
+ if (groupByList == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_INVALID_COL_REF_NON_GROUPED_SELECT_LIST, cr.getSQLColumnName());\r
+ }\r
+\r
+ if (groupByList.findGroupingColumn(cr) == null)\r
+ {\r
+ throw StandardException.newException(SQLState.LANG_INVALID_GROUPED_SELECT_LIST);\r
+ }\r
+ } \r
+ \r
+ /*\r
+ ** Subqueries are only valid if they do not have\r
+ ** correlations and are expression subqueries. RESOLVE:\r
+ ** this permits VARIANT expressions in the subquery --\r
+ ** should this be allowed? may be confusing to\r
+ ** users to complain about:\r
+ **\r
+ ** select max(x), (select sum(y).toString() from y) from x\r
+ */\r
+ else if (node instanceof SubqueryNode)\r
+ {\r
+ SubqueryNode subq = (SubqueryNode)node;\r
+ \r
+ if ((subq.getSubqueryType() != SubqueryNode.EXPRESSION_SUBQUERY) ||\r
+ subq.hasCorrelatedCRs())\r
+ {\r
+ throw StandardException.newException( (groupByList == null) ?\r
+ SQLState.LANG_INVALID_NON_GROUPED_SELECT_LIST :\r
+ SQLState.LANG_INVALID_GROUPED_SELECT_LIST);\r
+ }\r
+\r
+ /*\r
+ ** TEMPORARY RESTRICTION: we cannot handle an aggregate\r
+ ** in the subquery \r
+ */\r
+ HasNodeVisitor visitor = new HasNodeVisitor(AggregateNode.class);\r
+ subq.accept(visitor);\r
+ if (visitor.hasNode())\r
+ { \r
+ throw StandardException.newException( (groupByList == null) ?\r
+ SQLState.LANG_INVALID_NON_GROUPED_SELECT_LIST :\r
+ SQLState.LANG_INVALID_GROUPED_SELECT_LIST);\r
+ }\r
+ }\r
+ return node;\r
+ }\r
+\r
+ /**\r
+ * Don't visit children under an aggregate, subquery or any node which\r
+ * is equivalent to any of the group by expressions.\r
+ *\r
+ * @param node the node to process\r
+ *\r
+ * @return true/false\r
+ * @throws StandardException \r
+ */\r
+ public boolean skipChildren(Visitable node) throws StandardException \r
+ {\r
+ return ((node instanceof AggregateNode) ||\r
+ (node instanceof SubqueryNode) ||\r
+ (node instanceof ValueNode &&\r
+ groupByList != null \r
+ && groupByList.findGroupingColumn((ValueNode)node) != null));\r
+ }\r
+ \r
+ public boolean stopTraversal()\r
+ {\r
+ return false;\r
+ }\r
+} \r