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