From 41e68e5a82f0841b6d247e8eacc1e43f03b6f498 Mon Sep 17 00:00:00 2001 From: zipg <4047349+zipg@users.noreply.github.com> Date: Mon, 13 Jul 2026 11:58:11 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(dameng):=20=E9=81=BF=E5=85=8D=E5=85=83?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=9F=A5=E8=AF=A2=E5=B9=B6=E8=A1=8C=E9=94=81?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/dbx/agent/dameng/DamengAgent.java | 28 +++++---- .../agent/dameng/DamengAgentMetadataTest.java | 59 +++++++++++++++++-- 2 files changed, 71 insertions(+), 16 deletions(-) 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 103b20692..8949f85d7 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 ObjectSource getObjectSource(String schema, String name, String objectTyp 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 ObjectSource getObjectSource(String schema, String name, String objectTyp @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 List getColumns(String schema, String table) { 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 List getColumns(String schema, String table) { // 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 List getColumns(String schema, String table) { 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 @@ -1031,18 +1035,18 @@ private void fillMissingColumnComments(String schema, String table, List 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 @@ private List independentIndexes(String schema, String table) throws E 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 @@ private static boolean isInternalIndexMetadata(IndexInfo index) { 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..b9e063aa0 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,7 @@ void usesColumnCommentsMetadataQuery() { .findFirst() .orElseThrow(); Assertions.assertTrue(columnsSql.contains("LEFT JOIN ALL_COL_COMMENTS"), columnsSql); + Assertions.assertTrue(columnsSql.startsWith("SELECT /*+ PARALLEL(1) */"), columnsSql); } @Test @@ -203,6 +204,43 @@ void appendsTableAndColumnCommentsToTableDdl() { 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 +354,19 @@ private static Connection metadataConnection( 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 +377,7 @@ private static Connection metadataConnection( 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 +452,7 @@ private static List indexRow(String name, String columns, String uniquen 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 +461,11 @@ private static PreparedStatement dbmsMetadataStatement(String ddl) { 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 +532,10 @@ private static PreparedStatement metadataStatement(List> rows, List } 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 +577,7 @@ private static ResultSet metadataResultSet(List> rows) { }; } if ("close".equals(name)) { + onClose.run(); return null; } return defaultValue(method.getReturnType()); From da1818578d4612e0d6b0041c08c2834db8b0ae88 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Mon, 13 Jul 2026 23:46:19 +0800 Subject: [PATCH 2/2] fix(dameng): serialize index metadata query --- .../java/com/dbx/agent/dameng/DamengAgent.java | 2 +- .../dbx/agent/dameng/DamengAgentMetadataTest.java | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) 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 8949f85d7..bb5e137cc 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 @@ -741,7 +741,7 @@ public List listIndexes(String schema, String table) { 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, 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 b9e063aa0..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 @@ -37,6 +37,20 @@ void usesColumnCommentsMetadataQuery() { 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 void usesTableCommentsMetadataQuery() { DamengAgent agent = new DamengAgent();