fix(jdbc): fall back to unscoped schema listing for HANA

Closes #11209
This commit is contained in:
zipg
2026-10-08 13:06:25 +08:00
committed by GitHub
parent 46366b00ae
commit 5630f8b7f8
2 changed files with 64 additions and 0 deletions
@@ -65,6 +65,15 @@ public final class StandardJdbcMetadata {
} catch (Exception | AbstractMethodError ignored) {
}
}
// HANA's databaseName (for example HXE) is not a JDBC catalog.
// Its driver returns no rows, rather than throwing, for that catalog.
// Retry without it before falling back to only the current schema.
if (names.isEmpty() && catalog != null && profile.getCatalogFallbackEnabled()) {
try {
appendSchemas(names, meta.getSchemas(null, null));
} catch (Exception | AbstractMethodError ignored) {
}
}
try {
addNonBlank(names, conn.getSchema());
} catch (Exception | AbstractMethodError ignored) {
@@ -10,12 +10,14 @@ import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -48,10 +50,12 @@ class StandardJdbcMetadataTest {
@Test
void scopesSchemasToTheConnectionCatalog() {
AtomicReference<Object[]> capturedArgs = new AtomicReference<>();
AtomicInteger schemaCalls = new AtomicInteger();
DatabaseMetaData meta = proxy(DatabaseMetaData.class, new MethodHandler() {
@Override
public Object handle(Method method, Object[] args) {
if ("getSchemas".equals(method.getName())) {
schemaCalls.incrementAndGet();
capturedArgs.set(args);
return rows(row("TABLE_SCHEM", "APP"));
}
@@ -74,6 +78,57 @@ class StandardJdbcMetadataTest {
assertEquals(List.of("APP"), StandardJdbcMetadata.INSTANCE.listSchemas(conn, profile, "initial_catalog"));
assertEquals("regular_catalog", capturedArgs.get()[0]);
assertEquals(null, capturedArgs.get()[1]);
assertEquals(1, schemaCalls.get());
}
@ParameterizedTest
@CsvSource({"false, false, true", "true, false, true", "false, true, true", "false, false, false"})
void retriesEmptyCatalogSchemasBeforeAddingCurrentSchema(boolean fallbackFails, boolean currentCatalog, boolean fallbackEnabled) {
List<String> catalogs = new ArrayList<>();
DatabaseMetaData meta = proxy(DatabaseMetaData.class, new MethodHandler() {
@Override
public Object handle(Method method, Object[] args) {
if ("getSchemas".equals(method.getName())) {
String catalog = (String) args[0];
catalogs.add(catalog);
if (catalog != null) {
return rows();
}
if (fallbackFails) {
throw new UnsupportedOperationException("Unscoped schemas unavailable");
}
return rows(row("TABLE_SCHEM", "DBX_USER"), row("TABLE_SCHEM", "DBX_DEMO"), row("TABLE_SCHEM", "SYS"));
}
return defaultValue(method.getReturnType());
}
});
Connection conn = proxy(Connection.class, new MethodHandler() {
@Override
public Object handle(Method method, Object[] args) {
if ("getMetaData".equals(method.getName())) {
return meta;
}
if ("getCatalog".equals(method.getName())) {
return currentCatalog ? "HXE" : null;
}
if ("getSchema".equals(method.getName())) {
return "DBX_USER";
}
return defaultValue(method.getReturnType());
}
});
JdbcAgentProfile effectiveProfile = fallbackEnabled ? profile : new JdbcAgentProfile("example.Driver", "jdbc:example:") {
@Override
public boolean getCatalogFallbackEnabled() {
return false;
}
};
assertEquals(
fallbackFails || !fallbackEnabled ? List.of("DBX_USER") : List.of("DBX_DEMO", "DBX_USER"),
StandardJdbcMetadata.INSTANCE.listSchemas(conn, effectiveProfile, currentCatalog ? "initial_catalog" : "HXE")
);
assertEquals(fallbackEnabled ? Arrays.asList("HXE", null) : List.of("HXE"), catalogs);
}
@Test