Name | mintql JSON |
Version |
1.0.0b2
JSON |
| download |
home_page | None |
Summary | A fluent, type-safe SQL query builder library for building SQL queries safely and elegantly |
upload_time | 2025-07-17 19:52:17 |
maintainer | None |
docs_url | None |
author | Thomas Fazzari |
requires_python | <4.0,>=3.8 |
license | MIT |
keywords |
sql
query
builder
database
|
VCS |
 |
bugtrack_url |
|
requirements |
No requirements were recorded.
|
Travis-CI |
No Travis.
|
coveralls test coverage |
No coveralls.
|
# MintQL
**A minimalist SQL query builder for Python**
[](https://pypi.org/project/mintql/)
[](https://pypi.org/project/mintql/)
[](https://github.com/yourusername/mintql/blob/main/LICENSE)
---
## What is MintQL?
MintQL is a lightweight, type-safe SQL query builder designed to produce clean and parameterized SQL queries, helping to avoid common pitfalls such as string concatenation and injection vulnerabilities. **MintQL is NOT an ORM.**
### Why MintQL?
- **Tiny learning curve** - if you know SQL, you know MintQL
- **Zero dependencies** - built in pure Python
- **You keep control over your SQL** - see exactly what queries are generated
### Key Features
- **SQL injection protection** through parameterized queries
- **Type-safe** query building with IDE autocompletion
- **Multi-dialect support** (PostgreSQL, MySQL, SQLite)
- **Fluent API** for readable query construction
- **CTE support** including recursive CTEs
- **Zero dependencies** - lightweight and fast
## Installation
```bash
pip install mintql
```
**Requirements:**
- Python 3.8 or higher
- No external dependencies!
## Quick Start
```python
from mintql import pg
# Build your query
query = pg.select('name', 'email').from_('users').where('active', '=', True)
# Get SQL and parameters
sql, params = query.build()
# sql: "SELECT name, email FROM users WHERE active = $1"
# params: [True]
# Use with any PostgreSQL driver (psycopg2, asyncpg, etc.)
cursor.execute(sql, params)
```
## MintQL vs ORMs
### What MintQL IS:
- A **query builder** that generates clean, safe SQL
- A thin layer over SQL that adds safety and convenience
- Perfect for developers who want to write SQL without string manipulation
### What MintQL IS NOT:
- ❌ An ORM - no models, no migrations, no magic
- ❌ A database abstraction layer - you still write SQL logic
- ❌ A schema manager
## Basic Usage
### SELECT Queries
```python
from mintql import pg
# Simple SELECT
query = pg.select('*').from_('users')
# With conditions
query = (pg.select('id', 'name', 'email')
.from_('users')
.where('active', '=', True)
.where('age', '>=', 18)
.order_by('name'))
# Build and execute
sql, params = query.build()
# sql: "SELECT id, name, email FROM users WHERE active = $1 AND age >= $2 ORDER BY name ASC"
# params: [True, 18]
```
### INSERT Operations
```python
# Single insert
query = pg.insert('users').values({
'name': 'Jane Doe',
'email': 'jane@example.com',
'active': True
})
# With RETURNING (PostgreSQL)
query = (pg.insert('users')
.values({'name': 'John', 'email': 'john@example.com'})
.returning('id', 'created_at'))
```
### UPDATE Operations
```python
query = (pg.update('users')
.set({'last_login': 'NOW()', 'login_count': 'login_count + 1'})
.where('id', '=', user_id))
```
### DELETE Operations
```python
query = pg.delete('users').where('active', '=', False).where('created_at', '<', '2023-01-01')
```
## Integration Examples
### psycopg2 (PostgreSQL)
```python
import psycopg2
from mintql import pg
# Connect to your database
conn = psycopg2.connect("dbname=myapp user=postgres")
cur = conn.cursor()
# Build your query
query = (pg.select('u.name', 'COUNT(o.id) as order_count')
.from_('users u')
.left_join('orders o ON u.id = o.user_id')
.where('u.created_at', '>=', '2024-01-01')
.group_by('u.id', 'u.name')
.having('COUNT(o.id)', '>', 5))
# Execute
sql, params = query.build()
cur.execute(sql, params)
results = cur.fetchall()
```
### MySQL
```python
import mysql.connector
from mintql import mysql
conn = mysql.connector.connect(host='localhost', database='myapp')
cursor = conn.cursor()
query = mysql.select('*').from_('products').where('price', '<', 100)
sql, params = query.build()
# MySQL uses ? placeholders
cursor.execute(sql, params)
```
### SQLite
```python
import sqlite3
from mintql import sqlite
conn = sqlite3.connect('myapp.db')
cursor = conn.cursor()
query = sqlite.select('*').from_('users').limit(10)
sql, params = query.build()
cursor.execute(sql, params)
```
## Advanced Features
### CTEs (Common Table Expressions)
```python
# Simple CTE
subquery = (pg.select('department', 'AVG(salary) as avg_salary')
.from_('employees')
.group_by('department'))
query = (pg.select('*')
.with_('dept_averages', subquery)
.from_('dept_averages')
.where('avg_salary', '>', 50000))
# Recursive CTE
anchor = pg.select('id', 'name', 'manager_id', '0 as level').from_('employees').where('manager_id', 'is', None)
recursive = pg.select('e.id', 'e.name', 'e.manager_id', 'h.level + 1').from_('employees e').inner_join('hierarchy h ON e.manager_id = h.id')
query = (pg.select('*')
.with_recursive('hierarchy', anchor, recursive)
.from_('hierarchy')
.order_by('level'))
```
### Complex JOINs
```python
query = (pg.select('u.name', 'p.title', 'c.content')
.from_('users u')
.inner_join('posts p ON u.id = p.user_id')
.left_join('comments c ON p.id = c.post_id')
.where('u.active', '=', True)
.where('p.published', '=', True)
.order_by('p.created_at', 'DESC'))
```
### Using Operators & Functions
```python
from mintql import pg, O, F
# Operators
query = (pg.select('*')
.from_('users')
.where('age', O.BETWEEN, [18, 65])
.where('email', O.LIKE, '%@example.com')
.where('role', O.IN, ['admin', 'moderator']))
# Functions
query = (pg.select(
F.COUNT('*').as_('total'),
F.AVG('age').as_('avg_age'),
F.MAX('created_at').as_('latest')
)
.from_('users')
.where(F.YEAR('created_at'), '=', 2024))
```
## Safety Features
### SQL Injection Protection
MintQL automatically parameterizes all values:
```python
# User input is safely parameterized
user_input = "'; DROP TABLE users; --"
query = pg.select('*').from_('users').where('name', '=', user_input)
sql, params = query.build()
# sql: "SELECT * FROM users WHERE name = $1"
# params: ["'; DROP TABLE users; --"] # Safely escaped!
```
### Type Validation
```python
# ✅ Valid
query = pg.select('*').from_('users').where('id', 'IN', [1, 2, 3])
# ❌ Raises ValidationError
query = pg.select('*').from_('users').where('id', 'IN', 'not-a-list')
```
## Error Handling
```python
from mintql import pg
from mintql.exceptions import QueryError, ValidationError, UnsupportedOperationError
try:
# Invalid operator
query = pg.select('*').from_('users').where('age', 'INVALID_OP', 25)
except QueryError as e:
print(f"Query construction error: {e}")
try:
# Validation error
query = pg.select('*').from_('') # Empty table name
except ValidationError as e:
print(f"Validation error: {e}")
try:
# Unsupported operation for dialect
query = sqlite.select('*').from_('users').returning('id') # SQLite doesn't support RETURNING
except UnsupportedOperationError as e:
print(f"Not supported: {e}")
```
## Multi-Dialect Support
**PostgreSQL, MySQL, and SQLite**
MintQL handles dialect differences automatically:
```python
from mintql import MintQL
# Create dialect-specific instances
pg_query = MintQL.postgresql()
mysql_query = MintQL.mysql()
sqlite_query = MintQL.sqlite()
# Or use shortcuts
from mintql import pg, mysql, sqlite
# Same API, different SQL outputs
pg.select('*').from_('users').where('id', '=', 1)
# PostgreSQL: SELECT * FROM users WHERE id = $1
mysql.select('*').from_('users').where('id', '=', 1)
# MySQL: SELECT * FROM users WHERE id = ?
sqlite.select('*').from_('users').where('id', '=', 1)
# SQLite: SELECT * FROM users WHERE id = ?
```
## Full Documentation
Coming Soon
## When to use MintQL?
**Perfect for:**
- Projects where you want to write SQL but safely
- Applications with complex queries
- Teams that want readable, maintainable database code
- Scripts and tools that need simple database queries
**Consider alternatives for:**
- Projects that need full ORM features (relationships, lazy loading)
- Applications requiring automatic schema migration
## License
MintQL is MIT licensed. See [LICENSE](LICENSE) for details.
## Links
- [GitHub](https://github.com/thomasfazzari1/mintql)
- [PyPI](https://pypi.org/project/mintql/)
- Full Documentation (coming soon)
Raw data
{
"_id": null,
"home_page": null,
"name": "mintql",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.8",
"maintainer_email": null,
"keywords": "sql, query, builder, database",
"author": "Thomas Fazzari",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/b7/9d/fef184fbcdae4d146818d57a64ffdaabe5c9568722115ce68f1f4a33d1fd/mintql-1.0.0b2.tar.gz",
"platform": null,
"description": "# MintQL\n\n**A minimalist SQL query builder for Python**\n\n[](https://pypi.org/project/mintql/)\n[](https://pypi.org/project/mintql/)\n[](https://github.com/yourusername/mintql/blob/main/LICENSE)\n\n---\n\n## What is MintQL?\n\nMintQL is a lightweight, type-safe SQL query builder designed to produce clean and parameterized SQL queries, helping to avoid common pitfalls such as string concatenation and injection vulnerabilities. **MintQL is NOT an ORM.**\n\n### Why MintQL?\n- **Tiny learning curve** - if you know SQL, you know MintQL\n- **Zero dependencies** - built in pure Python\n- **You keep control over your SQL** - see exactly what queries are generated\n\n### Key Features\n- **SQL injection protection** through parameterized queries\n- **Type-safe** query building with IDE autocompletion \n- **Multi-dialect support** (PostgreSQL, MySQL, SQLite)\n- **Fluent API** for readable query construction\n- **CTE support** including recursive CTEs\n- **Zero dependencies** - lightweight and fast\n\n## Installation\n\n```bash\npip install mintql\n```\n\n**Requirements:**\n- Python 3.8 or higher\n- No external dependencies!\n\n## Quick Start\n\n```python\nfrom mintql import pg\n\n# Build your query\nquery = pg.select('name', 'email').from_('users').where('active', '=', True)\n\n# Get SQL and parameters\nsql, params = query.build()\n# sql: \"SELECT name, email FROM users WHERE active = $1\"\n# params: [True]\n\n# Use with any PostgreSQL driver (psycopg2, asyncpg, etc.)\ncursor.execute(sql, params)\n```\n\n## MintQL vs ORMs\n\n### What MintQL IS:\n- A **query builder** that generates clean, safe SQL\n- A thin layer over SQL that adds safety and convenience\n- Perfect for developers who want to write SQL without string manipulation\n\n### What MintQL IS NOT:\n- \u274c An ORM - no models, no migrations, no magic\n- \u274c A database abstraction layer - you still write SQL logic\n- \u274c A schema manager\n\n\n## Basic Usage\n\n### SELECT Queries\n\n```python\nfrom mintql import pg\n\n# Simple SELECT\nquery = pg.select('*').from_('users')\n\n# With conditions\nquery = (pg.select('id', 'name', 'email')\n .from_('users')\n .where('active', '=', True)\n .where('age', '>=', 18)\n .order_by('name'))\n\n# Build and execute\nsql, params = query.build()\n# sql: \"SELECT id, name, email FROM users WHERE active = $1 AND age >= $2 ORDER BY name ASC\"\n# params: [True, 18]\n```\n\n### INSERT Operations\n\n```python\n# Single insert\nquery = pg.insert('users').values({\n 'name': 'Jane Doe',\n 'email': 'jane@example.com',\n 'active': True\n})\n\n# With RETURNING (PostgreSQL)\nquery = (pg.insert('users')\n .values({'name': 'John', 'email': 'john@example.com'})\n .returning('id', 'created_at'))\n```\n\n### UPDATE Operations\n\n```python\nquery = (pg.update('users')\n .set({'last_login': 'NOW()', 'login_count': 'login_count + 1'})\n .where('id', '=', user_id))\n```\n\n### DELETE Operations\n\n```python\nquery = pg.delete('users').where('active', '=', False).where('created_at', '<', '2023-01-01')\n```\n\n## Integration Examples\n\n### psycopg2 (PostgreSQL)\n\n```python\nimport psycopg2\nfrom mintql import pg\n\n# Connect to your database\nconn = psycopg2.connect(\"dbname=myapp user=postgres\")\ncur = conn.cursor()\n\n# Build your query\nquery = (pg.select('u.name', 'COUNT(o.id) as order_count')\n .from_('users u')\n .left_join('orders o ON u.id = o.user_id')\n .where('u.created_at', '>=', '2024-01-01')\n .group_by('u.id', 'u.name')\n .having('COUNT(o.id)', '>', 5))\n\n# Execute\nsql, params = query.build()\ncur.execute(sql, params)\nresults = cur.fetchall()\n```\n\n### MySQL\n\n```python\nimport mysql.connector\nfrom mintql import mysql\n\nconn = mysql.connector.connect(host='localhost', database='myapp')\ncursor = conn.cursor()\n\nquery = mysql.select('*').from_('products').where('price', '<', 100)\nsql, params = query.build()\n\n# MySQL uses ? placeholders\ncursor.execute(sql, params)\n```\n\n### SQLite\n\n```python\nimport sqlite3\nfrom mintql import sqlite\n\nconn = sqlite3.connect('myapp.db')\ncursor = conn.cursor()\n\nquery = sqlite.select('*').from_('users').limit(10)\nsql, params = query.build()\n\ncursor.execute(sql, params)\n```\n\n## Advanced Features\n\n### CTEs (Common Table Expressions)\n\n```python\n# Simple CTE\nsubquery = (pg.select('department', 'AVG(salary) as avg_salary')\n .from_('employees')\n .group_by('department'))\n\nquery = (pg.select('*')\n .with_('dept_averages', subquery)\n .from_('dept_averages')\n .where('avg_salary', '>', 50000))\n\n# Recursive CTE\nanchor = pg.select('id', 'name', 'manager_id', '0 as level').from_('employees').where('manager_id', 'is', None)\nrecursive = pg.select('e.id', 'e.name', 'e.manager_id', 'h.level + 1').from_('employees e').inner_join('hierarchy h ON e.manager_id = h.id')\n\nquery = (pg.select('*')\n .with_recursive('hierarchy', anchor, recursive)\n .from_('hierarchy')\n .order_by('level'))\n```\n\n### Complex JOINs\n\n```python\nquery = (pg.select('u.name', 'p.title', 'c.content')\n .from_('users u')\n .inner_join('posts p ON u.id = p.user_id')\n .left_join('comments c ON p.id = c.post_id')\n .where('u.active', '=', True)\n .where('p.published', '=', True)\n .order_by('p.created_at', 'DESC'))\n```\n\n### Using Operators & Functions\n\n```python\nfrom mintql import pg, O, F\n\n# Operators\nquery = (pg.select('*')\n .from_('users')\n .where('age', O.BETWEEN, [18, 65])\n .where('email', O.LIKE, '%@example.com')\n .where('role', O.IN, ['admin', 'moderator']))\n\n# Functions\nquery = (pg.select(\n F.COUNT('*').as_('total'),\n F.AVG('age').as_('avg_age'),\n F.MAX('created_at').as_('latest')\n )\n .from_('users')\n .where(F.YEAR('created_at'), '=', 2024))\n```\n\n## Safety Features\n\n### SQL Injection Protection\n\nMintQL automatically parameterizes all values:\n\n```python\n# User input is safely parameterized\nuser_input = \"'; DROP TABLE users; --\"\nquery = pg.select('*').from_('users').where('name', '=', user_input)\n\nsql, params = query.build()\n# sql: \"SELECT * FROM users WHERE name = $1\"\n# params: [\"'; DROP TABLE users; --\"] # Safely escaped!\n```\n\n### Type Validation\n\n```python\n# \u2705 Valid\nquery = pg.select('*').from_('users').where('id', 'IN', [1, 2, 3])\n\n# \u274c Raises ValidationError\nquery = pg.select('*').from_('users').where('id', 'IN', 'not-a-list')\n```\n\n## Error Handling\n\n```python\nfrom mintql import pg\nfrom mintql.exceptions import QueryError, ValidationError, UnsupportedOperationError\n\ntry:\n # Invalid operator\n query = pg.select('*').from_('users').where('age', 'INVALID_OP', 25)\nexcept QueryError as e:\n print(f\"Query construction error: {e}\")\n\ntry:\n # Validation error\n query = pg.select('*').from_('') # Empty table name\nexcept ValidationError as e:\n print(f\"Validation error: {e}\")\n\ntry:\n # Unsupported operation for dialect\n query = sqlite.select('*').from_('users').returning('id') # SQLite doesn't support RETURNING\nexcept UnsupportedOperationError as e:\n print(f\"Not supported: {e}\")\n```\n\n## Multi-Dialect Support\n\n**PostgreSQL, MySQL, and SQLite**\n\nMintQL handles dialect differences automatically:\n\n```python\nfrom mintql import MintQL\n\n# Create dialect-specific instances\npg_query = MintQL.postgresql()\nmysql_query = MintQL.mysql()\nsqlite_query = MintQL.sqlite()\n\n# Or use shortcuts\nfrom mintql import pg, mysql, sqlite\n\n# Same API, different SQL outputs\npg.select('*').from_('users').where('id', '=', 1)\n# PostgreSQL: SELECT * FROM users WHERE id = $1\n\nmysql.select('*').from_('users').where('id', '=', 1) \n# MySQL: SELECT * FROM users WHERE id = ?\n\nsqlite.select('*').from_('users').where('id', '=', 1)\n# SQLite: SELECT * FROM users WHERE id = ?\n```\n\n## Full Documentation\n\nComing Soon\n\n## When to use MintQL?\n\n**Perfect for:**\n- Projects where you want to write SQL but safely\n- Applications with complex queries\n- Teams that want readable, maintainable database code\n- Scripts and tools that need simple database queries\n\n**Consider alternatives for:**\n- Projects that need full ORM features (relationships, lazy loading)\n- Applications requiring automatic schema migration\n\n## License\n\nMintQL is MIT licensed. See [LICENSE](LICENSE) for details.\n\n## Links\n\n- [GitHub](https://github.com/thomasfazzari1/mintql)\n- [PyPI](https://pypi.org/project/mintql/)\n- Full Documentation (coming soon)\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A fluent, type-safe SQL query builder library for building SQL queries safely and elegantly",
"version": "1.0.0b2",
"project_urls": {
"Homepage": "https://github.com/thomasfazzari1/mintql",
"Repository": "https://github.com/thomasfazzari1/mintql"
},
"split_keywords": [
"sql",
" query",
" builder",
" database"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "c3d48dbf7b5797ccfb03692fb97c849a5ea81e3b2a3e7a7860e9e9f449e15dc0",
"md5": "fa7163d8809b7bce6255dfda989854df",
"sha256": "c69fc487a39b1d504b2b5306c5c5ad248e33d814efc6936637e5e0fe93979068"
},
"downloads": -1,
"filename": "mintql-1.0.0b2-py3-none-any.whl",
"has_sig": false,
"md5_digest": "fa7163d8809b7bce6255dfda989854df",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.8",
"size": 22570,
"upload_time": "2025-07-17T19:52:15",
"upload_time_iso_8601": "2025-07-17T19:52:15.583030Z",
"url": "https://files.pythonhosted.org/packages/c3/d4/8dbf7b5797ccfb03692fb97c849a5ea81e3b2a3e7a7860e9e9f449e15dc0/mintql-1.0.0b2-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "b79dfef184fbcdae4d146818d57a64ffdaabe5c9568722115ce68f1f4a33d1fd",
"md5": "3323ab2c0ffe0371cde6d2e51dd5981a",
"sha256": "b4c5532d849eaebb41e46178a9a16fee1f0569f6c84083a4ac5214a0961a2859"
},
"downloads": -1,
"filename": "mintql-1.0.0b2.tar.gz",
"has_sig": false,
"md5_digest": "3323ab2c0ffe0371cde6d2e51dd5981a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.8",
"size": 22073,
"upload_time": "2025-07-17T19:52:17",
"upload_time_iso_8601": "2025-07-17T19:52:17.370862Z",
"url": "https://files.pythonhosted.org/packages/b7/9d/fef184fbcdae4d146818d57a64ffdaabe5c9568722115ce68f1f4a33d1fd/mintql-1.0.0b2.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-17 19:52:17",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "thomasfazzari1",
"github_project": "mintql",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"lcname": "mintql"
}