diff --git a/Lib/sqlite3/__main__.py b/Lib/sqlite3/__main__.py index b3746ed757332f..d29a4de0cca16a 100644 --- a/Lib/sqlite3/__main__.py +++ b/Lib/sqlite3/__main__.py @@ -62,12 +62,34 @@ def runsource(self, source, filename="", symbol="single"): return False # Remember to update CLI_COMMANDS in _completer.py if source[0] == ".": - match source[1:].strip(): + cmd_name = source[1:].strip() + match cmd_name: + case "tables" | "indices" | "indexes": + schema_names = (row[1] + for row in self._cur.execute("PRAGMA database_list")) + where_clause = ("""WHERE type IN ('table', 'view') + AND name NOT LIKE 'sqlite^_%' ESCAPE '^'""" + if cmd_name == "tables" else "WHERE type='index'" + ) + select_clauses = (f"""SELECT + CASE '{schema}' + WHEN 'main' THEN name + ELSE '{schema}.' || name + END + FROM "{schema}".sqlite_master {where_clause}""" + for schema in schema_names + ) + command = " UNION ALL ".join(select_clauses) + " ORDER BY 1" + for row in self._cur.execute(command): + print(row[0]) case "version": print(sqlite3.sqlite_version) case "help": t = theme.syntax print(f"Enter SQL code or one of the below commands, and press enter.\n\n" + f"{t.builtin}.tables{t.reset} Print names of tables\n" + f"{t.builtin}.indexes{t.reset} Print names of indexes\n" + f"{t.builtin}.indices{t.reset} Print names of indices\n" f"{t.builtin}.version{t.reset} Print underlying SQLite library version\n" f"{t.builtin}.help{t.reset} Print this help message\n" f"{t.builtin}.quit{t.reset} Exit the CLI, equivalent to CTRL-D\n") diff --git a/Lib/sqlite3/_completer.py b/Lib/sqlite3/_completer.py index ba580f968bf92d..74caa055b8d1ac 100644 --- a/Lib/sqlite3/_completer.py +++ b/Lib/sqlite3/_completer.py @@ -6,7 +6,7 @@ except ImportError: SQLITE_KEYWORDS = () -CLI_COMMANDS = ('.quit', '.help', '.version') +CLI_COMMANDS = ('.quit', '.help', '.version', '.tables', '.indices', '.indexes') _completion_matches = [] diff --git a/Lib/test/test_sqlite3/test_cli.py b/Lib/test/test_sqlite3/test_cli.py index 98aadaa829a969..82d1a10d144232 100644 --- a/Lib/test/test_sqlite3/test_cli.py +++ b/Lib/test/test_sqlite3/test_cli.py @@ -125,6 +125,60 @@ def test_interact_version(self): self.assertEqual(out.count(self.PS2), 0) self.assertIn(sqlite3.sqlite_version, out) + def test_interact_tables(self): + out, err = self.run_cli(commands=( + "CREATE TABLE table_ (id INTEGER);", + "CREATE TABLE sqlitee (id INTEGER);", + "CREATE TEMP TABLE table_ (id INTEGER);", + "CREATE VIEW view_ AS SELECT 1;", + "CREATE TEMP VIEW view_ AS SELECT 1;", + "ATTACH ':memory:' AS attach_;", + "CREATE TABLE attach_.table_ (id INTEGER);", + "CREATE VIEW attach_.view_ AS SELECT 1;", + "ATTACH ':memory:' AS 123;", + "CREATE TABLE \"123\".table_ (id INTEGER);", + "CREATE VIEW \"123\".view_ AS SELECT 1;", + ".tables", + )) + self.assertIn(self.MEMORY_DB_MSG, err) + self.assertEndsWith(out, self.PS1) + self.assertEqual(out.count(self.PS1), 13) + self.assertEqual(out.count(self.PS2), 0) + tables = ("123.table_", + "123.view_", + "attach_.table_", + "attach_.view_", + "sqlitee", + "table_", + "temp.table_", + "temp.view_", + "view_") + self.assertEqual("\n".join(tables), out.replace(self.PS1, "").strip()) + + def test_interact_indexes(self): + out, err = self.run_cli(commands=( + "CREATE TABLE table_ (id INTEGER);", + "CREATE INDEX idx_table_ ON table_ (id);", + "CREATE TEMP TABLE temp_table (id INTEGER);", + "CREATE INDEX temp.idx_temp_table_ ON temp_table (id);", + "ATTACH ':memory:' AS attach_;", + "CREATE TABLE attach_.attach_table (id INTEGER);", + "CREATE INDEX attach_.idx_attach_table ON attach_table (id);", + ".indexes", + ".indices", + )) + self.assertIn(self.MEMORY_DB_MSG, err) + self.assertEndsWith(out, self.PS1) + self.assertEqual(out.count(self.PS1), 10) + self.assertEqual(out.count(self.PS2), 0) + expected = ("attach_.idx_attach_table", + "idx_table_", + "temp.idx_temp_table_", + "attach_.idx_attach_table", + "idx_table_", + "temp.idx_temp_table_") + self.assertEqual("\n".join(expected), out.replace(self.PS1, "").strip()) + def test_interact_empty_source(self): out, err = self.run_cli(commands=("", " ")) self.assertIn(self.MEMORY_DB_MSG, err) diff --git a/Misc/NEWS.d/next/Library/2025-06-22-02-15-09.gh-issue-135795.5HPn-r.rst b/Misc/NEWS.d/next/Library/2025-06-22-02-15-09.gh-issue-135795.5HPn-r.rst new file mode 100644 index 00000000000000..3e50d7501622ff --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-06-22-02-15-09.gh-issue-135795.5HPn-r.rst @@ -0,0 +1 @@ +Support ``.tables`` in the :mod:`sqlite3` command-line interface.