Problem/Motivation

There are several issues related to primary keys across the 3 database drivers:

  1. Dropping a (schema) field that is part of a composite primary key throws an SQL exception on MariaDB >10.2.8. When dropping a field named id, for example:

    Key column 'id' does not exist in table

    Even though the field clearly does exist. https://mariadb.com/kb/en/library/alter-table explains this in more detail.

  2. When dropping a (schema) field that is part of a composite primary key, MySQL and our SQLite driver implementation remove the field from the primary key but leave the reamining primary key in place. This can leave the table in an invalid state. PostgreSQL on the other hand automatically drops the primary key entirely, which is the safer behavior.
  3. When renaming a field that is part of the primary key via Schema::changeField() our SQLite driver does not update the primary key definition accordingly so that the rename fails. This is because Schema::mapKeyDefinition() makes an incorrect assumption about the $mapping variable; it looks up a value by key, but it must look up a key by value.

Proposed resolution

  1. See 2.
  2. When dropping a field on any of the by Drupal supported databases (MySQL, PostgreSQL and SQLite), check whether it is part of a composite primary key, and - if so - drop the primary key - and do not recreate it afterwards.
  3. Fix SQLite's Schema::mapKeyDefinition() so that renaming a primary key field works on SQLite.
CommentFileSizeAuthor
#47 2974722-47.patch9.13 KBtstoeckler
#47 2974722-43-47-interdiff.txt6.03 KBtstoeckler
#43 2974722-43.patch8.88 KBtstoeckler
#43 2974722-43--tests-only.patch6.43 KBtstoeckler
#43 2974722-39-43-interdiff.txt5.06 KBtstoeckler
#39 2974722-39--for-review.patch8.83 KBtstoeckler
#39 2974722-36-39--interdiff.txt2.05 KBtstoeckler
#36 2974722-36--for-robots.patch21.07 KBtstoeckler
#36 2974722-36--for-robots--tests-only.patch17.38 KBtstoeckler
#36 2974722-36--for-humans.patch10.88 KBtstoeckler
#36 2974722-30-36--interdiff.txt8.27 KBtstoeckler
#30 2974722-29--for-robots.patch20.8 KBtstoeckler
#30 2974722-29--for-robots--tests-only.patch16.87 KBtstoeckler
#30 2974722-29--for-humans.patch10.61 KBtstoeckler
#30 2974722-15-29--interdiff.txt11.48 KBtstoeckler
#18 2881522-18--tests-only.patch15.74 KBtstoeckler
#17 2974722-drop-key--for-humans.patch7.53 KBtstoeckler
#17 2974722-15--drop-key--interdiff.txt4.5 KBtstoeckler
#16 2974722-15-next-interdiff.txt948 byteststoeckler
#15 2974722-15--for-robots.patch18.13 KBtstoeckler
#15 2974722-15--for-robots--tests-only.patch11.82 KBtstoeckler
#15 2974722-15--for-humans.patch7.67 KBtstoeckler
#15 2974722-11-15-interdiff.txt13.1 KBtstoeckler
#11 2974722-11--for-robots.patch20.73 KBtstoeckler
#11 2974722-11--for-humans.patch10.26 KBtstoeckler
#11 2974722-11--for-robots--tests-only.patch14.91 KBtstoeckler
#11 2974722-9-11-interdiff.txt8.9 KBtstoeckler
#9 2974722-9.patch1.36 KBtstoeckler
#9 2974722-2-9-interdiff.txt1.75 KBtstoeckler
#2 2974722-2.patch1.17 KBtstoeckler
Support from Acquia helps fund testing for Drupal Acquia logo

Comments

tstoeckler created an issue. See original summary.

tstoeckler’s picture

Status: Active » Needs review
FileSize
1.17 KB

This works for me.

To test this, you must be running MariaDB > 10.2.8.

I tested this myself with the patch over at #2938951-45: The primary key is not correctly re-created when updating the storage definition of an identifier field, i.e. (https://www.drupal.org/files/issues/2018-05-17/2938951-45.patch) and running \Drupal\KernelTests\Core\Entity\EntitySchemaTest::testPrimaryKeyUpdate() which is contained over there.

It fails in HEAD (in fact it generates the failures described at #2841291-155: Fix NOT NULL handling in the entity storage and 'primary key' changes when updating the storage definition of an identifier field) but it passes with this patch applied.

alexpott’s picture

What happens on other dbs to the primary key? Reading https://mariadb.com/kb/en/library/alter-table/#drop-column leaves we to suspect that we need to take care of re-creating the primary key without the column.

mondrake’s picture

Would be nice to do #2881522: Add a Schema::findPrimaryKeyColumns method to remove database specific logic from test first so to get a common method that introspects the primary keys across different db platforms.

tstoeckler’s picture

Actually the primary key is dropped entirely. This is tested by #2938951: The primary key is not correctly re-created when updating the storage definition of an identifier field. I just queued tests on PostgreSQL and SQLite there to make sure.

tstoeckler’s picture

Re @mondrake: I will look into that issue, thanks for pointing me there. I will let committers decide if we should block this on that. This is a bug specific to the MySQL driver, where that is a fix/improvement across all drivers. So I can see arguments either way, and I personally don't feel strongly.

alexpott’s picture

mysql> CREATE TABLE a (
    ->   a int,
    ->   b int,
    ->   primary key (a,b)
    -> );
Query OK, 0 rows affected (0.03 sec)

mysql> desc a;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| a     | int(11) | NO   | PRI | NULL    |       |
| b     | int(11) | NO   | PRI | NULL    |       |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.02 sec)

mysql> ALTER TABLE a DROP COLUMN a;
Query OK, 0 rows affected (0.04 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> desc a;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| b     | int(11) | NO   | PRI | NULL    |       |
+-------+---------+------+-----+---------+-------+
1 row in set (0.00 sec)

Testing on MySQL Server version: 5.7.19 Homebrew

tstoeckler’s picture

Status: Needs review » Needs work

Ahh, that's very interesting, thanks for setting me straight! I was wrong in #5. In #2938951-40: The primary key is not correctly re-created when updating the storage definition of an identifier field @amateescu actually dropped some of the test coverage without me noticing. Will re-add that. So that makes this a "needs work".

tstoeckler’s picture

Status: Needs work » Needs review
FileSize
1.75 KB
1.36 KB

OK, this seems to work for me with the updated patch over at #2938951: The primary key is not correctly re-created when updating the storage definition of an identifier field. This now re-creates the primary key after dropping the field.

tstoeckler’s picture

Title: Dropping a field that is part of a composite primary key fails on MariaDB 10.2.8 » [PP-1] Fix dropping of columns in a composite primary key on MariaDB >=10.2.8 and make it consistent across database drivers
Status: Needs review » Needs work
Related issues: +#2609626: mariadb-10.0 official container, +#2938951: The primary key is not correctly re-created when updating the storage definition of an identifier field

Per the latest comments in #2938951: The primary key is not correctly re-created when updating the storage definition of an identifier field I think we should expand this issue's scope so that it covers all database drivers. Also making this officially postponed on #2881522: Add a Schema::findPrimaryKeyColumns method to remove database specific logic from test.

Also adding a related issue that would allow testing with MariaDB in DrupalCI which would be awesome.

tstoeckler’s picture

Alright, so this pulls in the relevant Postgres fix from #2938951: The primary key is not correctly re-created when updating the storage definition of an identifier field (but adapted for #2881522: Add a Schema::findPrimaryKeyColumns method to remove database specific logic from test now).

It also adds some dedicated test coverage. I added this to the existing testSchemaAddField() so as not to add another test method, as the class is a bit chaotic anyway. That required to change a couple things in the existing code and also makes the code a tad less nice than it could be. If people don't like that we can also add a whole new test method instead.

This should pass on all three database drivers. The tests-only patch should fail on Postgres (and would fail on MariaDB, if we had an environment for that).

The last submitted patch, 11: 2974722-11--for-robots--tests-only.patch, failed testing. View results

The last submitted patch, 11: 2974722-11--for-humans.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 11: 2974722-11--for-robots.patch, failed testing. View results

tstoeckler’s picture

Not sure why I didn't see those failures locally. But it seems making various types of text and blob fields a primary key is not a good idea. That's not at all related to this issue, though, just an artifact of re-using the pre-existing test method. So I now went ahead and added dedicated test methods for what we want to test here.

WIth that the test looks quite a lot nicer. In fact this even allowed me to use a data provider. That in turn revealed one problem with the test because I had previously used $this->assertFalse(...) and had to switch to $this->assertEquals(FALSE, ...). Because assertFalse() does not check strict equality it was passing, even though the value returned was an empty array. So the test is now not only more readable but also more correct.

Using a data provider in the test also required me to create the table with 'primary key' => [] and this revealed that this currently actually breaks in PostgreSQL. So I fixed this as well. Since it's such an easy and simple fix, I sincerely hope that we can fix this here and don't have to split that into a separate issue. Adding a note about this to the issue summary, though, which needed an update anyway since #10.

Let's see where this leaves us. Again, unless I messed something up, the tests-only patch should fail on PostgreSQL and the actual patch should pass on all three drivers.

tstoeckler’s picture

FileSize
948 bytes

Just noticed that I forgot to update the code in MySQL's dropField() for #2881522: Add a Schema::findPrimaryKeyColumns method to remove database specific logic from test.

Here's an interdiff for that, but not re-uploading a full set of patches until they come back in the expected way.

tstoeckler’s picture

+++ a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
@@ -699,6 +699,116 @@ protected function assertFieldCharacteristics($table_name, $field_name, $field_s
+    // Drop the field and make sure the primary key was dropped, as well.

This comment is incorrect now and a leftover from the previous iteration of the test.

However, as I stated in #2938951-53: The primary key is not correctly re-created when updating the storage definition of an identifier field I think the more correct behavior would actually be to drop the primary key entirely instead of recreating a possibly broken primary key. So here's an interdiff and a review-patch for that, not sending that for a test-run either, but it does pass on all three drivers locally. I would love some feedback on that, so marking "Needs subsystem maintainer review".

tstoeckler’s picture

Ahh, I rolled the "test-only" patch incorrectly above, because I of course need to include the code - and not just the tests - from #2881522: Add a Schema::findPrimaryKeyColumns method to remove database specific logic from test. Ugh. Let's see if this is better. Not re-uploading the full patch again, as there are still some open questions here. Will re-upload the latest full patch once this is RTBC.

The last submitted patch, 15: 2974722-15--for-robots--tests-only.patch, failed testing. View results

daffie’s picture

Status: Needs review » Needs work

I have reviewed you for humans patch from comment #15. The patch looks good. I do have some remarks:

  1. +++ a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
    @@ -460,7 +460,24 @@ public function dropField($table, $field) {
    +    $primary_key = array_keys($this->connection->query('SHOW INDEX FROM {' . $table . '} WHERE key_name = ' . $this->connection->quote('PRIMARY'))->fetchAllAssoc('Column_name'));
    

    Can we use the method Schema-> findPrimaryKeyColumns() here?

  2. +++ a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
    @@ -249,7 +249,7 @@ protected function createTableSql($name, $table) {
    -    if (isset($table['primary key']) && is_array($table['primary key'])) {
    +    if (!empty($table['primary key']) && is_array($table['primary key'])) {
    

    I do not see why this change is necessary. Please explain. :)

  3. +++ a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
    @@ -606,7 +606,20 @@ public function dropField($table, $field) {
    +    if ((count($primary_key) > 1) && in_array($field, $primary_key, TRUE)) {
    +      $this->addPrimaryKey($table, array_diff($primary_key, [$field]));
    +    }
    

    Is it maybe better to calculate from the saved primary key what the new primary should be and test that to the actual primary key. Just an idea.

  4. +++ a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
    @@ -699,6 +699,116 @@ protected function assertFieldCharacteristics($table_name, $field_name, $field_s
    +  public function testSchemaCreateTablePrimaryKey(array $primary_key) {
    ...
    +    $expected = array_values(array_diff($primary_key, ['test_field']));
    

    Can we add an extra parameter to the test method. It should be the expected primary key. The test method should not calculate it.

  5. +++ a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
    @@ -699,6 +699,116 @@ protected function assertFieldCharacteristics($table_name, $field_name, $field_s
    +   * Tests adding and dropping fields that are part of the primary key.
    ...
    +  public function testSchemaAddFieldPrimaryKey($initial_primary_key, $new_primary_key) {
    

    We are not testing the dropping of fields in this method.

  6. +++ a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
    @@ -699,6 +699,116 @@ protected function assertFieldCharacteristics($table_name, $field_name, $field_s
    +    $field_spec = ['type' => 'int'];
    ...
    +        'other_test_field' => $field_spec,
    ...
    +    $schema->addField($table_name, 'test_field', $field_spec, ['primary key' => $new_primary_key]);
    

    I think that the readability improves if we remove the variable $field_spec and just put ['type' => 'int'] in its place.

  7. +++ a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
    @@ -699,6 +699,116 @@ protected function assertFieldCharacteristics($table_name, $field_name, $field_s
    +    $connection = Database::getConnection();
    +    $schema = $connection->schema();
    ...
    +    $connection = Database::getConnection();
    +    $schema = $connection->schema();
    

    Can we replace these two lines with:

        /** @var \Drupal\Core\Database\Schema $schema */
        $schema = Database::getConnection()->schema();
    

    .
    The variable $connection is never used.

  8. +++ a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
    @@ -699,6 +699,116 @@ protected function assertFieldCharacteristics($table_name, $field_name, $field_s
    +  public function testSchemaCreateTablePrimaryKey(array $primary_key) {
    ...
    +  public function testSchemaAddFieldPrimaryKey($initial_primary_key, $new_primary_key) {
    

    Can we add: @covers ::findPrimaryKeyColumns

  9. I am missing testing with the method Schema->changeField().
tstoeckler’s picture

Status: Needs work » Needs review

Thanks for the excellent review, great points! Do you have any opinion on #17?

Some feedback in detail:

  1. Yes, will fix that, see #16
  2. See the third paragraph in #15 or the third item in the issue summary
  3. Sorry, I don't understand this point. Can you elaborate?
  4. Leaving as is for now, as #17 may make that unnecessary (as the expected state will always be an empty array then). If we stick with the approach in #15, I will fix this, though
  5. Will fix
  6. Sure, will fix
  7. Good catch, that was a leftover from the previous version of the test.
  8. Sure
  9. OK, will look into that

Still waiting for the new tests-only patch to come back before doing another round of patches.

tstoeckler’s picture

Status: Needs review » Needs work

Oops, status change was unintentional

daffie’s picture

@tstoeckler:

For #21.2 Can you add a comment to the patch.

For #21.3 My idea was that it would maybe better use the saved primary key and use it to calculate what the new paimary key should be.
Then after the column drop test if the primary key is the same as the expected primary key. If not then update the primary key.

daffie’s picture

I think the more correct behavior would actually be to drop the primary key entirely instead of recreating a possibly broken primary key.

The big question for me is what other developers expect when they use the functionality. My idea is that they will not expect that the primary key will be dropped. I think that we should be as helpful to them as we can be, therefor update the primary key.

We do not have a subsystem maintainer any more since @crell left.

tstoeckler’s picture

Re #21.2: I could add something like: "Ignore an explicitly specified empty array as primary key." or something like that, but I'm not really sure that adds much value?! The similar code in e.g. the MySQL driver also does not have a comment.

Re #21.3: We already do use the saved primary key. Or maybe you mean something else by that than I? And after the column drop there will be no primary key, because it will have been dropped. Sorry, I still don't think I really understand what you are getting at, maybe you can explain what you mean with a code sample?

daffie’s picture

The requested code example:

  $primary_key = $this->findPrimaryKeyColumns($table);

  $this->connection->query('ALTER TABLE {' . $table . '} DROP COLUMN "' . $field . '"');

  $expected_primary_key = array_values(array_diff($primary_key, [$field]));

  if ($expected_primary_key != $this->findPrimaryKeyColumns($table)) {
    $this->addPrimaryKey($table, $expected_primary_key);
  }

I am not saying that my solution is better. It is just an idea.

daffie’s picture

For #21.2: After some thinking I come to the conclusion that if we want to solve correctly then we also need testing for it. And therefor it needs its own issue. The problem is that it is very subtle change. I know that I would easily mix it up. Sorry :(

tstoeckler’s picture

Re #26: Hmm... to be honest, I like my version better, as it avoids a needless round-trip to the database. (Not that this makes a difference performance wise, I just think stating up-front what we know about the behavior of the database sever makes it more clear.) I'll let a third person be the judge before changing it.

Re #27: But we do have explicit test coverage of this! The test added here fails on PostgreSQL without that. And it is explicitly a test about primary keys upon table creation, so it's not "tested by accident". So I disagree on opening another issue. Again, maybe we can get another opinion on this or maybe you can clarify your reasoning a bit more.

tstoeckler’s picture

Status: Needs work » Needs review

Alright, here's an updated version to get the non-controversial stuff out of the way.

Unfortunately adding test coverage for changeField() uncovered two more bugs one for PostgreSQL one for SQLite. I think that really strengthens my case that it doesn't make sense to split this issue up. We do not commit bug fixed without test coverage. However, to provide proper test coverage across all drivers, fixing multiple things at once is necessary without introducing hacks or workarounds to the tests.

What I would be fine with would be to disable certain parts of the test for certain database drivers and to fix those issues in separate issues that are then postponed on this one. That would keep the hack/workaround in a minimal way and would allow us to proceed here more or less undisturbed. What I - again - object to is blocking this issue on an ever-increasing list of bugs all across the three database drivers. This is already postponed on another issue and it is currently blocking a very important issue that is itself the base of numerous fixes in the entity storage system.

amateescu’s picture

The patch looks great to me! I love the fact that we're normalizing this behavior across all db drivers :)

  1. +++ a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
    @@ -460,7 +460,24 @@ public function dropField($table, $field) {
    +    // this recreation for compatibility. See
    +    // https://mariadb.com/kb/en/library/alter-table for more information.
    
    +++ a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
    @@ -606,7 +606,20 @@ public function dropField($table, $field) {
    +    // case. See
    +    // https://www.postgresql.org/docs/current/static/sql-altertable.html for
    +    // more information.
    

    I would change these to only @see URL on the next comment line, without the "see ... for more information." part.

  2. +++ a/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
    @@ -700,9 +700,107 @@ protected function assertFieldCharacteristics($table_name, $field_name, $field_s
    +   * @covers ::findPrimaryKeyColumns
    

    We should mention that we're also testing ::addField, ::changeField and ::dropField here.

tstoeckler’s picture

Status: Needs review » Needs work

Thanks @amateescu, will fix your comments in the next round. In the meantime: do you have any opinion on #17?

mondrake’s picture

My 2 cents... sorry I did not go through the entire issue, comment based on the IS.

IMHO when you are dropping a field that is part of the primary key, most likely you are in a transient situation where you'll probably be doing more changes to the table (adding new fields for example) before you'll re-create the primary key itself. However, I do not think we can assume that in the atomic dropField method. Dropping and recreating the primary key at that point is more or less an assumption; the calling code should be in charge of identifying if a field is part of the pk, drop it, drop the field, do more stuff and eventually create the new pk. I believe the right thing to do in dropField would be to throw an exception if you are trying to drop a field that is part of the PK - and avoid the risk of ending up with a wrong pk. Do not know how badly this will break things, though :(

amateescu’s picture

I just read through the documentation linked in the patch and my opinion is that PostgreSQL's and MariaDB's >= 10.2.8 behavior makes sense: after dropping a field that is part of a composite primary key, we have no idea if the resulting primary key without that field is still correct for the table or not.

A few examples in core also support this conclusion. For example, dropping the langcode field from an entity revision data table makes the resulting primary key (id + revision_id) unusable. Same for all the other entity tables with composite primary keys.

So I think it's fair to leave it up to the "application"/caller to decide and act upon whether they want to re-instate the primary key with the remaining columns or not.

amateescu’s picture

Great to see that @mondrake has the same opinion :)

As for the choice between dropping the primary key automatically in dropField() (PostgreSQL's behavior) and raising an exception (MariaDB' >= 10.2.8 behavior), I'm afraid we don't have much choice because of the BC implications, so we can realistically only choose the first option and drop the PK automatically.

tstoeckler’s picture

OK, went ahead and updated the patch accordingly, then. It seems only @daffie is of a different opinion. Leaving the "Needs framework manager review" tag, though, to get some more opinions on this.

I added a couple of lines to the test to re-gain some part of the test coverage we were otherwise losing due to the automatically dropped primary key. Also fixed #31.

And lastly fixed the test failure in ##3. I'm not sure why I'm not seeing that utf8mb4 index length issue locally, but can't be bothered to figure that out right now. The test should hopefully be green, though.

The last submitted patch, 36: 2974722-36--for-robots--tests-only.patch, failed testing. View results

daffie’s picture

Issue summary: View changes
Status: Needs review » Needs work

The patch looks good to me.

I have one remark left. There is no testing for an empty primary key. In the issue summary we state that we will allow the creation of tables without a primary key. After which the patch is RTBC for me.

For the dropping of a combined primary key, the argument of:

As for the choice between dropping the primary key automatically in dropField() (PostgreSQL's behavior) and raising an exception (MariaDB' >= 10.2.8 behavior), I'm afraid we don't have much choice because of the BC implications, so we can realistically only choose the first option and drop the PK automatically.

I for me a great argument to change my position on the issue.

I am not is a position to manual test this patch with MariaDB's >= 10.2.8. It would be great if somebody else could do that.

tstoeckler’s picture

Thanks @daffie, that's great news!

You're right that the issue summary was not quite up to date, fixed that. The tests-only patch above proves that the PostgreSQL fixes are not strictly necessary here, so I split them into #2976493: Creating a table with an explicitly empty primary key is broken on PostgreSQL and #2976495: $fixnull behavior is broken on PostgreSQL's ::addField(). That should alleviate your concern in #38, I think. What the tests-only patch also proves is that the SQLite fix is necessary, adding a description of that to the issue summary now.

Not providing a patch for the testbot now, as #36 already shows the correct test-fail scenario, this just removes some hunks from PostgreSQL driver.

daffie’s picture

Thanks @tstoeckler: great work from you.

Now we have to wait on #2881522: Add a Schema::findPrimaryKeyColumns method to remove database specific logic from test.

Also we have to wait for somebody who can manual test this patch with MariaDB's >= 10.2.8.

For the rest is the patch RTBC for me.

daffie’s picture

Title: [PP-1] Fix dropping of columns in a composite primary key on MariaDB >=10.2.8 and make it consistent across database drivers » Fix dropping of columns in a composite primary key on MariaDB >=10.2.8 and make it consistent across database drivers
daffie’s picture

Status: Needs review » Needs work
Issue tags: +Needs reroll

The last submitted patch, 43: 2974722-43--tests-only.patch, failed testing. View results

daffie’s picture

Looks good to me.
The test only patch show that there is a bug and the full patch fixes that bug.
The code for this patch comes from is parent issue: #2881522: Add a Schema::findPrimaryKeyColumns method to remove database specific logic from test
For me there is enough testing for the problem.

What is mis is for somebody to manual test this patch with MariaDB's >= 10.2.8.

For the rest is the patch RTBC for me.

alexpott’s picture

  1. +++ b/core/tests/Drupal/KernelTests/Core/Database/SchemaTest.php
    @@ -704,9 +704,119 @@ protected function assertFieldCharacteristics($table_name, $field_name, $field_s
    +    $method = new \ReflectionMethod(get_class($schema), 'findPrimaryKeyColumns');
    

    Let's use a more descriptive name than $method as that will make the test more readable.

  2. +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php
    @@ -633,8 +635,8 @@ protected function mapKeyDefinition(array $key_definition, array $mapping) {
    -      if (isset($mapping[$field])) {
    -        $field = $mapping[$field];
    +      if (in_array($field, $mapping, TRUE)) {
    +        $field = array_search($field, $mapping);
           }
    

    There's no need to do in_array() followed by array_search(). We can do something like:

          $mapped_field = array_search($field, $mapping, TRUE);
          if ($mapped_field !== FALSE) {
            $field = $mapped_field;
          }
    

    Also it would be great is there was documentation about what the mapping is. As far as I can see its a list that maps [new] => [old]. Where the keys are the new field names and the values are the old field names.

    What's really strange is the code that creates the mapping..

        // Map the old field to the new field.
        if ($field != $field_new) {
          $mapping[$field_new] = $field;
        }
        else {
          $mapping = [];
        }
    

    Er... no... you've mapped the new field to the old field!

    In fact it is almost tempting to fix the caller here to do what it says and do:

        // Map the old field to the new field.
        if ($field != $field_new) {
          $mapping[$field] = $field_new;
        }
        else {
          $mapping = [];
        }
    

    I.e. swap $field and $field_new.

tstoeckler’s picture

Thanks fore the review, this fixes #46.

Re:

In fact it is almost tempting to fix the caller here to do what it says and do:

    // Map the old field to the new field.
    if ($field != $field_new) {
      $mapping[$field] = $field_new;
    }
    else {
      $mapping = [];
    }

I.e. swap $field and $field_new.

I had tried that initially, but I don't think it's possible - or at least not easily possible - because $mapping is also passed further below into ::alterTable(). Looking into that you see that the values of $mapping can in fact be arrays, so we can't just swap that. The array values are not used in ::changeField() but ::alterTable() is called from various other places. So I don't think we can do that change here.

amateescu’s picture

Status: Needs review » Reviewed & tested by the community
Issue tags: -Needs manual testing, -Needs framework manager review

The framework manager's review has been addressed, so removing that tag. The patch has also been manually tested by @tstoeckler on MariaDB >= 10.2.8.

Can't find anything else to point out, not even nitpicks :)

hchonov’s picture

I was asked by @tstoeckler to execute the test \Drupal\KernelTests\Core\Database\SchemaTest::testSchemaChangePrimaryKey() on my system, which runs with MariaDB 10.3.7. I confirm that the test passes. Great work! +1 RTBC.

alexpott’s picture

Crediting reviewers whose reviews affected the patch.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed 8faf0fa and pushed to 8.6.x. Thanks!

We can only do this in 8.6.x because #2881522: Add a Schema::findPrimaryKeyColumns method to remove database specific logic from test was an 8.6.x only change.

  • alexpott committed 8faf0fa on 8.6.x
    Issue #2974722 by tstoeckler, daffie, amateescu, alexpott, mondrake: Fix...
tacituseu’s picture

This introduced intermittent test failure on PostgreSQL 9.1
https://www.drupal.org/pift-ci-job/986641
https://www.drupal.org/pift-ci-job/991466

Status: Fixed » Closed (fixed)

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

tacituseu’s picture