This issue has been split from the original parent/meta issue #2001350: [meta] Drupal cannot be installed on PostgreSQL, but is now a follow-up issue and under #2157455: [Meta] Make Drupal 8 work with PostgreSQL or remove support from core before release.

Updated: Comment #7

Problem/Motivation

In transaction context, the first failure makes the transaction abort, and subsequent operations, even if they would normally succeed, also fail with the error "current transaction is aborted, commands ignored until end of transaction block". This differs from the behavior of MySQL. The workaround is to wrap all queries that occur inside a transaction with SAVEPOINT.

This issue is the root cause of many test failures on the PostgreSQL test bot, which is probably covering up general Drupal bugs that could be discovered through environment/platform test bots.

Other related issues that can be closed/retested when this is fixed:

Proposed resolution

Damien Tournoud outlines the issue in Comment #12, which suggests the following resolutions:

  1. Add a save point and release after each query in the driver to mimic MySQL Innodb.
  2. Or mimic postgresql behavior in mysql and sqlite drivers by rolling back the entire transaction, which would also help exposed "hidden bugs" in Drupal that mysql is masking for us.

#1 was implemented using regular expression matching, but this did not pass code review by Crell in comment #117 . He offers some suggestions, but nothing to go on.

This issue is blocking several of the other PostgreSQL issues from being fully testable on PostgreSQL itself, but may not necessarily be a blocker to getting those issues fixed.

Patch Contributors

- fvideon, Damien Tournoud: initial issue patches and ideas
- mradcliffe: patch in this issue, new approach based on Crell's review.
- lokapujya: re-rolling patch to keep up with HEAD.
- bzrudi71, steinmb, grom358: manual testing & patch review
- ricardoamaro, jaredsmith: automated test bot & patch review

Remaining tasks

  • Review & Commit

API changes

Yes, most likely. Needs to be documented here.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

mradcliffe’s picture

Issue summary: View changes
mradcliffe’s picture

Status: Active » Needs work
FileSize
4.6 KB

Here's my progress in attempting to do something similar to the patch in the parent issue, but in the query classes.

There are two problems with this approach:

1. It's really repetitive code and would need to be done in each of the Query classes and any driver class that does a query.
2. The install proceeds swimmingly until a menu rebuild is called from menu.inc, which starts a brand new transaction via db_transaction(). The only code that uses db_transaction() is menu.inc and some test classes. I'm not sure if using pushTransaction or popTransaction is appropriate given db_transaction() exists.

My development process has been, in case any one is going to work on this, the following:

mradcliffe’s picture

Status: Needs work » Needs review
FileSize
5.2 KB
6.32 KB

Patch provides the following changes:

  1. Fix for menu router rebuild case by extending startTransaction method and releasing transactions on the transactionLayers statck.
  2. Create a couple of methods on Connection to handle the add and release savepoints.

Is this the correct approach?

Edit: with this patch and the other patches, Drupal 8 installs on PostgreSQL 9.2.4.

mradcliffe’s picture

Added clarification to documentation to methods.

Removed inTransaction condition as that is done within pushTransaction and popTransaction respectively. Well popTransaction uses supportsTransaction, which is better. I attempted to just do away with those new methods and use push/pop, but I ran into some issues. I'll try that again later because I think I made a typo or something.

mradcliffe’s picture

Status: Needs review » Needs work

I probably should set this to needs work because of my last comment.

mradcliffe’s picture

mradcliffe’s picture

Issue summary: View changes
bzrudi71’s picture

mradcliffe’s picture

Status: Needs work » Needs review
FileSize
8.3 KB
8.53 KB

Still needs work or run through tests to see what else is broken. The previous patches really screwed up transaction support. This patch should fix that and pass transaction tests.

Also this current patch means the band-aid fix is no longer necessary in #2325421: Installer broken on PostgreSQL (again) so think about removing that code in a patch if that gets in first.

Setting to NR first and then NW later.

ricardoamaro’s picture

Testing this patch locally now...

Preparing a local environment:

~/# git clone --branch master http://git.drupal.org/project/drupalci_testbot.git
~/# cd drupalci_testbot

~/drupalci_testbot# ./scripts/build_all.sh update pgsql-9.1
mradcliffe’s picture

Issue summary: View changes
ricardoamaro’s picture

Running tests with patch:

~/drupalci_testbot# sudo DCI_PATCH="https://www.drupal.org/files/issues/drupal-2181291-pgsql-aborted-transaction-3.patch,." DCI_DBTYPE='pgsql' DCI_DBVER='9.1' DCI_TESTGROUPS='--all' DCI_DRUPALBRANCH='8.0.x' DCI_VERBOSE="true" DCI_CONCURRENCY="8" ./containers/web/run.sh

ricardoamaro’s picture

Will paste the results in the end

ricardoamaro’s picture

Attaching results

They seem to fail on:

Test run duration: 14 min 26 sec

Detailed test results
---------------------

Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[HY000]: General error: 1 too many SQL variables' in /var/www/core/lib/Drupal/Core/Database/Connection.php:336
Stack trace:
#0 /var/www/core/lib/Drupal/Core/Database/Connection.php(336): PDO->prepare('SELECT * FROM s...')
#1 /var/www/core/lib/Drupal/Core/Database/Connection.php(536): Drupal\Core\Database\Connection->prepareQuery('SELECT * FROM {...')
#2 /var/www/core/scripts/run-tests.sh(978): Drupal\Core\Database\Connection->query('SELECT * FROM {...', Array)
#3 /var/www/core/scripts/run-tests.sh(89): simpletest_script_reporter_display_results()
#4 {main}

Next exception 'Drupal\Core\Database\DatabaseExceptionWrapper' with message 'SQLSTATE[HY000]: General error: 1 too many SQL variables: SELECT * FROM {simpletest} WHERE test_id IN (:test_ids_0, :test_ids_1, :test_ids_2, :test_ids_3, :test_ids_4, :test_ids_5, :test_ids_6, :test_ids_7, :test_ids_8, :test_ids_9, :test_ids_10, :test_ids_11, :test_ids_12, :test_ids_13, :test_ids_14, :test_ids_15, :test_ids_ in /var/www/core/lib/Drupal/Core/Database/Connection.php on line 569
mradcliffe’s picture

Oh, I forgot one little fix that I was trying out in another issue that helps. Finally making the change that other drivers made and not throwing PDOException because the application side no longer catches it.

hinfox’s picture

For me it installs with the last patch applied (on PostgreSQL 9.3.5 Ubuntu 14.04.1 LTS 64 bit).

bzrudi71’s picture

Just startet run on PG testbot with latest patch applied (like in #12). Test is still running, but it seems we are back on track as most/all errors are gone and just leaving the ones we already know about. Big thanks @mradcliffe! Let's get this in after line by line review...

@ricardoamaro can you please crosscheck with my results? Thanks :-)

steinmb’s picture

PostgreSQL 9.3.4
Patch #15

Tested #15 and it came back green and it fixes the broken installer. Some warnings though, not sure if they are related to this or some of the other issues related to PostgreSQL problem.

ERROR:  relation "cache_config" does not exist at character 102
STATEMENT:  SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM cache_config WHERE cid IN ('system.filter')
ERROR:  relation "config" does not exist at character 24
STATEMENT:  SELECT name, data FROM config WHERE collection = '' AND name IN ('system.filter')
ERROR:  relation "cache_config" does not exist at character 13
STATEMENT:  DELETE FROM cache_config
  WHERE  (cid IN  ('system.filter'))
ERROR:  relation "cache_discovery" does not exist at character 13
STATEMENT:  DELETE FROM cache_discovery
  WHERE  (cid IN  ('typed_config_definitions'))
ERROR:  relation "config" does not exist at character 24
STATEMENT:  SELECT name, data FROM config WHERE collection = '' AND name IN ('core.extension')
ERROR:  relation "config" does not exist at character 18
STATEMENT:  SELECT name FROM config WHERE collection = '' AND name::text LIKE '%'
ERROR:  relation "cache_bootstrap" does not exist at character 102
STATEMENT:  SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM cache_bootstrap WHERE cid IN ('module_implements')
ERROR:  relation "cache_bootstrap" does not exist at character 102
STATEMENT:  SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM cache_bootstrap WHERE cid IN ('hook_info')
ERROR:  relation "cache_bootstrap" does not exist at character 30
STATEMENT:  SELECT 1 AS expression
  FROM
  cache_bootstrap cache_bootstrap
  WHERE ( (cid = 'hook_info') )
ERROR:  relation "config" does not exist at character 30
STATEMENT:  SELECT 1 AS expression
  FROM
  config config
  WHERE ( (collection = '') AND (name = 'core.extension') )
ERROR:  relation "cache_default" does not exist at character 30
STATEMENT:  SELECT 1 AS expression
  FROM
  cache_default cache_default
  WHERE ( (cid = 'schema') )
ERROR:  relation "cache_entity" does not exist at character 13
STATEMENT:  DELETE FROM cache_entity
  WHERE  (cid IN  ('values:user:0'))
ERROR:  relation "menu_tree" does not exist at character 56
STATEMENT:  SELECT DISTINCT menu_tree.menu_name AS menu_name
  FROM
  menu_tree menu_tree
ERROR:  relation "cache_menu" does not exist at character 8
STATEMENT:  UPDATE cache_menu SET expire='1409820431'
ERROR:  relation "cache_menu" does not exist at character 8
STATEMENT:  UPDATE cache_menu SET expire='1409820608'
ERROR:  relation "cache_menu" does not exist at character 13
STATEMENT:  DELETE FROM cache_menu
  WHERE  (expire <> '-1') AND (expire < '1409820609')
ERROR:  relation "cache_render" does not exist at character 13
STATEMENT:  DELETE FROM cache_render
  WHERE  (expire <> '-1') AND (expire < '1409820609')
ERROR:  relation "cache_data" does not exist at character 13
STATEMENT:  DELETE FROM cache_data
  WHERE  (expire <> '-1') AND (expire < '1409820609')
ERROR:  relation "config_snapshot" does not exist at character 13
STATEMENT:  DELETE FROM config_snapshot
  WHERE  (name::text ILIKE '%') AND (collection = '')
ERROR:  relation "config_snapshot" does not exist at character 33
STATEMENT:  SELECT DISTINCT collection FROM config_snapshot WHERE collection <> '' ORDER by collection
ERROR:  relation "config_snapshot" does not exist at character 30
STATEMENT:  SELECT 1 AS expression
  FROM
  config_snapshot config_snapshot
  WHERE ( (collection = '') AND (name = 'core.date_format.fallback') )
ERROR:  relation "cache_render" does not exist at character 102
STATEMENT:  SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM cache_render WHERE cid IN ('entity_view:user:1:full:stark:r.authenticated:Europe/Berlin')
ERROR:  relation "cache_render" does not exist at character 30
STATEMENT:  SELECT 1 AS expression
  FROM
  cache_render cache_render
  WHERE ( (cid = 'entity_view:user:1:full:stark:r.authenticated:Europe/Berlin') )
ERROR:  relation "cache_menu" does not exist at character 102
STATEMENT:  SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM cache_menu WHERE cid IN ('current-route-parameters:main:route:entity.user.canonical:route_parameters:a:1:{s:4:"user";s:1:"1";}')
ERROR:  relation "cache_menu" does not exist at character 30
STATEMENT:  SELECT 1 AS expression
  FROM
  cache_menu cache_menu
  WHERE ( (cid = 'current-route-parameters:main:route:entity.user.canonical:route_parameters:a:1:{s:4:"user";s:1:"1";}') )
mradcliffe’s picture

Those are normal for Drupal 8. Drupal 8 installer WILL send garbage queries because of cache clears and it's up to the driver to handle not aborting everything because of it.

steinmb’s picture

Status: Needs review » Reviewed & tested by the community

Then I'm RTBC

ricardoamaro’s picture

Per request i'm running:

~/drupalci_testbot# ./scripts/build_all.sh refresh pgsql-9.1

~/drupalci_testbot# sudo \
DCI_PATCH="https://www.drupal.org/files/issues/drupal-2181291-pgsql-aborted-transaction-15.patch,." \
DCI_DBTYPE='pgsql' DCI_DBVER='9.1'  \
DCI_TESTGROUPS='--all' \
DCI_DRUPALBRANCH='8.0.x' \
DCI_VERBOSE="true" \
DCI_CONCURRENCY="8" 
./containers/web/run.sh
ricardoamaro’s picture

mradcliffe’s picture

I'm not sure if that's the right file you uploaded. The file tried to apply

Applying Patch: https://www.drupal.org/files/issues/drupal-2181291-pgsql-aborted-transaction-3.patch
ricardoamaro’s picture

ok, let me try and run this again.

mradcliffe’s picture

Status: Reviewed & tested by the community » Needs review

I don't think this should be RTBC, which probably is confusing core committers.

ricardoamaro’s picture

My bad...
I did the tests again against patch 15 and here are the results:

https://www.drupal.org/files/issues/drupal-2181291-pgsql-aborted-transac...

Tell me if you need anything further ;)

mradcliffe’s picture

I downgraded Postgresql to 9.1 (surprisingly painless with my puphpet config :-) and ran the following test that failed on the testbot. I did fetch and rebase to origin/8.0.x as 2014.09.06 23:50 UTC.

I'm not able to reproduce these test exceptions with the patch. I'll look more at the docs on your postgresql testbot to see what's different in config. Maybe the test bot is having trouble like in SQLite related to #2031261: Make SQLite faster by combining multiple inserts and updates in a single query, #2028713: Menu router does "too many SQL variables" on SQLite resulting in silent nondisplay of menu items, #1884182: Import of large number of translations fails on SQLite?

Test Result
Drupal\action\Tests\ConfigurationTest 51 passes, 0 fails, 0 exceptions, 14 debug messages
Drupal\aggregator\Tests\AddFeedTest 67 passes, 0 fails, 0 exceptions, 18 debug messages
Drupal\views\Tests\Handler\HandlerTest 127 passes, 0 fails, 0 exceptions, 13 debug messages
mradcliffe’s picture

Oh, hah. It is related. The new test bots store simpletest data in sqlite, which means #2031261: Make SQLite faster by combining multiple inserts and updates in a single query is related.

ricardoamaro’s picture

@mradcliffe would it help if i would tell the testbot to use drush installer?

Like this:

~/drupalci_testbot# ./scripts/build_all.sh refresh pgsql-9.1
~/drupalci_testbot# sudo \
DCI_PATCH="https://www.drupal.org/files/issues/drupal-2181291-pgsql-aborted-transaction-15.patch,." \
DCI_DBTYPE='pgsql' \
DCI_DBVER='9.1'  \
DCI_TESTGROUPS='--all' \
DCI_DRUPALBRANCH='8.0.x' \
DCI_VERBOSE="true" \
DCI_CONCURRENCY="8" \
DCI_INSTALLER="drush" \
./containers/web/run.sh

Let me start a test with these and post the results

ricardoamaro’s picture

lokapujya’s picture

Rerolled. I'm able to run standard install with this patch.:
Mac OS X 10.9.4
PHP 5.5.17
Postgres 9.35

lokapujya’s picture

FileSize
8.78 KB

Oops. patch is now attached.

jaredsmith’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: +Amsterdam2014

I've tested the patch and it works as advertised. I'm now able to go a clean installation on PostgreSQL from the 8.0.x branch. I've also read through the code of the patch, and agree that the code looks great.

My only concern, however, is that that we seem to be papering over a more fundamental problem in the installer code -- namely, that the installer tries to SELECT (and even worse, DELETE) from tables before they've been created. I don't know if this is really an expected thing (in which case, let's get this patch committed quickly), or unexpected (in which case, we should really fix the installation code).

Either way, I'd like to get this fixed sooner rather than later, and I'm willing to put some time into it here at DrupalCon Amsterdam.

mradcliffe’s picture

My only concern are the test results from @ricardoamaro's test bot from earlier and trying to reconcile those with not being able to reproduce no my vagrant environment.

mradcliffe’s picture

Ok, it looks like changes in the test bot since 3 weeks ago fixed some things, and with patching #32 we can get clean tests on Database group and various others that allow things to install.

Waiting on http://testbot.drupal-pt.org/node/381

grom358’s picture

Alternative to doing savepoints in code is by using ON_ERROR_ROLLBACK.

I also have to agree with #34, the installer shouldn't do SELECT or DELETE on non-existent tables.

Performance wise, I found this old post saying average slowdown of 12%. I wasn't able to find benchmarks for version 9 of postgres.

mradcliffe’s picture

Status: Reviewed & tested by the community » Needs review
FileSize
179.99 KB

Latest test bot results attached from ricardo's test bot. 206 exceptions should show pretty much all of the bugs in Drupal 8 wrt to PostgreSQL. I'd say the patch is successful given manual testing and test bot runs.

Setting to Needs review for the moment to look at performance if we care about that.

bzrudi71’s picture

Just did a quick grep and it doesn't seem that Drupal makes heavily use of transactions. There are some transactions in the Entity System and mostly in storage context. So I guess we are save here in regards of performance...

ricardoamaro’s picture

Status: Patch (to be ported) » Reviewed & tested by the community
mradcliffe’s picture

Just did a bit of reading to confirm that the uniqueness of the save point name. "If multiple savepoints have the same name, only the one that was most recently defined is released." This should not cause anything to leak out on Exceptions, which is @Damien Tournoud's concern.

We discussed this briefly this morning about being re-entrant, and if so, that may be an additional bug in some other part of Drupal core (or contrib).

I'd say I'm confident in this being RTBC.

mradcliffe’s picture

Issue summary: View changes

Added patch contributors to issue summary.

webchick’s picture

Status: Reviewed & tested by the community » Fixed
+++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Delete.php
@@ -9,4 +9,23 @@
-class Delete extends QueryDelete { }
+class Delete extends QueryDelete {

Um. That's crazy that this wasn't implemented before?!

Anyway, this only touches the pgsql lib so looks safe to commit. GREAT work on this effort, folks! Onward to 100% passing tests!

Committed and pushed to 8.x. Thanks! I'm pretty sure I got everyone in the commit message. :)

  • webchick committed cf44d38 on 8.0.x
    Issue #2181291 by lokapujya, ricardoamaro, mradcliffe, fvideon, Damien...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

bzrudi71’s picture

Just did a bit of reading to confirm that the uniqueness of the save point name. "If multiple savepoints have the same name, only the one that was most recently defined is released."

CommentTestBook test fails because of:
Drupal\Core\Entity\EntityStorageException: mimic_implicit_commit is already in use.

Please see #2356967: PostgreSQL: Fix tests in comment test group.
Follow-up?

grom358’s picture

Still think it simpler to use ON_ERROR_ROLLBACK. Have that option turned on when establishing the connection. Bonus of being a lot less code, be one extra line of code when connection is established instead of all this code to wrap queries with creating explicit savepoints.

What pros of doing it with code? what are the cons with using ON_ERROR_ROLLBACK option?

bzrudi71’s picture

@grom358 the ON_ERROR_ROLLBACK option is not a feature of the PostgreSQL server itself. It's a feature of the PostgreSQL client(s) and as far as I can see not supported by PDO. Anyway, it does exactly the same as this patch does (adding SAVEPOINTS), so there will be no profit in terms of performance ;-)

grom358’s picture

@bzrudi71 correct you are. I just assumed the driver would support it. I was not suggesting it for performance reasons but in reducing the code needed to be maintained.

Besides the installer (which I notice has been marked as fixed) where else in Drupal does it make use of implict savepoints?

This patch means have the same behaviour as MySQL. But wether that is a good thing here is the question. Personally I think can be problematic if we committing a transaction that happen to contain SQL statements that errored.