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
@@ -311,7 +313,8 @@ private List<Expr> analyzeSelect(SelectList selectList, Relation fromRelation, b
311
313
312
314
private List <OrderByElement > analyzeOrderBy (List <OrderByElement > orderByElements , AnalyzeState analyzeState ,
313
315
Scope orderByScope ,
314
- List <Expr > outputExpressions ) {
316
+ List <Expr > outputExpressions ,
317
+ boolean isDistinct ) {
315
318
if (orderByElements == null ) {
316
319
analyzeState .setOrderBy (Collections .emptyList ());
317
320
return Collections .emptyList ();
@@ -327,20 +330,33 @@ private List<OrderByElement> analyzeOrderBy(List<OrderByElement> orderByElements
327
330
if (ordinal < 1 || ordinal > outputExpressions .size ()) {
328
331
throw new SemanticException ("ORDER BY position %s is not in select list" , ordinal );
329
332
}
333
+ // index can ensure no ambiguous, we don't need to re-analyze this output expression
330
334
expression = outputExpressions .get ((int ) ordinal - 1 );
331
- }
332
-
333
- if (expression instanceof FieldReference ) {
334
- // If the expression of order by is a FieldReference, it means that the type of sql is
335
+ } else if (expression instanceof FieldReference ) {
336
+ // If the expression of order by is a FieldReference, and it's not a distinct select,
337
+ // it means that the type of sql is
335
338
// "select * from t order by 1", then this FieldReference cannot be parsed in OrderByScope,
336
339
// but should be parsed in sourceScope
337
- analyzeExpression (expression , analyzeState , orderByScope .getParent ());
340
+ if (isDistinct ) {
341
+ analyzeExpression (expression , analyzeState , orderByScope );
342
+ } else {
343
+ analyzeExpression (expression , analyzeState , orderByScope .getParent ());
344
+ }
338
345
} else {
339
346
ExpressionAnalyzer expressionAnalyzer = new ExpressionAnalyzer (session );
340
347
expressionAnalyzer .analyzeWithoutUpdateState (expression , analyzeState , orderByScope );
341
348
List <Expr > aggregations = Lists .newArrayList ();
342
349
expression .collectAll (e -> e .isAggregate (), aggregations );
343
- aggregations .forEach (e -> analyzeExpression (e , analyzeState , orderByScope .getParent ()));
350
+ if (isDistinct && !aggregations .isEmpty ()) {
351
+ throw new SemanticException ("for SELECT DISTINCT, ORDER BY expressions must appear in select list" ,
352
+ expression .getPos ());
353
+ }
354
+
355
+ if (!aggregations .isEmpty ()) {
356
+ // use parent scope to analyze agg func firstly
357
+ Preconditions .checkState (orderByScope .getParent () != null , "parent scope not be set" );
358
+ aggregations .forEach (e -> analyzeExpression (e , analyzeState , orderByScope .getParent ()));
359
+ }
344
360
analyzeExpression (expression , analyzeState , orderByScope );
345
361
}
346
362
@@ -655,23 +671,24 @@ public Expr visitSlot(SlotRef slotRef, Void context) {
655
671
}
656
672
}
657
673
658
- private Scope computeAndAssignOrderScope (AnalyzeState analyzeState , Scope sourceScope , Scope outputScope ) {
659
- // The Scope used by order by allows parsing of the same column,
660
- // such as 'select v1 as v, v1 as v from t0 order by v'
661
- // but normal parsing does not allow it. So add a de-duplication operation here.
674
+ private Scope computeAndAssignOrderScope (AnalyzeState analyzeState , Scope sourceScope , Scope outputScope ,
675
+ boolean isDistinct ) {
676
+
677
+ List <Field > allFields = Lists .newArrayList ();
678
+ // order by can only "see" fields from distinct output
679
+ if (isDistinct ) {
680
+ allFields = removeDuplicateField (outputScope .getRelationFields ().getAllFields ());
681
+ Scope orderScope = new Scope (outputScope .getRelationId (), new RelationFields (allFields ));
682
+ analyzeState .setOrderScope (orderScope );
683
+ return orderScope ;
684
+ }
662
685
663
- List <Field > allFields = new ArrayList <>();
664
686
for (int i = 0 ; i < analyzeState .getOutputExprInOrderByScope ().size (); ++i ) {
665
687
Field field = outputScope .getRelationFields ()
666
688
.getFieldByIndex (analyzeState .getOutputExprInOrderByScope ().get (i ));
667
- if (field .getName () != null && field .getOriginExpression () != null &&
668
- allFields .stream ().anyMatch (f -> f .getOriginExpression () != null
669
- && f .getName () != null && field .getName ().equals (f .getName ())
670
- && field .getOriginExpression ().equals (f .getOriginExpression ()))) {
671
- continue ;
672
- }
673
689
allFields .add (field );
674
690
}
691
+ allFields = removeDuplicateField (allFields );
675
692
676
693
Scope orderScope = new Scope (outputScope .getRelationId (), new RelationFields (allFields ));
677
694
@@ -688,4 +705,29 @@ private Scope computeAndAssignOrderScope(AnalyzeState analyzeState, Scope source
688
705
private void analyzeExpression (Expr expr , AnalyzeState analyzeState , Scope scope ) {
689
706
ExpressionAnalyzer .analyzeExpression (expr , analyzeState , scope , session );
690
707
}
708
+
709
+
710
+ // The Scope used by order by allows parsing of the same column,
711
+ // such as 'select v1 as v, v1 as v from t0 order by v'
712
+ // but normal parsing does not allow it. So add a de-duplication operation here.
713
+ private List <Field > removeDuplicateField (List <Field > originalFields ) {
714
+ List <Field > allFields = Lists .newArrayList ();
715
+ for (Field field : originalFields ) {
716
+ if (session .getSessionVariable ().isEnableStrictOrderBy ()) {
717
+ if (field .getName () != null && field .getOriginExpression () != null &&
718
+ allFields .stream ().anyMatch (f -> f .getOriginExpression () != null
719
+ && f .getName () != null && field .getName ().equals (f .getName ())
720
+ && field .getOriginExpression ().equals (f .getOriginExpression ()))) {
721
+ continue ;
722
+ }
723
+ } else {
724
+ if (field .getName () != null &&
725
+ allFields .stream ().anyMatch (f -> f .getName () != null && field .getName ().equals (f .getName ()))) {
726
+ continue ;
727
+ }
728
+ }
729
+ allFields .add (field );
730
+ }
731
+ return allFields ;
732
+ }
691
733
}
0 commit comments