fix(jdbc): include routines in object metadata

This commit is contained in:
t8y2 2026-07-11 09:20:47 +08:00
parent 5d5f7feb4a
commit fda65621e2
3 changed files with 134 additions and 5 deletions

View File

@ -73,15 +73,24 @@ public abstract class ConfiguredJdbcAgent extends AbstractJdbcAgent {
@Override
public List<ObjectInfo> listObjects(String schema) {
return StandardJdbcMetadata.INSTANCE.listObjects(listTables(schema), schema);
return StandardJdbcMetadata.INSTANCE.listObjects(
requireConnection(),
profile,
configuredDatabase,
schema,
MetadataListConstraints.NONE
);
}
@Override
public List<ObjectInfo> listObjects(String schema, MetadataListConstraints constraints) {
MetadataListConstraints normalized = MetadataListConstraints.orNone(constraints);
MetadataListConstraints tableConstraints =
new MetadataListConstraints(normalized.getFilter(), null, null, normalized.getObjectTypes());
return StandardJdbcMetadata.INSTANCE.listObjects(listTables(schema, tableConstraints), schema, normalized);
return StandardJdbcMetadata.INSTANCE.listObjects(
requireConnection(),
profile,
configuredDatabase,
schema,
constraints
);
}
@Override

View File

@ -108,6 +108,33 @@ public final class StandardJdbcMetadata {
return MetadataListConstraints.orNone(constraints).filterObjects(result);
}
public List<ObjectInfo> listObjects(
Connection conn,
JdbcAgentProfile profile,
String configuredDatabase,
String schema,
MetadataListConstraints constraints
) {
return unchecked(() -> {
MetadataListConstraints normalized = MetadataListConstraints.orNone(constraints);
MetadataListConstraints tableConstraints =
new MetadataListConstraints(normalized.getFilter(), null, null, normalized.getObjectTypes());
List<ObjectInfo> result = new ArrayList<>(
listObjects(listTables(conn, profile, configuredDatabase, schema, tableConstraints), schema)
);
DatabaseMetaData meta = conn.getMetaData();
appendRoutines(result, meta, null, blankToNull(schema), schema);
if (!containsRoutine(result)
&& profile.getCatalogFallbackEnabled()
&& configuredDatabase != null
&& !configuredDatabase.trim().isEmpty()) {
appendRoutines(result, meta, configuredDatabase, blankToNull(schema), schema);
}
result.sort(Comparator.comparing(ObjectInfo::getName));
return normalized.filterObjects(result);
});
}
public List<String> listDataTypes(Connection conn) {
return unchecked(() -> {
Set<String> seen = new LinkedHashSet<>();
@ -587,6 +614,47 @@ public final class StandardJdbcMetadata {
return type;
}
private static void appendRoutines(
List<ObjectInfo> result,
DatabaseMetaData meta,
String catalog,
String schemaPattern,
String schema
) {
Set<String> procedureNames = new LinkedHashSet<>();
try (ResultSet rs = meta.getProcedures(catalog, schemaPattern, "%")) {
while (rs.next()) {
String name = rs.getString("PROCEDURE_NAME");
if (name != null && !name.trim().isEmpty()) {
procedureNames.add(name);
result.add(new ObjectInfo(name, "PROCEDURE", schema, rs.getString("REMARKS")));
}
}
} catch (Exception | AbstractMethodError ignored) {
// Some JDBC drivers don't implement routine metadata.
}
try (ResultSet rs = meta.getFunctions(catalog, schemaPattern, "%")) {
while (rs.next()) {
String name = rs.getString("FUNCTION_NAME");
if (name != null && !name.trim().isEmpty() && !procedureNames.contains(name)) {
result.add(new ObjectInfo(name, "FUNCTION", schema, rs.getString("REMARKS")));
}
}
} catch (Exception | AbstractMethodError ignored) {
// Some JDBC drivers don't implement function metadata separately.
}
}
private static boolean containsRoutine(List<ObjectInfo> objects) {
for (ObjectInfo object : objects) {
if ("PROCEDURE".equalsIgnoreCase(object.getObject_type())
|| "FUNCTION".equalsIgnoreCase(object.getObject_type())) {
return true;
}
}
return false;
}
private static Integer intOrNull(ResultSet rs, String column) throws Exception {
Object value = rs.getObject(column);
return value instanceof Number ? ((Number) value).intValue() : null;

View File

@ -108,6 +108,30 @@ class StandardJdbcMetadataTest {
assertEquals("TABLE", tables.get(1).getTable_type());
}
@Test
void listsProceduresAndFunctionsFromJdbcRoutineMetadata() {
Connection conn = routineConnection(
rows(
row("PROCEDURE_NAME", "PROCESS_ORDER", "REMARKS", "processes an order"),
row("PROCEDURE_NAME", "SHARED_ROUTINE", "REMARKS", null)
),
rows(
row("FUNCTION_NAME", "CALCULATE_TOTAL", "REMARKS", "calculates a total"),
row("FUNCTION_NAME", "SHARED_ROUTINE", "REMARKS", null)
)
);
MetadataListConstraints constraints =
new MetadataListConstraints(null, null, null, Collections.singletonList("PROCEDURE"));
List<ObjectInfo> objects = StandardJdbcMetadata.INSTANCE.listObjects(conn, profile, "", "APP", constraints);
assertEquals(2, objects.size());
assertEquals("PROCESS_ORDER", objects.get(0).getName());
assertEquals("PROCEDURE", objects.get(0).getObject_type());
assertEquals("SHARED_ROUTINE", objects.get(1).getName());
assertEquals("PROCEDURE", objects.get(1).getObject_type());
}
@Test
void listsDataTypesFromJdbcTypeInfo() {
Connection conn = connection(
@ -581,6 +605,34 @@ class StandardJdbcMetadataTest {
});
}
private static Connection routineConnection(ResultSet procedures, ResultSet functions) {
DatabaseMetaData meta = proxy(DatabaseMetaData.class, new MethodHandler() {
@Override
public Object handle(Method method, Object[] args) {
String name = method.getName();
if ("getTables".equals(name) || "getTableTypes".equals(name)) {
return rows();
}
if ("getProcedures".equals(name)) {
return procedures;
}
if ("getFunctions".equals(name)) {
return functions;
}
return defaultValue(method.getReturnType());
}
});
return proxy(Connection.class, new MethodHandler() {
@Override
public Object handle(Method method, Object[] args) {
if ("getMetaData".equals(method.getName())) {
return meta;
}
return defaultValue(method.getReturnType());
}
});
}
private static ResultSet orderColumns() {
return rows(row(
"COLUMN_NAME", "ID",