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]