Getting Started
Start here for 0.4.0
If you want the short path through the biggest new features in this release, focus on: predicates, set operations, ordering, and optional MySQL execution.
Install
First Query
import sqlite3
from sqlstratum import SELECT, Table, col, SQLiteRunner
users = Table(
"users",
col("id", int),
col("email", str),
col("active", int),
)
conn = sqlite3.connect(":memory:")
runner = SQLiteRunner(conn)
runner.exec_ddl("CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT, active INTEGER)")
q = (
SELECT(users.c.id, users.c.email)
.FROM(users)
.WHERE(users.c.active.is_true())
.hydrate(dict)
)
rows = runner.fetch_all(q)
print(rows)
Compile For Another Dialect
SQLStratum keeps execution SQLite-first today, but you can compile SQL for supported dialects.
from sqlstratum import compile
compiled = compile(
SELECT(users.c.id, users.c.email).FROM(users).WHERE(users.c.id == 1),
dialect="mysql",
)
print(compiled.sql)
For SQLite-only features, bind intent explicitly:
from sqlstratum.sqlite import using_sqlite, TOTAL
q = using_sqlite(SELECT(TOTAL(users.c.id).AS("n")).FROM(users))
compiled = compile(q) # sqlite intent
For MySQL intent, bind explicitly as well:
from sqlstratum.mysql import using_mysql
q = using_mysql(SELECT(users.c.id, users.c.email).FROM(users))
compiled = compile(q, dialect="mysql")
See Dialect wrappers for behavior and guardrails.
0.4.0 Feature Tour
This release adds the pieces that make SQLStratum feel much more complete for application-level queries:
- portable predicates for list, range, and existence filtering
- set operations for cross-query composition
- explicit ordering helpers
- broader MySQL runtime and integration coverage
If you want the prose walkthrough instead of the short reference version on this page, read Latest release.
Ordering
Primary style:
from sqlstratum import ASC, DESC
q = (
SELECT(users.c.id, users.c.email)
.FROM(users)
.ORDER_BY(
DESC(users.c.created_at),
ASC(users.c.email),
ASC(users.c.id),
)
)
Alternative fluent style:
q = (
SELECT(users.c.id, users.c.email)
.FROM(users)
.ORDER_BY(users.c.id)
.ASC()
.THEN(users.c.email)
.DESC()
)
Mixed style is supported too:
q = (
SELECT(users.c.id, users.c.email)
.FROM(users)
.ORDER_BY(DESC(users.c.created_at), users.c.email)
.ASC()
)
ORDER_BY(...) with a bare expression requires a following .ASC() or .DESC() before compile/execute.
Predicates And Set Operations
from sqlstratum import EXISTS, NOT_EXISTS, SELECT
active_orgs = SELECT(orgs.c.id).FROM(orgs).WHERE(orgs.c.active == 1)
sub = SELECT(orgs.c.id).FROM(orgs).WHERE(orgs.c.id == users.c.org_id)
q = (
SELECT(users.c.id, users.c.email)
.FROM(users)
.WHERE(
users.c.org_id.IN(active_orgs),
users.c.age.BETWEEN(18, 65),
EXISTS(sub),
NOT_EXISTS(SELECT(orgs.c.id).FROM(orgs).WHERE(orgs.c.id == 9999)),
)
)
q2 = SELECT(users.c.id).FROM(users).UNION_ALL(SELECT(admins.c.id).FROM(admins))
Ordering also applies cleanly to set queries:
from sqlstratum import ASC, DESC
q3 = (
SELECT(users.c.id, users.c.email).FROM(users)
.UNION_ALL(SELECT(admins.c.id, admins.c.email).FROM(admins))
.ORDER_BY(DESC(users.c.email), ASC(users.c.id))
)
Optional MySQL Execution
Install optional connectors as needed:
pip install sqlstratum[pymysql]
pip install sqlstratum[asyncmy]
# or both
pip install sqlstratum[mysql]
These extras include cryptography, which is commonly required by MySQL 8 authentication schemes.
Then use MySQLRunner (sync) or AsyncMySQLRunner (async) for query execution.
from sqlstratum import SELECT, Table, col, MySQLRunner
users = Table("users", col("id", int), col("email", str))
runner = MySQLRunner.connect(
host="127.0.0.1",
port=3306,
user="app",
password="secret",
database="appdb",
)
rows = runner.fetch_all(SELECT(users.c.id, users.c.email).FROM(users))
print(rows)
URL form is also supported (and mutually exclusive with individual parameters):
Async URL form:
from sqlstratum import AsyncMySQLRunner
runner = await AsyncMySQLRunner.connect(
url="mysql+asyncmy://app:secret@127.0.0.1:3306/appdb"
)
Supported URL forms:
- SQLite: sqlite:///relative/path.db, sqlite:////absolute/path.db, sqlite:///:memory:
- MySQL sync: mysql://... or mysql+pymysql://...
- MySQL async: mysql://... or mysql+asyncmy://...
Note: URL query parameters/fragments are not supported yet.
URL Validation Matrix
| URL | Sync MySQLRunner | Async AsyncMySQLRunner | SQLiteRunner | Notes |
|---|---|---|---|---|
sqlite:///:memory: |
No | No | Yes | In-memory SQLite |
sqlite:///data/app.db |
No | No | Yes | Relative SQLite path |
sqlite:////var/lib/app.db |
No | No | Yes | Absolute SQLite path |
mysql://user:pass@127.0.0.1:3306/cities_db |
Yes | Yes | No | Generic MySQL scheme |
mysql+pymysql://user:pass@127.0.0.1:3306/cities_db |
Yes | No | No | Explicit sync driver |
mysql+asyncmy://user:pass@127.0.0.1:3306/cities_db |
No | Yes | No | Explicit async driver |
mysql://user:pass@127.0.0.1 |
No | No | No | Rejected: missing database |
mysql://user@127.0.0.1:3306/cities_db |
No | No | No | Rejected: missing password |
sqlite://localhost/data.db |
No | No | No | Rejected: hostname not allowed |
mysql://user:pass@127.0.0.1:3306/cities_db?charset=utf8mb4 |
No | No | No | Rejected: query params unsupported |
SQLStratum focuses on queries. DDL statements such as CREATE TABLE or ALTER TABLE are intended
to live in a complementary library with similar design goals that is currently in the works.
Capability Contract
For a concise matrix of portable vs dialect-specific behavior, see SQL profile.
For the narrative release overview of why these features landed together in 0.4.0, see
Latest release.