Repository navigation
bpo-27645: Supporting native backup facility of SQLite #377
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
d93391d
7b1277a
1258d47
d8ea457
35fa0ad
c1af5a0
c07d982
cdc7eda
6d3eb69
6fab17f
6449f3d
f84d375
e748942
426dad4
895726b
77dfa39
71726ba
c4fbc20
ff1609b
d688f20
4ed6c31
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…s do
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1483,63 +1483,69 @@ pysqlite_connection_backup(pysqlite_Connection* self, PyObject* args) | |
| char* filename; | ||
| int pages = -1; | ||
| PyObject* progress = Py_None; | ||
| PyObject* retval = NULL; | ||
| int rc; | ||
| sqlite3 *bckconn; | ||
| sqlite3_backup *bckhandle; | ||
|
|
||
| if (!PyArg_ParseTuple(args, "s|iO:backup(filename, pages, progress)", | ||
| &filename, &pages, &progress)) { | ||
| return NULL; | ||
| goto finally; | ||
| } | ||
|
|
||
| if (progress != Py_None && !PyCallable_Check(progress)) { | ||
| PyErr_SetString(PyExc_TypeError, "progress argument must be a callable"); | ||
| return NULL; | ||
| goto finally; | ||
| } | ||
|
|
||
| if (pages == 0) { | ||
| pages = -1; | ||
| } | ||
|
|
||
| rc = sqlite3_open(filename, &bckconn); | ||
| if (rc == SQLITE_OK) { | ||
| bckhandle = sqlite3_backup_init(bckconn, "main", self->db, "main"); | ||
| if (bckhandle) { | ||
| do { | ||
| rc = sqlite3_backup_step(bckhandle, pages); | ||
|
|
||
| if (progress != Py_None) { | ||
| if (!PyObject_CallFunction(progress, "ii", | ||
| sqlite3_backup_remaining(bckhandle), | ||
| sqlite3_backup_pagecount(bckhandle))) { | ||
| /* User's callback raised an error: interrupt the loop and | ||
| propagate it. */ | ||
| rc = -1; | ||
| } | ||
| } | ||
| if (rc != SQLITE_OK) { | ||
| goto finally; | ||
| } | ||
|
|
||
| bckhandle = sqlite3_backup_init(bckconn, "main", self->db, "main"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't database name be a parameter?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's not really the database name: accordingly to the documentation that name denotes the target, that can either be "main" for the main storage, "temp" for the temporary storage or the name of the attached database.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uhm, further investigation seems to reveal that the bck = sqlite.connect('/tmp/thebackup')
bck.execute(f"ATTACH DATABASE '/tmp/theattachment' AS attached_db")
bck.commit()
db = sqlite.connect('/tmp/database')
db.backup('/tmp/thebackup', dest_name='attached_db')Changed back to a single |
||
| if (bckhandle) { | ||
| do { | ||
| rc = sqlite3_backup_step(bckhandle, pages); | ||
|
|
||
| /* Sleep for 250ms if there are still further pages to copy */ | ||
| if (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED) { | ||
| sqlite3_sleep(250); | ||
| if (progress != Py_None) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should call
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure about that: maybe a better option would be pass the result status to the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now the progress callback receives three arguments. |
||
| if (!PyObject_CallFunction(progress, "ii", | ||
| sqlite3_backup_remaining(bckhandle), | ||
| sqlite3_backup_pagecount(bckhandle))) { | ||
| /* User's callback raised an error: interrupt the loop and | ||
| propagate it. */ | ||
| rc = -1; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should add a test for the case that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, will do!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
| } | ||
| } while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED); | ||
| } | ||
|
|
||
| sqlite3_backup_finish(bckhandle); | ||
| } | ||
| /* Sleep for 250ms if there are still further pages to copy */ | ||
| if (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED) { | ||
| sqlite3_sleep(250); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we sure we need to sleep when
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I think we should: the step function returns OK when it “successfully copies N pages and there are still more pages to be copied”.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reconsidering, I think you're right here: there's no reason to delay next iteration when the result was |
||
| } | ||
| } while (rc == SQLITE_OK || rc == SQLITE_BUSY || rc == SQLITE_LOCKED); | ||
|
|
||
| if (rc != -1) { | ||
| rc = _pysqlite_seterror(bckconn, NULL); | ||
| } | ||
| sqlite3_backup_finish(bckhandle); | ||
| } | ||
|
|
||
| if (rc != -1) { | ||
| rc = _pysqlite_seterror(bckconn, NULL); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure about this but from the documentation
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will try to understand the point.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried to address this concern in my latest patches. |
||
| } | ||
|
|
||
| sqlite3_close(bckconn); | ||
|
|
||
| if (rc != 0) { | ||
| /* TODO: should the (probably incomplete/invalid) backup be removed here? */ | ||
| return NULL; | ||
| if (rc == SQLITE_OK) { | ||
| Py_INCREF(Py_None); | ||
| retval = Py_None; | ||
| } else { | ||
| Py_RETURN_NONE; | ||
| /* TODO: should the (probably incomplete/invalid) backup be removed here? */ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you should delete the backup
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, will do that. Thanks a lot for the review!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I need some help here: I expected to find an abstract/portable
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I took the simplest way for now, using unlink(2) from |
||
| } | ||
|
|
||
| finally: | ||
| return retval; | ||
| } | ||
|
|
||
| static PyObject * | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#359 could be useful here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure what you are suggesting: should I take the same approach of conditionally using sqlite3_open_v2() instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was trying to say that this should be merged after #359.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This don't actually have any effect.
sqlite3_open(filename, db)is the same assqlite3_open_v2(filename, db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL)