From 9ccfc97e73d6ac27fccad61198bb92221381ad61 Mon Sep 17 00:00:00 2001 From: Marco Villegas Date: Mon, 23 Aug 2010 12:36:09 +0200 Subject: [PATCH] bug #890090: Inserting into a just created table fails when trying to set a value to a serial. --- includes/database/pgsql/database.inc | 24 ++++++++++++++++++++++++ includes/database/pgsql/query.inc | 20 ++++++++++++++++++++ modules/simpletest/tests/database_test.test | 21 +++++++++++++++++++++ 3 files changed, 65 insertions(+), 0 deletions(-) diff --git includes/database/pgsql/database.inc includes/database/pgsql/database.inc index 053bf1d..33ecfc1 100644 --- includes/database/pgsql/database.inc +++ includes/database/pgsql/database.inc @@ -188,6 +188,30 @@ class DatabaseConnection_pgsql extends DatabaseConnection { return $id; } + + /** + * Overrides PDO::lastInsertId(). + * + * Catchs and handles the exception in case currval() fails. + */ + public function lastInsertId($name = null) { + try { + return parent::lastInsertId($name); + } catch(PDOException $e) { + if ($e->getCode() == 55000) { + // This fails if a value was inserted into the serial column and + // nextval() was never called. In that case, the last_insert_id is known + // by the caller and NULL is returned. + $last_insert_id = NULL; + } + else { + // If it is a different exception, re-throw it. + throw $e; + } + } + return $last_insert_id; + } + } /** diff --git includes/database/pgsql/query.inc includes/database/pgsql/query.inc index 94f2138..67c1236 100644 --- includes/database/pgsql/query.inc +++ includes/database/pgsql/query.inc @@ -69,6 +69,26 @@ class InsertQuery_pgsql extends InsertQuery { } $last_insert_id = $this->connection->query($stmt, array(), $options); + // If a value was inserted into a serial column, update the sequence. + if (!empty($table_information->serial_fields)) { + + // If the first serial field is in the insert fields, update the + // last_insert_id to that value. + if (in_array($table_information->serial_fields[0], $this->insertFields)) { + $last_insert_id = $insert_values[array_search($table_information->serial_fields[0], $this->insertFields)]; + } + + foreach ($table_information->serial_fields as $index => $serial_field) { + if (in_array($serial_field, $this->insertFields)) { + // Load the next sequence value and the max value of the table. + $new_value = $this->connection->query("SELECT NEXTVAL('" . $table_information->sequences[$index] . "') AS nextval, MAX(" . $serial_field . ") AS max FROM {" . $this->table . "}")->fetchObject(); + // Set the current sequence value to the bigger of those two values. + $curval = $new_value->nextval > $new_value->max ? $new_value->nextval : $new_value->max; + $this->connection->query("SELECT SETVAL('" . $table_information->sequences[$index] . "', :curval)", array(':curval' => $curval)); + } + } + } + // Re-initialize the values array so that we can re-use this query. $this->insertValues = array(); diff --git modules/simpletest/tests/database_test.test modules/simpletest/tests/database_test.test index 5eab091..5ce5bcf 100644 --- modules/simpletest/tests/database_test.test +++ modules/simpletest/tests/database_test.test @@ -562,6 +562,27 @@ class DatabaseInsertTestCase extends DatabaseTestCase { ->execute(); $this->assertIdentical($id, '5', t('Auto-increment ID returned successfully.')); + + // Test a manual insert. + $id = db_insert('test') + ->fields(array( + 'id' => '10', + 'name' => 'Curly', + 'age' => '29', + )) + ->execute(); + + $this->assertIdentical($id, '10', t('Auto-increment ID returned properly for a manual ID insert.')); + + $id = db_insert('test') + ->fields(array( + 'id' => '7', + 'name' => 'Sascha', + 'age' => '24', + )) + ->execute(); + + $this->assertIdentical($id, '7', t('Auto-increment ID returned successfully for a manual ID insert smaller than the max value.')); } /** -- 1.7.1