This is an automated email from the ASF dual-hosted git repository.
lvyanquan 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 4e3767a75 [FLINK-40360][runtime] Fix DECIMAL precision truncation when
arithmetic result precision exceeds 19 digits (#4503)
4e3767a75 is described below
commit 4e3767a75a7f0fd38b68941d061c7f12f1316ce1
Author: haruki <[email protected]>
AuthorDate: Tue Aug 18 21:03:14 2026 +0800
[FLINK-40360][runtime] Fix DECIMAL precision truncation when arithmetic
result precision exceeds 19 digits (#4503)
Co-authored-by: 春栖 <[email protected]>
---
docs/content.zh/docs/core-concept/data-pipeline.md | 1 +
docs/content/docs/core-concept/data-pipeline.md | 2 +-
.../cdc/common/converter/CommonConverter.java | 15 +-
.../common/converter/InternalObjectConverter.java | 2 +-
.../cdc/common/pipeline/DecimalPrecisionMode.java | 34 ++++
.../flink/cdc/common/pipeline/PipelineOptions.java | 19 ++
.../converter/InternalObjectConverterTest.java | 2 +-
.../cdc/composer/flink/FlinkPipelineComposer.java | 3 +
.../flink/translator/TransformTranslator.java | 3 +
.../flink/FlinkPipelineTransformITCase.java | 90 ++++++++--
.../cdc/composer/specs/TransformSpecsITCase.java | 11 +-
.../src/test/resources/specs/casting.yaml | 120 +++++++++++++
.../src/test/resources/specs/decimal.yaml | 196 ++++++++++++++++++---
.../operators/transform/PostTransformOperator.java | 14 +-
.../transform/PostTransformOperatorBuilder.java | 11 +-
.../transform/TransformFilterProcessor.java | 14 +-
.../transform/TransformProjectionProcessor.java | 7 +-
.../cdc/runtime/parser/FlinkCdcTypeSystem.java | 56 ++++++
.../flink/cdc/runtime/parser/JaninoCompiler.java | 47 ++++-
.../flink/cdc/runtime/parser/TransformParser.java | 97 ++++++++--
.../cdc/runtime/parser/TransformParserTest.java | 34 ++++
21 files changed, 706 insertions(+), 72 deletions(-)
diff --git a/docs/content.zh/docs/core-concept/data-pipeline.md
b/docs/content.zh/docs/core-concept/data-pipeline.md
index 928f6880c..bf4f3212e 100644
--- a/docs/content.zh/docs/core-concept/data-pipeline.md
+++ b/docs/content.zh/docs/core-concept/data-pipeline.md
@@ -124,5 +124,6 @@ under the License.
| `schema-operator.rpc-timeout` | SchemaOperator 等待下游 SchemaChangeEvent
应用完成的超时时间,默认值是 3 分钟。
[...]
| `operator.uid.prefix` | Pipeline 中算子 UID 的前缀。如果不设置,Flink 会为每个算子生成唯一的
UID。 建议设置这个参数以提供稳定和可识别的算子 ID,这有助于有状态升级、问题排查和在 Flink UI 上的诊断。
[...]
| `sink.partitioning.strategy` | Sink
写入数据时使用的分区策略。数据类型:String。默认值:`SINK_DEFINED`。备注:可配置的值如下:`SINK_DEFINED`:使用 Sink
定义的分区策略;`PRIMARY_KEY`:按表 ID 和主键分区;`TABLE_ID`:仅按表 ID 分区。
| optional |
+| `transform.decimal.precision.mode` | transform 表达式求值中 DECIMAL
类型的最大精度模式。可选值:`UP_TO_19`(默认,使用 Calcite 默认类型系统)或 `UP_TO_38`(允许 DECIMAL 精度最高为 38
位)。
| optional |
注意:虽然上述参数都是可选的,但至少需要指定其中一个。`pipeline` 部分是必需的,不能为空。
diff --git a/docs/content/docs/core-concept/data-pipeline.md
b/docs/content/docs/core-concept/data-pipeline.md
index 68f3ca212..4b35ad6ca 100644
--- a/docs/content/docs/core-concept/data-pipeline.md
+++ b/docs/content/docs/core-concept/data-pipeline.md
@@ -126,6 +126,6 @@ Note that whilst the parameters are each individually
optional, at least one of
| `schema-operator.rpc-timeout` | The timeout time for SchemaOperator to wait
downstream SchemaChangeEvent applying finished, the default value is 3 minutes.
[...]
| `operator.uid.prefix` | The prefix to use for all pipeline operator
UIDs. If not set, all pipeline operator UIDs will be generated by Flink. It is
recommended to set this parameter to ensure stable and recognizable operator
UIDs, which can help with stateful upgrades, troubleshooting, and Flink UI
diagnostics.
[...]
| `sink.partitioning.strategy` | The partitioning strategy used when writing
data to the sink. Data type: String. Default value: `SINK_DEFINED`. Available
values: `SINK_DEFINED`: uses the partitioning strategy defined by the sink;
`PRIMARY_KEY`: partitions by table ID and primary key; `TABLE_ID`: partitions
only by table ID.
[...]
+| `transform.decimal.precision.mode` | Maximum precision mode for DECIMAL type
in transform expression evaluation. One of: `UP_TO_19` (default, match
Calcite's default type system) or `UP_TO_38` (allow DECIMAL precision up to 38
digits).
[...]
NOTE: Whilst the above parameters are each individually optional, at least one
of them must be specified. The `pipeline` section is mandatory and cannot be
empty.
-
diff --git
a/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/converter/CommonConverter.java
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/converter/CommonConverter.java
index feb0e4459..8260c305d 100644
---
a/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/converter/CommonConverter.java
+++
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/converter/CommonConverter.java
@@ -33,6 +33,7 @@ import org.apache.flink.cdc.common.data.ZonedTimestampData;
import org.apache.flink.cdc.common.data.binary.BinaryStringData;
import org.apache.flink.cdc.common.types.ArrayType;
import org.apache.flink.cdc.common.types.DataType;
+import org.apache.flink.cdc.common.types.DecimalType;
import org.apache.flink.cdc.common.types.MapType;
import org.apache.flink.cdc.common.types.RowType;
import org.apache.flink.cdc.common.types.VariantType;
@@ -153,13 +154,21 @@ public class CommonConverter {
"Cannot convert " + obj + " of type " + obj.getClass() + " to
STRING DATA.");
}
- static DecimalData convertToDecimalData(Object obj) {
+ static DecimalData convertToDecimalData(Object obj, DecimalType
decimalType) {
if (obj instanceof DecimalData) {
- return (DecimalData) obj;
+ DecimalData dd = (DecimalData) obj;
+ // Re-convert to target precision and scale if different
+ if (dd.precision() == decimalType.getPrecision()
+ && dd.scale() == decimalType.getScale()) {
+ return dd;
+ }
+ return DecimalData.fromBigDecimal(
+ dd.toBigDecimal(), decimalType.getPrecision(),
decimalType.getScale());
}
if (obj instanceof BigDecimal) {
BigDecimal bd = (BigDecimal) obj;
- return DecimalData.fromBigDecimal(bd, bd.precision(), bd.scale());
+ return DecimalData.fromBigDecimal(
+ bd, decimalType.getPrecision(), decimalType.getScale());
}
throw new RuntimeException(
"Cannot convert " + obj + " of type " + obj.getClass() + " to
DECIMAL DATA.");
diff --git
a/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/converter/InternalObjectConverter.java
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/converter/InternalObjectConverter.java
index 7f1cc97bd..0dc664e54 100644
---
a/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/converter/InternalObjectConverter.java
+++
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/converter/InternalObjectConverter.java
@@ -87,7 +87,7 @@ public class InternalObjectConverter {
@Override
public Function<Object, DecimalData> visit(DecimalType decimalType) {
- return CommonConverter::convertToDecimalData;
+ return obj -> CommonConverter.convertToDecimalData(obj,
decimalType);
}
@Override
diff --git
a/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/pipeline/DecimalPrecisionMode.java
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/pipeline/DecimalPrecisionMode.java
new file mode 100644
index 000000000..79a733fb1
--- /dev/null
+++
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/pipeline/DecimalPrecisionMode.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.cdc.common.pipeline;
+
+import org.apache.flink.cdc.common.annotation.PublicEvolving;
+
+/**
+ * Maximum precision mode for DECIMAL type in transform expressions. Controls
the upper bound of
+ * numeric precision used by the SQL type system during expression evaluation.
+ */
+@PublicEvolving
+public enum DecimalPrecisionMode {
+
+ /** Limits DECIMAL precision to 19 digits, matching Calcite's default type
system behavior. */
+ UP_TO_19,
+
+ /** Allows DECIMAL precision up to 38 digits, matching Flink CDC's
extended type system. */
+ UP_TO_38
+}
diff --git
a/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/pipeline/PipelineOptions.java
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/pipeline/PipelineOptions.java
index 7c6e49b9e..d46c94c65 100644
---
a/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/pipeline/PipelineOptions.java
+++
b/flink-cdc-common/src/main/java/org/apache/flink/cdc/common/pipeline/PipelineOptions.java
@@ -156,5 +156,24 @@ public class PipelineOptions {
"TABLE_ID: Hash by
TableId only. All events from the same table will land on the same subtask,
ensuring per-table ordering semantics. This strategy is not supported for
paimon, fluss, or maxcompute sinks.")))
.build());
+ public static final ConfigOption<DecimalPrecisionMode>
+ PIPELINE_TRANSFORM_DECIMAL_PRECISION_MODE =
+ ConfigOptions.key("transform.decimal.precision.mode")
+ .enumType(DecimalPrecisionMode.class)
+ .defaultValue(DecimalPrecisionMode.UP_TO_19)
+ .withDescription(
+ Description.builder()
+ .text(
+ "Maximum precision mode
for DECIMAL type in transform expression evaluation. ")
+ .linebreak()
+ .add(
+ ListElement.list(
+ text(
+ "UP_TO_19:
Limits DECIMAL precision to 19 digits, matching Calcite's default type system. "
+ +
"This is the default behavior for all versions."),
+ text(
+ "UP_TO_38:
Allows DECIMAL precision up to 38 digits, matching Flink CDC's extended type
system.")))
+ .build());
+
private PipelineOptions() {}
}
diff --git
a/flink-cdc-common/src/test/java/org/apache/flink/cdc/common/converter/InternalObjectConverterTest.java
b/flink-cdc-common/src/test/java/org/apache/flink/cdc/common/converter/InternalObjectConverterTest.java
index 2a4c3e1b3..ee4ff5859 100644
---
a/flink-cdc-common/src/test/java/org/apache/flink/cdc/common/converter/InternalObjectConverterTest.java
+++
b/flink-cdc-common/src/test/java/org/apache/flink/cdc/common/converter/InternalObjectConverterTest.java
@@ -195,7 +195,7 @@ class InternalObjectConverterTest {
.hasToString("4.2");
assertThat(convertToInternal(new BigDecimal("-3.1415926"),
DataTypes.DECIMAL(20, 10)))
.isInstanceOf(DecimalData.class)
- .hasToString("-3.1415926");
+ .hasToString("-3.1415926000");
assertThat(
convertToInternal(
diff --git
a/flink-cdc-composer/src/main/java/org/apache/flink/cdc/composer/flink/FlinkPipelineComposer.java
b/flink-cdc-composer/src/main/java/org/apache/flink/cdc/composer/flink/FlinkPipelineComposer.java
index 71fc865f9..c0d693ed2 100644
---
a/flink-cdc-composer/src/main/java/org/apache/flink/cdc/composer/flink/FlinkPipelineComposer.java
+++
b/flink-cdc-composer/src/main/java/org/apache/flink/cdc/composer/flink/FlinkPipelineComposer.java
@@ -197,6 +197,9 @@ public class FlinkPipelineComposer implements
PipelineComposer {
stream,
pipelineDef.getTransforms(),
pipelineDef.getConfig().get(PipelineOptions.PIPELINE_LOCAL_TIME_ZONE),
+ pipelineDef
+ .getConfig()
+
.get(PipelineOptions.PIPELINE_TRANSFORM_DECIMAL_PRECISION_MODE),
pipelineDef.getUdfs(),
pipelineDef.getModels(),
dataSource.supportedMetadataColumns(),
diff --git
a/flink-cdc-composer/src/main/java/org/apache/flink/cdc/composer/flink/translator/TransformTranslator.java
b/flink-cdc-composer/src/main/java/org/apache/flink/cdc/composer/flink/translator/TransformTranslator.java
index f24e1a99a..20c8bc54e 100644
---
a/flink-cdc-composer/src/main/java/org/apache/flink/cdc/composer/flink/translator/TransformTranslator.java
+++
b/flink-cdc-composer/src/main/java/org/apache/flink/cdc/composer/flink/translator/TransformTranslator.java
@@ -24,6 +24,7 @@ import org.apache.flink.cdc.common.factories.FactoryHelper;
import org.apache.flink.cdc.common.model.AiModelClient;
import org.apache.flink.cdc.common.model.AiModelClientFactory;
import org.apache.flink.cdc.common.model.ModelContext;
+import org.apache.flink.cdc.common.pipeline.DecimalPrecisionMode;
import org.apache.flink.cdc.common.source.SupportedMetadataColumn;
import org.apache.flink.cdc.composer.definition.ModelDef;
import org.apache.flink.cdc.composer.definition.TransformDef;
@@ -110,6 +111,7 @@ public class TransformTranslator {
DataStream<Event> input,
List<TransformDef> transforms,
String timezone,
+ DecimalPrecisionMode decimalPrecisionMode,
List<UdfDef> udfFunctions,
List<ModelDef> models,
SupportedMetadataColumn[] supportedMetadataColumns,
@@ -134,6 +136,7 @@ public class TransformTranslator {
supportedMetadataColumns);
}
postTransformFunctionBuilder.addTimezone(timezone);
+
postTransformFunctionBuilder.addDecimalPrecisionMode(decimalPrecisionMode);
postTransformFunctionBuilder.addUdfFunctions(
udfFunctions.stream().map(this::udfDefToUDFTuple).collect(Collectors.toList()));
postTransformFunctionBuilder.addUdfFunctions(
diff --git
a/flink-cdc-composer/src/test/java/org/apache/flink/cdc/composer/flink/FlinkPipelineTransformITCase.java
b/flink-cdc-composer/src/test/java/org/apache/flink/cdc/composer/flink/FlinkPipelineTransformITCase.java
index 9a774642e..f1b89a94b 100644
---
a/flink-cdc-composer/src/test/java/org/apache/flink/cdc/composer/flink/FlinkPipelineTransformITCase.java
+++
b/flink-cdc-composer/src/test/java/org/apache/flink/cdc/composer/flink/FlinkPipelineTransformITCase.java
@@ -33,6 +33,7 @@ import org.apache.flink.cdc.common.event.DropColumnEvent;
import org.apache.flink.cdc.common.event.Event;
import org.apache.flink.cdc.common.event.RenameColumnEvent;
import org.apache.flink.cdc.common.event.TableId;
+import org.apache.flink.cdc.common.pipeline.DecimalPrecisionMode;
import org.apache.flink.cdc.common.pipeline.PipelineOptions;
import org.apache.flink.cdc.common.pipeline.SchemaChangeBehavior;
import org.apache.flink.cdc.common.schema.Column;
@@ -2179,7 +2180,8 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=default_namespace.default_schema.mytable2, before=[4,
Derrida, 25, student, Derrida, 26, extras], after=[], op=DELETE, meta=()}");
}
- String[] runNumericCastingWith(String expression) throws Exception {
+ String[] runNumericCastingWith(DecimalPrecisionMode decimalPrecisionMode,
String expression)
+ throws Exception {
try {
FlinkPipelineComposer composer =
FlinkPipelineComposer.ofMiniCluster();
@@ -2207,6 +2209,9 @@ class FlinkPipelineTransformITCase {
pipelineConfig.set(PipelineOptions.PIPELINE_PARALLELISM, 1);
pipelineConfig.set(
PipelineOptions.PIPELINE_SCHEMA_CHANGE_BEHAVIOR,
SchemaChangeBehavior.EVOLVE);
+ pipelineConfig.set(
+ PipelineOptions.PIPELINE_TRANSFORM_DECIMAL_PRECISION_MODE,
+ decimalPrecisionMode);
PipelineDef pipelineDef =
new PipelineDef(
sourceDef,
@@ -2253,9 +2258,10 @@ class FlinkPipelineTransformITCase {
.collect(Collectors.joining(", "));
}
- @Test
- void testNumericCastingsWithTruncation() throws Exception {
- assertThat(runNumericCastingWith("*"))
+ @ParameterizedTest(name = "Decimal mode: {0}")
+ @EnumSource
+ void testNumericCastingsWithTruncation(DecimalPrecisionMode mode) throws
Exception {
+ assertThat(runNumericCastingWith(mode, "*"))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` TINYINT,`small_c`
SMALLINT,`int_c` INT,`bigint_c` BIGINT,`float_c` FLOAT,`double_c`
DOUBLE,`decimal_c` DECIMAL(10, 2),`valid_char_c` VARCHAR(17),`invalid_char_c`
VARCHAR(17)}, primaryKeys=id, options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2, -3, -4, -5, -6.7, -8.9, -10.11, -12.13, foo], op=INSERT,
meta=()}",
@@ -2263,7 +2269,7 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2, 3, 4, 5, 6.7, 8.9, 10.11, 12.13, baz], op=INSERT, meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
- assertThat(runNumericCastingWith(generateCastTo("BOOLEAN")))
+ assertThat(runNumericCastingWith(mode, generateCastTo("BOOLEAN")))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` BOOLEAN,`small_c` BOOLEAN,`int_c`
BOOLEAN,`bigint_c` BOOLEAN,`float_c` BOOLEAN,`double_c` BOOLEAN,`decimal_c`
BOOLEAN,`valid_char_c` BOOLEAN,`invalid_char_c` BOOLEAN}, primaryKeys=id,
options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, true, true, true, true, true, true, true, false, false], op=INSERT,
meta=()}",
@@ -2271,7 +2277,7 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, true, true, true, true, true, true, true, false, false], op=INSERT,
meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
- assertThat(runNumericCastingWith(generateCastTo("TINYINT")))
+ assertThat(runNumericCastingWith(mode, generateCastTo("TINYINT")))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` TINYINT,`small_c` TINYINT,`int_c`
TINYINT,`bigint_c` TINYINT,`float_c` TINYINT,`double_c` TINYINT,`decimal_c`
TINYINT,`valid_char_c` TINYINT,`invalid_char_c` TINYINT}, primaryKeys=id,
options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2, -3, -4, -5, -6, -8, -10, -12, null], op=INSERT, meta=()}",
@@ -2279,7 +2285,7 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2, 3, 4, 5, 6, 8, 10, 12, null], op=INSERT, meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
- assertThat(runNumericCastingWith(generateCastTo("SMALLINT")))
+ assertThat(runNumericCastingWith(mode, generateCastTo("SMALLINT")))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` SMALLINT,`small_c`
SMALLINT,`int_c` SMALLINT,`bigint_c` SMALLINT,`float_c` SMALLINT,`double_c`
SMALLINT,`decimal_c` SMALLINT,`valid_char_c` SMALLINT,`invalid_char_c`
SMALLINT}, primaryKeys=id, options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2, -3, -4, -5, -6, -8, -10, -12, null], op=INSERT, meta=()}",
@@ -2287,7 +2293,7 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2, 3, 4, 5, 6, 8, 10, 12, null], op=INSERT, meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
- assertThat(runNumericCastingWith(generateCastTo("INT")))
+ assertThat(runNumericCastingWith(mode, generateCastTo("INT")))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` INT,`small_c` INT,`int_c`
INT,`bigint_c` INT,`float_c` INT,`double_c` INT,`decimal_c` INT,`valid_char_c`
INT,`invalid_char_c` INT}, primaryKeys=id, options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2, -3, -4, -5, -6, -8, -10, -12, null], op=INSERT, meta=()}",
@@ -2295,7 +2301,7 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2, 3, 4, 5, 6, 8, 10, 12, null], op=INSERT, meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
- assertThat(runNumericCastingWith(generateCastTo("BIGINT")))
+ assertThat(runNumericCastingWith(mode, generateCastTo("BIGINT")))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` BIGINT,`small_c` BIGINT,`int_c`
BIGINT,`bigint_c` BIGINT,`float_c` BIGINT,`double_c` BIGINT,`decimal_c`
BIGINT,`valid_char_c` BIGINT,`invalid_char_c` BIGINT}, primaryKeys=id,
options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2, -3, -4, -5, -6, -8, -10, -12, null], op=INSERT, meta=()}",
@@ -2303,7 +2309,7 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2, 3, 4, 5, 6, 8, 10, 12, null], op=INSERT, meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
- assertThat(runNumericCastingWith(generateCastTo("FLOAT")))
+ assertThat(runNumericCastingWith(mode, generateCastTo("FLOAT")))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` FLOAT,`small_c` FLOAT,`int_c`
FLOAT,`bigint_c` FLOAT,`float_c` FLOAT,`double_c` FLOAT,`decimal_c`
FLOAT,`valid_char_c` FLOAT,`invalid_char_c` FLOAT}, primaryKeys=id,
options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2.0, -3.0, -4.0, -5.0, -6.7, -8.9, -10.11, -12.13, null],
op=INSERT, meta=()}",
@@ -2311,7 +2317,7 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2.0, 3.0, 4.0, 5.0, 6.7, 8.9, 10.11, 12.13, null], op=INSERT,
meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
- assertThat(runNumericCastingWith(generateCastTo("DOUBLE")))
+ assertThat(runNumericCastingWith(mode, generateCastTo("DOUBLE")))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` DOUBLE,`small_c` DOUBLE,`int_c`
DOUBLE,`bigint_c` DOUBLE,`float_c` DOUBLE,`double_c` DOUBLE,`decimal_c`
DOUBLE,`valid_char_c` DOUBLE,`invalid_char_c` DOUBLE}, primaryKeys=id,
options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2.0, -3.0, -4.0, -5.0, -6.699999809265137, -8.9, -10.11, -12.13,
null], op=INSERT, meta=()}",
@@ -2319,7 +2325,7 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2.0, 3.0, 4.0, 5.0, 6.699999809265137, 8.9, 10.11, 12.13, null],
op=INSERT, meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
- assertThat(runNumericCastingWith(generateCastTo("DECIMAL(1, 0)")))
+ assertThat(runNumericCastingWith(mode, generateCastTo("DECIMAL(1,
0)")))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` DECIMAL(1, 0),`small_c`
DECIMAL(1, 0),`int_c` DECIMAL(1, 0),`bigint_c` DECIMAL(1, 0),`float_c`
DECIMAL(1, 0),`double_c` DECIMAL(1, 0),`decimal_c` DECIMAL(1, 0),`valid_char_c`
DECIMAL(1, 0),`invalid_char_c` DECIMAL(1, 0)}, primaryKeys=id, options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2, -3, -4, -5, -7, -9, null, null, null], op=INSERT, meta=()}",
@@ -2327,7 +2333,7 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2, 3, 4, 5, 7, 9, null, null, null], op=INSERT, meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
- assertThat(runNumericCastingWith(generateCastTo("DECIMAL(2, 0)")))
+ assertThat(runNumericCastingWith(mode, generateCastTo("DECIMAL(2,
0)")))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` DECIMAL(2, 0),`small_c`
DECIMAL(2, 0),`int_c` DECIMAL(2, 0),`bigint_c` DECIMAL(2, 0),`float_c`
DECIMAL(2, 0),`double_c` DECIMAL(2, 0),`decimal_c` DECIMAL(2, 0),`valid_char_c`
DECIMAL(2, 0),`invalid_char_c` DECIMAL(2, 0)}, primaryKeys=id, options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2, -3, -4, -5, -7, -9, -10, -12, null], op=INSERT, meta=()}",
@@ -2335,7 +2341,7 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2, 3, 4, 5, 7, 9, 10, 12, null], op=INSERT, meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
- assertThat(runNumericCastingWith(generateCastTo("DECIMAL(3, 1)")))
+ assertThat(runNumericCastingWith(mode, generateCastTo("DECIMAL(3,
1)")))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` DECIMAL(3, 1),`small_c`
DECIMAL(3, 1),`int_c` DECIMAL(3, 1),`bigint_c` DECIMAL(3, 1),`float_c`
DECIMAL(3, 1),`double_c` DECIMAL(3, 1),`decimal_c` DECIMAL(3, 1),`valid_char_c`
DECIMAL(3, 1),`invalid_char_c` DECIMAL(3, 1)}, primaryKeys=id, options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2.0, -3.0, -4.0, -5.0, -6.7, -8.9, -10.1, -12.1, null], op=INSERT,
meta=()}",
@@ -2343,13 +2349,67 @@ class FlinkPipelineTransformITCase {
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2.0, 3.0, 4.0, 5.0, 6.7, 8.9, 10.1, 12.1, null], op=INSERT, meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
- assertThat(runNumericCastingWith(generateCastTo("DECIMAL(19, 10)")))
+ assertThat(runNumericCastingWith(mode, generateCastTo("DECIMAL(19,
10)")))
.containsExactly(
"CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` DECIMAL(19, 10),`small_c`
DECIMAL(19, 10),`int_c` DECIMAL(19, 10),`bigint_c` DECIMAL(19, 10),`float_c`
DECIMAL(19, 10),`double_c` DECIMAL(19, 10),`decimal_c` DECIMAL(19,
10),`valid_char_c` DECIMAL(19, 10),`invalid_char_c` DECIMAL(19, 10)},
primaryKeys=id, options=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2.0000000000, -3.0000000000, -4.0000000000, -5.0000000000,
-6.7000000000, -8.9000000000, -10.1100000000, -12.1300000000, null], op=INSERT,
meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[0, 0.0000000000, 0.0000000000, 0.0000000000, 0.0000000000, 0.0000000000,
0.0000000000, 0.0000000000, 0.0000000000, null], op=INSERT, meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2.0000000000, 3.0000000000, 4.0000000000, 5.0000000000, 6.7000000000,
8.9000000000, 10.1100000000, 12.1300000000, null], op=INSERT, meta=()}",
"DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
+
+ // Test DECIMAL with maximum precision (38)
+
+ if (mode.equals(DecimalPrecisionMode.UP_TO_38)) {
+ assertThat(runNumericCastingWith(mode, generateCastTo("DECIMAL(38,
0)")))
+ .containsExactly(
+ "CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` DECIMAL(38, 0),`small_c`
DECIMAL(38, 0),`int_c` DECIMAL(38, 0),`bigint_c` DECIMAL(38, 0),`float_c`
DECIMAL(38, 0),`double_c` DECIMAL(38, 0),`decimal_c` DECIMAL(38,
0),`valid_char_c` DECIMAL(38, 0),`invalid_char_c` DECIMAL(38, 0)},
primaryKeys=id, options=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2, -3, -4, -5, -7, -9, -10, -12, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[0, 0, 0, 0, 0, 0, 0, 0, 0, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2, 3, 4, 5, 7, 9, 10, 12, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
+
+ assertThat(runNumericCastingWith(mode, generateCastTo("DECIMAL(38,
10)")))
+ .containsExactly(
+ "CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` DECIMAL(38, 10),`small_c`
DECIMAL(38, 10),`int_c` DECIMAL(38, 10),`bigint_c` DECIMAL(38, 10),`float_c`
DECIMAL(38, 10),`double_c` DECIMAL(38, 10),`decimal_c` DECIMAL(38,
10),`valid_char_c` DECIMAL(38, 10),`invalid_char_c` DECIMAL(38, 10)},
primaryKeys=id, options=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2.0000000000, -3.0000000000, -4.0000000000, -5.0000000000,
-6.7000000000, -8.9000000000, -10.1100000000, -12.1300000000, null], op=INSERT,
meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[0, 0.0000000000, 0.0000000000, 0.0000000000, 0.0000000000, 0.0000000000,
0.0000000000, 0.0000000000, 0.0000000000, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2.0000000000, 3.0000000000, 4.0000000000, 5.0000000000, 6.7000000000,
8.9000000000, 10.1100000000, 12.1300000000, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
+
+ assertThat(runNumericCastingWith(mode, generateCastTo("DECIMAL(38,
18)")))
+ .containsExactly(
+ "CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` DECIMAL(38, 18),`small_c`
DECIMAL(38, 18),`int_c` DECIMAL(38, 18),`bigint_c` DECIMAL(38, 18),`float_c`
DECIMAL(38, 18),`double_c` DECIMAL(38, 18),`decimal_c` DECIMAL(38,
18),`valid_char_c` DECIMAL(38, 18),`invalid_char_c` DECIMAL(38, 18)},
primaryKeys=id, options=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2.000000000000000000, -3.000000000000000000, -4.000000000000000000,
-5.000000000000000000, -6.700000000000000000, -8.900000000000000000,
-10.110000000000000000, -12.130000000000000000, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[0, 0.000000000000000000, 0.000000000000000000, 0.000000000000000000,
0.000000000000000000, 0.000000000000000000, 0.000000000000000000,
0.000000000000000000, 0.000000000000000000, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2.000000000000000000, 3.000000000000000000, 4.000000000000000000,
5.000000000000000000, 6.700000000000000000, 8.900000000000000000,
10.110000000000000000, 12.130000000000000000, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
+ } else if (mode.equals(DecimalPrecisionMode.UP_TO_19)) {
+ assertThat(runNumericCastingWith(mode, generateCastTo("DECIMAL(38,
0)")))
+ .containsExactly(
+ "CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` DECIMAL(19, 0),`small_c`
DECIMAL(19, 0),`int_c` DECIMAL(19, 0),`bigint_c` DECIMAL(19, 0),`float_c`
DECIMAL(19, 0),`double_c` DECIMAL(19, 0),`decimal_c` DECIMAL(19,
0),`valid_char_c` DECIMAL(19, 0),`invalid_char_c` DECIMAL(19, 0)},
primaryKeys=id, options=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2, -3, -4, -5, -7, -9, -10, -12, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[0, 0, 0, 0, 0, 0, 0, 0, 0, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2, 3, 4, 5, 7, 9, 10, 12, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
+
+ assertThat(runNumericCastingWith(mode, generateCastTo("DECIMAL(38,
10)")))
+ .containsExactly(
+ "CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` DECIMAL(19, 10),`small_c`
DECIMAL(19, 10),`int_c` DECIMAL(19, 10),`bigint_c` DECIMAL(19, 10),`float_c`
DECIMAL(19, 10),`double_c` DECIMAL(19, 10),`decimal_c` DECIMAL(19,
10),`valid_char_c` DECIMAL(19, 10),`invalid_char_c` DECIMAL(19, 10)},
primaryKeys=id, options=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2.0000000000, -3.0000000000, -4.0000000000, -5.0000000000,
-6.7000000000, -8.9000000000, -10.1100000000, -12.1300000000, null], op=INSERT,
meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[0, 0.0000000000, 0.0000000000, 0.0000000000, 0.0000000000, 0.0000000000,
0.0000000000, 0.0000000000, 0.0000000000, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2.0000000000, 3.0000000000, 4.0000000000, 5.0000000000, 6.7000000000,
8.9000000000, 10.1100000000, 12.1300000000, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
+
+ assertThat(runNumericCastingWith(mode, generateCastTo("DECIMAL(38,
18)")))
+ .containsExactly(
+ "CreateTableEvent{tableId=ns.scm.tbl,
schema=columns={`id` BIGINT NOT NULL,`tiny_c` DECIMAL(19, 18),`small_c`
DECIMAL(19, 18),`int_c` DECIMAL(19, 18),`bigint_c` DECIMAL(19, 18),`float_c`
DECIMAL(19, 18),`double_c` DECIMAL(19, 18),`decimal_c` DECIMAL(19,
18),`valid_char_c` DECIMAL(19, 18),`invalid_char_c` DECIMAL(19, 18)},
primaryKeys=id, options=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[-1, -2.000000000000000000, -3.000000000000000000, -4.000000000000000000,
-5.000000000000000000, -6.700000000000000000, -8.900000000000000000, null,
null, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[0, 0.000000000000000000, 0.000000000000000000, 0.000000000000000000,
0.000000000000000000, 0.000000000000000000, 0.000000000000000000,
0.000000000000000000, 0.000000000000000000, null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[1, 2.000000000000000000, 3.000000000000000000, 4.000000000000000000,
5.000000000000000000, 6.700000000000000000, 8.900000000000000000, null, null,
null], op=INSERT, meta=()}",
+ "DataChangeEvent{tableId=ns.scm.tbl, before=[],
after=[2, null, null, null, null, null, null, null, null, null], op=INSERT,
meta=()}");
+ } else {
+ Assertions.fail("Unexpected decimal precision mode: " + mode);
+ }
}
@Test
diff --git
a/flink-cdc-composer/src/test/java/org/apache/flink/cdc/composer/specs/TransformSpecsITCase.java
b/flink-cdc-composer/src/test/java/org/apache/flink/cdc/composer/specs/TransformSpecsITCase.java
index 0d8079e71..337df690b 100644
---
a/flink-cdc-composer/src/test/java/org/apache/flink/cdc/composer/specs/TransformSpecsITCase.java
+++
b/flink-cdc-composer/src/test/java/org/apache/flink/cdc/composer/specs/TransformSpecsITCase.java
@@ -32,6 +32,7 @@ import org.apache.flink.cdc.common.event.CreateTableEvent;
import org.apache.flink.cdc.common.event.DataChangeEvent;
import org.apache.flink.cdc.common.event.Event;
import org.apache.flink.cdc.common.event.TableId;
+import org.apache.flink.cdc.common.pipeline.DecimalPrecisionMode;
import org.apache.flink.cdc.common.pipeline.PipelineOptions;
import org.apache.flink.cdc.common.pipeline.SchemaChangeBehavior;
import org.apache.flink.cdc.common.schema.Schema;
@@ -342,7 +343,6 @@ class TransformSpecsITCase {
private static final ObjectMapper mapper = new ObjectMapper(new
YAMLFactory());
- @SuppressWarnings("unchecked")
private static Stream<TestSpec> loadTestSpec(Path specPath) {
List<TestSpec> specs = new ArrayList<>();
try {
@@ -356,6 +356,11 @@ class TransformSpecsITCase {
if (specNode.has("time-zone")) {
spec.timeZone = asTextOrNull(specNode.get("time-zone"));
}
+ if (specNode.has("decimal-precision-mode")) {
+ spec.decimalPrecisionMode =
+ DecimalPrecisionMode.valueOf(
+
specNode.get("decimal-precision-mode").asText().toUpperCase());
+ }
if (specNode.has("projection")) {
spec.projectionRules =
List.of(
@@ -399,6 +404,7 @@ class TransformSpecsITCase {
public String name;
public String ignore;
public String timeZone = "UTC";
+ public DecimalPrecisionMode decimalPrecisionMode =
DecimalPrecisionMode.UP_TO_19;
public List<String> projectionRules = new ArrayList<>();
public @Nullable String filterRule;
public @Nullable String primaryKey;
@@ -474,6 +480,9 @@ class TransformSpecsITCase {
pipelineConfig.set(PipelineOptions.PIPELINE_LOCAL_TIME_ZONE,
spec.timeZone);
pipelineConfig.set(
PipelineOptions.PIPELINE_SCHEMA_CHANGE_BEHAVIOR,
SchemaChangeBehavior.EVOLVE);
+ pipelineConfig.set(
+ PipelineOptions.PIPELINE_TRANSFORM_DECIMAL_PRECISION_MODE,
+ spec.decimalPrecisionMode);
PipelineDef pipelineDef =
new PipelineDef(
sourceDef,
diff --git a/flink-cdc-composer/src/test/resources/specs/casting.yaml
b/flink-cdc-composer/src/test/resources/specs/casting.yaml
index 288b8df16..817f52b8d 100644
--- a/flink-cdc-composer/src/test/resources/specs/casting.yaml
+++ b/flink-cdc-composer/src/test/resources/specs/casting.yaml
@@ -220,6 +220,126 @@
DataChangeEvent{tableId=foo.bar.baz, before=[-1, null, 0.00000, 1.00000,
2.22000, 333.00000, 44.44000, 5555555.00000, null], after=[], op=DELETE,
meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, 0.00000,
1.00000, 2.22000, 333.00000, 44.44000, 5555555.00000, null], op=INSERT, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[0, null, 0.00000, 1.00000,
2.22000, 333.00000, 44.44000, 5555555.00000, null], after=[], op=DELETE,
meta=()}
+- do: Cast To Decimal(38, 0) (Decimal-19 mode)
+ decimal-precision-mode: up_to_19
+ projection: |-
+ id_
+ CAST(null AS DECIMAL(38, 0)) AS comp_0
+ CAST(0 AS DECIMAL(38, 0)) AS comp_1
+ CAST(1 AS DECIMAL(38, 0)) AS comp_2
+ CAST('2.22' AS DECIMAL(38, 0)) AS comp_3
+ CAST('333' AS DECIMAL(38, 0)) AS comp_4
+ CAST('44.44' AS DECIMAL(38, 0)) AS comp_5
+ CAST('5555555' AS DECIMAL(38, 0)) AS comp_6
+ CAST('FOOBAR' AS DECIMAL(38, 0)) AS comp_7
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_0` DECIMAL(19, 0),`comp_1` DECIMAL(19, 0),`comp_2`
DECIMAL(19, 0),`comp_3` DECIMAL(19, 0),`comp_4` DECIMAL(19, 0),`comp_5`
DECIMAL(19, 0),`comp_6` DECIMAL(19, 0),`comp_7` DECIMAL(19, 0)},
primaryKeys=id_, options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, null, 0, 1, 2,
333, 44, 5555555, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, null, 0, 1, 2, 333, 44,
5555555, null], after=[-1, null, 0, 1, 2, 333, 44, 5555555, null], op=UPDATE,
meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, null, 0, 1, 2, 333, 44,
5555555, null], after=[], op=DELETE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, 0, 1, 2,
333, 44, 5555555, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[0, null, 0, 1, 2, 333, 44,
5555555, null], after=[], op=DELETE, meta=()}
+- do: Cast To Decimal(38, 0) (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
+ projection: |-
+ id_
+ CAST(null AS DECIMAL(38, 0)) AS comp_0
+ CAST(0 AS DECIMAL(38, 0)) AS comp_1
+ CAST(1 AS DECIMAL(38, 0)) AS comp_2
+ CAST('2.22' AS DECIMAL(38, 0)) AS comp_3
+ CAST('333' AS DECIMAL(38, 0)) AS comp_4
+ CAST('44.44' AS DECIMAL(38, 0)) AS comp_5
+ CAST('5555555' AS DECIMAL(38, 0)) AS comp_6
+ CAST('FOOBAR' AS DECIMAL(38, 0)) AS comp_7
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_0` DECIMAL(38, 0),`comp_1` DECIMAL(38, 0),`comp_2`
DECIMAL(38, 0),`comp_3` DECIMAL(38, 0),`comp_4` DECIMAL(38, 0),`comp_5`
DECIMAL(38, 0),`comp_6` DECIMAL(38, 0),`comp_7` DECIMAL(38, 0)},
primaryKeys=id_, options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, null, 0, 1, 2,
333, 44, 5555555, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, null, 0, 1, 2, 333, 44,
5555555, null], after=[-1, null, 0, 1, 2, 333, 44, 5555555, null], op=UPDATE,
meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, null, 0, 1, 2, 333, 44,
5555555, null], after=[], op=DELETE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, 0, 1, 2,
333, 44, 5555555, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[0, null, 0, 1, 2, 333, 44,
5555555, null], after=[], op=DELETE, meta=()}
+- do: Cast To Decimal(38, 10) (Decimal-19 mode)
+ decimal-precision-mode: up_to_19
+ projection: |-
+ id_
+ CAST(null AS DECIMAL(38, 10)) AS comp_0
+ CAST(0 AS DECIMAL(38, 10)) AS comp_1
+ CAST(1 AS DECIMAL(38, 10)) AS comp_2
+ CAST('2.22' AS DECIMAL(38, 10)) AS comp_3
+ CAST('333' AS DECIMAL(38, 10)) AS comp_4
+ CAST('44.44' AS DECIMAL(38, 10)) AS comp_5
+ CAST('5555555' AS DECIMAL(38, 10)) AS comp_6
+ CAST('FOOBAR' AS DECIMAL(38, 10)) AS comp_7
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_0` DECIMAL(19, 10),`comp_1` DECIMAL(19, 10),`comp_2`
DECIMAL(19, 10),`comp_3` DECIMAL(19, 10),`comp_4` DECIMAL(19, 10),`comp_5`
DECIMAL(19, 10),`comp_6` DECIMAL(19, 10),`comp_7` DECIMAL(19, 10)},
primaryKeys=id_, options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, null,
0.0000000000, 1.0000000000, 2.2200000000, 333.0000000000, 44.4400000000,
5555555.0000000000, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, null, 0.0000000000,
1.0000000000, 2.2200000000, 333.0000000000, 44.4400000000, 5555555.0000000000,
null], after=[-1, null, 0.0000000000, 1.0000000000, 2.2200000000,
333.0000000000, 44.4400000000, 5555555.0000000000, null], op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, null, 0.0000000000,
1.0000000000, 2.2200000000, 333.0000000000, 44.4400000000, 5555555.0000000000,
null], after=[], op=DELETE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null,
0.0000000000, 1.0000000000, 2.2200000000, 333.0000000000, 44.4400000000,
5555555.0000000000, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[0, null, 0.0000000000,
1.0000000000, 2.2200000000, 333.0000000000, 44.4400000000, 5555555.0000000000,
null], after=[], op=DELETE, meta=()}
+- do: Cast To Decimal(38, 10) (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
+ projection: |-
+ id_
+ CAST(null AS DECIMAL(38, 10)) AS comp_0
+ CAST(0 AS DECIMAL(38, 10)) AS comp_1
+ CAST(1 AS DECIMAL(38, 10)) AS comp_2
+ CAST('2.22' AS DECIMAL(38, 10)) AS comp_3
+ CAST('333' AS DECIMAL(38, 10)) AS comp_4
+ CAST('44.44' AS DECIMAL(38, 10)) AS comp_5
+ CAST('5555555' AS DECIMAL(38, 10)) AS comp_6
+ CAST('FOOBAR' AS DECIMAL(38, 10)) AS comp_7
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_0` DECIMAL(38, 10),`comp_1` DECIMAL(38, 10),`comp_2`
DECIMAL(38, 10),`comp_3` DECIMAL(38, 10),`comp_4` DECIMAL(38, 10),`comp_5`
DECIMAL(38, 10),`comp_6` DECIMAL(38, 10),`comp_7` DECIMAL(38, 10)},
primaryKeys=id_, options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, null,
0.0000000000, 1.0000000000, 2.2200000000, 333.0000000000, 44.4400000000,
5555555.0000000000, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, null, 0.0000000000,
1.0000000000, 2.2200000000, 333.0000000000, 44.4400000000, 5555555.0000000000,
null], after=[-1, null, 0.0000000000, 1.0000000000, 2.2200000000,
333.0000000000, 44.4400000000, 5555555.0000000000, null], op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, null, 0.0000000000,
1.0000000000, 2.2200000000, 333.0000000000, 44.4400000000, 5555555.0000000000,
null], after=[], op=DELETE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null,
0.0000000000, 1.0000000000, 2.2200000000, 333.0000000000, 44.4400000000,
5555555.0000000000, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[0, null, 0.0000000000,
1.0000000000, 2.2200000000, 333.0000000000, 44.4400000000, 5555555.0000000000,
null], after=[], op=DELETE, meta=()}
+- do: Cast To Decimal(38, 18) (Decimal-19 mode)
+ decimal-precision-mode: up_to_19
+ projection: |-
+ id_
+ CAST(null AS DECIMAL(38, 18)) AS comp_0
+ CAST(0 AS DECIMAL(38, 18)) AS comp_1
+ CAST(1 AS DECIMAL(38, 18)) AS comp_2
+ CAST('2.22' AS DECIMAL(38, 18)) AS comp_3
+ CAST('333' AS DECIMAL(38, 18)) AS comp_4
+ CAST('44.44' AS DECIMAL(38, 18)) AS comp_5
+ CAST('5555555' AS DECIMAL(38, 18)) AS comp_6
+ CAST('FOOBAR' AS DECIMAL(38, 18)) AS comp_7
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_0` DECIMAL(19, 18),`comp_1` DECIMAL(19, 18),`comp_2`
DECIMAL(19, 18),`comp_3` DECIMAL(19, 18),`comp_4` DECIMAL(19, 18),`comp_5`
DECIMAL(19, 18),`comp_6` DECIMAL(19, 18),`comp_7` DECIMAL(19, 18)},
primaryKeys=id_, options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, null,
0.000000000000000000, 1.000000000000000000, 2.220000000000000000, null, null,
null, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, null,
0.000000000000000000, 1.000000000000000000, 2.220000000000000000, null, null,
null, null], after=[-1, null, 0.000000000000000000, 1.000000000000000000,
2.220000000000000000, null, null, null, null], op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, null,
0.000000000000000000, 1.000000000000000000, 2.220000000000000000, null, null,
null, null], after=[], op=DELETE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null,
0.000000000000000000, 1.000000000000000000, 2.220000000000000000, null, null,
null, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[0, null,
0.000000000000000000, 1.000000000000000000, 2.220000000000000000, null, null,
null, null], after=[], op=DELETE, meta=()}
+- do: Cast To Decimal(38, 18) (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
+ projection: |-
+ id_
+ CAST(null AS DECIMAL(38, 18)) AS comp_0
+ CAST(0 AS DECIMAL(38, 18)) AS comp_1
+ CAST(1 AS DECIMAL(38, 18)) AS comp_2
+ CAST('2.22' AS DECIMAL(38, 18)) AS comp_3
+ CAST('333' AS DECIMAL(38, 18)) AS comp_4
+ CAST('44.44' AS DECIMAL(38, 18)) AS comp_5
+ CAST('5555555' AS DECIMAL(38, 18)) AS comp_6
+ CAST('FOOBAR' AS DECIMAL(38, 18)) AS comp_7
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_0` DECIMAL(38, 18),`comp_1` DECIMAL(38, 18),`comp_2`
DECIMAL(38, 18),`comp_3` DECIMAL(38, 18),`comp_4` DECIMAL(38, 18),`comp_5`
DECIMAL(38, 18),`comp_6` DECIMAL(38, 18),`comp_7` DECIMAL(38, 18)},
primaryKeys=id_, options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, null,
0.000000000000000000, 1.000000000000000000, 2.220000000000000000,
333.000000000000000000, 44.440000000000000000, 5555555.000000000000000000,
null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, null,
0.000000000000000000, 1.000000000000000000, 2.220000000000000000,
333.000000000000000000, 44.440000000000000000, 5555555.000000000000000000,
null], after=[-1, null, 0.000000000000000000, 1.000000000000000000,
2.220000000000000000, 333.000000000000000000, 44.440000000000000000,
5555555.000000000000000000, null], op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, null,
0.000000000000000000, 1.000000000000000000, 2.220000000000000000,
333.000000000000000000, 44.440000000000000000, 5555555.000000000000000000,
null], after=[], op=DELETE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null,
0.000000000000000000, 1.000000000000000000, 2.220000000000000000,
333.000000000000000000, 44.440000000000000000, 5555555.000000000000000000,
null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[0, null,
0.000000000000000000, 1.000000000000000000, 2.220000000000000000,
333.000000000000000000, 44.440000000000000000, 5555555.000000000000000000,
null], after=[], op=DELETE, meta=()}
- do: Cast To Timestamp (UTC)
projection: |-
id_
diff --git a/flink-cdc-composer/src/test/resources/specs/decimal.yaml
b/flink-cdc-composer/src/test/resources/specs/decimal.yaml
index f1418dd32..7e60cba37 100644
--- a/flink-cdc-composer/src/test/resources/specs/decimal.yaml
+++ b/flink-cdc-composer/src/test/resources/specs/decimal.yaml
@@ -13,7 +13,8 @@
# limitations under the License.
################################################################################
-- do: Add Op
+- do: Add Op (Decimal-19 mode)
+ decimal-precision-mode: up_to_19
projection: |-
id_
decimal_10_0_ + CAST(1 AS DECIMAL(1, 0)) AS comp_1
@@ -25,7 +26,38 @@
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567891,
null], op=INSERT, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567891, null],
after=[-1, -9876543209, null], op=UPDATE, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543209, null],
after=[], op=DELETE, meta=()}
-- do: Subtract Op
+
+- do: Add Op (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
+ projection: |-
+ id_
+ decimal_10_0_ + CAST(1 AS DECIMAL(1, 0)) AS comp_1
+ decimal_20_2_ + CAST(1 AS DECIMAL(1, 0)) AS comp_2
+ non-null: 'true'
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_1` DECIMAL(11, 0),`comp_2` DECIMAL(21, 2)},
primaryKeys=id_, options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567891,
123456789012345679.90], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567891,
123456789012345679.90], after=[-1, -9876543209, -987654321098765431.10],
op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543209,
-987654321098765431.10], after=[], op=DELETE, meta=()}
+
+- do: Nested Expressions (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
+ projection: |-
+ id_
+ IFNULL(decimal_20_2_ + CAST(1 AS DECIMAL(1, 0)), CAST(0 AS DECIMAL(1, 0)))
AS ifnull_decimal
+ NULLIF(decimal_20_2_ + CAST(1 AS DECIMAL(1, 0)), CAST(0 AS DECIMAL(1, 0)))
AS nullif_decimal
+ ARRAY[decimal_20_2_ + CAST(1 AS DECIMAL(1, 0)), CAST(0 AS DECIMAL(1, 0))]
AS decimal_array
+ non-null: 'true'
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`ifnull_decimal` DECIMAL(21, 2) NOT NULL,`nullif_decimal`
DECIMAL(21, 2),`decimal_array` ARRAY<DECIMAL(21, 2)>}, primaryKeys=id_,
options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1,
123456789012345679.90, 123456789012345679.90, [123456789012345679.90, 0.00]],
op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 123456789012345679.90,
123456789012345679.90, [123456789012345679.90, 0.00]], after=[-1,
-987654321098765431.10, -987654321098765431.10, [-987654321098765431.10,
0.00]], op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, -987654321098765431.10,
-987654321098765431.10, [-987654321098765431.10, 0.00]], after=[], op=DELETE,
meta=()}
+
+- do: Subtract Op (Decimal-19 mode)
+ decimal-precision-mode: up_to_19
projection: |-
id_
decimal_10_0_ - CAST(1 AS DECIMAL(1, 0)) AS comp_1
@@ -37,31 +69,83 @@
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567889,
null], op=INSERT, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567889, null],
after=[-1, -9876543211, null], op=UPDATE, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543211, null],
after=[], op=DELETE, meta=()}
-- do: Multiply Op
+
+- do: Subtract Op (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
+ projection: |-
+ id_
+ decimal_10_0_ - CAST(1 AS DECIMAL(1, 0)) AS comp_1
+ decimal_20_2_ - CAST(1 AS DECIMAL(1, 0)) AS comp_2
+ primary-key: id_
+ non-null: 'true'
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_1` DECIMAL(11, 0),`comp_2` DECIMAL(21, 2)},
primaryKeys=id_, options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567889,
123456789012345677.90], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567889,
123456789012345677.90], after=[-1, -9876543211, -987654321098765433.10],
op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543211,
-987654321098765433.10], after=[], op=DELETE, meta=()}
+
+- do: Multiply Op (Decimal-19 mode)
+ decimal-precision-mode: up_to_19
projection: |-
id_
+ decimal_10_0_, decimal_20_2_
decimal_10_0_ * CAST(2 AS DECIMAL(1, 0)) AS comp_1
decimal_20_2_ * CAST(2 AS DECIMAL(1, 0)) AS comp_2
primary-key: id_
non-null: 'true'
expect: |-
- CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_1` DECIMAL(11, 0),`comp_2` DECIMAL(19, 2)},
primaryKeys=id_, options=()}
- DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 2469135780,
null], op=INSERT, meta=()}
- DataChangeEvent{tableId=foo.bar.baz, before=[1, 2469135780, null],
after=[-1, -19753086420, null], op=UPDATE, meta=()}
- DataChangeEvent{tableId=foo.bar.baz, before=[-1, -19753086420, null],
after=[], op=DELETE, meta=()}
-- do: Divide Op
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`decimal_10_0_` DECIMAL(10, 0),`decimal_20_2_` DECIMAL(20,
2),`comp_1` DECIMAL(11, 0),`comp_2` DECIMAL(19, 2)}, primaryKeys=id_,
options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567890,
123456789012345678.90, 2469135780, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567890,
123456789012345678.90, 2469135780, null], after=[-1, -9876543210,
-987654321098765432.10, -19753086420, null], op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543210,
-987654321098765432.10, -19753086420, null], after=[], op=DELETE, meta=()}
+
+- do: Multiply Op (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
+ projection: |-
+ id_
+ decimal_10_0_, decimal_20_2_
+ decimal_10_0_ * CAST(2 AS DECIMAL(1, 0)) AS comp_1
+ decimal_20_2_ * CAST(2 AS DECIMAL(1, 0)) AS comp_2
+ primary-key: id_
+ non-null: 'true'
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`decimal_10_0_` DECIMAL(10, 0),`decimal_20_2_` DECIMAL(20,
2),`comp_1` DECIMAL(11, 0),`comp_2` DECIMAL(21, 2)}, primaryKeys=id_,
options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567890,
123456789012345678.90, 2469135780, 246913578024691357.80], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567890,
123456789012345678.90, 2469135780, 246913578024691357.80], after=[-1,
-9876543210, -987654321098765432.10, -19753086420, -1975308642197530864.20],
op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543210,
-987654321098765432.10, -19753086420, -1975308642197530864.20], after=[],
op=DELETE, meta=()}
+
+- do: Divide Op (Decimal-19 mode)
+ decimal-precision-mode: up_to_19
projection: |-
id_
+ decimal_10_0_, decimal_20_2_
decimal_10_0_ / CAST(2 AS DECIMAL(1, 0)) AS comp_1
decimal_20_2_ / CAST(2 AS DECIMAL(1, 0)) AS comp_2
primary-key: id_
non-null: 'true'
expect: |-
- CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_1` DECIMAL(16, 6),`comp_2` DECIMAL(19, 2)},
primaryKeys=id_, options=()}
- DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 617.283945,
61728394506172839.45], op=INSERT, meta=()}
- DataChangeEvent{tableId=foo.bar.baz, before=[1, 617.283945,
61728394506172839.45], after=[-1, -4938.271605, null], op=UPDATE, meta=()}
- DataChangeEvent{tableId=foo.bar.baz, before=[-1, -4938.271605, null],
after=[], op=DELETE, meta=()}
-- do: Abs Op
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`decimal_10_0_` DECIMAL(10, 0),`decimal_20_2_` DECIMAL(20,
2),`comp_1` DECIMAL(16, 6),`comp_2` DECIMAL(19, 2)}, primaryKeys=id_,
options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567890,
123456789012345678.90, 617283945.000000, 61728394506172839.45], op=INSERT,
meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567890,
123456789012345678.90, 617283945.000000, 61728394506172839.45], after=[-1,
-9876543210, -987654321098765432.10, -4938271605.000000, null], op=UPDATE,
meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543210,
-987654321098765432.10, -4938271605.000000, null], after=[], op=DELETE, meta=()}
+
+- do: Divide Op (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
+ projection: |-
+ id_
+ decimal_10_0_, decimal_20_2_
+ decimal_10_0_ / CAST(2 AS DECIMAL(1, 0)) AS comp_1
+ decimal_20_2_ / CAST(2 AS DECIMAL(1, 0)) AS comp_2
+ primary-key: id_
+ non-null: 'true'
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`decimal_10_0_` DECIMAL(10, 0),`decimal_20_2_` DECIMAL(20,
2),`comp_1` DECIMAL(16, 6),`comp_2` DECIMAL(24, 6)}, primaryKeys=id_,
options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567890,
123456789012345678.90, 617283945.000000, 61728394506172839.450000], op=INSERT,
meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567890,
123456789012345678.90, 617283945.000000, 61728394506172839.450000], after=[-1,
-9876543210, -987654321098765432.10, -4938271605.000000,
-493827160549382716.050000], op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543210,
-987654321098765432.10, -4938271605.000000, -493827160549382716.050000],
after=[], op=DELETE, meta=()}
+
+- do: Abs Op (Decimal-19 mode)
+ decimal-precision-mode: up_to_19
projection: |-
id_
ABS(decimal_10_0_) AS comp_1
@@ -74,7 +158,24 @@
DataChangeEvent{tableId=foo.bar.baz, before=[-1, 9876543210, null],
after=[], op=DELETE, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null],
op=INSERT, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null], after=[],
op=DELETE, meta=()}
-- do: Ceil Op
+
+- do: Abs Op (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
+ projection: |-
+ id_
+ ABS(decimal_10_0_) AS comp_1
+ ABS(decimal_20_2_) AS comp_2
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_1` DECIMAL(10, 0),`comp_2` DECIMAL(20, 2)},
primaryKeys=id_, options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567890,
123456789012345678.90], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567890,
123456789012345678.90], after=[-1, 9876543210, 987654321098765432.10],
op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, 9876543210,
987654321098765432.10], after=[], op=DELETE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null],
op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null], after=[],
op=DELETE, meta=()}
+
+- do: Ceil Op (Decimal-19 mode)
+ decimal-precision-mode: up_to_19
projection: |-
id_
CEIL(decimal_10_0_) AS comp_1
@@ -87,7 +188,24 @@
DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543210,
-987654321098765432], after=[], op=DELETE, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null],
op=INSERT, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null], after=[],
op=DELETE, meta=()}
-- do: Floor Op
+
+- do: Ceil Op (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
+ projection: |-
+ id_
+ CEIL(decimal_10_0_) AS comp_1
+ CEIL(decimal_20_2_) AS comp_2
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_1` DECIMAL(10, 0),`comp_2` DECIMAL(20, 0)},
primaryKeys=id_, options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567890,
123456789012345679], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567890,
123456789012345679], after=[-1, -9876543210, -987654321098765432], op=UPDATE,
meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543210,
-987654321098765432], after=[], op=DELETE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null],
op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null], after=[],
op=DELETE, meta=()}
+
+- do: Floor Op (Decimal-19 mode)
+ decimal-precision-mode: up_to_19
projection: |-
id_
FLOOR(decimal_10_0_) AS comp_1
@@ -100,16 +218,50 @@
DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543210,
-987654321098765433], after=[], op=DELETE, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null],
op=INSERT, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null], after=[],
op=DELETE, meta=()}
-- do: Round Op
+
+- do: Floor Op (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
projection: |-
id_
- ROUND(decimal_10_0_, 1) AS comp_1
- ROUND(decimal_20_2_, 1) AS comp_2
+ FLOOR(decimal_10_0_) AS comp_1
+ FLOOR(decimal_20_2_) AS comp_2
primary-key: id_
expect: |-
- CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_1` DECIMAL(10, 0),`comp_2` DECIMAL(19, 1)},
primaryKeys=id_, options=()}
- DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 12345678900,
123456789012345678.9], op=INSERT, meta=()}
- DataChangeEvent{tableId=foo.bar.baz, before=[1, 12345678900,
123456789012345678.9], after=[-1, -98765432100, -987654321098765432.1],
op=UPDATE, meta=()}
- DataChangeEvent{tableId=foo.bar.baz, before=[-1, -98765432100,
-987654321098765432.1], after=[], op=DELETE, meta=()}
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`comp_1` DECIMAL(10, 0),`comp_2` DECIMAL(20, 0)},
primaryKeys=id_, options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567890,
123456789012345678], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567890,
123456789012345678], after=[-1, -9876543210, -987654321098765433], op=UPDATE,
meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543210,
-987654321098765433], after=[], op=DELETE, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null],
op=INSERT, meta=()}
DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null], after=[],
op=DELETE, meta=()}
+
+- do: Round Op (Decimal-19 mode)
+ decimal-precision-mode: up_to_19
+ projection: |-
+ id_
+ decimal_10_0_, decimal_20_2_
+ ROUND(decimal_10_0_, 1) AS comp_1
+ ROUND(decimal_20_2_, 1) AS comp_2
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`decimal_10_0_` DECIMAL(10, 0),`decimal_20_2_` DECIMAL(20,
2),`comp_1` DECIMAL(10, 0),`comp_2` DECIMAL(19, 1)}, primaryKeys=id_,
options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567890,
123456789012345678.90, 1234567890, 123456789012345678.9], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567890,
123456789012345678.90, 1234567890, 123456789012345678.9], after=[-1,
-9876543210, -987654321098765432.10, -9876543210, -987654321098765432.1],
op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543210,
-987654321098765432.10, -9876543210, -987654321098765432.1], after=[],
op=DELETE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null,
null, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null, null, null],
after=[], op=DELETE, meta=()}
+
+- do: Round Op (Decimal-38 mode)
+ decimal-precision-mode: up_to_38
+ projection: |-
+ id_
+ decimal_10_0_, decimal_20_2_
+ ROUND(decimal_10_0_, 1) AS comp_1
+ ROUND(decimal_20_2_, 1) AS comp_2
+ primary-key: id_
+ expect: |-
+ CreateTableEvent{tableId=foo.bar.baz, schema=columns={`id_` BIGINT NOT
NULL 'Identifier',`decimal_10_0_` DECIMAL(10, 0),`decimal_20_2_` DECIMAL(20,
2),`comp_1` DECIMAL(10, 0),`comp_2` DECIMAL(20, 1)}, primaryKeys=id_,
options=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[1, 1234567890,
123456789012345678.90, 1234567890, 123456789012345678.9], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[1, 1234567890,
123456789012345678.90, 1234567890, 123456789012345678.9], after=[-1,
-9876543210, -987654321098765432.10, -9876543210, -987654321098765432.1],
op=UPDATE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[-1, -9876543210,
-987654321098765432.10, -9876543210, -987654321098765432.1], after=[],
op=DELETE, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[], after=[0, null, null,
null, null], op=INSERT, meta=()}
+ DataChangeEvent{tableId=foo.bar.baz, before=[0, null, null, null, null],
after=[], op=DELETE, meta=()}
diff --git
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/PostTransformOperator.java
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/PostTransformOperator.java
index 9c6552fbe..36deef753 100644
---
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/PostTransformOperator.java
+++
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/PostTransformOperator.java
@@ -30,6 +30,7 @@ import org.apache.flink.cdc.common.event.Event;
import org.apache.flink.cdc.common.event.SchemaChangeEvent;
import org.apache.flink.cdc.common.event.TableId;
import org.apache.flink.cdc.common.model.AiModelClient;
+import org.apache.flink.cdc.common.pipeline.DecimalPrecisionMode;
import org.apache.flink.cdc.common.schema.Schema;
import org.apache.flink.cdc.common.schema.Selectors;
import org.apache.flink.cdc.common.udf.UserDefinedFunctionContext;
@@ -77,6 +78,7 @@ public class PostTransformOperator extends
AbstractStreamOperatorAdapter<Event>
private static final Logger LOG =
LoggerFactory.getLogger(PostTransformOperator.class);
private final String timezone;
+ private final DecimalPrecisionMode decimalPrecisionMode;
private final List<TransformRule> transformRules;
private final Map<TableId, Boolean> hasAsteriskMap;
private final Map<TableId, List<String>> projectedColumnsMap;
@@ -107,9 +109,11 @@ public class PostTransformOperator extends
AbstractStreamOperatorAdapter<Event>
PostTransformOperator(
List<TransformRule> transformRules,
String timezone,
+ DecimalPrecisionMode decimalPrecisionMode,
List<Tuple3<String, String, Map<String, String>>> udfFunctions,
Map<String, AiModelClient> modelClients) {
this.timezone = timezone;
+ this.decimalPrecisionMode = decimalPrecisionMode;
this.transformRules = transformRules;
this.hasAsteriskMap = new HashMap<>();
this.projectedColumnsMap = new HashMap<>();
@@ -383,7 +387,8 @@ public class PostTransformOperator extends
AbstractStreamOperatorAdapter<Event>
.orElse(null),
preSchema.getColumns(),
udfDescriptors,
- transformer.getSupportedMetadataColumns());
+ transformer.getSupportedMetadataColumns(),
+ decimalPrecisionMode);
return preSchema.copy(
projectionColumns.stream()
.map(ProjectionColumn::getColumn)
@@ -460,6 +465,7 @@ public class PostTransformOperator extends
AbstractStreamOperatorAdapter<Event>
.map(TransformProjection::getProjection)
.orElse(null),
timezone,
+ decimalPrecisionMode,
udfDescriptors,
udfFunctionInstances,
postTransformer.getSupportedMetadataColumns(),
@@ -476,7 +482,10 @@ public class PostTransformOperator extends
AbstractStreamOperatorAdapter<Event>
TableId tableId, PostTransformer postTransformer) {
if (!filterProcessors.contains(tableId, postTransformer)) {
if (!postTransformer.getFilter().isPresent()) {
- filterProcessors.put(tableId, postTransformer,
TransformFilterProcessor.ofNoOp());
+ filterProcessors.put(
+ tableId,
+ postTransformer,
+ TransformFilterProcessor.ofNoOp(decimalPrecisionMode));
} else {
PostTransformChangeInfo changeInfo =
postTransformInfoMap.get(tableId);
filterProcessors.put(
@@ -486,6 +495,7 @@ public class PostTransformOperator extends
AbstractStreamOperatorAdapter<Event>
changeInfo,
postTransformer.getFilter().orElse(null),
timezone,
+ decimalPrecisionMode,
udfDescriptors,
udfFunctionInstances,
postTransformer.getSupportedMetadataColumns(),
diff --git
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/PostTransformOperatorBuilder.java
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/PostTransformOperatorBuilder.java
index be2e8823b..a5de56c75 100644
---
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/PostTransformOperatorBuilder.java
+++
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/PostTransformOperatorBuilder.java
@@ -19,6 +19,7 @@ package org.apache.flink.cdc.runtime.operators.transform;
import org.apache.flink.api.java.tuple.Tuple3;
import org.apache.flink.cdc.common.model.AiModelClient;
+import org.apache.flink.cdc.common.pipeline.DecimalPrecisionMode;
import org.apache.flink.cdc.common.pipeline.PipelineOptions;
import org.apache.flink.cdc.common.source.SupportedMetadataColumn;
@@ -34,6 +35,7 @@ import java.util.Map;
public class PostTransformOperatorBuilder {
private final List<TransformRule> transformRules = new ArrayList<>();
private String timezone;
+ private DecimalPrecisionMode decimalPrecisionMode =
DecimalPrecisionMode.UP_TO_19;
private final List<Tuple3<String, String, Map<String, String>>>
udfFunctions =
new ArrayList<>();
private final Map<String, AiModelClient> modelClients = new
LinkedHashMap<>();
@@ -108,6 +110,12 @@ public class PostTransformOperatorBuilder {
return this;
}
+ public PostTransformOperatorBuilder addDecimalPrecisionMode(
+ DecimalPrecisionMode decimalPrecisionMode) {
+ this.decimalPrecisionMode = decimalPrecisionMode;
+ return this;
+ }
+
public PostTransformOperatorBuilder addUdfFunctions(
List<Tuple3<String, String, Map<String, String>>> udfFunctions) {
this.udfFunctions.addAll(udfFunctions);
@@ -120,6 +128,7 @@ public class PostTransformOperatorBuilder {
}
public PostTransformOperator build() {
- return new PostTransformOperator(transformRules, timezone,
udfFunctions, modelClients);
+ return new PostTransformOperator(
+ transformRules, timezone, decimalPrecisionMode, udfFunctions,
modelClients);
}
}
diff --git
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/TransformFilterProcessor.java
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/TransformFilterProcessor.java
index 6781ea191..02b2c5171 100644
---
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/TransformFilterProcessor.java
+++
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/TransformFilterProcessor.java
@@ -20,6 +20,7 @@ package org.apache.flink.cdc.runtime.operators.transform;
import org.apache.flink.api.java.tuple.Tuple2;
import org.apache.flink.cdc.common.converter.JavaClassConverter;
import org.apache.flink.cdc.common.model.AiModelClient;
+import org.apache.flink.cdc.common.pipeline.DecimalPrecisionMode;
import org.apache.flink.cdc.common.schema.Column;
import org.apache.flink.cdc.common.source.SupportedMetadataColumn;
import org.apache.flink.cdc.runtime.parser.JaninoCompiler;
@@ -46,6 +47,7 @@ public class TransformFilterProcessor {
private final PostTransformChangeInfo tableInfo;
private final TransformFilter transformFilter;
private final String timezone;
+ private final DecimalPrecisionMode decimalPrecisionMode;
private final List<Object> udfFunctionInstances;
private final Map<String, SupportedMetadataColumn>
supportedMetadataColumns;
private final Map<String, AiModelClient> modelClients;
@@ -58,6 +60,7 @@ public class TransformFilterProcessor {
PostTransformChangeInfo tableInfo,
TransformFilter transformFilter,
String timezone,
+ DecimalPrecisionMode decimalPrecisionMode,
List<UserDefinedFunctionDescriptor> udfDescriptors,
List<Object> udfFunctionInstances,
Map<String, SupportedMetadataColumn> supportedMetadataColumns,
@@ -66,6 +69,7 @@ public class TransformFilterProcessor {
this.tableInfo = tableInfo;
this.transformFilter = transformFilter;
this.timezone = timezone;
+ this.decimalPrecisionMode = decimalPrecisionMode;
this.udfFunctionInstances = udfFunctionInstances;
this.supportedMetadataColumns = supportedMetadataColumns;
this.modelClients = modelClients;
@@ -87,14 +91,16 @@ public class TransformFilterProcessor {
}
}
- public static TransformFilterProcessor ofNoOp() {
- return new TransformFilterProcessor(true, null, null, null, null,
null, null, null);
+ public static TransformFilterProcessor ofNoOp(DecimalPrecisionMode
decimalPrecisionMode) {
+ return new TransformFilterProcessor(
+ true, null, null, null, decimalPrecisionMode, null, null,
null, null);
}
public static TransformFilterProcessor of(
PostTransformChangeInfo tableInfo,
TransformFilter transformFilter,
String timezone,
+ DecimalPrecisionMode decimalPrecisionMode,
List<UserDefinedFunctionDescriptor> udfDescriptors,
List<Object> udfFunctionInstances,
SupportedMetadataColumn[] supportedMetadataColumns,
@@ -109,6 +115,7 @@ public class TransformFilterProcessor {
tableInfo,
transformFilter,
timezone,
+ decimalPrecisionMode,
udfDescriptors,
udfFunctionInstances,
supportedMetadataColumnsMap,
@@ -238,7 +245,8 @@ public class TransformFilterProcessor {
columns,
udfDescriptors,
supportedMetadataColumns,
- transformFilter.getColumnNameMap());
+ transformFilter.getColumnNameMap(),
+ decimalPrecisionMode);
return TransformExpressionKey.of(
transformFilter.getExpression(),
diff --git
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/TransformProjectionProcessor.java
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/TransformProjectionProcessor.java
index 09fe5fb19..84b4d3a9a 100644
---
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/TransformProjectionProcessor.java
+++
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/operators/transform/TransformProjectionProcessor.java
@@ -18,6 +18,7 @@
package org.apache.flink.cdc.runtime.operators.transform;
import org.apache.flink.cdc.common.model.AiModelClient;
+import org.apache.flink.cdc.common.pipeline.DecimalPrecisionMode;
import org.apache.flink.cdc.common.source.SupportedMetadataColumn;
import org.apache.flink.cdc.common.utils.Preconditions;
import org.apache.flink.cdc.runtime.parser.TransformParser;
@@ -48,6 +49,7 @@ public class TransformProjectionProcessor {
private final PostTransformChangeInfo changeInfo;
private final String projectionExpression;
private final String timezone;
+ private final DecimalPrecisionMode decimalPrecisionMode;
private final List<UserDefinedFunctionDescriptor> udfDescriptors;
private final List<Object> udfFunctionInstances;
private final List<ProjectionColumnProcessor> columnProcessors;
@@ -59,6 +61,7 @@ public class TransformProjectionProcessor {
PostTransformChangeInfo changeInfo,
String projectionExpression,
String timezone,
+ DecimalPrecisionMode decimalPrecisionMode,
List<UserDefinedFunctionDescriptor> udfDescriptors,
List<Object> udfFunctionInstances,
SupportedMetadataColumn[] supportedMetadataColumns,
@@ -66,6 +69,7 @@ public class TransformProjectionProcessor {
this.changeInfo = changeInfo;
this.projectionExpression = projectionExpression;
this.timezone = timezone;
+ this.decimalPrecisionMode = decimalPrecisionMode;
this.udfDescriptors = udfDescriptors;
this.udfFunctionInstances = udfFunctionInstances;
this.supportedMetadataColumns = supportedMetadataColumns;
@@ -97,7 +101,8 @@ public class TransformProjectionProcessor {
projectionExpression,
changeInfo.getPreTransformedSchema().getColumns(),
udfDescriptors,
- supportedMetadataColumns);
+ supportedMetadataColumns,
+ decimalPrecisionMode);
List<ProjectionColumnProcessor> columnProcessors =
projectionColumns.stream()
diff --git
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/FlinkCdcTypeSystem.java
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/FlinkCdcTypeSystem.java
new file mode 100644
index 000000000..8bc0f8aae
--- /dev/null
+++
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/FlinkCdcTypeSystem.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.flink.cdc.runtime.parser;
+
+import org.apache.flink.cdc.common.pipeline.DecimalPrecisionMode;
+
+import org.apache.calcite.rel.type.RelDataTypeSystemImpl;
+
+/** A customized version of {@link
org.apache.calcite.rel.type.RelDataTypeSystem}. */
+public class FlinkCdcTypeSystem extends RelDataTypeSystemImpl {
+
+ public static final FlinkCdcTypeSystem UP_TO_38 = new
FlinkCdcTypeSystem(38);
+ public static final FlinkCdcTypeSystem UP_TO_19 = new
FlinkCdcTypeSystem(19);
+
+ private final int maxPrecision;
+
+ private FlinkCdcTypeSystem(int maxPrecision) {
+ this.maxPrecision = maxPrecision;
+ }
+
+ public static FlinkCdcTypeSystem of(DecimalPrecisionMode mode) {
+ switch (mode) {
+ case UP_TO_38:
+ return UP_TO_38;
+ case UP_TO_19:
+ return UP_TO_19;
+ default:
+ throw new IllegalArgumentException("Unexpected decimal
precision mode: " + mode);
+ }
+ }
+
+ @Override
+ public int getMaxNumericPrecision() {
+ return maxPrecision;
+ }
+
+ @Override
+ public int getMaxNumericScale() {
+ return maxPrecision;
+ }
+}
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 4caef2768..d03664a35 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
@@ -21,6 +21,7 @@ import org.apache.flink.api.common.InvalidProgramException;
import org.apache.flink.api.common.io.ParseException;
import org.apache.flink.cdc.common.annotation.VisibleForTesting;
import org.apache.flink.cdc.common.converter.JavaClassConverter;
+import org.apache.flink.cdc.common.pipeline.DecimalPrecisionMode;
import org.apache.flink.cdc.common.schema.Column;
import org.apache.flink.cdc.common.source.SupportedMetadataColumn;
import org.apache.flink.cdc.common.types.DataType;
@@ -502,7 +503,8 @@ public class JaninoCompiler {
context.columns,
sqlBasicCall,
context.udfDescriptors,
- context.supportedMetadataColumns);
+ context.supportedMetadataColumns,
+ context.decimalPrecisionMode);
return generateCollectionConstructorOperation(
context, sqlBasicCall, functionName, resultType);
}
@@ -550,7 +552,8 @@ public class JaninoCompiler {
context.columns,
operand,
context.udfDescriptors,
- context.supportedMetadataColumns);
+ context.supportedMetadataColumns,
+ context.decimalPrecisionMode);
atoms[i] = generateImplicitTypeConvertMethod(operandType,
targetType, atoms[i]);
}
}
@@ -771,7 +774,8 @@ public class JaninoCompiler {
context.columns,
sqlBasicCall,
context.udfDescriptors,
- context.supportedMetadataColumns);
+ context.supportedMetadataColumns,
+ context.decimalPrecisionMode);
if (resultType.is(DataTypeRoot.DECIMAL)) {
return new Java.MethodInvocation(Location.NOWHERE, null,
handler, atoms);
}
@@ -950,7 +954,8 @@ public class JaninoCompiler {
context.columns,
sqlBasicCall,
context.udfDescriptors,
- context.supportedMetadataColumns);
+ context.supportedMetadataColumns,
+ context.decimalPrecisionMode);
// Get the Java class for the result type and add a cast
// Use getCanonicalName() to correctly handle array types (e.g.,
byte[] instead of "[B")
@@ -1035,7 +1040,8 @@ public class JaninoCompiler {
context.columns,
sqlBasicCall,
context.udfDescriptors,
- context.supportedMetadataColumns);
+ context.supportedMetadataColumns,
+ context.decimalPrecisionMode);
Java.Rvalue[] coercedAtoms = new Java.Rvalue[atoms.length];
for (int index = 0; index < atoms.length; index++) {
coercedAtoms[index] = generateNumericTypeConvertMethod(resultType,
atoms[index]);
@@ -1058,7 +1064,8 @@ public class JaninoCompiler {
context.columns,
value,
context.udfDescriptors,
- context.supportedMetadataColumns);
+ context.supportedMetadataColumns,
+ context.decimalPrecisionMode);
return castToJavaType(resultType, operation);
}
@@ -1273,15 +1280,20 @@ public class JaninoCompiler {
// Readable metadata columns
public final SupportedMetadataColumn[] supportedMetadataColumns;
+ // Maximum precision mode for DECIMAL type evaluation
+ public final DecimalPrecisionMode decimalPrecisionMode;
+
private Context(
List<Column> columns,
Map<String, String> columnNameMap,
List<UserDefinedFunctionDescriptor> udfDescriptors,
- SupportedMetadataColumn[] supportedMetadataColumns) {
+ SupportedMetadataColumn[] supportedMetadataColumns,
+ DecimalPrecisionMode decimalPrecisionMode) {
this.columns = columns;
this.columnNameMap = columnNameMap;
this.udfDescriptors = udfDescriptors;
this.supportedMetadataColumns = supportedMetadataColumns;
+ this.decimalPrecisionMode = decimalPrecisionMode;
}
public static Context of(
@@ -1289,7 +1301,26 @@ public class JaninoCompiler {
Map<String, String> columnNameMap,
List<UserDefinedFunctionDescriptor> udfDescriptors,
SupportedMetadataColumn[] supportedMetadataColumns) {
- return new Context(columns, columnNameMap, udfDescriptors,
supportedMetadataColumns);
+ return of(
+ columns,
+ columnNameMap,
+ udfDescriptors,
+ supportedMetadataColumns,
+ DecimalPrecisionMode.UP_TO_19);
+ }
+
+ public static Context of(
+ List<Column> columns,
+ Map<String, String> columnNameMap,
+ List<UserDefinedFunctionDescriptor> udfDescriptors,
+ SupportedMetadataColumn[] supportedMetadataColumns,
+ DecimalPrecisionMode decimalPrecisionMode) {
+ return new Context(
+ columns,
+ columnNameMap,
+ udfDescriptors,
+ supportedMetadataColumns,
+ decimalPrecisionMode);
}
}
}
diff --git
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/TransformParser.java
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/TransformParser.java
index 5fcf1b608..1020a3970 100644
---
a/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/TransformParser.java
+++
b/flink-cdc-runtime/src/main/java/org/apache/flink/cdc/runtime/parser/TransformParser.java
@@ -18,6 +18,7 @@
package org.apache.flink.cdc.runtime.parser;
import org.apache.flink.api.common.io.ParseException;
+import org.apache.flink.cdc.common.pipeline.DecimalPrecisionMode;
import org.apache.flink.cdc.common.schema.Column;
import org.apache.flink.cdc.common.source.SupportedMetadataColumn;
import org.apache.flink.cdc.common.types.DataType;
@@ -41,7 +42,6 @@ import org.apache.calcite.rel.RelRoot;
import org.apache.calcite.rel.type.RelDataType;
import org.apache.calcite.rel.type.RelDataTypeFactory;
import org.apache.calcite.rel.type.RelDataTypeField;
-import org.apache.calcite.rel.type.RelDataTypeSystem;
import org.apache.calcite.rex.RexBuilder;
import org.apache.calcite.schema.ScalarFunction;
import org.apache.calcite.schema.SchemaPlus;
@@ -158,7 +158,8 @@ public class TransformParser {
List<Column> columns,
SqlNode sqlNode,
List<UserDefinedFunctionDescriptor> udfDescriptors,
- SupportedMetadataColumn[] supportedMetadataColumns) {
+ SupportedMetadataColumn[] supportedMetadataColumns,
+ DecimalPrecisionMode decimalPrecisionMode) {
List<Column> columnsWithMetadata =
copyFillMetadataColumn(columns, supportedMetadataColumns);
CalciteSchema rootSchema = CalciteSchema.createRootSchema(true);
@@ -202,7 +203,8 @@ public class TransformParser {
throw new RuntimeException("Failed to resolve UDF: " + udf, e);
}
}
- SqlTypeFactoryImpl factory = new
SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+ SqlTypeFactoryImpl factory =
+ new
SqlTypeFactoryImpl(FlinkCdcTypeSystem.of(decimalPrecisionMode));
CalciteCatalogReader calciteCatalogReader =
new CalciteCatalogReader(
rootSchema,
@@ -328,6 +330,20 @@ public class TransformParser {
List<Column> columns,
List<UserDefinedFunctionDescriptor> udfDescriptors,
SupportedMetadataColumn[] supportedMetadataColumns) {
+ return generateProjectionColumns(
+ projectionExpression,
+ columns,
+ udfDescriptors,
+ supportedMetadataColumns,
+ DecimalPrecisionMode.UP_TO_19);
+ }
+
+ public static List<ProjectionColumn> generateProjectionColumns(
+ String projectionExpression,
+ List<Column> columns,
+ List<UserDefinedFunctionDescriptor> udfDescriptors,
+ SupportedMetadataColumn[] supportedMetadataColumns,
+ DecimalPrecisionMode decimalPrecisionMode) {
if (isNullOrWhitespaceOnly(projectionExpression)) {
return new ArrayList<>();
}
@@ -345,7 +361,8 @@ public class TransformParser {
originalColumnMap,
sqlSelect,
udfDescriptors,
- supportedMetadataColumns);
+ supportedMetadataColumns,
+ decimalPrecisionMode);
List<ProjectionColumn> projectionColumns = new ArrayList<>();
Map<String, Integer> addedProjectionColumnNames = new HashMap<>();
@@ -415,7 +432,8 @@ public class TransformParser {
columns,
columnNameMap,
udfDescriptors,
- supportedMetadataColumns),
+ supportedMetadataColumns,
+ decimalPrecisionMode),
exprNode),
originalColumnNames,
columnNameMap);
@@ -455,10 +473,16 @@ public class TransformParser {
Map<String, Column> originalColumnMap,
SqlSelect sqlSelect,
List<UserDefinedFunctionDescriptor> udfDescriptors,
- SupportedMetadataColumn[] supportedMetadataColumns) {
+ SupportedMetadataColumn[] supportedMetadataColumns,
+ DecimalPrecisionMode decimalPrecisionMode) {
try {
RelNode relNode =
- sqlToRel(columns, sqlSelect, udfDescriptors,
supportedMetadataColumns);
+ sqlToRel(
+ columns,
+ sqlSelect,
+ udfDescriptors,
+ supportedMetadataColumns,
+ decimalPrecisionMode);
return relNode.getRowType().getFieldList().stream()
.map(RelDataTypeField::getType)
.toArray(RelDataType[]::new);
@@ -467,7 +491,8 @@ public class TransformParser {
// Keep Calcite as the primary type inference path. This
fallback only covers
// transform predicates that Janino can evaluate but Calcite
may fail to convert
// while building projection columns.
- SqlTypeFactoryImpl typeFactory = new
SqlTypeFactoryImpl(RelDataTypeSystem.DEFAULT);
+ SqlTypeFactoryImpl typeFactory =
+ new
SqlTypeFactoryImpl(FlinkCdcTypeSystem.of(decimalPrecisionMode));
List<RelDataType> relDataTypes = new ArrayList<>();
for (SqlNode sqlNode : sqlSelect.getSelectList()) {
relDataTypes.add(
@@ -477,7 +502,8 @@ public class TransformParser {
originalColumnMap,
unwrapAsExpression(sqlNode),
udfDescriptors,
- supportedMetadataColumns));
+ supportedMetadataColumns,
+ decimalPrecisionMode));
}
return relDataTypes.toArray(new RelDataType[0]);
} catch (RuntimeException fallbackException) {
@@ -504,7 +530,8 @@ public class TransformParser {
Map<String, Column> originalColumnMap,
SqlNode exprNode,
List<UserDefinedFunctionDescriptor> udfDescriptors,
- SupportedMetadataColumn[] supportedMetadataColumns) {
+ SupportedMetadataColumn[] supportedMetadataColumns,
+ DecimalPrecisionMode decimalPrecisionMode) {
if (exprNode instanceof SqlIdentifier) {
String columnName =
((SqlIdentifier) exprNode)
@@ -522,7 +549,11 @@ public class TransformParser {
return toRelDataType(
typeFactory,
deduceSubExpressionType(
- columns, exprNode, udfDescriptors,
supportedMetadataColumns));
+ columns,
+ exprNode,
+ udfDescriptors,
+ supportedMetadataColumns,
+ decimalPrecisionMode));
}
private static DataType findIdentifierDataType(
@@ -635,6 +666,22 @@ public class TransformParser {
List<UserDefinedFunctionDescriptor> udfDescriptors,
SupportedMetadataColumn[] supportedMetadataColumns,
Map<String, String> columnNameMap) {
+ return translateFilterExpressionToJaninoExpression(
+ filterExpression,
+ columns,
+ udfDescriptors,
+ supportedMetadataColumns,
+ columnNameMap,
+ DecimalPrecisionMode.UP_TO_19);
+ }
+
+ public static String translateFilterExpressionToJaninoExpression(
+ String filterExpression,
+ List<Column> columns,
+ List<UserDefinedFunctionDescriptor> udfDescriptors,
+ SupportedMetadataColumn[] supportedMetadataColumns,
+ Map<String, String> columnNameMap,
+ DecimalPrecisionMode decimalPrecisionMode) {
if (isNullOrWhitespaceOnly(filterExpression)) {
return "";
}
@@ -645,7 +692,11 @@ public class TransformParser {
SqlNode where = sqlSelect.getWhere();
return JaninoCompiler.translateSqlNodeToJaninoExpression(
JaninoCompiler.Context.of(
- columns, columnNameMap, udfDescriptors,
supportedMetadataColumns),
+ columns,
+ columnNameMap,
+ udfDescriptors,
+ supportedMetadataColumns,
+ decimalPrecisionMode),
where);
}
@@ -863,6 +914,20 @@ public class TransformParser {
SqlNode subExpression,
List<UserDefinedFunctionDescriptor> udfDescriptors,
SupportedMetadataColumn[] supportedMetadataColumns) {
+ return deduceSubExpressionType(
+ columns,
+ subExpression,
+ udfDescriptors,
+ supportedMetadataColumns,
+ DecimalPrecisionMode.UP_TO_19);
+ }
+
+ public static DataType deduceSubExpressionType(
+ List<Column> columns,
+ SqlNode subExpression,
+ List<UserDefinedFunctionDescriptor> udfDescriptors,
+ SupportedMetadataColumn[] supportedMetadataColumns,
+ DecimalPrecisionMode decimalPrecisionMode) {
SqlSelect sqlSelect =
new SqlSelect(
SqlParserPos.QUOTED_ZERO,
@@ -877,7 +942,13 @@ public class TransformParser {
null,
null,
null);
- RelNode relNode = sqlToRel(columns, sqlSelect, udfDescriptors,
supportedMetadataColumns);
+ RelNode relNode =
+ sqlToRel(
+ columns,
+ sqlSelect,
+ udfDescriptors,
+ supportedMetadataColumns,
+ decimalPrecisionMode);
RelDataType[] relDataTypes =
relNode.getRowType().getFieldList().stream()
.map(RelDataTypeField::getType)
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 621188bbb..16136323f 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
@@ -18,6 +18,7 @@
package org.apache.flink.cdc.runtime.parser;
import org.apache.flink.api.common.io.ParseException;
+import org.apache.flink.cdc.common.pipeline.DecimalPrecisionMode;
import org.apache.flink.cdc.common.schema.Column;
import org.apache.flink.cdc.common.schema.Schema;
import org.apache.flink.cdc.common.source.SupportedMetadataColumn;
@@ -1092,6 +1093,39 @@ class TransformParserTest {
Assertions.assertThat(result).hasToString("[" + String.join(", ",
expected) + "]");
}
+ @Test
+ void testGenerateProjectionColumnsWithDecimalPrecisionMode() {
+ List<Column> columns =
+ Arrays.asList(
+ Column.physicalColumn("id", DataTypes.INT()),
+ Column.physicalColumn("deposit", DataTypes.DECIMAL(20,
2)));
+ String projection =
+ "deposit + CAST(1 AS DECIMAL(1, 0)) AS amount, "
+ + "id IS DISTINCT FROM 1 AS changed";
+
+ List<ProjectionColumn> upTo19 =
+ TransformParser.generateProjectionColumns(
+ projection,
+ columns,
+ Collections.emptyList(),
+ new SupportedMetadataColumn[0],
+ DecimalPrecisionMode.UP_TO_19);
+ List<ProjectionColumn> upTo38 =
+ TransformParser.generateProjectionColumns(
+ projection,
+ columns,
+ Collections.emptyList(),
+ new SupportedMetadataColumn[0],
+ DecimalPrecisionMode.UP_TO_38);
+
+ Assertions.assertThat(upTo19)
+ .extracting(ProjectionColumn::getDataType)
+ .containsExactly(DataTypes.DECIMAL(19, 2),
DataTypes.BOOLEAN());
+ Assertions.assertThat(upTo38)
+ .extracting(ProjectionColumn::getDataType)
+ .containsExactly(DataTypes.DECIMAL(21, 2),
DataTypes.BOOLEAN());
+ }
+
@Test
void testGenerateReferencedColumns() {
List<Column> testColumns =