Skip to content

MyBatis extractor: single-quote + XML-comment fixes (small PR ready), and iBatis <sqlMap> coverage (runnable opt-in branch) #1182

Description

@ESPINS

Thanks for #389 — first-class MyBatis mapper nodes are the right direction. Using CodeGraph on some legacy Java/XML codebases surfaced three concrete gaps in the regex extractor (src/extraction/mybatis-extractor.ts), all verified against main today. Two are small fixes that fit the current regex approach; the third (iBatis) needs a parser. I've split the work into a PR-ready branch and a separate opt-in branch you can run and evaluate directly — take as much or as little as fits.

Disclosure up front: the parser used in the second branch is batis-xml, a project of mine — so I have a stake in suggesting it. Everything below is reproducible on your own corpora; I'm not asking you to take anything on faith.

The gaps (verified on main)

  1. Single-quoted attributes are dropped. The namespace/id/refid/resultType/parameterType regexes hardcode "([^"]+)", so id='getById' (legal, common in older mappers) is silently missed.
  2. No XML-comment awareness. The scan matches select/sql/include tags inside <!-- ... -->, producing phantom nodes/edges for commented-out SQL.
  3. iBatis 2 <sqlMap> → zero coverage. findMapperRoot gates on a <mapper namespace="..."> root; iBatis <sqlMap> files (namespace-less or DAO.method-style ids) fall through to the file-node-only path. On a legacy iBatis codebase that's no statement nodes at all.

Branch 1 — small fix, fits the regex extractor (fix-mybatis-quotes-comments)

Fixes gaps 1 and 2 with a minimal diff to mybatis-extractor.ts: the five attribute regexes accept either quote via a backreference ((["'])([^"']+)\1), and a length-preserving pre-pass blanks <!-- ... --> (keeping newlines so offsets/line numbers are unchanged; CDATA left intact). 10 focused tests added; no existing behavior changes. It stands alone as a clean fix — say the word and I'll open the PR.

https://github.com/ESPINS/codegraph/tree/fix-mybatis-quotes-comments

Branch 2 — iBatis + robustness via a parser (batis-xml-extractor, opt-in, runnable)

Gap 3 can't be closed by another regex without growing a second dialect's whole tag/#var# vocabulary, so this branch adds MyBatisParserExtractor, selected by CODEGRAPH_MYBATIS_EXTRACTOR=parserthe regex extractor stays the default; nothing changes unless you opt in. It maps the parser's output to the exact same node/edge shape (method nodes qualified <namespace>::<id>, contains edges, include references), so mybatisJavaXmlEdges and everything downstream are unchanged. To see it:

CODEGRAPH_MYBATIS_EXTRACTOR=parser  # then index a project with iBatis sqlMaps

What it adds beyond the regex path: iBatis <sqlMap> coverage (namespaced and DAO.method ids), correct comment/CDATA/single-quote/entity handling, and dynamic SQL flattened into the FTS-indexed docstring (<if>/<choose>/<foreach> expanded, #{}?, ${}→a sentinel) instead of tag-stripped. It's a wasm dependency (pure-JS to call, fits the web-tree-sitter precedent), lazy-loaded only when the parser extractor is used.

https://github.com/ESPINS/codegraph/tree/batis-xml-extractor

Notes / open questions

  • Both branches build clean (tsc --noEmit OK). On a Node whose node:sqlite bundles FTS5 (22.16+ or 24 — 22.14 doesn't, which trips the DB/end-to-end suites when run from source), I get the full suite green on both branches (2135 passing / 0 failing), including the MyBatis Java↔XML bridge e2e in parser mode. The parser branch's own tests are extractor-level, so they pass on older Node too.
  • The parser dep is pre-1.0 and mine — if the parser route is interesting, I'm glad to talk about a stability/maintenance commitment and an exact pin rather than a range.
  • Questions: (a) want Branch 1 as a PR now? (b) any appetite for iBatis coverage / a wasm dep in the extraction path, or is pure-JS/regex a hard line? (c) if a wasm dep is a hard no, I can grow the regex extractor to give <sqlMap> the same statement / <sql> / <include> node coverage it already gives <mapper> — that closes the zero-coverage gap with no new dependency. To be clear about the ceiling: that's coverage parity, not the parser's correctness — it wouldn't flatten iBatis dynamic tags (<isNotEmpty>/<iterate>/<dynamic>) into real SQL, and comment/CDATA/quote handling would need the same manual care as Branch 1. Regex can reach the fidelity the <mapper> extractor has today; the dynamic-SQL and robustness wins are the parser's alone.

Fixtures: Branch 1 adds synthetic single-quote and XML-comment test cases; Branch 2 adds synthetic iBatis <sqlMap>, CDATA, <include>, and dynamic-SQL cases (several adapted from batis-xml's own conformance corpus). All are synthetic — invented for the tests, not derived from any proprietary code.


Update (2026-07-07)

Since posting I hardened Branch 2 and found one more real bug — this one in the default regex extractor, not just the parser path.

  • The Java↔XML bridge is now a committed e2e test. init + indexAll over synthetic iBatis <sqlMap> and MyBatis <mapper> fixtures, asserting mybatisJavaXmlEdges fires in parser mode (and that the regex default sees zero statements for the iBatis sqlMap). Added unit coverage for databaseId vendor-split statements and <selectKey> (split into its own "<id>!selectKey" node).

  • A fourth gap — silent statement loss on same-line duplicate ids, and it affects main's default extractor. Node ids hash filePath:kind:qualifiedName:startLine (generateNodeId), so two statements sharing a qualifiedName and a start line collide, and INSERT OR REPLACE INTO nodes (id is the PRIMARY KEY) silently drops the first. The realistic trigger is a vendor-split pair (<select id="findUser" databaseId="oracle">…</select><select id="findUser" databaseId="mysql">…) written on one line. It's low-frequency — formatted XML puts them on separate lines and never hits it — but it's a real data-loss path on the default regex extractor, independent of anything parser-related. Fixed on both extractors by folding the statement's byte offset into the id-hash input; the stored qualifiedName is unchanged, so the bridge is untouched. Regression tests added. The regex-side fix is a ~1-line change to mybatis-extractor.ts and, like Branch 1, stands alone — happy to send it as its own small PR if that's easiest to take.

  • Full suite still green on Node 22.16+/24 (now 2140 passing; the +5 are the new tests).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions