sqlite-utils tables now takes --counts, --columns, --csv, --nl

This commit is contained in:
Simon Willison 2019-02-22 18:12:53 -08:00
commit e3c0ed1b1a
4 changed files with 124 additions and 33 deletions

View file

@ -13,8 +13,8 @@ Install it like this:
Now you can do things like this:
$ sqlite-utils tables dogs.db
dogs
$ sqlite-utils tables dogs.db --counts
[{"table": "dogs", "count": 2}]
$ sqlite-utils dogs.db "select * from dogs"
[{"id": 1, "age": 4, "name": "Cleo"},

View file

@ -92,14 +92,37 @@ Listing tables
You can list the names of tables in a database using the ``tables`` subcommand::
$ sqlite-utils tables mydb.db
[{"table": "dogs"},
{"table": "cats"},
{"table": "chickens"}]
You can output this list in CSV using the ``-csv`` option::
$ sqlite-utils tables mydb.db --csv --no-headers
dogs
cats
chickens
If you just want to see the FTS4 tables, you can use ``--fts4`` (or ``--fts5`` for FTS5 tables)::
$ sqlite-utils tables --fts4 docs.db
docs_fts
$ sqlite-utils tables docs.db --fts4
[{"table": "docs_fts"}]
Use ``--counts`` to include a count of the number of rows in each table::
$ sqlite-utils tables mydb.db --counts
[{"table": "dogs", "count": 12},
{"table": "cats", "count": 332},
{"table": "chickens", "count": 9}]
Use ``--columns`` to include a list of columns in each table::
$ sqlite-utils tables dogs.db --counts --columns
[{"table": "Gosh", "count": 0, "columns": ["c1", "c2", "c3"]},
{"table": "Gosh2", "count": 0, "columns": ["c1", "c2", "c3"]},
{"table": "dogs", "count": 2, "columns": ["id", "age", "name"]}]
The ``--nl``, ``--csv`` and ``--no-headers`` options are all available.
.. _cli_inserting_data:

View file

@ -8,6 +8,29 @@ import csv as csv_std
import sqlite3
def output_options(fn):
for decorator in reversed(
(
click.option(
"--nl",
help="Output newline-delimited JSON",
is_flag=True,
default=False,
),
click.option(
"--arrays",
help="Output rows as arrays instead of objects",
is_flag=True,
default=False,
),
click.option("--csv", is_flag=True, help="Output CSV"),
click.option("--no-headers", is_flag=True, help="Omit CSV headers"),
)
):
fn = decorator(fn)
return fn
@click.group(cls=DefaultGroup, default="query", default_if_no_args=True)
@click.version_option()
def cli():
@ -27,11 +50,47 @@ def cli():
@click.option(
"--fts5", help="Just show FTS5 enabled tables", default=False, is_flag=True
)
def tables(path, fts4, fts5):
@click.option(
"--counts", help="Include row counts per table", default=False, is_flag=True
)
@output_options
@click.option(
"--columns",
help="Include list of columns for each table",
is_flag=True,
default=False,
)
def tables(path, fts4, fts5, counts, nl, arrays, csv, no_headers, columns):
"""List the tables in the database"""
db = sqlite_utils.Database(path)
for name in db.table_names(fts4=fts4, fts5=fts5):
print(name)
headers = ["table"]
if counts:
headers.append("count")
if columns:
headers.append("columns")
def _iter():
for name in db.table_names(fts4=fts4, fts5=fts5):
row = [name]
if counts:
row.append(db[name].count)
if columns:
cols = [c.name for c in db[name].columns]
if csv:
row.append("\n".join(cols))
else:
row.append(cols)
yield row
if csv:
writer = csv_std.writer(sys.stdout)
if not no_headers:
writer.writerow(headers)
for row in _iter():
writer.writerow(row)
else:
for line in output_rows(_iter(), headers, nl, arrays):
click.echo(line)
@cli.command()
@ -104,29 +163,6 @@ def populate_fts(path, table, column):
db[table].populate_fts(column)
def output_options(fn):
for decorator in reversed(
(
click.option(
"--nl",
help="Output newline-delimited JSON",
is_flag=True,
default=False,
),
click.option(
"--arrays",
help="Output rows as arrays instead of objects",
is_flag=True,
default=False,
),
click.option("--csv", is_flag=True, help="Output CSV"),
click.option("--no-headers", is_flag=True, help="Omit CSV headers"),
)
):
fn = decorator(fn)
return fn
def insert_upsert_options(fn):
for decorator in reversed(
(

View file

@ -22,19 +22,51 @@ def db_path(tmpdir):
def test_tables(db_path):
result = CliRunner().invoke(cli.cli, ["tables", db_path])
assert "Gosh\nGosh2" == result.output.strip()
assert '[{"table": "Gosh"},\n {"table": "Gosh2"}]' == result.output.strip()
def test_tables_fts4(db_path):
Database(db_path)["Gosh"].enable_fts(["c2"], fts_version="FTS4")
result = CliRunner().invoke(cli.cli, ["tables", "--fts4", db_path])
assert "Gosh_fts" == result.output.strip()
assert '[{"table": "Gosh_fts"}]' == result.output.strip()
def test_tables_fts5(db_path):
Database(db_path)["Gosh"].enable_fts(["c2"], fts_version="FTS5")
result = CliRunner().invoke(cli.cli, ["tables", "--fts5", db_path])
assert "Gosh_fts" == result.output.strip()
assert '[{"table": "Gosh_fts"}]' == result.output.strip()
def test_tables_counts_and_columns(db_path):
db = Database(db_path)
with db.conn:
db["lots"].insert_all([{"id": i, "age": i + 1} for i in range(30)])
result = CliRunner().invoke(cli.cli, ["tables", "--counts", "--columns", db_path])
assert (
'[{"table": "Gosh", "count": 0, "columns": ["c1", "c2", "c3"]},\n'
' {"table": "Gosh2", "count": 0, "columns": ["c1", "c2", "c3"]},\n'
' {"table": "lots", "count": 30, "columns": ["id", "age"]}]'
) == result.output.strip()
def test_tables_counts_and_columns_csv(db_path):
db = Database(db_path)
with db.conn:
db["lots"].insert_all([{"id": i, "age": i + 1} for i in range(30)])
result = CliRunner().invoke(
cli.cli, ["tables", "--counts", "--columns", "--csv", db_path]
)
assert (
"table,count,columns\n"
'Gosh,0,"c1\n'
"c2\n"
'c3"\n'
'Gosh2,0,"c1\n'
"c2\n"
'c3"\n'
'lots,30,"id\n'
'age"'
) == result.output.strip()
def test_enable_fts(db_path):