Skip to content

feat(sql): add parameterized query support#6692

Open
Lucas61000 wants to merge 6 commits intoEventual-Inc:mainfrom
Lucas61000:issue-4156
Open

feat(sql): add parameterized query support#6692
Lucas61000 wants to merge 6 commits intoEventual-Inc:mainfrom
Lucas61000:issue-4156

Conversation

@Lucas61000
Copy link
Copy Markdown
Contributor

@Lucas61000 Lucas61000 commented Apr 15, 2026

Changes Made

Add parameterized query support to daft.sql() to prevent SQL injection attacks.

  • Add params argument supporting three parameter styles:
    • Auto-incremented: ? (sequential substitution)
    • Positional: $1, $2, $3... (indexed substitution)
    • Named: :name (named substitution from dict)
  • Validate that only one parameter style is used per query

Related Issues

Closes #4156

@github-actions github-actions bot added the feat label Apr 15, 2026
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 15, 2026

Greptile Summary

This PR adds parameterized query support to daft.sql() via a new params argument, implementing three placeholder styles (?, $n, :name) through Python-side string substitution before the SQL is forwarded to Daft's SQL engine.

  • P0 — SQL injection: _format_sql_value embeds strings without escaping internal single quotes. A value like \"' OR '1'='1\" becomes '' OR '1'='1' in the final SQL, defeating the stated injection-prevention goal entirely.
  • P1 — Broken doctests: All three parameterized docstring examples pass the params list/dict as the second positional argument, which binds to register_globals rather than params; $1/$2 and :name examples also show incorrect expected output.
  • P1 — ::TYPE cast false positives: The :(\\w+) regex matches inside PostgreSQL-style ::INTEGER casts, causing spurious "parameter not found" or "cannot mix styles" errors.

Confidence Score: 3/5

Not safe to merge: the feature's primary goal (injection prevention) is unmet, doctests are broken, and ::TYPE casts cause false errors.

A P0 security issue (unescaped single quotes enabling SQL injection), two P1 logic issues (broken docstring examples, ::TYPE cast false positives), and broken doctests that would fail make doctests in CI collectively block this PR.

daft/sql/sql.py — _format_sql_value (single-quote escaping), _apply_parameters (:(\w+) regex for casts), and all three parameterized docstring examples.

Security Review

  • SQL injection via unescaped single quotes (daft/sql/sql.py, _format_sql_value): string values are interpolated as '<value>' without escaping internal ' characters. A value such as \"' OR '1'='1\" renders as '' OR '1'='1', producing a tautology that returns every row. This directly contradicts the PR's stated purpose of preventing SQL injection.

Important Files Changed

Filename Overview
daft/sql/sql.py Adds _apply_parameters and _format_sql_value helpers plus a params arg to sql(); has a P0 SQL-injection bug (unescaped single quotes), P1 broken doctests, and P1 false-positive named-param detection on ::TYPE casts.
tests/sql/test_parameterized_queries.py Adds 8 tests covering all three placeholder styles and error cases with correct params= keyword usage, but no test covers injection-safety (strings with single quotes).

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["daft.sql(sql, params=...)"] --> B{params is None?}
    B -- Yes --> E[Pass SQL as-is to _sql_exec]
    B -- No --> C["_apply_parameters(sql, params)"]
    C --> D{type of params?}
    D -- dict --> F["re.sub replace_named\n⚠️ Also matches ::TYPE casts"]
    D -- list/tuple --> G{Detect style}
    G --> H[has_dollar]
    G --> I[has_question]
    G --> J["has_named\n⚠️ False-positive on ::TYPE"]
    H & I & J --> K{style_count > 1?}
    K -- Yes --> L[raise ValueError: Cannot mix]
    K -- No --> M{has_dollar?}
    M -- Yes --> N[replace_indexed]
    M -- No --> O[replace_question]
    N & O & F --> P["_format_sql_value\n⚠️ No single-quote escaping"]
    P --> Q[Substituted SQL]
    Q --> E
    E --> R[DataFrame result]
Loading

Reviews (1): Last reviewed commit: "feat(sql): add parameterized query suppo..." | Re-trigger Greptile

Comment thread daft/sql/sql.py Outdated
Comment thread daft/sql/sql.py
Comment thread daft/sql/sql.py Outdated
Comment thread daft/sql/sql.py Outdated
Lucas61000 and others added 5 commits April 15, 2026 11:27
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
- Fix doctest examples: use params= keyword argument and correct
  expected output (age >= 25 instead of age > 25 for Alice)
- Fix ::TYPE casts being mistaken for named parameters using
  negative lookbehind (?<!:)
- Raise ValueError when surplus ? parameters are provided
- Add tests for type cast detection and surplus parameters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parameterized queries to prevent SQL injection attacks

1 participant