Skip to main content

diesel/pg/types/
json.rs

1//! Support for JSON and `jsonb` values under PostgreSQL.
2
3extern crate serde_json;
4
5use std::io::prelude::*;
6
7use crate::deserialize::{self, FromSql};
8use crate::pg::{Pg, PgValue};
9use crate::serialize::{self, IsNull, Output, ToSql};
10use crate::sql_types;
11
12#[cfg(all(feature = "postgres_backend", feature = "serde_json"))]
13impl FromSql<sql_types::Json, Pg> for serde_json::Value {
14    fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
15        serde_json::from_slice(value.as_bytes()).map_err(|_| "Invalid Json".into())
16    }
17}
18
19#[cfg(all(feature = "postgres_backend", feature = "serde_json"))]
20impl ToSql<sql_types::Json, Pg> for serde_json::Value {
21    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
22        serde_json::to_writer(out, self)
23            .map(|_| IsNull::No)
24            .map_err(Into::into)
25    }
26}
27
28#[cfg(all(feature = "postgres_backend", feature = "serde_json"))]
29impl FromSql<sql_types::Jsonb, Pg> for serde_json::Value {
30    fn from_sql(value: PgValue<'_>) -> deserialize::Result<Self> {
31        let bytes = value.as_bytes();
32        let first_byte = bytes
33            .first()
34            .ok_or("Received an empty response from the server")?;
35
36        if *first_byte != 1 {
37            return Err("Unsupported JSONB encoding version".into());
38        }
39        // That's an empty slice if there is only
40        // one response byte
41        serde_json::from_slice(&bytes[1..]).map_err(|_| "Invalid Json".into())
42    }
43}
44
45#[cfg(all(feature = "postgres_backend", feature = "serde_json"))]
46impl ToSql<sql_types::Jsonb, Pg> for serde_json::Value {
47    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
48        out.write_all(&[1])?;
49        serde_json::to_writer(out, self)
50            .map(|_| IsNull::No)
51            .map_err(Into::into)
52    }
53}
54
55#[cfg(test)]
56mod tests {
57    use crate::deserialize::FromSql;
58    use crate::pg::{Pg, PgValue};
59    use crate::query_builder::bind_collector::ByteWrapper;
60    use crate::serialize::{Output, ToSql};
61    use crate::sql_types;
62    use crate::test_helpers::format_error;
63
64    #[diesel_test_helper::test]
65    fn regression_json_float_survives_a_round_trip() {
66        crate::test_helpers::assert_floats_survive_a_json_round_trip::<sql_types::Json, Pg>(
67            |bytes| FromSql::<sql_types::Json, Pg>::from_sql(PgValue::for_test(bytes)),
68        );
69        crate::test_helpers::assert_floats_survive_a_json_round_trip::<sql_types::Jsonb, Pg>(
70            |bytes| FromSql::<sql_types::Jsonb, Pg>::from_sql(PgValue::for_test(bytes)),
71        );
72    }
73
74    #[diesel_test_helper::test]
75    fn json_to_sql() {
76        let mut buffer = Vec::new();
77        let mut bytes = Output::test(ByteWrapper(&mut buffer));
78        let test_json = serde_json::Value::Bool(true);
79        ToSql::<sql_types::Json, Pg>::to_sql(&test_json, &mut bytes).unwrap();
80        assert_eq!(buffer, b"true");
81    }
82
83    #[diesel_test_helper::test]
84    fn some_json_from_sql() {
85        let input_json = b"true";
86        let output_json: serde_json::Value =
87            FromSql::<sql_types::Json, Pg>::from_sql(PgValue::for_test(input_json)).unwrap();
88        assert_eq!(output_json, serde_json::Value::Bool(true));
89    }
90
91    #[diesel_test_helper::test]
92    fn bad_json_from_sql() {
93        let uuid: Result<serde_json::Value, _> =
94            FromSql::<sql_types::Json, Pg>::from_sql(PgValue::for_test(b"boom"));
95        assert_eq!(format_error(&*uuid.unwrap_err()), "Invalid Json");
96    }
97
98    #[diesel_test_helper::test]
99    fn no_json_from_sql() {
100        let uuid: Result<serde_json::Value, _> =
101            FromSql::<sql_types::Json, Pg>::from_nullable_sql(None);
102        assert_eq!(
103            format_error(&*uuid.unwrap_err()),
104            "Unexpected null for non-null column"
105        );
106    }
107
108    #[diesel_test_helper::test]
109    fn jsonb_to_sql() {
110        let mut buffer = Vec::new();
111        let mut bytes = Output::test(ByteWrapper(&mut buffer));
112        let test_json = serde_json::Value::Bool(true);
113        ToSql::<sql_types::Jsonb, Pg>::to_sql(&test_json, &mut bytes).unwrap();
114        assert_eq!(buffer, b"\x01true");
115    }
116
117    #[diesel_test_helper::test]
118    fn some_jsonb_from_sql() {
119        let input_json = b"\x01true";
120        let output_json: serde_json::Value =
121            FromSql::<sql_types::Jsonb, Pg>::from_sql(PgValue::for_test(input_json)).unwrap();
122        assert_eq!(output_json, serde_json::Value::Bool(true));
123    }
124
125    #[diesel_test_helper::test]
126    fn bad_jsonb_from_sql() {
127        let uuid: Result<serde_json::Value, _> =
128            FromSql::<sql_types::Jsonb, Pg>::from_sql(PgValue::for_test(b"\x01boom"));
129        assert_eq!(format_error(&*uuid.unwrap_err()), "Invalid Json");
130    }
131
132    #[diesel_test_helper::test]
133    fn bad_jsonb_version_from_sql() {
134        let uuid: Result<serde_json::Value, _> =
135            FromSql::<sql_types::Jsonb, Pg>::from_sql(PgValue::for_test(b"\x02true"));
136        assert_eq!(
137            format_error(&*uuid.unwrap_err()),
138            "Unsupported JSONB encoding version"
139        );
140    }
141
142    #[diesel_test_helper::test]
143    fn no_jsonb_from_sql() {
144        let uuid: Result<serde_json::Value, _> =
145            FromSql::<sql_types::Jsonb, Pg>::from_nullable_sql(None);
146        assert_eq!(
147            format_error(&*uuid.unwrap_err()),
148            "Unexpected null for non-null column"
149        );
150    }
151}