From adfa6cce14a18c21d136c1425ca94863365d3041 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Tue, 27 Feb 2024 20:49:54 -0800 Subject: [PATCH] insert --alter support, closes #23 --- dclient/cli.py | 9 ++++++++- docs/inserting.md | 2 ++ tests/test_insert.py | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/dclient/cli.py b/dclient/cli.py index 04f4681..fc9323f 100644 --- a/dclient/cli.py +++ b/dclient/cli.py @@ -108,6 +108,7 @@ def query(url_or_alias, sql, token): ) @click.option("--ignore", is_flag=True, help="Ignore rows with a matching primary key") @click.option("--create", is_flag=True, help="Create table if it does not exist") +@click.option("--alter", is_flag=True, help="Alter table to add any missing columns") @click.option( "pks", "--pk", @@ -135,6 +136,7 @@ def insert( replace, ignore, create, + alter, pks, batch_size, interval, @@ -240,6 +242,7 @@ def insert( batch=batch, token=token, create=create, + alter=alter, pks=pks, replace=replace, ignore=ignore, @@ -422,7 +425,7 @@ def _batches(iterable, size, interval=None): last_yield_time = time.time() -def _insert_batch(*, url, table, batch, token, create, pks, replace, ignore): +def _insert_batch(*, url, table, batch, token, create, alter, pks, replace, ignore): if create: data = { "table": table, @@ -432,6 +435,8 @@ def _insert_batch(*, url, table, batch, token, create, pks, replace, ignore): data["replace"] = True if ignore: data["ignore"] = True + if alter: + data["alter"] = True if pks: if len(pks) == 1: data["pk"] = pks[0] @@ -446,6 +451,8 @@ def _insert_batch(*, url, table, batch, token, create, pks, replace, ignore): data["replace"] = True if ignore: data["ignore"] = True + if alter: + data["alter"] = True url = "{}/{}/-/insert".format(url, table) response = httpx.post( url, diff --git a/docs/inserting.md b/docs/inserting.md index fb01d47..af6bb97 100644 --- a/docs/inserting.md +++ b/docs/inserting.md @@ -88,6 +88,7 @@ You can disable this and have every value treated as a string using `--no-detect - `--create` - create the table if it doesn't already exist - `--replace` - replace any rows with a matching primary key - `--ignore` - ignore any rows with a matching existing primary key +- `--alter` - alter table to add any columns that are missing - `--pk id` - set a primary key (for if the table is being created) If you use `--create` a table will be created with rows to match the columns in your uploaded data - using the correctly detected types, unless you use `--no-detect-types` in which case every column will be of type `text`. @@ -125,6 +126,7 @@ Options: --replace Replace rows with a matching primary key --ignore Ignore rows with a matching primary key --create Create table if it does not exist + --alter Alter table to add any missing columns --pk TEXT Columns to use as the primary key when creating the table --batch-size INTEGER Send rows in batches of this size diff --git a/tests/test_insert.py b/tests/test_insert.py index 293a025..c849a67 100644 --- a/tests/test_insert.py +++ b/tests/test_insert.py @@ -206,6 +206,27 @@ def make_format_test(content, arg): should_error=False, expected_table_json=[{"a": 1, "b": 2, "c": 3}, {"a": 4, "b": 5, "c": 6}], ), + # Existing table without alter should fail + InsertTest( + input_data="a,b,c,d\n1,2,4,5\n", + cmd_args=["--ignore"], + table_exists=True, + expected_output="Inserting rows\nError: Row 0 has invalid columns: d\n", + should_error=True, + expected_table_json=[{"a": 1, "b": 2, "c": 3}, {"a": 4, "b": 5, "c": 6}], + ), + # Existing table with --alter should work + InsertTest( + input_data="a,b,c,d\n1,2,4,5\n", + cmd_args=["--replace", "--alter"], + table_exists=True, + expected_output="Inserting rows\n", + should_error=False, + expected_table_json=[ + {"a": 1, "b": 2, "c": 4, "d": 5}, + {"a": 4, "b": 5, "c": 6, "d": None}, + ], + ), ), ) def test_insert_against_datasette( @@ -224,6 +245,7 @@ def test_insert_against_datasette( "create-table": {"id": "*"}, "insert-row": {"id": "*"}, "update-row": {"id": "*"}, + "alter-table": {"id": "*"}, } } )