|
18 | 18 | package org.apache.flink.cdc.composer.flink; |
19 | 19 |
|
20 | 20 | import org.apache.flink.cdc.common.configuration.Configuration; |
| 21 | +import org.apache.flink.cdc.common.data.DecimalData; |
21 | 22 | import org.apache.flink.cdc.common.data.LocalZonedTimestampData; |
22 | 23 | import org.apache.flink.cdc.common.data.TimestampData; |
23 | 24 | import org.apache.flink.cdc.common.data.ZonedTimestampData; |
|
61 | 62 |
|
62 | 63 | import java.io.ByteArrayOutputStream; |
63 | 64 | import java.io.PrintStream; |
| 65 | +import java.math.BigDecimal; |
64 | 66 | import java.time.Instant; |
65 | 67 | import java.time.LocalDateTime; |
66 | 68 | import java.time.ZoneId; |
@@ -1216,6 +1218,88 @@ void testMergingTemporalTypesWithPromotedPrecisions(ValuesDataSink.SinkApi sinkA |
1216 | 1218 | assertThat(outputEvents).containsExactlyInAnyOrder(expected); |
1217 | 1219 | } |
1218 | 1220 |
|
| 1221 | + @ParameterizedTest |
| 1222 | + @EnumSource |
| 1223 | + void testMergingDecimalWithVariousPrecisions(ValuesDataSink.SinkApi sinkApi) throws Exception { |
| 1224 | + FlinkPipelineComposer composer = FlinkPipelineComposer.ofMiniCluster(); |
| 1225 | + |
| 1226 | + // Setup value source |
| 1227 | + Configuration sourceConfig = new Configuration(); |
| 1228 | + sourceConfig.set( |
| 1229 | + ValuesDataSourceOptions.EVENT_SET_ID, |
| 1230 | + ValuesDataSourceHelper.EventSetId.CUSTOM_SOURCE_EVENTS); |
| 1231 | + |
| 1232 | + List<Event> events = generateDecimalColumnEvents("default_table_"); |
| 1233 | + ValuesDataSourceHelper.setSourceEvents(Collections.singletonList(events)); |
| 1234 | + |
| 1235 | + SourceDef sourceDef = |
| 1236 | + new SourceDef(ValuesDataFactory.IDENTIFIER, "Value Source", sourceConfig); |
| 1237 | + |
| 1238 | + // Setup value sink |
| 1239 | + Configuration sinkConfig = new Configuration(); |
| 1240 | + sinkConfig.set(ValuesDataSinkOptions.SINK_API, sinkApi); |
| 1241 | + SinkDef sinkDef = new SinkDef(ValuesDataFactory.IDENTIFIER, "Value Sink", sinkConfig); |
| 1242 | + |
| 1243 | + // Setup pipeline |
| 1244 | + Configuration pipelineConfig = new Configuration(); |
| 1245 | + pipelineConfig.set(PipelineOptions.PIPELINE_PARALLELISM, 1); |
| 1246 | + pipelineConfig.set( |
| 1247 | + PipelineOptions.PIPELINE_SCHEMA_CHANGE_BEHAVIOR, SchemaChangeBehavior.EVOLVE); |
| 1248 | + PipelineDef pipelineDef = |
| 1249 | + new PipelineDef( |
| 1250 | + sourceDef, |
| 1251 | + sinkDef, |
| 1252 | + Collections.singletonList( |
| 1253 | + new RouteDef( |
| 1254 | + "default_namespace.default_schema.default_table_\\.*", |
| 1255 | + "default_namespace.default_schema.default_everything_merged", |
| 1256 | + null, |
| 1257 | + "Merge all decimal columns with different precision")), |
| 1258 | + Collections.emptyList(), |
| 1259 | + Collections.emptyList(), |
| 1260 | + pipelineConfig); |
| 1261 | + |
| 1262 | + // Execute the pipeline |
| 1263 | + PipelineExecution execution = composer.compose(pipelineDef); |
| 1264 | + |
| 1265 | + execution.execute(); |
| 1266 | + |
| 1267 | + // Check the order and content of all received events |
| 1268 | + String[] outputEvents = outCaptor.toString().trim().split("\n"); |
| 1269 | + |
| 1270 | + String[] expected = |
| 1271 | + Stream.of( |
| 1272 | + "CreateTableEvent{tableId={}, schema=columns={`id` INT,`name` STRING,`age` INT,`fav_num` TINYINT}, primaryKeys=id, options=()}", |
| 1273 | + "DataChangeEvent{tableId={}, before=[], after=[1, Alice, 17, 1], op=INSERT, meta=()}", |
| 1274 | + "AlterColumnTypeEvent{tableId={}, typeMapping={fav_num=BIGINT}, oldTypeMapping={fav_num=TINYINT}}", |
| 1275 | + "DataChangeEvent{tableId={}, before=[], after=[2, Alice, 17, 22], op=INSERT, meta=()}", |
| 1276 | + "DataChangeEvent{tableId={}, before=[], after=[3, Alice, 17, 3333], op=INSERT, meta=()}", |
| 1277 | + "DataChangeEvent{tableId={}, before=[], after=[4, Alice, 17, 44444444], op=INSERT, meta=()}", |
| 1278 | + "AlterColumnTypeEvent{tableId={}, typeMapping={fav_num=DECIMAL(19, 0)}, oldTypeMapping={fav_num=BIGINT}}", |
| 1279 | + "DataChangeEvent{tableId={}, before=[], after=[5, Alice, 17, 555555555555555], op=INSERT, meta=()}", |
| 1280 | + "AlterColumnTypeEvent{tableId={}, typeMapping={fav_num=DECIMAL(24, 5)}, oldTypeMapping={fav_num=DECIMAL(19, 0)}}", |
| 1281 | + "DataChangeEvent{tableId={}, before=[], after=[6, Alice, 17, 66666.66666], op=INSERT, meta=()}", |
| 1282 | + "DataChangeEvent{tableId={}, before=[], after=[7, Alice, 17, 77777777.17000], op=INSERT, meta=()}", |
| 1283 | + "AlterColumnTypeEvent{tableId={}, typeMapping={fav_num=DECIMAL(38, 19)}, oldTypeMapping={fav_num=DECIMAL(24, 5)}}", |
| 1284 | + "DataChangeEvent{tableId={}, before=[], after=[8, Alice, 17, 888888888.8888888888888888888], op=INSERT, meta=()}", |
| 1285 | + "DataChangeEvent{tableId={}, before=[], after=[101, Zen, 19, 1.0000000000000000000], op=INSERT, meta=()}", |
| 1286 | + "DataChangeEvent{tableId={}, before=[], after=[102, Zen, 19, 22.0000000000000000000], op=INSERT, meta=()}", |
| 1287 | + "DataChangeEvent{tableId={}, before=[], after=[103, Zen, 19, 3333.0000000000000000000], op=INSERT, meta=()}", |
| 1288 | + "DataChangeEvent{tableId={}, before=[], after=[104, Zen, 19, 44444444.0000000000000000000], op=INSERT, meta=()}", |
| 1289 | + "DataChangeEvent{tableId={}, before=[], after=[105, Zen, 19, 555555555555555.0000000000000000000], op=INSERT, meta=()}", |
| 1290 | + "DataChangeEvent{tableId={}, before=[], after=[106, Zen, 19, 66666.6666600000000000000], op=INSERT, meta=()}", |
| 1291 | + "DataChangeEvent{tableId={}, before=[], after=[107, Zen, 19, 77777777.1700000000000000000], op=INSERT, meta=()}", |
| 1292 | + "DataChangeEvent{tableId={}, before=[], after=[108, Zen, 19, 888888888.8888888888888888888], op=INSERT, meta=()}") |
| 1293 | + .map( |
| 1294 | + s -> |
| 1295 | + s.replace( |
| 1296 | + "{}", |
| 1297 | + "default_namespace.default_schema.default_everything_merged")) |
| 1298 | + .toArray(String[]::new); |
| 1299 | + |
| 1300 | + assertThat(outputEvents).containsExactlyInAnyOrder(expected); |
| 1301 | + } |
| 1302 | + |
1219 | 1303 | private List<Event> generateTemporalColumnEvents(String tableNamePrefix) { |
1220 | 1304 | List<Event> events = new ArrayList<>(); |
1221 | 1305 |
|
@@ -1286,6 +1370,83 @@ private List<Event> generateTemporalColumnEvents(String tableNamePrefix) { |
1286 | 1370 | return events; |
1287 | 1371 | } |
1288 | 1372 |
|
| 1373 | + private List<Event> generateDecimalColumnEvents(String tableNamePrefix) { |
| 1374 | + List<Event> events = new ArrayList<>(); |
| 1375 | + |
| 1376 | + // Initialize schemas |
| 1377 | + List<String> names = |
| 1378 | + Arrays.asList( |
| 1379 | + "tiny", |
| 1380 | + "small", |
| 1381 | + "vanilla", |
| 1382 | + "big", |
| 1383 | + "dec_15_0", |
| 1384 | + "decimal_10_10", |
| 1385 | + "decimal_16_2", |
| 1386 | + "decimal_29_19"); |
| 1387 | + |
| 1388 | + List<DataType> types = |
| 1389 | + Arrays.asList( |
| 1390 | + DataTypes.TINYINT(), |
| 1391 | + DataTypes.SMALLINT(), |
| 1392 | + DataTypes.INT(), |
| 1393 | + DataTypes.BIGINT(), |
| 1394 | + DataTypes.DECIMAL(15, 0), |
| 1395 | + DataTypes.DECIMAL(10, 5), |
| 1396 | + DataTypes.DECIMAL(16, 2), |
| 1397 | + DataTypes.DECIMAL(29, 19)); |
| 1398 | + |
| 1399 | + List<Object> values = |
| 1400 | + Arrays.asList( |
| 1401 | + (byte) 1, |
| 1402 | + (short) 22, |
| 1403 | + 3333, |
| 1404 | + (long) 44444444, |
| 1405 | + DecimalData.fromBigDecimal(new BigDecimal("555555555555555"), 15, 0), |
| 1406 | + DecimalData.fromBigDecimal(new BigDecimal("66666.66666"), 10, 5), |
| 1407 | + DecimalData.fromBigDecimal(new BigDecimal("77777777.17"), 16, 2), |
| 1408 | + DecimalData.fromBigDecimal( |
| 1409 | + new BigDecimal("888888888.8888888888888888888"), 29, 19)); |
| 1410 | + |
| 1411 | + List<Schema> schemas = |
| 1412 | + types.stream() |
| 1413 | + .map( |
| 1414 | + temporalColumnType -> |
| 1415 | + Schema.newBuilder() |
| 1416 | + .physicalColumn("id", DataTypes.INT()) |
| 1417 | + .physicalColumn("name", DataTypes.STRING()) |
| 1418 | + .physicalColumn("age", DataTypes.INT()) |
| 1419 | + .physicalColumn("fav_num", temporalColumnType) |
| 1420 | + .primaryKey("id") |
| 1421 | + .build()) |
| 1422 | + .collect(Collectors.toList()); |
| 1423 | + |
| 1424 | + for (int i = 0; i < names.size(); i++) { |
| 1425 | + TableId generatedTableId = |
| 1426 | + TableId.tableId( |
| 1427 | + "default_namespace", "default_schema", tableNamePrefix + names.get(i)); |
| 1428 | + Schema generatedSchema = schemas.get(i); |
| 1429 | + events.add(new CreateTableEvent(generatedTableId, generatedSchema)); |
| 1430 | + events.add( |
| 1431 | + DataChangeEvent.insertEvent( |
| 1432 | + generatedTableId, |
| 1433 | + generate(generatedSchema, 1 + i, "Alice", 17, values.get(i)))); |
| 1434 | + } |
| 1435 | + |
| 1436 | + for (int i = 0; i < names.size(); i++) { |
| 1437 | + TableId generatedTableId = |
| 1438 | + TableId.tableId( |
| 1439 | + "default_namespace", "default_schema", tableNamePrefix + names.get(i)); |
| 1440 | + Schema generatedSchema = schemas.get(i); |
| 1441 | + events.add( |
| 1442 | + DataChangeEvent.insertEvent( |
| 1443 | + generatedTableId, |
| 1444 | + generate(generatedSchema, 101 + i, "Zen", 19, values.get(i)))); |
| 1445 | + } |
| 1446 | + |
| 1447 | + return events; |
| 1448 | + } |
| 1449 | + |
1289 | 1450 | BinaryRecordData generate(Schema schema, Object... fields) { |
1290 | 1451 | return (new BinaryRecordDataGenerator(schema.getColumnDataTypes().toArray(new DataType[0]))) |
1291 | 1452 | .generate( |
|
0 commit comments