diff -r 787cc3d1d3af Doc/library/sqlite3.rst --- a/Doc/library/sqlite3.rst Sun May 10 15:09:46 2015 -0400 +++ b/Doc/library/sqlite3.rst Sun May 10 21:00:12 2015 -0700 @@ -619,6 +619,16 @@ method. For operations other than ``INSERT`` or when :meth:`executemany` is called, :attr:`lastrowid` is set to :const:`None`. + .. versionchanged:: 3.5 + lastrowid is now provides the rowid of the last modified row for + ``INSERT OR REPLACE`` statements as well as ``INSERT`` + + + .. versionchanged:: 3.5 + lastrowid is now provides the rowid of the last modified row for + ``INSERT OR REPLACE`` statements as well as ``INSERT`` + + .. attribute:: description This read-only attribute provides the column names of the last query. To diff -r 787cc3d1d3af Doc/whatsnew/3.5.rst --- a/Doc/whatsnew/3.5.rst Sun May 10 15:09:46 2015 -0400 +++ b/Doc/whatsnew/3.5.rst Sun May 10 21:00:12 2015 -0700 @@ -438,6 +438,16 @@ :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 both + ``INSERT`` and ``INSERT AND REPLACE`` statements. + +sqlite3 +------- +* :func:`~sqlite3.Cursor.lastrowid` now provides the last row id for both + ``INSERT`` and ``INSERT AND REPLACE`` statements. + signal ------ diff -r 787cc3d1d3af Lib/sqlite3/test/dbapi.py --- a/Lib/sqlite3/test/dbapi.py Sun May 10 15:09:46 2015 -0400 +++ b/Lib/sqlite3/test/dbapi.py Sun May 10 21:00:12 2015 -0700 @@ -185,7 +185,9 @@ 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)") + sql_string = ("create table test(id integer primary key, name text, " + "income number, unique_test text unique)") + self.cu.execute(sql_string) self.cu.execute("insert into test(name) values (?)", ("foo",)) def tearDown(self): @@ -511,6 +513,72 @@ except TypeError: pass + def CheckLastRowIDOnRplace(self): + """ + Originally added to address http://bugs.python.org/issue16864 + """ + #test curser values for unique and non-unique insert or replace + self.cu.execute("insert or replace into test(name) values (?)", ('What',)) + self.assertEqual(self.cu.lastrowid, 2) + self.cu.execute("insert or replace into test(name, unique_test) values (?, ?)", ('is', 'A',)) + self.assertEqual(self.cu.lastrowid, 3) + self.cu.execute("insert or replace into test(name, unique_test) values (?, ?)", ('baby', 'A',)) + self.assertEqual(self.cu.lastrowid, 4) + self.cu.execute("insert or replace into test(id, name, unique_test) values (?, ?, ?)", (4, "don't", 'A',)) + self.assertEqual(self.cu.lastrowid, 4) + #test curser values for replace, which should be synonymous for insert and replace + self.cu.execute("replace into test(name) values (?)", ('hurt',)) + self.assertEqual(self.cu.lastrowid, 5) + self.cu.execute("replace into test(name, unique_test) values (?, ?)", ("me", 'B',)) + self.assertEqual(self.cu.lastrowid, 6) + self.cu.execute("replace into test(name, unique_test) values (?, ?)", ('baby', 'A',)) + self.assertEqual(self.cu.lastrowid, 7) + self.cu.execute("replace into test(id, name, unique_test) values (?, ?, ?)", (4, "don't", 'A',)) + self.assertEqual(self.cu.lastrowid, 4) + + def CheckLastRowIDOnIgnore(self): + """ + Originally added to address http://bugs.python.org/issue16864 + """ + #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): + """ + Originally added to address http://bugs.python.org/issue16864 + """ + #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): + """ + Originally added to address http://bugs.python.org/issue16864 + """ + #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): + """ + Originally added to address http://bugs.python.org/issue16864 + """ + #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 787cc3d1d3af Modules/_sqlite/cursor.c --- a/Modules/_sqlite/cursor.c Sun May 10 15:09:46 2015 -0400 +++ b/Modules/_sqlite/cursor.c Sun May 10 21:00:12 2015 -0700 @@ -702,7 +702,7 @@ } 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);