RAW_SQL.md¶
Status: not shipped in 0.6.0
This document is a design draft for 0.8.0. First-class raw text() support
is not available yet. 0.5 works with SQLAlchemy Select / Table sources.
See Supported vs planned.
RowGuard Raw SQL Integration¶
Purpose¶
RowGuard primarily targets SQLAlchemy Core expressions. However, many applications use handwritten SQL for reporting, analytics, legacy systems, or performance tuning.
When shipped, RowGuard will support raw SQL while preserving its central guarantee:
Every returned row is explicitly validated against the requested Pydantic model.
Raw SQL is simply another execution source.
Design Philosophy¶
Raw SQL support should be:
Explicit
Safe
Parameterized
Observable
RowGuard should never parse, rewrite, or concatenate SQL strings.
SQLAlchemy remains responsible for SQL execution and parameter binding.
Architecture¶
SQL Text
│
▼
SQLAlchemy text()
│
▼
Bound Parameters
│
▼
Database
│
▼
Result Rows
│
▼
Row Adapter
│
▼
Pydantic Validation
Supported APIs¶
Preferred entry point:
from sqlalchemy import text
stmt = text(
"SELECT user_id AS id, display_name AS name, age "
"FROM users WHERE tenant_id=:tenant_id"
)
result = rowguard.execute(
session=session,
statement=stmt,
parameters={"tenant_id": tenant_id},
model=UserRead,
)
Result Shape¶
Raw SQL should expose predictable column names.
Preferred techniques:
SQL aliases
Explicit field maps
Example:
SELECT
user_id AS id,
display_name AS name
Parameter Binding¶
Always use bound parameters.
Good:
text("SELECT * FROM users WHERE id=:id")
Never:
f"SELECT * FROM users WHERE id={user_id}"
SQLRules Integration¶
Automatic SQLRules pushdown is disabled by default for arbitrary SQL because RowGuard cannot safely rewrite SQL text.
Pydantic validation still executes for every returned row.
Streaming¶
Raw SQL supports the same streaming interface as Core execution.
Rejection Handling¶
All standard rejection policies are supported:
raise
collect
skip
callback
quarantine
Diagnostics¶
Suggested diagnostics:
statement type
parameter names
result keys
pushdown disabled
field mappings
Parameter values should never be logged automatically.
Security¶
Recommendations:
Always use bound parameters.
Never concatenate SQL.
Restrict SQL to trusted application code.
Do not expose raw SQL from untrusted users.
Error Handling¶
Suggested hierarchy:
RowGuardError
└── RawSQLError
├── InvalidRawSQLShapeError
├── MissingResultColumnError
├── InvalidFieldMapError
└── RawSQLExecutionError
Testing¶
Tests should verify:
parameter binding
aliases
field maps
streaming
rejection handling
diagnostics
SQLite
PostgreSQL
MVP Scope¶
Initial support:
SQLAlchemy text()
Bound parameters
Row adaptation
Pydantic validation
Streaming
Rejection handling
Deferred:
SQL parsing
Query rewriting
Automatic SQLRules pushdown
Design Principles¶
SQLAlchemy owns SQL execution.
Validation always occurs after execution.
Parameterization is mandatory.
Raw SQL is explicit and opt-in.
Never rewrite arbitrary SQL.