Skip to content

Commit 096b56b

Browse files
committed
Updated readme and cli
1 parent 159f934 commit 096b56b

12 files changed

Lines changed: 476 additions & 5 deletions

File tree

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ FROM rust:1.94.0-slim AS builder
44
RUN apt-get update && apt-get install -y \
55
pkg-config \
66
libssl-dev \
7+
libsqlite3-dev \
78
cmake \
89
protobuf-compiler \
910
g++ \
@@ -19,6 +20,7 @@ FROM debian:trixie-slim
1920

2021
RUN apt-get update && apt-get install -y \
2122
libssl3t64 \
23+
libsqlite3-0 \
2224
zlib1g \
2325
ca-certificates \
2426
&& rm -rf /var/lib/apt/lists/*

README.md

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Skardi lets AI agents and applications query files, databases, data lakes, and v
2828
- **CLI for local agents & queries** — Run SQL against local files, remote object stores (S3, GCS, Azure), databases, and datalake formats — ideal for local AI agents like [OpenClaw](https://github.com/openclaw/openclaw)
2929
- **Declarative pipelines** — Define SQL queries in YAML, get REST APIs automatically
3030
- **Automatic parameter inference** — Request parameters, types, and response schemas are inferred from your SQL
31-
- **Multi-source federation** — JOIN across CSV, Parquet, PostgreSQL, MySQL, MongoDB, Iceberg, and Lance in a single query
31+
- **Multi-source federation** — JOIN across CSV, Parquet, PostgreSQL, MySQL, SQLite, MongoDB, Iceberg, and Lance in a single query
3232
- **Full CRUD** — SELECT, INSERT, UPDATE, and DELETE operations on supported databases
3333
- **Vector search** — Native KNN similarity search via Lance integration
3434
- **S3 support** — Read CSV, Parquet, and Lance files directly from S3
@@ -52,6 +52,7 @@ Skardi lets AI agents and applications query files, databases, data lakes, and v
5252
- [Parquet](#parquet)
5353
- [PostgreSQL](#postgresql)
5454
- [MySQL](#mysql)
55+
- [SQLite](#sqlite)
5556
- [MongoDB](#mongodb)
5657
- [Apache Iceberg](#apache-iceberg)
5758
- [Lance (Vector Search)](#lance-vector-search)
@@ -132,7 +133,7 @@ skardi query --ctx ./ctx.yaml --schema -t products
132133
| Local files | CSV, Parquet, JSON/NDJSON, Lance |
133134
| Remote stores | S3, GCS, Azure Blob, HTTP/HTTPS, OSS, COS |
134135
| Datalake formats | Lance, Iceberg |
135-
| Databases | PostgreSQL, MySQL, MongoDB |
136+
| Databases | PostgreSQL, MySQL, SQLite, MongoDB |
136137

137138
**Context file resolution** (when `--ctx` is omitted): checks `SKARDICONFIG` env var, then `~/.skardi/config/ctx.yaml`. If no context file is found, the query runs without pre-registered tables (you can still query files directly by path).
138139

@@ -205,7 +206,7 @@ data_sources:
205206
206207
### Access Mode
207208
208-
By default, all data sources are **read-only** — only `SELECT` queries are allowed. To enable write operations (`INSERT`, `UPDATE`, `DELETE`), set `access_mode: read_write` on the data source. Only `postgres` and `mysql` sources support `read_write` mode; setting it on other types will produce an error at startup.
209+
By default, all data sources are **read-only** — only `SELECT` queries are allowed. To enable write operations (`INSERT`, `UPDATE`, `DELETE`), set `access_mode: read_write` on the data source. Only `postgres`, `mysql`, and `sqlite` sources support `read_write` mode; setting it on other types will produce an error at startup.
209210

210211
```yaml
211212
data_sources:
@@ -366,6 +367,28 @@ export MYSQL_PASSWORD="mypassword"
366367

367368
For detailed setup, CRUD examples, and federated queries, see [demo/mysql/MYSQL_DEMO.md](demo/mysql/MYSQL_DEMO.md).
368369

370+
### SQLite
371+
372+
Full CRUD support (SELECT, INSERT, UPDATE, DELETE) with no external server required — just a local `.db` file.
373+
374+
```yaml
375+
- name: "users"
376+
type: "sqlite"
377+
path: "data/my_database.db"
378+
options:
379+
table: "users"
380+
busy_timeout_ms: "5000" # Optional, default: 5000
381+
```
382+
383+
SQLite requires no credentials — just the path to the database file.
384+
385+
**CLI direct path query** (no context file needed):
386+
```bash
387+
skardi query --sql "SELECT * FROM './data/my_database.db.users'"
388+
```
389+
390+
For detailed setup, CRUD examples, and federated queries, see [demo/sqlite/SQLITE_DEMO.md](demo/sqlite/SQLITE_DEMO.md).
391+
369392
### MongoDB
370393

371394
Full CRUD support with point lookups, full scans, and federated queries.
@@ -587,6 +610,7 @@ The [demo/](demo/) directory contains complete working examples:
587610
| [demo/README.md](demo/README.md) | Product search demo (CSV/Parquet) |
588611
| [demo/postgres/](demo/postgres/) | PostgreSQL CRUD and federated query examples |
589612
| [demo/mysql/](demo/mysql/) | MySQL CRUD and federated query examples |
613+
| [demo/sqlite/](demo/sqlite/) | SQLite CRUD and federated query examples |
590614
| [demo/mongo/](demo/mongo/) | MongoDB CRUD and federated query examples |
591615
| [demo/iceberg/](demo/iceberg/) | Apache Iceberg integration examples |
592616
| [demo/lance/](demo/lance/) | Lance vector search examples |

crates/cli/README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Execute a SQL query or show table schema(s). Data sources can come from:
2929
- **Local files** — CSV, Parquet, JSON/NDJSON (directly by path in SQL or via context file)
3030
- **Remote files** — S3, GCS, Azure Blob, HTTP/HTTPS, OSS, COS (directly by URL in SQL or via context file)
3131
- **Datalake formats** — Lance (directly by path in SQL or via context file), Iceberg (via context file)
32-
- **Databases** — PostgreSQL, MySQL, MongoDB (via context file)
32+
- **Databases** — PostgreSQL, MySQL, SQLite, MongoDB (via context file or direct path for SQLite)
3333

3434
#### Query files directly (no context file needed)
3535

@@ -44,6 +44,10 @@ skardi query --sql "SELECT * FROM './data/logs.json'"
4444
# Lance datasets
4545
skardi query --sql "SELECT * FROM './embeddings.lance' LIMIT 5"
4646

47+
# SQLite tables (pattern: path/to/file.db.table_name)
48+
skardi query --sql "SELECT * FROM './data/my_database.db.users'"
49+
skardi query --sql "SELECT * FROM './data/app.sqlite.customers'"
50+
4751
# Remote files (S3, GCS, Azure)
4852
skardi query --sql "SELECT * FROM 's3://mybucket/data/events.parquet'"
4953
skardi query --sql "SELECT * FROM 'gs://mybucket/data.csv'"
@@ -153,6 +157,14 @@ data_sources:
153157
user_env: MYSQL_USER
154158
pass_env: MYSQL_PASS
155159

160+
# SQLite
161+
- name: users
162+
type: sqlite
163+
path: data/my_database.db
164+
options:
165+
table: users
166+
busy_timeout_ms: "5000" # Optional
167+
156168
# MongoDB
157169
- name: profiles
158170
type: mongo
@@ -174,6 +186,7 @@ data_sources:
174186
| `iceberg` | Apache Iceberg tables | Warehouse path (local or S3) |
175187
| `postgres` | PostgreSQL tables | `postgresql://host:port/db` |
176188
| `mysql` | MySQL tables | `mysql://host:port/db` |
189+
| `sqlite` | SQLite tables | Local file path (e.g. `data/my.db`) |
177190
| `mongo` | MongoDB collections | `mongodb://host:port` |
178191

179192
**Path resolution:** Relative paths in the context file are resolved relative to your **current working directory**.
@@ -258,6 +271,9 @@ skardi query --sql "SELECT * FROM 's3://mybucket/events.parquet' LIMIT 10"
258271
# Query a Lance dataset
259272
skardi query --sql "SELECT * FROM './embeddings.lance' LIMIT 5"
260273
274+
# Query a SQLite table directly
275+
skardi query --sql "SELECT * FROM './data/app.db.users' LIMIT 10"
276+
261277
# With context file
262278
cargo run -p skardi-cli -- query --ctx ./demo/ctx.yaml --sql "SELECT * FROM products LIMIT 5"
263279

crates/cli/src/main.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ use serde::Deserialize;
1919
use source::lance::knn_table_function::register_lance_knn_udtf;
2020
use source::providers::{
2121
iceberg::register_iceberg_table, lance::register_lance_table, mongo::register_mongo_tables,
22-
mysql::register_mysql_tables, sqlx::postgres::register_postgres_tables,
22+
mysql::register_mysql_tables, sqlite::register_sqlite_tables,
23+
sqlx::postgres::register_postgres_tables,
2324
};
2425
use std::collections::HashMap;
2526
use std::fmt;
@@ -244,6 +245,25 @@ impl UrlTableFactory for SkardiUrlTableFactory {
244245
&self,
245246
url: &str,
246247
) -> datafusion::error::Result<Option<Arc<dyn TableProvider>>> {
248+
// Handle SQLite databases: detect patterns like "path/to/file.db.table_name"
249+
let sqlite_extensions = [".db.", ".sqlite.", ".sqlite3."];
250+
if let Some(ext) = sqlite_extensions.iter().find(|ext| url.contains(*ext)) {
251+
let pos = url.find(ext).unwrap();
252+
let db_path = &url[..pos + ext.len() - 1]; // include .db but not trailing dot
253+
let table_name = &url[pos + ext.len()..];
254+
255+
if !table_name.is_empty() {
256+
let provider =
257+
source::providers::sqlite::create_sqlite_table_provider(db_path, table_name)
258+
.await
259+
.map_err(|e| {
260+
datafusion::error::DataFusionError::Execution(e.to_string())
261+
})?;
262+
263+
return Ok(Some(provider));
264+
}
265+
}
266+
247267
// Handle Lance datasets by path suffix
248268
if url.ends_with(".lance") || url.contains(".lance/") {
249269
let dataset = Dataset::open(url)
@@ -530,6 +550,23 @@ async fn register_source(
530550
.await
531551
.with_context(|| format!("Failed to register Lance '{}'", source.name))?;
532552
}
553+
"sqlite" => {
554+
let path_str = source
555+
.path
556+
.as_deref()
557+
.ok_or_else(|| anyhow::anyhow!("SQLite source '{}': path required", source.name))?;
558+
let resolved = resolve_path(path_str)?;
559+
560+
register_sqlite_tables(
561+
session_ctx,
562+
&source.name,
563+
&resolved,
564+
source.options.as_ref(),
565+
false,
566+
)
567+
.await
568+
.with_context(|| format!("Failed to register SQLite '{}'", source.name))?;
569+
}
533570
"iceberg" => {
534571
let path_str = source.path.as_deref().ok_or_else(|| {
535572
anyhow::anyhow!(

crates/source/src/providers/sqlite.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,29 @@ use std::sync::Arc;
3030
use std::time::Duration;
3131
use tokio_rusqlite::Connection;
3232

33+
/// Create a read-only SQLite table provider for a single table.
34+
pub async fn create_sqlite_table_provider(
35+
db_path: &str,
36+
table_name: &str,
37+
) -> Result<Arc<dyn TableProvider>> {
38+
let pool = Arc::new(
39+
SqliteConnectionPoolFactory::new(db_path, Mode::File, Duration::from_millis(5000))
40+
.build()
41+
.await
42+
.map_err(|e| anyhow::anyhow!("Failed to create SQLite connection pool: {}", e))?,
43+
);
44+
45+
let factory = SqliteTableFactory::new(Arc::clone(&pool));
46+
let table_ref = TableReference::bare(table_name);
47+
48+
let provider = factory
49+
.table_provider(table_ref)
50+
.await
51+
.map_err(|e| anyhow::anyhow!("Failed to create SQLite table provider: {}", e))?;
52+
53+
Ok(provider)
54+
}
55+
3356
/// Register SQLite tables into DataFusion SessionContext
3457
///
3558
/// # Arguments

0 commit comments

Comments
 (0)