Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

All notable changes will be documented in this file.

## 0.5.0 - 2025-11-26

- BREAKING: remove register_logger method from Vfs
- return SqliteLogger instance from register_dynamic/register_static

## 0.4.1 - 2025-06-19

- expose SqliteApi in public API
Expand Down
73 changes: 34 additions & 39 deletions examples/memvfs.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
// cargo build --example memvfs --features dynamic

use std::{
ffi::{CStr, c_void},
os::raw::c_char,
sync::Arc,
};
use std::{ffi::c_void, os::raw::c_char, sync::Arc};

use parking_lot::Mutex;
use sqlite_plugin::{
Expand Down Expand Up @@ -45,33 +41,6 @@ struct MemVfs {
impl Vfs for MemVfs {
type Handle = File;

fn register_logger(&self, logger: SqliteLogger) {
struct LogCompat {
logger: Mutex<SqliteLogger>,
}

impl log::Log for LogCompat {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}

fn log(&self, record: &log::Record) {
let level = match record.level() {
log::Level::Error => SqliteLogLevel::Error,
log::Level::Warn => SqliteLogLevel::Warn,
_ => SqliteLogLevel::Notice,
};
let msg = format!("{}", record.args());
self.logger.lock().log(level, msg.as_bytes());
}

fn flush(&self) {}
}

let log = LogCompat { logger: Mutex::new(logger) };
log::set_boxed_logger(Box::new(log)).expect("failed to setup global logger");
}

fn open(&self, path: Option<&str>, opts: OpenOpts) -> VfsResult<Self::Handle> {
log::debug!("open: path={:?}, opts={:?}", path, opts);
let mode = opts.mode();
Expand Down Expand Up @@ -216,6 +185,33 @@ impl Vfs for MemVfs {
}
}

fn setup_logger(logger: SqliteLogger) {
struct LogCompat {
logger: Mutex<SqliteLogger>,
}

impl log::Log for LogCompat {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}

fn log(&self, record: &log::Record) {
let level = match record.level() {
log::Level::Error => SqliteLogLevel::Error,
log::Level::Warn => SqliteLogLevel::Warn,
_ => SqliteLogLevel::Notice,
};
let msg = format!("{}", record.args());
self.logger.lock().log(level, &msg);
}

fn flush(&self) {}
}

let log = LogCompat { logger: Mutex::new(logger) };
log::set_boxed_logger(Box::new(log)).expect("failed to setup global logger");
}

/// This function is called by `SQLite` when the extension is loaded. It registers
/// the memvfs VFS with `SQLite`.
/// # Safety
Expand All @@ -226,18 +222,17 @@ pub unsafe extern "C" fn sqlite3_memvfs_init(
_pz_err_msg: *mut *mut c_char,
p_api: *mut sqlite3_api_routines,
) -> std::os::raw::c_int {
let vfs = MemVfs { files: Default::default() };
const MEMVFS_NAME: &CStr = c"mem";
if let Err(err) = unsafe {
match unsafe {
register_dynamic(
p_api,
MEMVFS_NAME.to_owned(),
vfs,
c"mem".to_owned(),
MemVfs { files: Default::default() },
RegisterOpts { make_default: true },
)
} {
return err;
}
Ok(logger) => setup_logger(logger),
Err(err) => return err,
};

// set the log level to trace
log::set_max_level(log::LevelFilter::Trace);
Expand Down
2 changes: 2 additions & 0 deletions examples/test_memvfs.sql
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

.load target/debug/examples/libmemvfs.so
.open main.db
.mode table
.log stdout

.databases
.vfsinfo
Expand Down
22 changes: 8 additions & 14 deletions src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use core::ffi::{c_char, c_int};

use crate::vars;

#[allow(non_snake_case)]
type Sqlite3Log = unsafe extern "C" fn(iErrCode: c_int, arg2: *const c_char, ...);

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
Expand Down Expand Up @@ -32,20 +33,13 @@ impl SqliteLogger {
Self { log }
}

/// Log bytes to the `SQLite3` log handle.
/// This function will write each line separately to `SQLite3`.
/// Note that `SQLite3` silently truncates log lines larger than roughly
/// 230 bytes by default.
pub fn log(&self, level: SqliteLogLevel, buf: &[u8]) {
/// Log bytes directly to the `SQLite3` log handle.
/// Note that `SQLite` silently truncates writes larger than
/// roughly 230 bytes by default. It's recommended that you
/// split your log messages by lines before calling this method.
pub fn log(&self, level: SqliteLogLevel, msg: &str) {
let code = level.into_err_code();
for line in buf.split(|b| *b == b'\n') {
// skip if line only contains whitespace
if line.iter().all(|b| b.is_ascii_whitespace()) {
continue;
}

let z_format = CString::new(line).unwrap();
unsafe { (self.log)(code, z_format.as_ptr()) }
}
let z_format = CString::new(msg).unwrap();
unsafe { (self.log)(code, z_format.as_ptr()) }
}
}
Loading