feat(export): add DB2 comments to XLSX headers

This commit is contained in:
大峰 authored and GitHub committed 2026-07-29 22:37:05 +08:00
1 parent 51b5c8ae95
commit 843f945581
35 files changed
+640 -87

No files matched your search

@@ -97,7 +97,7 @@ public final class Db2Agent extends BaseDatabaseAgent {
public List<TableInfo> listTables(String schema) {
return unchecked(() -> {
List<TableInfo> result = new ArrayList<>();
String sql = "SELECT TABNAME, TYPE FROM SYSCAT.TABLES WHERE TABSCHEMA = ? AND TYPE IN ('T','V') ORDER BY TABNAME";
String sql = "SELECT TABNAME, TYPE, REMARKS FROM SYSCAT.TABLES WHERE TABSCHEMA = ? AND TYPE IN ('T','V') ORDER BY TABNAME";
try (PreparedStatement stmt = requireConnected().prepareStatement(sql)) {
stmt.setString(1, schema);
try (ResultSet rs = stmt.executeQuery()) {
@@ -108,7 +108,7 @@ public final class Db2Agent extends BaseDatabaseAgent {
case "V" -> "VIEW";
default -> db2Type;
};
result.add(new TableInfo(rs.getString(1).trim(), type, null));
result.add(new TableInfo(rs.getString(1).trim(), type, rs.getString(3)));
}
}
}
@@ -136,7 +136,7 @@ public final class Db2Agent extends BaseDatabaseAgent {
return unchecked(() -> {
List<TableInfo> result = new ArrayList<>();
List<Object> args = new ArrayList<>();
StringBuilder sql = new StringBuilder("SELECT TABNAME, TYPE FROM SYSCAT.TABLES WHERE TABSCHEMA = ?");
StringBuilder sql = new StringBuilder("SELECT TABNAME, TYPE, REMARKS FROM SYSCAT.TABLES WHERE TABSCHEMA = ?");
args.add(schema);
appendDb2TableTypePredicate(sql, args, constraints);
MetadataSqlSupport.appendNameFilter(sql, args, "TABNAME", constraints);
@@ -146,7 +146,7 @@ public final class Db2Agent extends BaseDatabaseAgent {
MetadataSqlSupport.bind(stmt, args);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
result.add(new TableInfo(rs.getString(1).trim(), db2TableType(rs.getString(2)), null));
result.add(new TableInfo(rs.getString(1).trim(), db2TableType(rs.getString(2)), rs.getString(3)));
}
}
}
@@ -198,7 +198,7 @@ public final class Db2Agent extends BaseDatabaseAgent {
List<Object> args = new ArrayList<>();
if (constraints.includesTableLikeTypes()) {
StringBuilder tableSql = new StringBuilder(
"SELECT TABNAME AS OBJECT_NAME, CASE TYPE WHEN 'T' THEN 'TABLE' WHEN 'V' THEN 'VIEW' ELSE TYPE END AS OBJECT_TYPE FROM SYSCAT.TABLES WHERE TABSCHEMA = ?"
"SELECT TABNAME AS OBJECT_NAME, CASE TYPE WHEN 'T' THEN 'TABLE' WHEN 'V' THEN 'VIEW' ELSE TYPE END AS OBJECT_TYPE, REMARKS AS OBJECT_COMMENT FROM SYSCAT.TABLES WHERE TABSCHEMA = ?"
);
args.add(schema);
appendDb2TableTypePredicate(tableSql, args, constraints);
@@ -207,7 +207,7 @@ public final class Db2Agent extends BaseDatabaseAgent {
}
if (constraints.objectTypeAllowed("PROCEDURE")) {
StringBuilder procedureSql = new StringBuilder(
"SELECT PROCNAME AS OBJECT_NAME, 'PROCEDURE' AS OBJECT_TYPE FROM SYSCAT.PROCEDURES WHERE PROCSCHEMA = ?"
"SELECT PROCNAME AS OBJECT_NAME, 'PROCEDURE' AS OBJECT_TYPE, CAST(NULL AS VARCHAR(254)) AS OBJECT_COMMENT FROM SYSCAT.PROCEDURES WHERE PROCSCHEMA = ?"
);
args.add(schema);
MetadataSqlSupport.appendNameFilter(procedureSql, args, "PROCNAME", constraints);
@@ -216,7 +216,7 @@ public final class Db2Agent extends BaseDatabaseAgent {
if (branches.isEmpty()) {
return List.of();
}
StringBuilder sql = new StringBuilder("SELECT OBJECT_NAME, OBJECT_TYPE FROM (")
StringBuilder sql = new StringBuilder("SELECT OBJECT_NAME, OBJECT_TYPE, OBJECT_COMMENT FROM (")
.append(String.join(" UNION ALL ", branches))
.append(") metadata_objects ORDER BY CASE OBJECT_TYPE WHEN 'TABLE' THEN 0 WHEN 'VIEW' THEN 1 WHEN 'PROCEDURE' THEN 2 ELSE 9 END, OBJECT_NAME");
MetadataSqlSupport.appendLiteralOffsetFetch(sql, constraints);
@@ -224,7 +224,7 @@ public final class Db2Agent extends BaseDatabaseAgent {
MetadataSqlSupport.bind(stmt, args);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
result.add(new ObjectInfo(rs.getString(1).trim(), rs.getString(2), schema, null));
result.add(new ObjectInfo(rs.getString(1).trim(), rs.getString(2), schema, rs.getString(3)));
}
}
}
@@ -269,7 +269,7 @@ public final class Db2Agent extends BaseDatabaseAgent {
List<ColumnInfo> result = new ArrayList<>();
String colSql = """
SELECT COLNAME, TYPENAME, NULLS, DEFAULT, LENGTH, SCALE
SELECT COLNAME, TYPENAME, NULLS, DEFAULT, LENGTH, SCALE, REMARKS
FROM SYSCAT.COLUMNS
WHERE TABSCHEMA = ? AND TABNAME = ?
ORDER BY COLNO
@@ -292,7 +292,7 @@ public final class Db2Agent extends BaseDatabaseAgent {
trimNullable(rs.getString("DEFAULT")),
pkColumns.contains(name),
null,
null,
rs.getString("REMARKS"),
NUMERIC_PRECISION_TYPES.contains(typeName) ? length : null,
NUMERIC_SCALE_TYPES.contains(typeName) ? scale : null,
CHARACTER_LENGTH_TYPES.contains(typeName) ? length : null
@@ -3,6 +3,8 @@ package com.dbx.agent.db2;
import com.dbx.agent.DatabaseAgent;
import com.dbx.agent.ConnectParams;
import com.dbx.agent.MetadataListConstraints;
import com.dbx.agent.ObjectInfo;
import com.dbx.agent.TableInfo;
import com.dbx.agent.test.JdbcFakeExecutionBehaviorTest;
import com.dbx.agent.test.JdbcMetadataSqlFake;
import com.dbx.agent.test.TestSupport;
@@ -12,6 +14,7 @@ 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.sql.Statement;
import java.util.Arrays;
@@ -91,6 +94,70 @@ class Db2AgentTest extends JdbcFakeExecutionBehaviorTest {
assertEquals(Arrays.asList("param:1=APP", "param:2=%S%Y%N%C%"), JdbcMetadataSqlFake.statements.subList(1, 3));
}
@Test
void constrainedTableMetadataPassesRemarks() {
Db2Agent agent = new Db2Agent();
TestSupport.setPrivateConnection(agent, preparedConnection(
row("TABNAME", "MY_TABLE", "TYPE", "T", "REMARKS", "Test table comment")
));
List<TableInfo> tables = agent.listTables("APP",
new MetadataListConstraints(null, 25, null, List.of("TABLE")));
assertEquals(1, tables.size());
assertEquals("MY_TABLE", tables.get(0).getName());
assertEquals("TABLE", tables.get(0).getTable_type());
assertEquals("Test table comment", tables.get(0).getComment());
}
@Test
void constrainedObjectMetadataPassesRemarks() {
Db2Agent agent = new Db2Agent();
TestSupport.setPrivateConnection(agent, preparedConnection(
row("OBJECT_NAME", "MY_VIEW", "OBJECT_TYPE", "VIEW", "OBJECT_COMMENT", "Test view comment")
));
List<ObjectInfo> objects = agent.listObjects("APP",
new MetadataListConstraints("my", 10, null, List.of("VIEW")));
assertEquals(1, objects.size());
assertEquals("MY_VIEW", objects.get(0).getName());
assertEquals("VIEW", objects.get(0).getObject_type());
assertEquals("Test view comment", objects.get(0).getComment());
}
private static Connection preparedConnection(Map<String, Object>... rows) {
final ResultSet resultSet = rows(rows);
return proxy(Connection.class, new MethodHandler() {
@Override
public Object handle(Method method, Object[] args) {
String name = method.getName();
if ("prepareStatement".equals(name)) {
return proxy(PreparedStatement.class, new MethodHandler() {
@Override
public Object handle(Method stmtMethod, Object[] stmtArgs) {
String m = stmtMethod.getName();
if ("setString".equals(m) || "setObject".equals(m) || "setInt".equals(m)) {
return null;
}
if ("executeQuery".equals(m)) {
return resultSet;
}
if ("close".equals(m)) {
return null;
}
return defaultValue(stmtMethod.getReturnType());
}
});
}
if ("isClosed".equals(name)) {
return false;
}
return defaultValue(method.getReturnType());
}
});
}
private static Connection connection(AtomicReference<String> executedSql, ResultSet resultSet) {
Statement statement = proxy(Statement.class, new MethodHandler() {
@Override
@@ -131,8 +198,13 @@ class Db2AgentTest extends JdbcFakeExecutionBehaviorTest {
return index < rows.length;
}
if ("getString".equals(name)) {
Object key = args[0] instanceof Number ? rows[index].keySet().iterator().next() : args[0];
Object value = rows[index].get(key);
if (args[0] instanceof Number) {
int colIndex = ((Number) args[0]).intValue() - 1;
String key = rows[index].keySet().stream().skip(colIndex).findFirst().orElse(null);
Object value = rows[index].get(key);
return value == null ? null : String.valueOf(value);
}
Object value = rows[index].get(args[0]);
return value == null ? null : String.valueOf(value);
}
if ("close".equals(name)) {