postgres: simplify JSON support to just Json<T>

This commit is contained in:
Ryan Leckey 2020-03-25 04:14:33 -07:00
parent 8454fa4e96
commit 7ab772ea80
5 changed files with 55 additions and 138 deletions

View File

@ -1,8 +1,10 @@
use crate::decode::Decode;
use crate::encode::Encode;
use crate::postgres::types::{PgJsonb, PgTypeInfo};
use crate::postgres::{PgValue, Postgres};
use crate::io::{Buf, BufMut};
use crate::postgres::protocol::TypeId;
use crate::postgres::{PgData, PgTypeInfo, PgValue, Postgres};
use crate::types::{Json, Type};
use crate::value::RawValue;
use serde::{Deserialize, Serialize};
use serde_json::value::RawValue as JsonRawValue;
use serde_json::Value as JsonValue;
@ -15,43 +17,43 @@ use serde_json::Value as JsonValue;
impl Type<Postgres> for JsonValue {
fn type_info() -> PgTypeInfo {
<PgJsonb<Self> as Type<Postgres>>::type_info()
<Json<Self> as Type<Postgres>>::type_info()
}
}
impl Encode<Postgres> for JsonValue {
fn encode(&self, buf: &mut Vec<u8>) {
PgJsonb(self).encode(buf)
Json(self).encode(buf)
}
}
impl<'de> Decode<'de, Postgres> for JsonValue {
fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
<PgJsonb<Self> as Decode<Postgres>>::decode(value).map(|item| item.0)
<Json<Self> as Decode<Postgres>>::decode(value).map(|item| item.0)
}
}
impl Type<Postgres> for &'_ JsonRawValue {
fn type_info() -> PgTypeInfo {
<PgJsonb<Self> as Type<Postgres>>::type_info()
<Json<Self> as Type<Postgres>>::type_info()
}
}
impl Encode<Postgres> for &'_ JsonRawValue {
fn encode(&self, buf: &mut Vec<u8>) {
PgJsonb(self).encode(buf)
Json(self).encode(buf)
}
}
impl<'de> Decode<'de, Postgres> for &'de JsonRawValue {
fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
<PgJsonb<Self> as Decode<Postgres>>::decode(value).map(|item| item.0)
<Json<Self> as Decode<Postgres>>::decode(value).map(|item| item.0)
}
}
impl<T> Type<Postgres> for Json<T> {
fn type_info() -> PgTypeInfo {
<PgJsonb<T> as Type<Postgres>>::type_info()
PgTypeInfo::new(TypeId::JSONB, "JSONB")
}
}
@ -60,7 +62,11 @@ where
T: Serialize,
{
fn encode(&self, buf: &mut Vec<u8>) {
PgJsonb(&self.0).encode(buf)
// JSONB version (as of 2020-03-20)
buf.put_u8(1);
serde_json::to_writer(buf, &self.0)
.expect("failed to serialize json for encoding to database");
}
}
@ -70,6 +76,23 @@ where
T: Deserialize<'de>,
{
fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
<PgJsonb<T> as Decode<Postgres>>::decode(value).map(|item| Self(item.0))
(match value.try_get()? {
PgData::Text(s) => serde_json::from_str(s),
PgData::Binary(mut buf) => {
if value.type_info().id == TypeId::JSONB {
let version = buf.get_u8()?;
assert_eq!(
version, 1,
"unsupported JSONB format version {}; please open an issue",
version
);
}
serde_json::from_slice(buf)
}
})
.map(Json)
.map_err(crate::Error::decode)
}
}

View File

@ -58,26 +58,16 @@
//!
//! | Rust type | Postgres type(s) |
//! |---------------------------------------|------------------------------------------------------|
//! | [`Json<T>`] | JSONB |
//! | [`PgJson<T>`] | JSON |
//! | [`PgJsonb<T>`] | JSONB |
//! | `serde_json::Value` | JSONB |
//! | `PgJson<serde_json::Value>` | JSON |
//! | `&serde_json::value::RawValue` | JSONB |
//! | `PgJson<&serde_json::value::RawValue>`| JSON |
//!
//! By default, Rust types are mapped to `JSONB`. They can be wrapped in [`PgJson<T>`] to use `JSON`
//! instead.
//! | [`Json<T>`] | JSON, JSONB |
//! | `serde_json::Value` | JSON, JSONB |
//! | `&serde_json::value::RawValue` | JSON, JSONB |
//!
//! `Value` and `RawValue` from `serde_json` can be used for unstructured JSON data with
//! Postgres.
//!
//! [`Json<T>`] can be used for structured JSON data with Postgres. [`Json<T>`] is an alias
//! for [`PgJsonb<T>`].
//! [`Json<T>`] can be used for structured JSON data with Postgres.
//!
//! [`Json<T>`]: crate::types::Json
//! [`PgJson<T>`]: crate::postgres::types::PgJson
//! [`PgJsonb<T>`]: crate::postgres::types::PgJsonb
//!
//! # [Composite types](https://www.postgresql.org/docs/current/rowtypes.html)
//!
@ -177,9 +167,6 @@ mod json;
#[cfg(feature = "ipnetwork")]
mod ipnetwork;
#[cfg(feature = "json")]
pub use raw::{PgJson, PgJsonb};
/// Type information for a Postgres SQL type.
#[derive(Debug, Clone)]
pub struct PgTypeInfo {
@ -289,6 +276,16 @@ impl TypeInfo for PgTypeInfo {
true
}
// JSON <=> JSONB
(TypeId::JSON, other) | (TypeId::JSONB, other)
if match other {
TypeId::JSON | TypeId::JSONB => true,
_ => false,
} =>
{
true
}
_ => self.id.0 == other.id.0,
}
}

View File

@ -1,91 +0,0 @@
use crate::decode::Decode;
use crate::encode::Encode;
use crate::io::{Buf, BufMut};
use crate::postgres::protocol::TypeId;
use crate::postgres::types::PgTypeInfo;
use crate::postgres::{PgData, PgValue, Postgres};
use crate::types::Type;
use serde::{Deserialize, Serialize};
#[derive(Debug, PartialEq)]
pub struct PgJson<T>(pub T);
impl<T> Type<Postgres> for PgJson<T> {
fn type_info() -> PgTypeInfo {
PgTypeInfo::new(TypeId::JSON, "JSON")
}
}
impl<T> Encode<Postgres> for PgJson<T>
where
T: Serialize,
{
fn encode(&self, buf: &mut Vec<u8>) {
serde_json::to_writer(buf, &self.0)
.expect("failed to serialize json for encoding to database");
}
}
impl<'de, T> Decode<'de, Postgres> for PgJson<T>
where
T: 'de,
T: Deserialize<'de>,
{
fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
(match value.try_get()? {
PgData::Text(s) => serde_json::from_str(s),
PgData::Binary(buf) => serde_json::from_slice(buf),
})
.map(PgJson)
.map_err(crate::Error::decode)
}
}
// This type has the Pg prefix as it is a postgres-only extension
// unlike the normal Json<T> wrapper
#[derive(Debug, PartialEq)]
pub struct PgJsonb<T>(pub T);
impl<T> Type<Postgres> for PgJsonb<T> {
fn type_info() -> PgTypeInfo {
PgTypeInfo::new(TypeId::JSONB, "JSONB")
}
}
impl<T> Encode<Postgres> for PgJsonb<T>
where
T: Serialize,
{
fn encode(&self, buf: &mut Vec<u8>) {
// JSONB version (as of 2020-03-20)
buf.put_u8(1);
serde_json::to_writer(buf, &self.0)
.expect("failed to serialize json for encoding to database");
}
}
impl<'de, T> Decode<'de, Postgres> for PgJsonb<T>
where
T: 'de,
T: Deserialize<'de>,
{
fn decode(value: PgValue<'de>) -> crate::Result<Postgres, Self> {
(match value.try_get()? {
PgData::Text(s) => serde_json::from_str(s),
PgData::Binary(mut buf) => {
let version = buf.get_u8()?;
assert_eq!(
version, 1,
"unsupported JSONB format version {}; please open an issue",
version
);
serde_json::from_slice(buf)
}
})
.map(PgJsonb)
.map_err(crate::Error::decode)
}
}

View File

@ -3,12 +3,6 @@ mod numeric;
mod record;
mod sequence;
#[cfg(feature = "json")]
mod json;
#[cfg(feature = "json")]
pub use json::{PgJson, PgJsonb};
pub(crate) use array::{PgArrayDecoder, PgArrayEncoder};
// Used in integration tests

View File

@ -534,7 +534,6 @@ mod json {
use super::*;
use serde_json::value::RawValue;
use serde_json::{json, Value as JsonValue};
use sqlx::postgres::types::PgJson;
use sqlx::postgres::PgRow;
use sqlx::types::Json;
use sqlx::Row;
@ -544,12 +543,12 @@ mod json {
test_type!(json(
Postgres,
PgJson<JsonValue>,
JsonValue,
"SELECT {0}::jsonb is not distinct from $1::jsonb, $2::text as _1, {0} as _2, $3 as _3",
"'\"Hello, World\"'::json" == PgJson(json!("Hello, World")),
"'\"😎\"'::json" == PgJson(json!("😎")),
"'\"🙋‍♀️\"'::json" == PgJson(json!("🙋‍♀️")),
"'[\"Hello\", \"World!\"]'::json" == PgJson(json!(["Hello", "World!"]))
"'\"Hello, World\"'::json" == json!("Hello, World"),
"'\"😎\"'::json" == json!("😎"),
"'\"🙋‍♀️\"'::json" == json!("🙋‍♀️"),
"'[\"Hello\", \"World!\"]'::json" == json!(["Hello", "World!"])
));
test_type!(jsonb(
@ -567,20 +566,15 @@ mod json {
age: u32,
}
// The default JSON type that SQLx chooses is JSONB
// sqlx::types::Json -> JSONB
// sqlx::postgres::types::PgJson -> JSON
// sqlx::postgres::types::PgJsonB -> JSONB
test_type!(jsonb_struct(Postgres, Json<Friend>,
"'{\"name\":\"Joe\",\"age\":33}'::jsonb" == Json(Friend { name: "Joe".to_string(), age: 33 })
));
test_type!(json_struct(
Postgres,
PgJson<Friend>,
Json<Friend>,
"SELECT {0}::jsonb is not distinct from $1::jsonb, $2::text as _1, {0} as _2, $3 as _3",
"'{\"name\":\"Joe\",\"age\":33}'::json" == PgJson(Friend { name: "Joe".to_string(), age: 33 })
"'{\"name\":\"Joe\",\"age\":33}'::json" == Json(Friend { name: "Joe".to_string(), age: 33 })
));
#[cfg_attr(feature = "runtime-async-std", async_std::test)]