public function Schema::changeField in Drupal driver for SQL Server and SQL Azure 8.2
Same name and namespace in other branches
- 8 drivers/lib/Drupal/Driver/Database/sqlsrv/Schema.php \Drupal\Driver\Database\sqlsrv\Schema::changeField()
- 3.0.x drivers/lib/Drupal/Driver/Database/sqlsrv/Schema.php \Drupal\Driver\Database\sqlsrv\Schema::changeField()
Override DatabaseSchema::changeField().
@status complete
Overrides Schema::changeField
File
- drivers/
lib/ Drupal/ Driver/ Database/ sqlsrv/ Schema.php, line 778 - Definition of Drupal\Driver\Database\sqlsrv\Schema
Class
Namespace
Drupal\Driver\Database\sqlsrvCode
public function changeField($table, $field, $field_new, $spec, $new_keys = []) {
if (!$this
->fieldExists($table, $field)) {
throw new DatabaseSchemaObjectDoesNotExistException(t("Cannot change the definition of field %table.%name: field doesn't exist.", array(
'%table' => $table,
'%name' => $field,
)));
}
if ($field != $field_new && $this
->fieldExists($table, $field_new)) {
throw new DatabaseSchemaObjectExistsException(t("Cannot rename field %table.%name to %name_new: target field already exists.", array(
'%table' => $table,
'%name' => $field,
'%name_new' => $field_new,
)));
}
// SQL Server supports transactional DDL, so we can just start a transaction
// here and pray for the best.
$real_table = $this->connection
->prefixTable($table);
/** @var Transaction $transaction */
$transaction = $this->connection
->startTransaction(null, DatabaseTransactionSettings::GetDDLCompatibleDefaults());
// Prepare the specifications.
$spec = $this
->processField($spec);
// IMPORTANT NOTE: To maintain database portability, you have to explicitly recreate all indices and primary keys that are using the changed field.
// That means that you have to drop all affected keys and indexes with db_drop_{primary_key,unique_key,index}() before calling db_change_field().
// @see https://api.drupal.org/api/drupal/includes!database!database.inc/function/db_change_field/7
//
// What we are going to do in the SQL Server Driver is a best-effort try to preserve original keys if they do not conflict
// with the new_keys parameter, and if the callee has done it's job (droping constraints/keys) then they will of course not be recreated.
// Retrive the original field specification.
$original_field_spec = $this->connection
->schema()
->getColumnIntrospection($table, $field);
// Introspect the schema and save the current primary key if the column
// we are modifying is part of it. Make sure the schema is FRESH.
$this
->getTableIntrospectionInvalidate($table);
$primary_key_fields = $this
->introspectPrimaryKeyFields($table);
if (in_array($field, $primary_key_fields)) {
// Let's drop the PK
$this
->cleanUpPrimaryKey($table);
}
// If there is a generated unique key for this field, we will need to
// add it back in when we are done
$unique_key = $this
->uniqueKeyExists($table, $field);
// Drop the related objects.
$this
->dropFieldRelatedObjects($table, $field);
// Start by renaming the current column.
$this->connection
->query_direct('EXEC sp_rename :old, :new, :type', array(
':old' => "{$real_table}.{$field}",
':new' => "{$field}_old",
':type' => 'COLUMN',
));
// Remove the comment from the transaction static cache
$schema = $this
->GetDefaultSchema();
unset($this->columnComments[$schema . '.' . $table . '.' . $field]);
// Clear introspection cache
$this->connection
->Scheme()
->TableDetailsInvalidate($table);
// If the new column does not allow nulls, we need to
// create it first as nullable, then either migrate
// data from previous column or populate default values.
$fixnull = false;
if (!empty($spec['not null'])) {
$fixnull = true;
$spec['not null'] = false;
}
// Create a new field.
$this
->addField($table, $field_new, $spec);
// Conversiones between data types are not trivial...
$collation = isset($spec['sqlsrv_collation']) ? $spec['sqlsrv_collation'] : null;
$converted_expression = DatabaseUtils::convertTypes("[{$field}_old]", $original_field_spec['type'], $spec['sqlsrv_type'], $collation);
$this->connection
->query_direct("UPDATE [{$real_table}] SET [{$field_new}] = {$converted_expression}");
// Switch to NOT NULL now.
if ($fixnull === true) {
// There is no warranty that the old data did not have NULL values, we need to populate
// nulls with the default value because this won't be done by MSSQL by default.
if (isset($spec['default'])) {
$default_expression = $this->connection
->Scheme()
->DefaultValueExpression($spec['sqlsrv_type'], $spec['default']);
$this->connection
->query_direct("UPDATE [{$real_table}] SET [{$field_new}] = {$default_expression} WHERE [{$field_new}] IS NULL", [], array(
'prefix_tables' => false,
));
}
// Now it's time to make this non-nullable.
$spec['not null'] = true;
$this->connection
->query_direct("ALTER TABLE [{$real_table}] ALTER COLUMN " . $this
->createFieldSql($table, $field_new, $spec, true), [], array(
'prefix_tables' => false,
));
}
// Initialize new keys.
if (!isset($new_keys)) {
$new_keys = array(
'unique keys' => [],
'primary keys' => [],
);
}
// Recreate the primary key if no new primary key
// has been sent along with the change field.
if (in_array($field, $primary_key_fields) && (!isset($new_keys['primary keys']) || empty($new_keys['primary keys']))) {
// The new primary key needs to have
// the new column name.
unset($primary_key_fields[$field]);
$primary_key_fields[$field_new] = $field_new;
$new_keys['primary key'] = $primary_key_fields;
}
// Recreate the unique constraint if it existed.
if ($unique_key && !isset($new_keys['unique keys'])) {
$new_keys['unique keys'] = [];
}
if ($unique_key && !in_array($field_new, $new_keys['unique keys'])) {
$new_keys['unique keys'][$field] = [
$field_new,
];
}
// Drop the old field.
$this
->dropField($table, $field . '_old');
// Add the new keys.
$this
->recreateTableKeys($table, $new_keys);
// Refresh introspection for this table.
$this
->getTableIntrospectionInvalidate($table);
// Commit.
$transaction
->commit();
}