From eb3d5e407153b2d070eafe6f64bf99704b618513 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Fri, 10 Jul 2026 22:14:26 +0800 Subject: [PATCH] fix(gbase8s): load table metadata details --- .../com/dbx/agent/gbase8s/Gbase8sAgent.java | 135 ++++++++++++++++-- .../dbx/agent/gbase8s/Gbase8sAgentTest.java | 78 +++++++++- 2 files changed, 198 insertions(+), 15 deletions(-) diff --git a/agents/drivers/gbase8s/src/main/java/com/dbx/agent/gbase8s/Gbase8sAgent.java b/agents/drivers/gbase8s/src/main/java/com/dbx/agent/gbase8s/Gbase8sAgent.java index 8997c7f95..52cdd7c6a 100644 --- a/agents/drivers/gbase8s/src/main/java/com/dbx/agent/gbase8s/Gbase8sAgent.java +++ b/agents/drivers/gbase8s/src/main/java/com/dbx/agent/gbase8s/Gbase8sAgent.java @@ -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 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 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 listIndexes(String schema, String table) { + try { + String owner = trim(schema); + List 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 columnNames = loadColumnNamesByNumber(owner, table); + List 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 parts = readIndexParts(rs); + List 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 resolveIndexColumns(List parts, Map columnNames) { + List 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 loadColumnNamesByNumber(String owner, String table) throws Exception { + List 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 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 readIndexParts(ResultSet rs) throws Exception { + List 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 getPrimaryKeyColumnNumbers(Connection conn, String owner, String table) throws Exception { List args = new ArrayList<>(); args.add(table); diff --git a/agents/drivers/gbase8s/src/test/java/com/dbx/agent/gbase8s/Gbase8sAgentTest.java b/agents/drivers/gbase8s/src/test/java/com/dbx/agent/gbase8s/Gbase8sAgentTest.java index d30bc4a49..2b3e6d733 100644 --- a/agents/drivers/gbase8s/src/test/java/com/dbx/agent/gbase8s/Gbase8sAgentTest.java +++ b/agents/drivers/gbase8s/src/test/java/com/dbx/agent/gbase8s/Gbase8sAgentTest.java @@ -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 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 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 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 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