New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Database: SqlBuilder::addWhere() consider NOT [Closes #667] #1047
Conversation
Well, I just realized maybe not more efficent, but more understandable solution :D |
Does it work even for more complex expressions than |
$replace = $match[2][0] . '(NULL)'; | ||
// expr IN () ==> expr IS NULL AND FALSE | ||
// NOT expr IN () ==> (expr IS NULL OR TRUE) | ||
// expr NOT IN () ==> (expr IS NULl OR TRUE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo in NULL
Rebased with brand new implementation :)
|
Nice :) I must test it with fresh head. |
Good work, but I've found some issues when whole condition is surrounded by brackets when using OR: $selection->where('(id NOT ? OR id = 3)', array()); ... leads to: WHERE ((`id` IS NULL OR `TRUEid` = 3)) and: $selection->where('(id = 3 OR id NOT ?)', array()); ... leads to: WHERE ((`id` = 3 OR `id` IS NULL OR TRUE) |
Thanks for testing :) rebased & fixed. See the change - only one line moved outside condition :D |
One more: $selection->where('id = 3 OR id = 2 AND id NOT ?', array()); leads to: WHERE ((`id` = 3 OR `id` = 2) AND (`id` IS NULL OR TRUE)) Added brackets change the meaning of the expression... |
Fixed :) now it should be finally ready :D |
cc @dg it's ready :) |
@hrach I believe you, but let's wait another 24 hours, ok? ;) |
@dg of course :) cc means review it, not merge :) |
Few more issues I am afraid: $selection->where('NOT id NOT ?', array()); leads to: WHERE (NOT `id` IS NULL OR TRUE)
-- should be:
WHERE (NOT (`id` IS NULL OR TRUE))
-- or:
WHERE (`id` IS NULL AND FALSE) $selection->where('NOT (id ? OR title ?)', array(), 'foo'); leads to: WHERE (((`id` IS NULL OR TRUE) OR (`title` = 'foo')))
-- should be:
WHERE (NOT ((`id` IS NULL AND FALSE) OR (`title` = 'foo'))) $selection->where('NOT (id NOT ? OR title ?)', array(), 'foo'); leads to: WHERE ((NOT (`id` IS NULL OR TRUE) OR (`title` = 'foo')))
-- should be:
WHERE (NOT ((`id` IS NULL OR TRUE) OR (`title` = 'foo'))) |
@tomaswindsor 1st example is bullshit, I'm not going to solve it. Other examples are fixed. $selection->where('NOT (id NOT ?)', array()); But I'm not going to support sth like SELECT * FROM `users` WHERE NOT NOT NOT NOT `id` = 3; which is also valid. rebased & fixed. |
->where('(id))', array()); // works
->where('(id) ?)', array()); // works
->where('(id) NOT', array()); // triggers exception
->where('(id) NOT ?', array()); // triggers exception
->where('NOT (id)', array()); // doesn't work properly
->where('NOT (id) ?', array()); // doesn't work properly
->where('(id NOT ? OR id ?)', array(), 3); // works with 2 extra brackets
->where('(id = ? OR id NOT ?)', 3, array()); // works with 2 extra brackets
->where('(id NOT ? AND id ?)', array(), 3); // works with 2 extra brackets
->where('(id = ? AND id NOT ?)', 3, array()); // works with 2 extra brackets
->where('NOT (title ? AND id NOT ? AND id > ?)', 'foo', array(), 3); // works with 1 extra bracket
->where('NOT (id NOT ? OR id ?)', array(), 3); // works with 1 extra bracket
->where('NOT id NOT ?', array()); // bullshit or not, doesn't work properly
->where('NOT (id) NOT ?)', array()); // bullshit or not, triggers exception I haven't tested the NOT NOT NOT case, but I think those shouldn't be that hard to resolve (replace NOT NOT with empty string comes to mind or at least NOT NOT NOT with NOT). |
@tomaswindsor As said, none of these will be fixed. Do something useful. |
Database: SqlBuilder::addWhere() consider NOT [Closes #667]
@hrach I am only trying to help. I understand some of the currently problematic conditions don't appear as particulary useful in real use, but if we want to be sure it will work in all useful cases, it's best to make sure it works in all valid ones. I appreciate the work and time you have put into this so don't interpret my reports as something trying to belittle your work. An example of the problematic query in more realistic use: ->where("SUBSTRING_INDEX(url, '.', -2) NOT ?", array()); ... incorrectly leads to: WHERE (SUBSTRING_INDEX((`url`, '.', -2)IS NULL OR TRUE ?) ... while: ->where("SUBSTRING_INDEX(url, '.', -2) NOT ?", array('mysql.com')); ... correctly leads to: WHERE (SUBSTRING_INDEX(`url`, '.', -2) NOT IN ('mysql.com')) Same problem appears even if the NOT is used before the expression rather than after. $blockedDomains = $db->table('domains')->select('url')->where('blocked', TRUE);
$anotherSelection->where("SUBSTRING_INDEX(url, '.', -2) NOT ?", $blockedDomains); And I know this could be resolved in other ways, where this problem wouldn't occur, but that's not the point. |
I think I've found a better solution. It involves using a special function to determine beginning of the expression just to the left of the IN operator (which is the only problematic part IMHO). You may check the implementation of that function here: https://gist.github.com/tomaswindsor/05ab413c5e2fe995b14b It should allow support of all the problematic queries mentioned above, and also strings with arbitrary content as part of the expression. What do you think? |
Your comment with substring must be definitelly fixed. The way how is the question. I'd like to avoid things such as parsing sql, you posted in the last query. Much more propper solution seems to me just note in the documentation, In the meantime I'm thinking this should be backed out. |
I think some sort of parser (like the one I referenced) is necessary to avoid most possible conflicts. And the one I referenced seems to work good enough IMHO (prove me wrong and come with condition it fails to parse correctly if you can). There may be needed multiple versions of this function for different database engines (different escaping of strings, possible differences in operators, etc.), and perhaps one fallback version with basic support. The usage in Only other issue could be speed of parsing, but even if that turns out to be too slow (which is unlikely IMHO), we can always cache the results of that function for given strings. |
My solution of fixing #667 and #869