Skip to content

Commit

Permalink
mysql: Add JSON support
Browse files Browse the repository at this point in the history
  • Loading branch information
blackwolf12333 committed Apr 14, 2020
1 parent c6e9b27 commit d7b1228
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 2 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 58 additions & 0 deletions sqlx-core/src/mysql/types/json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
use crate::decode::Decode;
use crate::encode::Encode;
use crate::mysql::database::MySql;
use crate::mysql::protocol::TypeId;
use crate::mysql::types::*;
use crate::mysql::{MySqlTypeInfo, MySqlValue};
use crate::types::{Json, Type};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;

impl Type<MySql> for JsonValue {
fn type_info() -> MySqlTypeInfo {
<Json<Self> as Type<MySql>>::type_info()
}
}

impl Encode<MySql> for JsonValue {
fn encode(&self, buf: &mut Vec<u8>) {
(Box::new(Json(self)) as Box<dyn Encode<MySql>>).encode(buf)
}
}

impl<'de> Decode<'de, MySql> for JsonValue {
fn decode(value: MySqlValue<'de>) -> crate::Result<Self> {
<Json<Self> as Decode<MySql>>::decode(value).map(|item| item.0)
}
}

impl<T> Type<MySql> for Json<T> {
fn type_info() -> MySqlTypeInfo {
// MySql uses the CHAR type to pass JSON data from and to the client
MySqlTypeInfo::new(TypeId::CHAR)
}
}

impl<T> Encode<MySql> for Json<T>
where
T: Serialize,
{
fn encode(&self, buf: &mut Vec<u8>) {
let json_string_value =
serde_json::to_string(&self.0).expect("serde_json failed to convert to string");
<str as Encode<MySql>>::encode(json_string_value.as_str(), buf);
}
}

impl<'de, T> Decode<'de, MySql> for Json<T>
where
T: 'de,
T: for<'de1> Deserialize<'de1>,
{
fn decode(value: MySqlValue<'de>) -> crate::Result<Self> {
let string_value = <&'de str as Decode<MySql>>::decode(value).unwrap();
serde_json::from_str(&string_value)
.map(Json)
.map_err(crate::Error::decode)
}
}
10 changes: 10 additions & 0 deletions sqlx-core/src/mysql/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@
//! | Rust type | MySQL type(s) |
//! |---------------------------------------|------------------------------------------------------|
//! | `bigdecimal::BigDecimal` | DECIMAL |
//! ### [`json`](https://crates.io/crates/json)
//!
//! Requires the `json` Cargo feature flag.
//!
//! | Rust type | MySQL type(s) |
//! |---------------------------------------|------------------------------------------------------|
//! | `json::JsonValue` | JSON
//!
//! # Nullable
//!
Expand All @@ -70,6 +77,9 @@ mod chrono;
#[cfg(feature = "time")]
mod time;

#[cfg(feature = "json")]
mod json;

use crate::decode::Decode;
use crate::mysql::{MySql, MySqlValue};

Expand Down
4 changes: 2 additions & 2 deletions sqlx-core/src/postgres/types/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ impl Type<Postgres> for JsonValue {

impl Encode<Postgres> for JsonValue {
fn encode(&self, buf: &mut PgRawBuffer) {
Json(self).encode(buf)
(Box::new(Json(self)) as Box<dyn Encode<Postgres>>).encode(buf)
}
}

Expand All @@ -41,7 +41,7 @@ impl Type<Postgres> for &'_ JsonRawValue {

impl Encode<Postgres> for &'_ JsonRawValue {
fn encode(&self, buf: &mut PgRawBuffer) {
Json(self).encode(buf)
(Box::new(Json(self)) as Box<dyn Encode<Postgres>>).encode(buf)
}
}

Expand Down
31 changes: 31 additions & 0 deletions tests/mysql-types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,34 @@ test_type!(decimal(
"CAST(12.34 AS DECIMAL(4, 2))" == "12.34".parse::<sqlx::types::BigDecimal>().unwrap(),
"CAST(12345.6789 AS DECIMAL(9, 4))" == "12345.6789".parse::<sqlx::types::BigDecimal>().unwrap(),
));

#[cfg(feature = "json")]
mod json_tests {
use super::*;
use serde_json::{json, Value as JsonValue};
use sqlx::types::Json;
use sqlx_test::test_type;

test_type!(json(
MySql,
JsonValue,
"SELECT CAST({0} AS JSON) <=> CAST(? AS JSON), '<UNKNOWN>' as _1, ? as _2, ? as _3",
"'\"Hello, World\"'" == json!("Hello, World"),
"'\"😎\"'" == json!("😎"),
"'\"🙋‍♀️\"'" == json!("🙋‍♀️"),
"'[\"Hello\", \"World!\"]'" == json!(["Hello", "World!"])
));

#[derive(serde::Deserialize, serde::Serialize, Debug, PartialEq)]
struct Friend {
name: String,
age: u32,
}

test_type!(json_struct(
MySql,
Json<Friend>,
"SELECT CAST({0} AS JSON) <=> CAST(? AS JSON), '<UNKNOWN>' as _1, ? as _2, ? as _3",
"\'{\"name\": \"Joe\", \"age\":33}\'" == Json(Friend { name: "Joe".to_string(), age: 33 })
));
}

0 comments on commit d7b1228

Please sign in to comment.