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 (
 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:
co-authored by
Claude Opus 4.7
parent
4b5c19b5d2
commit
3fa436103a
@@ -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 
 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 
FROM dbo.T WHERE x > 1 & 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/);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user