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

jerryshao pushed a commit to branch branch-1.3
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/branch-1.3 by this push:
     new 4458ae8a2d [Cherry-pick to branch-1.3] [#11972] fix(clickhouse): read 
sort key from system.tables (#12016) (#12704)
4458ae8a2d is described below

commit 4458ae8a2db84947e04d52d983d7913a9a7750d3
Author: StormSpirit <[email protected]>
AuthorDate: Fri Aug 28 15:49:43 2026 +0800

    [Cherry-pick to branch-1.3] [#11972] fix(clickhouse): read sort key from 
system.tables (#12016) (#12704)
    
    ### What changes were proposed in this pull request?
    
    This PR manually backports #12016 to `branch-1.3` and supersedes the
    unresolved automated cherry-pick PR #12684.
    
    - Read table sort orders from the canonical `system.tables.sorting_key`
    field instead of parsing `ORDER BY` from the full `SHOW CREATE TABLE`
    output.
    - Read table-level Settings from `system.tables.engine_full`, preventing
    projection-level `WITH SETTINGS` clauses from being exposed as table
    properties.
    - Keep the existing `SHOW CREATE TABLE` path only for partition metadata
    and preserve the engine-parameter behavior already backported by #12672.
    - Add focused unit coverage for the exact metadata query, expression
    conversion, blank values, Settings, and missing rows, plus a real
    ClickHouse projection regression test.
    
    The manual conflict resolution starts from the latest `branch-1.3`,
    retains #12672, and excludes the unrelated enum round-trip test that
    exists only on the source branch.
    
    ### Why are the changes needed?
    
    When a ClickHouse table contains a projection with its own `ORDER BY`,
    parsing the complete `SHOW CREATE TABLE` output can select the
    projection's `ORDER BY` instead of the table-level sorting key.
    Gravitino then returns incorrect sort-order metadata without reporting
    an error.
    
    The automated backport was generated before the dependent
    engine-parameter change reached `branch-1.3`, so its three-way
    cherry-pick produced unresolved conflicts in the ClickHouse
    implementation and tests.
    
    Fix: #11972
    
    ### Does this PR introduce _any_ user-facing change?
    
    No public API or property key changes are introduced. Loading a
    ClickHouse table with projections now returns the correct table-level
    sort orders while preserving the existing `settings.*` property
    contract.
    
    ### How was this patch tested?
    
    - `./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:spotlessCheck` —
    passed.
    - `./gradlew rat` — passed.
    - `./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:test --tests
    
org.apache.gravitino.catalog.clickhouse.operations.TestClickHouseTableOperationsUnit
    -PskipITs -PskipDockerTests=true` — passed.
    - `./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:test -PskipITs
    -PskipDockerTests=true` — passed.
    - `./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:test --tests
    org.apache.gravitino.catalog.clickhouse.integration.test.CatalogClickHouseIT
    -PskipDockerTests=false` — 52 tests, 0 skipped, 0 failures, 0 errors;
    `testLoadTableWithProjectionUsesTableSortKey` executed successfully.
    - `./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:test --tests
    
org.apache.gravitino.catalog.clickhouse.integration.test.CatalogClickHouseClusterIT
    -PskipDockerTests=false` — 17 tests, 0 skipped, 0 failures, 0 errors.
    - `./gradlew :catalogs-contrib:catalog-jdbc-clickhouse:build -x test` —
    passed.
    
    Signed-off-by: jiangxt2 <[email protected]>
    Co-authored-by: Qi Yu <[email protected]>
---
 .../operations/ClickHouseTableOperations.java      |  87 +++++++++-----
 .../integration/test/CatalogClickHouseIT.java      |  40 ++++++-
 .../operations/TestClickHouseTableOperations.java  |  58 +++-------
 .../TestClickHouseTableOperationsUnit.java         | 127 +++++++++++++++++++++
 4 files changed, 238 insertions(+), 74 deletions(-)

diff --git 
a/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseTableOperations.java
 
b/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseTableOperations.java
index a5be1b0753..8d7afb9ed5 100644
--- 
a/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseTableOperations.java
+++ 
b/catalogs-contrib/catalog-jdbc-clickhouse/src/main/java/org/apache/gravitino/catalog/clickhouse/operations/ClickHouseTableOperations.java
@@ -102,10 +102,6 @@ public class ClickHouseTableOperations extends 
JdbcTableOperations {
               ENGINE.SUMMINGMERGETREE,
               ENGINE.COLLAPSINGMERGETREE,
               ENGINE.VERSIONEDCOLLAPSINGMERGETREE));
-
-  private static final Pattern ORDER_BY_PATTERN =
-      Pattern.compile(
-          
"(?is)\\bORDER\\s+BY\\s*(.+?)(?=\\bPARTITION\\s+BY\\b|\\bPRIMARY\\s+KEY\\b|\\bSAMPLE\\s+BY\\b|\\bTTL\\b|\\bSETTINGS\\b|\\bCOMMENT\\b|$)");
   private static final Pattern PARTITION_BY_PATTERN =
       Pattern.compile(
           
"(?is)\\bPARTITION\\s+BY\\s*(.+?)(?=\\bORDER\\s+BY\\b|\\bPRIMARY\\s+KEY\\b|\\bSAMPLE\\s+BY\\b|\\bTTL\\b|\\bSETTINGS\\b|\\bCOMMENT\\b|$)");
@@ -808,25 +804,28 @@ public class ClickHouseTableOperations extends 
JdbcTableOperations {
       List<Index> indexes = getIndexes(connection, databaseName, tableName);
       jdbcTableBuilder.withIndexes(indexes.toArray(new Index[0]));
 
-      ShowCreateTableMetadata metadata = parseShowCreateTable(connection, 
tableName);
-      Transform[] partitioning = metadata.partitioning;
+      SystemTableMetadata systemTableMetadata =
+          getSystemTableMetadata(connection, databaseName, tableName);
+      ShowCreateTableMetadata showCreateMetadata = 
parseShowCreateTable(connection, tableName);
+      Transform[] partitioning = showCreateMetadata.partitioning;
       if (ArrayUtils.isEmpty(partitioning)) {
         partitioning = getTablePartitioning(connection, databaseName, 
tableName);
       }
       jdbcTableBuilder.withPartitioning(partitioning);
-      jdbcTableBuilder.withSortOrders(metadata.sortOrders);
+      jdbcTableBuilder.withSortOrders(systemTableMetadata.sortOrders());
 
       Distribution distribution = getDistributionInfo(connection, 
databaseName, tableName);
       jdbcTableBuilder.withDistribution(distribution);
 
       Map<String, String> tableProperties = getTableProperties(connection, 
tableName);
-      // Merge SETTINGS parsed from SHOW CREATE TABLE into table properties.
-      // SHOW CREATE TABLE is the authoritative source for SETTINGS; it takes 
precedence
+      // Merge SETTINGS parsed from system.tables.engine_full into table 
properties.
+      // engine_full contains only table-level storage clauses, so projection 
SETTINGS cannot be
+      // mistaken for table SETTINGS. These values take precedence
       // over any settings.* keys that might exist in system.tables (though 
getTableProperties()
       // currently does not read SETTINGS from system.tables, so no overlap 
occurs in practice).
-      if (!metadata.settings.isEmpty()) {
+      if (!systemTableMetadata.settings().isEmpty()) {
         Map<String, String> merged = new HashMap<>(tableProperties);
-        merged.putAll(metadata.settings);
+        merged.putAll(systemTableMetadata.settings());
         tableProperties = Collections.unmodifiableMap(merged);
       }
       jdbcTableBuilder.withProperties(tableProperties);
@@ -856,6 +855,26 @@ public class ClickHouseTableOperations extends 
JdbcTableOperations {
     return kinds;
   }
 
+  @VisibleForTesting
+  SystemTableMetadata getSystemTableMetadata(
+      Connection connection, String databaseName, String tableName) throws 
SQLException {
+    String sql =
+        "SELECT sorting_key, engine_full FROM system.tables WHERE database = ? 
AND name = ?";
+    try (PreparedStatement statement = connection.prepareStatement(sql)) {
+      statement.setString(1, databaseName);
+      statement.setString(2, tableName);
+      try (ResultSet resultSet = statement.executeQuery()) {
+        if (resultSet.next()) {
+          return new SystemTableMetadata(
+              parseOrderByClause(resultSet.getString("sorting_key")),
+              parseSettingsFromEngineFull(resultSet.getString("engine_full")));
+        }
+      }
+    }
+
+    throw new NoSuchTableException("Table %s does not exist in %s.", 
tableName, databaseName);
+  }
+
   @Override
   protected Transform[] getTablePartitioning(
       Connection connection, String databaseName, String tableName) throws 
SQLException {
@@ -1408,21 +1427,11 @@ public class ClickHouseTableOperations extends 
JdbcTableOperations {
       return metadata;
     }
 
-    Matcher orderMatcher = ORDER_BY_PATTERN.matcher(createSql);
-    if (orderMatcher.find()) {
-      metadata.sortOrders = parseOrderByClause(orderMatcher.group(1));
-    }
-
     Matcher partitionMatcher = PARTITION_BY_PATTERN.matcher(createSql);
     if (partitionMatcher.find()) {
       metadata.partitioning = parsePartitioning(partitionMatcher.group(1));
     }
 
-    Matcher settingsMatcher = SETTINGS_PATTERN.matcher(createSql);
-    if (settingsMatcher.find()) {
-      metadata.settings = parseSettingsClause(settingsMatcher.group(1));
-    }
-
     return metadata;
   }
 
@@ -1446,13 +1455,16 @@ public class ClickHouseTableOperations extends 
JdbcTableOperations {
   }
 
   @VisibleForTesting
-  SortOrder[] parseSortOrdersFromCreateSql(String createSql) {
-    return parseCreateStatement(createSql).sortOrders;
-  }
+  Map<String, String> parseSettingsFromEngineFull(String engineFull) {
+    if (StringUtils.isBlank(engineFull)) {
+      return Collections.emptyMap();
+    }
 
-  @VisibleForTesting
-  Map<String, String> parseSettingsFromCreateSql(String createSql) {
-    return parseCreateStatement(createSql).settings;
+    Matcher settingsMatcher = SETTINGS_PATTERN.matcher(engineFull);
+    if (settingsMatcher.find()) {
+      return parseSettingsClause(settingsMatcher.group(1));
+    }
+    return Collections.emptyMap();
   }
 
   private ShowCreateTableMetadata parseShowCreateTable(Connection connection, 
String tableName)
@@ -1571,10 +1583,27 @@ public class ClickHouseTableOperations extends 
JdbcTableOperations {
     return expression.toString();
   }
 
+  @VisibleForTesting
+  static final class SystemTableMetadata {
+    private final SortOrder[] sortOrders;
+    private final Map<String, String> settings;
+
+    private SystemTableMetadata(SortOrder[] sortOrders, Map<String, String> 
settings) {
+      this.sortOrders = sortOrders;
+      this.settings = settings;
+    }
+
+    SortOrder[] sortOrders() {
+      return sortOrders;
+    }
+
+    Map<String, String> settings() {
+      return settings;
+    }
+  }
+
   private static final class ShowCreateTableMetadata {
     private Transform[] partitioning = Transforms.EMPTY_TRANSFORM;
-    private SortOrder[] sortOrders = SortOrders.NONE;
-    private Map<String, String> settings = Collections.emptyMap();
   }
 
   @VisibleForTesting
diff --git 
a/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/integration/test/CatalogClickHouseIT.java
 
b/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/integration/test/CatalogClickHouseIT.java
index f05224a795..f99ee59ed2 100644
--- 
a/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/integration/test/CatalogClickHouseIT.java
+++ 
b/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/integration/test/CatalogClickHouseIT.java
@@ -431,8 +431,8 @@ public class CatalogClickHouseIT extends BaseIT {
   }
 
   @Test
-  void testLoadTableFromShowCreateParsing() {
-    String name = GravitinoITUtils.genRandomName("show_create_table");
+  void testLoadTableMetadataFromNativeSql() {
+    String name = GravitinoITUtils.genRandomName("native_table_metadata");
     clickhouseService.executeQuery(
         String.format(
             "CREATE TABLE `%s`.`%s` (\n"
@@ -2876,6 +2876,42 @@ public class CatalogClickHouseIT extends BaseIT {
                     Indexes.EMPTY_INDEXES));
   }
 
+  @Test
+  void testLoadTableWithProjectionUsesTableSortKey() {
+    String name = GravitinoITUtils.genRandomName("proj_normal");
+    clickhouseService.executeQuery(
+        String.format(
+            "CREATE TABLE `%s`.`%s` (\n"
+                + "  `id` Int64,\n"
+                + "  `dt` Date,\n"
+                + "  `val` String,\n"
+                + "  PROJECTION p_normal\n"
+                + "  (\n"
+                + "      SELECT *\n"
+                + "      ORDER BY dt\n"
+                + "  )\n"
+                + ")\n"
+                + "ENGINE = MergeTree\n"
+                + "ORDER BY (id, dt)\n"
+                + "SETTINGS index_granularity = 8192",
+            schemaName, name));
+
+    Table loaded = 
catalog.asTableCatalog().loadTable(NameIdentifier.of(schemaName, name));
+    SortOrder[] sortOrders = loaded.sortOrder();
+
+    Assertions.assertEquals(2, sortOrders.length);
+    Assertions.assertTrue(
+        sortOrders[0].expression() instanceof NamedReference,
+        "First sort key should be a named reference");
+    Assertions.assertArrayEquals(
+        new String[] {"id"}, ((NamedReference) 
sortOrders[0].expression()).fieldName());
+    Assertions.assertTrue(sortOrders[1].expression() instanceof 
NamedReference);
+    Assertions.assertArrayEquals(
+        new String[] {"dt"}, ((NamedReference) 
sortOrders[1].expression()).fieldName());
+    Assertions.assertEquals(
+        "8192", loaded.properties().get(TableConstants.SETTINGS_PREFIX + 
"index_granularity"));
+  }
+
   @Test
   void testEngineParametersRoundTrip() {
     // Create a ReplacingMergeTree table with engine parameter via Gravitino 
API and verify
diff --git 
a/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/operations/TestClickHouseTableOperations.java
 
b/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/operations/TestClickHouseTableOperations.java
index 909b5ec81a..5f3fdbd8ea 100644
--- 
a/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/operations/TestClickHouseTableOperations.java
+++ 
b/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/operations/TestClickHouseTableOperations.java
@@ -1362,44 +1362,20 @@ public class TestClickHouseTableOperations extends 
TestClickHouse {
   }
 
   @Test
-  void testParseSortOrdersFromMultilineShowCreateSql() {
-    TestableClickHouseTableOperations ops = new 
TestableClickHouseTableOperations();
-    String showCreateSql =
-        """
-        CREATE TABLE `t1`
-        (
-          `id` Int32,
-          `event_time` DateTime
-        )
-        ENGINE = MergeTree
-        ORDER BY
-          (`id`, toDate(`event_time`))
-        SETTINGS index_granularity = 8192
-        """;
-
-    SortOrder[] sortOrders = ops.parseSortOrders(showCreateSql);
-    Assertions.assertEquals(2, sortOrders.length);
-    Assertions.assertTrue(sortOrders[0].expression() instanceof 
NamedReference);
-    Assertions.assertEquals("id", ((NamedReference) 
sortOrders[0].expression()).fieldName()[0]);
-  }
-
-  @Test
-  void testParseSettingsFromCreateSql() {
+  void testParseSettingsFromEngineFull() {
     TestableClickHouseTableOperations ops = new 
TestableClickHouseTableOperations();
 
     // Single setting
-    String sql1 =
-        "CREATE TABLE t1 (id Int32) ENGINE = MergeTree ORDER BY id SETTINGS 
index_granularity = 4096";
-    Map<String, String> settings1 = ops.parseSettings(sql1);
+    String engineFull1 = "MergeTree ORDER BY id SETTINGS index_granularity = 
4096";
+    Map<String, String> settings1 = ops.parseSettings(engineFull1);
     Assertions.assertEquals(1, settings1.size());
     Assertions.assertEquals(
         "4096", settings1.get(TableConstants.SETTINGS_PREFIX + 
"index_granularity"));
 
     // Multiple settings
-    String sql2 =
-        "CREATE TABLE t2 (id Int32) ENGINE = MergeTree ORDER BY id"
-            + " SETTINGS index_granularity = 4096, min_bytes_for_wide_part = 
0";
-    Map<String, String> settings2 = ops.parseSettings(sql2);
+    String engineFull2 =
+        "MergeTree ORDER BY id" + " SETTINGS index_granularity = 4096, 
min_bytes_for_wide_part = 0";
+    Map<String, String> settings2 = ops.parseSettings(engineFull2);
     Assertions.assertEquals(2, settings2.size());
     Assertions.assertEquals(
         "4096", settings2.get(TableConstants.SETTINGS_PREFIX + 
"index_granularity"));
@@ -1407,15 +1383,15 @@ public class TestClickHouseTableOperations extends 
TestClickHouse {
         "0", settings2.get(TableConstants.SETTINGS_PREFIX + 
"min_bytes_for_wide_part"));
 
     // No SETTINGS clause
-    String sql3 = "CREATE TABLE t3 (id Int32) ENGINE = MergeTree ORDER BY id";
-    Map<String, String> settings3 = ops.parseSettings(sql3);
+    String engineFull3 = "MergeTree ORDER BY id";
+    Map<String, String> settings3 = ops.parseSettings(engineFull3);
     Assertions.assertTrue(settings3.isEmpty());
 
-    // SETTINGS with COMMENT after
-    String sql4 =
-        "CREATE TABLE t4 (id Int32) ENGINE = MergeTree ORDER BY id"
-            + " SETTINGS index_granularity = 8192 COMMENT 'test'";
-    Map<String, String> settings4 = ops.parseSettings(sql4);
+    // Engine arguments before SETTINGS
+    String engineFull4 =
+        "ReplicatedMergeTree('/path', '{replica}') ORDER BY id"
+            + " SETTINGS index_granularity = 8192";
+    Map<String, String> settings4 = ops.parseSettings(engineFull4);
     Assertions.assertEquals(1, settings4.size());
     Assertions.assertEquals(
         "8192", settings4.get(TableConstants.SETTINGS_PREFIX + 
"index_granularity"));
@@ -1435,12 +1411,8 @@ public class TestClickHouseTableOperations extends 
TestClickHouse {
           tableName, columns, comment, properties, partitioning, distribution, 
indexes, sortOrders);
     }
 
-    SortOrder[] parseSortOrders(String createSql) {
-      return parseSortOrdersFromCreateSql(createSql);
-    }
-
-    Map<String, String> parseSettings(String createSql) {
-      return parseSettingsFromCreateSql(createSql);
+    Map<String, String> parseSettings(String engineFull) {
+      return parseSettingsFromEngineFull(engineFull);
     }
   }
 
diff --git 
a/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/operations/TestClickHouseTableOperationsUnit.java
 
b/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/operations/TestClickHouseTableOperationsUnit.java
index e2cfa0ad07..ae24dbf649 100644
--- 
a/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/operations/TestClickHouseTableOperationsUnit.java
+++ 
b/catalogs-contrib/catalog-jdbc-clickhouse/src/test/java/org/apache/gravitino/catalog/clickhouse/operations/TestClickHouseTableOperationsUnit.java
@@ -32,7 +32,11 @@ import 
org.apache.gravitino.catalog.clickhouse.converter.ClickHouseColumnDefault
 import 
org.apache.gravitino.catalog.clickhouse.converter.ClickHouseExceptionConverter;
 import 
org.apache.gravitino.catalog.clickhouse.converter.ClickHouseTypeConverter;
 import org.apache.gravitino.catalog.jdbc.JdbcColumn;
+import org.apache.gravitino.exceptions.NoSuchTableException;
+import org.apache.gravitino.rel.expressions.FunctionExpression;
+import org.apache.gravitino.rel.expressions.NamedReference;
 import org.apache.gravitino.rel.expressions.distributions.Distributions;
+import org.apache.gravitino.rel.expressions.sorts.SortOrder;
 import org.apache.gravitino.rel.expressions.transforms.Transforms;
 import org.apache.gravitino.rel.indexes.Index;
 import org.apache.gravitino.rel.indexes.Indexes;
@@ -50,6 +54,11 @@ public class TestClickHouseTableOperationsUnit {
       return getIndexes(connection, databaseName, tableName);
     }
 
+    SystemTableMetadata callGetSystemTableMetadata(
+        Connection connection, String databaseName, String tableName) throws 
Exception {
+      return getSystemTableMetadata(connection, databaseName, tableName);
+    }
+
     Map<String, String> callGetTableProperties(Connection connection, String 
tableName)
         throws Exception {
       return getTableProperties(connection, tableName);
@@ -136,6 +145,124 @@ public class TestClickHouseTableOperationsUnit {
     Assertions.assertTrue(primaryKeySql.contains("t''1"), "table single quote 
should be doubled");
   }
 
+  @Test
+  void testGetSystemTableMetadataQueriesExactTable() throws Exception {
+    ExposedClickHouseTableOperations ops = newOps();
+    Connection connection = Mockito.mock(Connection.class);
+    PreparedStatement statement = Mockito.mock(PreparedStatement.class);
+    ResultSet resultSet = Mockito.mock(ResultSet.class);
+    ArgumentCaptor<String> sqlCaptor = ArgumentCaptor.forClass(String.class);
+
+    
Mockito.when(connection.prepareStatement(sqlCaptor.capture())).thenReturn(statement);
+    Mockito.when(statement.executeQuery()).thenReturn(resultSet);
+    Mockito.when(resultSet.next()).thenReturn(true);
+    Mockito.when(resultSet.getString("sorting_key")).thenReturn("id");
+    Mockito.when(resultSet.getString("engine_full")).thenReturn("MergeTree 
ORDER BY id");
+
+    ClickHouseTableOperations.SystemTableMetadata metadata =
+        ops.callGetSystemTableMetadata(connection, "db_name", "table_name");
+
+    Assertions.assertEquals(
+        "SELECT sorting_key, engine_full FROM system.tables WHERE database = ? 
AND name = ?",
+        sqlCaptor.getValue());
+    Mockito.verify(statement).setString(1, "db_name");
+    Mockito.verify(statement).setString(2, "table_name");
+    Assertions.assertEquals(1, metadata.sortOrders().length);
+    Assertions.assertEquals(NamedReference.field("id"), 
metadata.sortOrders()[0].expression());
+    Assertions.assertTrue(metadata.settings().isEmpty());
+    Mockito.verify(resultSet).close();
+    Mockito.verify(statement).close();
+  }
+
+  @Test
+  void testGetSystemTableMetadataParsesCompoundAndFunctionExpressions() throws 
Exception {
+    ExposedClickHouseTableOperations ops = newOps();
+    Connection connection = Mockito.mock(Connection.class);
+    PreparedStatement statement = Mockito.mock(PreparedStatement.class);
+    ResultSet resultSet = Mockito.mock(ResultSet.class);
+
+    
Mockito.when(connection.prepareStatement(Mockito.anyString())).thenReturn(statement);
+    Mockito.when(statement.executeQuery()).thenReturn(resultSet);
+    Mockito.when(resultSet.next()).thenReturn(true);
+    Mockito.when(resultSet.getString("sorting_key")).thenReturn("id, 
toDate(event_time)");
+    Mockito.when(resultSet.getString("engine_full")).thenReturn("MergeTree 
ORDER BY id");
+
+    SortOrder[] sortOrders =
+        ops.callGetSystemTableMetadata(connection, "db_name", 
"table_name").sortOrders();
+
+    Assertions.assertEquals(2, sortOrders.length);
+    Assertions.assertEquals(NamedReference.field("id"), 
sortOrders[0].expression());
+    Assertions.assertEquals(
+        FunctionExpression.of("toDate", NamedReference.field("event_time")),
+        sortOrders[1].expression());
+  }
+
+  @Test
+  void testGetSystemTableMetadataReturnsNoneForBlankValues() throws Exception {
+    ExposedClickHouseTableOperations ops = newOps();
+    Connection connection = Mockito.mock(Connection.class);
+    PreparedStatement statement = Mockito.mock(PreparedStatement.class);
+    ResultSet resultSet = Mockito.mock(ResultSet.class);
+
+    
Mockito.when(connection.prepareStatement(Mockito.anyString())).thenReturn(statement);
+    Mockito.when(statement.executeQuery()).thenReturn(resultSet);
+    Mockito.when(resultSet.next()).thenReturn(true);
+    Mockito.when(resultSet.getString("sorting_key")).thenReturn("   ");
+    Mockito.when(resultSet.getString("engine_full")).thenReturn("   ");
+
+    ClickHouseTableOperations.SystemTableMetadata metadata =
+        ops.callGetSystemTableMetadata(connection, "db_name", "table_name");
+
+    Assertions.assertArrayEquals(new SortOrder[0], metadata.sortOrders());
+    Assertions.assertTrue(metadata.settings().isEmpty());
+  }
+
+  @Test
+  void testGetSystemTableMetadataParsesSettingsFromEngineFull() throws 
Exception {
+    ExposedClickHouseTableOperations ops = newOps();
+    Connection connection = Mockito.mock(Connection.class);
+    PreparedStatement statement = Mockito.mock(PreparedStatement.class);
+    ResultSet resultSet = Mockito.mock(ResultSet.class);
+
+    
Mockito.when(connection.prepareStatement(Mockito.anyString())).thenReturn(statement);
+    Mockito.when(statement.executeQuery()).thenReturn(resultSet);
+    Mockito.when(resultSet.next()).thenReturn(true);
+    Mockito.when(resultSet.getString("sorting_key")).thenReturn("id");
+    Mockito.when(resultSet.getString("engine_full"))
+        .thenReturn(
+            "MergeTree ORDER BY id SETTINGS index_granularity = 4096, "
+                + "min_bytes_for_wide_part = 0");
+
+    Map<String, String> settings =
+        ops.callGetSystemTableMetadata(connection, "db_name", 
"table_name").settings();
+
+    Assertions.assertEquals(2, settings.size());
+    Assertions.assertEquals(
+        "4096", settings.get(TableConstants.SETTINGS_PREFIX + 
"index_granularity"));
+    Assertions.assertEquals(
+        "0", settings.get(TableConstants.SETTINGS_PREFIX + 
"min_bytes_for_wide_part"));
+  }
+
+  @Test
+  void testGetSystemTableMetadataThrowsWhenTableIsNotVisible() throws 
Exception {
+    ExposedClickHouseTableOperations ops = newOps();
+    Connection connection = Mockito.mock(Connection.class);
+    PreparedStatement statement = Mockito.mock(PreparedStatement.class);
+    ResultSet resultSet = Mockito.mock(ResultSet.class);
+
+    
Mockito.when(connection.prepareStatement(Mockito.anyString())).thenReturn(statement);
+    Mockito.when(statement.executeQuery()).thenReturn(resultSet);
+    Mockito.when(resultSet.next()).thenReturn(false);
+
+    NoSuchTableException exception =
+        Assertions.assertThrows(
+            NoSuchTableException.class,
+            () -> ops.callGetSystemTableMetadata(connection, "db_name", 
"table_name"));
+
+    Assertions.assertTrue(exception.getMessage().contains("table_name"));
+    Assertions.assertTrue(exception.getMessage().contains("db_name"));
+  }
+
   // 
---------------------------------------------------------------------------
   // extractEngineParams
   // 
---------------------------------------------------------------------------

Reply via email to