Support array datatype in PostgreSQL (#1132)
* PostgreSQL array (draft) * Fixup * Fixup * Fixup * Fixup * Fixup * Refactoring * generate entity for Postgres array fields * Add tests * Update Cargo.toml Co-authored-by: Chris Tsang <chris.2y3@outlook.com>
This commit is contained in:
parent
2757190ba4
commit
b5b9790252
@ -55,7 +55,7 @@ actix-rt = { version = "2.2.0" }
|
||||
maplit = { version = "^1" }
|
||||
rust_decimal_macros = { version = "^1" }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
sea-orm = { path = ".", features = ["mock", "debug-print", "tests-cfg"] }
|
||||
sea-orm = { path = ".", features = ["mock", "debug-print", "tests-cfg", "postgres-array"] }
|
||||
pretty_assertions = { version = "^0.7" }
|
||||
time = { version = "^0.3", features = ["macros"] }
|
||||
|
||||
@ -76,6 +76,7 @@ with-chrono = ["chrono", "sea-query/with-chrono", "sea-query-binder?/with-chrono
|
||||
with-rust_decimal = ["rust_decimal", "sea-query/with-rust_decimal", "sea-query-binder?/with-rust_decimal", "sqlx?/decimal"]
|
||||
with-uuid = ["uuid", "sea-query/with-uuid", "sea-query-binder?/with-uuid", "sqlx?/uuid"]
|
||||
with-time = ["time", "sea-query/with-time", "sea-query-binder?/with-time", "sqlx?/time"]
|
||||
postgres-array = ["sea-query/postgres-array", "sea-query-binder?/postgres-array"]
|
||||
sqlx-dep = []
|
||||
sqlx-all = ["sqlx-mysql", "sqlx-postgres", "sqlx-sqlite"]
|
||||
sqlx-mysql = ["sqlx-dep", "sea-query-binder/sqlx-mysql", "sqlx/mysql"]
|
||||
|
@ -35,7 +35,7 @@ clap = { version = "^3.2", features = ["env", "derive"] }
|
||||
dotenvy = { version = "^0.15", optional = true }
|
||||
async-std = { version = "^1.9", features = [ "attributes", "tokio1" ], optional = true }
|
||||
sea-orm-codegen = { version = "^0.10.0", path = "../sea-orm-codegen", optional = true }
|
||||
sea-schema = { version = "^0.10.0" }
|
||||
sea-schema = { version = "^0.10.1" }
|
||||
sqlx = { version = "^0.6", default-features = false, features = [ "mysql", "postgres" ], optional = true }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
tracing = { version = "0.1" }
|
||||
|
@ -28,53 +28,59 @@ impl Column {
|
||||
}
|
||||
|
||||
pub fn get_rs_type(&self, date_time_crate: &DateTimeCrate) -> TokenStream {
|
||||
#[allow(unreachable_patterns)]
|
||||
let ident: TokenStream = match &self.col_type {
|
||||
ColumnType::Char(_)
|
||||
| ColumnType::String(_)
|
||||
| ColumnType::Text
|
||||
| ColumnType::Custom(_) => "String".to_owned(),
|
||||
ColumnType::TinyInteger(_) => "i8".to_owned(),
|
||||
ColumnType::SmallInteger(_) => "i16".to_owned(),
|
||||
ColumnType::Integer(_) => "i32".to_owned(),
|
||||
ColumnType::BigInteger(_) => "i64".to_owned(),
|
||||
ColumnType::TinyUnsigned(_) => "u8".to_owned(),
|
||||
ColumnType::SmallUnsigned(_) => "u16".to_owned(),
|
||||
ColumnType::Unsigned(_) => "u32".to_owned(),
|
||||
ColumnType::BigUnsigned(_) => "u64".to_owned(),
|
||||
ColumnType::Float(_) => "f32".to_owned(),
|
||||
ColumnType::Double(_) => "f64".to_owned(),
|
||||
ColumnType::Json | ColumnType::JsonBinary => "Json".to_owned(),
|
||||
ColumnType::Date => match date_time_crate {
|
||||
DateTimeCrate::Chrono => "Date".to_owned(),
|
||||
DateTimeCrate::Time => "TimeDate".to_owned(),
|
||||
},
|
||||
ColumnType::Time(_) => match date_time_crate {
|
||||
DateTimeCrate::Chrono => "Time".to_owned(),
|
||||
DateTimeCrate::Time => "TimeTime".to_owned(),
|
||||
},
|
||||
ColumnType::DateTime(_) => match date_time_crate {
|
||||
DateTimeCrate::Chrono => "DateTime".to_owned(),
|
||||
DateTimeCrate::Time => "TimeDateTime".to_owned(),
|
||||
},
|
||||
ColumnType::Timestamp(_) => match date_time_crate {
|
||||
DateTimeCrate::Chrono => "DateTimeUtc".to_owned(),
|
||||
// ColumnType::Timpestamp(_) => time::PrimitiveDateTime: https://docs.rs/sqlx/0.3.5/sqlx/postgres/types/index.html#time
|
||||
DateTimeCrate::Time => "TimeDateTime".to_owned(),
|
||||
},
|
||||
ColumnType::TimestampWithTimeZone(_) => match date_time_crate {
|
||||
DateTimeCrate::Chrono => "DateTimeWithTimeZone".to_owned(),
|
||||
DateTimeCrate::Time => "TimeDateTimeWithTimeZone".to_owned(),
|
||||
},
|
||||
ColumnType::Decimal(_) | ColumnType::Money(_) => "Decimal".to_owned(),
|
||||
ColumnType::Uuid => "Uuid".to_owned(),
|
||||
ColumnType::Binary(_) | ColumnType::VarBinary(_) => "Vec<u8>".to_owned(),
|
||||
ColumnType::Boolean => "bool".to_owned(),
|
||||
ColumnType::Enum { name, .. } => name.to_string().to_camel_case(),
|
||||
_ => unimplemented!(),
|
||||
fn write_rs_type(col_type: &ColumnType, date_time_crate: &DateTimeCrate) -> String {
|
||||
#[allow(unreachable_patterns)]
|
||||
match col_type {
|
||||
ColumnType::Char(_)
|
||||
| ColumnType::String(_)
|
||||
| ColumnType::Text
|
||||
| ColumnType::Custom(_) => "String".to_owned(),
|
||||
ColumnType::TinyInteger(_) => "i8".to_owned(),
|
||||
ColumnType::SmallInteger(_) => "i16".to_owned(),
|
||||
ColumnType::Integer(_) => "i32".to_owned(),
|
||||
ColumnType::BigInteger(_) => "i64".to_owned(),
|
||||
ColumnType::TinyUnsigned(_) => "u8".to_owned(),
|
||||
ColumnType::SmallUnsigned(_) => "u16".to_owned(),
|
||||
ColumnType::Unsigned(_) => "u32".to_owned(),
|
||||
ColumnType::BigUnsigned(_) => "u64".to_owned(),
|
||||
ColumnType::Float(_) => "f32".to_owned(),
|
||||
ColumnType::Double(_) => "f64".to_owned(),
|
||||
ColumnType::Json | ColumnType::JsonBinary => "Json".to_owned(),
|
||||
ColumnType::Date => match date_time_crate {
|
||||
DateTimeCrate::Chrono => "Date".to_owned(),
|
||||
DateTimeCrate::Time => "TimeDate".to_owned(),
|
||||
},
|
||||
ColumnType::Time(_) => match date_time_crate {
|
||||
DateTimeCrate::Chrono => "Time".to_owned(),
|
||||
DateTimeCrate::Time => "TimeTime".to_owned(),
|
||||
},
|
||||
ColumnType::DateTime(_) => match date_time_crate {
|
||||
DateTimeCrate::Chrono => "DateTime".to_owned(),
|
||||
DateTimeCrate::Time => "TimeDateTime".to_owned(),
|
||||
},
|
||||
ColumnType::Timestamp(_) => match date_time_crate {
|
||||
DateTimeCrate::Chrono => "DateTimeUtc".to_owned(),
|
||||
// ColumnType::Timpestamp(_) => time::PrimitiveDateTime: https://docs.rs/sqlx/0.3.5/sqlx/postgres/types/index.html#time
|
||||
DateTimeCrate::Time => "TimeDateTime".to_owned(),
|
||||
},
|
||||
ColumnType::TimestampWithTimeZone(_) => match date_time_crate {
|
||||
DateTimeCrate::Chrono => "DateTimeWithTimeZone".to_owned(),
|
||||
DateTimeCrate::Time => "TimeDateTimeWithTimeZone".to_owned(),
|
||||
},
|
||||
ColumnType::Decimal(_) | ColumnType::Money(_) => "Decimal".to_owned(),
|
||||
ColumnType::Uuid => "Uuid".to_owned(),
|
||||
ColumnType::Binary(_) | ColumnType::VarBinary(_) => "Vec<u8>".to_owned(),
|
||||
ColumnType::Boolean => "bool".to_owned(),
|
||||
ColumnType::Enum { name, .. } => name.to_string().to_camel_case(),
|
||||
ColumnType::Array(column_type) => {
|
||||
format!("Vec<{}>", write_rs_type(column_type, date_time_crate))
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
.parse()
|
||||
.unwrap();
|
||||
let ident: TokenStream = write_rs_type(&self.col_type, date_time_crate)
|
||||
.parse()
|
||||
.unwrap();
|
||||
match self.not_null {
|
||||
true => quote! { #ident },
|
||||
false => quote! { Option<#ident> },
|
||||
@ -97,62 +103,72 @@ impl Column {
|
||||
}
|
||||
|
||||
pub fn get_def(&self) -> TokenStream {
|
||||
let mut col_def = match &self.col_type {
|
||||
ColumnType::Char(s) => match s {
|
||||
Some(s) => quote! { ColumnType::Char(Some(#s)).def() },
|
||||
None => quote! { ColumnType::Char(None).def() },
|
||||
},
|
||||
ColumnType::String(s) => match s {
|
||||
Some(s) => quote! { ColumnType::String(Some(#s)).def() },
|
||||
None => quote! { ColumnType::String(None).def() },
|
||||
},
|
||||
ColumnType::Text => quote! { ColumnType::Text.def() },
|
||||
ColumnType::TinyInteger(_) => quote! { ColumnType::TinyInteger.def() },
|
||||
ColumnType::SmallInteger(_) => quote! { ColumnType::SmallInteger.def() },
|
||||
ColumnType::Integer(_) => quote! { ColumnType::Integer.def() },
|
||||
ColumnType::BigInteger(_) => quote! { ColumnType::BigInteger.def() },
|
||||
ColumnType::TinyUnsigned(_) => quote! { ColumnType::TinyUnsigned.def() },
|
||||
ColumnType::SmallUnsigned(_) => quote! { ColumnType::SmallUnsigned.def() },
|
||||
ColumnType::Unsigned(_) => quote! { ColumnType::Unsigned.def() },
|
||||
ColumnType::BigUnsigned(_) => quote! { ColumnType::BigUnsigned.def() },
|
||||
ColumnType::Float(_) => quote! { ColumnType::Float.def() },
|
||||
ColumnType::Double(_) => quote! { ColumnType::Double.def() },
|
||||
ColumnType::Decimal(s) => match s {
|
||||
Some((s1, s2)) => quote! { ColumnType::Decimal(Some((#s1, #s2))).def() },
|
||||
None => quote! { ColumnType::Decimal(None).def() },
|
||||
},
|
||||
ColumnType::DateTime(_) => quote! { ColumnType::DateTime.def() },
|
||||
ColumnType::Timestamp(_) => quote! { ColumnType::Timestamp.def() },
|
||||
ColumnType::TimestampWithTimeZone(_) => {
|
||||
quote! { ColumnType::TimestampWithTimeZone.def() }
|
||||
fn write_col_def(col_type: &ColumnType) -> TokenStream {
|
||||
match col_type {
|
||||
ColumnType::Char(s) => match s {
|
||||
Some(s) => quote! { ColumnType::Char(Some(#s)) },
|
||||
None => quote! { ColumnType::Char(None) },
|
||||
},
|
||||
ColumnType::String(s) => match s {
|
||||
Some(s) => quote! { ColumnType::String(Some(#s)) },
|
||||
None => quote! { ColumnType::String(None) },
|
||||
},
|
||||
ColumnType::Text => quote! { ColumnType::Text },
|
||||
ColumnType::TinyInteger(_) => quote! { ColumnType::TinyInteger },
|
||||
ColumnType::SmallInteger(_) => quote! { ColumnType::SmallInteger },
|
||||
ColumnType::Integer(_) => quote! { ColumnType::Integer },
|
||||
ColumnType::BigInteger(_) => quote! { ColumnType::BigInteger },
|
||||
ColumnType::TinyUnsigned(_) => quote! { ColumnType::TinyUnsigned },
|
||||
ColumnType::SmallUnsigned(_) => quote! { ColumnType::SmallUnsigned },
|
||||
ColumnType::Unsigned(_) => quote! { ColumnType::Unsigned },
|
||||
ColumnType::BigUnsigned(_) => quote! { ColumnType::BigUnsigned },
|
||||
ColumnType::Float(_) => quote! { ColumnType::Float },
|
||||
ColumnType::Double(_) => quote! { ColumnType::Double },
|
||||
ColumnType::Decimal(s) => match s {
|
||||
Some((s1, s2)) => quote! { ColumnType::Decimal(Some((#s1, #s2))) },
|
||||
None => quote! { ColumnType::Decimal(None) },
|
||||
},
|
||||
ColumnType::DateTime(_) => quote! { ColumnType::DateTime },
|
||||
ColumnType::Timestamp(_) => quote! { ColumnType::Timestamp },
|
||||
ColumnType::TimestampWithTimeZone(_) => {
|
||||
quote! { ColumnType::TimestampWithTimeZone }
|
||||
}
|
||||
ColumnType::Time(_) => quote! { ColumnType::Time },
|
||||
ColumnType::Date => quote! { ColumnType::Date },
|
||||
ColumnType::Binary(BlobSize::Blob(_)) | ColumnType::VarBinary(_) => {
|
||||
quote! { ColumnType::Binary }
|
||||
}
|
||||
ColumnType::Binary(BlobSize::Tiny) => quote! { ColumnType::TinyBinary },
|
||||
ColumnType::Binary(BlobSize::Medium) => quote! { ColumnType::MediumBinary },
|
||||
ColumnType::Binary(BlobSize::Long) => quote! { ColumnType::LongBinary },
|
||||
ColumnType::Boolean => quote! { ColumnType::Boolean },
|
||||
ColumnType::Money(s) => match s {
|
||||
Some((s1, s2)) => quote! { ColumnType::Money(Some((#s1, #s2))) },
|
||||
None => quote! { ColumnType::Money(None) },
|
||||
},
|
||||
ColumnType::Json => quote! { ColumnType::Json },
|
||||
ColumnType::JsonBinary => quote! { ColumnType::JsonBinary },
|
||||
ColumnType::Uuid => quote! { ColumnType::Uuid },
|
||||
ColumnType::Custom(s) => {
|
||||
let s = s.to_string();
|
||||
quote! { ColumnType::Custom(#s.to_owned()) }
|
||||
}
|
||||
ColumnType::Enum { name, .. } => {
|
||||
let enum_ident = format_ident!("{}", name.to_string().to_camel_case());
|
||||
quote! { #enum_ident::db_type() }
|
||||
}
|
||||
ColumnType::Array(column_type) => {
|
||||
let column_type = write_col_def(column_type);
|
||||
quote! { ColumnType::Array(sea_orm::sea_query::SeaRc::new(#column_type)) }
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
ColumnType::Time(_) => quote! { ColumnType::Time.def() },
|
||||
ColumnType::Date => quote! { ColumnType::Date.def() },
|
||||
ColumnType::Binary(BlobSize::Blob(_)) | ColumnType::VarBinary(_) => {
|
||||
quote! { ColumnType::Binary.def() }
|
||||
}
|
||||
ColumnType::Binary(BlobSize::Tiny) => quote! { ColumnType::TinyBinary.def() },
|
||||
ColumnType::Binary(BlobSize::Medium) => quote! { ColumnType::MediumBinary.def() },
|
||||
ColumnType::Binary(BlobSize::Long) => quote! { ColumnType::LongBinary.def() },
|
||||
ColumnType::Boolean => quote! { ColumnType::Boolean.def() },
|
||||
ColumnType::Money(s) => match s {
|
||||
Some((s1, s2)) => quote! { ColumnType::Money(Some((#s1, #s2))).def() },
|
||||
None => quote! { ColumnType::Money(None).def() },
|
||||
},
|
||||
ColumnType::Json => quote! { ColumnType::Json.def() },
|
||||
ColumnType::JsonBinary => quote! { ColumnType::JsonBinary.def() },
|
||||
ColumnType::Uuid => quote! { ColumnType::Uuid.def() },
|
||||
ColumnType::Custom(s) => {
|
||||
let s = s.to_string();
|
||||
quote! { ColumnType::Custom(#s.to_owned()).def() }
|
||||
}
|
||||
ColumnType::Enum { name, .. } => {
|
||||
let enum_ident = format_ident!("{}", name.to_string().to_camel_case());
|
||||
quote! { #enum_ident::db_type() }
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
}
|
||||
let mut col_def = write_col_def(&self.col_type);
|
||||
col_def.extend(quote! {
|
||||
.def()
|
||||
});
|
||||
if !self.not_null {
|
||||
col_def.extend(quote! {
|
||||
.null()
|
||||
|
@ -672,7 +672,7 @@ mod tests {
|
||||
};
|
||||
use pretty_assertions::assert_eq;
|
||||
use proc_macro2::TokenStream;
|
||||
use sea_query::{ColumnType, ForeignKeyAction};
|
||||
use sea_query::{ColumnType, ForeignKeyAction, SeaRc};
|
||||
use std::io::{self, BufRead, BufReader, Read};
|
||||
|
||||
fn setup() -> Vec<Entity> {
|
||||
@ -1120,6 +1120,41 @@ mod tests {
|
||||
name: "id".to_owned(),
|
||||
}],
|
||||
},
|
||||
Entity {
|
||||
table_name: "collection".to_owned(),
|
||||
columns: vec![
|
||||
Column {
|
||||
name: "id".to_owned(),
|
||||
col_type: ColumnType::Integer(Some(11)),
|
||||
auto_increment: true,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
Column {
|
||||
name: "integers".to_owned(),
|
||||
col_type: ColumnType::Array(SeaRc::new(Box::new(ColumnType::Integer(
|
||||
None,
|
||||
)))),
|
||||
auto_increment: false,
|
||||
not_null: true,
|
||||
unique: false,
|
||||
},
|
||||
Column {
|
||||
name: "integers_opt".to_owned(),
|
||||
col_type: ColumnType::Array(SeaRc::new(Box::new(ColumnType::Integer(
|
||||
None,
|
||||
)))),
|
||||
auto_increment: false,
|
||||
not_null: false,
|
||||
unique: false,
|
||||
},
|
||||
],
|
||||
relations: vec![],
|
||||
conjunct_relations: vec![],
|
||||
primary_keys: vec![PrimaryKey {
|
||||
name: "id".to_owned(),
|
||||
}],
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@ -1144,7 +1179,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_gen_expanded_code_blocks() -> io::Result<()> {
|
||||
let entities = setup();
|
||||
const ENTITY_FILES: [&str; 8] = [
|
||||
const ENTITY_FILES: [&str; 9] = [
|
||||
include_str!("../../tests/expanded/cake.rs"),
|
||||
include_str!("../../tests/expanded/cake_filling.rs"),
|
||||
include_str!("../../tests/expanded/filling.rs"),
|
||||
@ -1153,8 +1188,9 @@ mod tests {
|
||||
include_str!("../../tests/expanded/rust_keyword.rs"),
|
||||
include_str!("../../tests/expanded/cake_with_float.rs"),
|
||||
include_str!("../../tests/expanded/cake_with_double.rs"),
|
||||
include_str!("../../tests/expanded/collection.rs"),
|
||||
];
|
||||
const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 8] = [
|
||||
const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 9] = [
|
||||
include_str!("../../tests/expanded_with_schema_name/cake.rs"),
|
||||
include_str!("../../tests/expanded_with_schema_name/cake_filling.rs"),
|
||||
include_str!("../../tests/expanded_with_schema_name/filling.rs"),
|
||||
@ -1163,6 +1199,7 @@ mod tests {
|
||||
include_str!("../../tests/expanded_with_schema_name/rust_keyword.rs"),
|
||||
include_str!("../../tests/expanded_with_schema_name/cake_with_float.rs"),
|
||||
include_str!("../../tests/expanded_with_schema_name/cake_with_double.rs"),
|
||||
include_str!("../../tests/expanded_with_schema_name/collection.rs"),
|
||||
];
|
||||
|
||||
assert_eq!(entities.len(), ENTITY_FILES.len());
|
||||
@ -1224,7 +1261,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_gen_compact_code_blocks() -> io::Result<()> {
|
||||
let entities = setup();
|
||||
const ENTITY_FILES: [&str; 8] = [
|
||||
const ENTITY_FILES: [&str; 9] = [
|
||||
include_str!("../../tests/compact/cake.rs"),
|
||||
include_str!("../../tests/compact/cake_filling.rs"),
|
||||
include_str!("../../tests/compact/filling.rs"),
|
||||
@ -1233,8 +1270,9 @@ mod tests {
|
||||
include_str!("../../tests/compact/rust_keyword.rs"),
|
||||
include_str!("../../tests/compact/cake_with_float.rs"),
|
||||
include_str!("../../tests/compact/cake_with_double.rs"),
|
||||
include_str!("../../tests/compact/collection.rs"),
|
||||
];
|
||||
const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 8] = [
|
||||
const ENTITY_FILES_WITH_SCHEMA_NAME: [&str; 9] = [
|
||||
include_str!("../../tests/compact_with_schema_name/cake.rs"),
|
||||
include_str!("../../tests/compact_with_schema_name/cake_filling.rs"),
|
||||
include_str!("../../tests/compact_with_schema_name/filling.rs"),
|
||||
@ -1243,6 +1281,7 @@ mod tests {
|
||||
include_str!("../../tests/compact_with_schema_name/rust_keyword.rs"),
|
||||
include_str!("../../tests/compact_with_schema_name/cake_with_float.rs"),
|
||||
include_str!("../../tests/compact_with_schema_name/cake_with_double.rs"),
|
||||
include_str!("../../tests/compact_with_schema_name/collection.rs"),
|
||||
];
|
||||
|
||||
assert_eq!(entities.len(), ENTITY_FILES.len());
|
||||
|
17
sea-orm-codegen/tests/compact/collection.rs
Normal file
17
sea-orm-codegen/tests/compact/collection.rs
Normal file
@ -0,0 +1,17 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.10.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(table_name = "collection")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub integers: Vec<i32> ,
|
||||
pub integers_opt: Option<Vec<i32> > ,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
17
sea-orm-codegen/tests/compact_with_schema_name/collection.rs
Normal file
17
sea-orm-codegen/tests/compact_with_schema_name/collection.rs
Normal file
@ -0,0 +1,17 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.10.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)]
|
||||
#[sea_orm(schema_name = "schema_name", table_name = "collection")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub integers: Vec<i32> ,
|
||||
pub integers_opt: Option<Vec<i32> > ,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
60
sea-orm-codegen/tests/expanded/collection.rs
Normal file
60
sea-orm-codegen/tests/expanded/collection.rs
Normal file
@ -0,0 +1,60 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.10.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||
pub struct Entity;
|
||||
|
||||
impl EntityName for Entity {
|
||||
fn table_name(&self) -> &str {
|
||||
"collection"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)]
|
||||
pub struct Model {
|
||||
pub id: i32,
|
||||
pub integers: Vec<i32> ,
|
||||
pub integers_opt: Option<Vec<i32> > ,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
pub enum Column {
|
||||
Id,
|
||||
Integers,
|
||||
IntegersOpt,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||
pub enum PrimaryKey {
|
||||
Id,
|
||||
}
|
||||
|
||||
impl PrimaryKeyTrait for PrimaryKey {
|
||||
type ValueType = i32;
|
||||
fn auto_increment() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ColumnTrait for Column {
|
||||
type EntityName = Entity;
|
||||
fn def(&self) -> ColumnDef {
|
||||
match self {
|
||||
Self::Id => ColumnType::Integer.def(),
|
||||
Self::Integers => ColumnType::Array(sea_orm::sea_query::SeaRc::new(ColumnType::Integer)).def(),
|
||||
Self::IntegersOpt => ColumnType::Array(sea_orm::sea_query::SeaRc::new(ColumnType::Integer)).def().null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RelationTrait for Relation {
|
||||
fn def(&self) -> RelationDef {
|
||||
panic!("No RelationDef")
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
@ -0,0 +1,64 @@
|
||||
//! SeaORM Entity. Generated by sea-orm-codegen 0.10.0
|
||||
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Copy, Clone, Default, Debug, DeriveEntity)]
|
||||
pub struct Entity;
|
||||
|
||||
impl EntityName for Entity {
|
||||
fn schema_name(&self) -> Option< &str > {
|
||||
Some("schema_name")
|
||||
}
|
||||
|
||||
fn table_name(&self) -> &str {
|
||||
"collection"
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)]
|
||||
pub struct Model {
|
||||
pub id: i32,
|
||||
pub integers: Vec<i32> ,
|
||||
pub integers_opt: Option<Vec<i32> > ,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)]
|
||||
pub enum Column {
|
||||
Id,
|
||||
Integers,
|
||||
IntegersOpt,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)]
|
||||
pub enum PrimaryKey {
|
||||
Id,
|
||||
}
|
||||
|
||||
impl PrimaryKeyTrait for PrimaryKey {
|
||||
type ValueType = i32;
|
||||
fn auto_increment() -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ColumnTrait for Column {
|
||||
type EntityName = Entity;
|
||||
fn def(&self) -> ColumnDef {
|
||||
match self {
|
||||
Self::Id => ColumnType::Integer.def(),
|
||||
Self::Integers => ColumnType::Array(sea_orm::sea_query::SeaRc::new(ColumnType::Integer)).def(),
|
||||
Self::IntegersOpt => ColumnType::Array(sea_orm::sea_query::SeaRc::new(ColumnType::Integer)).def().null(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl RelationTrait for Relation {
|
||||
fn def(&self) -> RelationDef {
|
||||
panic!("No RelationDef")
|
||||
}
|
||||
}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
@ -711,4 +711,79 @@ mod tests {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "postgres-array")]
|
||||
#[smol_potat::test]
|
||||
async fn test_postgres_array_1() -> Result<(), DbErr> {
|
||||
mod collection {
|
||||
use crate as sea_orm;
|
||||
use crate::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "collection")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub integers: Vec<i32>,
|
||||
pub integers_opt: Option<Vec<i32>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
}
|
||||
|
||||
let db = MockDatabase::new(DbBackend::Postgres)
|
||||
.append_query_results(vec![vec![
|
||||
collection::Model {
|
||||
id: 1,
|
||||
integers: vec![1, 2, 3],
|
||||
integers_opt: Some(vec![1, 2, 3]),
|
||||
},
|
||||
collection::Model {
|
||||
id: 2,
|
||||
integers: vec![],
|
||||
integers_opt: Some(vec![]),
|
||||
},
|
||||
collection::Model {
|
||||
id: 3,
|
||||
integers: vec![3, 1, 4],
|
||||
integers_opt: None,
|
||||
},
|
||||
]])
|
||||
.into_connection();
|
||||
|
||||
assert_eq!(
|
||||
collection::Entity::find().all(&db).await?,
|
||||
vec![
|
||||
collection::Model {
|
||||
id: 1,
|
||||
integers: vec![1, 2, 3],
|
||||
integers_opt: Some(vec![1, 2, 3]),
|
||||
},
|
||||
collection::Model {
|
||||
id: 2,
|
||||
integers: vec![],
|
||||
integers_opt: Some(vec![]),
|
||||
},
|
||||
collection::Model {
|
||||
id: 3,
|
||||
integers: vec![3, 1, 4],
|
||||
integers_opt: None,
|
||||
},
|
||||
]
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
db.into_transaction_log(),
|
||||
vec![Transaction::from_sql_and_values(
|
||||
DbBackend::Postgres,
|
||||
r#"SELECT "collection"."id", "collection"."integers", "collection"."integers_opt" FROM "collection""#,
|
||||
vec![]
|
||||
),]
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ pub enum ColumnType {
|
||||
/// Variants of enum
|
||||
variants: Vec<DynIden>,
|
||||
},
|
||||
/// Array of a specific data type (PostgreSQL only)
|
||||
Array(SeaRc<ColumnType>),
|
||||
}
|
||||
|
||||
impl PartialEq for ColumnType {
|
||||
@ -398,80 +400,111 @@ impl ColumnDef {
|
||||
}
|
||||
|
||||
impl From<ColumnType> for sea_query::ColumnType {
|
||||
fn from(col: ColumnType) -> Self {
|
||||
match col {
|
||||
ColumnType::Char(s) => sea_query::ColumnType::Char(s),
|
||||
ColumnType::String(s) => sea_query::ColumnType::String(s),
|
||||
ColumnType::Text => sea_query::ColumnType::Text,
|
||||
ColumnType::TinyInteger => sea_query::ColumnType::TinyInteger(None),
|
||||
ColumnType::SmallInteger => sea_query::ColumnType::SmallInteger(None),
|
||||
ColumnType::Integer => sea_query::ColumnType::Integer(None),
|
||||
ColumnType::BigInteger => sea_query::ColumnType::BigInteger(None),
|
||||
ColumnType::TinyUnsigned => sea_query::ColumnType::TinyUnsigned(None),
|
||||
ColumnType::SmallUnsigned => sea_query::ColumnType::SmallUnsigned(None),
|
||||
ColumnType::Unsigned => sea_query::ColumnType::Unsigned(None),
|
||||
ColumnType::BigUnsigned => sea_query::ColumnType::BigUnsigned(None),
|
||||
ColumnType::Float => sea_query::ColumnType::Float(None),
|
||||
ColumnType::Double => sea_query::ColumnType::Double(None),
|
||||
ColumnType::Decimal(s) => sea_query::ColumnType::Decimal(s),
|
||||
ColumnType::DateTime => sea_query::ColumnType::DateTime(None),
|
||||
ColumnType::Timestamp => sea_query::ColumnType::Timestamp(None),
|
||||
ColumnType::TimestampWithTimeZone => sea_query::ColumnType::TimestampWithTimeZone(None),
|
||||
ColumnType::Time => sea_query::ColumnType::Time(None),
|
||||
ColumnType::Date => sea_query::ColumnType::Date,
|
||||
ColumnType::Binary => sea_query::ColumnType::Binary(sea_query::BlobSize::Blob(None)),
|
||||
ColumnType::TinyBinary => sea_query::ColumnType::Binary(sea_query::BlobSize::Tiny),
|
||||
ColumnType::MediumBinary => sea_query::ColumnType::Binary(sea_query::BlobSize::Medium),
|
||||
ColumnType::LongBinary => sea_query::ColumnType::Binary(sea_query::BlobSize::Long),
|
||||
ColumnType::Boolean => sea_query::ColumnType::Boolean,
|
||||
ColumnType::Money(s) => sea_query::ColumnType::Money(s),
|
||||
ColumnType::Json => sea_query::ColumnType::Json,
|
||||
ColumnType::JsonBinary => sea_query::ColumnType::JsonBinary,
|
||||
ColumnType::Custom(s) => {
|
||||
sea_query::ColumnType::Custom(sea_query::SeaRc::new(sea_query::Alias::new(&s)))
|
||||
fn from(column_type: ColumnType) -> Self {
|
||||
fn convert_column_type(column_type: &ColumnType) -> sea_query::ColumnType {
|
||||
match column_type {
|
||||
ColumnType::Char(s) => sea_query::ColumnType::Char(*s),
|
||||
ColumnType::String(s) => sea_query::ColumnType::String(*s),
|
||||
ColumnType::Text => sea_query::ColumnType::Text,
|
||||
ColumnType::TinyInteger => sea_query::ColumnType::TinyInteger(None),
|
||||
ColumnType::SmallInteger => sea_query::ColumnType::SmallInteger(None),
|
||||
ColumnType::Integer => sea_query::ColumnType::Integer(None),
|
||||
ColumnType::BigInteger => sea_query::ColumnType::BigInteger(None),
|
||||
ColumnType::TinyUnsigned => sea_query::ColumnType::TinyUnsigned(None),
|
||||
ColumnType::SmallUnsigned => sea_query::ColumnType::SmallUnsigned(None),
|
||||
ColumnType::Unsigned => sea_query::ColumnType::Unsigned(None),
|
||||
ColumnType::BigUnsigned => sea_query::ColumnType::BigUnsigned(None),
|
||||
ColumnType::Float => sea_query::ColumnType::Float(None),
|
||||
ColumnType::Double => sea_query::ColumnType::Double(None),
|
||||
ColumnType::Decimal(s) => sea_query::ColumnType::Decimal(*s),
|
||||
ColumnType::DateTime => sea_query::ColumnType::DateTime(None),
|
||||
ColumnType::Timestamp => sea_query::ColumnType::Timestamp(None),
|
||||
ColumnType::TimestampWithTimeZone => {
|
||||
sea_query::ColumnType::TimestampWithTimeZone(None)
|
||||
}
|
||||
ColumnType::Time => sea_query::ColumnType::Time(None),
|
||||
ColumnType::Date => sea_query::ColumnType::Date,
|
||||
ColumnType::Binary => {
|
||||
sea_query::ColumnType::Binary(sea_query::BlobSize::Blob(None))
|
||||
}
|
||||
ColumnType::TinyBinary => sea_query::ColumnType::Binary(sea_query::BlobSize::Tiny),
|
||||
ColumnType::MediumBinary => {
|
||||
sea_query::ColumnType::Binary(sea_query::BlobSize::Medium)
|
||||
}
|
||||
ColumnType::LongBinary => sea_query::ColumnType::Binary(sea_query::BlobSize::Long),
|
||||
ColumnType::Boolean => sea_query::ColumnType::Boolean,
|
||||
ColumnType::Money(s) => sea_query::ColumnType::Money(*s),
|
||||
ColumnType::Json => sea_query::ColumnType::Json,
|
||||
ColumnType::JsonBinary => sea_query::ColumnType::JsonBinary,
|
||||
ColumnType::Custom(s) => {
|
||||
sea_query::ColumnType::Custom(sea_query::SeaRc::new(sea_query::Alias::new(s)))
|
||||
}
|
||||
ColumnType::Uuid => sea_query::ColumnType::Uuid,
|
||||
ColumnType::Enum { name, variants } => sea_query::ColumnType::Enum {
|
||||
name: SeaRc::clone(name),
|
||||
variants: variants.clone(),
|
||||
},
|
||||
ColumnType::Array(column_type) => {
|
||||
let column_type = convert_column_type(column_type);
|
||||
sea_query::ColumnType::Array(SeaRc::new(Box::new(column_type)))
|
||||
}
|
||||
}
|
||||
ColumnType::Uuid => sea_query::ColumnType::Uuid,
|
||||
ColumnType::Enum { name, variants } => sea_query::ColumnType::Enum { name, variants },
|
||||
}
|
||||
convert_column_type(&column_type)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<sea_query::ColumnType> for ColumnType {
|
||||
fn from(col_type: sea_query::ColumnType) -> Self {
|
||||
#[allow(unreachable_patterns)]
|
||||
match col_type {
|
||||
sea_query::ColumnType::Char(s) => Self::Char(s),
|
||||
sea_query::ColumnType::String(s) => Self::String(s),
|
||||
sea_query::ColumnType::Text => Self::Text,
|
||||
sea_query::ColumnType::TinyInteger(_) => Self::TinyInteger,
|
||||
sea_query::ColumnType::SmallInteger(_) => Self::SmallInteger,
|
||||
sea_query::ColumnType::Integer(_) => Self::Integer,
|
||||
sea_query::ColumnType::BigInteger(_) => Self::BigInteger,
|
||||
sea_query::ColumnType::TinyUnsigned(_) => Self::TinyUnsigned,
|
||||
sea_query::ColumnType::SmallUnsigned(_) => Self::SmallUnsigned,
|
||||
sea_query::ColumnType::Unsigned(_) => Self::Unsigned,
|
||||
sea_query::ColumnType::BigUnsigned(_) => Self::BigUnsigned,
|
||||
sea_query::ColumnType::Float(_) => Self::Float,
|
||||
sea_query::ColumnType::Double(_) => Self::Double,
|
||||
sea_query::ColumnType::Decimal(s) => Self::Decimal(s),
|
||||
sea_query::ColumnType::DateTime(_) => Self::DateTime,
|
||||
sea_query::ColumnType::Timestamp(_) => Self::Timestamp,
|
||||
sea_query::ColumnType::TimestampWithTimeZone(_) => Self::TimestampWithTimeZone,
|
||||
sea_query::ColumnType::Time(_) => Self::Time,
|
||||
sea_query::ColumnType::Date => Self::Date,
|
||||
sea_query::ColumnType::Binary(sea_query::BlobSize::Blob(_)) => Self::Binary,
|
||||
sea_query::ColumnType::Binary(sea_query::BlobSize::Tiny) => Self::TinyBinary,
|
||||
sea_query::ColumnType::Binary(sea_query::BlobSize::Medium) => Self::MediumBinary,
|
||||
sea_query::ColumnType::Binary(sea_query::BlobSize::Long) => Self::LongBinary,
|
||||
sea_query::ColumnType::Boolean => Self::Boolean,
|
||||
sea_query::ColumnType::Money(s) => Self::Money(s),
|
||||
sea_query::ColumnType::Json => Self::Json,
|
||||
sea_query::ColumnType::JsonBinary => Self::JsonBinary,
|
||||
sea_query::ColumnType::Custom(s) => Self::Custom(s.to_string()),
|
||||
sea_query::ColumnType::Uuid => Self::Uuid,
|
||||
sea_query::ColumnType::Enum { name, variants } => Self::Enum { name, variants },
|
||||
_ => unimplemented!(),
|
||||
fn from(column_type: sea_query::ColumnType) -> Self {
|
||||
#[allow(clippy::redundant_allocation)]
|
||||
fn convert_column_type(column_type: &sea_query::ColumnType) -> ColumnType {
|
||||
#[allow(unreachable_patterns)]
|
||||
match column_type {
|
||||
sea_query::ColumnType::Char(s) => ColumnType::Char(*s),
|
||||
sea_query::ColumnType::String(s) => ColumnType::String(*s),
|
||||
sea_query::ColumnType::Text => ColumnType::Text,
|
||||
sea_query::ColumnType::TinyInteger(_) => ColumnType::TinyInteger,
|
||||
sea_query::ColumnType::SmallInteger(_) => ColumnType::SmallInteger,
|
||||
sea_query::ColumnType::Integer(_) => ColumnType::Integer,
|
||||
sea_query::ColumnType::BigInteger(_) => ColumnType::BigInteger,
|
||||
sea_query::ColumnType::TinyUnsigned(_) => ColumnType::TinyUnsigned,
|
||||
sea_query::ColumnType::SmallUnsigned(_) => ColumnType::SmallUnsigned,
|
||||
sea_query::ColumnType::Unsigned(_) => ColumnType::Unsigned,
|
||||
sea_query::ColumnType::BigUnsigned(_) => ColumnType::BigUnsigned,
|
||||
sea_query::ColumnType::Float(_) => ColumnType::Float,
|
||||
sea_query::ColumnType::Double(_) => ColumnType::Double,
|
||||
sea_query::ColumnType::Decimal(s) => ColumnType::Decimal(*s),
|
||||
sea_query::ColumnType::DateTime(_) => ColumnType::DateTime,
|
||||
sea_query::ColumnType::Timestamp(_) => ColumnType::Timestamp,
|
||||
sea_query::ColumnType::TimestampWithTimeZone(_) => {
|
||||
ColumnType::TimestampWithTimeZone
|
||||
}
|
||||
sea_query::ColumnType::Time(_) => ColumnType::Time,
|
||||
sea_query::ColumnType::Date => ColumnType::Date,
|
||||
sea_query::ColumnType::Binary(sea_query::BlobSize::Blob(_)) => ColumnType::Binary,
|
||||
sea_query::ColumnType::Binary(sea_query::BlobSize::Tiny) => ColumnType::TinyBinary,
|
||||
sea_query::ColumnType::Binary(sea_query::BlobSize::Medium) => {
|
||||
ColumnType::MediumBinary
|
||||
}
|
||||
sea_query::ColumnType::Binary(sea_query::BlobSize::Long) => ColumnType::LongBinary,
|
||||
sea_query::ColumnType::Boolean => ColumnType::Boolean,
|
||||
sea_query::ColumnType::Money(s) => ColumnType::Money(*s),
|
||||
sea_query::ColumnType::Json => ColumnType::Json,
|
||||
sea_query::ColumnType::JsonBinary => ColumnType::JsonBinary,
|
||||
sea_query::ColumnType::Custom(s) => ColumnType::Custom(s.to_string()),
|
||||
sea_query::ColumnType::Uuid => ColumnType::Uuid,
|
||||
sea_query::ColumnType::Enum { name, variants } => ColumnType::Enum {
|
||||
name: SeaRc::clone(name),
|
||||
variants: variants.clone(),
|
||||
},
|
||||
sea_query::ColumnType::Array(column_type) => {
|
||||
let column_type = convert_column_type(column_type);
|
||||
ColumnType::Array(SeaRc::new(column_type))
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
convert_column_type(&column_type)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -444,6 +444,130 @@ impl TryGetable for u32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "postgres-array")]
|
||||
mod postgres_array {
|
||||
use super::*;
|
||||
|
||||
#[allow(unused_macros)]
|
||||
macro_rules! try_getable_postgres_array {
|
||||
( $type: ty ) => {
|
||||
impl TryGetable for Vec<$type> {
|
||||
#[allow(unused_variables)]
|
||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
|
||||
let column = format!("{}{}", pre, col);
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
panic!("{} unsupported by sqlx-mysql", stringify!($type))
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<Vec<$type>>, _>(column.as_str())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or(TryGetError::Null(column)))
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(_) => {
|
||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
TryGetError::Null(column)
|
||||
}),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
try_getable_postgres_array!(bool);
|
||||
try_getable_postgres_array!(i8);
|
||||
try_getable_postgres_array!(i16);
|
||||
try_getable_postgres_array!(i32);
|
||||
try_getable_postgres_array!(i64);
|
||||
try_getable_postgres_array!(f32);
|
||||
try_getable_postgres_array!(f64);
|
||||
try_getable_postgres_array!(String);
|
||||
|
||||
#[cfg(feature = "with-json")]
|
||||
try_getable_postgres_array!(serde_json::Value);
|
||||
|
||||
#[cfg(feature = "with-chrono")]
|
||||
try_getable_postgres_array!(chrono::NaiveDate);
|
||||
|
||||
#[cfg(feature = "with-chrono")]
|
||||
try_getable_postgres_array!(chrono::NaiveTime);
|
||||
|
||||
#[cfg(feature = "with-chrono")]
|
||||
try_getable_postgres_array!(chrono::NaiveDateTime);
|
||||
|
||||
#[cfg(feature = "with-chrono")]
|
||||
try_getable_postgres_array!(chrono::DateTime<chrono::FixedOffset>);
|
||||
|
||||
#[cfg(feature = "with-chrono")]
|
||||
try_getable_postgres_array!(chrono::DateTime<chrono::Utc>);
|
||||
|
||||
#[cfg(feature = "with-chrono")]
|
||||
try_getable_postgres_array!(chrono::DateTime<chrono::Local>);
|
||||
|
||||
#[cfg(feature = "with-time")]
|
||||
try_getable_postgres_array!(time::Date);
|
||||
|
||||
#[cfg(feature = "with-time")]
|
||||
try_getable_postgres_array!(time::Time);
|
||||
|
||||
#[cfg(feature = "with-time")]
|
||||
try_getable_postgres_array!(time::PrimitiveDateTime);
|
||||
|
||||
#[cfg(feature = "with-time")]
|
||||
try_getable_postgres_array!(time::OffsetDateTime);
|
||||
|
||||
#[cfg(feature = "with-rust_decimal")]
|
||||
try_getable_postgres_array!(rust_decimal::Decimal);
|
||||
|
||||
#[cfg(feature = "with-uuid")]
|
||||
try_getable_postgres_array!(uuid::Uuid);
|
||||
|
||||
impl TryGetable for Vec<u32> {
|
||||
#[allow(unused_variables)]
|
||||
fn try_get(res: &QueryResult, pre: &str, col: &str) -> Result<Self, TryGetError> {
|
||||
let column = format!("{}{}", pre, col);
|
||||
match &res.row {
|
||||
#[cfg(feature = "sqlx-mysql")]
|
||||
QueryResultRow::SqlxMySql(row) => {
|
||||
panic!("{} unsupported by sqlx-mysql", stringify!($type))
|
||||
}
|
||||
#[cfg(feature = "sqlx-postgres")]
|
||||
QueryResultRow::SqlxPostgres(row) => {
|
||||
use sqlx::postgres::types::Oid;
|
||||
// Since 0.6.0, SQLx has dropped direct mapping from PostgreSQL's OID to Rust's `u32`;
|
||||
// Instead, `u32` was wrapped by a `sqlx::Oid`.
|
||||
use sqlx::Row;
|
||||
row.try_get::<Option<Vec<Oid>>, _>(column.as_str())
|
||||
.map_err(|e| TryGetError::DbErr(crate::sqlx_error_to_query_err(e)))
|
||||
.and_then(|opt| opt.ok_or(TryGetError::Null(column)))
|
||||
.map(|oids| oids.into_iter().map(|oid| oid.0).collect())
|
||||
}
|
||||
#[cfg(feature = "sqlx-sqlite")]
|
||||
QueryResultRow::SqlxSqlite(_) => {
|
||||
panic!("{} unsupported by sqlx-sqlite", stringify!($type))
|
||||
}
|
||||
#[cfg(feature = "mock")]
|
||||
QueryResultRow::Mock(row) => row.try_get(column.as_str()).map_err(|e| {
|
||||
debug_print!("{:#?}", e.to_string());
|
||||
TryGetError::Null(column)
|
||||
}),
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TryGetableMany //
|
||||
|
||||
/// Perform a query on multiple columns
|
||||
|
@ -41,7 +41,7 @@ pub async fn create_and_update(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
};
|
||||
|
||||
let update_res = Entity::update(updated_active_model.clone())
|
||||
.filter(Column::Id.eq(vec![1, 2, 4]))
|
||||
.filter(Column::Id.eq(vec![1_u8, 2_u8, 4_u8])) // annotate it as Vec<u8> explicitly
|
||||
.exec(db)
|
||||
.await;
|
||||
|
||||
@ -53,7 +53,7 @@ pub async fn create_and_update(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
);
|
||||
|
||||
let update_res = Entity::update(updated_active_model)
|
||||
.filter(Column::Id.eq(vec![1, 2, 3]))
|
||||
.filter(Column::Id.eq(vec![1_u8, 2_u8, 3_u8])) // annotate it as Vec<u8> explicitly
|
||||
.exec(db)
|
||||
.await?;
|
||||
|
||||
@ -67,7 +67,7 @@ pub async fn create_and_update(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
|
||||
assert_eq!(
|
||||
Entity::find()
|
||||
.filter(Column::Id.eq(vec![1, 2, 3]))
|
||||
.filter(Column::Id.eq(vec![1_u8, 2_u8, 3_u8])) // annotate it as Vec<u8> explicitly
|
||||
.one(db)
|
||||
.await?,
|
||||
Some(Model {
|
||||
|
95
tests/collection_tests.rs
Normal file
95
tests/collection_tests.rs
Normal file
@ -0,0 +1,95 @@
|
||||
pub mod common;
|
||||
|
||||
pub use common::{features::*, setup::*, TestContext};
|
||||
use pretty_assertions::assert_eq;
|
||||
use sea_orm::{entity::prelude::*, entity::*, DatabaseConnection};
|
||||
|
||||
#[sea_orm_macros::test]
|
||||
#[cfg(all(feature = "sqlx-postgres", feature = "postgres-array"))]
|
||||
async fn main() -> Result<(), DbErr> {
|
||||
let ctx = TestContext::new("collection_tests").await;
|
||||
create_tables(&ctx.db).await?;
|
||||
insert_collection(&ctx.db).await?;
|
||||
update_collection(&ctx.db).await?;
|
||||
ctx.delete().await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn insert_collection(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
use collection::*;
|
||||
|
||||
assert_eq!(
|
||||
Model {
|
||||
id: 1,
|
||||
integers: vec![1, 2, 3],
|
||||
integers_opt: Some(vec![1, 2, 3]),
|
||||
}
|
||||
.into_active_model()
|
||||
.insert(db)
|
||||
.await?,
|
||||
Model {
|
||||
id: 1,
|
||||
integers: vec![1, 2, 3],
|
||||
integers_opt: Some(vec![1, 2, 3]),
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Model {
|
||||
id: 2,
|
||||
integers: vec![10, 9],
|
||||
integers_opt: None,
|
||||
}
|
||||
.into_active_model()
|
||||
.insert(db)
|
||||
.await?,
|
||||
Model {
|
||||
id: 2,
|
||||
integers: vec![10, 9],
|
||||
integers_opt: None,
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Model {
|
||||
id: 3,
|
||||
integers: vec![],
|
||||
integers_opt: Some(vec![]),
|
||||
}
|
||||
.into_active_model()
|
||||
.insert(db)
|
||||
.await?,
|
||||
Model {
|
||||
id: 3,
|
||||
integers: vec![],
|
||||
integers_opt: Some(vec![]),
|
||||
}
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_collection(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
use collection::*;
|
||||
|
||||
let model = Entity::find_by_id(1).one(db).await?.unwrap();
|
||||
|
||||
ActiveModel {
|
||||
integers: Set(vec![4, 5, 6]),
|
||||
integers_opt: Set(Some(vec![4, 5, 6])),
|
||||
..model.into_active_model()
|
||||
}
|
||||
.update(db)
|
||||
.await?;
|
||||
|
||||
ActiveModel {
|
||||
id: Unchanged(3),
|
||||
integers: Set(vec![3, 1, 4]),
|
||||
integers_opt: Set(None),
|
||||
}
|
||||
.update(db)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
15
tests/common/features/collection.rs
Normal file
15
tests/common/features/collection.rs
Normal file
@ -0,0 +1,15 @@
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "collection")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub integers: Vec<i32>,
|
||||
pub integers_opt: Option<Vec<i32>>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
||||
pub enum Relation {}
|
||||
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
@ -2,6 +2,7 @@ pub mod active_enum;
|
||||
pub mod active_enum_child;
|
||||
pub mod applog;
|
||||
pub mod byte_primary_key;
|
||||
pub mod collection;
|
||||
pub mod custom_active_model;
|
||||
pub mod insert_default;
|
||||
pub mod json_struct;
|
||||
@ -18,6 +19,7 @@ pub use active_enum::Entity as ActiveEnum;
|
||||
pub use active_enum_child::Entity as ActiveEnumChild;
|
||||
pub use applog::Entity as Applog;
|
||||
pub use byte_primary_key::Entity as BytePrimaryKey;
|
||||
pub use collection::Entity as Collection;
|
||||
pub use insert_default::Entity as InsertDefault;
|
||||
pub use json_struct::Entity as JsonStruct;
|
||||
pub use json_vec::Entity as JsonVec;
|
||||
|
@ -42,6 +42,10 @@ pub async fn create_tables(db: &DatabaseConnection) -> Result<(), DbErr> {
|
||||
create_active_enum_child_table(db).await?;
|
||||
create_insert_default_table(db).await?;
|
||||
|
||||
if DbBackend::Postgres == db_backend {
|
||||
create_collection_table(db).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -326,3 +330,27 @@ pub async fn create_json_struct_table(db: &DbConn) -> Result<ExecResult, DbErr>
|
||||
|
||||
create_table(db, &stmt, JsonStruct).await
|
||||
}
|
||||
|
||||
pub async fn create_collection_table(db: &DbConn) -> Result<ExecResult, DbErr> {
|
||||
let stmt = sea_query::Table::create()
|
||||
.table(collection::Entity)
|
||||
.col(
|
||||
ColumnDef::new(collection::Column::Id)
|
||||
.integer()
|
||||
.not_null()
|
||||
.auto_increment()
|
||||
.primary_key(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(collection::Column::Integers)
|
||||
.array(sea_query::ColumnType::Integer(None))
|
||||
.not_null(),
|
||||
)
|
||||
.col(
|
||||
ColumnDef::new(collection::Column::IntegersOpt)
|
||||
.array(sea_query::ColumnType::Integer(None)),
|
||||
)
|
||||
.to_owned();
|
||||
|
||||
create_table(db, &stmt, Collection).await
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user