diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php index 48a3bb2..baac2ee 100644 --- a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php +++ b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php @@ -108,6 +108,9 @@ protected function createTableSql($name, $table) { } // Process keys & indexes. + if (!empty($table['primary key']) && is_array($table['primary key'])) { + $this->ensureNotNullPrimaryKey($table['primary key'], $table['fields']); + } $keys = $this->createKeysSql($table); if (count($keys)) { $sql .= implode(", \n", $keys) . ", \n"; @@ -409,6 +412,9 @@ public function addField($table, $field, $spec, $keys_new = []) { // Fields that are part of a PRIMARY KEY must be added as NOT NULL. $is_primary_key = isset($keys_new['primary key']) && in_array($field, $keys_new['primary key'], TRUE); + if ($is_primary_key) { + $this->ensureNotNullPrimaryKey($keys_new['primary key'], [$field => $spec]); + } $fixnull = FALSE; if (!empty($spec['not null']) && !isset($spec['default']) && !$is_primary_key) { @@ -610,6 +616,9 @@ public function changeField($table, $field, $field_new, $spec, $keys_new = []) { if (($field != $field_new) && $this->fieldExists($table, $field_new)) { throw new SchemaObjectExistsException(t("Cannot rename field @table.@name to @name_new: target field already exists.", ['@table' => $table, '@name' => $field, '@name_new' => $field_new])); } + if (isset($keys_new['primary key']) && in_array($field_new, $keys_new['primary key'], TRUE)) { + $this->ensureNotNullPrimaryKey($keys_new['primary key'], [$field_new => $spec]); + } $sql = 'ALTER TABLE {' . $table . '} CHANGE `' . $field . '` ' . $this->createFieldSql($field_new, $this->processField($spec)); if ($keys_sql = $this->createKeysSql($keys_new)) { diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php index 1aec2d7..88c3905 100644 --- a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php +++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php @@ -250,6 +250,7 @@ protected function createTableSql($name, $table) { $sql_keys = []; if (!empty($table['primary key']) && is_array($table['primary key'])) { + $this->ensureNotNullPrimaryKey($table['primary key'], $table['fields']); $sql_keys[] = 'CONSTRAINT ' . $this->ensureIdentifiersLength($name, '', 'pkey') . ' PRIMARY KEY (' . $this->createPrimaryKeySql($table['primary key']) . ')'; } if (isset($table['unique keys']) && is_array($table['unique keys'])) { @@ -551,7 +552,10 @@ public function addField($table, $field, $spec, $new_keys = []) { } // Fields that are part of a PRIMARY KEY must be added as NOT NULL. - $is_primary_key = isset($keys_new['primary key']) && in_array($field, $keys_new['primary key'], TRUE); + $is_primary_key = isset($new_keys['primary key']) && in_array($field, $new_keys['primary key'], TRUE); + if ($is_primary_key) { + $this->ensureNotNullPrimaryKey($new_keys['primary key'], [$field => $spec]); + } $fixnull = FALSE; if (!empty($spec['not null']) && !isset($spec['default']) && !$is_primary_key) { @@ -804,6 +808,9 @@ public function changeField($table, $field, $field_new, $spec, $new_keys = []) { if (($field != $field_new) && $this->fieldExists($table, $field_new)) { throw new SchemaObjectExistsException(t("Cannot rename field @table.@name to @name_new: target field already exists.", ['@table' => $table, '@name' => $field, '@name_new' => $field_new])); } + if (isset($keys_new['primary key']) && in_array($field_new, $new_keys['primary key'], TRUE)) { + $this->ensureNotNullPrimaryKey($new_keys['primary key'], [$field_new => $spec]); + } $spec = $this->processField($spec); diff --git a/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php b/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php index ad2ab2d..82e1831 100644 --- a/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php +++ b/core/lib/Drupal/Core/Database/Driver/sqlite/Schema.php @@ -52,6 +52,10 @@ public function fieldExists($table, $column) { * An array of SQL statements to create the table. */ public function createTableSql($name, $table) { + if (!empty($table['primary key']) && is_array($table['primary key'])) { + $this->ensureNotNullPrimaryKey($table['primary key'], $table['fields']); + } + $sql = []; $sql[] = "CREATE TABLE {" . $name . "} (\n" . $this->createColumnsSql($name, $table) . "\n)\n"; return array_merge($sql, $this->createIndexSql($name, $table)); @@ -315,6 +319,9 @@ public function addField($table, $field, $specification, $keys_new = []) { if ($this->fieldExists($table, $field)) { throw new SchemaObjectExistsException(t("Cannot add field @table.@field: field already exists.", ['@field' => $field, '@table' => $table])); } + if (isset($keys_new['primary key']) && in_array($field, $keys_new['primary key'], TRUE)) { + $this->ensureNotNullPrimaryKey($keys_new['primary key'], [$field => $specification]); + } // SQLite doesn't have a full-featured ALTER TABLE statement. It only // supports adding new fields to a table, in some simple cases. In most @@ -584,6 +591,9 @@ public function changeField($table, $field, $field_new, $spec, $keys_new = []) { if (($field != $field_new) && $this->fieldExists($table, $field_new)) { throw new SchemaObjectExistsException(t("Cannot rename field @table.@name to @name_new: target field already exists.", ['@table' => $table, '@name' => $field, '@name_new' => $field_new])); } + if (isset($keys_new['primary key']) && in_array($field_new, $keys_new['primary key'], TRUE)) { + $this->ensureNotNullPrimaryKey($keys_new['primary key'], [$field_new => $spec]); + } $old_schema = $this->introspectSchema($table); $new_schema = $old_schema; @@ -733,6 +743,7 @@ public function addPrimaryKey($table, $fields) { } $new_schema['primary key'] = $fields; + $this->ensureNotNullPrimaryKey($new_schema['primary key'], $new_schema['fields']); $this->alterTable($table, $old_schema, $new_schema); } diff --git a/core/lib/Drupal/Core/Database/Schema.php b/core/lib/Drupal/Core/Database/Schema.php index d78a463..279b5d4 100644 --- a/core/lib/Drupal/Core/Database/Schema.php +++ b/core/lib/Drupal/Core/Database/Schema.php @@ -682,4 +682,25 @@ protected function escapeDefaultValue($value) { return is_string($value) ? $this->connection->quote($value) : $value; } + /** + * Ensures that all the primary key fields are correctly defined. + * + * @param array $primary_key + * An array containing the fields that will form the primary key of a table. + * @param array $fields + * An array containing the field specifications of the table, as per the + * schema data structure format. + * + * @throws \Drupal\Core\Database\SchemaException + * Thrown if any primary key field specification does not exist or if they + * do not define 'not null' as TRUE. + */ + protected function ensureNotNullPrimaryKey(array $primary_key, array $fields) { + foreach (array_intersect($primary_key, array_keys($fields)) as $field_name) { + if (!isset($fields[$field_name]['not null']) || $fields[$field_name]['not null'] !== TRUE) { + throw new SchemaException("The '$field_name' field specification does not define 'not null' as TRUE."); + } + } + } + }