diff --git a/docs/changelog.rst b/docs/changelog.rst index 9623ec2..cf9486a 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -4,6 +4,13 @@ Changelog =========== +.. _v_unreleased: + +Unreleased +---------- + +- ``sqlite-utils query`` can now read the SQL query from standard input by passing ``-`` in place of the query, for example ``echo "select * from dogs" | sqlite-utils query dogs.db -``. (:issue:`765`) + .. _v4_0: 4.0 (2026-07-07) diff --git a/docs/cli-reference.rst b/docs/cli-reference.rst index 49eba52..000f88f 100644 --- a/docs/cli-reference.rst +++ b/docs/cli-reference.rst @@ -109,6 +109,10 @@ See :ref:`cli_query`. "select * from chickens where age > :age" \ -p age 1 + Pass "-" as the SQL to read the query from standard input: + + echo "select * from chickens" | sqlite-utils data.db - + Options: --attach ... Additional databases to attach - specify alias and filepath diff --git a/docs/cli.rst b/docs/cli.rst index 063e80f..bbbbf03 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -29,6 +29,16 @@ The ``sqlite-utils query`` command lets you run queries directly against a SQLit .. note:: In Python: :ref:`db.query() ` CLI reference: :ref:`sqlite-utils query ` +Pass ``-`` as the SQL query to read the query from standard input. This is useful for longer queries that would otherwise require careful shell escaping, or for piping in SQL generated by another tool: + +.. code-block:: bash + + echo "select * from dogs" | sqlite-utils query dogs.db - + +.. code-block:: bash + + sqlite-utils query dogs.db - < query.sql + .. _cli_query_json: Returning JSON diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index fe7dbe4..c6496d4 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -1893,7 +1893,15 @@ def query( sqlite-utils data.db \\ "select * from chickens where age > :age" \\ -p age 1 + + Pass "-" as the SQL to read the query from standard input: + + \b + echo "select * from chickens" | sqlite-utils data.db - """ + if sql == "-": + # Read SQL from standard input + sql = sys.stdin.read() db = sqlite_utils.Database(path) _register_db_for_cleanup(db) for alias, attach_path in attach: diff --git a/tests/test_cli.py b/tests/test_cli.py index 06e14ea..8196a08 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -782,6 +782,25 @@ def test_query_json(db_path, sql, args, expected): assert expected == result.output.strip() +def test_query_sql_from_stdin(db_path): + # https://github.com/simonw/sqlite-utils/issues/765 + db = Database(db_path) + with db.conn: + db["dogs"].insert_all( + [ + {"id": 1, "age": 4, "name": "Cleo"}, + {"id": 2, "age": 2, "name": "Pancakes"}, + ] + ) + result = CliRunner().invoke( + cli.cli, + ["query", db_path, "-"], + input="select name from dogs order by name", + ) + assert result.exit_code == 0, result.output + assert json.loads(result.output) == [{"name": "Cleo"}, {"name": "Pancakes"}] + + def test_query_json_empty(db_path): result = CliRunner().invoke( cli.cli,