001: /*
002:
003: Derby - Class org.apache.derby.impl.sql.compile.VerifyAggregateExpressionsVisitor
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derby.impl.sql.compile;
023:
024: import org.apache.derby.iapi.sql.compile.Visitable;
025: import org.apache.derby.iapi.sql.compile.Visitor;
026:
027: import org.apache.derby.iapi.reference.SQLState;
028: import org.apache.derby.iapi.error.StandardException;
029:
030: /**
031: * If a RCL (SELECT list) contains an aggregate, then we must verify
032: * that the RCL (SELECT list) is valid.
033: * For ungrouped queries,
034: * the RCL must be composed entirely of valid aggregate expressions -
035: * in this case, no column references outside of an aggregate.
036: * For grouped aggregates,
037: * the RCL must be composed of grouping columns or valid aggregate
038: * expressions - in this case, the only column references allowed outside of
039: * an aggregate are grouping columns.
040: *
041: * @author jamie, from code written by jerry
042: */
043: public class VerifyAggregateExpressionsVisitor implements Visitor {
044: private GroupByList groupByList;
045:
046: public VerifyAggregateExpressionsVisitor(GroupByList groupByList) {
047: this .groupByList = groupByList;
048: }
049:
050: ////////////////////////////////////////////////
051: //
052: // VISITOR INTERFACE
053: //
054: ////////////////////////////////////////////////
055:
056: /**
057: * Verify that this expression is ok
058: * for an aggregate query.
059: *
060: * @param node the node to process
061: *
062: * @return me
063: *
064: * @exception StandardException on ColumnReference not
065: * in group by list, ValueNode or
066: * JavaValueNode that isn't under an
067: * aggregate
068: */
069: public Visitable visit(Visitable node) throws StandardException {
070: if (node instanceof ColumnReference) {
071: ColumnReference cr = (ColumnReference) node;
072:
073: if (groupByList == null) {
074: throw StandardException
075: .newException(
076: SQLState.LANG_INVALID_COL_REF_NON_GROUPED_SELECT_LIST,
077: cr.getSQLColumnName());
078: }
079:
080: if (groupByList.findGroupingColumn(cr) == null) {
081: throw StandardException
082: .newException(SQLState.LANG_INVALID_GROUPED_SELECT_LIST);
083: }
084: }
085:
086: /*
087: ** Subqueries are only valid if they do not have
088: ** correlations and are expression subqueries. RESOLVE:
089: ** this permits VARIANT expressions in the subquery --
090: ** should this be allowed? may be confusing to
091: ** users to complain about:
092: **
093: ** select max(x), (select sum(y).toString() from y) from x
094: */
095: else if (node instanceof SubqueryNode) {
096: SubqueryNode subq = (SubqueryNode) node;
097:
098: if ((subq.getSubqueryType() != SubqueryNode.EXPRESSION_SUBQUERY)
099: || subq.hasCorrelatedCRs()) {
100: throw StandardException
101: .newException((groupByList == null) ? SQLState.LANG_INVALID_NON_GROUPED_SELECT_LIST
102: : SQLState.LANG_INVALID_GROUPED_SELECT_LIST);
103: }
104:
105: /*
106: ** TEMPORARY RESTRICTION: we cannot handle an aggregate
107: ** in the subquery
108: */
109: HasNodeVisitor visitor = new HasNodeVisitor(
110: AggregateNode.class);
111: subq.accept(visitor);
112: if (visitor.hasNode()) {
113: throw StandardException
114: .newException((groupByList == null) ? SQLState.LANG_INVALID_NON_GROUPED_SELECT_LIST
115: : SQLState.LANG_INVALID_GROUPED_SELECT_LIST);
116: }
117: } else if (node instanceof JavaToSQLValueNode) {
118: // disallow any expression which involves native java computation.
119: // Not possible to consider java expressions for equivalence.
120: throw StandardException
121: .newException((groupByList == null) ? SQLState.LANG_INVALID_NON_GROUPED_SELECT_LIST
122: : SQLState.LANG_INVALID_GROUPED_SELECT_LIST);
123: }
124:
125: return node;
126: }
127:
128: /**
129: * Don't visit children under an aggregate, subquery or any node which
130: * is equivalent to any of the group by expressions.
131: *
132: * @param node the node to process
133: *
134: * @return true/false
135: * @throws StandardException
136: */
137: public boolean skipChildren(Visitable node)
138: throws StandardException {
139: return ((node instanceof AggregateNode)
140: || (node instanceof SubqueryNode) || (node instanceof ValueNode
141: && groupByList != null && groupByList
142: .findGroupingColumn((ValueNode) node) != null));
143: }
144:
145: public boolean stopTraversal() {
146: return false;
147: }
148: }
|