| 1152 | #[pymethod] |
| 1153 | #[allow(clippy::manual_c_str_literals)] |
| 1154 | fn backup(zelf: &Py<Self>, args: BackupArgs, vm: &VirtualMachine) -> PyResult<()> { |
| 1155 | let BackupArgs { |
| 1156 | target, |
| 1157 | pages, |
| 1158 | progress, |
| 1159 | name, |
| 1160 | sleep, |
| 1161 | } = args; |
| 1162 | if zelf.is(&target) { |
| 1163 | return Err(vm.new_value_error("target cannot be the same connection instance")); |
| 1164 | } |
| 1165 | |
| 1166 | let pages = if pages == 0 { -1 } else { pages }; |
| 1167 | |
| 1168 | let name_cstring; |
| 1169 | let name_ptr = if let Some(name) = &name { |
| 1170 | name_cstring = name.to_cstring(vm)?; |
| 1171 | name_cstring.as_ptr() |
| 1172 | } else { |
| 1173 | b"main\0".as_ptr().cast() |
| 1174 | }; |
| 1175 | |
| 1176 | let sleep_ms = (sleep * 1000.0) as c_int; |
| 1177 | |
| 1178 | let db = zelf.db_lock(vm)?; |
| 1179 | let target_db = target.db_lock(vm)?; |
| 1180 | |
| 1181 | let handle = unsafe { |
| 1182 | sqlite3_backup_init(target_db.db, b"main\0".as_ptr().cast(), db.db, name_ptr) |
| 1183 | }; |
| 1184 | |
| 1185 | if handle.is_null() { |
| 1186 | return Err(target_db.error_extended(vm)); |
| 1187 | } |
| 1188 | |
| 1189 | drop(db); |
| 1190 | drop(target_db); |
| 1191 | |
| 1192 | loop { |
| 1193 | let ret = unsafe { sqlite3_backup_step(handle, pages) }; |
| 1194 | |
| 1195 | if let Some(progress) = &progress { |
| 1196 | let remaining = unsafe { sqlite3_backup_remaining(handle) }; |
| 1197 | let pagecount = unsafe { sqlite3_backup_pagecount(handle) }; |
| 1198 | if let Err(err) = progress.invoke((ret, remaining, pagecount), vm) { |
| 1199 | unsafe { sqlite3_backup_finish(handle) }; |
| 1200 | return Err(err); |
| 1201 | } |
| 1202 | } |
| 1203 | |
| 1204 | if ret == SQLITE_BUSY || ret == SQLITE_LOCKED { |
| 1205 | unsafe { sqlite3_sleep(sleep_ms) }; |
| 1206 | } else if ret != SQLITE_OK { |
| 1207 | break; |
| 1208 | } |
| 1209 | } |
| 1210 | |
| 1211 | let ret = unsafe { sqlite3_backup_finish(handle) }; |