}
// 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";
$sql .= 'ENGINE = ' . $table['mysql_engine'] . ' DEFAULT CHARACTER SET ' . $table['mysql_character_set'];
// By default, MySQL uses the default collation for new tables, which is
- // 'utf8mb4_general_ci' for utf8mb4. If an alternate collation has been
- // set, it needs to be explicitly specified.
+ // 'utf8mb4_general_ci' (MySQL 5) or 'utf8mb4_0900_ai_ci' (MySQL 8) for
+ // utf8mb4. If an alternate collation has been set, it needs to be
+ // explicitly specified.
// @see \Drupal\Core\Database\Driver\mysql\Schema
if (!empty($info['collation'])) {
$sql .= ' COLLATE ' . $info['collation'];
if (isset($spec['length'])) {
$sql .= '(' . $spec['length'] . ')';
}
+ if (isset($spec['type']) && $spec['type'] == 'varchar_ascii') {
+ $sql .= ' CHARACTER SET ascii';
+ }
if (!empty($spec['binary'])) {
$sql .= ' BINARY';
}
// Note we check for the "type" key here. "mysql_type" is VARCHAR:
- if (isset($spec['type']) && $spec['type'] == 'varchar_ascii') {
- $sql .= ' CHARACTER SET ascii COLLATE ascii_general_ci';
+ elseif (isset($spec['type']) && $spec['type'] == 'varchar_ascii') {
+ $sql .= ' COLLATE ascii_general_ci';
}
}
elseif (isset($spec['precision']) && isset($spec['scale'])) {
// Set the correct database-engine specific datatype.
// In case one is already provided, force it to uppercase.
if (isset($field['mysql_type'])) {
- $field['mysql_type'] = Unicode::strtoupper($field['mysql_type']);
+ $field['mysql_type'] = mb_strtoupper($field['mysql_type']);
}
else {
$map = $this->getFieldTypeMap();
// 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) {
$query .= ', ADD ' . implode(', ADD ', $keys_sql);
}
$this->connection->query($query);
- if (isset($spec['initial'])) {
+ if (isset($spec['initial_from_field'])) {
+ if (isset($spec['initial'])) {
+ $expression = 'COALESCE(' . $spec['initial_from_field'] . ', :default_initial_value)';
+ $arguments = [':default_initial_value' => $spec['initial']];
+ }
+ else {
+ $expression = $spec['initial_from_field'];
+ $arguments = [];
+ }
$this->connection->update($table)
- ->fields([$field => $spec['initial']])
+ ->expression($field, $expression, $arguments)
->execute();
}
- if (isset($spec['initial_from_field'])) {
+ elseif (isset($spec['initial'])) {
$this->connection->update($table)
- ->expression($field, $spec['initial_from_field'])
+ ->fields([$field => $spec['initial']])
->execute();
}
if ($fixnull) {
return FALSE;
}
+ // When dropping a field that is part of a composite primary key MySQL
+ // automatically removes the field from the primary key, which can leave the
+ // table in an invalid state. MariaDB 10.2.8 requires explicitly dropping
+ // the primary key first for this reason. We perform this deletion
+ // explicitly which also makes the behavior on both MySQL and MariaDB
+ // consistent with PostgreSQL.
+ // @see https://mariadb.com/kb/en/library/alter-table
+ $primary_key = $this->findPrimaryKeyColumns($table);
+ if ((count($primary_key) > 1) && in_array($field, $primary_key, TRUE)) {
+ $this->dropPrimaryKey($table);
+ }
+
$this->connection->query('ALTER TABLE {' . $table . '} DROP `' . $field . '`');
return TRUE;
}
return TRUE;
}
+ /**
+ * {@inheritdoc}
+ */
+ protected function findPrimaryKeyColumns($table) {
+ if (!$this->tableExists($table)) {
+ return FALSE;
+ }
+ $result = $this->connection->query("SHOW KEYS FROM {" . $table . "} WHERE Key_name = 'PRIMARY'")->fetchAllAssoc('Column_name');
+ return array_keys($result);
+ }
+
/**
* {@inheritdoc}
*/
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)) {
$condition->condition('column_name', $column);
$condition->compile($this->connection, $this);
// Don't use {} around information_schema.columns table.
- return $this->connection->query("SELECT column_comment FROM information_schema.columns WHERE " . (string) $condition, $condition->arguments())->fetchField();
+ return $this->connection->query("SELECT column_comment as column_comment FROM information_schema.columns WHERE " . (string) $condition, $condition->arguments())->fetchField();
}
$condition->compile($this->connection, $this);
// Don't use {} around information_schema.tables table.
- $comment = $this->connection->query("SELECT table_comment FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments())->fetchField();
+ $comment = $this->connection->query("SELECT table_comment as table_comment FROM information_schema.tables WHERE " . (string) $condition, $condition->arguments())->fetchField();
// Work-around for MySQL 5.0 bug http://bugs.mysql.com/bug.php?id=11379
return preg_replace('/; InnoDB free:.*$/', '', $comment);
}