-
-
Notifications
You must be signed in to change notification settings - Fork 33.1k
gh-133390: Support table, index, trigger, view, column, function, and schema completion in the sqlite3 CLI #136101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
7164755
a641e59
0bfc0fd
47d7fb4
393e24c
dfd3721
62b9dc5
164a9e1
2c99f21
088e741
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
from _sqlite3 import OperationalError | ||
from contextlib import contextmanager | ||
|
||
try: | ||
|
@@ -8,29 +9,70 @@ | |
_completion_matches = [] | ||
|
||
|
||
def _complete(text, state): | ||
def _complete(con, text, state): | ||
global _completion_matches | ||
|
||
if state == 0: | ||
text_upper = text.upper() | ||
_completion_matches = [c for c in SQLITE_KEYWORDS if c.startswith(text_upper)] | ||
text_lower = text.lower() | ||
_completion_matches = [c + " " for c in SQLITE_KEYWORDS if c.startswith(text_upper)] | ||
cursor = con.cursor() | ||
schemata = tuple(row[1] for row | ||
in cursor.execute("PRAGMA database_list")) | ||
# tables, indexes, triggers, and views | ||
select_clauses = (f"SELECT name FROM \"{schema}\".sqlite_master" | ||
for schema in schemata) | ||
tables = (row[0] for row | ||
in cursor.execute(" UNION ".join(select_clauses))) | ||
|
||
_completion_matches.extend(c + " " for c in tables | ||
if c.lower().startswith(text_lower)) | ||
# columns | ||
try: | ||
select_clauses = (f"""\ | ||
SELECT pti.name FROM "{schema}".sqlite_master AS sm | ||
JOIN pragma_table_xinfo(sm.name,'{schema}') AS pti | ||
WHERE sm.type='table'""" for schema in schemata) | ||
columns = (row[0] for row | ||
in cursor.execute(" UNION ".join(select_clauses))) | ||
_completion_matches.extend(c + " " for c in columns | ||
if c.lower().startswith(text_lower)) | ||
except OperationalError: | ||
# skip on SQLite<3.16.0 where pragma table-valued function is not | ||
# supported yet | ||
pass | ||
# functions | ||
try: | ||
funcs = (row[0] for row in cursor.execute("""\ | ||
SELECT DISTINCT UPPER(name) FROM pragma_function_list() | ||
WHERE name NOT IN ('->', '->>')""")) | ||
_completion_matches.extend(c + "(" for c in funcs | ||
if c.startswith(text_upper)) | ||
except OperationalError: | ||
# skip on SQLite<3.30.0 where function_list is not supported yet | ||
pass | ||
# schemata | ||
_completion_matches.extend(c for c in schemata | ||
if c.lower().startswith(text_lower)) | ||
_completion_matches = sorted(set(_completion_matches)) | ||
try: | ||
return _completion_matches[state] + " " | ||
return _completion_matches[state] | ||
except IndexError: | ||
return None | ||
|
||
|
||
@contextmanager | ||
def completer(): | ||
def completer(con): | ||
try: | ||
import readline | ||
except ImportError: | ||
yield | ||
return | ||
|
||
old_completer = readline.get_completer() | ||
def complete(text, state): | ||
return _complete(con, text, state) | ||
try: | ||
readline.set_completer(_complete) | ||
readline.set_completer(complete) | ||
if readline.backend == "editline": | ||
# libedit uses "^I" instead of "tab" | ||
command_string = "bind ^I rl_complete" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
Support table, index, trigger, view, column, function, and schema completion | ||
for :mod:`sqlite3`'s :ref:`command-line interface <sqlite3-cli>`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you consider asking SQLite for more of the manipulation and filtering, e.g.
SELECT name || ' ' FROM \"{schema}\".sqlite_master WHERE name LIKE :text || '%'
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for pointing that out! I've modified the code to make more use of SQLite.