-
-
Notifications
You must be signed in to change notification settings - Fork 106
Description
You use this pattern to parse yer SQL where clause and extarct values for query params:
\s*(((?:\s+(?:NOT\s+)?LIKE)|(?:\s+(?:NOT\s+)?IN)|(?:\s+IS(?:\s+NOT)?)|(?:<>)|(?:<=)|(?:>=)|(?:!=)|(?:!<)|(?:!>)|=|<|>))\s*(\('.+?'\)|\(((?:\+|-)?[0-9\.],?)+\)|'.+?'()|''|((?:\+|-)?[0-9\.]+)()|NULL)((\s*$|\)|\s+(AND|OR)))
This is derived from here: https://github.com/cfwheels/cfwheels/blob/master/wheels/model/initialization.cfm#L23-L25
There's a slight bug in it. It should be:
\s*(((?:\s+(?:NOT\s+)?LIKE)|(?:\s+(?:NOT\s+)?IN)|(?:\s+IS(?:\s+NOT)?)|(?:<>)|(?:<=)|(?:>=)|(?:!=)|(?:!<)|(?:!>)|=|<|>))\s*(\('.+?'\)|\(((?:\+|-)?[0-9\.],?)+\)|'.+?'()|''|((?:\+|-)?[0-9\.]+)()|NULL)((\s*$|\s*\)|\s+(AND|OR)))
The key bit is how you handle identifying the end of a [operand / operator / value] block:
(\s*$|\)
So: either the end of the string ($) or a closing parenthesis (\)). You overlook the fact that there might be whitespace between the previously matched value and the closing parenthesis.
This situation can arise from how the SQL clause is indented in the source code, eg:
lines = invoice.lines(
where="
someColumn IN ('x', 'y')
AND anotherCoumn > '#someValue#'
" // <--- note the space there after the value
)
CFWheels goes ahead and wraps that in parentheses, so it ends up being:
(
someColumn IN ('x', 'y')
AND anotherColumn > '#someValue#'
)
An easy work around is for me to control that whitespace in my code, but obvs I should not have to.
I wonder if you didn't do yourselves any favours by running that entire pattern into one continuous string, whereas if you used the (?x) flag you could have broken it down into sections with comments explaining what yer up to. If you had done that, you'd've probably noticed the problem.
Some reading on the (?x) flag here: https://blog.adamcameron.me/2013/01/regular-expressions-in-coldfusion-part.html#regexcomments
It would also be super helpful for ppl like me who are trying to troubleshoot bugs, and despite being pretty comfortable with regex patterns, could benefit from explanatory comments that articulate what you think the pattern is doing. So I can spot where it's not actually doing that ;-)
Also when testing regexes, you really need discrete test cases for each subexpression, and each conditional. If you had those, you'd've like caught the problem too. That said I didn't check your testing, but just assumed it's superficial cos testing of this sort of thing usually is.
No urgency on this from my perspective cos I can solve it on my end, and know now to avoid it in future. But figured you'd like to know about it.
Lastly, can I recommend you drop this approach in CFWheels 3.x. If ppl want to put params in their SQL, they just can with param markers now (? or :paramRef), and pass an array or struct with the param values. This would be more "modern CFML" than hacking <cfqueryparam> tags into a <cfquery> tag, and a lot easier and less error prone for you.
Cheers!