From 7dff7ceea5a60a75ba4b61030e0950dd7efd4c9b Mon Sep 17 00:00:00 2001 From: "Chris (ChrisJr404)" <11917633+ChrisJr404@users.noreply.github.com> Date: Mon, 24 Aug 2026 23:56:30 -0400 Subject: [PATCH] Add -o/--column-order option to insert and upsert --- docs/cli-reference.rst | 2 ++ docs/cli.rst | 13 +++++++++++++ sqlite_utils/cli.py | 14 ++++++++++++++ tests/test_cli_insert.py | 21 +++++++++++++++++++++ 4 files changed, 50 insertions(+) diff --git a/docs/cli-reference.rst b/docs/cli-reference.rst index c53d64222..ace323e7d 100644 --- a/docs/cli-reference.rst +++ b/docs/cli-reference.rst @@ -311,6 +311,7 @@ See :ref:`cli_inserting_data`, :ref:`cli_insert_csv_tsv`, :ref:`cli_insert_unstr --not-null TEXT Columns that should be created as NOT NULL --default ... Default value that should be set for a column --type ... Column types to use when creating the table + -o, --column-order TEXT Columns that should come first in the created table --no-detect-types Treat all CSV/TSV columns as TEXT --analyze Run ANALYZE at the end of this operation --load-extension TEXT Path to SQLite extension, with optional :entrypoint @@ -375,6 +376,7 @@ See :ref:`cli_upsert`. --not-null TEXT Columns that should be created as NOT NULL --default ... Default value that should be set for a column --type ... Column types to use when creating the table + -o, --column-order TEXT Columns that should come first in the created table --no-detect-types Treat all CSV/TSV columns as TEXT --analyze Run ANALYZE at the end of this operation --load-extension TEXT Path to SQLite extension, with optional :entrypoint diff --git a/docs/cli.rst b/docs/cli.rst index 78c33b8ae..0ac195e04 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -1423,6 +1423,19 @@ To import them as ``NULL`` values instead, use the ``--empty-null`` option: sqlite-utils insert creatures.db creatures creatures.csv --csv --empty-null +.. _cli_insert_column_order: + +Setting the column order +------------------------ + +When ``insert`` or ``upsert`` creates a new table the columns are ordered based on the incoming data. Use ``-o`` (or ``--column-order``) one or more times to say which columns should come first: + +.. code-block:: bash + + sqlite-utils insert dogs.db dogs dogs.csv --csv -o name -o age + +Any columns that are not listed keep their original order after the ones you specify. This only affects tables created by the command - the order of columns in an existing table is left unchanged. + .. _cli_insert_csv_tsv_delimiter: Alternative delimiters and quote characters diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index c23090283..07854dd55 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -1032,6 +1032,13 @@ def inner(fn): multiple=True, help="Column types to use when creating the table", ), + click.option( + "-o", + "--column-order", + type=str, + multiple=True, + help="Columns that should come first in the created table", + ), click.option( "--no-detect-types", is_flag=True, @@ -1087,6 +1094,7 @@ def insert_upsert_implementation( not_null=None, default=None, types=None, + column_order=None, no_detect_types=False, analyze=False, load_extension=None, @@ -1116,6 +1124,8 @@ def _insert_docs(docs, tracker=None): extra_kwargs["defaults"] = dict(default) if column_type_overrides: extra_kwargs["columns"] = column_type_overrides + if column_order: + extra_kwargs["column_order"] = list(column_order) if upsert: extra_kwargs["upsert"] = upsert @@ -1385,6 +1395,7 @@ def insert( not_null, default, types, + column_order, strict, ): """ @@ -1479,6 +1490,7 @@ def rows(): not_null=not_null, default=default, types=types, + column_order=column_order, strict=strict, code=code, ) @@ -1514,6 +1526,7 @@ def upsert( not_null, default, types, + column_order, no_detect_types, analyze, load_extension, @@ -1565,6 +1578,7 @@ def upsert( not_null=not_null, default=default, types=types, + column_order=column_order, no_detect_types=no_detect_types, analyze=analyze, load_extension=load_extension, diff --git a/tests/test_cli_insert.py b/tests/test_cli_insert.py index 011786259..72d7cd668 100644 --- a/tests/test_cli_insert.py +++ b/tests/test_cli_insert.py @@ -79,6 +79,27 @@ def test_insert_json_flatten_nl(tmpdir): ] +@pytest.mark.parametrize("command", ("insert", "upsert")) +@pytest.mark.parametrize( + "args,expected_columns", + ( + ([], ["id", "name", "age"]), + (["-o", "name"], ["name", "id", "age"]), + (["--column-order", "age", "-o", "name"], ["age", "name", "id"]), + ), +) +def test_insert_column_order(db_path, tmpdir, command, args, expected_columns): + json_path = str(tmpdir / "dog.json") + with open(json_path, "w") as fp: + fp.write(json.dumps({"id": 1, "name": "Cleo", "age": 4})) + result = CliRunner().invoke( + cli.cli, [command, db_path, "dogs", json_path, "--pk", "id"] + args + ) + assert result.exit_code == 0 + columns = [c.name for c in Database(db_path).table("dogs").columns] + assert columns == expected_columns + + @pytest.mark.parametrize( "args,expected_pks", (