From 49747384ba5ad5b93197cb894dcaf0be33ff34e8 Mon Sep 17 00:00:00 2001 From: songww Date: Thu, 26 Aug 2021 22:36:13 +0800 Subject: [PATCH 1/5] Value refactoring to respect sqlx::types. --- .vim/coc-settings.json | 5 +- Cargo.toml | 28 +- build.rs | 130 +++++++++ src/ast/expression.rs | 14 +- src/ast/mod.rs | 3 - src/ast/values.rs | 549 +++++++++++++++++++++------------------ src/databases/mod.rs | 65 +++-- src/error.rs | 6 +- src/lib.rs | 3 +- src/visitors/mssql.rs | 87 +------ src/visitors/mysql.rs | 96 ++++--- src/visitors/postgres.rs | 111 ++++---- src/visitors/sqlite.rs | 99 ++++--- 13 files changed, 643 insertions(+), 553 deletions(-) create mode 100644 build.rs diff --git a/.vim/coc-settings.json b/.vim/coc-settings.json index 2fabdbe..b242330 100644 --- a/.vim/coc-settings.json +++ b/.vim/coc-settings.json @@ -34,10 +34,7 @@ "rust-analyzer.updates.channel": "nightly", "rust-analyzer.cargo.autoreload": true, "rust-analyzer.cargo.features": [ - "doc", - "mssql", - "mysql", - "sqlite", + "docs", "postgres", "json", "uuid", diff --git a/Cargo.toml b/Cargo.toml index 0afaa34..93ce4ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,32 +19,36 @@ name = "xiayu" [dependencies] xiayu-derive = { version = "0.1.0-alpha0", path = "./derive" } -thiserror = "1.0" -tracing = "0.1" -hex = "0.4" -sqlx = { version = "0.5" } -either = { version = "1.6", optional = true } -serde_json = { version = "1.0", optional = true } +async-trait = "0.1" base64 = { version = "0.13", optional = true } -num-bigint = { version = "0.4", optional = true } derive_more = { version = "0.99", features = ["as_ref", "as_mut", "deref", "deref_mut"] } +either = { version = "1.6", optional = true } +hex = "0.4" indoc = { version = "1.0", optional = true } -num = { version = "0.4.0", optional = true } -async-trait = "0.1.51" +num = { version = "0.4", optional = true } +num-bigint = { version = "0.4", optional = true } +thiserror = "1.0" +tracing = "0.1" +serde = { version = "1.0", optional = true } +serde_json = { version = "1.0", optional = true } +sqlx = { version = "0.5" } [dev-dependencies] -tokio = { version = "1.10", features = ["rt"] } +tokio = { version = "1.10", features = [ "rt", "macros" ] } entities = { path = "./entity-examples", package = "xiayu-entity-examples" } +[build-dependencies] +cfg_aliases = "0.1" + [features] default = [ "sqlite", "mysql" ] -docs = [ "sqlx/runtime-tokio-rustls" ] +docs = [ "sqlx/runtime-tokio-rustls", "sqlx/mysql", "sqlx/mssql", "sqlx/sqlite", "sqlx/postgres", "json", "uuid", "chrono", "decimal", "bigdecimal" ] mssql = [ "uuid", "chrono", "either", "sqlx/mssql", "indoc" ] mysql = [ "sqlx/mysql" ] sqlite = [ "sqlx/sqlite" ] postgres = [ "sqlx/postgres" ] uuid = [ "sqlx/uuid" ] -json = [ "base64", "sqlx/json", "serde_json", "num/serde" ] +json = [ "base64", "sqlx/json", "serde", "serde_json", "num/serde" ] chrono = [ "sqlx/chrono" ] decimal = [ "sqlx/decimal" ] bigdecimal = [ "num", "num-bigint", "sqlx/bigdecimal" ] diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..d221b60 --- /dev/null +++ b/build.rs @@ -0,0 +1,130 @@ +use cfg_aliases::cfg_aliases; + +fn main() { + // Setup cfg aliases + cfg_aliases! { + docs: { feature = "docs" }, + }; + if cfg!(feature = "docs") { + cfg_aliases! { + only_mysql: { docs }, + only_postgres: { docs }, + only_sqlite: { docs }, + only_mssql: { docs }, + not_mssql: { docs }, + mysql_or_sqlite: { docs }, + json: { docs }, + uuid: { docs }, + bigdecimal: { docs }, + decimal: { docs }, + chrono: { docs }, + time: { docs }, + ipnetwork: { docs }, + } + } else { + cfg_aliases! { + only_mysql: { all( + feature = "mysql", + not(any(feature = "mssql", feature = "sqlite", feature = "postgres")) + ) }, + only_postgres: { all( + feature = "postgres", + not(any(feature = "mysql", feature = "mssql", feature = "sqlite")) + ) }, + only_sqlite: { all( + feature = "sqlite", + not(any(feature = "mysql", feature = "mssql", feature = "postgres")) + ) }, + only_mssql: { all( + feature = "mssql", + not(any(feature = "mysql", feature = "sqlite", feature = "postgres")) + ) }, + not_mssql: { all( + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") + ) }, + mysql_or_sqlite: { all( + any(feature = "mysql", feature = "sqlite"), + not(any(feature = "mssql", feature = "postgres")) + ) }, + json: { all( + feature = "json", + any(feature = "postgres", feature = "mysql", feature = "sqlite"), + not(feature = "mssql") + ) }, + uuid: { all( + feature = "uuid", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") + ) }, + bigdecimal: { all( + feature = "bigdecimal", + any(feature = "mysql", feature = "postgres"), + not(any(feature = "sqlite", feature = "mssql")) + ) }, + decimal: { all( + feature = "decimal", + any(feature = "mysql", feature = "postgres"), + not(any(feature = "sqlite", feature = "mssql")) + ) }, + chrono: { all( + feature = "chrono", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") + ) }, + time: { all( + feature = "time", + not(any(feature = "mssql", feature = "sqlite")) + ) }, + ipnetwork: { all( + feature = "ipnetwork", + only_postgres + ) }, + } + + if cfg!(all(feature = "json", not(json))) { + println!("cargo:warning=feature `json` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); + } + + if cfg!(all(feature = "uuid", not(uuid))) { + println!("cargo:warning=feature `uuid` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); + } + + if cfg!(all(feature = "bigdecimal", not(bigdecimal))) { + println!("cargo:warning=feature `bigdecimal` still disabled, because it only support for `MySQL` and `PostgreSQL`, but `{}` is enabled .", + if cfg!(feature = "mssql") { + "MSSQL" + } else if cfg!(feature = "sqlite") { + "Sqlite" + } else { + unreachable!() + } + ); + } + + if cfg!(all(feature = "decimal", not(decimal))) { + println!("cargo:warning=feature `decimal` still disabled, because it only support for `MySQL` and `PostgreSQL`, but `{}` is enabled .", + if cfg!(feature = "mssql") { + "MSSQL" + } else if cfg!(feature = "sqlite") { + "Sqlite" + } else { + unreachable!() + } + ); + } + + if cfg!(all(feature = "chrono", not(chrono))) { + println!("cargo:warning=feature `chrono` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); + } + + /* + if cfg!(all(feature = "time", not(time))) { + println!("cargo:warning=feature `chrono` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); + } + */ + if cfg!(all(feature = "ipnetwork", not(ipnetwork))) { + println!("cargo:warning=feature `ipnetwork` still disabled, because it only support for `PostgreSQL`, but `MySQL` `Sqlite` or `MSSQL` is enabled ."); + } + } +} diff --git a/src/ast/expression.rs b/src/ast/expression.rs index 025baa0..3198fb5 100644 --- a/src/ast/expression.rs +++ b/src/ast/expression.rs @@ -46,21 +46,21 @@ impl<'a> Expression<'a> { #[allow(dead_code)] pub(crate) fn is_json_value(&self) -> bool { match &self.kind { - #[cfg(feature = "json")] + #[cfg(json)] ExpressionKind::Parameterized(Value::Json(_)) => true, - #[cfg(feature = "json")] + #[cfg(json)] ExpressionKind::Value(expr) => expr.is_json_value(), _ => false, } } #[allow(dead_code)] - #[cfg(feature = "json")] + #[cfg(json)] pub(crate) fn into_json_value(self) -> Option { match self.kind { - #[cfg(feature = "json")] + #[cfg(json)] ExpressionKind::Parameterized(Value::Json(Json::JsonValue(json_val))) => json_val, - #[cfg(feature = "json")] + #[cfg(json)] ExpressionKind::Value(expr) => expr.into_json_value(), _ => None, } @@ -78,10 +78,12 @@ impl<'a> Expression<'a> { } } + /* #[allow(dead_code)] pub(crate) fn is_xml_value(&self) -> bool { self.kind.is_xml_value() } + */ #[allow(dead_code)] pub fn is_asterisk(&self) -> bool { @@ -212,6 +214,7 @@ pub enum ExpressionKind<'a> { } impl<'a> ExpressionKind<'a> { + /* pub(crate) fn is_xml_value(&self) -> bool { match self { Self::Parameterized(Value::Xml(_)) => true, @@ -219,6 +222,7 @@ impl<'a> ExpressionKind<'a> { _ => false, } } + */ } /// A quick alias to create an asterisk to a table. diff --git a/src/ast/mod.rs b/src/ast/mod.rs index ccc8363..f02134f 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -51,7 +51,4 @@ pub use select::Select; pub use table::*; pub use union::Union; pub use update::*; -#[cfg(feature = "json")] -pub use values::Json; -pub(crate) use values::Params; pub use values::{IntoRaw, Raw, Value, Values}; diff --git a/src/ast/values.rs b/src/ast/values.rs index db062ff..11a9421 100644 --- a/src/ast/values.rs +++ b/src/ast/values.rs @@ -1,17 +1,17 @@ use std::any::Any; use std::borrow::{Borrow, Cow}; use std::convert::TryFrom; -use std::fmt; +use std::{fmt, option}; -#[cfg(feature = "bigdecimal")] +#[cfg(bigdecimal)] use num::{FromPrimitive, ToPrimitive}; -#[cfg(feature = "json")] +#[cfg(json)] use serde_json::{Number, Value as JsonValue}; -#[cfg(feature = "chrono")] +#[cfg(chrono)] use sqlx::types::chrono::{self, DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; -#[cfg(feature = "bigdecimal")] +#[cfg(bigdecimal)] use sqlx::types::BigDecimal; -#[cfg(feature = "uuid")] +#[cfg(uuid)] use sqlx::types::Uuid; use crate::ast::*; @@ -55,6 +55,7 @@ trait PgCompatibleType: // } // } +/* #[cfg(feature = "json")] #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] #[derive(Debug, Clone, PartialEq)] @@ -62,72 +63,102 @@ pub enum Json<'a> { // #[cfg(feature = "postgres")] // #[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] // Json(Option>>>), + #[cfg(any(feature = "mysql", feature = "postgres"))] JsonValue(Option), + #[cfg(feature = "postgres")] JsonRawValue(Option>), } -#[cfg(feature = "json")] +#[cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))] +#[cfg_attr( + feature = "docs", + doc(cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))) +)] impl<'a> From for Json<'a> { fn from(v: JsonValue) -> Self { Json::JsonValue(Some(v)) } } -#[cfg(feature = "json")] +#[cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))] +#[cfg_attr( + feature = "docs", + doc(cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))) +)] impl<'a> From> for Json<'a> { fn from(v: Option) -> Self { Json::JsonValue(v) } } -#[cfg(feature = "json")] +#[cfg(all(feature = "json", feature = "postgres"))] +#[cfg_attr( + feature = "docs", + doc(cfg(all(feature = "json", feature = "postgres"))) +)] impl<'a> From>> for Json<'a> { fn from(v: Option>) -> Self { Json::JsonRawValue(v) } } -#[cfg(feature = "json")] +#[cfg(all(feature = "json", feature = "postgres"))] +#[cfg_attr( + feature = "docs", + doc(cfg(all(feature = "json", feature = "postgres"))) +)] impl<'a> From> for Json<'a> { fn from(v: JsonRawValue<'a>) -> Self { Json::JsonRawValue(Some(v)) } } -#[cfg(feature = "json")] +#[cfg(all(feature = "json", feature = "postgres"))] +#[cfg_attr( + feature = "docs", + doc(cfg(all(feature = "json", feature = "postgres"))) +)] impl<'a> From<&'a serde_json::value::RawValue> for JsonRawValue<'a> { fn from(v: &'a serde_json::value::RawValue) -> Self { JsonRawValue(v) } } -#[cfg(feature = "json")] +#[cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))] +#[cfg_attr( + feature = "docs", + doc(cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))) +)] impl<'a> Json<'a> { pub const fn is_none(&self) -> bool { match self { Json::JsonValue(v) => v.is_none(), + #[cfg(feature = "postgres")] Json::JsonRawValue(v) => v.is_none(), } } } +#[cfg(feature = "json")] #[derive(Debug, Clone, AsRef, Deref)] pub struct JsonRawValue<'a>(&'a serde_json::value::RawValue); +#[cfg(feature = "json")] impl<'a> PartialEq for JsonRawValue<'a> { fn eq(&self, other: &Self) -> bool { self.0.get() == other.0.get() } } +*/ + +#[cfg(json)] +pub type Json = sqlx::types::Json; /// A value we must parameterize for the prepared statement. Null values should be /// defined by their corresponding type variants with a `None` value for best /// compatibility. #[derive(Debug, Clone, PartialEq)] pub enum Value<'a> { - /// 64-bit signed integer. - Integer(Option), - I8(Option), I16(Option), I32(Option), @@ -139,10 +170,34 @@ pub enum Value<'a> { /// String value. Text(Option>), /// Bytes value. + #[cfg(not_mssql)] + #[cfg_attr(docs, doc(cfg(not_mssql)))] Bytes(Option>), /// Boolean value. Boolean(Option), + #[cfg(all( + any(feature = "mysql", feature = "sqlite"), + not(any(feature = "mssql", feature = "postgres")) + ))] + #[cfg_attr( + docs, + doc(cfg(all( + any(feature = "mysql", feature = "sqlite"), + not(any(feature = "mssql", feature = "postgres")) + ))) + )] + U8(Option), + #[cfg(mysql_or_sqlite)] + #[cfg_attr(feature = "docs", doc(cfg(mysql_or_sqlite)))] + U16(Option), + #[cfg(not_mssql)] + #[cfg_attr(feature = "docs", doc(cfg(not_mssql)))] + U32(Option), + #[cfg(only_mysql)] + #[cfg_attr(feature = "docs", doc(cfg(only_mysql)))] + U64(Option), + /* /// Database enum value. Enum(Option>), /// A single character. @@ -151,90 +206,73 @@ pub enum Value<'a> { /// An array value (PostgreSQL). Array(Option>>), /// A numeric value. - #[cfg(feature = "bigdecimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] - Numeric(Option), /// A XML value. Xml(Option>), - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] - /// A datetime value. - DateTime(Option>), - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] - /// A date value. - Date(Option), - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] - /// A time value. - Time(Option), - - #[cfg(feature = "json")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] + */ + #[cfg(json)] + #[cfg_attr(feature = "docs", doc(cfg(json)))] /// A JSON value. - Json(Json<'a>), - #[cfg(feature = "uuid")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] + Json(Option), + #[cfg(uuid)] + #[cfg_attr(feature = "docs", doc(cfg(uuid)))] /// An UUID value. Uuid(Option), - #[cfg(feature = "postgres")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] + #[cfg(only_postgres)] + #[cfg_attr(feature = "docs", doc(cfg(only_postgres)))] /// INTERVAL PgInterval(Option), /* - #[cfg(feature = "postgres")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] + #[cfg(only_postgres)] + #[cfg_attr(feature = "docs", doc(cfg(only_postgres)))] /// INT8RANGE, INT4RANGE, TSRANGE, TSTZTRANGE, DATERANGE, NUMRANGE PgRange(Option>>>), */ - #[cfg(feature = "postgres")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] /// INT8RANGE, INT4RANGE, TSRANGE, TSTZTRANGE, DATERANGE, NUMRANGE + #[cfg(only_postgres)] + #[cfg_attr(feature = "docs", doc(cfg(only_postgres)))] PgMoney(Option), /// A numeric value. - #[cfg(feature = "bigdecimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] + #[cfg(bigdecimal)] + #[cfg_attr(feature = "docs", doc(cfg(bigdecimal)))] BigDecimal(Option), /// A numeric value. - #[cfg(feature = "decimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "decimal")))] + #[cfg(decimal)] + #[cfg_attr(feature = "docs", doc(cfg(decimal)))] Decimal(Option), /// TIMESTAMPTZ - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg(chrono)] + #[cfg_attr(feature = "docs", doc(cfg(chrono)))] UtcDateTime(Option>), /// TIMESTAMPTZ - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg(chrono)] + #[cfg_attr(feature = "docs", doc(cfg(chrono)))] LocalDateTime(Option>), /// TIMESTAMP - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg(chrono)] + #[cfg_attr(feature = "docs", doc(cfg(chrono)))] NaiveDateTime(Option), /// DATE - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg(chrono)] + #[cfg_attr(feature = "docs", doc(cfg(chrono)))] NaiveDate(Option), /// TIME - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg(chrono)] + #[cfg_attr(feature = "docs", doc(cfg(chrono)))] NaiveTime(Option), /// TIMETZ - #[cfg(all(feature = "time", feature = "postgres"))] - #[cfg_attr( - feature = "docs", - doc(cfg(all(feature = "time", feature = "postgres"))) - )] + #[cfg(all(time, only_postgres))] + #[cfg_attr(feature = "docs", doc(cfg(all(time, only_postgres))))] PgTimeTz(Option), - #[cfg(feature = "ipnetwork")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "ipnetwork")))] /// INET, CIDR + #[cfg(ipnetwork)] + #[cfg_attr(feature = "docs", doc(cfg(ipnetwork)))] IpNetwork(Option), } +/* pub(crate) struct Params<'a>(pub(crate) &'a [Value<'a>]); impl<'a> fmt::Display for Params<'a> { @@ -358,23 +396,9 @@ impl<'a> From> for serde_json::Value { } } } +*/ impl<'a> Value<'a> { - /// Creates a new integer value. - pub fn integer(value: I) -> Self - where - I: Into, - { - Value::Integer(Some(value.into())) - } - - /// Creates a new decimal value. - #[cfg(feature = "bigdecimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] - pub const fn numeric(value: BigDecimal) -> Self { - Value::Numeric(Some(value)) - } - /// Creates a new float value. pub const fn float(value: f32) -> Self { Self::Float(Some(value)) @@ -393,15 +417,9 @@ impl<'a> Value<'a> { Value::Text(Some(value.into())) } - /// Creates a new enum value. - pub fn enum_variant(value: T) -> Self - where - T: Into>, - { - Value::Enum(Some(value.into())) - } - /// Creates a new bytes value. + #[cfg(not_mssql)] + #[cfg_attr(feature = "docs", doc(cfg(not_mssql)))] pub fn bytes(value: B) -> Self where B: Into>, @@ -417,14 +435,7 @@ impl<'a> Value<'a> { Value::Boolean(Some(value.into())) } - /// Creates a new character value. - pub fn character(value: C) -> Self - where - C: Into, - { - Value::Char(Some(value.into())) - } - + /* /// Creates a new array value. pub fn array(value: I) -> Self where @@ -433,24 +444,26 @@ impl<'a> Value<'a> { { Value::Array(Some(value.into_iter().map(|v| v.into()).collect())) } + */ /// Creates a new uuid value. - #[cfg(feature = "uuid")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] + #[cfg(uuid)] + #[cfg_attr(feature = "docs", doc(cfg(uuid)))] pub const fn uuid(value: Uuid) -> Self { Value::Uuid(Some(value)) } + /* /// Creates a new datetime value. - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg(chrono)] + #[cfg_attr(feature = "docs", doc(cfg(chrono)))] pub const fn datetime(value: DateTime) -> Self { Value::DateTime(Some(value)) } /// Creates a new date value. - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg(chrono)] + #[cfg_attr(feature = "docs", doc(cfg(chrono)))] pub const fn date(value: NaiveDate) -> Self { Value::Date(Some(value)) } @@ -574,11 +587,15 @@ impl<'a> Value<'a> { pub const fn is_integer(&self) -> bool { matches!(self, Value::Integer(_)) } + */ /// Returns an `i64` if the value is an integer, otherwise `None`. pub const fn as_i64(&self) -> Option { match self { - Value::Integer(i) => *i, + Value::I8(Some(i)) => Some(*i as i64), + Value::I16(Some(i)) => Some(*i as i64), + Value::I32(Some(i)) => Some(*i as i64), + Value::I64(i) => *i, _ => None, } } @@ -599,208 +616,219 @@ impl<'a> Value<'a> { } } - /// `true` if the `Value` is a numeric value or can be converted to one. - #[cfg(feature = "bigdecimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] - pub const fn is_numeric(&self) -> bool { - matches!(self, Value::Numeric(_) | Value::Float(_) | Value::Double(_)) - } + /* + /// `true` if the `Value` is a numeric value or can be converted to one. + #[cfg(feature = "bigdecimal")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] + pub const fn is_numeric(&self) -> bool { + matches!(self, Value::Numeric(_) | Value::Float(_) | Value::Double(_)) + } - /// Returns a bigdecimal, if the value is a numeric, float or double value, - /// otherwise `None`. - #[cfg(feature = "bigdecimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] - pub fn into_numeric(self) -> Option { - match self { - Value::Numeric(d) => d, - Value::Float(f) => f.and_then(BigDecimal::from_f32), - Value::Double(f) => f.and_then(BigDecimal::from_f64), - _ => None, + /// Returns a bigdecimal, if the value is a numeric, float or double value, + /// otherwise `None`. + #[cfg(feature = "bigdecimal")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] + pub fn into_numeric(self) -> Option { + match self { + Value::Numeric(d) => d, + Value::Float(f) => f.and_then(BigDecimal::from_f32), + Value::Double(f) => f.and_then(BigDecimal::from_f64), + _ => None, + } } - } - /// Returns a reference to a bigdecimal, if the value is a numeric. - /// Otherwise `None`. - #[cfg(feature = "bigdecimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] - pub const fn as_numeric(&self) -> Option<&BigDecimal> { - match self { - Value::Numeric(d) => d.as_ref(), - _ => None, + /// Returns a reference to a bigdecimal, if the value is a numeric. + /// Otherwise `None`. + #[cfg(feature = "bigdecimal")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] + pub const fn as_numeric(&self) -> Option<&BigDecimal> { + match self { + Value::Numeric(d) => d.as_ref(), + _ => None, + } } - } - /// `true` if the `Value` is a boolean value. - pub const fn is_bool(&self) -> bool { - match self { - Value::Boolean(_) => true, - // For schemas which don't tag booleans - Value::Integer(Some(i)) if *i == 0 || *i == 1 => true, - _ => false, + /// `true` if the `Value` is a boolean value. + pub const fn is_bool(&self) -> bool { + match self { + Value::Boolean(_) => true, + // For schemas which don't tag booleans + Value::Integer(Some(i)) if *i == 0 || *i == 1 => true, + _ => false, + } } - } - /// Returns a bool if the value is a boolean, otherwise `None`. - pub const fn as_bool(&self) -> Option { - match self { - Value::Boolean(b) => *b, - // For schemas which don't tag booleans - Value::Integer(Some(i)) if *i == 0 || *i == 1 => Some(*i == 1), - _ => None, + /// Returns a bool if the value is a boolean, otherwise `None`. + pub const fn as_bool(&self) -> Option { + match self { + Value::Boolean(b) => *b, + // For schemas which don't tag booleans + Value::Integer(Some(i)) if *i == 0 || *i == 1 => Some(*i == 1), + _ => None, + } } - } - /// `true` if the `Value` is an Array. - pub const fn is_array(&self) -> bool { - matches!(self, Value::Array(_)) - } + /// `true` if the `Value` is an Array. + pub const fn is_array(&self) -> bool { + matches!(self, Value::Array(_)) + } - /// `true` if the `Value` is of UUID type. - #[cfg(feature = "uuid")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] - pub const fn is_uuid(&self) -> bool { - matches!(self, Value::Uuid(_)) - } + /// `true` if the `Value` is of UUID type. + #[cfg(feature = "uuid")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] + pub const fn is_uuid(&self) -> bool { + matches!(self, Value::Uuid(_)) + } - /// Returns an UUID if the value is of UUID type, otherwise `None`. - #[cfg(feature = "uuid")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] - pub const fn as_uuid(&self) -> Option { - match self { - Value::Uuid(u) => *u, - _ => None, + /// Returns an UUID if the value is of UUID type, otherwise `None`. + #[cfg(feature = "uuid")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] + pub const fn as_uuid(&self) -> Option { + match self { + Value::Uuid(u) => *u, + _ => None, + } } - } - /// `true` if the `Value` is a DateTime. - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] - pub const fn is_datetime(&self) -> bool { - matches!(self, Value::DateTime(_)) - } + /// `true` if the `Value` is a DateTime. + #[cfg(feature = "chrono")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + pub const fn is_datetime(&self) -> bool { + matches!(self, Value::DateTime(_)) + } - /// Returns a `DateTime` if the value is a `DateTime`, otherwise `None`. - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] - pub const fn as_datetime(&self) -> Option> { - match self { - Value::DateTime(dt) => *dt, - _ => None, + /// Returns a `DateTime` if the value is a `DateTime`, otherwise `None`. + #[cfg(feature = "chrono")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + pub const fn as_datetime(&self) -> Option> { + match self { + Value::DateTime(dt) => *dt, + _ => None, + } } - } - /// `true` if the `Value` is a Date. - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] - pub const fn is_date(&self) -> bool { - matches!(self, Value::Date(_)) - } + /// `true` if the `Value` is a Date. + #[cfg(feature = "chrono")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + pub const fn is_date(&self) -> bool { + matches!(self, Value::Date(_)) + } - /// Returns a `NaiveDate` if the value is a `Date`, otherwise `None`. - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] - pub const fn as_date(&self) -> Option { - match self { - Value::Date(dt) => *dt, - _ => None, + /// Returns a `NaiveDate` if the value is a `Date`, otherwise `None`. + #[cfg(feature = "chrono")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + pub const fn as_date(&self) -> Option { + match self { + Value::Date(dt) => *dt, + _ => None, + } } - } - /// `true` if the `Value` is a `Time`. - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] - pub const fn is_time(&self) -> bool { - matches!(self, Value::Time(_)) - } + /// `true` if the `Value` is a `Time`. + #[cfg(feature = "chrono")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + pub const fn is_time(&self) -> bool { + matches!(self, Value::Time(_)) + } - /// Returns a `NaiveTime` if the value is a `Time`, otherwise `None`. - #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] - pub const fn as_time(&self) -> Option { - match self { - Value::Time(time) => *time, - _ => None, + /// Returns a `NaiveTime` if the value is a `Time`, otherwise `None`. + #[cfg(feature = "chrono")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + pub const fn as_time(&self) -> Option { + match self { + Value::Time(time) => *time, + _ => None, + } } - } - /// `true` if the `Value` is a JSON value. - #[cfg(feature = "json")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] - pub const fn is_json(&self) -> bool { - matches!(self, Value::Json(_)) - } + /// `true` if the `Value` is a JSON value. + #[cfg(feature = "json")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] + pub const fn is_json(&self) -> bool { + matches!(self, Value::Json(_)) + } - /// Returns a reference to a JSON Value if of Json type, otherwise `None`. - #[cfg(feature = "json")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] - pub const fn as_json(&self) -> Option<&serde_json::Value> { - match self { - Value::Json(Json::JsonValue(Some(j))) => Some(j), - _ => None, + /// Returns a reference to a JSON Value if of Json type, otherwise `None`. + #[cfg(feature = "json")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] + pub const fn as_json(&self) -> Option<&serde_json::Value> { + match self { + Value::Json(Json::JsonValue(Some(j))) => Some(j), + _ => None, + } } - } - /// Transforms to a JSON Value if of Json type, otherwise `None`. - #[cfg(feature = "json")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] - pub fn into_json(self) -> Option { - match self { - Value::Json(Json::JsonValue(Some(j))) => Some(j), - Value::Json(Json::JsonRawValue(Some(j))) => serde_json::to_value(*j).ok(), - _ => None, + /// Transforms to a JSON Value if of Json type, otherwise `None`. + #[cfg(feature = "json")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] + pub fn into_json(self) -> Option { + match self { + Value::Json(Json::JsonValue(Some(j))) => Some(j), + Value::Json(Json::JsonRawValue(Some(j))) => serde_json::to_value(*j).ok(), + _ => None, + } } - } - /// Returns a Vec if the value is an array of T, otherwise `None`. - pub fn into_vec(self) -> Option> - where - // Implement From - T: TryFrom>, - { - match self { - Value::Array(Some(vec)) => { - let rslt: Result, _> = vec.into_iter().map(T::try_from).collect(); - match rslt { - Err(_) => None, - Ok(values) => Some(values), + /// Returns a Vec if the value is an array of T, otherwise `None`. + pub fn into_vec(self) -> Option> + where + // Implement From + T: TryFrom>, + { + match self { + Value::Array(Some(vec)) => { + let rslt: Result, _> = vec.into_iter().map(T::try_from).collect(); + match rslt { + Err(_) => None, + Ok(values) => Some(values), + } } + _ => None, } - _ => None, } - } + */ } -value!(val: i64, Integer, val); +value!(val: i8, I8, val); +value!(val: i16, I16, val); +value!(val: i32, I32, val); +value!(val: i64, I64, val); +#[cfg(mysql_or_sqlite)] +value!(val: u8, U8, val); +#[cfg(mysql_or_sqlite)] +value!(val: u16, U16, val); +#[cfg(not_mssql)] +value!(val: u32, U32, val); +#[cfg(only_mysql)] +value!(val: u64, U64, val); value!(val: bool, Boolean, val); value!(val: &'a str, Text, val.into()); value!(val: String, Text, val.into()); -value!(val: usize, Integer, i64::try_from(val).unwrap()); -value!(val: i32, Integer, i64::try_from(val).unwrap()); +value!(val: usize, I64, i64::try_from(val).unwrap()); +#[cfg(not_mssql)] value!(val: &'a [u8], Bytes, val.into()); value!(val: f64, Double, val); value!(val: f32, Float, val); -#[cfg(feature = "chrono")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] -value!(val: DateTime, DateTime, val); -#[cfg(feature = "chrono")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] +#[cfg(chrono)] +#[cfg_attr(feature = "docs", doc(cfg(chrono)))] +value!(val: DateTime, UtcDateTime, val); +#[cfg(chrono)] +#[cfg_attr(feature = "docs", doc(cfg(chrono)))] value!(val: chrono::NaiveTime, Time, val); -#[cfg(feature = "chrono")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] +#[cfg(chrono)] +#[cfg_attr(feature = "docs", doc(cfg(chrono)))] value!(val: chrono::NaiveDate, Date, val); -#[cfg(feature = "bigdecimal")] -value!(val: BigDecimal, Numeric, val); -#[cfg(feature = "json")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] -impl_value_from_json!(val: JsonValue, JsonValue, val); -#[cfg(feature = "json")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] -impl_value_from_json!(val: JsonRawValue<'a>, JsonRawValue, val); -#[cfg(feature = "uuid")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] +#[cfg(bigdecimal)] +value!(val: BigDecimal, BigDecimal, val); +#[cfg(json)] +#[cfg_attr(feature = "docs", doc(cfg(json)))] +value!(val: JsonValue, Json, Json(val)); +#[cfg(uuid)] +#[cfg_attr(feature = "docs", doc(cfg(uuid)))] value!(val: Uuid, Uuid, val); +/* impl<'a> TryFrom> for i64 { type Error = Error; @@ -863,6 +891,7 @@ impl<'a> TryFrom> for DateTime { .ok_or_else(|| Error::builder(ErrorKind::conversion("Not a datetime")).build()) } } +*/ /// An in-memory temporary table. Can be used in some of the databases in a /// place of an actual table. Doesn't work in MySQL 5.7. diff --git a/src/databases/mod.rs b/src/databases/mod.rs index 0054dc1..ff7a83c 100644 --- a/src/databases/mod.rs +++ b/src/databases/mod.rs @@ -1,14 +1,14 @@ use std::default; use std::marker::{PhantomData}; +use async_trait::async_trait; #[cfg(feature = "chrono")] use sqlx::types::chrono; +#[cfg(feature = "json")] +use sqlx::types::Json; use sqlx::{Executor, Arguments, Database, IntoArguments, FromRow}; -use async_trait::async_trait; use crate::ast::{Value}; -#[cfg(feature = "json")] -use crate::ast::Json; use crate::prelude::{Column, Delete, Entity, HasPrimaryKey, Insert, MultiRowInsert, SingleRowInsert, Row, OnConflict, Select, Update, Expression}; use crate::visitors::Visitor; @@ -54,54 +54,53 @@ impl<'a> HasVisitor<'a> for sqlx::Sqlite { macro_rules! bind_value { ($query:ident, $value: ident) => { match $value { - Value::Integer(integer) => $query.bind(integer), - Value::I8(int8) => $query.bind(int8), Value::I16(int16) => $query.bind(int16), Value::I32(int32) => $query.bind(int32), Value::I64(int64) => $query.bind(int64), Value::Float(float) => $query.bind(float) , Value::Double(double) => $query.bind(double), + Value::Boolean(boolean) => $query.bind(boolean), Value::Text(text) => $query.bind(text.map(|text|text.into_owned())), + #[cfg(not_mssql)] Value::Bytes(bytes) => $query.bind(bytes.map(|b|b.into_owned())), - Value::Boolean(boolean) => $query.bind(boolean), - #[cfg(feature = "json")] - Value::Json(json) => match json { - // #[cfg(feature = "postgres")] - // Json::Json(json) => { - // // - // } - Json::JsonValue(value) => $query.bind(value), - Json::JsonRawValue(raw_value) => $query.bind(raw_value.map(|v|sqlx::types::Json(*v))) - }, - #[cfg(all(feature = "uuid", feature = "postgres"))] + + #[cfg(mysql_or_sqlite)] + Value::U8(uint8) => $query.bind(uint8), + #[cfg(mysql_or_sqlite)] + Value::U16(uint16) => $query.bind(uint16), + #[cfg(not_mssql)] + Value::U32(uint32) => $query.bind(uint32), + #[cfg(only_mysql)] + Value::U64(uint64) => $query.bind(uint64), + #[cfg(json)] + Value::Json(json) => $query.bind(json), + #[cfg(uuid)] Value::Uuid(uuid) => $query.bind(uuid), - #[cfg(feature = "postgres")] + #[cfg(only_postgres)] Value::PgInterval(interval) => $query.bind(interval), - // #[cfg(feature = "postgres")] + // #[cfg(only_postgres)] // Value::PgRange(range) => $query.bind(range), - #[cfg(feature = "postgres")] + #[cfg(only_postgres)] Value::PgMoney(money) => $query.bind(money), - #[cfg(all(feature = "bigdecimal", feature = "postgres"))] + #[cfg(bigdecimal)] Value::BigDecimal(bigdecimal) => $query.bind(bigdecimal), - #[cfg(all(feature = "decimal", feature = "postgres"))] + #[cfg(decimal)] Value::Decimal(decimal) => $query.bind(decimal), - #[cfg(all(feature = "chrono", feature = "postgres"))] + #[cfg(chrono)] Value::UtcDateTime(datetime) => $query.bind(datetime), - #[cfg(all(feature = "chrono", feature = "postgres"))] + #[cfg(chrono)] Value::LocalDateTime(datetime) => $query.bind(datetime), - #[cfg(all(feature = "chrono", feature = "postgres"))] + #[cfg(chrono)] Value::NaiveDateTime(datetime) => $query.bind(datetime), - #[cfg(all(feature = "chrono", feature = "postgres"))] + #[cfg(chrono)] Value::NaiveDate(date) => $query.bind(date), - #[cfg(all(feature = "chrono", feature = "postgres"))] + #[cfg(chrono)] Value::NaiveTime(time) => $query.bind(time), - #[cfg(all(feature = "time", feature = "postgres"))] + #[cfg(all(time, only_postgres))] Value::PgTimeTz(timetz) => $query.bind(timetz), - #[cfg(all(feature = "ipnetwork", feature = "postgres"))] + #[cfg(ipnetwork)] Value::IpNetwork(ipnetwork) => $query.bind(ipnetwork), - - _ => unimplemented!() } }; } @@ -141,7 +140,7 @@ impl<'a, O> Binder<'a, sqlx::MySql> for sqlx::query::QueryAs<'a, sqlx::MySql, O, } #[cfg(feature = "mssql")] -impl<'a> Binder<'a, sqlx::Mssql> for sqlx::query::Query<'a, sqlx::Mssql, O, sqlx::mssql::MssqlArguments> { +impl<'a> Binder<'a, sqlx::Mssql> for sqlx::query::Query<'a, sqlx::Mssql, sqlx::mssql::MssqlArguments> { fn bind_value(self, value: Value<'a>) -> Self { bind_value!(self, value) } @@ -303,14 +302,14 @@ impl<'e, E: HasPrimaryKey, DB: Database> SavingExecution<'e, E, DB> { } } -/// create table. Returned by [`get`][crate::prelude::entity::create_table]. +/// create table. Returned by [`create`][crate::prelude::Executioner::create]. #[must_use = "create table must be executed to affect database"] pub struct CreateTableExecution { _marker: PhantomData, compiled: Option, } -/// create table. Returned by [`get`][crate::prelude::entity::create]. +/// insert into table. Returned by [`insert`][crate::prelude::Entity::insert]. #[must_use = "insert must be executed to affect database"] #[derive(Clone, Debug)] pub struct InsertingExecution { diff --git a/src/error.rs b/src/error.rs index 7e72081..dadd154 100644 --- a/src/error.rs +++ b/src/error.rs @@ -190,14 +190,14 @@ pub enum ErrorKind { /// A [`Pool::acquire`] timed out due to connections not becoming available or /// because another task encountered too many errors while trying to open a new connection. /// - /// [`Pool::acquire`]: crate::pool::Pool::acquire + /// [`Pool::acquire`]: sqlx::pool::Pool::acquire #[error("{0}")] SQLxPoolTimedOut(#[source] sqlx::error::Error), /// [`Pool::close`] was called while we were waiting in [`Pool::acquire`]. /// - /// [`Pool::acquire`]: crate::pool::Pool::acquire - /// [`Pool::close`]: crate::pool::Pool::close + /// [`Pool::acquire`]: sqlx::pool::Pool::acquire + /// [`Pool::close`]: sqlx::pool::Pool::close #[error("{0}")] SQLxPoolClosed(#[source] sqlx::error::Error), diff --git a/src/lib.rs b/src/lib.rs index 5a117a3..f67b2c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ -#![feature(const_panic)] #![cfg_attr(feature = "docs", feature(doc_cfg))] #[cfg(not(any( + feature = "docs", feature = "sqlite", feature = "postgres", feature = "mysql", @@ -9,6 +9,7 @@ )))] compile_error!("one of 'sqlite', 'postgres', 'mysql' or 'mssql' features must be enabled"); + #[macro_use] extern crate derive_more; #[macro_use] diff --git a/src/visitors/mssql.rs b/src/visitors/mssql.rs index 46679f7..750571d 100644 --- a/src/visitors/mssql.rs +++ b/src/visitors/mssql.rs @@ -242,7 +242,6 @@ impl<'a> Visitor<'a> for Mssql<'a> { } (left_kind, right_kind) => { let (l_alias, r_alias) = (left.alias, right.alias); - let (left_xml, right_xml) = (left_kind.is_xml_value(), right_kind.is_xml_value()); let mut left = Expression::from(left_kind); @@ -256,23 +255,11 @@ impl<'a> Visitor<'a> for Mssql<'a> { right = right.alias(alias); } - if right_xml { - self.surround_with("CAST(", " AS NVARCHAR(MAX))", |x| { - x.visit_expression(left) - })?; - } else { - self.visit_expression(left)?; - } + self.visit_expression(left)?; self.write(" = ")?; - if left_xml { - self.surround_with("CAST(", " AS NVARCHAR(MAX))", |x| { - x.visit_expression(right) - })?; - } else { - self.visit_expression(right)?; - } + self.visit_expression(right)?; } } @@ -291,7 +278,6 @@ impl<'a> Visitor<'a> for Mssql<'a> { } (left_kind, right_kind) => { let (l_alias, r_alias) = (left.alias, right.alias); - let (left_xml, right_xml) = (left_kind.is_xml_value(), right_kind.is_xml_value()); let mut left = Expression::from(left_kind); @@ -305,23 +291,11 @@ impl<'a> Visitor<'a> for Mssql<'a> { right = right.alias(alias); } - if right_xml { - self.surround_with("CAST(", " AS NVARCHAR(MAX))", |x| { - x.visit_expression(left) - })?; - } else { - self.visit_expression(left)?; - } + self.visit_expression(left)?; self.write(" <> ")?; - if left_xml { - self.surround_with("CAST(", " AS NVARCHAR(MAX))", |x| { - x.visit_expression(right) - })?; - } else { - self.visit_expression(right)?; - } + self.visit_expression(right)?; } } @@ -330,7 +304,10 @@ impl<'a> Visitor<'a> for Mssql<'a> { fn visit_raw_value(&mut self, value: Value<'a>) -> visitors::Result { let res = match value { - Value::Integer(i) => i.map(|i| self.write(i)), + Value::I8(i) => i.map(|i| self.write(i)), + Value::I16(i) => i.map(|i| self.write(i)), + Value::I32(i) => i.map(|i| self.write(i)), + Value::I64(i) => i.map(|i| self.write(i)), Value::Float(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f32::INFINITY => self.write("'Infinity'"), @@ -344,51 +321,7 @@ impl<'a> Visitor<'a> for Mssql<'a> { v => self.write(format!("{:?}", v)), }), Value::Text(t) => t.map(|t| self.write(format!("'{}'", t))), - Value::Enum(e) => e.map(|e| self.write(e)), - Value::Bytes(b) => b.map(|b| self.write(format!("0x{}", hex::encode(b)))), Value::Boolean(b) => b.map(|b| self.write(if b { 1 } else { 0 })), - Value::Char(c) => c.map(|c| self.write(format!("'{}'", c))), - Value::Array(_) => { - let msg = "Arrays are not supported in T-SQL."; - let kind = ErrorKind::conversion(msg); - - let mut builder = Error::builder(kind); - builder.set_original_message(msg); - - return Err(builder.build()); - } - #[cfg(feature = "json")] - Value::Json(j) => { - j.map(|j| self.write(format!("'{}'", serde_json::to_string(&j).unwrap()))) - } - #[cfg(feature = "bigdecimal")] - Value::Numeric(r) => r.map(|r| self.write(r)), - #[cfg(feature = "uuid")] - Value::Uuid(uuid) => uuid.map(|uuid| { - let s = format!( - "CONVERT(uniqueidentifier, N'{}')", - uuid.to_hyphenated().to_string() - ); - self.write(s) - }), - #[cfg(feature = "chrono")] - Value::DateTime(dt) => dt.map(|dt| { - let s = format!("CONVERT(datetimeoffset, N'{}')", dt.to_rfc3339()); - self.write(s) - }), - #[cfg(feature = "chrono")] - Value::Date(date) => date.map(|date| { - let s = format!("CONVERT(date, N'{}')", date); - self.write(s) - }), - #[cfg(feature = "chrono")] - Value::Time(time) => time.map(|time| { - let s = format!("CONVERT(time, N'{}')", time); - self.write(s) - }), - // Style 3 is keep all whitespace + internal DTD processing: - // https://docs.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?redirectedfrom=MSDN&view=sql-server-ver15#xml-styles - Value::Xml(cow) => cow.map(|cow| self.write(format!("CONVERT(XML, N'{}', 3)", cow))), }; match res { @@ -1007,11 +940,10 @@ mod tests { #[derive(Entity)] struct User { - #[column(name = "xmlField")] - xml: String, id: i32, } + /* #[test] fn equality_with_a_xml_value() { let expected = expected_values( @@ -1071,6 +1003,7 @@ mod tests { assert_eq!(expected.0, sql); assert_eq!(expected.1, params); } + */ #[test] fn test_select_and() { diff --git a/src/visitors/mysql.rs b/src/visitors/mysql.rs index 37a2df8..5bef8e0 100644 --- a/src/visitors/mysql.rs +++ b/src/visitors/mysql.rs @@ -46,15 +46,15 @@ impl<'a> Mysql<'a> { right: Expression<'a>, sign: &str, ) -> visitors::Result { - #[cfg(feature = "json")] + #[cfg(json)] fn json_to_quaint_value<'a>(json: serde_json::Value) -> crate::Result> { match json { serde_json::Value::String(str) => Ok(Value::text(str)), serde_json::Value::Number(number) => { if let Some(int) = number.as_i64() { - Ok(Value::integer(int)) - } else if let Some(float) = number.as_f64() { - Ok(Value::double(float)) + Ok(Value::I64(Some(int))) + } else if let Some(double) = number.as_f64() { + Ok(Value::double(Some(double))) } else { unreachable!() } @@ -72,7 +72,7 @@ impl<'a> Mysql<'a> { } match (left, right) { - #[cfg(feature = "json")] + #[cfg(json)] (left, right) if left.is_json_value() && right.is_json_extract_fun() => { let quaint_value = json_to_quaint_value(left.into_json_value().unwrap())?; @@ -80,7 +80,7 @@ impl<'a> Mysql<'a> { self.write(format!(" {} ", sign))?; self.visit_expression(right)?; } - #[cfg(feature = "json")] + #[cfg(json)] (left, right) if left.is_json_extract_fun() && right.is_json_value() => { let quaint_value = json_to_quaint_value(right.into_json_value().unwrap())?; @@ -135,7 +135,18 @@ impl<'a> Visitor<'a> for Mysql<'a> { fn visit_raw_value(&mut self, value: Value<'a>) -> visitors::Result { let res = match value { - Value::Integer(i) => i.map(|i| self.write(i)), + Value::I8(i) => i.map(|i| self.write(i)), + Value::I16(i) => i.map(|i| self.write(i)), + Value::I32(i) => i.map(|i| self.write(i)), + Value::I64(i) => i.map(|i| self.write(i)), + #[cfg(mysql_or_sqlite)] + Value::U8(i) => i.map(|i| self.write(i)), + #[cfg(mysql_or_sqlite)] + Value::U16(i) => i.map(|i| self.write(i)), + #[cfg(mysql_or_sqlite)] + Value::U32(i) => i.map(|i| self.write(i)), + #[cfg(mysql_or_sqlite)] + Value::U64(i) => i.map(|i| self.write(i)), Value::Float(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f32::INFINITY => self.write("'Infinity'"), @@ -149,45 +160,37 @@ impl<'a> Visitor<'a> for Mysql<'a> { v => self.write(format!("{:?}", v)), }), Value::Text(t) => t.map(|t| self.write(format!("'{}'", t))), - Value::Enum(e) => e.map(|e| self.write(e)), + #[cfg(not_mssql)] Value::Bytes(b) => b.map(|b| self.write(format!("x'{}'", hex::encode(b)))), Value::Boolean(b) => b.map(|b| self.write(b)), - Value::Char(c) => c.map(|c| self.write(format!("'{}'", c))), - Value::Array(_) => { - let msg = "Arrays are not supported in MySQL."; - let kind = ErrorKind::conversion(msg); - - let mut builder = Error::builder(kind); - builder.set_original_message(msg); - - return Err(builder.build()); - } - #[cfg(feature = "bigdecimal")] - Value::Numeric(r) => r.map(|r| self.write(r)), - #[cfg(feature = "json")] + #[cfg(bigdecimal)] + Value::BigDecimal(r) => r.map(|r| self.write(r)), + #[cfg(decimal)] + Value::Decimal(r) => r.map(|r| self.write(r)), + #[cfg(json)] Value::Json(j) => match j { - crate::ast::Json::JsonValue(Some(v)) => { + Some(v) => { let s = serde_json::to_string(&v)?; Some(self.write(format!("CONVERT('{}', JSON)", s))) } - crate::ast::Json::JsonRawValue(Some(v)) => { - let s = serde_json::to_string(*v)?; - Some(self.write(format!("CONVERT('{}', JSON)", s))) - } _ => None, }, - #[cfg(feature = "uuid")] + #[cfg(uuid)] Value::Uuid(uuid) => { uuid.map(|uuid| self.write(format!("'{}'", uuid.to_hyphenated().to_string()))) } - #[cfg(feature = "chrono")] - Value::DateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), - #[cfg(feature = "chrono")] - Value::Date(date) => date.map(|date| self.write(format!("'{}'", date))), - #[cfg(feature = "chrono")] - Value::Time(time) => time.map(|time| self.write(format!("'{}'", time))), - Value::Xml(cow) => cow.map(|cow| self.write(format!("'{}'", cow))), - _ => todo!(), + #[cfg(chrono)] + Value::UtcDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), + #[cfg(chrono)] + Value::LocalDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), + #[cfg(chrono)] + Value::NaiveDateTime(datetime) => { + datetime.map(|datetime| self.write(format!("'{}'", datetime))) + } + #[cfg(chrono)] + Value::NaiveDate(date) => date.map(|date| self.write(format!("'{}'", date))), + #[cfg(chrono)] + Value::NaiveTime(time) => time.map(|time| self.write(format!("'{}'", date))), }; match res { @@ -285,10 +288,10 @@ impl<'a> Visitor<'a> for Mysql<'a> { self.write(" OFFSET ")?; self.visit_parameterized(offset) } - (None, Some(Value::Integer(Some(offset)))) if offset < 1 => Ok(()), + (None, Some(Value::I64(Some(offset)))) if offset < 1 => Ok(()), (None, Some(offset)) => { self.write(" LIMIT ")?; - self.visit_parameterized(Value::from(9_223_372_036_854_775_807i64))?; + self.visit_parameterized(Value::I64(Some(i64::MAX)))?; self.write(" OFFSET ")?; self.visit_parameterized(offset) @@ -686,19 +689,15 @@ mod tests { let expected_sql = "SELECT `test`.* FROM `test` WHERE (`test`.`id1`,`test`.`id2`) IN ((?,?),(?,?))"; let query = Select::from_table(TestEntity::table()).so_that( - Row::from((TestEntity::id1, TestEntity::id2)).in_selection(values!((1, 2), (3, 4))), + Row::from((TestEntity::id1, TestEntity::id2)) + .in_selection(values!((1i32, 2i32), (3i32, 4i32))), ); let (sql, params) = Mysql::build(query).unwrap(); assert_eq!(expected_sql, sql); assert_eq!( - vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), - ], + vec![Value::I32(1), Value::I32(2), Value::I32(3), Value::I32(4),], params ); } @@ -763,6 +762,7 @@ mod tests { assert!(params.is_empty()); } + #[cfg(not_mssql)] #[test] fn test_raw_bytes() { let (sql, params) = @@ -782,14 +782,6 @@ mod tests { assert!(params.is_empty()); } - #[test] - fn test_raw_char() { - let (sql, params) = - Mysql::build(Select::default().value(Value::character('a').raw())).unwrap(); - assert_eq!("SELECT 'a'", sql); - assert!(params.is_empty()); - } - #[test] fn test_distinct() { let expected_sql = "SELECT DISTINCT `test`.`bar` FROM `test`"; diff --git a/src/visitors/postgres.rs b/src/visitors/postgres.rs index 910a5d2..fce4458 100644 --- a/src/visitors/postgres.rs +++ b/src/visitors/postgres.rs @@ -83,13 +83,16 @@ impl<'a> Visitor<'a> for Postgres<'a> { fn visit_raw_value(&mut self, value: Value<'a>) -> visitors::Result { let res = match value { - Value::Integer(i) => i.map(|i| self.write(i)), + Value::I8(i) => i.map(|i| self.write(i)), + Value::I16(i) => i.map(|i| self.write(i)), + Value::I32(i) => i.map(|i| self.write(i)), + Value::I64(i) => i.map(|i| self.write(i)), + #[cfg(not_mssql)] + Value::U32(i) => i.map(|i| self.write(i)), Value::Text(t) => t.map(|t| self.write(format!("'{}'", t))), - Value::Enum(e) => e.map(|e| self.write(e)), + #[cfg(not_mssql)] Value::Bytes(b) => b.map(|b| self.write(format!("E'{}'", hex::encode(b)))), Value::Boolean(b) => b.map(|b| self.write(b)), - Value::Xml(cow) => cow.map(|cow| self.write(format!("'{}'", cow))), - Value::Char(c) => c.map(|c| self.write(format!("'{}'", c))), Value::Float(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f32::INFINITY => self.write("'Infinity'"), @@ -102,43 +105,55 @@ impl<'a> Visitor<'a> for Postgres<'a> { f if f == f64::NEG_INFINITY => self.write("'-Infinity"), v => self.write(format!("{:?}", v)), }), - Value::Array(ary) => ary.map(|ary| { - self.surround_with("'{", "}'", |ref mut s| { - let len = ary.len(); - - for (i, item) in ary.into_iter().enumerate() { - s.write(item)?; - - if i < len - 1 { - s.write(",")?; - } - } - - Ok(()) - }) - }), - #[cfg(feature = "json")] + // #[cfg(only_postgres)] + // Value::Array(ary) => ary.map(|ary| { + // self.surround_with("'{", "}'", |ref mut s| { + // let len = ary.len(); + + // for (i, item) in ary.into_iter().enumerate() { + // s.write(item)?; + + // if i < len - 1 { + // s.write(",")?; + // } + // } + + // Ok(()) + // }) + // }), + #[cfg(json)] Value::Json(j) => match j { - crate::ast::Json::JsonRawValue(v) => { - v.map(|j| self.write(format!("'{}'", serde_json::to_string(*j).unwrap()))) - } - crate::ast::Json::JsonValue(v) => { - v.map(|j| self.write(format!("'{}'", serde_json::to_string(&j).unwrap()))) - } + Some(v) => self.write(format!("'{}'", serde_json::to_string(&j).unwrap())), + None => None, }, - #[cfg(feature = "bigdecimal")] - Value::Numeric(r) => r.map(|r| self.write(r)), - #[cfg(feature = "uuid")] + #[cfg(bigdecimal)] + Value::BigDecimal(r) => r.map(|r| self.write(r)), + #[cfg(decimal)] + Value::Decimal(r) => r.map(|r| self.write(r)), + #[cfg(uuid)] Value::Uuid(uuid) => { uuid.map(|uuid| self.write(format!("'{}'", uuid.to_hyphenated().to_string()))) } - #[cfg(feature = "chrono")] - Value::DateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), - #[cfg(feature = "chrono")] - Value::Date(date) => date.map(|date| self.write(format!("'{}'", date))), - #[cfg(feature = "chrono")] - Value::Time(time) => time.map(|time| self.write(format!("'{}'", time))), - _ => unimplemented!(), + #[cfg(chrono)] + Value::UtcDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), + #[cfg(chrono)] + Value::LocalDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), + #[cfg(chrono)] + Value::NaiveDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), + #[cfg(chrono)] + Value::NaiveDate(date) => date.map(|date| self.write(format!("'{}'", date))), + #[cfg(only_postgres)] + Value::PgMoney(money) => money.map(|money| self.write(money.to_bigdecimal(2))), + #[cfg(only_postgres)] + Value::PgInterval(interval) => { + interval.map(|interval| self.write(format!("{:?}", interval))) + } + #[cfg(all(time, only_postgres))] + Value::PgTimeTz(timetz) => timetz.map(|timetz| self.write(format!("{:?}", timtz))), + #[cfg(ipnetwork)] + Value::IpNetwork(ipnetwork) => { + ipnetwork.map(|ipnetwork| self.write(format!("'{}'", ipnetwork))) + } }; match res { @@ -238,16 +253,14 @@ impl<'a> Visitor<'a> for Postgres<'a> { fn visit_equals(&mut self, left: Expression<'a>, right: Expression<'a>) -> visitors::Result { // LHS must be cast to json/xml-text if the right is a json/xml-text value and vice versa. let right_cast = match left { - #[cfg(feature = "json")] + #[cfg(json)] _ if left.is_json_value() => "::jsonb", - _ if left.is_xml_value() => "::text", _ => "", }; let left_cast = match right { - #[cfg(feature = "json")] + #[cfg(json)] _ if right.is_json_value() => "::jsonb", - _ if right.is_xml_value() => "::text", _ => "", }; @@ -267,16 +280,14 @@ impl<'a> Visitor<'a> for Postgres<'a> { ) -> visitors::Result { // LHS must be cast to json/xml-text if the right is a json/xml-text value and vice versa. let right_cast = match left { - #[cfg(feature = "json")] + #[cfg(json)] _ if left.is_json_value() => "::jsonb", - _ if left.is_xml_value() => "::text", _ => "", }; let left_cast = match right { - #[cfg(feature = "json")] + #[cfg(json)] _ if right.is_json_value() => "::jsonb", - _ if right.is_xml_value() => "::text", _ => "", }; @@ -465,7 +476,7 @@ mod tests { #[column(primary_key)] id: i32, foo: i32, - #[cfg(feature = "json")] + #[cfg(json)] #[column(name = "jsonField")] json: serde_json::Value, #[column(name = "xmlField")] @@ -670,7 +681,7 @@ mod tests { vec![serde_json::json!({"a": "b"})], ); - let value_expr: Expression = Value::json(serde_json::json!({"a":"b"})).into(); + let value_expr: Expression = Value::Json(serde_json::json!({"a":"b"})).into(); let query = Select::from_table(User::table()).so_that(value_expr.equals(User::json)); let (sql, params) = Postgres::build(query).unwrap(); @@ -702,7 +713,7 @@ mod tests { vec![serde_json::json!({"a": "b"})], ); - let value_expr: Expression = Value::json(serde_json::json!({"a":"b"})).into(); + let value_expr: Expression = Value::Json(serde_json::json!({"a":"b"})).into(); let query = Select::from_table(User::table()).so_that(value_expr.not_equals(User::json)); let (sql, params) = Postgres::build(query).unwrap(); @@ -710,6 +721,7 @@ mod tests { assert_eq!(expected.1, params); } + /* #[test] fn equality_with_a_xml_value() { let expected = expected_values( @@ -769,6 +781,7 @@ mod tests { assert_eq!(expected.0, sql); assert_eq!(expected.1, params); } + */ #[test] fn test_raw_null() { @@ -802,7 +815,7 @@ mod tests { #[test] fn test_raw_bytes() { let (sql, params) = - Postgres::build(Select::default().value(Value::bytes(vec![1, 2, 3]).raw())).unwrap(); + Postgres::build(Select::default().value(Value::Bytes(vec![1, 2, 3]).raw())).unwrap(); assert_eq!("SELECT E'010203'", sql); assert!(params.is_empty()); } @@ -818,6 +831,7 @@ mod tests { assert!(params.is_empty()); } + /* #[test] fn test_raw_char() { let (sql, params) = @@ -825,6 +839,7 @@ mod tests { assert_eq!("SELECT 'a'", sql); assert!(params.is_empty()); } + */ #[test] #[cfg(feature = "json")] diff --git a/src/visitors/sqlite.rs b/src/visitors/sqlite.rs index 2647ef5..f89e53c 100644 --- a/src/visitors/sqlite.rs +++ b/src/visitors/sqlite.rs @@ -52,12 +52,20 @@ impl<'a> Visitor<'a> for Sqlite<'a> { fn visit_raw_value(&mut self, value: Value<'a>) -> visitors::Result { let res = match value { - Value::Integer(i) => i.map(|i| self.write(i)), + Value::I8(i) => i.map(|i| self.write(i)), + Value::I16(i) => i.map(|i| self.write(i)), + Value::I32(i) => i.map(|i| self.write(i)), + Value::I64(i) => i.map(|i| self.write(i)), + #[cfg(mysql_or_sqlite)] + Value::U8(i) => i.map(|i| self.write(i)), + #[cfg(mysql_or_sqlite)] + Value::U16(i) => i.map(|i| self.write(i)), + #[cfg(not_mssql)] + Value::U32(i) => i.map(|i| self.write(i)), Value::Text(t) => t.map(|t| self.write(format!("'{}'", t))), - Value::Enum(e) => e.map(|e| self.write(e)), + #[cfg(not_mssql)] Value::Bytes(b) => b.map(|b| self.write(format!("x'{}'", hex::encode(b)))), Value::Boolean(b) => b.map(|b| self.write(b)), - Value::Char(c) => c.map(|c| self.write(format!("'{}'", c))), Value::Float(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f32::INFINITY => self.write("'Infinity'"), @@ -70,41 +78,29 @@ impl<'a> Visitor<'a> for Sqlite<'a> { f if f == f64::NEG_INFINITY => self.write("'-Infinity"), v => self.write(format!("{:?}", v)), }), - Value::Array(_) => { - let msg = "Arrays are not supported in SQLite."; - let kind = ErrorKind::conversion(msg); - let mut builder = Error::builder(kind); - builder.set_original_message(msg); - - return Err(builder.build()); - } - #[cfg(feature = "json")] + #[cfg(json)] Value::Json(j) => match j { - crate::ast::Json::JsonValue(Some(ref v)) => { + Some(v) => { let s = serde_json::to_string(&v)?; Some(self.write(format!("'{}'", s))) } - crate::ast::Json::JsonRawValue(Some(ref v)) => { - let s = serde_json::to_string(&**v)?; - Some(self.write(format!("'{}'", s))) - } _ => None, }, - #[cfg(feature = "bigdecimal")] - Value::Numeric(r) => r.map(|r| self.write(r)), - #[cfg(feature = "uuid")] + #[cfg(uuid)] Value::Uuid(uuid) => { uuid.map(|uuid| self.write(format!("'{}'", uuid.to_hyphenated().to_string()))) } - #[cfg(feature = "chrono")] - Value::DateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), - #[cfg(feature = "chrono")] - Value::Date(date) => date.map(|date| self.write(format!("'{}'", date))), - #[cfg(feature = "chrono")] - Value::Time(time) => time.map(|time| self.write(format!("'{}'", time))), - Value::Xml(cow) => cow.map(|cow| self.write(format!("'{}'", cow))), - _ => todo!(), + #[cfg(chrono)] + Value::UtcDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), + #[cfg(chrono)] + Value::LocalDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), + #[cfg(chrono)] + Value::NaiveDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), + #[cfg(chrono)] + Value::NaiveDate(date) => date.map(|date| self.write(format!("'{}'", date))), + #[cfg(chrono)] + Value::NaiveTime(time) => time.map(|time| self.write(format!("'{}'", time))), }; match res { @@ -404,18 +400,13 @@ mod tests { use crate::values; let expected_sql = "SELECT `vals`.* FROM (VALUES (?,?),(?,?)) AS `vals`"; - let values = Table::from(values!((1, 2), (3, 4))).alias("vals"); + let values = Table::from(values!((1i32, 2i32), (3i32, 4i32))).alias("vals"); let query = Select::from_table(values); let (sql, params) = Sqlite::build(query).unwrap(); assert_eq!(expected_sql, sql); assert_eq!( - vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), - ], + vec![Value::I32(1), Value::I32(2), Value::I32(3), Value::I32(4),], params ); } @@ -427,19 +418,15 @@ mod tests { let expected_sql = "SELECT `test`.* FROM `test` WHERE (`test`.`id1`,`test`.`id2`) IN (VALUES (?,?),(?,?))"; let query = Select::from_table(TestEntity::table()).so_that( - Row::from((TestEntity::id1, TestEntity::id2)).in_selection(values!((1, 2), (3, 4))), + Row::from((TestEntity::id1, TestEntity::id2)) + .in_selection(values!((1i32, 2i32), (3i32, 4i32))), ); let (sql, params) = Sqlite::build(query).unwrap(); assert_eq!(expected_sql, sql); assert_eq!( - vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), - ], + vec![Value::I32(1), Value::I32(2), Value::I32(3), Value::I32(4),], params ); } @@ -453,10 +440,10 @@ mod tests { { let mut row1 = Row::new(); - row1.push(1); + row1.push(1i32); let mut row2 = Row::new(); - row2.push(2); + row2.push(2i32); vals.push(row1); vals.push(row2); @@ -467,7 +454,7 @@ mod tests { let expected_sql = "SELECT `test`.* FROM `test` WHERE `test`.`id1` IN (?,?)"; assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::integer(1), Value::integer(2),], params) + assert_eq!(vec![Value::I32(1), Value::I32(2),], params) } #[test] @@ -607,7 +594,7 @@ mod tests { let expected_sql = "SELECT `naukio`.* FROM `naukio` WHERE (`naukio`.`word` = ? AND `naukio`.`age` < ? AND `naukio`.`paw` = ?)"; - let expected_params = vec![Value::text("meow"), Value::integer(10), Value::text("warm")]; + let expected_params = vec![Value::Text("meow"), Value::I32(10), Value::Text("warm")]; let conditions = Naukio::word .equals("meow") @@ -627,11 +614,11 @@ mod tests { let expected_sql = "SELECT `naukio`.* FROM `naukio` WHERE (`naukio`.`word` = ? AND (`naukio`.`age` < ? AND `naukio`.`paw` = ?))"; - let expected_params = vec![Value::text("meow"), Value::integer(10), Value::text("warm")]; + let expected_params = vec![Value::Text("meow"), Value::I32(10), Value::Text("warm")]; let conditions = Naukio::word .equals("meow") - .and(Naukio::age.less_than(10).and(Naukio::paw.equals("warm"))); + .and(Naukio::age.less_than(10i32).and(Naukio::paw.equals("warm"))); let query = Select::from_table(Naukio::table()).so_that(conditions); @@ -646,11 +633,11 @@ mod tests { let expected_sql = "SELECT `naukio`.* FROM `naukio` WHERE ((`naukio`.`word` = ? OR `naukio`.`age` < ?) AND `naukio`.`paw` = ?)"; - let expected_params = vec![Value::text("meow"), Value::integer(10), Value::text("warm")]; + let expected_params = vec![Value::Text("meow"), Value::I32(10), Value::Text("warm")]; let conditions = Naukio::word .equals("meow") - .or(Naukio::age.less_than(10)) + .or(Naukio::age.less_than(10i32)) .and(Naukio::paw.equals("warm")); let query = Select::from_table(Naukio::table()).so_that(conditions); @@ -666,7 +653,7 @@ mod tests { let expected_sql = "SELECT `naukio`.* FROM `naukio` WHERE (NOT ((`naukio`.`word` = ? OR `naukio`.`age` < ?) AND `naukio`.`paw` = ?))"; - let expected_params = vec![Value::text("meow"), Value::integer(10), Value::text("warm")]; + let expected_params = vec![Value::Text("meow"), Value::I32(10), Value::Text("warm")]; let conditions = Naukio::word .equals("meow") @@ -687,7 +674,7 @@ mod tests { let expected_sql = "SELECT `naukio`.* FROM `naukio` WHERE (NOT ((`naukio`.`word` = ? OR `naukio`.`age` < ?) AND `naukio`.`paw` = ?))"; - let expected_params = vec![Value::text("meow"), Value::integer(10), Value::text("warm")]; + let expected_params = vec![Value::Text("meow"), Value::I32(10), Value::Text("warm")]; let conditions = ConditionTree::not( Naukio::word @@ -740,7 +727,7 @@ mod tests { let (sql, params) = Sqlite::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(default_params(vec![Value::boolean(true),]), params); + assert_eq!(default_params(vec![Value::Boolean(true),]), params); } #[test] @@ -769,7 +756,7 @@ mod tests { let (sql, params) = Sqlite::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(default_params(vec![Value::boolean(true),]), params); + assert_eq!(default_params(vec![Value::Boolean(true),]), params); } #[derive(Entity)] @@ -873,7 +860,7 @@ mod tests { #[test] fn test_raw_bytes() { let (sql, params) = - Sqlite::build(Select::default().value(Value::bytes(vec![1, 2, 3]).raw())).unwrap(); + Sqlite::build(Select::default().value(Value::Bytes(vec![1, 2, 3]).raw())).unwrap(); assert_eq!("SELECT x'010203'", sql); assert!(params.is_empty()); } @@ -889,6 +876,7 @@ mod tests { assert!(params.is_empty()); } + /* #[test] fn test_raw_char() { let (sql, params) = @@ -896,6 +884,7 @@ mod tests { assert_eq!("SELECT 'a'", sql); assert!(params.is_empty()); } + */ #[test] #[cfg(feature = "json")] From a9d83b056ec13a36086e48f3727e2a7463dddf01 Mon Sep 17 00:00:00 2001 From: songww Date: Thu, 26 Aug 2021 22:47:11 +0800 Subject: [PATCH 2/5] Fix json nagation (c1f7cf4c7074e8999d89f7d8d63476d3a2f13270) --- src/visitors/mysql.rs | 96 ++++++++++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 33 deletions(-) diff --git a/src/visitors/mysql.rs b/src/visitors/mysql.rs index 5bef8e0..8f87544 100644 --- a/src/visitors/mysql.rs +++ b/src/visitors/mysql.rs @@ -310,23 +310,25 @@ impl<'a> Visitor<'a> for Mysql<'a> { } fn visit_equals(&mut self, left: Expression<'a>, right: Expression<'a>) -> visitors::Result { - #[cfg(feature = "json")] + #[cfg(json)] { if right.is_json_value() || left.is_json_value() { - self.write("JSON_CONTAINS")?; - self.surround_with("(", ")", |s| { - s.visit_expression(left.clone())?; - s.write(", ")?; - s.visit_expression(right.clone()) - })?; - - self.write(" AND ")?; - - self.write("JSON_CONTAINS")?; - self.surround_with("(", ")", |s| { - s.visit_expression(right)?; - s.write(", ")?; - s.visit_expression(left) + self.surround_with("(", ")", |ref mut s| { + s.write("JSON_CONTAINS")?; + s.surround_with("(", ")", |s| { + s.visit_expression(left.clone())?; + s.write(", ")?; + s.visit_expression(right.clone()) + })?; + + s.write(" AND ")?; + + s.write("JSON_CONTAINS")?; + s.surround_with("(", ")", |s| { + s.visit_expression(right)?; + s.write(", ")?; + s.visit_expression(left) + }) }) } else { self.visit_regular_equality_comparison(left, right) @@ -344,30 +346,32 @@ impl<'a> Visitor<'a> for Mysql<'a> { left: Expression<'a>, right: Expression<'a>, ) -> visitors::Result { - #[cfg(feature = "json")] + #[cfg(json)] { if right.is_json_value() || left.is_json_value() { - self.write("NOT JSON_CONTAINS")?; - self.surround_with("(", ")", |s| { - s.visit_expression(left.clone())?; - s.write(", ")?; - s.visit_expression(right.clone()) - })?; - - self.write(" OR ")?; - - self.write("NOT JSON_CONTAINS")?; - self.surround_with("(", ")", |s| { - s.visit_expression(right)?; - s.write(", ")?; - s.visit_expression(left) + self.surround_with("(", ")", |ref mut s| { + s.write("NOT JSON_CONTAINS")?; + s.surround_with("(", ")", |s| { + s.visit_expression(left.clone())?; + s.write(", ")?; + s.visit_expression(right.clone()) + })?; + + s.write(" OR ")?; + + s.write("NOT JSON_CONTAINS")?; + s.surround_with("(", ")", |s| { + s.visit_expression(right)?; + s.write(", ")?; + s.visit_expression(left) + }) }) } else { self.visit_regular_difference_comparison(left, right) } } - #[cfg(not(feature = "json"))] + #[cfg(not(json))] { self.visit_regular_difference_comparison(left, right) } @@ -706,7 +710,7 @@ mod tests { #[test] fn equality_with_a_json_value() { let expected = expected_values( - r#"SELECT `users`.* FROM `users` WHERE JSON_CONTAINS(`users`.`jsonField`, ?) AND JSON_CONTAINS(?, `users`.`jsonField`)"#, + r#"SELECT `users`.* FROM `users` WHERE (JSON_CONTAINS(`users`.`jsonField`, ?) AND JSON_CONTAINS(?, `users`.`jsonField`))"#, vec![serde_json::json!({"a": "b"}), serde_json::json!({"a": "b"})], ); @@ -722,7 +726,7 @@ mod tests { #[test] fn difference_with_a_json_value() { let expected = expected_values( - r#"SELECT `users`.* FROM `users` WHERE NOT JSON_CONTAINS(`users`.`jsonField`, ?) OR NOT JSON_CONTAINS(?, `users`.`jsonField`)"#, + r#"SELECT `users`.* FROM `users` WHERE (NOT JSON_CONTAINS(`users`.`jsonField`, ?) OR NOT JSON_CONTAINS(?, `users`.`jsonField`))"#, vec![serde_json::json!({"a": "b"}), serde_json::json!({"a": "b"})], ); @@ -903,4 +907,30 @@ mod tests { sql ); } + + #[test] + #[cfg(feature = "json")] + fn test_json_negation() { + let conditions = + ConditionTree::not("json".equals(Value::Json(Some(serde_json::Value::Null)))); + let (sql, _) = Mysql::build(Select::from_table("test").so_that(conditions)).unwrap(); + + assert_eq!( + "SELECT `test`.* FROM `test` WHERE (NOT (JSON_CONTAINS(`json`, ?) AND JSON_CONTAINS(?, `json`)))", + sql + ); + } + + #[test] + #[cfg(feature = "json")] + fn test_json_not_negation() { + let conditions = + ConditionTree::not("json".not_equals(Value::Json(Some(serde_json::Value::Null)))); + let (sql, _) = Mysql::build(Select::from_table("test").so_that(conditions)).unwrap(); + + assert_eq!( + "SELECT `test`.* FROM `test` WHERE (NOT (NOT JSON_CONTAINS(`json`, ?) OR NOT JSON_CONTAINS(?, `json`)))", + sql + ); + } } From 7494e19b53a27066f99b69f24c91cdb05af0dd8c Mon Sep 17 00:00:00 2001 From: songww Date: Fri, 27 Aug 2021 23:56:43 +0800 Subject: [PATCH 3/5] Fix features for docs.rs --- Cargo.toml | 21 +- build.rs | 182 +++++++-------- src/ast/expression.rs | 13 +- src/ast/function/row_to_json.rs | 4 +- src/ast/insert.rs | 2 +- src/ast/values.rs | 346 ++++++++++++---------------- src/databases/mod.rs | 386 +++++++++++++++++++++++++------- src/error.rs | 24 +- src/lib.rs | 5 +- src/macros.rs | 20 -- src/visitors/mod.rs | 4 + src/visitors/mssql.rs | 2 +- src/visitors/mysql.rs | 50 ++--- src/visitors/postgres.rs | 51 +++-- src/visitors/sqlite.rs | 26 +-- 15 files changed, 620 insertions(+), 516 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 93ce4ad..90dfe4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,12 @@ keywords = ["orm", "sqlx", "sqlite", "mysql", "mssql", "postgresql"] license = "MIT OR Apache-2.0" readme = "README.md" +# docs.rs-specific configuration [package.metadata.docs.rs] -features = ["docs", "all"] +# document all features +all-features = true +# defines the configuration attribute `docsrs` +rustdoc-args = ["--cfg", "docsrs"] [lib] name = "xiayu" @@ -41,8 +45,8 @@ entities = { path = "./entity-examples", package = "xiayu-entity-examples" } cfg_aliases = "0.1" [features] -default = [ "sqlite", "mysql" ] -docs = [ "sqlx/runtime-tokio-rustls", "sqlx/mysql", "sqlx/mssql", "sqlx/sqlite", "sqlx/postgres", "json", "uuid", "chrono", "decimal", "bigdecimal" ] +default = [ "uuid", "json", "chrono", "decimal", "bigdecimal" ] +_docs = [ "sqlx/runtime-tokio-rustls", "sqlx/mysql", "sqlx/mssql", "sqlx/sqlite", "sqlx/postgres", "json", "uuid", "chrono", "decimal", "bigdecimal" ] mssql = [ "uuid", "chrono", "either", "sqlx/mssql", "indoc" ] mysql = [ "sqlx/mysql" ] sqlite = [ "sqlx/sqlite" ] @@ -53,17 +57,6 @@ chrono = [ "sqlx/chrono" ] decimal = [ "sqlx/decimal" ] bigdecimal = [ "num", "num-bigint", "sqlx/bigdecimal" ] -all = [ - "mssql", - "mysql", - "sqlite", - "postgres", - "json", - "uuid", - "chrono", - "bigdecimal", -] - [workspace] members = [ "derive", diff --git a/build.rs b/build.rs index d221b60..c45749e 100644 --- a/build.rs +++ b/build.rs @@ -1,97 +1,78 @@ use cfg_aliases::cfg_aliases; fn main() { + println!("cargo:rustc-cfg=docsrs"); // Setup cfg aliases cfg_aliases! { - docs: { feature = "docs" }, - }; - if cfg!(feature = "docs") { - cfg_aliases! { - only_mysql: { docs }, - only_postgres: { docs }, - only_sqlite: { docs }, - only_mssql: { docs }, - not_mssql: { docs }, - mysql_or_sqlite: { docs }, - json: { docs }, - uuid: { docs }, - bigdecimal: { docs }, - decimal: { docs }, - chrono: { docs }, - time: { docs }, - ipnetwork: { docs }, - } - } else { - cfg_aliases! { - only_mysql: { all( - feature = "mysql", - not(any(feature = "mssql", feature = "sqlite", feature = "postgres")) - ) }, - only_postgres: { all( - feature = "postgres", - not(any(feature = "mysql", feature = "mssql", feature = "sqlite")) - ) }, - only_sqlite: { all( - feature = "sqlite", - not(any(feature = "mysql", feature = "mssql", feature = "postgres")) - ) }, - only_mssql: { all( - feature = "mssql", - not(any(feature = "mysql", feature = "sqlite", feature = "postgres")) - ) }, - not_mssql: { all( - any(feature = "mysql", feature = "sqlite", feature = "postgres"), - not(feature = "mssql") - ) }, - mysql_or_sqlite: { all( - any(feature = "mysql", feature = "sqlite"), - not(any(feature = "mssql", feature = "postgres")) - ) }, - json: { all( - feature = "json", - any(feature = "postgres", feature = "mysql", feature = "sqlite"), - not(feature = "mssql") - ) }, - uuid: { all( - feature = "uuid", - any(feature = "mysql", feature = "sqlite", feature = "postgres"), - not(feature = "mssql") - ) }, - bigdecimal: { all( - feature = "bigdecimal", - any(feature = "mysql", feature = "postgres"), - not(any(feature = "sqlite", feature = "mssql")) - ) }, - decimal: { all( - feature = "decimal", - any(feature = "mysql", feature = "postgres"), - not(any(feature = "sqlite", feature = "mssql")) - ) }, - chrono: { all( - feature = "chrono", - any(feature = "mysql", feature = "sqlite", feature = "postgres"), - not(feature = "mssql") - ) }, - time: { all( - feature = "time", - not(any(feature = "mssql", feature = "sqlite")) - ) }, - ipnetwork: { all( - feature = "ipnetwork", - only_postgres - ) }, - } + only_mysql: { all( + feature = "mysql", + not(any(feature = "mssql", feature = "sqlite", feature = "postgres")) + ) }, + only_postgres: { all( + feature = "postgres", + not(any(feature = "mysql", feature = "mssql", feature = "sqlite")) + ) }, + only_sqlite: { all( + feature = "sqlite", + not(any(feature = "mysql", feature = "mssql", feature = "postgres")) + ) }, + only_mssql: { all( + feature = "mssql", + not(any(feature = "mysql", feature = "sqlite", feature = "postgres")) + ) }, + not_mssql: { all( + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") + ) }, + mysql_or_sqlite: { all( + any(feature = "mysql", feature = "sqlite"), + not(any(feature = "mssql", feature = "postgres")) + ) }, + json: { all( + feature = "json", + any(feature = "postgres", feature = "mysql", feature = "sqlite"), + not(feature = "mssql") + ) }, + uuid: { all( + feature = "uuid", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") + ) }, + bigdecimal: { all( + feature = "bigdecimal", + any(feature = "mysql", feature = "postgres"), + not(any(feature = "sqlite", feature = "mssql")) + ) }, + decimal: { all( + feature = "decimal", + any(feature = "mysql", feature = "postgres"), + not(any(feature = "sqlite", feature = "mssql")) + ) }, + chrono: { all( + feature = "chrono", + any(feature = "mysql", feature = "sqlite", feature = "postgres"), + not(feature = "mssql") + ) }, + time: { all( + feature = "time", + not(any(feature = "mssql", feature = "sqlite")) + ) }, + ipnetwork: { all( + feature = "ipnetwork", + only_postgres + ) }, + } - if cfg!(all(feature = "json", not(json))) { - println!("cargo:warning=feature `json` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); - } + if cfg!(all(feature = "json", not(json), not(docsrs))) { + println!("cargo:warning=feature `json` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); + } - if cfg!(all(feature = "uuid", not(uuid))) { - println!("cargo:warning=feature `uuid` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); - } + if cfg!(all(feature = "uuid", not(uuid), not(docsrs))) { + println!("cargo:warning=feature `uuid` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); + } - if cfg!(all(feature = "bigdecimal", not(bigdecimal))) { - println!("cargo:warning=feature `bigdecimal` still disabled, because it only support for `MySQL` and `PostgreSQL`, but `{}` is enabled .", + if cfg!(all(feature = "bigdecimal", not(bigdecimal), not(docsrs))) { + println!("cargo:warning=feature `bigdecimal` still disabled, because it only support for `MySQL` and `PostgreSQL`, but `{}` is enabled .", if cfg!(feature = "mssql") { "MSSQL" } else if cfg!(feature = "sqlite") { @@ -100,10 +81,10 @@ fn main() { unreachable!() } ); - } + } - if cfg!(all(feature = "decimal", not(decimal))) { - println!("cargo:warning=feature `decimal` still disabled, because it only support for `MySQL` and `PostgreSQL`, but `{}` is enabled .", + if cfg!(all(feature = "decimal", not(decimal), not(docsrs))) { + println!("cargo:warning=feature `decimal` still disabled, because it only support for `MySQL` and `PostgreSQL`, but `{}` is enabled .", if cfg!(feature = "mssql") { "MSSQL" } else if cfg!(feature = "sqlite") { @@ -112,19 +93,18 @@ fn main() { unreachable!() } ); - } + } - if cfg!(all(feature = "chrono", not(chrono))) { - println!("cargo:warning=feature `chrono` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); - } + if cfg!(all(feature = "chrono", not(chrono), not(docsrs))) { + println!("cargo:warning=feature `chrono` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); + } - /* - if cfg!(all(feature = "time", not(time))) { - println!("cargo:warning=feature `chrono` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); - } - */ - if cfg!(all(feature = "ipnetwork", not(ipnetwork))) { - println!("cargo:warning=feature `ipnetwork` still disabled, because it only support for `PostgreSQL`, but `MySQL` `Sqlite` or `MSSQL` is enabled ."); - } + /* + if cfg!(all(feature = "time", not(time))) { + println!("cargo:warning=feature `chrono` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); + } + */ + if cfg!(all(feature = "ipnetwork", not(ipnetwork), not(docsrs))) { + println!("cargo:warning=feature `ipnetwork` still disabled, because it only support for `PostgreSQL`, but `MySQL` `Sqlite` or `MSSQL` is enabled ."); } } diff --git a/src/ast/expression.rs b/src/ast/expression.rs index 3198fb5..95a6e8a 100644 --- a/src/ast/expression.rs +++ b/src/ast/expression.rs @@ -46,21 +46,22 @@ impl<'a> Expression<'a> { #[allow(dead_code)] pub(crate) fn is_json_value(&self) -> bool { match &self.kind { - #[cfg(json)] + #[cfg(feature = "json")] ExpressionKind::Parameterized(Value::Json(_)) => true, - #[cfg(json)] + #[cfg(feature = "json")] ExpressionKind::Value(expr) => expr.is_json_value(), _ => false, } } #[allow(dead_code)] - #[cfg(json)] + #[cfg(feature = "json")] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub(crate) fn into_json_value(self) -> Option { match self.kind { - #[cfg(json)] - ExpressionKind::Parameterized(Value::Json(Json::JsonValue(json_val))) => json_val, - #[cfg(json)] + #[cfg(feature = "json")] + ExpressionKind::Parameterized(Value::Json(json_val)) => serde_json::to_value(json_val).ok(), + #[cfg(feature = "json")] ExpressionKind::Value(expr) => expr.into_json_value(), _ => None, } diff --git a/src/ast/function/row_to_json.rs b/src/ast/function/row_to_json.rs index 915b310..8ff10bc 100644 --- a/src/ast/function/row_to_json.rs +++ b/src/ast/function/row_to_json.rs @@ -2,7 +2,7 @@ use super::Function; use crate::ast::Table; #[derive(Debug, Clone, PartialEq)] -#[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] +#[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] #[cfg(all(feature = "json", feature = "postgres"))] /// A representation of the `ROW_TO_JSON` function in the database. /// Only for `postgres` @@ -39,7 +39,7 @@ pub struct RowToJson<'a> { /// # Ok(()) /// # } /// ``` -#[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] +#[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] #[cfg(all(feature = "json", feature = "postgres"))] pub fn row_to_json<'a, T>(expr: T, pretty_print: bool) -> Function<'a> where diff --git a/src/ast/insert.rs b/src/ast/insert.rs index a6ace7a..88b08e0 100644 --- a/src/ast/insert.rs +++ b/src/ast/insert.rs @@ -231,7 +231,7 @@ impl<'a> Insert<'a> { /// ``` #[cfg(any(feature = "postgres", feature = "mssql", feature = "sqlite"))] #[cfg_attr( - feature = "docs", + docsrs, doc(cfg(any(feature = "postgres", feature = "mssql", feature = "sqlite"))) )] pub fn returning(mut self, columns: I) -> Self diff --git a/src/ast/values.rs b/src/ast/values.rs index 11a9421..7e4664a 100644 --- a/src/ast/values.rs +++ b/src/ast/values.rs @@ -3,15 +3,15 @@ use std::borrow::{Borrow, Cow}; use std::convert::TryFrom; use std::{fmt, option}; -#[cfg(bigdecimal)] +#[cfg(feature = "bigdecimal")] use num::{FromPrimitive, ToPrimitive}; -#[cfg(json)] +#[cfg(feature = "json")] use serde_json::{Number, Value as JsonValue}; -#[cfg(chrono)] +#[cfg(feature = "chrono")] use sqlx::types::chrono::{self, DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; -#[cfg(bigdecimal)] +#[cfg(feature = "bigdecimal")] use sqlx::types::BigDecimal; -#[cfg(uuid)] +#[cfg(feature = "uuid")] use sqlx::types::Uuid; use crate::ast::*; @@ -55,110 +55,11 @@ trait PgCompatibleType: // } // } -/* -#[cfg(feature = "json")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] -#[derive(Debug, Clone, PartialEq)] -pub enum Json<'a> { - // #[cfg(feature = "postgres")] - // #[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] - // Json(Option>>>), - #[cfg(any(feature = "mysql", feature = "postgres"))] - JsonValue(Option), - #[cfg(feature = "postgres")] - JsonRawValue(Option>), -} - -#[cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))] -#[cfg_attr( - feature = "docs", - doc(cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))) -)] -impl<'a> From for Json<'a> { - fn from(v: JsonValue) -> Self { - Json::JsonValue(Some(v)) - } -} - -#[cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))] -#[cfg_attr( - feature = "docs", - doc(cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))) -)] -impl<'a> From> for Json<'a> { - fn from(v: Option) -> Self { - Json::JsonValue(v) - } -} - -#[cfg(all(feature = "json", feature = "postgres"))] -#[cfg_attr( - feature = "docs", - doc(cfg(all(feature = "json", feature = "postgres"))) -)] -impl<'a> From>> for Json<'a> { - fn from(v: Option>) -> Self { - Json::JsonRawValue(v) - } -} - -#[cfg(all(feature = "json", feature = "postgres"))] -#[cfg_attr( - feature = "docs", - doc(cfg(all(feature = "json", feature = "postgres"))) -)] -impl<'a> From> for Json<'a> { - fn from(v: JsonRawValue<'a>) -> Self { - Json::JsonRawValue(Some(v)) - } -} - -#[cfg(all(feature = "json", feature = "postgres"))] -#[cfg_attr( - feature = "docs", - doc(cfg(all(feature = "json", feature = "postgres"))) -)] -impl<'a> From<&'a serde_json::value::RawValue> for JsonRawValue<'a> { - fn from(v: &'a serde_json::value::RawValue) -> Self { - JsonRawValue(v) - } -} - -#[cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))] -#[cfg_attr( - feature = "docs", - doc(cfg(all(feature = "json", any(feature = "mysql", feature = "postgres")))) -)] -impl<'a> Json<'a> { - pub const fn is_none(&self) -> bool { - match self { - Json::JsonValue(v) => v.is_none(), - #[cfg(feature = "postgres")] - Json::JsonRawValue(v) => v.is_none(), - } - } -} - -#[cfg(feature = "json")] -#[derive(Debug, Clone, AsRef, Deref)] -pub struct JsonRawValue<'a>(&'a serde_json::value::RawValue); - -#[cfg(feature = "json")] -impl<'a> PartialEq for JsonRawValue<'a> { - fn eq(&self, other: &Self) -> bool { - self.0.get() == other.0.get() - } -} -*/ - -#[cfg(json)] -pub type Json = sqlx::types::Json; - /// A value we must parameterize for the prepared statement. Null values should be /// defined by their corresponding type variants with a `None` value for best /// compatibility. #[derive(Debug, Clone, PartialEq)] -pub enum Value<'a> { +pub enum Value<'a, J: serde::ser::Serialize = ()> { I8(Option), I16(Option), I32(Option), @@ -170,105 +71,102 @@ pub enum Value<'a> { /// String value. Text(Option>), /// Bytes value. - #[cfg(not_mssql)] - #[cfg_attr(docs, doc(cfg(not_mssql)))] + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] + #[cfg_attr( + docsrs, + doc(cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))) + )] Bytes(Option>), /// Boolean value. Boolean(Option), - #[cfg(all( - any(feature = "mysql", feature = "sqlite"), - not(any(feature = "mssql", feature = "postgres")) - ))] - #[cfg_attr( - docs, - doc(cfg(all( - any(feature = "mysql", feature = "sqlite"), - not(any(feature = "mssql", feature = "postgres")) - ))) - )] + #[cfg(all(any(docsrs, feature = "mysql", feature = "sqlite"),))] + #[cfg_attr(docsrs, doc(cfg(all(any(feature = "mysql", feature = "sqlite"),))))] U8(Option), - #[cfg(mysql_or_sqlite)] - #[cfg_attr(feature = "docs", doc(cfg(mysql_or_sqlite)))] + #[cfg(all(any(docsrs, feature = "mysql", feature = "sqlite"),))] + #[cfg_attr(docsrs, doc(cfg(all(any(feature = "mysql", feature = "sqlite"),))))] U16(Option), - #[cfg(not_mssql)] - #[cfg_attr(feature = "docs", doc(cfg(not_mssql)))] + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] + #[cfg_attr( + docsrs, + doc(cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))) + )] U32(Option), - #[cfg(only_mysql)] - #[cfg_attr(feature = "docs", doc(cfg(only_mysql)))] + #[cfg(feature = "mysql")] + #[cfg_attr(docsrs, doc(cfg(feature = "mysql")))] U64(Option), /* /// Database enum value. Enum(Option>), /// A single character. Char(Option), - #[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] + #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] /// An array value (PostgreSQL). Array(Option>>), /// A numeric value. /// A XML value. Xml(Option>), */ - #[cfg(json)] - #[cfg_attr(feature = "docs", doc(cfg(json)))] + #[cfg(feature = "json")] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] /// A JSON value. - Json(Option), - #[cfg(uuid)] - #[cfg_attr(feature = "docs", doc(cfg(uuid)))] + Json(Option>), + #[cfg(feature = "uuid")] + #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] /// An UUID value. Uuid(Option), - #[cfg(only_postgres)] - #[cfg_attr(feature = "docs", doc(cfg(only_postgres)))] + #[cfg(feature = "postgres")] + #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] /// INTERVAL PgInterval(Option), /* - #[cfg(only_postgres)] - #[cfg_attr(feature = "docs", doc(cfg(only_postgres)))] + #[cfg(feature = "postgres")] + #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] /// INT8RANGE, INT4RANGE, TSRANGE, TSTZTRANGE, DATERANGE, NUMRANGE PgRange(Option>>>), */ /// INT8RANGE, INT4RANGE, TSRANGE, TSTZTRANGE, DATERANGE, NUMRANGE - #[cfg(only_postgres)] - #[cfg_attr(feature = "docs", doc(cfg(only_postgres)))] + #[cfg(feature = "postgres")] + #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] PgMoney(Option), /// A numeric value. - #[cfg(bigdecimal)] - #[cfg_attr(feature = "docs", doc(cfg(bigdecimal)))] + #[cfg(feature = "bigdecimal")] + #[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))] BigDecimal(Option), /// A numeric value. - #[cfg(decimal)] - #[cfg_attr(feature = "docs", doc(cfg(decimal)))] + #[cfg(feature = "decimal")] + #[cfg_attr(docsrs, doc(cfg(feature = "decimal")))] Decimal(Option), /// TIMESTAMPTZ - #[cfg(chrono)] - #[cfg_attr(feature = "docs", doc(cfg(chrono)))] + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] UtcDateTime(Option>), /// TIMESTAMPTZ - #[cfg(chrono)] - #[cfg_attr(feature = "docs", doc(cfg(chrono)))] + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] LocalDateTime(Option>), /// TIMESTAMP - #[cfg(chrono)] - #[cfg_attr(feature = "docs", doc(cfg(chrono)))] + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] NaiveDateTime(Option), /// DATE - #[cfg(chrono)] - #[cfg_attr(feature = "docs", doc(cfg(chrono)))] + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] NaiveDate(Option), /// TIME - #[cfg(chrono)] - #[cfg_attr(feature = "docs", doc(cfg(chrono)))] + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] NaiveTime(Option), /// TIMETZ - #[cfg(all(time, only_postgres))] - #[cfg_attr(feature = "docs", doc(cfg(all(time, only_postgres))))] + #[cfg(all(feature = "time", feature = "postgres"))] + #[cfg_attr(docsrs, doc(cfg(all(feature = "time", feature = "postgres"))))] PgTimeTz(Option), /// INET, CIDR - #[cfg(ipnetwork)] - #[cfg_attr(feature = "docs", doc(cfg(ipnetwork)))] + #[cfg(feature = "ipnetwork")] + #[cfg_attr(docsrs, doc(cfg(feature = "ipnetwork")))] IpNetwork(Option), } @@ -342,7 +240,7 @@ impl<'a> fmt::Display for Value<'a> { } #[cfg(feature = "json")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] +#[cfg_attr(docsrs, doc(cfg(feature = "json")))] impl<'a> From> for serde_json::Value { fn from(pv: Value<'a>) -> Self { let res = match pv { @@ -418,8 +316,11 @@ impl<'a> Value<'a> { } /// Creates a new bytes value. - #[cfg(not_mssql)] - #[cfg_attr(feature = "docs", doc(cfg(not_mssql)))] + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] + #[cfg_attr( + docsrs, + doc(cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))) + )] pub fn bytes(value: B) -> Self where B: Into>, @@ -447,37 +348,37 @@ impl<'a> Value<'a> { */ /// Creates a new uuid value. - #[cfg(uuid)] - #[cfg_attr(feature = "docs", doc(cfg(uuid)))] + #[cfg(feature = "uuid")] + #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] pub const fn uuid(value: Uuid) -> Self { Value::Uuid(Some(value)) } /* /// Creates a new datetime value. - #[cfg(chrono)] - #[cfg_attr(feature = "docs", doc(cfg(chrono)))] + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub const fn datetime(value: DateTime) -> Self { Value::DateTime(Some(value)) } /// Creates a new date value. - #[cfg(chrono)] - #[cfg_attr(feature = "docs", doc(cfg(chrono)))] + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub const fn date(value: NaiveDate) -> Self { Value::Date(Some(value)) } /// Creates a new time value. #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub const fn time(value: NaiveTime) -> Self { Value::Time(Some(value)) } /// Creates a new JSON value. #[cfg(feature = "json")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub const fn json(value: Json<'a>) -> Self { Value::Json(value) } @@ -619,7 +520,7 @@ impl<'a> Value<'a> { /* /// `true` if the `Value` is a numeric value or can be converted to one. #[cfg(feature = "bigdecimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] + #[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))] pub const fn is_numeric(&self) -> bool { matches!(self, Value::Numeric(_) | Value::Float(_) | Value::Double(_)) } @@ -627,7 +528,7 @@ impl<'a> Value<'a> { /// Returns a bigdecimal, if the value is a numeric, float or double value, /// otherwise `None`. #[cfg(feature = "bigdecimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] + #[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))] pub fn into_numeric(self) -> Option { match self { Value::Numeric(d) => d, @@ -640,7 +541,7 @@ impl<'a> Value<'a> { /// Returns a reference to a bigdecimal, if the value is a numeric. /// Otherwise `None`. #[cfg(feature = "bigdecimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] + #[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))] pub const fn as_numeric(&self) -> Option<&BigDecimal> { match self { Value::Numeric(d) => d.as_ref(), @@ -675,14 +576,14 @@ impl<'a> Value<'a> { /// `true` if the `Value` is of UUID type. #[cfg(feature = "uuid")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] + #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] pub const fn is_uuid(&self) -> bool { matches!(self, Value::Uuid(_)) } /// Returns an UUID if the value is of UUID type, otherwise `None`. #[cfg(feature = "uuid")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] + #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] pub const fn as_uuid(&self) -> Option { match self { Value::Uuid(u) => *u, @@ -692,14 +593,14 @@ impl<'a> Value<'a> { /// `true` if the `Value` is a DateTime. #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub const fn is_datetime(&self) -> bool { matches!(self, Value::DateTime(_)) } /// Returns a `DateTime` if the value is a `DateTime`, otherwise `None`. #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub const fn as_datetime(&self) -> Option> { match self { Value::DateTime(dt) => *dt, @@ -709,14 +610,14 @@ impl<'a> Value<'a> { /// `true` if the `Value` is a Date. #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub const fn is_date(&self) -> bool { matches!(self, Value::Date(_)) } /// Returns a `NaiveDate` if the value is a `Date`, otherwise `None`. #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub const fn as_date(&self) -> Option { match self { Value::Date(dt) => *dt, @@ -726,14 +627,14 @@ impl<'a> Value<'a> { /// `true` if the `Value` is a `Time`. #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub const fn is_time(&self) -> bool { matches!(self, Value::Time(_)) } /// Returns a `NaiveTime` if the value is a `Time`, otherwise `None`. #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] pub const fn as_time(&self) -> Option { match self { Value::Time(time) => *time, @@ -743,28 +644,27 @@ impl<'a> Value<'a> { /// `true` if the `Value` is a JSON value. #[cfg(feature = "json")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub const fn is_json(&self) -> bool { matches!(self, Value::Json(_)) } /// Returns a reference to a JSON Value if of Json type, otherwise `None`. #[cfg(feature = "json")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] - pub const fn as_json(&self) -> Option<&serde_json::Value> { + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] + pub const fn as_json(&self) -> Option<&Json> { match self { - Value::Json(Json::JsonValue(Some(j))) => Some(j), + Value::Json(j) => j, _ => None, } } /// Transforms to a JSON Value if of Json type, otherwise `None`. #[cfg(feature = "json")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] - pub fn into_json(self) -> Option { + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] + pub fn into_json(self) -> Option { match self { - Value::Json(Json::JsonValue(Some(j))) => Some(j), - Value::Json(Json::JsonRawValue(Some(j))) => serde_json::to_value(*j).ok(), + Value::Json(j) => *j, _ => None, } } @@ -793,41 +693,77 @@ value!(val: i8, I8, val); value!(val: i16, I16, val); value!(val: i32, I32, val); value!(val: i64, I64, val); -#[cfg(mysql_or_sqlite)] +#[cfg(any(features = "mysql", feature = "sqlite"))] +#[cfg_attr(docsrs, doc(cfg(any(features = "mysql", feature = "sqlite"))))] value!(val: u8, U8, val); -#[cfg(mysql_or_sqlite)] +#[cfg(any(features = "mysql", feature = "sqlite"))] +#[cfg_attr(docsrs, doc(cfg(any(features = "mysql", feature = "sqlite"))))] value!(val: u16, U16, val); -#[cfg(not_mssql)] +#[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] +#[cfg_attr( + docsrs, + doc(cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))) +)] value!(val: u32, U32, val); -#[cfg(only_mysql)] +#[cfg(feature = "mysql")] +#[cfg_attr(docsrs, doc(cfg(feature = "mysql")))] value!(val: u64, U64, val); value!(val: bool, Boolean, val); value!(val: &'a str, Text, val.into()); value!(val: String, Text, val.into()); value!(val: usize, I64, i64::try_from(val).unwrap()); -#[cfg(not_mssql)] +#[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] +#[cfg_attr( + docsrs, + doc(cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))) +)] value!(val: &'a [u8], Bytes, val.into()); value!(val: f64, Double, val); value!(val: f32, Float, val); -#[cfg(chrono)] -#[cfg_attr(feature = "docs", doc(cfg(chrono)))] -value!(val: DateTime, UtcDateTime, val); -#[cfg(chrono)] -#[cfg_attr(feature = "docs", doc(cfg(chrono)))] -value!(val: chrono::NaiveTime, Time, val); -#[cfg(chrono)] -#[cfg_attr(feature = "docs", doc(cfg(chrono)))] -value!(val: chrono::NaiveDate, Date, val); -#[cfg(bigdecimal)] +#[cfg(feature = "chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] +value!(val: chrono::NaiveTime, NaiveTime, val); +#[cfg(feature = "chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] +value!(val: chrono::NaiveDate, NaiveDate, val); +#[cfg(feature = "chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] +value!(val: chrono::NaiveDateTime, NaiveDateTime, val); +#[cfg(feature = "chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] +value!(val: chrono::DateTime, UtcDateTime, val); +#[cfg(feature = "chrono")] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] +value!(val: chrono::DateTime, LocalDateTime, val); +#[cfg(feature = "bigdecimal")] value!(val: BigDecimal, BigDecimal, val); -#[cfg(json)] -#[cfg_attr(feature = "docs", doc(cfg(json)))] -value!(val: JsonValue, Json, Json(val)); -#[cfg(uuid)] -#[cfg_attr(feature = "docs", doc(cfg(uuid)))] +#[cfg(feature = "uuid")] +#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] value!(val: Uuid, Uuid, val); +#[cfg(feature = "json")] +#[cfg_attr(docsrs, doc(cfg(feature = "json")))] +impl<'a, T> From for Value<'a, T> +where + T: serde::ser::Serialize, +{ + fn from(val: T) -> Self { + Value::Json(Some(sqlx::types::Json(val))) + } +} + +#[cfg(feature = "json")] +#[cfg_attr(docsrs, doc(cfg(feature = "json")))] +impl<'a, T> From> for Value<'a, T> +where + T: serde::ser::Serialize, +{ + fn from(val: sqlx::types::Json) -> Self { + Value::Json(Some(val)) + } +} + /* impl<'a> TryFrom> for i64 { type Error = Error; @@ -881,7 +817,7 @@ impl<'a> TryFrom> for bool { } #[cfg(feature = "chrono")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] +#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] impl<'a> TryFrom> for DateTime { type Error = Error; diff --git a/src/databases/mod.rs b/src/databases/mod.rs index ff7a83c..4dd8c8d 100644 --- a/src/databases/mod.rs +++ b/src/databases/mod.rs @@ -49,121 +49,358 @@ impl<'a> HasVisitor<'a> for sqlx::Sqlite { } } -// pub struct Values<'a>(Vec>); +/// Values that for mysql +#[cfg(feature = "mysql")] +struct MyValue { + // +} -macro_rules! bind_value { - ($query:ident, $value: ident) => { +macro_rules! try_bind_value { + ($query:ident, $value: ident, $driver: literal) => { match $value { - Value::I8(int8) => $query.bind(int8), - Value::I16(int16) => $query.bind(int16), - Value::I32(int32) => $query.bind(int32), - Value::I64(int64) => $query.bind(int64), - Value::Float(float) => $query.bind(float) , - Value::Double(double) => $query.bind(double), - Value::Boolean(boolean) => $query.bind(boolean), - Value::Text(text) => $query.bind(text.map(|text|text.into_owned())), - #[cfg(not_mssql)] - Value::Bytes(bytes) => $query.bind(bytes.map(|b|b.into_owned())), - - #[cfg(mysql_or_sqlite)] - Value::U8(uint8) => $query.bind(uint8), - #[cfg(mysql_or_sqlite)] - Value::U16(uint16) => $query.bind(uint16), - #[cfg(not_mssql)] - Value::U32(uint32) => $query.bind(uint32), - #[cfg(only_mysql)] - Value::U64(uint64) => $query.bind(uint64), - #[cfg(json)] - Value::Json(json) => $query.bind(json), - #[cfg(uuid)] - Value::Uuid(uuid) => $query.bind(uuid), - #[cfg(only_postgres)] - Value::PgInterval(interval) => $query.bind(interval), - // #[cfg(only_postgres)] + Value::I8(int8) => Ok($query.bind(int8)), + Value::I16(int16) => Ok($query.bind(int16)), + Value::I32(int32) => Ok($query.bind(int32)), + Value::I64(int64) => Ok($query.bind(int64)), + Value::Float(float) => Ok($query.bind(float)) , + Value::Double(double) => Ok($query.bind(double)), + Value::Boolean(boolean) => Ok($query.bind(boolean)), + Value::Text(text) => Ok($query.bind(text.map(|text|text.into_owned()))), + #[cfg(all(any(feature = "mysql", feature = "sqlite", feature = "postgres")))] + Value::Bytes(bytes) => match $driver { + "mysql" | "sqlite" | "postgres" => { + Ok($query.bind(bytes.map(|b|b.into_owned()))) + }, + "mssql" | _ => { + let msg = "Bytes are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(any(feature = "mysql", feature = "sqlite"))] + Value::U8(uint8) => match $driver { + "mysql" | "sqlite" => { + Ok($query.bind(uint8)) + }, + "mssql" | "postgres" | _ => { + let msg = "u8 are not supported by SQLx with SQL Server/PostgreSQL."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(any(feature = "mysql", feature = "sqlite"))] + Value::U16(uint16) => match $driver { + "mysql" | "sqlite" => { + Ok($query.bind(uint16)) + }, + "mssql" | "postgres" | _ => { + let msg = "u16 are not supported by SQLx with SQL Server/PostgreSQL."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + } + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] + Value::U32(uint32) => match $driver { + "mysql" | "sqlite" | "postgres" => { + Ok($query.bind(uint32)) + }, + "mssql" | _ => { + let msg = "u32 are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "mysql")] + Value::U64(uint64) => match $driver { + "mysql" => { + Ok($query.bind(uint64)) + }, + "mssql" | "sqlite" | "postgres" | _ => { + let msg = "u64 are only supported by SQLx with MySQL, but not Sqlite/PostgreSQL/SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "json")] + Value::Json(json) => match $driver { + "mysql" | "sqlite" | "postgres" => { + Ok($query.bind(json)) + }, + "mssql" | _ => { + let msg = "JsonType are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "uuid")] + Value::Uuid(uuid) => match $driver { + "mysql" | "sqlite" | "postgres" => { + Ok($query.bind(uuid)) + }, + "mssql" | _ => { + let msg = "UUID Type are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "postgres")] + Value::PgInterval(interval) => match $driver { + "postgres" => { + Ok($query.bind(interval)) + }, + "mssql" | "mysql" | "sqlite" | _ => { + let msg = "PgInterval are only supported by SQLx with PostgreSQL, but not Sqlite / MySQL / SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + // #[cfg(feature = "postgres")] // Value::PgRange(range) => $query.bind(range), - #[cfg(only_postgres)] - Value::PgMoney(money) => $query.bind(money), - #[cfg(bigdecimal)] - Value::BigDecimal(bigdecimal) => $query.bind(bigdecimal), - #[cfg(decimal)] - Value::Decimal(decimal) => $query.bind(decimal), - #[cfg(chrono)] - Value::UtcDateTime(datetime) => $query.bind(datetime), - #[cfg(chrono)] - Value::LocalDateTime(datetime) => $query.bind(datetime), - #[cfg(chrono)] - Value::NaiveDateTime(datetime) => $query.bind(datetime), - #[cfg(chrono)] - Value::NaiveDate(date) => $query.bind(date), - #[cfg(chrono)] - Value::NaiveTime(time) => $query.bind(time), - #[cfg(all(time, only_postgres))] - Value::PgTimeTz(timetz) => $query.bind(timetz), - #[cfg(ipnetwork)] - Value::IpNetwork(ipnetwork) => $query.bind(ipnetwork), + #[cfg(feature = "postgres")] + Value::PgMoney(money) => match $driver { + "postgres" => { + Ok($query.bind(money)) + }, + "mssql" | "mysql" | "sqlite" | _ => { + let msg = "PgMoney are only supported by SQLx with PostgreSQL, but not Sqlite / MySQL / SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "bigdecimal")] + Value::BigDecimal(bigdecimal) => match $driver { + "postgres" | "mysql" => { + Ok($query.bind(bigdecimal)) + }, + "mssql" | "sqlite" | _ => { + let msg = "BigDecimal are only supported by SQLx with PostgreSQL / MySQL, but not Sqlite / SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "decimal")] + Value::Decimal(decimal) => match $driver { + "postgres" | "mysql" => { + Ok($query.bind(decimal)) + }, + "mssql" | "sqlite" | _ => { + let msg = "Decimal are only supported by SQLx with PostgreSQL / MySQL, but not Sqlite / SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "chrono")] + Value::UtcDateTime(datetime) => match $driver { + "mysql" | "sqlite" | "postgres" => { + Ok($query.bind(datetime)) + }, + "mssql" | _ => { + let msg = "DateTime are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "chrono")] + Value::LocalDateTime(datetime) => match $driver { + "mysql" | "sqlite" | "postgres" => { + Ok($query.bind(datetime)) + }, + "mssql" | _ => { + let msg = "DataTime are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "chrono")] + Value::NaiveDateTime(datetime) => match $driver { + "mysql" | "sqlite" | "postgres" => { + Ok($query.bind(datetime)) + }, + "mssql" | _ => { + let msg = "NaiveDateTime are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "chrono")] + Value::NaiveDate(date) => match $driver { + "mysql" | "sqlite" | "postgres" => { + Ok($query.bind(date)) + }, + "mssql" | _ => { + let msg = "NaiveDate are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "chrono")] + Value::NaiveTime(time) => match $driver { + "mysql" | "sqlite" | "postgres" => { + Ok($query.bind(time)) + }, + "mssql" | _ => { + let msg = "NaiveTime are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(all(feature = "time", feature = "postgres"))] + Value::PgTimeTz(timetz) => match $driver { + "postgres" => { + Ok($query.bind(timetz)) + }, + "mysql" | "sqlite" | "mssql" | _ => { + let msg = "PgTimeTz are only supported by SQLx with PostgreSQL, but not Sqlite / MySQL / SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, + #[cfg(feature = "ipnetwork")] + Value::IpNetwork(ipnetwork) => match $driver { + "postgres" => { + Ok($query.bind(ipnetwork)) + }, + "mysql" | "sqlite" | "mssql" | _ => { + let msg = "IpNetwork are only supported by SQLx with PostgreSQL, but not Sqlite / MySQL / SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + }, } - }; + } } pub trait Binder<'a, DB> where DB: sqlx::Database { - fn bind_value(self, value: Value<'a>) -> Self + fn try_bind_value(self, value: Value<'a>) -> crate::Result where Self: Sized; } #[cfg(feature = "postgres")] impl<'a> Binder<'a, sqlx::Postgres> for sqlx::query::Query<'a, sqlx::Postgres, sqlx::postgres::PgArguments> { - fn bind_value(self, value: Value<'a>) -> Self { - bind_value!(self, value) + fn try_bind_value(self, value: Value<'a>) -> crate::Result { + try_bind_value!(self, value, "postgres") } } #[cfg(feature = "postgres")] impl<'a, O> Binder<'a, sqlx::Postgres> for sqlx::query::QueryAs<'a, sqlx::Postgres, O, sqlx::postgres::PgArguments> { - fn bind_value(self, value: Value<'a>) -> Self { - bind_value!(self, value) + fn try_bind_value(self, value: Value<'a>) -> crate::Result { + try_bind_value!(self, value, "postgres") } } #[cfg(feature = "mysql")] impl<'a> Binder<'a, sqlx::MySql> for sqlx::query::Query<'a, sqlx::MySql, sqlx::mysql::MySqlArguments> { - fn bind_value(self, value: Value<'a>) -> Self { - bind_value!(self, value) + fn try_bind_value(self, value: Value<'a>) -> crate::Result { + try_bind_value!(self, value, "mysql") } } #[cfg(feature = "mysql")] impl<'a, O> Binder<'a, sqlx::MySql> for sqlx::query::QueryAs<'a, sqlx::MySql, O, sqlx::mysql::MySqlArguments> { - fn bind_value(self, value: Value<'a>) -> Self { - bind_value!(self, value) + fn try_bind_value(self, value: Value<'a>) -> crate::Result { + try_bind_value!(self, value, "mysql") } } #[cfg(feature = "mssql")] impl<'a> Binder<'a, sqlx::Mssql> for sqlx::query::Query<'a, sqlx::Mssql, sqlx::mssql::MssqlArguments> { - fn bind_value(self, value: Value<'a>) -> Self { - bind_value!(self, value) + fn try_bind_value(self, value: Value<'a>) -> crate::Result { + try_bind_value!(self, value, "mssql") } } #[cfg(feature = "mssql")] impl<'a, O> Binder<'a, sqlx::Mssql> for sqlx::query::QueryAs<'a, sqlx::Mssql, O, sqlx::mssql::MssqlArguments> { - fn bind_value(self, value: Value<'a>) -> Self { - bind_value!(self, value) + fn try_bind_value(self, value: Value<'a>) -> crate::Result { + try_bind_value!(self, value, "mssql") } } #[cfg(feature = "sqlite")] impl<'a> Binder<'a, sqlx::Sqlite> for sqlx::query::Query<'a, sqlx::Sqlite, sqlx::sqlite::SqliteArguments<'a>> { - fn bind_value(self, value: Value<'a>) -> Self { - bind_value!(self, value) + fn try_bind_value(self, value: Value<'a>) -> crate::Result { + try_bind_value!(self, value, "sqlite") } } #[cfg(feature = "sqlite")] impl<'a, O> Binder<'a, sqlx::Sqlite> for sqlx::query::QueryAs<'a, sqlx::Sqlite, O, sqlx::sqlite::SqliteArguments<'a>> { - fn bind_value(self, value: Value<'a>) -> Self { - bind_value!(self, value) + fn try_bind_value(self, value: Value<'a>) -> crate::Result { + try_bind_value!(self, value, "sqlite") } } @@ -190,7 +427,7 @@ impl SelectingExecution { self.compiled.replace(query); let mut query = sqlx::query_as::(self.compiled.as_ref().unwrap()); for parameter in parameters { - query = query.bind_value(parameter); + query = query.try_bind_value(parameter)?; } let v = query.fetch_one(conn).await?; @@ -246,8 +483,7 @@ impl<'e, E: HasPrimaryKey, DB: Database> DeletingExecution<'e, E, DB> { self.compiled.replace(compiled); let mut query = sqlx::query::(self.compiled.as_ref().unwrap()); for parameter in parameters { - // query = bind_value!(query, parameter); - query = query.bind_value(parameter); + query = query.try_bind_value(parameter)?; } let _query_result = query @@ -293,7 +529,7 @@ impl<'e, E: HasPrimaryKey, DB: Database> SavingExecution<'e, E, DB> { self.compiled.replace(compiled); let mut query = sqlx::query::(self.compiled.as_ref().unwrap()); for parameter in parameters { - query = query.bind_value(parameter); + query = query.try_bind_value(parameter)?; } let _query_result = query .execute(conn) @@ -408,14 +644,11 @@ macro_rules! impl_executioner_for { let (compiled, parameters) = <$database as HasVisitor>::Visitor::build(request.saving.clone())?; // 'a for borrowed from self.compiled - // println!("compiled saving: {}", &compiled); - // println!("parameters ---> {:?}", parameters); request.compiled.replace(compiled); let mut query = sqlx::query::<$database>(request.compiled.as_ref().unwrap()); for parameter in parameters { - query = query.bind_value(parameter); + query = query.try_bind_value(parameter)?; } - // println!("query ---> {:?}", query); let _query_result = self.execute(query).await?; Ok(()) } @@ -430,9 +663,8 @@ macro_rules! impl_executioner_for { request.compiled.replace(compiled); let mut query = sqlx::query::<$database>(request.compiled.as_ref().unwrap()); for parameter in parameters { - query = query.bind_value(parameter); + query = query.try_bind_value(parameter)?; } - // println!("query ---> {:?}", query); let query_result = self.execute(query).await?; Ok(query_result) } diff --git a/src/error.rs b/src/error.rs index dadd154..5135984 100644 --- a/src/error.rs +++ b/src/error.rs @@ -287,11 +287,6 @@ pub enum ErrorKind { #[error("Value out of range error. {}", message)] ValueOutOfRange { message: String }, - #[cfg(feature = "serde-support")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "serde-support")))] - #[error("Deserializing a ResultRow {:?}", _0)] - FromRowError(serde::de::value::Error), - #[error( "Incorrect number of parameters given to a statement. Expected {}: got: {}.", expected, @@ -331,7 +326,7 @@ impl From for ErrorKind { /* #[cfg(feature = "bigdecimal")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] +#[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))] impl From for Error { fn from(e: bigdecimal::ParseBigDecimalError) -> Self { let kind = ErrorKind::conversion(format!("{}", e)); @@ -341,7 +336,7 @@ impl From for Error { */ #[cfg(feature = "json")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] +#[cfg_attr(docsrs, doc(cfg(feature = "json")))] impl From for Error { fn from(_: serde_json::Error) -> Self { Self::builder(ErrorKind::conversion("Malformed JSON data.")).build() @@ -391,21 +386,6 @@ impl From for Error { } } -/* -impl From for Error { - fn from(err: connection_string::Error) -> Error { - Self::builder(ErrorKind::DatabaseUrlIsInvalid(err.to_string())).build() - } -} - -impl From for Error { - fn from(e: url::ParseError) -> Error { - let kind = ErrorKind::DatabaseUrlIsInvalid(e.to_string()); - Error::builder(kind).build() - } -} -8*/ - impl From for Error { fn from(e: io::Error) -> Error { Error::builder(ErrorKind::IoError(e)).build() diff --git a/src/lib.rs b/src/lib.rs index f67b2c0..12be540 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ -#![cfg_attr(feature = "docs", feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(not(any( - feature = "docs", + docsrs, feature = "sqlite", feature = "postgres", feature = "mysql", @@ -9,7 +9,6 @@ )))] compile_error!("one of 'sqlite', 'postgres', 'mysql' or 'mssql' features must be enabled"); - #[macro_use] extern crate derive_more; #[macro_use] diff --git a/src/macros.rs b/src/macros.rs index d63c10c..0dc7a3a 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -88,26 +88,6 @@ macro_rules! val { }; } -macro_rules! impl_value_from_json { - ($target:ident: $kind:ty,$paramkind:ident,$that:expr) => { - impl<'a> From<$kind> for crate::ast::Value<'a> { - fn from(that: $kind) -> Self { - let $target = that; - crate::ast::Value::Json(crate::ast::Json::$paramkind(Some($that))) - } - } - - impl<'a> From> for crate::ast::Value<'a> { - fn from(that: Option<$kind>) -> Self { - match that { - Some(val) => crate::ast::Value::Json(crate::ast::Json::$paramkind(Some(val))), - None => crate::ast::Value::Json(crate::ast::Json::$paramkind(None)), - } - } - } - }; -} - macro_rules! value { ($target:ident: $kind:ty,$paramkind:ident,$that:expr) => { impl<'a> From<$kind> for crate::ast::Value<'a> { diff --git a/src/visitors/mod.rs b/src/visitors/mod.rs index 47653a6..3452482 100644 --- a/src/visitors/mod.rs +++ b/src/visitors/mod.rs @@ -7,12 +7,16 @@ //! //! For prelude, all important imports are in `xiayu::visitors::*`; #[cfg(feature = "mssql")] +#[cfg_attr(docsrs, doc(cfg(feature = "mssql")))] mod mssql; #[cfg(feature = "mysql")] +#[cfg_attr(docsrs, doc(cfg(feature = "mysql")))] mod mysql; #[cfg(feature = "postgres")] +#[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] mod postgres; #[cfg(feature = "sqlite")] +#[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))] mod sqlite; #[cfg(feature = "mssql")] diff --git a/src/visitors/mssql.rs b/src/visitors/mssql.rs index 750571d..7e4f208 100644 --- a/src/visitors/mssql.rs +++ b/src/visitors/mssql.rs @@ -17,7 +17,7 @@ static GENERATED_KEYS: &str = "@generated_keys"; /// A visitor to generate queries for the SQL Server database. /// /// The returned parameter values can be used directly with the tiberius crate. -#[cfg_attr(feature = "docs", doc(cfg(feature = "mssql")))] +#[cfg_attr(docsrs, doc(cfg(feature = "mssql")))] pub struct Mssql<'a> { query: String, parameters: Vec>, diff --git a/src/visitors/mysql.rs b/src/visitors/mysql.rs index 8f87544..f33ec97 100644 --- a/src/visitors/mysql.rs +++ b/src/visitors/mysql.rs @@ -9,7 +9,7 @@ use crate::{ /// A visitor to generate queries for the MySQL database. /// /// The returned parameter values can be used directly with the mysql crate. -#[cfg_attr(feature = "docs", doc(cfg(feature = "mysql")))] +#[cfg_attr(docsrs, doc(cfg(feature = "mysql")))] pub struct Mysql<'a> { query: String, parameters: Vec>, @@ -46,7 +46,7 @@ impl<'a> Mysql<'a> { right: Expression<'a>, sign: &str, ) -> visitors::Result { - #[cfg(json)] + #[cfg(feature = "json")] fn json_to_quaint_value<'a>(json: serde_json::Value) -> crate::Result> { match json { serde_json::Value::String(str) => Ok(Value::text(str)), @@ -54,13 +54,13 @@ impl<'a> Mysql<'a> { if let Some(int) = number.as_i64() { Ok(Value::I64(Some(int))) } else if let Some(double) = number.as_f64() { - Ok(Value::double(Some(double))) + Ok(Value::double(double)) } else { unreachable!() } } x => { - let msg = format!("Expected JSON string or number, found: {}", x); + let msg = format!("Expected JSON string or number, found: {:?}", x); let kind = ErrorKind::conversion(msg.clone()); let mut builder = Error::builder(kind); @@ -72,7 +72,7 @@ impl<'a> Mysql<'a> { } match (left, right) { - #[cfg(json)] + #[cfg(feature = "json")] (left, right) if left.is_json_value() && right.is_json_extract_fun() => { let quaint_value = json_to_quaint_value(left.into_json_value().unwrap())?; @@ -80,7 +80,7 @@ impl<'a> Mysql<'a> { self.write(format!(" {} ", sign))?; self.visit_expression(right)?; } - #[cfg(json)] + #[cfg(feature = "json")] (left, right) if left.is_json_extract_fun() && right.is_json_value() => { let quaint_value = json_to_quaint_value(right.into_json_value().unwrap())?; @@ -139,13 +139,13 @@ impl<'a> Visitor<'a> for Mysql<'a> { Value::I16(i) => i.map(|i| self.write(i)), Value::I32(i) => i.map(|i| self.write(i)), Value::I64(i) => i.map(|i| self.write(i)), - #[cfg(mysql_or_sqlite)] + #[cfg(any(feature = "mysql", feature = "sqlite"))] Value::U8(i) => i.map(|i| self.write(i)), - #[cfg(mysql_or_sqlite)] + #[cfg(any(feature = "mysql", feature = "sqlite"))] Value::U16(i) => i.map(|i| self.write(i)), - #[cfg(mysql_or_sqlite)] + #[cfg(any(feature = "mysql", feature = "sqlite"))] Value::U32(i) => i.map(|i| self.write(i)), - #[cfg(mysql_or_sqlite)] + #[cfg(any(feature = "mysql", feature = "sqlite"))] Value::U64(i) => i.map(|i| self.write(i)), Value::Float(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), @@ -160,14 +160,14 @@ impl<'a> Visitor<'a> for Mysql<'a> { v => self.write(format!("{:?}", v)), }), Value::Text(t) => t.map(|t| self.write(format!("'{}'", t))), - #[cfg(not_mssql)] + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] Value::Bytes(b) => b.map(|b| self.write(format!("x'{}'", hex::encode(b)))), Value::Boolean(b) => b.map(|b| self.write(b)), - #[cfg(bigdecimal)] + #[cfg(feature = "bigdecimal")] Value::BigDecimal(r) => r.map(|r| self.write(r)), - #[cfg(decimal)] + #[cfg(feature = "decimal")] Value::Decimal(r) => r.map(|r| self.write(r)), - #[cfg(json)] + #[cfg(feature = "json")] Value::Json(j) => match j { Some(v) => { let s = serde_json::to_string(&v)?; @@ -175,22 +175,22 @@ impl<'a> Visitor<'a> for Mysql<'a> { } _ => None, }, - #[cfg(uuid)] + #[cfg(feature = "uuid")] Value::Uuid(uuid) => { uuid.map(|uuid| self.write(format!("'{}'", uuid.to_hyphenated().to_string()))) } - #[cfg(chrono)] + #[cfg(feature = "chrono")] Value::UtcDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), - #[cfg(chrono)] + #[cfg(feature = "chrono")] Value::LocalDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), - #[cfg(chrono)] + #[cfg(feature = "chrono")] Value::NaiveDateTime(datetime) => { datetime.map(|datetime| self.write(format!("'{}'", datetime))) } - #[cfg(chrono)] + #[cfg(feature = "chrono")] Value::NaiveDate(date) => date.map(|date| self.write(format!("'{}'", date))), - #[cfg(chrono)] - Value::NaiveTime(time) => time.map(|time| self.write(format!("'{}'", date))), + #[cfg(feature = "chrono")] + Value::NaiveTime(time) => time.map(|time| self.write(format!("'{}'", time))), }; match res { @@ -310,7 +310,7 @@ impl<'a> Visitor<'a> for Mysql<'a> { } fn visit_equals(&mut self, left: Expression<'a>, right: Expression<'a>) -> visitors::Result { - #[cfg(json)] + #[cfg(feature = "json")] { if right.is_json_value() || left.is_json_value() { self.surround_with("(", ")", |ref mut s| { @@ -346,7 +346,7 @@ impl<'a> Visitor<'a> for Mysql<'a> { left: Expression<'a>, right: Expression<'a>, ) -> visitors::Result { - #[cfg(json)] + #[cfg(feature = "json")] { if right.is_json_value() || left.is_json_value() { self.surround_with("(", ")", |ref mut s| { @@ -371,7 +371,7 @@ impl<'a> Visitor<'a> for Mysql<'a> { } } - #[cfg(not(json))] + #[cfg(not(feature = "json"))] { self.visit_regular_difference_comparison(left, right) } @@ -766,7 +766,7 @@ mod tests { assert!(params.is_empty()); } - #[cfg(not_mssql)] + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] #[test] fn test_raw_bytes() { let (sql, params) = diff --git a/src/visitors/postgres.rs b/src/visitors/postgres.rs index fce4458..bd6ae23 100644 --- a/src/visitors/postgres.rs +++ b/src/visitors/postgres.rs @@ -7,7 +7,7 @@ use crate::visitors::{self, Visitor}; /// /// The returned parameter values implement the `ToSql` trait from postgres and /// can be used directly with the database. -#[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] +#[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] pub struct Postgres<'a> { query: String, parameters: Vec>, @@ -87,10 +87,10 @@ impl<'a> Visitor<'a> for Postgres<'a> { Value::I16(i) => i.map(|i| self.write(i)), Value::I32(i) => i.map(|i| self.write(i)), Value::I64(i) => i.map(|i| self.write(i)), - #[cfg(not_mssql)] + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] Value::U32(i) => i.map(|i| self.write(i)), Value::Text(t) => t.map(|t| self.write(format!("'{}'", t))), - #[cfg(not_mssql)] + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] Value::Bytes(b) => b.map(|b| self.write(format!("E'{}'", hex::encode(b)))), Value::Boolean(b) => b.map(|b| self.write(b)), Value::Float(d) => d.map(|f| match f { @@ -105,7 +105,7 @@ impl<'a> Visitor<'a> for Postgres<'a> { f if f == f64::NEG_INFINITY => self.write("'-Infinity"), v => self.write(format!("{:?}", v)), }), - // #[cfg(only_postgres)] + // #[cfg(feature = "postgres")] // Value::Array(ary) => ary.map(|ary| { // self.surround_with("'{", "}'", |ref mut s| { // let len = ary.len(); @@ -121,36 +121,35 @@ impl<'a> Visitor<'a> for Postgres<'a> { // Ok(()) // }) // }), - #[cfg(json)] - Value::Json(j) => match j { - Some(v) => self.write(format!("'{}'", serde_json::to_string(&j).unwrap())), - None => None, - }, - #[cfg(bigdecimal)] + #[cfg(feature = "json")] + Value::Json(j) => { + j.map(|j| self.write(format!("'{}'", serde_json::to_string(&j).unwrap()))) + } + #[cfg(feature = "bigdecimal")] Value::BigDecimal(r) => r.map(|r| self.write(r)), - #[cfg(decimal)] + #[cfg(feature = "decimal")] Value::Decimal(r) => r.map(|r| self.write(r)), - #[cfg(uuid)] + #[cfg(feature = "uuid")] Value::Uuid(uuid) => { uuid.map(|uuid| self.write(format!("'{}'", uuid.to_hyphenated().to_string()))) } - #[cfg(chrono)] + #[cfg(feature = "chrono")] Value::UtcDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), - #[cfg(chrono)] + #[cfg(feature = "chrono")] Value::LocalDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), - #[cfg(chrono)] - Value::NaiveDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), - #[cfg(chrono)] + #[cfg(feature = "chrono")] + Value::NaiveDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt))), + #[cfg(feature = "chrono")] Value::NaiveDate(date) => date.map(|date| self.write(format!("'{}'", date))), - #[cfg(only_postgres)] + #[cfg(feature = "postgres")] Value::PgMoney(money) => money.map(|money| self.write(money.to_bigdecimal(2))), - #[cfg(only_postgres)] + #[cfg(feature = "postgres")] Value::PgInterval(interval) => { interval.map(|interval| self.write(format!("{:?}", interval))) } - #[cfg(all(time, only_postgres))] + #[cfg(all(feature = "time", feature = "postgres"))] Value::PgTimeTz(timetz) => timetz.map(|timetz| self.write(format!("{:?}", timtz))), - #[cfg(ipnetwork)] + #[cfg(feature = "ipnetwork")] Value::IpNetwork(ipnetwork) => { ipnetwork.map(|ipnetwork| self.write(format!("'{}'", ipnetwork))) } @@ -253,13 +252,13 @@ impl<'a> Visitor<'a> for Postgres<'a> { fn visit_equals(&mut self, left: Expression<'a>, right: Expression<'a>) -> visitors::Result { // LHS must be cast to json/xml-text if the right is a json/xml-text value and vice versa. let right_cast = match left { - #[cfg(json)] + #[cfg(feature = "json")] _ if left.is_json_value() => "::jsonb", _ => "", }; let left_cast = match right { - #[cfg(json)] + #[cfg(feature = "json")] _ if right.is_json_value() => "::jsonb", _ => "", }; @@ -280,13 +279,13 @@ impl<'a> Visitor<'a> for Postgres<'a> { ) -> visitors::Result { // LHS must be cast to json/xml-text if the right is a json/xml-text value and vice versa. let right_cast = match left { - #[cfg(json)] + #[cfg(feature = "json")] _ if left.is_json_value() => "::jsonb", _ => "", }; let left_cast = match right { - #[cfg(json)] + #[cfg(feature = "json")] _ if right.is_json_value() => "::jsonb", _ => "", }; @@ -476,7 +475,7 @@ mod tests { #[column(primary_key)] id: i32, foo: i32, - #[cfg(json)] + #[cfg(feature = "json")] #[column(name = "jsonField")] json: serde_json::Value, #[column(name = "xmlField")] diff --git a/src/visitors/sqlite.rs b/src/visitors/sqlite.rs index f89e53c..41ca38e 100644 --- a/src/visitors/sqlite.rs +++ b/src/visitors/sqlite.rs @@ -10,7 +10,7 @@ use std::fmt::{self, Write}; /// /// The returned parameter values implement the `ToSql` trait from rusqlite and /// can be used directly with the database. -#[cfg_attr(feature = "docs", doc(cfg(feature = "sqlite")))] +#[cfg_attr(docsrs, doc(cfg(feature = "sqlite")))] pub struct Sqlite<'a> { query: String, parameters: Vec>, @@ -56,14 +56,14 @@ impl<'a> Visitor<'a> for Sqlite<'a> { Value::I16(i) => i.map(|i| self.write(i)), Value::I32(i) => i.map(|i| self.write(i)), Value::I64(i) => i.map(|i| self.write(i)), - #[cfg(mysql_or_sqlite)] + #[cfg(any(feature = "mysql", feature = "sqlite"))] Value::U8(i) => i.map(|i| self.write(i)), - #[cfg(mysql_or_sqlite)] + #[cfg(any(feature = "mysql", feature = "sqlite"))] Value::U16(i) => i.map(|i| self.write(i)), - #[cfg(not_mssql)] + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] Value::U32(i) => i.map(|i| self.write(i)), Value::Text(t) => t.map(|t| self.write(format!("'{}'", t))), - #[cfg(not_mssql)] + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] Value::Bytes(b) => b.map(|b| self.write(format!("x'{}'", hex::encode(b)))), Value::Boolean(b) => b.map(|b| self.write(b)), Value::Float(d) => d.map(|f| match f { @@ -79,7 +79,7 @@ impl<'a> Visitor<'a> for Sqlite<'a> { v => self.write(format!("{:?}", v)), }), - #[cfg(json)] + #[cfg(feature = "json")] Value::Json(j) => match j { Some(v) => { let s = serde_json::to_string(&v)?; @@ -87,19 +87,19 @@ impl<'a> Visitor<'a> for Sqlite<'a> { } _ => None, }, - #[cfg(uuid)] + #[cfg(feature = "uuid")] Value::Uuid(uuid) => { uuid.map(|uuid| self.write(format!("'{}'", uuid.to_hyphenated().to_string()))) } - #[cfg(chrono)] + #[cfg(feature = "chrono")] Value::UtcDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), - #[cfg(chrono)] + #[cfg(feature = "chrono")] Value::LocalDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), - #[cfg(chrono)] - Value::NaiveDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), - #[cfg(chrono)] + #[cfg(feature = "chrono")] + Value::NaiveDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt))), + #[cfg(feature = "chrono")] Value::NaiveDate(date) => date.map(|date| self.write(format!("'{}'", date))), - #[cfg(chrono)] + #[cfg(feature = "chrono")] Value::NaiveTime(time) => time.map(|time| self.write(format!("'{}'", time))), }; From 00640652929b012b7ed306ec65e0f31734bcdeb6 Mon Sep 17 00:00:00 2001 From: songww Date: Sat, 28 Aug 2021 17:23:54 +0800 Subject: [PATCH 4/5] Fix values. --- .vim/coc-settings.json | 34 +-- Cargo.toml | 2 + examples/basic.rs | 20 +- src/ast/values.rs | 40 +-- src/databases/has_value.rs | 3 + src/databases/has_visitor.rs | 4 + src/databases/mod.rs | 507 +++++--------------------------- src/databases/mssql/mod.rs | 48 +++ src/databases/mssql/value.rs | 222 ++++++++++++++ src/databases/mysql/mod.rs | 72 +++++ src/databases/mysql/value.rs | 155 ++++++++++ src/databases/postgres/mod.rs | 76 +++++ src/databases/postgres/value.rs | 153 ++++++++++ src/databases/sqlite/mod.rs | 74 +++++ src/databases/sqlite/value.rs | 169 +++++++++++ src/databases/try_bind.rs | 8 + src/lib.rs | 6 +- src/macros.rs | 1 + src/visitors/mssql.rs | 10 +- src/visitors/mysql.rs | 10 +- src/visitors/postgres.rs | 11 +- src/visitors/sqlite.rs | 11 +- 22 files changed, 1126 insertions(+), 510 deletions(-) create mode 100644 src/databases/has_value.rs create mode 100644 src/databases/has_visitor.rs create mode 100644 src/databases/mssql/mod.rs create mode 100644 src/databases/mssql/value.rs create mode 100644 src/databases/mysql/mod.rs create mode 100644 src/databases/mysql/value.rs create mode 100644 src/databases/postgres/mod.rs create mode 100644 src/databases/postgres/value.rs create mode 100644 src/databases/sqlite/mod.rs create mode 100644 src/databases/sqlite/value.rs create mode 100644 src/databases/try_bind.rs diff --git a/.vim/coc-settings.json b/.vim/coc-settings.json index b242330..a46d420 100644 --- a/.vim/coc-settings.json +++ b/.vim/coc-settings.json @@ -1,4 +1,6 @@ { + "codeLens.enable": true, + "diagnostic.enableMessage": "always", "languageserver": { "clangd": { "rootPatterns": [ @@ -15,45 +17,17 @@ "objcpp" ], "command": "/usr/local/opt/llvm/bin/clangd" - }, - "rust": { - "command": "rust-analyzer", - "filetypes": [ - "rust" - ], - "rootPatterns": [ - "Cargo.toml", - ".git" - ] } }, "clangd.path": "/usr/local/opt/llvm/bin/clangd", - "rust-analyzer.enable": false, + "rust-analyzer.enable": true, "rust-analyzer.server.path": "/usr/local/bin/rust-analyzer", "rust-analyzer.updates.prompt": true, "rust-analyzer.updates.channel": "nightly", "rust-analyzer.cargo.autoreload": true, - "rust-analyzer.cargo.features": [ - "docs", - "postgres", - "json", - "uuid", - "chrono", - "bigdecimal" - ], "rust-analyzer.cargo.allFeatures": true, - "rust-analyzer.checkOnSave.features": [ - "doc", - "mssql", - "mysql", - "sqlite", - "postgres", - "json", - "uuid", - "chrono", - "bigdecimal" - ], "rust-analyzer.cargo.noDefaultFeatures": false, + "rust-analyzer.checkOnSave.allFeatures": true, "rust-analyzer.checkOnSave.noDefaultFeatures": false, "python.pythonPath": "python", "python.formatting.provider": "black", diff --git a/Cargo.toml b/Cargo.toml index 90dfe4f..5d3801c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,8 @@ json = [ "base64", "sqlx/json", "serde", "serde_json", "num/serde" ] chrono = [ "sqlx/chrono" ] decimal = [ "sqlx/decimal" ] bigdecimal = [ "num", "num-bigint", "sqlx/bigdecimal" ] +time = [ "sqlx/time" ] +ipnetwork = [ "sqlx/ipnetwork" ] [workspace] members = [ diff --git a/examples/basic.rs b/examples/basic.rs index 58db9f1..79e2d8b 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,31 +1,15 @@ #[macro_use] extern crate xiayu_derive; -use std::marker::PhantomData; - use xiayu::prelude::*; use xiayu::visitors::Visitor; -use xiayu_derive::*; - -#[derive(Debug)] -pub struct Relation { - _phantom: PhantomData, -} - -impl Relation { - fn new() -> Self { - Self { - _phantom: PhantomData, - } - } -} #[derive(Debug, Entity)] #[tablename = "entities"] pub struct AnEntity { #[column(primary_key, autoincrement, comment = "some comments")] pub id: i32, - pub another_entity_id: Relation, + pub another_entity_id: i32, pub maybe_float: Option, } @@ -43,7 +27,7 @@ fn main() { let entity = AnEntity { id: 2, - another_entity_id: Relation::::new(), + another_entity_id: 1, maybe_float: None, }; assert_eq!(entity.id, 2); diff --git a/src/ast/values.rs b/src/ast/values.rs index 7e4664a..2852aed 100644 --- a/src/ast/values.rs +++ b/src/ast/values.rs @@ -1,12 +1,10 @@ -use std::any::Any; -use std::borrow::{Borrow, Cow}; + +use std::borrow::{Cow}; use std::convert::TryFrom; -use std::{fmt, option}; -#[cfg(feature = "bigdecimal")] -use num::{FromPrimitive, ToPrimitive}; -#[cfg(feature = "json")] -use serde_json::{Number, Value as JsonValue}; + + + #[cfg(feature = "chrono")] use sqlx::types::chrono::{self, DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; #[cfg(feature = "bigdecimal")] @@ -15,7 +13,7 @@ use sqlx::types::BigDecimal; use sqlx::types::Uuid; use crate::ast::*; -use crate::error::{Error, ErrorKind}; + /// A value written to the query as-is without parameterization. #[derive(Debug, Clone, PartialEq)] @@ -60,40 +58,48 @@ trait PgCompatibleType: /// compatibility. #[derive(Debug, Clone, PartialEq)] pub enum Value<'a, J: serde::ser::Serialize = ()> { + /// TINYINT I8(Option), + /// SMALLINT I16(Option), + /// INT I32(Option), + /// BIGINT I64(Option), - /// 32-bit floating point. + /// FLOAT -> 32-bit floating point. Float(Option), - /// 64-bit floating point. + /// DOUBLE -> 64-bit floating point. Double(Option), - /// String value. + /// VARCHAR, CHAR, TEXT -> String value. Text(Option>), - /// Bytes value. + /// VARBINARY, BINARY, BLOB -> Bytes value. #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))) )] Bytes(Option>), - /// Boolean value. + /// TINYINT(1), BOOLEAN -> Boolean value. Boolean(Option), - #[cfg(all(any(docsrs, feature = "mysql", feature = "sqlite"),))] + #[cfg(any(docsrs, feature = "mysql", feature = "sqlite"))] #[cfg_attr(docsrs, doc(cfg(all(any(feature = "mysql", feature = "sqlite"),))))] + /// TINYINT UNSIGNED U8(Option), - #[cfg(all(any(docsrs, feature = "mysql", feature = "sqlite"),))] + #[cfg(any(docsrs, feature = "mysql", feature = "sqlite"))] #[cfg_attr(docsrs, doc(cfg(all(any(feature = "mysql", feature = "sqlite"),))))] + /// SMALLINT UNSIGNED U16(Option), #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))) )] + /// INT UNSIGNED U32(Option), #[cfg(feature = "mysql")] #[cfg_attr(docsrs, doc(cfg(feature = "mysql")))] + /// BIGINT UNSIGNED U64(Option), /* /// Database enum value. @@ -126,7 +132,7 @@ pub enum Value<'a, J: serde::ser::Serialize = ()> { /// INT8RANGE, INT4RANGE, TSRANGE, TSTZTRANGE, DATERANGE, NUMRANGE PgRange(Option>>>), */ - /// INT8RANGE, INT4RANGE, TSRANGE, TSTZTRANGE, DATERANGE, NUMRANGE + /// MONEY #[cfg(feature = "postgres")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] PgMoney(Option), @@ -167,7 +173,7 @@ pub enum Value<'a, J: serde::ser::Serialize = ()> { /// INET, CIDR #[cfg(feature = "ipnetwork")] #[cfg_attr(docsrs, doc(cfg(feature = "ipnetwork")))] - IpNetwork(Option), + IpNetwork(Option), } /* diff --git a/src/databases/has_value.rs b/src/databases/has_value.rs new file mode 100644 index 0000000..5ab1f24 --- /dev/null +++ b/src/databases/has_value.rs @@ -0,0 +1,3 @@ +pub trait HasValue: sqlx::Database { + type Value; +} diff --git a/src/databases/has_visitor.rs b/src/databases/has_visitor.rs new file mode 100644 index 0000000..bbcea1f --- /dev/null +++ b/src/databases/has_visitor.rs @@ -0,0 +1,4 @@ +pub trait HasVisitor<'a> { + type Visitor: crate::visitors::Visitor<'a>; + fn visitor() -> Self::Visitor; +} diff --git a/src/databases/mod.rs b/src/databases/mod.rs index 4dd8c8d..2e8172b 100644 --- a/src/databases/mod.rs +++ b/src/databases/mod.rs @@ -1,408 +1,33 @@ -use std::default; -use std::marker::{PhantomData}; -use async_trait::async_trait; -#[cfg(feature = "chrono")] -use sqlx::types::chrono; -#[cfg(feature = "json")] -use sqlx::types::Json; -use sqlx::{Executor, Arguments, Database, IntoArguments, FromRow}; - -use crate::ast::{Value}; -use crate::prelude::{Column, Delete, Entity, HasPrimaryKey, Insert, MultiRowInsert, SingleRowInsert, Row, OnConflict, Select, Update, Expression}; -use crate::visitors::Visitor; - -pub trait HasVisitor<'a> { - type Visitor: crate::visitors::Visitor<'a>; - fn visitor() -> Self::Visitor; -} - -#[cfg(feature = "postgres")] -impl<'a> HasVisitor<'a> for sqlx::Postgres { - type Visitor = crate::visitors::Postgres<'a>; - fn visitor() -> Self::Visitor { - Self::Visitor::default() - } -} - -#[cfg(feature = "mssql")] -impl<'a> HasVisitor<'a> for sqlx::Mssql { - type Visitor = crate::visitors::Mssql<'a>; - fn visitor() -> Self::Visitor { - Self::Visitor::default() - } -} - -#[cfg(feature = "mysql")] -impl<'a> HasVisitor<'a> for sqlx::MySql { - type Visitor = crate::visitors::Mysql<'a>; - fn visitor() -> Self::Visitor { - Self::Visitor::default() - } -} +use std::marker::PhantomData; -#[cfg(feature = "sqlite")] -impl<'a> HasVisitor<'a> for sqlx::Sqlite { - type Visitor = crate::visitors::Sqlite<'a>; - fn visitor() -> Self::Visitor { - Self::Visitor::default() - } -} - -/// Values that for mysql -#[cfg(feature = "mysql")] -struct MyValue { - // -} +use async_trait::async_trait; -macro_rules! try_bind_value { - ($query:ident, $value: ident, $driver: literal) => { - match $value { - Value::I8(int8) => Ok($query.bind(int8)), - Value::I16(int16) => Ok($query.bind(int16)), - Value::I32(int32) => Ok($query.bind(int32)), - Value::I64(int64) => Ok($query.bind(int64)), - Value::Float(float) => Ok($query.bind(float)) , - Value::Double(double) => Ok($query.bind(double)), - Value::Boolean(boolean) => Ok($query.bind(boolean)), - Value::Text(text) => Ok($query.bind(text.map(|text|text.into_owned()))), - #[cfg(all(any(feature = "mysql", feature = "sqlite", feature = "postgres")))] - Value::Bytes(bytes) => match $driver { - "mysql" | "sqlite" | "postgres" => { - Ok($query.bind(bytes.map(|b|b.into_owned()))) - }, - "mssql" | _ => { - let msg = "Bytes are not supported by SQLx with SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(any(feature = "mysql", feature = "sqlite"))] - Value::U8(uint8) => match $driver { - "mysql" | "sqlite" => { - Ok($query.bind(uint8)) - }, - "mssql" | "postgres" | _ => { - let msg = "u8 are not supported by SQLx with SQL Server/PostgreSQL."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(any(feature = "mysql", feature = "sqlite"))] - Value::U16(uint16) => match $driver { - "mysql" | "sqlite" => { - Ok($query.bind(uint16)) - }, - "mssql" | "postgres" | _ => { - let msg = "u16 are not supported by SQLx with SQL Server/PostgreSQL."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - } - #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] - Value::U32(uint32) => match $driver { - "mysql" | "sqlite" | "postgres" => { - Ok($query.bind(uint32)) - }, - "mssql" | _ => { - let msg = "u32 are not supported by SQLx with SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "mysql")] - Value::U64(uint64) => match $driver { - "mysql" => { - Ok($query.bind(uint64)) - }, - "mssql" | "sqlite" | "postgres" | _ => { - let msg = "u64 are only supported by SQLx with MySQL, but not Sqlite/PostgreSQL/SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "json")] - Value::Json(json) => match $driver { - "mysql" | "sqlite" | "postgres" => { - Ok($query.bind(json)) - }, - "mssql" | _ => { - let msg = "JsonType are not supported by SQLx with SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "uuid")] - Value::Uuid(uuid) => match $driver { - "mysql" | "sqlite" | "postgres" => { - Ok($query.bind(uuid)) - }, - "mssql" | _ => { - let msg = "UUID Type are not supported by SQLx with SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "postgres")] - Value::PgInterval(interval) => match $driver { - "postgres" => { - Ok($query.bind(interval)) - }, - "mssql" | "mysql" | "sqlite" | _ => { - let msg = "PgInterval are only supported by SQLx with PostgreSQL, but not Sqlite / MySQL / SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - // #[cfg(feature = "postgres")] - // Value::PgRange(range) => $query.bind(range), - #[cfg(feature = "postgres")] - Value::PgMoney(money) => match $driver { - "postgres" => { - Ok($query.bind(money)) - }, - "mssql" | "mysql" | "sqlite" | _ => { - let msg = "PgMoney are only supported by SQLx with PostgreSQL, but not Sqlite / MySQL / SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "bigdecimal")] - Value::BigDecimal(bigdecimal) => match $driver { - "postgres" | "mysql" => { - Ok($query.bind(bigdecimal)) - }, - "mssql" | "sqlite" | _ => { - let msg = "BigDecimal are only supported by SQLx with PostgreSQL / MySQL, but not Sqlite / SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "decimal")] - Value::Decimal(decimal) => match $driver { - "postgres" | "mysql" => { - Ok($query.bind(decimal)) - }, - "mssql" | "sqlite" | _ => { - let msg = "Decimal are only supported by SQLx with PostgreSQL / MySQL, but not Sqlite / SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "chrono")] - Value::UtcDateTime(datetime) => match $driver { - "mysql" | "sqlite" | "postgres" => { - Ok($query.bind(datetime)) - }, - "mssql" | _ => { - let msg = "DateTime are not supported by SQLx with SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "chrono")] - Value::LocalDateTime(datetime) => match $driver { - "mysql" | "sqlite" | "postgres" => { - Ok($query.bind(datetime)) - }, - "mssql" | _ => { - let msg = "DataTime are not supported by SQLx with SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "chrono")] - Value::NaiveDateTime(datetime) => match $driver { - "mysql" | "sqlite" | "postgres" => { - Ok($query.bind(datetime)) - }, - "mssql" | _ => { - let msg = "NaiveDateTime are not supported by SQLx with SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "chrono")] - Value::NaiveDate(date) => match $driver { - "mysql" | "sqlite" | "postgres" => { - Ok($query.bind(date)) - }, - "mssql" | _ => { - let msg = "NaiveDate are not supported by SQLx with SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "chrono")] - Value::NaiveTime(time) => match $driver { - "mysql" | "sqlite" | "postgres" => { - Ok($query.bind(time)) - }, - "mssql" | _ => { - let msg = "NaiveTime are not supported by SQLx with SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(all(feature = "time", feature = "postgres"))] - Value::PgTimeTz(timetz) => match $driver { - "postgres" => { - Ok($query.bind(timetz)) - }, - "mysql" | "sqlite" | "mssql" | _ => { - let msg = "PgTimeTz are only supported by SQLx with PostgreSQL, but not Sqlite / MySQL / SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - #[cfg(feature = "ipnetwork")] - Value::IpNetwork(ipnetwork) => match $driver { - "postgres" => { - Ok($query.bind(ipnetwork)) - }, - "mysql" | "sqlite" | "mssql" | _ => { - let msg = "IpNetwork are only supported by SQLx with PostgreSQL, but not Sqlite / MySQL / SQL Server."; - let kind = crate::error::ErrorKind::conversion(msg); - - let mut builder = crate::error::Error::builder(kind); - builder.set_original_message(msg); - - Err(builder.build()) - } - }, - } - } -} +use sqlx::{Database, Executor, FromRow, IntoArguments}; -pub trait Binder<'a, DB> where DB: sqlx::Database { - fn try_bind_value(self, value: Value<'a>) -> crate::Result - where - Self: Sized; -} -#[cfg(feature = "postgres")] -impl<'a> Binder<'a, sqlx::Postgres> for sqlx::query::Query<'a, sqlx::Postgres, sqlx::postgres::PgArguments> { - fn try_bind_value(self, value: Value<'a>) -> crate::Result { - try_bind_value!(self, value, "postgres") - } -} +use crate::prelude::{ + Column, Delete, Expression, HasPrimaryKey, Insert, MultiRowInsert, Row, Select, + SingleRowInsert, Update, +}; +use crate::visitors::Visitor; #[cfg(feature = "postgres")] -impl<'a, O> Binder<'a, sqlx::Postgres> for sqlx::query::QueryAs<'a, sqlx::Postgres, O, sqlx::postgres::PgArguments> { - fn try_bind_value(self, value: Value<'a>) -> crate::Result { - try_bind_value!(self, value, "postgres") - } -} - -#[cfg(feature = "mysql")] -impl<'a> Binder<'a, sqlx::MySql> for sqlx::query::Query<'a, sqlx::MySql, sqlx::mysql::MySqlArguments> { - fn try_bind_value(self, value: Value<'a>) -> crate::Result { - try_bind_value!(self, value, "mysql") - } -} - +pub mod postgres; +#[cfg(feature = "sqlite")] +pub mod sqlite; #[cfg(feature = "mysql")] -impl<'a, O> Binder<'a, sqlx::MySql> for sqlx::query::QueryAs<'a, sqlx::MySql, O, sqlx::mysql::MySqlArguments> { - fn try_bind_value(self, value: Value<'a>) -> crate::Result { - try_bind_value!(self, value, "mysql") - } -} - +pub mod mysql; #[cfg(feature = "mssql")] -impl<'a> Binder<'a, sqlx::Mssql> for sqlx::query::Query<'a, sqlx::Mssql, sqlx::mssql::MssqlArguments> { - fn try_bind_value(self, value: Value<'a>) -> crate::Result { - try_bind_value!(self, value, "mssql") - } -} +pub mod mssql; -#[cfg(feature = "mssql")] -impl<'a, O> Binder<'a, sqlx::Mssql> for sqlx::query::QueryAs<'a, sqlx::Mssql, O, sqlx::mssql::MssqlArguments> { - fn try_bind_value(self, value: Value<'a>) -> crate::Result { - try_bind_value!(self, value, "mssql") - } -} +mod try_bind; +mod has_visitor; +mod has_value; -#[cfg(feature = "sqlite")] -impl<'a> Binder<'a, sqlx::Sqlite> for sqlx::query::Query<'a, sqlx::Sqlite, sqlx::sqlite::SqliteArguments<'a>> { - fn try_bind_value(self, value: Value<'a>) -> crate::Result { - try_bind_value!(self, value, "sqlite") - } -} - -#[cfg(feature = "sqlite")] -impl<'a, O> Binder<'a, sqlx::Sqlite> for sqlx::query::QueryAs<'a, sqlx::Sqlite, O, sqlx::sqlite::SqliteArguments<'a>> { - fn try_bind_value(self, value: Value<'a>) -> crate::Result { - try_bind_value!(self, value, "sqlite") - } -} +pub use try_bind::TryBind; +pub use has_visitor::HasVisitor; +pub use has_value::HasValue; /// fetch entity from table. Returned by [`get`][crate::prelude::HasPrimaryKey::get]. #[must_use = "query must be executed to affect database"] @@ -419,15 +44,18 @@ impl SelectingExecution { DB: 'a + sqlx::Database + HasVisitor<'a>, >::Arguments: 'a + IntoArguments<'a, DB>, T: 'a + for<'r> sqlx::FromRow<'r, ::Row> + Send + Unpin, - sqlx::query::QueryAs<'a, DB, T, >::Arguments>: Binder<'a, DB> + sqlx::query::QueryAs<'a, DB, T, >::Arguments>: + TryBind<'a, DB>, { let (query, parameters) = - <>::Database as HasVisitor>::Visitor::build(self.select.clone())?; + <>::Database as HasVisitor>::Visitor::build( + self.select.clone(), + )?; // 'a for borrowed from self.query self.compiled.replace(query); let mut query = sqlx::query_as::(self.compiled.as_ref().unwrap()); for parameter in parameters { - query = query.try_bind_value(parameter)?; + query = query.try_bind(parameter)?; } let v = query.fetch_one(conn).await?; @@ -438,7 +66,7 @@ impl SelectingExecution { impl From> for SelectingExecution where T: for<'r> FromRow<'r, ::Row>, - DB: sqlx::Database + DB: sqlx::Database, { fn from(select: crate::ast::Select<'static>) -> Self { Self { @@ -459,8 +87,7 @@ pub struct DeletingExecution<'a, E, DB> { } impl<'e, E: HasPrimaryKey, DB: Database> DeletingExecution<'e, E, DB> { - pub fn new<'a>(delete: crate::ast::Delete<'static>, entity: &'e mut E) -> Self - { + pub fn new<'a>(delete: crate::ast::Delete<'static>, entity: &'e mut E) -> Self { Self { entity, delete, @@ -475,20 +102,21 @@ impl<'e, E: HasPrimaryKey, DB: Database> DeletingExecution<'e, E, DB> { C: 'a + sqlx::Executor<'a, Database = DB>, DB: 'a + sqlx::Database + HasVisitor<'a>, >::Arguments: 'a + IntoArguments<'a, DB>, - sqlx::query::Query<'a, DB, >::Arguments>: Binder<'a, DB> + sqlx::query::Query<'a, DB, >::Arguments>: + TryBind<'a, DB>, { let (compiled, parameters) = - <>::Database as HasVisitor>::Visitor::build(self.delete.clone())?; + <>::Database as HasVisitor>::Visitor::build( + self.delete.clone(), + )?; // 'a for borrowed from self.compiled self.compiled.replace(compiled); let mut query = sqlx::query::(self.compiled.as_ref().unwrap()); for parameter in parameters { - query = query.try_bind_value(parameter)?; + query = query.try_bind(parameter)?; } - let _query_result = query - .execute(conn) - .await?; + let _query_result = query.execute(conn).await?; Ok(()) } } @@ -503,8 +131,7 @@ pub struct SavingExecution<'a, E, DB> { } impl<'e, E: HasPrimaryKey, DB: Database> SavingExecution<'e, E, DB> { - pub fn new<'a>(saving: Update<'static>, entity: &'e mut E) -> Self - { + pub fn new<'a>(saving: Update<'static>, entity: &'e mut E) -> Self { Self { entity, saving, @@ -520,20 +147,21 @@ impl<'e, E: HasPrimaryKey, DB: Database> SavingExecution<'e, E, DB> { C: 'a + Executioner<'a, DB>, DB: 'a + sqlx::Database + for<'v> HasVisitor<'v>, >::Arguments: 'a + IntoArguments<'a, DB>, - sqlx::query::Query<'a, DB, >::Arguments>: Binder<'a, DB> + sqlx::query::Query<'a, DB, >::Arguments>: + TryBind<'a, DB>, { let (compiled, parameters) = - <>::Database as HasVisitor>::Visitor::build(self.saving.clone())?; + <>::Database as HasVisitor>::Visitor::build( + self.saving.clone(), + )?; // 'a for borrowed from self.compiled println!("compiled update: {}", &compiled); self.compiled.replace(compiled); let mut query = sqlx::query::(self.compiled.as_ref().unwrap()); for parameter in parameters { - query = query.try_bind_value(parameter)?; + query = query.try_bind(parameter)?; } - let _query_result = query - .execute(conn) - .await?; + let _query_result = query.execute(conn).await?; Ok(()) } } @@ -592,7 +220,7 @@ impl InsertingExecution { pub async fn conn<'a, C>(self, conn: C) -> crate::Result where C: Executioner<'a, DB>, - DB: sqlx::Database + for <'v> HasVisitor<'v>, + DB: sqlx::Database + for<'v> HasVisitor<'v>, I: for<'i> Into> + Clone + Send, { conn.insert(self).await @@ -604,35 +232,49 @@ impl<'insert, DB> From> for InsertingExecution From> for InsertingExecution> { +impl<'insert, DB> From> + for InsertingExecution> +{ fn from(ins: SingleRowInsert<'insert>) -> Self { Self { insertion: ins, compiled: None, - _marker: PhantomData + _marker: PhantomData, } } } -impl<'insert, DB> From> for InsertingExecution> { +impl<'insert, DB> From> + for InsertingExecution> +{ fn from(ins: MultiRowInsert<'insert>) -> Self { Self { insertion: ins, compiled: None, - _marker: PhantomData + _marker: PhantomData, } } } #[async_trait] -pub trait Executioner<'c, DB>: sqlx::Executor<'c, Database = DB> where DB: for<'v> HasVisitor<'v> + sqlx::Database { +pub trait Executioner<'c, DB>: sqlx::Executor<'c, Database = DB> +where + DB: for<'v> HasVisitor<'v> + sqlx::Database, +{ async fn save(self, entity: &mut E) -> crate::Result<()>; - async fn insert<'query, I: Into> + Send, IE: Into> + Send>(self, insertion: IE) -> crate::Result; + async fn insert< + 'query, + I: Into> + Send, + IE: Into> + Send, + >( + self, + insertion: IE, + ) -> crate::Result; } macro_rules! impl_executioner_for { @@ -647,7 +289,7 @@ macro_rules! impl_executioner_for { request.compiled.replace(compiled); let mut query = sqlx::query::<$database>(request.compiled.as_ref().unwrap()); for parameter in parameters { - query = query.try_bind_value(parameter)?; + query = query.try_bind(parameter)?; } let _query_result = self.execute(query).await?; Ok(()) @@ -663,7 +305,7 @@ macro_rules! impl_executioner_for { request.compiled.replace(compiled); let mut query = sqlx::query::<$database>(request.compiled.as_ref().unwrap()); for parameter in parameters { - query = query.try_bind_value(parameter)?; + query = query.try_bind(parameter)?; } let query_result = self.execute(query).await?; Ok(query_result) @@ -700,8 +342,9 @@ impl_executioner_for!(<'c, 't>, &'c mut sqlx::Transaction<'t, sqlx::Sqlite>, sql impl_executioner_for!(<'c, 't>, &'c mut sqlx::Transaction<'t, sqlx::Postgres>, sqlx::Postgres); #[async_trait] -impl<'p, DB> Executioner<'p, DB> for &'_ sqlx::Pool where - DB: sqlx::Database + for <'v> HasVisitor<'v>, +impl<'p, DB> Executioner<'p, DB> for &'_ sqlx::Pool +where + DB: sqlx::Database + for<'v> HasVisitor<'v>, for<'c> &'c mut ::Connection: Executioner<'c, DB>, { async fn save(self, entity: &mut E) -> crate::Result<()> { @@ -710,12 +353,16 @@ impl<'p, DB> Executioner<'p, DB> for &'_ sqlx::Pool where conn.save(entity).await } - async fn insert<'query, I, IE>(self, insertion: IE) -> crate::Result<::QueryResult> - where IE: Into> + Send, - I: Into> + Send, + async fn insert<'query, I, IE>( + self, + insertion: IE, + ) -> crate::Result<::QueryResult> + where + IE: Into> + Send, + I: Into> + Send, { let pool = self.clone(); let mut conn = pool.acquire().await?; conn.insert(insertion).await - } + } } diff --git a/src/databases/mssql/mod.rs b/src/databases/mssql/mod.rs new file mode 100644 index 0000000..714b66a --- /dev/null +++ b/src/databases/mssql/mod.rs @@ -0,0 +1,48 @@ +use std::convert::TryFrom; + +mod value; + +pub use value::MsValue; + +#[cfg(feature = "mssql")] +impl<'a> super::HasVisitor<'a> for sqlx::Mssql { + type Visitor = crate::visitors::Mssql<'a>; + fn visitor() -> Self::Visitor { + Self::Visitor::default() + } +} + +macro_rules! bind { + ($query:ident, $value: ident) => { + match $value { + MsValue::I8(int8) => $query.bind(int8), + MsValue::I16(int16) => $query.bind(int16), + MsValue::I32(int32) => $query.bind(int32), + MsValue::I64(int64) => $query.bind(int64), + MsValue::Float(float) => $query.bind(float) , + MsValue::Double(double) => $query.bind(double), + MsValue::Boolean(boolean) => $query.bind(boolean), + MsValue::Text(text) => $query.bind(text.map(|text|text.into_owned())) + } + } +} + +#[cfg(feature = "mssql")] +impl<'a> super::TryBind<'a, sqlx::Mssql> + for sqlx::query::Query<'a, sqlx::Mssql, sqlx::mssql::MssqlArguments> +{ + fn try_bind(self, value: crate::ast::Value<'a>) -> crate::Result { + let value = MsValue::try_from(value)?; + Ok(bind!(self, value)) + } +} + +#[cfg(feature = "mssql")] +impl<'a, O> super::TryBind<'a, sqlx::Mssql> + for sqlx::query::QueryAs<'a, sqlx::Mssql, O, sqlx::mssql::MssqlArguments> +{ + fn try_bind(self, value: crate::ast::Value<'a>) -> crate::Result { + let value = MsValue::try_from(value)?; + Ok(bind!(self, value)) + } +} diff --git a/src/databases/mssql/value.rs b/src/databases/mssql/value.rs new file mode 100644 index 0000000..79e6f6f --- /dev/null +++ b/src/databases/mssql/value.rs @@ -0,0 +1,222 @@ +use std::borrow::Cow; +use std::convert::TryFrom; + +use crate::ast::Value; + +/// Values that supported by SQL Server. +#[cfg(feature = "mssql")] +pub enum MsValue<'a> { + /// TINYINT + I8(Option), + /// SMALLINT + I16(Option), + /// INT + I32(Option), + /// BIGINT + I64(Option), + /// FLOAT -> 32-bit floating point. + Float(Option), + /// DOUBLE -> 64-bit floating point. + Double(Option), + /// VARCHAR, CHAR, TEXT -> String value. + Text(Option>), + /// TINYINT(1), BOOLEAN -> Boolean value. + Boolean(Option), +} + +#[cfg(feature = "mssql")] +impl<'a> TryFrom> for MsValue<'a> { + type Error = crate::error::Error; + fn try_from(v: Value<'a>) -> crate::Result { + match v { + Value::I8(int8) => Ok(MsValue::I8(int8)), + Value::I16(int16) => Ok(MsValue::I16(int16)), + Value::I32(int32) => Ok(MsValue::I32(int32)), + Value::I64(int64) => Ok(MsValue::I64(int64)), + #[cfg(any(feature = "mysql", feature = "sqlite"))] + Value::U8(_) => { + let msg = "u8 are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(any(feature = "mysql", feature = "sqlite"))] + Value::U16(_) => { + let msg = "u16 are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] + Value::U32(_) => { + let msg = "u32 are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "mysql")] + Value::U64(_) => { + let msg = "u64 are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + Value::Float(float) => Ok(MsValue::Float(float)), + Value::Double(double) => Ok(MsValue::Double(double)), + Value::Boolean(boolean) => Ok(MsValue::Boolean(boolean)), + Value::Text(text) => Ok(MsValue::Text(text)), + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] + Value::Bytes(_) => { + let msg = "&[u8]/Vec are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + }, + #[cfg(feature = "json")] + Value::Json(_) => { + let msg = "Json are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "uuid")] + Value::Uuid(_) => { + let msg = "Uuid are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "postgres")] + Value::PgInterval(_) => { + let msg = "PgInterval are only supported by SQLx with PostgreSQL, but not SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "postgres")] + Value::PgMoney(_) => { + let msg = "PgMoney are only supported by SQLx with PostgreSQL, but not SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(all(feature = "time", feature = "postgres"))] + Value::PgTimeTz(_) => { + let msg = "PgTimeTz are only supported by SQLx with PostgreSQL, but not SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "ipnetwork")] + Value::IpNetwork(_) => { + let msg = "IpNetwork are only supported by SQLx with PostgreSQL, but not SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "bigdecimal")] + Value::BigDecimal(_) => { + let msg = "BigDecimal are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "decimal")] + Value::Decimal(_decimal) => { + let msg = "Decimal are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "chrono")] + Value::UtcDateTime(_dt) => { + let msg = "DataTime are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + }, + #[cfg(feature = "chrono")] + Value::LocalDateTime(_dt) => { + let msg = "DataTime are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "chrono")] + Value::NaiveDateTime(_dt) => { + let msg = "NaiveDateTime are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "chrono")] + Value::NaiveDate(_date) => { + let msg = "NaiveDate are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "chrono")] + Value::NaiveTime(_time) => { + let msg = "NaiveTime are not supported by SQLx with SQL Server."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + } + } +} diff --git a/src/databases/mysql/mod.rs b/src/databases/mysql/mod.rs new file mode 100644 index 0000000..ec079d2 --- /dev/null +++ b/src/databases/mysql/mod.rs @@ -0,0 +1,72 @@ +use std::convert::TryFrom; + +mod value; + +pub use value::MyValue; + +#[cfg(feature = "mysql")] +impl<'a> super::HasVisitor<'a> for sqlx::MySql { + type Visitor = crate::visitors::Mysql<'a>; + fn visitor() -> Self::Visitor { + Self::Visitor::default() + } +} + +macro_rules! bind { + ($query:ident, $value: ident) => { + match $value { + MyValue::I8(int8) => $query.bind(int8), + MyValue::I16(int16) => $query.bind(int16), + MyValue::I32(int32) => $query.bind(int32), + MyValue::I64(int64) => $query.bind(int64), + MyValue::Float(float) => $query.bind(float) , + MyValue::Double(double) => $query.bind(double), + MyValue::Boolean(boolean) => $query.bind(boolean), + MyValue::Text(text) => $query.bind(text.map(|text|text.into_owned())), + MyValue::Bytes(bytes) => $query.bind(bytes.map(|b|b.into_owned())), + MyValue::U8(uint8) => $query.bind(uint8), + MyValue::U16(uint16) => $query.bind(uint16), + MyValue::U32(uint32) => $query.bind(uint32), + MyValue::U64(uint64) => $query.bind(uint64), + #[cfg(feature = "json")] + MyValue::Json(json) => $query.bind(json), + #[cfg(feature = "uuid")] + MyValue::Uuid(uuid) => $query.bind(uuid), + #[cfg(feature = "bigdecimal")] + MyValue::BigDecimal(bigdecimal) => $query.bind(bigdecimal), + #[cfg(feature = "decimal")] + MyValue::Decimal(decimal) => $query.bind(decimal), + #[cfg(feature = "chrono")] + MyValue::UtcDateTime(datetime) => $query.bind(datetime), + #[cfg(feature = "chrono")] + MyValue::LocalDateTime(datetime) => $query.bind(datetime), + #[cfg(feature = "chrono")] + MyValue::NaiveDateTime(datetime) => $query.bind(datetime), + #[cfg(feature = "chrono")] + MyValue::NaiveDate(date) => $query.bind(date), + #[cfg(feature = "chrono")] + MyValue::NaiveTime(time) => $query.bind(time), + } + } +} + + +#[cfg(feature = "mysql")] +impl<'a> super::TryBind<'a, sqlx::MySql> + for sqlx::query::Query<'a, sqlx::MySql, sqlx::mysql::MySqlArguments> +{ + fn try_bind(self, value: crate::ast::Value<'a>) -> crate::Result { + let value = MyValue::try_from(value)?; + Ok(bind!(self, value)) + } +} + +#[cfg(feature = "mysql")] +impl<'a, O> super::TryBind<'a, sqlx::MySql> + for sqlx::query::QueryAs<'a, sqlx::MySql, O, sqlx::mysql::MySqlArguments> +{ + fn try_bind(self, value: crate::ast::Value<'a>) -> crate::Result { + let value = MyValue::try_from(value)?; + Ok(bind!(self, value)) + } +} diff --git a/src/databases/mysql/value.rs b/src/databases/mysql/value.rs new file mode 100644 index 0000000..fe196b9 --- /dev/null +++ b/src/databases/mysql/value.rs @@ -0,0 +1,155 @@ +use std::borrow::Cow; +use std::convert::TryFrom; + +use crate::ast::Value; + +/// Values that supported by MySQL. +#[cfg(feature = "mysql")] +pub enum MyValue<'a, J: serde::ser::Serialize = ()> { + /// TINYINT + I8(Option), + /// SMALLINT + I16(Option), + /// INT + I32(Option), + /// BIGINT + I64(Option), + /// FLOAT -> 32-bit floating point. + Float(Option), + /// DOUBLE -> 64-bit floating point. + Double(Option), + /// VARCHAR, CHAR, TEXT -> String value. + Text(Option>), + /// VARBINARY, BINARY, BLOB -> Bytes value. + Bytes(Option>), + /// TINYINT(1), BOOLEAN -> Boolean value. + Boolean(Option), + + /// TINYINT UNSIGNED + U8(Option), + /// SMALLINT UNSIGNED + U16(Option), + /// INT UNSIGNED + U32(Option), + /// BIGINT UNSIGNED + U64(Option), + + #[cfg(feature = "json")] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] + /// A JSON value. + Json(Option>), + #[cfg(feature = "uuid")] + #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] + /// An UUID value. + Uuid(Option), + + /// A numeric value. + #[cfg(feature = "bigdecimal")] + #[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))] + BigDecimal(Option), + /// A numeric value. + #[cfg(feature = "decimal")] + #[cfg_attr(docsrs, doc(cfg(feature = "decimal")))] + Decimal(Option), + + /// TIMESTAMPTZ + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + UtcDateTime(Option>), + /// TIMESTAMPTZ + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + LocalDateTime(Option>), + /// TIMESTAMP + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + NaiveDateTime(Option), + /// DATE + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + NaiveDate(Option), + /// TIME + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + NaiveTime(Option), +} + +#[cfg(feature = "mysql")] +impl<'a> TryFrom> for MyValue<'a> { + type Error = crate::error::Error; + fn try_from(v: Value<'a>) -> crate::Result { + match v { + Value::I8(int8) => Ok(MyValue::I8(int8)), + Value::I16(int16) => Ok(MyValue::I16(int16)), + Value::I32(int32) => Ok(MyValue::I32(int32)), + Value::I64(int64) => Ok(MyValue::I64(int64)), + Value::U8(uint8) => Ok(MyValue::U8(uint8)), + Value::U16(uint16) => Ok(MyValue::U16(uint16)), + Value::U32(uint32) => Ok(MyValue::U32(uint32)), + Value::U64(uint64) => Ok(MyValue::U64(uint64)), + Value::Float(float) => Ok(MyValue::Float(float)), + Value::Double(double) => Ok(MyValue::Double(double)), + Value::Boolean(boolean) => Ok(MyValue::Boolean(boolean)), + Value::Text(text) => Ok(MyValue::Text(text)), + Value::Bytes(bytes) => Ok(MyValue::Bytes(bytes)), + #[cfg(feature = "json")] + Value::Json(json) => Ok(MyValue::Json(json)), + #[cfg(feature = "uuid")] + Value::Uuid(uuid) => Ok(MyValue::Uuid(uuid)), + #[cfg(feature = "postgres")] + Value::PgInterval(_) => { + let msg = "PgInterval are only supported by SQLx with PostgreSQL, but not MySQL."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "postgres")] + Value::PgMoney(_) => { + let msg = "PgMoney are only supported by SQLx with PostgreSQL, but not MySQL."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(all(feature = "time", feature = "postgres"))] + Value::PgTimeTz(_) => { + let msg = "PgTimeTz are only supported by SQLx with PostgreSQL, but not MySQL."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "ipnetwork")] + Value::IpNetwork(_) => { + let msg = "IpNetwork are only supported by SQLx with PostgreSQL, but not MySQL."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "bigdecimal")] + Value::BigDecimal(bigdecimal) => Ok(MyValue::BigDecimal(bigdecimal)), + #[cfg(feature = "decimal")] + Value::Decimal(decimal) => Ok(MyValue::Decimal(decimal)), + #[cfg(feature = "chrono")] + Value::UtcDateTime(dt) => Ok(MyValue::UtcDateTime(dt)), + #[cfg(feature = "chrono")] + Value::LocalDateTime(dt) => Ok(MyValue::LocalDateTime(dt)), + #[cfg(feature = "chrono")] + Value::NaiveDateTime(dt) => Ok(MyValue::NaiveDateTime(dt)), + #[cfg(feature = "chrono")] + Value::NaiveDate(date) => Ok(MyValue::NaiveDate(date)), + #[cfg(feature = "chrono")] + Value::NaiveTime(time) => Ok(MyValue::NaiveTime(time)), + } + } +} diff --git a/src/databases/postgres/mod.rs b/src/databases/postgres/mod.rs new file mode 100644 index 0000000..f66c3cd --- /dev/null +++ b/src/databases/postgres/mod.rs @@ -0,0 +1,76 @@ +use std::convert::TryFrom; + +mod value; + +pub use value::PgValue; + +#[cfg(feature = "postgres")] +impl<'a> super::HasVisitor<'a> for sqlx::Postgres { + type Visitor = crate::visitors::Postgres<'a>; + fn visitor() -> Self::Visitor { + Self::Visitor::default() + } +} + +macro_rules! bind { + ($query:ident, $value: ident) => { + match $value { + PgValue::I8(int8) => $query.bind(int8), + PgValue::I16(int16) => $query.bind(int16), + PgValue::I32(int32) => $query.bind(int32), + PgValue::I64(int64) => $query.bind(int64), + PgValue::Float(float) => $query.bind(float) , + PgValue::Double(double) => $query.bind(double), + PgValue::Boolean(boolean) => $query.bind(boolean), + PgValue::Text(text) => $query.bind(text.map(|text|text.into_owned())), + PgValue::Bytes(bytes) => $query.bind(bytes.map(|b|b.into_owned())), + PgValue::U32(uint32) => $query.bind(uint32), + #[cfg(feature = "json")] + PgValue::Json(json) => $query.bind(json), + #[cfg(feature = "uuid")] + PgValue::Uuid(uuid) => $query.bind(uuid), + PgValue::PgInterval(interval) => $query.bind(interval), + // #[cfg(feature = "postgres")] + // PgValue::PgRange(range) => $query.bind(range), + PgValue::PgMoney(money) => $query.bind(money), + #[cfg(feature = "bigdecimal")] + PgValue::BigDecimal(bigdecimal) => $query.bind(bigdecimal), + #[cfg(feature = "decimal")] + PgValue::Decimal(decimal) => $query.bind(decimal), + #[cfg(feature = "chrono")] + PgValue::UtcDateTime(datetime) => $query.bind(datetime), + #[cfg(feature = "chrono")] + PgValue::LocalDateTime(datetime) => $query.bind(datetime), + #[cfg(feature = "chrono")] + PgValue::NaiveDateTime(datetime) => $query.bind(datetime), + #[cfg(feature = "chrono")] + PgValue::NaiveDate(date) => $query.bind(date), + #[cfg(feature = "chrono")] + PgValue::NaiveTime(time) => $query.bind(time), + #[cfg(feature = "time")] + PgValue::PgTimeTz(timetz) => $query.bind(timetz), + #[cfg(feature = "ipnetwork")] + PgValue::IpNetwork(ipnetwork) => $query.bind(ipnetwork), + } + } +} + +#[cfg(feature = "postgres")] +impl<'a> super::TryBind<'a, sqlx::Postgres> + for sqlx::query::Query<'a, sqlx::Postgres, sqlx::postgres::PgArguments> +{ + fn try_bind(self, value: crate::ast::Value<'a>) -> crate::Result { + let value = PgValue::try_from(value)?; + Ok(bind!(self, value)) + } +} + +#[cfg(feature = "postgres")] +impl<'a, O> super::TryBind<'a, sqlx::Postgres> + for sqlx::query::QueryAs<'a, sqlx::Postgres, O, sqlx::postgres::PgArguments> +{ + fn try_bind(self, value: crate::ast::Value<'a>) -> crate::Result { + let value = PgValue::try_from(value)?; + Ok(bind!(self, value)) + } +} diff --git a/src/databases/postgres/value.rs b/src/databases/postgres/value.rs new file mode 100644 index 0000000..e742453 --- /dev/null +++ b/src/databases/postgres/value.rs @@ -0,0 +1,153 @@ +use std::borrow::Cow; +use std::convert::TryFrom; + +use crate::ast::Value; + +/// Values that supported by PostgreSQL. +#[cfg(feature = "postgres")] +pub enum PgValue<'a, J: serde::ser::Serialize = ()> { + /// TINYINT + I8(Option), + /// SMALLINT + I16(Option), + /// INT + I32(Option), + /// BIGINT + I64(Option), + /// FLOAT -> 32-bit floating point. + Float(Option), + /// DOUBLE -> 64-bit floating point. + Double(Option), + /// VARCHAR, CHAR, TEXT -> String value. + Text(Option>), + /// VARBINARY, BINARY, BLOB -> Bytes value. + Bytes(Option>), + /// TINYINT(1), BOOLEAN -> Boolean value. + Boolean(Option), + + /// INT UNSIGNED + U32(Option), + + #[cfg(feature = "json")] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] + /// A JSON value. + Json(Option>), + #[cfg(feature = "uuid")] + #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] + /// An UUID value. + Uuid(Option), + + /// A numeric value. + #[cfg(feature = "bigdecimal")] + #[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))] + BigDecimal(Option), + /// A numeric value. + #[cfg(feature = "decimal")] + #[cfg_attr(docsrs, doc(cfg(feature = "decimal")))] + Decimal(Option), + + /// TIMESTAMPTZ + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + UtcDateTime(Option>), + /// TIMESTAMPTZ + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + LocalDateTime(Option>), + /// TIMESTAMP + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + NaiveDateTime(Option), + /// DATE + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + NaiveDate(Option), + /// TIME + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + NaiveTime(Option), + + /// INTERVAL + PgInterval(Option), + /// MONEY + PgMoney(Option), + /// TIMETZ + #[cfg(feature = "time")] + PgTimeTz(Option), + /// INET, CIDR + #[cfg(feature = "ipnetwork")] + IpNetwork(Option), +} + +#[cfg(feature = "postgres")] +impl<'a> TryFrom> for PgValue<'a> { + type Error = crate::error::Error; + fn try_from(v: Value<'a>) -> crate::Result { + match v { + Value::I8(int8) => Ok(PgValue::I8(int8)), + Value::I16(int16) => Ok(PgValue::I16(int16)), + Value::I32(int32) => Ok(PgValue::I32(int32)), + Value::I64(int64) => Ok(PgValue::I64(int64)), + #[cfg(any(feature = "mysql", feature = "sqlite"))] + Value::U8(_uint8) => { + let msg = "u8 are not supported by SQLx with PostgreSQL."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(any(feature = "mysql", feature = "sqlite"))] + Value::U16(_uint16) => { + let msg = "u16 are not supported by SQLx with PostgreSQL."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + Value::U32(uint32) => Ok(PgValue::U32(uint32)), + #[cfg(feature = "mysql")] + Value::U64(_) => { + let msg = "u64 are not supported by SQLx with PostgreSQL."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + Value::Float(float) => Ok(PgValue::Float(float)), + Value::Double(double) => Ok(PgValue::Double(double)), + Value::Boolean(boolean) => Ok(PgValue::Boolean(boolean)), + Value::Text(text) => Ok(PgValue::Text(text)), + Value::Bytes(bytes) => Ok(PgValue::Bytes(bytes)), + #[cfg(feature = "json")] + Value::Json(json) => Ok(PgValue::Json(json)), + #[cfg(feature = "uuid")] + Value::Uuid(uuid) => Ok(PgValue::Uuid(uuid)), + Value::PgInterval(interval) => Ok(PgValue::PgInterval(interval)), + Value::PgMoney(money) => Ok(PgValue::PgMoney(money)), + #[cfg(feature = "time")] + Value::PgTimeTz(timetz) => Ok(PgValue::PgTimeTz(timetz)), + #[cfg(feature = "ipnetwork")] + Value::IpNetwork(ipnetwork) => Ok(PgValue::IpNetwork(ipnetwork)), + #[cfg(feature = "bigdecimal")] + Value::BigDecimal(bigdecimal) => Ok(PgValue::BigDecimal(bigdecimal)), + #[cfg(feature = "decimal")] + Value::Decimal(decimal) => Ok(PgValue::Decimal(decimal)), + #[cfg(feature = "chrono")] + Value::UtcDateTime(dt) => Ok(PgValue::UtcDateTime(dt)), + #[cfg(feature = "chrono")] + Value::LocalDateTime(dt) => Ok(PgValue::LocalDateTime(dt)), + #[cfg(feature = "chrono")] + Value::NaiveDateTime(dt) => Ok(PgValue::NaiveDateTime(dt)), + #[cfg(feature = "chrono")] + Value::NaiveDate(date) => Ok(PgValue::NaiveDate(date)), + #[cfg(feature = "chrono")] + Value::NaiveTime(time) => Ok(PgValue::NaiveTime(time)), + } + } +} diff --git a/src/databases/sqlite/mod.rs b/src/databases/sqlite/mod.rs new file mode 100644 index 0000000..7c63239 --- /dev/null +++ b/src/databases/sqlite/mod.rs @@ -0,0 +1,74 @@ +use std::convert::TryFrom; + +mod value; + +pub use value::SQLiteValue; + +#[cfg(feature = "sqlite")] +impl<'a> super::HasVisitor<'a> for sqlx::Sqlite { + type Visitor = crate::visitors::Sqlite<'a>; + fn visitor() -> Self::Visitor { + Self::Visitor::default() + } +} + +/* +impl super::HasValue for sqlx::Sqlite { + type Value = SQLiteValue; +} +*/ + + +macro_rules! bind { + ($query:ident, $value: ident) => { + match $value { + SQLiteValue::I8(int8) => $query.bind(int8), + SQLiteValue::I16(int16) => $query.bind(int16), + SQLiteValue::I32(int32) => $query.bind(int32), + SQLiteValue::I64(int64) => $query.bind(int64), + SQLiteValue::Float(float) => $query.bind(float) , + SQLiteValue::Double(double) => $query.bind(double), + SQLiteValue::Boolean(boolean) => $query.bind(boolean), + SQLiteValue::Text(text) => $query.bind(text.map(|text|text.into_owned())), + SQLiteValue::Bytes(bytes) => $query.bind(bytes.map(|b|b.into_owned())), + SQLiteValue::U8(uint8) => $query.bind(uint8), + SQLiteValue::U16(uint16) => $query.bind(uint16), + SQLiteValue::U32(uint32) => $query.bind(uint32), + #[cfg(feature = "json")] + SQLiteValue::Json(json) => $query.bind(json), + #[cfg(feature = "uuid")] + SQLiteValue::Uuid(uuid) => $query.bind(uuid), + #[cfg(feature = "chrono")] + SQLiteValue::UtcDateTime(datetime) => $query.bind(datetime), + #[cfg(feature = "chrono")] + SQLiteValue::LocalDateTime(datetime) => $query.bind(datetime), + #[cfg(feature = "chrono")] + SQLiteValue::NaiveDateTime(datetime) => $query.bind(datetime), + #[cfg(feature = "chrono")] + SQLiteValue::NaiveDate(date) => $query.bind(date), + #[cfg(feature = "chrono")] + SQLiteValue::NaiveTime(time) => $query.bind(time), + } + } + +} + +#[cfg(feature = "sqlite")] +impl<'a> super::TryBind<'a, sqlx::Sqlite> + for sqlx::query::Query<'a, sqlx::Sqlite, sqlx::sqlite::SqliteArguments<'a>> +{ + fn try_bind(self, value: crate::ast::Value<'a>) -> crate::Result { + let value = SQLiteValue::try_from(value)?; + Ok(bind!(self, value)) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, O> super::TryBind<'a, sqlx::Sqlite> + for sqlx::query::QueryAs<'a, sqlx::Sqlite, O, sqlx::sqlite::SqliteArguments<'a>> +{ + fn try_bind(self, value: crate::ast::Value<'a>) -> crate::Result { + let value = SQLiteValue::try_from(value)?; + Ok(bind!(self, value)) + } +} diff --git a/src/databases/sqlite/value.rs b/src/databases/sqlite/value.rs new file mode 100644 index 0000000..813fbf0 --- /dev/null +++ b/src/databases/sqlite/value.rs @@ -0,0 +1,169 @@ +use std::borrow::Cow; +use std::convert::TryFrom; + +use crate::ast::Value; + +/// Values that supported by SQLite. +#[cfg(feature = "sqlite")] +pub enum SQLiteValue<'a, J: serde::ser::Serialize = ()> { + /// TINYINT + I8(Option), + /// SMALLINT + I16(Option), + /// INT + I32(Option), + /// BIGINT + I64(Option), + /// FLOAT -> 32-bit floating point. + Float(Option), + /// DOUBLE -> 64-bit floating point. + Double(Option), + /// VARCHAR, CHAR, TEXT -> String value. + Text(Option>), + /// VARBINARY, BINARY, BLOB -> Bytes value. + Bytes(Option>), + /// TINYINT(1), BOOLEAN -> Boolean value. + Boolean(Option), + + /// TINYINT UNSIGNED + U8(Option), + /// SMALLINT UNSIGNED + U16(Option), + /// INT UNSIGNED + U32(Option), + + #[cfg(feature = "json")] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] + /// A JSON value. + Json(Option>), + #[cfg(feature = "uuid")] + #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] + /// An UUID value. + Uuid(Option), + + /// TIMESTAMPTZ + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + UtcDateTime(Option>), + /// TIMESTAMPTZ + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + LocalDateTime(Option>), + /// TIMESTAMP + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + NaiveDateTime(Option), + /// DATE + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + NaiveDate(Option), + /// TIME + #[cfg(feature = "chrono")] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] + NaiveTime(Option), +} + +#[cfg(feature = "sqlite")] +impl<'a> TryFrom> for SQLiteValue<'a> { + type Error = crate::error::Error; + fn try_from(v: Value<'a>) -> crate::Result { + match v { + Value::I8(int8) => Ok(SQLiteValue::I8(int8)), + Value::I16(int16) => Ok(SQLiteValue::I16(int16)), + Value::I32(int32) => Ok(SQLiteValue::I32(int32)), + Value::I64(int64) => Ok(SQLiteValue::I64(int64)), + Value::U8(uint8) => Ok(SQLiteValue::U8(uint8)), + Value::U16(uint16) => Ok(SQLiteValue::U16(uint16)), + Value::U32(uint32) => Ok(SQLiteValue::U32(uint32)), + #[cfg(feature = "mysql")] + Value::U64(_) => { + let msg = "u64 are not supported by SQLx with SQLite."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + Value::Float(float) => Ok(SQLiteValue::Float(float)), + Value::Double(double) => Ok(SQLiteValue::Double(double)), + Value::Boolean(boolean) => Ok(SQLiteValue::Boolean(boolean)), + Value::Text(text) => Ok(SQLiteValue::Text(text)), + Value::Bytes(bytes) => Ok(SQLiteValue::Bytes(bytes)), + #[cfg(feature = "json")] + Value::Json(json) => Ok(SQLiteValue::Json(json)), + #[cfg(feature = "uuid")] + Value::Uuid(uuid) => Ok(SQLiteValue::Uuid(uuid)), + #[cfg(feature = "postgres")] + Value::PgInterval(_) => { + let msg = "PgInterval are only supported by SQLx with PostgreSQL, but not SQLite."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "postgres")] + Value::PgMoney(_) => { + let msg = "PgMoney are only supported by SQLx with PostgreSQL, but not SQLite."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(all(feature = "time", feature = "postgres"))] + Value::PgTimeTz(_) => { + let msg = "PgTimeTz are only supported by SQLx with PostgreSQL, but not SQLite."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "ipnetwork")] + Value::IpNetwork(_) => { + let msg = "IpNetwork are only supported by SQLx with PostgreSQL, but not SQLite."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "bigdecimal")] + Value::BigDecimal(_) => { + let msg = "BigDecimal are not supported by SQLx with SQLite."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "decimal")] + Value::Decimal(_decimal) => { + let msg = "Decimal are not supported by SQLx with SQLite."; + let kind = crate::error::ErrorKind::conversion(msg); + + let mut builder = crate::error::Error::builder(kind); + builder.set_original_message(msg); + + Err(builder.build()) + } + #[cfg(feature = "chrono")] + Value::UtcDateTime(dt) => Ok(SQLiteValue::UtcDateTime(dt)), + #[cfg(feature = "chrono")] + Value::LocalDateTime(dt) => Ok(SQLiteValue::LocalDateTime(dt)), + #[cfg(feature = "chrono")] + Value::NaiveDateTime(dt) => Ok(SQLiteValue::NaiveDateTime(dt)), + #[cfg(feature = "chrono")] + Value::NaiveDate(date) => Ok(SQLiteValue::NaiveDate(date)), + #[cfg(feature = "chrono")] + Value::NaiveTime(time) => Ok(SQLiteValue::NaiveTime(time)), + } + } +} diff --git a/src/databases/try_bind.rs b/src/databases/try_bind.rs new file mode 100644 index 0000000..d55113b --- /dev/null +++ b/src/databases/try_bind.rs @@ -0,0 +1,8 @@ +pub trait TryBind<'a, DB> +where + DB: sqlx::Database, +{ + fn try_bind(self, v: crate::ast::Value<'a>) -> crate::Result + where + Self: Sized; +} diff --git a/src/lib.rs b/src/lib.rs index 12be540..c7962ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,11 +22,11 @@ pub mod error; pub type Result = std::result::Result; pub mod prelude { - use std::future::Future; + use std::marker::PhantomData; - use sqlx::Database; - use sqlx::Executor; + + pub use xiayu_derive::*; diff --git a/src/macros.rs b/src/macros.rs index 0dc7a3a..60ba97d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -162,6 +162,7 @@ macro_rules! expression { } /// A test-generator to test types in the defined database. +#[allow(unused_macros)] #[cfg(test)] macro_rules! test_type { ($name:ident($db:ident, $sql_type:literal, $(($input:expr, $output:expr)),+ $(,)?)) => { diff --git a/src/visitors/mssql.rs b/src/visitors/mssql.rs index 7e4f208..0db5238 100644 --- a/src/visitors/mssql.rs +++ b/src/visitors/mssql.rs @@ -6,7 +6,6 @@ use crate::{ Column, Comparable, Expression, ExpressionKind, Insert, IntoRaw, Join, JoinData, Joinable, Merge, OnConflict, Order, Ordering, Row, Table, TypeDataLength, TypeFamily, Value, Values, }, - error::{Error, ErrorKind}, prelude::{Aliasable, Average, Query}, visitors, }; @@ -322,6 +321,11 @@ impl<'a> Visitor<'a> for Mssql<'a> { }), Value::Text(t) => t.map(|t| self.write(format!("'{}'", t))), Value::Boolean(b) => b.map(|b| self.write(if b { 1 } else { 0 })), + v @ _ => { + // FIXME: Maybe define MsValue at here? + crate::databases::mssql::MsValue::try_from(v)?; + None + } }; match res { @@ -1312,7 +1316,7 @@ mod tests { #[test] #[cfg(feature = "uuid")] fn test_raw_uuid() { - let uuid = uuid::Uuid::new_v4(); + let uuid = sqlx::types::Uuid::new_v4(); let (sql, params) = Mssql::build(Select::default().value(uuid.raw())).unwrap(); assert_eq!( @@ -1329,7 +1333,7 @@ mod tests { #[test] #[cfg(feature = "chrono")] fn test_raw_datetime() { - let dt = chrono::Utc::now(); + let dt = sqlx::types::chrono::Utc::now(); let (sql, params) = Mssql::build(Select::default().value(dt.raw())).unwrap(); assert_eq!( diff --git a/src/visitors/mysql.rs b/src/visitors/mysql.rs index f33ec97..3bebd43 100644 --- a/src/visitors/mysql.rs +++ b/src/visitors/mysql.rs @@ -1,4 +1,4 @@ -use std::fmt::{self, Write}; +use std::{convert::TryFrom, fmt::{self, Write}}; use crate::{ ast::*, @@ -191,6 +191,10 @@ impl<'a> Visitor<'a> for Mysql<'a> { Value::NaiveDate(date) => date.map(|date| self.write(format!("'{}'", date))), #[cfg(feature = "chrono")] Value::NaiveTime(time) => time.map(|time| self.write(format!("'{}'", time))), + v @ _ => { + crate::databases::mysql::MyValue::try_from(v)?; + None + } }; match res { @@ -853,7 +857,7 @@ mod tests { #[test] #[cfg(feature = "uuid")] fn test_raw_uuid() { - let uuid = uuid::Uuid::new_v4(); + let uuid = sqlx::types::Uuid::new_v4(); let (sql, params) = Mysql::build(Select::default().value(uuid.raw())).unwrap(); assert_eq!( @@ -867,7 +871,7 @@ mod tests { #[test] #[cfg(feature = "chrono")] fn test_raw_datetime() { - let dt = chrono::Utc::now(); + let dt = sqlx::types::chrono::Utc::now(); let (sql, params) = Mysql::build(Select::default().value(dt.raw())).unwrap(); assert_eq!(format!("SELECT '{}'", dt.to_rfc3339(),), sql); diff --git a/src/visitors/postgres.rs b/src/visitors/postgres.rs index bd6ae23..e4a507c 100644 --- a/src/visitors/postgres.rs +++ b/src/visitors/postgres.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::fmt::{self, Write}; use crate::ast::*; @@ -148,11 +149,15 @@ impl<'a> Visitor<'a> for Postgres<'a> { interval.map(|interval| self.write(format!("{:?}", interval))) } #[cfg(all(feature = "time", feature = "postgres"))] - Value::PgTimeTz(timetz) => timetz.map(|timetz| self.write(format!("{:?}", timtz))), + Value::PgTimeTz(timetz) => timetz.map(|timetz| self.write(format!("{:?}", timetz))), #[cfg(feature = "ipnetwork")] Value::IpNetwork(ipnetwork) => { ipnetwork.map(|ipnetwork| self.write(format!("'{}'", ipnetwork))) } + v @ _ => { + crate::databases::postgres::PgValue::try_from(v)?; + None + } }; match res { @@ -469,6 +474,8 @@ impl<'a> Visitor<'a> for Postgres<'a> { mod tests { use crate::{prelude::*, visitors::*}; use xiayu_derive::*; + use sqlx::types::Uuid; + use sqlx::types::chrono; #[derive(Entity)] struct User { @@ -853,7 +860,7 @@ mod tests { #[test] #[cfg(feature = "uuid")] fn test_raw_uuid() { - let uuid = uuid::Uuid::new_v4(); + let uuid = Uuid::new_v4(); let (sql, params) = Postgres::build(Select::default().value(uuid.raw())).unwrap(); assert_eq!( diff --git a/src/visitors/sqlite.rs b/src/visitors/sqlite.rs index 41ca38e..70deb85 100644 --- a/src/visitors/sqlite.rs +++ b/src/visitors/sqlite.rs @@ -1,10 +1,9 @@ use crate::{ ast::*, - error::{Error, ErrorKind}, visitors::{self, Visitor}, }; -use std::fmt::{self, Write}; +use std::{convert::TryFrom, fmt::{self, Write}}; /// A visitor to generate queries for the SQLite database. /// @@ -101,6 +100,10 @@ impl<'a> Visitor<'a> for Sqlite<'a> { Value::NaiveDate(date) => date.map(|date| self.write(format!("'{}'", date))), #[cfg(feature = "chrono")] Value::NaiveTime(time) => time.map(|time| self.write(format!("'{}'", time))), + v @ _ => { + crate::databases::sqlite::SQLiteValue::try_from(v)?; + None + } }; match res { @@ -899,7 +902,7 @@ mod tests { #[test] #[cfg(feature = "uuid")] fn test_raw_uuid() { - let uuid = uuid::Uuid::new_v4(); + let uuid = sqlx::types::Uuid::new_v4(); let (sql, params) = Sqlite::build(Select::default().value(uuid.raw())).unwrap(); assert_eq!( @@ -913,7 +916,7 @@ mod tests { #[test] #[cfg(feature = "chrono")] fn test_raw_datetime() { - let dt = chrono::Utc::now(); + let dt = sqlx::types::chrono::Utc::now(); let (sql, params) = Sqlite::build(Select::default().value(dt.raw())).unwrap(); assert_eq!(format!("SELECT '{}'", dt.to_rfc3339(),), sql); From 1c60c328c60fb7dce935c3164d24bbbaf0641fd8 Mon Sep 17 00:00:00 2001 From: songww Date: Fri, 10 Sep 2021 21:57:00 +0800 Subject: [PATCH 5/5] Fixing tests --- Cargo.toml | 4 +- build.rs | 107 ----------------------- derive/src/lib.rs | 4 +- entity-examples/src/lib.rs | 28 +++--- src/ast/update.rs | 15 ++-- src/ast/values.rs | 148 +++++++++++--------------------- src/databases/mysql/value.rs | 4 +- src/databases/postgres/value.rs | 4 +- src/databases/sqlite/value.rs | 4 +- src/lib.rs | 3 - src/macros.rs | 4 +- src/visitors/mssql.rs | 68 ++++++++------- src/visitors/mysql.rs | 14 +-- src/visitors/postgres.rs | 6 +- src/visitors/sqlite.rs | 24 +++--- 15 files changed, 146 insertions(+), 291 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5d3801c..a95fa73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,9 +40,7 @@ sqlx = { version = "0.5" } [dev-dependencies] tokio = { version = "1.10", features = [ "rt", "macros" ] } entities = { path = "./entity-examples", package = "xiayu-entity-examples" } - -[build-dependencies] -cfg_aliases = "0.1" +uuid_ = { version = "0.8", package = "uuid", features = [ "v4" ] } [features] default = [ "uuid", "json", "chrono", "decimal", "bigdecimal" ] diff --git a/build.rs b/build.rs index c45749e..1f8bb47 100644 --- a/build.rs +++ b/build.rs @@ -1,110 +1,3 @@ -use cfg_aliases::cfg_aliases; - fn main() { println!("cargo:rustc-cfg=docsrs"); - // Setup cfg aliases - cfg_aliases! { - only_mysql: { all( - feature = "mysql", - not(any(feature = "mssql", feature = "sqlite", feature = "postgres")) - ) }, - only_postgres: { all( - feature = "postgres", - not(any(feature = "mysql", feature = "mssql", feature = "sqlite")) - ) }, - only_sqlite: { all( - feature = "sqlite", - not(any(feature = "mysql", feature = "mssql", feature = "postgres")) - ) }, - only_mssql: { all( - feature = "mssql", - not(any(feature = "mysql", feature = "sqlite", feature = "postgres")) - ) }, - not_mssql: { all( - any(feature = "mysql", feature = "sqlite", feature = "postgres"), - not(feature = "mssql") - ) }, - mysql_or_sqlite: { all( - any(feature = "mysql", feature = "sqlite"), - not(any(feature = "mssql", feature = "postgres")) - ) }, - json: { all( - feature = "json", - any(feature = "postgres", feature = "mysql", feature = "sqlite"), - not(feature = "mssql") - ) }, - uuid: { all( - feature = "uuid", - any(feature = "mysql", feature = "sqlite", feature = "postgres"), - not(feature = "mssql") - ) }, - bigdecimal: { all( - feature = "bigdecimal", - any(feature = "mysql", feature = "postgres"), - not(any(feature = "sqlite", feature = "mssql")) - ) }, - decimal: { all( - feature = "decimal", - any(feature = "mysql", feature = "postgres"), - not(any(feature = "sqlite", feature = "mssql")) - ) }, - chrono: { all( - feature = "chrono", - any(feature = "mysql", feature = "sqlite", feature = "postgres"), - not(feature = "mssql") - ) }, - time: { all( - feature = "time", - not(any(feature = "mssql", feature = "sqlite")) - ) }, - ipnetwork: { all( - feature = "ipnetwork", - only_postgres - ) }, - } - - if cfg!(all(feature = "json", not(json), not(docsrs))) { - println!("cargo:warning=feature `json` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); - } - - if cfg!(all(feature = "uuid", not(uuid), not(docsrs))) { - println!("cargo:warning=feature `uuid` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); - } - - if cfg!(all(feature = "bigdecimal", not(bigdecimal), not(docsrs))) { - println!("cargo:warning=feature `bigdecimal` still disabled, because it only support for `MySQL` and `PostgreSQL`, but `{}` is enabled .", - if cfg!(feature = "mssql") { - "MSSQL" - } else if cfg!(feature = "sqlite") { - "Sqlite" - } else { - unreachable!() - } - ); - } - - if cfg!(all(feature = "decimal", not(decimal), not(docsrs))) { - println!("cargo:warning=feature `decimal` still disabled, because it only support for `MySQL` and `PostgreSQL`, but `{}` is enabled .", - if cfg!(feature = "mssql") { - "MSSQL" - } else if cfg!(feature = "sqlite") { - "Sqlite" - } else { - unreachable!() - } - ); - } - - if cfg!(all(feature = "chrono", not(chrono), not(docsrs))) { - println!("cargo:warning=feature `chrono` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); - } - - /* - if cfg!(all(feature = "time", not(time))) { - println!("cargo:warning=feature `chrono` still disabled, because it only support for `MySQL` `Sqlite` and `PostgreSQL`, but `MSSQL` is enabled ."); - } - */ - if cfg!(all(feature = "ipnetwork", not(ipnetwork), not(docsrs))) { - println!("cargo:warning=feature `ipnetwork` still disabled, because it only support for `PostgreSQL`, but `MySQL` `Sqlite` or `MSSQL` is enabled ."); - } } diff --git a/derive/src/lib.rs b/derive/src/lib.rs index 735ab4a..cc14fca 100644 --- a/derive/src/lib.rs +++ b/derive/src/lib.rs @@ -272,9 +272,10 @@ pub fn derive_entity(input: TokenStream) -> TokenStream { // let orig_generics = &entity_def.generics; tokens.extend(quote! { impl #ident { + #[allow(non_snake_case)] const _table: #namespace::Table<'static> = #table_def; - #(pub const #names: #namespace::ColumnOptions<#types> = #column_options;) * + #(#[allow(non_snake_case)] pub const #names: #namespace::ColumnOptions<#types> = #column_options;) * } impl #namespace::Entity for #ident { @@ -302,6 +303,7 @@ pub fn derive_entity(input: TokenStream) -> TokenStream { // impl HasPrimaryKey if PrimaryKey exists. let token = quote! { impl #ident { + #[allow(non_snake_case)] const _primary_key: ::PrimaryKey = #primary_key_column; } diff --git a/entity-examples/src/lib.rs b/entity-examples/src/lib.rs index d6f82d0..8288ecb 100644 --- a/entity-examples/src/lib.rs +++ b/entity-examples/src/lib.rs @@ -1,31 +1,39 @@ +#![allow(dead_code)] + use xiayu::prelude::*; #[derive(Entity)] pub struct User { - id: i32, + pub id: i32, } #[derive(Entity)] pub struct Post { - id: i32, - user_id: i32, + pub id: i32, + pub user_id: i32, } #[derive(Entity)] pub struct Recipe { - name: String, - ingredients: String, + pub name: String, + pub ingredients: String, } #[derive(Entity)] pub struct Cat { - master_id: i32, - ingredients: String, + pub master_id: i32, + pub ingredients: String, } #[derive(Entity)] pub struct Dog { - slave_id: i32, - age: i32, - ingredients: String, + pub slave_id: i32, + pub age: i32, + pub ingredients: String, +} + +#[derive(Debug, Entity)] +pub struct Bar { + pub id: i32, + pub uniq_val: i32, } diff --git a/src/ast/update.rs b/src/ast/update.rs index d335b7c..e729fdd 100644 --- a/src/ast/update.rs +++ b/src/ast/update.rs @@ -32,9 +32,10 @@ impl<'a> Update<'a> { /// Add another column value assignment to the query /// /// ```rust - /// # use xiayu::{ast::*, visitors::{Visitor, Sqlite}}; + /// # use xiayu::{prelude::*, visitors::{Visitor, Sqlite}}; + /// # use entities::User; /// # fn main() -> Result<(), xiayu::error::Error> { - /// let query = Update::table("users").set("foo", 10).set("bar", false); + /// let query = Update::table(User::table()).set(User::foo, 10).set(User::bar, false); /// let (sql, params) = Sqlite::build(query)?; /// /// assert_eq!("UPDATE `users` SET `foo` = ?, `bar` = ?", sql); @@ -64,9 +65,10 @@ impl<'a> Update<'a> { /// [Comparable](trait.Comparable.html#required-methods) for more examples. /// /// ```rust - /// # use xiayu::{ast::*, visitors::{Visitor, Sqlite}}; + /// # use xiayu::{prelude::*, visitors::{Visitor, Sqlite}}; + /// # use entities::User; /// # fn main() -> Result<(), xiayu::error::Error> { - /// let query = Update::table("users").set("foo", 1).so_that("bar".equals(false)); + /// let query = Update::table(User::table()).set(User::foo, 1).so_that(User::bar.equals(false)); /// let (sql, params) = Sqlite::build(query)?; /// /// assert_eq!("UPDATE `users` SET `foo` = ? WHERE `bar` = ?", sql); @@ -86,9 +88,10 @@ impl<'a> Update<'a> { /// /// ```rust /// # use xiayu::{ast::*, visitors::{Visitor, Sqlite}}; + /// # use entities::Bar; /// # fn main() -> Result<(), xiayu::error::Error> { - /// let select = Select::from_table("bars").column("id").so_that("uniq_val".equals(3)); - /// let query = Update::table("users").set("foo", 1).so_that("bar".equals(select)); + /// let select = Select::from_table(Bar::table()).column(Bar::id).so_that(Bar::uniq_val.equals(3)); + /// let query = Update::table(User::table()).set(User::foo, 1).so_that(User::bar.equals(select)); /// let (sql, params) = Sqlite::build(query)?; /// /// assert_eq!( diff --git a/src/ast/values.rs b/src/ast/values.rs index 2852aed..c354d00 100644 --- a/src/ast/values.rs +++ b/src/ast/values.rs @@ -1,10 +1,7 @@ -use std::borrow::{Cow}; +use std::borrow::Cow; use std::convert::TryFrom; - - - #[cfg(feature = "chrono")] use sqlx::types::chrono::{self, DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime, Utc}; #[cfg(feature = "bigdecimal")] @@ -57,7 +54,7 @@ trait PgCompatibleType: /// defined by their corresponding type variants with a `None` value for best /// compatibility. #[derive(Debug, Clone, PartialEq)] -pub enum Value<'a, J: serde::ser::Serialize = ()> { +pub enum Value<'a> { /// TINYINT I8(Option), /// SMALLINT @@ -104,19 +101,17 @@ pub enum Value<'a, J: serde::ser::Serialize = ()> { /* /// Database enum value. Enum(Option>), - /// A single character. - Char(Option), - #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] - /// An array value (PostgreSQL). - Array(Option>>), - /// A numeric value. /// A XML value. Xml(Option>), */ + // #[cfg(feature = "postgres")] + // #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] + // /// An array value (PostgreSQL). + // Array(Option>), #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] /// A JSON value. - Json(Option>), + Json(Option), #[cfg(feature = "uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] /// An UUID value. @@ -177,9 +172,9 @@ pub enum Value<'a, J: serde::ser::Serialize = ()> { } /* -pub(crate) struct Params<'a>(pub(crate) &'a [Value<'a>]); +pub(crate) struct Parameters<'a>(pub(crate) &'a [Value<'a>]); -impl<'a> fmt::Display for Params<'a> { +impl<'a> fmt::Display for Parameters<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let len = self.0.len(); @@ -244,65 +239,19 @@ impl<'a> fmt::Display for Value<'a> { } } } +*/ -#[cfg(feature = "json")] -#[cfg_attr(docsrs, doc(cfg(feature = "json")))] -impl<'a> From> for serde_json::Value { - fn from(pv: Value<'a>) -> Self { - let res = match pv { - Value::Integer(i) => i.map(|i| serde_json::Value::Number(Number::from(i))), - Value::Float(f) => f.map(|f| match Number::from_f64(f as f64) { - Some(number) => serde_json::Value::Number(number), - None => serde_json::Value::Null, - }), - Value::Double(f) => f.map(|f| match Number::from_f64(f) { - Some(number) => serde_json::Value::Number(number), - None => serde_json::Value::Null, - }), - Value::Text(cow) => cow.map(|cow| serde_json::Value::String(cow.into_owned())), - Value::Bytes(bytes) => { - bytes.map(|bytes| serde_json::Value::String(base64::encode(&bytes))) - } - Value::Enum(cow) => cow.map(|cow| serde_json::Value::String(cow.into_owned())), - Value::Boolean(b) => b.map(serde_json::Value::Bool), - Value::Char(c) => c.map(|c| { - let bytes = [c as u8]; - let s = std::str::from_utf8(&bytes) - .expect("interpret byte as UTF-8") - .to_string(); - serde_json::Value::String(s) - }), - Value::Xml(cow) => cow.map(|cow| serde_json::Value::String(cow.into_owned())), - Value::Array(v) => v.map(|v| { - serde_json::Value::Array(v.into_iter().map(serde_json::Value::from).collect()) - }), - #[cfg(feature = "bigdecimal")] - Value::Numeric(d) => d.map(|d| serde_json::to_value(d.to_f64().unwrap()).unwrap()), - #[cfg(feature = "json")] - Value::Json(v) => match v { - Json::JsonValue(v) => v, - Json::JsonRawValue(v) => v.and_then(|v| serde_json::to_value(*v).ok()), - }, - #[cfg(feature = "uuid")] - Value::Uuid(u) => u.map(|u| serde_json::Value::String(u.to_hyphenated().to_string())), - #[cfg(feature = "chrono")] - Value::DateTime(dt) => dt.map(|dt| serde_json::Value::String(dt.to_rfc3339())), - #[cfg(feature = "chrono")] - Value::Date(date) => date.map(|date| serde_json::Value::String(format!("{}", date))), - #[cfg(feature = "chrono")] - Value::Time(time) => time.map(|time| serde_json::Value::String(format!("{}", time))), - _ => unimplemented!(), - }; +impl<'a> Value<'a> { + /// Creates a new int32 value. + pub const fn int32(value: i32) -> Self { + Self::I32(Some(value)) + } - match res { - Some(val) => val, - None => serde_json::Value::Null, - } + /// Creates a new int64 value. + pub const fn int64(value: i64) -> Self { + Self::I64(Some(value)) } -} -*/ -impl<'a> Value<'a> { /// Creates a new float value. pub const fn float(value: f32) -> Self { Self::Float(Some(value)) @@ -360,6 +309,13 @@ impl<'a> Value<'a> { Value::Uuid(Some(value)) } + /// Creates a new JSON value. + #[cfg(feature = "json")] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] + pub fn json(value: J) -> Self where J: serde::ser::Serialize { + Value::Json(serde_json::to_value(value).ok()) + } + /* /// Creates a new datetime value. #[cfg(feature = "chrono")] @@ -382,13 +338,6 @@ impl<'a> Value<'a> { Value::Time(Some(value)) } - /// Creates a new JSON value. - #[cfg(feature = "json")] - #[cfg_attr(docsrs, doc(cfg(feature = "json")))] - pub const fn json(value: Json<'a>) -> Self { - Value::Json(value) - } - /// Creates a new XML value. pub fn xml(value: T) -> Self where @@ -697,59 +646,42 @@ impl<'a> Value<'a> { value!(val: i8, I8, val); value!(val: i16, I16, val); -value!(val: i32, I32, val); +// value!(val: i32, I32, val); value!(val: i64, I64, val); #[cfg(any(features = "mysql", feature = "sqlite"))] -#[cfg_attr(docsrs, doc(cfg(any(features = "mysql", feature = "sqlite"))))] value!(val: u8, U8, val); #[cfg(any(features = "mysql", feature = "sqlite"))] -#[cfg_attr(docsrs, doc(cfg(any(features = "mysql", feature = "sqlite"))))] value!(val: u16, U16, val); #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] -#[cfg_attr( - docsrs, - doc(cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))) -)] value!(val: u32, U32, val); #[cfg(feature = "mysql")] -#[cfg_attr(docsrs, doc(cfg(feature = "mysql")))] value!(val: u64, U64, val); value!(val: bool, Boolean, val); value!(val: &'a str, Text, val.into()); value!(val: String, Text, val.into()); value!(val: usize, I64, i64::try_from(val).unwrap()); #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] -#[cfg_attr( - docsrs, - doc(cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))) -)] value!(val: &'a [u8], Bytes, val.into()); value!(val: f64, Double, val); value!(val: f32, Float, val); #[cfg(feature = "chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] value!(val: chrono::NaiveTime, NaiveTime, val); #[cfg(feature = "chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] value!(val: chrono::NaiveDate, NaiveDate, val); #[cfg(feature = "chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] value!(val: chrono::NaiveDateTime, NaiveDateTime, val); #[cfg(feature = "chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] value!(val: chrono::DateTime, UtcDateTime, val); #[cfg(feature = "chrono")] -#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] value!(val: chrono::DateTime, LocalDateTime, val); #[cfg(feature = "bigdecimal")] value!(val: BigDecimal, BigDecimal, val); #[cfg(feature = "uuid")] -#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] value!(val: Uuid, Uuid, val); +/* #[cfg(feature = "json")] -#[cfg_attr(docsrs, doc(cfg(feature = "json")))] impl<'a, T> From for Value<'a, T> where T: serde::ser::Serialize, @@ -758,15 +690,30 @@ where Value::Json(Some(sqlx::types::Json(val))) } } +*/ + +impl<'a> From for Value<'a> { + fn from(i: i32) -> Self + { + Value::I32(Some(i)) + } +} + +#[cfg(feature = "json")] +impl<'a> From for Value<'a> { + fn from(v: serde_json::Value) -> Self { + Value::Json(Some(v)) + } +} #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] -impl<'a, T> From> for Value<'a, T> +impl<'a, J> From> for Value<'a> where - T: serde::ser::Serialize, + J: serde::ser::Serialize, { - fn from(val: sqlx::types::Json) -> Self { - Value::Json(Some(val)) + fn from(val: sqlx::types::Json) -> Self { + Value::Json(serde_json::to_value(val).ok()) } } @@ -925,7 +872,9 @@ mod tests { use super::*; #[cfg(feature = "chrono")] use std::str::FromStr; + extern crate self as xiayu; + /* #[test] fn a_parameterized_value_of_ints_can_be_converted_into_a_vec() { let pv = Value::array(vec![1]); @@ -969,4 +918,5 @@ mod tests { let rslt: Option> = pv.into_vec(); assert!(rslt.is_none()); } + */ } diff --git a/src/databases/mysql/value.rs b/src/databases/mysql/value.rs index fe196b9..ea2bb80 100644 --- a/src/databases/mysql/value.rs +++ b/src/databases/mysql/value.rs @@ -5,7 +5,7 @@ use crate::ast::Value; /// Values that supported by MySQL. #[cfg(feature = "mysql")] -pub enum MyValue<'a, J: serde::ser::Serialize = ()> { +pub enum MyValue<'a> { /// TINYINT I8(Option), /// SMALLINT @@ -37,7 +37,7 @@ pub enum MyValue<'a, J: serde::ser::Serialize = ()> { #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] /// A JSON value. - Json(Option>), + Json(Option), #[cfg(feature = "uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] /// An UUID value. diff --git a/src/databases/postgres/value.rs b/src/databases/postgres/value.rs index e742453..748a19b 100644 --- a/src/databases/postgres/value.rs +++ b/src/databases/postgres/value.rs @@ -5,7 +5,7 @@ use crate::ast::Value; /// Values that supported by PostgreSQL. #[cfg(feature = "postgres")] -pub enum PgValue<'a, J: serde::ser::Serialize = ()> { +pub enum PgValue<'a> { /// TINYINT I8(Option), /// SMALLINT @@ -31,7 +31,7 @@ pub enum PgValue<'a, J: serde::ser::Serialize = ()> { #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] /// A JSON value. - Json(Option>), + Json(Option), #[cfg(feature = "uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] /// An UUID value. diff --git a/src/databases/sqlite/value.rs b/src/databases/sqlite/value.rs index 813fbf0..b5b9c33 100644 --- a/src/databases/sqlite/value.rs +++ b/src/databases/sqlite/value.rs @@ -5,7 +5,7 @@ use crate::ast::Value; /// Values that supported by SQLite. #[cfg(feature = "sqlite")] -pub enum SQLiteValue<'a, J: serde::ser::Serialize = ()> { +pub enum SQLiteValue<'a> { /// TINYINT I8(Option), /// SMALLINT @@ -35,7 +35,7 @@ pub enum SQLiteValue<'a, J: serde::ser::Serialize = ()> { #[cfg(feature = "json")] #[cfg_attr(docsrs, doc(cfg(feature = "json")))] /// A JSON value. - Json(Option>), + Json(Option), #[cfg(feature = "uuid")] #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] /// An UUID value. diff --git a/src/lib.rs b/src/lib.rs index c7962ca..dfca90a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,9 +25,6 @@ pub mod prelude { use std::marker::PhantomData; - - - pub use xiayu_derive::*; pub use crate::ast::*; diff --git a/src/macros.rs b/src/macros.rs index 60ba97d..7e86d92 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -29,7 +29,7 @@ macro_rules! values { /// /// ``` rust /// # use entities::{Cat, Dog}; -/// # use xiayu::{col, val, ast::*, visitors::{Visitor, Sqlite}}; +/// # use xiayu::{prelude::*, col, val, visitors::{Visitor, Sqlite}}; /// # fn main() -> Result<(), xiayu::error::Error> { /// let join = Dog::table().on(Dog::slave_id.equals(Cat::master_id)); /// @@ -63,7 +63,7 @@ macro_rules! col { /// /// ``` rust /// # use entities::{Cat, Dog}; -/// # use xiayu::{col, val, ast::*, visitors::{Visitor, Sqlite}}; +/// # use xiayu::{col, val, prelude::*, visitors::{Visitor, Sqlite}}; /// # fn main() -> Result<(), xiayu::error::Error> { /// let join = Dog::table().on(Dog::slave_id.equals(Cat::master_id)); /// diff --git a/src/visitors/mssql.rs b/src/visitors/mssql.rs index 0db5238..27a374c 100644 --- a/src/visitors/mssql.rs +++ b/src/visitors/mssql.rs @@ -699,11 +699,11 @@ mod tests { #[test] fn test_aliased_null() { let expected_sql = "SELECT @P1 AS [test]"; - let query = Select::default().value(val!(Value::Integer(None)).alias("test")); + let query = Select::default().value(val!(Value::I32(None)).alias("test")); let (sql, params) = Mssql::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::Integer(None)], params); + assert_eq!(vec![Value::I32(None)], params); } #[derive(Entity)] @@ -750,10 +750,10 @@ mod tests { assert_eq!(expected_sql, sql); assert_eq!( vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), + Value::int32(1), + Value::int32(2), + Value::int32(3), + Value::int32(4), ], params ); @@ -775,10 +775,10 @@ mod tests { assert_eq!(expected_sql, sql); assert_eq!( vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4), + Value::int32(1), + Value::int32(2), + Value::int32(3), + Value::int32(4), ], params ); @@ -807,7 +807,7 @@ mod tests { let expected_sql = "SELECT [test].* FROM [test] WHERE [test].[id1] IN (@P1,@P2)"; assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::integer(1), Value::integer(2),], params) + assert_eq!(vec![Value::int32(1), Value::int32(2),], params) } #[test] @@ -1014,7 +1014,7 @@ mod tests { let expected_sql = "SELECT [naukio].* FROM [naukio] WHERE ([naukio].[word] = @P1 AND [naukio].[age] < @P2 AND [naukio].[paw] = @P3)"; - let expected_params = vec![Value::text("meow"), Value::integer(10), Value::text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10), Value::text("warm")]; let conditions = Naukio::word .equals("meow") @@ -1031,7 +1031,7 @@ mod tests { fn test_select_and_different_execution_order() { let expected_sql = "SELECT [naukio].* FROM [naukio] WHERE ([naukio].[word] = @P1 AND ([naukio].[age] < @P2 AND [naukio].[paw] = @P3))"; - let expected_params = vec![Value::text("meow"), Value::integer(10), Value::text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10), Value::text("warm")]; let conditions = Naukio::word .equals("meow") @@ -1048,7 +1048,7 @@ mod tests { let expected_sql = "SELECT [naukio].* FROM [naukio] WHERE (([naukio].[word] = @P1 OR [naukio].[age] < @P2) AND [naukio].[paw] = @P3)"; - let expected_params = vec![Value::text("meow"), Value::integer(10), Value::text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10), Value::text("warm")]; let conditions = Naukio::word .equals("meow") @@ -1068,7 +1068,7 @@ mod tests { let expected_sql = "SELECT [naukio].* FROM [naukio] WHERE (NOT (([naukio].[word] = @P1 OR [naukio].[age] < @P2) AND [naukio].[paw] = @P3))"; - let expected_params = vec![Value::text("meow"), Value::integer(10), Value::text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10), Value::text("warm")]; let conditions = Naukio::word .equals("meow") @@ -1089,7 +1089,7 @@ mod tests { let expected_sql = "SELECT [naukio].* FROM [naukio] WHERE (NOT (([naukio].[word] = @P1 OR [naukio].[age] < @P2) AND [naukio].[paw] = @P3))"; - let expected_params = vec![Value::text("meow"), Value::integer(10), Value::text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10), Value::text("warm")]; let conditions = ConditionTree::not( Naukio::word @@ -1202,7 +1202,7 @@ mod tests { let (sql, params) = Mssql::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::integer(0), Value::integer(10)], params); + assert_eq!(vec![Value::int32(0), Value::int64(10)], params); } #[test] @@ -1215,7 +1215,7 @@ mod tests { let (sql, params) = Mssql::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::integer(10)], params); + assert_eq!(vec![Value::int64(10)], params); } #[test] @@ -1230,7 +1230,7 @@ mod tests { let (sql, params) = Mssql::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::integer(10), Value::integer(9)], params); + assert_eq!(vec![Value::int64(10), Value::int64(9)], params); } #[test] @@ -1244,7 +1244,7 @@ mod tests { let (sql, params) = Mssql::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::integer(10), Value::integer(9)], params); + assert_eq!(vec![Value::int64(10), Value::int64(9)], params); } #[test] @@ -1298,7 +1298,7 @@ mod tests { #[test] fn test_raw_char() { let (sql, params) = - Mssql::build(Select::default().value(Value::character('a').raw())).unwrap(); + Mssql::build(Select::default().value(Value::text("a").raw())).unwrap(); assert_eq!("SELECT 'a'", sql); assert!(params.is_empty()); } @@ -1315,8 +1315,9 @@ mod tests { #[test] #[cfg(feature = "uuid")] + #[should_panic] fn test_raw_uuid() { - let uuid = sqlx::types::Uuid::new_v4(); + let uuid = uuid_::Uuid::new_v4(); let (sql, params) = Mssql::build(Select::default().value(uuid.raw())).unwrap(); assert_eq!( @@ -1332,6 +1333,7 @@ mod tests { #[test] #[cfg(feature = "chrono")] + #[should_panic] fn test_raw_datetime() { let dt = sqlx::types::chrono::Utc::now(); let (sql, params) = Mssql::build(Select::default().value(dt.raw())).unwrap(); @@ -1737,7 +1739,7 @@ mod tests { } #[test] fn test_from() { - let expected_sql = "SELECT [foo].*, [bar].[a] FROM [foo], (SELECT [a] FROM [baz]) AS [bar]"; + let expected_sql = "SELECT [foo].*, [bar].[a] FROM [foo], (SELECT [baz].[a] FROM [baz]) AS [bar]"; let query = Select::default() .and_from(Foo::table()) .and_from(Table::from(Select::from_table(Baz::table()).column(Baz::a)).alias("bar")) @@ -1775,7 +1777,7 @@ mod tests { let (sql, params) = Mssql::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::integer(1), Value::integer(2)], params); + assert_eq!(vec![Value::int32(1), Value::int32(2)], params); } #[test] @@ -1796,7 +1798,7 @@ mod tests { let (sql, params) = Mssql::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::integer(1), Value::integer(2)], params); + assert_eq!(vec![Value::int32(1), Value::int32(2)], params); } #[test] @@ -1826,8 +1828,8 @@ mod tests { assert_eq!( vec![ - Value::integer(1), - Value::integer(2), + Value::int32(1), + Value::int32(2), Value::text("bar"), Value::text("foo") ], @@ -1861,8 +1863,8 @@ mod tests { assert_eq!( vec![ - Value::integer(1), - Value::integer(2), + Value::int32(1), + Value::int32(2), Value::text("bar"), Value::text("foo") ], @@ -1901,10 +1903,10 @@ mod tests { assert_eq!( vec![ - Value::integer(1), - Value::integer(2), - Value::integer(3), - Value::integer(4) + Value::int32(1), + Value::int32(2), + Value::int32(3), + Value::int32(4) ], params ); diff --git a/src/visitors/mysql.rs b/src/visitors/mysql.rs index 3bebd43..ba43dd5 100644 --- a/src/visitors/mysql.rs +++ b/src/visitors/mysql.rs @@ -688,6 +688,8 @@ mod tests { id1: i32, id2: i32, bar: String, + #[cfg(feature = "json")] + json: serde_json::Value, } #[test] @@ -705,7 +707,7 @@ mod tests { assert_eq!(expected_sql, sql); assert_eq!( - vec![Value::I32(1), Value::I32(2), Value::I32(3), Value::I32(4),], + vec![Value::int32(1), Value::int32(2), Value::int32(3), Value::int32(4),], params ); } @@ -857,7 +859,7 @@ mod tests { #[test] #[cfg(feature = "uuid")] fn test_raw_uuid() { - let uuid = sqlx::types::Uuid::new_v4(); + let uuid = uuid_::Uuid::new_v4(); let (sql, params) = Mysql::build(Select::default().value(uuid.raw())).unwrap(); assert_eq!( @@ -916,8 +918,8 @@ mod tests { #[cfg(feature = "json")] fn test_json_negation() { let conditions = - ConditionTree::not("json".equals(Value::Json(Some(serde_json::Value::Null)))); - let (sql, _) = Mysql::build(Select::from_table("test").so_that(conditions)).unwrap(); + ConditionTree::not(TestEntity::json.equals(Value::Json(Some(serde_json::Value::Null)))); + let (sql, _) = Mysql::build(Select::from_table(TestEntity::table()).so_that(conditions)).unwrap(); assert_eq!( "SELECT `test`.* FROM `test` WHERE (NOT (JSON_CONTAINS(`json`, ?) AND JSON_CONTAINS(?, `json`)))", @@ -929,8 +931,8 @@ mod tests { #[cfg(feature = "json")] fn test_json_not_negation() { let conditions = - ConditionTree::not("json".not_equals(Value::Json(Some(serde_json::Value::Null)))); - let (sql, _) = Mysql::build(Select::from_table("test").so_that(conditions)).unwrap(); + ConditionTree::not(TestEntity::json.not_equals(Value::Json(Some(serde_json::Value::Null)))); + let (sql, _) = Mysql::build(Select::from_table(TestEntity::table()).so_that(conditions)).unwrap(); assert_eq!( "SELECT `test`.* FROM `test` WHERE (NOT (NOT JSON_CONTAINS(`json`, ?) OR NOT JSON_CONTAINS(?, `json`)))", diff --git a/src/visitors/postgres.rs b/src/visitors/postgres.rs index e4a507c..bc4ca5f 100644 --- a/src/visitors/postgres.rs +++ b/src/visitors/postgres.rs @@ -687,7 +687,7 @@ mod tests { vec![serde_json::json!({"a": "b"})], ); - let value_expr: Expression = Value::Json(serde_json::json!({"a":"b"})).into(); + let value_expr: Expression = Value::json(serde_json::json!({"a":"b"})).into(); let query = Select::from_table(User::table()).so_that(value_expr.equals(User::json)); let (sql, params) = Postgres::build(query).unwrap(); @@ -719,7 +719,7 @@ mod tests { vec![serde_json::json!({"a": "b"})], ); - let value_expr: Expression = Value::Json(serde_json::json!({"a":"b"})).into(); + let value_expr: Expression = Value::json(serde_json::json!({"a":"b"})).into(); let query = Select::from_table(User::table()).so_that(value_expr.not_equals(User::json)); let (sql, params) = Postgres::build(query).unwrap(); @@ -821,7 +821,7 @@ mod tests { #[test] fn test_raw_bytes() { let (sql, params) = - Postgres::build(Select::default().value(Value::Bytes(vec![1, 2, 3]).raw())).unwrap(); + Postgres::build(Select::default().value(Value::bytes(vec![1, 2, 3]).raw())).unwrap(); assert_eq!("SELECT E'010203'", sql); assert!(params.is_empty()); } diff --git a/src/visitors/sqlite.rs b/src/visitors/sqlite.rs index 70deb85..b93eb4c 100644 --- a/src/visitors/sqlite.rs +++ b/src/visitors/sqlite.rs @@ -409,7 +409,7 @@ mod tests { assert_eq!(expected_sql, sql); assert_eq!( - vec![Value::I32(1), Value::I32(2), Value::I32(3), Value::I32(4),], + vec![Value::int32(1), Value::int32(2), Value::int32(3), Value::int32(4),], params ); } @@ -429,7 +429,7 @@ mod tests { assert_eq!(expected_sql, sql); assert_eq!( - vec![Value::I32(1), Value::I32(2), Value::I32(3), Value::I32(4),], + vec![Value::int32(1), Value::int32(2), Value::int32(3), Value::int32(4),], params ); } @@ -457,7 +457,7 @@ mod tests { let expected_sql = "SELECT `test`.* FROM `test` WHERE `test`.`id1` IN (?,?)"; assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::I32(1), Value::I32(2),], params) + assert_eq!(vec![Value::int32(1), Value::int32(2),], params) } #[test] @@ -597,7 +597,7 @@ mod tests { let expected_sql = "SELECT `naukio`.* FROM `naukio` WHERE (`naukio`.`word` = ? AND `naukio`.`age` < ? AND `naukio`.`paw` = ?)"; - let expected_params = vec![Value::Text("meow"), Value::I32(10), Value::Text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10.into()), Value::text("warm")]; let conditions = Naukio::word .equals("meow") @@ -617,7 +617,7 @@ mod tests { let expected_sql = "SELECT `naukio`.* FROM `naukio` WHERE (`naukio`.`word` = ? AND (`naukio`.`age` < ? AND `naukio`.`paw` = ?))"; - let expected_params = vec![Value::Text("meow"), Value::I32(10), Value::Text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10.into()), Value::text("warm")]; let conditions = Naukio::word .equals("meow") @@ -636,7 +636,7 @@ mod tests { let expected_sql = "SELECT `naukio`.* FROM `naukio` WHERE ((`naukio`.`word` = ? OR `naukio`.`age` < ?) AND `naukio`.`paw` = ?)"; - let expected_params = vec![Value::Text("meow"), Value::I32(10), Value::Text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10), Value::text("warm")]; let conditions = Naukio::word .equals("meow") @@ -656,7 +656,7 @@ mod tests { let expected_sql = "SELECT `naukio`.* FROM `naukio` WHERE (NOT ((`naukio`.`word` = ? OR `naukio`.`age` < ?) AND `naukio`.`paw` = ?))"; - let expected_params = vec![Value::Text("meow"), Value::I32(10), Value::Text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10), Value::text("warm")]; let conditions = Naukio::word .equals("meow") @@ -677,7 +677,7 @@ mod tests { let expected_sql = "SELECT `naukio`.* FROM `naukio` WHERE (NOT ((`naukio`.`word` = ? OR `naukio`.`age` < ?) AND `naukio`.`paw` = ?))"; - let expected_params = vec![Value::Text("meow"), Value::I32(10), Value::Text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10), Value::text("warm")]; let conditions = ConditionTree::not( Naukio::word @@ -730,7 +730,7 @@ mod tests { let (sql, params) = Sqlite::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(default_params(vec![Value::Boolean(true),]), params); + assert_eq!(default_params(vec![Value::boolean(true),]), params); } #[test] @@ -759,7 +759,7 @@ mod tests { let (sql, params) = Sqlite::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(default_params(vec![Value::Boolean(true),]), params); + assert_eq!(default_params(vec![Value::boolean(true),]), params); } #[derive(Entity)] @@ -863,7 +863,7 @@ mod tests { #[test] fn test_raw_bytes() { let (sql, params) = - Sqlite::build(Select::default().value(Value::Bytes(vec![1, 2, 3]).raw())).unwrap(); + Sqlite::build(Select::default().value(Value::bytes(vec![1, 2, 3]).raw())).unwrap(); assert_eq!("SELECT x'010203'", sql); assert!(params.is_empty()); } @@ -902,7 +902,7 @@ mod tests { #[test] #[cfg(feature = "uuid")] fn test_raw_uuid() { - let uuid = sqlx::types::Uuid::new_v4(); + let uuid = uuid_::Uuid::new_v4(); let (sql, params) = Sqlite::build(Select::default().value(uuid.raw())).unwrap(); assert_eq!(