Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions sqlx-core/src/any/types/json.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::any::{Any, AnyArgumentBuffer, AnyTypeInfo, AnyTypeInfoKind, AnyValueKind, AnyValueRef};
use crate::decode::Decode;
use crate::encode::{Encode, IsNull};
use crate::error::BoxDynError;
use crate::types::{Json, Type};
use serde::{Deserialize, Serialize};

impl<T> Type<Any> for Json<T> {
fn type_info() -> AnyTypeInfo {
AnyTypeInfo {
kind: AnyTypeInfoKind::Text,
}
}

fn compatible(ty: &AnyTypeInfo) -> bool {
matches!(ty.kind, AnyTypeInfoKind::Text | AnyTypeInfoKind::Blob)
}
}

impl<T> Encode<'_, Any> for Json<T>
where
T: Serialize,
{
fn encode_by_ref(&self, buf: &mut AnyArgumentBuffer<'_>) -> Result<IsNull, BoxDynError> {
let json_string = self.encode_to_string()?;
buf.0.push(AnyValueKind::Text(json_string.into()));
Ok(IsNull::No)
}
}

impl<T> Decode<'_, Any> for Json<T>
where
T: for<'de> Deserialize<'de>,
{
fn decode(value: AnyValueRef<'_>) -> Result<Self, BoxDynError> {
match value.kind {
AnyValueKind::Text(text) => Json::decode_from_string(&text),
AnyValueKind::Blob(blob) => Json::decode_from_bytes(&blob),
other => other.unexpected(),
}
}
}
4 changes: 4 additions & 0 deletions sqlx-core/src/any/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ mod blob;
mod bool;
mod float;
mod int;
mod json;
mod str;

#[test]
Expand Down Expand Up @@ -50,4 +51,7 @@ fn test_type_impls() {
// These imply that there are also impls for the equivalent slice types.
has_type::<Vec<u8>>();
has_type::<String>();

// JSON types
has_type::<crate::types::Json<serde_json::Value>>();
}
49 changes: 49 additions & 0 deletions tests/any/any.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ use sqlx::{Any, Connection, Executor, Row};
use sqlx_core::sql_str::AssertSqlSafe;
use sqlx_test::new;

#[cfg(feature = "json")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "json")]
use sqlx::types::Json;

#[sqlx_macros::test]
async fn it_connects() -> anyhow::Result<()> {
sqlx::any::install_default_drivers();
Expand Down Expand Up @@ -142,3 +147,47 @@ async fn it_can_fail_and_recover_with_pool() -> anyhow::Result<()> {

Ok(())
}

#[cfg(feature = "json")]
#[sqlx_macros::test]
async fn it_encodes_decodes_json() -> anyhow::Result<()> {
sqlx::any::install_default_drivers();

let mut conn = new::<Any>().await?;

// Test with serde_json::Value
let json_value = serde_json::json!({
"name": "test",
"value": 42,
"items": [1, 2, 3]
});

// This will work by encoding JSON as text and decoding it back
let result: serde_json::Value = sqlx::query_scalar("SELECT ?")
.bind(Json(&json_value))
.fetch_one(&mut conn)
.await?;

assert_eq!(result, json_value);

// Test with custom struct
#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct TestData {
name: String,
count: i32,
}

let test_data = TestData {
name: "example".to_string(),
count: 100,
};

let result: Json<TestData> = sqlx::query_scalar("SELECT ?")
.bind(Json(&test_data))
.fetch_one(&mut conn)
.await?;

assert_eq!(result.0, test_data);

Ok(())
}
Loading