| Crates.io | sqlite_wasm_reader |
| lib.rs | sqlite_wasm_reader |
| version | 0.3.2 |
| created_at | 2025-07-01 18:54:31.971808+00 |
| updated_at | 2025-07-08 14:11:47.249702+00 |
| description | A pure Rust SQLite reader library for WASI environments |
| homepage | |
| repository | https://github.com/execve-ai/sqlite-wasm-reader |
| max_upload_size | |
| id | 1733486 |
| size | 298,621 |
A pure Rust SQLite reader library designed for WASI (WebAssembly System Interface) environments. This library provides read-only access to SQLite databases without any C dependencies, making it perfect for WebAssembly applications running in WasmTime or other WASI-compatible runtimes.
This version introduces comprehensive SQL query support with enhanced WHERE clause capabilities including logical operators (AND, OR, NOT), null checks (IS NULL, IS NOT NULL), membership testing (IN), range queries (BETWEEN), pattern matching (LIKE), and complex expressions with parentheses.
See CHANGELOG.md for detailed release information.
byteorder and thiserrorThis library is intentionally designed as read-only for several important reasons:
This library is specifically designed for the following scenarios:
// Process SQLite data in a WASI environment
use sqlite_wasm_reader::{Database, Error};
use sqlite_wasm_reader::query::SelectQuery;
fn analyze_user_data(db_path: &str) -> Result<(), Error> {
let mut db = Database::open(db_path)?;
let users = db.execute_query(&SelectQuery::parse("SELECT * FROM users")?)?;
// Perform analysis without modifying the database
for user in users {
// Analyze user data...
}
Ok(())
}
// Extract data from SQLite for ETL processes
fn extract_table_data(db_path: &str, table_name: &str) -> Result<Vec<Row>, Error> {
let mut db = Database::open(db_path)?;
db.execute_query(&SelectQuery::parse(&format!("SELECT * FROM {}", table_name))?)
}
Use this library when you need to:
This library is intentionally read-only because writing to SQLite from WebAssembly sandboxes presents significant risks:
Add this to your Cargo.toml:
[dependencies]
sqlite_wasm_reader = "0.3.2"
use sqlite_wasm_reader::{Database, Error, LogLevel, init_default_logger};
fn main() -> Result<(), Error> {
// Initialize logging (optional, defaults to Info level)
init_default_logger();
// Open a SQLite database
let mut db = Database::open("example.db")?;
// List all tables
let tables = db.tables()?;
for table in tables {
println!("Table: {}", table);
}
// Execute a query using indexes
let query = SelectQuery::parse("SELECT * FROM users WHERE id = 1")?;
let rows = db.execute_query(&query)?;
for row in rows {
println!("{:?}", row);
}
Ok(())
}
The library includes a configurable logging system to help with debugging and monitoring:
use sqlite_wasm_reader::{LogLevel, init_logger, set_log_level};
// Initialize with custom log level
init_logger(LogLevel::Debug);
// Change log level at runtime
set_log_level(LogLevel::Trace);
// Log levels available:
// - Error: Critical errors that prevent operation
// - Warn: Important warnings and errors
// - Info: General information about operations (default)
// - Debug: Detailed debugging information
// - Trace: Very detailed tracing information
// Open a database
let mut db = Database::open("path/to/database.db")?;
// List all tables
let tables = db.tables()?;
// Execute a query using indexes
let query = SelectQuery::parse("SELECT * FROM table_name WHERE column = 'value'")?;
let rows = db.execute_query(&query)?;
// Count rows in a table efficiently
let count = db.count_table_rows("table_name")?;
For programmatic construction of SELECT queries without writing raw SQL, use the fluent helper API:
use sqlite_wasm_reader::{query::{SelectQuery, Expr}, Value};
let query = SelectQuery::new("users")
.select_columns(vec!["id".into(), "name".into()])
.with_where(
Expr::eq("status", Value::Text("active".into()))
.and(Expr::between("age", Value::Integer(18), Value::Integer(65)))
.or(Expr::is_null("deleted_at"))
)
.with_order_by("name", true)
.with_limit(100);
let rows = db.execute_query(&query)?;
sqlite_wasm_reader lets you query data either by parsing raw SQL or by constructing SelectQuery objects directly and executing them with Database::execute_query().
use sqlite_wasm_reader::{Database, Error};
use sqlite_wasm_reader::query::SelectQuery;
use sqlite_wasm_reader::query::{SelectQuery, Expr};
use sqlite_wasm_reader::value::Value;
fn complex_report(db: &mut Database) -> Result<(), Error> {
// Option 1. Parse raw SQL, then execute
let raw = "SELECT * FROM users WHERE age > 18 AND status = 'active' ORDER BY name LIMIT 10";
let parsed = SelectQuery::parse(raw)?;
let rows = db.execute_query(&parsed)?;
println!("{} rows (raw SQL): {}", rows.len(), raw);
// Option 2. Build programmatically using helpers
let builder = SelectQuery::new("users")
.select_columns(vec!["id".into(), "name".into(), "age".into()])
.with_where(
Expr::gt("age", Value::Integer(18))
.and(Expr::eq("status", Value::Text("active".into())))
)
.with_order_by("name", true)
.with_limit(10);
let rows = db.execute_query(&builder)?;
println!("{} rows (builder API)", rows.len());
Ok(())
}
Both paths end in a call to execute_query, which accepts any SelectQuery (parsed or manually constructed). This method uses intelligent query processing:
AND, OR, NOT), LIKE, IN, BETWEEN, IS NULL / IS NOT NULL, and parenthesesSELECT * or explicit columns)ORDER BY and LIMIT processing in memoryUse whichever style (raw SQL vs builder) best fits your workflow.
The library supports all basic SQLite types:
use sqlite_wasm_reader::Value;
// NULL values
Value::Null
// Integer values
Value::Integer(42)
// Floating point values
Value::Real(3.14)
// Text values
Value::Text("hello".to_string())
// BLOB values
Value::Blob(vec![0x01, 0x02, 0x03])
Rows are represented as HashMap<String, Value>:
for row in rows {
// Access by column name
if let Some(id) = row.get("id") {
match id {
Value::Integer(i) => println!("ID: {}", i),
Value::Text(s) => println!("ID: {}", s),
_ => println!("Unexpected ID type"),
}
}
// Check if column exists
if row.contains_key("name") {
println!("Has name column");
}
}
To build this crate for WASI target:
# Add the WASI target
rustup target add wasm32-wasip1
# Build the project
cargo build --target wasm32-wasip1 --release
# Run with wasmtime
wasmtime run --dir=. target/wasm32-wasip1/release/your_app.wasm
# Run with file access
wasmtime run --dir=. --mapdir /data:./data target/wasm32-wasip1/release/your_app.wasm
The library includes several examples demonstrating different use cases:
use sqlite_wasm_reader::{Database, Error, Value};
fn main() -> Result<(), Error> {
let mut db = Database::open("users.db")?;
// Read user table
let users = db.execute_query(&SelectQuery::parse("SELECT * FROM users")?)?;
for user in users {
let name = user.get("name").unwrap_or(&Value::Null);
let email = user.get("email").unwrap_or(&Value::Null);
println!("User: {} <{}>", name, email);
}
Ok(())
}
use sqlite_wasm_reader::{Database, Error};
use sqlite_wasm_reader::query::SelectQuery;
fn main() -> Result<(), Error> {
let mut db = Database::open("database.db")?;
// Count rows without loading all data into memory
let user_count = db.count_table_rows("users")?;
let order_count = db.count_table_rows("orders")?;
println!("Users: {}, Orders: {}", user_count, order_count);
Ok(())
}
use sqlite_wasm_reader::{Database, Error, LogLevel, init_default_logger, set_log_level, log_debug};
fn main() -> Result<(), Error> {
// Initialize logging with debug level
init_default_logger();
set_log_level(LogLevel::Debug);
let mut db = Database::open("database.db")?;
// Enable debug logging for troubleshooting
log_debug("Starting database analysis");
let tables = db.tables()?;
log_debug(&format!("Found {} tables", tables.len()));
for table in tables {
let count = db.count_table_rows(&table)?;
log_debug(&format!("Table {} has {} rows", table, count));
}
Ok(())
}
# Basic database reading
cargo run --example read_db -- database.db
# Logging example with custom log level
cargo run --example logging_example -- database.db debug
# Efficient row counting
cargo run --example count_rows -- database.db
# WASI-compatible example
cargo build --example wasi_example --target wasm32-wasip1
wasmtime run --dir=. target/wasm32-wasip1/debug/examples/wasi_example.wasm -- database.db
SELECT * on very large tables can be memory-intensive. Prefer filtering with WHERE clauses and/or fetching data in smaller chunks using LIMIT / OFFSET whenever possible.The library is structured into several modules:
format: SQLite file format constants and structurespage: Page reading and parsingbtree: B-tree traversal for table data with cycle detectionrecord: SQLite record parsingvalue: Value types (NULL, INTEGER, REAL, TEXT, BLOB)database: Main database interfacelogging: Configurable logging systemerror: Error types and handlingLIMIT / OFFSET, or add selective WHERE conditions to minimize the rows materialized at once.count_table_rows() for efficient row counting without loading dataThe library provides comprehensive error handling:
use sqlite_wasm_reader::{Database, Error};
use sqlite_wasm_reader::query::SelectQuery;
match Database::open("database.db") {
Ok(mut db) => {
// Database opened successfully
}
Err(Error::Io(e)) => {
eprintln!("IO error: {}", e);
}
Err(Error::InvalidFormat(msg)) => {
eprintln!("Invalid SQLite format: {}", msg);
}
Err(Error::TableNotFound(table)) => {
eprintln!("Table not found: {}", table);
}
Err(e) => {
eprintln!("Other error: {}", e);
}
}
The library is designed for production use with several robustness features:
This project is licensed under Apache License, Version 2.0, (LICENSE or http://www.apache.org/licenses/LICENSE-2.0)
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.