mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-07-24 01:44:31 +02:00
Ability to insert base64 binary data as JSON, closes #126
This commit is contained in:
parent
814d4a7f90
commit
1a61a6d3d6
5 changed files with 67 additions and 3 deletions
16
docs/cli.rst
16
docs/cli.rst
|
|
@ -65,10 +65,10 @@ Binary strings are not valid JSON, so BLOB columns containing binary data will b
|
|||
$ sqlite-utils dogs.db "select name, content from images" | python -mjson.tool
|
||||
[
|
||||
{
|
||||
"name": "smile.gif",
|
||||
"name": "transparent.gif",
|
||||
"content": {
|
||||
"$base64": true,
|
||||
"encoded": "eJzt0c1x..."
|
||||
"encoded": "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
@ -295,6 +295,18 @@ If you feed it a JSON list it will insert multiple records. For example, if ``do
|
|||
}
|
||||
]
|
||||
|
||||
You can insert binary data into a BLOB column by first encoding it using base64 and then structuring it like this::
|
||||
|
||||
[
|
||||
{
|
||||
"name": "transparent.gif",
|
||||
"content": {
|
||||
"$base64": true,
|
||||
"encoded": "R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
You can import all three records into an automatically created ``dogs`` table and set the ``id`` column as the primary key like so::
|
||||
|
||||
$ sqlite-utils insert dogs.db dogs dogs.json --pk=id
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import json
|
|||
import sys
|
||||
import csv as csv_std
|
||||
import tabulate
|
||||
from .utils import sqlite3
|
||||
from .utils import sqlite3, decode_base64_values
|
||||
|
||||
VALID_COLUMN_TYPES = ("INTEGER", "TEXT", "FLOAT", "BLOB")
|
||||
|
||||
|
|
@ -451,6 +451,8 @@ def insert_upsert_implementation(
|
|||
extra_kwargs["defaults"] = dict(default)
|
||||
if upsert:
|
||||
extra_kwargs["upsert"] = upsert
|
||||
# Apply {"$base64": true, ...} decoding, if needed
|
||||
docs = (decode_base64_values(doc) for doc in docs)
|
||||
db[table].insert_all(
|
||||
docs, pk=pk, batch_size=batch_size, alter=alter, **extra_kwargs
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import base64
|
||||
|
||||
try:
|
||||
import pysqlite3 as sqlite3
|
||||
import pysqlite3.dbapi2
|
||||
|
|
@ -58,3 +60,17 @@ def column_affinity(column_type):
|
|||
return float
|
||||
# Default is 'NUMERIC', which we currently also treat as float
|
||||
return float
|
||||
|
||||
|
||||
def decode_base64_values(doc):
|
||||
# Looks for '{"$base64": true..., "encoded": ...}' values and decodes them
|
||||
to_fix = [
|
||||
k
|
||||
for k in doc
|
||||
if isinstance(doc[k], dict)
|
||||
and doc[k].get("$base64") is True
|
||||
and "encoded" in doc[k]
|
||||
]
|
||||
if not to_fix:
|
||||
return doc
|
||||
return dict(doc, **{k: base64.b64decode(doc[k]["encoded"]) for k in to_fix})
|
||||
|
|
|
|||
|
|
@ -554,6 +554,18 @@ def test_insert_not_null_default(db_path, tmpdir):
|
|||
) == db["dogs"].schema
|
||||
|
||||
|
||||
def test_insert_binary_base64(db_path):
|
||||
result = CliRunner().invoke(
|
||||
cli.cli,
|
||||
["insert", db_path, "files", "-"],
|
||||
input=r'{"content": {"$base64": true, "encoded": "aGVsbG8="}}',
|
||||
)
|
||||
assert 0 == result.exit_code, result.output
|
||||
db = Database(db_path)
|
||||
actual = db.execute_returning_dicts("select content from files")
|
||||
assert actual == [{"content": b"hello"}]
|
||||
|
||||
|
||||
def test_insert_newline_delimited(db_path):
|
||||
result = CliRunner().invoke(
|
||||
cli.cli,
|
||||
|
|
|
|||
22
tests/test_utils.py
Normal file
22
tests/test_utils.py
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
from sqlite_utils import utils
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"input,expected,should_be_is",
|
||||
[
|
||||
({}, None, True),
|
||||
({"foo": "bar"}, None, True),
|
||||
(
|
||||
{"content": {"$base64": True, "encoded": "aGVsbG8="}},
|
||||
{"content": b"hello"},
|
||||
False,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_decode_base64_values(input, expected, should_be_is):
|
||||
actual = utils.decode_base64_values(input)
|
||||
if should_be_is:
|
||||
assert actual is input
|
||||
else:
|
||||
assert actual == expected
|
||||
Loading…
Add table
Add a link
Reference in a new issue