217217import java .util .TreeSet ;
218218import java .util .function .BiConsumer ;
219219import java .util .function .BiFunction ;
220+ import java .util .function .Consumer ;
220221import java .util .function .Supplier ;
221222import java .util .function .UnaryOperator ;
222223import java .util .stream .Collectors ;
@@ -3261,12 +3262,12 @@ protected RelNode createAsofJoin(
32613262 return node ;
32623263 }
32633264
3264- private @ Nullable CorrelationUse getCorrelationUse ( Blackboard bb , final RelNode r0 ) {
3265- final Set < CorrelationId > correlatedVariables =
3266- RelOptUtil . getVariablesUsed ( r0 );
3267- if ( correlatedVariables . isEmpty ()) {
3268- return null ;
3269- }
3265+ /** Common utility for resolving correlation names, required columns and field mappings used by
3266+ * both {@link #massageExpressionsForCorrelation} and {@link #getCorrelationUse}.
3267+ * returns null if no correlation names are detected for this scope.
3268+ */
3269+ private @ Nullable ResolvedCorrelationInfo getCorrelationInfo ( Blackboard bb ,
3270+ final Set < CorrelationId > correlatedVariables ) {
32703271 final ImmutableBitSet .Builder requiredColumns = ImmutableBitSet .builder ();
32713272 final List <CorrelationId > correlNames = new ArrayList <>();
32723273 // Mapping from (correlId, originalFieldIndex) to projectedFieldIndex for aggregation
@@ -3350,29 +3351,92 @@ protected RelNode createAsofJoin(
33503351 if (correlNames .isEmpty ()) {
33513352 // None of the correlating variables originated in this scope.
33523353 return null ;
3354+ } else {
3355+ return new ResolvedCorrelationInfo (correlNames , requiredColumns .build (), fieldMapping );
3356+ }
3357+ }
3358+
3359+ private @ Nullable CorrelationUse getCorrelationUse (Blackboard bb , final RelNode r0 ) {
3360+ final Set <CorrelationId > correlatedVariables =
3361+ RelOptUtil .getVariablesUsed (r0 );
3362+ if (correlatedVariables .isEmpty ()) {
3363+ return null ;
3364+ }
3365+
3366+ ResolvedCorrelationInfo correlationInfo = getCorrelationInfo (bb , correlatedVariables );
3367+ if (correlationInfo == null ) {
3368+ // None of the correlating variables originated in this scope.
3369+ return null ;
33533370 }
33543371
33553372 RelNode r = r0 ;
3356- if (correlNames .size () > 1 ) {
3373+ if (correlationInfo . correlNames .size () > 1 ) {
33573374 // The same table was referenced more than once.
33583375 // So we deduplicate.
33593376 r =
3360- DeduplicateCorrelateVariables .go (rexBuilder , correlNames .get (0 ),
3361- Util .skip (correlNames ), r0 );
3377+ DeduplicateCorrelateVariables .go (rexBuilder , correlationInfo . correlNames .get (0 ),
3378+ Util .skip (correlationInfo . correlNames ), r0 );
33623379 // Add new node to leaves.
33633380 leaves .put (r , r .getRowType ().getFieldCount ());
33643381 }
33653382
33663383 // If there are field mappings (due to aggregation), rewrite the RelNode tree
33673384 // to update correlation variable row type and field indices
3368- if (!fieldMapping .isEmpty ()) {
3385+ if (!correlationInfo . fieldMapping .isEmpty ()) {
33693386 r =
33703387 r .accept (
3371- new CorrelationFieldMappingShuttle (rexBuilder , correlNames .get (0 ),
3372- bb .root ().getRowType (), fieldMapping ));
3388+ new CorrelationFieldMappingShuttle (rexBuilder , correlationInfo . correlNames .get (0 ),
3389+ bb .root ().getRowType (), correlationInfo . fieldMapping ));
33733390 }
33743391
3375- return new CorrelationUse (correlNames .get (0 ), requiredColumns .build (), r );
3392+ return new CorrelationUse (correlationInfo .correlNames .get (0 ), correlationInfo .requiredColumns ,
3393+ r );
3394+ }
3395+
3396+ /** Before building a RelNode tree with the provided expressions, detect and resolve correlation
3397+ * names, rewrite expressions, and return a callback to be called after the tree is built.
3398+ * Returns null if no correlation is detected.
3399+ */
3400+ private @ Nullable MassagedCorrelationExpressions massageExpressionsForCorrelation (Blackboard bb ,
3401+ final List <RexNode > exprs ) {
3402+ Set <CorrelationId > correlatedVariables = new HashSet <>();
3403+ for (RexNode e : exprs ) {
3404+ correlatedVariables .addAll (RelOptUtil .getVariablesUsed (e ));
3405+ }
3406+ if (correlatedVariables .isEmpty ()) {
3407+ return null ;
3408+ }
3409+
3410+ ResolvedCorrelationInfo correlationInfo = getCorrelationInfo (bb , correlatedVariables );
3411+ if (correlationInfo == null ) {
3412+ // None of the correlating variables originated in this scope.
3413+ return null ;
3414+ }
3415+
3416+ List <RexNode > newExprs = new ArrayList <>(exprs );
3417+ Consumer <RelNode > callback = (RelNode r ) -> { };
3418+ if (correlationInfo .correlNames .size () > 1 ) {
3419+ // The same table was referenced more than once.
3420+ // So we deduplicate.
3421+ CorrelationId canonicalId = correlationInfo .correlNames .get (0 );
3422+ List <CorrelationId > tail = Util .skip (correlationInfo .correlNames );
3423+ newExprs .replaceAll (e ->
3424+ DeduplicateCorrelateVariables .go (rexBuilder , canonicalId , tail , e ));
3425+ // Add new node to leaves.
3426+ callback = r -> leaves .put (r , r .getRowType ().getFieldCount ());
3427+ }
3428+
3429+ // If there are field mappings (due to aggregation), rewrite the RelNode tree
3430+ // to update correlation variable row type and field indices
3431+ if (!correlationInfo .fieldMapping .isEmpty ()) {
3432+ CorrelationFieldMappingRexShuttle shuttle =
3433+ new CorrelationFieldMappingRexShuttle (rexBuilder , correlationInfo .correlNames .get (0 ),
3434+ bb .root ().getRowType (), correlationInfo .fieldMapping );
3435+ newExprs .replaceAll (e -> e .accept (shuttle ));
3436+ }
3437+
3438+ return new MassagedCorrelationExpressions (newExprs , correlationInfo .correlNames .get (0 ),
3439+ callback );
33763440 }
33773441
33783442 /**
@@ -3773,26 +3837,22 @@ private void createAggImpl(Blackboard bb,
37733837
37743838 final RelNode inputRel = bb .root ();
37753839
3776- // Project the expressions required by agg and having.
3777- RelNode intermediateProject = relBuilder .push (inputRel )
3778- .projectNamed (preExprs .leftList (), preExprs .rightList (), false )
3779- .build ();
3780- final RelNode r2 ;
3781- // deal with correlation
3782- final CorrelationUse p = getCorrelationUse (bb , intermediateProject );
3783- if (p != null ) {
3784- assert p .r instanceof Project ;
3785- // correlation variables have been normalized in p.r, we should use expressions
3786- // in p.r instead of the original exprs
3787- Project project1 = (Project ) p .r ;
3788- r2 = relBuilder .push (bb .root ())
3789- .projectNamed (project1 .getProjects (), project1 .getRowType ().getFieldNames (),
3790- true , ImmutableSet .of (p .id ))
3791- .build ();
3840+ final @ Nullable MassagedCorrelationExpressions massagedResult =
3841+ massageExpressionsForCorrelation (bb , preExprs .leftList ());
3842+ relBuilder .push (inputRel );
3843+ if (massagedResult == null ) {
3844+ relBuilder .projectNamed (preExprs .leftList (), preExprs .rightList (), false );
37923845 } else {
3793- r2 = intermediateProject ;
3846+ relBuilder .projectNamed (massagedResult .exprs , preExprs .rightList (), false ,
3847+ ImmutableSet .of (massagedResult .correlId ));
3848+ }
3849+
3850+ RelNode project = relBuilder .build ();
3851+ if (massagedResult != null ) {
3852+ massagedResult .callback .accept (project );
37943853 }
3795- bb .setRoot (r2 , false );
3854+
3855+ bb .setRoot (project , false );
37963856 bb .mapRootRelToFieldProjection .put (bb .root (), r .groupExprProjection );
37973857
37983858 // REVIEW jvs 31-Oct-2007: doesn't the declaration of
@@ -5006,27 +5066,22 @@ private void convertNonAggregateSelectList(
50065066 SqlValidatorUtil .uniquify (fieldNames ,
50075067 catalogReader .nameMatcher ().isCaseSensitive ());
50085068
5009- relBuilder .push (bb .root ())
5010- .projectNamed (exprs , uniqueFieldNames , true );
5069+ final @ Nullable MassagedCorrelationExpressions massagedResult =
5070+ massageExpressionsForCorrelation (bb , exprs );
5071+ relBuilder .push (bb .root ());
5072+ if (massagedResult == null ) {
5073+ relBuilder .projectNamed (exprs , uniqueFieldNames , true );
5074+ } else {
5075+ relBuilder .projectNamed (massagedResult .exprs , uniqueFieldNames , true ,
5076+ ImmutableSet .of (massagedResult .correlId ));
5077+ }
50115078
50125079 RelNode project = relBuilder .build ();
5013-
5014- final RelNode r ;
5015- final CorrelationUse p = getCorrelationUse (bb , project );
5016- if (p != null ) {
5017- assert p .r instanceof Project ;
5018- // correlation variables have been normalized in p.r, we should use expressions
5019- // in p.r instead of the original exprs
5020- Project project1 = (Project ) p .r ;
5021- r = relBuilder .push (bb .root ())
5022- .projectNamed (project1 .getProjects (), uniqueFieldNames , true ,
5023- ImmutableSet .of (p .id ))
5024- .build ();
5025- } else {
5026- r = project ;
5080+ if (massagedResult != null ) {
5081+ massagedResult .callback .accept (project );
50275082 }
50285083
5029- bb .setRoot (r , false );
5084+ bb .setRoot (project , false );
50305085
50315086 assert bb .columnMonotonicities .isEmpty ();
50325087 bb .columnMonotonicities .addAll (columnMonotonicityList );
@@ -6179,13 +6234,13 @@ RexFieldAccess getFieldAccess(CorrelationId name) {
61796234 * Shuttle that rewrites correlation field accesses to use projected field indices
61806235 * when correlation references aggregated relations.
61816236 */
6182- private static class CorrelationFieldMappingShuttle extends RelHomogeneousShuttle {
6237+ private static class CorrelationFieldMappingRexShuttle extends RexShuttle {
61836238 private final RexBuilder rexBuilder ;
61846239 private final CorrelationId targetCorrelId ;
61856240 private final RelDataType newCorrelRowType ;
61866241 private final Map <Pair <CorrelationId , Integer >, Integer > fieldMapping ;
61876242
6188- CorrelationFieldMappingShuttle (RexBuilder rexBuilder ,
6243+ CorrelationFieldMappingRexShuttle (RexBuilder rexBuilder ,
61896244 CorrelationId targetCorrelId ,
61906245 RelDataType newCorrelRowType ,
61916246 Map <Pair <CorrelationId , Integer >, Integer > fieldMapping ) {
@@ -6195,23 +6250,40 @@ private static class CorrelationFieldMappingShuttle extends RelHomogeneousShuttl
61956250 this .fieldMapping = fieldMapping ;
61966251 }
61976252
6198- @ Override public RelNode visit (RelNode other ) {
6199- return super .visit (other ).accept (new RexShuttle () {
6200- @ Override public RexNode visitFieldAccess (RexFieldAccess fieldAccess ) {
6201- if (fieldAccess .getReferenceExpr () instanceof RexCorrelVariable ) {
6202- RexCorrelVariable correlVar = (RexCorrelVariable ) fieldAccess .getReferenceExpr ();
6203- if (correlVar .id .equals (targetCorrelId )) {
6204- Integer newIndex =
6205- fieldMapping .get (Pair .of (correlVar .id , fieldAccess .getField ().getIndex ()));
6206- if (newIndex != null ) {
6207- return rexBuilder .makeFieldAccess (
6208- rexBuilder .makeCorrel (newCorrelRowType , correlVar .id ), newIndex );
6209- }
6210- }
6253+ @ Override public RexNode visitFieldAccess (RexFieldAccess fieldAccess ) {
6254+ if (fieldAccess .getReferenceExpr () instanceof RexCorrelVariable ) {
6255+ RexCorrelVariable correlVar = (RexCorrelVariable ) fieldAccess .getReferenceExpr ();
6256+ if (correlVar .id .equals (targetCorrelId )) {
6257+ Integer newIndex =
6258+ fieldMapping .get (Pair .of (correlVar .id , fieldAccess .getField ().getIndex ()));
6259+ if (newIndex != null ) {
6260+ return rexBuilder .makeFieldAccess (
6261+ rexBuilder .makeCorrel (newCorrelRowType , correlVar .id ), newIndex );
62116262 }
6212- return super .visitFieldAccess (fieldAccess );
62136263 }
6214- });
6264+ }
6265+ return super .visitFieldAccess (fieldAccess );
6266+ }
6267+ }
6268+
6269+ /**
6270+ * Shuttle that rewrites correlation field accesses to use projected field indices
6271+ * when correlation references aggregated relations.
6272+ */
6273+ private static class CorrelationFieldMappingShuttle extends RelHomogeneousShuttle {
6274+ private final CorrelationFieldMappingRexShuttle rexShuttle ;
6275+
6276+ CorrelationFieldMappingShuttle (RexBuilder rexBuilder ,
6277+ CorrelationId targetCorrelId ,
6278+ RelDataType newCorrelRowType ,
6279+ Map <Pair <CorrelationId , Integer >, Integer > fieldMapping ) {
6280+ this .rexShuttle =
6281+ new CorrelationFieldMappingRexShuttle (rexBuilder , targetCorrelId , newCorrelRowType ,
6282+ fieldMapping );
6283+ }
6284+
6285+ @ Override public RelNode visit (RelNode other ) {
6286+ return super .visit (other ).accept (this .rexShuttle );
62156287 }
62166288 }
62176289
@@ -6581,6 +6653,37 @@ private static class CorrelationUse {
65816653 }
65826654 }
65836655
6656+ /** Wrapper around information collected by {@link #getCorrelationInfo}. */
6657+ private static class ResolvedCorrelationInfo {
6658+ public final List <CorrelationId > correlNames ;
6659+ public final ImmutableBitSet requiredColumns ;
6660+ public final Map <Pair <CorrelationId , Integer >, Integer > fieldMapping ;
6661+
6662+ ResolvedCorrelationInfo (List <CorrelationId > correlNames , ImmutableBitSet requiredColumns ,
6663+ Map <Pair <CorrelationId , Integer >, Integer > fieldMapping ) {
6664+ this .correlNames = correlNames ;
6665+ this .requiredColumns = requiredColumns ;
6666+ this .fieldMapping = fieldMapping ;
6667+ }
6668+ }
6669+
6670+ /** Wrapper around optionally returned results from {@link #massageExpressionsForCorrelation}. */
6671+ private static class MassagedCorrelationExpressions {
6672+ /** Modified expressions. */
6673+ public final List <RexNode > exprs ;
6674+ /** CorrelationId to use when building the relational expression. */
6675+ public final CorrelationId correlId ;
6676+ /** Callback to be called after the RelNode tree which uses these expressions is built. */
6677+ public final Consumer <RelNode > callback ;
6678+
6679+ MassagedCorrelationExpressions (List <RexNode > exprs , CorrelationId correlId ,
6680+ Consumer <RelNode > callback ) {
6681+ this .exprs = exprs ;
6682+ this .correlId = correlId ;
6683+ this .callback = callback ;
6684+ }
6685+ }
6686+
65846687 /** Returns a default {@link Config}. */
65856688 public static Config config () {
65866689 return CONFIG ;
0 commit comments