feat(backends): implement native prepared statements for PostgreSQL and SQLite#560
Merged
Merged
Conversation
…nd SQLite Override select_prepared() and execute_prepared() in both backends to use wire-level parameter binding instead of string interpolation: - PostgreSQL (pqxx): uses pqxx::params + exec_params() - PostgreSQL (libpq): uses PQexecParams() with paramValues array - SQLite: uses sqlite3_prepare_v2() + sqlite3_bind_*() + sqlite3_step() Add SQL injection tests verifying prepared statement path blocks injection attempts, handles typed parameters, and manages NULL values. Part of #557 (Phase 2 of 3)
7 tasks
Contributor
Benchmark ResultsNo comparison reports available. Baseline may not be established yet. |
4 tasks
kcenon
added a commit
that referenced
this pull request
Apr 13, 2026
…nd SQLite (#560) Override select_prepared() and execute_prepared() in both backends to use wire-level parameter binding instead of string interpolation: - PostgreSQL (pqxx): uses pqxx::params + exec_params() - PostgreSQL (libpq): uses PQexecParams() with paramValues array - SQLite: uses sqlite3_prepare_v2() + sqlite3_bind_*() + sqlite3_step() Add SQL injection tests verifying prepared statement path blocks injection attempts, handles typed parameters, and manages NULL values. Part of #557 (Phase 2 of 3)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Summary
Implements native wire-level prepared statement support in PostgreSQL and SQLite backends by overriding
select_prepared()andexecute_prepared()from the base interface added in PR #559.Change Type
Affected Components
database/backends/postgresql_backend.h/.cpp— Native prepared statement overridesdatabase/backends/sqlite_backend.h/.cpp— Native prepared statement overridestests/security/sql_injection_test.cpp— Prepared statement injection testsWhy
Problem Solved
Phase 1 (#559) added the prepared statement interface with string-interpolation fallback. This Phase 2 PR replaces the fallback with true wire-level parameter binding, providing defense-in-depth against SQL injection.
Related Issues
unified_database_systemto use prepared path by defaultAlternative Approaches Considered
prepared_statementobject — Deferred to Phase 3 if caching is neededWhere
Files Changed
database/backends/postgresql_backend.hdatabase/backends/postgresql_backend.cppselect_prepared/execute_preparedvia pqxx and libpqdatabase/backends/sqlite_backend.hdatabase/backends/sqlite_backend.cppsqlite3_prepare_v2/sqlite3_bind_*tests/security/sql_injection_test.cppHow
Implementation Details
pqxx::params+pqxx::work::exec_params()for wire-level bindingPQexecParams()withparamValuesarray, letting server infer typessqlite3_prepare_v2()→ type-specificsqlite3_bind_*()→sqlite3_step()Result<T>,VoidResult)Testing Done
Test Plan
ctest --output-on-failure— all tests should passPreparedSelectBlocksInjectionpasses (injection treated as literal)PreparedBatchStatementInjectionBlockedpasses (table survives)Breaking Changes
None — additive overrides only, existing
execute_query/select_queryunchanged