function Schema::changeField

Same name in this branch
  1. 9 core/modules/sqlite/src/Driver/Database/sqlite/Schema.php \Drupal\sqlite\Driver\Database\sqlite\Schema::changeField()
  2. 9 core/modules/mysql/src/Driver/Database/mysql/Schema.php \Drupal\mysql\Driver\Database\mysql\Schema::changeField()
  3. 9 core/tests/fixtures/database_drivers/module/corefake/src/Driver/Database/corefakeWithAllCustomClasses/Schema.php \Drupal\corefake\Driver\Database\corefakeWithAllCustomClasses\Schema::changeField()
  4. 9 core/lib/Drupal/Core/Database/Schema.php \Drupal\Core\Database\Schema::changeField()
Same name and namespace in other branches
  1. 11.x core/modules/sqlite/src/Driver/Database/sqlite/Schema.php \Drupal\sqlite\Driver\Database\sqlite\Schema::changeField()
  2. 11.x core/modules/mysql/src/Driver/Database/mysql/Schema.php \Drupal\mysql\Driver\Database\mysql\Schema::changeField()
  3. 11.x core/modules/pgsql/src/Driver/Database/pgsql/Schema.php \Drupal\pgsql\Driver\Database\pgsql\Schema::changeField()
  4. 11.x core/lib/Drupal/Core/Database/Schema.php \Drupal\Core\Database\Schema::changeField()

File

core/modules/pgsql/src/Driver/Database/pgsql/Schema.php, line 894

Class

Schema
PostgreSQL implementation of \Drupal\Core\Database\Schema.

Namespace

Drupal\pgsql\Driver\Database\pgsql

Code

public function changeField($table, $field, $field_new, $spec, $new_keys = []) {
  if (!$this->fieldExists($table, $field)) {
    throw new SchemaObjectDoesNotExistException("Cannot change the definition of field '{$table}.{$field}': field doesn't exist.");
  }
  if ($field != $field_new && $this->fieldExists($table, $field_new)) {
    throw new SchemaObjectExistsException("Cannot rename field '{$table}.{$field}' to '{$field_new}': target field already exists.");
  }
  if (isset($new_keys['primary key']) && in_array($field_new, $new_keys['primary key'], TRUE)) {
    $this->ensureNotNullPrimaryKey($new_keys['primary key'], [
      $field_new => $spec,
    ]);
  }
  $spec = $this->processField($spec);
  // Type 'serial' is known to PostgreSQL, but only during table creation,
  // not when altering. Because of that, we create it here as an 'int'. After
  // we create it we manually re-apply the sequence.
  if (in_array($spec['pgsql_type'], [
    'serial',
    'bigserial',
  ])) {
    $field_def = 'int';
  }
  else {
    $field_def = $spec['pgsql_type'];
  }
  if (in_array($spec['pgsql_type'], [
    'varchar',
    'character',
    'text',
  ]) && isset($spec['length'])) {
    $field_def .= '(' . $spec['length'] . ')';
  }
  elseif (isset($spec['precision']) && isset($spec['scale'])) {
    $field_def .= '(' . $spec['precision'] . ', ' . $spec['scale'] . ')';
  }
  // Remove old check constraints.
  $field_info = $this->queryFieldInformation($table, $field);
  foreach ($field_info as $check) {
    $this->connection
      ->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT "' . $check . '"');
  }
  // Remove old default.
  $this->connection
    ->query('ALTER TABLE {' . $table . '} ALTER COLUMN "' . $field . '" DROP DEFAULT');
  // Convert field type.
  // Usually, we do this via a simple typecast 'USING fieldname::type'. But
  // the typecast does not work for conversions to bytea.
  // @see http://www.postgresql.org/docs/current/static/datatype-binary.html
  $table_information = $this->queryTableInformation($table);
  $is_bytea = !empty($table_information->blob_fields[$field]);
  if ($spec['pgsql_type'] != 'bytea') {
    if ($is_bytea) {
      $this->connection
        ->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $field_def . ' USING convert_from("' . $field . '"' . ", 'UTF8')");
    }
    else {
      $this->connection
        ->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $field_def . ' USING "' . $field . '"::' . $field_def);
    }
  }
  else {
    // Do not attempt to convert a field that is bytea already.
    if (!$is_bytea) {
      // Convert to a bytea type by using the SQL replace() function to
      // convert any single backslashes in the field content to double
      // backslashes ('\' to '\\').
      $this->connection
        ->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $field_def . ' USING decode(replace("' . $field . '"' . ", E'\\\\', E'\\\\\\\\'), 'escape');");
    }
  }
  if (isset($spec['not null'])) {
    if ($spec['not null']) {
      $null_action = 'SET NOT NULL';
    }
    else {
      $null_action = 'DROP NOT NULL';
    }
    $this->connection
      ->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" ' . $null_action);
  }
  if (in_array($spec['pgsql_type'], [
    'serial',
    'bigserial',
  ])) {
    // Type "serial" is known to PostgreSQL, but *only* during table creation,
    // not when altering. Because of that, the sequence needs to be created
    // and initialized by hand.
    $seq = $this->connection
      ->makeSequenceName($table, $field_new);
    $this->connection
      ->query("CREATE SEQUENCE " . $seq);
    // Set sequence to maximal field value to not conflict with existing
    // entries.
    $this->connection
      ->query("SELECT setval('" . $seq . "', MAX(\"" . $field . '")) FROM {' . $table . "}");
    $this->connection
      ->query('ALTER TABLE {' . $table . '} ALTER ' . $field . ' SET DEFAULT nextval(' . $this->connection
      ->quote($seq) . ')');
  }
  // Rename the column if necessary.
  if ($field != $field_new) {
    $this->connection
      ->query('ALTER TABLE {' . $table . '} RENAME "' . $field . '" TO "' . $field_new . '"');
  }
  // Add unsigned check if necessary.
  if (!empty($spec['unsigned'])) {
    $this->connection
      ->query('ALTER TABLE {' . $table . '} ADD CHECK ("' . $field_new . '" >= 0)');
  }
  // Add default if necessary.
  if (isset($spec['default'])) {
    $this->connection
      ->query('ALTER TABLE {' . $table . '} ALTER COLUMN "' . $field_new . '" SET DEFAULT ' . $this->escapeDefaultValue($spec['default']));
  }
  // Change description if necessary.
  if (!empty($spec['description'])) {
    $this->connection
      ->query('COMMENT ON COLUMN {' . $table . '}."' . $field_new . '" IS ' . $this->prepareComment($spec['description']));
  }
  if (isset($new_keys)) {
    $this->_createKeys($table, $new_keys);
  }
  $this->resetTableInformation($table);
}

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.