Problem/Motivation
The minimum MySQL version for Drupal 9 is 5.7.8, but this requirement isn't checked when updating a site to Drupal 9.
Steps to reproduce
- Given an existing Drupal 8.8.5 site running on MySQL 5.6
- Update Drupal to 9.x (including update code and database updates)
Expected result
Some sort of warning. I'm not sure of the exact expected results, but I would expect Drupal to warn me at some point that the minimum requirements aren't met.
Actual result
Drupal updated and runs without warning me at all that the minimum requirements aren't met.
Proposed resolution
For 9.0.0, the site seems to run just fine on MySQL 5.6. Presumably this is because no new 5.7-specific features have been added yet. For now, perhaps a warning on the status report page would suffice. This would allow people to update and not WSOD, but would make it clear that the minimum is not met and that it (likely) won't work on 9.1.
Remaining tasks
User interface changes
Adds warnings on both the status report and on update.php that the minimum MySQL version is not satisfied.
Before
Status report
update.php
After
Status report
update.php
API changes
None.
Data model changes
None.
Release notes snippet
The default database is now checked when Drupal is updated to ensure it complies with the minimum version supported by the database driver. (Previously, this only happened during installation.) Updates will not proceed if the requirement is not met.
If you see an error about your database version when running update.php
, ensure your database meets the minimum database requirements or install one of the legacy database drivers provided as contributed projects.
Comment | File | Size | Author |
---|---|---|---|
#83 | 3135629-83.8_9_x-hotfix.patch | 635 bytes | dww |
#83 | 3135629-83.8_9_x-hotfix-only-run-the-broken-test.patch | 2.2 KB | dww |
#76 | 3135629-8.9.x-76.patch | 7.17 KB | alexpott |
#76 | 65-76-interdiff.txt | 3.67 KB | alexpott |
#76 | 3135629-8.9.x-76.test-only.patch | 4.97 KB | alexpott |
Comments
Comment #2
xjmThanks @balsama!
Comment #3
xjmIt's probable that the minimum database version is only checked at install time (I'm guessing we'd see the same things for Maria, Postgres, and SQLite versions below the minimum).
The main risk of using an unsupported DB version is that we introduce some syntax that's not supported on the previous versions of the drivers, leading to fatal errors and/or data integrity problems.
I wouldn't want to hardcode any kind of
system_requirements()
check for MySQL 5.6 specifically, but it seems like a generic check for the DB driver for the status and update reports would be good.Comment #4
alexpottThis makes sense. We need to do what we do in the installer - ie.
$errors = db_installer_object($driver, $database['namespace'] ?? NULL)->runTasks();
on the driver configured in settings.php during update requirements checking. This is a bug that’s existed for a long time. We avoided in Drupal 7 to Drupal 8 updates.Comment #5
daffie CreditAttribution: daffie commentedIs this the fix we want for this issue?
Comment #6
daffie CreditAttribution: daffie commentedForgot to add
use Drupal\Core\Database\Database;
.Comment #8
catchI think #6 is good if we can get it committed prior to the release candidate.
The other option is to only do this check in system_requirements() and show a warning (we should add that anyway, for example for situations like reloading a database dump between different environments).
Comment #9
alexpottI think this should go in system_requirements(). There are decent arguments that these checks should run during the regular runtime check too. But to start with in this issue they should run only during the update phase.
One argument for a runtime check would be that it's possible to change a database to an earlier version of mysql and not run update.php. However this discussion is for a follow-up issue as calling runTasks() does a number of things including checking the engine version.
Thinking a bit more. Maybe we need to add a public function to \Drupal\Core\Database\Install\Tasks that only calls the protected checkEngineVersion function. Since the other tests are a bit unnecessary here. And then we could do the runtime and update time checks in one issue.
Comment #10
alexpottHere's an implementation of #9.
No interdiff because it's a different approach.
Also here are screenshots after manually testing by hacking a very high minimum version into the mysql driver.
Update requirements
System status report
Comment #11
alexpottThis is final because we want database drivers to extend checkEngineVersion to do engine version checking and not this method. Like \Drupal\Core\Database\Driver\mysql\Install\Tasks::checkEngineVersion() does already.
Comment #12
alexpottPretty sure that manual testing is the way forward here. We could add a db driver to core that wraps one of the core drivers and fakes the minimum version but that seems quite fraught and fragile.
Comment #13
catchWhy not
to save the nested if?
Comment #14
alexpottRe #13 because we only want to add the description & requirement error if there are errors.
Comment #15
alexpottAnother way to achieve the bullets when multiple but none when only one item would be:
Which is slightly mis-using the comma-list style but has an advantage of always having an unordered list here so there's less markup difference between the two situations. Using the comma list for more that one error doesn't make sense because each error returned from \Drupal\Core\Database\Install\Tasks::checkEngineVersion() is a full sentence.
Comment #16
alexpottHere's an implementation of #15 with a comment to explain.
Comment #18
atul4drupal CreditAttribution: atul4drupal at Srijan | A Material+ Company commentedRegarding patch at #10
excerpt from system.install:
+ $errors = $tasks->engineVersionRequirementsCheck();
+ $error_count = count($errors);
+ if ($error_count > 0) {
+ if ($error_count > 1) {
+ // If there are multiple errors use an item list.
+ $error_message = [
+ '#type' => 'inline_template',
+ '#template' => '{{ errors }}',
+ '#context' => [
+ 'errors' => [
+ '#theme' => 'item_list',
+ '#items' => $errors,
+ ],
+ ],
+ ];
+ }
+ else {
+ $error_message = reset($errors);
+ }
+ $requirements['database_system_version']['severity'] = REQUIREMENT_ERROR;
+ $requirements['database_system_version']['description'] = $error_message;
+ }
I think we must initialize the $error_message array right after first if statement
if ($error_count > 0) {
$error_message = reset($errors);
if ($error_count > 1) {
// your piece of code
and do away with the else statement
Comment #19
alexpott@atul4drupal I'm confused by #18. Here's what #16 is doing.
Yes with #18 we could avoid an
else
in the patch attached to #10 but that doesn't really solve #13. And it results in an unnecessary assignment when dealing with multiple errors.Comment #20
atul4drupal CreditAttribution: atul4drupal at Srijan | A Material+ Company commented@alexpott apologies for the confusion. actually I mistakenly have #10 and #16 patch mixed up
agreed with #19,patch at #16 does a clean job.
Comment #21
balsama+1 for #16. I reran the update with a restored db and the patch from #16. This time:
[error] Array (Currently using Database system version 5.6.32-78.1-log)<br />// Requirements check reports errors. Do you wish to continue?: (yes/no) [yes].
Comment #22
dwwTiny nit (maybe) that could be fixed on commit:
=== ?
Otherwise, tested this manually on a test server I have access to that's still on mysql 5.5. Everything works great. Adding before/after screenshots to the summary.
Comment #23
xjmQueued tests on other databases/drivers to be safe.
Comment #24
xjmThis is disruptive. Mostly in a good way, but we need to mention the change in the release notes for people who had a beta installed, and it needs a CR I think.
Comment #25
xjmCould we mock this to provide test coverage? Not sure if the
final
would work or not. I'd be okay with a followup given how bad this issue could be for the D9 upgrade path and the imminence of RC.Comment #26
alexpottAdded a CR and a release note.
Comment #27
dwwCR looks great, thanks. Added a few commas, but otherwise it was ready. Removing tags.
Comment #28
alexpottWorking on a test borrowing from @daffie's work in another issue.
Comment #29
dwwAdding the commas to the release note, and adding the sentence about the status report.
Comment #30
alexpottHere's a test. The test-only patch is the interdiff.
Comment #31
alexpott@dww I left the sentence about the status report out of the release note because it didn't really seem so important. It feels like the important change here is that you can no longer update if the database defined in settings.php does not meant the minimum requirements. The status report change is for consistency and especially to help contrib drivers if they change their minimum version because such changes are unlikely to involved db updates. Unlike a core database driver minimum version change, which we try to only do during a major version update.
Comment #32
alexpottNote: we need this same test driver for testing #3120731: Incorrect "Drupal already installed" if any database settings are wrong or unsatisfactory so adding & reusing here seems nice. The test driver is all @daffie's work.
Comment #33
xjmThat test driver is great!
Comment #34
xjmI did a little work on the release note and CR to make it clear that the warning previously only happened at install time and to give them an actionable step to take if it happens. I agree that we don't need to mention the status report in the release note.
Comment #35
xjmComment #36
xjmNit: What kind of array?
Is the idea to eventually provide test versions of each driver? (That's not really in scope here, just wondering.)
This says "to one" twice.
Comment #37
xjm#22 was also not addressed. No fix-on-commits please.
Comment #38
xjmComment #41
xjmComment #42
dwwFixes #22, #36.1, #36.3, and adds more missing phpdocs for more of the methods in
core/lib/Drupal/Core/Database/Install/Tasks.php
. I didn't look into fixing the test failure, so uploading this as a do-not-test.Comment #43
xjmI still don't get it. :-) It's still says "to" twice.
None of this is in scope; can you open a separate issue with these changes?
Ditto these.
Comment #44
dww#43.1: Trying again. /shrug
2-3: Crap, I totally misread the git status. I thought this was a new file, and saw a bunch of missing docs. My bad. Reverted all that crap.
Interdiff relative to #29, which is all that matters.
Meanwhile, I couldn't get this test to fail locally, either with phpunit nor run-tests.sh. Not sure WTF is going on with the bot failures. I'll let the bot churn on this one again, in case it was a random fail or something?
Comment #45
dwwAnother attempt to be even more clear about #43.1.
Comment #46
dwwFollow-up at #3136388: Fix phpdocs in core/lib/Drupal/Core/Database/Install/Tasks.php
Comment #48
dwwYeah, more WTF. The test passes locally for me, both phpunit and run-tests.sh. I suspected relative path weirdness in the 'autoload' directive in the test, but I confirmed that isn't the problem in this case. At least not locally.
Here's an initial shot in the dark.
Comment #50
dwwDoes this tell us any more?
Comment #52
dwwOkay, so that is interesting. We only died on line 73. Therefore, the first two attempts to fetch update.php worked fine. It's only once we try to swap out the DB driver that the access check is broken. Does rebuilding the container at that point help us?
Comment #54
dwwOkay, clearly the
protected static $modules = ['system']
doesn't help, norrebuildContainer()
. However, maybe it's the call toUrl::fromRoute()
that's killing it? Since we already did that above, maybe we can avoid it?Comment #56
jungleStrange. Just added another test to test that the service can be found.
Comment #57
dwwRe: #56: Sadly, that's not going to tell us anything. As I wrote at #52, #50 shows that the service works fine until we swap out the DB driver.
Based on the above, it seems that once we swap the DB connection details to try to use a different driver, all of Drupal breaks since there's no working DB to use for any queries. However, I have no idea why this only happens on the bot, not when testing locally.
I highly doubt this last experiment will work, but here's some defensive programming in case the relative path for the autoload is what's actually breaking things on the bot. Again, this works locally on both phpunit and run-tests.sh. /shrug
I give up for the night. Hopefully when @alexpott wakes up, he can make sense of this mess. 🤞He seems to have a knack for understanding why things are different on the bot vs. local testing.
Comment #58
daffie CreditAttribution: daffie commentedThe variable \PDO::ATTR_SERVER_VERSION returns something like: "5.7.27-0ubuntu0.18.04.1-log" or "10.2.31-MariaDB-1:10.2.31+maria~bionic-log".
The variable \PDO::ATTR_CLIENT_VERSION returns something like: "mysqlnd 5.0.12-dev - 20150407 - $Id: 7cc7cc96e675f6d72e5cf0f267f48e167c2abb23 $".
They are not the same thing. Removed the method override for
clientVersion()
in the new test driver class. Lets see if that makes the testbot happy.Comment #59
alexpottLet's try this. The test passes and fails locally as expected. But then again so did #30.
Comment #61
alexpottLet's try writing the db settings in a different way.
Comment #62
alexpottYay - so this is a run tests thing. If I run #59 using run-tests.sh with no db connection is the test runner database and using the sqlite option for report results I get the same fail as DrupalCI.
So here's a proper patch with a fail test and all changes back to the last proper patch in #45
Comment #63
Rkumar CreditAttribution: Rkumar as a volunteer and at Srijan | A Material+ Company for Drupal India Association commentedOn my local test cases are getting passed. Testing patch for test case failure.
Apologies I didn't check the updated thread.
Comment #65
daffie CreditAttribution: daffie commentedAll points of @xjm are addressed with the exception of #36.2. For #36.2: We can fix it for all by core supported database drivers. This will result in 2 more test database drivers being added to the code base. For me seeing that it works for MySQL is enough.
The added test work for me locally and the testbot is also happy.
All the code changes look to me as good.
The patch is for me RTBC.
Reuploading the patch from comment #62, because that is the one that needs to be committed.
@alexpott: Great work!
Comment #66
alexpottHere's a slightly better way to change the database settings since it relies on the writeSettings test helper. Leaving at RTBC because the changes are test only and very minor.
Comment #71
catchCommitted/pushed to 9.1.x and cherry-picked back to 8.9.x
Comment #73
alexpottHad to revert this on 8.9.x because this was causing https://www.drupal.org/pift-ci-job/1686421 - it's because #3124354: Remove unnecessary classes from the database drivers did not make it back to 8.9.x
Comment #74
xjmI think we should backport it without tests then?
Comment #75
catchI think this is fine to backport without tests, the tests are the complex part of the patch..
Comment #76
alexpottHere's a patch for 8.9.x with a test. Considering this is update path stuff it feels worth it.
Comment #78
daffie CreditAttribution: daffie commentedAll code changes look good.
Back to RTBC.
Comment #79
xjmCommitted the backport to 8.9.x. Thanks! I didn't commit it to 8.8.x because the new warning is a disruption.
Comment #81
xjmMrrrr, the test is failing on PHP 7.0:
Comment #82
dwwHotfix comin' right up. Stay tuned.
Comment #83
dwwComment #84
dwwPlease ignore the PHP 7.0.x-dev result, that was confusion on my part. Hotfix is green on all relevant PHP 7 configs. My work here is done. ;)
Comment #85
DamienMcKennaLooks good to me, the 'void' return type wasn't added until 7.1 so this seems the appropriate fix.
Comment #86
DamienMcKennaGiven it's such a tiny change, going to RTBC it.
Comment #87
dwwThanks!
Proposed commit message:
git commit -m 'Issue #3135629 by xjm, dww, DamienMcKenna: Follow-up hotfix for PHP 7.0 compatibility.'
Comment #89
xjmCommitted the hotfix to 8.9.x. Thanks for the quick fix!
Comment #90
xjm