You are here

public function Schema::addField in Drupal driver for SQL Server and SQL Azure 3.0.x

Same name and namespace in other branches
  1. 8.2 drivers/lib/Drupal/Driver/Database/sqlsrv/Schema.php \Drupal\Driver\Database\sqlsrv\Schema::addField()
  2. 8 drivers/lib/Drupal/Driver/Database/sqlsrv/Schema.php \Drupal\Driver\Database\sqlsrv\Schema::addField()

Add a new field to a table.

Parameters

$table: Name of the table to be altered.

$field: Name of the field to be added.

$spec: The field specification array, as taken from a schema definition. The specification may also contain the key 'initial', the newly created field will be set to the value of the key in all rows. This is most useful for creating NOT NULL columns with no default value in existing tables. Alternatively, the 'initial_form_field' key may be used, which will auto-populate the new field with values from the specified field.

$keys_new: (optional) Keys and indexes specification to be created on the table along with adding the field. The format is the same as a table specification but without the 'fields' element. If you are adding a type 'serial' field, you MUST specify at least one key or index including it in this array. See ::changeField() for more explanation why.

Throws

\Drupal\Core\Database\SchemaObjectDoesNotExistException If the specified table doesn't exist.

\Drupal\Core\Database\SchemaObjectExistsException If the specified table already has a field by that name.

Overrides Schema::addField

1 call to Schema::addField()
Schema::changeField in drivers/lib/Drupal/Driver/Database/sqlsrv/Schema.php
Change a field definition.

File

drivers/lib/Drupal/Driver/Database/sqlsrv/Schema.php, line 242

Class

Schema

Namespace

Drupal\Driver\Database\sqlsrv

Code

public function addField($table, $field, $spec, $keys_new = []) {
  if (!$this
    ->tableExists($table)) {
    throw new SchemaObjectDoesNotExistException(t("Cannot add field @table.@field: table doesn't exist.", [
      '@field' => $field,
      '@table' => $table,
    ]));
  }
  if ($this
    ->fieldExists($table, $field)) {
    throw new SchemaObjectExistsException(t("Cannot add field @table.@field: field already exists.", [
      '@field' => $field,
      '@table' => $table,
    ]));
  }

  // 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,
    ]);
  }
  $transaction = $this->connection
    ->startTransaction();

  // Prepare the specifications.
  $spec = $this
    ->processField($spec);

  // Use already prefixed table name.
  $prefixInfo = $this
    ->getPrefixInfo($table, TRUE);
  $table_prefixed = $prefixInfo['table'];
  if ($this
    ->findPrimaryKeyColumns($table) !== [] && isset($keys_new['primary key']) && in_array($field, $keys_new['primary key'])) {
    $this
      ->cleanUpPrimaryKey($table);
  }

  // If the field is declared NOT NULL, we have to first create it NULL insert
  // the initial data (or populate default values) and then switch to NOT
  // NULL.
  $fixnull = FALSE;
  if (!empty($spec['not null'])) {
    $fixnull = TRUE;
    $spec['not null'] = FALSE;
  }

  // Create the field.
  // Because the default values of fields can contain string literals
  // with braces, we CANNOT allow the driver to prefix tables because the
  // algorithm to do so is a crappy str_replace.
  $query = "ALTER TABLE {{$table}} ADD ";
  $query .= $this
    ->createFieldSql($table, $field, $spec);
  $this->connection
    ->queryDirect($query, []);
  $this
    ->resetColumnInformation($table);

  // Load the initial data.
  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)
      ->expression($field, $expression, $arguments)
      ->execute();
  }
  elseif (isset($spec['initial'])) {
    $this->connection
      ->update($table)
      ->fields([
      $field => $spec['initial'],
    ])
      ->execute();
  }

  // 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
        ->defaultValueExpression($spec['sqlsrv_type'], $spec['default']);
      $sql = "UPDATE {{$table}} SET {$field}={$default_expression} WHERE {$field} IS NULL";
      $this->connection
        ->queryDirect($sql);
    }

    // Now it's time to make this non-nullable.
    $spec['not null'] = TRUE;
    $field_sql = $this
      ->createFieldSql($table, $field, $spec, TRUE);
    $this->connection
      ->queryDirect("ALTER TABLE {{$table}} ALTER COLUMN {$field_sql}");
    $this
      ->resetColumnInformation($table);
  }
  $this
    ->recreateTableKeys($table, $keys_new);
  if (isset($spec['description'])) {
    $this->connection
      ->queryDirect($this
      ->createCommentSql($spec['description'], $table, $field));
  }
}