in fe/fe-core/src/main/java/org/apache/doris/analysis/SelectStmt.java [493:785]
public void analyze(Analyzer analyzer) throws UserException {
if (isAnalyzed()) {
return;
}
super.analyze(analyzer);
if (mvSMap.size() != 0) {
mvSMap.useNotCheckDescIdEquals();
for (TableRef tableRef : getTableRefs()) {
if (tableRef.getOnClause() == null) {
continue;
}
try {
Expr expr = tableRef.getOnClause();
Expr originalExpr = expr.clone().substituteImpl(mvSMap, null, analyzer);
originalExpr.reset();
tableRef.setOnClause(originalExpr);
} catch (Exception e) {
LOG.warn("", e);
}
}
}
fromClause.setNeedToSql(needToSql);
fromClause.analyze(analyzer);
if (!isForbiddenMVRewrite()) {
Boolean haveMv = false;
for (TableRef tbl : fromClause) {
if (!tbl.haveDesc() || !(tbl.getTable() instanceof OlapTable)) {
continue;
}
OlapTable olapTable = (OlapTable) tbl.getTable();
if (olapTable.getIndexIds().size() != 1) {
haveMv = true;
}
}
if (!haveMv) {
forbiddenMVRewrite();
}
}
// Generate !empty() predicates to filter out empty collections.
// Skip this step when analyzing a WITH-clause because CollectionTableRefs
// do not register collection slots in their parent in that context
// (see CollectionTableRef.analyze()).
if (!analyzer.isWithClause()) {
registerIsNotEmptyPredicates(analyzer);
}
// populate selectListExprs, aliasSMap, groupingSmap and colNames
if (selectList.isExcept()) {
if (needToSql) {
originalExpr = new ArrayList<>();
}
List<SelectListItem> items = selectList.getItems();
TableName tblName = items.get(0).getTblName();
if (tblName == null) {
expandStar(analyzer);
} else {
expandStar(analyzer, tblName);
}
// get excepted cols
ArrayList<String> exceptCols = new ArrayList<>();
for (SelectListItem item : items) {
Expr expr = item.getExpr();
if (!(item.getExpr() instanceof SlotRef)) {
throw new AnalysisException("`SELECT * EXCEPT` only supports column name.");
}
exceptCols.add(expr.toColumnLabel());
}
// remove excepted columns
resultExprs.removeIf(expr -> exceptCols.contains(expr.toColumnLabel()));
colLabels.removeIf(exceptCols::contains);
originalExpr = new ArrayList<>(resultExprs);
} else {
if (needToSql) {
originalExpr = new ArrayList<>();
}
List<SelectListItem> items = selectList.getItems();
for (int i = 0; i < items.size(); i++) {
SelectListItem item = items.get(i);
if (item.isStar()) {
TableName tblName = item.getTblName();
if (tblName == null) {
expandStar(analyzer);
} else {
expandStar(analyzer, tblName);
}
} else {
// save originalExpr before being analyzed
// because analyze may change the expr by adding cast or some other stuff
if (needToSql) {
originalExpr.add(item.getExpr().clone());
}
// Analyze the resultExpr before generating a label to ensure enforcement
// of expr child and depth limits (toColumn() label may call toSql()).
item.getExpr().analyze(analyzer);
if (!(item.getExpr() instanceof CaseExpr)
&& item.getExpr().contains(Predicates.instanceOf(Subquery.class))) {
throw new AnalysisException("Subquery is not supported in the select list.");
}
resultExprs.add(rewriteQueryExprByMvColumnExpr(item.getExpr(), analyzer));
String columnLabel = null;
Class<? extends StatementBase> statementClazz = analyzer.getRootStatementClazz();
if (statementClazz != null
&& (!QueryStmt.class.isAssignableFrom(statementClazz) || hasOutFileClause())) {
// Infer column name when item is expr
columnLabel = item.toColumnLabel(i);
}
if (columnLabel == null) {
// use original column label
columnLabel = item.toColumnLabel();
}
SlotRef aliasRef = new SlotRef(null, columnLabel);
Expr existingAliasExpr = aliasSMap.get(aliasRef);
if (existingAliasExpr != null && !existingAliasExpr.equals(item.getExpr())) {
// If we have already seen this alias, it refers to more than one column and
// therefore is ambiguous.
ambiguousAliasList.add(aliasRef);
}
aliasSMap.put(aliasRef, item.getExpr().clone());
colLabels.add(columnLabel);
subColPath.add(item.toSubColumnLabels());
}
}
}
if (groupByClause != null && groupByClause.isGroupByExtension()) {
ArrayList<Expr> aggFnExprList = new ArrayList<>();
for (SelectListItem item : selectList.getItems()) {
aggFnExprList.clear();
getAggregateFnExpr(item.getExpr(), aggFnExprList);
for (Expr aggFnExpr : aggFnExprList) {
for (Expr expr : groupByClause.getGroupingExprs()) {
if (aggFnExpr.contains(expr)) {
throw new AnalysisException("column: " + expr.toSql() + " cannot both in select "
+ "list and aggregate functions when using GROUPING SETS/CUBE/ROLLUP, "
+ "please use union instead.");
}
}
}
}
groupingInfo = new GroupingInfo(analyzer, groupByClause);
groupingInfo.substituteGroupingFn(resultExprs, analyzer);
} else {
for (Expr expr : resultExprs) {
if (checkGroupingFn(expr)) {
throw new AnalysisException(
"cannot use GROUPING functions without [grouping sets|rollup|cube] "
+ "clause or grouping sets only have one element.");
}
}
}
if (valueList != null) {
if (!fromInsert) {
valueList.analyzeForSelect(analyzer);
}
for (Expr expr : valueList.getFirstRow()) {
if (expr instanceof DefaultValueExpr) {
resultExprs.add(new StringLiteral(DEFAULT_VALUE));
} else {
resultExprs.add(rewriteQueryExprByMvColumnExpr(expr, analyzer));
}
colLabels.add("col_" + colLabels.size());
subColPath.add(expr.toSubColumnLabel());
}
}
// analyze selectListExprs
Expr.analyze(resultExprs, analyzer);
if (TreeNode.contains(resultExprs, AnalyticExpr.class)) {
if (fromClause.isEmpty()) {
throw new AnalysisException("Analytic expressions require FROM clause.");
}
// do this here, not after analyzeAggregation(), otherwise the AnalyticExprs
// will get substituted away
if (selectList.isDistinct()) {
throw new AnalysisException(
"cannot combine SELECT DISTINCT with analytic functions");
}
}
if (whereClause != null) {
whereClauseRewrite();
if (checkGroupingFn(whereClause)) {
throw new AnalysisException("grouping operations are not allowed in WHERE.");
}
whereClause.analyze(analyzer);
if (whereClause.containsAggregate()) {
ErrorReport.reportAnalysisException(ErrorCode.ERR_INVALID_GROUP_FUNC_USE);
}
whereClause.checkReturnsBool("WHERE clause", false);
Expr e = whereClause.findFirstOf(AnalyticExpr.class);
if (e != null) {
throw new AnalysisException(
"WHERE clause must not contain analytic expressions: " + e.toSql());
}
analyzer.registerConjuncts(whereClause, false, getTableRefIds());
}
if (whereClause != null) {
whereClause = rewriteQueryExprByMvColumnExpr(whereClause, analyzer);
}
for (TableRef tableRef : getTableRefs()) {
if (tableRef.getOnClause() == null) {
continue;
}
tableRef.setOnClause(rewriteQueryExprByMvColumnExpr(tableRef.getOnClause(), analyzer));
}
createSortInfo(analyzer);
if (sortInfo != null && CollectionUtils.isNotEmpty(sortInfo.getOrderingExprs())) {
if (groupingInfo != null) {
// List of executable exprs in select clause has been substituted, only the unique expr in Ordering
// exprs needs to be substituted.
// Otherwise, if substitute twice for `Grouping Func Expr`, a null pointer will be reported.
List<Expr> orderingExprNotInSelect = sortInfo.getOrderingExprs().stream()
.filter(item -> !resultExprs.contains(item)).collect(Collectors.toList());
groupingInfo.substituteGroupingFn(orderingExprNotInSelect, analyzer);
}
}
analyzeAggregation(analyzer);
createAnalyticInfo(analyzer);
eliminatingSortNode();
checkAndSetPointQuery();
if (checkEnableTwoPhaseRead(analyzer)) {
// If optimize enabled, we try our best to read less columns from ScanNode,
// here we analyze conjunct exprs and ordering exprs before resultExprs,
// rest of resultExprs will be marked as `INVALID`, such columns will
// be prevent from reading from ScanNode.Those columns will be finally
// read by the second fetch phase
isTwoPhaseOptEnabled = true;
if (LOG.isDebugEnabled()) {
LOG.debug("two phase read optimize enabled");
}
// Expr.analyze(resultExprs, analyzer);
Set<SlotRef> resultSlots = Sets.newHashSet();
Set<SlotRef> orderingSlots = Sets.newHashSet();
Set<SlotRef> conjuntSlots = Sets.newHashSet();
TreeNode.collect(resultExprs, Predicates.instanceOf(SlotRef.class), resultSlots);
if (sortInfo != null) {
TreeNode.collect(sortInfo.getOrderingExprs(),
Predicates.instanceOf(SlotRef.class), orderingSlots);
}
if (whereClause != null) {
whereClause.collect(SlotRef.class, conjuntSlots);
}
if (havingClauseAfterAnalyzed != null) {
havingClauseAfterAnalyzed.collect(SlotRef.class, conjuntSlots);
}
resultSlots.removeAll(orderingSlots);
resultSlots.removeAll(conjuntSlots);
// reset slots need to do fetch column
for (SlotRef slot : resultSlots) {
// invalid slots will be pruned from reading from ScanNode
slot.setNeedMaterialize(false);
}
if (LOG.isDebugEnabled()) {
LOG.debug("resultsSlots {}", resultSlots);
LOG.debug("orderingSlots {}", orderingSlots);
LOG.debug("conjuntSlots {}", conjuntSlots);
}
}
if (evaluateOrderBy) {
createSortTupleInfo(analyzer);
}
if (needToSql) {
sqlString = toSql();
}
resolveInlineViewRefs(analyzer);
if (analyzer.hasEmptySpjResultSet() && aggInfo == null) {
analyzer.setHasEmptyResultSet();
}
if (aggInfo != null) {
if (LOG.isDebugEnabled()) {
LOG.debug("post-analysis " + aggInfo.debugString());
}
}
if (hasOutFileClause()) {
outFileClause.analyze(analyzer, resultExprs, colLabels);
}
}