Files
RustPython/Lib/sqlite3/__main__.py
Steve Shi 765933136c Implement Sqlite3 Module (#4260)
* add supporting for PyAtomic<PyObject>

* create sqlite module

* add dependency sqlite3-sys

* add module constants

* import sqlite3 from cpython

* adjust lib

* add module structure

* impl Connection.cursor

* add module exceptions

* impl lstrip_sql

* impl statement new

* wip cursor.execute

* wip cursor

* wip error to exception

* add SqliteRaw and SqliteStatementRaw

* impl statement parameters binding

* wip cursor.execute

* add test_sqlite

* impl closeable connection

* impl closeable cursor

* impl cursor.executemany

* impl cursor.executescript

* impl cursor.fetch*

* impl connection.backup

* stage 1

* add support connection.backup with progress

* fix backup deadlock

* support changable isolation_level

* impl converter

* impl adapter

* impl text_factory and blob

* impl create_function

* impl create_function 2

* fix empty statement

* impl blob support

* impl create_aggregate

* impl create_aggregate 2

* refactor create_*

* impl enable_callback_traceback

* impl create_collation

* refactor create_* with CallbackData

* fix text and blob use SQLITE_TRANSIENT

* fix str to SQLITE_TEXT

* impl thread check

* impl Connection Factory

* impl busy timeout

* shift sqlite3-sys -> libsqlite3-sys

* refactor CallbackData

* impl create_window_function

* refactor callback functions

* add module attr converters

* fix nullable isolation_level

* add module attr adapters

* fix nullable adapt proto

* impl set_authorizer

* impl trace_callback

* impl set_progress_handler

* impl cancellable sqlite function*

* impl attributes for Connection

* fix some failed tests

* impl Row

* impl Blob methods

* impl Blob subscript & ass_subscript

* pass tests

* rebase

* no sqlite for wasm

* use ThreadId instead u64

* no libsqlite3-sys for wasm

* fix into_cstring for all platform

* fixup

* rebase

* fix windows into_bytes

* disable sqlite for android

* fixup
2023-01-07 22:03:24 +02:00

133 lines
4.0 KiB
Python
Vendored

"""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
from argparse import ArgumentParser
from code import InteractiveConsole
from textwrap import dedent
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)
except sqlite3.Error as e:
tp = type(e).__name__
try:
print(f"{tp} ({e.sqlite_errorname}): {e}", file=sys.stderr)
except AttributeError:
print(f"{tp}: {e}", file=sys.stderr)
if not suppress_errors:
sys.exit(1)
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.
"""
if source == ".version":
print(f"{sqlite3.sqlite_version}")
elif source == ".help":
print("Enter SQL code and press enter.")
elif source == ".quit":
sys.exit(0)
elif not sqlite3.complete_statement(source):
return True
else:
execute(self._cur, source)
return False
# TODO: RUSTPYTHON match statement supporting
# match source:
# case ".version":
# print(f"{sqlite3.sqlite_version}")
# case ".help":
# print("Enter SQL code and press enter.")
# case ".quit":
# sys.exit(0)
# case _:
# if not sqlite3.complete_statement(source):
# return True
# execute(self._cur, source)
# return False
def main():
parser = ArgumentParser(
description="Python sqlite3 CLI",
prog="python -m sqlite3",
)
parser.add_argument(
"filename", type=str, default=":memory:", nargs="?",
help=(
"SQLite database to open (defaults to ':memory:'). "
"A new database is created if the file does not previously exist."
),
)
parser.add_argument(
"sql", type=str, nargs="?",
help=(
"An SQL query to execute. "
"Any returned rows are printed to stdout."
),
)
parser.add_argument(
"-v", "--version", action="version",
version=f"SQLite version {sqlite3.sqlite_version}",
help="Print underlying SQLite library version",
)
args = parser.parse_args()
if args.filename == ":memory:":
db_name = "a transient in-memory database"
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}
Each command will be run using execute() on the cursor.
Type ".help" for more information; type ".quit" or CTRL-D to quit.
""").strip()
sys.ps1 = "sqlite> "
sys.ps2 = " ... "
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:
con.close()
main()