Skip to content

fix(dm): support EXPLAIN execution plans - #2771

Merged
Aias00 merged 8 commits into
OtterMind:mainfrom
haizhi03:fix/dm-explain
Aug 31, 2026
Merged

Aias00 merged 8 commits into
OtterMind:mainfrom
haizhi03:fix/dm-explain

Conversation

@haizhi03

@haizhi03 haizhi03 commented Aug 26, 2026 •

Copy link
Copy Markdown
Contributor

Related issue

Closes #2762

Summary

Add DM-specific EXPLAIN execution support so execution plans are retrieved through the DM JDBC driver's DmdbConnection.getExplainInfo API instead of the default JDBC execution path.

Key changes:

  • Route DM user SQL through a dedicated DMCommandExecutor.
  • Delegate ordinary DM SQL to the existing DefaultSQLExecutor.
  • Add an independent DM lexer and parser with DM-specific EXPLAIN syntax.
  • Support both explicitly entered EXPLAIN <SQL> statements and the Explain action.
  • Avoid generating duplicate EXPLAIN EXPLAIN <SQL> statements.
  • Support original and wrapped DM connections without introducing a static DM JDBC dependency.
  • Preserve SQL guard checks, execution metrics, SQL type, cancellation checks, streaming behavior, and database error propagation.
  • Add focused tests for parsing, driver isolation, wrapped connections, streaming, cancellation, errors, and ordinary SQL behavior.

Affected surfaces

  • Frontend / Web
  • Backend / API / Storage
  • Database plugin / Driver
  • JCEF / Desktop packaging
  • CI / Build / Release
  • Documentation only

Verification

  • DMCommandExecutorTest: 16 tests, 0 failures/errors.
  • git diff --check origin/main...HEAD: passed.
  • Manual integration verification was performed against a real DM8 database
    using the DM JDBC driver.

Manual verification results:

  • Directly executing EXPLAIN SELECT ... returned the actual DM execution plan
    (#NSET2, #PRJT2, #BLKUP2, and #SSEK2) instead of an update count.
  • Regular DM INSERT, SELECT, and UPDATE statements executed normally.
  • The inserted row was returned successfully.
  • After the update, the queried row contained
    ID = 1 and NAME = DM Explain OK.

UI evidence

DM EXPLAIN result

image

Regular DM SQL result

image

Risk and compatibility

  • Public API or stored data: No storage schema or external API changes. The shared SPI changes expose only the existing SQL guard and result-publishing behavior required by the DM executor.
  • Database or driver compatibility: EXPLAIN requires a DM JDBC driver that provides DmdbConnection.getExplainInfo(String). Unsupported drivers return an explicit SQL error instead of falling back to the
    JDBC EXPLAIN path.
  • Network, privacy, or security: No network or privacy behavior changes. Existing SQL guard checks run before invoking the vendor API.
  • Community / Local / Pro boundary: No edition or runtime-mode behavior changes.
  • Backward compatibility: Ordinary DM SQL continues through the existing default executor behavior. Other database plugins are unchanged.

Reviewer map

  • Start here:
    • DMCommandExecutor for SQL routing and EXPLAIN result handling.
    • DMExplainClient for connection unwrapping and vendor API invocation.
    • DMSqlParser and the DM ANTLR grammar for EXPLAIN recognition.
    • DMMetaData and DMSqlBuilder for executor registration and SQL construction.
    • DMCommandExecutorTest for expected behavior and regression coverage.
  • Failure condition:
    • DM EXPLAIN falls through to the normal JDBC execution path.
    • The Explain action generates a duplicated EXPLAIN prefix.
    • Wrapped or dynamically loaded DM connections cannot resolve getExplainInfo.
    • Streaming results lose their SQL type, event ordering, metrics, or cancellation behavior.
    • Ordinary DM SQL behavior changes.
  • Rollback or disable path: Revert this PR to restore the previous DM executor and parser behavior.

Contributor declaration

  • I linked the Issue that defines this change.
  • I tested the affected behavior and reported the actual results above.
  • I did not include credentials, private data, or generated build output.
  • I disclosed substantial AI assistance below, or this PR contains no substantial AI-generated code.

AI assistance: AI assistance was used to draft and organize this PR description.

- route DM SQL through the DM command executor
- add dedicated DM lexer and parser support
- retrieve execution plans through DmdbConnection.getExplainInfo
- preserve SQL type, metrics, streaming, and error behavior
- cover explicit EXPLAIN, Explain actions, cancellation, and driver isolation

Fixes OtterMind#2762

@Aias00 Aias00 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the DM EXPLAIN support after the main-branch update.

I did not find any blocking code issues in this pass. The DM executor now routes EXPLAIN through getExplainInfo instead of falling back to JDBC PreparedStatement.execute(), buildExplain avoids double-prefixing EXPLAIN, the streaming path publishes the materialized explain plan consistently, and the explicit guardStatement hook covers the non-JDBC-statement execution path.

Local verification already run:

  • DMCommandExecutorTest: 16 tests, 0 failures/errors
  • git diff --check origin/main...HEAD: passed

Remaining risk: I did not run against a real DM database / real DM JDBC driver locally, so that part remains covered by CI or manual integration verification.

@haizhi03

Copy link
Copy Markdown
Contributor Author

Thanks for the review.

I have now completed manual integration verification against a real DM8
database using the DM JDBC driver.

Directly executing EXPLAIN SELECT ... returned the actual DM execution plan,
and regular DM INSERT, SELECT, and UPDATE statements continued to work
normally. The final queried row contained ID = 1 and
NAME = DM Explain OK.

I have updated the PR verification section and added the screenshots.

@Aias00 Aias00 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found one remaining parser issue that is not covered by the manual DM8 verification.

DMSimpleParserVisitor.visitExplain_statement sets the current statement type to EXPLAIN, but then returns super.visitExplain_statement(ctx). That descends into the inner statement and can overwrite the outer type, for example visitSelect_statement sets the same statement to SELECT. The same pattern appears in DMParserVisitor and DMValidTableVisitor.

The DM executor masks this on the execution path by mutating the statement back to EXPLAIN, so the real-DM getExplainInfo verification can still pass. But shared parser consumers such as SqlUtils.parseStatements, syntax parsing, AI tooling, refresh metadata, or validation can still see an explicit EXPLAIN SELECT ... as SELECT instead of EXPLAIN.

Could you preserve the outer EXPLAIN type in these visitors, either by not descending into the inner DML for an EXPLAIN statement or by restoring EXPLAIN after visiting children? Please also add focused parser coverage for explicit EXPLAIN SELECT/UPDATE/DELETE/INSERT/MERGE through the affected parser entry points.

@haizhi03

Copy link
Copy Markdown
Contributor Author

Thanks for catching this. I updated all three DM parser visitors to stop descending into the inner DML after setting EXPLAIN. I also added coverage for three parser entry points × five DML types (15 tests), all passing.

@haizhi03
haizhi03 requested a review from Aias00 August 28, 2026 09:11

@Aias00 Aias00 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am requesting changes for two verified behavioral issues.

  1. The DM EXPLAIN streaming path is not cancellable while DmdbConnection.getExplainInfo(...) is in flight. The normal path registers its PreparedStatement through statementListener.onStatementCreated(...), and the web/task cancellation paths call Statement.cancel(). DMCommandExecutor bypasses that lifecycle and only checks cancellation before and after the blocking vendor call, so cancel cannot interrupt a long-running EXPLAIN. Please preserve or replace that cancellable-resource contract and add a test that cancels while getExplainInfo is blocked, ideally through the real streaming caller path.

  2. Correction to my previous parser comment: Statement#setType is write-once, so descending into the inner DML did not overwrite an already assigned EXPLAIN type. I verified that all 15 new DMSqlParserTest cases already pass at commit 44de37696, before the visitor change in 6a8950c40. Replacing super.visitExplain_statement(ctx) with return null now prevents the valid-table visitor from collecting inner table and column metadata: EXPLAIN SELECT NAME FROM SYSOBJECTS returned SYSOBJECTS/NAME before that change, while the current head returns null table/column maps. Please restore child traversal and add assertions that the outer type remains EXPLAIN while inner table/column metadata is preserved.

Current focused verification: DMCommandExecutorTest and DMSqlParserTest, 31 tests, 0 failures/errors; git diff --check also passes. The requested changes concern behavior that the current tests do not cover.

@Aias00
Aias00 merged commit d2ac449 into OtterMind:main Aug 31, 2026
16 checks passed
@openai0229 openai0229 moved this from In Review to Done in Chat2DB Community Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

bug(dm): EXPLAIN displays affected rows 0 instead of the execution plan

3 participants