{"record":{"id":"590c6784b9b12ccf","repo":"tursodatabase/turso","slug":"you-can-only-execute-one-statement-at-a-time","errorCode":null,"errorMessage":"You can only execute one statement at a time","messagePattern":"You can only execute one statement at a time","errorType":"exception","errorClass":"ProgrammingError","httpStatus":null,"severity":"error","filePath":"bindings/python/turso/lib.py","lineNumber":362,"sourceCode":"        has_cols = len(columns) > 0\n        return _Prepared(stmt=stmt, tail_index=tail_idx, has_columns=has_cols, column_names=columns)\n\n    def _raise_if_multiple_statements(self, sql: str, tail_index: int) -> None:\n        \"\"\"\n        Ensure there is no second statement after the first one; otherwise raise ProgrammingError.\n        \"\"\"\n        # Skip any trailing whitespace/comments after tail_index, and check if another statement exists.\n        rest = sql[tail_index:]\n        try:\n            nxt = self._conn.prepare_first(rest)\n            if nxt is not None:\n                # Clean-up the prepared second statement immediately\n                second_stmt, _ = nxt\n                try:\n                    second_stmt.finalize()\n                except Exception:\n                    pass\n                raise ProgrammingError(\"You can only execute one statement at a time\")\n        except ProgrammingError:\n            raise\n        except Exception as exc:  # noqa: BLE001\n            raise _map_turso_exception(exc)\n\n    @property\n    def in_transaction(self) -> bool:\n        try:\n            return not self._conn.get_auto_commit()\n        except Exception as exc:  # noqa: BLE001\n            raise _map_turso_exception(exc)\n\n    # Provide autocommit property for sqlite3-like API (optional)\n    @property\n    def autocommit(self) -> object | bool:\n        return self._autocommit_mode\n\n    @autocommit.setter","sourceCodeStart":344,"sourceCodeEnd":380,"githubUrl":"https://github.com/tursodatabase/turso/blob/bad083fafbefdeae9a42ec19bdaaad8918dcf411/bindings/python/turso/lib.py#L344-L380","documentation":"Cursor.execute() prepares exactly one statement: after preparing the first, _raise_if_multiple_statements prepares the remainder and raises ProgrammingError if another real statement exists. This matches stdlib sqlite3, whose execute() also rejects multi-statement strings. A trailing semicolon, whitespace, or comments after the single statement are skipped and do not trigger it.","triggerScenarios":"`cur.execute(\"INSERT INTO t VALUES (1); INSERT INTO t VALUES (2);\")`, `cur.execute(\"CREATE TABLE a(...); CREATE INDEX ...\")`, or feeding a multi-statement .sql dump/migration through execute() instead of executescript().","commonSituations":"Running schema migrations or seed scripts built by concatenating statements; code ported from drivers that permit multi-statement execute (e.g. some MySQL/postgres configs); iterating over a file read as one string.","solutions":["Use cursor.executescript(sql) for any multi-statement script — it iterates prepare_first until exhausted","Split the script into single statements and call execute() per statement when you need per-statement results or error attribution","If you intended one statement, remove the accidental second one (often a duplicated line or an embedded ';' inside a string literal built by hand)"],"exampleFix":"# before\ncur.execute(\"\"\"\n    CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);\n    CREATE INDEX idx_users_name ON users(name);\n\"\"\")\n\n# after\ncur.executescript(\"\"\"\n    CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT);\n    CREATE INDEX idx_users_name ON users(name);\n\"\"\")","handlingStrategy":"validation","validationCode":"def split_statements(sql: str) -> list[str]:\n    \"\"\"Naive splitter for scripts without ';' inside strings — use executescript otherwise.\"\"\"\n    return [s for s in sql.split(\";\") if s.strip()]\n\nstmts = split_statements(sql)\nif len(stmts) > 1:\n    cur.executescript(sql)      # multi-statement path\nelse:\n    cur.execute(sql)","typeGuard":null,"tryCatchPattern":"try:\n    cur.execute(sql)\nexcept ProgrammingError as e:\n    if \"one statement at a time\" in str(e):\n        cur.executescript(sql)  # intentional fallback to the script path\n    else:\n        raise","preventionTips":["Default to executescript() for anything read from a file or built by concatenation","Reserve execute() for single, known statements with parameter binding","Note that a single trailing semicolon is fine — only a second real statement triggers it"],"tags":["python","sql","db-api","multi-statement","executescript"],"backgroundTag":"multiple-statements-in-execute","analyzedSha":"bad083fafbefdeae9a42ec19bdaaad8918dcf411","analyzedAt":"2026-08-16T23:12:11.798Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}