Executive summary: PostgreSQL and SQLite use a naming strategy for indexes and other constraints that can lead to collisions between indexes of two tables or even, on SQLite, between the name of a table and the name of an index.
Context: PostgreSQL and SQLite share that, contrary to MySQL, the have a flat naming space for indexes and other constraints: the name of those objects is not namespaced to the table they are attached to, but it is global. As a consequence, we are prefixing those objects with the name of their table, using the following strategy:
- Indexes:
[table name]_[index name]_idxon PostgreSQL,[table name]_[index name]on SQLite - Primary keys:
[table name]_[index name]_pkeyon PostgreSQL (not named on SQLite) - Constraints:
[table name]_[index name]_keyon PostgreSQL (non relevant on SQLite)
The problem arise when you have two tables sharing a common prefix: if an index named {test}.table_name will conflict with an index named {test_table}.name. In addition, on SQLite, the indexes and table names share the same namespace. As a consequence, a table named {test_table} will fail to be created if an index named {test}.table already exists.
This happens. On Drupal Commerce, for example, we have a table named commerce_product_type and an index named type on the commerce_product table.
Solution: We need to use a different separator then the underscore for the name of indexes, constraints and primary keys.
| Comment | File | Size | Author |
|---|---|---|---|
| #18 | drupal-1008128-pgsql-index-names.patch | 14.23 KB | steven jones |
| #14 | drupal-1008128-pgsql-index-names.patch | 14.16 KB | steven jones |
| #12 | drupal-1008128-pgsql-index-names.patch | 15.03 KB | steven jones |
| #6 | 1008128-index-constraint-names-2.patch | 15.06 KB | googletorp |
| #2 | 1008128-index-constraint-names.patch | 14.97 KB | damien tournoud |
Comments
Comment #1
webchickThis sounds like a bad bug, but doesn't need to block release. (But I'm happy to commit a fix when it's ready.)
Comment #2
damien tournoud commentedFirst shot at this.
Completely untested on PostgreSQL
Comment #3
damien tournoud commentedWe need to figure out the upgrade path for this... system_update_7070() is just a shot in the dark and is simply wrong (it will change the schema of all the tables before they are properly updated).
Comment #6
googletorp commentedI altered your patch Damien and took in the concept from the patch I made in #1009828: constraintExists doesn't work with multiple schemas.
From what I can tell schemaname isn't available in pg_constraint like it is in pg_index. That is the case in Postgres 9.x. I only altered that part in the constraint check, and do the schema check by joining pg_namespace, which should fix this.
We should make a test case for this by testing adding and deleting indexes, primary keys ect on a database with two identical schemas. I'm not familiar with the db backend, so I'm not sure if this should be done with SQL, or if Drupal can help us to do this.
Comment #7
Crell commentedI'm confused. Why couldn't we just do something like __ instead of _, on the assumption that no sane person would ever have a double-underscore in a table or field name? Wouldn't that resolve the collision? (It wouldn't help with the upgrade path but one thing at a time.)
It sounds like this has been an issue with Postgres before; we just haven't run into it.
Comment #8
Crell commentedAnd actually it seems this is closely related to #998898: Make sure that the identifiers are not more the 63 characters on PostgreSQL, is it not? Wouldn't a proper solution handle both issues?
Comment #9
damien tournoud commentedThis is exactly what the patches in this issue do. What are you confused about?
The hard part is definitely not changing the separator we use, but figuring out the upgrade path. Introducing in-database upgrades is the job of #1010188: Add support for database-driver updates.
Comment #10
josh waihi commentedWhy is the new function called getTableObjectName? A string is passed as its only argument and it returns an array. I dont' see why it needs the word "Object" in there.
Also there are a few places where the function is still used inline with a string like " some text " . $this->getTableObjectName($table) . "...". Unless this is one of those horrible objects that allow you to use array syntax in PHP, this will print "Array" in the string.
Comment #11
steven jones commentedThis needs changing as the return value of the method is now an array.
Would this be better named as: getTableOrObjectName?
Also, the documentation for the function needs updating to indicate the return value has changed.
Powered by Dreditor.
Comment #12
steven jones commentedLike this.
Comment #13
steven jones commentedYeah, needs work.
Comment #14
steven jones commentedAnother go at this. Just cleaning up the work of others in this issue, Damien Tournoud's point still holds:
I don't have access to a machine that can run all the pgsql tests in a sensible amount of time, but I've run the database tests and there's a single failure in the 'Select tests, subqueries', and I've not run this on sqlite at all either.
Failure is:
Fetched name is correct using EXISTS query.But I'm not sure if that is because my setup is bad or otherwise :(
I've removed the test for creating a table with zero columns, as that doesn't actually fail on Postgres, i.e. you can create a table with zero columns. I would suggest that if we want to test for that we should throw the exception in the create_table chain rather than just letting PDO do it for us, if we want to enforce such a restriction on Drupal created tables.
Comment #16
josh waihi commentedgetTableOrObjectName is not a good method name. The function only ever returns an array so just cast the array to an object. Why does the name of the function even need to change?
Comment #17
steven jones commentedFunction names shouldn't normally indicate the return value of the function, the doxygen does.
The method gets the name of a database Table or Object (constraints in the case of pgsql), hence the name. Though I'm not sure it does have much to do with tables, so maybe that can be changed.
Comment #18
steven jones commentedSo the test was failing because the auto_increment field wasn't involved in an index. So these tests should now pass on MySQL.
Comment #19
steven jones commentedThis issue is related to #998898: Make sure that the identifiers are not more the 63 characters on PostgreSQL.
I wonder if this issue is the better place to solve index length issues in PostgreSQL, instead of that one?
Comment #20
josh waihi commentedNice patch.
Maybe change this so that people don't get worried that they are going to lose data.
Other than that, I'm happy to RTBC it.
Comment #21
steven jones commentedI think that functionally the patch is probably ready to go too, but there is no upgrade path at the moment.
I reckon that we need to convert (or drop/create) all indexes that we've created in the old formats into the new one. This is a total pain, and probably makes this issue dependent on #1010188: Add support for database-driver updates.
Comment #22
rfaySubscribe
Comment #23
sunIt "sounds" like this update could run "a while"...? ;)
Powered by Dreditor.
Comment #24
steven jones commentedfrom #21:
Actually, I really don't think that issue is the way forward, and yes, I think we need to drop and re-create all indexes for SQLite and PostgreSQL.
Comment #25
yesct commentedWhile I was checking out the thresholds, I was looking at this because it's a major bug report for 8.x
I'm noticing it's been a long time since this was updated.
This could probably use an issue summary update, so tagging for that. (http://drupal.org/node/1427826)
And to enable people to jump back into this, it could use someone to spell out some next steps for moving this forward and just double checking that some of the assertions are still relevant now.
Comment #26
catchThis update code isn't right. drupal_get_schema() only gets tables for currently enabled modues, and it's recommended to disable modules during major version upgrades.
Looks like this could probably be changed to a db_find_tables() though, although the null operation would have to be changed then.
Comment #27
martin107 commentedpatch no longer applies.
Comment #28
stefan.r commented@martin107 actually I don't think anyone has posted a D8 patch yet.
As mentioned in #8 and #19 though, this issue would better be solved in #998898: Make sure that the identifiers are not more the 63 characters on PostgreSQL
Comment #29
bzrudi71 commentedGuess we could fix this in #998898: Make sure that the identifiers are not more the 63 characters on PostgreSQL at least for D8 where we don't need to care about all that update path stuff and independent of SQLITE?
Comment #30
stefan.r commentedThis had been fixed in #998898: Make sure that the identifiers are not more the 63 characters on PostgreSQL
Comment #32
aaronbaumanI searched around but didn't find any documentation about the double-underscore "feature" of field API.
Is this documented anywhere?
I happen to be one of the un-sane people using double-underscore in property names, and it's making things a bit haywire.