Skip to content

Commit 1dd7982

Browse files
authored
Add backup API with configurable sleep (#347)
* Add backup API with configurable sleep * chore(version): bump to 1.8.0
1 parent f02bf5a commit 1dd7982

3 files changed

Lines changed: 67 additions & 1 deletion

File tree

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "SQLite"
22
uuid = "0aa819cd-b072-5ff4-a722-6bc24af294d9"
3-
version = "1.7.1"
3+
version = "1.8.0"
44
authors = ["Jacob Quinn <quinn.jacobd@gmail.com>", "JuliaData Contributors"]
55

66
[deps]

src/SQLite.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,48 @@ Returns `true` if in a transaction, `false` if in autocommit mode.
9090
"""
9191
intransaction(db::DB) = C.sqlite3_get_autocommit(db.handle) == 0
9292

93+
"""
94+
SQLite.backup(db::SQLite.DB, path::AbstractString; sleep_ms::Integer=250)
95+
96+
Create a backup of `db` at `path` using the SQLite backup API.
97+
`sleep_ms` controls the delay when the backup encounters `SQLITE_BUSY`
98+
or `SQLITE_LOCKED`.
99+
Returns `path`.
100+
"""
101+
function backup(db::DB, path::AbstractString; sleep_ms::Integer = 250)
102+
isopen(db) || throw(SQLiteException("DB is closed"))
103+
dest = DB(path)
104+
backup_handle = C.sqlite3_backup_init(dest.handle, "main", db.handle, "main")
105+
if backup_handle == C_NULL
106+
err = sqliteexception(dest)
107+
close(dest)
108+
throw(err)
109+
end
110+
rc = C.SQLITE_OK
111+
finish_rc = C.SQLITE_OK
112+
try
113+
while true
114+
rc = C.sqlite3_backup_step(backup_handle, -1)
115+
if rc == C.SQLITE_OK
116+
continue
117+
elseif rc == C.SQLITE_BUSY || rc == C.SQLITE_LOCKED
118+
C.sqlite3_sleep(Cint(sleep_ms))
119+
continue
120+
end
121+
break
122+
end
123+
finally
124+
finish_rc = C.sqlite3_backup_finish(backup_handle)
125+
end
126+
if rc != C.SQLITE_DONE || finish_rc != C.SQLITE_OK
127+
err = sqliteexception(dest)
128+
close(dest)
129+
throw(err)
130+
end
131+
close(dest)
132+
return path
133+
end
134+
93135
function finalize_statements!(db::DB)
94136
# close stmts
95137
for stmt_wrapper in keys(db.stmt_wrappers)

test/runtests.jl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,30 @@ end
854854
@test SQLite.busy_timeout(db, 300) == 0
855855
end
856856

857+
@testset "backup" begin
858+
db = SQLite.DB()
859+
DBInterface.execute(db, "CREATE TABLE backup_test (x INT)")
860+
DBInterface.execute(db, "INSERT INTO backup_test VALUES (1), (2)")
861+
tmp_path, tmp_io = mktemp()
862+
close(tmp_io)
863+
try
864+
@test SQLite.backup(db, tmp_path; sleep_ms = 1) == tmp_path
865+
db2 = SQLite.DB(tmp_path)
866+
try
867+
r = DBInterface.execute(
868+
db2,
869+
"SELECT x FROM backup_test ORDER BY x",
870+
) |> columntable
871+
@test r.x == [1, 2]
872+
finally
873+
close(db2)
874+
end
875+
finally
876+
close(db)
877+
rm(tmp_path; force = true)
878+
end
879+
end
880+
857881
@testset "Issue #253: Ensure query column names are unique by default" begin
858882
db = SQLite.DB()
859883
res =

0 commit comments

Comments
 (0)