2424package org .dbsp .sqlCompiler .compiler .frontend ;
2525
2626import org .apache .calcite .rex .*;
27+ import org .apache .calcite .sql .SqlKind ;
2728import org .dbsp .sqlCompiler .compiler .errors .InternalCompilerError ;
2829import org .dbsp .sqlCompiler .compiler .frontend .calciteObject .CalciteObject ;
2930import org .dbsp .sqlCompiler .ir .type .DBSPType ;
3536import java .util .List ;
3637import java .util .Objects ;
3738
38- public class JoinConditionAnalyzer extends RexVisitorImpl < Void > implements IWritesLogs {
39+ public class JoinConditionAnalyzer implements IWritesLogs {
3940 private final int leftTableColumnCount ;
40- private final ConditionDecomposition result ;
4141 private final TypeCompiler typeCompiler ;
4242
4343 public JoinConditionAnalyzer (CalciteObject object , int leftTableColumnCount , TypeCompiler typeCompiler ) {
44- super (true );
4544 this .leftTableColumnCount = leftTableColumnCount ;
46- this .result = new ConditionDecomposition (object );
4745 this .typeCompiler = typeCompiler ;
4846 }
4947
@@ -99,19 +97,80 @@ void validate() {
9997 throw new InternalCompilerError ("Unexpected empty join condition" , this .object );
10098 }
10199
102- /**
103- * Part of the join condition that is not an equality test.
104- * @return Null if the entire condition is an equality test.
105- */
100+ /** Part of the join condition that is not an equality test.
101+ * @return Null if the entire condition is an equality test. */
106102 @ Nullable
107103 public RexNode getLeftOver () {
108104 this .validate ();
109105 return this .leftOver ;
110106 }
111- }
112107
113- public boolean completed () {
114- return this .result .leftOver != null ;
108+ void analyzeAnd (RexCall call ) {
109+ List <RexNode > operands = call .getOperands ();
110+ List <RexNode > unprocessed = new ArrayList <>();
111+ for (int i = 0 ; i < operands .size (); i ++) {
112+ RexNode operand = call .operands .get (i );
113+ if (!(operand instanceof RexCall opCall )) {
114+ unprocessed .add (operand );
115+ continue ;
116+ }
117+
118+ if (opCall .op .kind != SqlKind .EQUALS &&
119+ opCall .op .kind != SqlKind .IS_NOT_DISTINCT_FROM ) {
120+ unprocessed .add (operand );
121+ continue ;
122+ }
123+
124+ boolean eq = this .analyzeEquals (opCall );
125+ if (!eq ) {
126+ unprocessed .add (opCall );
127+ }
128+ }
129+
130+ if (!unprocessed .isEmpty ()) {
131+ if (unprocessed .size () == 1 ) {
132+ this .setLeftOver (unprocessed .get (0 ));
133+ } else {
134+ call = call .clone (call .type , unprocessed );
135+ this .setLeftOver (call );
136+ }
137+ }
138+ }
139+
140+ /** Analyze an equality comparison. Return 'true' if this is suitable for an equijoin */
141+ public boolean analyzeEquals (RexCall call ) {
142+ assert call .operands .size () == 2 : "Expected 2 operands for equality checking" ;
143+ RexNode left = call .operands .get (0 );
144+ RexNode right = call .operands .get (1 );
145+ @ Nullable
146+ Boolean leftIsLeft = JoinConditionAnalyzer .this .isLeftTableColumnReference (left );
147+ @ Nullable
148+ Boolean rightIsLeft = JoinConditionAnalyzer .this .isLeftTableColumnReference (right );
149+ if (leftIsLeft == null || rightIsLeft == null ) {
150+ return false ;
151+ }
152+ if (leftIsLeft == rightIsLeft ) {
153+ // Both columns refer to the same table.
154+ return false ;
155+ }
156+ DBSPType leftType = JoinConditionAnalyzer .this .typeCompiler .convertType (
157+ left .getType (), true );
158+ DBSPType rightType = JoinConditionAnalyzer .this .typeCompiler .convertType (
159+ right .getType (), true );
160+ if (call .op .kind == SqlKind .IS_NOT_DISTINCT_FROM ) {
161+ // Only used if any of the operands is not nullable
162+ if (leftType .mayBeNull && rightType .mayBeNull ) {
163+ return false ;
164+ }
165+ }
166+ DBSPType commonType = ExpressionCompiler .reduceType (leftType , rightType ).setMayBeNull (false );
167+ if (leftIsLeft ) {
168+ this .addEquality (left , right , commonType );
169+ } else {
170+ this .addEquality (right , left , commonType );
171+ }
172+ return true ;
173+ }
115174 }
116175
117176 @ Nullable
@@ -134,79 +193,26 @@ public Boolean isLeftTableColumnReference(RexNode node) {
134193 return ref .getIndex () < this .leftTableColumnCount ;
135194 }
136195
137- @ Override
138- public Void visitInputRef (RexInputRef ref ) {
139- this .result .setLeftOver (ref );
140- return null ;
141- }
142-
143- @ Override
144- public Void visitLiteral (RexLiteral lit ) {
145- this .result .setLeftOver (lit );
146- return null ;
147- }
148-
149- @ Override
150- public Void visitCall (RexCall call ) {
151- switch (call .op .kind ) {
152- case AND :
153- List <RexNode > operands = call .getOperands ();
154- for (int i = 0 ; i < operands .size (); i ++) {
155- call .operands .get (i ).accept (this );
156- if (this .completed ()) {
157- if (i == operands .size () - 1 ) {
158- // Just one left
159- this .result .setLeftOver (operands .get (i ));
160- return null ;
161- }
162- List <RexNode > remaining = new ArrayList <>();
163- for (int j = i ; j < operands .size (); j ++)
164- remaining .add (call .operands .get (j ));
165- call = call .clone (call .type , remaining );
166- this .result .setLeftOver (call );
167- return null ;
168- }
169- }
170- return null ;
171- case EQUALS :
172- assert call .operands .size () == 2 : "Expected 2 operands for equality checking" ;
173- RexNode left = call .operands .get (0 );
174- RexNode right = call .operands .get (1 );
175- @ Nullable
176- Boolean leftIsLeft = this .isLeftTableColumnReference (left );
177- @ Nullable
178- Boolean rightIsLeft = this .isLeftTableColumnReference (right );
179- if (leftIsLeft == null || rightIsLeft == null ) {
180- this .result .setLeftOver (call );
181- return null ;
182- }
183- if (leftIsLeft == rightIsLeft ) {
184- // Both columns refer to the same table.
185- this .result .setLeftOver (call );
186- return null ;
187- }
188- DBSPType leftType = this .typeCompiler .convertType (left .getType (), true );
189- DBSPType rightType = this .typeCompiler .convertType (right .getType (), true );
190- DBSPType commonType = ExpressionCompiler .reduceType (leftType , rightType ).setMayBeNull (false );
191- if (leftIsLeft ) {
192- this .result .addEquality (left , right , commonType );
193- } else {
194- this .result .addEquality (right , left , commonType );
195- }
196- return null ;
197- default :
198- // We are done: we don't know how to handle this condition.
199- this .result .setLeftOver (call );
200- return null ;
201- }
202- }
203-
204196 JoinConditionAnalyzer .ConditionDecomposition analyze (RexNode expression ) {
205197 Logger .INSTANCE .belowLevel (this , 1 )
206198 .append ("Analyzing " )
207199 .append (expression .toString ())
208200 .newline ();
209- expression .accept (this );
210- return this .result ;
201+ final ConditionDecomposition result = new ConditionDecomposition (CalciteObject .create (expression ));
202+ if (! (expression instanceof RexCall call )) {
203+ result .setLeftOver (expression );
204+ return result ;
205+ }
206+ if (call .op .kind == SqlKind .AND ) {
207+ result .analyzeAnd (call );
208+ } else if (call .op .kind == SqlKind .EQUALS || call .op .kind == SqlKind .IS_NOT_DISTINCT_FROM ) {
209+ boolean success = result .analyzeEquals (call );
210+ if (!success ) {
211+ result .setLeftOver (call );
212+ }
213+ } else {
214+ result .setLeftOver (call );
215+ }
216+ return result ;
211217 }
212218}
0 commit comments