Skip to content
Open
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
53 changes: 50 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ tokio-postgres = "0.7.16"
tokio = { version = "1.50.0", features = ["rt-multi-thread", "macros", "default"]}
async-recursion = "1.1.1"
bb8 = "0.9.1"
rusqlite = { version = "0.31", features = ["bundled"] }
log = "0.4.29"

[dev-dependencies]
Expand All @@ -39,3 +40,4 @@ predicates = "3.1.4"
tempfile = "3.27.0"
test_utils = { path="test-utils" }
pretty_assertions = "1.4.1"
rusqlite = { version = "0.31", features = ["bundled"] }
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rusqlite is added both under [dependencies] and [dev-dependencies] with the same version/features. Since the crate code now depends on rusqlite, the dev-dependency entry is redundant and can be removed to avoid duplication.

Suggested change
rusqlite = { version = "0.31", features = ["bundled"] }

Copilot uses AI. Check for mistakes.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
SQLx-ts is a CLI application featuring compile-time checked queries without a DSL and generates types against SQLs to keep your code type-safe

- **Compile time checked queries** - never ship a broken SQL query to production (and [sqlx-ts is not an ORM](https://github.com/JasonShin/sqlx-ts#sqlx-ts-is-not-an-orm))
- **TypeScript type generations** - generates type definitions based on the raw SQLs and you can use them with any MySQL or PostgreSQL driver
- **Database Agnostic** - support for [PostgreSQL](http://postgresql.org/) and [MySQL](https://www.mysql.com/) (and more DB supports to come)
- **TypeScript type generations** - generates type definitions based on the raw SQLs and you can use them with any MySQL, PostgreSQL, or SQLite driver
- **Database Agnostic** - support for [PostgreSQL](http://postgresql.org/), [MySQL](https://www.mysql.com/), and [SQLite](https://www.sqlite.org/)
- **TypeScript and JavaScript** - supports for both [TypeScript](https://jasonshin.github.io/sqlx-ts/reference-guide/4.typescript-types-generation.html) and [JavaScript](https://github.com/JasonShin/sqlx-ts#using-sqlx-ts-in-vanilla-javascript)

<br>
Expand Down
8 changes: 8 additions & 0 deletions book/docs/connect/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ $ sqlx-ts <path> --db-type postgres --db-url postgres://user:pass@localhost:5432
$ sqlx-ts <path> --db-type mysql --db-url mysql://user:pass@localhost:3306/mydb
```

#### SQLite

For SQLite, you only need to provide the database file path. No host, port, or user credentials are required:

```bash
$ sqlx-ts <path> --db-type sqlite --db-name ./mydb.sqlite
```

**Note:** When `--db-url` is provided, it takes precedence over individual connection parameters (`--db-host`, `--db-port`, `--db-user`, `--db-pass`, `--db-name`).

Run the following command for more details:
Expand Down
18 changes: 17 additions & 1 deletion book/docs/connect/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ Alternatively, you can use `DB_URL` to specify the connection string directly:
}
```

For SQLite, only `DB_TYPE` and `DB_NAME` (the file path) are required:

```json
{
"generate_types": {
"enabled": true
},
"connections": {
"default": {
"DB_TYPE": "sqlite",
"DB_NAME": "./mydb.sqlite"
}
}
}
```

## Configuration options

### connections (required)
Expand All @@ -92,7 +108,7 @@ const postgresSQL = sql`

Supported fields of each connection include
- `DB_URL`: Database connection URL (e.g. `postgres://user:pass@host:port/dbname` or `mysql://user:pass@host:port/dbname`). If provided, this overrides individual connection parameters (`DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASS`, `DB_NAME`)
- `DB_TYPE`: type of database connection (mysql | postgres)
- `DB_TYPE`: type of database connection (mysql | postgres | sqlite)
- `DB_USER`: database user name
- `DB_PASS`: database password
- `DB_HOST`: database host (e.g. 127.0.0.1)
Expand Down
13 changes: 12 additions & 1 deletion book/docs/connect/environment-variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
| DB_HOST | Primary DB host |
| DB_PASS | Primary DB password |
| DB_PORT | Primary DB port number |
| DB_TYPE | Type of primary database to connect [default: postgres] [possible values: postgres, mysql] |
| DB_TYPE | Type of primary database to connect [default: postgres] [possible values: postgres, mysql, sqlite] |
| DB_USER | Primary DB user name |
| DB_NAME | Primary DB name |
| PG_SEARCH_PATH | PostgreSQL schema search path (default is "$user,public") [https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH](https://www.postgresql.org/docs/current/ddl-schemas.html#DDL-SCHEMAS-PATH) |
Expand Down Expand Up @@ -47,3 +47,14 @@ sqlx-ts <path>

**Note:** When `DB_URL` is set, it takes precedence over individual connection parameters (`DB_HOST`, `DB_PORT`, `DB_USER`, `DB_PASS`, `DB_NAME`).

### SQLite

For SQLite, only `DB_TYPE` and `DB_NAME` (file path) are required:

```bash
export DB_TYPE=sqlite
export DB_NAME=./mydb.sqlite

sqlx-ts <path>
```

124 changes: 124 additions & 0 deletions playpen/db/sqlite_migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
-- SQLite Migration
-- This migration creates the same tables as the PostgreSQL and MySQL migrations
-- but using SQLite-compatible syntax.

-- Factions Table
CREATE TABLE IF NOT EXISTS factions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
description TEXT
);

-- Races Table
CREATE TABLE IF NOT EXISTS races (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
faction_id INTEGER REFERENCES factions(id) ON DELETE CASCADE
);

-- Classes Table
CREATE TABLE IF NOT EXISTS classes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
specialization TEXT
);

-- Characters Table
CREATE TABLE IF NOT EXISTS characters (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
race_id INTEGER REFERENCES races(id),
class_id INTEGER REFERENCES classes(id),
level INTEGER DEFAULT 1,
experience INTEGER DEFAULT 0,
gold REAL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- Guilds Table
CREATE TABLE IF NOT EXISTS guilds (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT UNIQUE NOT NULL,
description TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- Guild Members Table
CREATE TABLE IF NOT EXISTS guild_members (
guild_id INTEGER REFERENCES guilds(id) ON DELETE CASCADE,
character_id INTEGER REFERENCES characters(id) ON DELETE CASCADE,
rank TEXT,
joined_at DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (guild_id, character_id)
);

-- Inventory Table
CREATE TABLE IF NOT EXISTS inventory (
id INTEGER PRIMARY KEY AUTOINCREMENT,
character_id INTEGER REFERENCES characters(id) ON DELETE CASCADE,
quantity INTEGER DEFAULT 1
);

-- Items Table
CREATE TABLE IF NOT EXISTS items (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
rarity TEXT,
flavor_text TEXT,
inventory_id INTEGER REFERENCES inventory(id) ON DELETE CASCADE
);

-- Quests Table
CREATE TABLE IF NOT EXISTS quests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
description TEXT,
rewards TEXT,
completed BOOLEAN DEFAULT 0,
required_level INTEGER DEFAULT 1
);

-- Character Quests Table
CREATE TABLE IF NOT EXISTS character_quests (
character_id INTEGER REFERENCES characters(id) ON DELETE CASCADE,
quest_id INTEGER REFERENCES quests(id) ON DELETE CASCADE,
status TEXT DEFAULT 'In Progress',
PRIMARY KEY (character_id, quest_id)
);

-- Random types table for testing SQLite type mappings
CREATE TABLE IF NOT EXISTS random (
int1 INTEGER,
real1 REAL,
text1 TEXT,
blob1 BLOB,
numeric1 NUMERIC,
bool1 BOOLEAN,
date1 DATE,
datetime1 DATETIME,
float1 FLOAT,
double1 DOUBLE,
varchar1 VARCHAR(100),
char1 CHAR(10),
json1 JSON
);

--- SEED DATA

INSERT INTO factions (name, description) VALUES
('alliance', 'The noble and righteous faction'),
('horde', 'The fierce and battle-hardened faction');

INSERT INTO races (name, faction_id) VALUES
('human', 1),
('night elf', 1),
('dwarf', 1),
('gnome', 1),
('orc', 2),
('troll', 2),
('tauren', 2),
('undead', 2);

INSERT INTO classes (name, specialization) VALUES
('warrior', '{"role": "tank", "weapon": "sword", "abilities": ["charge", "slam", "shield block"]}'),
('hunter', '{"role": "ranged", "weapon": "bow", "abilities": ["aimed shot", "multi-shot", "trap"]}');
21 changes: 18 additions & 3 deletions src/common/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,9 @@ impl Config {
.or_else(|| default_config.map(|x| x.db_host.clone()))
};

let db_host = match (db_url.is_some(), db_host_chain()) {
let is_sqlite = matches!(db_type, DatabaseType::Sqlite);

let db_host = match (db_url.is_some() || is_sqlite, db_host_chain()) {
(true, Some(v)) => v,
(true, None) => String::new(),
(false, Some(v)) => v,
Expand All @@ -254,7 +256,7 @@ impl Config {
.or_else(|| default_config.map(|x| x.db_port))
};

let db_port = match (db_url.is_some(), db_port_chain()) {
let db_port = match (db_url.is_some() || is_sqlite, db_port_chain()) {
(true, Some(v)) => v,
(true, None) => 0,
(false, Some(v)) => v,
Expand All @@ -275,7 +277,7 @@ impl Config {
.or_else(|| default_config.map(|x| x.db_user.clone()))
};

let db_user = match (db_url.is_some(), db_user_chain()) {
let db_user = match (db_url.is_some() || is_sqlite, db_user_chain()) {
(true, Some(v)) => v,
(true, None) => String::new(),
(false, Some(v)) => v,
Expand Down Expand Up @@ -381,6 +383,19 @@ impl Config {
.to_string()
}

/// Returns the file path for a SQLite database connection.
/// If DB_URL is provided, it's used directly. Otherwise DB_NAME is used as the file path.
pub fn get_sqlite_path(&self, conn: &DbConnectionConfig) -> String {
if let Some(db_url) = &conn.db_url {
return db_url.to_owned();
}

conn
.db_name
.clone()
.unwrap_or_else(|| panic!("DB_NAME (file path) is required for SQLite connections"))
}
Comment on lines +386 to +397
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get_sqlite_path treats DB_URL as a filesystem path and passes it to rusqlite::Connection::open. This differs from the existing meaning of DB_URL as a full connection URL (postgres/mysql) and may surprise users who provide a sqlite://... style URL. Consider either documenting that SQLite expects a file path here, or parsing/handling common SQLite URL/URI formats explicitly.

Copilot uses AI. Check for mistakes.

pub fn get_postgres_cred(&self, conn: &DbConnectionConfig) -> String {
// If custom DB_URL is provided, use it directly
if let Some(db_url) = &conn.db_url {
Expand Down
12 changes: 5 additions & 7 deletions src/common/dotenv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,11 @@ impl Dotenv {
Dotenv {
db_type: match Self::get_var("DB_TYPE") {
None => None,
Some(val) => {
if val == "mysql" {
Some(DatabaseType::Mysql)
} else {
Some(DatabaseType::Postgres)
}
}
Some(val) => match val.as_str() {
"mysql" => Some(DatabaseType::Mysql),
"sqlite" => Some(DatabaseType::Sqlite),
_ => Some(DatabaseType::Postgres),
},
},
db_user: Self::get_var("DB_USER"),
db_host: Self::get_var("DB_HOST"),
Expand Down
Loading
Loading