Skip to main content

fsqlite_ext_json/
lib.rs

1//! JSON1 foundations for `fsqlite-ext-json` (`bd-3cvl`).
2//!
3//! This module currently provides:
4//! - JSON validation/minification (`json`, `json_valid`)
5//! - JSONB encode/decode helpers (`jsonb`, `jsonb_*`, `json_valid` JSONB flags)
6//! - JSON type inspection (`json_type`)
7//! - JSON path extraction with SQLite-like single vs multi-path semantics (`json_extract`)
8//! - JSON value constructors and aggregates (`json_quote`, `json_array`, `json_object`,
9//!   `json_group_array`, `json_group_object`)
10//! - mutators (`json_set`, `json_insert`, `json_replace`, `json_remove`, `json_patch`)
11//! - formatting and diagnostics (`json_pretty`, `json_error_position`, `json_array_length`)
12//!
13//! Path support in this slice:
14//! - `$` root
15//! - `$.key` object member
16//! - `$."key.with.dots"` quoted object member
17//! - `$[N]` array index
18//! - `$[#]` append pseudo-index
19//! - `$[#-N]` reverse array index
20
21use std::borrow::Cow;
22use std::sync::Arc;
23
24use fsqlite_error::{FrankenError, Result};
25use fsqlite_func::{
26    ColumnContext, FunctionRegistry, IndexInfo, ScalarFunction, VirtualTable, VirtualTableCursor,
27};
28use fsqlite_types::{SqliteValue, cx::Cx};
29use serde_json::{Map, Number, Value};
30
31const JSON_VALID_DEFAULT_FLAGS: u8 = 0x01;
32const JSON_VALID_RFC_8259_FLAG: u8 = 0x01;
33const JSON_VALID_JSON5_FLAG: u8 = 0x02;
34const JSON_VALID_JSONB_SUPERFICIAL_FLAG: u8 = 0x04;
35const JSON_VALID_JSONB_STRICT_FLAG: u8 = 0x08;
36const JSON_PRETTY_DEFAULT_INDENT_WIDTH: usize = 4;
37/// Output columns for the `json_each` / `json_tree` table-valued functions.
38pub const JSON_TABLE_COLUMN_NAMES: [&str; 8] = [
39    "key", "value", "type", "atom", "id", "parent", "fullkey", "path",
40];
41
42const JSONB_NULL_TYPE: u8 = 0x0;
43const JSONB_TRUE_TYPE: u8 = 0x1;
44const JSONB_FALSE_TYPE: u8 = 0x2;
45const JSONB_INT_TYPE: u8 = 0x3;
46const JSONB_FLOAT_TYPE: u8 = 0x5;
47const JSONB_TEXT_TYPE: u8 = 0x7;
48const JSONB_TEXT_JSON_TYPE: u8 = 0x8;
49const JSONB_ARRAY_TYPE: u8 = 0xB;
50const JSONB_OBJECT_TYPE: u8 = 0xC;
51
52#[derive(Debug, Clone, PartialEq, Eq)]
53enum PathSegment {
54    Key(String),
55    Index(usize),
56    Append,
57    FromEnd(usize),
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
61enum EditMode {
62    Set,
63    Insert,
64    Replace,
65}
66
67/// Parse and minify JSON text.
68///
69/// Returns a canonical minified JSON string or a `FunctionError` if invalid.
70pub fn json(input: &str) -> Result<String> {
71    let value = parse_json_text(input)?;
72    encode_json_text("json serialize failed", &value)
73}
74
75/// Validate JSON text under flags compatible with SQLite `json_valid`.
76///
77/// Supported flags:
78/// - `0x01`: strict RFC-8259 JSON text
79/// - `0x02`: JSON5 text
80/// - `0x04`: superficial JSONB check
81/// - `0x08`: strict JSONB parse
82#[must_use]
83pub fn json_valid(input: &str, flags: Option<u8>) -> i64 {
84    json_valid_blob(input.as_bytes(), flags)
85}
86
87/// Validate binary JSONB payloads and/or JSON text (when UTF-8).
88#[must_use]
89pub fn json_valid_blob(input: &[u8], flags: Option<u8>) -> i64 {
90    let effective_flags = flags.unwrap_or(JSON_VALID_DEFAULT_FLAGS);
91    if effective_flags == 0 {
92        return 0;
93    }
94
95    let allow_json = effective_flags & JSON_VALID_RFC_8259_FLAG != 0;
96    let allow_json5 = effective_flags & JSON_VALID_JSON5_FLAG != 0;
97    let allow_jsonb_superficial = effective_flags & JSON_VALID_JSONB_SUPERFICIAL_FLAG != 0;
98    let allow_jsonb_strict = effective_flags & JSON_VALID_JSONB_STRICT_FLAG != 0;
99
100    if allow_json || allow_json5 {
101        if let Ok(text) = std::str::from_utf8(input) {
102            if allow_json && parse_json_text(text).is_ok() {
103                return 1;
104            }
105            if allow_json5 && parse_json5_text(text).is_ok() {
106                return 1;
107            }
108        }
109    }
110
111    if allow_jsonb_strict && decode_jsonb_root(input).is_ok() {
112        return 1;
113    }
114    if allow_jsonb_superficial && is_superficially_valid_jsonb(input) {
115        return 1;
116    }
117
118    0
119}
120
121/// Convert JSON text into JSONB bytes.
122pub fn jsonb(input: &str) -> Result<Vec<u8>> {
123    let value = parse_json_text(input)?;
124    encode_jsonb_root(&value)
125}
126
127/// Convert JSONB bytes back into minified JSON text.
128pub fn json_from_jsonb(input: &[u8]) -> Result<String> {
129    let value = decode_jsonb_root(input)?;
130    encode_json_text("json_from_jsonb encode failed", &value)
131}
132
133fn encode_json_text(context: &str, value: &Value) -> Result<String> {
134    serde_json::to_string(value)
135        .map_err(|error| FrankenError::function_error(format!("{context}: {error}")))
136}
137
138fn parse_json_input_blob(input: &[u8]) -> Result<Value> {
139    match decode_jsonb_root(input) {
140        Ok(value) => Ok(value),
141        Err(jsonb_error) => match std::str::from_utf8(input) {
142            Ok(text) => parse_json_text(text),
143            Err(_) => Err(jsonb_error),
144        },
145    }
146}
147
148fn json_type_value(root: &Value, path: Option<&str>) -> Result<Option<&'static str>> {
149    let target = match path {
150        Some(path_expr) => resolve_path(root, path_expr)?,
151        None => Some(root),
152    };
153    Ok(target.map(json_type_name))
154}
155
156fn json_extract_value(root: &Value, paths: &[&str]) -> Result<SqliteValue> {
157    if paths.is_empty() {
158        return Err(FrankenError::function_error(
159            "json_extract requires at least one path",
160        ));
161    }
162
163    if paths.len() == 1 {
164        let selected = resolve_path(root, paths[0])?;
165        return Ok(selected.map_or(SqliteValue::Null, json_to_sqlite_scalar));
166    }
167
168    let mut out = Vec::with_capacity(paths.len());
169    for path_expr in paths {
170        let selected = resolve_path(root, path_expr)?;
171        out.push(selected.cloned().unwrap_or(Value::Null));
172    }
173
174    let encoded = encode_json_text("json_extract array encode failed", &Value::Array(out))?;
175    Ok(SqliteValue::Text(Arc::from(encoded)))
176}
177
178fn jsonb_extract_value(root: &Value, paths: &[&str]) -> Result<Vec<u8>> {
179    if paths.is_empty() {
180        return Err(FrankenError::function_error(
181            "jsonb_extract requires at least one path",
182        ));
183    }
184
185    let output = if paths.len() == 1 {
186        resolve_path(root, paths[0])?
187            .cloned()
188            .unwrap_or(Value::Null)
189    } else {
190        let mut values = Vec::with_capacity(paths.len());
191        for path_expr in paths {
192            values.push(
193                resolve_path(root, path_expr)?
194                    .cloned()
195                    .unwrap_or(Value::Null),
196            );
197        }
198        Value::Array(values)
199    };
200
201    encode_jsonb_root(&output)
202}
203
204fn json_arrow_value(root: &Value, path: &str) -> Result<SqliteValue> {
205    let selected = resolve_path(root, path)?;
206    let Some(value) = selected else {
207        return Ok(SqliteValue::Null);
208    };
209    let encoded = encode_json_text("json_arrow encode failed", value)?;
210    Ok(SqliteValue::Text(Arc::from(encoded)))
211}
212
213fn json_array_length_value(root: &Value, path: Option<&str>) -> Result<Option<usize>> {
214    let target = match path {
215        Some(path_expr) => resolve_path(root, path_expr)?,
216        None => Some(root),
217    };
218    Ok(target.and_then(Value::as_array).map(Vec::len))
219}
220
221fn json_error_position_blob(input: &[u8]) -> usize {
222    if decode_jsonb_root(input).is_ok() {
223        return 0;
224    }
225    match std::str::from_utf8(input) {
226        Ok(text) => json_error_position(text),
227        Err(_) => 1,
228    }
229}
230
231fn json_pretty_value(root: &Value, indent: Option<&str>) -> Result<String> {
232    let indent_unit = match indent {
233        Some(indent) => indent.to_owned(),
234        None => " ".repeat(JSON_PRETTY_DEFAULT_INDENT_WIDTH),
235    };
236    let mut out = String::new();
237    write_pretty_value(root, &indent_unit, 0, &mut out)?;
238    Ok(out)
239}
240
241fn edit_json_paths_value(
242    root: &Value,
243    pairs: &[(&str, SqliteValue)],
244    mode: EditMode,
245) -> Result<Value> {
246    let mut edited = root.clone();
247    for (path, value) in pairs {
248        let segments = parse_path(path)?;
249        let replacement = sqlite_to_json(value)?;
250        apply_edit(&mut edited, &segments, replacement, mode);
251    }
252    Ok(edited)
253}
254
255fn json_remove_value(root: &Value, paths: &[&str]) -> Result<Value> {
256    let mut edited = root.clone();
257    for path in paths {
258        let segments = parse_path(path)?;
259        remove_at_path(&mut edited, &segments);
260    }
261    Ok(edited)
262}
263
264fn json_patch_value(root: &Value, patch: &Value) -> Value {
265    merge_patch(root.clone(), patch.clone())
266}
267
268/// Return JSON type name at the root or an optional path.
269///
270/// Returns `None` when the path does not resolve.
271pub fn json_type(input: &str, path: Option<&str>) -> Result<Option<&'static str>> {
272    let root = parse_json_text(input)?;
273    json_type_value(&root, path)
274}
275
276/// Extract JSON value(s) by path, following SQLite single vs multi-path behavior.
277///
278/// - One path: return SQL-native value (text unwrapped, number typed, JSON null -> SQL NULL)
279/// - Multiple paths: return JSON array text of extracted values (missing paths become `null`)
280pub fn json_extract(input: &str, paths: &[&str]) -> Result<SqliteValue> {
281    let root = parse_json_text(input)?;
282    json_extract_value(&root, paths)
283}
284
285/// JSONB variant of `json_extract`.
286///
287/// The extracted JSON subtree is always returned as JSONB bytes.
288pub fn jsonb_extract(input: &str, paths: &[&str]) -> Result<Vec<u8>> {
289    let root = parse_json_text(input)?;
290    jsonb_extract_value(&root, paths)
291}
292
293/// Extract with `->` semantics: always returns JSON text for the selected node.
294///
295/// Missing paths yield SQL NULL.
296pub fn json_arrow(input: &str, path: &str) -> Result<SqliteValue> {
297    let root = parse_json_text(input)?;
298    json_arrow_value(&root, path)
299}
300
301/// Extract with `->>` semantics: returns SQL-native value.
302pub fn json_double_arrow(input: &str, path: &str) -> Result<SqliteValue> {
303    json_extract(input, &[path])
304}
305
306/// Return the array length at root or path, or `None` when target is not an array.
307pub fn json_array_length(input: &str, path: Option<&str>) -> Result<Option<usize>> {
308    let root = parse_json_text(input)?;
309    json_array_length_value(&root, path)
310}
311
312/// Return 0 for valid JSON, otherwise a 1-based position for first parse error.
313#[must_use]
314pub fn json_error_position(input: &str) -> usize {
315    match serde_json::from_str::<Value>(input) {
316        Ok(_) => 0,
317        Err(error) => {
318            let line = error.line();
319            let column = error.column();
320            if line == 0 || column == 0 {
321                return 1;
322            }
323
324            let mut current_line = 1usize;
325            let mut current_col = 1usize;
326            let mut char_pos = 1usize;
327            for (_idx, ch) in input.char_indices() {
328                if current_line == line && current_col == column {
329                    return char_pos;
330                }
331                if ch == '\n' {
332                    current_line += 1;
333                    current_col = 1;
334                } else {
335                    current_col += 1;
336                }
337                char_pos += 1;
338            }
339            char_pos
340        }
341    }
342}
343
344/// Pretty-print JSON with default 4-space indentation or custom indent token.
345pub fn json_pretty(input: &str, indent: Option<&str>) -> Result<String> {
346    let root = parse_json_text(input)?;
347    json_pretty_value(&root, indent)
348}
349
350/// Quote a SQL value as JSON.
351pub fn json_quote(value: &SqliteValue) -> Result<String> {
352    match value {
353        SqliteValue::Null => Ok("null".to_owned()),
354        SqliteValue::Integer(i) => Ok(i.to_string()),
355        v @ SqliteValue::Float(f) => {
356            if f.is_finite() {
357                Ok(v.to_text())
358            } else {
359                Ok("null".to_owned())
360            }
361        }
362        SqliteValue::Text(text) => {
363            Ok(serde_json::to_string(text).unwrap_or_else(|_| "\"\"".to_owned()))
364        }
365        SqliteValue::Blob(_) => Err(FrankenError::function_error("JSON cannot hold BLOB values")),
366    }
367}
368
369/// Build a JSON array from SQL values.
370pub fn json_array(values: &[SqliteValue]) -> Result<String> {
371    let mut out = Vec::with_capacity(values.len());
372    for value in values {
373        out.push(sqlite_to_json(value)?);
374    }
375    serde_json::to_string(&Value::Array(out))
376        .map_err(|error| FrankenError::function_error(format!("json_array encode failed: {error}")))
377}
378
379/// Build a JSON object from alternating key/value SQL arguments.
380///
381/// Duplicate keys are overwritten by later entries.
382pub fn json_object(args: &[SqliteValue]) -> Result<String> {
383    if args.len() % 2 != 0 {
384        return Err(FrankenError::function_error(
385            "json_object requires an even number of arguments",
386        ));
387    }
388
389    let mut map = Map::with_capacity(args.len() / 2);
390    let mut idx = 0;
391    while idx < args.len() {
392        let key = match &args[idx] {
393            SqliteValue::Text(text) => text.to_string(),
394            _ => {
395                return Err(FrankenError::function_error(
396                    "json_object keys must be text",
397                ));
398            }
399        };
400        let value = sqlite_to_json(&args[idx + 1])?;
401        map.insert(key, value);
402        idx += 2;
403    }
404
405    serde_json::to_string(&Value::Object(map)).map_err(|error| {
406        FrankenError::function_error(format!("json_object encode failed: {error}"))
407    })
408}
409
410/// Build JSONB from SQL values.
411pub fn jsonb_array(values: &[SqliteValue]) -> Result<Vec<u8>> {
412    let json_text = json_array(values)?;
413    jsonb(&json_text)
414}
415
416/// Build JSONB object from alternating key/value SQL arguments.
417pub fn jsonb_object(args: &[SqliteValue]) -> Result<Vec<u8>> {
418    let json_text = json_object(args)?;
419    jsonb(&json_text)
420}
421
422/// Aggregate rows into a JSON array, preserving SQL NULL as JSON null.
423pub fn json_group_array(values: &[SqliteValue]) -> Result<String> {
424    json_array(values)
425}
426
427/// JSONB variant of `json_group_array`.
428pub fn jsonb_group_array(values: &[SqliteValue]) -> Result<Vec<u8>> {
429    let json_text = json_group_array(values)?;
430    jsonb(&json_text)
431}
432
433/// Aggregate key/value pairs into a JSON object.
434///
435/// Duplicate keys keep the last value.
436pub fn json_group_object(entries: &[(SqliteValue, SqliteValue)]) -> Result<String> {
437    let mut map = Map::with_capacity(entries.len());
438    for (key_value, value) in entries {
439        let key = match key_value {
440            SqliteValue::Text(text) => text.to_string(),
441            _ => {
442                return Err(FrankenError::function_error(
443                    "json_group_object keys must be text",
444                ));
445            }
446        };
447        map.insert(key, sqlite_to_json(value)?);
448    }
449    serde_json::to_string(&Value::Object(map)).map_err(|error| {
450        FrankenError::function_error(format!("json_group_object encode failed: {error}"))
451    })
452}
453
454/// JSONB variant of `json_group_object`.
455pub fn jsonb_group_object(entries: &[(SqliteValue, SqliteValue)]) -> Result<Vec<u8>> {
456    let json_text = json_group_object(entries)?;
457    jsonb(&json_text)
458}
459
460/// Set JSON values at path(s), creating object keys when missing.
461pub fn json_set(input: &str, pairs: &[(&str, SqliteValue)]) -> Result<String> {
462    let root = parse_json_text(input)?;
463    let edited = edit_json_paths_value(&root, pairs, EditMode::Set)?;
464    encode_json_text("json edit encode failed", &edited)
465}
466
467/// JSONB variant of `json_set`.
468pub fn jsonb_set(input: &str, pairs: &[(&str, SqliteValue)]) -> Result<Vec<u8>> {
469    let root = parse_json_text(input)?;
470    let edited = edit_json_paths_value(&root, pairs, EditMode::Set)?;
471    encode_jsonb_root(&edited)
472}
473
474/// Insert JSON values at path(s) only when path does not already exist.
475pub fn json_insert(input: &str, pairs: &[(&str, SqliteValue)]) -> Result<String> {
476    let root = parse_json_text(input)?;
477    let edited = edit_json_paths_value(&root, pairs, EditMode::Insert)?;
478    encode_json_text("json edit encode failed", &edited)
479}
480
481/// JSONB variant of `json_insert`.
482pub fn jsonb_insert(input: &str, pairs: &[(&str, SqliteValue)]) -> Result<Vec<u8>> {
483    let root = parse_json_text(input)?;
484    let edited = edit_json_paths_value(&root, pairs, EditMode::Insert)?;
485    encode_jsonb_root(&edited)
486}
487
488/// Replace JSON values at path(s) only when path already exists.
489pub fn json_replace(input: &str, pairs: &[(&str, SqliteValue)]) -> Result<String> {
490    let root = parse_json_text(input)?;
491    let edited = edit_json_paths_value(&root, pairs, EditMode::Replace)?;
492    encode_json_text("json edit encode failed", &edited)
493}
494
495/// JSONB variant of `json_replace`.
496pub fn jsonb_replace(input: &str, pairs: &[(&str, SqliteValue)]) -> Result<Vec<u8>> {
497    let root = parse_json_text(input)?;
498    let edited = edit_json_paths_value(&root, pairs, EditMode::Replace)?;
499    encode_jsonb_root(&edited)
500}
501
502/// Remove JSON values at path(s). Array removals compact the array.
503pub fn json_remove(input: &str, paths: &[&str]) -> Result<String> {
504    let root = parse_json_text(input)?;
505    let edited = json_remove_value(&root, paths)?;
506    encode_json_text("json_remove encode failed", &edited)
507}
508
509/// JSONB variant of `json_remove`.
510pub fn jsonb_remove(input: &str, paths: &[&str]) -> Result<Vec<u8>> {
511    let root = parse_json_text(input)?;
512    let edited = json_remove_value(&root, paths)?;
513    encode_jsonb_root(&edited)
514}
515
516/// Apply RFC 7396 JSON Merge Patch.
517pub fn json_patch(input: &str, patch: &str) -> Result<String> {
518    let root = parse_json_text(input)?;
519    let patch_value = parse_json_text(patch)?;
520    let merged = json_patch_value(&root, &patch_value);
521    encode_json_text("json_patch encode failed", &merged)
522}
523
524/// JSONB variant of `json_patch`.
525pub fn jsonb_patch(input: &str, patch: &str) -> Result<Vec<u8>> {
526    let root = parse_json_text(input)?;
527    let patch_value = parse_json_text(patch)?;
528    let merged = json_patch_value(&root, &patch_value);
529    encode_jsonb_root(&merged)
530}
531
532/// Row shape produced by `json_each` and `json_tree`.
533#[derive(Debug, Clone, PartialEq, Eq)]
534pub struct JsonTableRow {
535    /// Object key, array index, or NULL (root/scalar).
536    pub key: SqliteValue,
537    /// Value column: scalars are SQL-native, objects/arrays are JSON text.
538    pub value: SqliteValue,
539    /// One of: null, true, false, integer, real, text, array, object.
540    pub type_name: &'static str,
541    /// Scalar atom or NULL for arrays/objects.
542    pub atom: SqliteValue,
543    /// Stable row identifier within the result set.
544    pub id: i64,
545    /// Parent row id (NULL at root/top-level).
546    pub parent: SqliteValue,
547    /// Absolute JSON path for this row.
548    pub fullkey: String,
549    /// Parent container path (or same as fullkey for root/scalar rows).
550    pub path: String,
551}
552
553/// Table-valued `json_each`: iterate immediate children at root or `path`.
554pub fn json_each(input: &str, path: Option<&str>) -> Result<Vec<JsonTableRow>> {
555    let root = parse_json_text(input)?;
556    json_each_value(&root, path)
557}
558
559fn json_each_value(root: &Value, path: Option<&str>) -> Result<Vec<JsonTableRow>> {
560    let base_path = path.unwrap_or("$");
561    let target = match path {
562        Some(path_expr) => resolve_path(root, path_expr)?,
563        None => Some(root),
564    };
565    let Some(target) = target else {
566        return Ok(Vec::new());
567    };
568
569    let mut rows = Vec::new();
570    let mut next_id = 1_i64;
571
572    match target {
573        Value::Array(array) => {
574            for (index, item) in array.iter().enumerate() {
575                let index_i64 = i64::try_from(index).map_err(|error| {
576                    FrankenError::function_error(format!("json_each index overflow: {error}"))
577                })?;
578                let fullkey = append_array_path(base_path, index);
579                rows.push(JsonTableRow {
580                    key: SqliteValue::Integer(index_i64),
581                    value: json_value_column(item)?,
582                    type_name: json_type_name(item),
583                    atom: json_atom_column(item),
584                    id: next_id,
585                    parent: SqliteValue::Null,
586                    fullkey,
587                    path: base_path.to_owned(),
588                });
589                next_id += 1;
590            }
591        }
592        Value::Object(object) => {
593            for (key, item) in object {
594                let fullkey = append_object_path(base_path, key);
595                rows.push(JsonTableRow {
596                    key: SqliteValue::Text(Arc::from(key.as_str())),
597                    value: json_value_column(item)?,
598                    type_name: json_type_name(item),
599                    atom: json_atom_column(item),
600                    id: next_id,
601                    parent: SqliteValue::Null,
602                    fullkey,
603                    path: base_path.to_owned(),
604                });
605                next_id += 1;
606            }
607        }
608        scalar => {
609            rows.push(JsonTableRow {
610                key: SqliteValue::Null,
611                value: json_value_column(scalar)?,
612                type_name: json_type_name(scalar),
613                atom: json_atom_column(scalar),
614                id: next_id,
615                parent: SqliteValue::Null,
616                fullkey: base_path.to_owned(),
617                path: base_path.to_owned(),
618            });
619        }
620    }
621
622    Ok(rows)
623}
624
625/// Table-valued `json_tree`: recursively iterate subtree at root or `path`.
626pub fn json_tree(input: &str, path: Option<&str>) -> Result<Vec<JsonTableRow>> {
627    let root = parse_json_text(input)?;
628    json_tree_value(&root, path)
629}
630
631fn json_tree_value(root: &Value, path: Option<&str>) -> Result<Vec<JsonTableRow>> {
632    let base_path = path.unwrap_or("$");
633    let target = match path {
634        Some(path_expr) => resolve_path(root, path_expr)?,
635        None => Some(root),
636    };
637    let Some(target) = target else {
638        return Ok(Vec::new());
639    };
640
641    let mut rows = Vec::new();
642    let mut next_id = 0_i64;
643    append_tree_rows(
644        &mut rows,
645        target,
646        SqliteValue::Null,
647        None,
648        base_path,
649        base_path,
650        &mut next_id,
651    )?;
652    Ok(rows)
653}
654
655/// Virtual table module for `json_each`.
656pub struct JsonEachVtab;
657
658/// Cursor for `json_each` virtual table scans.
659#[derive(Default)]
660pub struct JsonEachCursor {
661    rows: Vec<JsonTableRow>,
662    pos: usize,
663}
664
665impl VirtualTable for JsonEachVtab {
666    type Cursor = JsonEachCursor;
667
668    fn connect(_cx: &Cx, _args: &[&str]) -> Result<Self> {
669        Ok(Self)
670    }
671
672    fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
673        info.estimated_cost = 100.0;
674        info.estimated_rows = 100;
675        Ok(())
676    }
677
678    fn open(&self) -> Result<Self::Cursor> {
679        Ok(JsonEachCursor::default())
680    }
681}
682
683impl VirtualTableCursor for JsonEachCursor {
684    fn filter(
685        &mut self,
686        _cx: &Cx,
687        _idx_num: i32,
688        _idx_str: Option<&str>,
689        args: &[SqliteValue],
690    ) -> Result<()> {
691        let (input, path) = parse_json_table_filter_args(args)?;
692        self.rows = json_each_value(&input, path)?;
693        self.pos = 0;
694        Ok(())
695    }
696
697    fn next(&mut self, _cx: &Cx) -> Result<()> {
698        if self.pos < self.rows.len() {
699            self.pos += 1;
700        }
701        Ok(())
702    }
703
704    fn eof(&self) -> bool {
705        self.pos >= self.rows.len()
706    }
707
708    fn column(&self, ctx: &mut ColumnContext, col: i32) -> Result<()> {
709        let row = self.rows.get(self.pos).ok_or_else(|| {
710            FrankenError::function_error("json_each cursor is out of bounds for column read")
711        })?;
712        write_json_table_column(row, ctx, col)
713    }
714
715    fn rowid(&self) -> Result<i64> {
716        self.rows.get(self.pos).map(|row| row.id).ok_or_else(|| {
717            FrankenError::function_error("json_each cursor is out of bounds for rowid")
718        })
719    }
720}
721
722/// Virtual table module for `json_tree`.
723pub struct JsonTreeVtab;
724
725/// Cursor for `json_tree` virtual table scans.
726#[derive(Default)]
727pub struct JsonTreeCursor {
728    rows: Vec<JsonTableRow>,
729    pos: usize,
730}
731
732impl VirtualTable for JsonTreeVtab {
733    type Cursor = JsonTreeCursor;
734
735    fn connect(_cx: &Cx, _args: &[&str]) -> Result<Self> {
736        Ok(Self)
737    }
738
739    fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
740        info.estimated_cost = 200.0;
741        info.estimated_rows = 1_000;
742        Ok(())
743    }
744
745    fn open(&self) -> Result<Self::Cursor> {
746        Ok(JsonTreeCursor::default())
747    }
748}
749
750impl VirtualTableCursor for JsonTreeCursor {
751    fn filter(
752        &mut self,
753        _cx: &Cx,
754        _idx_num: i32,
755        _idx_str: Option<&str>,
756        args: &[SqliteValue],
757    ) -> Result<()> {
758        let (input, path) = parse_json_table_filter_args(args)?;
759        self.rows = json_tree_value(&input, path)?;
760        self.pos = 0;
761        Ok(())
762    }
763
764    fn next(&mut self, _cx: &Cx) -> Result<()> {
765        if self.pos < self.rows.len() {
766            self.pos += 1;
767        }
768        Ok(())
769    }
770
771    fn eof(&self) -> bool {
772        self.pos >= self.rows.len()
773    }
774
775    fn column(&self, ctx: &mut ColumnContext, col: i32) -> Result<()> {
776        let row = self.rows.get(self.pos).ok_or_else(|| {
777            FrankenError::function_error("json_tree cursor is out of bounds for column read")
778        })?;
779        write_json_table_column(row, ctx, col)
780    }
781
782    fn rowid(&self) -> Result<i64> {
783        self.rows.get(self.pos).map(|row| row.id).ok_or_else(|| {
784            FrankenError::function_error("json_tree cursor is out of bounds for rowid")
785        })
786    }
787}
788
789fn parse_json_text(input: &str) -> Result<Value> {
790    serde_json::from_str::<Value>(input)
791        .map_err(|error| FrankenError::function_error(format!("invalid JSON input: {error}")))
792}
793
794fn parse_json5_text(input: &str) -> Result<Value> {
795    json5::from_str::<Value>(input)
796        .map_err(|error| FrankenError::function_error(format!("invalid JSON5 input: {error}")))
797}
798
799fn encode_jsonb_root(value: &Value) -> Result<Vec<u8>> {
800    let mut out = Vec::new();
801    encode_jsonb_value(value, &mut out)?;
802    Ok(out)
803}
804
805fn encode_jsonb_value(value: &Value, out: &mut Vec<u8>) -> Result<()> {
806    match value {
807        Value::Null => append_jsonb_node(JSONB_NULL_TYPE, &[], out),
808        Value::Bool(true) => append_jsonb_node(JSONB_TRUE_TYPE, &[], out),
809        Value::Bool(false) => append_jsonb_node(JSONB_FALSE_TYPE, &[], out),
810        Value::Number(number) => {
811            if let Some(i) = number.as_i64() {
812                append_jsonb_node(JSONB_INT_TYPE, &i.to_be_bytes(), out)
813            } else if let Some(u) = number.as_u64() {
814                if let Ok(i) = i64::try_from(u) {
815                    append_jsonb_node(JSONB_INT_TYPE, &i.to_be_bytes(), out)
816                } else {
817                    let float = u as f64;
818                    append_jsonb_node(JSONB_FLOAT_TYPE, &float.to_bits().to_be_bytes(), out)
819                }
820            } else {
821                let float = number.as_f64().ok_or_else(|| {
822                    FrankenError::function_error("failed to encode non-finite JSON number")
823                })?;
824                append_jsonb_node(JSONB_FLOAT_TYPE, &float.to_bits().to_be_bytes(), out)
825            }
826        }
827        Value::String(text) => append_jsonb_node(JSONB_TEXT_TYPE, text.as_bytes(), out),
828        Value::Array(array) => {
829            let mut payload = Vec::new();
830            for item in array {
831                encode_jsonb_value(item, &mut payload)?;
832            }
833            append_jsonb_node(JSONB_ARRAY_TYPE, &payload, out)
834        }
835        Value::Object(object) => {
836            let mut payload = Vec::new();
837            for (key, item) in object {
838                append_jsonb_node(JSONB_TEXT_JSON_TYPE, key.as_bytes(), &mut payload)?;
839                encode_jsonb_value(item, &mut payload)?;
840            }
841            append_jsonb_node(JSONB_OBJECT_TYPE, &payload, out)
842        }
843    }
844}
845
846fn append_jsonb_node(node_type: u8, payload: &[u8], out: &mut Vec<u8>) -> Result<()> {
847    let (len_size, len_bytes) = encode_jsonb_payload_len(payload.len())?;
848    let len_size_u8 = u8::try_from(len_size).map_err(|error| {
849        FrankenError::function_error(format!("jsonb length-size conversion failed: {error}"))
850    })?;
851    // JSONB spec: lower 4 bits = node type, upper 4 bits = payload size category.
852    let header = (len_size_u8 << 4) | node_type;
853    out.push(header);
854    out.extend_from_slice(&len_bytes[..len_size]);
855    out.extend_from_slice(payload);
856    Ok(())
857}
858
859fn encode_jsonb_payload_len(payload_len: usize) -> Result<(usize, [u8; 8])> {
860    if payload_len == 0 {
861        return Ok((0, [0; 8]));
862    }
863
864    let payload_u64 = u64::try_from(payload_len).map_err(|error| {
865        FrankenError::function_error(format!("jsonb payload too large: {error}"))
866    })?;
867    let len_size = if u8::try_from(payload_u64).is_ok() {
868        1
869    } else if u16::try_from(payload_u64).is_ok() {
870        2
871    } else if u32::try_from(payload_u64).is_ok() {
872        4
873    } else {
874        8
875    };
876
877    let raw = payload_u64.to_be_bytes();
878    let mut out = [0u8; 8];
879    out[..len_size].copy_from_slice(&raw[8 - len_size..]);
880    Ok((len_size, out))
881}
882
883fn decode_jsonb_root(input: &[u8]) -> Result<Value> {
884    let (value, consumed) = decode_jsonb_value(input)?;
885    if consumed != input.len() {
886        return Err(FrankenError::function_error(
887            "invalid JSONB: trailing bytes",
888        ));
889    }
890    Ok(value)
891}
892
893fn decode_jsonb_value(input: &[u8]) -> Result<(Value, usize)> {
894    let (node_type, payload, consumed) = decode_jsonb_node(input)?;
895    let value = match node_type {
896        JSONB_NULL_TYPE => {
897            if !payload.is_empty() {
898                return Err(FrankenError::function_error("invalid JSONB null payload"));
899            }
900            Value::Null
901        }
902        JSONB_TRUE_TYPE => {
903            if !payload.is_empty() {
904                return Err(FrankenError::function_error("invalid JSONB true payload"));
905            }
906            Value::Bool(true)
907        }
908        JSONB_FALSE_TYPE => {
909            if !payload.is_empty() {
910                return Err(FrankenError::function_error("invalid JSONB false payload"));
911            }
912            Value::Bool(false)
913        }
914        JSONB_INT_TYPE => {
915            if payload.len() != 8 {
916                return Err(FrankenError::function_error(
917                    "invalid JSONB integer payload size",
918                ));
919            }
920            let mut raw = [0u8; 8];
921            raw.copy_from_slice(payload);
922            Value::Number(Number::from(i64::from_be_bytes(raw)))
923        }
924        JSONB_FLOAT_TYPE => {
925            if payload.len() != 8 {
926                return Err(FrankenError::function_error(
927                    "invalid JSONB float payload size",
928                ));
929            }
930            let mut raw = [0u8; 8];
931            raw.copy_from_slice(payload);
932            let float = f64::from_bits(u64::from_be_bytes(raw));
933            let number = Number::from_f64(float).ok_or_else(|| {
934                FrankenError::function_error("invalid non-finite JSONB float payload")
935            })?;
936            Value::Number(number)
937        }
938        JSONB_TEXT_TYPE | JSONB_TEXT_JSON_TYPE => {
939            let text = String::from_utf8(payload.to_vec()).map_err(|error| {
940                FrankenError::function_error(format!("invalid JSONB text payload: {error}"))
941            })?;
942            Value::String(text)
943        }
944        JSONB_ARRAY_TYPE => {
945            let mut cursor = 0usize;
946            let mut values = Vec::new();
947            while cursor < payload.len() {
948                let (item, used) = decode_jsonb_value(&payload[cursor..])?;
949                values.push(item);
950                cursor += used;
951            }
952            Value::Array(values)
953        }
954        JSONB_OBJECT_TYPE => {
955            let mut cursor = 0usize;
956            let mut map = Map::new();
957            while cursor < payload.len() {
958                let (key_node, key_used) = decode_jsonb_value(&payload[cursor..])?;
959                cursor += key_used;
960                let Value::String(key) = key_node else {
961                    return Err(FrankenError::function_error(
962                        "invalid JSONB object key payload",
963                    ));
964                };
965                if cursor >= payload.len() {
966                    return Err(FrankenError::function_error(
967                        "invalid JSONB object missing value",
968                    ));
969                }
970                let (item, used) = decode_jsonb_value(&payload[cursor..])?;
971                cursor += used;
972                map.insert(key, item);
973            }
974            Value::Object(map)
975        }
976        _ => {
977            return Err(FrankenError::function_error("invalid JSONB node type"));
978        }
979    };
980
981    Ok((value, consumed))
982}
983
984fn decode_jsonb_node(input: &[u8]) -> Result<(u8, &[u8], usize)> {
985    if input.is_empty() {
986        return Err(FrankenError::function_error("invalid JSONB: empty payload"));
987    }
988
989    let header = input[0];
990    // JSONB spec: lower 4 bits = node type, upper 4 bits = payload size category.
991    let node_type = header & 0x0f;
992    let len_size = usize::from(header >> 4);
993    if !matches!(len_size, 0 | 1 | 2 | 4 | 8) {
994        return Err(FrankenError::function_error(
995            "invalid JSONB length-size nibble",
996        ));
997    }
998    if !matches!(
999        node_type,
1000        JSONB_NULL_TYPE
1001            | JSONB_TRUE_TYPE
1002            | JSONB_FALSE_TYPE
1003            | JSONB_INT_TYPE
1004            | JSONB_FLOAT_TYPE
1005            | JSONB_TEXT_TYPE
1006            | JSONB_TEXT_JSON_TYPE
1007            | JSONB_ARRAY_TYPE
1008            | JSONB_OBJECT_TYPE
1009    ) {
1010        return Err(FrankenError::function_error("invalid JSONB node type"));
1011    }
1012
1013    if input.len() < 1 + len_size {
1014        return Err(FrankenError::function_error(
1015            "invalid JSONB: truncated payload length",
1016        ));
1017    }
1018
1019    let len_end = 1 + len_size;
1020    let payload_len = decode_jsonb_payload_len(&input[1..len_end])?;
1021    let total = 1 + len_size + payload_len;
1022    if input.len() < total {
1023        return Err(FrankenError::function_error(
1024            "invalid JSONB: truncated payload",
1025        ));
1026    }
1027
1028    Ok((node_type, &input[1 + len_size..total], total))
1029}
1030
1031fn decode_jsonb_payload_len(bytes: &[u8]) -> Result<usize> {
1032    if bytes.is_empty() {
1033        return Ok(0);
1034    }
1035    if !matches!(bytes.len(), 1 | 2 | 4 | 8) {
1036        return Err(FrankenError::function_error(
1037            "invalid JSONB length encoding size",
1038        ));
1039    }
1040
1041    let mut raw = [0u8; 8];
1042    raw[8 - bytes.len()..].copy_from_slice(bytes);
1043    let payload_len = u64::from_be_bytes(raw);
1044    usize::try_from(payload_len).map_err(|error| {
1045        FrankenError::function_error(format!("JSONB payload length overflow: {error}"))
1046    })
1047}
1048
1049fn is_superficially_valid_jsonb(input: &[u8]) -> bool {
1050    if input.is_empty() {
1051        return false;
1052    }
1053    let header = input[0];
1054    // JSONB spec: lower 4 bits = node type, upper 4 bits = payload size category.
1055    let node_type = header & 0x0f;
1056    let len_size = usize::from(header >> 4);
1057    if !matches!(len_size, 0 | 1 | 2 | 4 | 8) {
1058        return false;
1059    }
1060    if !matches!(
1061        node_type,
1062        JSONB_NULL_TYPE
1063            | JSONB_TRUE_TYPE
1064            | JSONB_FALSE_TYPE
1065            | JSONB_INT_TYPE
1066            | JSONB_FLOAT_TYPE
1067            | JSONB_TEXT_TYPE
1068            | JSONB_TEXT_JSON_TYPE
1069            | JSONB_ARRAY_TYPE
1070            | JSONB_OBJECT_TYPE
1071    ) {
1072        return false;
1073    }
1074    if input.len() < 1 + len_size {
1075        return false;
1076    }
1077    let len_end = 1 + len_size;
1078    let Ok(payload_len) = decode_jsonb_payload_len(&input[1..len_end]) else {
1079        return false;
1080    };
1081    1 + len_size + payload_len == input.len()
1082}
1083
1084#[allow(clippy::too_many_lines)]
1085fn parse_path(path: &str) -> Result<Vec<PathSegment>> {
1086    let bytes = path.as_bytes();
1087    if bytes.first().copied() != Some(b'$') {
1088        return Err(FrankenError::function_error(format!(
1089            "invalid json path `{path}`: must start with `$`"
1090        )));
1091    }
1092
1093    let mut idx = 1;
1094    let mut segments = Vec::new();
1095    while idx < bytes.len() {
1096        match bytes[idx] {
1097            b'.' => {
1098                idx += 1;
1099                if idx >= bytes.len() {
1100                    return Err(FrankenError::function_error(format!(
1101                        "invalid json path `{path}`: empty key segment"
1102                    )));
1103                }
1104                // ... rest of the dot case remains same ...
1105
1106                if bytes[idx] == b'"' {
1107                    let quoted_start = idx;
1108                    idx += 1;
1109                    let mut escaped = false;
1110                    while idx < bytes.len() {
1111                        let byte = bytes[idx];
1112                        if escaped {
1113                            escaped = false;
1114                            idx += 1;
1115                            continue;
1116                        }
1117                        if byte == b'\\' {
1118                            escaped = true;
1119                            idx += 1;
1120                            continue;
1121                        }
1122                        if byte == b'"' {
1123                            break;
1124                        }
1125                        idx += 1;
1126                    }
1127                    if idx >= bytes.len() {
1128                        return Err(FrankenError::function_error(format!(
1129                            "invalid json path `{path}`: missing closing quote in key segment"
1130                        )));
1131                    }
1132                    let quoted_key = &path[quoted_start..=idx];
1133                    let key = serde_json::from_str::<String>(quoted_key).map_err(|error| {
1134                        FrankenError::function_error(format!(
1135                            "invalid json path `{path}` quoted key `{quoted_key}`: {error}"
1136                        ))
1137                    })?;
1138                    idx += 1; // closing quote
1139                    segments.push(PathSegment::Key(key));
1140                } else {
1141                    let start = idx;
1142                    while idx < bytes.len() && bytes[idx] != b'.' && bytes[idx] != b'[' {
1143                        idx += 1;
1144                    }
1145                    if start == idx {
1146                        return Err(FrankenError::function_error(format!(
1147                            "invalid json path `{path}`: empty key segment"
1148                        )));
1149                    }
1150                    segments.push(PathSegment::Key(path[start..idx].to_owned()));
1151                }
1152            }
1153            b'[' => {
1154                idx += 1;
1155                let start = idx;
1156                let mut escaped = false;
1157                while idx < bytes.len() {
1158                    let byte = bytes[idx];
1159                    if escaped {
1160                        escaped = false;
1161                        idx += 1;
1162                        continue;
1163                    }
1164                    if byte == b'\\' {
1165                        escaped = true;
1166                        idx += 1;
1167                        continue;
1168                    }
1169                    if byte == b']' {
1170                        break;
1171                    }
1172                    idx += 1;
1173                }
1174                if idx >= bytes.len() {
1175                    return Err(FrankenError::function_error(format!(
1176                        "invalid json path `{path}`: missing closing `]`"
1177                    )));
1178                }
1179                let segment_text = &path[start..idx];
1180                idx += 1;
1181
1182                if segment_text == "#" {
1183                    segments.push(PathSegment::Append);
1184                } else if let Some(rest) = segment_text.strip_prefix("#-") {
1185                    let from_end = rest.parse::<usize>().map_err(|error| {
1186                        FrankenError::function_error(format!(
1187                            "invalid json path `{path}` from-end index `{segment_text}`: {error}"
1188                        ))
1189                    })?;
1190                    if from_end == 0 {
1191                        return Err(FrankenError::function_error(format!(
1192                            "invalid json path `{path}`: from-end index must be >= 1"
1193                        )));
1194                    }
1195                    segments.push(PathSegment::FromEnd(from_end));
1196                } else {
1197                    let index = segment_text.parse::<usize>().map_err(|error| {
1198                        FrankenError::function_error(format!(
1199                            "invalid json path `{path}` array index `{segment_text}`: {error}"
1200                        ))
1201                    })?;
1202                    segments.push(PathSegment::Index(index));
1203                }
1204            }
1205            _ => {
1206                return Err(FrankenError::function_error(format!(
1207                    "invalid json path `{path}` at byte offset {idx}"
1208                )));
1209            }
1210        }
1211    }
1212
1213    Ok(segments)
1214}
1215
1216fn resolve_path<'a>(root: &'a Value, path: &str) -> Result<Option<&'a Value>> {
1217    let segments = parse_path(path)?;
1218    let mut cursor = root;
1219
1220    for segment in segments {
1221        match segment {
1222            PathSegment::Key(key) => {
1223                let Some(next) = cursor.get(&key) else {
1224                    return Ok(None);
1225                };
1226                cursor = next;
1227            }
1228            PathSegment::Index(index) => {
1229                let Some(array) = cursor.as_array() else {
1230                    return Ok(None);
1231                };
1232                let Some(next) = array.get(index) else {
1233                    return Ok(None);
1234                };
1235                cursor = next;
1236            }
1237            PathSegment::FromEnd(from_end) => {
1238                let Some(array) = cursor.as_array() else {
1239                    return Ok(None);
1240                };
1241                if from_end > array.len() {
1242                    return Ok(None);
1243                }
1244                let index = array.len() - from_end;
1245                cursor = &array[index];
1246            }
1247            PathSegment::Append => return Ok(None),
1248        }
1249    }
1250
1251    Ok(Some(cursor))
1252}
1253
1254fn append_object_path(base: &str, key: &str) -> String {
1255    // Quote keys containing special characters that would make the path
1256    // ambiguous (dots, brackets, quotes, whitespace).
1257    if key.contains(|c: char| matches!(c, '.' | '[' | ']' | '"' | '\'') || c.is_whitespace()) {
1258        format!("{base}.\"{}\"", key.replace('"', "\\\""))
1259    } else {
1260        format!("{base}.{key}")
1261    }
1262}
1263
1264fn append_array_path(base: &str, index: usize) -> String {
1265    format!("{base}[{index}]")
1266}
1267
1268fn json_value_column(value: &Value) -> Result<SqliteValue> {
1269    match value {
1270        Value::Array(_) | Value::Object(_) => serde_json::to_string(value)
1271            .map(|s| SqliteValue::Text(Arc::from(s)))
1272            .map_err(|error| {
1273                FrankenError::function_error(format!("json table value encode failed: {error}"))
1274            }),
1275        _ => Ok(json_to_sqlite_scalar(value)),
1276    }
1277}
1278
1279fn json_atom_column(value: &Value) -> SqliteValue {
1280    match value {
1281        Value::Array(_) | Value::Object(_) => SqliteValue::Null,
1282        _ => json_to_sqlite_scalar(value),
1283    }
1284}
1285
1286fn append_tree_rows(
1287    rows: &mut Vec<JsonTableRow>,
1288    value: &Value,
1289    key: SqliteValue,
1290    parent_id: Option<i64>,
1291    fullkey: &str,
1292    path: &str,
1293    next_id: &mut i64,
1294) -> Result<()> {
1295    let current_id = *next_id;
1296    *next_id += 1;
1297
1298    rows.push(JsonTableRow {
1299        key,
1300        value: json_value_column(value)?,
1301        type_name: json_type_name(value),
1302        atom: json_atom_column(value),
1303        id: current_id,
1304        parent: parent_id.map_or(SqliteValue::Null, SqliteValue::Integer),
1305        fullkey: fullkey.to_owned(),
1306        path: path.to_owned(),
1307    });
1308
1309    match value {
1310        Value::Array(array) => {
1311            for (index, item) in array.iter().enumerate() {
1312                let index_i64 = i64::try_from(index).map_err(|error| {
1313                    FrankenError::function_error(format!("json_tree index overflow: {error}"))
1314                })?;
1315                let child_fullkey = append_array_path(fullkey, index);
1316                append_tree_rows(
1317                    rows,
1318                    item,
1319                    SqliteValue::Integer(index_i64),
1320                    Some(current_id),
1321                    &child_fullkey,
1322                    fullkey,
1323                    next_id,
1324                )?;
1325            }
1326        }
1327        Value::Object(object) => {
1328            for (child_key, item) in object {
1329                let child_fullkey = append_object_path(fullkey, child_key);
1330                append_tree_rows(
1331                    rows,
1332                    item,
1333                    SqliteValue::Text(Arc::from(child_key.as_str())),
1334                    Some(current_id),
1335                    &child_fullkey,
1336                    fullkey,
1337                    next_id,
1338                )?;
1339            }
1340        }
1341        _ => {}
1342    }
1343
1344    Ok(())
1345}
1346
1347fn parse_json_table_filter_args(args: &[SqliteValue]) -> Result<(Value, Option<&str>)> {
1348    let Some(input_arg) = args.first() else {
1349        return Err(FrankenError::function_error(
1350            "json table-valued functions require JSON input argument",
1351        ));
1352    };
1353    let input_value = match input_arg {
1354        SqliteValue::Text(input_text) => parse_json_text(input_text)?,
1355        SqliteValue::Blob(input_blob) => parse_json_input_blob(input_blob)?,
1356        _ => {
1357            return Err(FrankenError::function_error(
1358                "json table-valued input must be TEXT or BLOB JSON",
1359            ));
1360        }
1361    };
1362
1363    let path = match args.get(1) {
1364        None | Some(SqliteValue::Null) => None,
1365        Some(SqliteValue::Text(path)) => Some(&**path),
1366        Some(_) => {
1367            return Err(FrankenError::function_error(
1368                "json table-valued PATH argument must be TEXT or NULL",
1369            ));
1370        }
1371    };
1372
1373    Ok((input_value, path))
1374}
1375
1376fn write_json_table_column(row: &JsonTableRow, ctx: &mut ColumnContext, col: i32) -> Result<()> {
1377    let value = match col {
1378        0 => row.key.clone(),
1379        1 => row.value.clone(),
1380        2 => SqliteValue::Text(Arc::from(row.type_name)),
1381        3 => row.atom.clone(),
1382        4 => SqliteValue::Integer(row.id),
1383        5 => row.parent.clone(),
1384        6 => SqliteValue::Text(Arc::from(row.fullkey.as_str())),
1385        7 => SqliteValue::Text(Arc::from(row.path.as_str())),
1386        _ => {
1387            return Err(FrankenError::function_error(format!(
1388                "json table-valued invalid column index {col}"
1389            )));
1390        }
1391    };
1392    ctx.set_value(value);
1393    Ok(())
1394}
1395
1396fn json_type_name(value: &Value) -> &'static str {
1397    match value {
1398        Value::Null => "null",
1399        Value::Bool(true) => "true",
1400        Value::Bool(false) => "false",
1401        Value::Number(number) => {
1402            if number.is_i64() || number.is_u64() {
1403                "integer"
1404            } else {
1405                "real"
1406            }
1407        }
1408        Value::String(_) => "text",
1409        Value::Array(_) => "array",
1410        Value::Object(_) => "object",
1411    }
1412}
1413
1414fn json_to_sqlite_scalar(value: &Value) -> SqliteValue {
1415    match value {
1416        Value::Null => SqliteValue::Null,
1417        Value::Bool(true) => SqliteValue::Integer(1),
1418        Value::Bool(false) => SqliteValue::Integer(0),
1419        Value::Number(number) => {
1420            if let Some(i) = number.as_i64() {
1421                SqliteValue::Integer(i)
1422            } else if let Some(u) = number.as_u64() {
1423                if let Ok(i) = i64::try_from(u) {
1424                    SqliteValue::Integer(i)
1425                } else {
1426                    SqliteValue::Float(u as f64)
1427                }
1428            } else {
1429                SqliteValue::Float(number.as_f64().unwrap_or(0.0))
1430            }
1431        }
1432        Value::String(text) => SqliteValue::Text(Arc::from(text.as_str())),
1433        Value::Array(_) | Value::Object(_) => {
1434            let encoded = serde_json::to_string(value).unwrap_or_else(|_| "null".to_owned());
1435            SqliteValue::Text(Arc::from(encoded))
1436        }
1437    }
1438}
1439
1440fn sqlite_to_json(value: &SqliteValue) -> Result<Value> {
1441    match value {
1442        SqliteValue::Null => Ok(Value::Null),
1443        SqliteValue::Integer(i) => Ok(Value::Number(Number::from(*i))),
1444        SqliteValue::Float(f) => {
1445            if !f.is_finite() {
1446                return Err(FrankenError::function_error(
1447                    "non-finite float is not representable in JSON",
1448                ));
1449            }
1450            Number::from_f64(*f).map(Value::Number).ok_or_else(|| {
1451                FrankenError::function_error("failed to convert floating-point value to JSON")
1452            })
1453        }
1454        SqliteValue::Text(text) => Ok(Value::String(text.to_string())),
1455        SqliteValue::Blob(_) => Err(FrankenError::function_error("JSON cannot hold BLOB values")),
1456    }
1457}
1458
1459fn write_pretty_value(value: &Value, indent: &str, depth: usize, out: &mut String) -> Result<()> {
1460    match value {
1461        Value::Array(array) => {
1462            if array.is_empty() {
1463                out.push_str("[]");
1464                return Ok(());
1465            }
1466
1467            out.push('[');
1468            out.push('\n');
1469            for (idx, item) in array.iter().enumerate() {
1470                out.push_str(&indent.repeat(depth + 1));
1471                write_pretty_value(item, indent, depth + 1, out)?;
1472                if idx + 1 < array.len() {
1473                    out.push(',');
1474                }
1475                out.push('\n');
1476            }
1477            out.push_str(&indent.repeat(depth));
1478            out.push(']');
1479            Ok(())
1480        }
1481        Value::Object(object) => {
1482            if object.is_empty() {
1483                out.push_str("{}");
1484                return Ok(());
1485            }
1486
1487            out.push('{');
1488            out.push('\n');
1489            for (idx, (key, item)) in object.iter().enumerate() {
1490                out.push_str(&indent.repeat(depth + 1));
1491                let key_quoted = serde_json::to_string(key).map_err(|error| {
1492                    FrankenError::function_error(format!(
1493                        "json_pretty key-encode failed for `{key}`: {error}"
1494                    ))
1495                })?;
1496                out.push_str(&key_quoted);
1497                out.push_str(": ");
1498                write_pretty_value(item, indent, depth + 1, out)?;
1499                if idx + 1 < object.len() {
1500                    out.push(',');
1501                }
1502                out.push('\n');
1503            }
1504            out.push_str(&indent.repeat(depth));
1505            out.push('}');
1506            Ok(())
1507        }
1508        _ => {
1509            let encoded = serde_json::to_string(value).map_err(|error| {
1510                FrankenError::function_error(format!("json_pretty value-encode failed: {error}"))
1511            })?;
1512            out.push_str(&encoded);
1513            Ok(())
1514        }
1515    }
1516}
1517
1518fn apply_edit(root: &mut Value, segments: &[PathSegment], new_value: Value, mode: EditMode) {
1519    if segments.is_empty() {
1520        match mode {
1521            EditMode::Set | EditMode::Replace => *root = new_value,
1522            EditMode::Insert => {}
1523        }
1524        return;
1525    }
1526    if !matches!(root, Value::Object(_) | Value::Array(_)) {
1527        // Match SQLite JSON1 semantics: non-root path edits are no-ops when
1528        // the document root is a scalar value.
1529        return;
1530    }
1531
1532    let original = root.clone();
1533    let (parent_segments, last) = segments.split_at(segments.len() - 1);
1534    let Some(last_segment) = last.first() else {
1535        return;
1536    };
1537    let Some(parent) = resolve_parent_for_edit(root, parent_segments, Some(last_segment), mode)
1538    else {
1539        *root = original;
1540        return;
1541    };
1542
1543    let applied = match (parent, last_segment) {
1544        (Value::Object(object), PathSegment::Key(key)) => {
1545            let exists = object.contains_key(key);
1546            match mode {
1547                EditMode::Set => {
1548                    object.insert(key.clone(), new_value);
1549                    true
1550                }
1551                EditMode::Insert => {
1552                    if exists {
1553                        false
1554                    } else {
1555                        object.insert(key.clone(), new_value);
1556                        true
1557                    }
1558                }
1559                EditMode::Replace => {
1560                    if exists {
1561                        object.insert(key.clone(), new_value);
1562                        true
1563                    } else {
1564                        false
1565                    }
1566                }
1567            }
1568        }
1569        (Value::Array(array), PathSegment::Index(index)) => {
1570            apply_array_edit(array, *index, new_value, mode)
1571        }
1572        (Value::Array(array), PathSegment::Append) => {
1573            if matches!(mode, EditMode::Set | EditMode::Insert) {
1574                array.push(new_value);
1575                true
1576            } else {
1577                false
1578            }
1579        }
1580        (Value::Array(array), PathSegment::FromEnd(from_end)) => {
1581            if *from_end == 0 || *from_end > array.len() {
1582                false
1583            } else {
1584                let index = array.len() - *from_end;
1585                apply_array_edit(array, index, new_value, mode)
1586            }
1587        }
1588        _ => false,
1589    };
1590
1591    if !applied {
1592        *root = original;
1593    }
1594}
1595
1596fn apply_array_edit(
1597    array: &mut Vec<Value>,
1598    index: usize,
1599    new_value: Value,
1600    mode: EditMode,
1601) -> bool {
1602    if index > array.len() {
1603        return false;
1604    }
1605
1606    if index == array.len() {
1607        if matches!(mode, EditMode::Set | EditMode::Insert) {
1608            array.push(new_value);
1609            return true;
1610        }
1611        return false;
1612    }
1613
1614    match mode {
1615        EditMode::Set | EditMode::Replace => {
1616            array[index] = new_value;
1617            true
1618        }
1619        EditMode::Insert => false,
1620    }
1621}
1622
1623fn remove_at_path(root: &mut Value, segments: &[PathSegment]) {
1624    if segments.is_empty() {
1625        *root = Value::Null;
1626        return;
1627    }
1628
1629    let (parent_segments, last) = segments.split_at(segments.len() - 1);
1630    let Some(last_segment) = last.first() else {
1631        return;
1632    };
1633    let Some(parent) = resolve_path_mut(root, parent_segments) else {
1634        return;
1635    };
1636
1637    match (parent, last_segment) {
1638        (Value::Object(object), PathSegment::Key(key)) => {
1639            object.remove(key);
1640        }
1641        (Value::Array(array), PathSegment::Index(index)) => {
1642            if *index < array.len() {
1643                array.remove(*index);
1644            }
1645        }
1646        (Value::Array(array), PathSegment::FromEnd(from_end)) => {
1647            if *from_end == 0 || *from_end > array.len() {
1648                return;
1649            }
1650            let index = array.len() - *from_end;
1651            array.remove(index);
1652        }
1653        _ => {}
1654    }
1655}
1656
1657fn resolve_path_mut<'a>(root: &'a mut Value, segments: &[PathSegment]) -> Option<&'a mut Value> {
1658    let mut cursor = root;
1659
1660    for segment in segments {
1661        match segment {
1662            PathSegment::Key(key) => {
1663                let next = cursor.as_object_mut()?.get_mut(key)?;
1664                cursor = next;
1665            }
1666            PathSegment::Index(index) => {
1667                let next = cursor.as_array_mut()?.get_mut(*index)?;
1668                cursor = next;
1669            }
1670            PathSegment::FromEnd(from_end) => {
1671                let array = cursor.as_array_mut()?;
1672                if *from_end == 0 || *from_end > array.len() {
1673                    return None;
1674                }
1675                let index = array.len() - *from_end;
1676                let next = array.get_mut(index)?;
1677                cursor = next;
1678            }
1679            PathSegment::Append => return None,
1680        }
1681    }
1682
1683    Some(cursor)
1684}
1685
1686fn resolve_parent_for_edit<'a>(
1687    root: &'a mut Value,
1688    segments: &[PathSegment],
1689    tail_hint: Option<&PathSegment>,
1690    mode: EditMode,
1691) -> Option<&'a mut Value> {
1692    fn scaffold_for_next_segment(next: Option<&PathSegment>) -> Value {
1693        match next {
1694            Some(PathSegment::Index(_) | PathSegment::Append | PathSegment::FromEnd(_)) => {
1695                Value::Array(Vec::new())
1696            }
1697            _ => Value::Object(Map::new()),
1698        }
1699    }
1700
1701    let mut cursor = root;
1702
1703    for (idx, segment) in segments.iter().enumerate() {
1704        let next_segment = segments.get(idx + 1).or_else(|| {
1705            if idx + 1 == segments.len() {
1706                tail_hint
1707            } else {
1708                None
1709            }
1710        });
1711        match segment {
1712            PathSegment::Key(key) => {
1713                if cursor.is_null() {
1714                    if matches!(mode, EditMode::Set | EditMode::Insert) {
1715                        *cursor = Value::Object(Map::new());
1716                    } else {
1717                        return None;
1718                    }
1719                }
1720
1721                let object = match cursor {
1722                    Value::Object(o) => o,
1723                    _ => return None,
1724                };
1725
1726                if !object.contains_key(key) {
1727                    if !matches!(mode, EditMode::Set | EditMode::Insert) {
1728                        return None;
1729                    }
1730                    object.insert(key.clone(), scaffold_for_next_segment(next_segment));
1731                }
1732                cursor = object.get_mut(key).expect("just inserted or checked");
1733            }
1734            PathSegment::Index(index) => {
1735                if cursor.is_null() {
1736                    if matches!(mode, EditMode::Set | EditMode::Insert) {
1737                        *cursor = Value::Array(Vec::new());
1738                    } else {
1739                        return None;
1740                    }
1741                }
1742                let array = match cursor {
1743                    Value::Array(a) => a,
1744                    _ => return None,
1745                };
1746                if *index > array.len() {
1747                    return None;
1748                }
1749                if *index == array.len() {
1750                    if !matches!(mode, EditMode::Set | EditMode::Insert) {
1751                        return None;
1752                    }
1753                    array.push(scaffold_for_next_segment(next_segment));
1754                }
1755                cursor = array.get_mut(*index).expect("just pushed or checked");
1756            }
1757            PathSegment::Append => {
1758                if cursor.is_null() {
1759                    if matches!(mode, EditMode::Set | EditMode::Insert) {
1760                        *cursor = Value::Array(Vec::new());
1761                    } else {
1762                        return None;
1763                    }
1764                }
1765                let array = match cursor {
1766                    Value::Array(a) => a,
1767                    _ => return None,
1768                };
1769                if !matches!(mode, EditMode::Set | EditMode::Insert) {
1770                    return None;
1771                }
1772                array.push(scaffold_for_next_segment(next_segment));
1773                cursor = array.last_mut().expect("just pushed");
1774            }
1775            PathSegment::FromEnd(from_end) => {
1776                let array = match cursor {
1777                    Value::Array(a) => a,
1778                    _ => return None,
1779                };
1780                if *from_end == 0 || *from_end > array.len() {
1781                    return None;
1782                }
1783                let index = array.len() - *from_end;
1784                cursor = array.get_mut(index).expect("checked length");
1785            }
1786        }
1787    }
1788
1789    Some(cursor)
1790}
1791
1792fn merge_patch(target: Value, patch: Value) -> Value {
1793    match patch {
1794        Value::Object(patch_map) => {
1795            let mut target_map = match target {
1796                Value::Object(map) => map,
1797                _ => Map::new(),
1798            };
1799
1800            for (key, patch_value) in patch_map {
1801                if patch_value.is_null() {
1802                    target_map.remove(&key);
1803                    continue;
1804                }
1805                if let Some(prior) = target_map.get_mut(&key) {
1806                    let old_val = std::mem::replace(prior, Value::Null);
1807                    *prior = merge_patch(old_val, patch_value);
1808                } else {
1809                    target_map.insert(key, merge_patch(Value::Null, patch_value));
1810                }
1811            }
1812
1813            Value::Object(target_map)
1814        }
1815        other => other,
1816    }
1817}
1818
1819// ---------------------------------------------------------------------------
1820// Scalar function registration
1821// ---------------------------------------------------------------------------
1822
1823fn invalid_arity(name: &str, expected: &str, got: usize) -> FrankenError {
1824    FrankenError::function_error(format!("{name} expects {expected}; got {got} argument(s)"))
1825}
1826
1827fn text_arg<'a>(name: &str, args: &'a [SqliteValue], index: usize) -> Result<&'a str> {
1828    match args.get(index) {
1829        Some(SqliteValue::Text(text)) => Ok(&**text),
1830        Some(other) => Err(FrankenError::function_error(format!(
1831            "{name} argument {} must be TEXT, got {}",
1832            index + 1,
1833            other.typeof_str()
1834        ))),
1835        None => Err(FrankenError::function_error(format!(
1836            "{name} missing argument {}",
1837            index + 1
1838        ))),
1839    }
1840}
1841
1842fn json_arg_value(name: &str, args: &[SqliteValue], index: usize) -> Result<Value> {
1843    match args.get(index) {
1844        Some(SqliteValue::Text(text)) => parse_json_text(text),
1845        Some(SqliteValue::Blob(bytes)) => parse_json_input_blob(bytes),
1846        Some(other) => Err(FrankenError::function_error(format!(
1847            "{name} argument {} must be TEXT or BLOB, got {}",
1848            index + 1,
1849            other.typeof_str()
1850        ))),
1851        None => Err(FrankenError::function_error(format!(
1852            "{name} missing argument {}",
1853            index + 1
1854        ))),
1855    }
1856}
1857
1858fn optional_flags_arg(name: &str, args: &[SqliteValue], index: usize) -> Result<Option<u8>> {
1859    let Some(value) = args.get(index) else {
1860        return Ok(None);
1861    };
1862    let raw = value.to_integer();
1863    let flags = u8::try_from(raw).map_err(|_| {
1864        FrankenError::function_error(format!("{name} flags out of range for u8: {raw}"))
1865    })?;
1866    Ok(Some(flags))
1867}
1868
1869fn usize_to_i64(name: &str, value: usize) -> Result<i64> {
1870    i64::try_from(value).map_err(|_| {
1871        FrankenError::function_error(format!("{name} result does not fit in i64: {value}"))
1872    })
1873}
1874
1875fn collect_path_args<'a>(
1876    name: &str,
1877    args: &'a [SqliteValue],
1878    start: usize,
1879) -> Result<Vec<&'a str>> {
1880    let mut out = Vec::with_capacity(args.len().saturating_sub(start));
1881    for idx in start..args.len() {
1882        out.push(text_arg(name, args, idx)?);
1883    }
1884    Ok(out)
1885}
1886
1887fn normalize_arrow_path_arg<'a>(
1888    name: &str,
1889    value: &'a SqliteValue,
1890    index: usize,
1891) -> Result<Cow<'a, str>> {
1892    match value {
1893        SqliteValue::Text(path) => {
1894            if path.starts_with('$') || path.is_empty() {
1895                Ok(Cow::Borrowed(&**path))
1896            } else {
1897                let quoted = serde_json::to_string(path).map_err(|error| {
1898                    FrankenError::function_error(format!(
1899                        "{name} argument {} key encoding failed: {error}",
1900                        index + 1
1901                    ))
1902                })?;
1903                Ok(Cow::Owned(format!("$.{quoted}")))
1904            }
1905        }
1906        SqliteValue::Integer(index_value) => {
1907            if *index_value >= 0 {
1908                Ok(Cow::Owned(format!("$[{index_value}]")))
1909            } else {
1910                Ok(Cow::Owned(format!("$[#-{}]", index_value.unsigned_abs())))
1911            }
1912        }
1913        other => Err(FrankenError::function_error(format!(
1914            "{name} argument {} must be TEXT or INTEGER, got {}",
1915            index + 1,
1916            other.typeof_str()
1917        ))),
1918    }
1919}
1920
1921fn invoke_json_arrow(name: &str, args: &[SqliteValue], double_arrow: bool) -> Result<SqliteValue> {
1922    if args.len() != 2 {
1923        return Err(invalid_arity(name, "exactly 2 arguments", args.len()));
1924    }
1925    if args.iter().any(SqliteValue::is_null) {
1926        return Ok(SqliteValue::Null);
1927    }
1928
1929    let input = json_arg_value(name, args, 0)?;
1930    let path = normalize_arrow_path_arg(name, &args[1], 1)?;
1931    if double_arrow {
1932        json_extract_value(&input, &[path.as_ref()])
1933    } else {
1934        json_arrow_value(&input, path.as_ref())
1935    }
1936}
1937
1938fn collect_path_value_pairs(
1939    name: &str,
1940    args: &[SqliteValue],
1941    start: usize,
1942) -> Result<Vec<(String, SqliteValue)>> {
1943    let mut pairs = Vec::with_capacity((args.len().saturating_sub(start)) / 2);
1944    let mut idx = start;
1945    while idx < args.len() {
1946        let path = text_arg(name, args, idx)?.to_owned();
1947        let value = args[idx + 1].clone();
1948        pairs.push((path, value));
1949        idx += 2;
1950    }
1951    Ok(pairs)
1952}
1953
1954pub struct JsonFunc;
1955
1956impl ScalarFunction for JsonFunc {
1957    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1958        if args.len() != 1 {
1959            return Err(invalid_arity(self.name(), "exactly 1 argument", args.len()));
1960        }
1961        if matches!(args[0], SqliteValue::Null) {
1962            return Ok(SqliteValue::Null);
1963        }
1964        let input = json_arg_value(self.name(), args, 0)?;
1965        let encoded = encode_json_text("json serialize failed", &input)?;
1966        Ok(SqliteValue::Text(Arc::from(encoded)))
1967    }
1968
1969    fn num_args(&self) -> i32 {
1970        1
1971    }
1972
1973    fn name(&self) -> &'static str {
1974        "json"
1975    }
1976}
1977
1978pub struct JsonbFunc;
1979
1980impl ScalarFunction for JsonbFunc {
1981    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
1982        if args.len() != 1 {
1983            return Err(invalid_arity(self.name(), "exactly 1 argument", args.len()));
1984        }
1985        if matches!(args[0], SqliteValue::Null) {
1986            return Ok(SqliteValue::Null);
1987        }
1988        let input = json_arg_value(self.name(), args, 0)?;
1989        let blob = encode_jsonb_root(&input)?;
1990        Ok(SqliteValue::Blob(Arc::from(blob.as_slice())))
1991    }
1992
1993    fn num_args(&self) -> i32 {
1994        1
1995    }
1996
1997    fn name(&self) -> &'static str {
1998        "jsonb"
1999    }
2000}
2001
2002pub struct JsonValidFunc;
2003
2004impl ScalarFunction for JsonValidFunc {
2005    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2006        if !(1..=2).contains(&args.len()) {
2007            return Err(invalid_arity(self.name(), "1 or 2 arguments", args.len()));
2008        }
2009        let flags = optional_flags_arg(self.name(), args, 1)?;
2010        let value = match &args[0] {
2011            SqliteValue::Null => return Ok(SqliteValue::Null),
2012            SqliteValue::Text(text) => json_valid(text, flags),
2013            SqliteValue::Blob(bytes) => json_valid_blob(bytes, flags),
2014            _ => 0,
2015        };
2016        Ok(SqliteValue::Integer(value))
2017    }
2018
2019    fn num_args(&self) -> i32 {
2020        -1
2021    }
2022
2023    fn name(&self) -> &'static str {
2024        "json_valid"
2025    }
2026}
2027
2028pub struct JsonTypeFunc;
2029
2030impl ScalarFunction for JsonTypeFunc {
2031    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2032        if !(1..=2).contains(&args.len()) {
2033            return Err(invalid_arity(self.name(), "1 or 2 arguments", args.len()));
2034        }
2035        if matches!(args[0], SqliteValue::Null) {
2036            return Ok(SqliteValue::Null);
2037        }
2038        let input = json_arg_value(self.name(), args, 0)?;
2039        let path = if args.len() == 2 {
2040            if matches!(args[1], SqliteValue::Null) {
2041                return Ok(SqliteValue::Null);
2042            }
2043            Some(text_arg(self.name(), args, 1)?)
2044        } else {
2045            None
2046        };
2047        Ok(match json_type_value(&input, path)? {
2048            Some(kind) => SqliteValue::Text(Arc::from(kind)),
2049            None => SqliteValue::Null,
2050        })
2051    }
2052
2053    fn num_args(&self) -> i32 {
2054        -1
2055    }
2056
2057    fn name(&self) -> &'static str {
2058        "json_type"
2059    }
2060}
2061
2062pub struct JsonExtractFunc;
2063
2064impl ScalarFunction for JsonExtractFunc {
2065    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2066        if args.is_empty() {
2067            return Err(invalid_arity(
2068                self.name(),
2069                "at least 1 argument (json, path...)",
2070                args.len(),
2071            ));
2072        }
2073        if args.len() == 1 {
2074            return Ok(SqliteValue::Null);
2075        }
2076        if matches!(args[0], SqliteValue::Null) {
2077            return Ok(SqliteValue::Null);
2078        }
2079        // SQLite returns NULL when any path argument is NULL.
2080        if args[1..].iter().any(|a| matches!(a, SqliteValue::Null)) {
2081            return Ok(SqliteValue::Null);
2082        }
2083        let input = json_arg_value(self.name(), args, 0)?;
2084        let paths = collect_path_args(self.name(), args, 1)?;
2085        json_extract_value(&input, &paths)
2086    }
2087
2088    fn num_args(&self) -> i32 {
2089        -1
2090    }
2091
2092    fn name(&self) -> &'static str {
2093        "json_extract"
2094    }
2095}
2096
2097pub struct JsonbExtractFunc;
2098
2099impl ScalarFunction for JsonbExtractFunc {
2100    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2101        if args.is_empty() {
2102            return Err(invalid_arity(
2103                self.name(),
2104                "at least 1 argument (json, path...)",
2105                args.len(),
2106            ));
2107        }
2108        if args.len() == 1 {
2109            return Ok(SqliteValue::Null);
2110        }
2111        if matches!(args[0], SqliteValue::Null) {
2112            return Ok(SqliteValue::Null);
2113        }
2114        if args[1..].iter().any(|a| matches!(a, SqliteValue::Null)) {
2115            return Ok(SqliteValue::Null);
2116        }
2117        let input = json_arg_value(self.name(), args, 0)?;
2118        let paths = collect_path_args(self.name(), args, 1)?;
2119        let blob = jsonb_extract_value(&input, &paths)?;
2120        Ok(SqliteValue::Blob(Arc::from(blob.as_slice())))
2121    }
2122
2123    fn num_args(&self) -> i32 {
2124        -1
2125    }
2126
2127    fn name(&self) -> &'static str {
2128        "jsonb_extract"
2129    }
2130}
2131
2132pub struct JsonArrowFunc;
2133
2134impl ScalarFunction for JsonArrowFunc {
2135    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2136        invoke_json_arrow(self.name(), args, false)
2137    }
2138
2139    fn num_args(&self) -> i32 {
2140        2
2141    }
2142
2143    fn name(&self) -> &'static str {
2144        "json_arrow"
2145    }
2146}
2147
2148pub struct JsonDoubleArrowFunc;
2149
2150impl ScalarFunction for JsonDoubleArrowFunc {
2151    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2152        invoke_json_arrow(self.name(), args, true)
2153    }
2154
2155    fn num_args(&self) -> i32 {
2156        2
2157    }
2158
2159    fn name(&self) -> &'static str {
2160        "json_double_arrow"
2161    }
2162}
2163
2164pub struct JsonArrayFunc;
2165
2166impl ScalarFunction for JsonArrayFunc {
2167    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2168        let s = json_array(args)?;
2169        Ok(SqliteValue::Text(Arc::from(s)))
2170    }
2171
2172    fn num_args(&self) -> i32 {
2173        -1
2174    }
2175
2176    fn name(&self) -> &'static str {
2177        "json_array"
2178    }
2179}
2180
2181pub struct JsonbArrayFunc;
2182
2183impl ScalarFunction for JsonbArrayFunc {
2184    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2185        let blob = jsonb_array(args)?;
2186        Ok(SqliteValue::Blob(Arc::from(blob.as_slice())))
2187    }
2188
2189    fn num_args(&self) -> i32 {
2190        -1
2191    }
2192
2193    fn name(&self) -> &'static str {
2194        "jsonb_array"
2195    }
2196}
2197
2198pub struct JsonObjectFunc;
2199
2200impl ScalarFunction for JsonObjectFunc {
2201    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2202        let s = json_object(args)?;
2203        Ok(SqliteValue::Text(Arc::from(s)))
2204    }
2205
2206    fn num_args(&self) -> i32 {
2207        -1
2208    }
2209
2210    fn name(&self) -> &'static str {
2211        "json_object"
2212    }
2213}
2214
2215pub struct JsonbObjectFunc;
2216
2217impl ScalarFunction for JsonbObjectFunc {
2218    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2219        let blob = jsonb_object(args)?;
2220        Ok(SqliteValue::Blob(Arc::from(blob.as_slice())))
2221    }
2222
2223    fn num_args(&self) -> i32 {
2224        -1
2225    }
2226
2227    fn name(&self) -> &'static str {
2228        "jsonb_object"
2229    }
2230}
2231
2232pub struct JsonQuoteFunc;
2233
2234impl ScalarFunction for JsonQuoteFunc {
2235    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2236        if args.len() != 1 {
2237            return Err(invalid_arity(self.name(), "exactly 1 argument", args.len()));
2238        }
2239        let s = json_quote(&args[0])?;
2240        Ok(SqliteValue::Text(Arc::from(s)))
2241    }
2242
2243    fn num_args(&self) -> i32 {
2244        1
2245    }
2246
2247    fn name(&self) -> &'static str {
2248        "json_quote"
2249    }
2250}
2251
2252pub struct JsonSetFunc;
2253
2254impl ScalarFunction for JsonSetFunc {
2255    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2256        if args.len() < 3 || args.len() % 2 == 0 {
2257            return Err(invalid_arity(
2258                self.name(),
2259                "an odd argument count >= 3 (json, path, value, ...)",
2260                args.len(),
2261            ));
2262        }
2263        if matches!(args[0], SqliteValue::Null) {
2264            return Ok(SqliteValue::Null);
2265        }
2266        // SQLite returns NULL when any path argument is NULL.
2267        if args[1..]
2268            .iter()
2269            .step_by(2)
2270            .any(|a| matches!(a, SqliteValue::Null))
2271        {
2272            return Ok(SqliteValue::Null);
2273        }
2274        let input = json_arg_value(self.name(), args, 0)?;
2275        let pairs_owned = collect_path_value_pairs(self.name(), args, 1)?;
2276        let pairs = pairs_owned
2277            .iter()
2278            .map(|(path, value)| (path.as_str(), value.clone()))
2279            .collect::<Vec<_>>();
2280        let edited = edit_json_paths_value(&input, &pairs, EditMode::Set)?;
2281        let encoded = encode_json_text("json edit encode failed", &edited)?;
2282        Ok(SqliteValue::Text(Arc::from(encoded)))
2283    }
2284
2285    fn num_args(&self) -> i32 {
2286        -1
2287    }
2288
2289    fn name(&self) -> &'static str {
2290        "json_set"
2291    }
2292}
2293
2294pub struct JsonbSetFunc;
2295
2296impl ScalarFunction for JsonbSetFunc {
2297    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2298        if args.len() < 3 || args.len() % 2 == 0 {
2299            return Err(invalid_arity(
2300                self.name(),
2301                "an odd argument count >= 3 (json, path, value, ...)",
2302                args.len(),
2303            ));
2304        }
2305        if matches!(args[0], SqliteValue::Null) {
2306            return Ok(SqliteValue::Null);
2307        }
2308        if args[1..]
2309            .iter()
2310            .step_by(2)
2311            .any(|a| matches!(a, SqliteValue::Null))
2312        {
2313            return Ok(SqliteValue::Null);
2314        }
2315        let input = json_arg_value(self.name(), args, 0)?;
2316        let pairs_owned = collect_path_value_pairs(self.name(), args, 1)?;
2317        let pairs = pairs_owned
2318            .iter()
2319            .map(|(path, value)| (path.as_str(), value.clone()))
2320            .collect::<Vec<_>>();
2321        let edited = edit_json_paths_value(&input, &pairs, EditMode::Set)?;
2322        let blob = encode_jsonb_root(&edited)?;
2323        Ok(SqliteValue::Blob(Arc::from(blob.as_slice())))
2324    }
2325
2326    fn num_args(&self) -> i32 {
2327        -1
2328    }
2329
2330    fn name(&self) -> &'static str {
2331        "jsonb_set"
2332    }
2333}
2334
2335pub struct JsonInsertFunc;
2336
2337impl ScalarFunction for JsonInsertFunc {
2338    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2339        if args.len() < 3 || args.len() % 2 == 0 {
2340            return Err(invalid_arity(
2341                self.name(),
2342                "an odd argument count >= 3 (json, path, value, ...)",
2343                args.len(),
2344            ));
2345        }
2346        if matches!(args[0], SqliteValue::Null) {
2347            return Ok(SqliteValue::Null);
2348        }
2349        // SQLite returns NULL when any path argument is NULL.
2350        if args[1..]
2351            .iter()
2352            .step_by(2)
2353            .any(|a| matches!(a, SqliteValue::Null))
2354        {
2355            return Ok(SqliteValue::Null);
2356        }
2357        let input = json_arg_value(self.name(), args, 0)?;
2358        let pairs_owned = collect_path_value_pairs(self.name(), args, 1)?;
2359        let pairs = pairs_owned
2360            .iter()
2361            .map(|(path, value)| (path.as_str(), value.clone()))
2362            .collect::<Vec<_>>();
2363        let edited = edit_json_paths_value(&input, &pairs, EditMode::Insert)?;
2364        let encoded = encode_json_text("json edit encode failed", &edited)?;
2365        Ok(SqliteValue::Text(Arc::from(encoded)))
2366    }
2367
2368    fn num_args(&self) -> i32 {
2369        -1
2370    }
2371
2372    fn name(&self) -> &'static str {
2373        "json_insert"
2374    }
2375}
2376
2377pub struct JsonbInsertFunc;
2378
2379impl ScalarFunction for JsonbInsertFunc {
2380    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2381        if args.len() < 3 || args.len() % 2 == 0 {
2382            return Err(invalid_arity(
2383                self.name(),
2384                "an odd argument count >= 3 (json, path, value, ...)",
2385                args.len(),
2386            ));
2387        }
2388        if matches!(args[0], SqliteValue::Null) {
2389            return Ok(SqliteValue::Null);
2390        }
2391        if args[1..]
2392            .iter()
2393            .step_by(2)
2394            .any(|a| matches!(a, SqliteValue::Null))
2395        {
2396            return Ok(SqliteValue::Null);
2397        }
2398        let input = json_arg_value(self.name(), args, 0)?;
2399        let pairs_owned = collect_path_value_pairs(self.name(), args, 1)?;
2400        let pairs = pairs_owned
2401            .iter()
2402            .map(|(path, value)| (path.as_str(), value.clone()))
2403            .collect::<Vec<_>>();
2404        let edited = edit_json_paths_value(&input, &pairs, EditMode::Insert)?;
2405        let blob = encode_jsonb_root(&edited)?;
2406        Ok(SqliteValue::Blob(Arc::from(blob.as_slice())))
2407    }
2408
2409    fn num_args(&self) -> i32 {
2410        -1
2411    }
2412
2413    fn name(&self) -> &'static str {
2414        "jsonb_insert"
2415    }
2416}
2417
2418pub struct JsonReplaceFunc;
2419
2420impl ScalarFunction for JsonReplaceFunc {
2421    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2422        if args.len() < 3 || args.len() % 2 == 0 {
2423            return Err(invalid_arity(
2424                self.name(),
2425                "an odd argument count >= 3 (json, path, value, ...)",
2426                args.len(),
2427            ));
2428        }
2429        if matches!(args[0], SqliteValue::Null) {
2430            return Ok(SqliteValue::Null);
2431        }
2432        // SQLite returns NULL when any path argument is NULL.
2433        if args[1..]
2434            .iter()
2435            .step_by(2)
2436            .any(|a| matches!(a, SqliteValue::Null))
2437        {
2438            return Ok(SqliteValue::Null);
2439        }
2440        let input = json_arg_value(self.name(), args, 0)?;
2441        let pairs_owned = collect_path_value_pairs(self.name(), args, 1)?;
2442        let pairs = pairs_owned
2443            .iter()
2444            .map(|(path, value)| (path.as_str(), value.clone()))
2445            .collect::<Vec<_>>();
2446        let edited = edit_json_paths_value(&input, &pairs, EditMode::Replace)?;
2447        let encoded = encode_json_text("json edit encode failed", &edited)?;
2448        Ok(SqliteValue::Text(Arc::from(encoded)))
2449    }
2450
2451    fn num_args(&self) -> i32 {
2452        -1
2453    }
2454
2455    fn name(&self) -> &'static str {
2456        "json_replace"
2457    }
2458}
2459
2460pub struct JsonbReplaceFunc;
2461
2462impl ScalarFunction for JsonbReplaceFunc {
2463    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2464        if args.len() < 3 || args.len() % 2 == 0 {
2465            return Err(invalid_arity(
2466                self.name(),
2467                "an odd argument count >= 3 (json, path, value, ...)",
2468                args.len(),
2469            ));
2470        }
2471        if matches!(args[0], SqliteValue::Null) {
2472            return Ok(SqliteValue::Null);
2473        }
2474        if args[1..]
2475            .iter()
2476            .step_by(2)
2477            .any(|a| matches!(a, SqliteValue::Null))
2478        {
2479            return Ok(SqliteValue::Null);
2480        }
2481        let input = json_arg_value(self.name(), args, 0)?;
2482        let pairs_owned = collect_path_value_pairs(self.name(), args, 1)?;
2483        let pairs = pairs_owned
2484            .iter()
2485            .map(|(path, value)| (path.as_str(), value.clone()))
2486            .collect::<Vec<_>>();
2487        let edited = edit_json_paths_value(&input, &pairs, EditMode::Replace)?;
2488        let blob = encode_jsonb_root(&edited)?;
2489        Ok(SqliteValue::Blob(Arc::from(blob.as_slice())))
2490    }
2491
2492    fn num_args(&self) -> i32 {
2493        -1
2494    }
2495
2496    fn name(&self) -> &'static str {
2497        "jsonb_replace"
2498    }
2499}
2500
2501pub struct JsonRemoveFunc;
2502
2503impl ScalarFunction for JsonRemoveFunc {
2504    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2505        if args.is_empty() {
2506            return Err(invalid_arity(
2507                self.name(),
2508                "at least 1 argument (json [, path...])",
2509                args.len(),
2510            ));
2511        }
2512        if matches!(args[0], SqliteValue::Null) {
2513            return Ok(SqliteValue::Null);
2514        }
2515        let input = json_arg_value(self.name(), args, 0)?;
2516        if args.len() == 1 {
2517            // With just the JSON argument, validate and return minified.
2518            let encoded = encode_json_text("json serialize failed", &input)?;
2519            return Ok(SqliteValue::Text(Arc::from(encoded)));
2520        }
2521        // SQLite returns NULL when any path argument is NULL.
2522        if args[1..].iter().any(|a| matches!(a, SqliteValue::Null)) {
2523            return Ok(SqliteValue::Null);
2524        }
2525        let paths = collect_path_args(self.name(), args, 1)?;
2526        let edited = json_remove_value(&input, &paths)?;
2527        let encoded = encode_json_text("json_remove encode failed", &edited)?;
2528        Ok(SqliteValue::Text(Arc::from(encoded)))
2529    }
2530
2531    fn num_args(&self) -> i32 {
2532        -1
2533    }
2534
2535    fn name(&self) -> &'static str {
2536        "json_remove"
2537    }
2538}
2539
2540pub struct JsonbRemoveFunc;
2541
2542impl ScalarFunction for JsonbRemoveFunc {
2543    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2544        if args.is_empty() {
2545            return Err(invalid_arity(
2546                self.name(),
2547                "at least 1 argument (json [, path...])",
2548                args.len(),
2549            ));
2550        }
2551        if matches!(args[0], SqliteValue::Null) {
2552            return Ok(SqliteValue::Null);
2553        }
2554        let input = json_arg_value(self.name(), args, 0)?;
2555        if args.len() == 1 {
2556            let blob = encode_jsonb_root(&input)?;
2557            return Ok(SqliteValue::Blob(Arc::from(blob.as_slice())));
2558        }
2559        if args[1..].iter().any(|a| matches!(a, SqliteValue::Null)) {
2560            return Ok(SqliteValue::Null);
2561        }
2562        let paths = collect_path_args(self.name(), args, 1)?;
2563        let edited = json_remove_value(&input, &paths)?;
2564        let blob = encode_jsonb_root(&edited)?;
2565        Ok(SqliteValue::Blob(Arc::from(blob.as_slice())))
2566    }
2567
2568    fn num_args(&self) -> i32 {
2569        -1
2570    }
2571
2572    fn name(&self) -> &'static str {
2573        "jsonb_remove"
2574    }
2575}
2576
2577pub struct JsonPatchFunc;
2578
2579impl ScalarFunction for JsonPatchFunc {
2580    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2581        if args.len() != 2 {
2582            return Err(invalid_arity(
2583                self.name(),
2584                "exactly 2 arguments",
2585                args.len(),
2586            ));
2587        }
2588        if matches!(args[0], SqliteValue::Null) || matches!(args[1], SqliteValue::Null) {
2589            return Ok(SqliteValue::Null);
2590        }
2591        let input = json_arg_value(self.name(), args, 0)?;
2592        let patch = json_arg_value(self.name(), args, 1)?;
2593        let merged = json_patch_value(&input, &patch);
2594        let encoded = encode_json_text("json_patch encode failed", &merged)?;
2595        Ok(SqliteValue::Text(Arc::from(encoded)))
2596    }
2597
2598    fn num_args(&self) -> i32 {
2599        2
2600    }
2601
2602    fn name(&self) -> &'static str {
2603        "json_patch"
2604    }
2605}
2606
2607pub struct JsonbPatchFunc;
2608
2609impl ScalarFunction for JsonbPatchFunc {
2610    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2611        if args.len() != 2 {
2612            return Err(invalid_arity(
2613                self.name(),
2614                "exactly 2 arguments",
2615                args.len(),
2616            ));
2617        }
2618        if matches!(args[0], SqliteValue::Null) || matches!(args[1], SqliteValue::Null) {
2619            return Ok(SqliteValue::Null);
2620        }
2621        let input = json_arg_value(self.name(), args, 0)?;
2622        let patch = json_arg_value(self.name(), args, 1)?;
2623        let merged = json_patch_value(&input, &patch);
2624        let blob = encode_jsonb_root(&merged)?;
2625        Ok(SqliteValue::Blob(Arc::from(blob.as_slice())))
2626    }
2627
2628    fn num_args(&self) -> i32 {
2629        2
2630    }
2631
2632    fn name(&self) -> &'static str {
2633        "jsonb_patch"
2634    }
2635}
2636
2637pub struct JsonArrayLengthFunc;
2638
2639impl ScalarFunction for JsonArrayLengthFunc {
2640    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2641        if !(1..=2).contains(&args.len()) {
2642            return Err(invalid_arity(self.name(), "1 or 2 arguments", args.len()));
2643        }
2644        if matches!(args[0], SqliteValue::Null) {
2645            return Ok(SqliteValue::Null);
2646        }
2647        let input = json_arg_value(self.name(), args, 0)?;
2648        let path = if args.len() == 2 {
2649            if matches!(args[1], SqliteValue::Null) {
2650                return Ok(SqliteValue::Null);
2651            }
2652            Some(text_arg(self.name(), args, 1)?)
2653        } else {
2654            None
2655        };
2656        Ok(match json_array_length_value(&input, path)? {
2657            Some(len) => SqliteValue::Integer(usize_to_i64(self.name(), len)?),
2658            None => SqliteValue::Null,
2659        })
2660    }
2661
2662    fn num_args(&self) -> i32 {
2663        -1
2664    }
2665
2666    fn name(&self) -> &'static str {
2667        "json_array_length"
2668    }
2669}
2670
2671pub struct JsonErrorPositionFunc;
2672
2673impl ScalarFunction for JsonErrorPositionFunc {
2674    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2675        if args.len() != 1 {
2676            return Err(invalid_arity(self.name(), "exactly 1 argument", args.len()));
2677        }
2678        if matches!(args[0], SqliteValue::Null) {
2679            return Ok(SqliteValue::Null);
2680        }
2681        let position = match &args[0] {
2682            SqliteValue::Text(text) => json_error_position(text),
2683            SqliteValue::Blob(bytes) => json_error_position_blob(bytes),
2684            other => {
2685                return Err(FrankenError::function_error(format!(
2686                    "{} argument 1 must be TEXT or BLOB, got {}",
2687                    self.name(),
2688                    other.typeof_str()
2689                )));
2690            }
2691        };
2692        Ok(SqliteValue::Integer(usize_to_i64(self.name(), position)?))
2693    }
2694
2695    fn num_args(&self) -> i32 {
2696        1
2697    }
2698
2699    fn name(&self) -> &'static str {
2700        "json_error_position"
2701    }
2702}
2703
2704pub struct JsonPrettyFunc;
2705
2706impl ScalarFunction for JsonPrettyFunc {
2707    fn invoke(&self, args: &[SqliteValue]) -> Result<SqliteValue> {
2708        if !(1..=2).contains(&args.len()) {
2709            return Err(invalid_arity(self.name(), "1 or 2 arguments", args.len()));
2710        }
2711        if matches!(args[0], SqliteValue::Null) {
2712            return Ok(SqliteValue::Null);
2713        }
2714        let input = json_arg_value(self.name(), args, 0)?;
2715        let indent = if args.len() == 2 {
2716            if matches!(args[1], SqliteValue::Null) {
2717                None
2718            } else {
2719                Some(text_arg(self.name(), args, 1)?)
2720            }
2721        } else {
2722            None
2723        };
2724        let s = json_pretty_value(&input, indent)?;
2725        Ok(SqliteValue::Text(Arc::from(s)))
2726    }
2727
2728    fn num_args(&self) -> i32 {
2729        -1
2730    }
2731
2732    fn name(&self) -> &'static str {
2733        "json_pretty"
2734    }
2735}
2736
2737/// Register JSON1 scalar functions into a `FunctionRegistry`.
2738pub fn register_json_scalars(registry: &mut FunctionRegistry) {
2739    registry.register_scalar(JsonFunc);
2740    registry.register_scalar(JsonbFunc);
2741    registry.register_scalar(JsonValidFunc);
2742    registry.register_scalar(JsonTypeFunc);
2743    registry.register_scalar(JsonExtractFunc);
2744    registry.register_scalar(JsonbExtractFunc);
2745    registry.register_scalar(JsonArrowFunc);
2746    registry.register_scalar(JsonDoubleArrowFunc);
2747    registry.register_scalar(JsonArrayFunc);
2748    registry.register_scalar(JsonbArrayFunc);
2749    registry.register_scalar(JsonObjectFunc);
2750    registry.register_scalar(JsonbObjectFunc);
2751    registry.register_scalar(JsonQuoteFunc);
2752    registry.register_scalar(JsonSetFunc);
2753    registry.register_scalar(JsonbSetFunc);
2754    registry.register_scalar(JsonInsertFunc);
2755    registry.register_scalar(JsonbInsertFunc);
2756    registry.register_scalar(JsonReplaceFunc);
2757    registry.register_scalar(JsonbReplaceFunc);
2758    registry.register_scalar(JsonRemoveFunc);
2759    registry.register_scalar(JsonbRemoveFunc);
2760    registry.register_scalar(JsonPatchFunc);
2761    registry.register_scalar(JsonbPatchFunc);
2762    registry.register_scalar(JsonArrayLengthFunc);
2763    registry.register_scalar(JsonErrorPositionFunc);
2764    registry.register_scalar(JsonPrettyFunc);
2765}
2766
2767#[cfg(test)]
2768mod tests {
2769    use super::*;
2770    use fsqlite_func::FunctionRegistry;
2771
2772    #[test]
2773    fn test_register_json_scalars_registers_core_functions() {
2774        let mut registry = FunctionRegistry::new();
2775        register_json_scalars(&mut registry);
2776
2777        for name in [
2778            "json",
2779            "jsonb",
2780            "json_valid",
2781            "json_type",
2782            "json_extract",
2783            "jsonb_extract",
2784            "json_arrow",
2785            "json_double_arrow",
2786            "json_set",
2787            "jsonb_set",
2788            "json_remove",
2789            "jsonb_remove",
2790            "json_array",
2791            "jsonb_array",
2792            "json_object",
2793            "jsonb_object",
2794            "json_quote",
2795            "json_patch",
2796            "jsonb_patch",
2797        ] {
2798            assert!(
2799                registry.contains_scalar(name),
2800                "missing registration for {name}"
2801            );
2802        }
2803    }
2804
2805    #[test]
2806    fn test_registered_json_extract_scalar_executes() {
2807        let mut registry = FunctionRegistry::new();
2808        register_json_scalars(&mut registry);
2809        let func = registry
2810            .find_scalar("json_extract", 2)
2811            .expect("json_extract should be registered");
2812        let out = func
2813            .invoke(&[
2814                SqliteValue::Text(Arc::from(r#"{"a":1,"b":[2,3]}"#)),
2815                SqliteValue::Text(Arc::from("$.b[1]")),
2816            ])
2817            .unwrap();
2818        assert_eq!(out, SqliteValue::Integer(3));
2819    }
2820
2821    #[test]
2822    fn test_registered_json_arrow_scalar_normalizes_label_shorthand() {
2823        let mut registry = FunctionRegistry::new();
2824        register_json_scalars(&mut registry);
2825        let func = registry
2826            .find_scalar("json_arrow", 2)
2827            .expect("json_arrow should be registered");
2828        let out = func
2829            .invoke(&[
2830                SqliteValue::Text(Arc::from(r#"{"a.b":1,"a":{"b":2}}"#)),
2831                SqliteValue::Text(Arc::from("a.b")),
2832            ])
2833            .expect("json_arrow should normalize bare labels");
2834        assert_eq!(out, SqliteValue::Text(Arc::from("1")));
2835    }
2836
2837    #[test]
2838    fn test_registered_json_double_arrow_scalar_normalizes_integer_shorthand() {
2839        let mut registry = FunctionRegistry::new();
2840        register_json_scalars(&mut registry);
2841        let func = registry
2842            .find_scalar("json_double_arrow", 2)
2843            .expect("json_double_arrow should be registered");
2844        let out = func
2845            .invoke(&[
2846                SqliteValue::Text(Arc::from("[10,20,30]")),
2847                SqliteValue::Integer(1),
2848            ])
2849            .expect("json_double_arrow should normalize integer indexes");
2850        assert_eq!(out, SqliteValue::Integer(20));
2851    }
2852
2853    #[test]
2854    fn test_registered_json_set_scalar_executes() {
2855        let mut registry = FunctionRegistry::new();
2856        register_json_scalars(&mut registry);
2857        let func = registry
2858            .find_scalar("json_set", 3)
2859            .expect("json_set should be registered");
2860        let out = func
2861            .invoke(&[
2862                SqliteValue::Text(Arc::from(r#"{"a":1}"#)),
2863                SqliteValue::Text(Arc::from("$.b")),
2864                SqliteValue::Integer(2),
2865            ])
2866            .unwrap();
2867        assert_eq!(out, SqliteValue::Text(Arc::from(r#"{"a":1,"b":2}"#)));
2868    }
2869
2870    #[test]
2871    fn test_registered_jsonb_scalar_executes() {
2872        let mut registry = FunctionRegistry::new();
2873        register_json_scalars(&mut registry);
2874        let func = registry
2875            .find_scalar("jsonb", 1)
2876            .expect("jsonb should be registered");
2877        let out = func
2878            .invoke(&[SqliteValue::Text(Arc::from(r#"{"a":[1,2]}"#))])
2879            .expect("jsonb should encode to JSONB");
2880        let SqliteValue::Blob(blob) = out else {
2881            panic!("jsonb should return BLOB");
2882        };
2883        assert_eq!(json_from_jsonb(&blob).unwrap(), r#"{"a":[1,2]}"#);
2884    }
2885
2886    #[test]
2887    fn test_registered_json_extract_accepts_jsonb_blob_input() {
2888        let mut registry = FunctionRegistry::new();
2889        register_json_scalars(&mut registry);
2890        let func = registry
2891            .find_scalar("json_extract", 2)
2892            .expect("json_extract should be registered");
2893        let input = jsonb(r#"{"a":{"b":7}}"#).unwrap();
2894        let out = func
2895            .invoke(&[
2896                SqliteValue::Blob(Arc::from(input)),
2897                SqliteValue::Text(Arc::from("$.a.b")),
2898            ])
2899            .expect("json_extract should accept JSONB blob input");
2900        assert_eq!(out, SqliteValue::Integer(7));
2901    }
2902
2903    #[test]
2904    fn test_registered_jsonb_set_accepts_jsonb_blob_input() {
2905        let mut registry = FunctionRegistry::new();
2906        register_json_scalars(&mut registry);
2907        let func = registry
2908            .find_scalar("jsonb_set", 3)
2909            .expect("jsonb_set should be registered");
2910        let input = jsonb(r#"{"a":1}"#).unwrap();
2911        let out = func
2912            .invoke(&[
2913                SqliteValue::Blob(Arc::from(input)),
2914                SqliteValue::Text(Arc::from("$.b")),
2915                SqliteValue::Integer(9),
2916            ])
2917            .expect("jsonb_set should accept JSONB blob input");
2918        let SqliteValue::Blob(blob) = out else {
2919            panic!("jsonb_set should return BLOB");
2920        };
2921        assert_eq!(json_from_jsonb(&blob).unwrap(), r#"{"a":1,"b":9}"#);
2922    }
2923
2924    #[test]
2925    fn test_registered_json_pretty_accepts_jsonb_blob_input() {
2926        let mut registry = FunctionRegistry::new();
2927        register_json_scalars(&mut registry);
2928        let func = registry
2929            .find_scalar("json_pretty", 1)
2930            .expect("json_pretty should be registered");
2931        let input = jsonb(r#"{"a":[1,2]}"#).unwrap();
2932        let out = func
2933            .invoke(&[SqliteValue::Blob(Arc::from(input))])
2934            .expect("json_pretty should accept JSONB blob input");
2935        let SqliteValue::Text(pretty) = out else {
2936            panic!("json_pretty should return TEXT");
2937        };
2938        assert!(pretty.contains('\n'));
2939        assert!(pretty.contains("\"a\""));
2940    }
2941
2942    #[test]
2943    fn test_registered_json_error_position_accepts_jsonb_blob_input() {
2944        let mut registry = FunctionRegistry::new();
2945        register_json_scalars(&mut registry);
2946        let func = registry
2947            .find_scalar("json_error_position", 1)
2948            .expect("json_error_position should be registered");
2949        let input = jsonb(r#"{"a":1}"#).unwrap();
2950        let out = func
2951            .invoke(&[SqliteValue::Blob(Arc::from(input))])
2952            .expect("json_error_position should accept JSONB blob input");
2953        assert_eq!(out, SqliteValue::Integer(0));
2954    }
2955
2956    #[test]
2957    fn test_json_valid_text() {
2958        assert_eq!(json(r#"{"a":1}"#).unwrap(), r#"{"a":1}"#);
2959    }
2960
2961    #[test]
2962    fn test_json_invalid_error() {
2963        let err = json("not json").unwrap_err();
2964        assert!(matches!(err, FrankenError::FunctionError(_)));
2965    }
2966
2967    #[test]
2968    fn test_json_valid_flags_default() {
2969        assert_eq!(json_valid(r#"{"a":1}"#, None), 1);
2970        assert_eq!(json_valid("not json", None), 0);
2971    }
2972
2973    #[test]
2974    fn test_json_valid_flags_json5() {
2975        let json5_text = concat!("{", "a:1", "}");
2976        assert_eq!(json_valid(json5_text, Some(JSON_VALID_JSON5_FLAG)), 1);
2977        assert_eq!(json_valid(json5_text, Some(JSON_VALID_RFC_8259_FLAG)), 0);
2978    }
2979
2980    #[test]
2981    fn test_json_valid_flags_strict() {
2982        assert_eq!(json_valid("invalid", Some(JSON_VALID_RFC_8259_FLAG)), 0);
2983    }
2984
2985    #[test]
2986    fn test_json_valid_flags_jsonb() {
2987        let payload = jsonb(r#"{"a":[1,2,3]}"#).unwrap();
2988        assert_eq!(
2989            json_valid_blob(&payload, Some(JSON_VALID_JSONB_SUPERFICIAL_FLAG)),
2990            1
2991        );
2992        assert_eq!(
2993            json_valid_blob(&payload, Some(JSON_VALID_JSONB_STRICT_FLAG)),
2994            1
2995        );
2996        // Trailing byte changes total blob size — both superficial and strict
2997        // reject it since header + payload_len != blob_len.
2998        let mut trailing = payload.clone();
2999        trailing.push(0xFF);
3000        assert_eq!(
3001            json_valid_blob(&trailing, Some(JSON_VALID_JSONB_SUPERFICIAL_FLAG)),
3002            0
3003        );
3004        assert_eq!(
3005            json_valid_blob(&trailing, Some(JSON_VALID_JSONB_STRICT_FLAG)),
3006            0
3007        );
3008
3009        // Corrupt an interior header byte — top-level header still valid
3010        // (superficial passes) but deep decode fails (strict rejects).
3011        // Byte 2 is the first sub-element header inside the object payload;
3012        // 0xFF has node_type=0x0F (invalid) so strict parsing fails.
3013        let mut corrupted = payload;
3014        assert!(corrupted.len() > 3);
3015        corrupted[2] = 0xFF;
3016        assert_eq!(
3017            json_valid_blob(&corrupted, Some(JSON_VALID_JSONB_SUPERFICIAL_FLAG)),
3018            1
3019        );
3020        assert_eq!(
3021            json_valid_blob(&corrupted, Some(JSON_VALID_JSONB_STRICT_FLAG)),
3022            0
3023        );
3024    }
3025
3026    #[test]
3027    fn test_json_type_object() {
3028        assert_eq!(json_type(r#"{"a":1}"#, None).unwrap(), Some("object"));
3029    }
3030
3031    #[test]
3032    fn test_json_type_path() {
3033        assert_eq!(
3034            json_type(r#"{"a":1}"#, Some("$.a")).unwrap(),
3035            Some("integer")
3036        );
3037    }
3038
3039    #[test]
3040    fn test_json_type_missing_path() {
3041        assert_eq!(json_type(r#"{"a":1}"#, Some("$.b")).unwrap(), None);
3042    }
3043
3044    #[test]
3045    fn test_json_extract_single() {
3046        let result = json_extract(r#"{"a":1}"#, &["$.a"]).unwrap();
3047        assert_eq!(result, SqliteValue::Integer(1));
3048    }
3049
3050    #[test]
3051    fn test_json_extract_multiple() {
3052        let result = json_extract(r#"{"a":1,"b":2}"#, &["$.a", "$.b"]).unwrap();
3053        assert_eq!(result, SqliteValue::Text(Arc::from("[1,2]")));
3054    }
3055
3056    #[test]
3057    fn test_json_extract_string_unwrap() {
3058        let result = json_extract(r#"{"a":"hello"}"#, &["$.a"]).unwrap();
3059        assert_eq!(result, SqliteValue::Text(Arc::from("hello")));
3060    }
3061
3062    #[test]
3063    fn test_arrow_preserves_json() {
3064        let result = json_arrow(r#"{"a":"hello"}"#, "$.a").unwrap();
3065        assert_eq!(result, SqliteValue::Text(Arc::from(r#""hello""#)));
3066    }
3067
3068    #[test]
3069    fn test_double_arrow_unwraps() {
3070        let result = json_double_arrow(r#"{"a":"hello"}"#, "$.a").unwrap();
3071        assert_eq!(result, SqliteValue::Text(Arc::from("hello")));
3072    }
3073
3074    #[test]
3075    fn test_json_extract_array_index() {
3076        let result = json_extract("[10,20,30]", &["$[1]"]).unwrap();
3077        assert_eq!(result, SqliteValue::Integer(20));
3078    }
3079
3080    #[test]
3081    fn test_json_extract_quoted_key_segment() {
3082        let result = json_extract(r#"{"a.b":1}"#, &["$.\"a.b\""]).unwrap();
3083        assert_eq!(result, SqliteValue::Integer(1));
3084    }
3085
3086    #[test]
3087    fn test_json_extract_from_end() {
3088        let result = json_extract("[10,20,30]", &["$[#-1]"]).unwrap();
3089        assert_eq!(result, SqliteValue::Integer(30));
3090    }
3091
3092    #[test]
3093    fn test_jsonb_extract_returns_blob() {
3094        let blob = jsonb_extract(r#"{"a":"hello"}"#, &["$.a"]).unwrap();
3095        let text = json_from_jsonb(&blob).unwrap();
3096        assert_eq!(text, r#""hello""#);
3097    }
3098
3099    #[test]
3100    fn test_json_quote_text() {
3101        assert_eq!(
3102            json_quote(&SqliteValue::Text(Arc::from("hello"))).unwrap(),
3103            r#""hello""#
3104        );
3105    }
3106
3107    #[test]
3108    fn test_json_quote_null() {
3109        assert_eq!(json_quote(&SqliteValue::Null).unwrap(), "null");
3110    }
3111
3112    #[test]
3113    fn test_json_array_basic() {
3114        let out = json_array(&[
3115            SqliteValue::Integer(1),
3116            SqliteValue::Text(Arc::from("two")),
3117            SqliteValue::Null,
3118        ])
3119        .unwrap();
3120        assert_eq!(out, r#"[1,"two",null]"#);
3121    }
3122
3123    #[test]
3124    fn test_json_object_basic() {
3125        let out = json_object(&[
3126            SqliteValue::Text(Arc::from("a")),
3127            SqliteValue::Integer(1),
3128            SqliteValue::Text(Arc::from("b")),
3129            SqliteValue::Text(Arc::from("two")),
3130        ])
3131        .unwrap();
3132        assert_eq!(out, r#"{"a":1,"b":"two"}"#);
3133    }
3134
3135    #[test]
3136    fn test_jsonb_roundtrip() {
3137        let blob = jsonb(r#"{"a":1,"b":[2,3]}"#).unwrap();
3138        let text = json_from_jsonb(&blob).unwrap();
3139        assert_eq!(text, r#"{"a":1,"b":[2,3]}"#);
3140    }
3141
3142    #[test]
3143    fn test_jsonb_array_variant() {
3144        let blob = jsonb_array(&[
3145            SqliteValue::Integer(1),
3146            SqliteValue::Text(Arc::from("two")),
3147            SqliteValue::Null,
3148        ])
3149        .unwrap();
3150        assert_eq!(json_from_jsonb(&blob).unwrap(), r#"[1,"two",null]"#);
3151    }
3152
3153    #[test]
3154    fn test_jsonb_object_variant() {
3155        let blob = jsonb_object(&[
3156            SqliteValue::Text(Arc::from("a")),
3157            SqliteValue::Integer(1),
3158            SqliteValue::Text(Arc::from("b")),
3159            SqliteValue::Text(Arc::from("two")),
3160        ])
3161        .unwrap();
3162        assert_eq!(json_from_jsonb(&blob).unwrap(), r#"{"a":1,"b":"two"}"#);
3163    }
3164
3165    #[test]
3166    fn test_json_array_length() {
3167        assert_eq!(json_array_length("[1,2,3]", None).unwrap(), Some(3));
3168        assert_eq!(json_array_length("[]", None).unwrap(), Some(0));
3169        assert_eq!(json_array_length(r#"{"a":1}"#, None).unwrap(), None);
3170    }
3171
3172    #[test]
3173    fn test_json_array_length_path() {
3174        assert_eq!(
3175            json_array_length(r#"{"a":[1,2,3]}"#, Some("$.a")).unwrap(),
3176            Some(3)
3177        );
3178    }
3179
3180    #[test]
3181    fn test_json_array_length_not_array() {
3182        assert_eq!(json_array_length(r#"{"a":1}"#, Some("$.a")).unwrap(), None);
3183        assert_eq!(json_array_length(r#""text""#, None).unwrap(), None);
3184    }
3185
3186    #[test]
3187    fn test_json_error_position_valid() {
3188        assert_eq!(json_error_position(r#"{"a":1}"#), 0);
3189    }
3190
3191    #[test]
3192    fn test_json_error_position_invalid() {
3193        assert!(json_error_position(r#"{"a":}"#) > 0);
3194    }
3195
3196    #[test]
3197    fn test_json_pretty_default() {
3198        let output = json_pretty(r#"{"a":1}"#, None).unwrap();
3199        assert!(output.contains('\n'));
3200        assert!(output.contains("    \"a\""));
3201    }
3202
3203    #[test]
3204    fn test_json_pretty_custom_indent() {
3205        let output = json_pretty(r#"{"a":1}"#, Some("\t")).unwrap();
3206        assert!(output.contains("\n\t\"a\""));
3207    }
3208
3209    #[test]
3210    fn test_json_set_create() {
3211        let out = json_set(r#"{"a":1}"#, &[("$.b", SqliteValue::Integer(2))]).unwrap();
3212        assert_eq!(out, r#"{"a":1,"b":2}"#);
3213    }
3214
3215    #[test]
3216    fn test_json_set_nested_path_create() {
3217        let out = json_set("{}", &[("$.a.b", SqliteValue::Integer(1))]).unwrap();
3218        assert_eq!(out, r#"{"a":{"b":1}}"#);
3219    }
3220
3221    #[test]
3222    fn test_json_set_nested_array_path_create() {
3223        let out = json_set("{}", &[("$.a[0]", SqliteValue::Integer(1))]).unwrap();
3224        assert_eq!(out, r#"{"a":[1]}"#);
3225    }
3226
3227    #[test]
3228    fn test_json_set_nested_append_path_create() {
3229        let out = json_set("{}", &[("$.a[#]", SqliteValue::Integer(1))]).unwrap();
3230        assert_eq!(out, r#"{"a":[1]}"#);
3231    }
3232
3233    #[test]
3234    fn test_json_set_nested_array_object_create() {
3235        let out = json_set("{}", &[("$.a[0].b", SqliteValue::Integer(1))]).unwrap();
3236        assert_eq!(out, r#"{"a":[{"b":1}]}"#);
3237    }
3238
3239    #[test]
3240    fn test_json_set_nested_array_index_out_of_range_does_not_scaffold() {
3241        let out = json_set("{}", &[("$.a[1]", SqliteValue::Integer(1))]).unwrap();
3242        assert_eq!(out, "{}");
3243    }
3244
3245    #[test]
3246    fn test_json_set_nested_from_end_does_not_scaffold() {
3247        let out = json_set("{}", &[("$.a[#-1]", SqliteValue::Integer(1))]).unwrap();
3248        assert_eq!(out, "{}");
3249    }
3250
3251    #[test]
3252    fn test_json_set_scalar_root_with_array_path_is_noop() {
3253        let out = json_set("null", &[("$.a[0]", SqliteValue::Integer(1))]).unwrap();
3254        assert_eq!(out, "null");
3255    }
3256
3257    #[test]
3258    fn test_json_set_existing_null_value_with_array_path_is_noop() {
3259        let out = json_set(r#"{"a":null}"#, &[("$.a[1]", SqliteValue::Integer(1))]).unwrap();
3260        assert_eq!(out, r#"{"a":null}"#);
3261    }
3262
3263    #[test]
3264    fn test_json_set_overwrite() {
3265        let out = json_set(r#"{"a":1}"#, &[("$.a", SqliteValue::Integer(2))]).unwrap();
3266        assert_eq!(out, r#"{"a":2}"#);
3267    }
3268
3269    #[test]
3270    fn test_json_insert_no_overwrite() {
3271        let out = json_insert(r#"{"a":1}"#, &[("$.a", SqliteValue::Integer(2))]).unwrap();
3272        assert_eq!(out, r#"{"a":1}"#);
3273    }
3274
3275    #[test]
3276    fn test_json_insert_create() {
3277        let out = json_insert(r#"{"a":1}"#, &[("$.b", SqliteValue::Integer(2))]).unwrap();
3278        assert_eq!(out, r#"{"a":1,"b":2}"#);
3279    }
3280
3281    #[test]
3282    fn test_json_insert_nested_path_create() {
3283        let out = json_insert("{}", &[("$.a.b", SqliteValue::Integer(1))]).unwrap();
3284        assert_eq!(out, r#"{"a":{"b":1}}"#);
3285    }
3286
3287    #[test]
3288    fn test_json_insert_nested_array_path_create() {
3289        let out = json_insert("{}", &[("$.a[0]", SqliteValue::Integer(1))]).unwrap();
3290        assert_eq!(out, r#"{"a":[1]}"#);
3291    }
3292
3293    #[test]
3294    fn test_json_replace_overwrite() {
3295        let out = json_replace(r#"{"a":1}"#, &[("$.a", SqliteValue::Integer(2))]).unwrap();
3296        assert_eq!(out, r#"{"a":2}"#);
3297    }
3298
3299    #[test]
3300    fn test_json_replace_no_create() {
3301        let out = json_replace(r#"{"a":1}"#, &[("$.b", SqliteValue::Integer(2))]).unwrap();
3302        assert_eq!(out, r#"{"a":1}"#);
3303    }
3304
3305    #[test]
3306    fn test_json_remove_key() {
3307        let out = json_remove(r#"{"a":1,"b":2}"#, &["$.a"]).unwrap();
3308        assert_eq!(out, r#"{"b":2}"#);
3309    }
3310
3311    #[test]
3312    fn test_json_remove_array_compact() {
3313        let out = json_remove("[1,2,3]", &["$[1]"]).unwrap();
3314        assert_eq!(out, "[1,3]");
3315    }
3316
3317    #[test]
3318    fn test_json_patch_merge() {
3319        let out = json_patch(r#"{"a":1,"b":2}"#, r#"{"b":3,"c":4}"#).unwrap();
3320        assert_eq!(out, r#"{"a":1,"b":3,"c":4}"#);
3321    }
3322
3323    #[test]
3324    fn test_json_patch_delete() {
3325        let out = json_patch(r#"{"a":1,"b":2}"#, r#"{"b":null}"#).unwrap();
3326        assert_eq!(out, r#"{"a":1}"#);
3327    }
3328
3329    #[test]
3330    fn test_jsonb_set_variant() {
3331        let blob = jsonb_set(r#"{"a":1}"#, &[("$.a", SqliteValue::Integer(9))]).unwrap();
3332        let text = json_from_jsonb(&blob).unwrap();
3333        assert_eq!(text, r#"{"a":9}"#);
3334    }
3335
3336    #[test]
3337    fn test_jsonb_insert_variant() {
3338        let blob = jsonb_insert(r#"{"a":1}"#, &[("$.b", SqliteValue::Integer(2))]).unwrap();
3339        let text = json_from_jsonb(&blob).unwrap();
3340        assert_eq!(text, r#"{"a":1,"b":2}"#);
3341    }
3342
3343    #[test]
3344    fn test_jsonb_replace_variant() {
3345        let blob = jsonb_replace(r#"{"a":1}"#, &[("$.a", SqliteValue::Integer(5))]).unwrap();
3346        let text = json_from_jsonb(&blob).unwrap();
3347        assert_eq!(text, r#"{"a":5}"#);
3348    }
3349
3350    #[test]
3351    fn test_jsonb_remove_variant() {
3352        let blob = jsonb_remove(r#"{"a":1,"b":2}"#, &["$.a"]).unwrap();
3353        let text = json_from_jsonb(&blob).unwrap();
3354        assert_eq!(text, r#"{"b":2}"#);
3355    }
3356
3357    #[test]
3358    fn test_jsonb_patch_variant() {
3359        let blob = jsonb_patch(r#"{"a":1,"b":2}"#, r#"{"b":7}"#).unwrap();
3360        let text = json_from_jsonb(&blob).unwrap();
3361        assert_eq!(text, r#"{"a":1,"b":7}"#);
3362    }
3363
3364    #[test]
3365    fn test_json_group_array_includes_nulls() {
3366        let out = json_group_array(&[
3367            SqliteValue::Integer(1),
3368            SqliteValue::Null,
3369            SqliteValue::Integer(3),
3370        ])
3371        .unwrap();
3372        assert_eq!(out, "[1,null,3]");
3373    }
3374
3375    #[test]
3376    fn test_json_group_array_basic() {
3377        let out = json_group_array(&[
3378            SqliteValue::Integer(1),
3379            SqliteValue::Integer(2),
3380            SqliteValue::Integer(3),
3381        ])
3382        .unwrap();
3383        assert_eq!(out, "[1,2,3]");
3384    }
3385
3386    #[test]
3387    fn test_json_group_object_basic() {
3388        let out = json_group_object(&[
3389            (SqliteValue::Text(Arc::from("a")), SqliteValue::Integer(1)),
3390            (SqliteValue::Text(Arc::from("b")), SqliteValue::Integer(2)),
3391        ])
3392        .unwrap();
3393        assert_eq!(out, r#"{"a":1,"b":2}"#);
3394    }
3395
3396    #[test]
3397    fn test_json_group_object_duplicate_keys_last_wins() {
3398        let out = json_group_object(&[
3399            (SqliteValue::Text(Arc::from("k")), SqliteValue::Integer(1)),
3400            (SqliteValue::Text(Arc::from("k")), SqliteValue::Integer(2)),
3401        ])
3402        .unwrap();
3403        assert_eq!(out, r#"{"k":2}"#);
3404    }
3405
3406    #[test]
3407    fn test_jsonb_group_array_and_object_variants() {
3408        let array_blob = jsonb_group_array(&[SqliteValue::Integer(1), SqliteValue::Null]).unwrap();
3409        assert_eq!(json_from_jsonb(&array_blob).unwrap(), "[1,null]");
3410
3411        let object_blob =
3412            jsonb_group_object(&[(SqliteValue::Text(Arc::from("a")), SqliteValue::Integer(7))])
3413                .unwrap();
3414        assert_eq!(json_from_jsonb(&object_blob).unwrap(), r#"{"a":7}"#);
3415    }
3416
3417    #[test]
3418    fn test_json_each_array() {
3419        let rows = json_each("[10,20]", None).unwrap();
3420        assert_eq!(rows.len(), 2);
3421        assert_eq!(rows[0].key, SqliteValue::Integer(0));
3422        assert_eq!(rows[1].key, SqliteValue::Integer(1));
3423        assert_eq!(rows[0].value, SqliteValue::Integer(10));
3424        assert_eq!(rows[1].value, SqliteValue::Integer(20));
3425    }
3426
3427    #[test]
3428    fn test_json_each_object() {
3429        let rows = json_each(r#"{"a":1,"b":2}"#, None).unwrap();
3430        assert_eq!(rows.len(), 2);
3431        assert_eq!(rows[0].key, SqliteValue::Text(Arc::from("a")));
3432        assert_eq!(rows[1].key, SqliteValue::Text(Arc::from("b")));
3433        assert_eq!(rows[0].value, SqliteValue::Integer(1));
3434        assert_eq!(rows[1].value, SqliteValue::Integer(2));
3435    }
3436
3437    #[test]
3438    fn test_json_each_path() {
3439        let rows = json_each(r#"{"a":{"b":1,"c":2}}"#, Some("$.a")).unwrap();
3440        assert_eq!(rows.len(), 2);
3441        assert_eq!(rows[0].path, "$.a");
3442        assert_eq!(rows[1].path, "$.a");
3443    }
3444
3445    #[test]
3446    fn test_json_each_nested_value_is_json_text() {
3447        let rows = json_each(r#"{"a":[1,2]}"#, None).unwrap();
3448        assert_eq!(rows[0].value, SqliteValue::Text(Arc::from("[1,2]")));
3449        assert_eq!(rows[0].atom, SqliteValue::Null); // arrays have null atom
3450    }
3451
3452    #[test]
3453    fn test_json_tree_recursive() {
3454        let rows = json_tree(r#"{"a":{"b":1}}"#, None).unwrap();
3455        assert!(rows.iter().any(|row| row.fullkey == "$.a"));
3456        assert!(rows.iter().any(|row| row.fullkey == "$.a.b"));
3457    }
3458
3459    #[test]
3460    fn test_json_tree_columns() {
3461        let rows = json_tree(r#"{"a":{"b":1}}"#, None).unwrap();
3462        let row = rows
3463            .iter()
3464            .find(|candidate| candidate.fullkey == "$.a.b")
3465            .expect("nested row should exist");
3466        assert_eq!(row.key, SqliteValue::Text(Arc::from("b")));
3467        assert_eq!(row.value, SqliteValue::Integer(1));
3468        assert_eq!(row.type_name, "integer");
3469        assert_eq!(row.atom, SqliteValue::Integer(1));
3470        assert_eq!(row.path, "$.a");
3471    }
3472
3473    #[test]
3474    fn test_json_tree_vtab_cursor_scan() {
3475        let cx = Cx::new();
3476        let vtab = JsonTreeVtab::connect(&cx, &[]).unwrap();
3477        let mut cursor = vtab.open().unwrap();
3478        cursor
3479            .filter(
3480                &cx,
3481                0,
3482                None,
3483                &[
3484                    SqliteValue::Text(Arc::from(r#"{"a":{"b":1}}"#)),
3485                    SqliteValue::Text(Arc::from("$.a")),
3486                ],
3487            )
3488            .unwrap();
3489
3490        let mut fullkeys = Vec::new();
3491        while !cursor.eof() {
3492            let mut ctx = ColumnContext::new();
3493            cursor.column(&mut ctx, 6).unwrap();
3494            let fullkey = ctx.take_value().unwrap();
3495            if let SqliteValue::Text(text) = fullkey {
3496                fullkeys.push(text);
3497            }
3498            cursor.next(&cx).unwrap();
3499        }
3500
3501        assert_eq!(fullkeys, vec![Arc::from("$.a"), Arc::from("$.a.b")]);
3502    }
3503
3504    #[test]
3505    fn test_json_each_vtab_cursor_accepts_jsonb_blob_input() {
3506        let cx = Cx::new();
3507        let vtab = JsonEachVtab::connect(&cx, &[]).unwrap();
3508        let mut cursor = vtab.open().unwrap();
3509        let input = jsonb(r#"{"a":[10,20]}"#).unwrap();
3510        cursor
3511            .filter(
3512                &cx,
3513                0,
3514                None,
3515                &[
3516                    SqliteValue::Blob(Arc::from(input)),
3517                    SqliteValue::Text(Arc::from("$.a")),
3518                ],
3519            )
3520            .expect("json_each cursor should accept JSONB blob input");
3521
3522        let mut values = Vec::new();
3523        while !cursor.eof() {
3524            let mut ctx = ColumnContext::new();
3525            cursor.column(&mut ctx, 1).unwrap();
3526            values.push(ctx.take_value().unwrap());
3527            cursor.next(&cx).unwrap();
3528        }
3529
3530        assert_eq!(
3531            values,
3532            vec![SqliteValue::Integer(10), SqliteValue::Integer(20)]
3533        );
3534    }
3535
3536    #[test]
3537    fn test_json_tree_vtab_cursor_accepts_jsonb_blob_input() {
3538        let cx = Cx::new();
3539        let vtab = JsonTreeVtab::connect(&cx, &[]).unwrap();
3540        let mut cursor = vtab.open().unwrap();
3541        let input = jsonb(r#"{"a":{"b":1}}"#).unwrap();
3542        cursor
3543            .filter(
3544                &cx,
3545                0,
3546                None,
3547                &[
3548                    SqliteValue::Blob(Arc::from(input)),
3549                    SqliteValue::Text(Arc::from("$.a")),
3550                ],
3551            )
3552            .expect("json_tree cursor should accept JSONB blob input");
3553
3554        let mut fullkeys = Vec::new();
3555        while !cursor.eof() {
3556            let mut ctx = ColumnContext::new();
3557            cursor.column(&mut ctx, 6).unwrap();
3558            let fullkey = ctx.take_value().unwrap();
3559            if let SqliteValue::Text(text) = fullkey {
3560                fullkeys.push(text);
3561            }
3562            cursor.next(&cx).unwrap();
3563        }
3564
3565        assert_eq!(fullkeys, vec![Arc::from("$.a"), Arc::from("$.a.b")]);
3566    }
3567
3568    #[test]
3569    fn test_jsonb_chain_validity() {
3570        let first = jsonb_set(r#"{"a":1}"#, &[("$.a", SqliteValue::Integer(9))]).unwrap();
3571        let first_text = json_from_jsonb(&first).unwrap();
3572        let second = jsonb_patch(&first_text, r#"{"b":2}"#).unwrap();
3573        assert_eq!(
3574            json_valid_blob(&second, Some(JSON_VALID_JSONB_STRICT_FLAG)),
3575            1
3576        );
3577    }
3578
3579    // -----------------------------------------------------------------------
3580    // json() edge cases
3581    // -----------------------------------------------------------------------
3582
3583    #[test]
3584    fn test_json_minify_whitespace() {
3585        assert_eq!(json("  { \"a\" : 1 }  ").unwrap(), r#"{"a":1}"#);
3586    }
3587
3588    #[test]
3589    fn test_json_scalar_string() {
3590        assert_eq!(json(r#""hello""#).unwrap(), r#""hello""#);
3591    }
3592
3593    #[test]
3594    fn test_json_scalar_number() {
3595        assert_eq!(json("42").unwrap(), "42");
3596    }
3597
3598    #[test]
3599    fn test_json_scalar_null() {
3600        assert_eq!(json("null").unwrap(), "null");
3601    }
3602
3603    #[test]
3604    fn test_json_scalar_bool() {
3605        assert_eq!(json("true").unwrap(), "true");
3606        assert_eq!(json("false").unwrap(), "false");
3607    }
3608
3609    #[test]
3610    fn test_json_nested_structure() {
3611        let input = r#"{"a":{"b":[1,2,{"c":3}]}}"#;
3612        assert_eq!(json(input).unwrap(), input);
3613    }
3614
3615    #[test]
3616    fn test_json_unicode() {
3617        let input = r#"{"key":"\u00fc\u00e9"}"#;
3618        let result = json(input).unwrap();
3619        // After parse/re-serialize, unicode escapes become literal chars
3620        assert!(result.contains("key"));
3621    }
3622
3623    // -----------------------------------------------------------------------
3624    // json_valid edge cases
3625    // -----------------------------------------------------------------------
3626
3627    #[test]
3628    fn test_json_valid_zero_flags() {
3629        assert_eq!(json_valid(r#"{"a":1}"#, Some(0)), 0);
3630    }
3631
3632    #[test]
3633    fn test_json_valid_empty_string() {
3634        assert_eq!(json_valid("", None), 0);
3635    }
3636
3637    // -----------------------------------------------------------------------
3638    // json_type all variants
3639    // -----------------------------------------------------------------------
3640
3641    #[test]
3642    fn test_json_type_null() {
3643        assert_eq!(json_type("null", None).unwrap(), Some("null"));
3644    }
3645
3646    #[test]
3647    fn test_json_type_true() {
3648        assert_eq!(json_type("true", None).unwrap(), Some("true"));
3649    }
3650
3651    #[test]
3652    fn test_json_type_false() {
3653        assert_eq!(json_type("false", None).unwrap(), Some("false"));
3654    }
3655
3656    #[test]
3657    fn test_json_type_real() {
3658        assert_eq!(json_type("3.14", None).unwrap(), Some("real"));
3659    }
3660
3661    #[test]
3662    fn test_json_type_text() {
3663        assert_eq!(json_type(r#""hello""#, None).unwrap(), Some("text"));
3664    }
3665
3666    #[test]
3667    fn test_json_type_array() {
3668        assert_eq!(json_type("[1,2]", None).unwrap(), Some("array"));
3669    }
3670
3671    // -----------------------------------------------------------------------
3672    // json_extract edge cases
3673    // -----------------------------------------------------------------------
3674
3675    #[test]
3676    fn test_json_extract_missing_path_null() {
3677        let result = json_extract(r#"{"a":1}"#, &["$.b"]).unwrap();
3678        assert_eq!(result, SqliteValue::Null);
3679    }
3680
3681    #[test]
3682    fn test_json_extract_no_paths_error() {
3683        let empty: &[&str] = &[];
3684        assert!(json_extract(r#"{"a":1}"#, empty).is_err());
3685
3686        let func = JsonExtractFunc;
3687        let args = vec![SqliteValue::Text(Arc::from(r#"{"a":1}"#))];
3688        assert_eq!(func.invoke(&args).unwrap(), SqliteValue::Null);
3689    }
3690
3691    #[test]
3692    fn test_json_extract_null_value() {
3693        let result = json_extract(r#"{"a":null}"#, &["$.a"]).unwrap();
3694        assert_eq!(result, SqliteValue::Null);
3695    }
3696
3697    #[test]
3698    fn test_json_extract_boolean() {
3699        let result = json_extract(r#"{"a":true}"#, &["$.a"]).unwrap();
3700        assert_eq!(result, SqliteValue::Integer(1));
3701        let result = json_extract(r#"{"a":false}"#, &["$.a"]).unwrap();
3702        assert_eq!(result, SqliteValue::Integer(0));
3703    }
3704
3705    #[test]
3706    fn test_json_extract_nested_array() {
3707        let result = json_extract(r#"{"a":[[1,2],[3,4]]}"#, &["$.a[1][0]"]).unwrap();
3708        assert_eq!(result, SqliteValue::Integer(3));
3709    }
3710
3711    #[test]
3712    fn test_json_extract_multiple_with_missing() {
3713        let result = json_extract(r#"{"a":1}"#, &["$.a", "$.b"]).unwrap();
3714        assert_eq!(result, SqliteValue::Text(Arc::from("[1,null]")));
3715    }
3716
3717    // -----------------------------------------------------------------------
3718    // json_arrow edge cases
3719    // -----------------------------------------------------------------------
3720
3721    #[test]
3722    fn test_json_arrow_missing_path_null() {
3723        let result = json_arrow(r#"{"a":1}"#, "$.b").unwrap();
3724        assert_eq!(result, SqliteValue::Null);
3725    }
3726
3727    #[test]
3728    fn test_json_arrow_number() {
3729        let result = json_arrow(r#"{"a":42}"#, "$.a").unwrap();
3730        assert_eq!(result, SqliteValue::Text(Arc::from("42")));
3731    }
3732
3733    #[test]
3734    fn test_json_arrow_null() {
3735        let result = json_arrow(r#"{"a":null}"#, "$.a").unwrap();
3736        assert_eq!(result, SqliteValue::Text(Arc::from("null")));
3737    }
3738
3739    // -----------------------------------------------------------------------
3740    // json_array_length edge cases
3741    // -----------------------------------------------------------------------
3742
3743    #[test]
3744    fn test_json_array_length_nested_not_array() {
3745        assert_eq!(
3746            json_array_length(r#"{"a":"text"}"#, Some("$.a")).unwrap(),
3747            None
3748        );
3749    }
3750
3751    #[test]
3752    fn test_json_array_length_missing_path() {
3753        assert_eq!(json_array_length(r#"{"a":1}"#, Some("$.b")).unwrap(), None);
3754    }
3755
3756    // -----------------------------------------------------------------------
3757    // json_error_position edge cases
3758    // -----------------------------------------------------------------------
3759
3760    #[test]
3761    fn test_json_error_position_empty() {
3762        assert!(json_error_position("") > 0);
3763    }
3764
3765    #[test]
3766    fn test_json_error_position_just_brace() {
3767        assert!(json_error_position("{") > 0);
3768    }
3769
3770    // -----------------------------------------------------------------------
3771    // json_pretty edge cases
3772    // -----------------------------------------------------------------------
3773
3774    #[test]
3775    fn test_json_pretty_empty_array() {
3776        assert_eq!(json_pretty("[]", None).unwrap(), "[]");
3777    }
3778
3779    #[test]
3780    fn test_json_pretty_empty_object() {
3781        assert_eq!(json_pretty("{}", None).unwrap(), "{}");
3782    }
3783
3784    #[test]
3785    fn test_json_pretty_scalar() {
3786        assert_eq!(json_pretty("42", None).unwrap(), "42");
3787    }
3788
3789    #[test]
3790    fn test_json_pretty_nested() {
3791        let result = json_pretty(r#"{"a":[1,2]}"#, None).unwrap();
3792        assert!(result.contains('\n'));
3793        assert!(result.contains("\"a\""));
3794    }
3795
3796    // -----------------------------------------------------------------------
3797    // bd-6i2s required tests: json_pretty + jsonb availability
3798    // -----------------------------------------------------------------------
3799
3800    #[test]
3801    fn test_json_pretty_object() {
3802        let output = json_pretty(r#"{"a":1,"b":[2,3]}"#, None).unwrap();
3803        assert!(output.contains('\n'));
3804        assert!(output.contains("    \"a\""));
3805        assert!(output.contains("    \"b\""));
3806    }
3807
3808    #[test]
3809    fn test_json_pretty_array() {
3810        let output = json_pretty("[1,2,3]", None).unwrap();
3811        assert!(output.contains('\n'));
3812        assert!(output.contains("    1"));
3813        assert!(output.contains("    2"));
3814        assert!(output.contains("    3"));
3815    }
3816
3817    #[test]
3818    fn test_json_pretty_idempotent() {
3819        let input = r#"{"a":1,"b":[2,3]}"#;
3820        let first = json_pretty(input, None).unwrap();
3821        let second = json_pretty(&first, None).unwrap();
3822        assert_eq!(first, second, "json_pretty should be idempotent");
3823    }
3824
3825    #[test]
3826    fn test_jsonb_functions_available() {
3827        let blob = jsonb_array(&[SqliteValue::Integer(1), SqliteValue::Integer(2)]).unwrap();
3828        assert!(
3829            !blob.is_empty(),
3830            "jsonb_array should produce non-empty output"
3831        );
3832
3833        let blob2 = jsonb_set(r#"{"a":1}"#, &[("$.a", SqliteValue::Integer(9))]).unwrap();
3834        assert!(
3835            !blob2.is_empty(),
3836            "jsonb_set should produce non-empty output"
3837        );
3838
3839        let blob3 = jsonb_object(&[
3840            SqliteValue::Text(Arc::from("key")),
3841            SqliteValue::Integer(42),
3842        ])
3843        .unwrap();
3844        assert!(
3845            !blob3.is_empty(),
3846            "jsonb_object should produce non-empty output"
3847        );
3848    }
3849
3850    // -----------------------------------------------------------------------
3851    // json_quote edge cases
3852    // -----------------------------------------------------------------------
3853
3854    #[test]
3855    fn test_json_quote_integer() {
3856        assert_eq!(json_quote(&SqliteValue::Integer(42)).unwrap(), "42");
3857        assert_eq!(json_quote(&SqliteValue::Integer(-1)).unwrap(), "-1");
3858    }
3859
3860    #[test]
3861    fn test_json_quote_float() {
3862        #[allow(clippy::approx_constant)]
3863        let result = json_quote(&SqliteValue::Float(3.14)).unwrap();
3864        assert!(result.starts_with("3.14"));
3865    }
3866
3867    #[test]
3868    fn test_json_quote_float_infinity() {
3869        assert_eq!(
3870            json_quote(&SqliteValue::Float(f64::INFINITY)).unwrap(),
3871            "null"
3872        );
3873        assert_eq!(
3874            json_quote(&SqliteValue::Float(f64::NEG_INFINITY)).unwrap(),
3875            "null"
3876        );
3877        assert_eq!(json_quote(&SqliteValue::Float(f64::NAN)).unwrap(), "null");
3878    }
3879
3880    #[test]
3881    fn test_json_quote_blob() {
3882        let result = json_quote(&SqliteValue::Blob(Arc::from(vec![0xDE, 0xAD])));
3883        assert!(result.is_err());
3884        assert!(
3885            result
3886                .unwrap_err()
3887                .to_string()
3888                .contains("JSON cannot hold BLOB values")
3889        );
3890    }
3891
3892    #[test]
3893    fn test_json_quote_text_special_chars() {
3894        let result = json_quote(&SqliteValue::Text(Arc::from("a\"b\\c"))).unwrap();
3895        assert!(result.contains("\\\""));
3896        assert!(result.contains("\\\\"));
3897    }
3898
3899    // -----------------------------------------------------------------------
3900    // json_object edge cases
3901    // -----------------------------------------------------------------------
3902
3903    #[test]
3904    fn test_json_object_odd_args_error() {
3905        let err = json_object(&[
3906            SqliteValue::Text(Arc::from("a")),
3907            SqliteValue::Integer(1),
3908            SqliteValue::Text(Arc::from("b")),
3909        ]);
3910        assert!(err.is_err());
3911    }
3912
3913    #[test]
3914    fn test_json_object_non_text_key_error() {
3915        let err = json_object(&[SqliteValue::Integer(1), SqliteValue::Integer(2)]);
3916        assert!(err.is_err());
3917    }
3918
3919    #[test]
3920    fn test_json_object_empty() {
3921        assert_eq!(json_object(&[]).unwrap(), "{}");
3922    }
3923
3924    #[test]
3925    fn test_json_object_duplicate_keys() {
3926        let out = json_object(&[
3927            SqliteValue::Text(Arc::from("k")),
3928            SqliteValue::Integer(1),
3929            SqliteValue::Text(Arc::from("k")),
3930            SqliteValue::Integer(2),
3931        ])
3932        .unwrap();
3933        assert_eq!(out, r#"{"k":2}"#);
3934    }
3935
3936    // -----------------------------------------------------------------------
3937    // json_set/insert/replace array index
3938    // -----------------------------------------------------------------------
3939
3940    #[test]
3941    fn test_json_set_array_element() {
3942        let out = json_set("[1,2,3]", &[("$[1]", SqliteValue::Integer(99))]).unwrap();
3943        assert_eq!(out, "[1,99,3]");
3944    }
3945
3946    #[test]
3947    fn test_json_set_array_append_at_len() {
3948        let out = json_set("[1,2]", &[("$[2]", SqliteValue::Integer(3))]).unwrap();
3949        assert_eq!(out, "[1,2,3]");
3950    }
3951
3952    #[test]
3953    fn test_json_insert_array_append_at_len() {
3954        let out = json_insert("[1,2]", &[("$[2]", SqliteValue::Integer(3))]).unwrap();
3955        assert_eq!(out, "[1,2,3]");
3956    }
3957
3958    #[test]
3959    fn test_json_set_append_pseudo_index() {
3960        let out = json_set("[1,2]", &[("$[#]", SqliteValue::Integer(3))]).unwrap();
3961        assert_eq!(out, "[1,2,3]");
3962    }
3963
3964    #[test]
3965    fn test_json_replace_append_pseudo_index_noop() {
3966        let out = json_replace("[1,2]", &[("$[#]", SqliteValue::Integer(3))]).unwrap();
3967        assert_eq!(out, "[1,2]");
3968    }
3969
3970    #[test]
3971    fn test_json_replace_array_element() {
3972        let out = json_replace("[1,2,3]", &[("$[0]", SqliteValue::Integer(0))]).unwrap();
3973        assert_eq!(out, "[0,2,3]");
3974    }
3975
3976    #[test]
3977    fn test_json_set_multiple_paths() {
3978        let out = json_set(
3979            r#"{"a":1,"b":2}"#,
3980            &[
3981                ("$.a", SqliteValue::Integer(10)),
3982                ("$.c", SqliteValue::Integer(30)),
3983            ],
3984        )
3985        .unwrap();
3986        let parsed: serde_json::Value = serde_json::from_str(&out).unwrap();
3987        assert_eq!(parsed["a"], 10);
3988        assert_eq!(parsed["c"], 30);
3989    }
3990
3991    // -----------------------------------------------------------------------
3992    // json_remove edge cases
3993    // -----------------------------------------------------------------------
3994
3995    #[test]
3996    fn test_json_remove_missing_key_no_change() {
3997        let out = json_remove(r#"{"a":1}"#, &["$.b"]).unwrap();
3998        assert_eq!(out, r#"{"a":1}"#);
3999    }
4000
4001    #[test]
4002    fn test_json_remove_multiple_paths() {
4003        let out = json_remove(r#"{"a":1,"b":2,"c":3}"#, &["$.a", "$.c"]).unwrap();
4004        assert_eq!(out, r#"{"b":2}"#);
4005    }
4006
4007    #[test]
4008    fn test_json_remove_from_end_index() {
4009        let out = json_remove("[1,2,3]", &["$[#-1]"]).unwrap();
4010        assert_eq!(out, "[1,2]");
4011    }
4012
4013    // -----------------------------------------------------------------------
4014    // json_patch edge cases
4015    // -----------------------------------------------------------------------
4016
4017    #[test]
4018    fn test_json_patch_non_object_replaces() {
4019        let out = json_patch(r#"{"a":1}"#, "42").unwrap();
4020        assert_eq!(out, "42");
4021    }
4022
4023    #[test]
4024    fn test_json_patch_nested_merge() {
4025        let out = json_patch(r#"{"a":{"b":1,"c":2}}"#, r#"{"a":{"b":10,"d":4}}"#).unwrap();
4026        let parsed: serde_json::Value = serde_json::from_str(&out).unwrap();
4027        assert_eq!(parsed["a"]["b"], 10);
4028        assert_eq!(parsed["a"]["c"], 2);
4029        assert_eq!(parsed["a"]["d"], 4);
4030    }
4031
4032    // -----------------------------------------------------------------------
4033    // json_each edge cases
4034    // -----------------------------------------------------------------------
4035
4036    #[test]
4037    fn test_json_each_scalar() {
4038        let rows = json_each("42", None).unwrap();
4039        assert_eq!(rows.len(), 1);
4040        assert_eq!(rows[0].key, SqliteValue::Null);
4041        assert_eq!(rows[0].value, SqliteValue::Integer(42));
4042        assert_eq!(rows[0].type_name, "integer");
4043    }
4044
4045    #[test]
4046    fn test_json_each_empty_array() {
4047        let rows = json_each("[]", None).unwrap();
4048        assert!(rows.is_empty());
4049    }
4050
4051    #[test]
4052    fn test_json_each_empty_object() {
4053        let rows = json_each("{}", None).unwrap();
4054        assert!(rows.is_empty());
4055    }
4056
4057    #[test]
4058    fn test_json_each_missing_path() {
4059        let rows = json_each(r#"{"a":1}"#, Some("$.b")).unwrap();
4060        assert!(rows.is_empty());
4061    }
4062
4063    // -----------------------------------------------------------------------
4064    // json_tree edge cases
4065    // -----------------------------------------------------------------------
4066
4067    #[test]
4068    fn test_json_tree_scalar() {
4069        let rows = json_tree("42", None).unwrap();
4070        assert_eq!(rows.len(), 1);
4071        assert_eq!(rows[0].type_name, "integer");
4072    }
4073
4074    #[test]
4075    fn test_json_tree_empty_array() {
4076        let rows = json_tree("[]", None).unwrap();
4077        assert_eq!(rows.len(), 1);
4078        assert_eq!(rows[0].type_name, "array");
4079    }
4080
4081    #[test]
4082    fn test_json_tree_parent_ids() {
4083        let rows = json_tree(r#"{"a":1}"#, None).unwrap();
4084        assert_eq!(rows.len(), 2);
4085        assert_eq!(rows[0].parent, SqliteValue::Null); // root
4086        assert_eq!(rows[1].parent, SqliteValue::Integer(rows[0].id)); // child
4087    }
4088
4089    #[test]
4090    fn test_json_tree_missing_path() {
4091        let rows = json_tree(r#"{"a":1}"#, Some("$.b")).unwrap();
4092        assert!(rows.is_empty());
4093    }
4094
4095    // -----------------------------------------------------------------------
4096    // JSONB edge cases
4097    // -----------------------------------------------------------------------
4098
4099    #[test]
4100    fn test_jsonb_null() {
4101        let blob = jsonb("null").unwrap();
4102        assert_eq!(json_from_jsonb(&blob).unwrap(), "null");
4103    }
4104
4105    #[test]
4106    fn test_jsonb_booleans() {
4107        assert_eq!(json_from_jsonb(&jsonb("true").unwrap()).unwrap(), "true");
4108        assert_eq!(json_from_jsonb(&jsonb("false").unwrap()).unwrap(), "false");
4109    }
4110
4111    #[test]
4112    fn test_jsonb_integer() {
4113        let blob = jsonb("42").unwrap();
4114        assert_eq!(json_from_jsonb(&blob).unwrap(), "42");
4115    }
4116
4117    #[test]
4118    fn test_jsonb_float() {
4119        let blob = jsonb("3.14").unwrap();
4120        let text = json_from_jsonb(&blob).unwrap();
4121        assert!(text.starts_with("3.14"));
4122    }
4123
4124    #[test]
4125    fn test_jsonb_nested_array() {
4126        let blob = jsonb("[[1],[2,3]]").unwrap();
4127        assert_eq!(json_from_jsonb(&blob).unwrap(), "[[1],[2,3]]");
4128    }
4129
4130    #[test]
4131    fn test_jsonb_empty_string() {
4132        let blob = jsonb(r#""""#).unwrap();
4133        assert_eq!(json_from_jsonb(&blob).unwrap(), r#""""#);
4134    }
4135
4136    #[test]
4137    fn test_jsonb_extract_multiple_paths() {
4138        let blob = jsonb_extract(r#"{"a":1,"b":2}"#, &["$.a", "$.b"]).unwrap();
4139        assert_eq!(json_from_jsonb(&blob).unwrap(), "[1,2]");
4140    }
4141
4142    #[test]
4143    fn test_jsonb_extract_no_paths_error() {
4144        let empty: &[&str] = &[];
4145        assert!(jsonb_extract(r#"{"a":1}"#, empty).is_err());
4146    }
4147
4148    #[test]
4149    fn test_jsonb_decode_trailing_bytes() {
4150        let mut blob = jsonb("42").unwrap();
4151        blob.push(0xFF); // trailing garbage
4152        assert!(json_from_jsonb(&blob).is_err());
4153        assert_eq!(
4154            json_valid_blob(&blob, Some(JSON_VALID_JSONB_SUPERFICIAL_FLAG)),
4155            0
4156        );
4157    }
4158
4159    #[test]
4160    fn test_jsonb_decode_empty() {
4161        assert!(json_from_jsonb(&[]).is_err());
4162    }
4163
4164    // -----------------------------------------------------------------------
4165    // Path parsing edge cases
4166    // -----------------------------------------------------------------------
4167
4168    #[test]
4169    fn test_path_invalid_no_dollar() {
4170        assert!(json_extract(r#"{"a":1}"#, &["a"]).is_err());
4171    }
4172
4173    #[test]
4174    fn test_path_empty_key_error() {
4175        assert!(json_extract(r#"{"a":1}"#, &["$."]).is_err());
4176    }
4177
4178    #[test]
4179    fn test_path_unclosed_bracket() {
4180        assert!(json_extract(r"[1,2]", &["$[0"]).is_err());
4181    }
4182
4183    #[test]
4184    fn test_path_from_end_zero_error() {
4185        assert!(json_extract("[1,2,3]", &["$[#-0]"]).is_err());
4186    }
4187
4188    #[test]
4189    fn test_path_from_end_beyond_length() {
4190        let result = json_extract("[1,2,3]", &["$[#-10]"]).unwrap();
4191        assert_eq!(result, SqliteValue::Null);
4192    }
4193
4194    // -----------------------------------------------------------------------
4195    // json_group_object edge cases
4196    // -----------------------------------------------------------------------
4197
4198    #[test]
4199    fn test_json_group_object_non_text_key_error() {
4200        let err = json_group_object(&[(SqliteValue::Integer(1), SqliteValue::Integer(2))]);
4201        assert!(err.is_err());
4202    }
4203
4204    #[test]
4205    fn test_json_group_object_empty() {
4206        assert_eq!(json_group_object(&[]).unwrap(), "{}");
4207    }
4208
4209    // -----------------------------------------------------------------------
4210    // json_array edge cases
4211    // -----------------------------------------------------------------------
4212
4213    #[test]
4214    fn test_json_array_empty() {
4215        assert_eq!(json_array(&[]).unwrap(), "[]");
4216    }
4217
4218    #[test]
4219    fn test_json_array_with_blob() {
4220        let err = json_array(&[SqliteValue::Blob(Arc::from(vec![0xCA, 0xFE]))]).unwrap_err();
4221        assert!(err.to_string().contains("JSON cannot hold BLOB values"));
4222    }
4223}