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,32 @@ 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
+ }
358
+
359
+ if (!aggregations .isEmpty ()) {
360
+ // use parent scope to analyze agg func firstly
361
+ Preconditions .checkState (orderByScope .getParent () != null , "parent scope not be set" );
362
+ aggregations .forEach (e -> analyzeExpression (e , analyzeState , orderByScope .getParent ()));
363
+ }
349
364
analyzeExpression (expression , analyzeState , orderByScope );
350
365
}
351
366
@@ -659,23 +674,24 @@ public Expr visitSlot(SlotRef slotRef, Void context) {
659
674
}
660
675
}
661
676
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.
677
+ private Scope computeAndAssignOrderScope (AnalyzeState analyzeState , Scope sourceScope , Scope outputScope ,
678
+ boolean isDistinct ) {
679
+
680
+ List <Field > allFields = Lists .newArrayList ();
681
+ // order by can only "see" fields from distinct output
682
+ if (isDistinct ) {
683
+ allFields = removeDuplicateField (outputScope .getRelationFields ().getAllFields ());
684
+ Scope orderScope = new Scope (outputScope .getRelationId (), new RelationFields (allFields ));
685
+ analyzeState .setOrderScope (orderScope );
686
+ return orderScope ;
687
+ }
666
688
667
- List <Field > allFields = new ArrayList <>();
668
689
for (int i = 0 ; i < analyzeState .getOutputExprInOrderByScope ().size (); ++i ) {
669
690
Field field = outputScope .getRelationFields ()
670
691
.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
692
allFields .add (field );
678
693
}
694
+ allFields = removeDuplicateField (allFields );
679
695
680
696
Scope orderScope = new Scope (outputScope .getRelationId (), new RelationFields (allFields ));
681
697
@@ -692,4 +708,29 @@ private Scope computeAndAssignOrderScope(AnalyzeState analyzeState, Scope source
692
708
private void analyzeExpression (Expr expr , AnalyzeState analyzeState , Scope scope ) {
693
709
ExpressionAnalyzer .analyzeExpression (expr , analyzeState , scope , session );
694
710
}
711
+
712
+
713
+ // The Scope used by order by allows parsing of the same column,
714
+ // such as 'select v1 as v, v1 as v from t0 order by v'
715
+ // but normal parsing does not allow it. So add a de-duplication operation here.
716
+ private List <Field > removeDuplicateField (List <Field > originalFields ) {
717
+ List <Field > allFields = Lists .newArrayList ();
718
+ for (Field field : originalFields ) {
719
+ if (session .getSessionVariable ().isEnableStrictOrderBy ()) {
720
+ if (field .getName () != null && field .getOriginExpression () != null &&
721
+ allFields .stream ().anyMatch (f -> f .getOriginExpression () != null
722
+ && f .getName () != null && field .getName ().equals (f .getName ())
723
+ && field .getOriginExpression ().equals (f .getOriginExpression ()))) {
724
+ continue ;
725
+ }
726
+ } else {
727
+ if (field .getName () != null &&
728
+ allFields .stream ().anyMatch (f -> f .getName () != null && field .getName ().equals (f .getName ()))) {
729
+ continue ;
730
+ }
731
+ }
732
+ allFields .add (field );
733
+ }
734
+ return allFields ;
735
+ }
695
736
}
0 commit comments