Option to ignore inserts if primary key exists already

Support for SQLite's INSERT OR IGNORE

In the API layer it looks like this:

    fresh_db["test"].insert({"id": 1, "bar": 3}, ignore=True)

For the CLI layer it looks like this:

    $ sqlite-utils insert data.db dogs dogs.json --ignore

Closes #21
This commit is contained in:
Simon Willison 2019-05-28 21:15:57 -07:00
commit 00c5a49a87
4 changed files with 66 additions and 7 deletions

View file

@ -289,7 +289,7 @@ def insert_upsert_options(fn):
def insert_upsert_implementation(
path, table, json_file, pk, nl, csv, batch_size, alter, upsert
path, table, json_file, pk, nl, csv, batch_size, alter, upsert, ignore=False
):
db = sqlite_utils.Database(path)
if nl and csv:
@ -307,14 +307,19 @@ def insert_upsert_implementation(
docs = [docs]
if upsert:
method = db[table].upsert_all
extra_kwargs = {}
else:
method = db[table].insert_all
method(docs, pk=pk, batch_size=batch_size, alter=alter)
extra_kwargs = {"ignore": ignore}
method(docs, pk=pk, batch_size=batch_size, alter=alter, **extra_kwargs)
@cli.command()
@insert_upsert_options
def insert(path, table, json_file, pk, nl, csv, batch_size, alter):
@click.option(
"--ignore", is_flag=True, default=False, help="Ignore records if pk already exists"
)
def insert(path, table, json_file, pk, nl, csv, batch_size, alter, ignore):
"""
Insert records from JSON file into a table, creating the table if it
does not already exist.
@ -322,7 +327,16 @@ def insert(path, table, json_file, pk, nl, csv, batch_size, alter):
Input should be a JSON array of objects, unless --nl or --csv is used.
"""
insert_upsert_implementation(
path, table, json_file, pk, nl, csv, batch_size, alter=alter, upsert=False
path,
table,
json_file,
pk,
nl,
csv,
batch_size,
alter=alter,
upsert=False,
ignore=ignore,
)

View file

@ -446,6 +446,7 @@ class Table:
column_order=None,
hash_id=None,
alter=False,
ignore=False,
):
return self.insert_all(
[record],
@ -455,6 +456,7 @@ class Table:
column_order=column_order,
hash_id=hash_id,
alter=alter,
ignore=ignore,
)
def insert_all(
@ -467,6 +469,7 @@ class Table:
column_order=None,
hash_id=None,
alter=False,
ignore=False,
):
"""
Like .insert() but takes a list of records and ensures that the table
@ -474,6 +477,9 @@ class Table:
data
"""
assert not (hash_id and pk), "Use either pk= or hash_id="
assert not (
ignore and upsert
), "Use either ignore=True or upsert=True, not both"
all_columns = None
first = True
for chunk in chunks(records, batch_size):
@ -495,10 +501,15 @@ class Table:
if hash_id:
all_columns.insert(0, hash_id)
first = False
or_what = ""
if upsert:
or_what = "OR REPLACE "
elif ignore:
or_what = "OR IGNORE "
sql = """
INSERT {upsert} INTO [{table}] ({columns}) VALUES {rows};
INSERT {or_what}INTO [{table}] ({columns}) VALUES {rows};
""".format(
upsert="OR REPLACE" if upsert else "",
or_what=or_what,
table=self.name,
columns=", ".join("[{}]".format(c) for c in all_columns),
rows=", ".join(
@ -530,7 +541,8 @@ class Table:
raise
self.last_rowid = result.lastrowid
self.last_pk = None
if hash_id or pk:
# self.last_rowid will be 0 if a "INSERT OR IGNORE" happened
if (hash_id or pk) and self.last_rowid:
self.last_pk = self.db.conn.execute(
"select [{}] from [{}] where rowid = ?".format(
hash_id or pk, self.name