Metadata-Version: 2.4
Name: dbqa
Version: 0.2.0
Summary: Deterministic DB-schema exploration and safe querying toolkit for QA testers
Project-URL: Repository, https://github.com/ptuan21/db_qa
Project-URL: Issues, https://github.com/ptuan21/db_qa/issues
Author-email: tuanpham <arielpham.pioneerx@gmail.com>
License: MIT
License-File: LICENSE
Keywords: cli,database,qa,sql,sqlalchemy,testing
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
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 :: Software Development :: Testing
Requires-Python: >=3.10
Requires-Dist: networkx>=3.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.0
Requires-Dist: sqlalchemy>=2.0
Requires-Dist: sqlglot>=25.0
Requires-Dist: typer>=0.12
Provides-Extra: all
Requires-Dist: oracledb>=2.0; extra == 'all'
Requires-Dist: psycopg[binary]>=3.1; extra == 'all'
Requires-Dist: pymssql>=2.3; extra == 'all'
Requires-Dist: pymysql>=1.1; extra == 'all'
Provides-Extra: dev
Requires-Dist: pytest-cov>=5.0; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Provides-Extra: mssql
Requires-Dist: pymssql>=2.3; extra == 'mssql'
Provides-Extra: mysql
Requires-Dist: pymysql>=1.1; extra == 'mysql'
Provides-Extra: oracle
Requires-Dist: oracledb>=2.0; extra == 'oracle'
Provides-Extra: postgres
Requires-Dist: psycopg[binary]>=3.1; extra == 'postgres'
Description-Content-Type: text/markdown

# dbqa

Deterministic database-schema exploration and safe querying for QA testers.

`dbqa` answers the four questions testers keep hitting when they don't know
a project's schema well enough to test it:

1. To query this table, what data do I need from which other tables, and how are they linked?
2. How do I write the join correctly?
3. How do I query it safely, without accidentally writing, leaking sensitive columns, or running away?
4. How do I get valid test data for a table -- data that actually satisfies its foreign-key constraints?

No LLM involved -- everything is derived deterministically from the database's
own foreign-key metadata (with a YAML fallback for relationships the database
doesn't declare).

## Install

```
pip install dbqa
```

`dbqa` works against SQLite and PostgreSQL out of the box. For other engines, install the matching extra:

```
pip install "dbqa[postgres]"   # psycopg
pip install "dbqa[mysql]"      # pymysql
pip install "dbqa[mssql]"      # pymssql
pip install "dbqa[oracle]"     # python-oracledb (thin mode, no Oracle client needed)
pip install "dbqa[all]"        # all of the above
```

## Quickstart

Point `dbqa` at a database via a standard SQLAlchemy connection string:

```
export DBQA_DATABASE_URL="postgresql+psycopg://user:pass@host/dbname"
```

Then:

```
dbqa doctor                    # check connectivity, read-only enforcement, and lint dbqa.yaml
dbqa tables                    # list every table (optionally filtered: dbqa tables order)
dbqa schema                    # whole-database overview: every table and every FK edge at once
dbqa schema --mermaid          # same overview as a Mermaid flowchart, paste into any renderer
dbqa relations orders          # what tables does "orders" reference, and what references it?
dbqa path orders customers     # find the join path and print ready-to-run SQL
dbqa sample orders --n 10      # pull sample rows, safely
dbqa query orders --where "status = 'active'" --limit 20
dbqa fixture orders --id 42    # pull the minimal set of parent rows so order #42 is valid test data
```

Every command supports `--format table|json|csv` (default `table`) for scripting or piping into other tools.

Run `dbqa --help` or `dbqa <command> --help` for the full option list. Typing a table name that doesn't exist suggests close matches (`Unknown table: 'orderz'. Did you mean: orders, order_items?`).

## Safety

`dbqa query`/`dbqa sample` only ever run a single read-only `SELECT`:

- The query is built with SQLAlchemy, never string concatenation.
- Any `--where` fragment is parsed and rejected if it contains writes, DDL, subqueries, or known DoS-style function calls (`SLEEP`, `PG_SLEEP`, `WAITFOR`, ...).
- Row counts are hard-capped (`DBQA_MAX_ROWS`, default 1000).
- A statement timeout is applied where the engine supports it (`DBQA_STATEMENT_TIMEOUT_MS`, default 5000).

This is defense in depth, not a substitute for pointing `dbqa` at a database
user that only has `SELECT` privileges -- that's the durable guarantee.

## Config file (`dbqa.yaml`)

An optional YAML file in the working directory, or passed via `--relations-file` / `--config-file`:

```yaml
relationships:
  # declare a relationship the database itself doesn't have a real FK for
  - from_table: audit_logs
    from_columns: [customer_ref]
    to_table: customers
    to_columns: [id]

sensitive_columns:
  users.ssn: redact           # value replaced with ***REDACTED***
  users.email: partial_mask   # a***@example.com
  users.password_hash: exclude  # column is never selected at all
```

## Using it as a library

Every CLI command is a thin wrapper around a plain Python function, importable directly for use in `pytest`:

```python
from dbqa import get_engine, pull_fixture

def test_order_processing():
    engine = get_engine()  # reads DBQA_DATABASE_URL
    bundle = pull_fixture("orders", 42, engine=engine)
    customer = bundle.rows_for("customers")[0]
    ...
```

## Development

```
pip install -e ".[dev]"
pytest
```

## License

MIT
