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

hello-stephen 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 57e8aa36eba [fix](information_schema) Prune TABLE_COMMENT in tables 
status scan for external tables (#65989)
57e8aa36eba is described below

commit 57e8aa36ebac600037852c1c564bc0a5648cad60
Author: Raghvendra Singh <[email protected]>
AuthorDate: Mon Oct 19 15:13:57 2026 +0530

    [fix](information_schema) Prune TABLE_COMMENT in tables status scan for 
external tables (#65989)
    
    Related PR: #64933, #64263
    
    Problem Summary:
    
    `information_schema.tables` on an external Iceberg catalog issues one
    blocking `loadTable` against the remote catalog for every table in the
    catalog, even when the query only projects cheap columns like
    `TABLE_SCHEMA`/`TABLE_NAME`. A listing of a few-thousand-table REST
    catalog takes minutes and floods the catalog service with per-table GETs
    (measured: 3,882 tables → 2m52s, 4,365 tables → 5m38s).
    
    This is an interaction of two changes: #64263 made
    `IcebergExternalTable.getComment()` read the comment from table metadata
    (a remote table load), and #64933's required-columns pruning in
    `listTableStatus` guards every status column except the
    `status.setComment(...)` fill, which remained unconditional — so the one
    column whose fill is a remote round-trip is also the one column that is
    always computed.
    
    Fix: guard the comment fill with the same
    `needTableStatusColumn(requiredColumns, "TABLE_COMMENT")` check used by
    the other status columns. Callers that do not project `TABLE_COMMENT`
    skip the remote loads entirely; explicit `TABLE_COMMENT` projections
    keep the #64263 behavior. When `required_columns` is not set (older BE),
    behavior is unchanged (fill everything), consistent with the rest of the
    pruning.
    
    ### Release note
    
    Fix information_schema.tables scans on external catalogs loading every
    table's metadata from the remote catalog when TABLE_COMMENT is not
    projected.
    
    Signed-off-by: Raghvendra Singh <[email protected]>
---
 .../apache/doris/service/FrontendServiceImpl.java  |   9 +-
 .../doris/service/FrontendServiceImplTest.java     | 118 +++++++++++++++++++++
 .../test_tables_status_column_pruning.groovy       |  33 ++++++
 3 files changed, 159 insertions(+), 1 deletion(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java 
b/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java
index 03d5a2c9f8d..ee1e5fa9f19 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/service/FrontendServiceImpl.java
@@ -747,7 +747,14 @@ public class FrontendServiceImpl implements 
FrontendService.Iface {
                             TTableStatus status = new TTableStatus();
                             status.setName(table.getName());
                             status.setType(table.getMysqlType());
-                            status.setComment(table.getComment());
+                            // TABLE_COMMENT is a required Thrift field, so it 
must always be
+                            // set — but for some external tables (e.g. 
Iceberg since #64263)
+                            // getComment() loads the full table metadata from 
the remote
+                            // catalog. Only pay that lookup when the scan 
actually projects
+                            // TABLE_COMMENT, same as the other pruned status 
columns.
+                            
status.setComment(needTableStatusColumn(requiredColumns, "TABLE_COMMENT")
+                                    ? table.getComment()
+                                    : "");
                             if (needTableStatusColumn(requiredColumns, 
"ENGINE")) {
                                 status.setEngine(table.getEngine());
                             }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java
index 6c2cf4aa08c..543ab7ebae7 100644
--- 
a/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/service/FrontendServiceImplTest.java
@@ -313,6 +313,124 @@ public class FrontendServiceImplTest extends 
TestWithFeService {
         }
     }
 
+    @Test
+    public void testListTableStatusPrunesCommentLookupWhenNotProjected() 
throws Exception {
+        String createOlapTblStmt = "CREATE TABLE 
test.prune_comment_status_column(\n"
+                + "    k1 INT,\n"
+                + "    v1 INT\n"
+                + ")\n"
+                + "DUPLICATE KEY(k1)\n"
+                + "DISTRIBUTED BY HASH(k1) BUCKETS 1\n"
+                + "PROPERTIES(\"replication_num\" = \"1\");";
+        createTable(createOlapTblStmt);
+        Database db = 
Env.getCurrentInternalCatalog().getDbOrAnalysisException("test");
+        OlapTable table = (OlapTable) 
db.getTableOrAnalysisException("prune_comment_status_column");
+        OlapTable spyTable = Mockito.spy(table);
+        db.unregisterTable(table.getName());
+        db.registerTable(spyTable);
+        try {
+            FrontendServiceImpl impl = new FrontendServiceImpl(exeEnv);
+            TGetTablesParams params = new TGetTablesParams();
+            params.setCatalog(InternalCatalog.INTERNAL_CATALOG_NAME);
+            params.setDb("test");
+            params.setTable("prune_comment_status_column");
+            params.setRequiredColumns(Collections.singleton("UPDATE_TIME"));
+            
params.setCurrentUserIdent(connectContext.getCurrentUserIdentity().toThrift());
+
+            TListTableStatusResult result = impl.listTableStatus(params);
+
+            Assertions.assertEquals(1, result.getTablesSize());
+            TTableStatus status = result.getTables().get(0);
+            // comment is a required Thrift field: it must be set (empty 
placeholder),
+            // and the response must pass Thrift validation without the lookup.
+            Assertions.assertTrue(status.isSetComment());
+            Assertions.assertEquals("", status.getComment());
+            status.validate();
+            Mockito.verify(spyTable, Mockito.never()).getComment();
+        } finally {
+            db.unregisterTable(spyTable.getName());
+            db.registerTable(table);
+        }
+    }
+
+    @Test
+    public void testListTableStatusFillsCommentWhenProjected() throws 
Exception {
+        String createOlapTblStmt = "CREATE TABLE 
test.fill_comment_status_column(\n"
+                + "    k1 INT,\n"
+                + "    v1 INT\n"
+                + ")\n"
+                + "DUPLICATE KEY(k1)\n"
+                + "DISTRIBUTED BY HASH(k1) BUCKETS 1\n"
+                + "PROPERTIES(\"replication_num\" = \"1\");";
+        createTable(createOlapTblStmt);
+        Database db = 
Env.getCurrentInternalCatalog().getDbOrAnalysisException("test");
+        OlapTable table = (OlapTable) 
db.getTableOrAnalysisException("fill_comment_status_column");
+        OlapTable spyTable = Mockito.spy(table);
+        Mockito.doReturn("projected comment").when(spyTable).getComment();
+        db.unregisterTable(table.getName());
+        db.registerTable(spyTable);
+        try {
+            FrontendServiceImpl impl = new FrontendServiceImpl(exeEnv);
+            TGetTablesParams params = new TGetTablesParams();
+            params.setCatalog(InternalCatalog.INTERNAL_CATALOG_NAME);
+            params.setDb("test");
+            params.setTable("fill_comment_status_column");
+            params.setRequiredColumns(Collections.singleton("TABLE_COMMENT"));
+            
params.setCurrentUserIdent(connectContext.getCurrentUserIdentity().toThrift());
+
+            TListTableStatusResult result = impl.listTableStatus(params);
+
+            Assertions.assertEquals(1, result.getTablesSize());
+            TTableStatus status = result.getTables().get(0);
+            Assertions.assertTrue(status.isSetComment());
+            Assertions.assertEquals("projected comment", status.getComment());
+            status.validate();
+            Mockito.verify(spyTable, Mockito.atLeastOnce()).getComment();
+        } finally {
+            db.unregisterTable(spyTable.getName());
+            db.registerTable(table);
+        }
+    }
+
+    @Test
+    public void testListTableStatusFillsCommentWhenRequiredColumnsUnset() 
throws Exception {
+        String createOlapTblStmt = "CREATE TABLE 
test.legacy_comment_status_column(\n"
+                + "    k1 INT,\n"
+                + "    v1 INT\n"
+                + ")\n"
+                + "DUPLICATE KEY(k1)\n"
+                + "DISTRIBUTED BY HASH(k1) BUCKETS 1\n"
+                + "PROPERTIES(\"replication_num\" = \"1\");";
+        createTable(createOlapTblStmt);
+        Database db = 
Env.getCurrentInternalCatalog().getDbOrAnalysisException("test");
+        OlapTable table = (OlapTable) 
db.getTableOrAnalysisException("legacy_comment_status_column");
+        OlapTable spyTable = Mockito.spy(table);
+        Mockito.doReturn("legacy comment").when(spyTable).getComment();
+        db.unregisterTable(table.getName());
+        db.registerTable(spyTable);
+        try {
+            FrontendServiceImpl impl = new FrontendServiceImpl(exeEnv);
+            TGetTablesParams params = new TGetTablesParams();
+            params.setCatalog(InternalCatalog.INTERNAL_CATALOG_NAME);
+            params.setDb("test");
+            params.setTable("legacy_comment_status_column");
+            // required_columns unset: an older BE — everything is filled.
+            
params.setCurrentUserIdent(connectContext.getCurrentUserIdentity().toThrift());
+
+            TListTableStatusResult result = impl.listTableStatus(params);
+
+            Assertions.assertEquals(1, result.getTablesSize());
+            TTableStatus status = result.getTables().get(0);
+            Assertions.assertTrue(status.isSetComment());
+            Assertions.assertEquals("legacy comment", status.getComment());
+            status.validate();
+            Mockito.verify(spyTable, Mockito.atLeastOnce()).getComment();
+        } finally {
+            db.unregisterTable(spyTable.getName());
+            db.registerTable(table);
+        }
+    }
+
     @Test
     public void 
testListTableStatusPrunesAllOptionalColumnsWhenRequiredColumnsIsEmpty() throws 
Exception {
         String createOlapTblStmt = "CREATE TABLE 
test.prune_all_optional_status_columns(\n"
diff --git 
a/regression-test/suites/information_schema_p0/test_tables_status_column_pruning.groovy
 
b/regression-test/suites/information_schema_p0/test_tables_status_column_pruning.groovy
index e6a3245d580..77c83e2fa19 100644
--- 
a/regression-test/suites/information_schema_p0/test_tables_status_column_pruning.groovy
+++ 
b/regression-test/suites/information_schema_p0/test_tables_status_column_pruning.groovy
@@ -38,6 +38,7 @@ suite("test_tables_status_column_pruning") {
         contains "col=TABLE_SCHEMA"
         contains "col=UPDATE_TIME"
         notContains "col=TABLE_NAME"
+        notContains "col=TABLE_COMMENT"
         assertNoTabletStatusColumns.delegate = delegate
         assertNoTabletStatusColumns.call()
     }
@@ -93,5 +94,37 @@ suite("test_tables_status_column_pruning") {
         contains "col=DATA_LENGTH"
         contains "col=AVG_ROW_LENGTH"
         contains "col=INDEX_LENGTH"
+        contains "col=TABLE_COMMENT"
+    }
+
+    // TABLE_COMMENT pruning: for external tables the comment fill can be a
+    // remote metadata load, so a projection without it must not request it.
+    explain {
+        verbose true
+        sql """
+            SELECT TABLE_SCHEMA, TABLE_NAME
+            FROM information_schema.tables
+            WHERE TABLE_SCHEMA = 'information_schema'
+        """
+        contains "TABLE: information_schema.tables"
+        contains "col=TABLE_SCHEMA"
+        contains "col=TABLE_NAME"
+        notContains "col=TABLE_COMMENT"
+        assertNoTabletStatusColumns.delegate = delegate
+        assertNoTabletStatusColumns.call()
+    }
+
+    explain {
+        verbose true
+        sql """
+            SELECT TABLE_NAME, TABLE_COMMENT
+            FROM information_schema.tables
+            WHERE TABLE_SCHEMA = 'information_schema'
+        """
+        contains "TABLE: information_schema.tables"
+        contains "col=TABLE_NAME"
+        contains "col=TABLE_COMMENT"
+        assertNoTabletStatusColumns.delegate = delegate
+        assertNoTabletStatusColumns.call()
     }
 }


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

Reply via email to