From df52d5c823725023b4a153cd48ee2210057062d0 Mon Sep 17 00:00:00 2001 From: Lee Rowlands <lee.rowlands@previousnext.com.au> Date: Sat, 1 Apr 2023 06:32:31 +1000 Subject: [PATCH] Revert "Issue #1148856 by drunken monkey, stefan.r, bzrudi71, xatoo, Ben Coleman, jyotisankar, daffie, mondrake, andypost, Damien Tournoud, alexpott: Postgres schema doesn't support keylength on a unique index" This reverts commit 5074bef03d63d224fe67557a9d536862c7daf79b. --- .../lib/Drupal/Core/Database/database.api.php | 3 - .../src/Driver/Database/pgsql/Schema.php | 14 +- .../SchemaUniquePrefixedKeysIndexTest.php | 135 ------------------ 3 files changed, 4 insertions(+), 148 deletions(-) delete mode 100644 core/tests/Drupal/KernelTests/Core/Database/SchemaUniquePrefixedKeysIndexTest.php diff --git a/core/lib/Drupal/Core/Database/database.api.php b/core/lib/Drupal/Core/Database/database.api.php index e4e5a0b9be0..ab704108873 100644 --- a/core/lib/Drupal/Core/Database/database.api.php +++ b/core/lib/Drupal/Core/Database/database.api.php @@ -340,9 +340,6 @@ use Drupal\Core\Database\Query\SelectInterface; * * A key column specifier is either a string naming a column or an array of two * elements, column name and length, specifying a prefix of the named column. - * Note that some DBMS drivers may opt to ignore the prefix length configuration - * and still use the whole field value for the key. Code should therefore not - * rely on this functionality. * * As an example, this is the schema definition for the 'users_data' table. It * shows five fields ('uid', 'module', 'name', 'value', and 'serialized'), the diff --git a/core/modules/pgsql/src/Driver/Database/pgsql/Schema.php b/core/modules/pgsql/src/Driver/Database/pgsql/Schema.php index 13f169c1518..b2fc2aa1571 100644 --- a/core/modules/pgsql/src/Driver/Database/pgsql/Schema.php +++ b/core/modules/pgsql/src/Driver/Database/pgsql/Schema.php @@ -286,11 +286,7 @@ EOD; } if (isset($table['unique keys']) && is_array($table['unique keys'])) { foreach ($table['unique keys'] as $key_name => $key) { - // Use the createPrimaryKeySql(), which already discards any prefix - // lengths passed as part of the key column specifiers. (Postgres - // doesn't support setting a prefix length for PRIMARY or UNIQUE - // indices.) - $sql_keys[] = 'CONSTRAINT ' . $this->ensureIdentifiersLength($name, $key_name, 'key') . ' UNIQUE (' . $this->createPrimaryKeySql($key) . ')'; + $sql_keys[] = 'CONSTRAINT ' . $this->ensureIdentifiersLength($name, $key_name, 'key') . ' UNIQUE (' . implode(', ', $key) . ')'; } } @@ -475,7 +471,7 @@ EOD; } /** - * Create the SQL expression for primary and unique keys. + * Create the SQL expression for primary keys. * * Postgresql does not support key length. It does support fillfactor, but * that requires a separate database lookup for each column in the key. The @@ -798,10 +794,8 @@ EOD; throw new SchemaObjectExistsException("Cannot add unique key '$name' to table '$table': unique key already exists."); } - // Use the createPrimaryKeySql(), which already discards any prefix lengths - // passed as part of the key column specifiers. (Postgres doesn't support - // setting a prefix length for PRIMARY or UNIQUE indices.) - $this->connection->query('ALTER TABLE {' . $table . '} ADD CONSTRAINT ' . $this->ensureIdentifiersLength($table, $name, 'key') . ' UNIQUE (' . $this->createPrimaryKeySql($fields) . ')'); + $fields = array_map([$this->connection, 'escapeField'], $fields); + $this->connection->query('ALTER TABLE {' . $table . '} ADD CONSTRAINT ' . $this->ensureIdentifiersLength($table, $name, 'key') . ' UNIQUE (' . implode(',', $fields) . ')'); $this->resetTableInformation($table); } diff --git a/core/tests/Drupal/KernelTests/Core/Database/SchemaUniquePrefixedKeysIndexTest.php b/core/tests/Drupal/KernelTests/Core/Database/SchemaUniquePrefixedKeysIndexTest.php deleted file mode 100644 index b21543c13cd..00000000000 --- a/core/tests/Drupal/KernelTests/Core/Database/SchemaUniquePrefixedKeysIndexTest.php +++ /dev/null @@ -1,135 +0,0 @@ -<?php - -namespace Drupal\KernelTests\Core\Database; - -use Drupal\Core\Database\DatabaseException; - -/** - * Tests adding UNIQUE keys to tables. - * - * @coversDefaultClass \Drupal\Core\Database\Schema - * - * @group Database - */ -class SchemaUniquePrefixedKeysIndexTest extends DatabaseTestBase { - - /** - * Tests UNIQUE keys put directly on the table definition. - * - * @covers ::createTable - */ - public function testCreateTable(): void { - $this->connection->schema()->createTable('test_unique', [ - 'fields' => [ - 'field' => [ - 'type' => 'varchar', - 'length' => 50, - ], - ], - 'unique keys' => [ - 'field' => [['field', 10]], - ], - ]); - - $this->checkUniqueConstraintException('test_unique', 'field'); - } - - /** - * Tests adding a UNIQUE key to an existing table. - * - * @covers ::addUniqueKey - */ - public function testAddUniqueKey(): void { - $this->connection->schema() - ->addUniqueKey('test_people', 'job', [['job', 10]]); - - $this->checkUniqueConstraintException('test_people', 'job'); - } - - /** - * Tests adding a new field with UNIQUE key. - * - * @covers ::addField - */ - public function testAddField(): void { - $field_spec = [ - 'type' => 'varchar', - 'length' => 50, - ]; - $keys_spec = [ - 'unique keys' => [ - 'field' => [['field', 10]], - ], - ]; - $this->connection->schema() - ->addField('test', 'field', $field_spec, $keys_spec); - - $this->checkUniqueConstraintException('test', 'field'); - } - - /** - * Tests changing a field to add a UNIQUE key. - * - * @covers ::changeField - */ - public function testChangeField(): void { - $field_spec = [ - 'description' => "The person's job", - 'type' => 'varchar_ascii', - 'length' => 50, - 'not null' => TRUE, - 'default' => '', - ]; - $keys_spec = [ - 'unique keys' => [ - 'job' => [['job', 10]], - ], - ]; - $this->connection->schema() - ->changeField('test_people', 'job', 'job', $field_spec, $keys_spec); - - $this->checkUniqueConstraintException('test_people', 'job'); - } - - /** - * Verifies that inserting the same value/prefix twice causes an exception. - * - * @param string $table - * The table to insert into. - * @param string $column - * The column on that table that has a UNIQUE index. If prefix lengths are - * accepted for UNIQUE keys on the current database, the prefix length for - * the field is expected to be set to 10 characters. - */ - protected function checkUniqueConstraintException(string $table, string $column): void { - $this->connection->insert($table) - ->fields([ - $column => '1234567890 foo', - ]) - ->execute(); - - $this->expectException(DatabaseException::class); - $value = '1234567890 ' . ($this->supportsPrefixLength() ? 'bar' : 'foo'); - $this->connection->insert($table) - ->fields([ - $column => $value, - ]) - ->execute(); - } - - /** - * Determines whether the current database supports prefix lengths for keys. - * - * The basic syntax of passing an array (field, prefix length) as a key column - * specifier must always be accepted by the driver. However, due to technical - * limitations, some drivers may choose to ignore them. - * - * @return bool - * TRUE if the current database (driver) will conform to the prefix length - * specified as part of a key column specifier, FALSE if it will be ignored. - */ - protected function supportsPrefixLength(): bool { - return $this->connection->driver() === 'mysql'; - } - -}