diff -r 257ea04df092 Doc/library/sqlite3.rst --- a/Doc/library/sqlite3.rst Sun May 17 14:47:00 2015 -0700 +++ b/Doc/library/sqlite3.rst Sun May 17 16:28:52 2015 -0700 @@ -619,6 +619,10 @@ method. For operations other than ``INSERT`` or when :meth:`executemany` is called, :attr:`lastrowid` is set to :const:`None`. + .. versionchanged:: 3.5 + lastrowid now provides the rowid of the last modified row for + ``REPLACE`` statements. + .. attribute:: description This read-only attribute provides the column names of the last query. To diff -r 257ea04df092 Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst Sun May 17 14:47:00 2015 -0700 +++ b/Doc/whatsnew/3.5.rst Sun May 17 16:28:52 2015 -0700 @@ -505,6 +505,12 @@ :func:`~shutil.copy2` if there is a need to ignore metadata. (Contributed by Claudiu Popa in :issue:`19840`.) +sqlite3 +------- + +* :func:`~sqlite3.Cursor.lastrowid` now provides the last row id for + ``REPLACE INTO``, an alias for ``INSERT OR REPLACE`` statements + signal ------ diff -r 257ea04df092 Lib/sqlite3/test/dbapi.py --- a/Lib/sqlite3/test/dbapi.py Sun May 17 14:47:00 2015 -0700 +++ b/Lib/sqlite3/test/dbapi.py Sun May 17 16:28:52 2015 -0700 @@ -185,7 +185,8 @@ def setUp(self): self.cx = sqlite.connect(":memory:") self.cu = self.cx.cursor() - self.cu.execute("create table test(id integer primary key, name text, income number)") + self.cu.execute("create table test(id integer primary key, name text, " + "income number, unique_test text unique)") self.cu.execute("insert into test(name) values (?)", ("foo",)) def tearDown(self): @@ -511,6 +512,71 @@ except TypeError: pass + def CheckLastRowIDOnRplace(self): + """ + Originally added to address http://bugs.python.org/issue16864 + + NOTE: INSERT OR REPLACE and REPLACE INTO should produce the same + behavior. + """ + self.cu.execute("INSERT OR REPLACE INTO test(id, unique_test) VALUES (?, ?)", (1, "bar",)) + self.assertEqual(self.cu.lastrowid, 1) + self.cu.execute("INSERT OR REPLACE INTO test(id, unique_test) VALUES (?, ?)", (1, "bar",)) + self.assertEqual(self.cu.lastrowid, 1) + self.cu.execute("REPLACE INTO test(id, unique_test) VALUES (?, ?)", (1, "bar",)) + self.assertEqual(self.cu.lastrowid, 1) + + def CheckLastRowIDOnIgnore(self): + """ + Issue16864 reported that REPLACE statements weren't changing the last + row id after a successful insert. Testing to see if Other INSERT OR + algorithms set the lastrowid correctly. + """ + #test curser values for unique and non-unique insert or replace + self.cu.execute("insert or ignore into test(unique_test) values (?)", ('test',)) + self.assertEqual(self.cu.lastrowid, 2) + self.cu.execute("insert or ignore into test(unique_test) values (?)", ('test',)) + self.assertEqual(self.cu.lastrowid, 2) + + def CheckLastRowIDOnFail(self): + """ + Issue16864 reported that REPLACE statements weren't changing the last + row id after a successful insert. Testing to see if Other INSERT OR + algorithms set the lastrowid correctly. + """ + #test curser values for unique and non-unique insert or replace + self.cu.execute("insert or fail into test(unique_test) values (?)", ('What',)) + self.assertEqual(self.cu.lastrowid, 2) + with self.assertRaises(sqlite.IntegrityError): + self.cu.execute("insert or fail into test(unique_test) values (?)", ('What',)) + self.assertEqual(self.cu.lastrowid, 2) + + def CheckLastRowIDOnAbort(self): + """ + Issue16864 reported that REPLACE statements weren't changing the last + row id after a successful insert. Testing to see if Other INSERT OR + algorithms set the lastrowid correctly. + """ + #test curser values for unique and non-unique insert or replace + self.cu.execute("insert or abort into test(unique_test) values (?)", ('What',)) + self.assertEqual(self.cu.lastrowid, 2) + with self.assertRaises(sqlite.IntegrityError): + self.cu.execute("insert or abort into test(unique_test) values (?)", ('What',)) + self.assertEqual(self.cu.lastrowid, 2) + + def CheckLastRowIDOnRollback(self): + """ + Issue16864 reported that REPLACE statements weren't changing the last + row id after a successful insert. Testing to see if Other INSERT OR + algorithms set the lastrowid correctly. + """ + #test curser values for unique and non-unique insert or replace + self.cu.execute("insert or rollback into test(unique_test) values (?)", ('What',)) + self.assertEqual(self.cu.lastrowid, 2) + with self.assertRaises(sqlite.IntegrityError): + self.cu.execute("insert or rollback into test(unique_test) values (?)", ('What',)) + self.assertEqual(self.cu.lastrowid, 2) + @unittest.skipUnless(threading, 'This test requires threading.') class ThreadTests(unittest.TestCase): def setUp(self): diff -r 257ea04df092 Modules/_sqlite/cursor.c --- a/Modules/_sqlite/cursor.c Sun May 17 14:47:00 2015 -0700 +++ b/Modules/_sqlite/cursor.c Sun May 17 16:28:52 2015 -0700 @@ -702,7 +702,8 @@ } Py_DECREF(self->lastrowid); - if (!multiple && statement_type == STATEMENT_INSERT) { + if (!multiple && (statement_type == STATEMENT_INSERT || + statement_type == STATEMENT_REPLACE)) { sqlite_int64 lastrowid; Py_BEGIN_ALLOW_THREADS lastrowid = sqlite3_last_insert_rowid(self->connection->db);