Skip to content

feat(backends): implement native prepared statements for PostgreSQL and SQLite#560

Merged
kcenon merged 1 commit into
mainfrom
feat/issue-557-native-prepared-statements
Apr 10, 2026
Merged

feat(backends): implement native prepared statements for PostgreSQL and SQLite#560
kcenon merged 1 commit into
mainfrom
feat/issue-557-native-prepared-statements

Conversation

@kcenon

@kcenon kcenon commented Apr 10, 2026

Copy link
Copy Markdown
Owner

What

Summary

Implements native wire-level prepared statement support in PostgreSQL and SQLite backends by overriding select_prepared() and execute_prepared() from the base interface added in PR #559.

Change Type

  • Feature (new functionality)

Affected Components

  • database/backends/postgresql_backend.h/.cpp — Native prepared statement overrides
  • database/backends/sqlite_backend.h/.cpp — Native prepared statement overrides
  • tests/security/sql_injection_test.cpp — Prepared statement injection tests

Why

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

Alternative Approaches Considered

  1. Template-based parameter packs — Rejected for ABI stability with virtual interface
  2. Separate prepared_statement object — Deferred to Phase 3 if caching is needed

Where

Files Changed

File Type of Change
database/backends/postgresql_backend.h Add override declarations
database/backends/postgresql_backend.cpp Implement select_prepared/execute_prepared via pqxx and libpq
database/backends/sqlite_backend.h Add override declarations
database/backends/sqlite_backend.cpp Implement via sqlite3_prepare_v2/sqlite3_bind_*
tests/security/sql_injection_test.cpp 5 new tests for prepared statement path

How

Implementation Details

  • PostgreSQL (pqxx): Uses pqxx::params + pqxx::work::exec_params() for wire-level binding
  • PostgreSQL (libpq): Uses PQexecParams() with paramValues array, letting server infer types
  • SQLite: Uses sqlite3_prepare_v2() → type-specific sqlite3_bind_*()sqlite3_step()
  • Mock mode: Falls back to base class string interpolation when backends are not compiled
  • All implementations follow existing error handling patterns (Result<T>, VoidResult)

Testing Done

  • 5 new prepared statement security tests added
  • Tests cover: correct results, injection blocking, typed params, batch injection, NULL handling
  • Existing tests unaffected (prepared methods are additive overrides)

Test Plan

  1. Run ctest --output-on-failure — all tests should pass
  2. Verify PreparedSelectBlocksInjection passes (injection treated as literal)
  3. Verify PreparedBatchStatementInjectionBlocked passes (table survives)

Breaking Changes

None — additive overrides only, existing execute_query/select_query unchanged

…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)
@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results

No comparison reports available. Baseline may not be established yet.

@kcenon kcenon merged commit 84d6df3 into main Apr 10, 2026
36 checks passed
@kcenon kcenon deleted the feat/issue-557-native-prepared-statements branch April 10, 2026 07:12
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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant