This is an automated email from the ASF dual-hosted git repository.

snuyanzin pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git

commit 27bed1c68b5fe6149a2cdc6b29634a671d10431e
Author: Sergey Nuyanzin <[email protected]>
AuthorDate: Thu Oct 8 23:06:52 2026 +0200

    [FLINK-40965][table] Treat `IF` as conditional
---
 .../table/planner/codegen/ExprCodeGenerator.scala  |  3 +++
 .../planner/runtime/stream/sql/FunctionITCase.java | 25 ++++++++++++++++++++++
 2 files changed, 28 insertions(+)

diff --git 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/ExprCodeGenerator.scala
 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/ExprCodeGenerator.scala
index 86ffa7b08e9..e6feb660c18 100644
--- 
a/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/ExprCodeGenerator.scala
+++ 
b/flink-table/flink-table-planner/src/main/scala/org/apache/flink/table/planner/codegen/ExprCodeGenerator.scala
@@ -565,10 +565,13 @@ class ExprCodeGenerator(
    *   - `CASE(when_1, then_1, when_2, then_2, ..., else)`: only `when_1` is 
unconditional.
    *   - `AND(a_0, a_1, ..., a_n)` / `OR(...)`: only `a_0` is unconditional; 
subsequent operands are
    *     short-circuited by the operator semantics and the codegen.
+   *   - `IF(cond, then, else)`: only `cond` is unconditional; `IfCallGen` 
guards `then`/`else`.
    */
   private def conditionalOperandIndices(call: RexCall): Set[Int] = 
call.getKind match {
     case SqlKind.CASE | SqlKind.AND | SqlKind.OR | SqlKind.COALESCE =>
       (1 until call.getOperands.size).toSet
+    case SqlKind.OTHER_FUNCTION if call.getOperator == IF =>
+      (1 until call.getOperands.size).toSet
     case _ => Set.empty
   }
 
diff --git 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/FunctionITCase.java
 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/FunctionITCase.java
index c9d31af6e97..01d2e8f5f06 100644
--- 
a/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/FunctionITCase.java
+++ 
b/flink-table/flink-table-planner/src/test/java/org/apache/flink/table/planner/runtime/stream/sql/FunctionITCase.java
@@ -1346,6 +1346,31 @@ public class FunctionITCase extends StreamingTestBase {
         assertThat(actual).containsExactly(Row.of(10), Row.of(30));
     }
 
+    /**
+     * Pins the IF guard interaction with the RexLocalRef cache. Without 
scoping the ELSE-branch
+     * {@code CAST(s AS DOUBLE)} is hoisted to the method top and throws on 
{@code s = ""}; with
+     * scoping it stays inside the branch and runs only when the guard is 
false.
+     */
+    @Test
+    void testCalcIfGuardShortCircuit() {
+        final List<Row> sourceData = List.of(Row.of(""), Row.of("1.5"));
+
+        TestCollectionTableFactory.reset();
+        TestCollectionTableFactory.initData(sourceData);
+        tEnv().executeSql("CREATE TABLE SourceTable (s STRING) WITH 
('connector' = 'COLLECTION')");
+
+        final List<Row> actual =
+                CollectionUtil.iteratorToList(
+                        tEnv().executeSql(
+                                        "SELECT IF(s IS NULL OR CHAR_LENGTH(s) 
= 0,"
+                                                + " CAST(NULL AS DOUBLE),"
+                                                + " CAST(s AS DOUBLE))"
+                                                + " FROM SourceTable")
+                                .collect());
+
+        assertThat(actual).containsExactly(Row.of((Object) null), 
Row.of(1.5d));
+    }
+
     @Test
     void testStructuredScalarFunction() throws Exception {
         final List<Row> sourceData =

Reply via email to