diff --git a/docs/cli-reference.rst b/docs/cli-reference.rst index 153e5f9..c830518 100644 --- a/docs/cli-reference.rst +++ b/docs/cli-reference.rst @@ -124,6 +124,7 @@ See :ref:`cli_query`. --json-cols Detect JSON cols and output them as JSON, not escaped strings -r, --raw Raw output, first column of first row + --raw-lines Raw output, first column of each row -p, --param ... Named :parameters for SQL query --functions TEXT Python code defining one or more custom SQL functions @@ -192,6 +193,7 @@ See :ref:`cli_memory`. --json-cols Detect JSON cols and output them as JSON, not escaped strings -r, --raw Raw output, first column of first row + --raw-lines Raw output, first column of each row -p, --param ... Named :parameters for SQL query --encoding TEXT Character encoding for CSV input, defaults to utf-8 diff --git a/docs/cli.rst b/docs/cli.rst index 1d67e88..dc0c072 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -239,6 +239,9 @@ For example, to retrieve a binary image from a ``BLOB`` column and store it in a $ sqlite-utils photos.db "select contents from photos where id=1" --raw > myphoto.jpg +To return the first column of each result as raw data, separated by newlines, use ``--raw-lines``:: + + $ sqlite-utils photos.db "select caption from photos" --raw-lines > captions.txt .. _cli_query_parameters: diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index d25b1df..da0e4b6 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -1653,6 +1653,7 @@ def drop_view(path, view, ignore, load_extension): ) @output_options @click.option("-r", "--raw", is_flag=True, help="Raw output, first column of first row") +@click.option("--raw-lines", is_flag=True, help="Raw output, first column of each row") @click.option( "-p", "--param", @@ -1677,6 +1678,7 @@ def query( fmt, json_cols, raw, + raw_lines, param, load_extension, functions, @@ -1700,7 +1702,19 @@ def query( _register_functions(db, functions) _execute_query( - db, sql, param, raw, table, csv, tsv, no_headers, fmt, nl, arrays, json_cols + db, + sql, + param, + raw, + raw_lines, + table, + csv, + tsv, + no_headers, + fmt, + nl, + arrays, + json_cols, ) @@ -1728,6 +1742,7 @@ def query( ) @output_options @click.option("-r", "--raw", is_flag=True, help="Raw output, first column of first row") +@click.option("--raw-lines", is_flag=True, help="Raw output, first column of each row") @click.option( "-p", "--param", @@ -1773,6 +1788,7 @@ def memory( fmt, json_cols, raw, + raw_lines, param, encoding, no_detect_types, @@ -1879,12 +1895,36 @@ def memory( _register_functions(db, functions) _execute_query( - db, sql, param, raw, table, csv, tsv, no_headers, fmt, nl, arrays, json_cols + db, + sql, + param, + raw, + raw_lines, + table, + csv, + tsv, + no_headers, + fmt, + nl, + arrays, + json_cols, ) def _execute_query( - db, sql, param, raw, table, csv, tsv, no_headers, fmt, nl, arrays, json_cols + db, + sql, + param, + raw, + raw_lines, + table, + csv, + tsv, + no_headers, + fmt, + nl, + arrays, + json_cols, ): with db.conn: try: @@ -1903,6 +1943,13 @@ def _execute_query( sys.stdout.buffer.write(data) else: sys.stdout.write(str(data)) + elif raw_lines: + for row in cursor: + data = row[0] + if isinstance(data, bytes): + sys.stdout.buffer.write(data + b"\n") + else: + sys.stdout.write(str(data) + "\n") elif fmt or table: print( tabulate.tabulate( diff --git a/tests/test_cli.py b/tests/test_cli.py index 20fe99e..c3ba682 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -916,6 +916,21 @@ def test_query_raw(db_path, content, is_binary): assert result.output == str(content) +@pytest.mark.parametrize( + "content,is_binary", + [(b"\x00\x0Fbinary", True), ("this is text", False), (1, False), (1.5, False)], +) +def test_query_raw_lines(db_path, content, is_binary): + Database(db_path)["files"].insert_all({"content": content} for _ in range(3)) + result = CliRunner().invoke( + cli.cli, [db_path, "select content from files", "--raw-lines"] + ) + if is_binary: + assert result.stdout_bytes == b"\n".join(content for _ in range(3)) + b"\n" + else: + assert result.output == "\n".join(str(content) for _ in range(3)) + "\n" + + def test_query_memory_does_not_create_file(tmpdir): owd = os.getcwd() try: