diff --git a/.vim/coc-settings.json b/.vim/coc-settings.json index 2fabdbe..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,48 +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": [ - "doc", - "mssql", - "mysql", - "sqlite", - "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 0afaa34..a95fa73 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" @@ -19,46 +23,39 @@ 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" } +uuid_ = { version = "0.8", package = "uuid", features = [ "v4" ] } [features] -default = [ "sqlite", "mysql" ] -docs = [ "sqlx/runtime-tokio-rustls" ] +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" ] 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" ] - -all = [ - "mssql", - "mysql", - "sqlite", - "postgres", - "json", - "uuid", - "chrono", - "bigdecimal", -] +time = [ "sqlx/time" ] +ipnetwork = [ "sqlx/ipnetwork" ] [workspace] members = [ diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..1f8bb47 --- /dev/null +++ b/build.rs @@ -0,0 +1,3 @@ +fn main() { + println!("cargo:rustc-cfg=docsrs"); +} 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/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/expression.rs b/src/ast/expression.rs index 025baa0..95a6e8a 100644 --- a/src/ast/expression.rs +++ b/src/ast/expression.rs @@ -56,10 +56,11 @@ impl<'a> Expression<'a> { #[allow(dead_code)] #[cfg(feature = "json")] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] pub(crate) fn into_json_value(self) -> Option { match self.kind { #[cfg(feature = "json")] - ExpressionKind::Parameterized(Value::Json(Json::JsonValue(json_val))) => json_val, + ExpressionKind::Parameterized(Value::Json(json_val)) => serde_json::to_value(json_val).ok(), #[cfg(feature = "json")] ExpressionKind::Value(expr) => expr.into_json_value(), _ => None, @@ -78,10 +79,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 +215,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 +223,7 @@ impl<'a> ExpressionKind<'a> { _ => false, } } + */ } /// A quick alias to create an asterisk to a table. 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/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/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 db062ff..c354d00 100644 --- a/src/ast/values.rs +++ b/src/ast/values.rs @@ -1,12 +1,7 @@ -use std::any::Any; -use std::borrow::{Borrow, Cow}; + +use std::borrow::Cow; use std::convert::TryFrom; -use std::fmt; -#[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 +10,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)] @@ -55,189 +50,131 @@ 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>>>), - JsonValue(Option), - JsonRawValue(Option>), -} - -#[cfg(feature = "json")] -impl<'a> From for Json<'a> { - fn from(v: JsonValue) -> Self { - Json::JsonValue(Some(v)) - } -} - -#[cfg(feature = "json")] -impl<'a> From> for Json<'a> { - fn from(v: Option) -> Self { - Json::JsonValue(v) - } -} - -#[cfg(feature = "json")] -impl<'a> From>> for Json<'a> { - fn from(v: Option>) -> Self { - Json::JsonRawValue(v) - } -} - -#[cfg(feature = "json")] -impl<'a> From> for Json<'a> { - fn from(v: JsonRawValue<'a>) -> Self { - Json::JsonRawValue(Some(v)) - } -} - -#[cfg(feature = "json")] -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")] -impl<'a> Json<'a> { - pub const fn is_none(&self) -> bool { - match self { - Json::JsonValue(v) => v.is_none(), - Json::JsonRawValue(v) => v.is_none(), - } - } -} - -#[derive(Debug, Clone, AsRef, Deref)] -pub struct JsonRawValue<'a>(&'a serde_json::value::RawValue); - -impl<'a> PartialEq for JsonRawValue<'a> { - fn eq(&self, other: &Self) -> bool { - self.0.get() == other.0.get() - } -} - /// 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), - + /// 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(any(docsrs, feature = "mysql", feature = "sqlite"))] + #[cfg_attr(docsrs, doc(cfg(all(any(feature = "mysql", feature = "sqlite"),))))] + /// TINYINT UNSIGNED + U8(Option), + #[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. Enum(Option>), - /// A single character. - Char(Option), - #[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] - /// 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 = "postgres")] + // #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] + // /// An array value (PostgreSQL). + // Array(Option>), #[cfg(feature = "json")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "json")))] + #[cfg_attr(docsrs, doc(cfg(feature = "json")))] /// A JSON value. - Json(Json<'a>), + Json(Option), #[cfg(feature = "uuid")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] + #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] /// An UUID value. Uuid(Option), #[cfg(feature = "postgres")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] + #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] /// INTERVAL PgInterval(Option), /* #[cfg(feature = "postgres")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] + #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] /// INT8RANGE, INT4RANGE, TSRANGE, TSTZTRANGE, DATERANGE, NUMRANGE PgRange(Option>>>), */ + /// MONEY #[cfg(feature = "postgres")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "postgres")))] - /// INT8RANGE, INT4RANGE, TSRANGE, TSTZTRANGE, DATERANGE, NUMRANGE + #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] PgMoney(Option), /// A numeric value. #[cfg(feature = "bigdecimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] + #[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))] BigDecimal(Option), /// A numeric value. #[cfg(feature = "decimal")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "decimal")))] + #[cfg_attr(docsrs, doc(cfg(feature = "decimal")))] Decimal(Option), /// TIMESTAMPTZ #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] UtcDateTime(Option>), /// TIMESTAMPTZ #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] LocalDateTime(Option>), /// TIMESTAMP #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] NaiveDateTime(Option), /// DATE #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] NaiveDate(Option), /// TIME #[cfg(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] + #[cfg_attr(docsrs, doc(cfg(feature = "chrono")))] NaiveTime(Option), /// TIMETZ #[cfg(all(feature = "time", feature = "postgres"))] - #[cfg_attr( - feature = "docs", - doc(cfg(all(feature = "time", feature = "postgres"))) - )] + #[cfg_attr(docsrs, doc(cfg(all(feature = "time", feature = "postgres"))))] PgTimeTz(Option), - #[cfg(feature = "ipnetwork")] - #[cfg_attr(feature = "docs", doc(cfg(feature = "ipnetwork")))] /// INET, CIDR - IpNetwork(Option), + #[cfg(feature = "ipnetwork")] + #[cfg_attr(docsrs, doc(cfg(feature = "ipnetwork")))] + IpNetwork(Option), } -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(); @@ -302,77 +239,17 @@ impl<'a> fmt::Display for Value<'a> { } } } - -#[cfg(feature = "json")] -#[cfg_attr(feature = "docs", 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!(), - }; - - match res { - Some(val) => val, - None => serde_json::Value::Null, - } - } -} +*/ 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 int32 value. + pub const fn int32(value: i32) -> Self { + Self::I32(Some(value)) } - /// 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 int64 value. + pub const fn int64(value: i64) -> Self { + Self::I64(Some(value)) } /// Creates a new float value. @@ -393,15 +270,12 @@ 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(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>, @@ -417,14 +291,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,42 +300,44 @@ 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_attr(docsrs, doc(cfg(feature = "uuid")))] pub const fn uuid(value: Uuid) -> Self { 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")] - #[cfg_attr(feature = "docs", doc(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(feature = "chrono")] - #[cfg_attr(feature = "docs", doc(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")))] - pub const fn json(value: Json<'a>) -> Self { - Value::Json(value) - } - /// Creates a new XML value. pub fn xml(value: T) -> Self where @@ -574,11 +443,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 +472,252 @@ 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(docsrs, 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(docsrs, 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(docsrs, 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(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")))] - 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(docsrs, 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(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")))] - 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(docsrs, 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(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")))] - 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(docsrs, 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(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")))] - 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(docsrs, 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(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> { - 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(docsrs, doc(cfg(feature = "json")))] + pub const fn as_json(&self) -> Option<&Json> { + match self { + 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 { - 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(docsrs, doc(cfg(feature = "json")))] + pub fn into_json(self) -> Option { + match self { + Value::Json(j) => *j, + _ => 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(any(features = "mysql", feature = "sqlite"))] +value!(val: u8, U8, val); +#[cfg(any(features = "mysql", feature = "sqlite"))] +value!(val: u16, U16, val); +#[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] +value!(val: u32, U32, val); +#[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, 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(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(feature = "docs", doc(cfg(feature = "chrono")))] -value!(val: DateTime, DateTime, val); +value!(val: chrono::NaiveTime, NaiveTime, val); +#[cfg(feature = "chrono")] +value!(val: chrono::NaiveDate, NaiveDate, val); #[cfg(feature = "chrono")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] -value!(val: chrono::NaiveTime, Time, val); +value!(val: chrono::NaiveDateTime, NaiveDateTime, val); #[cfg(feature = "chrono")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "chrono")))] -value!(val: chrono::NaiveDate, Date, val); +value!(val: chrono::DateTime, UtcDateTime, val); +#[cfg(feature = "chrono")] +value!(val: chrono::DateTime, LocalDateTime, 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); +value!(val: BigDecimal, BigDecimal, val); #[cfg(feature = "uuid")] -#[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] value!(val: Uuid, Uuid, val); +/* +#[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))) + } +} +*/ + +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, J> From> for Value<'a> +where + J: serde::ser::Serialize, +{ + fn from(val: sqlx::types::Json) -> Self { + Value::Json(serde_json::to_value(val).ok()) + } +} + +/* impl<'a> TryFrom> for i64 { type Error = Error; @@ -853,7 +770,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; @@ -863,6 +780,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. @@ -954,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]); @@ -998,4 +918,5 @@ mod tests { let rslt: Option> = pv.into_vec(); assert!(rslt.is_none()); } + */ } 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 0054dc1..2e8172b 100644 --- a/src/databases/mod.rs +++ b/src/databases/mod.rs @@ -1,172 +1,33 @@ -use std::default; -use std::marker::{PhantomData}; -#[cfg(feature = "chrono")] -use sqlx::types::chrono; -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; - -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() - } -} +use async_trait::async_trait; -// pub struct Values<'a>(Vec>); - -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::Text(text) => $query.bind(text.map(|text|text.into_owned())), - 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"))] - Value::Uuid(uuid) => $query.bind(uuid), - #[cfg(feature = "postgres")] - Value::PgInterval(interval) => $query.bind(interval), - // #[cfg(feature = "postgres")] - // Value::PgRange(range) => $query.bind(range), - #[cfg(feature = "postgres")] - Value::PgMoney(money) => $query.bind(money), - #[cfg(all(feature = "bigdecimal", feature = "postgres"))] - Value::BigDecimal(bigdecimal) => $query.bind(bigdecimal), - #[cfg(all(feature = "decimal", feature = "postgres"))] - Value::Decimal(decimal) => $query.bind(decimal), - #[cfg(all(feature = "chrono", feature = "postgres"))] - Value::UtcDateTime(datetime) => $query.bind(datetime), - #[cfg(all(feature = "chrono", feature = "postgres"))] - Value::LocalDateTime(datetime) => $query.bind(datetime), - #[cfg(all(feature = "chrono", feature = "postgres"))] - Value::NaiveDateTime(datetime) => $query.bind(datetime), - #[cfg(all(feature = "chrono", feature = "postgres"))] - Value::NaiveDate(date) => $query.bind(date), - #[cfg(all(feature = "chrono", feature = "postgres"))] - Value::NaiveTime(time) => $query.bind(time), - #[cfg(all(feature = "time", feature = "postgres"))] - Value::PgTimeTz(timetz) => $query.bind(timetz), - #[cfg(all(feature = "ipnetwork", feature = "postgres"))] - Value::IpNetwork(ipnetwork) => $query.bind(ipnetwork), - - _ => unimplemented!() - } - }; -} +use sqlx::{Database, Executor, FromRow, IntoArguments}; -pub trait Binder<'a, DB> where DB: sqlx::Database { - fn bind_value(self, value: Value<'a>) -> Self - 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) - } -} +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 bind_value(self, value: Value<'a>) -> Self { - bind_value!(self, value) - } -} - -#[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) - } -} - +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 bind_value(self, value: Value<'a>) -> Self { - bind_value!(self, value) - } -} - +pub mod mysql; #[cfg(feature = "mssql")] -impl<'a> Binder<'a, sqlx::Mssql> for sqlx::query::Query<'a, sqlx::Mssql, O, sqlx::mssql::MssqlArguments> { - fn bind_value(self, value: Value<'a>) -> Self { - bind_value!(self, value) - } -} - -#[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) - } -} +pub mod 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) - } -} +mod try_bind; +mod has_visitor; +mod has_value; -#[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) - } -} +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"] @@ -183,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.bind_value(parameter); + query = query.try_bind(parameter)?; } let v = query.fetch_one(conn).await?; @@ -202,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 { @@ -223,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, @@ -239,21 +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 = bind_value!(query, parameter); - query = query.bind_value(parameter); + query = query.try_bind(parameter)?; } - let _query_result = query - .execute(conn) - .await?; + let _query_result = query.execute(conn).await?; Ok(()) } } @@ -268,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, @@ -285,32 +147,33 @@ 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.bind_value(parameter); + query = query.try_bind(parameter)?; } - let _query_result = query - .execute(conn) - .await?; + let _query_result = query.execute(conn).await?; Ok(()) } } -/// 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 { @@ -357,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 @@ -369,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 { @@ -409,14 +286,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(parameter)?; } - // println!("query ---> {:?}", query); let _query_result = self.execute(query).await?; Ok(()) } @@ -431,9 +305,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(parameter)?; } - // println!("query ---> {:?}", query); let query_result = self.execute(query).await?; Ok(query_result) } @@ -469,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<()> { @@ -479,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..ea2bb80 --- /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> { + /// 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..748a19b --- /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> { + /// 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..b5b9c33 --- /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> { + /// 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/error.rs b/src/error.rs index 7e72081..5135984 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), @@ -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 5a117a3..dfca90a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,7 @@ -#![feature(const_panic)] -#![cfg_attr(feature = "docs", feature(doc_cfg))] +#![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(not(any( + docsrs, feature = "sqlite", feature = "postgres", feature = "mysql", @@ -22,12 +22,9 @@ 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::*; pub use crate::ast::*; diff --git a/src/macros.rs b/src/macros.rs index d63c10c..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)); /// @@ -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> { @@ -182,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/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 46679f7..27a374c 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, }; @@ -17,7 +16,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>, @@ -242,7 +241,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 +254,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 +277,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 +290,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 +303,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 +320,12 @@ 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()); + v @ _ => { + // FIXME: Maybe define MsValue at here? + crate::databases::mssql::MsValue::try_from(v)?; + None } - #[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 { @@ -762,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)] @@ -813,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 ); @@ -838,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 ); @@ -870,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] @@ -1007,11 +944,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,13 +1007,14 @@ mod tests { assert_eq!(expected.0, sql); assert_eq!(expected.1, params); } + */ #[test] fn test_select_and() { 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") @@ -1094,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") @@ -1111,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") @@ -1131,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") @@ -1152,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 @@ -1265,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] @@ -1278,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] @@ -1293,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] @@ -1307,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] @@ -1361,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()); } @@ -1378,8 +1315,9 @@ mod tests { #[test] #[cfg(feature = "uuid")] + #[should_panic] fn test_raw_uuid() { - let uuid = uuid::Uuid::new_v4(); + let uuid = uuid_::Uuid::new_v4(); let (sql, params) = Mssql::build(Select::default().value(uuid.raw())).unwrap(); assert_eq!( @@ -1395,8 +1333,9 @@ mod tests { #[test] #[cfg(feature = "chrono")] + #[should_panic] 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!( @@ -1800,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")) @@ -1838,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] @@ -1859,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] @@ -1889,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") ], @@ -1924,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") ], @@ -1964,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 37a2df8..ba43dd5 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::*, @@ -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>, @@ -52,15 +52,15 @@ impl<'a> Mysql<'a> { 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(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); @@ -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(any(feature = "mysql", feature = "sqlite"))] + Value::U8(i) => i.map(|i| self.write(i)), + #[cfg(any(feature = "mysql", feature = "sqlite"))] + Value::U16(i) => i.map(|i| self.write(i)), + #[cfg(any(feature = "mysql", feature = "sqlite"))] + Value::U32(i) => i.map(|i| self.write(i)), + #[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'"), f if f == f32::INFINITY => self.write("'Infinity'"), @@ -149,31 +160,19 @@ 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(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::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)), + Value::BigDecimal(r) => r.map(|r| self.write(r)), + #[cfg(feature = "decimal")] + Value::Decimal(r) => r.map(|r| self.write(r)), #[cfg(feature = "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")] @@ -181,13 +180,21 @@ impl<'a> Visitor<'a> for Mysql<'a> { 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(),))), + Value::UtcDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), + #[cfg(feature = "chrono")] + Value::LocalDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), + #[cfg(feature = "chrono")] + Value::NaiveDateTime(datetime) => { + datetime.map(|datetime| self.write(format!("'{}'", datetime))) + } #[cfg(feature = "chrono")] - Value::Date(date) => date.map(|date| self.write(format!("'{}'", date))), + Value::NaiveDate(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!(), + Value::NaiveTime(time) => time.map(|time| self.write(format!("'{}'", time))), + v @ _ => { + crate::databases::mysql::MyValue::try_from(v)?; + None + } }; match res { @@ -285,10 +292,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) @@ -310,20 +317,22 @@ impl<'a> Visitor<'a> for Mysql<'a> { #[cfg(feature = "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,20 +353,22 @@ impl<'a> Visitor<'a> for Mysql<'a> { #[cfg(feature = "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) @@ -677,6 +688,8 @@ mod tests { id1: i32, id2: i32, bar: String, + #[cfg(feature = "json")] + json: serde_json::Value, } #[test] @@ -686,19 +699,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::int32(1), Value::int32(2), Value::int32(3), Value::int32(4),], params ); } @@ -707,7 +716,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"})], ); @@ -723,7 +732,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"})], ); @@ -763,6 +772,7 @@ mod tests { assert!(params.is_empty()); } + #[cfg(any(feature = "mysql", feature = "sqlite", feature = "postgres"))] #[test] fn test_raw_bytes() { let (sql, params) = @@ -782,14 +792,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`"; @@ -857,7 +859,7 @@ mod tests { #[test] #[cfg(feature = "uuid")] fn test_raw_uuid() { - let uuid = uuid::Uuid::new_v4(); + let uuid = uuid_::Uuid::new_v4(); let (sql, params) = Mysql::build(Select::default().value(uuid.raw())).unwrap(); assert_eq!( @@ -871,7 +873,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); @@ -911,4 +913,30 @@ mod tests { sql ); } + + #[test] + #[cfg(feature = "json")] + fn test_json_negation() { + let conditions = + 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`)))", + sql + ); + } + + #[test] + #[cfg(feature = "json")] + fn test_json_not_negation() { + let conditions = + 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`)))", + sql + ); + } } diff --git a/src/visitors/postgres.rs b/src/visitors/postgres.rs index 910a5d2..bc4ca5f 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::*; @@ -7,7 +8,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>, @@ -83,13 +84,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(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))), - Value::Enum(e) => e.map(|e| self.write(e)), + #[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::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 +106,58 @@ 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 = "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(feature = "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()))) - } - }, + 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)), + Value::BigDecimal(r) => r.map(|r| self.write(r)), + #[cfg(feature = "decimal")] + Value::Decimal(r) => r.map(|r| self.write(r)), #[cfg(feature = "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(),))), + Value::UtcDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), + #[cfg(feature = "chrono")] + Value::LocalDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), #[cfg(feature = "chrono")] - Value::Date(date) => date.map(|date| self.write(format!("'{}'", date))), + Value::NaiveDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt))), #[cfg(feature = "chrono")] - Value::Time(time) => time.map(|time| self.write(format!("'{}'", time))), - _ => unimplemented!(), + Value::NaiveDate(date) => date.map(|date| self.write(format!("'{}'", date))), + #[cfg(feature = "postgres")] + Value::PgMoney(money) => money.map(|money| self.write(money.to_bigdecimal(2))), + #[cfg(feature = "postgres")] + Value::PgInterval(interval) => { + interval.map(|interval| self.write(format!("{:?}", interval))) + } + #[cfg(all(feature = "time", feature = "postgres"))] + 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 { @@ -240,14 +259,12 @@ impl<'a> Visitor<'a> for Postgres<'a> { let right_cast = match left { #[cfg(feature = "json")] _ if left.is_json_value() => "::jsonb", - _ if left.is_xml_value() => "::text", _ => "", }; let left_cast = match right { #[cfg(feature = "json")] _ if right.is_json_value() => "::jsonb", - _ if right.is_xml_value() => "::text", _ => "", }; @@ -269,14 +286,12 @@ impl<'a> Visitor<'a> for Postgres<'a> { let right_cast = match left { #[cfg(feature = "json")] _ if left.is_json_value() => "::jsonb", - _ if left.is_xml_value() => "::text", _ => "", }; let left_cast = match right { #[cfg(feature = "json")] _ if right.is_json_value() => "::jsonb", - _ if right.is_xml_value() => "::text", _ => "", }; @@ -459,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 { @@ -710,6 +727,7 @@ mod tests { assert_eq!(expected.1, params); } + /* #[test] fn equality_with_a_xml_value() { let expected = expected_values( @@ -769,6 +787,7 @@ mod tests { assert_eq!(expected.0, sql); assert_eq!(expected.1, params); } + */ #[test] fn test_raw_null() { @@ -818,6 +837,7 @@ mod tests { assert!(params.is_empty()); } + /* #[test] fn test_raw_char() { let (sql, params) = @@ -825,6 +845,7 @@ mod tests { assert_eq!("SELECT 'a'", sql); assert!(params.is_empty()); } + */ #[test] #[cfg(feature = "json")] @@ -839,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 2647ef5..b93eb4c 100644 --- a/src/visitors/sqlite.rs +++ b/src/visitors/sqlite.rs @@ -1,16 +1,15 @@ 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. /// /// 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>, @@ -52,12 +51,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(any(feature = "mysql", feature = "sqlite"))] + Value::U8(i) => i.map(|i| self.write(i)), + #[cfg(any(feature = "mysql", feature = "sqlite"))] + Value::U16(i) => i.map(|i| self.write(i)), + #[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))), - Value::Enum(e) => e.map(|e| self.write(e)), + #[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::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 +77,33 @@ 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")] 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")] 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(),))), + Value::UtcDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), #[cfg(feature = "chrono")] - Value::Date(date) => date.map(|date| self.write(format!("'{}'", date))), + Value::LocalDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339()))), #[cfg(feature = "chrono")] - Value::Time(time) => time.map(|time| self.write(format!("'{}'", time))), - Value::Xml(cow) => cow.map(|cow| self.write(format!("'{}'", cow))), - _ => todo!(), + Value::NaiveDateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt))), + #[cfg(feature = "chrono")] + 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 { @@ -404,18 +403,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::int32(1), Value::int32(2), Value::int32(3), Value::int32(4),], params ); } @@ -427,19 +421,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::int32(1), Value::int32(2), Value::int32(3), Value::int32(4),], params ); } @@ -453,10 +443,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 +457,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::int32(1), Value::int32(2),], params) } #[test] @@ -607,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::integer(10), Value::text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10.into()), Value::text("warm")]; let conditions = Naukio::word .equals("meow") @@ -627,11 +617,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::int32(10.into()), 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 +636,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::int32(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 +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::integer(10), Value::text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10), Value::text("warm")]; let conditions = Naukio::word .equals("meow") @@ -687,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::integer(10), Value::text("warm")]; + let expected_params = vec![Value::text("meow"), Value::int32(10), Value::text("warm")]; let conditions = ConditionTree::not( Naukio::word @@ -889,6 +879,7 @@ mod tests { assert!(params.is_empty()); } + /* #[test] fn test_raw_char() { let (sql, params) = @@ -896,6 +887,7 @@ mod tests { assert_eq!("SELECT 'a'", sql); assert!(params.is_empty()); } + */ #[test] #[cfg(feature = "json")] @@ -910,7 +902,7 @@ mod tests { #[test] #[cfg(feature = "uuid")] fn test_raw_uuid() { - let uuid = uuid::Uuid::new_v4(); + let uuid = uuid_::Uuid::new_v4(); let (sql, params) = Sqlite::build(Select::default().value(uuid.raw())).unwrap(); assert_eq!( @@ -924,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);