Skip to content

Commit

Permalink
gh-95273: Improve sqlite3.complete_statement docs (#95840)
Browse files Browse the repository at this point in the history
Co-authored-by: Ezio Melotti <ezio.melotti@gmail.com>
Co-authored-by: CAM Gerlach <CAM.Gerlach@Gerlach.CAM>
  • Loading branch information
3 people committed Aug 11, 2022
1 parent 6f6a4e6 commit e6623e7
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 39 deletions.
33 changes: 0 additions & 33 deletions Doc/includes/sqlite3/complete_statement.py

This file was deleted.

23 changes: 17 additions & 6 deletions Doc/library/sqlite3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,25 @@ Module functions

.. function:: complete_statement(statement)

Returns ``True`` if the string *statement* contains one or more complete SQL
statements terminated by semicolons. It does not verify that the SQL is
syntactically correct, only that there are no unclosed string literals and the
statement is terminated by a semicolon.
Return ``True`` if the string *statement* appears to contain
one or more complete SQL statements.
No syntactic verification or parsing of any kind is performed,
other than checking that there are no unclosed string literals
and the statement is terminated by a semicolon.

This can be used to build a shell for SQLite, as in the following example:
For example::

.. literalinclude:: ../includes/sqlite3/complete_statement.py
>>> sqlite3.complete_statement("SELECT foo FROM bar;")
True
>>> sqlite3.complete_statement("SELECT foo")
False

This function may be useful during command-line input
to determine if the entered text seems to form a complete SQL statement,
or if additional input is needed before calling :meth:`~Cursor.execute`.

See :func:`!runsource` in :source:`Lib/sqlite3/__main__.py`
for real-world use.

.. function:: enable_callback_tracebacks(flag, /)

Expand Down
23 changes: 23 additions & 0 deletions Lib/sqlite3/__main__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
"""A simple SQLite CLI for the sqlite3 module.
Apart from using 'argparse' for the command-line interface,
this module implements the REPL as a thin wrapper around
the InteractiveConsole class from the 'code' stdlib module.
"""
import sqlite3
import sys

Expand All @@ -7,6 +13,14 @@


def execute(c, sql, suppress_errors=True):
"""Helper that wraps execution of SQL code.
This is used both by the REPL and by direct execution from the CLI.
'c' may be a cursor or a connection.
'sql' is the SQL string to execute.
"""

try:
for row in c.execute(sql):
print(row)
Expand All @@ -21,13 +35,19 @@ def execute(c, sql, suppress_errors=True):


class SqliteInteractiveConsole(InteractiveConsole):
"""A simple SQLite REPL."""

def __init__(self, connection):
super().__init__()
self._con = connection
self._cur = connection.cursor()

def runsource(self, source, filename="<input>", symbol="single"):
"""Override runsource, the core of the InteractiveConsole REPL.
Return True if more input is needed; buffering is done automatically.
Return False is input is a complete statement ready for execution.
"""
match source:
case ".version":
print(f"{sqlite3.sqlite_version}")
Expand Down Expand Up @@ -73,6 +93,7 @@ def main():
else:
db_name = repr(args.filename)

# Prepare REPL banner and prompts.
banner = dedent(f"""
sqlite3 shell, running on SQLite version {sqlite3.sqlite_version}
Connected to {db_name}
Expand All @@ -86,8 +107,10 @@ def main():
con = sqlite3.connect(args.filename, isolation_level=None)
try:
if args.sql:
# SQL statement provided on the command-line; execute it directly.
execute(con, args.sql, suppress_errors=False)
else:
# No SQL provided; start the REPL.
console = SqliteInteractiveConsole(con)
console.interact(banner, exitmsg="")
finally:
Expand Down

0 comments on commit e6623e7

Please sign in to comment.