/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/node_sqlite/backup.rs
217 строк
6 KB
Daniel Osvaldo Rahmanto
fix(ext/node): add `limits` property on `node:sqlite` DatabaseSync (#33106)
29 апр 2026, 09:41
Не верифицирован
29 апр 2026, 09:41
54a12b8
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. use std::borrow::Cow; use std::cell::RefCell; use std::ffi::c_int; use std::rc::Rc; use deno_core::OpState; use deno_core::op2; use deno_core::v8; use deno_core::v8_static_strings; use deno_permissions::OpenAccessKind; use deno_permissions::PermissionsContainer; use rusqlite::Connection; use rusqlite::backup; use super::DatabaseSync; use super::SqliteError; use super::validators; const DEFAULT_BACKUP_RATE: c_int = 100; struct BackupOptions<'a> { source: String, target: String, rate: i32, progress: Option<v8::Local<'a, v8::Function>>, } impl<'a> Default for BackupOptions<'a> { fn default() -> Self { BackupOptions { source: "main".to_string(), target: "main".to_string(), rate: DEFAULT_BACKUP_RATE, progress: None, } } } impl<'a> BackupOptions<'a> { fn from_value( scope: &mut v8::PinScope<'a, '_>, value: v8::Local<'a, v8::Value>, ) -> Result<Self, validators::Error> { let mut options = BackupOptions::default(); if value.is_undefined() { return Ok(options); } let Ok(obj) = v8::Local::<v8::Object>::try_from(value) else { return Err(validators::Error::InvalidArgType( "The \"options\" argument must be an object.".into(), )); }; v8_static_strings! { SOURCE_STRING = "source", TARGET_STRING = "target", RATE_STRING = "rate", PROGRESS_STRING = "progress", } let source_string = SOURCE_STRING.v8_string(scope).unwrap(); if let Some(source_val) = obj.get(scope, source_string.into()) && !source_val.is_undefined() { let source_str = v8::Local::<v8::String>::try_from(source_val).map_err(|_| { validators::Error::InvalidArgType( "The \"options.source\" argument must be a string.".into(), ) })?; options.source = source_str.to_rust_string_lossy(scope); } let target_string = TARGET_STRING.v8_string(scope).unwrap(); if let Some(target_val) = obj.get(scope, target_string.into()) && !target_val.is_undefined() { let target_str = v8::Local::<v8::String>::try_from(target_val).map_err(|_| { validators::Error::InvalidArgType( "The \"options.target\" argument must be a string.".into(), ) })?; options.target = target_str.to_rust_string_lossy(scope); } let rate_string = RATE_STRING.v8_string(scope).unwrap(); if let Some(rate_val) = obj.get(scope, rate_string.into()) && !rate_val.is_undefined() { let rate_int = v8::Local::<v8::Integer>::try_from(rate_val) .map_err(|_| { validators::Error::InvalidArgType( "The \"options.rate\" argument must be an integer.".into(), ) })? .value(); options.rate = i32::try_from(rate_int).map_err(|_| { validators::Error::InvalidArgType( "The \"options.rate\" argument must be an integer.".into(), ) })?; } let progress_string = PROGRESS_STRING.v8_string(scope).unwrap(); if let Some(progress_val) = obj.get(scope, progress_string.into()) && !progress_val.is_undefined() { let progress_fn = v8::Local::<v8::Function>::try_from(progress_val) .map_err(|_| { validators::Error::InvalidArgType( "The \"options.progress\" argument must be a function.".into(), ) })?; options.progress = Some(progress_fn); } Ok(options) } } #[op2(reentrant, stack_trace)] #[smi] pub fn op_node_database_backup<'a>( state: Rc<RefCell<OpState>>, scope: &mut v8::PinScope<'a, '_>, #[cppgc] source_db: &DatabaseSync, #[string] path: &str, options: v8::Local<'a, v8::Value>, ) -> Result<Option<i32>, SqliteError> { use rusqlite::backup::StepResult::Busy; use rusqlite::backup::StepResult::Done; use rusqlite::backup::StepResult::Locked; use rusqlite::backup::StepResult::More; let options = BackupOptions::from_value(scope, options)?; let src_conn_ref = source_db.conn.borrow(); let src_conn = src_conn_ref.as_ref().ok_or(SqliteError::AlreadyClosed)?; let checked_path = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::<PermissionsContainer>(); permissions.check_open( Cow::Borrowed(std::path::Path::new(path)), OpenAccessKind::Write, Some("node:sqlite.backup"), )? }; let mut dst_conn = Connection::open(checked_path).map_err(|e| match e { rusqlite::Error::SqliteFailure(err, Some(msg)) => { let message = if err.extended_code == rusqlite::ffi::SQLITE_CANTOPEN { "unable to open database file".to_string() } else { msg }; SqliteError::SqliteSysError { message: message.clone(), errstr: message, errcode: err.extended_code as _, } } other_err => SqliteError::from(other_err), })?; let backup = backup::Backup::new_with_names( src_conn, options.source.as_str(), &mut dst_conn, options.target.as_str(), )?; v8_static_strings! { TOTAL_PAGES_STRING = "totalPages", REMAINING_PAGES_STRING = "remainingPages", } loop { let step_result = backup.step(options.rate)?; if let Some(progress_fn) = options.progress { let progress = backup.progress(); if progress.remaining != 0 { let js_progress_obj = v8::Object::new(scope); let total_pages_string = TOTAL_PAGES_STRING.v8_string(scope).unwrap(); let remaining_pages_string = REMAINING_PAGES_STRING.v8_string(scope).unwrap(); let total_pages_js = v8::Integer::new(scope, progress.pagecount); let remaining_pages_js = v8::Integer::new(scope, progress.remaining); js_progress_obj.set( scope, total_pages_string.into(), total_pages_js.into(), ); js_progress_obj.set( scope, remaining_pages_string.into(), remaining_pages_js.into(), ); let recv = v8::null(scope).into(); let res = progress_fn.call(scope, recv, &[js_progress_obj.into()]); if res.is_none() { // JS exception occurred in progress callback return Ok(None); } } } match step_result { Done => return Ok(Some(backup.progress().pagecount)), More | Busy | Locked | _ => continue, } } }