feat(desktop): add database info and SQL statement markers

This commit is contained in:
shen authored and GitHub committed 2026-07-15 18:35:34 +08:00
1 parent 83f7604a11
commit 3748d83cde
60 files changed
+2403 -134

No files matched your search

@@ -9,7 +9,9 @@ import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Types;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public abstract class AbstractJdbcAgent extends BaseDatabaseAgent {
private Connection connection;
@@ -42,14 +44,33 @@ public abstract class AbstractJdbcAgent extends BaseDatabaseAgent {
@Override
public final boolean testConnection(ConnectParams params) {
return Boolean.TRUE.equals(testConnectionWithInfo(params).get("ok"));
}
@Override
public final Map<String, Object> testConnectionWithInfo(ConnectParams params) {
return unchecked(() -> {
loadDriver(params);
try (Connection conn = openConnection(params)) {
return conn.isValid(5);
boolean valid = conn.isValid(5);
Map<String, Object> result = new LinkedHashMap<>();
result.put("ok", valid);
if (valid) {
Map<String, String> databaseInfo = JdbcDatabaseInfo.from(conn);
if (!databaseInfo.isEmpty()) {
result.put("databaseInfo", databaseInfo);
}
}
return result;
}
});
}
@Override
public final Map<String, String> getDatabaseInfo() {
return JdbcDatabaseInfo.from(getConnection());
}
private void loadDriver(ConnectParams params) throws Exception {
List<String> driverPaths = params.getJdbc_driver_paths();
String driverClass = params.getJdbc_driver_class();
@@ -4,7 +4,9 @@ import java.sql.Connection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -13,6 +15,16 @@ public interface DatabaseAgent {
boolean testConnection(ConnectParams params);
default Map<String, Object> testConnectionWithInfo(ConnectParams params) {
Map<String, Object> result = new LinkedHashMap<>();
result.put("ok", testConnection(params));
return result;
}
default Map<String, String> getDatabaseInfo() {
return Collections.emptyMap();
}
default String getIdentifierQuote() {
return "";
}
@@ -0,0 +1,95 @@
package com.dbx.agent;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
final class JdbcDatabaseInfo {
private JdbcDatabaseInfo() {
}
static Map<String, String> from(Connection connection) {
if (connection == null) {
return Collections.emptyMap();
}
final DatabaseMetaData metadata;
try {
metadata = connection.getMetaData();
} catch (SQLException | AbstractMethodError | UnsupportedOperationException ignored) {
return Collections.emptyMap();
}
if (metadata == null) {
return Collections.emptyMap();
}
Map<String, String> info = new LinkedHashMap<>();
putText(info, "productName", () -> metadata.getDatabaseProductName());
putText(info, "productVersion", () -> metadata.getDatabaseProductVersion());
putIdentifierCase(
info,
"unquotedIdentifierCase",
() -> metadata.storesLowerCaseIdentifiers(),
() -> metadata.storesUpperCaseIdentifiers(),
() -> metadata.storesMixedCaseIdentifiers()
);
putIdentifierCase(
info,
"quotedIdentifierCase",
() -> metadata.storesLowerCaseQuotedIdentifiers(),
() -> metadata.storesUpperCaseQuotedIdentifiers(),
() -> metadata.storesMixedCaseQuotedIdentifiers()
);
putText(info, "driverName", () -> metadata.getDriverName());
putText(info, "driverVersion", () -> metadata.getDriverVersion());
Integer jdbcMajor = readInteger(() -> metadata.getJDBCMajorVersion());
Integer jdbcMinor = readInteger(() -> metadata.getJDBCMinorVersion());
if (jdbcMajor != null && jdbcMinor != null && jdbcMajor >= 0 && jdbcMinor >= 0) {
info.put("jdbcVersion", jdbcMajor + "." + jdbcMinor);
}
return info;
}
private static void putText(Map<String, String> target, String key, SqlSupplier<String> supplier) {
String value = read(supplier);
if (value != null && !value.trim().isEmpty()) {
target.put(key, value.trim());
}
}
private static void putIdentifierCase(
Map<String, String> target,
String key,
SqlSupplier<Boolean> lower,
SqlSupplier<Boolean> upper,
SqlSupplier<Boolean> mixed
) {
if (Boolean.TRUE.equals(read(lower))) {
target.put(key, "lower");
} else if (Boolean.TRUE.equals(read(upper))) {
target.put(key, "upper");
} else if (Boolean.TRUE.equals(read(mixed))) {
target.put(key, "mixed");
}
}
private static Integer readInteger(SqlSupplier<Integer> supplier) {
return read(supplier);
}
private static <T> T read(SqlSupplier<T> supplier) {
try {
return supplier.get();
} catch (SQLException | AbstractMethodError | UnsupportedOperationException ignored) {
return null;
}
}
private interface SqlSupplier<T> {
T get() throws SQLException;
}
}
@@ -17,7 +17,9 @@ import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Connection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
public final class JsonRpcServer {
private static final long CONNECTION_VALIDATION_INTERVAL_MILLIS = 5_000L;
@@ -105,10 +107,11 @@ public final class JsonRpcServer {
return Collections.singletonMap("ok", true);
}
if (AgentProtocol.METHOD_TEST_CONNECTION.equals(method)) {
if (!agent.testConnection(gson.fromJson(params, ConnectParams.class))) {
Map<String, Object> result = agent.testConnectionWithInfo(gson.fromJson(params, ConnectParams.class));
if (!Boolean.TRUE.equals(result.get("ok"))) {
throw new RuntimeException("Connection failed");
}
return Collections.singletonMap("ok", true);
return result;
}
if (AgentProtocol.METHOD_VALIDATE_CONNECTION.equals(method)) {
Connection conn = agent.getConnection();
@@ -126,7 +129,13 @@ public final class JsonRpcServer {
}
ensureLiveConnection(method);
if (AgentProtocol.METHOD_CONNECTION_INFO.equals(method)) {
return Collections.singletonMap("identifierQuote", agent.getIdentifierQuote());
Map<String, Object> result = new LinkedHashMap<>();
result.put("identifierQuote", agent.getIdentifierQuote());
Map<String, String> databaseInfo = agent.getDatabaseInfo();
if (databaseInfo != null && !databaseInfo.isEmpty()) {
result.put("databaseInfo", databaseInfo);
}
return result;
}
if (AgentProtocol.METHOD_LIST_DATABASES.equals(method)) {
return agent.listDatabases();