3

I am trying to write a parametrized query in SQL server that uses a parameter value as part of the XPath, however it does not seem to work the way I would expect it to. Here is my sample:

create table ##example (xmltest xml)

declare @LanguagePath varchar(75)

set @LanguagePath = '(/languages/language[@id="en-US"])[1]'
insert into ##example 
values ('<languages>
            <language id="en-US">c</language>
            <language id="es-ES">c</language>
        </languages>')

insert into ##example 
values ('<languages>
            <language id="en-US">b</language>
            <language id="es-ES">b</language>
        </languages>')


insert into ##example 
values ('<languages>
            <language id="en-US">a</language>
            <language id="es-ES">a</language>
        </languages>')

--This is a working statement:
--select *  from ##example 
--order by xmltest.value('(/languages/language[@id="en-US"])[1]', 'varchar')


declare @SQL nvarchar(4000)
set @SQL = '
select *  from ##example 
order by xmltest.value(@LanguagePath1, ''varchar'')
'

exec sp_executesql @SQL, N'@LanguagePath1 varchar(75)',  @LanguagePath1 = @LanguagePath;

drop table ##example 

This code results in the error: The argument 1 of the xml data type method "value" must be a string literal.

Any ideas on how I can get this to work? I would like to try to make my xpath query safe from SQL injection.

Chris Mullins
  • 6,677
  • 2
  • 31
  • 40

2 Answers2

8

You should use sql:variable("@LanguagePath1") instead of just @LanguagePath1. Read more about it here. Though I'm not sure if dynamic xpath will work :) However something like xmltest.value('(/languages/language[@id=sql:variable("@languageCode")])[1] should work.

Saulius Valatka
  • 7,017
  • 5
  • 26
  • 27
0

What if the whole where condition is to be given dynamically.

@clause = xmltest.value('(/languages/language[@id="en-US"])[1]', 'varchar')=1

select *  from ##example 
where @clause
Mxyk
  • 10,678
  • 16
  • 57
  • 76
  • 1
    I don't think that would work because you can pass in a where clause as a parameter. You could turn the whole thing into a string and concatenate the @clause to it and that would work, but then that would not really be a parameterized query that would be no different than typing in the whole string, so you lose the saftey of the parameters when trying to avoid sql injection. The way Saulius did it works well. – Chris Mullins Nov 24 '10 at 12:27