• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

joaoh82 / rust_sqlite / 25093849524

29 Apr 2026 06:15AM UTC coverage: 69.044% (-1.9%) from 70.927%
25093849524

push

github

web-flow
Phase 7e: JSON column type + path queries (#54)

Adds the JSON storage class and four path-aware query functions, closing
the second of Phase 7's two storage primitives (the first was VECTOR(N)
in 7a). Shape mirrors SQLite's JSON1 extension — JSON values store as
canonical UTF-8 text, validated via `serde_json::from_str` at INSERT and
UPDATE time. Phase 7 plan Q3 originally proposed bincoded `serde_json::
Value`, but bincode was removed from the engine in Phase 3c (cell-based
encoding replaced it); rather than re-add bincode for one column type,
JSON-as-text matches SQLite's choice and reuses the existing Text storage
path. Q3 in `docs/phase-7-plan.md` records the scope correction inline.

Engine surface:

- `DataType::Json` variant alongside `Vector(N)`. `JSONB` parses as an
  alias (Postgres convention; both store as text in our case).
- INSERT/UPDATE on a JSON column runs `serde_json::from_str::<Value>`;
  malformed JSON is rejected with `Type mismatch: expected JSON for
  column 'foo': <serde error>`. NULLs pass through untouched.
- UNIQUE on a JSON column treats the value as raw text (string equality
  on the canonical form).
- `table_to_create_sql` round-trips JSON columns; `build_empty_table`,
  `Row::Text(BTreeMap::new())` storage, and the `clone_datatype` helpers
  in `executor.rs` and `pager/mod.rs` all gained the new arm.

Functions (executor.rs, ~370 LOC):

- `json_extract(json[, path])` — walks the path, returns the resolved
  node coerced to the closest SQL type. Strings → TEXT, numbers →
  INTEGER/REAL, booleans → BOOLEAN, `null` → NULL, composites
  (object/array) → canonical JSON text.
- `json_type(json[, path])` — returns one of `'object'`, `'array'`,
  `'string'`, `'integer'`, `'real'`, `'true'`, `'false'`, `'null'`.
- `json_array_length(json[, path])` — element count; errors if the
  resolved node isn't an array.
- `json_object_keys(json[, path])` — keys as a JSON-array text in
  insertion order (e.g. `'["a","b","c"]'`). Diverges f... (continued)

154 of 210 new or added lines in 5 files covered. (73.33%)

227 existing lines in 3 files now uncovered.

5382 of 7795 relevant lines covered (69.04%)

1.42 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

18.88
/src/error.rs
1
use thiserror::Error;
2

3
use std::result;
4

5
use sqlparser::parser::ParserError;
6

7
/// This is a type that encapsulated the `std::result` with the enum `SQLRiteError`
8
/// and makes function signatures easier to read.
9
pub type Result<T> = result::Result<T, SQLRiteError>;
10

11
/// SQLRiteError is an enum with all the standardized errors available for returning
12
///
13
#[derive(Error, Debug)]
14
pub enum SQLRiteError {
15
    #[error("Not Implemented error: {0}")]
16
    NotImplemented(String),
17
    #[error("General error: {0}")]
18
    General(String),
19
    #[error("Internal error: {0}")]
20
    Internal(String),
21
    #[error("Unknown command error: {0}")]
22
    UnknownCommand(String),
23
    #[error("SQL error: {0:?}")]
24
    SqlError(#[from] ParserError),
25
    #[error("IO error: {0}")]
26
    Io(#[from] std::io::Error),
27
}
28

29
// `std::io::Error` has no `PartialEq`, so we implement one by value-of-message.
×
30
// Used by existing tests that compare error variants.
×
31
impl PartialEq for SQLRiteError {
×
32
    fn eq(&self, other: &Self) -> bool {
1✔
33
        use SQLRiteError::*;
34
        match (self, other) {
1✔
35
            (NotImplemented(a), NotImplemented(b)) => a == b,
×
36
            (General(a), General(b)) => a == b,
1✔
37
            (Internal(a), Internal(b)) => a == b,
×
38
            (UnknownCommand(a), UnknownCommand(b)) => a == b,
×
39
            (SqlError(a), SqlError(b)) => format!("{a:?}") == format!("{b:?}"),
×
40
            (Io(a), Io(b)) => a.kind() == b.kind() && a.to_string() == b.to_string(),
×
41
            _ => false,
×
42
        }
43
    }
44
}
45

46
/// Returns SQLRiteError::General error from String
47
#[allow(dead_code)]
48
pub fn sqlrite_error(message: &str) -> SQLRiteError {
1✔
49
    SQLRiteError::General(message.to_owned())
1✔
50
}
51

52
#[cfg(test)]
×
53
mod tests {
54
    use super::*;
55

56
    #[test]
×
57
    fn sqlrite_error_test() {
3✔
58
        let input = String::from("test error");
1✔
59
        let expected = SQLRiteError::General("test error".to_string());
2✔
60

61
        let result = sqlrite_error(&input);
2✔
62
        assert_eq!(result, expected);
2✔
63
    }
64

65
    #[test]
×
66
    fn sqlrite_display_not_implemented_test() {
3✔
67
        let error_string = String::from("Feature not implemented.");
1✔
68
        let input = SQLRiteError::NotImplemented(error_string.clone());
2✔
69

70
        let expected = format!("Not Implemented error: {}", error_string);
2✔
71
        let result = format!("{}", input);
2✔
72
        assert_eq!(result, expected);
2✔
73
    }
74

75
    #[test]
×
76
    fn sqlrite_display_general_test() {
3✔
77
        let error_string = String::from("General error.");
1✔
78
        let input = SQLRiteError::General(error_string.clone());
2✔
79

80
        let expected = format!("General error: {}", error_string);
2✔
81
        let result = format!("{}", input);
2✔
82
        assert_eq!(result, expected);
2✔
83
    }
84

85
    #[test]
×
86
    fn sqlrite_display_internal_test() {
3✔
87
        let error_string = String::from("Internet error.");
1✔
88
        let input = SQLRiteError::Internal(error_string.clone());
2✔
89

90
        let expected = format!("Internal error: {}", error_string);
2✔
91
        let result = format!("{}", input);
2✔
92
        assert_eq!(result, expected);
2✔
93
    }
94

95
    #[test]
×
96
    fn sqlrite_display_sqlrite_test() {
3✔
97
        let error_string = String::from("SQL error.");
1✔
98
        let input = SQLRiteError::SqlError(ParserError::ParserError(error_string.clone()));
2✔
99

100
        let expected = format!("SQL error: ParserError(\"{}\")", error_string);
2✔
101
        let result = format!("{}", input);
2✔
102
        assert_eq!(result, expected);
2✔
103
    }
104

105
    #[test]
×
106
    fn sqlrite_unknown_test() {
3✔
107
        let error_string = String::from("Unknown error.");
1✔
108
        let input = SQLRiteError::UnknownCommand(error_string.clone());
2✔
109

110
        let expected = format!("Unknown command error: {}", error_string);
2✔
111
        let result = format!("{}", input);
2✔
112
        assert_eq!(result, expected);
2✔
113
    }
114
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc