feat(agents): move agents into dbx repo

This commit is contained in:
t8y2 committed 2026-06-23 11:38:16 +08:00
1 parent ab35fda299
commit 3a25487dea
215 files changed
+29740 -15

No files matched your search

@@ -0,0 +1,159 @@
package com.dbx.agent.iris;
import com.dbx.agent.ConfiguredJdbcAgent;
import com.dbx.agent.ColumnInfo;
import com.dbx.agent.JdbcAgentProfile;
import com.dbx.agent.JsonRpcServer;
import com.dbx.agent.StandardJdbcMetadata;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
public final class IrisAgent extends ConfiguredJdbcAgent {
public static final JdbcAgentProfile IRIS_PROFILE = new JdbcAgentProfile(
"com.intersystems.jdbc.IRISDriver",
"jdbc:IRIS://{host}:{port}/{database}",
1972,
true
);
public IrisAgent() {
super(IRIS_PROFILE);
}
@Override
public List<String> listSchemas() {
return dedupeCaseInsensitiveSchemas(StandardJdbcMetadata.INSTANCE.listSchemas(requireConnection(), IRIS_PROFILE));
}
@Override
public List<ColumnInfo> getColumns(String schema, String table) {
return irisColumns(requireConnection(), schema, table);
}
static List<ColumnInfo> irisColumns(Connection conn, String schema, String table) {
return unchecked(() -> {
Set<String> primaryKeys = irisPrimaryKeys(conn, schema, table);
List<ColumnInfo> columns = new ArrayList<>();
try (PreparedStatement stmt = conn.prepareStatement(columnSql(schema))) {
int index = 1;
if (!isBlank(schema)) {
stmt.setString(index++, schema.trim());
}
stmt.setString(index, table);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
String name = rs.getString("COLUMN_NAME");
if (isBlank(name)) {
continue;
}
columns.add(new ColumnInfo(
name,
stringOrDefault(rs, "DATA_TYPE", "Unknown"),
!"NO".equalsIgnoreCase(stringOrNull(rs, "IS_NULLABLE")),
stringOrNull(rs, "COLUMN_DEFAULT"),
primaryKeys.contains(name.toUpperCase(Locale.ROOT)),
null,
null,
intOrNull(rs, "NUMERIC_PRECISION"),
intOrNull(rs, "NUMERIC_SCALE"),
intOrNull(rs, "CHARACTER_MAXIMUM_LENGTH")
));
}
}
}
return columns;
});
}
static List<String> dedupeCaseInsensitiveSchemas(List<String> schemas) {
Map<String, String> byNormalizedName = new LinkedHashMap<>();
for (String schema : schemas) {
if (schema == null || schema.trim().isEmpty()) {
continue;
}
String name = schema.trim();
byNormalizedName.putIfAbsent(name.toUpperCase(Locale.ROOT), name);
}
return new ArrayList<>(byNormalizedName.values());
}
private static Set<String> irisPrimaryKeys(Connection conn, String schema, String table) {
Set<String> keys = new LinkedHashSet<>();
try (PreparedStatement stmt = conn.prepareStatement(primaryKeySql(schema))) {
int index = 1;
if (!isBlank(schema)) {
stmt.setString(index++, schema.trim());
}
stmt.setString(index, table);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
String column = rs.getString("COLUMN_NAME");
if (!isBlank(column)) {
keys.add(column.toUpperCase(Locale.ROOT));
}
}
}
} catch (Exception ignored) {
// Primary key metadata is optional for display; column listing must keep working.
}
return keys;
}
private static String columnSql(String schema) {
StringBuilder sql = new StringBuilder(
"SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, COLUMN_DEFAULT, "
+ "NUMERIC_PRECISION, NUMERIC_SCALE, CHARACTER_MAXIMUM_LENGTH "
+ "FROM INFORMATION_SCHEMA.COLUMNS WHERE "
);
if (!isBlank(schema)) {
sql.append("UPPER(TABLE_SCHEMA) = UPPER(?) AND ");
}
sql.append("UPPER(TABLE_NAME) = UPPER(?) ORDER BY ORDINAL_POSITION");
return sql.toString();
}
private static String primaryKeySql(String schema) {
StringBuilder sql = new StringBuilder(
"SELECT k.COLUMN_NAME "
+ "FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE k "
+ "WHERE k.CONSTRAINT_TYPE = 'PRIMARY KEY' AND "
);
if (!isBlank(schema)) {
sql.append("UPPER(k.TABLE_SCHEMA) = UPPER(?) AND ");
}
sql.append("UPPER(k.TABLE_NAME) = UPPER(?) ORDER BY k.ORDINAL_POSITION");
return sql.toString();
}
private static String stringOrDefault(ResultSet rs, String column, String fallback) throws Exception {
String value = stringOrNull(rs, column);
return isBlank(value) ? fallback : value;
}
private static String stringOrNull(ResultSet rs, String column) throws Exception {
String value = rs.getString(column);
return rs.wasNull() ? null : value;
}
private static Integer intOrNull(ResultSet rs, String column) throws Exception {
Object value = rs.getObject(column);
return value instanceof Number ? ((Number) value).intValue() : null;
}
private static boolean isBlank(String value) {
return value == null || value.trim().isEmpty();
}
public static void main(String[] args) {
new JsonRpcServer(new IrisAgent()).run();
}
}
@@ -0,0 +1,154 @@
package com.dbx.agent.iris;
import com.dbx.agent.ColumnInfo;
import org.junit.jupiter.api.Test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
class IrisAgentTest {
@Test
void skipsSchemaSwitchingBecauseIrisRejectsSetSchemaContext() {
IrisAgent agent = new IrisAgent();
assertEquals("", agent.setSchemaSQL("Ens"));
}
@Test
void dedupesSchemasCaseInsensitively() {
assertEquals(
Arrays.asList("APP", "SQLUSER", "z_user"),
IrisAgent.dedupeCaseInsensitiveSchemas(Arrays.asList("APP", "SQLUSER", "SQLUser", "app", "z_user"))
);
}
@Test
void ignoresBlankSchemasWhenDeduping() {
assertEquals(
Collections.singletonList("SQLUSER"),
IrisAgent.dedupeCaseInsensitiveSchemas(Arrays.asList("", " ", null, "SQLUSER"))
);
}
@Test
void readsColumnsWithNativeInformationSchemaQueries() {
List<String> sql = new ArrayList<>();
Connection conn = fakeConnection(sql);
List<ColumnInfo> columns = IrisAgent.irisColumns(conn, "SQLUser", "People");
assertEquals(2, columns.size());
assertEquals("ID", columns.get(0).getName());
assertEquals("INTEGER", columns.get(0).getData_type());
assertFalse(columns.get(0).getIs_nullable());
assertTrue(columns.get(0).getIs_primary_key());
assertEquals("NAME", columns.get(1).getName());
assertEquals(Integer.valueOf(64), columns.get(1).getCharacter_maximum_length());
assertFalse(sql.get(0).contains("LIMIT"));
assertTrue(sql.get(0).contains("INFORMATION_SCHEMA.KEY_COLUMN_USAGE"));
assertTrue(sql.get(1).contains("INFORMATION_SCHEMA.COLUMNS"));
}
private static Connection fakeConnection(List<String> sqlLog) {
return proxy(Connection.class, (method, args) -> {
if ("prepareStatement".equals(method.getName())) {
String sql = (String) args[0];
sqlLog.add(sql);
return fakeStatement(sql);
}
if ("getMetaData".equals(method.getName())) {
throw new AssertionError("IRIS columns must not use JDBC DatabaseMetaData.getColumns()");
}
return defaultValue(method.getReturnType());
});
}
private static PreparedStatement fakeStatement(String sql) {
return proxy(PreparedStatement.class, (method, args) -> {
if ("executeQuery".equals(method.getName())) {
if (sql.contains("KEY_COLUMN_USAGE")) {
return fakeResultSet(List.of(Map.of("COLUMN_NAME", "ID")));
}
return fakeResultSet(List.of(
Map.of(
"COLUMN_NAME", "ID",
"DATA_TYPE", "INTEGER",
"IS_NULLABLE", "NO",
"NUMERIC_PRECISION", 10
),
Map.of(
"COLUMN_NAME", "NAME",
"DATA_TYPE", "VARCHAR",
"IS_NULLABLE", "YES",
"CHARACTER_MAXIMUM_LENGTH", 64
)
));
}
return defaultValue(method.getReturnType());
});
}
private static ResultSet fakeResultSet(List<Map<String, Object>> rows) {
final int[] index = {-1};
final boolean[] wasNull = {false};
return proxy(ResultSet.class, (method, args) -> {
String name = method.getName();
if ("next".equals(name)) {
index[0] += 1;
return index[0] < rows.size();
}
if ("getString".equals(name)) {
Object value = rows.get(index[0]).get((String) args[0]);
wasNull[0] = value == null;
return value == null ? null : String.valueOf(value);
}
if ("getObject".equals(name)) {
Object value = rows.get(index[0]).get((String) args[0]);
wasNull[0] = value == null;
return value;
}
if ("wasNull".equals(name)) {
return wasNull[0];
}
return defaultValue(method.getReturnType());
});
}
@SuppressWarnings("unchecked")
private static <T> T proxy(Class<T> type, MethodHandler handler) {
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return handler.handle(method, args == null ? new Object[0] : args);
}
};
return (T) Proxy.newProxyInstance(type.getClassLoader(), new Class<?>[]{type}, invocationHandler);
}
private static Object defaultValue(Class<?> type) {
if (Boolean.TYPE.equals(type)) {
return false;
}
if (Integer.TYPE.equals(type)) {
return 0;
}
return null;
}
private interface MethodHandler {
Object handle(Method method, Object[] args) throws Throwable;
}
}