fix(dameng): include independent indexes in table DDL

This commit is contained in:
t8y2
2026-07-03 11:59:41 +08:00
parent ecb9372979
commit e3b3dfc095
2 changed files with 159 additions and 2 deletions
@@ -383,7 +383,8 @@ public final class DamengAgent extends BaseDatabaseAgent {
stmt.setString(3, schema);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return appendTableAndColumnComments(coalesce(rs.getString(1)), schema, table);
String ddl = appendTableAndColumnComments(coalesce(rs.getString(1)), schema, table);
return appendIndependentIndexDdl(ddl, schema, table);
}
}
}
@@ -831,6 +832,64 @@ public final class DamengAgent extends BaseDatabaseAgent {
return result.toString();
}
private String appendIndependentIndexDdl(String ddl, String schema, String table) throws Exception {
StringBuilder result = new StringBuilder(ddl == null ? "" : ddl.trim());
for (String indexName : independentIndexNames(schema, table)) {
if (containsCreateIndex(result.toString(), schema, indexName)) {
continue;
}
String indexDdl = indexDdl(schema, indexName);
if (notBlank(indexDdl)) {
appendDdlStatement(result, ensureStatementTerminator(indexDdl));
}
}
return result.toString();
}
private List<String> independentIndexNames(String schema, String table) throws Exception {
List<String> result = new ArrayList<>();
// Primary-key and unique-constraint backing indexes are already represented in table DDL.
String sql = """
SELECT i.INDEX_NAME
FROM ALL_INDEXES i
WHERE i.TABLE_OWNER = ? AND i.TABLE_NAME = ?
AND NOT EXISTS (
SELECT 1
FROM ALL_CONSTRAINTS c
WHERE c.OWNER = i.TABLE_OWNER
AND c.TABLE_NAME = i.TABLE_NAME
AND c.INDEX_NAME = i.INDEX_NAME
AND c.CONSTRAINT_TYPE IN ('P', 'U')
)
ORDER BY i.INDEX_NAME
""".stripIndent().trim();
try (PreparedStatement stmt = requireConnected().prepareStatement(sql)) {
stmt.setString(1, schema);
stmt.setString(2, table);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
String indexName = rs.getString(1);
if (notBlank(indexName)) {
result.add(indexName);
}
}
}
}
return result;
}
private String indexDdl(String schema, String indexName) throws Exception {
String sql = "SELECT DBMS_METADATA.GET_DDL(?, ?, ?) FROM DUAL";
try (PreparedStatement stmt = requireConnected().prepareStatement(sql)) {
stmt.setString(1, "INDEX");
stmt.setString(2, indexName);
stmt.setString(3, schema);
try (ResultSet rs = stmt.executeQuery()) {
return rs.next() ? rs.getString(1) : null;
}
}
}
private String tableComment(String schema, String table) throws Exception {
String sql = """
SELECT COMMENTS
@@ -847,6 +906,10 @@ public final class DamengAgent extends BaseDatabaseAgent {
}
private static void appendCommentStatement(StringBuilder ddl, String statement) {
appendDdlStatement(ddl, statement);
}
private static void appendDdlStatement(StringBuilder ddl, String statement) {
if (!ddl.isEmpty()) {
if (ddl.charAt(ddl.length() - 1) != '\n') {
ddl.append("\n");
@@ -856,6 +919,14 @@ public final class DamengAgent extends BaseDatabaseAgent {
ddl.append(statement);
}
private static String ensureStatementTerminator(String statement) {
String trimmed = coalesce(statement).trim();
if (trimmed.isEmpty() || trimmed.endsWith(";")) {
return trimmed;
}
return trimmed + ";";
}
private static boolean containsCommentOnTable(String ddl, String schema, String table) {
return normalizedDdl(ddl).contains("COMMENT ON TABLE " + normalizedQualifiedName(schema, table));
}
@@ -864,6 +935,12 @@ public final class DamengAgent extends BaseDatabaseAgent {
return normalizedDdl(ddl).contains("COMMENT ON COLUMN " + normalizedQualifiedName(schema, table) + "." + normalizedIdentifier(column));
}
private static boolean containsCreateIndex(String ddl, String schema, String indexName) {
String normalized = normalizedDdl(ddl);
return normalized.contains(" INDEX " + normalizedQualifiedName(schema, indexName) + " ")
|| normalized.contains(" INDEX " + normalizedIdentifier(indexName) + " ");
}
private static String qualifiedName(String schema, String name) {
if (!notBlank(schema)) {
return JdbcIdentifiers.INSTANCE.doubleQuote(name);
@@ -19,6 +19,7 @@ import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
class DamengAgentMetadataTest {
@Test
@@ -188,6 +189,27 @@ class DamengAgentMetadataTest {
Assertions.assertTrue(ddl.contains("COMMENT ON COLUMN \"APP\".\"USERS\".\"ID\" IS 'id comment';"), ddl);
}
@Test
void appendsIndependentIndexesToTableDdl() {
DamengAgent agent = new DamengAgent();
List<String> sqls = new ArrayList<>();
TestSupport.setPrivateConnection(agent, metadataConnectionWithIndexes(sqls));
String ddl = agent.getTableDdl("APP", "USERS");
Assertions.assertTrue(
ddl.contains("CREATE INDEX \"APP\".\"IDX_USERS_NAME\" ON \"APP\".\"USERS\" (\"NAME\");"),
ddl
);
Assertions.assertTrue(
ddl.contains("CREATE UNIQUE INDEX \"APP\".\"UX_USERS_EMAIL\" ON \"APP\".\"USERS\" (\"EMAIL\");"),
ddl
);
Assertions.assertFalse(ddl.contains("PK_USERS"), ddl);
String indexSql = sqls.stream().filter(sql -> sql.contains("ALL_INDEXES")).findFirst().orElseThrow();
Assertions.assertTrue(indexSql.contains("CONSTRAINT_TYPE IN ('P', 'U')"), indexSql);
}
private static Connection metadataConnection() {
return metadataConnection("id comment", null);
}
@@ -197,12 +219,40 @@ class DamengAgentMetadataTest {
}
private static Connection metadataConnection(String allColumnComment, String fallbackColumnComment, boolean includeMaterializedView) {
return metadataConnection(allColumnComment, fallbackColumnComment, includeMaterializedView, List.of(), Map.of(), null);
}
private static Connection metadataConnectionWithIndexes(List<String> sqls) {
return metadataConnection(
"id comment",
null,
false,
List.of("IDX_USERS_NAME", "UX_USERS_EMAIL"),
Map.of(
"IDX_USERS_NAME", "CREATE INDEX \"APP\".\"IDX_USERS_NAME\" ON \"APP\".\"USERS\" (\"NAME\")",
"UX_USERS_EMAIL", "CREATE UNIQUE INDEX \"APP\".\"UX_USERS_EMAIL\" ON \"APP\".\"USERS\" (\"EMAIL\")"
),
sqls
);
}
private static Connection metadataConnection(
String allColumnComment,
String fallbackColumnComment,
boolean includeMaterializedView,
List<String> independentIndexNames,
Map<String, String> indexDdlByName,
List<String> sqls
) {
return proxy(Connection.class, (method, args) -> {
String name = method.getName();
if ("prepareStatement".equals(name)) {
String sql = (String) args[0];
if (sqls != null) {
sqls.add(sql);
}
if (sql.contains("DBMS_METADATA.GET_DDL")) {
return metadataStatement(List.of(List.of("CREATE TABLE \"APP\".\"USERS\" (\n \"ID\" NUMBER\n);")));
return dbmsMetadataStatement(indexDdlByName);
}
if (sql.contains("ALL_CONS_COLUMNS")) {
return metadataStatement(List.of(List.of("ID")));
@@ -237,6 +287,9 @@ class DamengAgentMetadataTest {
if (sql.contains("ALL_OBJECTS")) {
return metadataStatement(List.of());
}
if (sql.contains("ALL_INDEXES")) {
return metadataStatement(independentIndexNames.stream().map(indexName -> List.of((Object) indexName)).toList());
}
if (sql.contains("ALL_TAB_COMMENTS")) {
List<List<Object>> rows = new ArrayList<>();
rows.add(List.of("USERS", "TABLE", "用户示例表"));
@@ -278,6 +331,33 @@ class DamengAgentMetadataTest {
});
}
private static PreparedStatement dbmsMetadataStatement(Map<String, String> indexDdlByName) {
List<String> params = new ArrayList<>();
return proxy(PreparedStatement.class, (method, args) -> {
String name = method.getName();
if ("executeQuery".equals(name)) {
String objectType = params.isEmpty() ? "" : params.get(0);
if ("INDEX".equals(objectType)) {
String indexName = params.size() > 1 ? params.get(1) : "";
return metadataResultSet(List.of(List.of(indexDdlByName.getOrDefault(indexName, ""))));
}
return metadataResultSet(List.of(List.of("CREATE TABLE \"APP\".\"USERS\" (\n \"ID\" NUMBER\n);")));
}
if ("setString".equals(name)) {
int index = ((Integer) args[0]) - 1;
while (params.size() <= index) {
params.add("");
}
params.set(index, String.valueOf(args[1]));
return null;
}
if ("close".equals(name)) {
return null;
}
return defaultValue(method.getReturnType());
});
}
private static void setConnectedUsername(DamengAgent agent, String username) {
try {
Field field = DamengAgent.class.getDeclaredField("connectedUsername");