1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
use crate::expression::Expression;
use crate::expression::grouped::Grouped;
use crate::expression::operators::RetrieveAsTextJson;
use crate::sql_types::SqlType;
/// PostgreSQL specific methods present on JSON and JSONB expressions.
#[cfg(any(feature = "postgres_backend", feature = "__sqlite-shared"))]
pub trait AnyJsonExpressionMethods: Expression + Sized {
/// Creates a `->>` expression JSON.
///
/// This operator extracts the value associated with the given key, that is provided on the
/// Right Hand Side of the operator.
///
/// Extracts n'th element of JSON array (array elements are indexed from zero, but negative integers count from the end).
/// Extracts JSON object field as Text with the given key.
/// # Example
///
/// ```rust
/// # include!("../doctest_setup.rs");
/// #
/// # table! {
/// # contacts {
/// # id -> Integer,
/// # name -> VarChar,
/// # address -> Jsonb,
/// # }
/// # }
/// #
/// # // Reading the query result requires reading memory allocated by the
/// # // native sqlite library, which miri cannot track provenance for.
/// # #[cfg(miri)]
/// # fn main() {}
/// # #[cfg(not(miri))]
/// # fn main() {
/// # run_test().unwrap();
/// # }
///
/// # #[cfg(feature = "serde_json")]
/// # fn run_test() -> QueryResult<()> {
/// # use self::contacts::dsl::*;
/// # let conn = &mut establish_connection();
/// # diesel::sql_query("DROP TABLE IF EXISTS contacts").execute(conn).unwrap();
/// # diesel::sql_query("CREATE TABLE contacts (
/// # id SERIAL PRIMARY KEY,
/// # name VARCHAR NOT NULL,
/// # address JSONB NOT NULL
/// # )").execute(conn)
/// # .unwrap();
/// #
/// let santas_address: serde_json::Value = serde_json::json!({
/// "street": "Article Circle Expressway 1",
/// "city": "North Pole",
/// "postcode": "99705",
/// "state": "Alaska"
/// });
/// diesel::insert_into(contacts)
/// .values((name.eq("Claus"), address.eq(&santas_address)))
/// .execute(conn)?;
///
/// let santas_postcode = contacts.select(address.retrieve_as_text("postcode")).get_result::<String>(conn)?;
/// assert_eq!(santas_postcode, "99705");
///
///
/// let robert_downey_jr_addresses: serde_json::Value = serde_json::json!([
/// {
/// "street": "Somewhere In La 251",
/// "city": "Los Angeles",
/// "postcode": "12231223",
/// "state": "California"
/// },
/// {
/// "street": "Somewhere In Ny 251",
/// "city": "New York",
/// "postcode": "3213212",
/// "state": "New York"
/// }
/// ]);
///
/// diesel::insert_into(contacts)
/// .values((name.eq("Robert Downey Jr."), address.eq(&robert_downey_jr_addresses)))
/// .execute(conn)?;
///
/// let roberts_second_address_in_db = contacts
/// .filter(name.eq("Robert Downey Jr."))
/// .select(address.retrieve_as_text(1))
/// .get_result::<String>(conn)?;
///
/// let roberts_second_address = serde_json::json!{{
/// "city": "New York",
/// "state": "New York",
/// "street": "Somewhere In Ny 251",
/// "postcode": "3213212"
/// }};
/// assert_eq!(roberts_second_address, serde_json::from_str::<serde_json::Value>(&roberts_second_address_in_db).unwrap());
/// # Ok(())
/// # }
/// # #[cfg(not(feature = "serde_json"))]
/// # fn run_test() -> QueryResult<()> {
/// # Ok(())
/// # }
/// ```
fn retrieve_as_text<T>(
self,
other: T,
) -> crate::expression::helper_types::RetrieveAsText<Self, T>
where
T: JsonIndex,
<T::Expression as Expression>::SqlType: SqlType,
{
Grouped(RetrieveAsTextJson::new(
self,
other.into_json_index_expression(),
))
}
}
/// A marker trait indicating which types can be used as index into a json field
pub trait JsonIndex: self::private::Sealed {
#[doc(hidden)]
type Expression: Expression;
#[doc(hidden)]
fn into_json_index_expression(self) -> Self::Expression;
}
impl<T> AnyJsonExpressionMethods for T
where
T: Expression,
T::SqlType: private::JsonOrNullableJsonOrJsonbOrNullableJsonb,
{
}
pub(crate) mod private {
use super::JsonIndex;
use crate::Expression;
use crate::expression::IntoSql;
use crate::sql_types::{Integer, Json, Jsonb, Nullable, Text};
use alloc::string::String;
pub trait Sealed {}
#[diagnostic::on_unimplemented(
message = "`{Self}` is neither `diesel::sql_types::Text` nor `diesel::sql_types::Integer`",
note = "try to provide an expression that produces one of the expected sql types"
)]
pub trait TextOrInteger {}
impl TextOrInteger for Text {}
impl TextOrInteger for Integer {}
/// Marker trait used to implement `PgAnyJsonExpressionMethods` on the appropriate types.
#[diagnostic::on_unimplemented(
message = "`{Self}` is neither `diesel::sql_types::Json`, `diesel::sql_types::Jsonb`, `diesel::sql_types::Nullable<Json>` nor `diesel::sql_types::Nullable<Jsonb>`",
note = "try to provide an expression that produces one of the expected sql types"
)]
pub trait JsonOrNullableJsonOrJsonbOrNullableJsonb {}
impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Json {}
impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Json> {}
impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Jsonb {}
impl JsonOrNullableJsonOrJsonbOrNullableJsonb for Nullable<Jsonb> {}
impl Sealed for &'_ str {}
impl Sealed for String {}
impl Sealed for i32 {}
impl<T> Sealed for T
where
T: Expression,
T::SqlType: TextOrInteger,
{
}
impl<'a> JsonIndex for &'a str {
type Expression = crate::dsl::AsExprOf<&'a str, crate::sql_types::Text>;
fn into_json_index_expression(self) -> Self::Expression {
self.into_sql::<Text>()
}
}
impl JsonIndex for String {
type Expression = crate::dsl::AsExprOf<String, crate::sql_types::Text>;
fn into_json_index_expression(self) -> Self::Expression {
self.into_sql::<Text>()
}
}
impl JsonIndex for i32 {
type Expression = crate::dsl::AsExprOf<i32, crate::sql_types::Int4>;
fn into_json_index_expression(self) -> Self::Expression {
self.into_sql::<crate::sql_types::Int4>()
}
}
impl<T> JsonIndex for T
where
T: Expression,
T::SqlType: TextOrInteger,
{
type Expression = Self;
fn into_json_index_expression(self) -> Self::Expression {
self
}
}
}