Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Why does the xml value() method handle variables differently when executing a dynamically built string vs injection?

I’m perplexed as to why these 2 executions give different results.

Say I have the below XML:

<Main>
    <job id="1">
        <Action>true</Action>
    </job>
    <job id="2">
        <Action>false</Action>
    </job>
</Main>

The below SQL will give a null for actionvalue (I got the syntax from example A here):

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

exec sp_executesql
N'
select 
    j.value(''(job[@id="@jobid"])[1]/Action[1]'',''varchar(max)'') as actionvalue
from dbo.xmlstuff
    cross apply content.nodes(''/Main'') a(j)
N'@jobid varchar(1)',
@jobid = '1'

While the below gives the actual value (in this case true) (I got this syntax from here):

declare @sql nvarchar(500),
    @jobid varchar(1) = '1'
        
set @sql = 
'
select 
    j.value(''(job[@id="' + @jobid + '"])[1]/Action[1]'',''varchar(max)'') as actionvalue
from dbo.xmlstuff
    cross apply content.nodes(''/Main'') a(j)

exec sp_executesql @sql, N'@jobid varchar(1)', @jobid=@jobid;

Why is that? It obviously has something to do with how dynamically built string executions work vs how injection executions work, but the above 2 queries seem like they should accomplish the same thing.

I tried making jobid int and varchar with the same result. I also tried using more variables in the top query, and they work just fine. It’s just the piece within the square brackets after job that seems to have a problem with using variables.

>Solution :

They act differently because they aren’t the same. Your first attempt passes a variable’s name in quotes, meaning it is inferred as the literal value "@jobid" not the value of that parameter.

The latter attempt used injection, and thus injects a 1 into the string. You could remove the 2 latter parameters from the call to sys.sp_executesql in that attempt and it would still work.

You, however, don’t need dynamic SQL here. Instead tell XQuery that you are passing it a variable and the execution works:

declare @xml xml = '<Main>
    <job id="1">
        <Action>true</Action>
    </job>
    <job id="2">
        <Action>false</Action>
    </job>
</Main>';

declare @jobid char(1) = '1';

select 
    j.value('(job[@id=sql:variable("@jobid")])[1]/Action[1]','varchar(max)') as actionvalue
from (values(@xml)) X (content)
    cross apply content.nodes('/Main') a(j);

db<>fiddle

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading