fix: digest real production plans (nesting depth, Object table names, character refs)

Measured against a 2.7 MB production plan that failed to import:
- maxNestedTags: real spaghettified plans nest 2729 tags deep and fast-xml-parser caps at 128, which surfaced as 'Maximum nested tags exceeded' and blamed the export. Raised to 20000; the 12 MB input cap bounds the worst case.
- table names live on <Object>, not on <IndexScan>/<TableScan>. The digest reported 0 tables for a plan with 51 objects; the analyst model never saw a table name. Now 37 distinct tables.
- SSMS writes StatementText with numeric character references (&#xD;&#xA; on every line, 90 in this plan). Attribute/tag processors decode them, so the model gets SQL instead of entity noise.
- 3 new digest tests, each from one of the three measurements above. 39 unit tests pass.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
ahmed mohammed
2026-09-22 11:16:18 +03:00
co-authored by Claude Opus 4.7
parent 4b5c19b5d2
commit 3fa436103a
4 changed files with 104 additions and 2 deletions
+40
View File
@@ -108,3 +108,43 @@ test("a parse failure says why instead of blaming the export", async () => {
assert.ok(e.message.length > 40, `message too thin: ${e.message}`);
assert.notEqual(e.message, "That XML will not parse — re-export the actual execution plan.");
});
test("a deeply nested real plan parses (spaghettified plans are thousands of tags deep)", async () => {
// fast-xml-parser defaults to maxNestedTags: 128. A 2.7 MB production plan measured 2729 deep and died
// with "Maximum nested tags exceeded", which read like a corrupt export.
const depth = 3000;
const open = "<ShowPlanXML><RelOp NodeId='1'>".repeat(1);
const inner = "<RelOp NodeId='n'>".repeat(depth) + "<ColumnReference Column='[dbo].[t].[c]'/>" + "</RelOp>".repeat(depth);
let err: unknown = null;
let out: any = null;
try { out = digestPlan(open + inner + "</RelOp></ShowPlanXML>"); } catch (e) { err = e; }
assert.equal(err, null, String((err as Error)?.message));
assert.equal(out.topOperators.length, 12, "digest should rank, not truncate");
});
// ---- real-plan shapes measured on a 2.7 MB production plan (plan-cxpaket.sqlplan) ----
test("table names come off the Object child, not the scan element", async () => {
// ShowPlan carries Database/Schema/Table on <Object>, so reading them off <IndexScan> returned nothing:
// a 51-object production plan digested to zero tables and the model never saw a table name.
const xml = `<ShowPlanXML><BatchSequence><Batch><Statements><StmtSimple StatementText="SELECT 1">
<QueryPlan><RelOp PhysicalOp="Index Scan"><IndexScan>
<Object Database="[crm]" Schema="[dbo]" Table="[Orders]" Index="[IX_Orders_CustomerId]" />
</IndexScan></RelOp>
<RelOp PhysicalOp="Table Scan"><TableScan>
<Object Database="[crm]" Schema="[dbo]" Table="[Customers]" />
</TableScan></RelOp></QueryPlan></StmtSimple></Statements></Batch></BatchSequence></ShowPlanXML>`;
assert.deepEqual(digestPlan(xml).tables, ["crm.dbo.Orders", "crm.dbo.Customers"]);
});
test("numeric character references in StatementText become the characters", () => {
// SSMS writes the plan text with &#xD;&#xA; line breaks: 90 of them in the production plan, so the
// analyst model was handed entity noise instead of SQL.
const xml = `<ShowPlanXML><BatchSequence><Batch><Statements>
<StmtSimple StatementText="SELECT a &#xD;&#xA;FROM dbo.T WHERE x &gt; 1 &amp; y = 2"/>
</Statements></Batch></BatchSequence></ShowPlanXML>`;
const sql = digestPlan(xml).sql;
assert.match(sql, /FROM dbo\.T/);
assert.match(sql, /\n/);
assert.ok(!sql.includes("&#x"), sql);
assert.match(sql, /x > 1 & y = 2/);
});