summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Pott2015-03-19 14:12:54 (GMT)
committerAlex Pott2015-03-19 14:12:54 (GMT)
commit6e51cc1d72f4ebbe84a6cb345e32242428279c14 (patch)
treef60b8febaaa8761c989ed4b465baa752f520a571
parent23ed78ecb9c294a6259bf18681d3cd33dc1625d0 (diff)
Issue #1668644 by Island Usurper, phayes, daffie, Brandonian, gease: PostgreSQL: Impossible to change a field to serial, bigserial, or numeric
-rw-r--r--core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php20
-rw-r--r--core/modules/system/src/Tests/Database/SchemaTest.php60
2 files changed, 70 insertions, 10 deletions
diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
index 96f930e..daa27a4 100644
--- a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
+++ b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php
@@ -668,21 +668,21 @@ class Schema extends DatabaseSchema {
$spec = $this->processField($spec);
- // We need to typecast the new column to best be able to transfer the data
- // Schema_pgsql::getFieldTypeMap() will return possibilities that are not
- // 'cast-able' such as 'serial' - so they need to be casted int instead.
- if (in_array($spec['pgsql_type'], array('serial', 'bigserial', 'numeric'))) {
- $typecast = 'int';
+ // 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'], array('serial', 'bigserial'))) {
+ $field_def = 'int';
}
else {
- $typecast = $spec['pgsql_type'];
+ $field_def = $spec['pgsql_type'];
}
if (in_array($spec['pgsql_type'], array('varchar', 'character', 'text')) && isset($spec['length'])) {
- $typecast .= '(' . $spec['length'] . ')';
+ $field_def .= '(' . $spec['length'] . ')';
}
elseif (isset($spec['precision']) && isset($spec['scale'])) {
- $typecast .= '(' . $spec['precision'] . ', ' . $spec['scale'] . ')';
+ $field_def .= '(' . $spec['precision'] . ', ' . $spec['scale'] . ')';
}
// Remove old check constraints.
@@ -700,7 +700,7 @@ class Schema extends DatabaseSchema {
// the typecast does not work for conversions to bytea.
// @see http://www.postgresql.org/docs/current/static/datatype-binary.html
if ($spec['pgsql_type'] != 'bytea') {
- $this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $typecast . ' USING "' . $field . '"::' . $typecast);
+ $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.
@@ -709,7 +709,7 @@ class Schema extends DatabaseSchema {
// 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 ' . $typecast . ' USING decode(replace("' . $field . '"' . ", '\\', '\\\\'), 'escape');");
+ $this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $field_def . ' USING decode(replace("' . $field . '"' . ", '\\', '\\\\'), 'escape');");
}
}
diff --git a/core/modules/system/src/Tests/Database/SchemaTest.php b/core/modules/system/src/Tests/Database/SchemaTest.php
index f5c0c02..c3d6ae0 100644
--- a/core/modules/system/src/Tests/Database/SchemaTest.php
+++ b/core/modules/system/src/Tests/Database/SchemaTest.php
@@ -477,4 +477,64 @@ class SchemaTest extends KernelTestBase {
$this->assertEqual($field_value, $field_spec['default'], 'Default value registered.');
}
}
+
+ /**
+ * Tests changing columns between numeric types.
+ */
+ function testSchemaChangeField() {
+ $field_specs = array(
+ array('type' => 'int', 'size' => 'normal','not null' => FALSE),
+ array('type' => 'int', 'size' => 'normal', 'not null' => TRUE, 'initial' => 1, 'default' => 17),
+ array('type' => 'float', 'size' => 'normal', 'not null' => FALSE),
+ array('type' => 'float', 'size' => 'normal', 'not null' => TRUE, 'initial' => 1, 'default' => 7.3),
+ array('type' => 'numeric', 'scale' => 2, 'precision' => 10, 'not null' => FALSE),
+ array('type' => 'numeric', 'scale' => 2, 'precision' => 10, 'not null' => TRUE, 'initial' => 1, 'default' => 7),
+ );
+
+ foreach ($field_specs as $i => $old_spec) {
+ foreach ($field_specs as $j => $new_spec) {
+ if ($i === $j) {
+ // Do not change a field into itself.
+ continue;
+ }
+ $this->assertFieldChange($old_spec, $new_spec);
+ }
+ }
+ }
+
+ /**
+ * Asserts that a field can be changed from one spec to another.
+ *
+ * @param $old_spec
+ * The beginning field specification.
+ * @param $new_spec
+ * The ending field specification.
+ */
+ protected function assertFieldChange($old_spec, $new_spec) {
+ $table_name = 'test_table_' . ($this->counter++);
+ $table_spec = array(
+ 'fields' => array(
+ 'serial_column' => array('type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE),
+ 'test_field' => $old_spec,
+ ),
+ 'primary key' => array('serial_column'),
+ );
+ db_create_table($table_name, $table_spec);
+ $this->pass(format_string('Table %table created.', array('%table' => $table_name)));
+
+ // Check the characteristics of the field.
+ $this->assertFieldCharacteristics($table_name, 'test_field', $old_spec);
+
+ // Remove inserted rows.
+ db_truncate($table_name)->execute();
+
+ // Change the field.
+ db_change_field($table_name, 'test_field', 'test_field', $new_spec);
+
+ // Check the field was changed.
+ $this->assertFieldCharacteristics($table_name, 'test_field', $new_spec);
+
+ // Clean-up.
+ db_drop_table($table_name);
+ }
}