The Database layer got its own autoloader. This is good.
The Database layer relies on class being not defined in its normal operation (ex. MySQL doesn't need to define SelectQuery_mysql, when this class is not found the Database layer revert to SelectQuery). This is also good.
Drupal has a Database-based autoloader (the registry), registered after the Database layer autoloader. This is also good.
But when combining the three, there are many cases where the DB autoloader fails, legitimately. As a consequence, the registry autoloader is called, and it will fails because if the database is not in a sane state. #fail for the DB autoloader, which was precisely supposed to prevent that.
We need to make the registry autoloader aware of the DB-related classes. It should not try to autoload those and simply bail-out.
Comment | File | Size | Author |
---|---|---|---|
#31 | 851136-db_not_autoload.patch | 20.99 KB | chx |
#22 | 851136-db_autoload.patch | 12.65 KB | Crell |
#21 | remove_db_autoloader.patch | 11.93 KB | chx |
#19 | remove_db_autoloader.patch | 11.34 KB | chx |
#17 | 851136-database-autoloading-classes.patch | 2.7 KB | Crell |
Comments
Comment #1
Crell CreditAttribution: Crell commentedTrue enough. Probably the easiest approach is to simply have the registry rebuild process skip includes/database entirely, and then copy the list of potential classes from the DB autoloader and have the registry autoloader just return if it sees one of those.
Comment #2
moshe weitzman CreditAttribution: moshe weitzman commentedAny chance one of you can write the patch for this? Its a blocker for mssql driver.
Comment #3
Damien Tournoud CreditAttribution: Damien Tournoud commentedHere is a patch implementing #1.
Comment #4
chx CreditAttribution: chx commentedThis code hurts my eyes. Can we put all statics on top of the file, separate classes from interfaces and have something like
Comment #5
Crell CreditAttribution: Crell commentedActually we probably shouldn't be separating interfaces and classes at all for the autoloader; that's my fault, really. Separate issue, though.
I don't quite get what the strtok() is for here, though.
Comment #6
Damien Tournoud CreditAttribution: Damien Tournoud commentedMy mistake for trying to add an unrelated comment.
This is the smallest possible patch (TM).
The strtok() is there to only take into consideration the base name, by stripping the engine-specific part.
Comment #7
Crell CreditAttribution: Crell commented($type == 'class' || $type == 'interface')
This is a tautology. There is nothing else that can be autoloaded so it will always be true.
Otherwise this looks OK to me.
Comment #8
Damien Tournoud CreditAttribution: Damien Tournoud commentedWell, except that this function as fluid parameters, so $type can also be REGISTRY_RESET_LOOKUP_CACHE or REGISTRY_WRITE_LOOKUP_CACHE.
Comment #9
Crell CreditAttribution: Crell commentedYou know, we really need to stop using that pattern. A parameter has meaning. We shouldn't have to guess that meaning from another parameter. That's fundamentally bad design.
But that's to be fixed another time. Let's do.
Comment #10
andypostAny reason to duplicate 'QueryAlterableInterface'
Comment #11
Crell CreditAttribution: Crell commentedNope, none whatsoever.
Comment #12
sunCan we write those like all the other array elements on separate lines, please?
This looks like a simple strpos() === 0 to me; doesn't require a slow preg_match(). Also, can we add a short inline comment here?
Powered by Dreditor.
Comment #13
Damien Tournoud CreditAttribution: Damien Tournoud commentedRerolled for #12.
Comment #14
Damien Tournoud CreditAttribution: Damien Tournoud commentedRaising to major, no decent third party database driver can be implemented without this.
Comment #15
Crell CreditAttribution: Crell commentedLucky #13...
Comment #16
chx CreditAttribution: chx commentedWe just spent like thirty minutes trying to fill me in why is this patch necessary and why no other way would suffice.
Here is an an example which is sorely missing from the whole issue: let's try to run a db_delete very early. DeleteQuery_driver is queried for. _registry_check_code runs a cache_get. That, in core, runs ... a db_delete in the garbageCollection called from the get method. Opsie! Another issue (this is what Damien got) the database layer tries to load DatabaseTransaction_sqlsrv, which causes _registry_check_code() to be called early during the installation, even before the {registry} table has been created.
What the patch misses are comments. Nowhere does the patch say why it needs to bug out early. Nor does it have a @TODO for creating a proper fix in Drupal 8 where the autoloaders communicate with each other about which classes they deal with (that's something that PHP should do but I digress).
Comment #17
Crell CreditAttribution: Crell commentedNo changes from #13 except comments. I did not add a @todo because I do not understand how autoloaders could even communicate with each other in anything resembling a performant way without support from PHP itself. Let's not hold up a major patch on that discussion.
Comment #18
webchickLarry and chx and I talked about this issue.
This seems like a really weird pattern to introduce, because it requires us to change things in two places (and we'll likely forget about this second place) when we extend the db system further. Also, one of the goals in D8 is to make DBTNG its own standalone library. This feels akin to us itemizing every single function in jQuery or something like that. Larry acknowledged that although autoloading was originally intended to reduce the amount of code loaded, that in practice we load the whole thing anyway, so this doesn't buy us much. (This would probably make Rasmus happy, too - Crell ;)).
chx pointed out that there are only single points of entry for DBTNG, so doing require_once in a central place should solve the problem.
Comment #19
chx CreditAttribution: chx commentedI am only 98% happy with this because of the require_once statements on top of select.inc and schema.inc but can't help those really. In fact if you remove the one from schema.inc Drupal won't install (but the tests will still pass). Other from that, I think this is nice. The actual line of code change is 34 LoC removed.
Comment #21
chx CreditAttribution: chx commentedOK I have no idea, whatsoever, why we added db_autoload in drupal_web_test_case.php DrupalUnitTestCase... but as it does not exist any more, surely it's not needed now :D that's the cause of all these failures.
Comment #22
Crell CreditAttribution: Crell commenteddb_load_file(): We don't need another function. Really. I moved this into the Database class. That's still ugly but at least it's less ugly.
The $use_registry flag is ugly, hideous, horrific, and I cannot think of an alternative at the moment. :-( At least we can generalize it a little bit and say "use autoload", since we could theoretically be using a non-registry autoload system. I have made that change.
The OS and PHP are smart enough to handle require_once() and file_exists() caching smarter than we can. It's 2010. OSes are smart these days. :-)
Also corrected a documentation typo.
Otherwise, I am OK with this approach.
Comment #23
chx CreditAttribution: chx commentedWhether you call it db_load_file or Database::loadDriverFile I do not care this close to RC.
Comment #24
Dries CreditAttribution: Dries commentedCommitted to CVS HEAD. Thanks.
Comment #25
David_Rothstein CreditAttribution: David_Rothstein commentedThis patch seems to have broken SQLite installations. Dries discovered this while testing out another patch (and then I reproduced it on fresh HEAD also):
http://drupal.org/node/346494#comment-3760516
The error occurs after you submit the database configuration form:
Comment #26
carlos8f CreditAttribution: carlos8f commentedsubscribing
Comment #27
dcrocks CreditAttribution: dcrocks commentedUsing 7-x dev dated 11/27 timestamp 17:11 generates a wsod when sqlite chosen. No error messages. The database contains one entry under Master Table: sqlite_master, which is empty. Using OS X 10.6.5. Found following system error log:
PHP Fatal error: Class 'SelectQuery' not found in /Users/rocks/Sites/dru1127/includes/database/sqlite/query.inc on line 17, referer: http://localhost/~rocks/dru1127/install.php?profile=standard&locale=en
I am not sure about this, but is the file 'select.inc' actually available to the line:
$class = $this->getDriverClass('SelectQuery', array('query.inc', 'select.inc'));
Is the current path 'includes/database' or 'includes/database/sqlite'?
Comment #28
dcrocks CreditAttribution: dcrocks commentedDoes postgresql also fail? I can't test.
Comment #29
webchickRolled back #22 until we figure this out. Downgrading from critical.
Comment #30
webchickAnd let's make sure this doesn't get bumped back to RTBC until it's tested on sqlite and pgsql, hmmmm? ;)
Comment #31
chx CreditAttribution: chx commentedThe proper solution is to move SelectQuery_* to select.inc. I always had some grumbles about the discoverability of DBTNG and this really did not help. Now is the last chance to move those classes to their respective files. And if the sqlite select.inc is small so what? install.inc is not big either and yet noone wanted to enroll it into anything else.
Comment #32
Eric_A CreditAttribution: Eric_A commentedWhile #22 broke stuff, there is a trivial -and pretty fundamental- patch in the NR queue that would not work without it: #978144: cache_get_multiple() inconsistent with cache_get() would end in Fatal error: Class 'SelectQuery_mysql' not found in C:\xampplite\htdocs\drupal\includes\database\database.inc on line 685.
Comment #33
dcrocks CreditAttribution: dcrocks commentedInstalled 7-x dev dated 11/28 with timestamp 12:08 on OS X 10.6.5 and using SQLITE with no errors. Applied patch from #31 to fresh copy of same 7-x dev. Again no errors. Enabled some modules, added a user, switched themes, but that's all. Can't test postgres and really can't check what the patch is supposed to accomplish but it no longer WSOD's when installing new drupal with SQLITE.
Comment #34
Crell CreditAttribution: Crell commentedWell bah and double bah.
For those playing at home, the issue here is that the loadDriverFile() method loads the common version of whatever file is being requested, followed by the driver-specific version if it exists. It does that for each file in turn. That results in the following order for SQLite/Postgres:
query.inc
sqlite/query.inc
select.inc
sqlite/select.inc
Unfortunately sqlite/query.inc contains SelectQuery_sqlite, which extends SelectQuery, which is in select.inc, which is not loaded yet. Fail. There is no SelectQuery_mysql class, so MySQL never had this problem.
The reason we're putting driver select classes into query.inc is for IO performance. Hitting disk again for a very small file is extra cost we don't need, especially when it turns out in practice that the code there will always be needed. Of course... we have no hard data on what the cost of that extra disk hit is on a modern OS. At least I don't.
So that leaves us with 2 options:
1) Force driver select classes to be in select.inc, and thereby eliminate the catch-22 above. That's what chx's patch in #31 does.
2) Reverse the loop in loadDriverFile() so that we instead load query.inc, select.inc, driver/query.inc, driver/select.inc.
Lacking any hard data as above at the moment, I am OK with #31 at this point. I don't like it, but I don't have more than an intuition that it would be a performance hit. If someone wants to implement #2 I'm OK with that too, but we already have a patch in #31 that resolves the issue (per comments above).
Comment #35
chx CreditAttribution: chx commentedNote that this "loading small files I/O hit" has been proved to be false about the time we added phptemplate to core.
Comment #36
Crell CreditAttribution: Crell commentedCorrection. As chx just pointed out to me in IRC, option 2 would still fail when trying to load, say, InsertQuery_sqlite for the same reason. So that leaves #31 as the only viable option. Per #33 this now works.
Comment #37
chx CreditAttribution: chx commentedAsked to clarify #35, these small files (exactly because they are small) will sit on the OS filesystem cache practically forever making the I/O hit a memory hit. That's pretty close to be not measurable.
Comment #38
webchickAnd can I get confirmation that someone tested this on SQLite and postgreSQL?
Comment #39
chx CreditAttribution: chx commentedI have installed postgresql using the instructions in https://help.ubuntu.com/community/Drupal (worked perfectly, one 8.1 needed to be changed to 8.4) applied the patch above and installed Drupal. That worked.
Comment #40
chx CreditAttribution: chx commented#33 tested on SQLite but I tested too when posted the patch.
Comment #41
webchickAnd for extra good measure, I tested on pgsql 9.0.1 on OS X too.
I'm now finally satisfied that this doesn't break anything. :) Committed to HEAD. Thanks!
Comment #42
Eric_A CreditAttribution: Eric_A commentedThe new files for sqllite and pgsql from #31 are not committed...
Comment #43
Crell CreditAttribution: Crell commentedComment #44
webchickOh son of a monkey.
This will teach me to say stuff like "I'm now finally satisfied that this doesn't break anything. :)" Hahahaha!
Committed to HEAD.
Comment #45
Eric_A CreditAttribution: Eric_A commentedAhum... I should have said "not added"? ;-)
Comment #46
webchick*&@#!
I fail at life today.
OK, they're definitely there now. :)