mirror of
https://github.com/python/cpython
synced 2024-09-15 23:57:10 +00:00
gh-95273: Improve sqlite3.complete_statement docs (#95840)
Co-authored-by: Ezio Melotti <ezio.melotti@gmail.com> Co-authored-by: CAM Gerlach <CAM.Gerlach@Gerlach.CAM>
This commit is contained in:
parent
6f6a4e6cc5
commit
e6623e7083
|
@ -1,33 +0,0 @@
|
||||||
# A minimal SQLite shell for experiments
|
|
||||||
|
|
||||||
import sqlite3
|
|
||||||
|
|
||||||
con = sqlite3.connect(":memory:")
|
|
||||||
con.isolation_level = None
|
|
||||||
cur = con.cursor()
|
|
||||||
|
|
||||||
buffer = ""
|
|
||||||
|
|
||||||
print("Enter your SQL commands to execute in sqlite3.")
|
|
||||||
print("Enter a blank line to exit.")
|
|
||||||
|
|
||||||
while True:
|
|
||||||
line = input()
|
|
||||||
if line == "":
|
|
||||||
break
|
|
||||||
buffer += line
|
|
||||||
if sqlite3.complete_statement(buffer):
|
|
||||||
try:
|
|
||||||
buffer = buffer.strip()
|
|
||||||
cur.execute(buffer)
|
|
||||||
|
|
||||||
if buffer.lstrip().upper().startswith("SELECT"):
|
|
||||||
print(cur.fetchall())
|
|
||||||
except sqlite3.Error as e:
|
|
||||||
err_msg = str(e)
|
|
||||||
err_code = e.sqlite_errorcode
|
|
||||||
err_name = e.sqlite_errorname
|
|
||||||
print(f"{err_name} ({err_code}): {err_msg}")
|
|
||||||
buffer = ""
|
|
||||||
|
|
||||||
con.close()
|
|
|
@ -222,14 +222,25 @@ Module functions
|
||||||
|
|
||||||
.. function:: complete_statement(statement)
|
.. function:: complete_statement(statement)
|
||||||
|
|
||||||
Returns ``True`` if the string *statement* contains one or more complete SQL
|
Return ``True`` if the string *statement* appears to contain
|
||||||
statements terminated by semicolons. It does not verify that the SQL is
|
one or more complete SQL statements.
|
||||||
syntactically correct, only that there are no unclosed string literals and the
|
No syntactic verification or parsing of any kind is performed,
|
||||||
statement is terminated by a semicolon.
|
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, /)
|
.. function:: enable_callback_tracebacks(flag, /)
|
||||||
|
|
||||||
|
|
|
@ -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 sqlite3
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -7,6 +13,14 @@
|
||||||
|
|
||||||
|
|
||||||
def execute(c, sql, suppress_errors=True):
|
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:
|
try:
|
||||||
for row in c.execute(sql):
|
for row in c.execute(sql):
|
||||||
print(row)
|
print(row)
|
||||||
|
@ -21,6 +35,7 @@ def execute(c, sql, suppress_errors=True):
|
||||||
|
|
||||||
|
|
||||||
class SqliteInteractiveConsole(InteractiveConsole):
|
class SqliteInteractiveConsole(InteractiveConsole):
|
||||||
|
"""A simple SQLite REPL."""
|
||||||
|
|
||||||
def __init__(self, connection):
|
def __init__(self, connection):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
@ -28,6 +43,11 @@ def __init__(self, connection):
|
||||||
self._cur = connection.cursor()
|
self._cur = connection.cursor()
|
||||||
|
|
||||||
def runsource(self, source, filename="<input>", symbol="single"):
|
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:
|
match source:
|
||||||
case ".version":
|
case ".version":
|
||||||
print(f"{sqlite3.sqlite_version}")
|
print(f"{sqlite3.sqlite_version}")
|
||||||
|
@ -73,6 +93,7 @@ def main():
|
||||||
else:
|
else:
|
||||||
db_name = repr(args.filename)
|
db_name = repr(args.filename)
|
||||||
|
|
||||||
|
# Prepare REPL banner and prompts.
|
||||||
banner = dedent(f"""
|
banner = dedent(f"""
|
||||||
sqlite3 shell, running on SQLite version {sqlite3.sqlite_version}
|
sqlite3 shell, running on SQLite version {sqlite3.sqlite_version}
|
||||||
Connected to {db_name}
|
Connected to {db_name}
|
||||||
|
@ -86,8 +107,10 @@ def main():
|
||||||
con = sqlite3.connect(args.filename, isolation_level=None)
|
con = sqlite3.connect(args.filename, isolation_level=None)
|
||||||
try:
|
try:
|
||||||
if args.sql:
|
if args.sql:
|
||||||
|
# SQL statement provided on the command-line; execute it directly.
|
||||||
execute(con, args.sql, suppress_errors=False)
|
execute(con, args.sql, suppress_errors=False)
|
||||||
else:
|
else:
|
||||||
|
# No SQL provided; start the REPL.
|
||||||
console = SqliteInteractiveConsole(con)
|
console = SqliteInteractiveConsole(con)
|
||||||
console.interact(banner, exitmsg="")
|
console.interact(banner, exitmsg="")
|
||||||
finally:
|
finally:
|
||||||
|
|
Loading…
Reference in a new issue