Skip to content

Offer to put parameter values back when copying a statement out of a plan #467

Closed

Description

@erikdarlingdata

Out of #466, where @joshdbe copied a statement out of a plan and got @0, @1 … @6 where his literals had been.

That text is correct — it is what SQL Server compiled, because the database has PARAMETERIZATION FORCED — but it is not what anyone wants on the clipboard. You copy a statement out of a plan to run it, and a query full of undeclared @0 will not run.

The values are right there

Forced parameterization keeps every literal in the plan's ParameterList, with both compiled and runtime values. From a local repro of #466:

<ColumnReference Column="@0" ParameterDataType="varchar(8000)" ParameterCompiledValue="'123456'" ParameterRuntimeValue="'123456'" />
<ColumnReference Column="@1" ParameterDataType="int" ParameterCompiledValue="(5)" ParameterRuntimeValue="(5)" />
...
<ColumnReference Column="@6" ParameterDataType="varchar(8000)" ParameterCompiledValue="'2026-05-28 10:28:07.3132561'" />

ShowParameters already parses and displays all of this. Nothing new needs reading — the substitution just is not offered anywhere.

What to add

On the statement context menu, alongside the existing Copy Query Text and Open in Query Editor, a variant that puts the parameter values back:

  • Substitute ParameterRuntimeValue where present, falling back to ParameterCompiledValue. Values arrive pre-quoted ('123456', (5)) — the parens on numerics need stripping, the quotes on strings do not.
  • Match on whole tokens. @1 must not match inside @11, and a @0 inside a string literal must be left alone.
  • Only offer it when the statement actually has parameters. A statement with none should not grow a dead menu item.
  • Worth considering whether this should be the default behavior of the existing two items rather than a third and fourth item, with the raw form as the variant. The parameterized text is the honest record of what was compiled, but the substituted text is what people want to run, and a context menu with four near-identical entries is its own problem.

This is equally useful for procedure parameters and sp_executesql, not just forced parameterization — any plan whose statement text carries @name and whose ParameterList carries the value.

Note for whoever picks this up

Do not reach for the query editor's buffer as the source of truth. It only holds the original text in the one case where the user just executed it from that tab, and it is wrong for a plan opened from a file, from Query Store, or from another session.

Activity

  1. erikdarlingdata commented on Sep 1, 2026

    @erikdarlingdata
    OwnerAuthor

    Merged to dev in #470.

    Copy Query Text and Open in Query Editor now put the plan's captured parameter values back into the statement before handing it over — runtime values first, compiled as the fallback — so what lands on your clipboard actually runs. The labels say (with values) whenever that happened, and a Copy Query Text (as parameterized) entry appears alongside for the times you want the exact text that matches the plan cache and Query Store. When the plan carries no values, nothing changes and no extra entries appear.

    Grid Ctrl+C produces the same text as the menu entry, because handing those two different statements would just be a new bug report.

    Works for anything whose ParameterList carries values — forced parameterization, stored procedure parameters, sp_executesql — and invents nothing for parameters the plan didn't capture (OPTION(RECOMPILE) plans keep their bare names, visibly).

    @joshdbe this is yours from #466 — next nightly off dev will have it.

  2. joshdbe commented on Sep 2, 2026

    @joshdbe

    Shows actual values in Copy/Paste and query editor. Still shows parametrized in Human and Robot Advice

  3. erikdarlingdata commented on Sep 2, 2026

    @erikdarlingdata
    OwnerAuthor

    Good catch, and it's the same mistake twice in one day — #447 was me fixing one consumer and leaving the others, and so was this.

    Both advice buttons come out of AnalysisResult: Humans through TextFormatter, Robots through the JSON serializer, and both take their text from ResultMapper. #467 substituted at the clipboard and the editor because that's where you'd reported it, and never followed the value down to where the analysis is built.

    Same seam feeds a few things you haven't hit yet — the HTML export, plan comparison output, and the MCP tools. That last one is the one that bothers me: handing a model @0 with no values is handing it a question it can't answer.

    Tracking it in #482, which is where the fix will land. There's one thing to establish before changing it — whether anything matches statements by their text, since substituting values into two runs of the same query with different parameters could stop them pairing up. That would be a worse bug than the one you reported, so it gets checked before anything moves.

    Thanks for going back and testing the rest of it rather than stopping at the part that worked.

  4. erikdarlingdata commented on Sep 2, 2026

    @erikdarlingdata
    OwnerAuthor

    Fixed on dev in #483, tracked in #482 — @joshdbe, tonight's nightly will have it.

    Advice for Humans, Advice for Robots, the HTML export, the comparison report and the MCP tools all get the values now, because it's done where the analysis is built rather than at each button.

    Two things it deliberately does not do, both found while fixing it. SELECT @job_name = name keeps its variable rather than becoming SELECT NULL = name, which would read as a comparison — assignment targets are left alone. And the repro-script tool still gets the parameterized text, because it wraps the statement in sp_executesql with a parameter list from the same plan; handing it inlined literals would declare parameters that appear nowhere in the body.

    Same ask as before: tell me if it's right rather than me telling you it is.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions