Skip to content

SQL predicate pushdown does not escape single quotes in string literals #7268

Description

@chuqCTC

Bug

Literal::display_sql (src/daft-core/src/lit/mod.rs, the Utf8 arm) renders a UTF-8 string as '{val}' without escaping embedded single quotes. When Daft pushes a predicate into a SQL source, Expr::to_sql() renders the predicate and SQLConnection.construct_sql_query (daft/sql/sql_connection.py) splices the result into the query sent to the source DB. A value containing a ' therefore produces malformed or silently altered SQL.

Failure modes

Silent wrong results:

daft.read_sql(..., conn).where(col("name") == lit("x' OR '1'='1"))

renders WHERE name = 'x' OR '1' = '1', so the source returns every row instead of the ones that match. The value is data, often sourced from another table, interpolated into SQL unescaped.

Tokenizer error on a legitimate value:

daft.read_sql(..., conn).where(col("name") == lit("O'Brien"))

renders WHERE name = 'O'Brien', which fails to tokenize (sqlglot TokenError) at query time.

Environment

  • Daft version: main (reproduces through current main)
  • OS: Linux
  • Python: 3.x
  • Runner: native

Proposed fix

Double embedded single quotes (the SQL-standard escape) in the Utf8 arm of display_sql. The predicate is re-rendered through sqlglot in the target dialect downstream, so display_sql only needs to emit a parseable standard-SQL literal; dialect-specific escaping is handled by the re-render. I can open a PR with the one-line change and a unit test.

Reported with Claude Code; repro verified against a SQLite source.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions