seesharper / DbReader

A simple and fast database reader for the .Net framework
17 stars 0 forks source link

ParameterMatcher throws InvalidOperationException when using XPath in query #69

Open vidaj opened 2 months ago

vidaj commented 2 months ago

Currently, DbReader validates all parameters in the SQL with the parameters supplied to Read.

Because ParameterMatcher.Match matches every parameter in the SQL with the supplied parameters, the query will not be run if ParameterMatcher find a parameter in the SQL that does not match the name of any supplied parameters.

The way ParameterMatcher finds parameters in the SQL is a simple regex, looking for a colon with some text after it. In most cases, this is good enough - but it also is prone to false positives.

For example, when a query uses Oracle XMLTable-syntax with namespaces, XPaths used in the query will use colon as a namespace separator - which clearly isn't a database parameter.

Example:

SELECT 
  xml.myvalue
FROM mytable t
CROSS APPLY XMLTABLE(
    XMLNAMESPACES('http://example.com/SomeType' as "m"),
    '//*:BaseType'
   passing t.xmltype
   columns
      myvalue varchar2(50) path 'm:MyValue'
) xml
WHERE t.id = :ID

For this query, ParameterMatcher will throw an exception because the parameters 'BaseType' and 'MyValue' is not supplied, but the only real parameter here is 'ID'.

I see two ways of fixing this: 1) Optional parameter to Read to not validate parameters at all 2) Change ParameterMatcher to match the supplied parameter names with the parameters from the sql. This will only throw an exception if you have supplied a IDataParameter with a name that is not found in the sql, and ignores false positives like above.

seesharper commented 2 months ago

DbReaderOptions has a property ParameterParser which can be set to a new instance of the RegExParameterParser. This way you can supply your own regex that takes care of your needs 👍 Something like this should work /(:\w+(?=(?:[^'\"]*(?:'|\")[^'\"]*(?:'|\"))*[^'\"]*$))|(@\w+(?=(?:[^'\"]*(?:'|\")[^'\"]*(?:'|\"))*[^'\"]*$))/gm