Metadata-Version: 2.4
Name: queryguard-sql
Version: 0.1.3
Summary: SQL validation and policy enforcement for NL2SQL pipelines
Project-URL: Homepage, https://github.com/WajeehAlamoudi/QueryGuard
Project-URL: Repository, https://github.com/WajeehAlamoudi/QueryGuard
Project-URL: Issues, https://github.com/WajeehAlamoudi/QueryGuard/issues
Author-email: WajeehAlamoudi <wajeehalamoudi@outlook.com>
License: MIT License
        
        Copyright (c) 2026 WajeehAlamoudi
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: ai,database,llm,nl2sql,query-guard,security,sql,sql-safety,sql-validation
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Database
Classifier: Topic :: Security
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: sqlglot>=20.0.0
Provides-Extra: dev
Requires-Dist: build>=1.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: twine>=5.0; extra == 'dev'
Description-Content-Type: text/markdown

# QueryGuard SQL

<p align="center">
    <img src="https://capsule-render.vercel.app/api?type=rect&height=160&color=0:0f172a,100:1e3a8a&text=QueryGuard%20SQL&fontColor=00eaff&fontSize=40&fontAlign=50&fontAlignY=35&desc=SQL%20validation%20for%20LLM%20and%20NL2SQL%20pipelines&descAlign=50&descSize=15&descAlignY=70&animation=fadeIn&pattern=dots" alt="QueryGuard SQL banner">
</p>

<p align="center">
  <a href="https://pypi.org/project/queryguard-sql/">
    <img src="https://img.shields.io/pypi/v/queryguard-sql.svg" alt="PyPI version">
  </a>
  <a href="https://pypi.org/project/queryguard-sql/">
    <img src="https://img.shields.io/pypi/pyversions/queryguard-sql.svg" alt="Python versions">
  </a>
  <a href="https://github.com/WajeehAlamoudi/QueryGuard/blob/main/LICENSE">
    <img src="https://img.shields.io/pypi/l/queryguard-sql.svg" alt="License">
  </a>
  <a href="https://github.com/WajeehAlamoudi/QueryGuard">
    <img src="https://img.shields.io/badge/GitHub-QueryGuard-181717?logo=github" alt="GitHub repository">
  </a>
</p>


* ### QueryGuard SQL is a small Python library for validating SQL before execution.
* ### It is useful for LLM-generated SQL, NL2SQL apps, analytics assistants, and any workflow where SQL should be checked before it reaches the database.

Repository: [github.com/WajeehAlamoudi/QueryGuard](https://github.com/WajeehAlamoudi/QueryGuard)

## Install

```bash
pip install queryguard-sql
```

Import package:

```python
from queryguard import QueryGuard, GuardConfig
```

## Quick Use

```python
from queryguard import QueryGuard, GuardConfig

config = GuardConfig(
    database_type="mysql",
    allowed_tables=["users", "orders"],
    read_only=True,
    max_rows=100,
)

guard = QueryGuard(config)

result = guard.check("SELECT * FROM users")

print(result.allowed)
print(result.final_sql)
print(result.detected_tables)
print(result.errors)
```

Output:

```python
True
"SELECT * FROM users LIMIT 100"
["users"]
[]
```

## When To Use

Use QueryGuard SQL when you need to:

- check SQL generated by an LLM
- enforce read-only SQL
- allow or block specific tables
- validate columns using a schema
- add safe row limits
- suggest similar table names
- inspect SQL before database execution

## GuardConfig

`GuardConfig` defines the policy used by `QueryGuard`.

```python
config = GuardConfig(
    database_type="postgres",
    allowed_tables=["public.users", "public.orders"],
    blocked_tables=["public.payments"],
    read_only=True,
    allow_select_star=False,
    max_rows=500,
    schema={
        "public.users": ["id", "name", "email"],
        "public.orders": ["id", "user_id", "total"],
    },
    suggest_tables=True,
    auto_fix_tables=False,
)
```

### Parameters

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `database_type` | `str` | required | SQL dialect, such as `mysql`, `postgres`, `sqlite`, `bigquery`, `snowflake`, `tsql`. |
| `allowed_tables` | `list[str] \| None` | `None` | Tables that may be queried. `None` means allow all tables unless blocked. |
| `blocked_tables` | `list[str]` | `[]` | Tables that must always be blocked. This wins over `allowed_tables`. |
| `read_only` | `bool` | `True` | Allows only read-only query types when enabled. |
| `allow_select_star` | `bool` | `False` | Allows `SELECT *` when schema rules are active. |
| `max_rows` | `int \| None` | `1000` | Adds or lowers row limits. `None` disables limit rewriting. |
| `schema` | `dict[str, list[str]] \| None` | `None` | Known tables and allowed columns. |
| `suggest_tables` | `bool` | `True` | Suggests similar known table names. |
| `auto_fix_tables` | `bool` | `False` | Reserved for table auto-fix behavior. Prefer suggestions for now. |

### Table Rules

```python
GuardConfig(
    database_type="mysql",
    allowed_tables=["users"],
)
```

Allowed:

```sql
SELECT * FROM users
```

Blocked:

```sql
SELECT * FROM payments
```

Schema-qualified table names are strict:

```python
allowed_tables=["analytics.users"]
```

allows:

```sql
SELECT id FROM analytics.users
```

but not:

```sql
SELECT id FROM public.users
```

### Schema Rules

If `schema` is provided, QueryGuard can validate selected columns.

```python
GuardConfig(
    database_type="mysql",
    schema={
        "users": ["id", "name", "email"],
    },
)
```

Allowed:

```sql
SELECT id, name FROM users
```

Blocked:

```sql
SELECT password_hash FROM users
```

### SELECT Star

When `schema` is provided and `allow_select_star=False`, this is blocked:

```sql
SELECT * FROM users
```

This remains allowed:

```sql
SELECT COUNT(*) FROM users
```

If no schema is provided, `SELECT *` is allowed because no column policy is active.

### Suggestions

Suggestions use known tables from `allowed_tables` or `schema`.

```python
guard = QueryGuard(
    GuardConfig(
        database_type="mysql",
        allowed_tables=["users", "orders"],
        suggest_tables=True,
    )
)

result = guard.check("SELECT * FROM usres")

print(result.suggestions)
```

Output:

```python
{"usres": ["users"]}
```

## QueryGuard

`QueryGuard` receives a `GuardConfig` and uses it to validate SQL.

```python
guard = QueryGuard(config)
```

### Methods

| Method | Returns | Description |
| --- | --- | --- |
| `check(sql, raise_on_blocked=False)` | `GuardResult` | Full validation result. |
| `validate(sql)` | `str` | Returns safe SQL or raises if blocked. |
| `rewrite(sql)` | `str` | Returns rewritten SQL or raises if blocked. |
| `is_safe(sql)` | `bool` | Returns `True` when SQL is allowed. |
| `tables(sql)` | `list[str]` | Returns detected table names. |
| `check_many(sqls)` | `list[GuardResult]` | Checks many SQL strings. |
| `explain(sql)` | `str` | Human-readable explanation. |
| `set_policy(config)` | `None` | Replaces the current config. |

### Example

```python
result = guard.check("DELETE FROM users")

print(result.allowed)
print(result.errors)
```

Output:

```python
False
["DELETE is not allowed in read_only mode"]
```

## GuardResult

`guard.check()` returns a `GuardResult`.

```python
result.allowed          # bool
result.original_sql     # str
result.final_sql        # str | None
result.database_type    # str | None
result.detected_tables  # list[str]
result.errors           # list[str]
result.warnings         # list[str]
result.suggestions      # dict[str, list[str]]
```

Example:

```python
result = guard.check("SELECT * FROM users")

if result.allowed:
    db.execute(result.final_sql)
else:
    print(result.errors)
```

## Supported Dialects

Common supported dialect names:

```text
mysql
mariadb
postgres
postgresql
sqlite
bigquery
snowflake
duckdb
redshift
trino
presto
spark
tsql
sqlserver
mssql
oracle
clickhouse
```

QueryGuard SQL uses `sqlglot` for SQL parsing and generation.

## Security Note

QueryGuard SQL validates SQL structure and configured policy before execution.
It does not replace database permissions, parameterized queries, or
least-privilege database users.

For production systems, always keep database-side protections enabled.

## License

MIT License.
