mintql


Namemintql JSON
Version 1.0.0b2 PyPI version JSON
download
home_pageNone
SummaryA fluent, type-safe SQL query builder library for building SQL queries safely and elegantly
upload_time2025-07-17 19:52:17
maintainerNone
docs_urlNone
authorThomas Fazzari
requires_python<4.0,>=3.8
licenseMIT
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**

[![PyPI - Version](https://img.shields.io/pypi/v/mintql?color=green)](https://pypi.org/project/mintql/)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/mintql)](https://pypi.org/project/mintql/)
[![License](https://img.shields.io/badge/license-MIT-blue)](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[![PyPI - Version](https://img.shields.io/pypi/v/mintql?color=green)](https://pypi.org/project/mintql/)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/mintql)](https://pypi.org/project/mintql/)\n[![License](https://img.shields.io/badge/license-MIT-blue)](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"
}
        
Elapsed time: 1.22662s