/*
* <A NAME="tableExpression">tableExpression</A>
*/
final public SelectNode tableExpression(ResultColumnList selectList) throws ParseException, StandardException {
SelectNode selectNode;
FromList fromList;
ValueNode whereClause = null;
GroupByList groupByList = null;
ValueNode havingClause = null;
Token whereToken;
fromList = fromClause();
switch (jj_nt.kind) {
case WHERE:
whereToken = jj_consume_token(WHERE);
whereClause = whereClause(whereToken);
break;
default:
jj_la1[161] = jj_gen;
;
}
switch (jj_nt.kind) {
case GROUP:
groupByList = groupByClause();
break;
default:
jj_la1[162] = jj_gen;
;
}
switch (jj_nt.kind) {
case HAVING:
havingClause = havingClause();
break;
default:
jj_la1[163] = jj_gen;
;
}
selectNode = (SelectNode) nodeFactory.getNode(
C_NodeTypes.SELECT_NODE,
selectList,
null, /* AGGREGATE list */
fromList,
whereClause,
groupByList,
getContextManager());
/* A SELECT with a GROUP BY or HAVING clause is the one instance where the
* parser will generate a tree that does not exactly match the query.
* The resulting tree is an outer SelectNode with the HAVING clause
* as its WHERE clause and a FromList containing a single FromSubquery -
* the user SELECT, minus the HAVING clause.
* The outer SELECT gets a SELECT *.
* The FromSubquery will have a null name, something that the user cannot do,
* to enable the binding of any correlated columns in the HAVING clause.
*/
if (groupByList != null || havingClause != null)
{
FromSubquery fromSubquery;
ResultColumnList outerRCL =
(ResultColumnList) nodeFactory.getNode(
C_NodeTypes.RESULT_COLUMN_LIST,
getContextManager());
/* Wrap the user SELECT in a FromSubquery */
fromList = (FromList) nodeFactory.getNode(
C_NodeTypes.FROM_LIST,
getNodeFactory().doJoinOrderOptimization(),
getContextManager());
fromSubquery = (FromSubquery) nodeFactory.getNode(
C_NodeTypes.FROM_SUBQUERY,
selectNode,
null,
null,
null,
getContextManager());
fromList.addElement(fromSubquery);
/* Pull any aggregates out of the HAVING clause and append them to
* SELECT list in the user's select, replacing the aggregates in
* the HAVING clause with ColumnReferences to the aggregate.
* Do NOT replace anything below a ResultSetNode. This means that
* we'll replace
* FROM x HAVING max(x.x) > 2
* but not
* FROM x HAVING x.x = (select max(y.y) from y)
* Aggregates under a SELECT are all fixed up correctly
* later, but here we need to get the aggregates that
* aren't under result sets.
*
* Appended ResultColumns marked as generated so the wrapper
* select won't see them when the * is expanded.
*
* RESOLVE - someday we should try to find matching aggregates
* instead of just adding them.
*
* NOTE: This rewriting of the query tree makes the handling of an ORDER BY
* clause difficult. See OrderByColumn.pullUpOrderByColumn. It makes specific
* assumptions about the structure of the generated query tree. Do not make
* any changes to this transformation without carefully considering the
* OrderByColumn pullUpOrderByColumn and bindOrderByColumn methods.
*/
if (havingClause != null)
{
ReplaceAggregatesWithCRVisitor visitor =
new ReplaceAggregatesWithCRVisitor(selectList, ResultSetNode.class);
havingClause = (ValueNode)havingClause.accept(visitor);
// fix for HAVING without GROUP BY, makes sure we get one
// aggregate operator by adding a count(*), this fixes beetle 5853, 5890
if (groupByList == null) {
ValueNode vn = (ValueNode) nodeFactory.getNode(
C_NodeTypes.AGGREGATE_NODE,
null,
org.apache.derby.impl.sql.compile.CountAggregateDefinition.class,
Boolean.FALSE, // distinct Boolean.TRUE?
"COUNT(*)",
getContextManager());
AggregateNode n = (AggregateNode) vn;
n.replaceAggregatesWithColumnReferences(selectList, 0);
}
}
outerRCL.addResultColumn((ResultColumn) nodeFactory.getNode(
C_NodeTypes.ALL_RESULT_COLUMN,
null,
getContextManager()));
// wrap another selectNode on the outside of the subquery
selectNode = (SelectNode) nodeFactory.getNode(
C_NodeTypes.SELECT_NODE,
outerRCL, /* SELECT * from user SELECT */
null, /* AGGREGATE list */
fromList,
havingClause,
null, /* GROUP BY list */
getContextManager());
/* Mark the wrapping FromSubquery and SelectNode as appropriate */
if (groupByList != null)
{
fromSubquery.markAsForGroupByClause();
selectNode.markAsForGroupByClause();
}
if (havingClause != null)
{
fromSubquery.markAsForHavingClause();
selectNode.markAsForHavingClause();
}
}
{if (true) return selectNode;}
throw new Error("Missing return statement in function");
}