diff --git a/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php b/core/lib/Drupal/Core/Database/Driver/pgsql/Schema.php index 96f930e01f77fca56492c213b4705233f743d756..daa27a4c8c26479853d141b3745888f302df94d7 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 @@ public function changeField($table, $field, $field_new, $spec, $new_keys = array $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 @@ public function changeField($table, $field, $field_new, $spec, $new_keys = array // 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 @@ public function changeField($table, $field, $field_new, $spec, $new_keys = array // 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 f5c0c0244ff19dd01de7d15764e3ff22aee8d625..c3d6ae06e9f87c14cf3c7e444bc319802bfa8b5e 100644 --- a/core/modules/system/src/Tests/Database/SchemaTest.php +++ b/core/modules/system/src/Tests/Database/SchemaTest.php @@ -477,4 +477,64 @@ protected function assertFieldCharacteristics($table_name, $field_name, $field_s $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); + } }