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

yiguolei pushed a commit to branch branch-4.1
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.1 by this push:
     new 1f85a05bece [fix](jdbc) Resolve missing remote table names in schema 
lookup (#65718)
1f85a05bece is described below

commit 1f85a05becefbbe5b21187ff92581e502fa19cc9
Author: Gabriel <[email protected]>
AuthorDate: Fri Jul 17 09:01:54 2026 +0800

    [fix](jdbc) Resolve missing remote table names in schema lookup (#65718)
    
    ### What problem does this PR solve?
    
    Issue Number: None
    
    On branch-4.1, `ExternalTable#getRemoteName()` defines the local table
    name as the fallback when the stored remote name is null or empty.
    `JdbcExternalTable#initSchema()` bypassed that API and passed the
    nullable field directly to JDBC metadata and identifier mapping.
    
    For JDBC, a null table pattern can enumerate columns from every table in
    the database. This can turn one table schema lookup into a multi-table
    result and produce misleading case-insensitive column conflicts for a
    `db.null` remote table.
    
    The same code path already uses the effective name for debug logging,
    comments, and scan table construction; schema initialization was the
    inconsistent path.
    
    ### How was it fixed?
    
    Resolve the effective remote table name once at the start of
    `initSchema()` and reuse it for:
    
    - JDBC column metadata lookup
    - remote-to-local column identifier mapping
    - conflict diagnostics
    
    Explicit remote-name mappings are preserved because `getRemoteName()`
    returns the stored value when present.
    
    ### Release note
    
    Fix JDBC external table schema lookup when a 4.1 table object has a
    missing remote table name.
    
    ### Check List (For Author)
    
    - Test
        - [x] Unit Test: `JdbcExternalTableTest` (2 tests)
    - [x] Red/green verified: before the fix the metadata call received null
    and the error contained `remote_db.null`; after the fix both tests pass
    - [x] `./run-fe-ut.sh --run
    org.apache.doris.datasource.jdbc.JdbcExternalTableTest` — full 28-module
    FE reactor `BUILD SUCCESS`
    - Behavior changed:
    - [x] Yes. A missing JDBC remote table name now falls back to the local
    table name instead of using JDBC null-pattern semantics.
    - Does this need documentation?
        - [x] No.
    
    Related: #65708 was closed because master has already migrated JDBC
    catalogs to the plugin-driven path and does not contain this branch-4.1
    code path.
---
 .../doris/datasource/jdbc/JdbcExternalTable.java   |  14 ++-
 .../jdbc/client/JdbcClickHouseClient.java          |   5 +
 .../doris/datasource/jdbc/client/JdbcClient.java   |  18 ++-
 .../datasource/jdbc/client/JdbcGbaseClient.java    |   8 ++
 .../datasource/jdbc/client/JdbcMySQLClient.java    |  19 +++
 .../datasource/jdbc/client/JdbcOracleClient.java   |   7 +-
 .../datasource/jdbc/JdbcExternalTableTest.java     | 113 +++++++++++++++++
 .../datasource/jdbc/client/JdbcClientTest.java     | 138 +++++++++++++++++++++
 8 files changed, 307 insertions(+), 15 deletions(-)

diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/JdbcExternalTable.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/JdbcExternalTable.java
index 0ba221f5d87..98581a29c4a 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/JdbcExternalTable.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/JdbcExternalTable.java
@@ -134,12 +134,15 @@ public class JdbcExternalTable extends ExternalTable {
     @Override
     public Optional<SchemaCacheValue> initSchema() {
         String remoteDbName = ((ExternalDatabase<?>) 
this.getDatabase()).getRemoteName();
+        // A missing table pattern makes JDBC enumerate every table in the 
database, so honor the
+        // effective-name fallback before any metadata or identifier-mapping 
call.
+        String remoteTableName = getRemoteName();
         if (DebugPointUtil.isEnable("JdbcExternalTable.initSchema.sleep")) {
             long sleepMs = DebugPointUtil.getDebugParamOrDefault(
                     "JdbcExternalTable.initSchema.sleep", "sleepMs", 0L);
             if (sleepMs > 0) {
                 LOG.info("debug point JdbcExternalTable.initSchema.sleep hit 
for {}.{}, sleep {}ms",
-                        remoteDbName, getRemoteName(), sleepMs);
+                        remoteDbName, remoteTableName, sleepMs);
                 try {
                     Thread.sleep(sleepMs);
                 } catch (InterruptedException ignore) {
@@ -149,7 +152,7 @@ public class JdbcExternalTable extends ExternalTable {
         }
 
         // 1. Retrieve remote column information
-        List<Column> columns = ((JdbcExternalCatalog) 
catalog).listColumns(remoteDbName, remoteName);
+        List<Column> columns = ((JdbcExternalCatalog) 
catalog).listColumns(remoteDbName, remoteTableName);
         if (columns == null || columns.isEmpty()) {
             return Optional.empty();
         }
@@ -161,7 +164,7 @@ public class JdbcExternalTable extends ExternalTable {
         List<String> localColumnNames = 
Lists.newArrayListWithCapacity(remoteColumnNames.size());
         for (String remoteColName : remoteColumnNames) {
             String localName = ((JdbcExternalCatalog) 
catalog).getIdentifierMapping()
-                    .fromRemoteColumnName(remoteDbName, remoteName, 
remoteColName);
+                    .fromRemoteColumnName(remoteDbName, remoteTableName, 
remoteColName);
             localColumnNames.add(localName);
         }
 
@@ -186,7 +189,7 @@ public class JdbcExternalTable extends ExternalTable {
                     "Found conflicting column names under case-insensitive 
conditions. "
                             + "Conflicting column names: %s in remote table 
'%s.%s' under catalog '%s'. "
                             + "Please use meta_names_mapping to handle name 
mapping.",
-                    String.join(", ", conflicts), remoteDbName, remoteName, 
catalog.getName()));
+                    String.join(", ", conflicts), remoteDbName, 
remoteTableName, catalog.getName()));
         }
 
         // 5. Update column objects with local names
@@ -239,7 +242,8 @@ public class JdbcExternalTable extends ExternalTable {
         Map<String, String> params = new HashMap<>();
         params.put("ctlName", catalog.getName());
         params.put("dbName", this.db.getRemoteName());
-        params.put("tblName", this.remoteName);
+        // Keep row count lookup consistent with schema and scan paths when 
the stored remote name is absent.
+        params.put("tblName", getRemoteName());
         switch (((JdbcExternalCatalog) catalog).getDatabaseTypeName()) {
             case JdbcResource.MYSQL:
                 params.put("sql", MYSQL_ROW_COUNT_SQL);
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClickHouseClient.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClickHouseClient.java
index 4f340bebed4..3837b7bbc79 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClickHouseClient.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClickHouseClient.java
@@ -121,6 +121,11 @@ public class JdbcClickHouseClient extends JdbcClient {
         }
     }
 
+    @Override
+    protected String getRemoteDatabaseName(ResultSet resultSet) throws 
SQLException {
+        return resultSet.getString(databaseTermIsCatalog ? "TABLE_CAT" : 
"TABLE_SCHEM");
+    }
+
     @Override
     protected String[] getTableTypes() {
         return new String[] {"TABLE", "VIEW", "SYSTEM TABLE"};
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClient.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClient.java
index 8b496f82dd7..85ce0b024cd 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClient.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcClient.java
@@ -397,6 +397,9 @@ public abstract class JdbcClient {
             String catalogName = getCatalogName(conn);
             rs = getRemoteColumns(databaseMetaData, catalogName, remoteDbName, 
remoteTableName);
             while (rs.next()) {
+                if (!isExactTable(databaseMetaData, rs, remoteDbName, 
remoteTableName)) {
+                    continue;
+                }
                 tableSchema.add(new JdbcFieldSchema(rs));
             }
         } catch (SQLException e) {
@@ -487,15 +490,22 @@ public abstract class JdbcClient {
         return remoteTableName;
     }
 
-    protected boolean isTableModified(String modifiedTableName, String 
actualTableName) {
-        return false;
-    }
-
     protected ResultSet getRemoteColumns(DatabaseMetaData databaseMetaData, 
String catalogName, String remoteDbName,
             String remoteTableName) throws SQLException {
         return databaseMetaData.getColumns(catalogName, remoteDbName, 
remoteTableName, null);
     }
 
+    protected boolean isExactTable(DatabaseMetaData databaseMetaData, 
ResultSet resultSet,
+            String remoteDbName, String remoteTableName) throws SQLException {
+        // JDBC treats schema and table names as patterns, so verify both 
identities on returned rows.
+        return remoteDbName.equals(getRemoteDatabaseName(resultSet))
+                && remoteTableName.equals(resultSet.getString("TABLE_NAME"));
+    }
+
+    protected String getRemoteDatabaseName(ResultSet resultSet) throws 
SQLException {
+        return resultSet.getString("TABLE_SCHEM");
+    }
+
     protected List<String> filterDatabaseNames(List<String> remoteDbNames) {
         Set<String> filterInternalDatabases = getFilterInternalDatabases();
         List<String> filteredDatabaseNames = Lists.newArrayList();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcGbaseClient.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcGbaseClient.java
index 6121ef2dbfc..de2e2cb02ec 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcGbaseClient.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcGbaseClient.java
@@ -96,6 +96,9 @@ public class JdbcGbaseClient extends JdbcClient {
             String catalogName = getCatalogName(conn);
             rs = getRemoteColumns(databaseMetaData, catalogName, remoteDbName, 
remoteTableName);
             while (rs.next()) {
+                if (!isExactTable(databaseMetaData, rs, remoteDbName, 
remoteTableName)) {
+                    continue;
+                }
                 JdbcFieldSchema field = new JdbcFieldSchema(rs);
                 tableSchema.add(field);
             }
@@ -108,6 +111,11 @@ public class JdbcGbaseClient extends JdbcClient {
         return tableSchema;
     }
 
+    @Override
+    protected String getRemoteDatabaseName(ResultSet resultSet) throws 
SQLException {
+        return resultSet.getString("TABLE_CAT");
+    }
+
     @Override
     protected Type jdbcTypeToDoris(JdbcFieldSchema fieldSchema) {
         switch (fieldSchema.getDataType()) {
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcMySQLClient.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcMySQLClient.java
index 88e69fa9c1d..154664332f6 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcMySQLClient.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcMySQLClient.java
@@ -187,6 +187,9 @@ public class JdbcMySQLClient extends JdbcClient {
             }
 
             while (rs.next()) {
+                if (!isExactTable(databaseMetaData, rs, remoteDbName, 
remoteTableName)) {
+                    continue;
+                }
                 JdbcFieldSchema field = new JdbcFieldSchema(rs, 
mapFieldtoType);
                 tableSchema.add(field);
             }
@@ -199,6 +202,22 @@ public class JdbcMySQLClient extends JdbcClient {
         return tableSchema;
     }
 
+    @Override
+    protected boolean isExactTable(DatabaseMetaData databaseMetaData, 
ResultSet resultSet,
+            String remoteDbName, String remoteTableName) throws SQLException {
+        String actualDbName = getRemoteDatabaseName(resultSet);
+        String actualTableName = resultSet.getString("TABLE_NAME");
+        // Connector/J reflects lower_case_table_names through 
supportsMixedCaseIdentifiers().
+        return databaseMetaData.supportsMixedCaseIdentifiers()
+                ? remoteDbName.equals(actualDbName) && 
remoteTableName.equals(actualTableName)
+                : remoteDbName.equalsIgnoreCase(actualDbName) && 
remoteTableName.equalsIgnoreCase(actualTableName);
+    }
+
+    @Override
+    protected String getRemoteDatabaseName(ResultSet resultSet) throws 
SQLException {
+        return resultSet.getString("TABLE_CAT");
+    }
+
     @Override
     public List<String> getPrimaryKeys(String remoteDbName, String 
remoteTableName) {
         Connection conn = getConnection();
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcOracleClient.java
 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcOracleClient.java
index 996608545cd..96b7d9605cb 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcOracleClient.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/datasource/jdbc/client/JdbcOracleClient.java
@@ -76,7 +76,7 @@ public class JdbcOracleClient extends JdbcClient {
                 rs = getRemoteColumns(databaseMetaData, catalogName, 
remoteDbName, remoteTableName);
             }
             while (rs.next()) {
-                if (isModify && isTableModified(rs.getString("TABLE_NAME"), 
remoteTableName)) {
+                if (!isExactTable(databaseMetaData, rs, remoteDbName, 
remoteTableName)) {
                     continue;
                 }
                 tableSchema.add(new JdbcFieldSchema(rs));
@@ -112,11 +112,6 @@ public class JdbcOracleClient extends JdbcClient {
         return remoteTableName.replace("/", "%");
     }
 
-    @Override
-    protected boolean isTableModified(String modifiedTableName, String 
actualTableName) {
-        return !modifiedTableName.equals(actualTableName);
-    }
-
     @Override
     protected Set<String> getFilterInternalDatabases() {
         return ImmutableSet.<String>builder()
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/jdbc/JdbcExternalTableTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/jdbc/JdbcExternalTableTest.java
new file mode 100644
index 00000000000..0eefa4b2e0a
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/jdbc/JdbcExternalTableTest.java
@@ -0,0 +1,113 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.datasource.jdbc;
+
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.JdbcResource;
+import org.apache.doris.catalog.PrimitiveType;
+import org.apache.doris.datasource.SchemaCacheValue;
+import org.apache.doris.datasource.mapping.IdentifierMapping;
+
+import com.google.common.collect.Lists;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mockito;
+
+import java.util.Map;
+import java.util.Optional;
+
+public class JdbcExternalTableTest {
+    private JdbcExternalCatalog catalog;
+    private JdbcExternalDatabase database;
+    private IdentifierMapping identifierMapping;
+    private TestJdbcExternalTable table;
+
+    @Before
+    public void setUp() {
+        catalog = Mockito.mock(JdbcExternalCatalog.class);
+        database = Mockito.mock(JdbcExternalDatabase.class);
+        identifierMapping = Mockito.mock(IdentifierMapping.class);
+
+        Mockito.when(catalog.getName()).thenReturn("test_catalog");
+        
Mockito.when(catalog.getIdentifierMapping()).thenReturn(identifierMapping);
+        Mockito.when(database.getFullName()).thenReturn("local_db");
+        Mockito.when(database.getRemoteName()).thenReturn("remote_db");
+        Mockito.when(identifierMapping.fromRemoteColumnName(
+                        Mockito.anyString(), 
ArgumentMatchers.nullable(String.class), Mockito.anyString()))
+                .thenAnswer(invocation -> invocation.getArgument(2));
+
+        table = new TestJdbcExternalTable(1L, "local_table", null, catalog, 
database);
+    }
+
+    @Test
+    public void testInitSchemaUsesEffectiveRemoteTableName() {
+        Mockito.when(catalog.listColumns(
+                        Mockito.anyString(), 
ArgumentMatchers.nullable(String.class)))
+                .thenReturn(Lists.newArrayList(new Column("id", 
PrimitiveType.INT)));
+
+        Optional<SchemaCacheValue> schema = table.initSchema();
+
+        Assert.assertTrue(schema.isPresent());
+        Mockito.verify(catalog).listColumns("remote_db", "local_table");
+        Mockito.verify(identifierMapping).fromRemoteColumnName("remote_db", 
"local_table", "id");
+    }
+
+    @Test
+    public void testConflictMessageUsesEffectiveRemoteTableName() {
+        Mockito.when(catalog.listColumns(
+                        Mockito.anyString(), 
ArgumentMatchers.nullable(String.class)))
+                .thenReturn(Lists.newArrayList(
+                        new Column("id", PrimitiveType.INT),
+                        new Column("ID", PrimitiveType.INT)));
+
+        RuntimeException exception = 
Assert.assertThrows(RuntimeException.class, table::initSchema);
+
+        Assert.assertTrue(exception.getMessage(),
+                exception.getMessage().contains("remote table 
'remote_db.local_table'"));
+    }
+
+    @Test
+    public void testFetchRowCountUsesEffectiveRemoteTableName() {
+        
Mockito.when(catalog.getDatabaseTypeName()).thenReturn(JdbcResource.MYSQL);
+
+        Assert.assertEquals(1L, table.fetchRowCount());
+
+        Assert.assertEquals("local_table", 
table.rowCountParams.get("tblName"));
+    }
+
+    private static class TestJdbcExternalTable extends JdbcExternalTable {
+        private Map<String, String> rowCountParams;
+
+        TestJdbcExternalTable(long id, String name, String remoteName,
+                JdbcExternalCatalog catalog, JdbcExternalDatabase database) {
+            super(id, name, remoteName, catalog, database);
+        }
+
+        @Override
+        protected synchronized void makeSureInitialized() {
+        }
+
+        @Override
+        protected long getRowCount(Map<String, String> params) {
+            rowCountParams = params;
+            return 1L;
+        }
+    }
+}
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/datasource/jdbc/client/JdbcClientTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/jdbc/client/JdbcClientTest.java
new file mode 100644
index 00000000000..149408616dc
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/datasource/jdbc/client/JdbcClientTest.java
@@ -0,0 +1,138 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+package org.apache.doris.datasource.jdbc.client;
+
+import org.apache.doris.datasource.jdbc.util.JdbcFieldSchema;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.Types;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class JdbcClientTest {
+
+    @Test
+    public void testGetJdbcColumnsInfoFiltersWildcardSiblingTable() throws 
Exception {
+        JdbcClient client = Mockito.mock(JdbcClient.class, 
Mockito.CALLS_REAL_METHODS);
+        ResultSet resultSet = mockColumns(
+                new String[] {"remote_db", "remote_db"},
+                new String[] {"localXtable", "local_table"},
+                new String[] {"sibling_column", "target_column"});
+        mockMetadata(client, resultSet, "catalog", "remote_db", "local_table");
+
+        List<JdbcFieldSchema> columns = client.getJdbcColumnsInfo("remote_db", 
"local_table");
+
+        Assert.assertEquals(1, columns.size());
+        Assert.assertEquals("target_column", columns.get(0).getColumnName());
+    }
+
+    @Test
+    public void testGetJdbcColumnsInfoFiltersWildcardSiblingSchema() throws 
Exception {
+        JdbcClient client = Mockito.mock(JdbcClient.class, 
Mockito.CALLS_REAL_METHODS);
+        ResultSet resultSet = mockColumns(
+                new String[] {"salesX2024", "sales_2024"},
+                new String[] {"orders", "orders"},
+                new String[] {"sibling_column", "target_column"});
+        mockMetadata(client, resultSet, "catalog", "sales_2024", "orders");
+
+        List<JdbcFieldSchema> columns = 
client.getJdbcColumnsInfo("sales_2024", "orders");
+
+        Assert.assertEquals(1, columns.size());
+        Assert.assertEquals("target_column", columns.get(0).getColumnName());
+    }
+
+    @Test
+    public void testMySqlColumnsAcceptCanonicalLowercaseTableName() throws 
Exception {
+        JdbcMySQLClient client = Mockito.mock(JdbcMySQLClient.class, 
Mockito.CALLS_REAL_METHODS);
+        ResultSet resultSet = mockColumns(
+                new String[] {"remote_db"},
+                new String[] {"tusers"},
+                new String[] {"target_column"});
+        DatabaseMetaData databaseMetaData = mockMetadata(client, resultSet, 
null, "Remote_DB", "TUsers");
+        
Mockito.when(databaseMetaData.supportsMixedCaseIdentifiers()).thenReturn(false);
+
+        List<JdbcFieldSchema> columns = client.getJdbcColumnsInfo("Remote_DB", 
"TUsers");
+
+        Assert.assertEquals(1, columns.size());
+        Assert.assertEquals("target_column", columns.get(0).getColumnName());
+    }
+
+    private DatabaseMetaData mockMetadata(JdbcClient client, ResultSet 
resultSet,
+            String catalogName, String remoteDbName, String remoteTableName) 
throws Exception {
+        Connection connection = Mockito.mock(Connection.class);
+        DatabaseMetaData databaseMetaData = 
Mockito.mock(DatabaseMetaData.class);
+        Mockito.doReturn(connection).when(client).getConnection();
+        Mockito.when(connection.getMetaData()).thenReturn(databaseMetaData);
+        Mockito.when(connection.getCatalog()).thenReturn(catalogName);
+        if (client instanceof JdbcMySQLClient) {
+            Mockito.when(databaseMetaData.getColumns(remoteDbName, null, 
remoteTableName, null))
+                    .thenReturn(resultSet);
+        } else {
+            Mockito.when(databaseMetaData.getColumns(catalogName, 
remoteDbName, remoteTableName, null))
+                    .thenReturn(resultSet);
+        }
+        return databaseMetaData;
+    }
+
+    private ResultSet mockColumns(String[] databaseNames, String[] tableNames, 
String[] columnNames)
+            throws Exception {
+        ResultSet resultSet = Mockito.mock(ResultSet.class);
+        AtomicInteger row = new AtomicInteger(-1);
+        Mockito.when(resultSet.next()).thenAnswer(invocation -> 
row.incrementAndGet() < tableNames.length);
+        
Mockito.when(resultSet.getString(Mockito.anyString())).thenAnswer(invocation -> 
{
+            String columnLabel = invocation.getArgument(0);
+            if ("TABLE_SCHEM".equals(columnLabel) || 
"TABLE_CAT".equals(columnLabel)) {
+                return databaseNames[row.get()];
+            }
+            if ("TABLE_NAME".equals(columnLabel)) {
+                return tableNames[row.get()];
+            }
+            if ("COLUMN_NAME".equals(columnLabel)) {
+                return columnNames[row.get()];
+            }
+            if ("TYPE_NAME".equals(columnLabel)) {
+                return "INT";
+            }
+            return null;
+        });
+        
Mockito.when(resultSet.getInt(Mockito.anyString())).thenAnswer(invocation -> {
+            String columnLabel = invocation.getArgument(0);
+            if ("DATA_TYPE".equals(columnLabel)) {
+                return Types.INTEGER;
+            }
+            if ("COLUMN_SIZE".equals(columnLabel)) {
+                return 11;
+            }
+            if ("NUM_PREC_RADIX".equals(columnLabel)) {
+                return 10;
+            }
+            if ("NULLABLE".equals(columnLabel)) {
+                return DatabaseMetaData.columnNullable;
+            }
+            return 0;
+        });
+        Mockito.when(resultSet.wasNull()).thenReturn(false);
+        return resultSet;
+    }
+}


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

Reply via email to