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)
- Single-quoted attributes are dropped. The
namespace/id/refid/resultType/parameterType regexes hardcode "([^"]+)", so id='getById' (legal, common in older mappers) is silently missed.
- No XML-comment awareness. The scan matches
select/sql/include tags inside <!-- ... -->, producing phantom nodes/edges for commented-out SQL.
- 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=parser — the 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).
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 againstmaintoday. 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)namespace/id/refid/resultType/parameterTyperegexes hardcode"([^"]+)", soid='getById'(legal, common in older mappers) is silently missed.select/sql/includetags inside<!-- ... -->, producing phantom nodes/edges for commented-out SQL.<sqlMap>→ zero coverage.findMapperRootgates on a<mapper namespace="...">root; iBatis<sqlMap>files (namespace-less orDAO.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.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 addsMyBatisParserExtractor, selected byCODEGRAPH_MYBATIS_EXTRACTOR=parser— the regex extractor stays the default; nothing changes unless you opt in. It maps the parser's output to the exact same node/edge shape (methodnodes qualified<namespace>::<id>,containsedges, include references), somybatisJavaXmlEdgesand everything downstream are unchanged. To see it:What it adds beyond the regex path: iBatis
<sqlMap>coverage (namespaced andDAO.methodids), 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 theweb-tree-sitterprecedent), lazy-loaded only when the parser extractor is used.Notes / open questions
tsc --noEmitOK). On a Node whosenode:sqlitebundles 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.<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+indexAllover synthetic iBatis<sqlMap>and MyBatis<mapper>fixtures, assertingmybatisJavaXmlEdgesfires in parser mode (and that the regex default sees zero statements for the iBatis sqlMap). Added unit coverage fordatabaseIdvendor-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 hashfilePath:kind:qualifiedName:startLine(generateNodeId), so two statements sharing a qualifiedName and a start line collide, andINSERT 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 storedqualifiedNameis unchanged, so the bridge is untouched. Regression tests added. The regex-side fix is a ~1-line change tomybatis-extractor.tsand, 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).