fix(gbase8s): load table metadata details

This commit is contained in:
t8y2 2026-07-10 22:14:26 +08:00
parent 53f2a4f215
commit eb3d5e4071
2 changed files with 198 additions and 15 deletions

View File

@ -7,6 +7,7 @@ import com.dbx.agent.DatabaseInfo;
import com.dbx.agent.ExecuteQueryOptions;
import com.dbx.agent.JdbcAgentProfile;
import com.dbx.agent.JsonRpcServer;
import com.dbx.agent.IndexInfo;
import com.dbx.agent.MetadataListConstraints;
import com.dbx.agent.ObjectSource;
import com.dbx.agent.QueryResult;
@ -18,9 +19,11 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
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 Gbase8sAgent extends ConfiguredJdbcAgent {
@ -163,9 +166,14 @@ public final class Gbase8sAgent extends ConfiguredJdbcAgent {
}
List<TableInfo> result = new ArrayList<>();
String owner = trim(schema);
String sql = "SELECT tabname, tabtype FROM systables WHERE tabid >= 100 AND tabtype IN ('T', 'V')";
String sql = """
SELECT t.tabname, t.tabtype, c.comments
FROM systables t
LEFT JOIN syscomms c ON c.tabid = t.tabid
WHERE t.tabid >= 100 AND t.tabtype IN ('T', 'V')
""".stripIndent().trim();
if (!owner.isEmpty()) {
sql += " AND owner = ?";
sql += " AND t.owner = ?";
}
sql += " ORDER BY tabname";
try (PreparedStatement stmt = requireConnection().prepareStatement(sql)) {
@ -176,7 +184,8 @@ public final class Gbase8sAgent extends ConfiguredJdbcAgent {
while (rs.next()) {
result.add(new TableInfo(
trim(rs.getString("tabname")),
tableType(rs.getString("tabtype"))
tableType(rs.getString("tabtype")),
emptyToNull(trim(rs.getString("comments")))
));
}
}
@ -207,9 +216,10 @@ public final class Gbase8sAgent extends ConfiguredJdbcAgent {
List<Object> args = new ArrayList<>();
args.add(table);
StringBuilder sql = new StringBuilder("""
SELECT c.colname, c.coltype, c.colno, c.collength
SELECT c.colname, c.coltype, c.colno, c.collength, cc.comments
FROM syscolumns c
JOIN systables t ON t.tabid = c.tabid
LEFT JOIN syscolcomms cc ON cc.tabid = c.tabid AND cc.colno = c.colno
WHERE t.tabid >= 100 AND t.tabname = ?
""".stripIndent().trim());
if (!owner.isEmpty()) {
@ -234,7 +244,7 @@ public final class Gbase8sAgent extends ConfiguredJdbcAgent {
null,
primaryKeyColumns.contains(rs.getInt("colno")),
null,
null,
emptyToNull(trim(rs.getString("comments"))),
numericPrecision(baseType, length),
numericScale(baseType, length),
characterMaximumLength(baseType, length)
@ -248,6 +258,60 @@ public final class Gbase8sAgent extends ConfiguredJdbcAgent {
}
}
@Override
public List<IndexInfo> listIndexes(String schema, String table) {
try {
String owner = trim(schema);
List<Object> args = new ArrayList<>();
args.add(table);
StringBuilder sql = new StringBuilder("""
SELECT i.idxname, i.idxtype, c.constrtype,
i.part1, i.part2, i.part3, i.part4, i.part5, i.part6, i.part7, i.part8,
i.part9, i.part10, i.part11, i.part12, i.part13, i.part14, i.part15, i.part16
FROM sysindexes i
JOIN systables t ON t.tabid = i.tabid
LEFT JOIN sysconstraints c ON c.tabid = i.tabid AND c.idxname = i.idxname
WHERE t.tabid >= 100 AND t.tabname = ?
""".stripIndent().trim());
if (!owner.isEmpty()) {
sql.append(" AND t.owner = ?");
args.add(owner);
}
sql.append(" ORDER BY i.idxname");
Map<Integer, String> columnNames = loadColumnNamesByNumber(owner, table);
List<IndexInfo> result = new ArrayList<>();
try (PreparedStatement stmt = requireConnection().prepareStatement(sql.toString())) {
bind(stmt, args);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
String name = trim(rs.getString("idxname"));
if (name.isEmpty()) {
continue;
}
List<Integer> parts = readIndexParts(rs);
List<String> columns = resolveIndexColumns(parts, columnNames);
String indexType = trim(rs.getString("idxtype"));
String constraintType = trim(rs.getString("constrtype"));
result.add(new IndexInfo(
name,
columns,
indexType.toUpperCase(Locale.ROOT).startsWith("U"),
"P".equalsIgnoreCase(constraintType),
null,
indexType,
null,
null
));
}
}
}
return result;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Override
public ObjectSource getObjectSource(String schema, String name, String objectType) {
String normalizedType = objectType == null ? "" : objectType.trim().toUpperCase(Locale.ROOT);
@ -281,24 +345,25 @@ public final class Gbase8sAgent extends ConfiguredJdbcAgent {
if (constraints.hasLimit()) {
sql.append("FIRST ").append(constraints.getLimit()).append(' ');
}
sql.append("tabname, tabtype FROM systables WHERE tabid >= 100");
sql.append("t.tabname, t.tabtype, c.comments FROM systables t LEFT JOIN syscomms c ON c.tabid = t.tabid WHERE t.tabid >= 100");
appendGbase8sTableTypePredicate(sql, constraints);
if (!owner.isEmpty()) {
sql.append(" AND owner = ?");
sql.append(" AND t.owner = ?");
args.add(owner);
}
if (constraints.hasFilter()) {
sql.append(" AND UPPER(tabname) LIKE ? ESCAPE '\\\\'");
sql.append(" AND UPPER(t.tabname) LIKE ? ESCAPE '\\\\'");
args.add(constraints.fuzzyLikePattern().toUpperCase(Locale.ROOT));
}
sql.append(" ORDER BY tabname");
sql.append(" ORDER BY t.tabname");
try (PreparedStatement stmt = requireConnection().prepareStatement(sql.toString())) {
bind(stmt, args);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
result.add(new TableInfo(
trim(rs.getString("tabname")),
tableType(rs.getString("tabtype"))
tableType(rs.getString("tabtype")),
emptyToNull(trim(rs.getString("comments")))
));
}
}
@ -554,6 +619,56 @@ public final class Gbase8sAgent extends ConfiguredJdbcAgent {
return result;
}
static List<String> resolveIndexColumns(List<Integer> parts, Map<Integer, String> columnNames) {
List<String> result = new ArrayList<>();
for (Integer part : parts) {
if (part == null || part == 0) {
continue;
}
// Informix-compatible catalogs encode descending index columns as negative column numbers.
String columnName = columnNames.get(Math.abs(part));
if (columnName != null && !columnName.isEmpty()) {
result.add(columnName);
}
}
return result;
}
private Map<Integer, String> loadColumnNamesByNumber(String owner, String table) throws Exception {
List<Object> args = new ArrayList<>();
args.add(table);
StringBuilder sql = new StringBuilder("""
SELECT c.colno, c.colname
FROM syscolumns c
JOIN systables t ON t.tabid = c.tabid
WHERE t.tabid >= 100 AND t.tabname = ?
""".stripIndent().trim());
if (!owner.isEmpty()) {
sql.append(" AND t.owner = ?");
args.add(owner);
}
Map<Integer, String> result = new LinkedHashMap<>();
try (PreparedStatement stmt = requireConnection().prepareStatement(sql.toString())) {
bind(stmt, args);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
result.put(rs.getInt("colno"), trim(rs.getString("colname")));
}
}
}
return result;
}
private static List<Integer> readIndexParts(ResultSet rs) throws Exception {
List<Integer> parts = new ArrayList<>();
for (int index = 1; index <= 16; index += 1) {
int value = rs.getInt("part" + index);
parts.add(rs.wasNull() ? null : value);
}
return parts;
}
private Set<Integer> getPrimaryKeyColumnNumbers(Connection conn, String owner, String table) throws Exception {
List<Object> args = new ArrayList<>();
args.add(table);

View File

@ -2,6 +2,7 @@ package com.dbx.agent.gbase8s;
import com.dbx.agent.ConnectParams;
import com.dbx.agent.ColumnInfo;
import com.dbx.agent.IndexInfo;
import com.dbx.agent.MetadataListConstraints;
import com.dbx.agent.ObjectSource;
import com.dbx.agent.TableInfo;
@ -18,6 +19,7 @@ import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
class Gbase8sAgentTest {
@ -132,7 +134,25 @@ class Gbase8sAgentTest {
Assertions.assertEquals("user_order", tables.get(0).getName());
Assertions.assertTrue(sql.get(0).contains("FROM systables"), sql.get(0));
Assertions.assertTrue(sql.get(0).contains("SELECT SKIP 1 FIRST 1"), sql.get(0));
Assertions.assertTrue(sql.get(0).contains("UPPER(tabname) LIKE ?"), sql.get(0));
Assertions.assertTrue(sql.get(0).contains("UPPER(t.tabname) LIKE ?"), sql.get(0));
}
@Test
void listTablesLoadsGbase8sTableComments() {
List<String> sql = new ArrayList<>();
Gbase8sAgent agent = new Gbase8sAgent();
TestSupport.setPrivateConnection(agent, preparedConnection(sql, resultSet(
new String[]{"tabname", "tabtype", "comments"},
new Object[][]{
{"products", "T", "Product catalog"}
}
)));
List<TableInfo> tables = agent.listTables("root");
Assertions.assertEquals("Product catalog", tables.get(0).getComment());
Assertions.assertTrue(sql.get(0).contains("LEFT JOIN syscomms"), sql.get(0));
Assertions.assertTrue(sql.get(0).contains("t.owner = ?"), sql.get(0));
}
@Test
@ -156,11 +176,11 @@ class Gbase8sAgentTest {
}
),
resultSet(
new String[]{"colname", "coltype", "colno", "collength"},
new String[]{"colname", "coltype", "colno", "collength", "comments"},
new Object[][]{
{"product_id", 258, 1, 4},
{"sku", 13, 2, 40},
{"price", 5, 3, 3074}
{"product_id", 258, 1, 4, "Product identifier"},
{"sku", 13, 2, 40, null},
{"price", 5, 3, 3074, "Unit price"}
}
)
));
@ -182,6 +202,54 @@ class Gbase8sAgentTest {
Assertions.assertEquals("DECIMAL", columns.get(2).getData_type());
Assertions.assertEquals(12, columns.get(2).getNumeric_precision());
Assertions.assertEquals(2, columns.get(2).getNumeric_scale());
Assertions.assertEquals("Product identifier", columns.get(0).getComment());
Assertions.assertEquals("Unit price", columns.get(2).getComment());
Assertions.assertTrue(sql.get(1).contains("LEFT JOIN syscolcomms"), sql.get(1));
}
@Test
void listIndexesLoadsGbase8sSystemCatalogIndexes() {
List<String> sql = new ArrayList<>();
Gbase8sAgent agent = new Gbase8sAgent();
TestSupport.setPrivateConnection(agent, preparedConnection(
sql,
resultSet(
new String[]{"colno", "colname"},
new Object[][]{
{1, "product_id"},
{2, "sku"},
{3, "created_at"}
}
),
resultSet(
new String[]{"idxname", "idxtype", "constrtype", "part1", "part2", "part3", "part4", "part5", "part6", "part7", "part8", "part9", "part10", "part11", "part12", "part13", "part14", "part15", "part16"},
new Object[][]{
{"products_pk", "U", "P", 1, 0, null, null, null, null, null, null, null, null, null, null, null, null, null, null},
{"products_sku_created", "D", null, 2, -3, 0, null, null, null, null, null, null, null, null, null, null, null, null, null}
}
)
));
List<IndexInfo> indexes = agent.listIndexes("root", "products");
Assertions.assertEquals(2, indexes.size());
Assertions.assertEquals(List.of("product_id"), indexes.get(0).getColumns());
Assertions.assertTrue(indexes.get(0).getIs_unique());
Assertions.assertTrue(indexes.get(0).getIs_primary());
Assertions.assertEquals(List.of("sku", "created_at"), indexes.get(1).getColumns());
Assertions.assertFalse(indexes.get(1).getIs_unique());
Assertions.assertFalse(indexes.get(1).getIs_primary());
Assertions.assertTrue(sql.get(0).contains("FROM syscolumns"), sql.get(0));
Assertions.assertTrue(sql.get(1).contains("FROM sysindexes"), sql.get(1));
Assertions.assertTrue(sql.get(1).contains("LEFT JOIN sysconstraints"), sql.get(1));
}
@Test
void resolvesGbase8sIndexPartsInDeclaredOrder() {
Assertions.assertEquals(
List.of("sku", "created_at"),
Gbase8sAgent.resolveIndexColumns(List.of(2, -3, 0), Map.of(2, "sku", 3, "created_at"))
);
}
@Test