diff --git includes/database/pgsql/schema.inc includes/database/pgsql/schema.inc index afe3a56..18cebf5 100644 --- includes/database/pgsql/schema.inc +++ includes/database/pgsql/schema.inc @@ -295,6 +295,7 @@ class DatabaseSchema_pgsql extends DatabaseSchema { // Ensure the new table name does not include schema syntax. $prefixInfo = $this->getPrefixInfo($new_name); $this->connection->query('ALTER TABLE {' . $table . '} RENAME TO ' . $prefixInfo['table']); + // TODO: rename sequences (is this really necessary?) } public function dropTable($table) { @@ -471,16 +472,30 @@ class DatabaseSchema_pgsql extends DatabaseSchema { $typecast = 'int'; } + $this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" TYPE ' . $typecast . ' USING "' . $field . '"::' . $typecast); - $this->connection->query("ALTER TABLE {" . $table . "} ALTER $field SET $field_new = CAST(" . $field . "_old as " . $typecast . ")"); - - $this->addField($table, "$field_new", $spec); - - $this->dropField($table, $field . '_old'); + // If changing to a serial, create sequence. + if (in_array($map[$spec['type'] . ':' . $spec['size']], array('serial', 'bigserial'))) { + // NB. type "serial" is known to postgres, but *only* during table creation, not when altering! + $seq = "{" . $table . "}_" . $field_new . "_seq"; + $this->connection->query("CREATE SEQUENCE " . $seq); + // set sequence to maximal field value in order not to conflict with existing ids + $this->connection->query("SELECT setval('" . $seq . "', MAX(" . $field . ")) FROM {" . $table . "}"); + $this->connection->query('ALTER TABLE {' . $table . '} ALTER "' . $field . '" SET DEFAULT nextval(\'' . $seq . '\')'); + } + // TODO: if changing NOT TO BE serial any more, delete sequence and default value + + // NB. it is not possible right now to change the NULL/NOT NULL nature of fields // Rename the column if necessary. if ($field != $field_new) { - $this->connection->query('ALTER TABLE {' . $table . '} RENAME "' . $field . '" TO "' . $field_new . '_old"'); + $this->connection->query('ALTER TABLE {' . $table . '} RENAME "' . $field . '" TO "' . $field_new . '"'); + } + // TODO: rename any indexes, if necessary + + // 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)) { diff --git modules/simpletest/tests/schema.test modules/simpletest/tests/schema.test index 53e8ea7..faf6e66 100644 --- modules/simpletest/tests/schema.test +++ modules/simpletest/tests/schema.test @@ -69,13 +69,24 @@ class SchemaTestCase extends DrupalWebTestCase { // Test for created index and test for the boolean result of indexExists(). $index_exists = Database::getConnection()->schema()->indexExists('test_table', 'test_field'); $this->assertIdentical($index_exists, TRUE, t('Index created.')); + + // retype id to serial and set default value in order to allow for insertion + db_change_field('test_table', 'id', 'id2', array('type' => 'serial', 'description' => 'Changed column description.')); + db_field_set_default('test_table', 'test_field', 1); // Rename the table. db_rename_table('test_table', 'test_table2'); // Index should be renamed. $index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field'); - $this->assertTrue($index_exists, t('Index was renamed.')); + $this->assertTrue($index_exists, t('Index was renamed after table rename.')); + + // check that insert into serial column works after table rename + $this->assertTrue($this->tryInsert("test_table2", "id2", TRUE), t('Insert into serialized column after table rename succeeded.')); + + // rename and retype column + db_change_field('test_table2', 'id2', 'id', array('type' => 'int', 'not null' => TRUE)); + // NB. for pgsql, we cannot check from here that the sequence is deleted // We need the default so that we can insert after the rename. db_field_set_default('test_table2', 'test_field', 0); @@ -84,7 +95,7 @@ class SchemaTestCase extends DrupalWebTestCase { // We should have successfully inserted exactly two rows. $count = db_query('SELECT COUNT(*) FROM {test_table2}')->fetchField(); - $this->assertEqual($count, 2, t('Two fields were successfully inserted.')); + $this->assertEqual($count, 3, t('Two fields were successfully inserted.')); // Try to drop the table. db_drop_table('test_table2'); @@ -109,19 +120,46 @@ class SchemaTestCase extends DrupalWebTestCase { $this->assertTrue($this->tryInsert(), t('Insert with a serial succeeded.')); $max2 = db_query('SELECT MAX(test_serial) FROM {test_table}')->fetchField(); $this->assertTrue($max2 > $max1, t('The serial is monotone.')); + + // insert with existing test_serial value given, expect unique violation + try { + db_insert($table)->fields(array("id" => mt_rand(10, 20), "test_serial" => $max2))->execute(); + $val = TRUE; + } + catch (Exception $e) { + $val = FALSE; + } + $this->assertFalse($val, t('Unique constraint works on primary key column.')); + + // rename test_serial to test_serial2, check that index is renamed + db_change_field('test_table', 'test_serial', 'test_serial2', array()); + $index_exists = Database::getConnection()->schema()->indexExists('test_table2', 'test_field'); + $this->assertTrue($index_exists, t('Index was renamed after column rename.')); + + // TODO: howto drop test_serial's primary key? + + // TODO: insert with same id given, expect no violation + + // TODO: drop serial character, insert with no id given, expect error + + // TODO: drop null constraint, insert with no id given, check that id is null + + // TODO: add null constraint, expect error $count = db_query('SELECT COUNT(*) FROM {test_table}')->fetchField(); $this->assertEqual($count, 2, t('There were two rows.')); } - function tryInsert($table = 'test_table') { + function tryInsert($table = 'test_table', $col="id", $debug=FALSE) { try { db_insert($table) - ->fields(array('id' => mt_rand(10, 20))) + ->fields(array($col => mt_rand(10, 20))) ->execute(); return TRUE; } catch (Exception $e) { + if ($debug) + debug($e); return FALSE; } }