-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Allow calling getQuery() multiple times, and other improvements #5127
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
Conversation
I added a handful of tests, as a first draft. Suggestions are welcome. Maybe it could be polished. Maybe some tests are redundant, maybe some cases are still not covered (combinations)… Note I couldn't (yet?) write a test where simple binds markers could be erroneously replaced twice, as the method |
To summarize the approach of this PR: I'm adding an optional, intermediary state "swappedQueryString". Starting from the original query string, two changes can be applied to this original string: swapping the db prefix, and compiling the binds. We have to always keep record of the original string, and we also need a state to know if the binds have been compiled (here "finalQueryString" fills this purpose, by being discarded when compiling has to be done again). We can't apply the swaps to "finalQueryString" as the property would no longer let us know when bind compiling is needed. And of course we should not apply the swaps to "originalQueryString", as its purpose is to keep the user-supplied string untouched. Thus, we need to record an additional state, which is "swappedQueryString" here. An alternative would have been to store an array of prefix swaps to make (a list of "from → to" pairs), and apply them to the original string, before the binds, when compiling. But nevertheless this would make us record an additional state as well, and the "swappedQueryString" approach is simpler. |
system/Database/Query.php
Outdated
} | ||
|
||
$sql = $this->finalQueryString; | ||
$sql = $this->getQuery(); |
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.
Please rebase and remove the following line:
$query->getQuery(); |
This not needed anymore with this change.
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.
Rebased :)
On a related note, here is the behavior when
That handling of So, in the following example, we still have the "42" bind after the second line: $query->setQuery('SELECT * FROM users WHERE id = ?', [42]);
$query->setQuery('SELECT * FROM users WHERE name = ?'); I don't know what would be the best to do when |
The code itself should be ready. I'm just a bit unsure that the tests are perfect (for instance: remaining uncovered edge cases, and where I split hairs: redundant tests!). I should have a fresh look at these, and I'm open to reviews. |
This has been at draft status for couple of months now. Where are we on this? Or should this be closed? |
I think it's a serious issue, therefore even if this PR is stale it shouldn't be closed. Even if I noticed and fixed an error afterwards ("Fix swapPrefix() not effective when called after a first call to getQuery()"), the code should be quite stable. Some alternative approaches I thought of:
Feel free to review the code (in particular to ensure the tests are clean!) or even to push changes. |
@vlakoff Do you mean this PR is completed and ready for review? |
I have squashed the commits. Therefore, marking the PR for review. |
@@ -448,7 +605,6 @@ public function testHighlightQueryKeywords($expected, $sql) | |||
{ | |||
$query = new Query($this->db); | |||
$query->setQuery($sql); | |||
$query->getQuery(); |
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.
Why do you remove it?
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.
This had been noticed previously, see above.
It's because getQuery()
can now be called multiple times without error, I could remove the ugly hack here. That hack was in place so that getQuery()
isn't called an additional time.
But… it required that getQuery()
had been be called beforehand, so that finalQueryString
is set, as compileBinds()
needed it to be set beforehand… hence the "artificial" getQuery()
here, just so that finalQueryString
is set.
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.
I see.
|
||
$query->setQuery($origSQL); | ||
$query->swapPrefix($origPrefix, $newPrefix1); | ||
$query->swapPrefix($newPrefix1, $newPrefix2); |
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.
I think there is no use case that calls swapPrefix() more than once.
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.
It is possible to swap prefix several times with the current code, so I made sure it is still possible with my new code, just in order to not introduce any regression.
Still, it should be worth covering this with a test, to ensure the behaviour remains consistent.
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.
Okay.
I think the user guide should be updated. Just remove the "final" ?
|
@vlakoff You missed GPG sign. |
Why do you need to make it possible? |
@kenjis how to do this setup? Can you please guide me to do this? Which editor you are using? |
@cijagani The coverage report? It is a HTML file generated by PHPUnit.
|
@kenjis oh wow. thank you. |
It has been a bit of a hassle to configure, but these commits are now signed. |
My final comment: #5127 (comment) |
Although the prefix swap propagates of course up to the final SQL, the replacements actually occur in the original SQL (i.e. before the binds are applied).
Done: 0839468 |
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.
Thank you!
LGTM
@vlakoff @samsonasik Thank you! |
Main fix:
getQuery()
can now be called multiple times without trouble,compileBinds()
will be called only on original SQL string. Previously, callinggetQuery()
additional times was executingcompileBinds()
again and again, and on the already compiled SQL string…Additional improvements and notes:
getQuery()
is called multiple times,compileBinds()
is executed only the first time.setQuery()
orsetBinds()
discardfinalQueryString
, so they can be called multiple times with new data, and the SQL will be generated with this new data.swapPrefix()
will always be executed on the original SQL string (with the bind placeholders), which is a bit safer than on the final SQL string (as the search/replace just operates on the whole SQL string).swapPrefix()
multiple times, to execute multiple prefix swaps.swapPrefix()
can be called before or aftersetBinds()
(but not beforesetQuery()
, as it operates onoriginalQueryString
).debugToolbarDisplay()
would fail if called beforefinalQueryString
is assigned, ascompileBinds()
was relying ongetQuery()
to assign this property beforehand. (refs [Query] Do not recompile binds if already compiled #4534)Checklist: