Problem/Motivation
The Connection::queryTemporary()
was removed in D10 (https://www.drupal.org/node/3211781) and no replacement functionality was added. This is an issue since certain contrib modules depends on creating the temporary tables, such as search_api (https://www.drupal.org/project/search_api/issues/3262092#comment-14691730).
Proposed resolution
We should bring back support for temporary tables for the by core supported database drivers.
Remaining tasks
User interface changes
API changes
The new interface Drupal\Core\Database\SupportsTemporaryTablesInterface
has been added. The new interface has only one method named queryTemporary()
. All three by Core supported databases (MySQL, MariaDB, PostgreSQL and SQLite) have the interface added to their implementation of Drupal\Core\Database\Connection
. Contrib database drivers are encouraged to do the same.
Data model changes
Release notes snippet
Connection::queryTemporary() which was previously removed from Drupal 10 has been reinstated. The new interface Drupal\Core\Database\SupportsTemporaryTablesInterface
has been added.
Comment | File | Size | Author |
---|---|---|---|
#51 | interdiff_9.4.x_48-51.txt | 926 bytes | pradhumanjain2311 |
#51 | 9.4.x-3312641-51.patch | 5.71 KB | pradhumanjain2311 |
#51 | interdiff_9.5.x_48-51.txt | 926 bytes | pradhumanjain2311 |
#51 | 9.5.x-3312641-51.patch | 5.71 KB | pradhumanjain2311 |
#48 | 9.4.x-3312641-47.patch | 6.55 KB | nkoporec |
Issue fork drupal-3312641
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
nkoporecinitial attempt
Comment #3
daffie CreditAttribution: daffie at Finalist commentedI get why your contrib module needs the temporary table functionality back. What you are now doing in the patch is making it a driver specific functionality. Only that is something that first to me needs framework managers approval. As do you want to have database driver specific functionality? Personally I do not like it very much.
Another solution is to create a contrib database driver for SQLite that extends the core one. That will be very easy to do, after #3256642: Introduce database driver extensions and autoload database drivers' dependencies has landed. That issue has been RTBC for 8 months and hopefully will go into D10.1. Only that will not fix the situation for D10.0. Maybe we could convince the core committers to have it land in D10.0.
Changing the priority to major as it is a contrib blocker.
Comment #4
andypostThere's related issue, so something should care about cleaning of this temporary tables
Comment #5
nkoporecYea I understand that driver specific functionality is not the perfect solutions here, but according to this drupal slack thread, https://drupal.slack.com/archives/C014CT1CN1M/p1663104242533149?thread_t... , it seems like they only want to bring back this feature to sqlite only, so that is why I made it sqlite specific.
I do think that making a contrib sqlite driver is not a bad idea though, then any modules that requires temporary tables would simply depend on it, but lets see what others think of it.
Comment #6
mglamanI think the idea of a contrib sqlite driver is bad. What would its namespace be? And does it automatically replace the core sqlite driver? Or do tests now need to be more specific.
Comment #7
mglamanHere's a comment from the CR:
It seems useful regardless. I don’t understand how this blocked removing table prefixes. Search API with its database backend is broken without this in drivers.
Comment #8
daffie CreditAttribution: daffie at Finalist commentedOk, lets do this. It is clear to me that we need this.
Can we give this method a different name then the just removed method. Something like:
sqliteCreateTemporaryTable
.Can we remove this method and move the code into the main function.
As I now the subsystem maintainer for the Database API and I am the one who has added the tag, I can remove the tag.
Comment #9
mglamanWhy is this only on SQLite? We need it for the other drivers as well.
Honestly this is nearly feasible without adding the method back to the drivers, if we could tell a statement to not process prefixes in
$options
.I have the following sample code I am working on:
It is fine, expect for SQLite because I can't opt-out of the table being prefixed by statements.
Comment #10
mglamanHere is the final code I provided in #3313708: Provide replacement for queryTemporary.
SQLite always stores temp tables in the
temp
table. So it has nothing to do with Drupal's prefixing system. It just needs to be provided a fully qualified table name for temp tables, because it's in thetemp
database, but happens to pass through if not fully-qualified.Comment #11
catchI think the 'for sqlite driver' is a mis-reading of a previous conversation, either way I don't think that's what's needed here - we'd just need to bring back the methods.
The original reason we deprecated the method was for two reasons:
1. No core usage and not much contrib usage (but this issue has demonstrated the contrib usage is significant enough to bring it back.
2. The sqlite implementation was doing weird things with db prefixes which were were also trying to remove.
This is what tied the original issue up with prefixes:
It looks like @mglaman's code achieves the same thing without using the prefixes system, so in that case I think we could essentially revert #3211780: Deprecate Connection::queryTemporary() but with the different sqlite implementation to ensure the table is fully qualified.
Comment #12
mglaman@catch so roll an MR that is a revert of the removal, but modified SQLite code to always fully-qualify to
temp.
? I can do that!edit: confirmed in Drupal Slack.
Comment #13
mondrakeIt was me removing this feature, and besides #11 my thinking was that the sheer concept of temp tables sounds strange in 2020 - we have other ways to store data temporarily and memory availability is orders of magnitude bigger now than when temp tables were invented to persist temp data on disk.
So for me (I admit I haven’t got in the details, please consider this), contrib should first think if the design using temp tables is still actual, or could be revisted. Can subselects be used instead? Or results be cached in arrays and accesssed in memory?
Comment #14
mglamanSearch API tries to fallback on a subselect when doing work for facets. The autocomplete does not - not sure why.
I was going researching and it seems like a useful technique. Especially if it's not viable as a View, which we don't even support.
This is especially useful when the dataset is too large for in-memory storage and better CPU usage. The DBMS is more performant for a lot of these things than writing it in PHP.
If temporary tables are supported by the engines, why remove support? Especially if the only reasoning was the SQLite workaround for prefixes.
Comment #15
nkoporecHere is a new patch that reverts the #3211780: Deprecate Connection::queryTemporary() and implements the sqlite solution from #10.
Comment #16
nkoporecoops, wrong patch above.
Comment #17
nkoporecUpdated patch, that fixes the phpstan issues
Comment #18
mondrakeJust thinking aloud: core is likely not going to need this, right? So bringing it back adds maintenance burden and no focus since it's unused i core. And contrib may get later frustation if they need changes. Also, forces contrib db drivers to comply, and I for sure can say that Oracle is not so flexible here as currently core supported dbs, https://oracle-base.com/articles/misc/temporary-tables.
Core has a pluggable option here: backend overrideable services. Maybe contrib can implement such in their module and then manage their own stuff - more flexibility to contrib modules consuming temp tables, no core maintenance burden, no forcing of contrib drivers to comply to this API.
Just to be clear: I am not pushing back here, trying to find better solutions than just a revert.
Comment #19
daffie CreditAttribution: daffie at Finalist commentedIt is clear to me that we need some solution for search_api module. Only I would rather not have to support the temporary tables functionality in core. I get a very hacky feeling about those "temporary tables".
I do very much like the solution of @mondrake to put the functionality in a contrib service module. I am willing to create and maintain the module.
@mglaman: Is this solution for you acceptable?
Comment #20
drunken monkeyJust quickly: Temporary tables are a big boon in case you have some DB result set that you want to run multiple further queries on. Instead of using a (very slow) nested
SELECT
each time, you save the result into a temporary table once and then get much better performance for your further queries.As such, just using a nested
SELECT
might be a valid replacement for a temporary table in general, but for larger sites it could easily mean the difference between usable and much too slow – or even between slow and crashing. So, I’d much rather have temporary table support in place.Also, since this has been supported in Core for ages, I generally don’t agree with its removal and think it should stay supported in Core, especially if it’s not that hard. That being said, though, if you decide against keeping support for it in Core, I’m not sure whether I’d use a separate contrib module for adding support back and not just add the code for it directly to Search API. It really doesn’t seem complicated enough to warrant its own module, even if we might end up with multiple contrib modules with the same code adding back temporary tables support. (Furthermore, this would only be needed for some functionality, so a dependency would add a completely unused module for some sites.)
(I of course cannot speak for other contrib developers who might want/need this functionality.)
Comment #21
mglamanCopying my response from the same question in Slack:
My reply to #19
Six months ago on the CR someone said they use it for production operations due to large datasets.
Why is it hacky? It's something supported by the systems and not going away. If you search the internet there are various articles on how it's used.
This doesn't feel like an answer. "Hey, we'll make you install yet another contributed module for 15 lines of code!"
Comment #22
mglamanReviewing #17
Let's skip the property and just track it statically within the method.
Comment #23
japerryMoving the component to the database system not the sqlite driver. As said by others, losing temporary query functionality is a problem for ALL databases. And per comment #20, without this core functionality, DB search in Search API, used by nearly 25% of D8+ users, would be less performant in Drupal 10. Clearly, based off this issue, and unrelated comment in the CR, this has much wider-reaching consequences than the maintainers expected.
Ignorance of how sqlite works with temporary tables was not a good reason to remove unrelated code to the prefix issue. Further review of #3211780: Deprecate Connection::queryTemporary() should have occurred and had this come up during that time, I don't think this would have been removed.
As per the 'not used in core' argument -- there was a discussion some time ago about bringing Search API into core at one point, and the fact that core doesn't use these methods is a technicality, not a reality. With Dries (and other core maintainers) making the point that core will have fewer modules and be composable, it is not a good argument to remove key functionality simply because it is not used by core. Its use by one of the top contrib modules demonstrates a need to have strong core APIs to empower contrib.
Comment #24
nkoporecUpdated the patch per #22 review.
Comment #25
daffie CreditAttribution: daffie at Finalist commentedCan we use the function
uniqid()
instead of using a static variable. If we do this than we can also remove this helper method.Can we move this method to an interface and have the 3 database drivers implement the interface. The abstract function forces all contrib database driver to implement this method.
Comment #26
mglamanThanks, @daffie! We'll get a new patch rolled on Monday.
Using
uniqid()
makes sense, so each driver will have this code and then others can do whatever they want, or copy and paste what is in the core drivers.Makes sense! So a
\Drupal\Core\Database\SupportsTemporaryTablesInterface
withqueryTemporary
Comment #27
daffie CreditAttribution: daffie at Finalist commentedCan we move this test to Drupal\KernelTests\Core\Database and change it into a kernel test. Can we also add:
In #3231950: Split Database tests in 'core' ones and 'driver specific' ones we will change the new test.
Comment #28
nkoporecUpdated the patch per #25 and #27 review.
Comment #29
daffie CreditAttribution: daffie at Finalist commentedThe patch looks good!
This part can be removed. The same for the changes to the database_test module.
Could we add testing that the 2 tables are temporary tables and that they have the same columns as the tables from which they are created.
Comment #30
Ratan Priya CreditAttribution: Ratan Priya at OpenSense Labs for DrupalFit commented@daffie,
Addressed point 1 for comment #29.
Needs review.
Comment #31
Anchal_gupta CreditAttribution: Anchal_gupta at Srijan | A Material+ Company for Drupal India Association commentedfixed cs error. please review it
Comment #32
daffie CreditAttribution: daffie at Finalist commentedThe changes to these files from the database_tests module can be removed.
As #3231950: Split Database tests in 'core' ones and 'driver specific' ones has just landed, we need to make some changes. The test should now extend
Drupal\KernelTests\Core\Database\DriverSpecificDatabaseTestBase
and the class will need to become an abstract class. For each of the 3 database driver modules, a test should be added that extends TemporaryQueryTest.Comment #33
nkoporecUpdated the patch per #32 suggestions.
Comment #34
daffie CreditAttribution: daffie at Finalist commentedAll this code can be moved to the class core/tests/Drupal/KernelTests/Core/Database/TemporaryQueryTestBase
Comment #35
nkoporecComment #37
daffie CreditAttribution: daffie at Finalist commentedThe patch looks great, only my point #29.2 still needs to be addressed.
Comment #38
nkoporechmm, we did have a test for that, but you suggested to remove it.
in TemporaryTestBase.php
and then in database_test module:
I'm not sure if there is a better way to test if the table is temporary ? As we must run it a separate request in order to check if the table was dropped at the end of the request and afaik there is no driver agnostic way to check if the table is indeed temporary (except the one above).
Comment #39
mglamanI think we could test it by using
\Drupal\Core\Database\Database::closeConnection
, correct? Temporary tables are cleaned up after a connection is closed. So a kernel test could close the connection and then re-open it to verify the table has been removed.Comment #40
daffie CreditAttribution: daffie at Finalist commentedComment #41
nkoporecUpdated the patch, added tests for same fields and temporary tables check
Comment #42
nkoporecAdded the missing words.
Comment #44
nkoporecchanging back to needs review as the failed test is not related to this.
Comment #45
daffie CreditAttribution: daffie at Finalist commentedAll code changes look good to me.
Tests are added.
I have updated the IS and created a CR.
For me it is RTBC.
The change is for D10.0 as it is contrib module blocker.
Comment #47
catchCommitted/pushed to 10.1.x and cherry-picked to 10.0.x, thanks!
I've updated https://www.drupal.org/node/3211781 to indicate that it's outdated and added a release note here.
I think we need 9.5 and 9.4 patches here to remove the deprecation from queryTemporary().
Comment #48
nkoporecPatches for removal of deprecation for 9.5.x and 9.4.x
Comment #50
daffie CreditAttribution: daffie at Finalist commentedThe method
generateTomporaryTableName()
will stay deprecated. Therefor this line needs to stay.Comment #51
pradhumanjain2311 CreditAttribution: pradhumanjain2311 at OpenSense Labs for DrupalFit commentedAddressed Comment #50.
Comment #53
daffie CreditAttribution: daffie at Finalist commentedLooks good to me.
Comment #55
catchCommitted/pushed to 9.5.x and 9.4.x, thanks!