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