Guide

Everything you need to install sqlite-msgpack and start querying MessagePack BLOBs in SQL.

Loading the extension

-- SQLite shell
.load ./msgpack

-- Application code (C)
sqlite3_load_extension(db, "./msgpack", NULL, &zErr);

Once loaded, all msgpack_* functions (including typed constructors, timestamp helpers, and schema validation) and the two table-valued functions (msgpack_each, msgpack_tree) are available in every database connection.

Building from source

Requires CMake ≥ 3.16 and a C11 and C++17 compiler (GCC, Clang, or MSVC). The SQLite extension itself compiles with C99; C++17 is required for the standalone C++ Blob library and its tests.

cmake -B build
cmake --build build
ctest --test-dir build

The default build produces:

ArtifactDescription
msgpack.so / msgpack.dll / msgpack.dylibLoadable extension
sqlite3_cliSQLite shell with extension-loading enabled

Build options

CMake optionDefaultEffect
BUILD_SHARED_LIBSONBuild the loadable extension
MSGPACK_BUILD_TESTSONBuild and register CTest test targets
MSGPACK_BUILD_BENCHOFFBuild benchmark binaries and graph-generation targets
MSGPACK_BUILD_FUZZOFFBuild fuzz-testing targets

Quick start

-- Encode a scalar value
SELECT hex(msgpack_quote(42));         -- 2A
SELECT hex(msgpack_quote('hello'));    -- A568656C6C6F
SELECT hex(msgpack_quote(NULL));       -- C0  (nil)

-- Build a map and extract from it
SELECT msgpack_extract(
  msgpack_object('name', 'Alice', 'age', 30),
  '$.name'
);  -- Alice

-- Build an array and query its length
SELECT msgpack_array_length(msgpack_array(10, 20, 30));  -- 3

-- Update a value (returns a new BLOB, original is unchanged)
SELECT msgpack_to_json(
  msgpack_set(msgpack_object('a', 1), '$.b', 2)
);  -- {"a":1,"b":2}

-- Convert to/from JSON
SELECT msgpack_to_json(msgpack_from_json('[1,true,"hi"]'));  -- [1,true,"hi"]

-- Aggregate rows into a msgpack array
CREATE TABLE t(v INTEGER);
INSERT INTO t VALUES (1),(2),(3);
SELECT msgpack_to_json(msgpack_group_array(v)) FROM t;  -- [1,2,3]

Path syntax

Path expressions follow the same conventions as SQLite's JSON1:

ExpressionMeaning
$The root element
$.keyValue stored under key in a map
$[N]Element at zero-based index N in an array
$.a.b[2].cChained navigation

A path that does not exist returns NULL from scalar functions and is treated as a missing element in multi-path and mutation operations.

Type system

Each MessagePack element has a type string returned by msgpack_type():

Type stringMessagePack formatsSQL affinity
nullnil (0xc0)NULL
boolfalse (0xc2), true (0xc3)INTEGER (0 or 1) when extracted
integerpositive fixint, negative fixint, uint8–uint64, int8–int64INTEGER
realfloat32, float64REAL
textfixstr, str8, str16, str32TEXT
blobbin8, bin16, bin32BLOB
arrayfixarray, array16, array32BLOB
mapfixmap, map16, map32BLOB
extfixext1/2/4/8/16, ext8, ext16, ext32BLOB
timestampext type −1 (ts32, ts64, ts96)BLOB (use msgpack_timestamp_s/ns to decode)

Note on SQL booleans. SQLite has no boolean type; 1=1 evaluates to integer 1. The bool type is only produced by msgpack_from_json when it parses JSON true or false literals, or by passing a manually crafted BLOB containing 0xc2/0xc3. When a bool element is extracted with msgpack_extract it becomes SQL integer 0 or 1.

BLOB auto-embedding

When a BLOB value is passed to any construction or mutation function, the extension checks whether it is valid msgpack:

This makes composition natural without extra wrapping:

-- Works just like JSON1's json() wrapper for nested values
SELECT msgpack_to_json(
  msgpack_object(
    'tags',   msgpack_array('sqlite', 'msgpack'),
    'meta',   msgpack_object('version', 1)
  )
);
-- {"tags":["sqlite","msgpack"],"meta":{"version":1}}

Spec compliance

This extension implements the MessagePack specification in full:

Limits & robustness

The implementation enforces the following hard limits to keep operations on untrusted blobs bounded:

LimitValueApplies to
Maximum nesting depth200Validation, JSON conversion, schema validation, merge-patch, iteration
Maximum output buffer64 MBEncoding, JSON conversion, mutation results

Inputs that exceed these limits are rejected as invalid rather than processed (validation returns false, mutating operations raise an SQL error). All decoding paths are bounds-checked: malformed, truncated, or adversarially deeply-nested blobs cannot crash the process or read out of bounds. The fuzz corpus exercises these adversarial cases (see Testing). For the security model and reporting policy see SECURITY.md.