-
Notifications
You must be signed in to change notification settings - Fork 3.7k
[Improve](streaming job) support custom table name mapping for CDC streaming job #61317
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,11 +43,37 @@ public class DataSourceConfigValidator { | |
| DataSourceConfigKeys.SSL_ROOTCERT | ||
| ); | ||
|
|
||
| // Known suffixes for per-table config keys (format: "table.<tableName>.<suffix>") | ||
| private static final Set<String> ALLOW_TABLE_LEVEL_SUFFIXES = Sets.newHashSet( | ||
| DataSourceConfigKeys.TABLE_TARGET_TABLE_SUFFIX | ||
JNSimba marked this conversation as resolved.
Show resolved
Hide resolved
JNSimba marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ); | ||
|
|
||
| private static final String TABLE_LEVEL_PREFIX = DataSourceConfigKeys.TABLE + "."; | ||
|
|
||
| public static void validateSource(Map<String, String> input) throws IllegalArgumentException { | ||
| for (Map.Entry<String, String> entry : input.entrySet()) { | ||
| String key = entry.getKey(); | ||
| String value = entry.getValue(); | ||
|
|
||
| if (key.startsWith(TABLE_LEVEL_PREFIX)) { | ||
| // per-table config key must be exactly: table.<tableName>.<suffix> | ||
| // reject malformed keys like "table.exclude_columns" (missing tableName) | ||
| String[] parts = key.split("\\.", -1); | ||
| if (parts.length != 3 || parts[1].isEmpty()) { | ||
| throw new IllegalArgumentException("Malformed per-table config key: '" + key | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Low] The This is a minor issue in practice (PG/MySQL identifiers rarely contain dots), but worth noting as a design inconsistency. Consider aligning both sides to use the same parsing strategy — the prefix/suffix approach is strictly more correct. |
||
| + "'. Expected format: table.<tableName>.<suffix>"); | ||
| } | ||
| String suffix = parts[parts.length - 1]; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Minor] Table names with dots will be rejected: Consider using int firstDot = key.indexOf('.', TABLE_LEVEL_PREFIX.length());
int lastDot = key.lastIndexOf('.');
if (firstDot == -1 || firstDot != lastDot - ???) { ... }Or at minimum, document this limitation (no dots in source table names). |
||
| if (!ALLOW_TABLE_LEVEL_SUFFIXES.contains(suffix)) { | ||
| throw new IllegalArgumentException("Unknown per-table config key: '" + key + "'"); | ||
| } | ||
JNSimba marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if (value == null || value.trim().isEmpty()) { | ||
| throw new IllegalArgumentException( | ||
| "Value for per-table config key '" + key + "' must not be empty"); | ||
| } | ||
| continue; | ||
JNSimba marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| if (!ALLOW_SOURCE_KEYS.contains(key)) { | ||
| throw new IllegalArgumentException("Unexpected key: '" + key + "'"); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -275,10 +275,20 @@ public static Map<String, String> convertCertFile(long dbId, Map<String, String> | |
| return newProps; | ||
| } | ||
|
|
||
| public static List<CreateTableCommand> generateCreateTableCmds(String targetDb, DataSourceType sourceType, | ||
| /** | ||
| * Generate CREATE TABLE commands for the Doris target tables. | ||
| * | ||
| * <p>Returns a {@link LinkedHashMap} whose key is the <b>source</b> (upstream) table name and | ||
| * whose value is the corresponding {@link CreateTableCommand} that creates the Doris target | ||
| * table (which may have a different name when {@code table.<src>.target_table} is configured). | ||
| * Callers must use the map key as the PG/MySQL source table identifier for CDC monitoring and | ||
| * the {@link CreateTableCommand} value for the actual DDL execution. | ||
| */ | ||
| public static LinkedHashMap<String, CreateTableCommand> generateCreateTableCmds(String targetDb, | ||
| DataSourceType sourceType, | ||
| Map<String, String> properties, Map<String, String> targetProperties) | ||
| throws JobException { | ||
| List<CreateTableCommand> createtblCmds = new ArrayList<>(); | ||
| LinkedHashMap<String, CreateTableCommand> createtblCmds = new LinkedHashMap<>(); | ||
| String includeTables = properties.get(DataSourceConfigKeys.INCLUDE_TABLES); | ||
| String excludeTables = properties.get(DataSourceConfigKeys.EXCLUDE_TABLES); | ||
| List<String> includeTablesList = new ArrayList<>(); | ||
|
|
@@ -319,6 +329,13 @@ public static List<CreateTableCommand> generateCreateTableCmds(String targetDb, | |
| if (primaryKeys.isEmpty()) { | ||
| noPrimaryKeyTables.add(table); | ||
| } | ||
|
|
||
| // Resolve target (Doris) table name; defaults to source table name if not configured | ||
| String targetTableName = properties.getOrDefault( | ||
| DataSourceConfigKeys.TABLE + "." + table + "." | ||
| + DataSourceConfigKeys.TABLE_TARGET_TABLE_SUFFIX, | ||
| table); | ||
|
|
||
| // Convert Column to ColumnDefinition | ||
| List<ColumnDefinition> columnDefinitions = columns.stream().map(col -> { | ||
| DataType dataType = DataType.fromCatalogType(col.getType()); | ||
|
|
@@ -340,7 +357,7 @@ public static List<CreateTableCommand> generateCreateTableCmds(String targetDb, | |
| false, // isTemp | ||
| InternalCatalog.INTERNAL_CATALOG_NAME, // ctlName | ||
| targetDb, // dbName | ||
| table, // tableName | ||
| targetTableName, // tableName | ||
| columnDefinitions, // columns | ||
| ImmutableList.of(), // indexes | ||
| "olap", // engineName | ||
|
|
@@ -355,7 +372,8 @@ public static List<CreateTableCommand> generateCreateTableCmds(String targetDb, | |
| ImmutableList.of() // clusterKeyColumnNames | ||
| ); | ||
| CreateTableCommand createtblCmd = new CreateTableCommand(Optional.empty(), createtblInfo); | ||
| createtblCmds.add(createtblCmd); | ||
| // Key: source (PG/MySQL) table name; Value: command that creates the Doris target table | ||
| createtblCmds.put(table, createtblCmd); | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Medium] Multi-table merge schema safety: When two source tables map to the same Doris target table, this This means the Doris target table is created with only the first source table's schema. If the two source tables have different columns, data from the second source may fail at stream-load time. Consider either:
The current test works because both PG source tables have identical schemas |
||
| if (createtblCmds.isEmpty()) { | ||
| throw new JobException("Can not found match table in database " + database); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| -- This file is automatically generated. You should know what you did if you want to edit this | ||
| -- !select_snapshot -- | ||
| 1 Alice | ||
| 2 Bob | ||
|
|
||
| -- !select_incremental -- | ||
| 2 Bob_v2 | ||
| 3 Carol | ||
|
|
||
| -- !select_merge_snapshot -- | ||
| 100 Src1_A | ||
| 200 Src2_A | ||
|
|
||
| -- !select_merge_incremental -- | ||
| 100 Src1_A | ||
| 101 Src1_B | ||
| 200 Src2_A | ||
| 201 Src2_B | ||
|
|
Uh oh!
There was an error while loading. Please reload this page.