diff --git a/agents/drivers/dameng/src/main/java/com/dbx/agent/dameng/DamengAgent.java b/agents/drivers/dameng/src/main/java/com/dbx/agent/dameng/DamengAgent.java index 2f0218a08..8ac4a03d8 100644 --- a/agents/drivers/dameng/src/main/java/com/dbx/agent/dameng/DamengAgent.java +++ b/agents/drivers/dameng/src/main/java/com/dbx/agent/dameng/DamengAgent.java @@ -598,7 +598,7 @@ public final class DamengAgent extends BaseDatabaseAgent { default -> throw new IllegalArgumentException("Unsupported object type: " + objectType); }; String source; - String sql = "SELECT DBMS_METADATA.GET_DDL(?, ?, ?) FROM DUAL"; + String sql = "SELECT /*+ PARALLEL(1) */ DBMS_METADATA.GET_DDL(?, ?, ?) FROM DUAL"; try (PreparedStatement stmt = requireConnected().prepareStatement(sql)) { stmt.setString(1, dbmsType); stmt.setString(2, name); @@ -614,18 +614,22 @@ public final class DamengAgent extends BaseDatabaseAgent { @Override public String getTableDdl(String schema, String table) { return unchecked(() -> { - String sql = "SELECT DBMS_METADATA.GET_DDL(?, ?, ?) FROM DUAL"; + String sql = "SELECT /*+ PARALLEL(1) */ DBMS_METADATA.GET_DDL(?, ?, ?) FROM DUAL"; + String ddl = null; try (PreparedStatement stmt = requireConnected().prepareStatement(sql)) { stmt.setString(1, "TABLE"); stmt.setString(2, table); stmt.setString(3, schema); try (ResultSet rs = stmt.executeQuery()) { if (rs.next()) { - String ddl = appendTableAndColumnComments(coalesce(readTextColumn(rs, 1)), schema, table); - return appendIndependentIndexDdl(ddl, schema, table); + ddl = coalesce(readTextColumn(rs, 1)); } } } + if (ddl != null) { + ddl = appendTableAndColumnComments(ddl, schema, table); + return appendIndependentIndexDdl(ddl, schema, table); + } throw new IllegalArgumentException("Table not found: " + schema + "." + table); }); } @@ -635,7 +639,7 @@ public final class DamengAgent extends BaseDatabaseAgent { return unchecked(() -> { Set pkColumns = new java.util.HashSet<>(); String pkSql = """ - SELECT cols.COLUMN_NAME FROM ALL_CONS_COLUMNS cols + SELECT /*+ PARALLEL(1) */ cols.COLUMN_NAME FROM ALL_CONS_COLUMNS cols JOIN ALL_CONSTRAINTS cons ON cols.CONSTRAINT_NAME = cons.CONSTRAINT_NAME AND cols.OWNER = cons.OWNER WHERE cons.CONSTRAINT_TYPE = 'P' AND cons.OWNER = ? AND cons.TABLE_NAME = ? """.stripIndent().trim(); @@ -654,7 +658,7 @@ public final class DamengAgent extends BaseDatabaseAgent { // DATA_DEFAULT is a LONG column — it must be selected first and read first // in JDBC, otherwise the data is truncated. String colSql = """ - SELECT c.DATA_DEFAULT, + SELECT /*+ PARALLEL(1) */ c.DATA_DEFAULT, c.COLUMN_NAME, c.DATA_TYPE, c.NULLABLE, @@ -709,7 +713,7 @@ public final class DamengAgent extends BaseDatabaseAgent { private Set identityColumns(String schema, String table) { Set result = new java.util.HashSet<>(); String sql = """ - SELECT c.NAME + SELECT /*+ PARALLEL(1) */ c.NAME FROM SYS.SYSCOLUMNS c JOIN SYS.SYSOBJECTS t ON c.ID = t.ID JOIN SYS.SYSOBJECTS s ON t.SCHID = s.ID @@ -737,7 +741,7 @@ public final class DamengAgent extends BaseDatabaseAgent { return unchecked(() -> { List result = new ArrayList<>(); String sql = """ - SELECT i.INDEX_NAME, + SELECT /*+ PARALLEL(1) */ i.INDEX_NAME, LISTAGG(ic.COLUMN_NAME, ',') WITHIN GROUP (ORDER BY ic.COLUMN_POSITION) AS COLUMNS, i.UNIQUENESS, CASE WHEN c.CONSTRAINT_TYPE = 'P' THEN 1 ELSE 0 END AS IS_PK, @@ -1031,18 +1035,18 @@ public final class DamengAgent extends BaseDatabaseAgent { Map comments = new HashMap<>(); queryColumnComments( comments, - "SELECT COLUMN_NAME, COMMENTS FROM USER_COL_COMMENTS WHERE TABLE_NAME = ?", + "SELECT /*+ PARALLEL(1) */ COLUMN_NAME, COMMENTS FROM USER_COL_COMMENTS WHERE TABLE_NAME = ?", table ); queryColumnComments( comments, - "SELECT COLNAME, COMMENT$ FROM SYS.SYSCOLUMNCOMMENTS WHERE SCHNAME = ? AND TVNAME = ?", + "SELECT /*+ PARALLEL(1) */ COLNAME, COMMENT$ FROM SYS.SYSCOLUMNCOMMENTS WHERE SCHNAME = ? AND TVNAME = ?", schema, table ); queryColumnComments( comments, - "SELECT COLUMN_NAME, COMMENTS FROM ALL_COL_COMMENTS WHERE UPPER(OWNER) = UPPER(?) AND UPPER(TABLE_NAME) = UPPER(?)", + "SELECT /*+ PARALLEL(1) */ COLUMN_NAME, COMMENTS FROM ALL_COL_COMMENTS WHERE UPPER(OWNER) = UPPER(?) AND UPPER(TABLE_NAME) = UPPER(?)", schema, table ); @@ -1111,7 +1115,7 @@ public final class DamengAgent extends BaseDatabaseAgent { List result = new ArrayList<>(); // Primary-key and unique-constraint backing indexes are already represented in table DDL. String sql = """ - SELECT i.INDEX_NAME, + SELECT /*+ PARALLEL(1) */ i.INDEX_NAME, LISTAGG(ic.COLUMN_NAME, ',') WITHIN GROUP (ORDER BY ic.COLUMN_POSITION) AS COLUMNS, i.UNIQUENESS, i.INDEX_TYPE @@ -1180,7 +1184,7 @@ public final class DamengAgent extends BaseDatabaseAgent { private String tableComment(String schema, String table) throws Exception { String sql = """ - SELECT COMMENTS + SELECT /*+ PARALLEL(1) */ COMMENTS FROM ALL_TAB_COMMENTS WHERE OWNER = ? AND TABLE_NAME = ? """.stripIndent().trim(); diff --git a/agents/drivers/dameng/src/test/java/com/dbx/agent/dameng/DamengAgentMetadataTest.java b/agents/drivers/dameng/src/test/java/com/dbx/agent/dameng/DamengAgentMetadataTest.java index 2358890e6..61c9356e9 100644 --- a/agents/drivers/dameng/src/test/java/com/dbx/agent/dameng/DamengAgentMetadataTest.java +++ b/agents/drivers/dameng/src/test/java/com/dbx/agent/dameng/DamengAgentMetadataTest.java @@ -34,6 +34,21 @@ class DamengAgentMetadataTest { .findFirst() .orElseThrow(); Assertions.assertTrue(columnsSql.contains("LEFT JOIN ALL_COL_COMMENTS"), columnsSql); + Assertions.assertTrue(columnsSql.startsWith("SELECT /*+ PARALLEL(1) */"), columnsSql); + } + + @Test + void disablesParallelExecutionForIndexMetadataQuery() { + DamengAgent agent = new DamengAgent(); + TestSupport.setPrivateConnection(agent, JdbcMetadataSqlFake.connection()); + + agent.listIndexes("APP", "USERS"); + + String indexesSql = JdbcMetadataSqlFake.statements.stream() + .filter(sql -> sql.contains("ALL_INDEXES")) + .findFirst() + .orElseThrow(); + Assertions.assertTrue(indexesSql.startsWith("SELECT /*+ PARALLEL(1) */"), indexesSql); } @Test @@ -203,6 +218,43 @@ class DamengAgentMetadataTest { Assertions.assertTrue(ddl.contains("COMMENT ON COLUMN \"APP\".\"USERS\".\"ID\" IS 'id comment';"), ddl); } + @Test + void closesDbmsMetadataResultBeforeLoadingSupplementalDdlMetadata() { + DamengAgent agent = new DamengAgent(); + TestSupport.setPrivateConnection(agent, metadataConnection()); + + Assertions.assertDoesNotThrow(() -> agent.getTableDdl("APP", "USERS")); + } + + @Test + void disablesParallelExecutionForTableDdlMetadataQueries() { + DamengAgent agent = new DamengAgent(); + List sqls = new ArrayList<>(); + TestSupport.setPrivateConnection(agent, metadataConnection( + "id comment", + null, + false, + List.of(), + sqls + )); + + agent.getTableDdl("APP", "USERS"); + + List ddlMetadataSql = sqls.stream() + .filter(sql -> sql.contains("DBMS_METADATA.GET_DDL") + || sql.contains("ALL_TAB_COLUMNS") + || sql.contains("ALL_CONS_COLUMNS") + || sql.contains("SYS.SYSCOLUMNS") + || sql.contains("ALL_TAB_COMMENTS") + || sql.contains("ALL_INDEXES")) + .toList(); + Assertions.assertFalse(ddlMetadataSql.isEmpty()); + Assertions.assertTrue( + ddlMetadataSql.stream().allMatch(sql -> sql.startsWith("SELECT /*+ PARALLEL(1) */")), + String.join("\n", ddlMetadataSql) + ); + } + @Test void readsFullTableDdlFromCharacterStreamWhenGetStringIsTruncated() { DamengAgent agent = new DamengAgent(); @@ -316,15 +368,19 @@ class DamengAgentMetadataTest { List sqls, String dbmsMetadataDdl ) { + boolean[] dbmsMetadataResultOpen = {false}; return proxy(Connection.class, (method, args) -> { String name = method.getName(); if ("prepareStatement".equals(name)) { String sql = (String) args[0]; + if (dbmsMetadataResultOpen[0]) { + throw new AssertionError("Supplemental metadata query started before DBMS_METADATA ResultSet closed: " + sql); + } if (sqls != null) { sqls.add(sql); } if (sql.contains("DBMS_METADATA.GET_DDL")) { - return dbmsMetadataStatement(dbmsMetadataDdl); + return dbmsMetadataStatement(dbmsMetadataDdl, dbmsMetadataResultOpen); } if (sql.contains("SYS.SYSOBJECTS") && sql.contains("TYPE$ = 'SCH'")) { return metadataStatement(List.of(List.of("APP"), List.of("EMPTY_SCHEMA"))); @@ -335,7 +391,7 @@ class DamengAgentMetadataTest { if (sql.contains("SYS.SYSCOLUMNS")) { return metadataStatement(List.of(List.of("ID"))); } - if (sql.startsWith("SELECT COMMENTS")) { + if (sql.contains("SELECT /*+ PARALLEL(1) */ COMMENTS")) { return metadataStatement(List.of(List.of("用户示例表"))); } if (sql.contains("USER_COL_COMMENTS")) { @@ -410,7 +466,7 @@ class DamengAgentMetadataTest { return List.of(name, columns, uniqueness, indexType); } - private static PreparedStatement dbmsMetadataStatement(String ddl) { + private static PreparedStatement dbmsMetadataStatement(String ddl, boolean[] resultOpen) { List params = new ArrayList<>(); return proxy(PreparedStatement.class, (method, args) -> { String name = method.getName(); @@ -419,7 +475,11 @@ class DamengAgentMetadataTest { if ("INDEX".equals(objectType)) { throw new AssertionError("Dameng table DDL should generate index DDL from metadata"); } - return metadataResultSet(List.of(List.of(new LongText(ddl, ddl.substring(0, Math.min(ddl.length(), 64)))))); + resultOpen[0] = true; + return metadataResultSet( + List.of(List.of(new LongText(ddl, ddl.substring(0, Math.min(ddl.length(), 64))))), + () -> resultOpen[0] = false + ); } if ("setString".equals(name)) { int index = ((Integer) args[0]) - 1; @@ -486,6 +546,10 @@ class DamengAgentMetadataTest { } private static ResultSet metadataResultSet(List> rows) { + return metadataResultSet(rows, () -> {}); + } + + private static ResultSet metadataResultSet(List> rows, Runnable onClose) { int[] index = {-1}; return proxy(ResultSet.class, (method, args) -> { String name = method.getName(); @@ -527,6 +591,7 @@ class DamengAgentMetadataTest { }; } if ("close".equals(name)) { + onClose.run(); return null; } return defaultValue(method.getReturnType());