Skip to content

Commit

Permalink
issues-336 Third try impl postgres-array
Browse files Browse the repository at this point in the history
  • Loading branch information
ikrivosheev committed Oct 10, 2022
1 parent 847c602 commit ac92bd1
Show file tree
Hide file tree
Showing 9 changed files with 445 additions and 71 deletions.
29 changes: 20 additions & 9 deletions sea-query-binder/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,33 @@ rust-version = "1.60"
[lib]

[dependencies]
sea-query = { version = "^0", path = ".." }
sea-query = { version = "^0", path = "..", features = ["thread-safe"] }
sqlx = { version = "^0.6", optional = true }
serde_json = { version = "^1", optional = true }
chrono = { version = "^0.4", default-features = false, features = ["clock"], optional = true }
postgres-types = { version = "^0", optional = true }
rust_decimal = { version = "^1", optional = true }
bigdecimal = { version = "^0.3", optional = true }
uuid = { version = "^1", optional = true }
proc-macro2 = { version = "1", optional = true }
quote = { version = "^1", optional = true }
time = { version = "^0.3", optional = true, features = ["macros", "formatting"] }
ipnetwork = { version = "^0.19", optional = true }
mac_address = { version = "^1.1", optional = true }

[features]
sqlx-mysql = ["sqlx/mysql"]
sqlx-postgres = ["sqlx/postgres"]
sqlx-sqlite = ["sqlx/sqlite"]
sqlx-any = ["sqlx/any"]
with-chrono = ["sqlx/chrono", "sea-query/with-chrono"]
with-json = ["sqlx/json", "sea-query/with-json"]
with-rust_decimal = ["sqlx/decimal", "sea-query/with-rust_decimal"]
with-bigdecimal = ["sqlx/bigdecimal", "sea-query/with-bigdecimal"]
with-uuid = ["sqlx/uuid", "sea-query/with-uuid"]
with-time = ["sqlx/time", "sea-query/with-time"]
with-ipnetwork = ["sqlx/ipnetwork", "sea-query/with-ipnetwork"]
with-mac_address = ["sqlx/mac_address", "sea-query/with-mac_address"]
with-chrono = ["sqlx/chrono", "sea-query/with-chrono", "chrono"]
with-json = ["sqlx/json", "sea-query/with-json", "serde_json"]
with-rust_decimal = ["sqlx/decimal", "sea-query/with-rust_decimal", "rust_decimal"]
with-bigdecimal = ["sqlx/bigdecimal", "sea-query/with-bigdecimal", "bigdecimal"]
with-uuid = ["sqlx/uuid", "sea-query/with-uuid", "uuid"]
with-time = ["sqlx/time", "sea-query/with-time", "time"]
with-ipnetwork = ["sqlx/ipnetwork", "sea-query/with-ipnetwork", "ipnetwork"]
with-mac_address = ["sqlx/mac_address", "sea-query/with-mac_address", "mac_address"]
postgres-array = ["sea-query/postgres-array"]
runtime-async-std-native-tls = ["sqlx/runtime-async-std-native-tls"]
runtime-async-std-rustls = ["sqlx/runtime-async-std-rustls", ]
Expand Down
219 changes: 212 additions & 7 deletions sea-query-binder/src/sqlx_postgres.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
use sea_query::{ArrayType, Value};

#[cfg(feature = "with-json")]
use serde_json::Value as Json;
#[cfg(feature = "with-json")]
use std::str::from_utf8;

#[cfg(feature = "with-chrono")]
use chrono::{DateTime, FixedOffset, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc};

#[cfg(feature = "with-rust_decimal")]
use rust_decimal::Decimal;

#[cfg(feature = "with-bigdecimal")]
use bigdecimal::BigDecimal;

#[cfg(feature = "with-uuid")]
use uuid::Uuid;

#[cfg(feature = "with-ipnetwork")]
use ipnetwork::IpNetwork;

#[cfg(feature = "with-ipnetwork")]
use std::net::IpAddr;

#[cfg(feature = "with-mac_address")]
use mac_address::MacAddress;

use crate::SqlxValues;
use sea_query::Value;

impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues {
fn into_arguments(self) -> sqlx::postgres::PgArguments {
Expand All @@ -26,13 +53,13 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues {
args.add(i.map(|i| i as i16));
}
Value::SmallUnsigned(i) => {
args.add(i.map(|i| i as i32));
args.add(i.map(|i| i as i16));
}
Value::Unsigned(i) => {
args.add(i.map(|i| i as i64));
}
Value::BigUnsigned(i) => {
args.add(i.map(|i| <i64 as std::convert::TryFrom<u64>>::try_from(i).unwrap()));
args.add(i.map(|i| <i64 as TryFrom<u64>>::try_from(i).unwrap()));
}
Value::Float(f) => {
args.add(f);
Expand Down Expand Up @@ -105,10 +132,6 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues {
Value::Json(j) => {
args.add(j.as_deref());
}
#[cfg(feature = "postgres-array")]
Value::Array(_) => {
panic!("SeaQuery doesn't support array arguments for Postgresql");
}
#[cfg(feature = "with-ipnetwork")]
Value::IpNetwork(ip) => {
args.add(ip.as_deref());
Expand All @@ -117,6 +140,188 @@ impl<'q> sqlx::IntoArguments<'q, sqlx::postgres::Postgres> for SqlxValues {
Value::MacAddress(mac) => {
args.add(mac.as_deref());
}
#[cfg(feature = "postgres-array")]
Value::Array(ty, v) => match ty {
ArrayType::Bool => {
let value: Option<Vec<bool>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value)
}
ArrayType::TinyInt => {
let value: Option<Vec<i8>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value)
}
ArrayType::SmallInt => {
let value: Option<Vec<i16>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value)
}
ArrayType::Int => {
let value: Option<Vec<i32>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value)
}
ArrayType::BigInt => {
let value: Option<Vec<i64>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value)
}
ArrayType::TinyUnsigned => {
let value: Option<Vec<u8>> =
Value::Array(ty, v).except("Invalid type for array value");
let value: Option<Vec<i16>> =
value.map(|vec| vec.into_iter().map(|i| i as i16).collect());
args.add(value)
}
ArrayType::SmallUnsigned => {
let value: Option<Vec<u16>> =
Value::Array(ty, v).except("Invalid type for array value");
let value: Option<Vec<i32>> =
value.map(|vec| vec.into_iter().map(|i| i as i32).collect());
args.add(value)
}
ArrayType::Unsigned => {
let value: Option<Vec<u32>> =
Value::Array(ty, v).except("Invalid type for array value");
let value: Option<Vec<i64>> =
value.map(|vec| vec.into_iter().map(|i| i as i64).collect());
args.add(value)
}
ArrayType::BigUnsigned => {
let value: Option<Vec<u64>> =
Value::Array(ty, v).except("Invalid type for array value");
let value: Option<Vec<i64>> = value.map(|vec| {
vec.into_iter()
.map(|i| <i64 as TryFrom<u64>>::try_from(i).unwrap())
.collect()
});
args.add(value)
}
ArrayType::Float => {
let value: Option<Vec<f32>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value)
}
ArrayType::Double => {
let value: Option<Vec<f64>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value)
}
ArrayType::String => {
let value: Option<Vec<String>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value)
}
ArrayType::Char => {
let value: Option<Vec<char>> =
Value::Array(ty, v).except("Invalid type for array value");
let value: Option<Vec<String>> =
value.map(|vec| vec.into_iter().map(|c| c.to_string()).collect());
args.add(value)
}
ArrayType::Bytes => {
let value: Option<Vec<Vec<u8>>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value)
}
#[cfg(feature = "with-chrono")]
ArrayType::ChronoDate => {
let value: Option<Vec<NaiveDate>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-chrono")]
ArrayType::ChronoTime => {
let value: Option<Vec<NaiveTime>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-chrono")]
ArrayType::ChronoDateTime => {
let value: Option<Vec<NaiveDateTime>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-chrono")]
ArrayType::ChronoDateTimeUtc => {
let value: Option<Vec<DateTime<Utc>>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-chrono")]
ArrayType::ChronoDateTimeLocal => {
let value: Option<Vec<DateTime<Local>>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-chrono")]
ArrayType::ChronoDateTimeWithTimeZone => {
let value: Option<Vec<DateTime<Local>>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-time")]
ArrayType::TimeDate => {
let value: Option<Vec<time::Date>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-time")]
ArrayType::TimeTime => {
let value: Option<Vec<time::Time>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-time")]
ArrayType::TimeDateTime => {
let value: Option<Vec<time::PrimitiveDateTime>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-time")]
ArrayType::TimeDateTimeWithTimeZone => {
let value: Option<Vec<time::OffsetDateTime>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-uuid")]
ArrayType::Uuid => {
let value: Option<Vec<Uuid>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-rust_decimal")]
ArrayType::Decimal => {
let value: Option<Vec<Decimal>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-bigdecimal")]
ArrayType::BigDecimal => {
let value: Option<Vec<BigDecimal>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-json")]
ArrayType::Json => {
let value: Option<Vec<Json>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-ipnetwork")]
ArrayType::IpNetwork => {
let value: Option<Vec<IpNetwork>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
#[cfg(feature = "with-mac_address")]
ArrayType::MacAddress => {
let value: Option<Vec<MacAddress>> =
Value::Array(ty, v).except("Invalid type for array value");
args.add(value);
}
},
}
}
args
Expand Down
2 changes: 1 addition & 1 deletion sea-query-postgres/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ rust-version = "1.60"
[lib]

[dependencies]
sea-query = { version = "^0", path = ".." }
sea-query = { version = "^0", path = "..", feature = ["thread-safe"] }
postgres-types = { version = "^0.2" }
bytes = { version = "^1" }
rust_decimal = { version = "^1", optional = true }
Expand Down
6 changes: 5 additions & 1 deletion src/backend/postgres/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ impl TableBuilder for PostgresQueryBuilder {
ColumnType::Json => "json".into(),
ColumnType::JsonBinary => "jsonb".into(),
ColumnType::Uuid => "uuid".into(),
ColumnType::Array(elem_type) => format!("{}[]", elem_type.as_ref().unwrap()),
ColumnType::Array(elem_type) => {
let mut sql = String::new();
self.prepare_column_type(elem_type, &mut sql);
format!("{}[]", sql)
}
ColumnType::Custom(iden) => iden.to_string(),
ColumnType::Enum { name, .. } => name.to_string(),
ColumnType::Cidr => "cidr".into(),
Expand Down
4 changes: 2 additions & 2 deletions src/backend/query_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder {
#[cfg(feature = "with-mac_address")]
Value::MacAddress(None) => write!(s, "NULL").unwrap(),
#[cfg(feature = "postgres-array")]
Value::Array(None) => write!(s, "NULL").unwrap(),
Value::Array(_, None) => write!(s, "NULL").unwrap(),
Value::Bool(Some(b)) => write!(s, "{}", if *b { "TRUE" } else { "FALSE" }).unwrap(),
Value::TinyInt(Some(v)) => write!(s, "{}", v).unwrap(),
Value::SmallInt(Some(v)) => write!(s, "{}", v).unwrap(),
Expand Down Expand Up @@ -1036,7 +1036,7 @@ pub trait QueryBuilder: QuotedBuilder + EscapeBuilder + TableRefBuilder {
#[cfg(feature = "with-uuid")]
Value::Uuid(Some(v)) => write!(s, "'{}'", v).unwrap(),
#[cfg(feature = "postgres-array")]
Value::Array(Some(v)) => write!(
Value::Array(_, Some(v)) => write!(
s,
"'{{{}}}'",
v.iter()
Expand Down
5 changes: 1 addition & 4 deletions src/query/insert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,7 @@ impl InsertStatement {
where
I: IntoIterator<Item = SimpleExpr>,
{
let values = values
.into_iter()
.map(|v| v.into())
.collect::<Vec<SimpleExpr>>();
let values = values.into_iter().collect::<Vec<SimpleExpr>>();
if self.columns.len() != values.len() {
return Err(Error::ColValNumMismatch {
col_len: self.columns.len(),
Expand Down
8 changes: 4 additions & 4 deletions src/table/column.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub enum ColumnType {
name: DynIden,
variants: Vec<DynIden>,
},
Array(Option<String>),
Array(SeaRc<Box<ColumnType>>),
Cidr,
Inet,
MacAddr,
Expand All @@ -64,7 +64,7 @@ pub enum ColumnSpec {
}

// All interval fields
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum PgInterval {
Year,
Month,
Expand Down Expand Up @@ -524,8 +524,8 @@ impl ColumnDef {

/// Set column type as an array with a specified element type.
/// This is only supported on Postgres.
pub fn array(&mut self, elem_type: String) -> &mut Self {
self.types = Some(ColumnType::Array(Some(elem_type)));
pub fn array(&mut self, elem_type: ColumnType) -> &mut Self {
self.types = Some(ColumnType::Array(SeaRc::new(Box::new(elem_type))));
self
}

Expand Down
6 changes: 3 additions & 3 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ macro_rules! iden_trait {
}

fn to_string(&self) -> String {
let s = &mut String::new();
self.unquoted(s);
s.to_owned()
let mut s = String::new();
self.unquoted(&mut s);
s
}

fn unquoted(&self, s: &mut dyn fmt::Write);
Expand Down
Loading

0 comments on commit ac92bd1

Please sign in to comment.