Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support For PostgreSQL Arrays In FromQueryResult Implementation Of JsonValue #1598

Merged
merged 9 commits into from
Aug 1, 2023
23 changes: 21 additions & 2 deletions src/query/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,33 @@ impl FromQueryResult for JsonValue {
crate::QueryResultRow::SqlxPostgres(row) => {
use serde_json::json;
use sqlx::{postgres::types::Oid, Column, Postgres, Row, Type};

for column in row.columns() {
let col = if !column.name().starts_with(pre) {
continue;
} else {
column.name().replacen(pre, "", 1)
};
let col_type = column.type_info();

macro_rules! match_postgres_type {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The macro is used for matching primitives against column types, but doesn't consider the fact that postgres columns can be of type array (of a primitive).

( $type: ty ) => {
if <$type as Type<Postgres>>::type_info().eq(col_type) {
try_get_type!($type, col)
match col_type.kind() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We try to conditionally match if the column is of type array, or not.

#[cfg(feature = "postgres-array")]
sqlx::postgres::PgTypeKind::Array(_) => {
if <Vec<$type> as Type<Postgres>>::type_info().eq(col_type) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If column is indeed of type array, and postgres-array feature is enabled, match the column type against Vec<primitive> instead.

try_get_type!(Vec<$type>, col);
}
}
_ => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise, match column with given primitive type (original logic)

if <$type as Type<Postgres>>::type_info().eq(col_type) {
try_get_type!($type, col);
}
}
}
};
}

match_postgres_type!(bool);
match_postgres_type!(i8);
match_postgres_type!(i16);
Expand Down Expand Up @@ -126,9 +139,15 @@ impl FromQueryResult for JsonValue {
match_postgres_type!(rust_decimal::Decimal);
#[cfg(feature = "with-json")]
try_get_type!(serde_json::Value, col);
#[cfg(all(feature = "with-json", feature = "postgres-array"))]
try_get_type!(Vec<serde_json::Value>, col);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since postgres also supports arrays of json or jsonb, we need to add explicit matching for it in addition to primitive arrays.

try_get_type!(String, col);
#[cfg(feature = "postgres-array")]
try_get_type!(Vec<String>, col);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add explicit check for String array, since it's not a primitive

#[cfg(feature = "with-uuid")]
try_get_type!(uuid::Uuid, col);
#[cfg(all(feature = "with-uuid", feature = "postgres-array"))]
try_get_type!(Vec<uuid::Uuid>, col);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add explicit check for UUID array, since it's not a primitive

try_get_type!(Vec<u8>, col);
}
Ok(JsonValue::Object(map))
Expand Down
49 changes: 49 additions & 0 deletions tests/collection_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use pretty_assertions::assert_eq;
use sea_orm::{
entity::prelude::*, entity::*, DatabaseConnection, DerivePartialModel, FromQueryResult,
};
use serde_json::json;

#[sea_orm_macros::test]
#[cfg(all(feature = "sqlx-postgres", feature = "postgres-array"))]
Expand Down Expand Up @@ -114,6 +115,54 @@ pub async fn insert_collection(db: &DatabaseConnection) -> Result<(), DbErr> {
}
);

assert_eq!(
Entity::find_by_id(1).into_json().one(db).await?,
Some(json!({
"id": 1,
"name": "Collection 1",
"integers": [1, 2, 3],
"integers_opt": [1, 2, 3],
"teas": ["BreakfastTea"],
"teas_opt": ["BreakfastTea"],
"colors": [0],
"colors_opt": [0],
"uuid": [uuid],
"uuid_hyphenated": [uuid.hyphenated()],
}))
);

assert_eq!(
Entity::find_by_id(2).into_json().one(db).await?,
Some(json!({
"id": 2,
"name": "Collection 2",
"integers": [10, 9],
"integers_opt": null,
"teas": ["BreakfastTea"],
"teas_opt": null,
"colors": [0],
"colors_opt": null,
"uuid": [uuid],
"uuid_hyphenated": [uuid.hyphenated()],
}))
);

assert_eq!(
Entity::find_by_id(3).into_json().one(db).await?,
Some(json!({
"id": 3,
"name": "Collection 3",
"integers": [],
"integers_opt": [],
"teas": [],
"teas_opt": [],
"colors": [],
"colors_opt": [],
"uuid": [uuid],
"uuid_hyphenated": [uuid.hyphenated()],
}))
);

Ok(())
}

Expand Down