Support for Drupal 7 is ending on 5 January 2025—it’s time to migrate to Drupal 10! Learn about the many benefits of Drupal 10 and find migration tools in our resource center.
Instead, placeholder names should be specific to a query.
Critical because this blocks Views caching, nicer hook_query_alter(), and #495968: Introduce drupal_render() cache pattern. Start using it for blocks.
Comment | File | Size | Author |
---|---|---|---|
#47 | 496500-fix-installation.patch | 1.27 KB | Damien Tournoud |
#44 | query_placeholder.patch | 15.38 KB | chx |
#42 | query_placeholder.patch | 16.41 KB | chx |
#40 | query_placeholder.patch | 14.84 KB | chx |
#39 | query_placeholder.patch | 49.67 KB | chx |
Comments
Comment #1
catchComment #2
BerdirOne problem i can see from this is when you have subqueries or otherwise "combined" queries, so the placeholders have to be unique for each sent query, and not for each query object/statement.
Comment #3
Crell CreditAttribution: Crell commentedHm. That means we have to make it re-entrant. That's highly ugly.
Random brainstorming: Could we have execute() reset the global counter before each compile? To make it re-entrant, we could have a semaphore static that gets set to TRUE at the start of execute() and FALSE at the end of execute()?
Comment #4
BerdirOk, the attached patch moves that $next_parameter thing to the DatabaseConnection class and does reset it after *every* sent query. It is also not static anymore, because it just needs to be unique per database connection imho.
It *seems* to work as expected, from looking at devel's query log.
Note: by *every* query, I mean *every*. This needs to be improved to only be called it when necessary (queries that use DatabaseCondition), but this was the fastest way to have something working ;)
Comment #5
Crell CreditAttribution: Crell commentedWe use dynamic placeholders in a lot of places all over the DB layer, not just in condition objects. If we're going to move to a centralized counter per-connection object, we should use it in those other places, too.
Comment #6
BerdirYes I know, I thought about that too, but...
a) Only conditions have the recursion problem, INSERT/UPDATE/MERGE are just a foreach
b) INSERT/UPDATE/MERGE do not have access to $connection when the Query string is built, so it would need to be static again, and I'm not sure if that is a good thing.Edit: Oh, I was wrong regarding b, they do have access :)
Comment #7
Crell CreditAttribution: Crell commentedAt minimum, Select queries can also recurse. You can also have Select queries inside an insert/update/delete via the condition, which compile themselves separately from the condition object. Plus, I really don't like having different parts of DBTNG track their indexes differently.
If we just have the counter take a type param, we can then have each class simply submit its __CLASS__ as the counter type. They could be tracked together or separately at that point; shouldn't make a huge difference.
It also occurs to me that the array handling of parameters also uses an auto-counter, so that should get centralized as well.
Comment #9
Crell CreditAttribution: Crell commentedOK I started working on this, and it's going to be more complex than I thought. Most of the query builders double-generate their placeholders: Once in execute(), once in __toString(). So we can't just use the connection's placeholder counter as we'd end up calling it twice.
... Except that is OK, because those aren't static to begin with. Hang on a minute here!
Comment #10
Crell CreditAttribution: Crell commentedRight, so, on further investigation Berdir is right. We only need to centralize the placeholders on conditions, not because other query objects don't use them but because they don't use static versions of them. In fact, as noted in #9 trying to centralize those would break because we have to double-generate them.
So the attached patch is mostly just a reroll. :-) Hey bot, how do you like it?
Comment #11
catchLooks great, and I think the removed comments don't need to be replaced beyond what's there, unique placeholders per query are how I'd expect it to work.
Comment #12
webchickNew code makes lots of sense.
Do we need to expand our test coverage to cover some of the "Oh, what about..." things brought up in this issue?
Comment #14
moshe weitzman CreditAttribution: moshe weitzman commentedanyone up for a reroll? we have 1 exception :(
Comment #15
c960657 CreditAttribution: c960657 commentedDue to the
if ($this->changed) {
check in DatabaseCondition::compile(), the previous query string is cached. Is this safe when placeholders are reused?Comment #16
Crell CreditAttribution: Crell commentedI couldn't reproduce the exception. Bot, are you sure?
Comment #17
killes@www.drop.org CreditAttribution: killes@www.drop.org commentedThe bot wasn't sure.
Comment #18
moshe weitzman CreditAttribution: moshe weitzman commentedre-upload. no changes. resetting status to rtbc as well. please don't commit until bot goes green.
Comment #19
Dries CreditAttribution: Dries commentedCommitted to CVS HEAD. Thanks for driving this one home! :)
Comment #20
c960657 CreditAttribution: c960657 commentedWith this patch the behaviour of the following code changed:
Before, the output was two empty arrays. Now the output is
array ( )array ( 0 => 'variables', )
, i.e. appending another condition to the default AND clause generates more results instead of less results as expected.The query string generated in the latter query is this: SELECT cache.cid AS cid FROM {cache} cache WHERE ( (cid = :db_condition_placeholder_1) OR (cid = :db_condition_placeholder_2) )AND (cid = :db_condition_placeholder_1)
Comment #21
Crell CreditAttribution: Crell commentedRe-using select objects isn't supported to begin with. Insert queries are the only ones that we explicitly support reusing. The combination of query_alter and extenders makes the select query compiler single-use.
Comment #22
chx CreditAttribution: chx commentedThis is broken. If you are using a complex query with simple conditions and db_or both this is broken because this is per condition not per query.
Comment #23
BerdirI'm pretty sure it is per query (It is actually per-connection and reset after a query has been executed). I'm pretty sure the tests would fail if it would be per condition.
Comment #24
chx CreditAttribution: chx commentedBut reverting this made #394182: DBTNG search.module pass.
Comment #25
chx CreditAttribution: chx commentedAlso? I read the code and placeholders are reset in the query method. If you happen to run that while building another query that is also broken. This definitely needs to be rolled back and a better approach worked out. I believe the placeholder needs to be part of the query object and probably db_or() needs either the query object or it needs to be a method.
Comment #27
chx CreditAttribution: chx commentedOK, here is the rollback patch.
Comment #28
Damien Tournoud CreditAttribution: Damien Tournoud commentedThe exact thing that chx describes in #25 happens with the search patch: we are building a big db_select() query that uses the result of another big db_select() query.
Comment #29
Damien Tournoud CreditAttribution: Damien Tournoud commentedSorry.
Comment #31
Damien Tournoud CreditAttribution: Damien Tournoud commentedMore precisely, what happens is that the new search DBTNG patch is *sharing* condition objects between queries. This used to work flawlessly when the placeholders where guaranteed to be different during the lifetime of the request, but that cannot work anymore. I'm not convinced the main issue is in the placeholder reset counter.
Comment #32
Crell CreditAttribution: Crell commentedHm. Yuck. We do want to support multiple active query objects at once, so that's an issue. Unfortunately, I can't think of another approach that would also support Moshe's use case. I agree that search module is higher priority. Fortunately, I wouldn't describe this as an API change so we don't need to have a solution this week. We do need a solution, though.
I'm OK with rolling back if it makes search.module work.
Comment #33
chx CreditAttribution: chx commentedFailed to run tests? WTH? But then the very same patch just worked inside the DBTNG patch. And, this is a straight roll back of this patch which is the last patch committed to core...
Comment #34
Dries CreditAttribution: Dries commentedCommitted. Thanks!
Comment #35
catchMarking back to active now HEAD is back to working in case there's another way to do this.
Comment #36
chx CreditAttribution: chx commentedComment #38
chx CreditAttribution: chx commentedComment #39
chx CreditAttribution: chx commentedwe worked on this with Crell.
Comment #40
chx CreditAttribution: chx commentedThat included search dbtng which is a great test case btw. :)
Comment #42
chx CreditAttribution: chx commentedSeemingly this not a trivial issue.
Edit: I ran the problematic DBTNG search tests and they pass too.
Comment #43
Crell CreditAttribution: Crell commented$query in the compile() method should be called something else, since it's not always a query.
$query should not allow NULL to be passed into the compile() method, as we're not handling NULL and that would break anyway.
There's no need to re-specify the $placeholder variable in InsertQuery, SelectQuery, etc. All of the placeholder behavior can be handled in the Query base class. Also, $placeholder should probably be called $nextPlaceholder. (It will be needed in SelectQueryExtender, though, since that doesn't extend Query.)
I think that covers it. Just nitpicky stuff. It's actually overall simpler than the size of the patch makes it look. We're just providing better context to parts of the query build system through passing in context rather than just assuming it. That's generally a good thing anyway.
Comment #44
chx CreditAttribution: chx commentedRenamed to queryPlaceholder everywhere, i can't help condition compile getting a NULL as most other compiles (like InsertQuery compile) wont get a query , renamed the variable too.
Comment #45
Crell CreditAttribution: Crell commentedThat should do it. Again, it's a much simpler patch than it looks at first glance. :-)
Comment #46
Dries CreditAttribution: Dries commentedCommitted to CVS HEAD. Thanks.
Comment #47
Damien Tournoud CreditAttribution: Damien Tournoud commentedOk, this broke SQLite and PostgreSQL.
This fixes it.
Comment #48
webchickThanks, committed to HEAD. :)
Comment #49
hass CreditAttribution: hass commented#564852: Subselect with placeholders causes invalid/duplicate placeholders may be a regression by the placeholder changes made here. The result in the specific case is a data loss!
Comment #51
hass CreditAttribution: hass commentedComment #52
Crell CreditAttribution: Crell commentedNo, this is closed. The other issue is a bug that is still open.