mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-09-28 04:44:26 +02:00
Better validation for upsert --pk, closes #390
This commit is contained in:
parent
4c6023452c
commit
2b20957b18
3 changed files with 79 additions and 59 deletions
|
|
@ -253,8 +253,6 @@ See :ref:`cli_upsert`.
|
||||||
incoming record has a primary key that matches an existing record the existing
|
incoming record has a primary key that matches an existing record the existing
|
||||||
record will be updated.
|
record will be updated.
|
||||||
|
|
||||||
The --pk option is required.
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
echo '[
|
echo '[
|
||||||
|
|
@ -264,6 +262,7 @@ See :ref:`cli_upsert`.
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--pk TEXT Columns to use as the primary key, e.g. id
|
--pk TEXT Columns to use as the primary key, e.g. id
|
||||||
|
[required]
|
||||||
--flatten Flatten nested JSON objects, so {"a": {"b": 1}}
|
--flatten Flatten nested JSON objects, so {"a": {"b": 1}}
|
||||||
becomes {"a_b": 1}
|
becomes {"a_b": 1}
|
||||||
--nl Expect newline-delimited JSON
|
--nl Expect newline-delimited JSON
|
||||||
|
|
|
||||||
|
|
@ -813,59 +813,65 @@ def import_options(fn):
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
|
|
||||||
def insert_upsert_options(fn):
|
def insert_upsert_options(*, require_pk=False):
|
||||||
for decorator in reversed(
|
def inner(fn):
|
||||||
(
|
for decorator in reversed(
|
||||||
click.argument(
|
(
|
||||||
"path",
|
click.argument(
|
||||||
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
|
"path",
|
||||||
required=True,
|
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.argument("table"),
|
||||||
click.option(
|
click.argument("file", type=click.File("rb"), required=True),
|
||||||
"--pk", help="Columns to use as the primary key, e.g. id", multiple=True
|
click.option(
|
||||||
),
|
"--pk",
|
||||||
)
|
help="Columns to use as the primary key, e.g. id",
|
||||||
+ _import_options
|
multiple=True,
|
||||||
+ (
|
required=require_pk,
|
||||||
click.option(
|
),
|
||||||
"--batch-size", type=int, default=100, help="Commit every X records"
|
)
|
||||||
),
|
+ _import_options
|
||||||
click.option(
|
+ (
|
||||||
"--alter",
|
click.option(
|
||||||
is_flag=True,
|
"--batch-size", type=int, default=100, help="Commit every X records"
|
||||||
help="Alter existing table to add any missing columns",
|
),
|
||||||
),
|
click.option(
|
||||||
click.option(
|
"--alter",
|
||||||
"--not-null",
|
is_flag=True,
|
||||||
multiple=True,
|
help="Alter existing table to add any missing columns",
|
||||||
help="Columns that should be created as NOT NULL",
|
),
|
||||||
),
|
click.option(
|
||||||
click.option(
|
"--not-null",
|
||||||
"--default",
|
multiple=True,
|
||||||
multiple=True,
|
help="Columns that should be created as NOT NULL",
|
||||||
type=(str, str),
|
),
|
||||||
help="Default value that should be set for a column",
|
click.option(
|
||||||
),
|
"--default",
|
||||||
click.option(
|
multiple=True,
|
||||||
"-d",
|
type=(str, str),
|
||||||
"--detect-types",
|
help="Default value that should be set for a column",
|
||||||
is_flag=True,
|
),
|
||||||
envvar="SQLITE_UTILS_DETECT_TYPES",
|
click.option(
|
||||||
help="Detect types for columns in CSV/TSV data",
|
"-d",
|
||||||
),
|
"--detect-types",
|
||||||
click.option(
|
is_flag=True,
|
||||||
"--analyze",
|
envvar="SQLITE_UTILS_DETECT_TYPES",
|
||||||
is_flag=True,
|
help="Detect types for columns in CSV/TSV data",
|
||||||
help="Run ANALYZE at the end of this operation",
|
),
|
||||||
),
|
click.option(
|
||||||
load_extension_option,
|
"--analyze",
|
||||||
click.option("--silent", is_flag=True, help="Do not show progress bar"),
|
is_flag=True,
|
||||||
)
|
help="Run ANALYZE at the end of this operation",
|
||||||
):
|
),
|
||||||
fn = decorator(fn)
|
load_extension_option,
|
||||||
return fn
|
click.option("--silent", is_flag=True, help="Do not show progress bar"),
|
||||||
|
)
|
||||||
|
):
|
||||||
|
fn = decorator(fn)
|
||||||
|
return fn
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
|
||||||
def insert_upsert_implementation(
|
def insert_upsert_implementation(
|
||||||
|
|
@ -1060,7 +1066,7 @@ def _find_variables(tb, vars):
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@insert_upsert_options
|
@insert_upsert_options()
|
||||||
@click.option(
|
@click.option(
|
||||||
"--ignore", is_flag=True, default=False, help="Ignore records if pk already exists"
|
"--ignore", is_flag=True, default=False, help="Ignore records if pk already exists"
|
||||||
)
|
)
|
||||||
|
|
@ -1168,7 +1174,7 @@ def insert(
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@insert_upsert_options
|
@insert_upsert_options(require_pk=True)
|
||||||
def upsert(
|
def upsert(
|
||||||
path,
|
path,
|
||||||
table,
|
table,
|
||||||
|
|
@ -1201,8 +1207,6 @@ def upsert(
|
||||||
an incoming record has a primary key that matches an existing record
|
an incoming record has a primary key that matches an existing record
|
||||||
the existing record will be updated.
|
the existing record will be updated.
|
||||||
|
|
||||||
The --pk option is required.
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
\b
|
\b
|
||||||
|
|
|
||||||
|
|
@ -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):
|
def test_upsert_analyze(db_path, tmpdir):
|
||||||
db = Database(db_path)
|
db = Database(db_path)
|
||||||
db["rows"].insert({"id": 1, "foo": "x", "n": 3}, pk="id")
|
db["rows"].insert({"id": 1, "foo": "x", "n": 3}, pk="id")
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue