Skip to content

Commit

Permalink
Better validation for upsert --pk, closes #390
Browse files Browse the repository at this point in the history
  • Loading branch information
simonw committed Jan 26, 2022
1 parent 4c60234 commit 2b20957
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 59 deletions.
3 changes: 1 addition & 2 deletions docs/cli-reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,6 @@ See :ref:`cli_upsert`.
incoming record has a primary key that matches an existing record the existing
record will be updated.

The --pk option is required.

Example:

echo '[
Expand All @@ -264,6 +262,7 @@ See :ref:`cli_upsert`.

Options:
--pk TEXT Columns to use as the primary key, e.g. id
[required]
--flatten Flatten nested JSON objects, so {"a": {"b": 1}}
becomes {"a_b": 1}
--nl Expect newline-delimited JSON
Expand Down
118 changes: 61 additions & 57 deletions sqlite_utils/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -813,59 +813,65 @@ def import_options(fn):
return fn


def insert_upsert_options(fn):
for decorator in reversed(
(
click.argument(
"path",
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
required=True,
),
click.argument("table"),
click.argument("file", type=click.File("rb"), required=True),
click.option(
"--pk", help="Columns to use as the primary key, e.g. id", multiple=True
),
)
+ _import_options
+ (
click.option(
"--batch-size", type=int, default=100, help="Commit every X records"
),
click.option(
"--alter",
is_flag=True,
help="Alter existing table to add any missing columns",
),
click.option(
"--not-null",
multiple=True,
help="Columns that should be created as NOT NULL",
),
click.option(
"--default",
multiple=True,
type=(str, str),
help="Default value that should be set for a column",
),
click.option(
"-d",
"--detect-types",
is_flag=True,
envvar="SQLITE_UTILS_DETECT_TYPES",
help="Detect types for columns in CSV/TSV data",
),
click.option(
"--analyze",
is_flag=True,
help="Run ANALYZE at the end of this operation",
),
load_extension_option,
click.option("--silent", is_flag=True, help="Do not show progress bar"),
)
):
fn = decorator(fn)
return fn
def insert_upsert_options(*, require_pk=False):
def inner(fn):
for decorator in reversed(
(
click.argument(
"path",
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
required=True,
),
click.argument("table"),
click.argument("file", type=click.File("rb"), required=True),
click.option(
"--pk",
help="Columns to use as the primary key, e.g. id",
multiple=True,
required=require_pk,
),
)
+ _import_options
+ (
click.option(
"--batch-size", type=int, default=100, help="Commit every X records"
),
click.option(
"--alter",
is_flag=True,
help="Alter existing table to add any missing columns",
),
click.option(
"--not-null",
multiple=True,
help="Columns that should be created as NOT NULL",
),
click.option(
"--default",
multiple=True,
type=(str, str),
help="Default value that should be set for a column",
),
click.option(
"-d",
"--detect-types",
is_flag=True,
envvar="SQLITE_UTILS_DETECT_TYPES",
help="Detect types for columns in CSV/TSV data",
),
click.option(
"--analyze",
is_flag=True,
help="Run ANALYZE at the end of this operation",
),
load_extension_option,
click.option("--silent", is_flag=True, help="Do not show progress bar"),
)
):
fn = decorator(fn)
return fn

return inner


def insert_upsert_implementation(
Expand Down Expand Up @@ -1060,7 +1066,7 @@ def _find_variables(tb, vars):


@cli.command()
@insert_upsert_options
@insert_upsert_options()
@click.option(
"--ignore", is_flag=True, default=False, help="Ignore records if pk already exists"
)
Expand Down Expand Up @@ -1168,7 +1174,7 @@ def insert(


@cli.command()
@insert_upsert_options
@insert_upsert_options(require_pk=True)
def upsert(
path,
table,
Expand Down Expand Up @@ -1201,8 +1207,6 @@ def upsert(
an incoming record has a primary key that matches an existing record
the existing record will be updated.
The --pk option is required.
Example:
\b
Expand Down
17 changes: 17 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -922,6 +922,23 @@ def test_upsert(db_path, tmpdir):
]


def test_upsert_pk_required(db_path, tmpdir):
json_path = str(tmpdir / "dogs.json")
db = Database(db_path)
insert_dogs = [
{"id": 1, "name": "Cleo", "age": 4},
{"id": 2, "name": "Nixie", "age": 4},
]
open(json_path, "w").write(json.dumps(insert_dogs))
result = CliRunner().invoke(
cli.cli,
["upsert", db_path, "dogs", json_path],
catch_exceptions=False,
)
assert result.exit_code == 2
assert "Error: Missing option '--pk'" in result.output


def test_upsert_analyze(db_path, tmpdir):
db = Database(db_path)
db["rows"].insert({"id": 1, "foo": "x", "n": 3}, pk="id")
Expand Down

0 comments on commit 2b20957

Please sign in to comment.