tursodatabase/turso · error · ProgrammingError

no SQL statements to execute

Error message

no SQL statements to execute

What it means

Connection._prepare_first calls the native prepare_first, which returns None when the SQL string contains no statement — only whitespace and/or comments. In that case the wrapper raises DB-API ProgrammingError("no SQL statements to execute") instead of silently doing nothing, so empty SQL is surfaced as a bug in the caller.

Source

Thrown at bindings/python/turso/lib.py:331

        """
        try:
            stmt = self._conn.prepare_single(sql)
            _run_execute_with_io(stmt, self.extra_io)
            # finalize to ensure completion; finalize never mixes with execute
            stmt.finalize()
        except Exception as exc:  # noqa: BLE001
            raise _map_turso_exception(exc)

    def _prepare_first(self, sql: str) -> _Prepared:
        """
        Prepare the first statement in the given SQL string and return metadata.
        """
        try:
            opt = self._conn.prepare_first(sql)
        except Exception as exc:  # noqa: BLE001
            raise _map_turso_exception(exc)
        if opt is None:
            raise ProgrammingError("no SQL statements to execute")

        stmt, tail_idx = opt
        # Determine whether statement returns columns (rows)
        try:
            columns = tuple(stmt.columns())
        except Exception as exc:  # noqa: BLE001
            # Clean up statement before re-raising
            try:
                stmt.finalize()
            except Exception:
                pass
            raise _map_turso_exception(exc)
        has_cols = len(columns) > 0
        return _Prepared(stmt=stmt, tail_index=tail_idx, has_columns=has_cols, column_names=columns)

    def _raise_if_multiple_statements(self, sql: str, tail_index: int) -> None:
        """
        Ensure there is no second statement after the first one; otherwise raise ProgrammingError.

View on GitHub (pinned to bad083fafb)

Solutions

  1. Guard before executing: skip when the stripped SQL is empty or contains only comments
  2. Log the exact SQL string when this fires so the builder bug is obvious
  3. Fix the builder so it always produces at least one real statement, or make the empty case an explicit no-op in your own code

Example fix

# before
sql = build_query(filters)  # may return ""
cur.execute(sql)  # ProgrammingError: no SQL statements to execute

# after
sql = build_query(filters)
if sql.strip():
    cur.execute(sql)
Defensive patterns

Strategy: validation

Validate before calling

import re

def has_statement(sql: str) -> bool:
    """True if sql contains at least one real statement (not only whitespace/comments)."""
    no_line = re.sub(r"--[^\n]*", "", sql)
    no_block = re.sub(r"/\*.*?\*/", "", no_block, flags=re.S)
    return bool(no_block.strip())

if has_statement(sql):
    cur.execute(sql)

Try / catch

try:
    cur.execute(sql)
except ProgrammingError as e:
    if str(e) == "no SQL statements to execute":
        logger.warning("empty SQL produced by builder: %r", sql)
        return []
    raise

Prevention

When it happens

Trigger: `cur.execute("")`, `cur.execute(" ")`, `cur.execute("-- only a comment")`, `cur.execute("/* nothing */")`, or execute() of dynamically built SQL whose fragments concatenated to an empty/comment-only string.

Common situations: SQL builder/template code that conditionally appends clauses and ends up empty; input filtering that strips every clause; leftover debug placeholders; config-driven SQL where the config omitted the statement.

Related errors


AI-assisted analysis of tursodatabase/turso@bad083fafb (2026-08-16). Data as JSON: /api/errors/b6e46f130515f032. Report an issue: GitHub.