Verified Commit 251a9465 authored by Lee Rowlands's avatar Lee Rowlands
Browse files

Issue #3191391 by Arantxio, larowlan, _utsavsharma, daffie:...

Issue #3191391 by Arantxio, larowlan, _utsavsharma, daffie: Schema::changeField() has bug when changing regular serial field to big serial field

(cherry picked from commit 6367bb87)
parent d3608560
Loading
Loading
Loading
Loading
+33 −6
Original line number Diff line number Diff line
@@ -893,12 +893,11 @@ public function changeField($table, $field, $field_new, $spec, $new_keys = []) {
    // 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'];
    }
    $field_def = match($spec['pgsql_type']) {
      'serial' => 'int',
      'bigserial' => 'bigint',
      default => $spec['pgsql_type'],
    };

    if (in_array($spec['pgsql_type'], ['varchar', 'character', 'text']) && isset($spec['length'])) {
      $field_def .= '(' . $spec['length'] . ')';
@@ -910,6 +909,14 @@ public function changeField($table, $field, $field_new, $spec, $new_keys = []) {
    // Remove old check constraints.
    $field_info = $this->queryFieldInformation($table, $field);

    // Remove old sequence.
    $seq_name = $this->getSequenceName($table, $field);
    if (!empty($seq_name)) {
      // We need to add CASCADE otherwise we cannot alter the sequence because
      // the table depends on it.
      $this->connection->query('DROP SEQUENCE IF EXISTS ' . $seq_name . ' CASCADE');
    }

    foreach ($field_info as $check) {
      $this->connection->query('ALTER TABLE {' . $table . '} DROP CONSTRAINT [' . $check . ']');
    }
@@ -1062,6 +1069,26 @@ public function extensionExists($name): bool {
    ])->fetchField();
  }

  /**
   * Retrieves a sequence name that is owned by the table and column..
   *
   * @param string $table
   *   A table name that is not prefixed or quoted.
   * @param string $column
   *   The column name.
   *
   * @return string|null
   *   The name of the sequence or NULL if it does not exist.
   */
  protected function getSequenceName(string $table, string $column): ?string {
    return $this->connection
      ->query("SELECT pg_get_serial_sequence(:table, :column)", [
        ':table' => $this->connection->getPrefix() . $table,
        ':column' => $column,
      ])
      ->fetchField();
  }

}

/**
+42 −0
Original line number Diff line number Diff line
@@ -1308,4 +1308,46 @@ public function testReservedKeywordsForNaming(): void {
    $this->assertFalse($this->schema->tableExists($table_name_new));
  }

  /**
   * Tests changing a field length.
   */
  public function testChangeSerialFieldLength(): void {
    $specification = [
      'fields' => [
        'id' => [
          'type' => 'serial',
          'not null' => TRUE,
          'description' => 'Primary Key: Unique ID.',
        ],
        'text' => [
          'type' => 'text',
          'description' => 'A text field',
        ],
      ],
      'primary key' => ['id'],
    ];
    $this->schema->createTable('change_serial_to_big', $specification);

    // Increase the size of the field.
    $new_specification = [
      'size' => 'big',
      'type' => 'serial',
      'not null' => TRUE,
      'description' => 'Primary Key: Unique ID.',
    ];
    $this->schema->changeField('change_serial_to_big', 'id', 'id', $new_specification);
    $this->assertTrue($this->schema->fieldExists('change_serial_to_big', 'id'));

    // Test if we can actually add a big int.
    $id = $this->connection->insert('change_serial_to_big')->fields([
      'id' => 21474836470,
    ])->execute();

    $id_two = $this->connection->insert('change_serial_to_big')->fields([
      'text' => 'Testing for ID generation',
    ])->execute();

    $this->assertEquals($id + 1, $id_two);
  }

}