Skip to content

Commit c1ae741

Browse files
author
Erlend Egeberg Aasland
authored
bpo-43265: Improve sqlite3.Connection.backup error handling (GH-24586)
1 parent b8509ff commit c1ae741

File tree

3 files changed

+38
-49
lines changed

3 files changed

+38
-49
lines changed

Lib/sqlite3/test/backup.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -149,10 +149,7 @@ def test_database_source_name(self):
149149
with self.assertRaises(sqlite.OperationalError) as cm:
150150
with sqlite.connect(':memory:') as bck:
151151
self.cx.backup(bck, name='non-existing')
152-
self.assertIn(
153-
str(cm.exception),
154-
['SQL logic error', 'SQL logic error or missing database']
155-
)
152+
self.assertIn("unknown database", str(cm.exception))
156153

157154
self.cx.execute("ATTACH DATABASE ':memory:' AS attached_db")
158155
self.cx.execute('CREATE TABLE attached_db.foo (key INTEGER)')
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Improve :meth:`sqlite3.Connection.backup` error handling. The error message
2+
for non-existant target database names is now ``unknown database <database
3+
name>`` instead of ``SQL logic error``. Patch by Erlend E. Aasland.

Modules/_sqlite/connection.c

Lines changed: 34 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1601,7 +1601,6 @@ pysqlite_connection_backup_impl(pysqlite_Connection *self,
16011601
/*[clinic end generated code: output=306a3e6a38c36334 input=30ae45fc420bfd3b]*/
16021602
{
16031603
int rc;
1604-
int callback_error = 0;
16051604
int sleep_ms = (int)(sleep * 1000.0);
16061605
sqlite3 *bck_conn;
16071606
sqlite3_backup *bck_handle;
@@ -1643,60 +1642,50 @@ pysqlite_connection_backup_impl(pysqlite_Connection *self,
16431642
bck_handle = sqlite3_backup_init(bck_conn, "main", self->db, name);
16441643
Py_END_ALLOW_THREADS
16451644

1646-
if (bck_handle) {
1647-
do {
1648-
Py_BEGIN_ALLOW_THREADS
1649-
rc = sqlite3_backup_step(bck_handle, pages);
1650-
Py_END_ALLOW_THREADS
1645+
if (bck_handle == NULL) {
1646+
_pysqlite_seterror(bck_conn, NULL);
1647+
return NULL;
1648+
}
16511649

1652-
if (progress != Py_None) {
1653-
PyObject *res;
1654-
1655-
res = PyObject_CallFunction(progress, "iii", rc,
1656-
sqlite3_backup_remaining(bck_handle),
1657-
sqlite3_backup_pagecount(bck_handle));
1658-
if (res == NULL) {
1659-
/* User's callback raised an error: interrupt the loop and
1660-
propagate it. */
1661-
callback_error = 1;
1662-
rc = -1;
1663-
} else {
1664-
Py_DECREF(res);
1665-
}
1666-
}
1650+
do {
1651+
Py_BEGIN_ALLOW_THREADS
1652+
rc = sqlite3_backup_step(bck_handle, pages);
1653+
Py_END_ALLOW_THREADS
16671654

1668-
/* Sleep for a while if there are still further pages to copy and
1669-
the engine could not make any progress */
1670-
if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
1655+
if (progress != Py_None) {
1656+
int remaining = sqlite3_backup_remaining(bck_handle);
1657+
int pagecount = sqlite3_backup_pagecount(bck_handle);
1658+
PyObject *res = PyObject_CallFunction(progress, "iii", rc,
1659+
remaining, pagecount);
1660+
if (res == NULL) {
1661+
/* Callback failed: abort backup and bail. */
16711662
Py_BEGIN_ALLOW_THREADS
1672-
sqlite3_sleep(sleep_ms);
1663+
sqlite3_backup_finish(bck_handle);
16731664
Py_END_ALLOW_THREADS
1665+
return NULL;
16741666
}
1675-
} while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);
1676-
1677-
Py_BEGIN_ALLOW_THREADS
1678-
rc = sqlite3_backup_finish(bck_handle);
1679-
Py_END_ALLOW_THREADS
1680-
} else {
1681-
rc = _pysqlite_seterror(bck_conn, NULL);
1682-
}
1667+
Py_DECREF(res);
1668+
}
16831669

1684-
if (!callback_error && rc != SQLITE_OK) {
1685-
/* We cannot use _pysqlite_seterror() here because the backup APIs do
1686-
not set the error status on the connection object, but rather on
1687-
the backup handle. */
1688-
if (rc == SQLITE_NOMEM) {
1689-
(void)PyErr_NoMemory();
1690-
} else {
1691-
PyErr_SetString(pysqlite_OperationalError, sqlite3_errstr(rc));
1670+
/* Sleep for a while if there are still further pages to copy and
1671+
the engine could not make any progress */
1672+
if (rc == SQLITE_BUSY || rc == SQLITE_LOCKED) {
1673+
Py_BEGIN_ALLOW_THREADS
1674+
sqlite3_sleep(sleep_ms);
1675+
Py_END_ALLOW_THREADS
16921676
}
1693-
}
1677+
} while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED);
16941678

1695-
if (!callback_error && rc == SQLITE_OK) {
1696-
Py_RETURN_NONE;
1697-
} else {
1679+
Py_BEGIN_ALLOW_THREADS
1680+
rc = sqlite3_backup_finish(bck_handle);
1681+
Py_END_ALLOW_THREADS
1682+
1683+
if (rc != SQLITE_OK) {
1684+
_pysqlite_seterror(bck_conn, NULL);
16981685
return NULL;
16991686
}
1687+
1688+
Py_RETURN_NONE;
17001689
}
17011690

17021691
/*[clinic input]

0 commit comments

Comments
 (0)