This is an automated email from the ASF dual-hosted git repository.

morningman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/master by this push:
     new 3f4a85a5cfc [fix](catalog) Allow Iceberg JDBC and Glue database 
properties and reject Paimon path changes (#69228)
3f4a85a5cfc is described below

commit 3f4a85a5cfcc2dd250373171a97217058d520642
Author: Mingyu Chen (Rayner) <[email protected]>
AuthorDate: Mon Oct 19 07:55:43 2026 +0100

    [fix](catalog) Allow Iceberg JDBC and Glue database properties and reject 
Paimon path changes (#69228)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    Related PR: #67163
    
    Problem Summary:
    
    **In short.** Ports the rest of branch-4.1 #67163 to master. Iceberg
    JDBC and Glue catalogs accept `CREATE DATABASE ... PROPERTIES (...)`,
    and Glue also accepts `location`. A Paimon `ALTER TABLE ... SET` that
    names `path` is rejected before anything is committed.
    
    **Background.** #67163 aligned database properties with what each
    catalog type can store and use, and rejected the Paimon `path` option in
    `ALTER TABLE ... SET`. Master has the Paimon database part since #67395
    (`PaimonConnectorMetadata.createDatabase`: HMS, JDBC, REST and DLF store
    properties, only HMS and DLF use `location`). The Iceberg part was not
    ported, and Paimon `ALTER TABLE ... SET` came to master later, with
    #69086, without the `path` check.
    
    **The problem, and what it cost.**
    
    - Iceberg accepts database properties only for HMS catalogs. On a JDBC
    or Glue catalog, which store namespace properties, `CREATE DATABASE db
    PROPERTIES ('owner' = 'doris')` fails with `Not supported: create
    database with properties for iceberg catalog type: jdbc`.
    - `ALTER TABLE paimon_t SET ('path' = '...')` commits a new schema
    version that stores the new path. Paimon leaves that check to the engine
    (`SchemaManager.checkAlterTablePath`, which nothing in paimon-core
    calls). The table does not move: its files stay where they are, and the
    catalog keeps opening it there, so the stored path is wrong metadata,
    shown by `$options`.
    
    **How this PR fixes it.**
    
    - `IcebergConnectorMetadata.createDatabase` checks the catalog type the
    way the Paimon connector does:
    
      | Iceberg catalog type | Database properties | `location` |
      |---|---|---|
      | HMS, Glue | Accepted, forwarded unchanged | Accepted |
    | JDBC | Accepted, forwarded unchanged | Rejected: the catalog would
    store it but not place new tables under it |
    | Hadoop, REST, DLF, S3 Tables | Rejected, as before (a REST server may
    ignore namespace properties) | Rejected |
    
    - `PaimonConnectorMetadata.setTableProperties` runs every key through
    `SchemaManager.checkAlterTablePath` before the remote alter. A `path`
    entry, in any case, fails with `Change path is not supported yet.`, and
    the other entries of the statement are not committed either.
    
    **Results.** On a local cluster with the Iceberg docker environment, and
    PostgreSQL 14 and MySQL 5.7 for the Iceberg JDBC catalog:
    
    | Statement | Before | After |
    |---|---|---|
    | Iceberg JDBC (PostgreSQL): `CREATE DATABASE db PROPERTIES ('owner' =
    'doris', 'team' = 'lakehouse')` | `Not supported: create database with
    properties for iceberg catalog type: jdbc` | Created;
    `iceberg_namespace_properties` has `owner` and `team` |
    | Iceberg JDBC: `CREATE DATABASE db PROPERTIES ('location' =
    's3://...')` | Same error | `Not supported: database property 'location'
    for iceberg catalog type: jdbc because it does not determine the default
    table location` |
    | Paimon: `ALTER TABLE t SET ('PATH' =
    's3://warehouse/relocated_table')` | New schema version whose options
    store the new path; data stays in place | `Change path is not supported
    yet.`; schema version and `$options` unchanged |
    
    The Paimon "before" row is what paimon-core does with that `SetOption`
    on a local `FileSystemCatalog`: schema 1 stores `path = <new location>`,
    while the files and the location the catalog opens stay where they were.
    
    Co-authored-by: Socrates <[email protected]>
---
 .../iceberg/IcebergConnectorMetadata.java          | 29 +++++++---
 .../iceberg/IcebergConnectorMetadataDdlTest.java   | 62 +++++++++++++++++++++-
 .../connector/paimon/PaimonConnectorMetadata.java  | 15 +++++-
 ...PaimonConnectorMetadataTablePropertiesTest.java | 49 +++++++++++++++++
 .../paimon/test_paimon_alter_properties.out        |  6 +++
 .../iceberg/test_iceberg_jdbc_catalog.groovy       | 12 ++++-
 .../paimon/test_paimon_alter_properties.groovy     | 12 +++++
 7 files changed, 174 insertions(+), 11 deletions(-)

diff --git 
a/fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadata.java
 
b/fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadata.java
index 0516fc0b15a..a1259061585 100644
--- 
a/fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadata.java
+++ 
b/fe/fe-connector/fe-connector-iceberg/src/main/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadata.java
@@ -939,16 +939,22 @@ public class IcebergConnectorMetadata implements 
ConnectorMetadata {
     // ========== DDL writes (B1): create/drop database + table ==========
 
     /**
-     * Creates an iceberg namespace, mirroring legacy {@code 
IcebergMetadataOps.performCreateDb}. Namespace
-     * properties are only honored by an HMS catalog; for every other flavor a 
non-empty property map fails
-     * loud (legacy parity) — the gate is a pure local check run BEFORE the 
auth context, like paimon.
-     * Existence / IF NOT EXISTS is resolved upstream by {@code 
PluginDrivenExternalCatalog.createDb}.
+     * Creates an iceberg namespace, mirroring legacy {@code 
IcebergMetadataOps.performCreateDb}. HMS, JDBC
+     * and Glue catalogs store namespace properties; only HMS and Glue place 
new tables under a namespace
+     * {@code location}. Hadoop, REST, DLF and S3 Tables catalogs reject a 
non-empty property map (a REST
+     * server may ignore namespace properties). The gate is a pure local check 
run BEFORE the auth context,
+     * like paimon. Existence / IF NOT EXISTS is resolved upstream by {@code 
PluginDrivenExternalCatalog.createDb}.
      */
     @Override
     public void createDatabase(ConnectorSession session, String dbName, 
Map<String, String> properties) {
-        if (!properties.isEmpty() && !isHmsCatalog()) {
+        String catalogType = catalogType();
+        if (!properties.isEmpty() && !supportsDatabaseProperties(catalogType)) 
{
             throw new DorisConnectorException(
-                    "Not supported: create database with properties for 
iceberg catalog type: " + catalogType());
+                    "Not supported: create database with properties for 
iceberg catalog type: " + catalogType);
+        }
+        if (properties.containsKey("location") && 
!supportsDatabaseLocation(catalogType)) {
+            throw new DorisConnectorException("Not supported: database 
property 'location' for iceberg catalog type: "
+                    + catalogType + " because it does not determine the 
default table location");
         }
         try {
             executeAuthenticated(() -> {
@@ -961,6 +967,17 @@ public class IcebergConnectorMetadata implements 
ConnectorMetadata {
         }
     }
 
+    private static boolean supportsDatabaseProperties(String catalogType) {
+        return IcebergCatalogProperties.TYPE_HMS.equals(catalogType)
+                || IcebergCatalogProperties.TYPE_JDBC.equals(catalogType)
+                || IcebergCatalogProperties.TYPE_GLUE.equals(catalogType);
+    }
+
+    private static boolean supportsDatabaseLocation(String catalogType) {
+        return IcebergCatalogProperties.TYPE_HMS.equals(catalogType)
+                || IcebergCatalogProperties.TYPE_GLUE.equals(catalogType);
+    }
+
     /**
      * Drops an iceberg namespace, mirroring legacy {@code 
IcebergMetadataOps.performDropDb}. With
      * {@code force} the contained tables are dropped (purged) first so a 
non-empty namespace can be removed;
diff --git 
a/fe/fe-connector/fe-connector-iceberg/src/test/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadataDdlTest.java
 
b/fe/fe-connector/fe-connector-iceberg/src/test/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadataDdlTest.java
index ff9dc79bc70..a767e72437f 100644
--- 
a/fe/fe-connector/fe-connector-iceberg/src/test/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadataDdlTest.java
+++ 
b/fe/fe-connector/fe-connector-iceberg/src/test/java/org/apache/doris/connector/iceberg/IcebergConnectorMetadataDdlTest.java
@@ -42,7 +42,7 @@ import java.util.Optional;
 /**
  * Behavior tests for the B1 DDL overrides on {@link IcebergConnectorMetadata} 
— driven entirely through the
  * {@link RecordingIcebergCatalogOps} seam + {@link RecordingConnectorContext} 
(no live catalog, no Mockito).
- * Asserts: every remote op runs INSIDE the auth context, the HMS-only 
properties gate, the force-drop
+ * Asserts: every remote op runs INSIDE the auth context, the per-catalog-type 
properties gate, the force-drop
  * cascade, and that the managed-location cleanup hook is invoked (HMS only) 
with the location captured
  * BEFORE the drop.
  */
@@ -75,7 +75,7 @@ public class IcebergConnectorMetadataDdlTest {
     }
 
     @Test
-    public void testCreateDatabaseNonHmsWithPropertiesFailsLoud() {
+    public void testCreateDatabaseRestWithPropertiesFailsLoud() {
         RecordingIcebergCatalogOps ops = new RecordingIcebergCatalogOps();
         RecordingConnectorContext ctx = new RecordingConnectorContext();
         IcebergConnectorMetadata md = metadata(ops, ctx, 
IcebergCatalogProperties.TYPE_REST);
@@ -87,6 +87,64 @@ public class IcebergConnectorMetadataDdlTest {
         Assertions.assertEquals(0, ctx.authCount);
     }
 
+    @Test
+    public void testCreateDatabaseWithPropertiesForSupportedCatalogs() {
+        // HMS, JDBC and Glue store namespace properties; the map is forwarded 
unchanged.
+        Map<String, String> dbProps = Collections.singletonMap("owner", 
"doris");
+        for (String catalogType : 
Arrays.asList(IcebergCatalogProperties.TYPE_HMS,
+                IcebergCatalogProperties.TYPE_JDBC, 
IcebergCatalogProperties.TYPE_GLUE)) {
+            RecordingIcebergCatalogOps ops = new RecordingIcebergCatalogOps();
+            RecordingConnectorContext ctx = new RecordingConnectorContext();
+            metadata(ops, ctx, catalogType).createDatabase(null, "db1", 
dbProps);
+            Assertions.assertEquals("db1", ops.lastCreateDb, catalogType);
+            Assertions.assertEquals(dbProps, ops.lastCreateDbProps, 
catalogType);
+            Assertions.assertEquals(1, ctx.authCount, catalogType);
+        }
+    }
+
+    @Test
+    public void testCreateDatabaseWithLocationForSupportedCatalogs() {
+        // Only HMS and Glue place new tables under the namespace location.
+        Map<String, String> dbProps = Collections.singletonMap("location", 
"s3://wh/db1");
+        for (String catalogType : 
Arrays.asList(IcebergCatalogProperties.TYPE_HMS,
+                IcebergCatalogProperties.TYPE_GLUE)) {
+            RecordingIcebergCatalogOps ops = new RecordingIcebergCatalogOps();
+            RecordingConnectorContext ctx = new RecordingConnectorContext();
+            metadata(ops, ctx, catalogType).createDatabase(null, "db1", 
dbProps);
+            Assertions.assertEquals(dbProps, ops.lastCreateDbProps, 
catalogType);
+        }
+    }
+
+    @Test
+    public void testCreateDatabaseWithLocationForJdbcCatalogIsRejected() {
+        // A JDBC catalog would store the location without placing later 
tables under it.
+        RecordingIcebergCatalogOps ops = new RecordingIcebergCatalogOps();
+        RecordingConnectorContext ctx = new RecordingConnectorContext();
+        IcebergConnectorMetadata md = metadata(ops, ctx, 
IcebergCatalogProperties.TYPE_JDBC);
+        DorisConnectorException ex = 
Assertions.assertThrows(DorisConnectorException.class,
+                () -> md.createDatabase(null, "db1", 
Collections.singletonMap("location", "s3://wh/db1")));
+        Assertions.assertTrue(ex.getMessage().contains("database property 
'location' for iceberg catalog type: jdbc"),
+                ex.getMessage());
+        Assertions.assertTrue(ops.log.isEmpty(), ops.log.toString());
+        Assertions.assertEquals(0, ctx.authCount);
+    }
+
+    @Test
+    public void testCreateDatabaseWithPropertiesForUnsupportedCatalogs() {
+        for (String catalogType : 
Arrays.asList(IcebergCatalogProperties.TYPE_HADOOP,
+                IcebergCatalogProperties.TYPE_REST, 
IcebergCatalogProperties.TYPE_DLF,
+                IcebergCatalogProperties.TYPE_S3_TABLES)) {
+            RecordingIcebergCatalogOps ops = new RecordingIcebergCatalogOps();
+            RecordingConnectorContext ctx = new RecordingConnectorContext();
+            IcebergConnectorMetadata md = metadata(ops, ctx, catalogType);
+            DorisConnectorException ex = 
Assertions.assertThrows(DorisConnectorException.class,
+                    () -> md.createDatabase(null, "db1", 
Collections.singletonMap("owner", "doris")));
+            Assertions.assertTrue(ex.getMessage().contains("iceberg catalog 
type: " + catalogType), ex.getMessage());
+            Assertions.assertTrue(ops.log.isEmpty(), catalogType + " " + 
ops.log);
+            Assertions.assertEquals(0, ctx.authCount, catalogType);
+        }
+    }
+
     @Test
     public void testCreateDatabaseNonHmsEmptyPropertiesSucceeds() {
         RecordingIcebergCatalogOps ops = new RecordingIcebergCatalogOps();
diff --git 
a/fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonConnectorMetadata.java
 
b/fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonConnectorMetadata.java
index 967d60df63a..b6dd5993eb2 100644
--- 
a/fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonConnectorMetadata.java
+++ 
b/fe/fe-connector/fe-connector-paimon/src/main/java/org/apache/doris/connector/paimon/PaimonConnectorMetadata.java
@@ -49,6 +49,7 @@ import org.apache.paimon.catalog.Identifier;
 import org.apache.paimon.partition.Partition;
 import org.apache.paimon.schema.Schema;
 import org.apache.paimon.schema.SchemaChange;
+import org.apache.paimon.schema.SchemaManager;
 import org.apache.paimon.table.DataTable;
 import org.apache.paimon.table.FileStoreTable;
 import org.apache.paimon.table.Table;
@@ -1198,13 +1199,25 @@ public class PaimonConnectorMetadata implements 
ConnectorMetadata {
      * {@link SchemaChange#setOption} in ONE {@code alterTable} call: paimon 
validates the whole list against
      * the new schema before committing it, so a rejected entry keeps the 
valid ones from being written too,
      * and the table gets one new schema version rather than one per option.
+     *
+     * <p>{@code path} is rejected before the remote alter: paimon would 
commit it as an ordinary option, but
+     * changing it does not relocate the table. Paimon leaves that check to 
the engine
+     * ({@link SchemaManager#checkAlterTablePath}).
      */
     @Override
     public void setTableProperties(ConnectorSession session, 
ConnectorTableHandle handle,
             Map<String, String> properties) {
+        PaimonTableHandle paimonHandle = (PaimonTableHandle) handle;
+        try {
+            properties.keySet().forEach(SchemaManager::checkAlterTablePath);
+        } catch (UnsupportedOperationException e) {
+            throw new DorisConnectorException("Failed to set properties for 
Paimon table "
+                    + Identifier.create(paimonHandle.getDatabaseName(), 
paimonHandle.getTableName())
+                    + ": " + e.getMessage(), e);
+        }
         List<SchemaChange> changes = new ArrayList<>(properties.size());
         properties.forEach((key, value) -> 
changes.add(SchemaChange.setOption(key, value)));
-        alterTable((PaimonTableHandle) handle, changes, "set properties");
+        alterTable(paimonHandle, changes, "set properties");
     }
 
     // ==================== DDL: Column evolution ====================
diff --git 
a/fe/fe-connector/fe-connector-paimon/src/test/java/org/apache/doris/connector/paimon/PaimonConnectorMetadataTablePropertiesTest.java
 
b/fe/fe-connector/fe-connector-paimon/src/test/java/org/apache/doris/connector/paimon/PaimonConnectorMetadataTablePropertiesTest.java
index c745ca9ee9a..83115772cb0 100644
--- 
a/fe/fe-connector/fe-connector-paimon/src/test/java/org/apache/doris/connector/paimon/PaimonConnectorMetadataTablePropertiesTest.java
+++ 
b/fe/fe-connector/fe-connector-paimon/src/test/java/org/apache/doris/connector/paimon/PaimonConnectorMetadataTablePropertiesTest.java
@@ -25,6 +25,7 @@ import org.apache.paimon.catalog.Identifier;
 import org.apache.paimon.fs.local.LocalFileIO;
 import org.apache.paimon.schema.Schema;
 import org.apache.paimon.schema.SchemaChange;
+import org.apache.paimon.schema.SchemaManager;
 import org.apache.paimon.table.FileStoreTable;
 import org.apache.paimon.types.DataTypes;
 import org.junit.jupiter.api.Assertions;
@@ -114,6 +115,54 @@ public class PaimonConnectorMetadataTablePropertiesTest {
         }
     }
 
+    @Test
+    public void setTablePropertiesRejectsPathBeforeRemoteAlter() {
+        RecordingPaimonCatalogOps ops = new RecordingPaimonCatalogOps();
+        RecordingConnectorContext context = new RecordingConnectorContext();
+        PaimonConnectorMetadata metadata = new PaimonConnectorMetadata(
+                ops, PaimonCatalogProperties.of(Collections.emptyMap()), 
context);
+        Map<String, String> properties = new LinkedHashMap<>();
+        properties.put("snapshot.num-retained.max", "10");
+        properties.put("PATH", "s3://warehouse/relocated_table");
+
+        DorisConnectorException e = 
Assertions.assertThrows(DorisConnectorException.class,
+                () -> metadata.setTableProperties(null, handle(), properties));
+
+        Assertions.assertTrue(e.getMessage().contains("Change path is not 
supported yet"), e.getMessage());
+        Assertions.assertTrue(ops.log.isEmpty(), ops.log.toString());
+        Assertions.assertEquals(0, context.authCount);
+    }
+
+    @Test
+    public void setTablePropertiesPathLeavesTableUnchanged(@TempDir Path 
warehouse) throws Exception {
+        try (Catalog catalog = localCatalog(warehouse)) {
+            catalog.createTable(TABLE_ID, Schema.newBuilder()
+                    .column("id", DataTypes.INT())
+                    .option("snapshot.num-retained.min", "1")
+                    .option("snapshot.num-retained.max", "8")
+                    .build(), false);
+            long schemaId = schemaId(catalog);
+            Map<String, String> properties = new LinkedHashMap<>();
+            properties.put("snapshot.num-retained.max", "10");
+            properties.put("path", 
warehouse.resolve("relocated_table").toUri().toString());
+
+            // Paimon itself would store path in a new schema version without 
moving the table; the
+            // catalog then overrides it on load, so check the stored schema 
rather than the loaded table.
+            Assertions.assertThrows(DorisConnectorException.class,
+                    () -> metadataOver(catalog).setTableProperties(null, 
handle(), properties));
+
+            Map<String, String> stored = storedSchemaOptions(catalog);
+            Assertions.assertEquals("8", 
stored.get("snapshot.num-retained.max"));
+            Assertions.assertFalse(stored.containsKey("path"), 
stored.toString());
+            Assertions.assertEquals(schemaId, schemaId(catalog));
+        }
+    }
+
+    private static Map<String, String> storedSchemaOptions(Catalog catalog) 
throws Exception {
+        FileStoreTable table = (FileStoreTable) catalog.getTable(TABLE_ID);
+        return new SchemaManager(LocalFileIO.create(), 
table.location()).latest().get().options();
+    }
+
     private static Map<String, String> options(String min, String max) {
         Map<String, String> properties = new LinkedHashMap<>();
         properties.put("snapshot.num-retained.min", min);
diff --git 
a/regression-test/data/external_table_p0/paimon/test_paimon_alter_properties.out
 
b/regression-test/data/external_table_p0/paimon/test_paimon_alter_properties.out
index ba7bb871388..0fbbf7ed1ff 100644
--- 
a/regression-test/data/external_table_p0/paimon/test_paimon_alter_properties.out
+++ 
b/regression-test/data/external_table_p0/paimon/test_paimon_alter_properties.out
@@ -28,3 +28,9 @@ snapshot.num-retained.max     8
 -- !rejected_options --
 snapshot.num-retained.max      8
 
+-- !path_schema --
+2
+
+-- !path_options --
+snapshot.num-retained.max      8
+
diff --git 
a/regression-test/suites/external_table_p0/iceberg/test_iceberg_jdbc_catalog.groovy
 
b/regression-test/suites/external_table_p0/iceberg/test_iceberg_jdbc_catalog.groovy
index 4d3f44e3cb9..c04e3404938 100644
--- 
a/regression-test/suites/external_table_p0/iceberg/test_iceberg_jdbc_catalog.groovy
+++ 
b/regression-test/suites/external_table_p0/iceberg/test_iceberg_jdbc_catalog.groovy
@@ -157,7 +157,15 @@ suite("test_iceberg_jdbc_catalog", "p0,external") {
 
         // Test: Create database
         sql """DROP DATABASE IF EXISTS ${db_name} FORCE"""
-        sql """CREATE DATABASE ${db_name}"""
+        // A JDBC catalog stores namespace properties, but would only record a 
location without placing
+        // new tables under it.
+        test {
+            sql """CREATE DATABASE ${db_name} PROPERTIES (
+                    'location' = 's3://warehouse/rejected_database_location/'
+                )"""
+            exception "database property 'location' for iceberg catalog type: 
jdbc"
+        }
+        sql """CREATE DATABASE ${db_name} PROPERTIES ('owner' = 'doris')"""
         
         def databases = sql """SHOW DATABASES"""
         assertTrue(databases.toString().contains(db_name))
@@ -315,7 +323,7 @@ suite("test_iceberg_jdbc_catalog", "p0,external") {
                 
                 String mysql_db_name = "mysql_test_db"
                 sql """DROP DATABASE IF EXISTS ${mysql_db_name} FORCE"""
-                sql """CREATE DATABASE ${mysql_db_name}"""
+                sql """CREATE DATABASE ${mysql_db_name} PROPERTIES ('owner' = 
'doris')"""
                 sql """USE ${mysql_db_name}"""
                 
                 sql """DROP TABLE IF EXISTS test_mysql_catalog"""
diff --git 
a/regression-test/suites/external_table_p0/paimon/test_paimon_alter_properties.groovy
 
b/regression-test/suites/external_table_p0/paimon/test_paimon_alter_properties.groovy
index d504d89a0f6..187177b1bc1 100644
--- 
a/regression-test/suites/external_table_p0/paimon/test_paimon_alter_properties.groovy
+++ 
b/regression-test/suites/external_table_p0/paimon/test_paimon_alter_properties.groovy
@@ -119,4 +119,16 @@ suite("test_paimon_alter_properties", 
"p0,external,paimon") {
         SELECT `key`, value FROM `paimon_alter_properties\$options`
         WHERE `key` IN ('snapshot.num-retained.max', 
'fields.missing.sequence-group')
     """
+
+    // Paimon keeps the table location in its path option, but changing it 
does not relocate the table.
+    // Doris rejects it before the remote alter, so no schema version is 
written and path is not stored.
+    test {
+        sql """ALTER TABLE `paimon_alter_properties` SET ('PATH' = 
's3://warehouse/relocated_table')"""
+        exception "Change path is not supported yet"
+    }
+    order_qt_path_schema """SELECT MAX(schema_id) FROM 
`paimon_alter_properties\$schemas`"""
+    order_qt_path_options """
+        SELECT `key`, value FROM `paimon_alter_properties\$options`
+        WHERE lower(`key`) IN ('path', 'snapshot.num-retained.max')
+    """
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to