|
|
|
|
@ -113,7 +113,7 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
// having clause which has been analyzed
|
|
|
|
|
// For example: select k1, sum(k2) a from t group by k1 having a>1;
|
|
|
|
|
// this parameter: sum(t.k2) > 1
|
|
|
|
|
private Expr havingClauseAfterAnaylzed;
|
|
|
|
|
private Expr havingClauseAfterAnalyzed;
|
|
|
|
|
|
|
|
|
|
// END: Members that need to be reset()
|
|
|
|
|
// ///////////////////////////////////////
|
|
|
|
|
@ -182,8 +182,8 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
originalWhereClause = (other.originalWhereClause != null) ? other.originalWhereClause.clone() : null;
|
|
|
|
|
groupByClause = (other.groupByClause != null) ? other.groupByClause.clone() : null;
|
|
|
|
|
havingClause = (other.havingClause != null) ? other.havingClause.clone() : null;
|
|
|
|
|
havingClauseAfterAnaylzed =
|
|
|
|
|
other.havingClauseAfterAnaylzed != null ? other.havingClauseAfterAnaylzed.clone() : null;
|
|
|
|
|
havingClauseAfterAnalyzed =
|
|
|
|
|
other.havingClauseAfterAnalyzed != null ? other.havingClauseAfterAnalyzed.clone() : null;
|
|
|
|
|
|
|
|
|
|
colLabels = Lists.newArrayList(other.colLabels);
|
|
|
|
|
aggInfo = (other.aggInfo != null) ? other.aggInfo.clone() : null;
|
|
|
|
|
@ -208,7 +208,7 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
if (havingClause != null) {
|
|
|
|
|
havingClause.reset();
|
|
|
|
|
}
|
|
|
|
|
havingClauseAfterAnaylzed = null;
|
|
|
|
|
havingClauseAfterAnalyzed = null;
|
|
|
|
|
havingPred = null;
|
|
|
|
|
aggInfo = null;
|
|
|
|
|
analyticInfo = null;
|
|
|
|
|
@ -227,8 +227,8 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
if (havingPred != null) {
|
|
|
|
|
exprs.add(havingPred);
|
|
|
|
|
}
|
|
|
|
|
if (havingClauseAfterAnaylzed != null) {
|
|
|
|
|
exprs.add(havingClauseAfterAnaylzed);
|
|
|
|
|
if (havingClauseAfterAnalyzed != null) {
|
|
|
|
|
exprs.add(havingClauseAfterAnalyzed);
|
|
|
|
|
}
|
|
|
|
|
if (orderByElementsAfterAnalyzed != null) {
|
|
|
|
|
exprs.addAll(orderByElementsAfterAnalyzed.stream().map(orderByElement -> orderByElement.getExpr())
|
|
|
|
|
@ -295,8 +295,8 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
return havingPred;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public Expr getHavingClauseAfterAnaylzed() {
|
|
|
|
|
return havingClauseAfterAnaylzed;
|
|
|
|
|
public Expr getHavingClauseAfterAnalyzed() {
|
|
|
|
|
return havingClauseAfterAnalyzed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<TableRef> getTableRefs() {
|
|
|
|
|
@ -838,8 +838,8 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
if (whereClause != null) {
|
|
|
|
|
whereClause.getIds(result, null);
|
|
|
|
|
}
|
|
|
|
|
if (havingClauseAfterAnaylzed != null) {
|
|
|
|
|
havingClauseAfterAnaylzed.getIds(result, null);
|
|
|
|
|
if (havingClauseAfterAnalyzed != null) {
|
|
|
|
|
havingClauseAfterAnalyzed.getIds(result, null);
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
@ -1252,48 +1252,48 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
// according to case3, column name do not exist, keep alias name inside alias map
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
havingClauseAfterAnaylzed = havingClause.substitute(excludeAliasSMap, analyzer, false);
|
|
|
|
|
havingClauseAfterAnalyzed = havingClause.substitute(excludeAliasSMap, analyzer, false);
|
|
|
|
|
} else {
|
|
|
|
|
// If user set force using alias, then having clauses prefer using alias rather than column name
|
|
|
|
|
havingClauseAfterAnaylzed = havingClause.substitute(aliasSMap, analyzer, false);
|
|
|
|
|
havingClauseAfterAnalyzed = havingClause.substitute(aliasSMap, analyzer, false);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// according to mysql
|
|
|
|
|
// if there is no group by clause, the having clause should use alias
|
|
|
|
|
havingClauseAfterAnaylzed = havingClause.substitute(aliasSMap, analyzer, false);
|
|
|
|
|
havingClauseAfterAnalyzed = havingClause.substitute(aliasSMap, analyzer, false);
|
|
|
|
|
}
|
|
|
|
|
havingClauseAfterAnaylzed = rewriteQueryExprByMvColumnExpr(havingClauseAfterAnaylzed, analyzer);
|
|
|
|
|
havingClauseAfterAnaylzed.checkReturnsBool("HAVING clause", true);
|
|
|
|
|
havingClauseAfterAnalyzed = rewriteQueryExprByMvColumnExpr(havingClauseAfterAnalyzed, analyzer);
|
|
|
|
|
havingClauseAfterAnalyzed.checkReturnsBool("HAVING clause", true);
|
|
|
|
|
if (groupingInfo != null) {
|
|
|
|
|
groupingInfo.substituteGroupingFn(Arrays.asList(havingClauseAfterAnaylzed), analyzer);
|
|
|
|
|
groupingInfo.substituteGroupingFn(Arrays.asList(havingClauseAfterAnalyzed), analyzer);
|
|
|
|
|
}
|
|
|
|
|
// can't contain analytic exprs
|
|
|
|
|
Expr analyticExpr = havingClauseAfterAnaylzed.findFirstOf(AnalyticExpr.class);
|
|
|
|
|
Expr analyticExpr = havingClauseAfterAnalyzed.findFirstOf(AnalyticExpr.class);
|
|
|
|
|
if (analyticExpr != null) {
|
|
|
|
|
throw new AnalysisException(
|
|
|
|
|
"HAVING clause must not contain analytic expressions: "
|
|
|
|
|
+ analyticExpr.toSql());
|
|
|
|
|
}
|
|
|
|
|
if (isContainInBitmap(havingClauseAfterAnaylzed)) {
|
|
|
|
|
if (isContainInBitmap(havingClauseAfterAnalyzed)) {
|
|
|
|
|
throw new AnalysisException(
|
|
|
|
|
"HAVING clause dose not support in bitmap syntax: " + havingClauseAfterAnaylzed.toSql());
|
|
|
|
|
"HAVING clause dose not support in bitmap syntax: " + havingClauseAfterAnalyzed.toSql());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (groupByClause == null && !selectList.isDistinct()
|
|
|
|
|
&& !TreeNode.contains(resultExprs, Expr.isAggregatePredicate())
|
|
|
|
|
&& (havingClauseAfterAnaylzed == null || !havingClauseAfterAnaylzed.contains(
|
|
|
|
|
&& (havingClauseAfterAnalyzed == null || !havingClauseAfterAnalyzed.contains(
|
|
|
|
|
Expr.isAggregatePredicate()))
|
|
|
|
|
&& (sortInfo == null || !TreeNode.contains(sortInfo.getOrderingExprs(),
|
|
|
|
|
Expr.isAggregatePredicate()))) {
|
|
|
|
|
// We're not computing aggregates but we still need to register the HAVING
|
|
|
|
|
// clause which could, e.g., contain a constant expression evaluating to false.
|
|
|
|
|
if (havingClauseAfterAnaylzed != null) {
|
|
|
|
|
if (havingClauseAfterAnaylzed.contains(Subquery.class)) {
|
|
|
|
|
if (havingClauseAfterAnalyzed != null) {
|
|
|
|
|
if (havingClauseAfterAnalyzed.contains(Subquery.class)) {
|
|
|
|
|
throw new AnalysisException("Only constant expr could be supported in having clause "
|
|
|
|
|
+ "when no aggregation in stmt");
|
|
|
|
|
}
|
|
|
|
|
analyzer.registerConjuncts(havingClauseAfterAnaylzed, true);
|
|
|
|
|
analyzer.registerConjuncts(havingClauseAfterAnalyzed, true);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
@ -1314,7 +1314,7 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
if (selectList.isDistinct()
|
|
|
|
|
&& (groupByClause != null
|
|
|
|
|
|| TreeNode.contains(resultExprs, Expr.isAggregatePredicate())
|
|
|
|
|
|| (havingClauseAfterAnaylzed != null && havingClauseAfterAnaylzed.contains(
|
|
|
|
|
|| (havingClauseAfterAnalyzed != null && havingClauseAfterAnalyzed.contains(
|
|
|
|
|
Expr.isAggregatePredicate())))) {
|
|
|
|
|
throw new AnalysisException("cannot combine SELECT DISTINCT with aggregate functions or GROUP BY");
|
|
|
|
|
}
|
|
|
|
|
@ -1335,8 +1335,8 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
// of this statement.
|
|
|
|
|
ArrayList<FunctionCallExpr> aggExprs = Lists.newArrayList();
|
|
|
|
|
TreeNode.collect(resultExprs, Expr.isAggregatePredicate(), aggExprs);
|
|
|
|
|
if (havingClauseAfterAnaylzed != null) {
|
|
|
|
|
havingClauseAfterAnaylzed.collect(Expr.isAggregatePredicate(), aggExprs);
|
|
|
|
|
if (havingClauseAfterAnalyzed != null) {
|
|
|
|
|
havingClauseAfterAnalyzed.collect(Expr.isAggregatePredicate(), aggExprs);
|
|
|
|
|
}
|
|
|
|
|
if (sortInfo != null) {
|
|
|
|
|
// TODO: Avoid evaluating aggs in ignored order-bys
|
|
|
|
|
@ -1362,9 +1362,9 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
// the resultExprs and havingClause must substitute in the same way as aggExprs
|
|
|
|
|
// then resultExprs and havingClause can be substitute correctly using combinedSmap
|
|
|
|
|
resultExprs = Expr.substituteList(resultExprs, countAllMap, analyzer, false);
|
|
|
|
|
if (havingClauseAfterAnaylzed != null) {
|
|
|
|
|
havingClauseAfterAnaylzed =
|
|
|
|
|
havingClauseAfterAnaylzed.substitute(countAllMap, analyzer, false);
|
|
|
|
|
if (havingClauseAfterAnalyzed != null) {
|
|
|
|
|
havingClauseAfterAnalyzed =
|
|
|
|
|
havingClauseAfterAnalyzed.substitute(countAllMap, analyzer, false);
|
|
|
|
|
}
|
|
|
|
|
if (sortInfo != null) {
|
|
|
|
|
// the ordering exprs must substitute in the same way as resultExprs
|
|
|
|
|
@ -1488,10 +1488,10 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
LOG.debug("resultexprs: " + Expr.debugString(resultExprs));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (havingClauseAfterAnaylzed != null) {
|
|
|
|
|
if (havingClauseAfterAnalyzed != null) {
|
|
|
|
|
// forbidden correlated subquery in having clause
|
|
|
|
|
List<Subquery> subqueryInHaving = Lists.newArrayList();
|
|
|
|
|
havingClauseAfterAnaylzed.collect(Subquery.class, subqueryInHaving);
|
|
|
|
|
havingClauseAfterAnalyzed.collect(Subquery.class, subqueryInHaving);
|
|
|
|
|
for (Subquery subquery : subqueryInHaving) {
|
|
|
|
|
if (subquery.isCorrelatedPredicate(getTableRefIds())) {
|
|
|
|
|
throw new AnalysisException("The correlated having clause is not supported");
|
|
|
|
|
@ -1516,8 +1516,8 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
if (LOG.isDebugEnabled()) {
|
|
|
|
|
LOG.debug("post-agg selectListExprs: " + Expr.debugString(resultExprs));
|
|
|
|
|
}
|
|
|
|
|
if (havingClauseAfterAnaylzed != null) {
|
|
|
|
|
havingPred = havingClauseAfterAnaylzed.substitute(combinedSmap, analyzer, false);
|
|
|
|
|
if (havingClauseAfterAnalyzed != null) {
|
|
|
|
|
havingPred = havingClauseAfterAnalyzed.substitute(combinedSmap, analyzer, false);
|
|
|
|
|
analyzer.registerConjuncts(havingPred, true, finalAggInfo.getOutputTupleId().asList());
|
|
|
|
|
if (LOG.isDebugEnabled()) {
|
|
|
|
|
LOG.debug("post-agg havingPred: " + havingPred.debugString());
|
|
|
|
|
@ -1782,7 +1782,7 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
}
|
|
|
|
|
if (havingClause != null) {
|
|
|
|
|
havingClause = rewriter.rewrite(havingClause, analyzer);
|
|
|
|
|
havingClauseAfterAnaylzed.collect(Subquery.class, subqueryExprs);
|
|
|
|
|
havingClauseAfterAnalyzed.collect(Subquery.class, subqueryExprs);
|
|
|
|
|
}
|
|
|
|
|
for (Subquery subquery : subqueryExprs) {
|
|
|
|
|
subquery.getStatement().rewriteExprs(rewriter);
|
|
|
|
|
@ -1876,9 +1876,9 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if (havingClause != null) {
|
|
|
|
|
registerExprId(havingClauseAfterAnaylzed);
|
|
|
|
|
exprMap.put(havingClauseAfterAnaylzed.getId().toString(), havingClauseAfterAnaylzed);
|
|
|
|
|
havingClauseAfterAnaylzed.collect(Subquery.class, subqueryExprs);
|
|
|
|
|
registerExprId(havingClauseAfterAnalyzed);
|
|
|
|
|
exprMap.put(havingClauseAfterAnalyzed.getId().toString(), havingClauseAfterAnalyzed);
|
|
|
|
|
havingClauseAfterAnalyzed.collect(Subquery.class, subqueryExprs);
|
|
|
|
|
}
|
|
|
|
|
for (Subquery subquery : subqueryExprs) {
|
|
|
|
|
registerExprId(subquery);
|
|
|
|
|
@ -1983,8 +1983,8 @@ public class SelectStmt extends QueryStmt {
|
|
|
|
|
whereClause.collect(Subquery.class, subqueryExprs);
|
|
|
|
|
}
|
|
|
|
|
if (havingClause != null) {
|
|
|
|
|
havingClause = rewrittenExprMap.get(havingClauseAfterAnaylzed.getId().toString());
|
|
|
|
|
havingClauseAfterAnaylzed.collect(Subquery.class, subqueryExprs);
|
|
|
|
|
havingClause = rewrittenExprMap.get(havingClauseAfterAnalyzed.getId().toString());
|
|
|
|
|
havingClauseAfterAnalyzed.collect(Subquery.class, subqueryExprs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (Subquery subquery : subqueryExprs) {
|
|
|
|
|
|