github-actions[bot] commented on code in PR #65955:
URL: https://github.com/apache/doris/pull/65955#discussion_r3639164831
##########
fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/AbstractPaimonProperties.java:
##########
@@ -121,6 +140,90 @@ public Map<String, String> getCatalogOptionsMap() {
}
}
+ public Map<String, String> getTableOptionsMap() {
+ return tableOptionsMap;
+ }
+
+ public static boolean isTableOptionProperty(String key) {
+ return key.toLowerCase(Locale.ROOT).startsWith(TABLE_OPTION_PREFIX);
+ }
+
+ private Map<String, String> extractTableOptions() {
+ Map<String, String> tableOptions = new LinkedHashMap<>();
+ origProps.forEach((key, value) -> {
+ if (isTableOptionProperty(key)) {
+ String tableOptionKey =
key.substring(TABLE_OPTION_PREFIX.length());
+ if (StringUtils.isBlank(tableOptionKey)) {
+ throw new IllegalArgumentException(
+ "Paimon table option name must not be empty after
prefix " + TABLE_OPTION_PREFIX);
+ }
+ validateTableOption(tableOptionKey, value);
+ tableOptions.put(tableOptionKey, value);
+ }
+ });
+ return Collections.unmodifiableMap(tableOptions);
+ }
+
+ private void validateTableOption(String key, String value) {
+ ConfigOption<?> option = SUPPORTED_TABLE_OPTIONS.find(key);
+ if (option == null) {
+ throw new IllegalArgumentException("Unsupported Paimon table
option '" + key
+ + "' for the bundled Paimon version");
+ }
+
+ try {
+ new Options(Collections.singletonMap(key, value)).get(option);
Review Comment:
[P1] Reject a zero Paimon read batch before scanning
`Options.get()` checks only that this value is an integer, so
`paimon.table-option.read.batch-size=0` is accepted and the new `putIfAbsent`
path preserves it. In bundled Paimon 1.3.1, the Parquet reader then computes a
zero-row batch, returns success without advancing `rowsReturned`, and repeats
that result. Doris consumes the empty iterator and immediately calls
`readBatch()` again, so a nonempty Parquet scan spins indefinitely. Please
require `read.batch-size > 0` during CREATE/ALTER validation and cover
zero/non-positive values with a reader-progress regression.
##########
fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/AbstractPaimonProperties.java:
##########
@@ -121,6 +140,90 @@ public Map<String, String> getCatalogOptionsMap() {
}
}
+ public Map<String, String> getTableOptionsMap() {
+ return tableOptionsMap;
+ }
+
+ public static boolean isTableOptionProperty(String key) {
+ return key.toLowerCase(Locale.ROOT).startsWith(TABLE_OPTION_PREFIX);
+ }
+
+ private Map<String, String> extractTableOptions() {
+ Map<String, String> tableOptions = new LinkedHashMap<>();
+ origProps.forEach((key, value) -> {
+ if (isTableOptionProperty(key)) {
+ String tableOptionKey =
key.substring(TABLE_OPTION_PREFIX.length());
+ if (StringUtils.isBlank(tableOptionKey)) {
+ throw new IllegalArgumentException(
+ "Paimon table option name must not be empty after
prefix " + TABLE_OPTION_PREFIX);
+ }
+ validateTableOption(tableOptionKey, value);
+ tableOptions.put(tableOptionKey, value);
+ }
+ });
+ return Collections.unmodifiableMap(tableOptions);
+ }
+
+ private void validateTableOption(String key, String value) {
+ ConfigOption<?> option = SUPPORTED_TABLE_OPTIONS.find(key);
+ if (option == null) {
+ throw new IllegalArgumentException("Unsupported Paimon table
option '" + key
+ + "' for the bundled Paimon version");
+ }
+
+ try {
+ new Options(Collections.singletonMap(key, value)).get(option);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Invalid value for Paimon table
option '" + key + "': "
+ + e.getMessage(), e);
+ }
+ }
+
+ private static final class SupportedTableOptions {
+ private final Map<String, ConfigOption<?>> exactOptions;
+ private final Map<String, ConfigOption<?>> prefixOptions;
+
+ private SupportedTableOptions(Map<String, ConfigOption<?>>
exactOptions,
+ Map<String, ConfigOption<?>> prefixOptions) {
+ this.exactOptions = exactOptions;
+ this.prefixOptions = prefixOptions;
+ }
+
+ private static SupportedTableOptions build() {
+ Map<String, ConfigOption<?>> exactOptions = new HashMap<>();
+ Map<String, ConfigOption<?>> prefixOptions = new HashMap<>();
+ for (ConfigOption<?> option : CoreOptions.getOptions()) {
Review Comment:
[P1] Only admit options legal for Paimon dynamic copies
`CoreOptions.getOptions()` includes `bucket` and other immutable table
options, so for example `paimon.table-option.bucket=4` passes this validator.
On first table load, `PaimonExternalCatalog` calls `table.copy(tableOptions)`;
bundled Paimon 1.3.1 rejects changed `bucket` in dynamic options and rejects
its immutable option set. Catalog creation can therefore succeed while a table
with a different stored value fails metadata/query access. Please build the
allow-list from options Paimon permits in `Table.copy` (canonicalizing
fallbacks), or reject immutable/bucket options during CREATE/ALTER, and add an
end-to-end negative test.
##########
be/src/exec/operator/file_scan_operator.cpp:
##########
@@ -116,14 +116,9 @@ bool FileScanLocalState::_should_use_file_scanner_v2(const
TQueryOptions& query_
const bool is_transactional_hive =
scan_params.__isset.table_format_params &&
scan_params.table_format_params.table_format_type ==
"transactional_hive";
- // JNI reader selection is stored per split, but this scan-level selector
cannot inspect the
- // split yet. Older FEs may omit both the scan-level Paimon marker and
split-level reader_type,
- // so keep JNI scans on V1 until scanner selection can distinguish every
compatibility shape.
return query_options.__isset.enable_file_scanner_v2 &&
query_options.enable_file_scanner_v2 &&
!is_load && scan_params.format_type != TFileFormatType::FORMAT_WAL
&&
- scan_params.format_type != TFileFormatType::FORMAT_ES_HTTP &&
- scan_params.format_type != TFileFormatType::FORMAT_LANCE &&
- scan_params.format_type != TFileFormatType::FORMAT_JNI &&
!is_transactional_hive;
+ scan_params.format_type != TFileFormatType::FORMAT_ES_HTTP &&
!is_transactional_hive;
Review Comment:
[P1] Keep JNI and Lance on V1 until V2 is semantically ready
This scan-level switch activates V2 for every eligible JNI/Lance scan, but
both capability and residual semantics are incomplete. V2 has no Lance case,
rejects LakeSoul, Paimon `PAIMON_CPP`, and legacy Paimon without `reader_type`,
then returns `NotSupported` with no V1 fallback; unchanged selector tests at
`file_scanner_v2_test.cpp:420-461` still require those shapes to stay on V1.
Even admitted JNI readers clone every scanner conjunct and filter it in
`finalize_jni_block()`, after which `Scanner::_filter_output_block()` evaluates
the original again, so a non-deterministic predicate such as `rand()` is
applied twice (unlike V1). Please keep JNI/Lance on V1 until selection is
capability-gated and the JNI residual path executes each predicate once, with
tests for unsupported shapes and non-deterministic predicates.
##########
fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/source/PaimonScanNode.java:
##########
@@ -93,9 +93,9 @@ public class PaimonScanNode extends FileQueryScanNode {
private static final String DORIS_INCREMENTAL_BETWEEN_SCAN_MODE =
"incrementalBetweenScanMode";
private static final String PAIMON_PROPERTY_PREFIX = "paimon.";
private static final String DORIS_ENABLE_FILE_READER_ASYNC =
"jni.enable_file_reader_async";
- private static final String DORIS_ENABLE_JNI_IO_MANAGER =
"doris.enable_jni_io_manager";
- private static final String DORIS_JNI_IO_MANAGER_TMP_DIR =
"doris.jni_io_manager.tmp_dir";
- private static final String DORIS_JNI_IO_MANAGER_IMPL_CLASS =
"doris.jni_io_manager.impl_class";
+ private static final String DORIS_ENABLE_JNI_IO_MANAGER =
"jni.enable_jni_io_manager";
Review Comment:
[P1] Preserve the legacy IO-manager keys across upgrades
These names are persisted catalog properties and cross-version protocol
inputs, not internal-only constants. Existing catalogs store `paimon.doris.*`,
but the new FE now searches only `paimon.jni.*`, so the option disappears and
the Java scanner silently defaults the IO manager to disabled. Old-FE/new-BE
and new-FE/old-BE pairs have the same inverse spelling mismatch. Please accept
both namespaces for a compatibility window, define new-key precedence,
normalize at FE/BE/Java boundaries, and retain old-catalog plus mixed-version
tests.
##########
fe/fe-core/src/main/java/org/apache/doris/datasource/paimon/PaimonExternalCatalog.java:
##########
@@ -134,7 +134,11 @@ public org.apache.paimon.table.Table
getPaimonTable(NameMapping nameMapping, Str
} else {
identifier = new Identifier(nameMapping.getRemoteDbName(),
nameMapping.getRemoteTblName());
}
- return executionAuthenticator.execute(() ->
catalog.getTable(identifier));
+ return executionAuthenticator.execute(() -> {
+ Table table = catalog.getTable(identifier);
+ Map<String, String> tableOptions =
paimonProperties.getTableOptionsMap();
+ return tableOptions.isEmpty() ? table :
table.copy(tableOptions);
Review Comment:
[P1] Exclude context selectors from catalog table options
Reflecting every `CoreOptions` admits options that cannot be safely
persisted and applied after load. `branch=dev` reaches this copy after main's
`TableSchema` is loaded; Paimon retains that schema while branch managers/files
switch to dev, so independent branch schemas can mismatch. Likewise a catalog
`scan.tag-name` remains when explicit `FOR VERSION AS OF` later adds
`scan.snapshot-id`; `copy()` merges both and Paimon rejects the mutually
exclusive selectors (incremental overlays leave the same stale keys). Please
restrict this namespace to context-free read tuning options; keep branch
selection on the `Identifier` path and time-travel/incremental selectors
query-scoped, with cross-branch-schema and cross-kind selector tests.
##########
fe/fe-core/src/main/java/org/apache/doris/datasource/property/metastore/AbstractPaimonProperties.java:
##########
@@ -121,6 +140,90 @@ public Map<String, String> getCatalogOptionsMap() {
}
}
+ public Map<String, String> getTableOptionsMap() {
+ return tableOptionsMap;
+ }
+
+ public static boolean isTableOptionProperty(String key) {
+ return key.toLowerCase(Locale.ROOT).startsWith(TABLE_OPTION_PREFIX);
+ }
+
+ private Map<String, String> extractTableOptions() {
+ Map<String, String> tableOptions = new LinkedHashMap<>();
+ origProps.forEach((key, value) -> {
+ if (isTableOptionProperty(key)) {
+ String tableOptionKey =
key.substring(TABLE_OPTION_PREFIX.length());
+ if (StringUtils.isBlank(tableOptionKey)) {
+ throw new IllegalArgumentException(
+ "Paimon table option name must not be empty after
prefix " + TABLE_OPTION_PREFIX);
+ }
+ validateTableOption(tableOptionKey, value);
+ tableOptions.put(tableOptionKey, value);
+ }
+ });
+ return Collections.unmodifiableMap(tableOptions);
+ }
+
+ private void validateTableOption(String key, String value) {
+ ConfigOption<?> option = SUPPORTED_TABLE_OPTIONS.find(key);
+ if (option == null) {
+ throw new IllegalArgumentException("Unsupported Paimon table
option '" + key
+ + "' for the bundled Paimon version");
+ }
+
+ try {
+ new Options(Collections.singletonMap(key, value)).get(option);
+ } catch (IllegalArgumentException e) {
+ throw new IllegalArgumentException("Invalid value for Paimon table
option '" + key + "': "
Review Comment:
[P1] Roll back catalog state for validation exceptions
This new `IllegalArgumentException` escapes the existing ALTER rollback
contract. `CatalogMgr.replayAlterCatalogProps()` first calls
`tryModifyCatalogProps()` (which mutates the map and resets its caches), but
restores `oldProperties` only from `catch (DdlException)`. A rejected unknown
or malformed table option is therefore not edit-logged yet remains in the
running FE; an unknown key cannot be removed by another SET and can keep
property reconstruction failing until restart/recreation. Please translate
these failures to `DdlException` or roll back on every validation exception,
and add an ALTER-level test that verifies the prior catalog state remains
usable after rejection.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]