A simple and extensible SQL migration and loader utility for Python.
Project description
sqloader
A lightweight Python utility for managing SQL migrations and loading SQL from JSON or .sql files. Supports MySQL, PostgreSQL, and SQLite with clean integration for any Python backend (e.g., FastAPI).
Installation
# Basic installation (MySQL sync only)
pip install sqloader
# With PostgreSQL support
pip install sqloader[postgresql]
# With async MySQL support
pip install sqloader[async-mysql]
# With async PostgreSQL support
pip install sqloader[async-postgresql]
# With async SQLite support
pip install sqloader[async-sqlite]
# Install all optional dependencies
pip install sqloader[all]
Features
- Easy database migration management
- Load SQL queries from
.jsonor.sqlfiles - Supports MySQL, PostgreSQL, and SQLite
- Integrated execution:
sqloader.execute(),sqloader.fetch_one(),sqloader.fetch_all() - Thread-safe connection pooling (Semaphore + psycopg2 SimpleConnectionPool)
- Transaction context manager with automatic commit / rollback
- Async support:
asyncpg,aiomysql,aiosqlite - Async integrated execution:
await sqloader.async_execute(),await sqloader.async_fetchone(),await sqloader.async_fetchall() - Query file sync: copy
.json/.sqlfiles between DB directories (sync(),sync_fromconfig, CLI)
Quickstart
MySQL
from sqloader.init import database_init
config = {
"type": "mysql",
"placeholder": ["?", "%s"],
"mysql": {
"host": "localhost",
"port": 3306,
"user": "root",
"password": "pass",
"database": "mydb"
},
"service": {
"sqloder": "res/sql/sqloader/mysql"
},
"migration": {
"auto_migration": True,
"migration_path": "res/sql/migration/mysql"
},
}
db, sq, migrator = database_init(config)
# Classic usage
query = sq.load_sql("user", "get_user_by_id")
result = db.fetch_one(query, [123])
# Integrated usage (SQLoader runs the query directly)
result = sq.fetch_one("user", "get_user_by_id", [123])
rows = sq.fetch_all("user", "get_all")
sq.execute("user", "update_name", ["Alice", 123])
PostgreSQL
config = {
"type": "postgresql",
"postgresql": {
"host": "localhost",
"port": 5432,
"user": "postgres",
"password": "pass",
"database": "mydb",
"max_parallel_queries": 10 # optional, default 5
},
"service": {
"sqloder": "res/sql/sqloader/postgresql"
},
"migration": {
"auto_migration": True,
"migration_path": "res/sql/migration/postgresql"
},
}
db, sq, migrator = database_init(config)
result = sq.fetch_one("user", "get_user_by_id", [123])
SQLite
config = {
"type": "sqlite3",
"sqlite3": {
"db_name": "local.db"
},
"service": {
"sqloder": "res/sql/sqloader/sqlite"
},
}
db, sq, migrator = database_init(config)
Async Usage
Async PostgreSQL (FastAPI example)
from sqloader.init import async_database_init
config = {
"type": "postgresql",
"postgresql": {
"host": "localhost",
"port": 5432,
"user": "postgres",
"password": "pass",
"database": "mydb",
"max_size": 10
},
"service": {
"sqloder": "res/sql/sqloader/postgresql"
},
}
db, sq = await async_database_init(config)
# Integrated async usage
result = await sq.async_fetchone("user", "get_user_by_id", [123])
rows = await sq.async_fetchall("user", "get_all")
await sq.async_execute("user", "update_name", ["Alice", 123])
# Direct wrapper usage
result = await db.fetchone("SELECT * FROM users WHERE id = $1", [123])
Async MySQL
config = {
"type": "mysql",
"mysql": {
"host": "localhost",
"port": 3306,
"user": "root",
"password": "pass",
"database": "mydb"
},
"service": {
"sqloder": "res/sql/sqloader/mysql"
},
}
db, sq = await async_database_init(config)
result = await sq.async_fetchone("user", "get_user_by_id", [123])
Async Transaction
async with db.begin_transaction() as txn:
await txn.execute("INSERT INTO users (name) VALUES (%s)", ["Alice"])
await txn.execute("UPDATE stats SET count = count + 1")
SQL Loading Behavior
If a value in the .json file ends with .sql, the referenced file is loaded from the same directory.
Otherwise the value is used directly as a SQL string.
user.json
{
"get_user_by_id": "SELECT * FROM users WHERE id = %s",
"get_all": "user_all.sql",
"admin": {
"bulk_delete": "DELETE FROM users WHERE id = %s"
}
}
# Simple key
sq.fetch_one("user", "get_user_by_id", [123])
# Nested key (dot notation)
sq.execute("user", "admin.bulk_delete", [999])
Transaction
with db.begin_transaction() as txn:
txn.execute("INSERT INTO users (name) VALUES (%s)", ["Alice"])
txn.execute("UPDATE stats SET count = count + 1")
# Commits automatically on success, rolls back on exception
Migration
Migration files are applied in filename-sorted order. Already applied files are skipped.
res/sql/migration/
001_create_users.sql
002_add_index.sql
Set auto_migration: True to apply pending migrations automatically during database_init().
Query File Sync
Copy .json and .sql query files from one DB directory to another. Useful when you want to
share a base set of queries across multiple database backends.
Directory structure
res/sql/sqloader/
├── sqlite3/
│ ├── user.json
│ ├── shared_link.json
│ └── sub/
│ └── detail.json
├── mysql/
│ └── user.json ← already exists
└── postgresql/ ← empty
Manual call
sq = SQLoader("res/sql/sqloader")
# Copy sqlite3 → mysql (skip existing files)
result = sq.sync("sqlite3", "mysql")
# {"copied": ["shared_link.json", "sub\\detail.json"], "skipped": ["user.json"]}
# Copy sqlite3 → postgresql (overwrite existing files)
result = sq.sync("sqlite3", "postgresql", overwrite=True)
Config-based auto sync (database_init)
Add sync_from to your config. The sync runs automatically before migration.
config = {
"type": "mysql",
"sync_from": "sqlite3", # sqlite3 → mysql on every init
"mysql": { "host": "localhost", ... },
"service": { "sqloder": "res/sql/sqloader" },
}
db, sq, migrator = database_init(config)
# Sync complete: 2 copied, 1 skipped
CLI
# Basic sync
python -m sqloader sync --from sqlite3 --to mysql
# With custom path
python -m sqloader sync --from sqlite3 --to mysql --path res/sql/sqloader
# Overwrite existing files
python -m sqloader sync --from sqlite3 --to postgresql --overwrite --path res/sql/sqloader
Output:
Synced sqlite3 -> mysql
Copied: 2 files
- shared_link.json
- sub\detail.json
Skipped: 1 files
- user.json
SQLoader Standalone Usage
from sqloader import SQLoader
from sqloader.mysql import MySqlWrapper
db = MySqlWrapper(host="localhost", user="root", password="pass", db="mydb")
sq = SQLoader("res/sql", db_type=2, db=db) # db_type: MYSQL=2, POSTGRESQL=3, SQLITE=1
# Or inject later
sq = SQLoader("res/sql", db_type=2)
sq.set_db(db)
Dependencies
Required
| Package | Purpose |
|---|---|
pymysql >= 1.1.1 |
MySQL (sync) |
sqlite3 |
SQLite sync (Python standard library) |
Optional
Install only what you need:
| Package | Purpose | Install with |
|---|---|---|
psycopg2-binary >= 2.9.0 |
PostgreSQL (sync) | pip install sqloader[postgresql] |
aiomysql >= 0.2.0 |
MySQL (async) | pip install sqloader[async-mysql] |
asyncpg >= 0.29.0 |
PostgreSQL (async) | pip install sqloader[async-postgresql] |
aiosqlite >= 0.20.0 |
SQLite (async) | pip install sqloader[async-sqlite] |
Project details
Release history Release notifications | RSS feed
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 sqloader-0.2.13.tar.gz.
File metadata
- Download URL: sqloader-0.2.13.tar.gz
- Upload date:
- Size: 31.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82a8122964b9a3020d5d15d1d4120bcab4c99ca7d98ba8357cc993df2db55e57
|
|
| MD5 |
b35281319d190c3357ef141fb0ad92ff
|
|
| BLAKE2b-256 |
d51c20026f78f8adf451aef291815a6a79328a4c07217d257652345c4bbc04ec
|
File details
Details for the file sqloader-0.2.13-py3-none-any.whl.
File metadata
- Download URL: sqloader-0.2.13-py3-none-any.whl
- Upload date:
- Size: 25.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
93a01b4c6ba494104d3f75ff03bdaa697fd64d851d3ef76e7a03acc99cad51be
|
|
| MD5 |
76dfea670e6377e20edde023aeae4d5f
|
|
| BLAKE2b-256 |
e3692e6ae4af874e2c060fe35b7904c35a980df8185e5705dfdf347ac51a3e05
|