From 6e26301fe272ba4ba0598fe43eb5d8df030be4db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20Fern=C3=A1ndez?= Date: Thu, 5 Oct 2023 16:54:17 +0200 Subject: [PATCH] feat(qe): Allow quaint::Value to include metainformation about the corresponding native database type (#4311) --- .../query-engine-driver-adapters.yml | 8 - .../src/test_each_connector.rs | 140 +++ quaint/src/ast.rs | 2 +- quaint/src/ast/column.rs | 2 +- quaint/src/ast/enums.rs | 7 +- quaint/src/ast/expression.rs | 20 +- quaint/src/ast/function/row_to_json.rs | 4 +- quaint/src/ast/values.rs | 881 ++++++++++++++---- quaint/src/connector/metrics.rs | 2 +- quaint/src/connector/mssql/conversion.rs | 76 +- quaint/src/connector/mysql.rs | 2 +- quaint/src/connector/mysql/conversion.rs | 66 +- quaint/src/connector/postgres.rs | 14 +- quaint/src/connector/postgres/conversion.rs | 398 ++++---- quaint/src/connector/postgres/error.rs | 6 +- quaint/src/connector/sqlite/conversion.rs | 60 +- quaint/src/lib.rs | 2 +- quaint/src/macros.rs | 22 +- quaint/src/serde.rs | 86 +- quaint/src/tests/query.rs | 189 ++-- quaint/src/tests/query/error.rs | 5 +- quaint/src/tests/types/mssql.rs | 50 +- quaint/src/tests/types/mssql/bigdecimal.rs | 12 +- quaint/src/tests/types/mysql.rs | 73 +- quaint/src/tests/types/postgres.rs | 166 ++-- quaint/src/tests/types/postgres/bigdecimal.rs | 12 +- quaint/src/tests/types/sqlite.rs | 24 +- quaint/src/visitor.rs | 13 +- quaint/src/visitor/mssql.rs | 46 +- quaint/src/visitor/mysql.rs | 76 +- quaint/src/visitor/postgres.rs | 63 +- quaint/src/visitor/sqlite.rs | 42 +- .../query-tests-setup/src/config.rs | 6 +- .../src/query_builder/group_by_builder.rs | 2 +- .../src/cursor_condition.rs | 4 +- .../sql-query-connector/src/filter/visitor.rs | 6 +- .../src/model_extensions/scalar_field.rs | 42 +- .../sql-query-connector/src/ordering.rs | 2 +- .../connectors/sql-query-connector/src/row.rs | 110 +-- .../sql-query-connector/src/value.rs | 52 +- .../sql-query-connector/src/value_ext.rs | 44 +- query-engine/dmmf/src/tests/tests.rs | 5 +- .../driver-adapters/src/conversion.rs | 13 +- query-engine/driver-adapters/src/proxy.rs | 138 +-- query-engine/driver-adapters/src/queryable.rs | 2 +- .../query-engine-node-api/src/logger.rs | 2 +- .../src/assertions/quaint_result_set_ext.rs | 12 +- .../tests/existing_data/mod.rs | 9 +- .../made_optional_field_required.rs | 6 +- .../sqlite_existing_data_tests.rs | 4 +- .../existing_data/type_migration_tests.rs | 4 +- .../tests/migrations/sql.rs | 12 +- .../tests/native_types/mssql.rs | 20 +- .../tests/native_types/mysql.rs | 96 +- .../tests/native_types/postgres.rs | 26 +- .../sql-schema-describer/src/postgres.rs | 79 +- .../sql-schema-describer/src/sqlite.rs | 7 +- 57 files changed, 1943 insertions(+), 1329 deletions(-) create mode 100644 quaint/quaint-test-macros/src/test_each_connector.rs diff --git a/.github/workflows/query-engine-driver-adapters.yml b/.github/workflows/query-engine-driver-adapters.yml index 8ab2d932e07f..f4207e2a6d51 100644 --- a/.github/workflows/query-engine-driver-adapters.yml +++ b/.github/workflows/query-engine-driver-adapters.yml @@ -61,14 +61,6 @@ jobs: run: | echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - uses: actions/cache@v3 - name: "Setup pnpm cache" - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- - - name: "Login to Docker Hub" uses: docker/login-action@v2 continue-on-error: true diff --git a/quaint/quaint-test-macros/src/test_each_connector.rs b/quaint/quaint-test-macros/src/test_each_connector.rs new file mode 100644 index 000000000000..c01aa695d1ad --- /dev/null +++ b/quaint/quaint-test-macros/src/test_each_connector.rs @@ -0,0 +1,140 @@ +use darling::FromMeta; +use once_cell::sync::Lazy; +use proc_macro::TokenStream; +use proc_macro2::Span; +use quaint_test_setup::{ConnectorDefinition, Tags, CONNECTORS}; +use quote::quote; +use std::str::FromStr; +use syn::{parse_macro_input, spanned::Spanned, AttributeArgs, Ident, ItemFn}; + +static TAGS_FILTER: Lazy = Lazy::new(|| { + let tags_str = std::env::var("TEST_EACH_CONNECTOR_TAGS").ok(); + let mut tags = Tags::empty(); + + if let Some(tags_str) = tags_str { + for tag_str in tags_str.split(',') { + let tag = Tags::from_str(tag_str).unwrap(); + tags |= tag; + } + } + + tags +}); + +#[derive(Debug, FromMeta)] +struct TestEachConnectorArgs { + /// If present, run only the tests for the connectors with any of the passed + /// in tags. + #[darling(default)] + tags: TagsWrapper, + + /// Optional list of tags to ignore. + #[darling(default)] + ignore: TagsWrapper, +} + +impl TestEachConnectorArgs { + fn connectors_to_test(&self) -> impl Iterator { + CONNECTORS + .all() + .filter(move |connector| TAGS_FILTER.is_empty() || connector.tags.contains(*TAGS_FILTER)) + .filter(move |connector| self.tags.0.is_empty() || connector.tags.intersects(self.tags.0)) + .filter(move |connector| !connector.tags.intersects(self.ignore.0)) + } +} + +#[derive(Debug)] +struct TagsWrapper(Tags); + +impl Default for TagsWrapper { + fn default() -> Self { + TagsWrapper(Tags::empty()) + } +} + +impl darling::FromMeta for TagsWrapper { + fn from_list(items: &[syn::NestedMeta]) -> Result { + let mut tags = Tags::empty(); + + for item in items { + match item { + syn::NestedMeta::Lit(syn::Lit::Str(s)) => { + let s = s.value(); + let tag = Tags::from_str(&s) + .map_err(|err| darling::Error::unknown_value(&err.to_string()).with_span(&item.span()))?; + tags.insert(tag); + } + syn::NestedMeta::Lit(other) => { + return Err(darling::Error::unexpected_lit_type(other).with_span(&other.span())) + } + syn::NestedMeta::Meta(meta) => { + return Err(darling::Error::unsupported_shape("Expected string literal").with_span(&meta.span())) + } + } + } + + Ok(TagsWrapper(tags)) + } +} + +#[allow(clippy::needless_borrow)] +pub fn test_each_connector_impl(attr: TokenStream, input: TokenStream) -> TokenStream { + let attributes_meta: syn::AttributeArgs = parse_macro_input!(attr as AttributeArgs); + let args = TestEachConnectorArgs::from_list(&attributes_meta); + + let mut test_function = parse_macro_input!(input as ItemFn); + super::strip_test_attribute(&mut test_function); + + let tests = match args { + Ok(args) => test_each_connector_async_wrapper_functions(&args, &test_function), + Err(err) => return err.write_errors().into(), + }; + + let output = quote! { + #(#tests)* + + #test_function + }; + + output.into() +} + +#[allow(clippy::needless_borrow)] +fn test_each_connector_async_wrapper_functions( + args: &TestEachConnectorArgs, + test_function: &ItemFn, +) -> Vec { + let test_fn_name = &test_function.sig.ident; + let mut tests = Vec::with_capacity(CONNECTORS.len()); + + let optional_unwrap = if super::function_returns_result(&test_function) { + Some(quote!(.unwrap())) + } else { + None + }; + + for connector in args.connectors_to_test() { + let connector_name = connector.name(); + let feature_name = connector.feature_name(); + let connector_test_fn_name = Ident::new(&format!("{}_on_{}", test_fn_name, connector_name), Span::call_site()); + + let conn_api_factory = Ident::new(connector.test_api(), Span::call_site()); + + let test = quote! { + #[test] + #[cfg(feature = #feature_name)] + fn #connector_test_fn_name() { + let fut = async { + let mut api = #conn_api_factory().await#optional_unwrap; + #test_fn_name(&mut api).await#optional_unwrap + }; + + quaint_test_setup::run_with_tokio(fut) + } + }; + + tests.push(test); + } + + tests +} diff --git a/quaint/src/ast.rs b/quaint/src/ast.rs index ae2a19960b2d..dc634423014a 100644 --- a/quaint/src/ast.rs +++ b/quaint/src/ast.rs @@ -54,4 +54,4 @@ pub use table::*; pub use union::Union; pub use update::*; pub(crate) use values::Params; -pub use values::{IntoRaw, Raw, Value, Values}; +pub use values::{IntoRaw, Raw, Value, ValueType, Values}; diff --git a/quaint/src/ast/column.rs b/quaint/src/ast/column.rs index 7e6fbb71c9b7..836b4ce96527 100644 --- a/quaint/src/ast/column.rs +++ b/quaint/src/ast/column.rs @@ -110,7 +110,7 @@ impl<'a> Column<'a> { /// Sets whether the column is selected. /// /// On Postgres, this defines whether an enum column should be casted to `TEXT` when rendered. - /// + /// /// Since enums are user-defined custom types, `tokio-postgres` fires an additional query /// when selecting columns of type enum to know which custom type the column refers to. /// Casting the enum column to `TEXT` avoid this roundtrip since `TEXT` is a builtin type. diff --git a/quaint/src/ast/enums.rs b/quaint/src/ast/enums.rs index d301df25cc13..a4e93836d24b 100644 --- a/quaint/src/ast/enums.rs +++ b/quaint/src/ast/enums.rs @@ -14,11 +14,14 @@ impl<'a> EnumVariant<'a> { } pub fn into_text(self) -> Value<'a> { - Value::Text(Some(self.0)) + Value::text(self.0) } pub fn into_enum(self, name: Option>) -> Value<'a> { - Value::Enum(Some(self), name) + match name { + Some(name) => Value::enum_variant_with_name(self.0, name), + None => Value::enum_variant(self.0), + } } } diff --git a/quaint/src/ast/expression.rs b/quaint/src/ast/expression.rs index 7e5912d1e027..ea4c32a4fb61 100644 --- a/quaint/src/ast/expression.rs +++ b/quaint/src/ast/expression.rs @@ -45,7 +45,10 @@ impl<'a> Expression<'a> { pub(crate) fn is_json_expr(&self) -> bool { match &self.kind { - ExpressionKind::Parameterized(Value::Json(_)) => true, + ExpressionKind::Parameterized(Value { + typed: ValueType::Json(_), + .. + }) => true, ExpressionKind::Value(expr) => expr.is_json_value(), @@ -58,7 +61,10 @@ impl<'a> Expression<'a> { pub(crate) fn is_json_value(&self) -> bool { match &self.kind { - ExpressionKind::Parameterized(Value::Json(_)) => true, + ExpressionKind::Parameterized(Value { + typed: ValueType::Json(_), + .. + }) => true, ExpressionKind::Value(expr) => expr.is_json_value(), _ => false, @@ -69,7 +75,10 @@ impl<'a> Expression<'a> { pub(crate) fn into_json_value(self) -> Option { match self.kind { - ExpressionKind::Parameterized(Value::Json(json_val)) => json_val, + ExpressionKind::Parameterized(Value { + typed: ValueType::Json(json_val), + .. + }) => json_val, ExpressionKind::Value(expr) => expr.into_json_value(), _ => None, @@ -217,7 +226,10 @@ pub enum ExpressionKind<'a> { impl<'a> ExpressionKind<'a> { pub(crate) fn is_xml_value(&self) -> bool { match self { - Self::Parameterized(Value::Xml(_)) => true, + Self::Parameterized(Value { + typed: ValueType::Xml(_), + .. + }) => true, Self::Value(expr) => expr.is_xml_value(), _ => false, } diff --git a/quaint/src/ast/function/row_to_json.rs b/quaint/src/ast/function/row_to_json.rs index 1093431e7412..40e2b0dec7fc 100644 --- a/quaint/src/ast/function/row_to_json.rs +++ b/quaint/src/ast/function/row_to_json.rs @@ -31,9 +31,9 @@ pub struct RowToJson<'a> { /// let result = conn.select(select).await?; /// /// assert_eq!( -/// Value::Json(Some(serde_json::json!({ +/// Value::json(serde_json::json!({ /// "toto": "hello_world" -/// }))), +/// })), /// result.into_single().unwrap()[0] /// ); /// # Ok(()) diff --git a/quaint/src/ast/values.rs b/quaint/src/ast/values.rs index 05acccd77fff..92719b982eb4 100644 --- a/quaint/src/ast/values.rs +++ b/quaint/src/ast/values.rs @@ -5,6 +5,7 @@ use crate::error::{Error, ErrorKind}; use bigdecimal::{BigDecimal, FromPrimitive, ToPrimitive}; use chrono::{DateTime, NaiveDate, NaiveTime, Utc}; use serde_json::{Number, Value as JsonValue}; +use std::fmt::Display; use std::{ borrow::{Borrow, Cow}, convert::TryFrom, @@ -34,11 +35,456 @@ where } } +#[derive(Debug, Clone, PartialEq)] +pub struct Value<'a> { + pub typed: ValueType<'a>, + pub native_column_type: Option>, +} + +impl<'a> Value<'a> { + /// Creates a new 32-bit signed integer. + pub fn int32(value: I) -> Self + where + I: Into, + { + ValueType::int32(value).into_value() + } + + /// Creates a new 64-bit signed integer. + pub fn int64(value: I) -> Self + where + I: Into, + { + ValueType::int64(value).into_value() + } + + /// Creates a new decimal value. + #[cfg(feature = "bigdecimal")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] + pub fn numeric(value: BigDecimal) -> Self { + ValueType::numeric(value).into_value() + } + + /// Creates a new float value. + pub fn float(value: f32) -> Self { + ValueType::float(value).into_value() + } + + /// Creates a new double value. + pub fn double(value: f64) -> Self { + ValueType::double(value).into_value() + } + + /// Creates a new string value. + pub fn text(value: T) -> Self + where + T: Into>, + { + ValueType::text(value).into_value() + } + + /// Creates a new enum value. + pub fn enum_variant(value: T) -> Self + where + T: Into>, + { + ValueType::enum_variant(value).into_value() + } + + /// Creates a new enum value with the name of the enum attached. + pub fn enum_variant_with_name(value: T, name: U) -> Self + where + T: Into>, + U: Into>, + { + ValueType::enum_variant_with_name(value, name).into_value() + } + + /// Creates a new enum array value + pub fn enum_array(value: T) -> Self + where + T: IntoIterator>, + { + ValueType::enum_array(value).into_value() + } + + /// Creates a new enum array value with the name of the enum attached. + pub fn enum_array_with_name(value: T, name: U) -> Self + where + T: IntoIterator>, + U: Into>, + { + ValueType::enum_array_with_name(value, name).into_value() + } + + /// Creates a new bytes value. + pub fn bytes(value: B) -> Self + where + B: Into>, + { + ValueType::bytes(value).into_value() + } + + /// Creates a new boolean value. + pub fn boolean(value: B) -> Self + where + B: Into, + { + ValueType::boolean(value).into_value() + } + + /// Creates a new character value. + pub fn character(value: C) -> Self + where + C: Into, + { + ValueType::character(value).into_value() + } + + /// Creates a new array value. + pub fn array(value: I) -> Self + where + I: IntoIterator, + V: Into>, + { + ValueType::array(value).into_value() + } + + /// Creates a new uuid value. + #[cfg(feature = "uuid")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] + pub fn uuid(value: Uuid) -> Self { + ValueType::uuid(value).into_value() + } + + /// Creates a new datetime value. + pub fn datetime(value: DateTime) -> Self { + ValueType::datetime(value).into_value() + } + + /// Creates a new date value. + pub fn date(value: NaiveDate) -> Self { + ValueType::date(value).into_value() + } + + /// Creates a new time value. + pub fn time(value: NaiveTime) -> Self { + ValueType::time(value).into_value() + } + + /// Creates a new JSON value. + pub fn json(value: serde_json::Value) -> Self { + ValueType::json(value).into_value() + } + + /// Creates a new XML value. + pub fn xml(value: T) -> Self + where + T: Into>, + { + ValueType::xml(value).into_value() + } + + /// `true` if the `Value` is null. + pub fn is_null(&self) -> bool { + self.typed.is_null() + } + + /// Returns a &str if the value is text, otherwise `None`. + pub fn as_str(&self) -> Option<&str> { + self.typed.as_str() + } + + /// `true` if the `Value` is text. + pub fn is_text(&self) -> bool { + self.typed.is_text() + } + + /// Returns a char if the value is a char, otherwise `None`. + pub fn as_char(&self) -> Option { + self.typed.as_char() + } + + /// Returns a cloned String if the value is text, otherwise `None`. + pub fn to_string(&self) -> Option { + self.typed.to_string() + } + + /// Transforms the `Value` to a `String` if it's text, + /// otherwise `None`. + pub fn into_string(self) -> Option { + self.typed.into_string() + } + + /// Returns whether this value is the `Bytes` variant. + pub fn is_bytes(&self) -> bool { + self.typed.is_bytes() + } + + /// Returns a bytes slice if the value is text or a byte slice, otherwise `None`. + pub fn as_bytes(&self) -> Option<&[u8]> { + self.typed.as_bytes() + } + + /// Returns a cloned `Vec` if the value is text or a byte slice, otherwise `None`. + pub fn to_bytes(&self) -> Option> { + self.typed.to_bytes() + } + + /// `true` if the `Value` is a 32-bit signed integer. + pub fn is_i32(&self) -> bool { + self.typed.is_i32() + } + + /// `true` if the `Value` is a 64-bit signed integer. + pub fn is_i64(&self) -> bool { + self.typed.is_i64() + } + + /// `true` if the `Value` is a signed integer. + pub fn is_integer(&self) -> bool { + self.typed.is_integer() + } + + /// Returns an `i64` if the value is a 64-bit signed integer, otherwise `None`. + pub fn as_i64(&self) -> Option { + self.typed.as_i64() + } + + /// Returns an `i32` if the value is a 32-bit signed integer, otherwise `None`. + pub fn as_i32(&self) -> Option { + self.typed.as_i32() + } + + /// Returns an `i64` if the value is a signed integer, otherwise `None`. + pub fn as_integer(&self) -> Option { + self.typed.as_integer() + } + + /// Returns a `f64` if the value is a double, otherwise `None`. + pub fn as_f64(&self) -> Option { + self.typed.as_f64() + } + + /// Returns a `f32` if the value is a double, otherwise `None`. + pub fn as_f32(&self) -> Option { + self.typed.as_f32() + } + + /// `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 fn is_numeric(&self) -> bool { + self.typed.is_numeric() + } + + /// 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 { + self.typed.into_numeric() + } + + /// 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 fn as_numeric(&self) -> Option<&BigDecimal> { + self.typed.as_numeric() + } + + /// `true` if the `Value` is a boolean value. + pub fn is_bool(&self) -> bool { + self.typed.is_bool() + } + + /// Returns a bool if the value is a boolean, otherwise `None`. + pub fn as_bool(&self) -> Option { + self.typed.as_bool() + } + + /// `true` if the `Value` is an Array. + pub fn is_array(&self) -> bool { + self.typed.is_array() + } + + /// `true` if the `Value` is of UUID type. + #[cfg(feature = "uuid")] + #[cfg_attr(feature = "docs", doc(cfg(feature = "uuid")))] + pub fn is_uuid(&self) -> bool { + self.typed.is_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 fn as_uuid(&self) -> Option { + self.typed.as_uuid() + } + + /// `true` if the `Value` is a DateTime. + pub fn is_datetime(&self) -> bool { + self.typed.is_datetime() + } + + /// Returns a `DateTime` if the value is a `DateTime`, otherwise `None`. + pub fn as_datetime(&self) -> Option> { + self.typed.as_datetime() + } + + /// `true` if the `Value` is a Date. + pub fn is_date(&self) -> bool { + self.typed.is_date() + } + + /// Returns a `NaiveDate` if the value is a `Date`, otherwise `None`. + pub fn as_date(&self) -> Option { + self.typed.as_date() + } + + /// `true` if the `Value` is a `Time`. + pub fn is_time(&self) -> bool { + self.typed.is_time() + } + + /// Returns a `NaiveTime` if the value is a `Time`, otherwise `None`. + pub fn as_time(&self) -> Option { + self.typed.as_time() + } + + /// `true` if the `Value` is a JSON value. + pub fn is_json(&self) -> bool { + self.typed.is_json() + } + + /// Returns a reference to a JSON Value if of Json type, otherwise `None`. + pub fn as_json(&self) -> Option<&serde_json::Value> { + self.typed.as_json() + } + + /// Transforms to a JSON Value if of Json type, otherwise `None`. + pub fn into_json(self) -> Option { + self.typed.into_json() + } + + /// Returns a `Vec` if the value is an array of `T`, otherwise `None`. + pub fn into_vec(self) -> Option> + where + T: TryFrom>, + { + self.typed.into_vec() + } + + /// Returns a cloned Vec if the value is an array of T, otherwise `None`. + pub fn to_vec(&self) -> Option> + where + T: TryFrom>, + { + self.typed.to_vec() + } + + pub fn null_int32() -> Self { + ValueType::Int32(None).into() + } + + pub fn null_int64() -> Self { + ValueType::Int64(None).into() + } + + pub fn null_float() -> Self { + ValueType::Float(None).into() + } + + pub fn null_double() -> Self { + ValueType::Double(None).into() + } + + pub fn null_text() -> Self { + ValueType::Text(None).into() + } + + pub fn null_enum() -> Self { + ValueType::Enum(None, None).into() + } + + pub fn null_enum_array() -> Self { + ValueType::EnumArray(None, None).into() + } + + pub fn null_bytes() -> Self { + ValueType::Bytes(None).into() + } + + pub fn null_boolean() -> Self { + ValueType::Boolean(None).into() + } + + pub fn null_character() -> Self { + ValueType::Char(None).into() + } + + pub fn null_array() -> Self { + ValueType::Array(None).into() + } + + #[cfg(feature = "bigdecimal")] + pub fn null_numeric() -> Self { + ValueType::Numeric(None).into() + } + + pub fn null_json() -> Self { + ValueType::Json(None).into() + } + + pub fn null_xml() -> Self { + ValueType::Xml(None).into() + } + + #[cfg(feature = "uuid")] + pub fn null_uuid() -> Self { + ValueType::Uuid(None).into() + } + + pub fn null_datetime() -> Self { + ValueType::DateTime(None).into() + } + + pub fn null_date() -> Self { + ValueType::Date(None).into() + } + + pub fn null_time() -> Self { + ValueType::Time(None).into() + } +} + +impl<'a> Display for Value<'a> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.typed.fmt(f) + } +} + +impl<'a> From> for Value<'a> { + fn from(inner: ValueType<'a>) -> Self { + Self { + typed: inner, + native_column_type: Default::default(), + } + } +} + +impl<'a> From> for ValueType<'a> { + fn from(val: Value<'a>) -> Self { + val.typed + } +} + /// A value we must parameterize for the prepared statement. Null values should be /// defined by their corresponding type variants with a `None` value for best /// compatibility. #[derive(Debug, Clone, PartialEq)] -pub enum Value<'a> { +pub enum ValueType<'a> { /// 32-bit signed integer. Int32(Option), /// 64-bit signed integer. @@ -54,7 +500,7 @@ pub enum Value<'a> { /// Read more about it here: https://github.com/prisma/prisma-engines/pull/4280 Enum(Option>, Option>), /// Database enum array (PostgreSQL specific). - /// We use a different variant than `Value::Array` to uplift the `EnumName` + /// We use a different variant than `ValueType::Array` to uplift the `EnumName` /// and have it available even for empty enum arrays. EnumArray(Option>>, Option>), /// Bytes value. @@ -87,7 +533,7 @@ pub enum Value<'a> { pub(crate) struct Params<'a>(pub(crate) &'a [Value<'a>]); -impl<'a> fmt::Display for Params<'a> { +impl<'a> Display for Params<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let len = self.0.len(); @@ -103,17 +549,17 @@ impl<'a> fmt::Display for Params<'a> { } } -impl<'a> fmt::Display for Value<'a> { +impl<'a> fmt::Display for ValueType<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let res = match self { - Value::Int32(val) => val.map(|v| write!(f, "{v}")), - Value::Int64(val) => val.map(|v| write!(f, "{v}")), - Value::Float(val) => val.map(|v| write!(f, "{v}")), - Value::Double(val) => val.map(|v| write!(f, "{v}")), - Value::Text(val) => val.as_ref().map(|v| write!(f, "\"{v}\"")), - Value::Bytes(val) => val.as_ref().map(|v| write!(f, "<{} bytes blob>", v.len())), - Value::Enum(val, _) => val.as_ref().map(|v| write!(f, "\"{v}\"")), - Value::EnumArray(vals, _) => vals.as_ref().map(|vals| { + ValueType::Int32(val) => val.map(|v| write!(f, "{v}")), + ValueType::Int64(val) => val.map(|v| write!(f, "{v}")), + ValueType::Float(val) => val.map(|v| write!(f, "{v}")), + ValueType::Double(val) => val.map(|v| write!(f, "{v}")), + ValueType::Text(val) => val.as_ref().map(|v| write!(f, "\"{v}\"")), + ValueType::Bytes(val) => val.as_ref().map(|v| write!(f, "<{} bytes blob>", v.len())), + ValueType::Enum(val, _) => val.as_ref().map(|v| write!(f, "\"{v}\"")), + ValueType::EnumArray(vals, _) => vals.as_ref().map(|vals| { let len = vals.len(); write!(f, "[")?; @@ -126,9 +572,9 @@ impl<'a> fmt::Display for Value<'a> { } write!(f, "]") }), - Value::Boolean(val) => val.map(|v| write!(f, "{v}")), - Value::Char(val) => val.map(|v| write!(f, "'{v}'")), - Value::Array(vals) => vals.as_ref().map(|vals| { + ValueType::Boolean(val) => val.map(|v| write!(f, "{v}")), + ValueType::Char(val) => val.map(|v| write!(f, "'{v}'")), + ValueType::Array(vals) => vals.as_ref().map(|vals| { let len = vals.len(); write!(f, "[")?; @@ -141,15 +587,15 @@ impl<'a> fmt::Display for Value<'a> { } write!(f, "]") }), - Value::Xml(val) => val.as_ref().map(|v| write!(f, "{v}")), + ValueType::Xml(val) => val.as_ref().map(|v| write!(f, "{v}")), #[cfg(feature = "bigdecimal")] - Value::Numeric(val) => val.as_ref().map(|v| write!(f, "{v}")), - Value::Json(val) => val.as_ref().map(|v| write!(f, "{v}")), + ValueType::Numeric(val) => val.as_ref().map(|v| write!(f, "{v}")), + ValueType::Json(val) => val.as_ref().map(|v| write!(f, "{v}")), #[cfg(feature = "uuid")] - Value::Uuid(val) => val.map(|v| write!(f, "\"{v}\"")), - Value::DateTime(val) => val.map(|v| write!(f, "\"{v}\"")), - Value::Date(val) => val.map(|v| write!(f, "\"{v}\"")), - Value::Time(val) => val.map(|v| write!(f, "\"{v}\"")), + ValueType::Uuid(val) => val.map(|v| write!(f, "\"{v}\"")), + ValueType::DateTime(val) => val.map(|v| write!(f, "\"{v}\"")), + ValueType::Date(val) => val.map(|v| write!(f, "\"{v}\"")), + ValueType::Time(val) => val.map(|v| write!(f, "\"{v}\"")), }; match res { @@ -161,21 +607,27 @@ impl<'a> fmt::Display for Value<'a> { impl<'a> From> for serde_json::Value { fn from(pv: Value<'a>) -> Self { + pv.typed.into() + } +} + +impl<'a> From> for serde_json::Value { + fn from(pv: ValueType<'a>) -> Self { let res = match pv { - Value::Int32(i) => i.map(|i| serde_json::Value::Number(Number::from(i))), - Value::Int64(i) => i.map(|i| serde_json::Value::Number(Number::from(i))), - Value::Float(f) => f.map(|f| match Number::from_f64(f as f64) { + ValueType::Int32(i) => i.map(|i| serde_json::Value::Number(Number::from(i))), + ValueType::Int64(i) => i.map(|i| serde_json::Value::Number(Number::from(i))), + ValueType::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) { + ValueType::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::EnumArray(values, _) => values.map(|values| { + ValueType::Text(cow) => cow.map(|cow| serde_json::Value::String(cow.into_owned())), + ValueType::Bytes(bytes) => bytes.map(|bytes| serde_json::Value::String(base64::encode(bytes))), + ValueType::Enum(cow, _) => cow.map(|cow| serde_json::Value::String(cow.into_owned())), + ValueType::EnumArray(values, _) => values.map(|values| { serde_json::Value::Array( values .into_iter() @@ -183,26 +635,26 @@ impl<'a> From> for serde_json::Value { .collect(), ) }), - Value::Boolean(b) => b.map(serde_json::Value::Bool), - Value::Char(c) => c.map(|c| { + ValueType::Boolean(b) => b.map(serde_json::Value::Bool), + ValueType::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) => { + ValueType::Xml(cow) => cow.map(|cow| serde_json::Value::String(cow.into_owned())), + ValueType::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()), - Value::Json(v) => v, + ValueType::Numeric(d) => d.map(|d| serde_json::to_value(d.to_f64().unwrap()).unwrap()), + ValueType::Json(v) => v, #[cfg(feature = "uuid")] - Value::Uuid(u) => u.map(|u| serde_json::Value::String(u.hyphenated().to_string())), - Value::DateTime(dt) => dt.map(|dt| serde_json::Value::String(dt.to_rfc3339())), - Value::Date(date) => date.map(|date| serde_json::Value::String(format!("{date}"))), - Value::Time(time) => time.map(|time| serde_json::Value::String(format!("{time}"))), + ValueType::Uuid(u) => u.map(|u| serde_json::Value::String(u.hyphenated().to_string())), + ValueType::DateTime(dt) => dt.map(|dt| serde_json::Value::String(dt.to_rfc3339())), + ValueType::Date(date) => date.map(|date| serde_json::Value::String(format!("{date}"))), + ValueType::Time(time) => time.map(|time| serde_json::Value::String(format!("{time}"))), }; match res { @@ -212,259 +664,271 @@ impl<'a> From> for serde_json::Value { } } -impl<'a> Value<'a> { +impl<'a> ValueType<'a> { + pub fn into_value(self) -> Value<'a> { + self.into() + } + /// Creates a new 32-bit signed integer. - pub fn int32(value: I) -> Self + pub(crate) fn int32(value: I) -> Self where I: Into, { - Value::Int32(Some(value.into())) + Self::Int32(Some(value.into())) } /// Creates a new 64-bit signed integer. - pub fn int64(value: I) -> Self + pub(crate) fn int64(value: I) -> Self where I: Into, { - Value::Int64(Some(value.into())) - } - - /// Creates a new 32-bit signed integer. - pub fn integer(value: I) -> Self - where - I: Into, - { - Value::Int32(Some(value.into())) + Self::Int64(Some(value.into())) } /// Creates a new decimal value. #[cfg(feature = "bigdecimal")] #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] - pub const fn numeric(value: BigDecimal) -> Self { - Value::Numeric(Some(value)) + pub(crate) fn numeric(value: BigDecimal) -> Self { + Self::Numeric(Some(value)) } /// Creates a new float value. - pub const fn float(value: f32) -> Self { + pub(crate) fn float(value: f32) -> Self { Self::Float(Some(value)) } /// Creates a new double value. - pub const fn double(value: f64) -> Self { + pub(crate) fn double(value: f64) -> Self { Self::Double(Some(value)) } /// Creates a new string value. - pub fn text(value: T) -> Self + pub(crate) fn text(value: T) -> Self where T: Into>, { - Value::Text(Some(value.into())) + Self::Text(Some(value.into())) } /// Creates a new enum value. - pub fn enum_variant(value: T) -> Self + pub(crate) fn enum_variant(value: T) -> Self where - T: Into>, + T: Into>, { - Value::Enum(Some(EnumVariant::new(value)), None) + Self::Enum(Some(value.into()), None) } /// Creates a new enum value with the name of the enum attached. - pub fn enum_variant_with_name(value: T, name: U, schema_name: Option) -> Self + pub(crate) fn enum_variant_with_name(value: T, enum_name: U) -> Self where - T: Into>, - U: Into>, - V: Into>, + T: Into>, + U: Into>, + { + Self::Enum(Some(value.into()), Some(enum_name.into())) + } + + /// Creates a new enum array value + pub(crate) fn enum_array(value: T) -> Self + where + T: IntoIterator>, { - Value::Enum(Some(EnumVariant::new(value)), Some(EnumName::new(name, schema_name))) + Self::EnumArray(Some(value.into_iter().collect()), None) + } + + /// Creates a new enum array value with the name of the enum attached. + pub(crate) fn enum_array_with_name(value: T, name: U) -> Self + where + T: IntoIterator>, + U: Into>, + { + Self::EnumArray(Some(value.into_iter().collect()), Some(name.into())) } /// Creates a new bytes value. - pub fn bytes(value: B) -> Self + pub(crate) fn bytes(value: B) -> Self where B: Into>, { - Value::Bytes(Some(value.into())) + Self::Bytes(Some(value.into())) } /// Creates a new boolean value. - pub fn boolean(value: B) -> Self + pub(crate) fn boolean(value: B) -> Self where B: Into, { - Value::Boolean(Some(value.into())) + Self::Boolean(Some(value.into())) } /// Creates a new character value. - pub fn character(value: C) -> Self + pub(crate) fn character(value: C) -> Self where C: Into, { - Value::Char(Some(value.into())) + Self::Char(Some(value.into())) } /// Creates a new array value. - pub fn array(value: I) -> Self + pub(crate) fn array(value: I) -> Self where I: IntoIterator, V: Into>, { - Value::Array(Some(value.into_iter().map(|v| v.into()).collect())) + Self::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")))] - pub const fn uuid(value: Uuid) -> Self { - Value::Uuid(Some(value)) + pub(crate) fn uuid(value: Uuid) -> Self { + Self::Uuid(Some(value)) } /// Creates a new datetime value. - pub const fn datetime(value: DateTime) -> Self { - Value::DateTime(Some(value)) + pub(crate) fn datetime(value: DateTime) -> Self { + Self::DateTime(Some(value)) } /// Creates a new date value. - pub const fn date(value: NaiveDate) -> Self { - Value::Date(Some(value)) + pub(crate) fn date(value: NaiveDate) -> Self { + Self::Date(Some(value)) } /// Creates a new time value. - pub const fn time(value: NaiveTime) -> Self { - Value::Time(Some(value)) + pub(crate) fn time(value: NaiveTime) -> Self { + Self::Time(Some(value)) } /// Creates a new JSON value. - pub const fn json(value: serde_json::Value) -> Self { - Value::Json(Some(value)) + pub(crate) fn json(value: serde_json::Value) -> Self { + Self::Json(Some(value)) } /// Creates a new XML value. - pub fn xml(value: T) -> Self + pub(crate) fn xml(value: T) -> Self where T: Into>, { - Value::Xml(Some(value.into())) + Self::Xml(Some(value.into())) } /// `true` if the `Value` is null. - pub const fn is_null(&self) -> bool { + pub fn is_null(&self) -> bool { match self { - Value::Int32(i) => i.is_none(), - Value::Int64(i) => i.is_none(), - Value::Float(i) => i.is_none(), - Value::Double(i) => i.is_none(), - Value::Text(t) => t.is_none(), - Value::Enum(e, _) => e.is_none(), - Value::EnumArray(e, _) => e.is_none(), - Value::Bytes(b) => b.is_none(), - Value::Boolean(b) => b.is_none(), - Value::Char(c) => c.is_none(), - Value::Array(v) => v.is_none(), - Value::Xml(s) => s.is_none(), + Self::Int32(i) => i.is_none(), + Self::Int64(i) => i.is_none(), + Self::Float(i) => i.is_none(), + Self::Double(i) => i.is_none(), + Self::Text(t) => t.is_none(), + Self::Enum(e, _) => e.is_none(), + Self::EnumArray(e, _) => e.is_none(), + Self::Bytes(b) => b.is_none(), + Self::Boolean(b) => b.is_none(), + Self::Char(c) => c.is_none(), + Self::Array(v) => v.is_none(), + Self::Xml(s) => s.is_none(), #[cfg(feature = "bigdecimal")] - Value::Numeric(r) => r.is_none(), + Self::Numeric(r) => r.is_none(), #[cfg(feature = "uuid")] - Value::Uuid(u) => u.is_none(), - Value::DateTime(dt) => dt.is_none(), - Value::Date(d) => d.is_none(), - Value::Time(t) => t.is_none(), - Value::Json(json) => json.is_none(), + Self::Uuid(u) => u.is_none(), + Self::DateTime(dt) => dt.is_none(), + Self::Date(d) => d.is_none(), + Self::Time(t) => t.is_none(), + Self::Json(json) => json.is_none(), } } /// `true` if the `Value` is text. - pub const fn is_text(&self) -> bool { - matches!(self, Value::Text(_)) + pub(crate) fn is_text(&self) -> bool { + matches!(self, Self::Text(_)) } /// Returns a &str if the value is text, otherwise `None`. - pub fn as_str(&self) -> Option<&str> { + pub(crate) fn as_str(&self) -> Option<&str> { match self { - Value::Text(Some(cow)) => Some(cow.borrow()), - Value::Bytes(Some(cow)) => std::str::from_utf8(cow.as_ref()).ok(), + Self::Text(Some(cow)) => Some(cow.borrow()), + Self::Bytes(Some(cow)) => std::str::from_utf8(cow.as_ref()).ok(), _ => None, } } /// Returns a char if the value is a char, otherwise `None`. - pub const fn as_char(&self) -> Option { + pub(crate) fn as_char(&self) -> Option { match self { - Value::Char(c) => *c, + Self::Char(c) => *c, _ => None, } } /// Returns a cloned String if the value is text, otherwise `None`. - pub fn to_string(&self) -> Option { + pub(crate) fn to_string(&self) -> Option { match self { - Value::Text(Some(cow)) => Some(cow.to_string()), - Value::Bytes(Some(cow)) => std::str::from_utf8(cow.as_ref()).map(|s| s.to_owned()).ok(), + Self::Text(Some(cow)) => Some(cow.to_string()), + Self::Bytes(Some(cow)) => std::str::from_utf8(cow.as_ref()).map(|s| s.to_owned()).ok(), _ => None, } } /// Transforms the `Value` to a `String` if it's text, /// otherwise `None`. - pub fn into_string(self) -> Option { + pub(crate) fn into_string(self) -> Option { match self { - Value::Text(Some(cow)) => Some(cow.into_owned()), - Value::Bytes(Some(cow)) => String::from_utf8(cow.into_owned()).ok(), + Self::Text(Some(cow)) => Some(cow.into_owned()), + Self::Bytes(Some(cow)) => String::from_utf8(cow.into_owned()).ok(), _ => None, } } /// Returns whether this value is the `Bytes` variant. - pub const fn is_bytes(&self) -> bool { - matches!(self, Value::Bytes(_)) + pub(crate) fn is_bytes(&self) -> bool { + matches!(self, Self::Bytes(_)) } /// Returns a bytes slice if the value is text or a byte slice, otherwise `None`. - pub fn as_bytes(&self) -> Option<&[u8]> { + pub(crate) fn as_bytes(&self) -> Option<&[u8]> { match self { - Value::Text(Some(cow)) => Some(cow.as_ref().as_bytes()), - Value::Bytes(Some(cow)) => Some(cow.as_ref()), + Self::Text(Some(cow)) => Some(cow.as_ref().as_bytes()), + Self::Bytes(Some(cow)) => Some(cow.as_ref()), _ => None, } } /// Returns a cloned `Vec` if the value is text or a byte slice, otherwise `None`. - pub fn to_bytes(&self) -> Option> { + pub(crate) fn to_bytes(&self) -> Option> { match self { - Value::Text(Some(cow)) => Some(cow.to_string().into_bytes()), - Value::Bytes(Some(cow)) => Some(cow.to_vec()), + Self::Text(Some(cow)) => Some(cow.to_string().into_bytes()), + Self::Bytes(Some(cow)) => Some(cow.to_vec()), _ => None, } } /// `true` if the `Value` is a 32-bit signed integer. - pub const fn is_i32(&self) -> bool { - matches!(self, Value::Int32(_)) + pub(crate) fn is_i32(&self) -> bool { + matches!(self, Self::Int32(_)) } /// `true` if the `Value` is a 64-bit signed integer. - pub const fn is_i64(&self) -> bool { - matches!(self, Value::Int64(_)) + pub(crate) fn is_i64(&self) -> bool { + matches!(self, Self::Int64(_)) } /// `true` if the `Value` is a signed integer. - pub const fn is_integer(&self) -> bool { - matches!(self, Value::Int32(_) | Value::Int64(_)) + pub fn is_integer(&self) -> bool { + matches!(self, Self::Int32(_) | Self::Int64(_)) } /// Returns an `i64` if the value is a 64-bit signed integer, otherwise `None`. - pub const fn as_i64(&self) -> Option { + pub(crate) fn as_i64(&self) -> Option { match self { - Value::Int64(i) => *i, + Self::Int64(i) => *i, _ => None, } } /// Returns an `i32` if the value is a 32-bit signed integer, otherwise `None`. - pub const fn as_i32(&self) -> Option { + pub(crate) fn as_i32(&self) -> Option { match self { - Value::Int32(i) => *i, + Self::Int32(i) => *i, _ => None, } } @@ -472,24 +936,24 @@ impl<'a> Value<'a> { /// Returns an `i64` if the value is a signed integer, otherwise `None`. pub fn as_integer(&self) -> Option { match self { - Value::Int32(i) => i.map(|i| i as i64), - Value::Int64(i) => *i, + Self::Int32(i) => i.map(|i| i as i64), + Self::Int64(i) => *i, _ => None, } } /// Returns a `f64` if the value is a double, otherwise `None`. - pub const fn as_f64(&self) -> Option { + pub(crate) fn as_f64(&self) -> Option { match self { - Value::Double(Some(f)) => Some(*f), + Self::Double(Some(f)) => Some(*f), _ => None, } } /// Returns a `f32` if the value is a double, otherwise `None`. - pub const fn as_f32(&self) -> Option { + pub(crate) fn as_f32(&self) -> Option { match self { - Value::Float(Some(f)) => Some(*f), + Self::Float(Some(f)) => Some(*f), _ => None, } } @@ -497,19 +961,19 @@ 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(_)) + pub(crate) fn is_numeric(&self) -> bool { + matches!(self, Self::Numeric(_) | Self::Float(_) | Self::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 { + pub(crate) 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), + Self::Numeric(d) => d, + Self::Float(f) => f.and_then(BigDecimal::from_f32), + Self::Double(f) => f.and_then(BigDecimal::from_f64), _ => None, } } @@ -518,125 +982,125 @@ impl<'a> Value<'a> { /// Otherwise `None`. #[cfg(feature = "bigdecimal")] #[cfg_attr(feature = "docs", doc(cfg(feature = "bigdecimal")))] - pub const fn as_numeric(&self) -> Option<&BigDecimal> { + pub(crate) fn as_numeric(&self) -> Option<&BigDecimal> { match self { - Value::Numeric(d) => d.as_ref(), + Self::Numeric(d) => d.as_ref(), _ => None, } } /// `true` if the `Value` is a boolean value. - pub const fn is_bool(&self) -> bool { + pub(crate) fn is_bool(&self) -> bool { match self { - Value::Boolean(_) => true, + Self::Boolean(_) => true, // For schemas which don't tag booleans - Value::Int32(Some(i)) if *i == 0 || *i == 1 => true, - Value::Int64(Some(i)) if *i == 0 || *i == 1 => true, + Self::Int32(Some(i)) if *i == 0 || *i == 1 => true, + Self::Int64(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 { + pub(crate) fn as_bool(&self) -> Option { match self { - Value::Boolean(b) => *b, + Self::Boolean(b) => *b, // For schemas which don't tag booleans - Value::Int32(Some(i)) if *i == 0 || *i == 1 => Some(*i == 1), - Value::Int64(Some(i)) if *i == 0 || *i == 1 => Some(*i == 1), + Self::Int32(Some(i)) if *i == 0 || *i == 1 => Some(*i == 1), + Self::Int64(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(_)) + pub(crate) fn is_array(&self) -> bool { + matches!(self, Self::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(_)) + pub(crate) fn is_uuid(&self) -> bool { + matches!(self, Self::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 { + pub(crate) fn as_uuid(&self) -> Option { match self { - Value::Uuid(u) => *u, + Self::Uuid(u) => *u, _ => None, } } /// `true` if the `Value` is a DateTime. - pub const fn is_datetime(&self) -> bool { - matches!(self, Value::DateTime(_)) + pub(crate) fn is_datetime(&self) -> bool { + matches!(self, Self::DateTime(_)) } /// Returns a `DateTime` if the value is a `DateTime`, otherwise `None`. - pub const fn as_datetime(&self) -> Option> { + pub(crate) fn as_datetime(&self) -> Option> { match self { - Value::DateTime(dt) => *dt, + Self::DateTime(dt) => *dt, _ => None, } } /// `true` if the `Value` is a Date. - pub const fn is_date(&self) -> bool { - matches!(self, Value::Date(_)) + pub(crate) fn is_date(&self) -> bool { + matches!(self, Self::Date(_)) } /// Returns a `NaiveDate` if the value is a `Date`, otherwise `None`. - pub const fn as_date(&self) -> Option { + pub(crate) fn as_date(&self) -> Option { match self { - Value::Date(dt) => *dt, + Self::Date(dt) => *dt, _ => None, } } /// `true` if the `Value` is a `Time`. - pub const fn is_time(&self) -> bool { - matches!(self, Value::Time(_)) + pub(crate) fn is_time(&self) -> bool { + matches!(self, Self::Time(_)) } /// Returns a `NaiveTime` if the value is a `Time`, otherwise `None`. - pub const fn as_time(&self) -> Option { + pub(crate) fn as_time(&self) -> Option { match self { - Value::Time(time) => *time, + Self::Time(time) => *time, _ => None, } } /// `true` if the `Value` is a JSON value. - pub const fn is_json(&self) -> bool { - matches!(self, Value::Json(_)) + pub(crate) fn is_json(&self) -> bool { + matches!(self, Self::Json(_)) } /// Returns a reference to a JSON Value if of Json type, otherwise `None`. - pub const fn as_json(&self) -> Option<&serde_json::Value> { + pub(crate) fn as_json(&self) -> Option<&serde_json::Value> { match self { - Value::Json(Some(j)) => Some(j), + Self::Json(Some(j)) => Some(j), _ => None, } } /// Transforms to a JSON Value if of Json type, otherwise `None`. - pub fn into_json(self) -> Option { + pub(crate) fn into_json(self) -> Option { match self { - Value::Json(Some(j)) => Some(j), + Self::Json(Some(j)) => Some(j), _ => None, } } /// Returns a `Vec` if the value is an array of `T`, otherwise `None`. - pub fn into_vec(self) -> Option> + pub(crate) fn into_vec(self) -> Option> where // Implement From T: TryFrom>, { match self { - Value::Array(Some(vec)) => { + Self::Array(Some(vec)) => { let rslt: Result, _> = vec.into_iter().map(T::try_from).collect(); match rslt { Err(_) => None, @@ -648,12 +1112,12 @@ impl<'a> Value<'a> { } /// Returns a cloned Vec if the value is an array of T, otherwise `None`. - pub fn to_vec(&self) -> Option> + pub(crate) fn to_vec(&self) -> Option> where T: TryFrom>, { match self { - Value::Array(Some(vec)) => { + Self::Array(Some(vec)) => { let rslt: Result, _> = vec.clone().into_iter().map(T::try_from).collect(); match rslt { Err(_) => None, @@ -669,6 +1133,8 @@ value!(val: i64, Int64, val); value!(val: i32, Int32, val); value!(val: bool, Boolean, val); value!(val: &'a str, Text, val.into()); +value!(val: &'a String, Text, val.into()); +value!(val: &'a &str, Text, (*val).into()); value!(val: String, Text, val.into()); value!(val: usize, Int64, i64::try_from(val).unwrap()); value!(val: &'a [u8], Bytes, val.into()); @@ -689,6 +1155,7 @@ impl<'a> TryFrom> for i64 { fn try_from(value: Value<'a>) -> Result { value + .typed .as_i64() .ok_or_else(|| Error::builder(ErrorKind::conversion("Not an i64")).build()) } @@ -759,24 +1226,24 @@ impl<'a> TryFrom<&Value<'a>> for Option { type Error = Error; fn try_from(value: &Value<'a>) -> Result, Self::Error> { - match value { - val @ Value::Text(Some(_)) => { - let text = val.as_str().unwrap(); + match &value.typed { + ValueType::Text(Some(_)) => { + let text = value.typed.as_str().unwrap(); match std::net::IpAddr::from_str(text) { Ok(ip) => Ok(Some(ip)), Err(e) => Err(e.into()), } } - val @ Value::Bytes(Some(_)) => { - let text = val.as_str().unwrap(); + ValueType::Bytes(Some(_)) => { + let text = value.typed.as_str().unwrap(); match std::net::IpAddr::from_str(text) { Ok(ip) => Ok(Some(ip)), Err(e) => Err(e.into()), } } - v if v.is_null() => Ok(None), + _ if value.typed.is_null() => Ok(None), v => { let kind = ErrorKind::conversion(format!("Couldn't convert value of type `{v:?}` to std::net::IpAddr.")); @@ -792,25 +1259,25 @@ impl<'a> TryFrom<&Value<'a>> for Option { type Error = Error; fn try_from(value: &Value<'a>) -> Result, Self::Error> { - match value { - Value::Uuid(uuid) => Ok(*uuid), - val @ Value::Text(Some(_)) => { - let text = val.as_str().unwrap(); + match &value.typed { + ValueType::Uuid(uuid) => Ok(*uuid), + ValueType::Text(Some(_)) => { + let text = value.typed.as_str().unwrap(); match uuid::Uuid::from_str(text) { Ok(ip) => Ok(Some(ip)), Err(e) => Err(e.into()), } } - val @ Value::Bytes(Some(_)) => { - let text = val.as_str().unwrap(); + ValueType::Bytes(Some(_)) => { + let text = value.typed.as_str().unwrap(); match uuid::Uuid::from_str(text) { Ok(ip) => Ok(Some(ip)), Err(e) => Err(e.into()), } } - v if v.is_null() => Ok(None), + _ if value.typed.is_null() => Ok(None), v => { let kind = ErrorKind::conversion(format!("Couldn't convert value of type `{v:?}` to uuid::Uuid.")); @@ -913,35 +1380,35 @@ mod tests { #[test] fn a_parameterized_value_of_ints32_can_be_converted_into_a_vec() { let pv = Value::array(vec![1]); - let values: Vec = pv.into_vec().expect("convert into Vec"); + let values: Vec = pv.typed.into_vec().expect("convert into Vec"); assert_eq!(values, vec![1]); } #[test] fn a_parameterized_value_of_ints64_can_be_converted_into_a_vec() { let pv = Value::array(vec![1_i64]); - let values: Vec = pv.into_vec().expect("convert into Vec"); + let values: Vec = pv.typed.into_vec().expect("convert into Vec"); assert_eq!(values, vec![1]); } #[test] fn a_parameterized_value_of_reals_can_be_converted_into_a_vec() { let pv = Value::array(vec![1.0]); - let values: Vec = pv.into_vec().expect("convert into Vec"); + let values: Vec = pv.typed.into_vec().expect("convert into Vec"); assert_eq!(values, vec![1.0]); } #[test] fn a_parameterized_value_of_texts_can_be_converted_into_a_vec() { let pv = Value::array(vec!["test"]); - let values: Vec = pv.into_vec().expect("convert into Vec"); + let values: Vec = pv.typed.into_vec().expect("convert into Vec"); assert_eq!(values, vec!["test"]); } #[test] fn a_parameterized_value_of_booleans_can_be_converted_into_a_vec() { let pv = Value::array(vec![true]); - let values: Vec = pv.into_vec().expect("convert into Vec"); + let values: Vec = pv.typed.into_vec().expect("convert into Vec"); assert_eq!(values, vec![true]); } @@ -949,14 +1416,14 @@ mod tests { fn a_parameterized_value_of_datetimes_can_be_converted_into_a_vec() { let datetime = DateTime::from_str("2019-07-27T05:30:30Z").expect("parsing date/time"); let pv = Value::array(vec![datetime]); - let values: Vec> = pv.into_vec().expect("convert into Vec"); + let values: Vec> = pv.typed.into_vec().expect("convert into Vec"); assert_eq!(values, vec![datetime]); } #[test] fn a_parameterized_value_of_an_array_cant_be_converted_into_a_vec_of_the_wrong_type() { let pv = Value::array(vec![1]); - let rslt: Option> = pv.into_vec(); + let rslt: Option> = pv.typed.into_vec(); assert!(rslt.is_none()); } diff --git a/quaint/src/connector/metrics.rs b/quaint/src/connector/metrics.rs index e806f98c1d8f..2705a40b32b2 100644 --- a/quaint/src/connector/metrics.rs +++ b/quaint/src/connector/metrics.rs @@ -36,7 +36,7 @@ where trace_query(&query_fmt, params, result, start); } else { - trace_query(&query, params, result, start); + trace_query(query, params, result, start); }; } diff --git a/quaint/src/connector/mssql/conversion.rs b/quaint/src/connector/mssql/conversion.rs index 682e75b44760..246d1a30cdde 100644 --- a/quaint/src/connector/mssql/conversion.rs +++ b/quaint/src/connector/mssql/conversion.rs @@ -1,4 +1,4 @@ -use crate::ast::Value; +use crate::ast::{Value, ValueType}; #[cfg(not(feature = "bigdecimal"))] use crate::error::*; #[cfg(feature = "bigdecimal")] @@ -10,26 +10,26 @@ use tiberius::{ColumnData, FromSql, IntoSql}; impl<'a> IntoSql<'a> for &'a Value<'a> { fn into_sql(self) -> ColumnData<'a> { - match self { - Value::Int32(val) => val.into_sql(), - Value::Int64(val) => val.into_sql(), - Value::Float(val) => val.into_sql(), - Value::Double(val) => val.into_sql(), - Value::Text(val) => val.as_deref().into_sql(), - Value::Bytes(val) => val.as_deref().into_sql(), - Value::Enum(val, _) => val.as_deref().into_sql(), - Value::Boolean(val) => val.into_sql(), - Value::Char(val) => val.as_ref().map(|val| format!("{val}")).into_sql(), - Value::Xml(val) => val.as_deref().into_sql(), - Value::Array(_) | Value::EnumArray(_, _) => panic!("Arrays are not supported on SQL Server."), + match &self.typed { + ValueType::Int32(val) => val.into_sql(), + ValueType::Int64(val) => val.into_sql(), + ValueType::Float(val) => val.into_sql(), + ValueType::Double(val) => val.into_sql(), + ValueType::Text(val) => val.as_deref().into_sql(), + ValueType::Bytes(val) => val.as_deref().into_sql(), + ValueType::Enum(val, _) => val.as_deref().into_sql(), + ValueType::Boolean(val) => val.into_sql(), + ValueType::Char(val) => val.as_ref().map(|val| format!("{val}")).into_sql(), + ValueType::Xml(val) => val.as_deref().into_sql(), + ValueType::Array(_) | ValueType::EnumArray(_, _) => panic!("Arrays are not supported on SQL Server."), #[cfg(feature = "bigdecimal")] - Value::Numeric(val) => (*val).to_sql(), - Value::Json(val) => val.as_ref().map(|val| serde_json::to_string(&val).unwrap()).into_sql(), + ValueType::Numeric(val) => (*val).to_sql(), + ValueType::Json(val) => val.as_ref().map(|val| serde_json::to_string(&val).unwrap()).into_sql(), #[cfg(feature = "uuid")] - Value::Uuid(val) => val.into_sql(), - Value::DateTime(val) => val.into_sql(), - Value::Date(val) => val.into_sql(), - Value::Time(val) => val.into_sql(), + ValueType::Uuid(val) => val.into_sql(), + ValueType::DateTime(val) => val.into_sql(), + ValueType::Date(val) => val.into_sql(), + ValueType::Time(val) => val.into_sql(), } } } @@ -39,18 +39,18 @@ impl TryFrom> for Value<'static> { fn try_from(cd: ColumnData<'static>) -> crate::Result { let res = match cd { - ColumnData::U8(num) => Value::Int32(num.map(i32::from)), - ColumnData::I16(num) => Value::Int32(num.map(i32::from)), - ColumnData::I32(num) => Value::Int32(num.map(i32::from)), - ColumnData::I64(num) => Value::Int64(num.map(i64::from)), - ColumnData::F32(num) => Value::Float(num), - ColumnData::F64(num) => Value::Double(num), - ColumnData::Bit(b) => Value::Boolean(b), - ColumnData::String(s) => Value::Text(s), - ColumnData::Guid(uuid) => Value::Uuid(uuid), - ColumnData::Binary(bytes) => Value::Bytes(bytes), + ColumnData::U8(num) => ValueType::Int32(num.map(i32::from)), + ColumnData::I16(num) => ValueType::Int32(num.map(i32::from)), + ColumnData::I32(num) => ValueType::Int32(num.map(i32::from)), + ColumnData::I64(num) => ValueType::Int64(num.map(i64::from)), + ColumnData::F32(num) => ValueType::Float(num), + ColumnData::F64(num) => ValueType::Double(num), + ColumnData::Bit(b) => ValueType::Boolean(b), + ColumnData::String(s) => ValueType::Text(s), + ColumnData::Guid(uuid) => ValueType::Uuid(uuid), + ColumnData::Binary(bytes) => ValueType::Bytes(bytes), #[cfg(feature = "bigdecimal")] - numeric @ ColumnData::Numeric(_) => Value::Numeric(BigDecimal::from_sql(&numeric)?), + numeric @ ColumnData::Numeric(_) => ValueType::Numeric(BigDecimal::from_sql(&numeric)?), #[cfg(not(feature = "bigdecimal"))] _numeric @ ColumnData::Numeric(_) => { let kind = ErrorKind::conversion("Please enable `bigdecimal` feature to read numeric values"); @@ -60,38 +60,38 @@ impl TryFrom> for Value<'static> { use tiberius::time::chrono::{DateTime, NaiveDateTime, Utc}; let dt = NaiveDateTime::from_sql(&dt)?.map(|dt| DateTime::::from_utc(dt, Utc)); - Value::DateTime(dt) + ValueType::DateTime(dt) } dt @ ColumnData::SmallDateTime(_) => { use tiberius::time::chrono::{DateTime, NaiveDateTime, Utc}; let dt = NaiveDateTime::from_sql(&dt)?.map(|dt| DateTime::::from_utc(dt, Utc)); - Value::DateTime(dt) + ValueType::DateTime(dt) } dt @ ColumnData::Time(_) => { use tiberius::time::chrono::NaiveTime; - Value::Time(NaiveTime::from_sql(&dt)?) + ValueType::Time(NaiveTime::from_sql(&dt)?) } dt @ ColumnData::Date(_) => { use tiberius::time::chrono::NaiveDate; - Value::Date(NaiveDate::from_sql(&dt)?) + ValueType::Date(NaiveDate::from_sql(&dt)?) } dt @ ColumnData::DateTime2(_) => { use tiberius::time::chrono::{DateTime, NaiveDateTime, Utc}; let dt = NaiveDateTime::from_sql(&dt)?.map(|dt| DateTime::::from_utc(dt, Utc)); - Value::DateTime(dt) + ValueType::DateTime(dt) } dt @ ColumnData::DateTimeOffset(_) => { use tiberius::time::chrono::{DateTime, Utc}; - Value::DateTime(DateTime::::from_sql(&dt)?) + ValueType::DateTime(DateTime::::from_sql(&dt)?) } - ColumnData::Xml(cow) => Value::Xml(cow.map(|xml_data| Cow::Owned(xml_data.into_owned().into_string()))), + ColumnData::Xml(cow) => ValueType::Xml(cow.map(|xml_data| Cow::Owned(xml_data.into_owned().into_string()))), }; - Ok(res) + Ok(Value::from(res)) } } diff --git a/quaint/src/connector/mysql.rs b/quaint/src/connector/mysql.rs index e4be7b47c404..d0c28a9786fe 100644 --- a/quaint/src/connector/mysql.rs +++ b/quaint/src/connector/mysql.rs @@ -560,7 +560,7 @@ impl Queryable for Mysql { let version_string = rows .get(0) - .and_then(|row| row.get("version").and_then(|version| version.to_string())); + .and_then(|row| row.get("version").and_then(|version| version.typed.to_string())); Ok(version_string) } diff --git a/quaint/src/connector/mysql/conversion.rs b/quaint/src/connector/mysql/conversion.rs index c9b1e812873d..9230199eaf40 100644 --- a/quaint/src/connector/mysql/conversion.rs +++ b/quaint/src/connector/mysql/conversion.rs @@ -1,5 +1,5 @@ use crate::{ - ast::Value, + ast::{Value, ValueType}, connector::{queryable::TakeRow, TypeIdentifier}, error::{Error, ErrorKind}, }; @@ -19,18 +19,18 @@ pub fn conv_params(params: &[Value<'_>]) -> crate::Result { let mut values = Vec::with_capacity(params.len()); for pv in params { - let res = match pv { - Value::Int32(i) => i.map(|i| my::Value::Int(i as i64)), - Value::Int64(i) => i.map(my::Value::Int), - Value::Float(f) => f.map(my::Value::Float), - Value::Double(f) => f.map(my::Value::Double), - Value::Text(s) => s.clone().map(|s| my::Value::Bytes((*s).as_bytes().to_vec())), - Value::Bytes(bytes) => bytes.clone().map(|bytes| my::Value::Bytes(bytes.into_owned())), - Value::Enum(s, _) => s.clone().map(|s| my::Value::Bytes((*s).as_bytes().to_vec())), - Value::Boolean(b) => b.map(|b| my::Value::Int(b as i64)), - Value::Char(c) => c.map(|c| my::Value::Bytes(vec![c as u8])), - Value::Xml(s) => s.as_ref().map(|s| my::Value::Bytes((s).as_bytes().to_vec())), - Value::Array(_) | Value::EnumArray(_, _) => { + let res = match &pv.typed { + ValueType::Int32(i) => i.map(|i| my::Value::Int(i as i64)), + ValueType::Int64(i) => i.map(my::Value::Int), + ValueType::Float(f) => f.map(my::Value::Float), + ValueType::Double(f) => f.map(my::Value::Double), + ValueType::Text(s) => s.clone().map(|s| my::Value::Bytes((*s).as_bytes().to_vec())), + ValueType::Bytes(bytes) => bytes.clone().map(|bytes| my::Value::Bytes(bytes.into_owned())), + ValueType::Enum(s, _) => s.clone().map(|s| my::Value::Bytes((*s).as_bytes().to_vec())), + ValueType::Boolean(b) => b.map(|b| my::Value::Int(b as i64)), + ValueType::Char(c) => c.map(|c| my::Value::Bytes(vec![c as u8])), + ValueType::Xml(s) => s.as_ref().map(|s| my::Value::Bytes((s).as_bytes().to_vec())), + ValueType::Array(_) | ValueType::EnumArray(_, _) => { let msg = "Arrays are not supported in MySQL."; let kind = ErrorKind::conversion(msg); @@ -40,8 +40,8 @@ pub fn conv_params(params: &[Value<'_>]) -> crate::Result { return Err(builder.build()); } #[cfg(feature = "bigdecimal")] - Value::Numeric(f) => f.as_ref().map(|f| my::Value::Bytes(f.to_string().as_bytes().to_vec())), - Value::Json(s) => match s { + ValueType::Numeric(f) => f.as_ref().map(|f| my::Value::Bytes(f.to_string().as_bytes().to_vec())), + ValueType::Json(s) => match s { Some(ref s) => { let json = serde_json::to_string(s)?; let bytes = json.into_bytes(); @@ -51,14 +51,14 @@ pub fn conv_params(params: &[Value<'_>]) -> crate::Result { None => None, }, #[cfg(feature = "uuid")] - Value::Uuid(u) => u.map(|u| my::Value::Bytes(u.hyphenated().to_string().into_bytes())), - Value::Date(d) => { + ValueType::Uuid(u) => u.map(|u| my::Value::Bytes(u.hyphenated().to_string().into_bytes())), + ValueType::Date(d) => { d.map(|d| my::Value::Date(d.year() as u16, d.month() as u8, d.day() as u8, 0, 0, 0, 0)) } - Value::Time(t) => { + ValueType::Time(t) => { t.map(|t| my::Value::Time(false, 0, t.hour() as u8, t.minute() as u8, t.second() as u8, 0)) } - Value::DateTime(dt) => dt.map(|dt| { + ValueType::DateTime(dt) => dt.map(|dt| { my::Value::Date( dt.year() as u16, dt.month() as u8, @@ -303,21 +303,21 @@ impl TakeRow for my::Row { Value::time(time) } my::Value::NULL => match column { - t if t.is_bool() => Value::Boolean(None), - t if t.is_enum() => Value::Enum(None, None), - t if t.is_null() => Value::Int32(None), - t if t.is_int64() => Value::Int64(None), - t if t.is_int32() => Value::Int32(None), - t if t.is_float() => Value::Float(None), - t if t.is_double() => Value::Double(None), - t if t.is_text() => Value::Text(None), - t if t.is_bytes() => Value::Bytes(None), + t if t.is_bool() => Value::null_boolean(), + t if t.is_enum() => Value::null_enum(), + t if t.is_null() => Value::null_int32(), + t if t.is_int64() => Value::null_int64(), + t if t.is_int32() => Value::null_int32(), + t if t.is_float() => Value::null_float(), + t if t.is_double() => Value::null_double(), + t if t.is_text() => Value::null_text(), + t if t.is_bytes() => Value::null_bytes(), #[cfg(feature = "bigdecimal")] - t if t.is_real() => Value::Numeric(None), - t if t.is_datetime() => Value::DateTime(None), - t if t.is_time() => Value::Time(None), - t if t.is_date() => Value::Date(None), - t if t.is_json() => Value::Json(None), + t if t.is_real() => Value::null_numeric(), + t if t.is_datetime() => Value::null_datetime(), + t if t.is_time() => Value::null_time(), + t if t.is_date() => Value::null_date(), + t if t.is_json() => Value::null_json(), typ => { let msg = format!("Value of type {typ:?} is not supported with the current configuration"); diff --git a/quaint/src/connector/postgres.rs b/quaint/src/connector/postgres.rs index c35208f84199..dadc39faea2a 100644 --- a/quaint/src/connector/postgres.rs +++ b/quaint/src/connector/postgres.rs @@ -1178,7 +1178,7 @@ mod tests { let result_set = client.query_raw("SHOW search_path", &[]).await.unwrap(); let row = result_set.first().unwrap(); - row[0].to_string() + row[0].typed.to_string() } // Safe @@ -1230,7 +1230,7 @@ mod tests { let result_set = client.query_raw("SHOW search_path", &[]).await.unwrap(); let row = result_set.first().unwrap(); - row[0].to_string() + row[0].typed.to_string() } // Safe @@ -1281,7 +1281,7 @@ mod tests { let result_set = client.query_raw("SHOW search_path", &[]).await.unwrap(); let row = result_set.first().unwrap(); - row[0].to_string() + row[0].typed.to_string() } // Safe @@ -1332,7 +1332,7 @@ mod tests { let result_set = client.query_raw("SHOW search_path", &[]).await.unwrap(); let row = result_set.first().unwrap(); - row[0].to_string() + row[0].typed.to_string() } // Safe @@ -1383,7 +1383,7 @@ mod tests { let result_set = client.query_raw("SHOW search_path", &[]).await.unwrap(); let row = result_set.first().unwrap(); - row[0].to_string() + row[0].typed.to_string() } // Safe @@ -1480,9 +1480,7 @@ mod tests { let url = Url::parse(&CONN_STR).unwrap(); let conn = Quaint::new(url.as_str()).await.unwrap(); - let res = conn - .query_raw("SELECT $1", &[Value::integer(1), Value::integer(2)]) - .await; + let res = conn.query_raw("SELECT $1", &[Value::int32(1), Value::int32(2)]).await; assert!(res.is_err()); diff --git a/quaint/src/connector/postgres/conversion.rs b/quaint/src/connector/postgres/conversion.rs index f321e1829529..0ab4413f792c 100644 --- a/quaint/src/connector/postgres/conversion.rs +++ b/quaint/src/connector/postgres/conversion.rs @@ -2,7 +2,7 @@ mod decimal; use crate::{ - ast::Value, + ast::{Value, ValueType}, connector::queryable::{GetRow, ToColumnNames}, error::{Error, ErrorKind}, }; @@ -38,27 +38,27 @@ pub(crate) fn params_to_types(params: &[Value<'_>]) -> Vec { return PostgresType::UNKNOWN; } - match p { - Value::Int32(_) => PostgresType::INT4, - Value::Int64(_) => PostgresType::INT8, - Value::Float(_) => PostgresType::FLOAT4, - Value::Double(_) => PostgresType::FLOAT8, - Value::Text(_) => PostgresType::TEXT, + match &p.typed { + ValueType::Int32(_) => PostgresType::INT4, + ValueType::Int64(_) => PostgresType::INT8, + ValueType::Float(_) => PostgresType::FLOAT4, + ValueType::Double(_) => PostgresType::FLOAT8, + ValueType::Text(_) => PostgresType::TEXT, // Enums are user-defined types, we can't statically infer them, so we let PG infer it - Value::Enum(_, _) | Value::EnumArray(_, _) => PostgresType::UNKNOWN, - Value::Bytes(_) => PostgresType::BYTEA, - Value::Boolean(_) => PostgresType::BOOL, - Value::Char(_) => PostgresType::CHAR, + ValueType::Enum(_, _) | ValueType::EnumArray(_, _) => PostgresType::UNKNOWN, + ValueType::Bytes(_) => PostgresType::BYTEA, + ValueType::Boolean(_) => PostgresType::BOOL, + ValueType::Char(_) => PostgresType::CHAR, #[cfg(feature = "bigdecimal")] - Value::Numeric(_) => PostgresType::NUMERIC, - Value::Json(_) => PostgresType::JSONB, - Value::Xml(_) => PostgresType::XML, + ValueType::Numeric(_) => PostgresType::NUMERIC, + ValueType::Json(_) => PostgresType::JSONB, + ValueType::Xml(_) => PostgresType::XML, #[cfg(feature = "uuid")] - Value::Uuid(_) => PostgresType::UUID, - Value::DateTime(_) => PostgresType::TIMESTAMPTZ, - Value::Date(_) => PostgresType::TIMESTAMP, - Value::Time(_) => PostgresType::TIME, - Value::Array(ref arr) => { + ValueType::Uuid(_) => PostgresType::UUID, + ValueType::DateTime(_) => PostgresType::TIMESTAMPTZ, + ValueType::Date(_) => PostgresType::TIMESTAMP, + ValueType::Time(_) => PostgresType::TIME, + ValueType::Array(ref arr) => { let arr = arr.as_ref().unwrap(); // If the array is empty, we can't infer the type so we let PG infer it @@ -71,33 +71,33 @@ pub(crate) fn params_to_types(params: &[Value<'_>]) -> Vec { // If the array does not contain the same types of values, we let PG infer the type if arr .iter() - .any(|val| std::mem::discriminant(first) != std::mem::discriminant(val)) + .any(|val| std::mem::discriminant(&first.typed) != std::mem::discriminant(&val.typed)) { return PostgresType::UNKNOWN; } - match first { - Value::Int32(_) => PostgresType::INT4_ARRAY, - Value::Int64(_) => PostgresType::INT8_ARRAY, - Value::Float(_) => PostgresType::FLOAT4_ARRAY, - Value::Double(_) => PostgresType::FLOAT8_ARRAY, - Value::Text(_) => PostgresType::TEXT_ARRAY, + match first.typed { + ValueType::Int32(_) => PostgresType::INT4_ARRAY, + ValueType::Int64(_) => PostgresType::INT8_ARRAY, + ValueType::Float(_) => PostgresType::FLOAT4_ARRAY, + ValueType::Double(_) => PostgresType::FLOAT8_ARRAY, + ValueType::Text(_) => PostgresType::TEXT_ARRAY, // Enums are special types, we can't statically infer them, so we let PG infer it - Value::Enum(_, _) | Value::EnumArray(_, _) => PostgresType::UNKNOWN, - Value::Bytes(_) => PostgresType::BYTEA_ARRAY, - Value::Boolean(_) => PostgresType::BOOL_ARRAY, - Value::Char(_) => PostgresType::CHAR_ARRAY, + ValueType::Enum(_, _) | ValueType::EnumArray(_, _) => PostgresType::UNKNOWN, + ValueType::Bytes(_) => PostgresType::BYTEA_ARRAY, + ValueType::Boolean(_) => PostgresType::BOOL_ARRAY, + ValueType::Char(_) => PostgresType::CHAR_ARRAY, #[cfg(feature = "bigdecimal")] - Value::Numeric(_) => PostgresType::NUMERIC_ARRAY, - Value::Json(_) => PostgresType::JSONB_ARRAY, - Value::Xml(_) => PostgresType::XML_ARRAY, + ValueType::Numeric(_) => PostgresType::NUMERIC_ARRAY, + ValueType::Json(_) => PostgresType::JSONB_ARRAY, + ValueType::Xml(_) => PostgresType::XML_ARRAY, #[cfg(feature = "uuid")] - Value::Uuid(_) => PostgresType::UUID_ARRAY, - Value::DateTime(_) => PostgresType::TIMESTAMPTZ_ARRAY, - Value::Date(_) => PostgresType::TIMESTAMP_ARRAY, - Value::Time(_) => PostgresType::TIME_ARRAY, + ValueType::Uuid(_) => PostgresType::UUID_ARRAY, + ValueType::DateTime(_) => PostgresType::TIMESTAMPTZ_ARRAY, + ValueType::Date(_) => PostgresType::TIMESTAMP_ARRAY, + ValueType::Time(_) => PostgresType::TIME_ARRAY, // In the case of nested arrays, we let PG infer the type - Value::Array(_) => PostgresType::UNKNOWN, + ValueType::Array(_) => PostgresType::UNKNOWN, } } } @@ -171,63 +171,63 @@ impl GetRow for PostgresRow { fn get_result_row(&self) -> crate::Result>> { fn convert(row: &PostgresRow, i: usize) -> crate::Result> { let result = match *row.columns()[i].type_() { - PostgresType::BOOL => Value::Boolean(row.try_get(i)?), + PostgresType::BOOL => ValueType::Boolean(row.try_get(i)?).into_value(), PostgresType::INT2 => match row.try_get(i)? { Some(val) => { let val: i16 = val; Value::int32(val) } - None => Value::Int32(None), + None => Value::null_int32(), }, PostgresType::INT4 => match row.try_get(i)? { Some(val) => { let val: i32 = val; Value::int32(val) } - None => Value::Int32(None), + None => Value::null_int32(), }, PostgresType::INT8 => match row.try_get(i)? { Some(val) => { let val: i64 = val; Value::int64(val) } - None => Value::Int64(None), + None => Value::null_int64(), }, PostgresType::FLOAT4 => match row.try_get(i)? { Some(val) => { let val: f32 = val; Value::float(val) } - None => Value::Float(None), + None => Value::null_float(), }, PostgresType::FLOAT8 => match row.try_get(i)? { Some(val) => { let val: f64 = val; Value::double(val) } - None => Value::Double(None), + None => Value::null_double(), }, PostgresType::BYTEA => match row.try_get(i)? { Some(val) => { let val: &[u8] = val; Value::bytes(val.to_owned()) } - None => Value::Bytes(None), + None => Value::null_bytes(), }, PostgresType::BYTEA_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec>> = val; - let byteas = val.into_iter().map(|b| Value::Bytes(b.map(Into::into))); + let byteas = val.into_iter().map(|b| ValueType::Bytes(b.map(Into::into))); Value::array(byteas) } - None => Value::Array(None), + None => Value::null_array(), }, #[cfg(feature = "bigdecimal")] PostgresType::NUMERIC => { let dw: Option = row.try_get(i)?; - Value::Numeric(dw.map(|dw| dw.0)) + ValueType::Numeric(dw.map(|dw| dw.0)).into_value() } #[cfg(feature = "bigdecimal")] PostgresType::MONEY => match row.try_get(i)? { @@ -235,7 +235,7 @@ impl GetRow for PostgresRow { let val: NaiveMoney = val; Value::numeric(val.0) } - None => Value::Numeric(None), + None => Value::null_numeric(), }, PostgresType::TIMESTAMP => match row.try_get(i)? { Some(val) => { @@ -243,29 +243,29 @@ impl GetRow for PostgresRow { let dt = DateTime::::from_utc(ts, Utc); Value::datetime(dt) } - None => Value::DateTime(None), + None => Value::null_datetime(), }, PostgresType::TIMESTAMPTZ => match row.try_get(i)? { Some(val) => { let ts: DateTime = val; Value::datetime(ts) } - None => Value::DateTime(None), + None => Value::null_datetime(), }, PostgresType::DATE => match row.try_get(i)? { Some(val) => Value::date(val), - None => Value::Date(None), + None => Value::null_date(), }, PostgresType::TIME => match row.try_get(i)? { Some(val) => Value::time(val), - None => Value::Time(None), + None => Value::null_time(), }, PostgresType::TIMETZ => match row.try_get(i)? { Some(val) => { let time: TimeTz = val; Value::time(time.0) } - None => Value::Time(None), + None => Value::null_time(), }, #[cfg(feature = "uuid")] PostgresType::UUID => match row.try_get(i)? { @@ -273,72 +273,72 @@ impl GetRow for PostgresRow { let val: Uuid = val; Value::uuid(val) } - None => Value::Uuid(None), + None => ValueType::Uuid(None).into_value(), }, #[cfg(feature = "uuid")] PostgresType::UUID_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let val = val.into_iter().map(Value::Uuid); + let val = val.into_iter().map(ValueType::Uuid); Value::array(val) } - None => Value::Array(None), + None => Value::null_array(), }, - PostgresType::JSON | PostgresType::JSONB => Value::Json(row.try_get(i)?), + PostgresType::JSON | PostgresType::JSONB => ValueType::Json(row.try_get(i)?).into_value(), PostgresType::INT2_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let ints = val.into_iter().map(|i| Value::Int32(i.map(|i| i as i32))); + let ints = val.into_iter().map(|i| ValueType::Int32(i.map(|i| i as i32))); Value::array(ints) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::INT4_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let ints = val.into_iter().map(Value::Int32); + let ints = val.into_iter().map(ValueType::Int32); Value::array(ints) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::INT8_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let ints = val.into_iter().map(Value::Int64); + let ints = val.into_iter().map(ValueType::Int64); Value::array(ints) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::FLOAT4_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let floats = val.into_iter().map(Value::Float); + let floats = val.into_iter().map(ValueType::Float); Value::array(floats) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::FLOAT8_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let floats = val.into_iter().map(Value::Double); + let floats = val.into_iter().map(ValueType::Double); Value::array(floats) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::BOOL_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let bools = val.into_iter().map(Value::Boolean); + let bools = val.into_iter().map(ValueType::Boolean); Value::array(bools) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::TIMESTAMP_ARRAY => match row.try_get(i)? { Some(val) => { @@ -346,11 +346,11 @@ impl GetRow for PostgresRow { let dates = val .into_iter() - .map(|dt| Value::DateTime(dt.map(|dt| DateTime::::from_utc(dt, Utc)))); + .map(|dt| ValueType::DateTime(dt.map(|dt| DateTime::::from_utc(dt, Utc)))); Value::array(dates) } - None => Value::Array(None), + None => Value::null_array(), }, #[cfg(feature = "bigdecimal")] PostgresType::NUMERIC_ARRAY => match row.try_get(i)? { @@ -359,11 +359,11 @@ impl GetRow for PostgresRow { let decimals = val .into_iter() - .map(|dec| Value::Numeric(dec.map(|dec| dec.0.to_string().parse().unwrap()))); + .map(|dec| ValueType::Numeric(dec.map(|dec| dec.0.to_string().parse().unwrap()))); Value::array(decimals) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::TEXT_ARRAY | PostgresType::NAME_ARRAY | PostgresType::VARCHAR_ARRAY => { match row.try_get(i)? { @@ -372,142 +372,140 @@ impl GetRow for PostgresRow { Value::array(strings.into_iter().map(|s| s.map(|s| s.to_string()))) } - None => Value::Array(None), + None => Value::null_array(), } } #[cfg(feature = "bigdecimal")] PostgresType::MONEY_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let nums = val.into_iter().map(|num| Value::Numeric(num.map(|num| num.0))); + let nums = val.into_iter().map(|num| ValueType::Numeric(num.map(|num| num.0))); Value::array(nums) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::OID_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let nums = val.into_iter().map(|oid| Value::Int64(oid.map(|oid| oid as i64))); + let nums = val.into_iter().map(|oid| ValueType::Int64(oid.map(|oid| oid as i64))); Value::array(nums) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::TIMESTAMPTZ_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec>> = val; - let dates = val.into_iter().map(Value::DateTime); + let dates = val.into_iter().map(ValueType::DateTime); Value::array(dates) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::DATE_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let dates = val.into_iter().map(Value::Date); + let dates = val.into_iter().map(ValueType::Date); Value::array(dates) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::TIME_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let times = val.into_iter().map(Value::Time); + let times = val.into_iter().map(ValueType::Time); Value::array(times) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::TIMETZ_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let timetzs = val.into_iter().map(|time| Value::Time(time.map(|time| time.0))); + let timetzs = val.into_iter().map(|time| ValueType::Time(time.map(|time| time.0))); Value::array(timetzs) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::JSON_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let jsons = val.into_iter().map(Value::Json); + let jsons = val.into_iter().map(ValueType::Json); Value::array(jsons) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::JSONB_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let jsons = val.into_iter().map(Value::Json); + let jsons = val.into_iter().map(ValueType::Json); Value::array(jsons) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::OID => match row.try_get(i)? { Some(val) => { let val: u32 = val; Value::int64(val) } - None => Value::Int64(None), + None => Value::null_int64(), }, PostgresType::CHAR => match row.try_get(i)? { Some(val) => { let val: i8 = val; Value::character((val as u8) as char) } - None => Value::Char(None), + None => Value::null_character(), }, PostgresType::INET | PostgresType::CIDR => match row.try_get(i)? { Some(val) => { let val: std::net::IpAddr = val; Value::text(val.to_string()) } - None => Value::Text(None), + None => Value::null_text(), }, PostgresType::INET_ARRAY | PostgresType::CIDR_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; let addrs = val .into_iter() - .map(|ip| Value::Text(ip.map(|ip| ip.to_string().into()))); + .map(|ip| ValueType::Text(ip.map(|ip| ip.to_string().into()))); Value::array(addrs) } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::BIT | PostgresType::VARBIT => match row.try_get(i)? { Some(val) => { let val: BitVec = val; Value::text(bits_to_string(&val)?) } - None => Value::Text(None), + None => Value::null_text(), }, PostgresType::BIT_ARRAY | PostgresType::VARBIT_ARRAY => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let stringified = val - .into_iter() + val.into_iter() .map(|bits| match bits { - Some(bits) => bits_to_string(&bits).map(Value::text), - None => Ok(Value::Text(None)), + Some(bits) => bits_to_string(&bits).map(|s| ValueType::Text(Some(s.into()))), + None => Ok(ValueType::Text(None)), }) - .collect::>>()?; - - Value::array(stringified) + .collect::>>() + .map(Value::array)? } - None => Value::Array(None), + None => Value::null_array(), }, PostgresType::XML => match row.try_get(i)? { Some(val) => { let val: XmlString = val; Value::xml(val.0) } - None => Value::Xml(None), + None => Value::null_xml(), }, PostgresType::XML_ARRAY => match row.try_get(i)? { Some(val) => { @@ -516,7 +514,7 @@ impl GetRow for PostgresRow { Value::array(xmls) } - None => Value::Array(None), + None => Value::null_array(), }, ref x => match x.kind() { Kind::Enum => match row.try_get(i)? { @@ -525,26 +523,28 @@ impl GetRow for PostgresRow { Value::enum_variant(val.value) } - None => Value::Enum(None, None), + None => Value::null_enum(), }, Kind::Array(inner) => match inner.kind() { Kind::Enum => match row.try_get(i)? { Some(val) => { let val: Vec> = val; - let variants = val.into_iter().map(|x| Value::Enum(x.map(|x| x.value.into()), None)); + let variants = val + .into_iter() + .map(|x| ValueType::Enum(x.map(|x| x.value.into()), None)); Ok(Value::array(variants)) } - None => Ok(Value::Array(None)), + None => Ok(Value::null_array()), }, _ => match row.try_get(i) { Ok(Some(val)) => { let val: Vec> = val; - let strings = val.into_iter().map(|str| Value::Text(str.map(Into::into))); + let strings = val.into_iter().map(|str| ValueType::Text(str.map(Into::into))); Ok(Value::array(strings)) } - Ok(None) => Ok(Value::Array(None)), + Ok(None) => Ok(Value::null_array()), Err(err) => { if err.source().map(|err| err.is::()).unwrap_or(false) { let kind = ErrorKind::UnsupportedColumnType { @@ -564,7 +564,7 @@ impl GetRow for PostgresRow { Ok(Value::text(val)) } - Ok(None) => Ok(Value::Text(None)), + Ok(None) => Ok(Value::from(ValueType::Text(None))), Err(err) => { if err.source().map(|err| err.is::()).unwrap_or(false) { let kind = ErrorKind::UnsupportedColumnType { @@ -606,8 +606,8 @@ impl<'a> ToSql for Value<'a> { ty: &PostgresType, out: &mut BytesMut, ) -> Result> { - let res = match (self, ty) { - (Value::Int32(integer), &PostgresType::INT2) => match integer { + let res = match (&self.typed, ty) { + (ValueType::Int32(integer), &PostgresType::INT2) => match integer { Some(i) => { let integer = i16::try_from(*i).map_err(|_| { let kind = ErrorKind::conversion(format!( @@ -621,9 +621,9 @@ impl<'a> ToSql for Value<'a> { } _ => None, }, - (Value::Int32(integer), &PostgresType::INT4) => integer.map(|integer| integer.to_sql(ty, out)), - (Value::Int32(integer), &PostgresType::INT8) => integer.map(|integer| (integer as i64).to_sql(ty, out)), - (Value::Int64(integer), &PostgresType::INT2) => match integer { + (ValueType::Int32(integer), &PostgresType::INT4) => integer.map(|integer| integer.to_sql(ty, out)), + (ValueType::Int32(integer), &PostgresType::INT8) => integer.map(|integer| (integer as i64).to_sql(ty, out)), + (ValueType::Int64(integer), &PostgresType::INT2) => match integer { Some(i) => { let integer = i16::try_from(*i).map_err(|_| { let kind = ErrorKind::conversion(format!( @@ -637,7 +637,7 @@ impl<'a> ToSql for Value<'a> { } _ => None, }, - (Value::Int64(integer), &PostgresType::INT4) => match integer { + (ValueType::Int64(integer), &PostgresType::INT4) => match integer { Some(i) => { let integer = i32::try_from(*i).map_err(|_| { let kind = ErrorKind::conversion(format!( @@ -651,20 +651,24 @@ impl<'a> ToSql for Value<'a> { } _ => None, }, - (Value::Int64(integer), &PostgresType::INT8) => integer.map(|integer| integer.to_sql(ty, out)), + (ValueType::Int64(integer), &PostgresType::INT8) => integer.map(|integer| integer.to_sql(ty, out)), #[cfg(feature = "bigdecimal")] - (Value::Int32(integer), &PostgresType::NUMERIC) => integer + (ValueType::Int32(integer), &PostgresType::NUMERIC) => integer .map(|integer| BigDecimal::from_i32(integer).unwrap()) .map(DecimalWrapper) .map(|dw| dw.to_sql(ty, out)), #[cfg(feature = "bigdecimal")] - (Value::Int64(integer), &PostgresType::NUMERIC) => integer + (ValueType::Int64(integer), &PostgresType::NUMERIC) => integer .map(|integer| BigDecimal::from_i64(integer).unwrap()) .map(DecimalWrapper) .map(|dw| dw.to_sql(ty, out)), - (Value::Int32(integer), &PostgresType::TEXT) => integer.map(|integer| format!("{integer}").to_sql(ty, out)), - (Value::Int64(integer), &PostgresType::TEXT) => integer.map(|integer| format!("{integer}").to_sql(ty, out)), - (Value::Int32(integer), &PostgresType::OID) => match integer { + (ValueType::Int32(integer), &PostgresType::TEXT) => { + integer.map(|integer| format!("{integer}").to_sql(ty, out)) + } + (ValueType::Int64(integer), &PostgresType::TEXT) => { + integer.map(|integer| format!("{integer}").to_sql(ty, out)) + } + (ValueType::Int32(integer), &PostgresType::OID) => match integer { Some(i) => { let integer = u32::try_from(*i).map_err(|_| { let kind = ErrorKind::conversion(format!( @@ -678,7 +682,7 @@ impl<'a> ToSql for Value<'a> { } _ => None, }, - (Value::Int64(integer), &PostgresType::OID) => match integer { + (ValueType::Int64(integer), &PostgresType::OID) => match integer { Some(i) => { let integer = u32::try_from(*i).map_err(|_| { let kind = ErrorKind::conversion(format!( @@ -692,43 +696,43 @@ impl<'a> ToSql for Value<'a> { } _ => None, }, - (Value::Int32(integer), _) => integer.map(|integer| integer.to_sql(ty, out)), - (Value::Int64(integer), _) => integer.map(|integer| integer.to_sql(ty, out)), - (Value::Float(float), &PostgresType::FLOAT8) => float.map(|float| (float as f64).to_sql(ty, out)), + (ValueType::Int32(integer), _) => integer.map(|integer| integer.to_sql(ty, out)), + (ValueType::Int64(integer), _) => integer.map(|integer| integer.to_sql(ty, out)), + (ValueType::Float(float), &PostgresType::FLOAT8) => float.map(|float| (float as f64).to_sql(ty, out)), #[cfg(feature = "bigdecimal")] - (Value::Float(float), &PostgresType::NUMERIC) => float + (ValueType::Float(float), &PostgresType::NUMERIC) => float .map(|float| BigDecimal::from_f32(float).unwrap()) .map(DecimalWrapper) .map(|dw| dw.to_sql(ty, out)), - (Value::Float(float), _) => float.map(|float| float.to_sql(ty, out)), - (Value::Double(double), &PostgresType::FLOAT4) => double.map(|double| (double as f32).to_sql(ty, out)), + (ValueType::Float(float), _) => float.map(|float| float.to_sql(ty, out)), + (ValueType::Double(double), &PostgresType::FLOAT4) => double.map(|double| (double as f32).to_sql(ty, out)), #[cfg(feature = "bigdecimal")] - (Value::Double(double), &PostgresType::NUMERIC) => double + (ValueType::Double(double), &PostgresType::NUMERIC) => double .map(|double| BigDecimal::from_f64(double).unwrap()) .map(DecimalWrapper) .map(|dw| dw.to_sql(ty, out)), - (Value::Double(double), _) => double.map(|double| double.to_sql(ty, out)), + (ValueType::Double(double), _) => double.map(|double| double.to_sql(ty, out)), #[cfg(feature = "bigdecimal")] - (Value::Numeric(decimal), &PostgresType::FLOAT4) => decimal.as_ref().map(|decimal| { + (ValueType::Numeric(decimal), &PostgresType::FLOAT4) => decimal.as_ref().map(|decimal| { let f = decimal.to_string().parse::().expect("decimal to f32 conversion"); f.to_sql(ty, out) }), #[cfg(feature = "bigdecimal")] - (Value::Numeric(decimal), &PostgresType::FLOAT8) => decimal.as_ref().map(|decimal| { + (ValueType::Numeric(decimal), &PostgresType::FLOAT8) => decimal.as_ref().map(|decimal| { let f = decimal.to_string().parse::().expect("decimal to f64 conversion"); f.to_sql(ty, out) }), #[cfg(feature = "bigdecimal")] - (Value::Array(values), &PostgresType::FLOAT4_ARRAY) => values.as_ref().map(|values| { + (ValueType::Array(values), &PostgresType::FLOAT4_ARRAY) => values.as_ref().map(|values| { let mut floats = Vec::with_capacity(values.len()); for value in values.iter() { - let float = match value { - Value::Numeric(n) => n.as_ref().and_then(|n| n.to_string().parse::().ok()), - Value::Int64(n) => n.map(|n| n as f32), - Value::Float(f) => *f, - Value::Double(d) => d.map(|d| d as f32), - v if v.is_null() => None, + let float = match &value.typed { + ValueType::Numeric(n) => n.as_ref().and_then(|n| n.to_string().parse::().ok()), + ValueType::Int64(n) => n.map(|n| n as f32), + ValueType::Float(f) => *f, + ValueType::Double(d) => d.map(|d| d as f32), + _ if value.is_null() => None, v => { let kind = ErrorKind::conversion(format!( "Couldn't add value of type `{v:?}` into a float array." @@ -744,15 +748,15 @@ impl<'a> ToSql for Value<'a> { floats.to_sql(ty, out) }), #[cfg(feature = "bigdecimal")] - (Value::Array(values), &PostgresType::FLOAT8_ARRAY) => values.as_ref().map(|values| { + (ValueType::Array(values), &PostgresType::FLOAT8_ARRAY) => values.as_ref().map(|values| { let mut floats = Vec::with_capacity(values.len()); for value in values.iter() { - let float = match value { - Value::Numeric(n) => n.as_ref().and_then(|n| n.to_string().parse::().ok()), - Value::Int64(n) => n.map(|n| n as f64), - Value::Float(f) => f.map(|f| f as f64), - Value::Double(d) => *d, + let float = match &value.typed { + ValueType::Numeric(n) => n.as_ref().and_then(|n| n.to_string().parse::().ok()), + ValueType::Int64(n) => n.map(|n| n as f64), + ValueType::Float(f) => f.map(|f| f as f64), + ValueType::Double(d) => *d, v if v.is_null() => None, v => { let kind = ErrorKind::conversion(format!( @@ -769,7 +773,7 @@ impl<'a> ToSql for Value<'a> { floats.to_sql(ty, out) }), #[cfg(feature = "bigdecimal")] - (Value::Numeric(decimal), &PostgresType::MONEY) => decimal.as_ref().map(|decimal| { + (ValueType::Numeric(decimal), &PostgresType::MONEY) => decimal.as_ref().map(|decimal| { let decimal = (decimal * BigInt::from_i32(100).unwrap()).round(0); let i = decimal.to_i64().ok_or_else(|| { @@ -780,20 +784,20 @@ impl<'a> ToSql for Value<'a> { i.to_sql(ty, out) }), #[cfg(feature = "bigdecimal")] - (Value::Numeric(decimal), &PostgresType::NUMERIC) => decimal + (ValueType::Numeric(decimal), &PostgresType::NUMERIC) => decimal .as_ref() .map(|decimal| DecimalWrapper(decimal.clone()).to_sql(ty, out)), #[cfg(feature = "bigdecimal")] - (Value::Numeric(float), _) => float + (ValueType::Numeric(float), _) => float .as_ref() .map(|float| DecimalWrapper(float.clone()).to_sql(ty, out)), #[cfg(feature = "uuid")] - (Value::Text(string), &PostgresType::UUID) => string.as_ref().map(|string| { + (ValueType::Text(string), &PostgresType::UUID) => string.as_ref().map(|string| { let parsed_uuid: Uuid = string.parse()?; parsed_uuid.to_sql(ty, out) }), #[cfg(feature = "uuid")] - (Value::Array(values), &PostgresType::UUID_ARRAY) => values.as_ref().map(|values| { + (ValueType::Array(values), &PostgresType::UUID_ARRAY) => values.as_ref().map(|values| { let parsed_uuid: Vec> = values .iter() .map(>::try_from) @@ -801,85 +805,83 @@ impl<'a> ToSql for Value<'a> { parsed_uuid.to_sql(ty, out) }), - (Value::Text(string), &PostgresType::INET) | (Value::Text(string), &PostgresType::CIDR) => { + (ValueType::Text(string), &PostgresType::INET) | (ValueType::Text(string), &PostgresType::CIDR) => { string.as_ref().map(|string| { let parsed_ip_addr: std::net::IpAddr = string.parse()?; parsed_ip_addr.to_sql(ty, out) }) } - (Value::Array(values), &PostgresType::INET_ARRAY) | (Value::Array(values), &PostgresType::CIDR_ARRAY) => { - values.as_ref().map(|values| { - let parsed_ip_addr: Vec> = values - .iter() - .map(>::try_from) - .collect::>()?; + (ValueType::Array(values), &PostgresType::INET_ARRAY) + | (ValueType::Array(values), &PostgresType::CIDR_ARRAY) => values.as_ref().map(|values| { + let parsed_ip_addr: Vec> = values + .iter() + .map(>::try_from) + .collect::>()?; - parsed_ip_addr.to_sql(ty, out) - }) - } - (Value::Text(string), &PostgresType::JSON) | (Value::Text(string), &PostgresType::JSONB) => string + parsed_ip_addr.to_sql(ty, out) + }), + (ValueType::Text(string), &PostgresType::JSON) | (ValueType::Text(string), &PostgresType::JSONB) => string .as_ref() .map(|string| serde_json::from_str::(string)?.to_sql(ty, out)), - (Value::Text(string), &PostgresType::BIT) | (Value::Text(string), &PostgresType::VARBIT) => { + (ValueType::Text(string), &PostgresType::BIT) | (ValueType::Text(string), &PostgresType::VARBIT) => { string.as_ref().map(|string| { let bits: BitVec = string_to_bits(string)?; bits.to_sql(ty, out) }) } - (Value::Text(string), _) => string.as_ref().map(|ref string| string.to_sql(ty, out)), - (Value::Array(values), &PostgresType::BIT_ARRAY) | (Value::Array(values), &PostgresType::VARBIT_ARRAY) => { - values.as_ref().map(|values| { - let bitvecs: Vec> = values - .iter() - .map(>::try_from) - .collect::>>()?; + (ValueType::Text(string), _) => string.as_ref().map(|ref string| string.to_sql(ty, out)), + (ValueType::Array(values), &PostgresType::BIT_ARRAY) + | (ValueType::Array(values), &PostgresType::VARBIT_ARRAY) => values.as_ref().map(|values| { + let bitvecs: Vec> = values + .iter() + .map(|value| value.try_into()) + .collect::>>()?; - bitvecs.to_sql(ty, out) - }) - } - (Value::Bytes(bytes), _) => bytes.as_ref().map(|bytes| bytes.as_ref().to_sql(ty, out)), - (Value::Enum(string, _), _) => string.as_ref().map(|string| { + bitvecs.to_sql(ty, out) + }), + (ValueType::Bytes(bytes), _) => bytes.as_ref().map(|bytes| bytes.as_ref().to_sql(ty, out)), + (ValueType::Enum(string, _), _) => string.as_ref().map(|string| { out.extend_from_slice(string.as_bytes()); Ok(IsNull::No) }), - (Value::Boolean(boo), _) => boo.map(|boo| boo.to_sql(ty, out)), - (Value::Char(c), _) => c.map(|c| (c as i8).to_sql(ty, out)), - (Value::Array(vec), typ) if matches!(typ.kind(), Kind::Array(_)) => { + (ValueType::Boolean(boo), _) => boo.map(|boo| boo.to_sql(ty, out)), + (ValueType::Char(c), _) => c.map(|c| (c as i8).to_sql(ty, out)), + (ValueType::Array(vec), typ) if matches!(typ.kind(), Kind::Array(_)) => { vec.as_ref().map(|vec| vec.to_sql(ty, out)) } - (Value::EnumArray(variants, _), typ) if matches!(typ.kind(), Kind::Array(_)) => variants + (ValueType::EnumArray(variants, _), typ) if matches!(typ.kind(), Kind::Array(_)) => variants .as_ref() .map(|vec| vec.iter().map(|val| val.as_ref()).collect::>().to_sql(ty, out)), - (Value::EnumArray(variants, _), typ) => { + (ValueType::EnumArray(variants, _), typ) => { let kind = ErrorKind::conversion(format!( "Couldn't serialize value `{variants:?}` into a `{typ}`. Value is a list but `{typ}` is not." )); return Err(Error::builder(kind).build().into()); } - (Value::Array(vec), typ) => { + (ValueType::Array(vec), typ) => { let kind = ErrorKind::conversion(format!( "Couldn't serialize value `{vec:?}` into a `{typ}`. Value is a list but `{typ}` is not." )); return Err(Error::builder(kind).build().into()); } - (Value::Json(value), _) => value.as_ref().map(|value| value.to_sql(ty, out)), - (Value::Xml(value), _) => value.as_ref().map(|value| value.to_sql(ty, out)), + (ValueType::Json(value), _) => value.as_ref().map(|value| value.to_sql(ty, out)), + (ValueType::Xml(value), _) => value.as_ref().map(|value| value.to_sql(ty, out)), #[cfg(feature = "uuid")] - (Value::Uuid(value), _) => value.map(|value| value.to_sql(ty, out)), - (Value::DateTime(value), &PostgresType::DATE) => value.map(|value| value.date_naive().to_sql(ty, out)), - (Value::Date(value), _) => value.map(|value| value.to_sql(ty, out)), - (Value::Time(value), _) => value.map(|value| value.to_sql(ty, out)), - (Value::DateTime(value), &PostgresType::TIME) => value.map(|value| value.time().to_sql(ty, out)), - (Value::DateTime(value), &PostgresType::TIMETZ) => value.map(|value| { + (ValueType::Uuid(value), _) => value.map(|value| value.to_sql(ty, out)), + (ValueType::DateTime(value), &PostgresType::DATE) => value.map(|value| value.date_naive().to_sql(ty, out)), + (ValueType::Date(value), _) => value.map(|value| value.to_sql(ty, out)), + (ValueType::Time(value), _) => value.map(|value| value.to_sql(ty, out)), + (ValueType::DateTime(value), &PostgresType::TIME) => value.map(|value| value.time().to_sql(ty, out)), + (ValueType::DateTime(value), &PostgresType::TIMETZ) => value.map(|value| { let result = value.time().to_sql(ty, out)?; // We assume UTC. see https://www.postgresql.org/docs/9.5/datatype-datetime.html out.extend_from_slice(&[0; 4]); Ok(result) }), - (Value::DateTime(value), _) => value.map(|value| value.naive_utc().to_sql(ty, out)), + (ValueType::DateTime(value), _) => value.map(|value| value.naive_utc().to_sql(ty, out)), }; match res { @@ -935,12 +937,18 @@ impl<'a> TryFrom<&Value<'a>> for Option { fn try_from(value: &Value<'a>) -> Result, Self::Error> { match value { - val @ Value::Text(Some(_)) => { + val @ Value { + typed: ValueType::Text(Some(_)), + .. + } => { let text = val.as_str().unwrap(); string_to_bits(text).map(Option::Some) } - val @ Value::Bytes(Some(_)) => { + val @ Value { + typed: ValueType::Bytes(Some(_)), + .. + } => { let text = val.as_str().unwrap(); string_to_bits(text).map(Option::Some) diff --git a/quaint/src/connector/postgres/error.rs b/quaint/src/connector/postgres/error.rs index 4f7bb23a5c85..dc8699875ea8 100644 --- a/quaint/src/connector/postgres/error.rs +++ b/quaint/src/connector/postgres/error.rs @@ -282,7 +282,7 @@ impl From for Error { }; builder.set_original_message(reason); - return builder.build(); + builder.build() } // sigh... // https://github.com/sfackler/rust-postgres/blob/0c84ed9f8201f4e5b4803199a24afa2c9f3723b2/tokio-postgres/src/connect_tls.rs#L37 "error performing TLS handshake: server does not support TLS" => { @@ -295,7 +295,7 @@ impl From for Error { }; builder.set_original_message(reason); - return builder.build(); + builder.build() } // double sigh _ => { let code = code.map(|c| c.to_string()); @@ -306,7 +306,7 @@ impl From for Error { }; builder.set_original_message(reason); - return builder.build(); + builder.build() } } } diff --git a/quaint/src/connector/sqlite/conversion.rs b/quaint/src/connector/sqlite/conversion.rs index dade118596c6..4f6dea515621 100644 --- a/quaint/src/connector/sqlite/conversion.rs +++ b/quaint/src/connector/sqlite/conversion.rs @@ -1,7 +1,7 @@ use std::convert::TryFrom; use crate::{ - ast::Value, + ast::{Value, ValueType}, connector::{ queryable::{GetRow, ToColumnNames}, TypeIdentifier, @@ -138,17 +138,17 @@ impl<'a> GetRow for SqliteRow<'a> { let pv = match self.get_ref_unwrap(i) { ValueRef::Null => match column { // NOTE: A value without decl_type would be Int32(None) - c if c.is_int32() | c.is_null() => Value::Int32(None), - c if c.is_int64() => Value::Int64(None), - c if c.is_text() => Value::Text(None), - c if c.is_bytes() => Value::Bytes(None), - c if c.is_float() => Value::Float(None), - c if c.is_double() => Value::Double(None), + c if c.is_int32() | c.is_null() => Value::null_int32(), + c if c.is_int64() => Value::null_int64(), + c if c.is_text() => Value::null_text(), + c if c.is_bytes() => Value::null_bytes(), + c if c.is_float() => Value::null_float(), + c if c.is_double() => Value::null_double(), #[cfg(feature = "bigdecimal")] - c if c.is_real() => Value::Numeric(None), - c if c.is_datetime() => Value::DateTime(None), - c if c.is_date() => Value::Date(None), - c if c.is_bool() => Value::Boolean(None), + c if c.is_real() => Value::null_numeric(), + c if c.is_datetime() => Value::null_datetime(), + c if c.is_date() => Value::null_date(), + c if c.is_bool() => Value::null_boolean(), c => match c.decl_type() { Some(n) => { let msg = format!("Value {n} not supported"); @@ -157,7 +157,7 @@ impl<'a> GetRow for SqliteRow<'a> { return Err(Error::builder(kind).build()); } // When we don't know what to do, the default value would be Int32(None) - None => Value::Int32(None), + None => Value::null_int32(), }, }, ValueRef::Integer(i) => { @@ -245,17 +245,17 @@ impl<'a> ToColumnNames for SqliteRows<'a> { impl<'a> ToSql for Value<'a> { fn to_sql(&self) -> Result { - let value = match self { - Value::Int32(integer) => integer.map(ToSqlOutput::from), - Value::Int64(integer) => integer.map(ToSqlOutput::from), - Value::Float(float) => float.map(|f| f as f64).map(ToSqlOutput::from), - Value::Double(double) => double.map(ToSqlOutput::from), - Value::Text(cow) => cow.as_ref().map(|cow| ToSqlOutput::from(cow.as_ref())), - Value::Enum(cow, _) => cow.as_ref().map(|cow| ToSqlOutput::from(cow.as_ref())), - Value::Boolean(boo) => boo.map(ToSqlOutput::from), - Value::Char(c) => c.map(|c| ToSqlOutput::from(c as u8)), - Value::Bytes(bytes) => bytes.as_ref().map(|bytes| ToSqlOutput::from(bytes.as_ref())), - Value::Array(_) | Value::EnumArray(_, _) => { + let value = match &self.typed { + ValueType::Int32(integer) => integer.map(ToSqlOutput::from), + ValueType::Int64(integer) => integer.map(ToSqlOutput::from), + ValueType::Float(float) => float.map(|f| f as f64).map(ToSqlOutput::from), + ValueType::Double(double) => double.map(ToSqlOutput::from), + ValueType::Text(cow) => cow.as_ref().map(|cow| ToSqlOutput::from(cow.as_ref())), + ValueType::Enum(cow, _) => cow.as_ref().map(|cow| ToSqlOutput::from(cow.as_ref())), + ValueType::Boolean(boo) => boo.map(ToSqlOutput::from), + ValueType::Char(c) => c.map(|c| ToSqlOutput::from(c as u8)), + ValueType::Bytes(bytes) => bytes.as_ref().map(|bytes| ToSqlOutput::from(bytes.as_ref())), + ValueType::Array(_) | ValueType::EnumArray(_, _) => { let msg = "Arrays are not supported in SQLite."; let kind = ErrorKind::conversion(msg); @@ -265,24 +265,24 @@ impl<'a> ToSql for Value<'a> { return Err(RusqlError::ToSqlConversionFailure(Box::new(builder.build()))); } #[cfg(feature = "bigdecimal")] - Value::Numeric(d) => d + ValueType::Numeric(d) => d .as_ref() .map(|d| ToSqlOutput::from(d.to_string().parse::().expect("BigDecimal is not a f64."))), - Value::Json(value) => value.as_ref().map(|value| { + ValueType::Json(value) => value.as_ref().map(|value| { let stringified = serde_json::to_string(value) .map_err(|err| RusqlError::ToSqlConversionFailure(Box::new(err))) .unwrap(); ToSqlOutput::from(stringified) }), - Value::Xml(cow) => cow.as_ref().map(|cow| ToSqlOutput::from(cow.as_ref())), + ValueType::Xml(cow) => cow.as_ref().map(|cow| ToSqlOutput::from(cow.as_ref())), #[cfg(feature = "uuid")] - Value::Uuid(value) => value.map(|value| ToSqlOutput::from(value.hyphenated().to_string())), - Value::DateTime(value) => value.map(|value| ToSqlOutput::from(value.timestamp_millis())), - Value::Date(date) => date + ValueType::Uuid(value) => value.map(|value| ToSqlOutput::from(value.hyphenated().to_string())), + ValueType::DateTime(value) => value.map(|value| ToSqlOutput::from(value.timestamp_millis())), + ValueType::Date(date) => date .and_then(|date| date.and_hms_opt(0, 0, 0)) .map(|dt| ToSqlOutput::from(dt.timestamp_millis())), - Value::Time(time) => time + ValueType::Time(time) => time .and_then(|time| chrono::NaiveDate::from_ymd_opt(1970, 1, 1).map(|d| (d, time))) .and_then(|(date, time)| { use chrono::Timelike; diff --git a/quaint/src/lib.rs b/quaint/src/lib.rs index 1fa817fddf55..5472c12885a4 100644 --- a/quaint/src/lib.rs +++ b/quaint/src/lib.rs @@ -133,6 +133,6 @@ pub mod single; mod tests; pub mod visitor; -pub use ast::Value; +pub use ast::{Value, ValueType}; pub type Result = std::result::Result; diff --git a/quaint/src/macros.rs b/quaint/src/macros.rs index 6289fe0bac23..cfb52bc0c6e1 100644 --- a/quaint/src/macros.rs +++ b/quaint/src/macros.rs @@ -88,21 +88,33 @@ macro_rules! val { macro_rules! value { ($target:ident: $kind:ty,$paramkind:ident,$that:expr) => { - impl<'a> From<$kind> for crate::ast::Value<'a> { + impl<'a> From<$kind> for crate::ast::ValueType<'a> { fn from(that: $kind) -> Self { let $target = that; - crate::ast::Value::$paramkind(Some($that)) + crate::ast::ValueType::$paramkind(Some($that)) } } - impl<'a> From> for crate::ast::Value<'a> { + impl<'a> From> for crate::ast::ValueType<'a> { fn from(that: Option<$kind>) -> Self { match that { - Some(val) => crate::ast::Value::from(val), - None => crate::ast::Value::$paramkind(None), + Some(val) => crate::ast::ValueType::from(val), + None => crate::ast::ValueType::$paramkind(None), } } } + + impl<'a> From<$kind> for crate::ast::Value<'a> { + fn from(that: $kind) -> Self { + crate::ast::Value::from(crate::ast::ValueType::from(that)) + } + } + + impl<'a> From> for crate::ast::Value<'a> { + fn from(that: Option<$kind>) -> Self { + crate::ast::Value::from(crate::ast::ValueType::from(that)) + } + } }; } diff --git a/quaint/src/serde.rs b/quaint/src/serde.rs index c88ff4ae520b..092ab344633d 100644 --- a/quaint/src/serde.rs +++ b/quaint/src/serde.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use crate::{ - ast::{EnumVariant, Value}, + ast::{EnumVariant, Value, ValueType}, connector::{ResultRow, ResultSet}, error::{Error, ErrorKind}, }; @@ -76,7 +76,7 @@ impl<'de> Deserializer<'de> for RowDeserializer { let kvs = columns.iter().enumerate().map(move |(v, k)| { // The unwrap is safe if `columns` is correct. let value = values.get_mut(v).unwrap(); - let taken_value = std::mem::replace(value, Value::Int64(None)); + let taken_value = std::mem::replace(value, Value::from(ValueType::Int64(None))); (k.as_str(), taken_value) }); @@ -115,69 +115,69 @@ impl<'de> Deserializer<'de> for ValueDeserializer<'de> { type Error = DeserializeError; fn deserialize_any>(self, visitor: V) -> Result { - match self.0 { - Value::Text(Some(s)) => visitor.visit_string(s.into_owned()), - Value::Text(None) => visitor.visit_none(), - Value::Bytes(Some(bytes)) => visitor.visit_bytes(bytes.as_ref()), - Value::Bytes(None) => visitor.visit_none(), - Value::Enum(Some(s), _) => visitor.visit_string(s.into_owned()), - Value::Enum(None, _) => visitor.visit_none(), - Value::EnumArray(Some(variants), _) => { + match self.0.typed { + ValueType::Text(Some(s)) => visitor.visit_string(s.into_owned()), + ValueType::Text(None) => visitor.visit_none(), + ValueType::Bytes(Some(bytes)) => visitor.visit_bytes(bytes.as_ref()), + ValueType::Bytes(None) => visitor.visit_none(), + ValueType::Enum(Some(s), _) => visitor.visit_string(s.into_owned()), + ValueType::Enum(None, _) => visitor.visit_none(), + ValueType::EnumArray(Some(variants), _) => { let deserializer = serde::de::value::SeqDeserializer::new(variants.into_iter()); visitor.visit_seq(deserializer) } - Value::EnumArray(None, _) => visitor.visit_none(), - Value::Int32(Some(i)) => visitor.visit_i32(i), - Value::Int32(None) => visitor.visit_none(), - Value::Int64(Some(i)) => visitor.visit_i64(i), - Value::Int64(None) => visitor.visit_none(), - Value::Boolean(Some(b)) => visitor.visit_bool(b), - Value::Boolean(None) => visitor.visit_none(), - Value::Char(Some(c)) => visitor.visit_char(c), - Value::Char(None) => visitor.visit_none(), - Value::Float(Some(num)) => visitor.visit_f64(num as f64), - Value::Float(None) => visitor.visit_none(), - Value::Double(Some(num)) => visitor.visit_f64(num), - Value::Double(None) => visitor.visit_none(), + ValueType::EnumArray(None, _) => visitor.visit_none(), + ValueType::Int32(Some(i)) => visitor.visit_i32(i), + ValueType::Int32(None) => visitor.visit_none(), + ValueType::Int64(Some(i)) => visitor.visit_i64(i), + ValueType::Int64(None) => visitor.visit_none(), + ValueType::Boolean(Some(b)) => visitor.visit_bool(b), + ValueType::Boolean(None) => visitor.visit_none(), + ValueType::Char(Some(c)) => visitor.visit_char(c), + ValueType::Char(None) => visitor.visit_none(), + ValueType::Float(Some(num)) => visitor.visit_f64(num as f64), + ValueType::Float(None) => visitor.visit_none(), + ValueType::Double(Some(num)) => visitor.visit_f64(num), + ValueType::Double(None) => visitor.visit_none(), #[cfg(feature = "bigdecimal")] - Value::Numeric(Some(num)) => { + ValueType::Numeric(Some(num)) => { use crate::bigdecimal::ToPrimitive; visitor.visit_f64(num.to_f64().unwrap()) } #[cfg(feature = "bigdecimal")] - Value::Numeric(None) => visitor.visit_none(), + ValueType::Numeric(None) => visitor.visit_none(), #[cfg(feature = "uuid")] - Value::Uuid(Some(uuid)) => visitor.visit_string(uuid.to_string()), + ValueType::Uuid(Some(uuid)) => visitor.visit_string(uuid.to_string()), #[cfg(feature = "uuid")] - Value::Uuid(None) => visitor.visit_none(), + ValueType::Uuid(None) => visitor.visit_none(), - Value::Json(Some(value)) => { + ValueType::Json(Some(value)) => { let de = value.into_deserializer(); de.deserialize_any(visitor) .map_err(|err| serde::de::value::Error::custom(format!("Error deserializing JSON value: {err}"))) } - Value::Json(None) => visitor.visit_none(), + ValueType::Json(None) => visitor.visit_none(), - Value::Xml(Some(s)) => visitor.visit_string(s.into_owned()), - Value::Xml(None) => visitor.visit_none(), + ValueType::Xml(Some(s)) => visitor.visit_string(s.into_owned()), + ValueType::Xml(None) => visitor.visit_none(), - Value::DateTime(Some(dt)) => visitor.visit_string(dt.to_rfc3339()), - Value::DateTime(None) => visitor.visit_none(), + ValueType::DateTime(Some(dt)) => visitor.visit_string(dt.to_rfc3339()), + ValueType::DateTime(None) => visitor.visit_none(), - Value::Date(Some(d)) => visitor.visit_string(format!("{d}")), - Value::Date(None) => visitor.visit_none(), + ValueType::Date(Some(d)) => visitor.visit_string(format!("{d}")), + ValueType::Date(None) => visitor.visit_none(), - Value::Time(Some(t)) => visitor.visit_string(format!("{t}")), - Value::Time(None) => visitor.visit_none(), + ValueType::Time(Some(t)) => visitor.visit_string(format!("{t}")), + ValueType::Time(None) => visitor.visit_none(), - Value::Array(Some(values)) => { + ValueType::Array(Some(values)) => { let deserializer = serde::de::value::SeqDeserializer::new(values.into_iter()); visitor.visit_seq(deserializer) } - Value::Array(None) => visitor.visit_none(), + ValueType::Array(None) => visitor.visit_none(), } } @@ -193,7 +193,7 @@ impl<'de> Deserializer<'de> for ValueDeserializer<'de> { where V: Visitor<'de>, { - if let Value::Bytes(Some(bytes)) = self.0 { + if let ValueType::Bytes(Some(bytes)) = self.0.typed { match bytes { Cow::Borrowed(bytes) => visitor.visit_borrowed_bytes(bytes), Cow::Owned(bytes) => visitor.visit_byte_buf(bytes), @@ -251,7 +251,7 @@ mod tests { #[test] fn deserialize_user() { - let row = make_row(vec![("id", Value::integer(12)), ("name", "Georgina".into())]); + let row = make_row(vec![("id", Value::int32(12)), ("name", "Georgina".into())]); let user: User = from_row(row).unwrap(); assert_eq!( @@ -267,9 +267,9 @@ mod tests { #[test] fn from_rows_works() { let first_row = make_row(vec![ - ("id", Value::integer(12)), + ("id", Value::int32(12)), ("name", "Georgina".into()), - ("bio", Value::Text(None)), + ("bio", Value::null_text()), ]); let second_row = make_row(vec![ ("id", 33.into()), diff --git a/quaint/src/tests/query.rs b/quaint/src/tests/query.rs index 7016262f2fec..ff16c118a46a 100644 --- a/quaint/src/tests/query.rs +++ b/quaint/src/tests/query.rs @@ -33,7 +33,7 @@ async fn aliased_value(api: &mut dyn TestApi) -> crate::Result<()> { #[test_each_connector] async fn aliased_null(api: &mut dyn TestApi) -> crate::Result<()> { - let query = Select::default().value(val!(Value::Int64(None)).alias("test")); + let query = Select::default().value(val!(Value::null_int64()).alias("test")); let res = api.conn().select(query).await?; let row = res.get(0).unwrap(); @@ -307,8 +307,8 @@ async fn where_equals(api: &mut dyn TestApi) -> crate::Result<()> { let table = api.create_temp_table("id int, name varchar(255)").await?; let insert = Insert::multi_into(&table, vec!["id", "name"]) - .values(vec![Value::integer(1), Value::text("Musti")]) - .values(vec![Value::integer(2), Value::text("Naukio")]); + .values(vec![Value::int32(1), Value::text("Musti")]) + .values(vec![Value::int32(2), Value::text("Naukio")]); api.conn().insert(insert.into()).await?; @@ -328,8 +328,8 @@ async fn where_like(api: &mut dyn TestApi) -> crate::Result<()> { let table = api.create_temp_table("id int, name varchar(255)").await?; let insert = Insert::multi_into(&table, vec!["id", "name"]) - .values(vec![Value::integer(1), Value::text("Musti")]) - .values(vec![Value::integer(2), Value::text("Naukio")]); + .values(vec![Value::int32(1), Value::text("Musti")]) + .values(vec![Value::int32(2), Value::text("Naukio")]); api.conn().insert(insert.into()).await?; @@ -349,8 +349,8 @@ async fn where_not_like(api: &mut dyn TestApi) -> crate::Result<()> { let table = api.create_temp_table("id int, name varchar(255)").await?; let insert = Insert::multi_into(&table, vec!["id", "name"]) - .values(vec![Value::integer(1), Value::text("Musti")]) - .values(vec![Value::integer(2), Value::text("Naukio")]); + .values(vec![Value::int32(1), Value::text("Musti")]) + .values(vec![Value::int32(2), Value::text("Naukio")]); api.conn().insert(insert.into()).await?; @@ -371,14 +371,14 @@ async fn inner_join(api: &mut dyn TestApi) -> crate::Result<()> { let table2 = api.create_temp_table("t1_id int, is_cat int").await?; let insert = Insert::multi_into(&table1, vec!["id", "name"]) - .values(vec![Value::integer(1), Value::text("Musti")]) - .values(vec![Value::integer(2), Value::text("Belka")]); + .values(vec![Value::int32(1), Value::text("Musti")]) + .values(vec![Value::int32(2), Value::text("Belka")]); api.conn().insert(insert.into()).await?; let insert = Insert::multi_into(&table2, vec!["t1_id", "is_cat"]) - .values(vec![Value::integer(1), Value::integer(1)]) - .values(vec![Value::integer(2), Value::integer(0)]); + .values(vec![Value::int32(1), Value::int32(1)]) + .values(vec![Value::int32(2), Value::int32(0)]); api.conn().insert(insert.into()).await?; @@ -414,18 +414,18 @@ async fn table_inner_join(api: &mut dyn TestApi) -> crate::Result<()> { let table3 = api.create_temp_table("id int, foo int").await?; let insert = Insert::multi_into(&table1, vec!["id", "name"]) - .values(vec![Value::integer(1), Value::text("Musti")]) - .values(vec![Value::integer(2), Value::text("Belka")]); + .values(vec![Value::int32(1), Value::text("Musti")]) + .values(vec![Value::int32(2), Value::text("Belka")]); api.conn().insert(insert.into()).await?; let insert = Insert::multi_into(&table2, vec!["t1_id", "is_cat"]) - .values(vec![Value::integer(1), Value::integer(1)]) - .values(vec![Value::integer(2), Value::integer(0)]); + .values(vec![Value::int32(1), Value::int32(1)]) + .values(vec![Value::int32(2), Value::int32(0)]); api.conn().insert(insert.into()).await?; - let insert = Insert::multi_into(&table3, vec!["id", "foo"]).values(vec![Value::integer(1), Value::integer(1)]); + let insert = Insert::multi_into(&table3, vec!["id", "foo"]).values(vec![Value::int32(1), Value::int32(1)]); api.conn().insert(insert.into()).await?; @@ -466,13 +466,12 @@ async fn left_join(api: &mut dyn TestApi) -> crate::Result<()> { let table2 = api.create_temp_table("t1_id int, is_cat int").await?; let insert = Insert::multi_into(&table1, vec!["id", "name"]) - .values(vec![Value::integer(1), Value::text("Musti")]) - .values(vec![Value::integer(2), Value::text("Belka")]); + .values(vec![Value::int32(1), Value::text("Musti")]) + .values(vec![Value::int32(2), Value::text("Belka")]); api.conn().insert(insert.into()).await?; - let insert = - Insert::multi_into(&table2, vec!["t1_id", "is_cat"]).values(vec![Value::integer(1), Value::integer(1)]); + let insert = Insert::multi_into(&table2, vec!["t1_id", "is_cat"]).values(vec![Value::int32(1), Value::int32(1)]); api.conn().insert(insert.into()).await?; @@ -508,17 +507,16 @@ async fn table_left_join(api: &mut dyn TestApi) -> crate::Result<()> { let table3 = api.create_temp_table("id int, foo int").await?; let insert = Insert::multi_into(&table1, vec!["id", "name"]) - .values(vec![Value::integer(1), Value::text("Musti")]) - .values(vec![Value::integer(2), Value::text("Belka")]); + .values(vec![Value::int32(1), Value::text("Musti")]) + .values(vec![Value::int32(2), Value::text("Belka")]); api.conn().insert(insert.into()).await?; - let insert = - Insert::multi_into(&table2, vec!["t1_id", "is_cat"]).values(vec![Value::integer(1), Value::integer(1)]); + let insert = Insert::multi_into(&table2, vec!["t1_id", "is_cat"]).values(vec![Value::int32(1), Value::int32(1)]); api.conn().insert(insert.into()).await?; - let insert = Insert::multi_into(&table3, vec!["id", "foo"]).values(vec![Value::integer(1), Value::integer(1)]); + let insert = Insert::multi_into(&table3, vec!["id", "foo"]).values(vec![Value::int32(1), Value::int32(1)]); api.conn().insert(insert.into()).await?; @@ -558,8 +556,8 @@ async fn limit_no_offset(api: &mut dyn TestApi) -> crate::Result<()> { let table = api.create_temp_table("id int, name varchar(255)").await?; let insert = Insert::multi_into(&table, vec!["id", "name"]) - .values(vec![Value::integer(1), Value::text("Musti")]) - .values(vec![Value::integer(2), Value::text("Naukio")]); + .values(vec![Value::int32(1), Value::text("Musti")]) + .values(vec![Value::int32(2), Value::text("Naukio")]); api.conn().insert(insert.into()).await?; @@ -580,8 +578,8 @@ async fn offset_no_limit(api: &mut dyn TestApi) -> crate::Result<()> { let table = api.create_temp_table("id int, name varchar(255)").await?; let insert = Insert::multi_into(&table, vec!["id", "name"]) - .values(vec![Value::integer(1), Value::text("Musti")]) - .values(vec![Value::integer(2), Value::text("Naukio")]); + .values(vec![Value::int32(1), Value::text("Musti")]) + .values(vec![Value::int32(2), Value::text("Naukio")]); api.conn().insert(insert.into()).await?; @@ -602,9 +600,9 @@ async fn limit_with_offset(api: &mut dyn TestApi) -> crate::Result<()> { let table = api.create_temp_table("id int, name varchar(255)").await?; let insert = Insert::multi_into(&table, vec!["id", "name"]) - .values(vec![Value::integer(1), Value::text("Musti")]) - .values(vec![Value::integer(2), Value::text("Naukio")]) - .values(vec![Value::integer(3), Value::text("Belka")]); + .values(vec![Value::int32(1), Value::text("Musti")]) + .values(vec![Value::int32(2), Value::text("Naukio")]) + .values(vec![Value::int32(3), Value::text("Belka")]); api.conn().insert(insert.into()).await?; @@ -625,9 +623,9 @@ async fn limit_with_offset_no_given_order(api: &mut dyn TestApi) -> crate::Resul let table = api.create_temp_table("id int, name varchar(255)").await?; let insert = Insert::multi_into(&table, vec!["id", "name"]) - .values(vec![Value::integer(1), Value::text("Musti")]) - .values(vec![Value::integer(2), Value::text("Naukio")]) - .values(vec![Value::integer(3), Value::text("Belka")]); + .values(vec![Value::int32(1), Value::text("Musti")]) + .values(vec![Value::int32(2), Value::text("Naukio")]) + .values(vec![Value::int32(3), Value::text("Belka")]); api.conn().insert(insert.into()).await?; @@ -1376,13 +1374,10 @@ async fn float_columns_cast_to_f32(api: &mut dyn TestApi) -> crate::Result<()> { #[test_each_connector(tags("mysql"), ignore("mysql8"))] #[cfg(feature = "bigdecimal")] async fn newdecimal_conversion_is_handled_correctly(api: &mut dyn TestApi) -> crate::Result<()> { - let select = Select::default().value(sum(Value::integer(1)).alias("theone")); + let select = Select::default().value(sum(Value::int32(1)).alias("theone")); let result = api.conn().select(select).await?; - assert_eq!( - Value::Numeric(Some("1.0".parse().unwrap())), - result.into_single().unwrap()[0] - ); + assert_eq!(Value::numeric("1.0".parse().unwrap()), result.into_single().unwrap()[0]); Ok(()) } @@ -1667,7 +1662,7 @@ async fn enum_values(api: &mut dyn TestApi) -> crate::Result<()> { Insert::single_into(&table) .value( "value", - Value::enum_variant_with_name("A", &type_name, Option::<&str>::None), + Value::enum_variant_with_name("A", EnumName::new(&type_name, Option::::None)), ) .into(), ) @@ -1678,18 +1673,14 @@ async fn enum_values(api: &mut dyn TestApi) -> crate::Result<()> { Insert::single_into(&table) .value( "value", - Value::enum_variant_with_name("B", &type_name, Option::<&str>::None), + Value::enum_variant_with_name("B", EnumName::new(&type_name, Option::::None)), ) .into(), ) .await?; api.conn() - .insert( - Insert::single_into(&table) - .value("value", Value::Enum(None, None)) - .into(), - ) + .insert(Insert::single_into(&table).value("value", Value::null_enum()).into()) .await?; let select = Select::from_table(&table).column("value").order_by("id".ascend()); @@ -1702,7 +1693,7 @@ async fn enum_values(api: &mut dyn TestApi) -> crate::Result<()> { assert_eq!(Some(&Value::enum_variant("B")), row.at(0)); let row = res.get(2).unwrap(); - assert_eq!(Some(&Value::Enum(None, None)), row.at(0)); + assert_eq!(Some(&Value::null_enum()), row.at(0)); Ok(()) } @@ -1717,9 +1708,9 @@ async fn row_to_json_normal(api: &mut dyn TestApi) -> crate::Result<()> { let result = api.conn().select(select).await?; assert_eq!( - Value::Json(Some(serde_json::json!({ + Value::json(serde_json::json!({ "toto": "hello_world" - }))), + })), result.into_single().unwrap()[0] ); @@ -1736,9 +1727,9 @@ async fn row_to_json_pretty(api: &mut dyn TestApi) -> crate::Result<()> { let result = api.conn().select(select).await?; assert_eq!( - Value::Json(Some(serde_json::json!({ + Value::json(serde_json::json!({ "toto": "hello_world" - }))), + })), result.into_single().unwrap()[0] ); @@ -2027,9 +2018,9 @@ async fn ints_read_write_to_numeric(api: &mut dyn TestApi) -> crate::Result<()> let table = api.create_temp_table("id int, value numeric(12,2)").await?; let insert = Insert::multi_into(&table, ["id", "value"]) - .values(vec![Value::integer(1), Value::double(1234.5)]) - .values(vec![Value::integer(2), Value::integer(1234)]) - .values(vec![Value::integer(3), Value::integer(12345)]); + .values(vec![Value::int32(1), Value::double(1234.5)]) + .values(vec![Value::int32(2), Value::int32(1234)]) + .values(vec![Value::int32(3), Value::int32(12345)]); api.conn().execute(insert.into()).await?; @@ -2057,7 +2048,7 @@ async fn bigdecimal_read_write_to_floating(api: &mut dyn TestApi) -> crate::Resu let val = BigDecimal::from_str("0.1").unwrap(); let insert = Insert::multi_into(&table, ["id", "a", "b"]).values(vec![ - Value::integer(1), + Value::int32(1), Value::numeric(val.clone()), Value::numeric(val.clone()), ]); @@ -2075,7 +2066,7 @@ async fn bigdecimal_read_write_to_floating(api: &mut dyn TestApi) -> crate::Resu #[test_each_connector] async fn coalesce_fun(api: &mut dyn TestApi) -> crate::Result<()> { - let exprs: Vec = vec![Value::Text(None).into(), Value::text("Individual").into()]; + let exprs: Vec = vec![Value::null_text().into(), Value::text("Individual").into()]; let select = Select::default().value(coalesce(exprs).alias("val")); let row = api.conn().select(select).await?.into_single()?; @@ -2085,15 +2076,15 @@ async fn coalesce_fun(api: &mut dyn TestApi) -> crate::Result<()> { } fn value_into_json(value: &Value) -> Option { - match value.clone() { + match value.typed.clone() { // MariaDB returns JSON as text - Value::Text(Some(text)) => { + ValueType::Text(Some(text)) => { let json: serde_json::Value = serde_json::from_str(&text) .unwrap_or_else(|_| panic!("expected parsable text to json, found {}", text)); Some(json) } - Value::Json(Some(json)) => Some(json), + ValueType::Json(Some(json)) => Some(json), _ => None, } } @@ -3007,7 +2998,7 @@ async fn generate_binary_uuid(api: &mut dyn TestApi) -> crate::Result<()> { let val = res.into_single()?; // If it is a byte type and has a value, it's a generated UUID. - assert!(matches!(val, Value::Bytes(x) if x.is_some())); + assert!(matches!(val.typed, ValueType::Bytes(x) if x.is_some())); Ok(()) } @@ -3020,7 +3011,7 @@ async fn generate_swapped_binary_uuid(api: &mut dyn TestApi) -> crate::Result<() let val = res.into_single()?; // If it is a byte type and has a value, it's a generated UUID. - assert!(matches!(val, Value::Bytes(x) if x.is_some())); + assert!(matches!(val.typed, ValueType::Bytes(x) if x.is_some())); Ok(()) } @@ -3033,7 +3024,7 @@ async fn generate_native_uuid(api: &mut dyn TestApi) -> crate::Result<()> { let val = res.into_single()?; // If it is a text type and has a value, it's a generated string UUID. - assert!(matches!(val, Value::Text(x) if x.is_some())); + assert!(matches!(val.typed, ValueType::Text(x) if x.is_some())); Ok(()) } @@ -3176,25 +3167,25 @@ async fn order_by_nulls_first_last(api: &mut dyn TestApi) -> crate::Result<()> { let insert = Insert::single_into(&table) .value("name", "b") - .value("age", Value::Int32(None)); + .value("age", Value::null_int32()); api.conn().insert(insert.into()).await?; let insert = Insert::single_into(&table) - .value("name", Value::Text(None)) + .value("name", Value::null_text()) .value("age", 2); api.conn().insert(insert.into()).await?; let insert = Insert::single_into(&table) - .value("name", Value::Text(None)) - .value("age", Value::Text(None)); + .value("name", Value::null_text()) + .value("age", Value::null_text()); api.conn().insert(insert.into()).await?; // name ASC NULLS FIRST let select = Select::from_table(table.clone()).order_by("name".ascend_nulls_first()); let res = api.conn().select(select).await?; - assert_eq!(res.get(0).unwrap()["name"], Value::Text(None)); - assert_eq!(res.get(1).unwrap()["name"], Value::Text(None)); + assert_eq!(res.get(0).unwrap()["name"], Value::null_text()); + assert_eq!(res.get(1).unwrap()["name"], Value::null_text()); assert_eq!(res.get(2).unwrap()["name"], Value::text("a")); assert_eq!(res.get(3).unwrap()["name"], Value::text("b")); @@ -3204,15 +3195,15 @@ async fn order_by_nulls_first_last(api: &mut dyn TestApi) -> crate::Result<()> { assert_eq!(res.get(0).unwrap()["name"], Value::text("a")); assert_eq!(res.get(1).unwrap()["name"], Value::text("b")); - assert_eq!(res.get(2).unwrap()["name"], Value::Text(None)); - assert_eq!(res.get(3).unwrap()["name"], Value::Text(None)); + assert_eq!(res.get(2).unwrap()["name"], Value::null_text()); + assert_eq!(res.get(3).unwrap()["name"], Value::null_text()); // name DESC NULLS FIRST let select = Select::from_table(table.clone()).order_by("name".descend_nulls_first()); let res = api.conn().select(select).await?; - assert_eq!(res.get(0).unwrap()["name"], Value::Text(None)); - assert_eq!(res.get(1).unwrap()["name"], Value::Text(None)); + assert_eq!(res.get(0).unwrap()["name"], Value::null_text()); + assert_eq!(res.get(1).unwrap()["name"], Value::null_text()); assert_eq!(res.get(2).unwrap()["name"], Value::text("b")); assert_eq!(res.get(3).unwrap()["name"], Value::text("a")); @@ -3222,8 +3213,8 @@ async fn order_by_nulls_first_last(api: &mut dyn TestApi) -> crate::Result<()> { assert_eq!(res.get(0).unwrap()["name"], Value::text("b")); assert_eq!(res.get(1).unwrap()["name"], Value::text("a")); - assert_eq!(res.get(2).unwrap()["name"], Value::Text(None)); - assert_eq!(res.get(3).unwrap()["name"], Value::Text(None)); + assert_eq!(res.get(2).unwrap()["name"], Value::null_text()); + assert_eq!(res.get(3).unwrap()["name"], Value::null_text()); // name ASC NULLS FIRST, age ASC NULLS FIRST let select = Select::from_table(table.clone()) @@ -3231,17 +3222,17 @@ async fn order_by_nulls_first_last(api: &mut dyn TestApi) -> crate::Result<()> { .order_by("age".ascend_nulls_first()); let res = api.conn().select(select).await?; - assert_eq!(res.get(0).unwrap()["name"], Value::Text(None)); - assert_eq!(res.get(0).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(0).unwrap()["name"], Value::null_text()); + assert_eq!(res.get(0).unwrap()["age"], Value::null_int32()); - assert_eq!(res.get(1).unwrap()["name"], Value::Text(None)); + assert_eq!(res.get(1).unwrap()["name"], Value::null_text()); assert_eq!(res.get(1).unwrap()["age"], Value::int32(2)); assert_eq!(res.get(2).unwrap()["name"], Value::text("a")); assert_eq!(res.get(2).unwrap()["age"], Value::int32(1)); assert_eq!(res.get(3).unwrap()["name"], Value::text("b")); - assert_eq!(res.get(3).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(3).unwrap()["age"], Value::null_int32()); // name ASC NULLS LAST, age ASC NULLS LAST let select = Select::from_table(table.clone()) @@ -3253,13 +3244,13 @@ async fn order_by_nulls_first_last(api: &mut dyn TestApi) -> crate::Result<()> { assert_eq!(res.get(0).unwrap()["age"], Value::int32(1)); assert_eq!(res.get(1).unwrap()["name"], Value::text("b")); - assert_eq!(res.get(1).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(1).unwrap()["age"], Value::null_int32()); - assert_eq!(res.get(2).unwrap()["name"], Value::Text(None)); + assert_eq!(res.get(2).unwrap()["name"], Value::null_text()); assert_eq!(res.get(2).unwrap()["age"], Value::int32(2)); - assert_eq!(res.get(3).unwrap()["name"], Value::Text(None)); - assert_eq!(res.get(3).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(3).unwrap()["name"], Value::null_text()); + assert_eq!(res.get(3).unwrap()["age"], Value::null_int32()); // name DESC NULLS FIRST, age DESC NULLS FIRST let select = Select::from_table(table.clone()) @@ -3267,14 +3258,14 @@ async fn order_by_nulls_first_last(api: &mut dyn TestApi) -> crate::Result<()> { .order_by("age".descend_nulls_first()); let res = api.conn().select(select).await?; - assert_eq!(res.get(0).unwrap()["name"], Value::Text(None)); - assert_eq!(res.get(0).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(0).unwrap()["name"], Value::null_text()); + assert_eq!(res.get(0).unwrap()["age"], Value::null_int32()); - assert_eq!(res.get(1).unwrap()["name"], Value::Text(None)); + assert_eq!(res.get(1).unwrap()["name"], Value::null_text()); assert_eq!(res.get(1).unwrap()["age"], Value::int32(2)); assert_eq!(res.get(2).unwrap()["name"], Value::text("b")); - assert_eq!(res.get(2).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(2).unwrap()["age"], Value::null_int32()); assert_eq!(res.get(3).unwrap()["name"], Value::text("a")); assert_eq!(res.get(3).unwrap()["age"], Value::int32(1)); @@ -3286,16 +3277,16 @@ async fn order_by_nulls_first_last(api: &mut dyn TestApi) -> crate::Result<()> { let res = api.conn().select(select).await?; assert_eq!(res.get(0).unwrap()["name"], Value::text("b")); - assert_eq!(res.get(0).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(0).unwrap()["age"], Value::null_int32()); assert_eq!(res.get(1).unwrap()["name"], Value::text("a")); assert_eq!(res.get(1).unwrap()["age"], Value::int32(1)); - assert_eq!(res.get(2).unwrap()["name"], Value::Text(None)); + assert_eq!(res.get(2).unwrap()["name"], Value::null_text()); assert_eq!(res.get(2).unwrap()["age"], Value::int32(2)); - assert_eq!(res.get(3).unwrap()["name"], Value::Text(None)); - assert_eq!(res.get(3).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(3).unwrap()["name"], Value::null_text()); + assert_eq!(res.get(3).unwrap()["age"], Value::null_int32()); // name ASC NULLS LAST, age DESC NULLS FIRST let select = Select::from_table(table.clone()) @@ -3307,12 +3298,12 @@ async fn order_by_nulls_first_last(api: &mut dyn TestApi) -> crate::Result<()> { assert_eq!(res.get(0).unwrap()["age"], Value::int32(1)); assert_eq!(res.get(1).unwrap()["name"], Value::text("b")); - assert_eq!(res.get(1).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(1).unwrap()["age"], Value::null_int32()); - assert_eq!(res.get(2).unwrap()["name"], Value::Text(None)); - assert_eq!(res.get(2).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(2).unwrap()["name"], Value::null_text()); + assert_eq!(res.get(2).unwrap()["age"], Value::null_int32()); - assert_eq!(res.get(3).unwrap()["name"], Value::Text(None)); + assert_eq!(res.get(3).unwrap()["name"], Value::null_text()); assert_eq!(res.get(3).unwrap()["age"], Value::int32(2)); // name DESC NULLS FIRST, age ASC NULLS LAST @@ -3321,14 +3312,14 @@ async fn order_by_nulls_first_last(api: &mut dyn TestApi) -> crate::Result<()> { .order_by("age".ascend_nulls_last()); let res = api.conn().select(select).await?; - assert_eq!(res.get(0).unwrap()["name"], Value::Text(None)); + assert_eq!(res.get(0).unwrap()["name"], Value::null_text()); assert_eq!(res.get(0).unwrap()["age"], Value::int32(2)); - assert_eq!(res.get(1).unwrap()["name"], Value::Text(None)); - assert_eq!(res.get(1).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(1).unwrap()["name"], Value::null_text()); + assert_eq!(res.get(1).unwrap()["age"], Value::null_int32()); assert_eq!(res.get(2).unwrap()["name"], Value::text("b")); - assert_eq!(res.get(2).unwrap()["age"], Value::Int32(None)); + assert_eq!(res.get(2).unwrap()["age"], Value::null_int32()); assert_eq!(res.get(3).unwrap()["name"], Value::text("a")); assert_eq!(res.get(3).unwrap()["age"], Value::int32(1)); diff --git a/quaint/src/tests/query/error.rs b/quaint/src/tests/query/error.rs index d9884a2c574a..e1c8a74202f5 100644 --- a/quaint/src/tests/query/error.rs +++ b/quaint/src/tests/query/error.rs @@ -129,7 +129,7 @@ async fn null_constraint_violation(api: &mut dyn TestApi) -> crate::Result<()> { let insert = Insert::single_into(&table).value("id1", 50).value("id2", 55); api.conn().insert(insert.into()).await?; - let update = Update::table(&table).set("id2", Value::Int64(None)); + let update = Update::table(&table).set("id2", ValueType::Int64(None)); let res = api.conn().update(update).await; assert!(res.is_err()); @@ -414,7 +414,8 @@ async fn array_into_scalar_should_fail(api: &mut dyn TestApi) -> crate::Result<( let err = result.unwrap_err(); - assert!(err.to_string().contains("Couldn't serialize value `Some([Text(Some(\"abc\")), Text(Some(\"def\"))])` into a `text`. Value is a list but `text` is not.")); + assert!(err.to_string().contains("Couldn't serialize value")); + assert!(err.to_string().contains("Value is a list but `text` is not.")); Ok(()) } diff --git a/quaint/src/tests/types/mssql.rs b/quaint/src/tests/types/mssql.rs index 6824562cde51..9d5d51317707 100644 --- a/quaint/src/tests/types/mssql.rs +++ b/quaint/src/tests/types/mssql.rs @@ -8,7 +8,7 @@ use crate::tests::test_api::*; test_type!(nvarchar_limited( mssql, "NVARCHAR(10)", - Value::Text(None), + Value::null_text(), Value::text("foobar"), Value::text("余"), )); @@ -16,7 +16,7 @@ test_type!(nvarchar_limited( test_type!(nvarchar_max( mssql, "NVARCHAR(max)", - Value::Text(None), + Value::null_text(), Value::text("foobar"), Value::text("余"), Value::text("test¥฿😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟😠😡😢😣😤😥�😧😨😩😪😫😬😭😮😯😰😱😲😳😴😵😶😷😸😹😺😻😼😽😾😿🙀🙁�🙂🙃🙄🙅🙆🙇🙈🙉🙊🙋🙌🙍🙎🙏ऀँंःऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढणतथदधनऩपफबभमयर€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿⃀"), @@ -25,7 +25,7 @@ test_type!(nvarchar_max( test_type!(ntext( mssql, "NTEXT", - Value::Text(None), + Value::null_text(), Value::text("foobar"), Value::text("余"), )); @@ -33,23 +33,23 @@ test_type!(ntext( test_type!(varchar_limited( mssql, "VARCHAR(10)", - Value::Text(None), + Value::null_text(), Value::text("foobar"), )); test_type!(varchar_max( mssql, "VARCHAR(max)", - Value::Text(None), + Value::null_text(), Value::text("foobar"), )); -test_type!(text(mssql, "TEXT", Value::Text(None), Value::text("foobar"))); +test_type!(text(mssql, "TEXT", Value::null_text(), Value::text("foobar"))); test_type!(tinyint( mssql, "tinyint", - Value::Int32(None), + Value::null_int32(), Value::int32(u8::MIN), Value::int32(u8::MAX), )); @@ -57,7 +57,7 @@ test_type!(tinyint( test_type!(smallint( mssql, "smallint", - Value::Int32(None), + Value::null_int32(), Value::int32(i16::MIN), Value::int32(i16::MAX), )); @@ -65,7 +65,7 @@ test_type!(smallint( test_type!(int( mssql, "int", - Value::Int32(None), + Value::null_int32(), Value::int32(i32::MIN), Value::int32(i32::MAX), )); @@ -73,35 +73,35 @@ test_type!(int( test_type!(bigint( mssql, "bigint", - Value::Int64(None), + Value::null_int64(), Value::int64(i64::MIN), Value::int64(i64::MAX), )); -test_type!(float_24(mssql, "float(24)", Value::Float(None), Value::float(1.23456),)); +test_type!(float_24(mssql, "float(24)", Value::null_float(), Value::float(1.23456),)); -test_type!(real(mssql, "real", Value::Float(None), Value::float(1.123456))); +test_type!(real(mssql, "real", Value::null_float(), Value::float(1.123456))); test_type!(float_53( mssql, "float(53)", - Value::Double(None), + Value::null_double(), Value::double(1.1234567891) )); -test_type!(money(mssql, "money", Value::Double(None), Value::double(3.14))); +test_type!(money(mssql, "money", Value::null_double(), Value::double(3.14))); test_type!(smallmoney( mssql, "smallmoney", - Value::Double(None), + Value::null_double(), Value::double(3.14) )); test_type!(boolean( mssql, "bit", - Value::Boolean(None), + Value::null_boolean(), Value::boolean(true), Value::boolean(false), )); @@ -109,54 +109,54 @@ test_type!(boolean( test_type!(binary( mssql, "binary(8)", - Value::Bytes(None), + Value::null_bytes(), Value::bytes(b"DEADBEEF".to_vec()), )); test_type!(varbinary( mssql, "varbinary(8)", - Value::Bytes(None), + Value::null_bytes(), Value::bytes(b"DEADBEEF".to_vec()), )); test_type!(image( mssql, "image", - Value::Bytes(None), + Value::null_bytes(), Value::bytes(b"DEADBEEF".to_vec()), )); test_type!(date( mssql, "date", - Value::Date(None), + Value::null_date(), Value::date(chrono::NaiveDate::from_ymd_opt(2020, 4, 20).unwrap()) )); test_type!(time( mssql, "time", - Value::Time(None), + Value::null_time(), Value::time(chrono::NaiveTime::from_hms_opt(16, 20, 00).unwrap()) )); -test_type!(datetime2(mssql, "datetime2", Value::DateTime(None), { +test_type!(datetime2(mssql, "datetime2", Value::null_datetime(), { let dt = chrono::DateTime::parse_from_rfc3339("2020-02-27T19:10:00Z").unwrap(); Value::datetime(dt.with_timezone(&chrono::Utc)) })); -test_type!(datetime(mssql, "datetime", Value::DateTime(None), { +test_type!(datetime(mssql, "datetime", Value::null_datetime(), { let dt = chrono::DateTime::parse_from_rfc3339("2020-02-27T19:10:22Z").unwrap(); Value::datetime(dt.with_timezone(&chrono::Utc)) })); -test_type!(datetimeoffset(mssql, "datetimeoffset", Value::DateTime(None), { +test_type!(datetimeoffset(mssql, "datetimeoffset", Value::null_datetime(), { let dt = chrono::DateTime::parse_from_rfc3339("2020-02-27T19:10:22Z").unwrap(); Value::datetime(dt.with_timezone(&chrono::Utc)) })); -test_type!(smalldatetime(mssql, "smalldatetime", Value::DateTime(None), { +test_type!(smalldatetime(mssql, "smalldatetime", Value::null_datetime(), { let dt = chrono::DateTime::parse_from_rfc3339("2020-02-27T19:10:00Z").unwrap(); Value::datetime(dt.with_timezone(&chrono::Utc)) })); diff --git a/quaint/src/tests/types/mssql/bigdecimal.rs b/quaint/src/tests/types/mssql/bigdecimal.rs index 821d419ad4a5..4dbd101ff456 100644 --- a/quaint/src/tests/types/mssql/bigdecimal.rs +++ b/quaint/src/tests/types/mssql/bigdecimal.rs @@ -6,7 +6,7 @@ use std::str::FromStr; test_type!(numeric( mssql, "numeric(10,2)", - Value::Numeric(None), + Value::null_numeric(), Value::numeric(BigDecimal::from_str("3.14")?) )); @@ -148,21 +148,21 @@ test_type!(numeric_38_6( test_type!(money( mssql, "money", - (Value::Numeric(None), Value::Double(None)), + (Value::null_numeric(), Value::null_double()), (Value::numeric(BigDecimal::from_str("3.14")?), Value::double(3.14)) )); test_type!(smallmoney( mssql, "smallmoney", - (Value::Numeric(None), Value::Double(None)), + (Value::null_numeric(), Value::null_double()), (Value::numeric(BigDecimal::from_str("3.14")?), Value::double(3.14)) )); test_type!(float_24( mssql, "float(24)", - (Value::Numeric(None), Value::Float(None)), + (Value::null_numeric(), Value::null_float()), ( Value::numeric(BigDecimal::from_str("1.123456")?), Value::float(1.123456) @@ -172,7 +172,7 @@ test_type!(float_24( test_type!(real( mssql, "real", - (Value::Numeric(None), Value::Float(None)), + (Value::null_numeric(), Value::null_float()), ( Value::numeric(BigDecimal::from_str("1.123456")?), Value::float(1.123456) @@ -182,7 +182,7 @@ test_type!(real( test_type!(float_53( mssql, "float(53)", - (Value::Numeric(None), Value::Double(None)), + (Value::null_numeric(), Value::null_double()), ( Value::numeric(BigDecimal::from_str("1.123456789012345")?), Value::double(1.123456789012345) diff --git a/quaint/src/tests/types/mysql.rs b/quaint/src/tests/types/mysql.rs index fc3d86a30bcb..cebfbef41033 100644 --- a/quaint/src/tests/types/mysql.rs +++ b/quaint/src/tests/types/mysql.rs @@ -11,7 +11,7 @@ use crate::bigdecimal::BigDecimal; test_type!(tinyint( mysql, "tinyint(4)", - Value::Int32(None), + Value::null_int32(), Value::int32(i8::MIN), Value::int32(i8::MAX) )); @@ -27,7 +27,7 @@ test_type!(tinyint1( test_type!(tinyint_unsigned( mysql, "tinyint(4) unsigned", - Value::Int32(None), + Value::null_int32(), Value::int32(0), Value::int32(255) )); @@ -35,7 +35,7 @@ test_type!(tinyint_unsigned( test_type!(year( mysql, "year", - Value::Int32(None), + Value::null_int32(), Value::int32(1984), Value::int32(2049) )); @@ -43,7 +43,7 @@ test_type!(year( test_type!(smallint( mysql, "smallint", - Value::Int32(None), + Value::null_int32(), Value::int32(i16::MIN), Value::int32(i16::MAX) )); @@ -51,7 +51,7 @@ test_type!(smallint( test_type!(smallint_unsigned( mysql, "smallint unsigned", - Value::Int32(None), + Value::null_int32(), Value::int32(0), Value::int32(65535) )); @@ -59,7 +59,7 @@ test_type!(smallint_unsigned( test_type!(mediumint( mysql, "mediumint", - Value::Int32(None), + Value::null_int32(), Value::int32(-8388608), Value::int32(8388607) )); @@ -67,7 +67,7 @@ test_type!(mediumint( test_type!(mediumint_unsigned( mysql, "mediumint unsigned", - Value::Int64(None), + Value::null_int64(), Value::int64(0), Value::int64(16777215) )); @@ -75,7 +75,7 @@ test_type!(mediumint_unsigned( test_type!(int( mysql, "int", - Value::Int32(None), + Value::null_int32(), Value::int32(i32::MIN), Value::int32(i32::MAX) )); @@ -83,7 +83,7 @@ test_type!(int( test_type!(int_unsigned( mysql, "int unsigned", - Value::Int64(None), + Value::null_int64(), Value::int64(0), Value::int64(2173158296i64), Value::int64(4294967295i64) @@ -100,7 +100,7 @@ test_type!(int_unsigned_not_null( test_type!(bigint( mysql, "bigint", - Value::Int64(None), + Value::null_int64(), Value::int64(i64::MIN), Value::int64(i64::MAX) )); @@ -109,7 +109,7 @@ test_type!(bigint( test_type!(decimal( mysql, "decimal(10,2)", - Value::Numeric(None), + Value::null_numeric(), Value::numeric(bigdecimal::BigDecimal::from_str("3.14").unwrap()) )); @@ -127,7 +127,7 @@ test_type!(decimal_65_6( test_type!(float_decimal( mysql, "float", - (Value::Numeric(None), Value::Float(None)), + (Value::null_numeric(), Value::null_float()), ( Value::numeric(bigdecimal::BigDecimal::from_str("3.14").unwrap()), Value::float(3.14) @@ -138,7 +138,7 @@ test_type!(float_decimal( test_type!(double_decimal( mysql, "double", - (Value::Numeric(None), Value::Double(None)), + (Value::null_numeric(), Value::null_double()), ( Value::numeric(bigdecimal::BigDecimal::from_str("3.14").unwrap()), Value::double(3.14) @@ -148,62 +148,67 @@ test_type!(double_decimal( test_type!(bit1( mysql, "bit(1)", - (Value::Bytes(None), Value::Boolean(None)), - (Value::integer(0), Value::boolean(false)), - (Value::integer(1), Value::boolean(true)), + (Value::null_bytes(), Value::null_boolean()), + (Value::int32(0), Value::boolean(false)), + (Value::int32(1), Value::boolean(true)), )); test_type!(bit64( mysql, "bit(64)", - Value::Bytes(None), + Value::null_bytes(), Value::bytes(vec![0, 0, 0, 0, 0, 6, 107, 58]) )); -test_type!(char(mysql, "char(255)", Value::Text(None), Value::text("foobar"))); -test_type!(float(mysql, "float", Value::Float(None), Value::float(1.12345),)); -test_type!(double(mysql, "double", Value::Double(None), Value::double(1.12314124))); -test_type!(varchar(mysql, "varchar(255)", Value::Text(None), Value::text("foobar"))); -test_type!(tinytext(mysql, "tinytext", Value::Text(None), Value::text("foobar"))); -test_type!(text(mysql, "text", Value::Text(None), Value::text("foobar"))); -test_type!(longtext(mysql, "longtext", Value::Text(None), Value::text("foobar"))); +test_type!(char(mysql, "char(255)", Value::null_text(), Value::text("foobar"))); +test_type!(float(mysql, "float", Value::null_float(), Value::float(1.12345),)); +test_type!(double(mysql, "double", Value::null_double(), Value::double(1.12314124))); +test_type!(varchar( + mysql, + "varchar(255)", + Value::null_text(), + Value::text("foobar") +)); +test_type!(tinytext(mysql, "tinytext", Value::null_text(), Value::text("foobar"))); +test_type!(text(mysql, "text", Value::null_text(), Value::text("foobar"))); +test_type!(longtext(mysql, "longtext", Value::null_text(), Value::text("foobar"))); test_type!(binary(mysql, "binary(5)", Value::bytes(vec![1, 2, 3, 0, 0]))); test_type!(varbinary(mysql, "varbinary(255)", Value::bytes(vec![1, 2, 3]))); test_type!(mediumtext( mysql, "mediumtext", - Value::Text(None), + Value::null_text(), Value::text("foobar") )); test_type!(tinyblob( mysql, "tinyblob", - Value::Bytes(None), + Value::null_bytes(), Value::bytes(vec![1, 2, 3]) )); test_type!(mediumblob( mysql, "mediumblob", - Value::Bytes(None), + Value::null_bytes(), Value::bytes(vec![1, 2, 3]) )); test_type!(longblob( mysql, "longblob", - Value::Bytes(None), + Value::null_bytes(), Value::bytes(vec![1, 2, 3]) )); -test_type!(blob(mysql, "blob", Value::Bytes(None), Value::bytes(vec![1, 2, 3]))); +test_type!(blob(mysql, "blob", Value::null_bytes(), Value::bytes(vec![1, 2, 3]))); test_type!(enum( mysql, "enum('pollicle_dogs','jellicle_cats')", - Value::Enum(None, None), + Value::null_enum(), Value::enum_variant("jellicle_cats"), Value::enum_variant("pollicle_dogs") )); @@ -211,11 +216,11 @@ test_type!(enum( test_type!(json( mysql, "json", - Value::Json(None), + Value::null_json(), Value::json(serde_json::json!({"this": "is", "a": "json", "number": 2})) )); -test_type!(date(mysql, "date", Value::Date(None), { +test_type!(date(mysql, "date", Value::null_date(), { let dt = chrono::DateTime::parse_from_rfc3339("2020-04-20T00:00:00Z").unwrap(); Value::datetime(dt.with_timezone(&chrono::Utc)) })); @@ -223,11 +228,11 @@ test_type!(date(mysql, "date", Value::Date(None), { test_type!(time( mysql, "time", - Value::Time(None), + Value::null_time(), Value::time(chrono::NaiveTime::from_hms_opt(16, 20, 00).unwrap()) )); -test_type!(datetime(mysql, "datetime", Value::DateTime(None), { +test_type!(datetime(mysql, "datetime", Value::null_datetime(), { let dt = chrono::DateTime::parse_from_rfc3339("2020-02-27T19:10:22Z").unwrap(); Value::datetime(dt.with_timezone(&chrono::Utc)) })); diff --git a/quaint/src/tests/types/postgres.rs b/quaint/src/tests/types/postgres.rs index 3ca40c822a77..bcbe30702431 100644 --- a/quaint/src/tests/types/postgres.rs +++ b/quaint/src/tests/types/postgres.rs @@ -8,7 +8,7 @@ use std::str::FromStr; test_type!(boolean( postgresql, "boolean", - Value::Boolean(None), + Value::null_boolean(), Value::boolean(true), Value::boolean(false), )); @@ -16,19 +16,19 @@ test_type!(boolean( test_type!(boolean_array( postgresql, "boolean[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::boolean(true), Value::boolean(false), Value::boolean(true), - Value::Boolean(None) + Value::null_boolean() ]), )); test_type!(int2( postgresql, "int2", - Value::Int32(None), + Value::null_int32(), Value::int32(i16::MIN), Value::int32(i16::MAX), )); @@ -36,7 +36,7 @@ test_type!(int2( test_type!(int2_with_int64( postgresql, "int2", - (Value::Int64(None), Value::Int32(None)), + (Value::null_int64(), Value::null_int32()), (Value::int64(i16::MIN), Value::int32(i16::MIN)), (Value::int64(i16::MAX), Value::int32(i16::MAX)) )); @@ -44,12 +44,12 @@ test_type!(int2_with_int64( test_type!(int2_array( postgresql, "int2[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::int32(1), Value::int32(2), Value::int32(3), - Value::Int32(None) + Value::null_int32() ]), )); @@ -57,15 +57,23 @@ test_type!(int2_array_with_i64( postgresql, "int2[]", ( - Value::array(vec![Value::int64(i16::MIN), Value::int64(i16::MAX), Value::Int64(None)]), - Value::array(vec![Value::int32(i16::MIN), Value::int32(i16::MAX), Value::Int32(None)]) + Value::array(vec![ + Value::int64(i16::MIN), + Value::int64(i16::MAX), + Value::null_int64() + ]), + Value::array(vec![ + Value::int32(i16::MIN), + Value::int32(i16::MAX), + Value::null_int32() + ]) ) )); test_type!(int4( postgresql, "int4", - Value::Int32(None), + Value::null_int32(), Value::int32(i32::MIN), Value::int32(i32::MAX), )); @@ -73,7 +81,7 @@ test_type!(int4( test_type!(int4_with_i64( postgresql, "int4", - (Value::Int64(None), Value::Int32(None)), + (Value::null_int64(), Value::null_int32()), (Value::int64(i32::MIN), Value::int32(i32::MIN)), (Value::int64(i32::MAX), Value::int32(i32::MAX)) )); @@ -81,23 +89,35 @@ test_type!(int4_with_i64( test_type!(int4_array( postgresql, "int4[]", - Value::Array(None), - Value::array(vec![Value::int32(i32::MIN), Value::int32(i32::MAX), Value::Int32(None)]), + Value::null_array(), + Value::array(vec![ + Value::int32(i32::MIN), + Value::int32(i32::MAX), + Value::null_int32() + ]), )); test_type!(int4_array_with_i64( postgresql, "int4[]", ( - Value::array(vec![Value::int64(i32::MIN), Value::int64(i32::MAX), Value::Int64(None)]), - Value::array(vec![Value::int32(i32::MIN), Value::int32(i32::MAX), Value::Int32(None)]) + Value::array(vec![ + Value::int64(i32::MIN), + Value::int64(i32::MAX), + Value::null_int64() + ]), + Value::array(vec![ + Value::int32(i32::MIN), + Value::int32(i32::MAX), + Value::null_int32() + ]) ) )); test_type!(int8( postgresql, "int8", - Value::Int64(None), + Value::null_int64(), Value::int64(i64::MIN), Value::int64(i64::MAX), )); @@ -105,36 +125,36 @@ test_type!(int8( test_type!(int8_array( postgresql, "int8[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::int64(1), Value::int64(2), Value::int64(3), - Value::Int64(None) + Value::null_int64() ]), )); -test_type!(float4(postgresql, "float4", Value::Float(None), Value::float(1.234))); +test_type!(float4(postgresql, "float4", Value::null_float(), Value::float(1.234))); test_type!(float4_array( postgresql, "float4[]", - Value::Array(None), - Value::array(vec![Value::float(1.1234), Value::float(4.321), Value::Float(None)]) + Value::null_array(), + Value::array(vec![Value::float(1.1234), Value::float(4.321), Value::null_float()]) )); test_type!(float8( postgresql, "float8", - Value::Double(None), + Value::null_double(), Value::double(1.12345764), )); test_type!(float8_array( postgresql, "float8[]", - Value::Array(None), - Value::array(vec![Value::double(1.1234), Value::double(4.321), Value::Double(None)]) + Value::null_array(), + Value::array(vec![Value::double(1.1234), Value::double(4.321), Value::null_double()]) )); // NOTE: OIDs are unsigned 32-bit integers (see https://www.postgresql.org/docs/9.4/datatype-oid.html) @@ -142,7 +162,7 @@ test_type!(float8_array( test_type!(oid_with_i32( postgresql, "oid", - (Value::Int32(None), Value::Int64(None)), + (Value::null_int32(), Value::null_int64()), (Value::int32(i32::MAX), Value::int64(i32::MAX)), (Value::int32(u32::MIN as i32), Value::int64(u32::MIN)), )); @@ -150,7 +170,7 @@ test_type!(oid_with_i32( test_type!(oid_with_i64( postgresql, "oid", - Value::Int64(None), + Value::null_int64(), Value::int64(u32::MAX), Value::int64(u32::MIN), )); @@ -158,12 +178,12 @@ test_type!(oid_with_i64( test_type!(oid_array( postgresql, "oid[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::int64(1), Value::int64(2), Value::int64(3), - Value::Int64(None) + Value::null_int64() ]), )); @@ -188,124 +208,124 @@ test_type!(serial8( Value::int64(i64::MAX), )); -test_type!(char(postgresql, "char(6)", Value::Text(None), Value::text("foobar"))); +test_type!(char(postgresql, "char(6)", Value::null_text(), Value::text("foobar"))); test_type!(char_array( postgresql, "char(6)[]", - Value::Array(None), - Value::array(vec![Value::text("foobar"), Value::text("omgwtf"), Value::Text(None)]) + Value::null_array(), + Value::array(vec![Value::text("foobar"), Value::text("omgwtf"), Value::null_text()]) )); test_type!(varchar( postgresql, "varchar(255)", - Value::Text(None), + Value::null_text(), Value::text("foobar") )); test_type!(varchar_array( postgresql, "varchar(255)[]", - Value::Array(None), - Value::array(vec![Value::text("foobar"), Value::text("omgwtf"), Value::Text(None)]) + Value::null_array(), + Value::array(vec![Value::text("foobar"), Value::text("omgwtf"), Value::null_text()]) )); -test_type!(text(postgresql, "text", Value::Text(None), Value::text("foobar"))); +test_type!(text(postgresql, "text", Value::null_text(), Value::text("foobar"))); test_type!(text_array( postgresql, "text[]", - Value::Array(None), - Value::array(vec![Value::text("foobar"), Value::text("omgwtf"), Value::Text(None)]) + Value::null_array(), + Value::array(vec![Value::text("foobar"), Value::text("omgwtf"), Value::null_text()]) )); -test_type!(bit(postgresql, "bit(4)", Value::Text(None), Value::text("1001"))); +test_type!(bit(postgresql, "bit(4)", Value::null_text(), Value::text("1001"))); test_type!(bit_array( postgresql, "bit(4)[]", - Value::Array(None), - Value::array(vec![Value::text("1001"), Value::text("0110"), Value::Text(None)]) + Value::null_array(), + Value::array(vec![Value::text("1001"), Value::text("0110"), Value::null_text()]) )); test_type!(varbit( postgresql, "varbit(20)", - Value::Text(None), + Value::null_text(), Value::text("001010101") )); test_type!(varbit_array( postgresql, "varbit(20)[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::text("001010101"), Value::text("01101111"), - Value::Text(None) + Value::null_text() ]) )); -test_type!(inet(postgresql, "inet", Value::Text(None), Value::text("127.0.0.1"))); +test_type!(inet(postgresql, "inet", Value::null_text(), Value::text("127.0.0.1"))); test_type!(inet_array( postgresql, "inet[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::text("127.0.0.1"), Value::text("192.168.1.1"), - Value::Text(None) + Value::null_text() ]) )); test_type!(json( postgresql, "json", - Value::Json(None), + Value::null_json(), Value::json(serde_json::json!({"foo": "bar"})) )); test_type!(json_array( postgresql, "json[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::json(serde_json::json!({"foo": "bar"})), Value::json(serde_json::json!({"omg": false})), - Value::Json(None) + Value::null_json() ]) )); test_type!(jsonb( postgresql, "jsonb", - Value::Json(None), + Value::null_json(), Value::json(serde_json::json!({"foo": "bar"})) )); test_type!(jsonb_array( postgresql, "jsonb[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::json(serde_json::json!({"foo": "bar"})), Value::json(serde_json::json!({"omg": false})), - Value::Json(None) + Value::null_json() ]) )); -test_type!(xml(postgresql, "xml", Value::Xml(None), Value::xml("1",))); +test_type!(xml(postgresql, "xml", Value::null_xml(), Value::xml("1",))); test_type!(xml_array( postgresql, "xml[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::text("1"), Value::text("2"), - Value::Text(None) + Value::null_text() ]) )); @@ -313,7 +333,7 @@ test_type!(xml_array( test_type!(uuid( postgresql, "uuid", - Value::Uuid(None), + Value::null_uuid(), Value::uuid(uuid::Uuid::from_str("936DA01F-9ABD-4D9D-80C7-02AF85C822A8").unwrap()) )); @@ -321,89 +341,89 @@ test_type!(uuid( test_type!(uuid_array( postgresql, "uuid[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::uuid(uuid::Uuid::from_str("936DA01F-9ABD-4D9D-80C7-02AF85C822A8").unwrap()), - Value::Uuid(None) + Value::null_uuid(), ]) )); test_type!(date( postgresql, "date", - Value::Date(None), + Value::null_date(), Value::date(chrono::NaiveDate::from_ymd_opt(2020, 4, 20).unwrap()) )); test_type!(date_array( postgresql, "date[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::date(chrono::NaiveDate::from_ymd_opt(2020, 4, 20).unwrap()), - Value::Date(None) + Value::null_date() ]) )); test_type!(time( postgresql, "time", - Value::Time(None), + Value::null_time(), Value::time(chrono::NaiveTime::from_hms_opt(16, 20, 00).unwrap()) )); test_type!(time_array( postgresql, "time[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::time(chrono::NaiveTime::from_hms_opt(16, 20, 00).unwrap()), - Value::Time(None) + Value::null_time() ]) )); -test_type!(timestamp(postgresql, "timestamp", Value::DateTime(None), { +test_type!(timestamp(postgresql, "timestamp", Value::null_datetime(), { let dt = chrono::DateTime::parse_from_rfc3339("2020-02-27T19:10:22Z").unwrap(); Value::datetime(dt.with_timezone(&chrono::Utc)) })); -test_type!(timestamp_array(postgresql, "timestamp[]", Value::Array(None), { +test_type!(timestamp_array(postgresql, "timestamp[]", Value::null_array(), { let dt = chrono::DateTime::parse_from_rfc3339("2020-02-27T19:10:22Z").unwrap(); Value::array(vec![ Value::datetime(dt.with_timezone(&chrono::Utc)), - Value::DateTime(None), + Value::null_datetime(), ]) })); -test_type!(timestamptz(postgresql, "timestamptz", Value::DateTime(None), { +test_type!(timestamptz(postgresql, "timestamptz", Value::null_datetime(), { let dt = chrono::DateTime::parse_from_rfc3339("2020-02-27T19:10:22Z").unwrap(); Value::datetime(dt.with_timezone(&chrono::Utc)) })); -test_type!(timestamptz_array(postgresql, "timestamptz[]", Value::Array(None), { +test_type!(timestamptz_array(postgresql, "timestamptz[]", Value::null_array(), { let dt = chrono::DateTime::parse_from_rfc3339("2020-02-27T19:10:22Z").unwrap(); Value::array(vec![ Value::datetime(dt.with_timezone(&chrono::Utc)), - Value::DateTime(None), + Value::null_datetime(), ]) })); test_type!(bytea( postgresql, "bytea", - Value::Bytes(None), + Value::null_bytes(), Value::bytes(b"DEADBEEF".to_vec()) )); test_type!(bytea_array( postgresql, "bytea[]", - Value::Array(None), + Value::null_array(), Value::array(vec![ Value::bytes(b"DEADBEEF".to_vec()), Value::bytes(b"BEEFBEEF".to_vec()), - Value::Bytes(None) + Value::null_bytes() ]) )); diff --git a/quaint/src/tests/types/postgres/bigdecimal.rs b/quaint/src/tests/types/postgres/bigdecimal.rs index f79c23a8ad78..894b2c967629 100644 --- a/quaint/src/tests/types/postgres/bigdecimal.rs +++ b/quaint/src/tests/types/postgres/bigdecimal.rs @@ -4,7 +4,7 @@ use crate::bigdecimal::BigDecimal; test_type!(decimal( postgresql, "decimal(10,2)", - Value::Numeric(None), + Value::null_numeric(), Value::numeric(BigDecimal::from_str("3.14")?) )); @@ -177,28 +177,28 @@ test_type!(decimal_128_6( test_type!(decimal_array( postgresql, "decimal(10,2)[]", - Value::Array(None), + Value::null_array(), Value::array(vec![BigDecimal::from_str("3.14")?, BigDecimal::from_str("5.12")?]) )); test_type!(money( postgresql, "money", - Value::Numeric(None), + Value::null_numeric(), Value::numeric(BigDecimal::from_str("1.12")?) )); test_type!(money_array( postgresql, "money[]", - Value::Array(None), + Value::null_array(), Value::array(vec![BigDecimal::from_str("1.12")?, BigDecimal::from_str("1.12")?]) )); test_type!(float4( postgresql, "float4", - (Value::Numeric(None), Value::Float(None)), + (Value::null_numeric(), Value::null_float()), ( Value::numeric(BigDecimal::from_str("1.123456")?), Value::float(1.123456) @@ -208,7 +208,7 @@ test_type!(float4( test_type!(float8( postgresql, "float8", - (Value::Numeric(None), Value::Double(None)), + (Value::null_numeric(), Value::null_double()), ( Value::numeric(BigDecimal::from_str("1.123456")?), Value::double(1.123456) diff --git a/quaint/src/tests/types/sqlite.rs b/quaint/src/tests/types/sqlite.rs index 80ab4bb5b8f2..ac2c69131e50 100644 --- a/quaint/src/tests/types/sqlite.rs +++ b/quaint/src/tests/types/sqlite.rs @@ -9,7 +9,7 @@ use std::str::FromStr; test_type!(integer( sqlite, "INTEGER", - Value::Int32(None), + Value::null_int32(), Value::int32(i8::MIN), Value::int32(i8::MAX), Value::int32(i16::MIN), @@ -21,18 +21,18 @@ test_type!(integer( test_type!(big_int( sqlite, "BIGINT", - Value::Int64(None), + Value::null_int64(), Value::int64(i64::MIN), Value::int64(i64::MAX), )); -test_type!(real(sqlite, "REAL", Value::Double(None), Value::double(1.12345))); +test_type!(real(sqlite, "REAL", Value::null_double(), Value::double(1.12345))); #[cfg(feature = "bigdecimal")] test_type!(float_decimal( sqlite, "FLOAT", - (Value::Numeric(None), Value::Float(None)), + (Value::null_numeric(), Value::null_float()), ( Value::numeric(bigdecimal::BigDecimal::from_str("3.14").unwrap()), Value::double(3.14) @@ -43,35 +43,35 @@ test_type!(float_decimal( test_type!(double_decimal( sqlite, "DOUBLE", - (Value::Numeric(None), Value::Double(None)), + (Value::null_numeric(), Value::null_double()), ( Value::numeric(bigdecimal::BigDecimal::from_str("3.14").unwrap()), Value::double(3.14) ) )); -test_type!(text(sqlite, "TEXT", Value::Text(None), Value::text("foobar huhuu"))); +test_type!(text(sqlite, "TEXT", Value::null_text(), Value::text("foobar huhuu"))); test_type!(blob( sqlite, "BLOB", - Value::Bytes(None), + Value::null_bytes(), Value::bytes(b"DEADBEEF".to_vec()) )); -test_type!(float(sqlite, "FLOAT", Value::Float(None), Value::double(1.23))); +test_type!(float(sqlite, "FLOAT", Value::null_float(), Value::double(1.23))); test_type!(double( sqlite, "DOUBLE", - Value::Double(None), + Value::null_double(), Value::double(1.2312313213) )); test_type!(boolean( sqlite, "BOOLEAN", - Value::Boolean(None), + Value::null_boolean(), Value::boolean(true), Value::boolean(false) )); @@ -79,14 +79,14 @@ test_type!(boolean( test_type!(date( sqlite, "DATE", - Value::Date(None), + Value::null_date(), Value::date(chrono::NaiveDate::from_ymd_opt(1984, 1, 1).unwrap()) )); test_type!(datetime( sqlite, "DATETIME", - Value::DateTime(None), + Value::null_datetime(), Value::datetime(chrono::DateTime::from_str("2020-07-29T09:23:44.458Z").unwrap()) )); diff --git a/quaint/src/visitor.rs b/quaint/src/visitor.rs index 29ca3d5ccbaa..8424bc7fbb2b 100644 --- a/quaint/src/visitor.rs +++ b/quaint/src/visitor.rs @@ -149,7 +149,10 @@ pub trait Visitor<'a> { fn visit_text_search_relevance(&mut self, text_search_relevance: TextSearchRelevance<'a>) -> Result; fn visit_parameterized_enum(&mut self, variant: EnumVariant<'a>, name: Option>) -> Result { - self.add_parameter(Value::Enum(Some(variant), name)); + match name { + Some(name) => self.add_parameter(Value::enum_variant_with_name(variant, name)), + None => self.add_parameter(Value::enum_variant(variant)), + } self.parameter_substitution()?; Ok(()) @@ -161,7 +164,7 @@ pub trait Visitor<'a> { .map(|variant| variant.into_enum(name.clone())) .collect(); - self.add_parameter(Value::Array(Some(enum_variants))); + self.add_parameter(Value::array(enum_variants)); self.parameter_substitution()?; Ok(()) @@ -169,9 +172,9 @@ pub trait Visitor<'a> { /// A visit to a value we parameterize fn visit_parameterized(&mut self, value: Value<'a>) -> Result { - match value { - Value::Enum(Some(variant), name) => self.visit_parameterized_enum(variant, name), - Value::EnumArray(Some(variants), name) => self.visit_parameterized_enum_array(variants, name), + match value.typed { + ValueType::Enum(Some(variant), name) => self.visit_parameterized_enum(variant, name), + ValueType::EnumArray(Some(variants), name) => self.visit_parameterized_enum_array(variants, name), _ => { self.add_parameter(value); self.parameter_substitution() diff --git a/quaint/src/visitor/mssql.rs b/quaint/src/visitor/mssql.rs index 7e8249f369e0..4344b307f197 100644 --- a/quaint/src/visitor/mssql.rs +++ b/quaint/src/visitor/mssql.rs @@ -8,7 +8,7 @@ use crate::{ }, error::{Error, ErrorKind}, prelude::{Aliasable, Average, Query}, - visitor, Value, + visitor, Value, ValueType, }; use std::{convert::TryFrom, fmt::Write, iter}; @@ -310,27 +310,27 @@ impl<'a> Visitor<'a> for Mssql<'a> { } fn visit_raw_value(&mut self, value: Value<'a>) -> visitor::Result { - let res = match value { - Value::Int32(i) => i.map(|i| self.write(i)), - Value::Int64(i) => i.map(|i| self.write(i)), - Value::Float(d) => d.map(|f| match f { + let res = match value.typed { + ValueType::Int32(i) => i.map(|i| self.write(i)), + ValueType::Int64(i) => i.map(|i| self.write(i)), + ValueType::Float(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f32::INFINITY => self.write("'Infinity'"), f if f == f32::NEG_INFINITY => self.write("'-Infinity"), v => self.write(format!("{v:?}")), }), - Value::Double(d) => d.map(|f| match f { + ValueType::Double(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f64::INFINITY => self.write("'Infinity'"), f if f == f64::NEG_INFINITY => self.write("'-Infinity"), 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(_) | Value::EnumArray(_, _) => { + ValueType::Text(t) => t.map(|t| self.write(format!("'{t}'"))), + ValueType::Enum(e, _) => e.map(|e| self.write(e)), + ValueType::Bytes(b) => b.map(|b| self.write(format!("0x{}", hex::encode(b)))), + ValueType::Boolean(b) => b.map(|b| self.write(if b { 1 } else { 0 })), + ValueType::Char(c) => c.map(|c| self.write(format!("'{c}'"))), + ValueType::Array(_) | ValueType::EnumArray(_, _) => { let msg = "Arrays are not supported in T-SQL."; let kind = ErrorKind::conversion(msg); @@ -340,29 +340,29 @@ impl<'a> Visitor<'a> for Mssql<'a> { return Err(builder.build()); } - Value::Json(j) => j.map(|j| self.write(format!("'{}'", serde_json::to_string(&j).unwrap()))), + ValueType::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)), + ValueType::Numeric(r) => r.map(|r| self.write(r)), #[cfg(feature = "uuid")] - Value::Uuid(uuid) => uuid.map(|uuid| { + ValueType::Uuid(uuid) => uuid.map(|uuid| { let s = format!("CONVERT(uniqueidentifier, N'{}')", uuid.hyphenated()); self.write(s) }), - Value::DateTime(dt) => dt.map(|dt| { + ValueType::DateTime(dt) => dt.map(|dt| { let s = format!("CONVERT(datetimeoffset, N'{}')", dt.to_rfc3339()); self.write(s) }), - Value::Date(date) => date.map(|date| { + ValueType::Date(date) => date.map(|date| { let s = format!("CONVERT(date, N'{date}')"); self.write(s) }), - Value::Time(time) => time.map(|time| { + ValueType::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'{cow}', 3)"))), + ValueType::Xml(cow) => cow.map(|cow| self.write(format!("CONVERT(XML, N'{cow}', 3)"))), }; match res { @@ -391,7 +391,7 @@ impl<'a> Visitor<'a> for Mssql<'a> { self.visit_parameterized(limit)?; self.write(" ROWS ONLY") } - (None, Some(offset)) if self.order_by_set || offset.as_i64().map(|i| i > 0).unwrap_or(false) => { + (None, Some(offset)) if self.order_by_set || offset.typed.as_i64().map(|i| i > 0).unwrap_or(false) => { add_ordering(self)?; self.write(" OFFSET ")?; @@ -749,11 +749,11 @@ mod tests { #[test] fn test_aliased_null() { let expected_sql = "SELECT @P1 AS [test]"; - let query = Select::default().value(val!(Value::Int32(None)).alias("test")); + let query = Select::default().value(val!(ValueType::Int32(None)).alias("test")); let (sql, params) = Mssql::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::Int32(None)], params); + assert_eq!(vec![Value::null_int32()], params); } #[test] @@ -1192,7 +1192,7 @@ mod tests { #[test] fn test_raw_null() { - let (sql, params) = Mssql::build(Select::default().value(Value::Text(None).raw())).unwrap(); + let (sql, params) = Mssql::build(Select::default().value(ValueType::Text(None).raw())).unwrap(); assert_eq!("SELECT null", sql); assert!(params.is_empty()); } diff --git a/quaint/src/visitor/mysql.rs b/quaint/src/visitor/mysql.rs index d4587753f8f8..928c8a8a9ed6 100644 --- a/quaint/src/visitor/mysql.rs +++ b/quaint/src/visitor/mysql.rs @@ -120,27 +120,27 @@ impl<'a> Visitor<'a> for Mysql<'a> { } fn visit_raw_value(&mut self, value: Value<'a>) -> visitor::Result { - let res = match value { - Value::Int32(i) => i.map(|i| self.write(i)), - Value::Int64(i) => i.map(|i| self.write(i)), - Value::Float(d) => d.map(|f| match f { + let res = match &value.typed { + ValueType::Int32(i) => i.map(|i| self.write(i)), + ValueType::Int64(i) => i.map(|i| self.write(i)), + ValueType::Float(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f32::INFINITY => self.write("'Infinity'"), f if f == f32::NEG_INFINITY => self.write("'-Infinity"), v => self.write(format!("{v:?}")), }), - Value::Double(d) => d.map(|f| match f { + ValueType::Double(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f64::INFINITY => self.write("'Infinity'"), f if f == f64::NEG_INFINITY => self.write("'-Infinity"), 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!("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(_) | Value::EnumArray(_, _) => { + ValueType::Text(t) => t.as_ref().map(|t| self.write(format!("'{t}'"))), + ValueType::Enum(e, _) => e.as_ref().map(|e| self.write(e)), + ValueType::Bytes(b) => b.as_ref().map(|b| self.write(format!("x'{}'", hex::encode(b)))), + ValueType::Boolean(b) => b.map(|b| self.write(b)), + ValueType::Char(c) => c.map(|c| self.write(format!("'{c}'"))), + ValueType::Array(_) | ValueType::EnumArray(_, _) => { let msg = "Arrays are not supported in MySQL."; let kind = ErrorKind::conversion(msg); @@ -150,9 +150,9 @@ impl<'a> Visitor<'a> for Mysql<'a> { return Err(builder.build()); } #[cfg(feature = "bigdecimal")] - Value::Numeric(r) => r.map(|r| self.write(r)), + ValueType::Numeric(r) => r.as_ref().map(|r| self.write(r)), - Value::Json(j) => match j { + ValueType::Json(j) => match j { Some(ref j) => { let s = serde_json::to_string(&j)?; Some(self.write(format!("CONVERT('{s}', JSON)"))) @@ -160,11 +160,11 @@ impl<'a> Visitor<'a> for Mysql<'a> { None => None, }, #[cfg(feature = "uuid")] - Value::Uuid(uuid) => uuid.map(|uuid| self.write(format!("'{}'", uuid.hyphenated()))), - Value::DateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), - Value::Date(date) => date.map(|date| self.write(format!("'{date}'"))), - Value::Time(time) => time.map(|time| self.write(format!("'{time}'"))), - Value::Xml(cow) => cow.map(|cow| self.write(format!("'{cow}'"))), + ValueType::Uuid(uuid) => uuid.map(|uuid| self.write(format!("'{}'", uuid.hyphenated()))), + ValueType::DateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), + ValueType::Date(date) => date.map(|date| self.write(format!("'{date}'"))), + ValueType::Time(time) => time.map(|time| self.write(format!("'{time}'"))), + ValueType::Xml(cow) => cow.as_ref().map(|cow| self.write(format!("'{cow}'"))), }; match res { @@ -293,8 +293,20 @@ impl<'a> Visitor<'a> for Mysql<'a> { self.write(" OFFSET ")?; self.visit_parameterized(offset) } - (None, Some(Value::Int32(Some(offset)))) if offset < 1 => Ok(()), - (None, Some(Value::Int64(Some(offset)))) if offset < 1 => Ok(()), + ( + None, + Some(Value { + typed: ValueType::Int32(Some(offset)), + .. + }), + ) if offset < 1 => Ok(()), + ( + None, + Some(Value { + typed: ValueType::Int64(Some(offset)), + .. + }), + ) if offset < 1 => Ok(()), (None, Some(offset)) => { self.write(" LIMIT ")?; self.visit_parameterized(Value::from(9_223_372_036_854_775_807i64))?; @@ -421,27 +433,27 @@ impl<'a> Visitor<'a> for Mysql<'a> { match json_type { JsonType::Array => { - self.visit_expression(Value::text("ARRAY").into())?; + self.visit_expression(Expression::from(Value::text("ARRAY")))?; } JsonType::Boolean => { - self.visit_expression(Value::text("BOOLEAN").into())?; + self.visit_expression(Expression::from(Value::text("BOOLEAN")))?; } JsonType::Number => { - self.visit_expression(Value::text("INTEGER").into())?; + self.visit_expression(Expression::from(Value::text("INTEGER")))?; self.write(" OR JSON_TYPE(")?; self.visit_expression(left)?; self.write(")")?; self.write(" = ")?; - self.visit_expression(Value::text("DOUBLE").into())?; + self.visit_expression(Expression::from(Value::text("DOUBLE")))?; } JsonType::Object => { - self.visit_expression(Value::text("OBJECT").into())?; + self.visit_expression(Expression::from(Value::text("OBJECT")))?; } JsonType::String => { - self.visit_expression(Value::text("STRING").into())?; + self.visit_expression(Expression::from(Value::text("STRING")))?; } JsonType::Null => { - self.visit_expression(Value::text("NULL").into())?; + self.visit_expression(Expression::from(Value::text("NULL")))?; } JsonType::ColumnRef(column) => { self.write("JSON_TYPE")?; @@ -741,7 +753,7 @@ mod tests { #[test] fn test_raw_null() { - let (sql, params) = Mysql::build(Select::default().value(Value::Text(None).raw())).unwrap(); + let (sql, params) = Mysql::build(Select::default().value(ValueType::Text(None).raw())).unwrap(); assert_eq!("SELECT null", sql); assert!(params.is_empty()); } @@ -769,7 +781,7 @@ mod tests { #[test] fn test_raw_bytes() { - let (sql, params) = Mysql::build(Select::default().value(Value::bytes(vec![1, 2, 3]).raw())).unwrap(); + let (sql, params) = Mysql::build(Select::default().value(ValueType::bytes(vec![1, 2, 3]).raw())).unwrap(); assert_eq!("SELECT x'010203'", sql); assert!(params.is_empty()); } @@ -787,7 +799,7 @@ mod tests { #[test] fn test_raw_char() { - let (sql, params) = Mysql::build(Select::default().value(Value::character('a').raw())).unwrap(); + let (sql, params) = Mysql::build(Select::default().value(ValueType::character('a').raw())).unwrap(); assert_eq!("SELECT 'a'", sql); assert!(params.is_empty()); } @@ -897,7 +909,7 @@ mod tests { #[test] fn test_json_negation() { - let conditions = ConditionTree::not("json".equals(Value::Json(Some(serde_json::Value::Null)))); + let conditions = ConditionTree::not("json".equals(ValueType::Json(Some(serde_json::Value::Null)))); let (sql, _) = Mysql::build(Select::from_table("test").so_that(conditions)).unwrap(); assert_eq!( @@ -909,7 +921,7 @@ mod tests { #[test] fn test_json_not_negation() { - let conditions = ConditionTree::not("json".not_equals(Value::Json(Some(serde_json::Value::Null)))); + let conditions = ConditionTree::not("json".not_equals(ValueType::Json(Some(serde_json::Value::Null)))); let (sql, _) = Mysql::build(Select::from_table("test").so_that(conditions)).unwrap(); assert_eq!( diff --git a/quaint/src/visitor/postgres.rs b/quaint/src/visitor/postgres.rs index 0e36abe68c24..ec90eda8d6f5 100644 --- a/quaint/src/visitor/postgres.rs +++ b/quaint/src/visitor/postgres.rs @@ -107,12 +107,9 @@ impl<'a> Visitor<'a> for Postgres<'a> { self.surround_with_backticks(enum_name.name.deref())?; self.write("[]")?; } else { - self.visit_parameterized(Value::Array(Some( - variants - .into_iter() - .map(|variant| variant.into_enum(name.clone())) - .collect(), - )))?; + self.visit_parameterized(Value::array( + variants.into_iter().map(|variant| variant.into_enum(name.clone())), + ))?; } Ok(()) @@ -167,32 +164,32 @@ impl<'a> Visitor<'a> for Postgres<'a> { } fn visit_raw_value(&mut self, value: Value<'a>) -> visitor::Result { - let res = match value { - Value::Int32(i) => i.map(|i| self.write(i)), - Value::Int64(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)), - 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 { + let res = match &value.typed { + ValueType::Int32(i) => i.map(|i| self.write(i)), + ValueType::Int64(i) => i.map(|i| self.write(i)), + ValueType::Text(t) => t.as_ref().map(|t| self.write(format!("'{t}'"))), + ValueType::Enum(e, _) => e.as_ref().map(|e| self.write(e)), + ValueType::Bytes(b) => b.as_ref().map(|b| self.write(format!("E'{}'", hex::encode(b)))), + ValueType::Boolean(b) => b.map(|b| self.write(b)), + ValueType::Xml(cow) => cow.as_ref().map(|cow| self.write(format!("'{cow}'"))), + ValueType::Char(c) => c.map(|c| self.write(format!("'{c}'"))), + ValueType::Float(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f32::INFINITY => self.write("'Infinity'"), f if f == f32::NEG_INFINITY => self.write("'-Infinity"), v => self.write(format!("{v:?}")), }), - Value::Double(d) => d.map(|f| match f { + ValueType::Double(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f64::INFINITY => self.write("'Infinity'"), f if f == f64::NEG_INFINITY => self.write("'-Infinity"), v => self.write(format!("{v:?}")), }), - Value::Array(ary) => ary.map(|ary| { + ValueType::Array(ary) => ary.as_ref().map(|ary| { self.surround_with("'{", "}'", |ref mut s| { let len = ary.len(); - for (i, item) in ary.into_iter().enumerate() { + for (i, item) in ary.iter().enumerate() { s.write(item)?; if i < len - 1 { @@ -203,11 +200,11 @@ impl<'a> Visitor<'a> for Postgres<'a> { Ok(()) }) }), - Value::EnumArray(variants, name) => variants.map(|variants| { + ValueType::EnumArray(variants, name) => variants.as_ref().map(|variants| { self.surround_with("ARRAY[", "]", |ref mut s| { let len = variants.len(); - for (i, item) in variants.into_iter().enumerate() { + for (i, item) in variants.iter().enumerate() { s.surround_with("'", "'", |t| t.write(item))?; if i < len - 1 { @@ -220,7 +217,7 @@ impl<'a> Visitor<'a> for Postgres<'a> { if let Some(enum_name) = name { self.write("::")?; - if let Some(schema_name) = enum_name.schema_name { + if let Some(schema_name) = &enum_name.schema_name { self.surround_with_backticks(schema_name.deref())?; self.write(".")? } @@ -229,14 +226,16 @@ impl<'a> Visitor<'a> for Postgres<'a> { Ok(()) }), - Value::Json(j) => j.map(|j| self.write(format!("'{}'", serde_json::to_string(&j).unwrap()))), + ValueType::Json(j) => j + .as_ref() + .map(|j| self.write(format!("'{}'", serde_json::to_string(&j).unwrap()))), #[cfg(feature = "bigdecimal")] - Value::Numeric(r) => r.map(|r| self.write(r)), + ValueType::Numeric(r) => r.as_ref().map(|r| self.write(r)), #[cfg(feature = "uuid")] - Value::Uuid(uuid) => uuid.map(|uuid| self.write(format!("'{}'", uuid.hyphenated()))), - Value::DateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), - Value::Date(date) => date.map(|date| self.write(format!("'{date}'"))), - Value::Time(time) => time.map(|time| self.write(format!("'{time}'"))), + ValueType::Uuid(uuid) => uuid.map(|uuid| self.write(format!("'{}'", uuid.hyphenated()))), + ValueType::DateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), + ValueType::Date(date) => date.map(|date| self.write(format!("'{date}'"))), + ValueType::Time(time) => time.map(|time| self.write(format!("'{time}'"))), }; match res { @@ -961,7 +960,7 @@ mod tests { #[test] fn test_raw_null() { - let (sql, params) = Postgres::build(Select::default().value(Value::Text(None).raw())).unwrap(); + let (sql, params) = Postgres::build(Select::default().value(Value::null_text().raw())).unwrap(); assert_eq!("SELECT null", sql); assert!(params.is_empty()); } @@ -1050,9 +1049,9 @@ mod tests { #[test] fn test_raw_enum_array() { - let enum_array = Value::EnumArray( - Some(vec![EnumVariant::new("A"), EnumVariant::new("B")]), - Some(EnumName::new("Alphabet", Some("foo"))), + let enum_array = Value::enum_array_with_name( + vec![EnumVariant::new("A"), EnumVariant::new("B")], + EnumName::new("Alphabet", Some("foo")), ); let (sql, params) = Postgres::build(Select::default().value(enum_array.raw())).unwrap(); diff --git a/quaint/src/visitor/sqlite.rs b/quaint/src/visitor/sqlite.rs index 209758bbeb20..45b9a82468ef 100644 --- a/quaint/src/visitor/sqlite.rs +++ b/quaint/src/visitor/sqlite.rs @@ -74,27 +74,27 @@ impl<'a> Visitor<'a> for Sqlite<'a> { } fn visit_raw_value(&mut self, value: Value<'a>) -> visitor::Result { - let res = match value { - Value::Int32(i) => i.map(|i| self.write(i)), - Value::Int64(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)), - 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 { + let res = match &value.typed { + ValueType::Int32(i) => i.map(|i| self.write(i)), + ValueType::Int64(i) => i.map(|i| self.write(i)), + ValueType::Text(t) => t.as_ref().map(|t| self.write(format!("'{t}'"))), + ValueType::Enum(e, _) => e.as_ref().map(|e| self.write(e)), + ValueType::Bytes(b) => b.as_ref().map(|b| self.write(format!("x'{}'", hex::encode(b)))), + ValueType::Boolean(b) => b.map(|b| self.write(b)), + ValueType::Char(c) => c.map(|c| self.write(format!("'{c}'"))), + ValueType::Float(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f32::INFINITY => self.write("'Infinity'"), f if f == f32::NEG_INFINITY => self.write("'-Infinity"), v => self.write(format!("{v:?}")), }), - Value::Double(d) => d.map(|f| match f { + ValueType::Double(d) => d.map(|f| match f { f if f.is_nan() => self.write("'NaN'"), f if f == f64::INFINITY => self.write("'Infinity'"), f if f == f64::NEG_INFINITY => self.write("'-Infinity"), v => self.write(format!("{v:?}")), }), - Value::Array(_) | Value::EnumArray(_, _) => { + ValueType::Array(_) | ValueType::EnumArray(_, _) => { let msg = "Arrays are not supported in SQLite."; let kind = ErrorKind::conversion(msg); @@ -104,7 +104,7 @@ impl<'a> Visitor<'a> for Sqlite<'a> { return Err(builder.build()); } - Value::Json(j) => match j { + ValueType::Json(j) => match j { Some(ref j) => { let s = serde_json::to_string(j)?; Some(self.write(format!("'{s}'"))) @@ -112,13 +112,13 @@ impl<'a> Visitor<'a> for Sqlite<'a> { None => None, }, #[cfg(feature = "bigdecimal")] - Value::Numeric(r) => r.map(|r| self.write(r)), + ValueType::Numeric(r) => r.as_ref().map(|r| self.write(r)), #[cfg(feature = "uuid")] - Value::Uuid(uuid) => uuid.map(|uuid| self.write(format!("'{}'", uuid.hyphenated()))), - Value::DateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), - Value::Date(date) => date.map(|date| self.write(format!("'{date}'"))), - Value::Time(time) => time.map(|time| self.write(format!("'{time}'"))), - Value::Xml(cow) => cow.map(|cow| self.write(format!("'{cow}'"))), + ValueType::Uuid(uuid) => uuid.map(|uuid| self.write(format!("'{}'", uuid.hyphenated()))), + ValueType::DateTime(dt) => dt.map(|dt| self.write(format!("'{}'", dt.to_rfc3339(),))), + ValueType::Date(date) => date.map(|date| self.write(format!("'{date}'"))), + ValueType::Time(time) => time.map(|time| self.write(format!("'{time}'"))), + ValueType::Xml(cow) => cow.as_ref().map(|cow| self.write(format!("'{cow}'"))), }; match res { @@ -432,11 +432,11 @@ mod tests { #[test] fn test_aliased_null() { let expected_sql = "SELECT ? AS `test`"; - let query = Select::default().value(val!(Value::Text(None)).alias("test")); + let query = Select::default().value(val!(Value::null_text()).alias("test")); let (sql, params) = Sqlite::build(query).unwrap(); assert_eq!(expected_sql, sql); - assert_eq!(vec![Value::Text(None)], params); + assert_eq!(vec![Value::null_text()], params); } #[test] @@ -861,7 +861,7 @@ mod tests { #[test] fn test_raw_null() { - let (sql, params) = Sqlite::build(Select::default().value(Value::Text(None).raw())).unwrap(); + let (sql, params) = Sqlite::build(Select::default().value(Value::null_text().raw())).unwrap(); assert_eq!("SELECT null", sql); assert!(params.is_empty()); } diff --git a/query-engine/connector-test-kit-rs/query-tests-setup/src/config.rs b/query-engine/connector-test-kit-rs/query-tests-setup/src/config.rs index 37f3bc89bbd4..b27f27406e5c 100644 --- a/query-engine/connector-test-kit-rs/query-tests-setup/src/config.rs +++ b/query-engine/connector-test-kit-rs/query-tests-setup/src/config.rs @@ -215,7 +215,11 @@ impl TestConfig { #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; - if path.metadata().is_ok_and(|md| md.permissions().mode() & 0o111 == 0) { + let is_executable = match path.metadata() { + Err(_) => false, + Ok(md) => md.permissions().mode() & 0o111 != 0, + }; + if !is_executable { exit_with_message(&format!( "The external test executor file `{}` must be have permissions to execute", file diff --git a/query-engine/connectors/mongodb-query-connector/src/query_builder/group_by_builder.rs b/query-engine/connectors/mongodb-query-connector/src/query_builder/group_by_builder.rs index b7667a45825f..4ea3d4590446 100644 --- a/query-engine/connectors/mongodb-query-connector/src/query_builder/group_by_builder.rs +++ b/query-engine/connectors/mongodb-query-connector/src/query_builder/group_by_builder.rs @@ -39,7 +39,7 @@ impl std::fmt::Display for AggregationType { impl GroupByBuilder { pub fn new() -> Self { - Self { ..Default::default() } + Default::default() } pub fn render(&self, by_fields: Vec) -> (Document, Option) { diff --git a/query-engine/connectors/sql-query-connector/src/cursor_condition.rs b/query-engine/connectors/sql-query-connector/src/cursor_condition.rs index 79e61523dec2..34373eaf3d5b 100644 --- a/query-engine/connectors/sql-query-connector/src/cursor_condition.rs +++ b/query-engine/connectors/sql-query-connector/src/cursor_condition.rs @@ -469,7 +469,7 @@ fn cursor_order_def_aggregation_scalar( order_by: &OrderByScalarAggregation, order_by_def: &OrderByDefinition, ) -> CursorOrderDefinition { - let coalesce_exprs: Vec = vec![order_by_def.order_column.clone(), Value::integer(0).into()]; + let coalesce_exprs: Vec = vec![order_by_def.order_column.clone(), Value::int32(0).into()]; // We coalesce the order column to 0 when it's compared to the cmp table since the aggregations joins // might return NULL on relations that have no connected records @@ -493,7 +493,7 @@ fn cursor_order_def_aggregation_rel( // cf: part #2 of the SQL query above, when a field is nullable. let fks = foreign_keys_from_order_path(&order_by.path, &order_by_def.joins); - let coalesce_exprs: Vec = vec![order_by_def.order_column.clone(), Value::integer(0).into()]; + let coalesce_exprs: Vec = vec![order_by_def.order_column.clone(), Value::int32(0).into()]; // We coalesce the order column to 0 when it's compared to the cmp table since the aggregations joins // might return NULL on relations that have no connected records let order_column: Expression = coalesce(coalesce_exprs).into(); diff --git a/query-engine/connectors/sql-query-connector/src/filter/visitor.rs b/query-engine/connectors/sql-query-connector/src/filter/visitor.rs index 26796bf79121..099967177b55 100644 --- a/query-engine/connectors/sql-query-connector/src/filter/visitor.rs +++ b/query-engine/connectors/sql-query-connector/src/filter/visitor.rs @@ -557,8 +557,8 @@ impl FilterVisitorExt for FilterVisitor { ScalarListCondition::ContainsSome(ConditionListValue::FieldRef(field_ref)) => { comparable.compare_raw("&&", field_ref.aliased_col(alias, ctx)) } - ScalarListCondition::IsEmpty(true) => comparable.compare_raw("=", Value::Array(Some(vec![])).raw()), - ScalarListCondition::IsEmpty(false) => comparable.compare_raw("<>", Value::Array(Some(vec![])).raw()), + ScalarListCondition::IsEmpty(true) => comparable.compare_raw("=", ValueType::Array(Some(vec![])).raw()), + ScalarListCondition::IsEmpty(false) => comparable.compare_raw("<>", ValueType::Array(Some(vec![])).raw()), }; ConditionTree::single(condition) @@ -1120,7 +1120,7 @@ fn convert_pv<'a>(field: &ScalarFieldRef, pv: PrismaValue, ctx: &Context<'_>) -> } fn convert_list_pv<'a>(field: &ScalarFieldRef, values: Vec, ctx: &Context<'_>) -> Expression<'a> { - Value::Array(Some(values.into_iter().map(|val| field.value(val, ctx)).collect())).into() + Expression::from(Value::array(values.into_iter().map(|val| field.value(val, ctx)))) } fn convert_pvs<'a>(fields: &[ScalarFieldRef], values: Vec, ctx: &Context<'_>) -> Vec> { diff --git a/query-engine/connectors/sql-query-connector/src/model_extensions/scalar_field.rs b/query-engine/connectors/sql-query-connector/src/model_extensions/scalar_field.rs index 1250fbf88f67..b8ea590f25dc 100644 --- a/query-engine/connectors/sql-query-connector/src/model_extensions/scalar_field.rs +++ b/query-engine/connectors/sql-query-connector/src/model_extensions/scalar_field.rs @@ -3,7 +3,7 @@ use chrono::Utc; use prisma_models::{ScalarField, TypeIdentifier}; use prisma_value::PrismaValue; use quaint::{ - ast::{EnumName, Value}, + ast::{EnumName, Value, ValueType}, prelude::{EnumVariant, TypeDataLength, TypeFamily}, }; @@ -27,7 +27,7 @@ impl ScalarFieldExt for ScalarField { .map(ToOwned::to_owned) .or(Some(ctx.schema_name().to_owned())); - Value::enum_variant_with_name(e, enum_name, schema_name) + Value::enum_variant_with_name(e, EnumName::new(enum_name, schema_name)) } (PrismaValue::List(vals), TypeIdentifier::Enum(enum_id)) => { let enum_walker = self.dm.clone().zip(enum_id); @@ -43,21 +43,21 @@ impl ScalarFieldExt for ScalarField { .map(ToOwned::to_owned) .or(Some(ctx.schema_name().to_owned())); - Value::EnumArray(Some(variants), Some(EnumName::new(enum_name, schema_name))) + Value::enum_array_with_name(variants, EnumName::new(enum_name, schema_name)) } (PrismaValue::Enum(e), _) => e.into(), (PrismaValue::Int(i), _) => i.into(), (PrismaValue::BigInt(i), _) => i.into(), (PrismaValue::Uuid(u), _) => u.to_string().into(), - (PrismaValue::List(l), _) => Value::Array(Some(l.into_iter().map(|x| self.value(x, ctx)).collect())), - (PrismaValue::Json(s), _) => Value::Json(Some(serde_json::from_str::(&s).unwrap())), - (PrismaValue::Bytes(b), _) => Value::Bytes(Some(b.into())), + (PrismaValue::List(l), _) => Value::array(l.into_iter().map(|x| self.value(x, ctx))), + (PrismaValue::Json(s), _) => Value::json(serde_json::from_str::(&s).unwrap()), + (PrismaValue::Bytes(b), _) => Value::bytes(b), (PrismaValue::Object(_), _) => unimplemented!(), (PrismaValue::Null, ident) => match ident { - TypeIdentifier::String => Value::Text(None), - TypeIdentifier::Float => Value::Numeric(None), - TypeIdentifier::Decimal => Value::Numeric(None), - TypeIdentifier::Boolean => Value::Boolean(None), + TypeIdentifier::String => Value::null_text(), + TypeIdentifier::Float => Value::null_numeric(), + TypeIdentifier::Decimal => Value::null_numeric(), + TypeIdentifier::Boolean => Value::null_boolean(), TypeIdentifier::Enum(enum_id) => { let enum_walker = self.dm.clone().zip(enum_id); let enum_name = enum_walker.db_name().to_owned(); @@ -66,14 +66,14 @@ impl ScalarFieldExt for ScalarField { .map(ToOwned::to_owned) .or(Some(ctx.schema_name().to_owned())); - Value::Enum(None, Some(EnumName::new(enum_name, schema_name))) + ValueType::Enum(None, Some(EnumName::new(enum_name, schema_name))).into_value() } - TypeIdentifier::Json => Value::Json(None), - TypeIdentifier::DateTime => Value::DateTime(None), - TypeIdentifier::UUID => Value::Uuid(None), - TypeIdentifier::Int => Value::Int32(None), - TypeIdentifier::BigInt => Value::Int64(None), - TypeIdentifier::Bytes => Value::Bytes(None), + TypeIdentifier::Json => Value::null_json(), + TypeIdentifier::DateTime => Value::null_datetime(), + TypeIdentifier::UUID => Value::null_uuid(), + TypeIdentifier::Int => Value::null_int32(), + TypeIdentifier::BigInt => Value::null_int64(), + TypeIdentifier::Bytes => Value::null_bytes(), TypeIdentifier::Unsupported => unreachable!("No unsupported field should reach that path"), }, } @@ -117,10 +117,10 @@ pub fn convert_lossy<'a>(pv: PrismaValue) -> Value<'a> { PrismaValue::Int(i) => i.into(), PrismaValue::BigInt(i) => i.into(), PrismaValue::Uuid(u) => u.to_string().into(), - PrismaValue::List(l) => Value::Array(Some(l.into_iter().map(convert_lossy).collect())), - PrismaValue::Json(s) => Value::Json(serde_json::from_str(&s).unwrap()), - PrismaValue::Bytes(b) => Value::Bytes(Some(b.into())), - PrismaValue::Null => Value::Int32(None), // Can't tell which type the null is supposed to be. + PrismaValue::List(l) => Value::array(l.into_iter().map(convert_lossy)), + PrismaValue::Json(s) => Value::json(serde_json::from_str(&s).unwrap()), + PrismaValue::Bytes(b) => Value::bytes(b), + PrismaValue::Null => Value::null_int32(), // Can't tell which type the null is supposed to be. PrismaValue::Object(_) => unimplemented!(), } } diff --git a/query-engine/connectors/sql-query-connector/src/ordering.rs b/query-engine/connectors/sql-query-connector/src/ordering.rs index 7ab1bc03d3ce..cf49698405ef 100644 --- a/query-engine/connectors/sql-query-connector/src/ordering.rs +++ b/query-engine/connectors/sql-query-connector/src/ordering.rs @@ -115,7 +115,7 @@ impl OrderByBuilder { let (joins, order_column) = self.compute_joins_aggregation(order_by, ctx); let order_definition: OrderDefinition = match order_by.sort_aggregation { SortAggregation::Count => { - let exprs: Vec = vec![order_column.clone().into(), Value::integer(0).into()]; + let exprs: Vec = vec![order_column.clone().into(), Value::int32(0).into()]; // We coalesce the order by expr to 0 so that if there's no relation, // `COALESCE(NULL, 0)` will return `0`, thus preserving the order diff --git a/query-engine/connectors/sql-query-connector/src/row.rs b/query-engine/connectors/sql-query-connector/src/row.rs index 9f0b69e73ff7..250ee7d9420f 100644 --- a/query-engine/connectors/sql-query-connector/src/row.rs +++ b/query-engine/connectors/sql-query-connector/src/row.rs @@ -3,7 +3,7 @@ use bigdecimal::{BigDecimal, FromPrimitive, ToPrimitive}; use chrono::{DateTime, NaiveDate, Utc}; use connector_interface::{coerce_null_to_zero_value, AggregationResult, AggregationSelection}; use prisma_models::{ConversionFailure, FieldArity, PrismaValue, Record, TypeIdentifier}; -use quaint::{ast::Value, connector::ResultRow}; +use quaint::{connector::ResultRow, Value, ValueType}; use std::{io, str::FromStr}; use uuid::Uuid; @@ -92,12 +92,12 @@ impl ToSqlRow for ResultRow { for (i, p_value) in self.into_iter().enumerate().take(row_width) { let pv = match (meta[i].identifier(), meta[i].arity()) { - (type_identifier, FieldArity::List) => match p_value { + (type_identifier, FieldArity::List) => match p_value.typed { value if value.is_null() => Ok(PrismaValue::List(Vec::new())), - Value::Array(None) => Ok(PrismaValue::List(Vec::new())), - Value::Array(Some(l)) => l + ValueType::Array(None) => Ok(PrismaValue::List(Vec::new())), + ValueType::Array(Some(l)) => l .into_iter() - .map(|p_value| row_value_to_prisma_value(p_value, meta[i])) + .map(|val| row_value_to_prisma_value(val, meta[i])) .collect::>>() .map(PrismaValue::List), _ => { @@ -140,35 +140,35 @@ fn row_value_to_prisma_value(p_value: Value, meta: ColumnMetadata<'_>) -> Result }; Ok(match meta.identifier() { - TypeIdentifier::Boolean => match p_value { + TypeIdentifier::Boolean => match p_value.typed { value if value.is_null() => PrismaValue::Null, - Value::Int32(Some(i)) => PrismaValue::Boolean(i != 0), - Value::Int64(Some(i)) => PrismaValue::Boolean(i != 0), - Value::Boolean(Some(b)) => PrismaValue::Boolean(b), - Value::Bytes(Some(bytes)) if bytes.as_ref() == [0u8] => PrismaValue::Boolean(false), - Value::Bytes(Some(bytes)) if bytes.as_ref() == [1u8] => PrismaValue::Boolean(true), + ValueType::Int32(Some(i)) => PrismaValue::Boolean(i != 0), + ValueType::Int64(Some(i)) => PrismaValue::Boolean(i != 0), + ValueType::Boolean(Some(b)) => PrismaValue::Boolean(b), + ValueType::Bytes(Some(bytes)) if bytes.as_ref() == [0u8] => PrismaValue::Boolean(false), + ValueType::Bytes(Some(bytes)) if bytes.as_ref() == [1u8] => PrismaValue::Boolean(true), _ => return Err(create_error(&p_value)), }, - TypeIdentifier::Enum(_) => match p_value { + TypeIdentifier::Enum(_) => match p_value.typed { value if value.is_null() => PrismaValue::Null, - Value::Enum(Some(cow), _) => PrismaValue::Enum(cow.into_owned()), - Value::Text(Some(cow)) => PrismaValue::Enum(cow.into_owned()), + ValueType::Enum(Some(cow), _) => PrismaValue::Enum(cow.into_owned()), + ValueType::Text(Some(cow)) => PrismaValue::Enum(cow.into_owned()), _ => return Err(create_error(&p_value)), }, - TypeIdentifier::Json => match p_value { + TypeIdentifier::Json => match p_value.typed { value if value.is_null() => PrismaValue::Null, - Value::Text(Some(json)) => PrismaValue::Json(json.into()), - Value::Json(Some(json)) => PrismaValue::Json(json.to_string()), + ValueType::Text(Some(json)) => PrismaValue::Json(json.into()), + ValueType::Json(Some(json)) => PrismaValue::Json(json.to_string()), _ => return Err(create_error(&p_value)), }, - TypeIdentifier::UUID => match p_value { + TypeIdentifier::UUID => match p_value.typed { value if value.is_null() => PrismaValue::Null, - Value::Text(Some(uuid)) => PrismaValue::Uuid(Uuid::parse_str(&uuid)?), - Value::Uuid(Some(uuid)) => PrismaValue::Uuid(uuid), + ValueType::Text(Some(uuid)) => PrismaValue::Uuid(Uuid::parse_str(&uuid)?), + ValueType::Uuid(Some(uuid)) => PrismaValue::Uuid(uuid), _ => return Err(create_error(&p_value)), }, - TypeIdentifier::DateTime => match p_value { + TypeIdentifier::DateTime => match p_value.typed { value if value.is_null() => PrismaValue::Null, value if value.is_integer() => { let ts = value.as_integer().unwrap(); @@ -179,47 +179,47 @@ fn row_value_to_prisma_value(p_value: Value, meta: ColumnMetadata<'_>) -> Result PrismaValue::DateTime(datetime.into()) } - Value::DateTime(Some(dt)) => PrismaValue::DateTime(dt.into()), - Value::Text(Some(ref dt_string)) => { + ValueType::DateTime(Some(dt)) => PrismaValue::DateTime(dt.into()), + ValueType::Text(Some(ref dt_string)) => { let dt = DateTime::parse_from_rfc3339(dt_string) .or_else(|_| DateTime::parse_from_rfc2822(dt_string)) .map_err(|_| create_error(&p_value))?; PrismaValue::DateTime(dt.with_timezone(&Utc).into()) } - Value::Date(Some(d)) => { + ValueType::Date(Some(d)) => { let dt = DateTime::::from_utc(d.and_hms_opt(0, 0, 0).unwrap(), Utc); PrismaValue::DateTime(dt.into()) } - Value::Time(Some(t)) => { + ValueType::Time(Some(t)) => { let d = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(); let dt = DateTime::::from_utc(d.and_time(t), Utc); PrismaValue::DateTime(dt.into()) } _ => return Err(create_error(&p_value)), }, - TypeIdentifier::Float | TypeIdentifier::Decimal => match p_value { + TypeIdentifier::Float | TypeIdentifier::Decimal => match p_value.typed { value if value.is_null() => PrismaValue::Null, - Value::Numeric(Some(f)) => PrismaValue::Float(f.normalized()), - Value::Double(Some(f)) => match f { + ValueType::Numeric(Some(f)) => PrismaValue::Float(f.normalized()), + ValueType::Double(Some(f)) => match f { f if f.is_nan() => return Err(create_error(&p_value)), f if f.is_infinite() => return Err(create_error(&p_value)), _ => PrismaValue::Float(BigDecimal::from_f64(f).unwrap().normalized()), }, - Value::Float(Some(f)) => match f { + ValueType::Float(Some(f)) => match f { f if f.is_nan() => return Err(create_error(&p_value)), f if f.is_infinite() => return Err(create_error(&p_value)), _ => PrismaValue::Float(BigDecimal::from_f32(f).unwrap().normalized()), }, - Value::Int32(Some(i)) => match BigDecimal::from_i32(i) { + ValueType::Int32(Some(i)) => match BigDecimal::from_i32(i) { Some(dec) => PrismaValue::Float(dec), None => return Err(create_error(&p_value)), }, - Value::Int64(Some(i)) => match BigDecimal::from_i64(i) { + ValueType::Int64(Some(i)) => match BigDecimal::from_i64(i) { Some(dec) => PrismaValue::Float(dec), None => return Err(create_error(&p_value)), }, - Value::Text(_) | Value::Bytes(_) => { + ValueType::Text(_) | ValueType::Bytes(_) => { let dec: BigDecimal = p_value .as_str() .expect("text/bytes as str") @@ -230,61 +230,61 @@ fn row_value_to_prisma_value(p_value: Value, meta: ColumnMetadata<'_>) -> Result } _ => return Err(create_error(&p_value)), }, - TypeIdentifier::Int => match p_value { + TypeIdentifier::Int => match p_value.typed { value if value.is_null() => PrismaValue::Null, - Value::Int32(Some(i)) => PrismaValue::Int(i as i64), - Value::Int64(Some(i)) => PrismaValue::Int(i), - Value::Bytes(Some(bytes)) => PrismaValue::Int(interpret_bytes_as_i64(&bytes)), - Value::Text(Some(ref txt)) => { + ValueType::Int32(Some(i)) => PrismaValue::Int(i as i64), + ValueType::Int64(Some(i)) => PrismaValue::Int(i), + ValueType::Bytes(Some(bytes)) => PrismaValue::Int(interpret_bytes_as_i64(&bytes)), + ValueType::Text(Some(ref txt)) => { PrismaValue::Int(i64::from_str(txt.trim_start_matches('\0')).map_err(|_| create_error(&p_value))?) } - Value::Float(Some(f)) => { + ValueType::Float(Some(f)) => { sanitize_f32(f, "Int")?; PrismaValue::Int(big_decimal_to_i64(BigDecimal::from_f32(f).unwrap(), "Int")?) } - Value::Double(Some(f)) => { + ValueType::Double(Some(f)) => { sanitize_f64(f, "Int")?; PrismaValue::Int(big_decimal_to_i64(BigDecimal::from_f64(f).unwrap(), "Int")?) } - Value::Numeric(Some(dec)) => PrismaValue::Int(big_decimal_to_i64(dec, "Int")?), - Value::Boolean(Some(bool)) => PrismaValue::Int(bool as i64), + ValueType::Numeric(Some(dec)) => PrismaValue::Int(big_decimal_to_i64(dec, "Int")?), + ValueType::Boolean(Some(bool)) => PrismaValue::Int(bool as i64), other => to_prisma_value(other)?, }, - TypeIdentifier::BigInt => match p_value { + TypeIdentifier::BigInt => match p_value.typed { value if value.is_null() => PrismaValue::Null, - Value::Int32(Some(i)) => PrismaValue::BigInt(i as i64), - Value::Int64(Some(i)) => PrismaValue::BigInt(i), - Value::Bytes(Some(bytes)) => PrismaValue::BigInt(interpret_bytes_as_i64(&bytes)), - Value::Text(Some(ref txt)) => { + ValueType::Int32(Some(i)) => PrismaValue::BigInt(i as i64), + ValueType::Int64(Some(i)) => PrismaValue::BigInt(i), + ValueType::Bytes(Some(bytes)) => PrismaValue::BigInt(interpret_bytes_as_i64(&bytes)), + ValueType::Text(Some(ref txt)) => { PrismaValue::BigInt(i64::from_str(txt.trim_start_matches('\0')).map_err(|_| create_error(&p_value))?) } - Value::Float(Some(f)) => { + ValueType::Float(Some(f)) => { sanitize_f32(f, "BigInt")?; PrismaValue::BigInt(big_decimal_to_i64(BigDecimal::from_f32(f).unwrap(), "BigInt")?) } - Value::Double(Some(f)) => { + ValueType::Double(Some(f)) => { sanitize_f64(f, "BigInt")?; PrismaValue::BigInt(big_decimal_to_i64(BigDecimal::from_f64(f).unwrap(), "BigInt")?) } - Value::Numeric(Some(dec)) => PrismaValue::BigInt(big_decimal_to_i64(dec, "BigInt")?), - Value::Boolean(Some(bool)) => PrismaValue::BigInt(bool as i64), + ValueType::Numeric(Some(dec)) => PrismaValue::BigInt(big_decimal_to_i64(dec, "BigInt")?), + ValueType::Boolean(Some(bool)) => PrismaValue::BigInt(bool as i64), other => to_prisma_value(other)?, }, - TypeIdentifier::String => match p_value { + TypeIdentifier::String => match p_value.typed { value if value.is_null() => PrismaValue::Null, - Value::Uuid(Some(uuid)) => PrismaValue::String(uuid.to_string()), - Value::Json(Some(ref json_value)) => { + ValueType::Uuid(Some(uuid)) => PrismaValue::String(uuid.to_string()), + ValueType::Json(Some(ref json_value)) => { PrismaValue::String(serde_json::to_string(json_value).map_err(|_| create_error(&p_value))?) } other => to_prisma_value(other)?, }, - TypeIdentifier::Bytes => match p_value { + TypeIdentifier::Bytes => match p_value.typed { value if value.is_null() => PrismaValue::Null, - Value::Bytes(Some(bytes)) => PrismaValue::Bytes(bytes.into()), + ValueType::Bytes(Some(bytes)) => PrismaValue::Bytes(bytes.into()), _ => return Err(create_error(&p_value)), }, TypeIdentifier::Unsupported => unreachable!("No unsupported field should reach that path"), diff --git a/query-engine/connectors/sql-query-connector/src/value.rs b/query-engine/connectors/sql-query-connector/src/value.rs index 8ccf89288071..4c31fc9eedb9 100644 --- a/query-engine/connectors/sql-query-connector/src/value.rs +++ b/query-engine/connectors/sql-query-connector/src/value.rs @@ -2,44 +2,44 @@ use crate::row::{sanitize_f32, sanitize_f64}; use bigdecimal::{BigDecimal, FromPrimitive}; use chrono::{DateTime, NaiveDate, Utc}; use prisma_models::PrismaValue; -use quaint::Value; +use quaint::ValueType; -pub fn to_prisma_value(quaint_value: Value<'_>) -> crate::Result { - let val = match quaint_value { - Value::Int32(i) => i.map(|i| PrismaValue::Int(i as i64)).unwrap_or(PrismaValue::Null), - Value::Int64(i) => i.map(PrismaValue::Int).unwrap_or(PrismaValue::Null), - Value::Float(Some(f)) => { +pub fn to_prisma_value<'a, T: Into>>(qv: T) -> crate::Result { + let val = match qv.into() { + ValueType::Int32(i) => i.map(|i| PrismaValue::Int(i as i64)).unwrap_or(PrismaValue::Null), + ValueType::Int64(i) => i.map(PrismaValue::Int).unwrap_or(PrismaValue::Null), + ValueType::Float(Some(f)) => { sanitize_f32(f, "BigDecimal")?; PrismaValue::Float(BigDecimal::from_f32(f).unwrap().normalized()) } - Value::Float(None) => PrismaValue::Null, + ValueType::Float(None) => PrismaValue::Null, - Value::Double(Some(f)) => { + ValueType::Double(Some(f)) => { sanitize_f64(f, "BigDecimal")?; PrismaValue::Float(BigDecimal::from_f64(f).unwrap().normalized()) } - Value::Double(None) => PrismaValue::Null, + ValueType::Double(None) => PrismaValue::Null, - Value::Numeric(d) => d + ValueType::Numeric(d) => d // chop the trailing zeroes off so javascript doesn't start rounding things wrong .map(|d| PrismaValue::Float(d.normalized())) .unwrap_or(PrismaValue::Null), - Value::Text(s) => s + ValueType::Text(s) => s .map(|s| PrismaValue::String(s.into_owned())) .unwrap_or(PrismaValue::Null), - Value::Enum(s, _) => s + ValueType::Enum(s, _) => s .map(|s| PrismaValue::Enum(s.into_owned())) .unwrap_or(PrismaValue::Null), - Value::Boolean(b) => b.map(PrismaValue::Boolean).unwrap_or(PrismaValue::Null), + ValueType::Boolean(b) => b.map(PrismaValue::Boolean).unwrap_or(PrismaValue::Null), - Value::Array(Some(v)) => { + ValueType::Array(Some(v)) => { let mut res = Vec::with_capacity(v.len()); for v in v.into_iter() { @@ -49,33 +49,33 @@ pub fn to_prisma_value(quaint_value: Value<'_>) -> crate::Result { PrismaValue::List(res) } - Value::Array(None) => PrismaValue::Null, + ValueType::Array(None) => PrismaValue::Null, - Value::EnumArray(Some(v), name) => { + ValueType::EnumArray(Some(v), name) => { let mut res = Vec::with_capacity(v.len()); for v in v.into_iter() { - res.push(to_prisma_value(Value::Enum(Some(v), name.clone()))?); + res.push(to_prisma_value(ValueType::Enum(Some(v), name.clone()))?); } PrismaValue::List(res) } - Value::EnumArray(None, _) => PrismaValue::Null, + ValueType::EnumArray(None, _) => PrismaValue::Null, - Value::Json(val) => val + ValueType::Json(val) => val .map(|val| PrismaValue::Json(val.to_string())) .unwrap_or(PrismaValue::Null), - Value::Uuid(uuid) => uuid.map(PrismaValue::Uuid).unwrap_or(PrismaValue::Null), + ValueType::Uuid(uuid) => uuid.map(PrismaValue::Uuid).unwrap_or(PrismaValue::Null), - Value::Date(d) => d + ValueType::Date(d) => d .map(|d| { let dt = DateTime::::from_utc(d.and_hms_opt(0, 0, 0).unwrap(), Utc); PrismaValue::DateTime(dt.into()) }) .unwrap_or(PrismaValue::Null), - Value::Time(t) => t + ValueType::Time(t) => t .map(|t| { let d = NaiveDate::from_ymd_opt(1970, 1, 1).unwrap(); let dt = DateTime::::from_utc(d.and_time(t), Utc); @@ -83,19 +83,19 @@ pub fn to_prisma_value(quaint_value: Value<'_>) -> crate::Result { }) .unwrap_or(PrismaValue::Null), - Value::DateTime(dt) => dt + ValueType::DateTime(dt) => dt .map(|dt| PrismaValue::DateTime(dt.into())) .unwrap_or(PrismaValue::Null), - Value::Char(c) => c + ValueType::Char(c) => c .map(|c| PrismaValue::String(c.to_string())) .unwrap_or(PrismaValue::Null), - Value::Bytes(bytes) => bytes + ValueType::Bytes(bytes) => bytes .map(|b| PrismaValue::Bytes(b.into_owned())) .unwrap_or(PrismaValue::Null), - Value::Xml(s) => s + ValueType::Xml(s) => s .map(|s| PrismaValue::String(s.into_owned())) .unwrap_or(PrismaValue::Null), }; diff --git a/query-engine/connectors/sql-query-connector/src/value_ext.rs b/query-engine/connectors/sql-query-connector/src/value_ext.rs index b0c42e5af38c..a84c9da0380b 100644 --- a/query-engine/connectors/sql-query-connector/src/value_ext.rs +++ b/query-engine/connectors/sql-query-connector/src/value_ext.rs @@ -11,24 +11,24 @@ impl<'a> IntoTypedJsonExtension for quaint::Value<'a> { return "null".to_owned(); } - let type_name = match self { - quaint::Value::Int32(_) => "int", - quaint::Value::Int64(_) => "bigint", - quaint::Value::Float(_) => "float", - quaint::Value::Double(_) => "double", - quaint::Value::Text(_) => "string", - quaint::Value::Enum(_, _) => "enum", - quaint::Value::Bytes(_) => "bytes", - quaint::Value::Boolean(_) => "bool", - quaint::Value::Char(_) => "char", - quaint::Value::Numeric(_) => "decimal", - quaint::Value::Json(_) => "json", - quaint::Value::Xml(_) => "xml", - quaint::Value::Uuid(_) => "uuid", - quaint::Value::DateTime(_) => "datetime", - quaint::Value::Date(_) => "date", - quaint::Value::Time(_) => "time", - quaint::Value::Array(_) | quaint::Value::EnumArray(_, _) => "array", + let type_name = match self.typed { + quaint::ValueType::Int32(_) => "int", + quaint::ValueType::Int64(_) => "bigint", + quaint::ValueType::Float(_) => "float", + quaint::ValueType::Double(_) => "double", + quaint::ValueType::Text(_) => "string", + quaint::ValueType::Enum(_, _) => "enum", + quaint::ValueType::Bytes(_) => "bytes", + quaint::ValueType::Boolean(_) => "bool", + quaint::ValueType::Char(_) => "char", + quaint::ValueType::Numeric(_) => "decimal", + quaint::ValueType::Json(_) => "json", + quaint::ValueType::Xml(_) => "xml", + quaint::ValueType::Uuid(_) => "uuid", + quaint::ValueType::DateTime(_) => "datetime", + quaint::ValueType::Date(_) => "date", + quaint::ValueType::Time(_) => "time", + quaint::ValueType::Array(_) | quaint::ValueType::EnumArray(_, _) => "array", }; type_name.to_owned() @@ -37,12 +37,12 @@ impl<'a> IntoTypedJsonExtension for quaint::Value<'a> { fn as_typed_json(self) -> serde_json::Value { let type_name = self.type_name(); - let json_value = match self { - quaint::Value::Array(Some(values)) => { + let json_value = match self.typed { + quaint::ValueType::Array(Some(values)) => { serde_json::Value::Array(values.into_iter().map(|value| value.as_typed_json()).collect()) } - quaint::Value::Int64(Some(value)) => serde_json::Value::String(value.to_string()), - quaint::Value::Numeric(Some(decimal)) => serde_json::Value::String(decimal.normalized().to_string()), + quaint::ValueType::Int64(Some(value)) => serde_json::Value::String(value.to_string()), + quaint::ValueType::Numeric(Some(decimal)) => serde_json::Value::String(decimal.normalized().to_string()), x => serde_json::Value::from(x), }; diff --git a/query-engine/dmmf/src/tests/tests.rs b/query-engine/dmmf/src/tests/tests.rs index 25f83e64447d..53f11a455ee3 100644 --- a/query-engine/dmmf/src/tests/tests.rs +++ b/query-engine/dmmf/src/tests/tests.rs @@ -19,7 +19,10 @@ fn views_ignore() { } fn assert_comment(actual: Option<&String>, expected: &str) { - assert!(actual.is_some_and(|c| c.as_str() == expected)) + match actual { + Some(actual) => assert_eq!(actual.as_str(), expected), + None => panic!("Expected comment: {}", expected), + } } #[test] diff --git a/query-engine/driver-adapters/src/conversion.rs b/query-engine/driver-adapters/src/conversion.rs index 1e32dc3b8306..2d469a5ab7c3 100644 --- a/query-engine/driver-adapters/src/conversion.rs +++ b/query-engine/driver-adapters/src/conversion.rs @@ -1,6 +1,7 @@ use napi::bindgen_prelude::{FromNapiValue, ToNapiValue}; use napi::NapiValue; use quaint::ast::Value as QuaintValue; +use quaint::ast::ValueType as QuaintValueType; use serde::Serialize; use serde_json::value::Value as JsonValue; @@ -61,27 +62,27 @@ impl ToNapiValue for JSArg { pub fn conv_params(params: &[QuaintValue<'_>]) -> serde_json::Result> { let mut values = Vec::with_capacity(params.len()); - for pv in params { - let res = match pv { - QuaintValue::Json(s) => match s { + for qv in params { + let res = match &qv.typed { + QuaintValueType::Json(s) => match s { Some(ref s) => { let json_str = serde_json::to_string(s)?; JSArg::RawString(json_str) } None => JsonValue::Null.into(), }, - QuaintValue::Bytes(bytes) => match bytes { + QuaintValueType::Bytes(bytes) => match bytes { Some(bytes) => JSArg::Buffer(bytes.to_vec()), None => JsonValue::Null.into(), }, - quaint_value @ QuaintValue::Numeric(bd) => match bd { + quaint_value @ QuaintValueType::Numeric(bd) => match bd { Some(bd) => match bd.to_string().parse::() { Ok(double) => JSArg::from(JsonValue::from(double)), Err(_) => JSArg::from(JsonValue::from(quaint_value.clone())), }, None => JsonValue::Null.into(), }, - QuaintValue::Array(Some(items)) => JSArg::Array(conv_params(items)?), + QuaintValueType::Array(Some(items)) => JSArg::Array(conv_params(items)?), quaint_value => JSArg::from(JsonValue::from(quaint_value.clone())), }; diff --git a/query-engine/driver-adapters/src/proxy.rs b/query-engine/driver-adapters/src/proxy.rs index aeaef30664d0..bdcab93a0c55 100644 --- a/query-engine/driver-adapters/src/proxy.rs +++ b/query-engine/driver-adapters/src/proxy.rs @@ -269,7 +269,7 @@ fn js_value_to_quaint( .parse::() .map(QuaintValue::int32) .map_err(|e| conversion_error!("string-encoded number must be an i32, got {s}: {e}")), - serde_json::Value::Null => Ok(QuaintValue::Int32(None)), + serde_json::Value::Null => Ok(QuaintValue::null_int32()), mismatch => Err(conversion_error!( "expected an i32 number in column {column_name}, found {mismatch}" )), @@ -283,7 +283,7 @@ fn js_value_to_quaint( .parse::() .map(QuaintValue::int64) .map_err(|e| conversion_error!("string-encoded number must be an i64, got {s}: {e}")), - serde_json::Value::Null => Ok(QuaintValue::Int64(None)), + serde_json::Value::Null => Ok(QuaintValue::null_int64()), mismatch => Err(conversion_error!( "expected a string or number in column {column_name}, found {mismatch}" )), @@ -296,7 +296,7 @@ fn js_value_to_quaint( .ok_or(conversion_error!("number must be a float, got {n}")) .and_then(f64_to_f32) .map(QuaintValue::float), - serde_json::Value::Null => Ok(QuaintValue::Float(None)), + serde_json::Value::Null => Ok(QuaintValue::null_float()), mismatch => Err(conversion_error!( "expected an f32 number in column {column_name}, found {mismatch}" )), @@ -306,7 +306,7 @@ fn js_value_to_quaint( .as_f64() .map(QuaintValue::double) .ok_or(conversion_error!("number must be a f64, got {n}")), - serde_json::Value::Null => Ok(QuaintValue::Double(None)), + serde_json::Value::Null => Ok(QuaintValue::null_double()), mismatch => Err(conversion_error!( "expected an f64 number in column {column_name}, found {mismatch}" )), @@ -320,14 +320,14 @@ fn js_value_to_quaint( .and_then(BigDecimal::from_f64) .ok_or(conversion_error!("number must be an f64, got {n}")) .map(QuaintValue::numeric), - serde_json::Value::Null => Ok(QuaintValue::Numeric(None)), + serde_json::Value::Null => Ok(QuaintValue::null_numeric()), mismatch => Err(conversion_error!( "expected a string-encoded number in column {column_name}, found {mismatch}", )), }, ColumnType::Boolean => match json_value { serde_json::Value::Bool(b) => Ok(QuaintValue::boolean(b)), - serde_json::Value::Null => Ok(QuaintValue::Boolean(None)), + serde_json::Value::Null => Ok(QuaintValue::null_boolean()), serde_json::Value::Number(n) => match n.as_i64() { Some(0) => Ok(QuaintValue::boolean(false)), Some(1) => Ok(QuaintValue::boolean(true)), @@ -345,15 +345,18 @@ fn js_value_to_quaint( )), }, ColumnType::Char => match json_value { - serde_json::Value::String(s) => Ok(QuaintValue::Char(s.chars().next())), - serde_json::Value::Null => Ok(QuaintValue::Char(None)), + serde_json::Value::String(s) => match s.chars().next() { + Some(c) => Ok(QuaintValue::character(c)), + None => Ok(QuaintValue::null_character()), + }, + serde_json::Value::Null => Ok(QuaintValue::null_character()), mismatch => Err(conversion_error!( "expected a string in column {column_name}, found {mismatch}" )), }, ColumnType::Text => match json_value { serde_json::Value::String(s) => Ok(QuaintValue::text(s)), - serde_json::Value::Null => Ok(QuaintValue::Text(None)), + serde_json::Value::Null => Ok(QuaintValue::null_text()), mismatch => Err(conversion_error!( "expected a string in column {column_name}, found {mismatch}" )), @@ -362,7 +365,7 @@ fn js_value_to_quaint( serde_json::Value::String(s) => NaiveDate::parse_from_str(&s, "%Y-%m-%d") .map(QuaintValue::date) .map_err(|_| conversion_error!("expected a date string, got {s}")), - serde_json::Value::Null => Ok(QuaintValue::Date(None)), + serde_json::Value::Null => Ok(QuaintValue::null_date()), mismatch => Err(conversion_error!( "expected a string in column {column_name}, found {mismatch}" )), @@ -371,7 +374,7 @@ fn js_value_to_quaint( serde_json::Value::String(s) => NaiveTime::parse_from_str(&s, "%H:%M:%S") .map(QuaintValue::time) .map_err(|_| conversion_error!("expected a time string, got {s}")), - serde_json::Value::Null => Ok(QuaintValue::Time(None)), + serde_json::Value::Null => Ok(QuaintValue::null_time()), mismatch => Err(conversion_error!( "expected a string in column {column_name}, found {mismatch}" )), @@ -382,7 +385,7 @@ fn js_value_to_quaint( .or_else(|_| DateTime::parse_from_rfc3339(&s).map(DateTime::::from)) .map(QuaintValue::datetime) .map_err(|_| conversion_error!("expected a datetime string, found {s}")), - serde_json::Value::Null => Ok(QuaintValue::DateTime(None)), + serde_json::Value::Null => Ok(QuaintValue::null_datetime()), mismatch => Err(conversion_error!( "expected a string in column {column_name}, found {mismatch}" )), @@ -390,7 +393,7 @@ fn js_value_to_quaint( ColumnType::Json => { match json_value { // DbNull - serde_json::Value::Null => Ok(QuaintValue::Json(None)), + serde_json::Value::Null => Ok(QuaintValue::null_json()), // JsonNull serde_json::Value::String(s) if s == "$__prisma_null" => Ok(QuaintValue::json(serde_json::Value::Null)), json => Ok(QuaintValue::json(json)), @@ -398,20 +401,20 @@ fn js_value_to_quaint( } ColumnType::Enum => match json_value { serde_json::Value::String(s) => Ok(QuaintValue::enum_variant(s)), - serde_json::Value::Null => Ok(QuaintValue::Enum(None, None)), + serde_json::Value::Null => Ok(QuaintValue::null_enum()), mismatch => Err(conversion_error!( "expected a string in column {column_name}, found {mismatch}" )), }, ColumnType::Bytes => match json_value { - serde_json::Value::String(s) => Ok(QuaintValue::Bytes(Some(s.into_bytes().into()))), + serde_json::Value::String(s) => Ok(QuaintValue::bytes(s.into_bytes())), serde_json::Value::Array(array) => array .iter() .map(|value| value.as_i64().and_then(|maybe_byte| maybe_byte.try_into().ok())) .collect::>>() .map(QuaintValue::bytes) .ok_or(conversion_error!("elements of the array must be u8")), - serde_json::Value::Null => Ok(QuaintValue::Bytes(None)), + serde_json::Value::Null => Ok(QuaintValue::null_bytes()), mismatch => Err(conversion_error!( "expected a string or an array in column {column_name}, found {mismatch}", )), @@ -420,7 +423,7 @@ fn js_value_to_quaint( serde_json::Value::String(s) => uuid::Uuid::parse_str(&s) .map(QuaintValue::uuid) .map_err(|_| conversion_error!("Expected a UUID string")), - serde_json::Value::Null => Ok(QuaintValue::Bytes(None)), + serde_json::Value::Null => Ok(QuaintValue::null_bytes()), mismatch => Err(conversion_error!( "Expected a UUID string in column {column_name}, found {mismatch}" )), @@ -464,14 +467,14 @@ fn js_array_to_quaint( column_name: &str, ) -> quaint::Result> { match json_value { - serde_json::Value::Array(array) => Ok(QuaintValue::Array(Some( + serde_json::Value::Array(array) => Ok(QuaintValue::array( array .into_iter() .enumerate() .map(|(index, elem)| js_value_to_quaint(elem, base_type, &format!("{column_name}[{index}]"))) .collect::>>()?, - ))), - serde_json::Value::Null => Ok(QuaintValue::Array(None)), + )), + serde_json::Value::Null => Ok(QuaintValue::null_array()), mismatch => Err(conversion_error!( "expected an array in column {column_name}, found {mismatch}", )), @@ -606,7 +609,6 @@ fn f64_to_f32(x: f64) -> quaint::Result { Err(conversion_error!("f32 overflow during conversion")) } } - #[cfg(test)] mod proxy_test { use num_bigint::BigInt; @@ -615,10 +617,10 @@ mod proxy_test { use super::*; #[track_caller] - fn test_null(quaint_none: QuaintValue, column_type: ColumnType) { + fn test_null<'a, T: Into>>(quaint_none: T, column_type: ColumnType) { let json_value = serde_json::Value::Null; let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, quaint_none); + assert_eq!(quaint_value, quaint_none.into()); } #[test] @@ -626,31 +628,31 @@ mod proxy_test { let column_type = ColumnType::Int32; // null - test_null(QuaintValue::Int32(None), column_type); + test_null(QuaintValue::null_int32(), column_type); // 0 let n: i32 = 0; let json_value = serde_json::Value::Number(serde_json::Number::from(n)); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Int32(Some(n))); + assert_eq!(quaint_value, QuaintValue::int32(n)); // max let n: i32 = i32::MAX; let json_value = serde_json::Value::Number(serde_json::Number::from(n)); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Int32(Some(n))); + assert_eq!(quaint_value, QuaintValue::int32(n)); // min let n: i32 = i32::MIN; let json_value = serde_json::Value::Number(serde_json::Number::from(n)); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Int32(Some(n))); + assert_eq!(quaint_value, QuaintValue::int32(n)); // string-encoded let n = i32::MAX; let json_value = serde_json::Value::String(n.to_string()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Int32(Some(n))); + assert_eq!(quaint_value, QuaintValue::int32(n)); } #[test] @@ -658,31 +660,31 @@ mod proxy_test { let column_type = ColumnType::Int64; // null - test_null(QuaintValue::Int64(None), column_type); + test_null(QuaintValue::null_int64(), column_type); // 0 let n: i64 = 0; let json_value = serde_json::Value::String(n.to_string()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Int64(Some(n))); + assert_eq!(quaint_value, QuaintValue::int64(n)); // max let n: i64 = i64::MAX; let json_value = serde_json::Value::String(n.to_string()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Int64(Some(n))); + assert_eq!(quaint_value, QuaintValue::int64(n)); // min let n: i64 = i64::MIN; let json_value = serde_json::Value::String(n.to_string()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Int64(Some(n))); + assert_eq!(quaint_value, QuaintValue::int64(n)); // number-encoded let n: i64 = (1 << 53) - 1; // max JS safe integer let json_value = serde_json::Value::Number(serde_json::Number::from(n)); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Int64(Some(n))); + assert_eq!(quaint_value, QuaintValue::int64(n)); } #[test] @@ -690,25 +692,25 @@ mod proxy_test { let column_type = ColumnType::Float; // null - test_null(QuaintValue::Float(None), column_type); + test_null(QuaintValue::null_float(), column_type); // 0 let n: f32 = 0.0; let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n.into()).unwrap()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Float(Some(n))); + assert_eq!(quaint_value, QuaintValue::float(n)); // max let n: f32 = f32::MAX; let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n.into()).unwrap()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Float(Some(n))); + assert_eq!(quaint_value, QuaintValue::float(n)); // min let n: f32 = f32::MIN; let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n.into()).unwrap()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Float(Some(n))); + assert_eq!(quaint_value, QuaintValue::float(n)); } #[test] @@ -716,25 +718,25 @@ mod proxy_test { let column_type = ColumnType::Double; // null - test_null(QuaintValue::Double(None), column_type); + test_null(QuaintValue::null_double(), column_type); // 0 let n: f64 = 0.0; let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n).unwrap()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Double(Some(n))); + assert_eq!(quaint_value, QuaintValue::double(n)); // max let n: f64 = f64::MAX; let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n).unwrap()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Double(Some(n))); + assert_eq!(quaint_value, QuaintValue::double(n)); // min let n: f64 = f64::MIN; let json_value = serde_json::Value::Number(serde_json::Number::from_f64(n).unwrap()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Double(Some(n))); + assert_eq!(quaint_value, QuaintValue::double(n)); } #[test] @@ -742,21 +744,21 @@ mod proxy_test { let column_type = ColumnType::Numeric; // null - test_null(QuaintValue::Numeric(None), column_type); + test_null(QuaintValue::null_numeric(), column_type); let n_as_string = "1234.99"; let decimal = BigDecimal::new(BigInt::parse_bytes(b"123499", 10).unwrap(), 2); let json_value = serde_json::Value::String(n_as_string.into()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Numeric(Some(decimal))); + assert_eq!(quaint_value, QuaintValue::numeric(decimal)); let n_as_string = "1234.999999"; let decimal = BigDecimal::new(BigInt::parse_bytes(b"1234999999", 10).unwrap(), 6); let json_value = serde_json::Value::String(n_as_string.into()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Numeric(Some(decimal))); + assert_eq!(quaint_value, QuaintValue::numeric(decimal)); } #[test] @@ -764,18 +766,18 @@ mod proxy_test { let column_type = ColumnType::Boolean; // null - test_null(QuaintValue::Boolean(None), column_type); + test_null(QuaintValue::null_boolean(), column_type); // true for truthy_value in [json!(true), json!(1), json!("true"), json!("TRUE"), json!("1")] { let quaint_value = js_value_to_quaint(truthy_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Boolean(Some(true))); + assert_eq!(quaint_value, QuaintValue::boolean(true)); } // false for falsy_value in [json!(false), json!(0), json!("false"), json!("FALSE"), json!("0")] { let quaint_value = js_value_to_quaint(falsy_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Boolean(Some(false))); + assert_eq!(quaint_value, QuaintValue::boolean(false)); } } @@ -784,12 +786,12 @@ mod proxy_test { let column_type = ColumnType::Char; // null - test_null(QuaintValue::Char(None), column_type); + test_null(QuaintValue::null_character(), column_type); let c = 'c'; let json_value = serde_json::Value::String(c.to_string()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Char(Some(c))); + assert_eq!(quaint_value, QuaintValue::character(c)); } #[test] @@ -797,12 +799,12 @@ mod proxy_test { let column_type = ColumnType::Text; // null - test_null(QuaintValue::Text(None), column_type); + test_null(QuaintValue::null_text(), column_type); let s = "some text"; let json_value = serde_json::Value::String(s.to_string()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Text(Some(s.into()))); + assert_eq!(quaint_value, QuaintValue::text(s)); } #[test] @@ -810,14 +812,14 @@ mod proxy_test { let column_type = ColumnType::Date; // null - test_null(QuaintValue::Date(None), column_type); + test_null(QuaintValue::null_date(), column_type); let s = "2023-01-01"; let json_value = serde_json::Value::String(s.to_string()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); let date = NaiveDate::from_ymd_opt(2023, 1, 1).unwrap(); - assert_eq!(quaint_value, QuaintValue::Date(Some(date))); + assert_eq!(quaint_value, QuaintValue::date(date)); } #[test] @@ -825,14 +827,14 @@ mod proxy_test { let column_type = ColumnType::Time; // null - test_null(QuaintValue::Time(None), column_type); + test_null(QuaintValue::null_time(), column_type); let s = "23:59:59"; let json_value = serde_json::Value::String(s.to_string()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); let time: NaiveTime = NaiveTime::from_hms_opt(23, 59, 59).unwrap(); - assert_eq!(quaint_value, QuaintValue::Time(Some(time))); + assert_eq!(quaint_value, QuaintValue::time(time)); } #[test] @@ -840,7 +842,7 @@ mod proxy_test { let column_type = ColumnType::DateTime; // null - test_null(QuaintValue::DateTime(None), column_type); + test_null(QuaintValue::null_datetime(), column_type); let s = "2023-01-01 23:59:59.415"; let json_value = serde_json::Value::String(s.to_string()); @@ -851,7 +853,7 @@ mod proxy_test { .and_hms_milli_opt(23, 59, 59, 415) .unwrap(); let datetime = DateTime::from_utc(datetime, Utc); - assert_eq!(quaint_value, QuaintValue::DateTime(Some(datetime))); + assert_eq!(quaint_value, QuaintValue::datetime(datetime)); let s = "2023-01-01 23:59:59.123456"; let json_value = serde_json::Value::String(s.to_string()); @@ -862,7 +864,7 @@ mod proxy_test { .and_hms_micro_opt(23, 59, 59, 123_456) .unwrap(); let datetime = DateTime::from_utc(datetime, Utc); - assert_eq!(quaint_value, QuaintValue::DateTime(Some(datetime))); + assert_eq!(quaint_value, QuaintValue::datetime(datetime)); let s = "2023-01-01 23:59:59"; let json_value = serde_json::Value::String(s.to_string()); @@ -873,7 +875,7 @@ mod proxy_test { .and_hms_milli_opt(23, 59, 59, 0) .unwrap(); let datetime = DateTime::from_utc(datetime, Utc); - assert_eq!(quaint_value, QuaintValue::DateTime(Some(datetime))); + assert_eq!(quaint_value, QuaintValue::datetime(datetime)); } #[test] @@ -881,7 +883,7 @@ mod proxy_test { let column_type = ColumnType::Json; // null - test_null(QuaintValue::Json(None), column_type); + test_null(QuaintValue::null_json(), column_type); let json = json!({ "key": "value", @@ -894,7 +896,7 @@ mod proxy_test { }); let json_value = json.clone(); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Json(Some(json.clone()))); + assert_eq!(quaint_value, QuaintValue::json(json.clone())); } #[test] @@ -902,30 +904,30 @@ mod proxy_test { let column_type = ColumnType::Enum; // null - test_null(QuaintValue::Enum(None, None), column_type); + test_null(QuaintValue::null_enum(), column_type); let s = "some enum variant"; let json_value = serde_json::Value::String(s.to_string()); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); - assert_eq!(quaint_value, QuaintValue::Enum(Some(s.into()), None)); + assert_eq!(quaint_value, QuaintValue::enum_variant(s)); } #[test] fn js_int32_array_to_quaint() { let column_type = ColumnType::Int32Array; - test_null(QuaintValue::Array(None), column_type); + test_null(QuaintValue::null_array(), column_type); let json_value = json!([1, 2, 3]); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); assert_eq!( quaint_value, - QuaintValue::Array(Some(vec![ + QuaintValue::array(vec![ QuaintValue::int32(1), QuaintValue::int32(2), QuaintValue::int32(3) - ])) + ]) ); let json_value = json!([1, 2, {}]); @@ -940,14 +942,14 @@ mod proxy_test { #[test] fn js_text_array_to_quaint() { let column_type = ColumnType::TextArray; - test_null(QuaintValue::Array(None), column_type); + test_null(QuaintValue::null_array(), column_type); let json_value = json!(["hi", "there"]); let quaint_value = js_value_to_quaint(json_value, column_type, "column_name").unwrap(); assert_eq!( quaint_value, - QuaintValue::Array(Some(vec![QuaintValue::text("hi"), QuaintValue::text("there"),])) + QuaintValue::array(vec![QuaintValue::text("hi"), QuaintValue::text("there"),]) ); let json_value = json!([10]); diff --git a/query-engine/driver-adapters/src/queryable.rs b/query-engine/driver-adapters/src/queryable.rs index 398286c8ca05..d8b022d0fa49 100644 --- a/query-engine/driver-adapters/src/queryable.rs +++ b/query-engine/driver-adapters/src/queryable.rs @@ -34,7 +34,7 @@ pub(crate) struct JsBaseQueryable { impl JsBaseQueryable { pub(crate) fn new(proxy: CommonProxy) -> Self { - let flavour: Flavour = proxy.flavour.to_owned().parse().unwrap(); + let flavour: Flavour = proxy.flavour.parse().unwrap(); Self { proxy, flavour } } diff --git a/query-engine/query-engine-node-api/src/logger.rs b/query-engine/query-engine-node-api/src/logger.rs index d327726d6567..da3e725c0218 100644 --- a/query-engine/query-engine-node-api/src/logger.rs +++ b/query-engine/query-engine-node-api/src/logger.rs @@ -58,7 +58,7 @@ impl Logger { None }; - let layer = CallbackLayer::new(log_callback.clone()).with_filter(filters); + let layer = CallbackLayer::new(log_callback).with_filter(filters); let metrics = if enable_metrics { query_engine_metrics::setup(); diff --git a/schema-engine/sql-migration-tests/src/assertions/quaint_result_set_ext.rs b/schema-engine/sql-migration-tests/src/assertions/quaint_result_set_ext.rs index 2aaf1f9801db..3f486f34163b 100644 --- a/schema-engine/sql-migration-tests/src/assertions/quaint_result_set_ext.rs +++ b/schema-engine/sql-migration-tests/src/assertions/quaint_result_set_ext.rs @@ -1,4 +1,4 @@ -use quaint::{connector::ResultRowRef, prelude::ResultSet, Value}; +use quaint::{connector::ResultRowRef, prelude::ResultSet, Value, ValueType}; pub trait ResultSetExt: Sized { fn assert_row_count(self, expected_count: usize) -> Self; @@ -34,8 +34,8 @@ pub struct RowAssertion<'a>(ResultRowRef<'a>); impl<'a> RowAssertion<'a> { pub fn assert_array_value(self, column_name: &str, expected_value: &[Value<'_>]) -> Self { - let actual_value = self.0.get(column_name).and_then(|col: &Value<'_>| match col { - Value::Array(x) => x.as_ref(), + let actual_value = self.0.get(column_name).and_then(|col: &Value<'_>| match &col.typed { + ValueType::Array(x) => x.as_ref(), _ => panic!("as_array"), }); @@ -87,9 +87,9 @@ impl<'a> RowAssertion<'a> { #[track_caller] pub fn assert_text_value(self, column_name: &str, expected_value: &str) -> Self { let value = self.0.get(column_name).expect("Expected a value, found none"); - let value_text: &str = match value { - Value::Text(val) => val.as_deref(), - Value::Enum(val, _) => val.as_deref(), + let value_text: &str = match &value.typed { + ValueType::Text(val) => val.as_deref(), + ValueType::Enum(val, _) => val.as_deref(), _ => None, } .expect("Expected a string value"); diff --git a/schema-engine/sql-migration-tests/tests/existing_data/mod.rs b/schema-engine/sql-migration-tests/tests/existing_data/mod.rs index 461214dd62ae..bed7b8fc80ca 100644 --- a/schema-engine/sql-migration-tests/tests/existing_data/mod.rs +++ b/schema-engine/sql-migration-tests/tests/existing_data/mod.rs @@ -334,7 +334,7 @@ fn changing_a_column_from_optional_to_required_is_unexecutable(api: TestApi) { let insert = Insert::multi_into(api.render_table_name("Test"), ["id", "age"]) .values(("a", 12)) .values(("b", 22)) - .values(("c", Value::Int32(None))); + .values(("c", ValueType::Int32(None))); api.query(insert.into()); @@ -756,10 +756,9 @@ fn set_default_current_timestamp_on_existing_column_works(api: TestApi) { api.schema_push_w_datasource(dm1).send().assert_green(); - let insert = Insert::single_into(api.render_table_name("User")).value("id", 5).value( - "created_at", - Value::DateTime(Some("2020-06-15T14:50:00Z".parse().unwrap())), - ); + let insert = Insert::single_into(api.render_table_name("User")) + .value("id", 5) + .value("created_at", Value::datetime("2020-06-15T14:50:00Z".parse().unwrap())); api.query(insert.into()); let dm2 = r#" diff --git a/schema-engine/sql-migration-tests/tests/existing_data/sql_unexecutable_migrations/made_optional_field_required.rs b/schema-engine/sql-migration-tests/tests/existing_data/sql_unexecutable_migrations/made_optional_field_required.rs index 8b3f0cf608c3..718b34a3230b 100644 --- a/schema-engine/sql-migration-tests/tests/existing_data/sql_unexecutable_migrations/made_optional_field_required.rs +++ b/schema-engine/sql-migration-tests/tests/existing_data/sql_unexecutable_migrations/made_optional_field_required.rs @@ -92,8 +92,8 @@ fn making_an_optional_field_required_with_data_with_a_default_works(api: TestApi .map(|row| row.into_iter().collect::>()) .collect::>(), &[ - &[Value::text("abc"), Value::text("george"), Value::integer(84)], - &[Value::text("def"), Value::text("X Æ A-12"), Value::integer(7)], + &[Value::text("abc"), Value::text("george"), Value::int32(84)], + &[Value::text("def"), Value::text("X Æ A-12"), Value::int32(7)], ] ); } @@ -151,7 +151,7 @@ fn making_an_optional_field_required_with_data_with_a_default_is_unexecutable(ap .map(|row| row.into_iter().collect::>()) .collect::>(), &[ - &[Value::text("abc"), Value::text("george"), Value::Int32(None)], + &[Value::text("abc"), Value::text("george"), Value::null_int32()], &[Value::text("def"), Value::text("X Æ A-12"), Value::int32(7)], ] ); diff --git a/schema-engine/sql-migration-tests/tests/existing_data/sqlite_existing_data_tests.rs b/schema-engine/sql-migration-tests/tests/existing_data/sqlite_existing_data_tests.rs index f87d9f931a28..4485ad4de719 100644 --- a/schema-engine/sql-migration-tests/tests/existing_data/sqlite_existing_data_tests.rs +++ b/schema-engine/sql-migration-tests/tests/existing_data/sqlite_existing_data_tests.rs @@ -1,4 +1,4 @@ -use quaint::{prelude::Insert, Value}; +use quaint::{prelude::Insert, ValueType}; use sql_migration_tests::test_api::*; use sql_schema_describer::DefaultValue; @@ -16,7 +16,7 @@ fn changing_a_column_from_optional_to_required_with_a_default_is_safe(api: TestA let insert = Insert::multi_into(api.render_table_name("Test"), ["id", "age"]) .values(("a", 12)) .values(("b", 22)) - .values(("c", Value::Int32(None))); + .values(("c", ValueType::Int32(None))); api.query(insert.into()); diff --git a/schema-engine/sql-migration-tests/tests/existing_data/type_migration_tests.rs b/schema-engine/sql-migration-tests/tests/existing_data/type_migration_tests.rs index 56d63f7860b3..2a77f3a29eba 100644 --- a/schema-engine/sql-migration-tests/tests/existing_data/type_migration_tests.rs +++ b/schema-engine/sql-migration-tests/tests/existing_data/type_migration_tests.rs @@ -101,7 +101,7 @@ fn changing_a_string_array_column_to_scalar_is_fine(api: TestApi) { .value("id", "film1") .value( "mainProtagonist", - Value::Array(Some(vec!["giant shark".into(), "jason statham".into()])), + Value::array(vec![Value::text("giant shark"), Value::text("jason statham")]), ) .result_raw(); @@ -138,7 +138,7 @@ fn changing_an_int_array_column_to_scalar_is_not_possible(api: TestApi) { api.insert("Film") .value("id", "film1") - .value("mainProtagonist", Value::Array(Some(vec![7.into(), 11.into()]))) + .value("mainProtagonist", Value::array(vec![Value::int32(7), Value::int32(11)])) .result_raw(); let dm2 = r#" diff --git a/schema-engine/sql-migration-tests/tests/migrations/sql.rs b/schema-engine/sql-migration-tests/tests/migrations/sql.rs index f8a9d0ef3202..8f87115db4dc 100644 --- a/schema-engine/sql-migration-tests/tests/migrations/sql.rs +++ b/schema-engine/sql-migration-tests/tests/migrations/sql.rs @@ -244,17 +244,17 @@ fn enum_defaults_must_work(api: TestApi) { assert_eq!(row.get("id").unwrap().to_string().unwrap(), "the-id"); assert_eq!( - match row.get("mood").unwrap() { - quaint::Value::Enum(Some(enm), _) => enm.as_ref(), - quaint::Value::Text(Some(enm)) => enm.as_ref(), + match &row.get("mood").unwrap().typed { + quaint::ValueType::Enum(Some(enm), _) => enm.as_ref(), + quaint::ValueType::Text(Some(enm)) => enm.as_ref(), _ => panic!("mood is not an enum value"), }, "hongry" ); assert_eq!( - match row.get("previousMood").unwrap() { - quaint::Value::Enum(Some(enm), _) => enm.as_ref(), - quaint::Value::Text(Some(enm)) => enm.as_ref(), + match &row.get("previousMood").unwrap().typed { + quaint::ValueType::Enum(Some(enm), _) => enm.as_ref(), + quaint::ValueType::Text(Some(enm)) => enm.as_ref(), _ => panic!("previousMood is not an enum value"), }, "ANGRY" diff --git a/schema-engine/sql-migration-tests/tests/native_types/mssql.rs b/schema-engine/sql-migration-tests/tests/native_types/mssql.rs index 32ac24688601..83d988acb176 100644 --- a/schema-engine/sql-migration-tests/tests/native_types/mssql.rs +++ b/schema-engine/sql-migration-tests/tests/native_types/mssql.rs @@ -43,7 +43,7 @@ static SAFE_CASTS: Lazy> = Lazy::new(|| { ), ( "TinyInt", - Value::integer(u8::MAX), + Value::int32(u8::MAX), &[ "SmallInt", "Int", @@ -69,7 +69,7 @@ static SAFE_CASTS: Lazy> = Lazy::new(|| { ), ( "SmallInt", - Value::integer(i16::MAX), + Value::int32(i16::MAX), &[ "Int", "BigInt", @@ -92,7 +92,7 @@ static SAFE_CASTS: Lazy> = Lazy::new(|| { ), ( "Int", - Value::integer(i32::MAX), + Value::int32(i32::MAX), &[ "BigInt", "Decimal", @@ -423,12 +423,12 @@ static RISKY_CASTS: Lazy> = Lazy::new(|| { vec![ ( "TinyInt", - Value::integer(u8::MAX), + Value::int32(u8::MAX), &["Decimal(2,0)", "Char(2)", "NChar(2)", "VarChar(2)", "NVarChar(2)"], ), ( "SmallInt", - Value::integer(i16::MAX), + Value::int32(i16::MAX), &[ "Bit", "TinyInt", @@ -443,7 +443,7 @@ static RISKY_CASTS: Lazy> = Lazy::new(|| { ), ( "Int", - Value::integer(i32::MAX), + Value::int32(i32::MAX), &[ "Bit", "TinyInt", @@ -468,7 +468,7 @@ static RISKY_CASTS: Lazy> = Lazy::new(|| { ), ( "BigInt", - Value::integer(i32::MAX), + Value::int32(i32::MAX), &[ "Bit", "TinyInt", @@ -1402,7 +1402,7 @@ static NOT_CASTABLE: Lazy> = Lazy::new(|| { ), ( "TinyInt", - Value::integer(u8::MAX), + Value::int32(u8::MAX), &[ "Date", "Time", @@ -1417,7 +1417,7 @@ static NOT_CASTABLE: Lazy> = Lazy::new(|| { ), ( "SmallInt", - Value::integer(i16::MAX), + Value::int32(i16::MAX), &[ "Date", "Time", @@ -1432,7 +1432,7 @@ static NOT_CASTABLE: Lazy> = Lazy::new(|| { ), ( "Int", - Value::integer(i32::MAX), + Value::int32(i32::MAX), &[ "Date", "Time", diff --git a/schema-engine/sql-migration-tests/tests/native_types/mysql.rs b/schema-engine/sql-migration-tests/tests/native_types/mysql.rs index 9144313af8ed..d8cf62f5767c 100644 --- a/schema-engine/sql-migration-tests/tests/native_types/mysql.rs +++ b/schema-engine/sql-migration-tests/tests/native_types/mysql.rs @@ -2,13 +2,13 @@ use sql_migration_tests::test_api::*; use std::{borrow::Cow, fmt::Write}; /// (source native type, test value to insert, target native type) -type Case = (&'static str, quaint::Value<'static>, &'static [&'static str]); +type Case = (&'static str, quaint::ValueType<'static>, &'static [&'static str]); type Cases = &'static [Case]; const SAFE_CASTS: Cases = &[ ( "BigInt", - quaint::Value::Int64(Some(99999999432)), + quaint::ValueType::Int64(Some(99999999432)), &[ "Binary(200)", "Bit(54)", @@ -30,7 +30,7 @@ const SAFE_CASTS: Cases = &[ ), ( "Binary(8)", - quaint::Value::Bytes(Some(Cow::Borrowed(b"08088044"))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b"08088044"))), &[ "Bit(64)", "Blob", @@ -51,7 +51,7 @@ const SAFE_CASTS: Cases = &[ ), ( "Int", - quaint::Value::Int32(Some(i32::MIN)), + quaint::ValueType::Int32(Some(i32::MIN)), &[ "BigInt", "Char(20)", @@ -64,7 +64,7 @@ const SAFE_CASTS: Cases = &[ ), ( "Bit(32)", - quaint::Value::Bytes(Some(Cow::Borrowed(b""))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b""))), &[ "SmallInt", "UnsignedSmallInt", @@ -86,12 +86,12 @@ const SAFE_CASTS: Cases = &[ ), ( "Blob", - quaint::Value::Bytes(Some(Cow::Borrowed(&[0xff]))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(&[0xff]))), &["TinyBlob", "MediumBlob", "LongBlob"], ), ( "Char(10)", - quaint::Value::Text(Some(Cow::Borrowed("1234"))), + quaint::ValueType::Text(Some(Cow::Borrowed("1234"))), &[ "Blob", "Char(11)", @@ -107,7 +107,7 @@ const SAFE_CASTS: Cases = &[ ), ( "Date", - quaint::Value::Text(Some(Cow::Borrowed("2020-01-12"))), + quaint::ValueType::Text(Some(Cow::Borrowed("2020-01-12"))), &[ "DateTime(0)", "Decimal(10,0)", @@ -131,7 +131,7 @@ const SAFE_CASTS: Cases = &[ ), ( "DateTime(0)", - quaint::Value::Text(Some(Cow::Borrowed("2020-01-08 08:00:00"))), + quaint::ValueType::Text(Some(Cow::Borrowed("2020-01-08 08:00:00"))), &[ "BigInt", "UnsignedBigInt", @@ -144,7 +144,7 @@ const SAFE_CASTS: Cases = &[ ), ( "Double", - quaint::Value::Float(Some(3.20)), + quaint::ValueType::Float(Some(3.20)), &[ "Float", "Bit(64)", @@ -169,7 +169,7 @@ const SAFE_CASTS: Cases = &[ ), ( "Float", - quaint::Value::Float(Some(3.20)), + quaint::ValueType::Float(Some(3.20)), &[ "Double", "Bit(32)", @@ -196,7 +196,7 @@ const SAFE_CASTS: Cases = &[ ), ( "Json", - quaint::Value::Text(Some(Cow::Borrowed("{\"a\":\"b\"}"))), + quaint::ValueType::Text(Some(Cow::Borrowed("{\"a\":\"b\"}"))), &[ // To string "Binary(10)", @@ -211,22 +211,22 @@ const SAFE_CASTS: Cases = &[ ), ( "LongBlob", - quaint::Value::Bytes(Some(Cow::Borrowed(&[0xff]))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(&[0xff]))), &["TinyBlob", "Blob", "MediumBlob"], ), ( "MediumBlob", - quaint::Value::Bytes(Some(Cow::Borrowed(&[0xff]))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(&[0xff]))), &["TinyBlob", "Blob", "LongBlob"], ), ( "TinyBlob", - quaint::Value::Bytes(Some(Cow::Borrowed(&[0xff]))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(&[0xff]))), &["LongBlob", "Blob", "MediumBlob"], ), ( "Time", - quaint::Value::Int32(Some(20)), + quaint::ValueType::Int32(Some(20)), &[ "VarChar(20)", "BigInt", @@ -238,7 +238,7 @@ const SAFE_CASTS: Cases = &[ ), ( "Year", - quaint::Value::Int32(Some(2000)), + quaint::ValueType::Int32(Some(2000)), &[ // To string "Binary(10)", @@ -272,7 +272,7 @@ const SAFE_CASTS: Cases = &[ const RISKY_CASTS: Cases = &[ ( "BigInt", - quaint::Value::Int64(Some(100)), + quaint::ValueType::Int64(Some(100)), &[ "Int", "MediumInt", @@ -285,30 +285,30 @@ const RISKY_CASTS: Cases = &[ "UnsignedTinyInt", ], ), - ("BigInt", quaint::Value::Int64(Some(2000)), &["Year"]), + ("BigInt", quaint::ValueType::Int64(Some(2000)), &["Year"]), ( "Binary(8)", - quaint::Value::Bytes(Some(Cow::Borrowed(b"08088044"))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b"08088044"))), &["Bit(32)", "Int", "UnsignedBigInt", "UnsignedInt", "UnsignedMediumInt"], ), ( "Binary(1)", - quaint::Value::Bytes(Some(Cow::Borrowed(b"0"))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b"0"))), &["Time(0)", "SmallInt", "TinyInt", "UnsignedSmallInt", "UnsignedTinyInt"], ), ( "Binary(4)", - quaint::Value::Bytes(Some(Cow::Borrowed(b"2000"))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b"2000"))), &["Year"], ), ( "Bit(32)", - quaint::Value::Bytes(Some(Cow::Borrowed(b""))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b""))), &["Decimal(10,2)", "Double", "Float"], ), ( "Blob", - quaint::Value::Bytes(Some(Cow::Borrowed(b"abc"))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b"abc"))), &[ "Binary(10)", "Char(10)", @@ -322,22 +322,22 @@ const RISKY_CASTS: Cases = &[ ), ( "Decimal(20,5)", - quaint::Value::Text(Some(Cow::Borrowed("350"))), + quaint::ValueType::Text(Some(Cow::Borrowed("350"))), &["BigInt", "UnsignedBigInt", "Time(0)", "Json"], ), ( "Double", - quaint::Value::Float(Some(0f32)), + quaint::ValueType::Float(Some(0f32)), &["Char(40)", "VarBinary(40)", "VarChar(40)"], ), ( "Float", - quaint::Value::Float(Some(0f32)), + quaint::ValueType::Float(Some(0f32)), &["Char(40)", "VarBinary(40)", "VarChar(40)"], ), ( "LongBlob", - quaint::Value::Bytes(Some(Cow::Borrowed(b"abc"))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b"abc"))), &[ "Binary(10)", "Char(10)", @@ -351,7 +351,7 @@ const RISKY_CASTS: Cases = &[ ), ( "MediumBlob", - quaint::Value::Bytes(Some(Cow::Borrowed(b"abc"))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b"abc"))), &[ "Binary(10)", "Char(10)", @@ -363,10 +363,10 @@ const RISKY_CASTS: Cases = &[ "VarChar(20)", ], ), - ("SmallInt", quaint::Value::Int32(Some(1990)), &["Year", "Double"]), + ("SmallInt", quaint::ValueType::Int32(Some(1990)), &["Year", "Double"]), ( "TinyBlob", - quaint::Value::Bytes(Some(Cow::Borrowed(b"abc"))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b"abc"))), &[ "Binary(10)", "Char(10)", @@ -380,12 +380,12 @@ const RISKY_CASTS: Cases = &[ ), ( "Time(0)", - quaint::Value::Int32(Some(5002)), + quaint::ValueType::Int32(Some(5002)), &["Date", "DateTime(0)", "Timestamp(0)"], ), ( "Year", - quaint::Value::Text(Some(Cow::Borrowed("1999"))), + quaint::ValueType::Text(Some(Cow::Borrowed("1999"))), &["Decimal(10,0)", "Json"], ), ]; @@ -393,22 +393,22 @@ const RISKY_CASTS: Cases = &[ const IMPOSSIBLE_CASTS: Cases = &[ ( "BigInt", - quaint::Value::Int64(Some(500)), + quaint::ValueType::Int64(Some(500)), &["Decimal(15,6)", "Date", "DateTime(0)", "Json", "Timestamp(0)"], ), ( "Binary(12)", - quaint::Value::Bytes(Some(Cow::Borrowed(b"8080008"))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b"8080008"))), &["Date", "DateTime(0)", "Json", "Timestamp(0)"], ), ( "Bit(32)", - quaint::Value::Bytes(Some(Cow::Borrowed(b""))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(b""))), &["Date", "DateTime(0)", "Time(0)", "Timestamp(0)", "Json"], ), ( "Blob", - quaint::Value::Bytes(Some(Cow::Borrowed(&[0x00]))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(&[0x00]))), &[ "TinyInt", "BigInt", @@ -433,7 +433,7 @@ const IMPOSSIBLE_CASTS: Cases = &[ ), ( "Date", - quaint::Value::Text(Some(Cow::Borrowed("2020-01-12"))), + quaint::ValueType::Text(Some(Cow::Borrowed("2020-01-12"))), &[ "TinyInt", "UnsignedTinyInt", @@ -446,7 +446,7 @@ const IMPOSSIBLE_CASTS: Cases = &[ ), ( "DateTime(0)", - quaint::Value::Text(Some(Cow::Borrowed("2020-01-08 08:00:00"))), + quaint::ValueType::Text(Some(Cow::Borrowed("2020-01-08 08:00:00"))), &[ "TinyInt", "UnsignedTinyInt", @@ -461,17 +461,17 @@ const IMPOSSIBLE_CASTS: Cases = &[ ), ( "Double", - quaint::Value::Float(Some(3.20)), + quaint::ValueType::Float(Some(3.20)), &["Binary(10)", "Date", "Timestamp(0)", "DateTime(0)"], ), ( "Float", - quaint::Value::Float(Some(3.20)), + quaint::ValueType::Float(Some(3.20)), &["Binary(10)", "Date", "Timestamp(0)", "DateTime(0)"], ), ( "Json", - quaint::Value::Text(Some(Cow::Borrowed("{\"a\":\"b\"}"))), + quaint::ValueType::Text(Some(Cow::Borrowed("{\"a\":\"b\"}"))), &[ // Integer types "Bit(64)", @@ -490,7 +490,7 @@ const IMPOSSIBLE_CASTS: Cases = &[ ), ( "LongBlob", - quaint::Value::Bytes(Some(Cow::Borrowed(&[0x00]))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(&[0x00]))), &[ "TinyInt", "BigInt", @@ -515,7 +515,7 @@ const IMPOSSIBLE_CASTS: Cases = &[ ), ( "MediumBlob", - quaint::Value::Bytes(Some(Cow::Borrowed(&[0x00]))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(&[0x00]))), &[ "TinyInt", "BigInt", @@ -538,10 +538,10 @@ const IMPOSSIBLE_CASTS: Cases = &[ "Year", ], ), - ("Time(0)", quaint::Value::Int32(Some(0)), &["Json", "Year"]), + ("Time(0)", quaint::ValueType::Int32(Some(0)), &["Json", "Year"]), ( "TinyBlob", - quaint::Value::Bytes(Some(Cow::Borrowed(&[0x00]))), + quaint::ValueType::Bytes(Some(Cow::Borrowed(&[0x00]))), &[ "TinyInt", "BigInt", @@ -566,7 +566,7 @@ const IMPOSSIBLE_CASTS: Cases = &[ ), ( "Year", - quaint::Value::Int32(Some(2001)), + quaint::ValueType::Int32(Some(2001)), &[ "TinyInt", "UnsignedTinyInt", @@ -638,7 +638,7 @@ fn colnames_for_cases(cases: Cases) -> Vec { fn expand_cases<'a, 'b>( from_type: &str, - test_value: &'a quaint::Value, + test_value: &'a quaint::ValueType<'a>, (to_types, nullable): (&[&str], bool), dm1: &'b mut String, dm2: &'b mut String, diff --git a/schema-engine/sql-migration-tests/tests/native_types/postgres.rs b/schema-engine/sql-migration-tests/tests/native_types/postgres.rs index 1e114f147e5d..24fa4a559744 100644 --- a/schema-engine/sql-migration-tests/tests/native_types/postgres.rs +++ b/schema-engine/sql-migration-tests/tests/native_types/postgres.rs @@ -7,12 +7,12 @@ use std::{collections::HashMap, fmt::Write as _, str::FromStr}; static SAFE_CASTS: Lazy> = Lazy::new(|| { vec![ - ("Oid", Value::integer(u8::MAX), &["VarChar(100)", "Integer", "BigInt"]), + ("Oid", Value::int32(u8::MAX), &["VarChar(100)", "Integer", "BigInt"]), ("Money", Value::int64(u8::MAX), &["VarChar(100)"]), ("Inet", Value::text("10.1.2.3"), &["VarChar(100)"]), ( "SmallInt", - Value::integer(u8::MAX), + Value::int32(u8::MAX), &[ "SmallInt", "Integer", @@ -26,7 +26,7 @@ static SAFE_CASTS: Lazy> = Lazy::new(|| { ), ( "Integer", - Value::integer(i32::MAX), + Value::int32(i32::MAX), &[ "Integer", "BigInt", @@ -67,7 +67,7 @@ static SAFE_CASTS: Lazy> = Lazy::new(|| { ), ( "DoublePrecision", - Value::Double(Some(f64::MIN)), + Value::double(f64::MIN), &["DoublePrecision", "Text", "VarChar", "Char(1000)"], ), ("VarChar", Value::text("fiver"), &["Text"]), @@ -155,13 +155,9 @@ static SAFE_CASTS: Lazy> = Lazy::new(|| { static RISKY_CASTS: Lazy> = Lazy::new(|| { vec![ ("Money", Value::int64(u8::MAX), &["Decimal"]), - ( - "SmallInt", - Value::integer(2), - &["Decimal(2,1)", "VarChar(3)", "Char(1)"], - ), - ("Integer", Value::integer(1), &["Decimal(2,1)", "VarChar(4)", "Char(1)"]), - ("BigInt", Value::integer(2), &["Decimal(2,1)", "VarChar(17)", "Char(1)"]), + ("SmallInt", Value::int32(2), &["Decimal(2,1)", "VarChar(3)", "Char(1)"]), + ("Integer", Value::int32(1), &["Decimal(2,1)", "VarChar(4)", "Char(1)"]), + ("BigInt", Value::int32(2), &["Decimal(2,1)", "VarChar(17)", "Char(1)"]), ( "Decimal(10,2)", Value::numeric(BigDecimal::from_str("1").unwrap()), @@ -227,7 +223,7 @@ static NOT_CASTABLE: Lazy> = Lazy::new(|| { vec![ ( "SmallInt", - Value::integer(u8::MAX), + Value::int32(u8::MAX), &[ "ByteA", "Timestamp(3)", @@ -246,7 +242,7 @@ static NOT_CASTABLE: Lazy> = Lazy::new(|| { ), ( "Integer", - Value::integer(i32::MAX), + Value::int32(i32::MAX), &[ "ByteA", "Timestamp(3)", @@ -1076,7 +1072,7 @@ static SAFE_CASTS_NON_LIST_TO_STRING: CastList = Lazy::new(|| { Value::array(vec![Value::numeric(BigDecimal::from_str("128.90").unwrap())]), ), ("Real", Value::array(vec![Value::float(f32::MIN)])), - ("DoublePrecision", Value::array(vec![Value::Double(Some(f64::MIN))])), + ("DoublePrecision", Value::array(vec![Value::double(f64::MIN)])), ("VarChar", Value::array(vec!["test"])), ("Char(1)", Value::array(vec!["a"])), ("Text", Value::array(vec!["text"])), @@ -1115,7 +1111,7 @@ static SAFE_CASTS_NON_LIST_TO_STRING: CastList = Lazy::new(|| { Value::array(vec![Value::numeric(BigDecimal::from_str("128.90").unwrap())]), ), ("Real", Value::array(vec![Value::float(f32::MIN)])), - ("DoublePrecision", Value::array(vec![Value::Double(Some(f64::MIN))])), + ("DoublePrecision", Value::array(vec![Value::double(f64::MIN)])), ("VarChar", Value::array(vec!["test"])), ("Char(1)", Value::array(vec!["a"])), ("Text", Value::array(vec!["text"])), diff --git a/schema-engine/sql-schema-describer/src/postgres.rs b/schema-engine/sql-schema-describer/src/postgres.rs index 8b6db47651ce..16bf0487dada 100644 --- a/schema-engine/sql-schema-describer/src/postgres.rs +++ b/schema-engine/sql-schema-describer/src/postgres.rs @@ -16,7 +16,7 @@ use psl::{ builtin_connectors::{CockroachType, PostgresType}, datamodel_connector::NativeTypeInstance, }; -use quaint::{connector::ResultRow, prelude::Queryable, Value::Array}; +use quaint::{connector::ResultRow, prelude::Queryable, Value}; use regex::Regex; use std::{ any::type_name, @@ -663,13 +663,7 @@ impl<'a> SqlSchemaDescriber<'a> { WHERE n.nspname = ANY ( $1 ) "#; - let rows = self - .conn - .query_raw( - sql, - &[Array(Some(namespaces.iter().map(|v| v.as_str().into()).collect()))], - ) - .await?; + let rows = self.conn.query_raw(sql, &[Value::array(namespaces)]).await?; let mut procedures = Vec::with_capacity(rows.len()); @@ -691,10 +685,7 @@ impl<'a> SqlSchemaDescriber<'a> { async fn get_namespaces(&self, sql_schema: &mut SqlSchema, namespaces: &[&str]) -> DescriberResult<()> { let sql = include_str!("postgres/namespaces_query.sql"); - let rows = self - .conn - .query_raw(sql, &[Array(Some(namespaces.iter().map(|s| (*s).into()).collect()))]) - .await?; + let rows = self.conn.query_raw(sql, &[Value::array(namespaces)]).await?; let names = rows.into_iter().map(|row| (row.get_expect_string("namespace_name"))); @@ -718,13 +709,7 @@ impl<'a> SqlSchemaDescriber<'a> { let namespaces = &sql_schema.namespaces; - let rows = self - .conn - .query_raw( - sql, - &[Array(Some(namespaces.iter().map(|v| v.as_str().into()).collect()))], - ) - .await?; + let rows = self.conn.query_raw(sql, &[Value::array(namespaces)]).await?; let mut names = Vec::with_capacity(rows.len()); @@ -826,13 +811,7 @@ impl<'a> SqlSchemaDescriber<'a> { WHERE schemaname = ANY ( $1 ) "#}; - let result_set = self - .conn - .query_raw( - sql, - &[Array(Some(namespaces.iter().map(|v| v.as_str().into()).collect()))], - ) - .await?; + let result_set = self.conn.query_raw(sql, &[Value::array(namespaces)]).await?; for row in result_set.into_iter() { let name = row.get_expect_string("view_name"); @@ -896,13 +875,7 @@ impl<'a> SqlSchemaDescriber<'a> { "# ); - let rows = self - .conn - .query_raw( - sql.as_str(), - &[Array(Some(namespaces.iter().map(|v| v.as_str().into()).collect()))], - ) - .await?; + let rows = self.conn.query_raw(sql.as_str(), &[Value::array(namespaces)]).await?; for col in rows { let namespace = col.get_expect_string("namespace"); @@ -1141,13 +1114,7 @@ impl<'a> SqlSchemaDescriber<'a> { // One foreign key with multiple columns will be represented here as several // rows with the same ID. - let result_set = self - .conn - .query_raw( - sql, - &[Array(Some(namespaces.iter().map(|v| v.as_str().into()).collect()))], - ) - .await?; + let result_set = self.conn.query_raw(sql, &[Value::array(namespaces)]).await?; for row in result_set.into_iter() { trace!("Got description FK row {:?}", row); @@ -1254,13 +1221,7 @@ impl<'a> SqlSchemaDescriber<'a> { let namespaces = &sql_schema.namespaces; let sql = include_str!("postgres/constraints_query.sql"); - let rows = self - .conn - .query_raw( - sql, - &[Array(Some(namespaces.iter().map(|v| v.as_str().into()).collect()))], - ) - .await?; + let rows = self.conn.query_raw(sql, &[Value::array(namespaces)]).await?; for row in rows { let namespace = row.get_expect_string("namespace"); @@ -1298,13 +1259,7 @@ impl<'a> SqlSchemaDescriber<'a> { ) -> DescriberResult<()> { let namespaces = &sql_schema.namespaces; let sql = include_str!("postgres/indexes_query.sql"); - let rows = self - .conn - .query_raw( - sql, - &[Array(Some(namespaces.iter().map(|v| v.as_str().into()).collect()))], - ) - .await?; + let rows = self.conn.query_raw(sql, &[Value::array(namespaces)]).await?; let mut result_rows = Vec::new(); let mut index_rows = rows.into_iter().peekable(); @@ -1374,13 +1329,7 @@ impl<'a> SqlSchemaDescriber<'a> { "# }; - let rows = self - .conn - .query_raw( - sql, - &[Array(Some(namespaces.iter().map(|v| v.as_str().into()).collect()))], - ) - .await?; + let rows = self.conn.query_raw(sql, &[Value::array(namespaces)]).await?; let sequences = rows.into_iter().map(|seq| Sequence { namespace_id: sql_schema .get_namespace_id(&seq.get_expect_string("namespace")) @@ -1414,13 +1363,7 @@ impl<'a> SqlSchemaDescriber<'a> { WHERE n.nspname = ANY ( $1 ) ORDER BY e.enumsortorder"; - let rows = self - .conn - .query_raw( - sql, - &[Array(Some(namespaces.iter().map(|v| v.as_str().into()).collect()))], - ) - .await?; + let rows = self.conn.query_raw(sql, &[Value::array(namespaces)]).await?; let mut enum_values: BTreeMap<(NamespaceId, String, Option), Vec> = BTreeMap::new(); for row in rows.into_iter() { diff --git a/schema-engine/sql-schema-describer/src/sqlite.rs b/schema-engine/sql-schema-describer/src/sqlite.rs index 3073be2b4daa..1f28958605a2 100644 --- a/schema-engine/sql-schema-describer/src/sqlite.rs +++ b/schema-engine/sql-schema-describer/src/sqlite.rs @@ -8,7 +8,7 @@ use crate::{ use either::Either; use indexmap::IndexMap; use quaint::{ - ast::Value, + ast::{Value, ValueType}, connector::{GetRow, ToColumnNames}, prelude::ResultRow, }; @@ -345,7 +345,10 @@ async fn push_columns( let default = match row.get("dflt_value") { None => None, Some(val) if val.is_null() => None, - Some(Value::Text(Some(cow_string))) => { + Some(Value { + typed: ValueType::Text(Some(cow_string)), + .. + }) => { let default_string = cow_string.to_string(); if default_string.to_lowercase() == "null" {