@@ -177,16 +177,20 @@ public static QueryExpression analyzeExpression(
177177 List <String > schema ,
178178 Map <String , ExprType > fieldTypes ,
179179 RelDataType rowType ,
180- RelOptCluster cluster ) throws ExpressionNotAnalyzableException {
180+ RelOptCluster cluster )
181+ throws ExpressionNotAnalyzableException {
181182 requireNonNull (expression , "expression" );
182183 try {
183184 // visits expression tree
184185 QueryExpression queryExpression =
185186 (QueryExpression ) expression .accept (new Visitor (schema , fieldTypes , rowType , cluster ));
186187 return queryExpression ;
187188 } catch (Throwable e ) {
188- Throwables .throwIfInstanceOf (e , UnsupportedOperationException .class );
189- throw new ExpressionNotAnalyzableException ("Can't convert " + expression , e );
189+ if (e instanceof UnsupportedScriptException ) {
190+ Throwables .throwIfInstanceOf (e , UnsupportedOperationException .class );
191+ throw new ExpressionNotAnalyzableException ("Can't convert " + expression , e );
192+ }
193+ return new ScriptQueryExpression (expression , rowType , fieldTypes , cluster );
190194 }
191195 }
192196
@@ -604,9 +608,6 @@ private static QueryExpression constructQueryExpressionForSearch(
604608 }
605609
606610 private QueryExpression andOr (RexCall call ) {
607- QueryExpression [] expressions = new QueryExpression [call .getOperands ().size ()];
608- boolean partial = false ;
609- int failedCount = 0 ;
610611 // For function isEmpty and isBlank, we implement them via expression `isNull or {@function}`,
611612 // Unlike `OR` in Java, `SHOULD` in DSL will evaluate both branches and lead to NPE.
612613 if (call .getKind () == SqlKind .OR
@@ -616,40 +617,41 @@ private QueryExpression andOr(RexCall call) {
616617 throw new UnsupportedScriptException (
617618 "DSL will evaluate both branches of OR with isNUll, prevent push-down to avoid NPE" );
618619 }
620+
621+ QueryExpression [] expressions = new QueryExpression [call .getOperands ().size ()];
622+ PredicateAnalyzerException firstError = null ;
623+ boolean partial = false ;
624+ int failedCount = 0 ;
619625 for (int i = 0 ; i < call .getOperands ().size (); i ++) {
626+ RexNode operand = call .getOperands ().get (i );
620627 try {
621- Expression expr = call .getOperands ().get (i ).accept (this );
622- if (expr instanceof NamedFieldExpression ) {
623- // nop currently
624- } else {
625- expressions [i ] = (QueryExpression ) call .getOperands ().get (i ).accept (this );
626- // Update or simplify the analyzed node list if it is not partial.
627- if (!expressions [i ].isPartial ())
628- expressions [i ].updateAnalyzedNodes (call .getOperands ().get (i ));
628+ Expression expr = tryAnalyzeOperand (operand );
629+ if (expr instanceof QueryExpression ) {
630+ expressions [i ] = (QueryExpression ) expr ;
631+ partial |= expressions [i ].isPartial ();
629632 }
630- partial |= expressions [i ].isPartial ();
631633 } catch (PredicateAnalyzerException e ) {
632- try {
633- expressions [i ] =
634- new ScriptQueryExpression (call .getOperands ().get (i ), rowType , fieldTypes , cluster );
635- if (!expressions [i ].isPartial ())
636- expressions [i ].updateAnalyzedNodes (call .getOperands ().get (i ));
637- } catch (UnsupportedScriptException ex ) {
638- if (call .getKind () == SqlKind .OR ) throw ex ;
639- partial = true ;
634+ if (firstError == null ) {
635+ firstError = e ;
640636 }
641- } catch (UnsupportedScriptException e ) {
642- if (call .getKind () == SqlKind .OR ) throw e ;
643637 partial = true ;
644638 ++failedCount ;
645639 // If we cannot analyze the operand, wrap the RexNode with UnAnalyzableQueryExpression and
646640 // record them in the array. We will reuse them later.
647- expressions [i ] = new UnAnalyzableQueryExpression (call . getOperands (). get ( i ) );
641+ expressions [i ] = new UnAnalyzableQueryExpression (operand );
648642 }
649643 }
650644
651645 switch (call .getKind ()) {
652646 case OR :
647+ if (partial ) {
648+ if (firstError != null ) {
649+ throw firstError ;
650+ } else {
651+ final String message = format (Locale .ROOT , "Unable to handle call: [%s]" , call );
652+ throw new PredicateAnalyzerException (message );
653+ }
654+ }
653655 return CompoundQueryExpression .or (expressions );
654656 case AND :
655657 if (failedCount == call .getOperands ().size ()) {
@@ -664,6 +666,30 @@ private QueryExpression andOr(RexCall call) {
664666 }
665667 }
666668
669+ private Expression tryAnalyzeOperand (RexNode node ) {
670+ try {
671+ Expression expr = node .accept (this );
672+ if (expr instanceof NamedFieldExpression ) {
673+ return expr ;
674+ }
675+ QueryExpression qe = (QueryExpression ) expr ;
676+ if (!qe .isPartial ()) {
677+ qe .updateAnalyzedNodes (node );
678+ }
679+ return qe ;
680+ } catch (PredicateAnalyzerException firstFailed ) {
681+ try {
682+ QueryExpression qe = new ScriptQueryExpression (node , rowType , fieldTypes , cluster );
683+ if (!qe .isPartial ()) {
684+ qe .updateAnalyzedNodes (node );
685+ }
686+ return qe ;
687+ } catch (UnsupportedScriptException secondFailed ) {
688+ throw new PredicateAnalyzerException (secondFailed );
689+ }
690+ }
691+ }
692+
667693 /**
668694 * Holder class for a pair of expressions. Used to convert {@code 1 = foo} into {@code foo = 1}
669695 */
@@ -1297,6 +1323,7 @@ private static String timestampValueForPushDown(String value) {
12971323
12981324 public static class ScriptQueryExpression extends QueryExpression {
12991325 private final String code ;
1326+ private RexNode analyzedNode ;
13001327
13011328 public ScriptQueryExpression (
13021329 RexNode rexNode ,
@@ -1331,12 +1358,12 @@ public QueryBuilder builder() {
13311358
13321359 @ Override
13331360 public List <RexNode > getAnalyzedNodes () {
1334- return List .of ();
1361+ return List .of (analyzedNode );
13351362 }
13361363
13371364 @ Override
13381365 public void updateAnalyzedNodes (RexNode rexNode ) {
1339-
1366+ this . analyzedNode = rexNode ;
13401367 }
13411368
13421369 @ Override
0 commit comments