mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 04:10:49 +00:00
fix(dameng): include independent indexes in table DDL
This commit is contained in:
@@ -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);
|
||||
|
||||
+81
-1
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user