lance-jsonb 14.0.2

JSONB binary encoding, parsing, and JSONPath evaluation for Lance
Documentation
// SPDX-License-Identifier: Apache-2.0
// SPDX-FileCopyrightText: Copyright The Lance Authors

//! Byte-level compatibility with the JSONB layout that Lance has already
//! written to disk. The expected bytes were produced by the `jsonb` crate
//! 0.5.6 with `default-features = false, features = ["databend"]`, which is
//! the configuration Lance used before vendoring.

use lance_jsonb::OwnedJsonb;
use lance_jsonb::RawJsonb;
use lance_jsonb::parse_value;

fn decode_hex(hex: &str) -> Vec<u8> {
    (0..hex.len())
        .step_by(2)
        .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap())
        .collect()
}

/// (JSON text, encoded JSONB bytes as hex, `RawJsonb::to_string` output)
const ENCODE_CASES: &[(&str, &str, &str)] = &[
    (r#"null"#, "2000000000000000", r#"null"#),
    (r#"true"#, "2000000040000000", r#"true"#),
    (r#"false"#, "2000000030000000", r#"false"#),
    (r#"0"#, "200000002000000100", r#"0"#),
    (r#"-0"#, "200000002000000100", r#"0"#),
    (r#"1"#, "20000000200000025001", r#"1"#),
    (r#"-1"#, "200000002000000240ff", r#"-1"#),
    (r#"127"#, "2000000020000002507f", r#"127"#),
    (r#"128"#, "20000000200000025080", r#"128"#),
    (r#"255"#, "200000002000000250ff", r#"255"#),
    (r#"256"#, "2000000020000003500100", r#"256"#),
    (r#"-128"#, "20000000200000024080", r#"-128"#),
    (r#"-129"#, "200000002000000340ff7f", r#"-129"#),
    (r#"32767"#, "2000000020000003507fff", r#"32767"#),
    (r#"32768"#, "2000000020000003508000", r#"32768"#),
    (
        r#"2147483647"#,
        "2000000020000005507fffffff",
        r#"2147483647"#,
    ),
    (
        r#"2147483648"#,
        "20000000200000055080000000",
        r#"2147483648"#,
    ),
    (
        r#"-2147483649"#,
        "200000002000000940ffffffff7fffffff",
        r#"-2147483649"#,
    ),
    (
        r#"9223372036854775807"#,
        "2000000020000009507fffffffffffffff",
        r#"9223372036854775807"#,
    ),
    (
        r#"-9223372036854775808"#,
        "2000000020000009408000000000000000",
        r#"-9223372036854775808"#,
    ),
    (
        r#"9223372036854775808"#,
        "2000000020000009508000000000000000",
        r#"9223372036854775808"#,
    ),
    (
        r#"18446744073709551615"#,
        "200000002000000950ffffffffffffffff",
        r#"18446744073709551615"#,
    ),
    (
        r#"18446744073709551616"#,
        "20000000200000096043f0000000000000",
        r#"1.8446744073709552e+19"#,
    ),
    (
        r#"-9223372036854775809"#,
        "200000002000000960c3e0000000000000",
        r#"-9.223372036854776e+18"#,
    ),
    (
        r#"123456789012345678901234567890"#,
        "20000000200000096045f8ee90ff6c373e",
        r#"1.2345678901234568e+29"#,
    ),
    (r#"0.0"#, "2000000020000009600000000000000000", r#"0.0"#),
    (r#"-0.0"#, "2000000020000009608000000000000000", r#"-0.0"#),
    (r#"1.5"#, "2000000020000009603ff8000000000000", r#"1.5"#),
    (r#"-1.5"#, "200000002000000960bff8000000000000", r#"-1.5"#),
    (r#"0.1"#, "2000000020000009603fb999999999999a", r#"0.1"#),
    (
        r#"1e10"#,
        "2000000020000009604202a05f20000000",
        r#"10000000000.0"#,
    ),
    (r#"1E-7"#, "2000000020000009603e7ad7f29abcaf48", r#"1e-7"#),
    (
        r#"3.141592653589793"#,
        "200000002000000960400921fb54442d18",
        r#"3.141592653589793"#,
    ),
    (
        r#"1.7976931348623157e308"#,
        "2000000020000009607fefffffffffffff",
        r#"1.7976931348623157e+308"#,
    ),
    (r#"1e400"#, "200000002000000120", r#"null"#),
    (
        r#"100.00"#,
        "2000000020000009604059000000000000",
        r#"100.0"#,
    ),
    (r#"2.50"#, "2000000020000009604004000000000000", r#"2.5"#),
    (r#""""#, "2000000010000000", r#""""#),
    (r#""a""#, "200000001000000161", r#""a""#),
    (
        r#""hello world""#,
        "200000001000000b68656c6c6f20776f726c64",
        r#""hello world""#,
    ),
    (
        r#""\u00e9\n\t\"\\""#,
        "2000000010000006c3a90a09225c",
        r#""é\n\t\"\\""#,
    ),
    (r#""中文""#, "2000000010000006e4b8ade69687", r#""中文""#),
    (r#""\ud83d\ude00""#, "2000000010000004f09f9880", r#""😀""#),
    (r#"[]"#, "80000000", r#"[]"#),
    (r#"{}"#, "40000000", r#"{}"#),
    (
        r#"[1,2,3]"#,
        "80000003200000022000000220000002500150025003",
        r#"[1,2,3]"#,
    ),
    (
        r#"[null,true,false,"x",1.25,[],{}]"#,
        "800000070000000040000000300000001000000120000009500000045000000478603ff40000000000008000000040000000",
        r#"[null,true,false,"x",1.25,[],{}]"#,
    ),
    (
        r#"{"b":1,"a":2}"#,
        "4000000210000001100000012000000220000002616250025001",
        r#"{"a":2,"b":1}"#,
    ),
    (
        r#"{"k":{"nested":[1,{"deep":"v"}]}}"#,
        "4000000110000001500000316b40000001100000065000001f6e657374656480000002200000025000001150014000000110000004100000016465657076",
        r#"{"k":{"nested":[1,{"deep":"v"}]}}"#,
    ),
    (
        r#"{"z":null,"aa":[1,2],"a":{"y":true,"x":false},"":"empty"}"#,
        "4000000410000000100000011000000210000001100000055000001650000010000000006161617a656d7074794000000210000001100000013000000040000000787980000002200000022000000250015002",
        r#"{"":"empty","a":{"x":false,"y":true},"aa":[1,2],"z":null}"#,
    ),
    (
        r#"  { "sp" : [ 1 , 2 ] }  "#,
        "400000011000000250000010737080000002200000022000000250015002",
        r#"{"sp":[1,2]}"#,
    ),
];

#[test]
fn test_encode_matches_released_layout() {
    for (json, hex, text) in ENCODE_CASES {
        let expected = decode_hex(hex);

        let from_value = parse_value(json.as_bytes()).unwrap().to_vec();
        assert_eq!(from_value, expected, "parse_value encoding of {json}");

        let from_str = json.parse::<OwnedJsonb>().unwrap().to_vec();
        assert_eq!(
            from_str, expected,
            "OwnedJsonb::from_str encoding of {json}"
        );

        assert_eq!(
            RawJsonb::new(&expected).to_string(),
            *text,
            "decoding of {json}"
        );
    }
}

#[test]
fn test_duplicate_object_key_is_rejected() {
    assert!(parse_value(br#"{"a":1,"a":2}"#).is_err());
    assert!(r#"{"a":1,"a":2}"#.parse::<OwnedJsonb>().is_err());
}

/// Values the text parser never produces but a JSONB column may still hold:
/// (description, encoded JSONB bytes as hex, `RawJsonb::to_string` output)
const DECODE_CASES: &[(&str, &str, &str)] = &[
    (
        "uint64",
        "200000002000000950ffffffffffffffff",
        r#"18446744073709551615"#,
    ),
    (
        "decimal64",
        "200000002000000a70ffffffffffffcfc702",
        r#"-123.45"#,
    ),
    (
        "decimal128",
        "2000000020000012700000000000661efdf158f2a82c9f4b870a",
        r#"1.2345678901234568e+16"#,
    ),
    (
        "decimal256",
        "200000002000002270ffffffffffffffffffffffffffffffffffffffffffffffca758afbde0b5ff00003",
        r#"-9.87654321e+17"#,
    ),
    ("binary", "20000000600000040000ff10", r#""00FF10""#),
    ("date", "20000000600000051000004a38", r#""2022-01-08""#),
    (
        "timestamp",
        "20000000600000092000060a2418202240",
        r#""2023-11-14 22:13:20.123456""#,
    ),
    (
        "timestamp_tz",
        "200000006000000d3000060a241820224000007080",
        r#""2023-11-15 06:13:20.123456 +0800""#,
    ),
    (
        "interval",
        "2000000060000011400000000e0000000300000000dde878c1",
        r#""1 year 2 months 3 days 01:02:03.000001""#,
    ),
    (
        "array_mixed",
        "80000003600000052000000a6000000310ffffffff70000000000000000700006162",
        r#"["1969-12-31",7.0,"6162"]"#,
    ),
];

#[test]
fn test_decode_extended_values() {
    for (name, hex, text) in DECODE_CASES {
        let bytes = decode_hex(hex);
        assert_eq!(
            RawJsonb::new(&bytes).to_string(),
            *text,
            "decoding of {name}"
        );
    }
}