SQL-first database access library for Python
Project description
sqlym
A SQL template engine for Python. Inspired by Java's Clione-SQL / Doma2, it provides a 2-way SQL parser and row-to-object mapping.
- SQL-first — Write SQL directly, not through an ORM. sqlym never auto-generates SQL
- 2-way SQL — SQL files remain directly executable by DB tools
- Zero dependencies — Core runs on the Python standard library only (Pydantic is optional)
- Flexible mapping — Auto-mapping for dataclass / Pydantic, or bring your own function
Quick Start
pip install sqlym
1. Define an Entity
from dataclasses import dataclass
from typing import Annotated
from sqlym import Column
@dataclass
class Employee:
id: int
name: Annotated[str, Column("EMP_NAME")] # when column name differs
dept_id: int | None = None
2. Write a SQL File
sql/employee/find_by_dept.sql:
SELECT
id,
EMP_NAME,
dept_id
FROM
employee
WHERE
id = /* $id */0
AND dept_id = /* $dept_id */1
AND status = /* $status */'active'
3. Query with Sqlym
import sqlite3
from sqlym import Sqlym
# Connect to database
conn = sqlite3.connect("example.db")
# Create Sqlym instance
db = Sqlym(conn, sql_dir="sql")
# Query with parameters (lines with None are automatically removed)
employees = db.query(Employee, "employee/find_by_dept.sql", {
"id": 100,
"dept_id": None, # this line is removed
"status": "active",
})
for emp in employees:
print(emp.name)
# Get a single record
employee = db.query_one(Employee, "employee/find_by_id.sql", {"id": 100})
# Execute INSERT/UPDATE/DELETE
affected = db.execute("employee/update.sql", {"id": 100, "status": "inactive"})
conn.commit()
For the full SQL syntax reference, see SQL Syntax.
Features
2-way SQL (Clione-SQL Style)
Parameters are written as SQL comments. The SQL file can be executed directly by DB tools.
-- None removes the line ($ prefix)
WHERE name = /* $name */'default'
-- None binds as NULL (no $ prefix)
WHERE name = /* name */'default'
Indent-based Parent-Child Relationships
When all children are removed, the parent is also removed.
WHERE
id = /* $id */0
AND (
status = /* $status1 */'a'
OR status = /* $status2 */'b'
)
-- If both status1 and status2 are None, the entire parenthesized block is
-- removed
Automatic IN Clause Expansion
WHERE dept_id IN /* $dept_ids */(1, 2, 3)
-- dept_ids=[10,20,30] → WHERE dept_id IN (?, ?, ?)
Error Message Settings
Errors raised during SQL parsing include the line number by default. If you want to hide the SQL snippet from error messages, disable it via config:
from sqlym.config import ERROR_INCLUDE_SQL, ERROR_MESSAGE_LANGUAGE
import sqlym.config as config
config.ERROR_INCLUDE_SQL = False
config.ERROR_MESSAGE_LANGUAGE = "en"
Set ERROR_MESSAGE_LANGUAGE to ja or en.
Mappers
# Auto-mapping (dataclass / Pydantic)
mapper = create_mapper(Employee)
# Manual mapping (when column names differ significantly)
mapper = create_mapper(Employee, mapper=lambda row: Employee(
id=row['EMP_ID'],
name=row['EMP_NM'],
dept_id=row['DEPT_CODE'],
))
Column Name Mapping
from typing import Annotated
from sqlym import Column, entity
@dataclass
class Employee:
# Per-field mapping
id: Annotated[int, Column("EMP_ID")]
name: Annotated[str, Column("EMP_NAME")]
# No mapping — uses field name as-is
email: str
# Or apply a naming convention
@entity(naming="snake_to_camel") # dept_id → deptId
@dataclass
class Employee:
dept_id: int # → deptId
RDBMS Support
Supports SQLite, PostgreSQL, MySQL, and Oracle.
| RDBMS | Driver | Placeholder | Extras |
|---|---|---|---|
| SQLite | sqlite3 (stdlib) | ? |
— |
| PostgreSQL | psycopg 3.1+ | %s |
sqlym[postgresql] |
| MySQL | PyMySQL 1.1+ | %s |
sqlym[mysql] |
| Oracle | python-oracledb 3.0+ | :name |
sqlym[oracle] |
For RDBMS other than SQLite, install with extras. The driver will be installed automatically.
pip install sqlym[postgresql]
| Feature | Description |
|---|---|
| LIKE escaping | Handles LIKE escape differences across databases |
| IN clause limit | Splits when exceeding Oracle's 1000-element limit |
| RDBMS-specific SQL file loading | Fallback: find.oracle.sql → find.sql |
When SQL syntax differs across databases, you can provide database-specific SQL files:
sql/employee/
├── find.sql # Common SQL
├── find.oracle.sql # Oracle-specific (loaded preferentially)
└── find.postgresql.sql # PostgreSQL-specific (loaded preferentially)
What sqlym Does Not Provide
sqlym is a SQL template engine. The following features are out of scope. Write SQL directly or combine with other libraries.
- SQL generation (INSERT/UPDATE/DELETE/UPSERT, etc.)
- Pagination SQL generation (
LIMIT/OFFSET,ROWNUM, etc.) - DDL management / migrations
- Connection management / connection pooling
- Transaction management
Acknowledgments
sqlym's 2-way SQL parser is based on the design of Clione-SQL by tauty. The four rules for line-based SQL processing, indent-driven parent-child relationships, and parameter comment syntax all originate from Clione-SQL.
The dialect design and RDBMS-specific behavior handling draw from Doma2 by the Doma Framework team.
We are grateful to both projects for their pioneering work in 2-way SQL.
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sqlym-0.3.0.tar.gz.
File metadata
- Download URL: sqlym-0.3.0.tar.gz
- Upload date:
- Size: 173.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1bcef0266ba69723fe7d137198ae2a2609139fc040e959358b3c8cdf6229f20e
|
|
| MD5 |
06e5d9f4aba6e208d3b449a01ef08402
|
|
| BLAKE2b-256 |
c0f3ea2b454cc16fc372daad183617ba67ae44e72c82145b6243b1c52750acee
|
Provenance
The following attestation bundles were made for sqlym-0.3.0.tar.gz:
Publisher:
release.yml on izuno4t/sqlym
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqlym-0.3.0.tar.gz -
Subject digest:
1bcef0266ba69723fe7d137198ae2a2609139fc040e959358b3c8cdf6229f20e - Sigstore transparency entry: 1005608772
- Sigstore integration time:
-
Permalink:
izuno4t/sqlym@e5bb7412f573d8929d340ab50f17c40582fa6874 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/izuno4t
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e5bb7412f573d8929d340ab50f17c40582fa6874 -
Trigger Event:
push
-
Statement type:
File details
Details for the file sqlym-0.3.0-py3-none-any.whl.
File metadata
- Download URL: sqlym-0.3.0-py3-none-any.whl
- Upload date:
- Size: 36.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5c02519ad3b2471a3a561a9e793e596a64c6fb0a443cb1e511d961ec79974d7b
|
|
| MD5 |
bc40946ee116e36a4eab666768e3bcca
|
|
| BLAKE2b-256 |
64dfabef30a547e402d843803d5b319feb6d6783b8fef18f19325d5900bbbc99
|
Provenance
The following attestation bundles were made for sqlym-0.3.0-py3-none-any.whl:
Publisher:
release.yml on izuno4t/sqlym
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
sqlym-0.3.0-py3-none-any.whl -
Subject digest:
5c02519ad3b2471a3a561a9e793e596a64c6fb0a443cb1e511d961ec79974d7b - Sigstore transparency entry: 1005608778
- Sigstore integration time:
-
Permalink:
izuno4t/sqlym@e5bb7412f573d8929d340ab50f17c40582fa6874 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/izuno4t
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@e5bb7412f573d8929d340ab50f17c40582fa6874 -
Trigger Event:
push
-
Statement type: