Issue #16864: Cursor.lastrowid now supports REPLACE statement

Initial patch by Alex LordThorsen.
This commit is contained in:
Berker Peksag 2016-06-14 15:25:36 +03:00
parent 34f12d7315
commit e0b70cd8a9
6 changed files with 66 additions and 5 deletions

View File

@ -629,9 +629,16 @@ Cursor Objects
.. attribute:: lastrowid .. attribute:: lastrowid
This read-only attribute provides the rowid of the last modified row. It is This read-only attribute provides the rowid of the last modified row. It is
only set if you issued an ``INSERT`` statement using the :meth:`execute` only set if you issued an ``INSERT`` or a ``REPLACE`` statement using the
method. For operations other than ``INSERT`` or when :meth:`executemany` is :meth:`execute` method. For operations other than ``INSERT`` or
called, :attr:`lastrowid` is set to :const:`None`. ``REPLACE`` or when :meth:`executemany` is called, :attr:`lastrowid` is
set to :const:`None`.
If the ``INSERT`` or ``REPLACE`` statement failed to insert the previous
successful rowid is returned.
.. versionchanged:: 3.6
Added support for the ``REPLACE`` statement.
.. attribute:: description .. attribute:: description

View File

@ -339,6 +339,13 @@ you may now specify file paths on top of directories (e.g. zip files).
(Contributed by Wolfgang Langner in :issue:`26587`). (Contributed by Wolfgang Langner in :issue:`26587`).
sqlite3
-------
* :attr:`sqlite3.Cursor.lastrowid` now supports the ``REPLACE`` statement.
(Contributed by Alex LordThorsen in :issue:`16864`.)
socketserver socketserver
------------ ------------

View File

@ -188,7 +188,10 @@ class CursorTests(unittest.TestCase):
def setUp(self): def setUp(self):
self.cx = sqlite.connect(":memory:") self.cx = sqlite.connect(":memory:")
self.cu = self.cx.cursor() 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",)) self.cu.execute("insert into test(name) values (?)", ("foo",))
def tearDown(self): def tearDown(self):
@ -462,6 +465,44 @@ class CursorTests(unittest.TestCase):
with self.assertRaises(TypeError): with self.assertRaises(TypeError):
cur = sqlite.Cursor(foo) cur = sqlite.Cursor(foo)
def CheckLastRowIDOnReplace(self):
"""
INSERT OR REPLACE and REPLACE INTO should produce the same behavior.
"""
sql = '{} INTO test(id, unique_test) VALUES (?, ?)'
for statement in ('INSERT OR REPLACE', 'REPLACE'):
with self.subTest(statement=statement):
self.cu.execute(sql.format(statement), (1, 'foo'))
self.assertEqual(self.cu.lastrowid, 1)
def CheckLastRowIDOnIgnore(self):
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 CheckLastRowIDInsertOR(self):
results = []
for statement in ('FAIL', 'ABORT', 'ROLLBACK'):
sql = 'INSERT OR {} INTO test(unique_test) VALUES (?)'
with self.subTest(statement='INSERT OR {}'.format(statement)):
self.cu.execute(sql.format(statement), (statement,))
results.append((statement, self.cu.lastrowid))
with self.assertRaises(sqlite.IntegrityError):
self.cu.execute(sql.format(statement), (statement,))
results.append((statement, self.cu.lastrowid))
expected = [
('FAIL', 2), ('FAIL', 2),
('ABORT', 3), ('ABORT', 3),
('ROLLBACK', 4), ('ROLLBACK', 4),
]
self.assertEqual(results, expected)
@unittest.skipUnless(threading, 'This test requires threading.') @unittest.skipUnless(threading, 'This test requires threading.')
class ThreadTests(unittest.TestCase): class ThreadTests(unittest.TestCase):
def setUp(self): def setUp(self):

View File

@ -895,6 +895,7 @@ Martin von Löwis
Hugo Lopes Tavares Hugo Lopes Tavares
Guillermo López-Anglada Guillermo López-Anglada
Anne Lord Anne Lord
Alex LordThorsen
Tom Loredo Tom Loredo
Justin Love Justin Love
Ned Jackson Lovely Ned Jackson Lovely

View File

@ -10,6 +10,9 @@ What's New in Python 3.6.0 alpha 3
Library Library
------- -------
- Issue #16864: sqlite3.Cursor.lastrowid now supports REPLACE statement.
Initial patch by Alex LordThorsen.
- Issue #26386: Fixed ttk.TreeView selection operations with item id's - Issue #26386: Fixed ttk.TreeView selection operations with item id's
containing spaces. containing spaces.

View File

@ -698,7 +698,9 @@ PyObject* _pysqlite_query_execute(pysqlite_Cursor* self, int multiple, PyObject*
} }
Py_DECREF(self->lastrowid); Py_DECREF(self->lastrowid);
if (!multiple && statement_type == STATEMENT_INSERT) { if (!multiple &&
/* REPLACE is an alias for INSERT OR REPLACE */
(statement_type == STATEMENT_INSERT || statement_type == STATEMENT_REPLACE)) {
sqlite_int64 lastrowid; sqlite_int64 lastrowid;
Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS
lastrowid = sqlite3_last_insert_rowid(self->connection->db); lastrowid = sqlite3_last_insert_rowid(self->connection->db);