14
14
15
15
package com .starrocks .sql .analyzer ;
16
16
17
+ import com .google .common .base .Preconditions ;
17
18
import com .google .common .collect .ImmutableList ;
18
19
import com .google .common .collect .Lists ;
19
20
import com .google .common .collect .Sets ;
@@ -86,10 +87,11 @@ public void analyze(AnalyzeState analyzeState,
86
87
analyzeHaving (havingClause , analyzeState , sourceScope , outputScope , outputExpressions );
87
88
88
89
// Construct sourceAndOutputScope with sourceScope and outputScope
89
- Scope sourceAndOutputScope = computeAndAssignOrderScope (analyzeState , sourceScope , outputScope );
90
+ Scope sourceAndOutputScope = computeAndAssignOrderScope (analyzeState , sourceScope , outputScope ,
91
+ selectList .isDistinct ());
90
92
91
93
List <OrderByElement > orderByElements =
92
- analyzeOrderBy (sortClause , analyzeState , sourceAndOutputScope , outputExpressions );
94
+ analyzeOrderBy (sortClause , analyzeState , sourceAndOutputScope , outputExpressions , selectList . isDistinct () );
93
95
List <Expr > orderByExpressions =
94
96
orderByElements .stream ().map (OrderByElement ::getExpr ).collect (Collectors .toList ());
95
97
@@ -183,7 +185,7 @@ public void analyze(AnalyzeState analyzeState,
183
185
.collect (Collectors .toList ());
184
186
185
187
Scope sourceScopeForOrder = new Scope (RelationId .anonymous (), new RelationFields (sourceForOrderFields ));
186
- computeAndAssignOrderScope (analyzeState , sourceScopeForOrder , outputScope );
188
+ computeAndAssignOrderScope (analyzeState , sourceScopeForOrder , outputScope , selectList . isDistinct () );
187
189
analyzeState .setOrderSourceExpressions (orderSourceExpressions );
188
190
}
189
191
@@ -317,7 +319,8 @@ private List<Expr> analyzeSelect(SelectList selectList, Relation fromRelation, b
317
319
318
320
private List <OrderByElement > analyzeOrderBy (List <OrderByElement > orderByElements , AnalyzeState analyzeState ,
319
321
Scope orderByScope ,
320
- List <Expr > outputExpressions ) {
322
+ List <Expr > outputExpressions ,
323
+ boolean isDistinct ) {
321
324
if (orderByElements == null ) {
322
325
analyzeState .setOrderBy (Collections .emptyList ());
323
326
return Collections .emptyList ();
@@ -333,20 +336,33 @@ private List<OrderByElement> analyzeOrderBy(List<OrderByElement> orderByElements
333
336
if (ordinal < 1 || ordinal > outputExpressions .size ()) {
334
337
throw new SemanticException ("ORDER BY position %s is not in select list" , ordinal );
335
338
}
339
+ // index can ensure no ambiguous, we don't need to re-analyze this output expression
336
340
expression = outputExpressions .get ((int ) ordinal - 1 );
337
- }
338
-
339
- if (expression instanceof FieldReference ) {
340
- // If the expression of order by is a FieldReference, it means that the type of sql is
341
+ } else if (expression instanceof FieldReference ) {
342
+ // If the expression of order by is a FieldReference, and it's not a distinct select,
343
+ // it means that the type of sql is
341
344
// "select * from t order by 1", then this FieldReference cannot be parsed in OrderByScope,
342
345
// but should be parsed in sourceScope
343
- analyzeExpression (expression , analyzeState , orderByScope .getParent ());
346
+ if (isDistinct ) {
347
+ analyzeExpression (expression , analyzeState , orderByScope );
348
+ } else {
349
+ analyzeExpression (expression , analyzeState , orderByScope .getParent ());
350
+ }
344
351
} else {
345
352
ExpressionAnalyzer expressionAnalyzer = new ExpressionAnalyzer (session );
346
353
expressionAnalyzer .analyzeWithoutUpdateState (expression , analyzeState , orderByScope );
347
354
List <Expr > aggregations = Lists .newArrayList ();
348
355
expression .collectAll (e -> e .isAggregate (), aggregations );
349
- aggregations .forEach (e -> analyzeExpression (e , analyzeState , orderByScope .getParent ()));
356
+ if (isDistinct && !aggregations .isEmpty ()) {
357
+ throw new SemanticException ("for SELECT DISTINCT, ORDER BY expressions must appear in select list" ,
358
+ expression .getPos ());
359
+ }
360
+
361
+ if (!aggregations .isEmpty ()) {
362
+ // use parent scope to analyze agg func firstly
363
+ Preconditions .checkState (orderByScope .getParent () != null , "parent scope not be set" );
364
+ aggregations .forEach (e -> analyzeExpression (e , analyzeState , orderByScope .getParent ()));
365
+ }
350
366
analyzeExpression (expression , analyzeState , orderByScope );
351
367
}
352
368
@@ -661,23 +677,24 @@ public Expr visitSlot(SlotRef slotRef, Void context) {
661
677
}
662
678
}
663
679
664
- private Scope computeAndAssignOrderScope (AnalyzeState analyzeState , Scope sourceScope , Scope outputScope ) {
665
- // The Scope used by order by allows parsing of the same column,
666
- // such as 'select v1 as v, v1 as v from t0 order by v'
667
- // but normal parsing does not allow it. So add a de-duplication operation here.
680
+ private Scope computeAndAssignOrderScope (AnalyzeState analyzeState , Scope sourceScope , Scope outputScope ,
681
+ boolean isDistinct ) {
682
+
683
+ List <Field > allFields = Lists .newArrayList ();
684
+ // order by can only "see" fields from distinct output
685
+ if (isDistinct ) {
686
+ allFields = removeDuplicateField (outputScope .getRelationFields ().getAllFields ());
687
+ Scope orderScope = new Scope (outputScope .getRelationId (), new RelationFields (allFields ));
688
+ analyzeState .setOrderScope (orderScope );
689
+ return orderScope ;
690
+ }
668
691
669
- List <Field > allFields = new ArrayList <>();
670
692
for (int i = 0 ; i < analyzeState .getOutputExprInOrderByScope ().size (); ++i ) {
671
693
Field field = outputScope .getRelationFields ()
672
694
.getFieldByIndex (analyzeState .getOutputExprInOrderByScope ().get (i ));
673
- if (field .getName () != null && field .getOriginExpression () != null &&
674
- allFields .stream ().anyMatch (f -> f .getOriginExpression () != null
675
- && f .getName () != null && field .getName ().equals (f .getName ())
676
- && field .getOriginExpression ().equals (f .getOriginExpression ()))) {
677
- continue ;
678
- }
679
695
allFields .add (field );
680
696
}
697
+ allFields = removeDuplicateField (allFields );
681
698
682
699
Scope orderScope = new Scope (outputScope .getRelationId (), new RelationFields (allFields ));
683
700
@@ -694,4 +711,29 @@ private Scope computeAndAssignOrderScope(AnalyzeState analyzeState, Scope source
694
711
private void analyzeExpression (Expr expr , AnalyzeState analyzeState , Scope scope ) {
695
712
ExpressionAnalyzer .analyzeExpression (expr , analyzeState , scope , session );
696
713
}
714
+
715
+
716
+ // The Scope used by order by allows parsing of the same column,
717
+ // such as 'select v1 as v, v1 as v from t0 order by v'
718
+ // but normal parsing does not allow it. So add a de-duplication operation here.
719
+ private List <Field > removeDuplicateField (List <Field > originalFields ) {
720
+ List <Field > allFields = Lists .newArrayList ();
721
+ for (Field field : originalFields ) {
722
+ if (session .getSessionVariable ().isEnableStrictOrderBy ()) {
723
+ if (field .getName () != null && field .getOriginExpression () != null &&
724
+ allFields .stream ().anyMatch (f -> f .getOriginExpression () != null
725
+ && f .getName () != null && field .getName ().equals (f .getName ())
726
+ && field .getOriginExpression ().equals (f .getOriginExpression ()))) {
727
+ continue ;
728
+ }
729
+ } else {
730
+ if (field .getName () != null &&
731
+ allFields .stream ().anyMatch (f -> f .getName () != null && field .getName ().equals (f .getName ()))) {
732
+ continue ;
733
+ }
734
+ }
735
+ allFields .add (field );
736
+ }
737
+ return allFields ;
738
+ }
697
739
}
0 commit comments