We are using:
Drupal 7.78
Drupal driver for SQL Server and SQL Azure version 7.x-2.6.
PHP 7.4.16
SQL Server 2016.
Problem/Motivation
We are attempting to update from Drupal 7.78 to 7.79, but are experiencing a critical error related to the \includes\database\sqlsrv\database.inc file. We determined that the core updates include changes to the \includes\database folder. It appears that all of the 3 supported databases have changes that conform with the changes made to the DatabaseConnection class. This module - /sqlsrv - appears to need changes to conform to the 7.79 core update.
Error message (partial): Fatal error: Uncaught Error: Non-static method PDO::__construct() cannot be called statically in \includes\database\sqlsrv\database.inc:139 Stack trace: #0 \includes\database\database.inc(1796): DatabaseConnection_sqlsrv->__construct(Array) #1 \includes\database\database.inc(1582): Database::openConnection('default', 'default') #2 \includes\database\database.inc(2457): Database::getConnection('default') #3
Steps to reproduce
Update core from Drupal 7.78 to 7.79. For us, this update causes a critical error to the site, which appears to be a problem with instantiating the DatabaseConnection class.
Proposed resolution
Update code in /sqlsrv to reflect the global changes made to the /includes/database files. It appears that major changes to how PDO is handled in the code have been included this update. The other 3 supported database core files have been changed.
| Comment | File | Size | Author |
|---|---|---|---|
| #23 | sqlsrv-pdo-fatal-error-3210240-16.patch | 2.52 KB | jkaufman |
Issue fork sqlsrv-3210240
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
ronraney commentedComment #3
ronraney commentedComment #4
ronraney commentedComment #5
ronraney commentedComment #6
ronraney commentedComment #7
ronraney commentedComment #8
avpadernoComment #9
ronraney commentedComment #10
avpadernoBugs are fixed in the Git branch code, which is used to create the development branch.
A version (which is created from a Git tag) cannot be fixed, once released; only the next version and the development snapshot can be fixed.
Comment #11
taylormadeapps commentedApologies in advance. I'm probably being super dumb. Kiamlaluno states that the fix is available on the 7.x.-2x-dev branch.
I've cloned the repo via ssh as an authenticated user 'taylormadeapps' but I cannot see this branch available on the origin.
Thinking that maybe I have to be a project member to access?
Comment #12
avpadernoI stated that bugs are fixed in the Git branch, which is used to create the development snapshot.
On Drupal.org, version 7.x-2.6 will never be fixed, as it's been already published.
The link for the 7.x-2.x development snapshot is given in the project page.
Comment #13
taylormadeapps commentedSo, the 7.x-2.x development snapshot hasn't been updated since the 9th of March, and doesn't currently contain any differences between the 2.6 release.
Can we assume that we are awaiting a fix to be published at this time.
Can I also assume, in the git repo, any changes will be available on the 7.x-2.x branch?
Thanks in advance for your patience.
Comment #14
sadarangans commentedHi, I am having the same issue. Can you let me know how can I fix this issue please, Thanks
Comment #15
taylormadeapps commentedI know all the project members are selflessly contributing their own time an effort on this project so I do not want to seem pushy or ungrateful. I would humbly like to enquire if anybody is working on this or intends to. We're in a situation that all Drupal 7 instances that use MsSQL are unable to be patched beyond Drupal 7.78 - so are exposing highly critical vulnerabilities out there in the wild. It would be for the common good, and of course my own selfish ends, if this issue were addressed promptly or at least the community given some time lines on what to expect. I'm a php noob at best so would contribute a full fix if I could. I've got the driver working in part with v7.80 through trial and error and a good dose of guess work, but still got a few issues that I can't clear up not knowing the driver architecture - but if I can help in anyway with moving this along please let me know.
Comment #16
jkaufman commentedHi all, I'm not a heavy contributor or even a PHP expert, but maybe somebody who is can look and see if this works and maybe create a patch if they agree that it does. Here is what I did to fix the Fatal Error in sqlserv\database.inc based on what I saw in the sqlite file and mysql file.
Line 145: change
PDO::__constructtoparent::__constructLine 843:
Remove
\PDO::beginTransaction();Add
Line 896: change
if (!PDO::commit())toif (!$this->connection->commit())I am concerned that if
\PDO::rollBack();on line 780 gets called it will also throw a Non static method error, but haven't quite figured out how that one should be changed yet.Apologies if this isn't the proper way to provide contributions.
Comment #17
jkaufman commentedI tried submitting a comment with a way to possibly patch the issue but I guess I got marked as spam. I have (from what I can tell so far) fixed the issue locally but I'm not sure how to provide the information here since I do not seem to be a confirmed user.
Edit: I've been confirmed, ignore this post
Comment #18
taylormadeapps commentedThanks jkaufman, those changes make a lot of sense to me from my initial foray into solving this. I couldn't quite work out the transaction stuff though, so I had a site that renders, but could not save any content :)
I think if we can solve the rollback issue then we have a workable fix.
I'll have a bit more of a play around today, now I have something to go on re the transactions.
Will report back if/when I make some progress.
Comment #19
taylormadeapps commentedHave pushed the proposed changes to the issue fork for review.
Still need an assist with the PDO::rollback statement.
Comment #20
taylormadeapps commentedJkaufman, I'm thinking that:
\PDO::rollBack(); on line 780should be replaced with
$this->connection->rollBack();also perhaps?
none of the other drivers have this transactional stuff in as far as I can see, so I don't have an example to go by.
Comment #21
jkaufman commentedtaylormadeapps, based on the PDO.rollback documentation and compared to beginTransaction that looks like the proper usage to me.
Comment #22
sadarangans commentedHi taylormadeapps and jkaufman
Thanks a lot for contributing to the issue. It will be such a great help. Is it possible for you to please post all the code changes, for us to try out.
Many thanks,
Comment #23
jkaufman commentedHere is a patch file with the code changes.
Comment #24
jkaufman commentedsadarangans #16 and #20 have all of the code changes listed
Comment #25
avpadernoComment #26
ronraney commentedNewbie question: Are the files found here in the Issue Fork the same as what's found in the patch?
https://git.drupalcode.org/issue/sqlsrv-3210240
Comment #27
jkaufman commentedrraney Yes, the patch and the fork that taylormadeapps submitted should be the same code. I just wasn't sure if having a patch would help the process so I went ahead and made one. Hopefully that didn't confuse things.
Comment #28
beakerboyThe 7.x branches of the sqlsrv module are no longer being maintained at this point. Sorry. I think the 7.x-2.x branches require a closed-source database abstraction layer, so I am unable to verify any fixes for that branch. Personally, I am completely unfamiliar with the 7.x codebase in general as well.
Comment #29
taylormadeapps commentedI don't think we're quite there, I've had my tester report a 500 error when trying to change a select field for their user account.
#0 F:\www\includes\database\sqlsrv\query.inc(33): DatabaseSchema_sqlsrv->queryColumnInformation('semaphore')
#1 F:\www\includes\lock.inc(134): InsertQuery_sqlsrv->execute()
#2 F:\www\includes\bootstrap.inc(438): lock_acquire('schema:runtime:...')
#3 F:\www\includes\bootstrap.inc(460): DrupalCacheArray->set(Array)
#4 [internal function]: DrupalCacheArray->__destruct()
#5 {main}
thrown in F:\www\includes\database\sqlsrv\schema.inc on line 141
[12-May-2021 16:57:47 Europe/London] PHP Fatal error: Uncaught DatabaseSchemaObjectDoesNotExistException: The table 'semaphore' does not exist. in F:\www\includes\database\sqlsrv\schema.inc:141
And that 'semaphore' table very much does exist. All works fine with Drupal v7.78 and the current sqlsvr driver.
Comment #30
ronraney commentedRegarding the issue above - this appears to be the function involved in /includes/database/sqlsrv/schema.inc
Once again, referencing other open source database modules, I see them using this syntax for similar checks:
if (!$this->fieldExists($table, $field))but not using (empty($table)). In our case, the table is empty, even though it exists. Does empty() check whether the table is empty, or the variable itself? I'm wondering if this condition could be left out, as other modules appear to not use this:
if (empty($table))I hope I'm making sense here. Perhaps it could simply be omitted, because we would still be checking if the table exists by using:
!$this->tableExists($table))Comment #31
ronraney commentedRemoved by rraney as unrelated content
Comment #32
beakerboyRegarding the `empty($table)` use, it’s possible that at some point the `tableExists()` method would throw an exception if $table was some sort of empty value. This may have been a way to control the error handling. I suggest that if `tableExists()` handles that case in a way that is appropriate, there’s no reason to check it in `queryColumnInformation()`.
I maintain the D8/D9 version of the driver, and there was a lot of weird stuff I had to clean out from there to get it working correctly. I’m sure D7 needs housekeeping too. If you are able to run the core test suite with Sql Server you’ll probably find a lot of little problems.
Comment #33
beakerboySounds like the patch has been reviewed, but not quite right. Please submit another patch for review.
Comment #34
beakerboy@rraney, if you find a new issue, please file a new issue. It’s usually easier to review and approve them one at a time versus making one patch that covers all the issues.
Comment #35
taylormadeapps commentedHave checked in the fix suggested by Beakerboy to the issue fork. e.g. to remove the empty table check from scheme.inc
That's solved that problem for me. Still got some weirdness around ajax calls in views and content edit admin pages, but getting a lot closer.
I think there's two issues to be unpicked here.
Issue #1 D7 driver contains a lot of cruft - ideally needs refactoring. Obviously that's unrealistic as we all acknowledge that it's not worth the effort and there's never going to be new functionality that warrants this.
Issue #2# Drupal Core v7.79 change a few minor things in the database driver implementation. To my noob eyes the changes don't look that extensive. This has flushed out some issues with the SQLSvr D7 driver. Hopefully these can be tidied up with a little bit of effort without a major refactor.
D7 is going to be around for a long while to come yet, as used by NGOs who simply don't have the resources to brave the dreaded D8/9 upgrade path. I do believe it's for the common good to patch this old driver up, so it still works, and do our bit to keep sections of the internet secure that can't afford to migrate.
I'd consider contributing to a modest bounty/reward if someone who knows the D7 branch could champion this.
Comment #36
beakerboy@taylormadeapps, I have a slack channel set up to discuss sqlsrv issues. By all means check in there and continue the conversation, or keep it here...I don’t care either way, but thought I’d throw that out if you like slack (#sql-server). We can discuss strategy if you would like.
Comment #37
ronraney commentedPlease delete #31 if possible. If not, perhaps I'll delete everything there. I understand about separate issues. I wasn't sure if it was related. I now suspect it is not related directly, and it's only adding to confusion. Thanks
Comment #38
beakerboy@rraney, I think the only thing you can do is edit the comment and remove the content.
Comment #39
ronraney commented@taylormadeapps
You mentioned weird Ajax calls surrounding Views. I'm experiencing this. I wonder if it ties into what Beakerboy said in the Slack channel about the way queries are handled in Views. I'm completely lost on this because the error messages are too cryptic. I'm hoping enough people are working on this module to come up with a new working version.
Comment #40
beakerboyWhat do you mean by “working on this module”? If you mean actively maintaining it, then that number is zero.
Comment #41
beakerboy@rraney, regarding your question in #26, maybe the driver needs to implement a new function that replaces
driver()or getConnection() is allowed (or encouraged) to return a different object type that renders driver() pointless. Maybe driver() has been deprecated and is now removed from core.I seem to remember a project in D8 whereby core would automatically know what the database driver was, and if it was a non-core driver, include the files automatically. This line may have been removed because core may have already included the files in an ideal world.
Comment #42
beakerboy@rraney
Regarding comment #26, that line does not appear in the version of that file that was included in the official release. Did someone else in your team add it at some point?
https://git.drupalcode.org/project/drupal/-/blob/7.78/modules/system/sys...
Comment #43
beakerboyAlso, could someone provide what versions of Drupal, PHP, and pdo_sqlsrv, and the sqlsrv driver you are using with success? I’m seeing this fatal error all the way back to PHP7.0.
Comment #44
beakerboyAs an update to the empty() question...the reason this is there is because the sqlsrv implmentation of Schema::tableExists() includes a reference to $table[0], the first character in the table name string. If the table name is empty, this check will cause a fatal error. I don’t think removing the call to empty() would fix the semaphore issue. However, it probably does make sense to remove it for consistency, but the check should be moved to TableExists().
Comment #45
beakerboyI figured this out. One month ago drupal core changed the DatabaseConnection class so that it no longer extends PDO. Because PDO was an ancestor of the sqlsrv connection class, PDO::__construct() was valid syntax. Now that it is not, we cannot call the constructor statically.
Comment #46
beakerboyIn case anyone here want to investigate this with CI tools, I have an Appveyor environment set up on this repository:
https://github.com/Beakerboy/sqlsrv/tree/7.x-2.x
It installs sql server 2016, PHP7.1.33, pdo_sqlsrv 5.6.1, Drupal 7.70, and the latest sqlsrv driver code, and runs the core test suite using scripts/run-tests.sh.
I have it set to just run the Database tests and there are a handful of failures. If anyone here wants to ensure that this driver works, and continues to work, this is how to do it.
Comment #47
ronraney commentedHello, in an earlier comment I said "enough people working on this module". There are people working on this, as evidenced by Taylormadeapps, jkaufman, etc. That's all I meant. People working on a solution. You've made it clear there is nobody actively maintaining the module.
Is this the most recent, collective (and unofficial) version of the module files (Issue fork)?
https://git.drupalcode.org/issue/sqlsrv-3210240
Comment #48
beakerboyThe whole issue fork/GitLab integration is new to me, but that seems like it. Make sure you look at the correct branch of the issue fork.
To me, the patch looks like it should cover the bases, but without having an actual D7 site to test it against, I don’t want to vouch for it. When @taylormadeapps said he was still seeing errors, I had to take that as a vote against it. Now that I have a test environment in Appveyor that works, I can see how it affects everything.
The only change I might make is to change the line from
parent::__construct(...)to$this->connection = new PDO(...). It seems like the constructor was a complete replacement of the parent, so this would keep the separation. I have not tested this, but it seem like it would be a more accurate replacement for that line.I’ve started a new issue where I plan to list all the test failures in the driver. If you want to weigh in on what should be fixed.
#3215085: Drupal 7.78 core database failures.
Comment #49
ronraney commentedWhy would there be a namespace operator, i.e. \PDO?
$this->connection = new PDO(...);
VS.
$this->connection = new \PDO(...);
Comment #50
beakerboyYou’re right..habit. Fixed.
Comment #51
ronraney commentedTo everyone who has contributed to this or used the patch/Issue fork:
Is there only one file involved so far? I pointed out something that I think may be problematic in the core files.
/includes/database/sqlsrv/schema.inc
Inside the module itself though, is database.inc the only file that appears to need changes? I know we have not resolved everything and it may be necessary to put out one fire at a time. Just wondering about our "collective" progress on this issue.
Comment #52
beakerboyLet me know if this is accurate. We have a module that “works” in one version of Drupal, but breaks upon core upgrade. We have a patch that appears to allow the site to upgrade, but new bugs are presented. Has anyone tested the patch under the original Drupal version? Does the patched driver operate successfully in Drupal 7.78?
If 7.78 is unaffected by the patch, and fixes the Fatal Error, my vote would be to apply the patch to the repository. If new errors come up after upgrading with the new driver version, these should be filed as a new issue and worked on. I don’t see where anyone has demonstrated that the patch causes these new regressions or if core changes cause the new regressions.
Has anyone compared calling
new PDO()versusparent::__construct()?Again, I’m don’t use this module and have not tested it. If @jkaufman made the patch, then @rraney and @taylormadeapps should chime in on their thoughts on it it’s been Reviewed And Tested.
The usual process is to specify which version of the patch you have tested, and your opinion, “needs work” or RTBC. A patch can alter multiple files. If you feel more changes need to be made then are in the provided patch, make the change and upload it, or change the issue fork and let us know. When everyone likes what’s in there, I’ll merge it.
/includes/database/sqlsrv/schema.inc Is not a core file, it’s in the driver...unless you are referring to something else.
Comment #53
ronraney commentedI have an instance of Drupal 7.78 that I can test with. It is not running the patched code, but I can go ahead and do this. I will report back on this.
Incidentally, I have not yet learned how to correctly implement a patch. I use drush to run updates, but it's been problematic. I have a Git Bash or Windows command which could conceivably connect the servers involved. I have been manually changing the files with a text editor based on the git fork and cross-referencing the patch.
Comment #54
beakerboyYou could check out the repository with git in a new location, apply the patch with
git apply Foo.patch, then copy the files to your test site. Would that work on windows? With git installed, you could probably check out the issue fork as well and use those files.Comment #55
ronraney commentedMy initial testing of the patch changes on a 7.78 site resulted in an Error page which made the site inaccessible. To reiterate, I changed the database.inc file on a site running 7.78 and it broke the site. This patch might break every site that is not running Drupal 7.79 or 7.80.
Someone else should verify this. If you get the updates working on older versions of Drupal, then perhaps I need to revisit the code changes that I need to make in database.inc.
If this post is somehow irrelevant, let me know. I am fairly new to the Drupal and SQL Server game.
P.S. This makes sense to me, because the overhaul of PDO changes in the core files didn't appear until 7.79 or 7.80 (not sure which one at the moment).
Comment #56
beakerboyAny time you are given an error message, please include that in your post.
There should be a way to construct the driver so that it works in both versions of core. The error is caused because calling a PHP object constructor is only valid if that object is an ancestor of the current object. When the parent of our class removed PDO as ITS parent, our call to PDO::__construct() was no longer valid. We should still be able to call the parent or PDO with
new PDO().Alternatively, we could add an if/then to look at the Drupal version and have two different versions of the constructor, and call the correct code in each case. This is probably to only way.
Comment #57
beakerboyCan you try this:
Comment #58
ronraney commentedI now have the unofficial forked version of the module working on a test installation of Drupal 7.80. I'm having issues with Views. When I try to edit anything inside a View, e.g. field, filter, title, anything at all, I get an AJAX popup error. The error isn't helpful.
On another site running Drupal 7.80 and the forked version of the module, I'm not having these issues. I can't figure out what's broken with Views on one site and not another.
I am seeing these errors but not sure if they are related to the Views AJAX issue:
Comment #59
beakerboyOdd,
Call to a member function beginTransaction() on nullwould imply that$this->connectionis null, but it is set to PDO byparent::__construct(). You’re using the code in the issue fork? You replaced the code in wwwroot\includes\sqlsrv as well as in wwwroot\sites\all\modules\sqlsrv ?Comment #60
ronraney commentedI tested a site with this code near line 146 in database.inc, as well as all other recent changes to this file. The site isn't functioning well, but it isn't broken.
When I revert ONLY this line of code to the previous, but with the other changes in place, I get the same issues. Therefore, I believe the conditional might be working.
I cannot run Flush Caches from Admin. with the code changes in place. I get the errors shown above having to do with pushTransaction() and beginTransaction(). Since I can't flush caches, I don't know if my code changes have been refreshed on the site.
P.S. When I reverted the file back to the current release of database.inc, everything started working again. There is some kind of issue with the pushTransaction() function.
Comment #61
beakerboyThe ideal process would be to update the driver, browse the site looking for errors, flush cache, update drupal core, then browse again and flush cache again. If we can come up with code that all this can happen without errors, we will be golden.
Also, please be more descriptive with Drupal core versions and sqlsrv versions. When you say “revert to current” I assume you mean 7.x-2.6. If so, please say that so there’s no confusion. Was this latest update in core 7.78? If so, then the error makes sense. Core 7.78 does not define $this->connection in the parent. We might need another if/then
And the rollback() change should be wrapped the same way.
Comment #62
beakerboyI've updated the issue fork with all three conditionals. Please test the changes against both 7.78 and 7.80
Comment #63
beakerboyComment #64
beakerboyHave we verified that removing the empty() check is necessary?
Comment #65
beakerboyI just added a fourth version check for the PDO::commit() change.
Comment #66
beakerboyComment #67
ronraney commentedTest results for this file:
https://git.drupalcode.org/issue/sqlsrv-3210240/-/blob/3210240-fatal-err...
Drupal 7.78
I've replaced the database.inc file with these if/else changes from yesterday. I refreshed the browser tab, then flushed all caches. All caches cleared successfully.
Drupal 7.80
I've replaced the database.inc file, same as above. I refreshed the browser and flushed all caches successfully. I went ahead and ran a database update. The site is working, to an extent. I'm still getting AJAX errors inside Views, when I edit any single component of the View. I currently cannot edit any View on this particular website. However, I am able to edit Views on another website using 7.80.
P.S. I disabled the jquery_update module and I'm now able to edit Views. I'm going to move forward with updating to Drupal 7.80 on another test site. If I can get Views to work, I think I may be ready to move forward with these changes to database.inc.
Comment #68
beakerboyAgain, What are the error messages? Is there anything different between the sites that might be causing a difference in AJAX errors?
Comment #69
ronraney commentedI reported these already and you told me to open a separate issue. Also I cannot copy and paste the AJAX errors.
Comment #70
beakerboyHave you opened the separate issue? Why can't you copy and paste the AJAX errors? Is there nothing in the web browser console log, nothing in the drupal log? Can you at least describe what it is doing? There might be a way to force the page to print the error to the screen or the error log.
Are you saying that the code as-is in the issue fork is completely working successfully on one of two 7.80 sites, and it works fine on 7.78? Are the two 7.80 sites copies of each other, or different development sites? Do they have different modules? Different themes? Maybe a custom module that is incompatible with either the sqlsrv changes or D7.80?
What do you think you still need before you call this RTBC, and good to merge into the main codebase?
Comment #71
beakerboyFor anyone watching this issue, my plan is not to keep the if/else statements in the driver indefinitely.
#3215864: Mark new versions for D7.79+
My plan is to create one "bridge release" that works on pre 7.79 and 7.79+. I will immediately make a release that is only 7.79 and greater.
This will allow users to test and upgrade stepwise if they desire.
Comment #72
ronraney commentedI did another update on a test server.
Updated to Drupal 7.80. I replaced the database.inc file inside the /sqlsrv module (which needs to be manually placed back into /includes/database) using the most recent version proposed by Beakerboy.
I'm getting some errors logged, but I think they are related to using PHP 7.4x. The production site is getting these errors using Drupal 7.78. I believe it's unrelated to /sqlsrv.
I will await other's feedback for a day or two before changing status of Issue to RBTC.
Comment #73
ronraney commentedMy testing is documented in the forum. I have 7.80 working on 3 test sites, and 1 live site. Some of my issues are not related to the driver. These included a jquery-update module and PHP version.
Tom has also done some testing with the proposed code changes provided by Beakerboy. He had similar AJAX issues, which were unrelated to the SQL Server module. He believes his outstanding issues are related to custom modules, and not the driver.
If we discover new issues, I can change the status back to Needs Work.
Comment #74
beakerboy@taylormadeapps gave it an RTBC in slack, so I’ll merge this change.
Comment #79
beakerboyThanks for the input everyone.