This issue is part of #2157455: [Meta] Make Drupal 8 work with PostgreSQL or remove support from core before release.
Problem/Motivation
We have failing tests with PostgreSQL as Database backend because of broken table renaming support.
Proposed resolution
Add support for database table renames and ensure test coverage.
User interface changes
None
API changes
None
Beta phase evaluation
| Issue category | Bug because of broken tests |
|---|---|
| Issue priority | Major because of broken test environment |
| Disruption | None disruptive for core/contributed and custom modules/themes because it is a bugfix only |
Originally reported by suvisor:
"PDOException: SQLSTATE[42P07]: Duplicate table: 7 ERROR: relation "date_formats_formats_key" already exists: CREATE TABLE {date_formats} ( dfid serial CHECK (dfid >= 0), format varchar(100) NOT NULL, type varchar(64) NOT NULL, locked smallint NOT NULL default 0, PRIMARY KEY (dfid), CONSTRAINT date_formats_formats_key UNIQUE (format, type) ); Array ( ) in db_create_table() (line 2588 of /var/www/home_page_7/includes/database/database.inc).
When old date_formats (from d6) table is renamed to d6_date_formats all its relations (indexes, checks etc.) doesn't renames automatically. So uniq check with name date_formats_formats_key still exist.
Sorry for my terrible English... :(
| Comment | File | Size | Author |
|---|---|---|---|
| #82 | 81-82-interdiff.txt | 1.84 KB | alexpott |
| #82 | 1013034.82.patch | 12.73 KB | alexpott |
| #81 | pgsql-constraint-rename-1013034-81.patch | 12.21 KB | jaredsmith |
| #74 | interdiff.txt | 1.94 KB | bzrudi71 |
| #74 | pgsql-constraint-rename-1013034-74.patch | 12.21 KB | bzrudi71 |
Comments
Comment #1
catchNice. Looks like the ideal thing would be fore db_rename_table() in the PostgreSQL driver will need to figure out all of these and rename them, have no idea if that's doable but moving this into that queue.
Comment #2
josh waihi commentedWhat version of PostgreSQL? I was able to upgrade a fresh install of Drupal 6.20 to Drupal 7 on PostgreSQL 9 PHP 5.3.2 fine.
Comment #3
suvisor commentedPostgresq 8.4, PHP 5.3.3 from Debian Squeeze distro.
Comment #4
damien tournoud commentedI think this is a won't fix. The
{date_format}table does not exist in D6.Comment #5
suvisor commentedMay be {date_formats} :)? And _that_ table is exist.
Comment #6
Stevel commentedcould this be a table from a contrib module? There is a table named date_formats in the D6 version of date_api (from the Date project), which might be the cause. The table no longer exists in the D7 version afaics, but there is no update function to remove the old table. Perhaps core should provide an upgrade path (since we pulled support for date formats into core now), keeping the custom defined date formats from the date_api module.
Comment #7
josh waihi commentedIn that case, its not a PostgreSQL issue.
Comment #8
endrus commentedTried to upgrade my MySQL site from D6.20 to D7. Got a similar fatal error:
Comment #9
endrus commentedEditing title, since same thing happened on a site with mysql database.
Comment #10
catchI'm sure this is a PostgreSQL issue.
The D6 contrib table is renamed here:
The original bug report shows that when the date formats table is renamed, the indexes on that table do not get renamed alongside it. This means that when you try to create the same index name on the new table, it fails.
What confuses me though is that this is already supposed to be fixed:
#718016: DatabaseSchema_pgsql::renameTable() needs to rename the indexes
The reports on MySQL look unrelated to me.
So I'm moving back to the postgres driver, but bumping down to 'normal' and someone please try to reproduce this again.
Comment #11
ncl commentedI'm able to reproduce while upgrading from 6.22 to 7.4. Got same error first time and then again after starting from scratch (ouch ;):
Info:
- FreeBSD 8.0-RELEASE-p2
- PostgreSQL 8.4.3
- Nginx 1.0.5
- PHP 5.3.5 via FastCGI
- Date module 6.x-2.7 installed in Drupal.
And sure enough the indexes did not get renamed. Connecting to the database after failed upgrade:
Comment #12
damien tournoud commentedConfirmed. The problem is that we are renaming the indexes, but not the constraints.
Comment #13
ncl commentedErghem... Are you? Look above. Neither indexes nor constraints got renamed in my case. I think the problem is in renameTable() in includes/database/pgsql/schema.inc:
I tried the SELECT on my database and it produces this:
So it does at least get the index names correct. It would seem that if(pgrep_match()) test fails here. What is it in there for anyway?
Comment #14
damien tournoud commentedWhat we call indexes and what PostgreSQL call indexes is slightly different: indexes are things that end in
_idxand are used for query performance only; constraints are things that end in_keyor_pkeyand are used for relational integrity.We do rename the indexes but not the constraints. We need to extend the regexp to add
_keyand maybe (has to be confirmed properly)_pkey.Comment #15
ncl commentedI see. I have no idea if this is the right way but...
Proof of concept patch. Rename all keys in renameTable(). Update completed with no errors. Successfully installed date-7.x-2.0-alpha3 afterwards.
Comment #17
catchThere's a patch here
Comment #18
xjmTagging.
Comment #19
damien tournoud commentedI think we want to only rename stuff that end in '_key' and '_idx'. We should not touch the indexes/constraints we are not sure we have created. Otherwise, on visual inspection the patch does the trick.
Comment #20
kathyh commentedUpdate of patch for D8 /core dir.
Comment #21
dries commentedWould be great if someone with PostgreSQL could test this patch.
Comment #22
drupdan3 commentedIs there any reason to apply this patch other than D6 -> D7 upgrades?
Comment #23
valthebaldIt makes sense not only for D6=>D7 upgrades, but for module updates as well
Tested on Debian testing, PHP5.3.8, Postgres 8.3/8.4 -
Comment #24
dries commentedThanks for testing valthebald (per the request in #21). Committed to 8.x. Moving to 7.x.
Comment #25
webchickHm. This looks a tad bit risky, so I'd like to hold it until after the release this Wednesday. But it'll be part of the release after that!
Comment #26
webchickCommitted and pushed to 7.x. Thanks!
Comment #28
catchI don't think this was fixed.
I just ran db_rename_table() on a table with a unique key, SELECT * FROM information_schema looks like:
In other words the id_seq was not renamed along with the table.
Comment #29
andypostConfirm, there's 2 issues:
1) sequence needs to be renamed with
alter sequence {name} rename to {new name}for pgsql >= 8.32) queryTableInformation() function works with non-updatable data so each schema change should be reflected here, or be need to quiery this data on each request (it seems not so much expensive because schema changed not so frequently)
Comment #30
andypostPatch adds reset for stored information for each schema changing functions
Also extends a schema test to check for primary keys, unique keys and psql sequences (I'm testing under pgsql 8.4 debian)
Comment #32
andypostChanged table definition - all keys are defined
Comment #33
catchI'm downgrading this to major since it's driver-specific (and not the MySQL driver). When we can support different databases with qa.drupal.org then bugs like this would cause actual test failures (and hence be critical), but until then it doesn't make sense to hold up everything else.
Comment #34
kerasai commented#32: 1013034-pgsql-32.patch queued for re-testing.
Comment #36
andypostre-roll,
SchemaTest.phpnow lives incore/modules/system/lib/Drupal/system/Tests/DatabaseComment #38
dbcollies commentedComment #39
dbcollies commentedComment #41
dbcollies commented#39: PostgreSQL-constraints-do-not-get-renamed-by-db_rename_table.101303-1.patch queued for re-testing.
Comment #43
dbcollies commented#39: PostgreSQL-constraints-do-not-get-renamed-by-db_rename_table.101303-1.patch queued for re-testing.
Comment #45
disasm commentedthis needs cleaned up (which might fix the error)
Something like this should work:
Comment #46
dlu commentedI'm stumped right at the beginning…
Tried to install D8 with Postgres and discovered that there isn't currently an option to try to use PostgreSQL as the database. Am I missing something obvious?
Comment #47
disasm commentedYeah, pgsql needs to be compiled into PHP for postgresql installation to work. If your using a mac, try something like brew install php54 --with-pgsql --with-mysql.
Comment #48
dlu commentedThanks! I thought I'd done that… But all evidence suggests that I did not :-)
Comment #49
mradcliffeRe-added tag removed from last comment.
Comment #50
yesct commentedit has been a while. un assigning. please make a comment to pick it up again if you like.
does this need tests also? I think so. See comment #45
Comment #51
jhedstromAdding a related issue, #2061879: Remove Schema::copyTable. Because of the single long test, when the copy table exception is thrown, none of the subsequent tests, including the ones added here, get called.
I've also attached a reroll of #39, and am removing the 'needs tests' tag since it adds tests.
Comment #52
bzrudi71 commented@jhedstrom thanks for picking up this one :-) Will do a PostgreSQL bot run tomorrow and post the results here. Also adding parent issue #2157455: [Meta] Make Drupal 8 work with PostgreSQL or remove support from core before release
Comment #53
bzrudi71 commentedWell, guess there is actually not much to test here as long as #2061879: Remove Schema::copyTable is not done ;-)
Comment #54
catchComment #55
catchThis is critical unless we do #2160433: [policy, no patch] Move PostgreSQL driver support into contrib.
I think it's OK for the initial beta-to-beta upgrade path to be MySQL only while test bot support is getting sorted out but this issue would need to be resolved before an RC.
Comment #56
bzrudi71 commentedOkay, let's move forward here to finally make SchemaTest pass, rerolled patch attached (doesn't apply any more). I did local testing of the patch and it shows 1 fail and 1 exception. The fail is because of Index renaming and this is because ensureIdentifiersLength() does stupid double imploding of '__' and adding the idx suffix again, so the new index results in something like this:
simpletest133555test_table2___test_field__idx__idxAnd then there is an new exception we need to take care of, because of:
Drupal\Core\Database\DatabaseExceptionWrapper: SQLSTATE[42P16]: Invalid table definition: 7 ERROR: multiple primary keys for table "simpletest133555test_table" are not allowed: ALTER TABLE {test_table} ADD PRIMARY KEY ("id"); Array ( ) in db_add_primary_key() (line 824 of /opt/local/apache2/htdocs/drupal8/core/includes/database.inc).Comment #57
bzrudi71 commentedThis one should fix the the failing index rename test by cleaning up the index name before calling ensureIdentifiersLength() to prevent wrong index names.
Comment #58
bzrudi71 commentedLet preg_match() do the index name clean up...
Comment #59
andypostLet's test patches
Comment #63
bzrudi71 commentedI just spend an hour to make index renaming in renameTable() a bit smarter, by something like this:
But this still fails (tested with the file->FileFieldValidateTest()). I also discovered while working on this, that we create pkeys not through ensureIdentifiersLength(). Usually this is not a problem, because pkeys usually do not exceed the 63chars limit, but that leads to more problems here, because they are named table_pkey, but it must read table__pkey. Please note the double underscore instead of the single one! Would be happy if anyone could pick up this issue, as I'm busy over the next week ;-)
Comment #64
bzrudi71 commentedGreat, I can get file->FileFieldValidateTest() and others pass now, but currently only due some hacks around pkey (please see #63 for details). So to move forward,
Feel free to pick up, I'm busy these days ;-)
Comment #65
bzrudi71 commentedOkay, spend another hour the last night on this. This patch passes all schema test on PostgreSQL now, and seems to address the issues discovered in #2425127: Prevent PostgreSQL from creating duplicated index names within schema. There is still an issue for now because pkeys are currently prefixed by four underscores (instead of two), but this could be done in a follow up #2426579: Change method name for pgsql driver's ensureIdentifiersLength() to accurately describe what the method is doing. Now I'm interested if we have schema test pass for MySQL too, so setting to needs review.
Comment #67
bzrudi71 commentedThis should pass now on MySQL also, let's see...
Comment #68
bzrudi71 commentedComment #69
jaredsmith commentedI have reviewed this patch, and it seems straightforward to me. It certainly helps PostgreSQL pass more of the failing simpletests, and doesn't seem to trigger any additional failures.
I'm almost ready to mark it RTBC, but I want to make sure we've come to a decision about whether to fix the underscores prefixing the pkey, or to make that part of another issue. Either way, I don't want it to fall through the cracks.
Comment #70
mradcliffeSmall coding standard fix here to put a space between control functions and opening parentheses.
Is it possible that $new_name can be passed straight into the ALTER query here? It's not a vulnerability since it's in code. I'm not sure if there's a module that provides an interface for renaming tables. I don't think it's a big deal, but something to think about. db_rename_table('node', '"; delete from users') ?
Comment #71
jaredsmith commentedI agree with @mradcliffe here -- the use of parameterized queries (placeholders) would make me a lot more comfortable here than just concatenating values together to form a SQL statement, and help us avoid SQL injection.
See the "Placeholders" section of https://www.drupal.org/node/310072 for some great examples.
Comment #72
bzrudi71 commentedThanks for the review! The patch attached address the syntax fix from #70. Regarding the placeholders, I already tried this but with no luck. This is because all placeholder values get escaped by a single quote only, e.g. 'value'. All my attempt to add double quotes fail, they are always removed. While this is not a problem for usual values in queries, it is a problem in our case here:
Open for suggestions :-)
Comment #73
larowlanWe should be doing strict checking here - strpos($key, '.') === FALSE
do we need to preg_quote $index_type?
Comment #74
bzrudi71 commentedUpdate with suggestions from @larowlan, thanks!
Comment #75
bzrudi71 commentedIssue summary update.
Comment #76
mradcliffeThere's only one module that could possibly cause data loss, and that's (ironically) data module via data_ui, but the module does not allow table renaming so it's a non-issue. I think looking into adding escaping could be done later after the escape methods are built out in the other issue.
Patch looks good to me too.
Comment #77
jaredsmith commentedI have added some additional safety checks around table and schema names, to help avoid the problem of unescaped table and schema names being simply concatenated to form SQL queries. After further reading, it looks like placeholders should *not* be used for table and schema names, and instead we should use db_escape_table() before using a table or schema name in a query.
Comment #78
mradcliffeIf that's the route to take, then there's a method on the Connection class to escape tables: escapeTable.
I don't see this being done in any other Core driver for alter queries. I'm trying to figure out why to see if this is necessary or not. So far this has me digging into PDO C code. :(
Comment #79
jaredsmith commentedRight... db_escape_table calls that same method.
As mentioned on another issue (which I can't seem to find at the moment), the risk seems to me to be limited to code, but there's a risk that someone passes "foo; drop table system" as the table name, or something similarly nefarious. I'd rather make sure that something like that can't happen. My patch might have gone a bit overboard (as it seems to be causing additional test failures, booooo....), but I'd rather err on the side of caution, personally. I'd love to hear from other developers on whether they think I'm being too paranoid.
Comment #80
bzrudi71 commented+1 for improved security, but this smells like a follow up to me, especially if we open new fails with that approach. Based on the comments from @mradcliffe in #78 no other driver for now takes care of this additional security improvement. Would be happy if we can agree on a follow up based on the interdiff in #77 and get this one in as is for now?
Comment #81
jaredsmith commentedOK, fair enough... I'm re-uploading the patch from comment 74 and marking it as RTBC, as I've re-reviewed it thoroughly and think it's ready for commit. I'll then go back and double-check if we really need to escape the table names (as I had attempted to do in comment 76), and create a follow-up issue if that's really necessary.
Comment #82
alexpottThe changes to the test fail on SQLite. SQLite does not create an index for primary keys. Please try to run all changed db tests on SQLite too - for info on how and what currently fails read #2318191: [meta] Database tests fail on SQLite.
Comment #83
webchickAwesome work, PostgreSQL Brigade! :)
Committed and pushed to 8.0.x. Thanks!
Moving for backport. I believe PostgreSQL issues are not critical in D7, so downgrading accordingly.
Comment #85
bzrudi71 commentedGreat to see this one committed right after my vacation, thanks! So I think #2425127: Prevent PostgreSQL from creating duplicated index names within schema and #2426581: Fix schema generators to prevent creating duplicate table, indexes, and other constraints so that database drivers do not need to implement their own solutions can now be set closed/fixed?
Comment #91
amateescu commentedNote that the patch that was committed in #83 failed to consider the case when the table name combined with the primary key column name exceeds 63 chars. Opened a followup issue to address this: #3026290: PostgreSQL constraints are still not renamed properly on table renames.
Also, I think it's time to mark this issue "fixed" in 8.x. If anyone wants to backport this patch and the one from the issue mentioned above to 7.x, please open a new issue :)