Skip to main content

rupg_types/
jsonb.rs

1//! `jsonb` in the text and the binary formats.
2//!
3//! PostgreSQL keeps a `jsonb` value as a tree, with the keys of each object in one order and no key twice, and prints the tree in one form. rupg keeps the text of that form. Then the output is a copy of the value, and the functions of `json` can read it. This is a port of `jsonb_in` in `src/backend/utils/adt/jsonb.c`, of the order of keys of `lengthCompareJsonbStringValue` in `jsonb_util.c` and of the output of `JsonbToCString`.
4//!
5//! The tree is in one vector and refers to its nodes by index, so a value nested to any depth takes no recursion to build, to print or to drop.
6//!
7//! Lifted from `crates/rudb-pgtypes/src/jsonb.rs` of tamnd/rudb at f5f7065a (spec/04 section 4.9).
8
9use crate::binary::Recv;
10use crate::error::TypeError;
11use crate::json::{self, Kind, Open, Sink, Stop};
12use crate::numeric::{numeric_in, numeric_out};
13
14/// The version byte at the start of the binary format, the only one that PostgreSQL knows.
15const VERSION: u8 = 1;
16
17enum Node {
18    /// A scalar, as its output text.
19    Scalar(String),
20    Array(Vec<usize>),
21    /// The fields, each a decoded key and the node of its value.
22    Object(Vec<(String, usize)>),
23}
24
25#[derive(Default)]
26struct Tree {
27    nodes: Vec<Node>,
28    /// The open arrays and objects, innermost last, each with the key of its next field.
29    open: Vec<(usize, Option<String>)>,
30    root: Option<usize>,
31}
32
33impl Tree {
34    /// Puts a finished node into the array or the object around it.
35    fn attach(&mut self, node: usize) {
36        let Some((parent, key)) = self.open.last_mut() else {
37            self.root = Some(node);
38            return;
39        };
40        match &mut self.nodes[*parent] {
41            Node::Array(items) => items.push(node),
42            Node::Object(fields) => fields.push((key.take().unwrap_or_default(), node)),
43            Node::Scalar(_) => {}
44        }
45    }
46
47    /// The text of the tree, with one space after each colon and each comma.
48    fn print(&self, out: &mut String) {
49        let Some(root) = self.root else { return };
50        // Each entry is a node and the place of its next child.
51        let mut stack = vec![(root, 0usize)];
52        while let Some((node, at)) = stack.pop() {
53            let child = match &self.nodes[node] {
54                Node::Scalar(text) => {
55                    out.push_str(text);
56                    continue;
57                }
58                Node::Array(items) => {
59                    if at == 0 {
60                        out.push('[');
61                    }
62                    let Some(&child) = items.get(at) else {
63                        out.push(']');
64                        continue;
65                    };
66                    if at > 0 {
67                        out.push_str(", ");
68                    }
69                    child
70                }
71                Node::Object(fields) => {
72                    if at == 0 {
73                        out.push('{');
74                    }
75                    let Some((key, child)) = fields.get(at) else {
76                        out.push('}');
77                        continue;
78                    };
79                    if at > 0 {
80                        out.push_str(", ");
81                    }
82                    escape(key, out);
83                    out.push_str(": ");
84                    *child
85                }
86            };
87            stack.push((node, at + 1));
88            stack.push((child, 0));
89        }
90    }
91}
92
93impl Sink for Tree {
94    fn open(&mut self, open: Open) -> Result<(), TypeError> {
95        let node = self.nodes.len();
96        self.nodes.push(match open {
97            Open::Array => Node::Array(Vec::new()),
98            Open::Object => Node::Object(Vec::new()),
99        });
100        self.open.push((node, None));
101        Ok(())
102    }
103
104    fn close(&mut self, _: Open) -> Result<(), TypeError> {
105        let Some((node, _)) = self.open.pop() else { return Ok(()) };
106        if let Node::Object(fields) = &mut self.nodes[node] {
107            unique(fields);
108        }
109        self.attach(node);
110        Ok(())
111    }
112
113    fn key(&mut self, key: &str) -> Result<(), TypeError> {
114        if let Some((_, slot)) = self.open.last_mut() {
115            *slot = Some(key.to_owned());
116        }
117        Ok(())
118    }
119
120    fn scalar(&mut self, kind: Kind, token: &str, text: &str) -> Result<(), TypeError> {
121        let mut out = String::new();
122        match kind {
123            Kind::String => escape(text, &mut out),
124            Kind::Number => {
125                let mut bytes = Vec::new();
126                numeric_out(&numeric_in(token, -1)?, &mut bytes);
127                out = String::from_utf8(bytes).unwrap_or_default();
128            }
129            _ => out.push_str(token),
130        }
131        let node = self.nodes.len();
132        self.nodes.push(Node::Scalar(out));
133        self.attach(node);
134        Ok(())
135    }
136}
137
138/// `uniqueifyJsonbObject`: the fields in the order of their keys, shorter keys first and keys of one length in the order of their bytes. Of the fields with one key, the last one stays.
139fn unique(fields: &mut Vec<(String, usize)>) {
140    let order = |a: &str, b: &str| a.len().cmp(&b.len()).then_with(|| a.cmp(b));
141    // A stable sort keeps the fields of one key in the order of the input.
142    fields.sort_by(|a, b| order(&a.0, &b.0));
143    let mut kept: Vec<(String, usize)> = Vec::with_capacity(fields.len());
144    for field in fields.drain(..) {
145        match kept.last_mut() {
146            Some(last) if last.0 == field.0 => *last = field,
147            _ => kept.push(field),
148        }
149    }
150    *fields = kept;
151}
152
153/// `escape_json`: a string in quotes, with the escapes that PostgreSQL writes.
154pub fn escape(text: &str, out: &mut String) {
155    out.push('"');
156    for c in text.chars() {
157        match c {
158            '\u{8}' => out.push_str("\\b"),
159            '\u{c}' => out.push_str("\\f"),
160            '\n' => out.push_str("\\n"),
161            '\r' => out.push_str("\\r"),
162            '\t' => out.push_str("\\t"),
163            '"' => out.push_str("\\\""),
164            '\\' => out.push_str("\\\\"),
165            c if u32::from(c) < 0x20 => out.push_str(&format!("\\u{:04x}", u32::from(c))),
166            c => out.push(c),
167        }
168    }
169    out.push('"');
170}
171
172/// The text input of `jsonb`: the text of the value in the one form that PostgreSQL prints.
173pub fn jsonb_in(s: &str) -> Result<String, TypeError> {
174    let mut tree = Tree::default();
175    match json::parse(s, true, &mut tree) {
176        Ok(()) => {}
177        Err(Stop::Syntax(fail)) => return Err(json::syntax(s, fail)),
178        Err(Stop::Action(error)) => return Err(error),
179    }
180    let mut out = String::with_capacity(s.len());
181    tree.print(&mut out);
182    Ok(out)
183}
184
185/// The binary input of `jsonb`: the version byte and then the text. `pq_getmsgtext` checks the encoding of the text before the parser reads it, so a zero byte is an error of the encoding and not of the syntax.
186pub fn jsonb_recv(bytes: &[u8]) -> Result<String, TypeError> {
187    let mut recv = Recv::new(bytes);
188    match recv.bytes(1)?[0] {
189        VERSION => jsonb_in(recv.text()?),
190        version => Err(TypeError::new(
191            rupg_common::SqlState::INTERNAL_ERROR,
192            format!("unsupported jsonb version number {version}"),
193        )),
194    }
195}
196
197/// The binary output of `jsonb`: the version byte and then the text.
198pub fn jsonb_send(text: &str, out: &mut Vec<u8>) {
199    out.push(VERSION);
200    out.extend_from_slice(text.as_bytes());
201}
202
203#[cfg(test)]
204mod tests {
205    use super::*;
206
207    fn out(s: &str) -> String {
208        jsonb_in(s).unwrap()
209    }
210
211    fn fails(s: &str) -> (String, String, String) {
212        let error = jsonb_in(s).unwrap_err();
213        (error.sqlstate.as_str().to_owned(), error.message, error.detail.unwrap_or_default())
214    }
215
216    #[test]
217    fn a_value_prints_in_the_form_of_postgres() {
218        assert_eq!(out(r#"{"b":1,"a":[1,2]}"#), r#"{"a": [1, 2], "b": 1}"#);
219        assert_eq!(out(r#"{"aa":1,"b":2,"a":3}"#), r#"{"a": 3, "b": 2, "aa": 1}"#);
220        assert_eq!(out(r#"{"a":1,"a":2,"b":{"c":1,"c":[]}}"#), r#"{"a": 2, "b": {"c": []}}"#);
221        assert_eq!(out(" [ ] "), "[]");
222        assert_eq!(out("{}"), "{}");
223        assert_eq!(out("[1.50, 1e2, -0, 1E-3, -0.0, 2.5e+1]"), "[1.50, 100, 0, 0.001, 0.0, 25]");
224        assert_eq!(out(" true "), "true");
225        assert_eq!(out("null"), "null");
226        assert_eq!(out(r#""aé\/\"\n\u0001😀""#), "\"a\u{e9}/\\\"\\n\\u0001\u{1f600}\"");
227        assert_eq!(out(r#"[{"é":1,"z":2}]"#), r#"[{"z": 2, "é": 1}]"#);
228    }
229
230    #[test]
231    fn the_errors_are_the_errors_of_postgres() {
232        let syntax = |detail: &str| {
233            ("22P02".to_owned(), "invalid input syntax for type json".to_owned(), detail.to_owned())
234        };
235        assert_eq!(fails("[1 2]"), syntax(r#"Expected "," or "]", but found "2"."#));
236        assert_eq!(
237            fails(r#"["\ud800"]"#),
238            syntax("Unicode low surrogate must follow a high surrogate.")
239        );
240        assert_eq!(
241            fails(r#""\ude00""#),
242            syntax("Unicode low surrogate must follow a high surrogate.")
243        );
244        assert_eq!(
245            fails(r#""\ud800\ud800""#),
246            syntax("Unicode high surrogate must not follow a high surrogate.")
247        );
248        assert_eq!(
249            fails(r#""\ud800x""#),
250            syntax("Unicode low surrogate must follow a high surrogate.")
251        );
252        assert_eq!(
253            fails(r#"["\u0000"]"#),
254            (
255                "22P05".to_owned(),
256                "unsupported Unicode escape sequence".to_owned(),
257                "\\u0000 cannot be converted to text.".to_owned()
258            )
259        );
260        // The lexer decodes a string before the parser looks at where it is.
261        assert_eq!(fails(r#"{"a": 1 "\u0000"}"#).0, "22P05");
262        assert!(json::json_in(r#"["\u0000", "\ud800"]"#).is_ok());
263        // `parse_scalar` reads the next token before the action reads the number, so a bad token after a number that is too large is the error.
264        assert_eq!(fails("[1e999999\x1d]"), syntax("Token \"\x1d\" is invalid."));
265        assert_eq!(fails(r#"{"a":1e999999 x}"#), syntax(r#"Token "x" is invalid."#));
266        assert_eq!(fails(r#"1e999999 "b"#), syntax(r#"Token ""b" is invalid."#));
267        let overflow =
268            ("22003".to_owned(), "value overflows numeric format".to_owned(), String::new());
269        assert_eq!(fails("[1e999999,\x1d]"), overflow);
270        assert_eq!(fails(r#"{"a":1e999999 "b"}"#), overflow);
271    }
272
273    #[test]
274    fn deep_nesting_needs_no_recursion() {
275        let deep = format!("{}{}", "[".repeat(100_000), "]".repeat(100_000));
276        assert_eq!(out(&deep), deep);
277    }
278
279    #[test]
280    fn the_binary_format_is_a_version_byte_and_the_text() {
281        let mut bytes = Vec::new();
282        jsonb_send(r#"{"a": 1}"#, &mut bytes);
283        assert_eq!(bytes, b"\x01{\"a\": 1}");
284        assert_eq!(jsonb_recv(b"\x01{\"b\":1,\"a\":2}").unwrap(), r#"{"a": 2, "b": 1}"#);
285        assert_eq!(
286            jsonb_recv(b"\x02{}").unwrap_err().message,
287            "unsupported jsonb version number 2"
288        );
289        let empty = jsonb_recv(b"").unwrap_err();
290        assert_eq!(empty.sqlstate, rupg_common::SqlState::PROTOCOL_VIOLATION);
291    }
292
293    /// The oracle checks the encoding of the whole text first, so a zero byte after a number that is too large is an error of the encoding (the fuzz target `type_recv`).
294    #[test]
295    fn the_binary_format_checks_the_encoding_first() {
296        let message = |bytes: &[u8]| {
297            let error = jsonb_recv(bytes).unwrap_err();
298            assert_eq!(error.sqlstate, rupg_common::SqlState::CHARACTER_NOT_IN_REPERTOIRE);
299            error.message
300        };
301        let mut big = vec![1];
302        big.extend_from_slice(&[b'9'; 59]);
303        big.extend_from_slice(b"\x15\0");
304        assert_eq!(message(&big), "invalid byte sequence for encoding \"UTF8\": 0x00");
305        assert_eq!(message(b"\x01[1,\0"), "invalid byte sequence for encoding \"UTF8\": 0x00");
306        assert_eq!(message(b"\x01\xfe"), "invalid byte sequence for encoding \"UTF8\": 0xfe");
307    }
308}