This is an automated email from the ASF dual-hosted git repository.
yuxiqian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink-cdc.git
The following commit(s) were added to refs/heads/master by this push:
new 78b4fef85 [FLINK-40337][runtime] Preserve operator precedence in
generated Janino expressions (#4499)
78b4fef85 is described below
commit 78b4fef854bcd6f7ba7581c2b67cda4958595f39
Author: haruki <[email protected]>
AuthorDate: Fri Aug 7 17:32:36 2026 +0800
[FLINK-40337][runtime] Preserve operator precedence in generated Janino
expressions (#4499)
---
.../flink/cdc/runtime/parser/JaninoCompiler.java | 8 ++-
.../cdc/runtime/parser/JaninoCompilerTest.java | 77 ++++++++++++++++++++++
.../cdc/runtime/parser/TransformParserTest.java | 32 ++++++++-
3 files changed, 115 insertions(+), 2 deletions(-)
diff --git
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/JaninoCompiler.java
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/JaninoCompiler.java
index 909c628fd..539a2cd55 100644
---
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/JaninoCompiler.java
+++
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/JaninoCompiler.java
@@ -46,7 +46,9 @@ import org.codehaus.commons.compiler.CompileException;
import org.codehaus.commons.compiler.Location;
import org.codehaus.janino.ExpressionEvaluator;
import org.codehaus.janino.Java;
+import org.codehaus.janino.Unparser;
+import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -131,7 +133,11 @@ public class JaninoCompiler {
public static String translateSqlNodeToJaninoExpression(Context context,
SqlNode transform) {
Java.Rvalue rvalue = translateSqlNodeToJaninoRvalue(context,
transform);
if (rvalue != null) {
- return rvalue.toString();
+ StringWriter writer = new StringWriter();
+ Unparser unparser = new Unparser(writer);
+ unparser.unparseAtom(rvalue);
+ unparser.close();
+ return writer.toString();
}
return "";
}
diff --git
a/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/parser/JaninoCompilerTest.java
b/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/parser/JaninoCompilerTest.java
index 8298d384a..adf0ee159 100644
---
a/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/parser/JaninoCompilerTest.java
+++
b/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/parser/JaninoCompilerTest.java
@@ -287,6 +287,83 @@ class JaninoCompilerTest {
Assertions.assertThat(nullableOrEvaluator.evaluate(new Object[]
{null})).isNull();
}
+ @Test
+ void testTranslatedNestedExpressionPreservesSemantics() throws
InvocationTargetException {
+ List<Column> arithmeticColumns =
+ List.of(
+ Column.physicalColumn("a", DataTypes.INT()),
+ Column.physicalColumn("b", DataTypes.INT()),
+ Column.physicalColumn("c", DataTypes.INT()));
+ Map<String, String> columnNameMap = Map.of("a", "$0", "b", "$1", "c",
"$2");
+ List<String> columnNames = List.of("$0", "$1", "$2");
+ List<Class<?>> columnTypes = List.of(Integer.class, Integer.class,
Integer.class);
+
+ ExpressionEvaluator additiveOperandEvaluator =
+ compileTranslatedFilterExpression(
+ "(a + b) * c = 9",
+ arithmeticColumns,
+ columnNameMap,
+ columnNames,
+ columnTypes);
+ Assertions.assertThat(additiveOperandEvaluator.evaluate(new Object[]
{1, 2, 3}))
+ .isEqualTo(true);
+
+ ExpressionEvaluator multiplicativeRightOperandEvaluator =
+ compileTranslatedFilterExpression(
+ "a / (b * c) = 2",
+ arithmeticColumns,
+ columnNameMap,
+ columnNames,
+ columnTypes);
+ Assertions.assertThat(multiplicativeRightOperandEvaluator.evaluate(new
Object[] {12, 2, 3}))
+ .isEqualTo(true);
+
+ ExpressionEvaluator subtractiveRightOperandEvaluator =
+ compileTranslatedFilterExpression(
+ "a - (b - c) = 6",
+ arithmeticColumns,
+ columnNameMap,
+ columnNames,
+ columnTypes);
+ Assertions.assertThat(subtractiveRightOperandEvaluator.evaluate(new
Object[] {10, 6, 2}))
+ .isEqualTo(true);
+
+ ExpressionEvaluator conditionalLeftOperandEvaluator =
+ compileTranslatedFilterExpression(
+ "IF(a > b, a, b) + c = 13",
+ arithmeticColumns,
+ columnNameMap,
+ columnNames,
+ columnTypes);
+ Assertions.assertThat(conditionalLeftOperandEvaluator.evaluate(new
Object[] {10, 2, 3}))
+ .isEqualTo(true);
+
+ ExpressionEvaluator conditionalRightOperandEvaluator =
+ compileTranslatedFilterExpression(
+ "c * IF(a > b, a, b) = 30",
+ arithmeticColumns,
+ columnNameMap,
+ columnNames,
+ columnTypes);
+ Assertions.assertThat(conditionalRightOperandEvaluator.evaluate(new
Object[] {10, 2, 3}))
+ .isEqualTo(true);
+
+ List<Column> booleanColumns =
+ List.of(
+ Column.physicalColumn("a",
DataTypes.BOOLEAN().notNull()),
+ Column.physicalColumn("b",
DataTypes.BOOLEAN().notNull()),
+ Column.physicalColumn("c",
DataTypes.BOOLEAN().notNull()));
+ ExpressionEvaluator logicalOperandEvaluator =
+ compileTranslatedFilterExpression(
+ "(a OR b) AND c",
+ booleanColumns,
+ columnNameMap,
+ columnNames,
+ List.of(Boolean.class, Boolean.class, Boolean.class));
+ Assertions.assertThat(logicalOperandEvaluator.evaluate(new Object[]
{true, false, false}))
+ .isEqualTo(false);
+ }
+
@Test
void testLargeNumericLiterals() {
// Test parsing integer literals
diff --git
a/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/parser/TransformParserTest.java
b/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/parser/TransformParserTest.java
index 519fe2669..281ea4721 100644
---
a/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/parser/TransformParserTest.java
+++
b/flink-cdc-runtime/src/test/java/org/apache/flink/cdc/runtime/parser/TransformParserTest.java
@@ -451,6 +451,36 @@ class TransformParserTest {
columns);
}
+ @Test
+ void testTranslateNestedExpressionPreservesOperatorPrecedence() {
+ List<Column> arithmeticColumns =
+ List.of(
+ Column.physicalColumn("a", DataTypes.INT()),
+ Column.physicalColumn("b", DataTypes.INT()),
+ Column.physicalColumn("c", DataTypes.INT()));
+
+ testFilterExpressionWithColumns("(a + b) * c", "((( a + b ))) * c",
arithmeticColumns);
+ testFilterExpressionWithColumns("a / (b * c)", "a / ((( b * c )))",
arithmeticColumns);
+ testFilterExpressionWithColumns("a - (b - c)", "a - ((( b - c )))",
arithmeticColumns);
+ testFilterExpressionWithColumns("a + b * c", "a + b * c",
arithmeticColumns);
+ testFilterExpressionWithColumns("(a - b) - c", "a - b - c",
arithmeticColumns);
+ testFilterExpressionWithColumns(
+ "IF(a > b, a, b) + c",
+ "((( isTrue(greaterThan(a, b)) ? a : b ))) + c",
+ arithmeticColumns);
+ testFilterExpressionWithColumns(
+ "c * IF(a > b, a, b)",
+ "c * ((( isTrue(greaterThan(a, b)) ? a : b )))",
+ arithmeticColumns);
+
+ List<Column> booleanColumns =
+ List.of(
+ Column.physicalColumn("a",
DataTypes.BOOLEAN().notNull()),
+ Column.physicalColumn("b",
DataTypes.BOOLEAN().notNull()),
+ Column.physicalColumn("c",
DataTypes.BOOLEAN().notNull()));
+ testFilterExpressionWithColumns("(a OR b) AND c", "((( a || b ))) &&
c", booleanColumns);
+ }
+
@Test
public void testTranslateItemAccessToJaninoExpression() {
// Test collection access functions (ARRAY, MAP) with proper column
schema
@@ -588,7 +618,7 @@ class TransformParserTest {
"ProjectionColumn{column=`newCreateTime` TIMESTAMP(3)
'newCreateTime', expression='createTime', scriptExpression='$0',
originalColumnNames=[createTime], columnNameMap={createTime=$0}}",
"ProjectionColumn{column=`newAddress` VARCHAR(50)
'newAddress', expression='address', scriptExpression='$0',
originalColumnNames=[address], columnNameMap={address=$0}}",
"ProjectionColumn{column=`deposits` DECIMAL(10, 2)
'deposit', expression='deposit', scriptExpression='$0',
originalColumnNames=[deposit], columnNameMap={deposit=$0}}",
- "ProjectionColumn{column=`bmi` DOUBLE,
expression='`TB`.`weight` / (`TB`.`height` * `TB`.`height`)',
scriptExpression='$0 / $1 * $1', originalColumnNames=[weight, height, height],
columnNameMap={weight=$0, height=$1}}");
+ "ProjectionColumn{column=`bmi` DOUBLE,
expression='`TB`.`weight` / (`TB`.`height` * `TB`.`height`)',
scriptExpression='$0 / ((( $1 * $1 )))', originalColumnNames=[weight, height,
height], columnNameMap={weight=$0, height=$1}}");
Assertions.assertThat(result).hasToString("[" + String.join(", ",
expected) + "]");
List<ProjectionColumn> regexpResult =