From 830716c1109d0dae7bf7c52df11f8c9a98d87b5f Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 27 Oct 2021 17:13:27 +0200 Subject: [PATCH 01/18] Rewrite bind serialization layer This commit removes a old hack from our bind serialization layer that requires multiple value copies for the sqlite backend going first to a binary format and later on deserialize the data again to call the corresponding bind function. After this commit it is possible to just store the corresponding values and call the corresponding bind functions directly onto those values. This removes a therotical overhead and should improve the performance on bind heavy workloads (like inserts). To do this we basically need to teach the compiler about a invariant that is already true: Bind values need to live at least as long as the corresponding query. To describe that in the type system we need to introduce a few lifetime bounds for `QueryFragment::walk_ast` and `ToSql::to_sql`. In exchange we can remove the write bound from `ToSql::to_sql`. In my opion that does not make the it harder to implement the corresponding traits as users don't need to interact with those lifetimes at all and they describe something that is already true for almost all cases. Additionally now that rust-analyzer can generate function bodies for trait implementations on the fly users don't need to type those bounds, as they are generated with the body stub. Most of the changes are releated to just introduce the lifetime bounds in all nessesary impls and fix minor incompatibilities there. --- diesel/Cargo.toml | 8 +- diesel/src/backend.rs | 34 ++- diesel/src/expression/array_comparison.rs | 37 ++- diesel/src/expression/assume_not_null.rs | 6 +- diesel/src/expression/bound.rs | 7 +- diesel/src/expression/coerce.rs | 5 +- diesel/src/expression/count.rs | 14 +- diesel/src/expression/exists.rs | 10 +- .../src/expression/functions/date_and_time.rs | 10 +- diesel/src/expression/functions/mod.rs | 6 +- diesel/src/expression/grouped.rs | 5 +- diesel/src/expression/nullable.rs | 6 +- diesel/src/expression/operators.rs | 18 +- diesel/src/expression/ops/numeric.rs | 4 +- diesel/src/expression/select_by.rs | 44 +++- diesel/src/expression/sql_literal.rs | 10 +- diesel/src/expression/subselect.rs | 5 +- diesel/src/insertable.rs | 52 +++-- diesel/src/lib.rs | 6 +- diesel/src/macros/mod.rs | 141 +++++++----- diesel/src/migration/mod.rs | 11 +- diesel/src/mysql/backend.rs | 6 +- .../src/mysql/query_builder/limit_offset.rs | 8 +- .../query_builder/query_fragment_impls.rs | 12 +- diesel/src/mysql/types/date_and_time.rs | 19 +- diesel/src/mysql/types/json.rs | 8 +- diesel/src/mysql/types/mod.rs | 68 ++++-- diesel/src/mysql/types/numeric.rs | 2 +- diesel/src/pg/backend.rs | 6 +- diesel/src/pg/expression/array.rs | 9 +- diesel/src/pg/expression/array_comparison.rs | 4 +- diesel/src/pg/expression/date_and_time.rs | 2 +- diesel/src/pg/query_builder/distinct_on.rs | 16 +- diesel/src/pg/query_builder/limit_offset.rs | 4 +- diesel/src/pg/query_builder/on_constraint.rs | 2 +- .../pg/query_builder/query_fragment_impls.rs | 28 ++- diesel/src/pg/serialize/write_tuple.rs | 9 +- diesel/src/pg/transaction.rs | 8 +- diesel/src/pg/types/array.rs | 11 +- diesel/src/pg/types/date_and_time/chrono.rs | 19 +- diesel/src/pg/types/date_and_time/mod.rs | 11 +- diesel/src/pg/types/date_and_time/std_time.rs | 5 +- diesel/src/pg/types/floats/mod.rs | 52 ++++- diesel/src/pg/types/integers.rs | 103 ++++++++- diesel/src/pg/types/json.rs | 14 +- diesel/src/pg/types/mac_addr.rs | 7 +- diesel/src/pg/types/money.rs | 4 +- diesel/src/pg/types/network_address.rs | 49 ++-- diesel/src/pg/types/numeric.rs | 5 +- diesel/src/pg/types/primitives.rs | 31 ++- diesel/src/pg/types/ranges.rs | 10 +- diesel/src/pg/types/record.rs | 9 +- diesel/src/pg/types/uuid.rs | 7 +- diesel/src/pg/value.rs | 8 - diesel/src/query_builder/ast_pass.rs | 43 ++-- diesel/src/query_builder/bind_collector.rs | 33 ++- diesel/src/query_builder/clause_macro.rs | 8 +- .../src/query_builder/combination_clause.rs | 50 +++- .../src/query_builder/delete_statement/mod.rs | 82 +++++-- diesel/src/query_builder/distinct_clause.rs | 10 +- diesel/src/query_builder/from_clause.rs | 139 +++++++++++ diesel/src/query_builder/functions.rs | 30 ++- .../insert_statement/batch_insert.rs | 192 ++-------------- .../insert_statement/insert_from_select.rs | 5 +- .../insert_with_default_for_sqlite.rs | 216 ++++++------------ .../src/query_builder/insert_statement/mod.rs | 95 +++++--- diesel/src/query_builder/locking_clause.rs | 10 +- diesel/src/query_builder/mod.rs | 53 +++-- diesel/src/query_builder/nodes/mod.rs | 60 +++-- diesel/src/query_builder/returning_clause.rs | 15 +- diesel/src/query_builder/select_clause.rs | 145 ++++++------ .../query_builder/select_statement/boxed.rs | 177 ++++++++++---- .../select_statement/dsl_impls.rs | 192 ++++++++++++++-- .../src/query_builder/select_statement/mod.rs | 118 +++++----- diesel/src/query_builder/sql_query.rs | 44 +++- .../update_statement/changeset.rs | 5 +- .../src/query_builder/update_statement/mod.rs | 40 ++-- .../upsert/into_conflict_clause.rs | 18 +- .../upsert/on_conflict_actions.rs | 30 ++- .../upsert/on_conflict_clause.rs | 5 +- .../upsert/on_conflict_target.rs | 29 ++- .../upsert/on_conflict_target_decorations.rs | 5 +- diesel/src/query_builder/where_clause.rs | 28 ++- diesel/src/query_dsl/boxed_dsl.rs | 7 +- diesel/src/query_dsl/distinct_dsl.rs | 14 +- diesel/src/query_dsl/group_by_dsl.rs | 5 +- diesel/src/query_dsl/locking_dsl.rs | 5 +- diesel/src/query_dsl/mod.rs | 2 + diesel/src/query_dsl/positional_order_dsl.rs | 15 +- diesel/src/query_source/joins.rs | 138 ++++++++--- diesel/src/serialize.rs | 142 ++++++++---- diesel/src/sqlite/backend.rs | 10 +- diesel/src/sqlite/connection/functions.rs | 30 +-- diesel/src/sqlite/connection/mod.rs | 10 +- diesel/src/sqlite/connection/raw.rs | 45 ++-- .../src/sqlite/connection/serialized_value.rs | 113 --------- diesel/src/sqlite/connection/stmt.rs | 75 +++++- .../src/sqlite/query_builder/limit_offset.rs | 25 +- diesel/src/sqlite/query_builder/mod.rs | 111 ++++++++- .../src/sqlite/types/date_and_time/chrono.rs | 34 +-- diesel/src/sqlite/types/date_and_time/mod.rs | 38 ++- diesel/src/sqlite/types/mod.rs | 85 ++++++- diesel/src/type_impls/floats.rs | 57 ----- diesel/src/type_impls/integers.rs | 96 +------- diesel/src/type_impls/mod.rs | 4 +- diesel/src/type_impls/option.rs | 30 ++- diesel/src/type_impls/primitives.rs | 64 +++--- diesel/src/type_impls/tuples.rs | 5 +- diesel/src/upsert/on_conflict_extension.rs | 9 +- diesel_cli/src/query_helper.rs | 4 +- ...ectable_if_inner_expr_is_selectable.stderr | 6 +- ...ay_expressions_must_be_correct_type.stderr | 45 ++-- ...array_expressions_must_be_same_type.stderr | 8 +- .../fail/boxed_queries_and_group_by.stderr | 44 ++-- ...must_be_used_with_proper_connection.stderr | 2 +- ...re_selectable_expression_for_filter.stderr | 2 +- ...ire_selectable_expression_for_order.stderr | 7 +- .../cannot_join_to_non_joinable_table.stderr | 14 +- ...t_load_default_select_with_group_by.stderr | 4 +- ...aggregate_and_non_aggregate_selects.stderr | 6 +- .../cannot_pass_aggregate_to_where.stderr | 2 +- ...th_methods_other_than_filter_called.stderr | 12 +- ...nct_on_clause_only_supported_for_pg.stderr | 4 +- ...t_on_requires_matching_order_clause.stderr | 12 +- .../exists_can_only_take_subselects.stderr | 4 +- ...ison_for_columns_from_another_table.stderr | 30 +-- ...quires_bool_nonaggregate_expression.stderr | 4 +- .../fail/find_requires_correct_type.stderr | 8 +- ...aving_cant_be_used_without_group_by.stderr | 22 +- ..._cant_be_used_with_tuples_or_arrays.stderr | 2 +- ...m_select_requires_valid_column_list.stderr | 8 +- ...n_requires_valid_boolean_expression.stderr | 8 +- ...es_not_support_offset_without_limit.stderr | 14 +- ...der_requires_column_from_same_table.stderr | 5 +- ...ions_cant_be_used_in_a_sqlite_query.stderr | 6 +- ...ct_carries_correct_result_type_info.stderr | 10 +- ...for_update_cannot_be_used_on_sqlite.stderr | 8 +- ...te_no_wait_cannot_be_used_on_sqlite.stderr | 8 +- ...kip_locked_cannot_be_used_on_sqlite.stderr | 8 +- .../select_requires_valid_grouping.stderr | 14 +- ...elect_sql_still_ensures_result_type.stderr | 18 +- ...lect_cannot_reference_random_tables.stderr | 30 +-- .../subselect_requires_correct_type.stderr | 4 +- .../update_requires_valid_where_clause.stderr | 12 +- ...iple_values_not_supported_on_sqlite.stderr | 20 +- ...nctions_follow_same_selection_rules.stderr | 6 +- ...alid_grouping_and_boxed_expressions.stderr | 12 +- diesel_derives/src/as_expression.rs | 4 +- diesel_derives/src/lib.rs | 2 +- diesel_derives/src/sql_function.rs | 19 +- diesel_derives/tests/as_expression.rs | 7 +- diesel_dynamic_schema/src/column.rs | 2 +- diesel_dynamic_schema/src/dynamic_select.rs | 30 +-- diesel_dynamic_schema/src/table.rs | 8 +- diesel_migrations/src/migration_harness.rs | 94 ++++++++ diesel_tests/tests/boxed_queries.rs | 6 +- diesel_tests/tests/custom_types.rs | 12 +- diesel_tests/tests/expressions/mod.rs | 5 +- diesel_tests/tests/filter.rs | 2 + diesel_tests/tests/schema_dsl/structures.rs | 34 ++- diesel_tests/tests/serialize_as.rs | 6 +- .../advanced-blog-cli/src/pagination.rs | 13 +- examples/postgres/custom_types/src/model.rs | 2 +- 163 files changed, 2907 insertions(+), 1813 deletions(-) create mode 100644 diesel/src/query_builder/from_clause.rs delete mode 100644 diesel/src/sqlite/connection/serialized_value.rs diff --git a/diesel/Cargo.toml b/diesel/Cargo.toml index 6da8b7be4d05..7d46ad13a92a 100644 --- a/diesel/Cargo.toml +++ b/diesel/Cargo.toml @@ -13,7 +13,7 @@ categories = ["database"] edition = "2018" [dependencies] -byteorder = "1.0" +byteorder = {version = "1.0", optional = true} chrono = { version = "0.4.19", optional = true, default-features = false, features = ["clock", "std"] } libc = { version = "0.2.0", optional = true } libsqlite3-sys = { version = ">=0.8.0, <0.24.0", optional = true, features = ["bundled_bindings"] } @@ -44,7 +44,7 @@ ipnetwork = ">=0.12.2, <0.19.0" quickcheck = "0.9" [features] -default = ["32-column-tables", "with-deprecated"] +default = ["with-deprecated", "32-column-tables"] extras = ["chrono", "serde_json", "uuid", "network-address", "numeric", "r2d2"] unstable = ["diesel_derives/nightly"] large-tables = ["32-column-tables"] @@ -59,8 +59,8 @@ without-deprecated = [] with-deprecated = [] network-address = ["ipnetwork", "libc"] numeric = ["num-bigint", "bigdecimal", "num-traits", "num-integer"] -postgres_backend = ["diesel_derives/postgres", "bitflags"] -mysql_backend = ["diesel_derives/mysql"] +postgres_backend = ["diesel_derives/postgres", "bitflags", "byteorder"] +mysql_backend = ["diesel_derives/mysql", "byteorder"] [package.metadata.docs.rs] features = ["postgres", "mysql", "sqlite", "extras"] diff --git a/diesel/src/backend.rs b/diesel/src/backend.rs index 8bdb52dd8682..2a21928ec032 100644 --- a/diesel/src/backend.rs +++ b/diesel/src/backend.rs @@ -1,10 +1,6 @@ //! Types which represent various database backends - -use byteorder::ByteOrder; - -use crate::query_builder::bind_collector::BindCollector; use crate::query_builder::QueryBuilder; -use crate::sql_types::{self, HasSqlType}; +use crate::sql_types::{self, HasSqlType, TypeMetadata}; /// A database backend /// @@ -33,19 +29,24 @@ where Self: HasSqlType, Self: HasSqlType, Self: for<'a> HasRawValue<'a>, + Self: for<'a> HasBindCollector<'a>, { /// The concrete `QueryBuilder` implementation for this backend. type QueryBuilder: QueryBuilder; +} + +/// The bind collector type used to collect query binds for this backend +/// +/// This trait is separate from `Backend` to imitate `type BindCollector<'a>`. It +/// should only be referenced directly by implementors. Users of this type +/// should instead use the [`BindCollector`] helper type instead. +pub trait HasBindCollector<'a>: TypeMetadata + Sized { /// The concrete `BindCollector` implementation for this backend. /// /// Most backends should use [`RawBytesBindCollector`]. /// /// [`RawBytesBindCollector`]: crate::query_builder::bind_collector::RawBytesBindCollector - type BindCollector: BindCollector; - /// What byte order is used to transmit integers? - /// - /// This type is only used if `RawValue` is `[u8]`. - type ByteOrder: ByteOrder; + type BindCollector: crate::query_builder::bind_collector::BindCollector<'a, Self> + 'a; } /// The raw representation of a database value given to `FromSql`. @@ -60,19 +61,14 @@ pub trait HasRawValue<'a> { type RawValue; } -/// A trait indicating that the provided raw value uses a binary representation internally -// That's a false positive, `HasRawValue<'a>` is essentially -// a reference wrapper -#[allow(clippy::wrong_self_convention)] -pub trait BinaryRawValue<'a>: HasRawValue<'a> { - /// Get the underlying binary representation of the raw value - fn as_bytes(value: Self::RawValue) -> &'a [u8]; -} - /// A helper type to get the raw representation of a database type given to /// `FromSql`. Equivalent to `::RawValue<'a>`. pub type RawValue<'a, DB> = >::RawValue; +/// A helper type to get the bind collector for a database backend. +/// Equivalent to `>::BindCollector<'a>`j +pub type BindCollector<'a, DB> = >::BindCollector; + /// This trait provides various options to configure the /// generated SQL for a specific backend. /// diff --git a/diesel/src/expression/array_comparison.rs b/diesel/src/expression/array_comparison.rs index a6e69d0fa9d6..987353c7e290 100644 --- a/diesel/src/expression/array_comparison.rs +++ b/diesel/src/expression/array_comparison.rs @@ -1,12 +1,12 @@ use crate::backend::sql_dialect; use crate::backend::Backend; use crate::backend::SqlDialect; -use crate::expression::bound::Bound; use crate::expression::subselect::Subselect; use crate::expression::*; use crate::query_builder::*; use crate::query_builder::{BoxedSelectStatement, SelectStatement}; use crate::result::QueryResult; +use crate::serialize::ToSql; use crate::sql_types::Bool; use std::marker::PhantomData; @@ -55,7 +55,10 @@ where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } @@ -68,7 +71,10 @@ where T: QueryFragment, U: QueryFragment + MaybeEmpty, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { if self.values.is_empty() { out.push_sql("1=0"); } else { @@ -86,7 +92,10 @@ where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } @@ -99,7 +108,10 @@ where T: QueryFragment, U: QueryFragment + MaybeEmpty, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { if self.values.is_empty() { out.push_sql("1=1"); } else { @@ -215,7 +227,10 @@ where Self: QueryFragment, DB: Backend, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } @@ -224,11 +239,15 @@ impl QueryFragment where DB: Backend + + HasSqlType + SqlDialect, ST: SingleValue, - for<'a> Bound: QueryFragment, + I: ToSql, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.unsafe_to_cache_prepared(); let mut first = true; for value in &self.0 { @@ -237,7 +256,7 @@ where } else { out.push_sql(", "); } - Bound::new(value).walk_ast(out.reborrow())?; + out.push_bind_param(value)?; } Ok(()) } diff --git a/diesel/src/expression/assume_not_null.rs b/diesel/src/expression/assume_not_null.rs index 756ecce9f99e..f9d6f22dc824 100644 --- a/diesel/src/expression/assume_not_null.rs +++ b/diesel/src/expression/assume_not_null.rs @@ -1,7 +1,6 @@ use crate::backend::Backend; use crate::expression::TypedExpressionType; use crate::expression::*; -use crate::query_builder::select_statement::NoFromClause; use crate::query_builder::*; use crate::query_source::joins::ToInnerJoin; use crate::result::QueryResult; @@ -30,7 +29,10 @@ where DB: Backend, T: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { self.0.walk_ast(pass) } } diff --git a/diesel/src/expression/bound.rs b/diesel/src/expression/bound.rs index 6c3d33d976e9..f47cdb5e5534 100644 --- a/diesel/src/expression/bound.rs +++ b/diesel/src/expression/bound.rs @@ -16,7 +16,7 @@ pub struct Bound { impl Bound { pub fn new(item: U) -> Self { Bound { - item: item, + item, _marker: PhantomData, } } @@ -34,7 +34,10 @@ where DB: Backend + HasSqlType, U: ToSql, { - fn walk_ast(&self, mut pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { pass.push_bind_param(&self.item)?; Ok(()) } diff --git a/diesel/src/expression/coerce.rs b/diesel/src/expression/coerce.rs index ecf3eacc5084..340f01e65e94 100644 --- a/diesel/src/expression/coerce.rs +++ b/diesel/src/expression/coerce.rs @@ -60,7 +60,10 @@ where T: QueryFragment, DB: Backend, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { self.expr.walk_ast(pass) } } diff --git a/diesel/src/expression/count.rs b/diesel/src/expression/count.rs index 6bb79e0211a5..a624dea3bbcb 100644 --- a/diesel/src/expression/count.rs +++ b/diesel/src/expression/count.rs @@ -68,7 +68,10 @@ impl Expression for CountStar { } impl QueryFragment for CountStar { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("COUNT(*)"); Ok(()) } @@ -147,11 +150,14 @@ impl QueryFragment for CountDistinct where T: SqlType + SingleValue, DB: Backend, - for<'a> &'a E: QueryFragment, + E: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("COUNT(DISTINCT "); - (&self.expr).walk_ast(out.reborrow())?; + self.expr.walk_ast(out.reborrow())?; out.push_sql(")"); Ok(()) } diff --git a/diesel/src/expression/exists.rs b/diesel/src/expression/exists.rs index 08607550f904..13236b12b333 100644 --- a/diesel/src/expression/exists.rs +++ b/diesel/src/expression/exists.rs @@ -55,7 +55,10 @@ where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } @@ -65,7 +68,10 @@ where DB: Backend + SqlDialect, T: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("EXISTS ("); self.0.walk_ast(out.reborrow())?; out.push_sql(")"); diff --git a/diesel/src/expression/functions/date_and_time.rs b/diesel/src/expression/functions/date_and_time.rs index 51d44189ab72..1dd86eb5d54f 100644 --- a/diesel/src/expression/functions/date_and_time.rs +++ b/diesel/src/expression/functions/date_and_time.rs @@ -17,7 +17,10 @@ impl Expression for now { } impl QueryFragment for now { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("CURRENT_TIMESTAMP"); Ok(()) } @@ -82,7 +85,10 @@ impl Expression for today { } impl QueryFragment for today { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("CURRENT_DATE"); Ok(()) } diff --git a/diesel/src/expression/functions/mod.rs b/diesel/src/expression/functions/mod.rs index 6191b3552f81..25f5da46587d 100644 --- a/diesel/src/expression/functions/mod.rs +++ b/diesel/src/expression/functions/mod.rs @@ -35,7 +35,9 @@ macro_rules! no_arg_sql_function_body { impl $crate::query_builder::QueryFragment for $type_name where DB: $crate::backend::Backend + $($constraint)::+, { - fn walk_ast(&self, mut out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: $crate::query_builder::AstPass<'_, 'b, DB>) + -> $crate::result::QueryResult<()> + { out.push_sql(concat!(stringify!($type_name), "()")); Ok(()) } @@ -48,7 +50,7 @@ macro_rules! no_arg_sql_function_body { impl $crate::query_builder::QueryFragment for $type_name where DB: $crate::backend::Backend, { - fn walk_ast(&self, mut out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: $crate::query_builder::AstPass<'_, 'b, DB>) -> $crate::result::QueryResult<()> { out.push_sql(concat!(stringify!($type_name), "()")); Ok(()) } diff --git a/diesel/src/expression/grouped.rs b/diesel/src/expression/grouped.rs index bd775821b964..257fd1bca76a 100644 --- a/diesel/src/expression/grouped.rs +++ b/diesel/src/expression/grouped.rs @@ -12,7 +12,10 @@ impl Expression for Grouped { } impl, DB: Backend> QueryFragment for Grouped { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("("); self.0.walk_ast(out.reborrow())?; out.push_sql(")"); diff --git a/diesel/src/expression/nullable.rs b/diesel/src/expression/nullable.rs index d95a65b19638..2f96fcdb1bfd 100644 --- a/diesel/src/expression/nullable.rs +++ b/diesel/src/expression/nullable.rs @@ -1,7 +1,6 @@ use crate::backend::Backend; use crate::expression::TypedExpressionType; use crate::expression::*; -use crate::query_builder::select_statement::NoFromClause; use crate::query_builder::*; use crate::query_source::joins::ToInnerJoin; use crate::result::QueryResult; @@ -30,7 +29,10 @@ where DB: Backend, T: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { self.0.walk_ast(pass) } } diff --git a/diesel/src/expression/operators.rs b/diesel/src/expression/operators.rs index de4eb8f04744..105656932c28 100644 --- a/diesel/src/expression/operators.rs +++ b/diesel/src/expression/operators.rs @@ -93,7 +93,12 @@ macro_rules! __diesel_operator_body { $($ty_param: $crate::query_builder::QueryFragment<$backend_ty>,)+ $($backend_ty_param: $crate::backend::Backend,)* { - fn walk_ast(&self, mut out: $crate::query_builder::AstPass<$backend_ty>) -> $crate::result::QueryResult<()> { + fn walk_ast<'a, 'b>( + &'a self, + mut out: $crate::query_builder::AstPass<'_, 'b, $backend_ty> + ) -> $crate::result::QueryResult<()> + where 'a: 'b + { $crate::__diesel_operator_to_sql!( notation = $notation, operator_expr = out.push_sql($operator), @@ -585,10 +590,13 @@ where R: QueryFragment, DB: Backend, { - fn walk_ast( - &self, - mut out: crate::query_builder::AstPass, - ) -> crate::result::QueryResult<()> { + fn walk_ast<'a, 'b>( + &'a self, + mut out: crate::query_builder::AstPass<'_, 'b, DB>, + ) -> crate::result::QueryResult<()> + where + 'a: 'b, + { // Those brackets are required because mysql is broken // https://github.com/diesel-rs/diesel/issues/2133#issuecomment-517432317 out.push_sql("("); diff --git a/diesel/src/expression/ops/numeric.rs b/diesel/src/expression/ops/numeric.rs index 1d5a5d4153cd..4436a0535b90 100644 --- a/diesel/src/expression/ops/numeric.rs +++ b/diesel/src/expression/ops/numeric.rs @@ -37,7 +37,9 @@ macro_rules! numeric_operation { Lhs: QueryFragment, Rhs: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where 'a: 'b + { out.push_sql("("); self.lhs.walk_ast(out.reborrow())?; out.push_sql($op); diff --git a/diesel/src/expression/select_by.rs b/diesel/src/expression/select_by.rs index dff302c1cfa3..dabfe01d42cb 100644 --- a/diesel/src/expression/select_by.rs +++ b/diesel/src/expression/select_by.rs @@ -7,16 +7,32 @@ use crate::expression::{ use crate::query_builder::*; use crate::result::QueryResult; -#[derive(Debug, Default)] -pub struct SelectBy(std::marker::PhantomData<(T, DB)>); +#[derive(Debug)] +pub struct SelectBy, DB: Backend> { + selection: T::SelectExpression, + p: std::marker::PhantomData<(T, DB)>, +} -impl Clone for SelectBy { +impl Clone for SelectBy +where + DB: Backend, + T: Selectable, +{ fn clone(&self) -> Self { - Self(self.0) + Self { + selection: T::construct_selection(), + p: std::marker::PhantomData, + } } } -impl Copy for SelectBy {} +impl Copy for SelectBy +where + T: Selectable, + DB: Backend, + T::SelectExpression: Copy, +{ +} impl QueryId for SelectBy where @@ -29,9 +45,16 @@ where const HAS_STATIC_QUERY_ID: bool = E::HAS_STATIC_QUERY_ID; } -impl SelectBy { +impl SelectBy +where + T: Selectable, + DB: Backend, +{ pub(crate) fn new() -> Self { - Self(Default::default()) + Self { + selection: T::construct_selection(), + p: std::marker::PhantomData, + } } } @@ -79,8 +102,11 @@ where T::SelectExpression: QueryFragment, DB: Backend, { - fn walk_ast(&self, out: AstPass) -> QueryResult<()> { - T::construct_selection().walk_ast(out) + fn walk_ast<'a, 'b>(&'a self, out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { + self.selection.walk_ast(out) } } diff --git a/diesel/src/expression/sql_literal.rs b/diesel/src/expression/sql_literal.rs index 64de67a44df4..e3aeb1a134c4 100644 --- a/diesel/src/expression/sql_literal.rs +++ b/diesel/src/expression/sql_literal.rs @@ -168,7 +168,10 @@ where DB: Backend, T: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.unsafe_to_cache_prepared(); self.inner.walk_ast(out.reborrow())?; out.push_sql(&self.sql); @@ -345,7 +348,10 @@ where Query: QueryFragment, Value: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { self.query.walk_ast(out.reborrow())?; self.value.walk_ast(out.reborrow())?; Ok(()) diff --git a/diesel/src/expression/subselect.rs b/diesel/src/expression/subselect.rs index a0bab4e8b240..7dcda4c8b3a5 100644 --- a/diesel/src/expression/subselect.rs +++ b/diesel/src/expression/subselect.rs @@ -60,7 +60,10 @@ where DB: Backend, T: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { self.values.walk_ast(out.reborrow())?; Ok(()) } diff --git a/diesel/src/insertable.rs b/diesel/src/insertable.rs index e60160d9bf38..07cf55386a8f 100644 --- a/diesel/src/insertable.rs +++ b/diesel/src/insertable.rs @@ -68,6 +68,7 @@ pub trait Insertable { /// ``` fn insert_into(self, table: T) -> InsertStatement where + T: Table, Self: Sized, { crate::insert_into(table).values(self) @@ -184,7 +185,10 @@ where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } @@ -194,7 +198,7 @@ where DB: Backend + SqlDialect, Expr: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> where 'a: 'b { out.unsafe_to_cache_prepared(); if let Self::Expression(ref inner) = *self { inner.walk_ast(out.reborrow())?; @@ -210,7 +214,10 @@ where DB: Backend, Expr: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { self.expr.walk_ast(pass) } } @@ -240,7 +247,13 @@ impl where Expr: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>( + &'a self, + mut out: AstPass<'_, 'b, crate::sqlite::Sqlite>, + ) -> QueryResult<()> + where + 'a: 'b, + { if let Self::Expression(ref inner) = *self { inner.walk_ast(out.reborrow())?; } @@ -250,12 +263,13 @@ where impl<'a, T, Tab> Insertable for &'a [T] where - &'a T: UndecoratedInsertRecord, + &'a T: UndecoratedInsertRecord + Insertable, { - type Values = BatchInsert<&'a [T], Tab, (), false>; + type Values = BatchInsert>::Values>, Tab, (), false>; fn values(self) -> Self::Values { - BatchInsert::new(self) + let values = self.iter().map(Insertable::values).collect::>(); + BatchInsert::new(values) } } @@ -274,10 +288,11 @@ impl Insertable for Vec where T: Insertable + UndecoratedInsertRecord, { - type Values = BatchInsert, Tab, (), false>; + type Values = BatchInsert, Tab, (), false>; fn values(self) -> Self::Values { - BatchInsert::new(self) + let values = self.into_iter().map(Insertable::values).collect::>(); + BatchInsert::new(values) } } @@ -285,23 +300,28 @@ impl Insertable for [T; N] where T: Insertable, { - type Values = BatchInsert<[T; N], Tab, [T::Values; N], true>; + type Values = BatchInsert, Tab, [T::Values; N], true>; fn values(self) -> Self::Values { - BatchInsert::new(self) + let values = std::array::IntoIter::new(self) + .map(Insertable::values) + .collect::>(); + BatchInsert::new(values) } } impl<'a, T, Tab, const N: usize> Insertable for &'a [T; N] where T: Insertable, + &'a T: Insertable, { // We can reuse the query id for [T; N] here as this // compiles down to the same query - type Values = BatchInsert<&'a [T; N], Tab, [T::Values; N], true>; + type Values = BatchInsert>::Values>, Tab, [T::Values; N], true>; fn values(self) -> Self::Values { - BatchInsert::new(self) + let values = self.iter().map(Insertable::values).collect(); + BatchInsert::new(values) } } @@ -311,10 +331,12 @@ where { // We can reuse the query id for [T; N] here as this // compiles down to the same query - type Values = BatchInsert, Tab, [T::Values; N], true>; + type Values = BatchInsert, Tab, [T::Values; N], true>; fn values(self) -> Self::Values { - BatchInsert::new(self) + let v = Vec::from(self as Box<[T]>); + let values = v.into_iter().map(Insertable::values).collect::>(); + BatchInsert::new(values) } } diff --git a/diesel/src/lib.rs b/diesel/src/lib.rs index 1fa71c86e194..db8acc828847 100644 --- a/diesel/src/lib.rs +++ b/diesel/src/lib.rs @@ -213,9 +213,9 @@ pub mod helper_types { pub type Select = >::Output; /// Represents the return type of `diesel::select(selection)` - pub type BareSelect = Select< - crate::query_builder::SelectStatement, - T, + pub type BareSelect = crate::query_builder::SelectStatement< + crate::query_builder::NoFromClause, + crate::query_builder::select_clause::SelectClause, >; /// Represents the return type of `.filter(predicate)` diff --git a/diesel/src/macros/mod.rs b/diesel/src/macros/mod.rs index ff2649741955..f049e0e9b13a 100644 --- a/diesel/src/macros/mod.rs +++ b/diesel/src/macros/mod.rs @@ -50,6 +50,8 @@ macro_rules! __diesel_internal_table_backend_specific_impls { macro_rules! __diesel_column { ( table = $table:ident, + table_sql_name = $table_sql_name:expr, + table_schema = $table_schema:ident, name = $column_name:ident, sql_name = $sql_name:expr, ty = ($($Type:tt)*), @@ -69,9 +71,13 @@ macro_rules! __diesel_column { <$table as $crate::QuerySource>::FromClause: $crate::query_builder::QueryFragment, { #[allow(non_snake_case)] - fn walk_ast(&self, mut __out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut __out: $crate::query_builder::AstPass<'_, 'b, DB>) -> $crate::result::QueryResult<()> + where 'a: 'b + { use $crate::QuerySource; - $table.from_clause().walk_ast(__out.reborrow())?; + const FROM_CLAUSE: $crate::query_builder::nodes::StaticQueryFragmentInstance = $crate::query_builder::nodes::StaticQueryFragmentInstance::new(); + + FROM_CLAUSE.walk_ast(__out.reborrow())?; __out.push_sql("."); __out.push_identifier($sql_name) } @@ -86,22 +92,23 @@ macro_rules! __diesel_column { } impl $crate::SelectableExpression< - $crate::query_source::joins::Join, + $crate::query_source::joins::Join, > for $column_name where $column_name: $crate::AppearsOnTable<$crate::query_source::joins::Join>, Self: $crate::SelectableExpression, // If our table is on the right side of this join, only // `Nullable` can be selected - Right: $crate::query_source::AppearsInFromClause<$table, Count=$crate::query_source::Never>, + Right: $crate::query_source::AppearsInFromClause<$table, Count=$crate::query_source::Never> + $crate::query_source::QuerySource, + Left: $crate::query_source::QuerySource { } impl $crate::SelectableExpression< - $crate::query_source::joins::Join, + $crate::query_source::joins::Join, > for $column_name where $column_name: $crate::AppearsOnTable<$crate::query_source::joins::Join>, - Left: $crate::query_source::AppearsInFromClause<$table>, - Right: $crate::query_source::AppearsInFromClause<$table>, + Left: $crate::query_source::AppearsInFromClause<$table> + $crate::query_source::QuerySource, + Right: $crate::query_source::AppearsInFromClause<$table> + $crate::query_source::QuerySource, (Left::Count, Right::Count): $crate::query_source::Pick, Self: $crate::SelectableExpression< <(Left::Count, Right::Count) as $crate::query_source::Pick>::Selection, @@ -116,8 +123,9 @@ macro_rules! __diesel_column { } // FIXME: Remove this when overlapping marker traits are stable - impl $crate::SelectableExpression<$crate::query_builder::SelectStatement> for $column_name where - $column_name: $crate::SelectableExpression + $crate::AppearsOnTable<$crate::query_builder::SelectStatement>, + impl $crate::SelectableExpression<$crate::query_builder::SelectStatement<$crate::query_builder::FromClause>> for $column_name where + From: $crate::query_source::QuerySource, + $column_name: $crate::SelectableExpression + $crate::AppearsOnTable<$crate::query_builder::SelectStatement<$crate::query_builder::FromClause>>, { } @@ -708,13 +716,27 @@ macro_rules! __diesel_table_impl { pub type SqlType = ($($($column_ty)*,)+); /// Helper type for representing a boxed query from this table - pub type BoxedQuery<'a, DB, ST = SqlType> = $crate::query_builder::BoxedSelectStatement<'a, ST, table, DB>; + pub type BoxedQuery<'a, DB, ST = SqlType> = $crate::query_builder::BoxedSelectStatement<'a, ST, $crate::query_builder::FromClause
, DB>; + + impl $crate::QuerySource for table { + type FromClause = $crate::query_builder::nodes::StaticQueryFragmentInstance
; + type DefaultSelection = ::AllColumns; + + fn from_clause(&self) -> Self::FromClause { + $crate::query_builder::nodes::StaticQueryFragmentInstance::new() + } + + fn default_selection(&self) -> Self::DefaultSelection { + use $crate::Table; + Self::all_columns() + } + } - $crate::__diesel_table_query_source_impl!(table, $schema, $sql_name); + $crate::__diesel_table_generate_static_query_fragment_for_table!($schema, table, $sql_name); impl $crate::query_builder::AsQuery for table { type SqlType = SqlType; - type Query = $crate::query_builder::SelectStatement; + type Query = $crate::query_builder::SelectStatement<$crate::query_builder::FromClause>; fn as_query(self) -> Self::Query { $crate::query_builder::SelectStatement::simple(self) @@ -747,7 +769,8 @@ macro_rules! __diesel_table_impl { fn into_update_target(self) -> $crate::query_builder::UpdateTarget { use $crate::query_builder::AsQuery; - self.as_query().into_update_target() + let q: $crate::query_builder::SelectStatement<$crate::query_builder::FromClause
> = self.as_query(); + q.into_update_target() } } @@ -761,6 +784,8 @@ macro_rules! __diesel_table_impl { impl $crate::JoinTo<$crate::query_source::joins::Join> for table where $crate::query_source::joins::Join: $crate::JoinTo
, + Left: $crate::query_source::QuerySource, + Right: $crate::query_source::QuerySource, { type FromClause = $crate::query_source::joins::Join; type OnClause = <$crate::query_source::joins::Join as $crate::JoinTo
>::OnClause; @@ -783,24 +808,26 @@ macro_rules! __diesel_table_impl { } } - impl $crate::JoinTo<$crate::query_builder::SelectStatement> for table where - $crate::query_builder::SelectStatement: $crate::JoinTo
, + impl $crate::JoinTo<$crate::query_builder::SelectStatement<$crate::query_builder::FromClause, S, D, W, O, L, Of, G>> for table where + $crate::query_builder::SelectStatement<$crate::query_builder::FromClause, S, D, W, O, L, Of, G>: $crate::JoinTo
, + F: $crate::query_source::QuerySource { - type FromClause = $crate::query_builder::SelectStatement; - type OnClause = <$crate::query_builder::SelectStatement as $crate::JoinTo
>::OnClause; + type FromClause = $crate::query_builder::SelectStatement<$crate::query_builder::FromClause, S, D, W, O, L, Of, G>; + type OnClause = <$crate::query_builder::SelectStatement<$crate::query_builder::FromClause, S, D, W, O, L, Of, G> as $crate::JoinTo
>::OnClause; - fn join_target(rhs: $crate::query_builder::SelectStatement) -> (Self::FromClause, Self::OnClause) { + fn join_target(rhs: $crate::query_builder::SelectStatement<$crate::query_builder::FromClause, S, D, W, O, L, Of, G>) -> (Self::FromClause, Self::OnClause) { let (_, on_clause) = $crate::query_builder::SelectStatement::join_target(table); (rhs, on_clause) } } - impl<'a, QS, ST, DB> $crate::JoinTo<$crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>> for table where - $crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>: $crate::JoinTo
, + impl<'a, QS, ST, DB> $crate::JoinTo<$crate::query_builder::BoxedSelectStatement<'a, $crate::query_builder::FromClause, ST, DB>> for table where + $crate::query_builder::BoxedSelectStatement<'a, $crate::query_builder::FromClause, ST, DB>: $crate::JoinTo
, + QS: $crate::query_source::QuerySource, { - type FromClause = $crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>; - type OnClause = <$crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB> as $crate::JoinTo
>::OnClause; - fn join_target(rhs: $crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>) -> (Self::FromClause, Self::OnClause) { + type FromClause = $crate::query_builder::BoxedSelectStatement<'a, $crate::query_builder::FromClause, ST, DB>; + type OnClause = <$crate::query_builder::BoxedSelectStatement<'a, $crate::query_builder::FromClause, ST, DB> as $crate::JoinTo
>::OnClause; + fn join_target(rhs: $crate::query_builder::BoxedSelectStatement<'a, $crate::query_builder::FromClause, ST, DB>) -> (Self::FromClause, Self::OnClause) { let (_, on_clause) = $crate::query_builder::BoxedSelectStatement::join_target(table); (rhs, on_clause) } @@ -859,9 +886,13 @@ macro_rules! __diesel_table_impl {
::FromClause: $crate::query_builder::QueryFragment, { #[allow(non_snake_case)] - fn walk_ast(&self, mut __out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut __out: $crate::query_builder::AstPass<'_, 'b, DB>) -> $crate::result::QueryResult<()> + where 'a: 'b + { use $crate::QuerySource; - table.from_clause().walk_ast(__out.reborrow())?; + const FROM_CLAUSE: $crate::query_builder::nodes::StaticQueryFragmentInstance
= $crate::query_builder::nodes::StaticQueryFragmentInstance::new(); + + FROM_CLAUSE.walk_ast(__out.reborrow())?; __out.push_sql(".*"); Ok(()) } @@ -875,6 +906,8 @@ macro_rules! __diesel_table_impl { $($crate::__diesel_column! { table = table, + table_sql_name = $sql_name, + table_schema = $schema, name = $column_name, sql_name = $column_sql_name, ty = ($($column_ty)*), @@ -935,47 +968,33 @@ macro_rules! __diesel_valid_grouping_for_table_columns { #[macro_export] #[doc(hidden)] -macro_rules! __diesel_table_query_source_impl { - ($table_struct:ident, public, $table_name:expr) => { - impl $crate::QuerySource for $table_struct { - type FromClause = $crate::query_builder::nodes::Identifier<'static>; - type DefaultSelection = ::AllColumns; - - fn from_clause(&self) -> Self::FromClause { - $crate::query_builder::nodes::Identifier($table_name) - } - - fn default_selection(&self) -> Self::DefaultSelection { - use $crate::Table; - Self::all_columns() - } +macro_rules! __diesel_table_generate_static_query_fragment_for_table { + (public, $table: ident, $table_name:expr) => { + impl $crate::query_builder::nodes::StaticQueryFragment for table { + type Component = $crate::query_builder::nodes::Identifier<'static>; + const STATIC_COMPONENT: &'static Self::Component = &$crate::query_builder::nodes::Identifier($table_name); } + }; + ($schema_name:ident, $table: ident, $table_name:expr) => { + impl $crate::query_builder::nodes::StaticQueryFragment for table { + type Component = $crate::query_builder::nodes::InfixNode< + $crate::query_builder::nodes::Identifier<'static>, + $crate::query_builder::nodes::Identifier<'static>, + &'static str + >; + const STATIC_COMPONENT: &'static Self::Component = &$crate::query_builder::nodes::InfixNode::new( + $crate::query_builder::nodes::Identifier(stringify!($schema_name)), + $crate::query_builder::nodes::Identifier($table_name), + "." + ); + } + } +} + - ($table_struct:ident, $schema_name:ident, $table_name:expr) => { - impl $crate::QuerySource for $table_struct { - type FromClause = $crate::query_builder::nodes::InfixNode< - 'static, - $crate::query_builder::nodes::Identifier<'static>, - $crate::query_builder::nodes::Identifier<'static>, - >; - type DefaultSelection = ::AllColumns; - fn from_clause(&self) -> Self::FromClause { - $crate::query_builder::nodes::InfixNode::new( - $crate::query_builder::nodes::Identifier(stringify!($schema_name)), - $crate::query_builder::nodes::Identifier($table_name), - ".", - ) - } - fn default_selection(&self) -> Self::DefaultSelection { - use $crate::Table; - Self::all_columns() - } - } - }; -} /// Allow two tables to be referenced in a join query without providing an /// explicit `ON` clause. diff --git a/diesel/src/migration/mod.rs b/diesel/src/migration/mod.rs index a6ca916ca5f2..1310f53eee79 100644 --- a/diesel/src/migration/mod.rs +++ b/diesel/src/migration/mod.rs @@ -48,10 +48,13 @@ where Cow<'a, str>: ToSql, DB: Backend, { - fn to_sql( - &self, - out: &mut crate::serialize::Output, - ) -> crate::serialize::Result { + fn to_sql<'b, 'c, 'd>( + &'b self, + out: &mut crate::serialize::Output<'c, 'd, DB>, + ) -> crate::serialize::Result + where + 'b: 'c, + { self.0.to_sql(out) } } diff --git a/diesel/src/mysql/backend.rs b/diesel/src/mysql/backend.rs index 7ddca27c59ef..8fea8493cb96 100644 --- a/diesel/src/mysql/backend.rs +++ b/diesel/src/mysql/backend.rs @@ -1,7 +1,5 @@ //! The MySQL backend -use byteorder::NativeEndian; - use super::query_builder::MysqlQueryBuilder; use super::MysqlValue; use crate::backend::*; @@ -64,8 +62,10 @@ pub enum MysqlType { impl Backend for Mysql { type QueryBuilder = MysqlQueryBuilder; +} + +impl<'a> HasBindCollector<'a> for Mysql { type BindCollector = RawBytesBindCollector; - type ByteOrder = NativeEndian; } impl<'a> HasRawValue<'a> for Mysql { diff --git a/diesel/src/mysql/query_builder/limit_offset.rs b/diesel/src/mysql/query_builder/limit_offset.rs index 2ddb0ae3f38b..88486fd519a7 100644 --- a/diesel/src/mysql/query_builder/limit_offset.rs +++ b/diesel/src/mysql/query_builder/limit_offset.rs @@ -6,7 +6,7 @@ use crate::query_builder::{AstPass, IntoBoxedClause, QueryFragment}; use crate::result::QueryResult; impl QueryFragment for LimitOffsetClause { - fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, _out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { Ok(()) } } @@ -15,7 +15,7 @@ impl QueryFragment for LimitOffsetClause, NoOffsetClaus where LimitClause: QueryFragment, { - fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { self.limit_clause.walk_ast(out)?; Ok(()) } @@ -26,7 +26,7 @@ where LimitClause: QueryFragment, OffsetClause: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { self.limit_clause.walk_ast(out.reborrow())?; self.offset_clause.walk_ast(out.reborrow())?; Ok(()) @@ -34,7 +34,7 @@ where } impl<'a> QueryFragment for BoxedLimitOffsetClause<'a, Mysql> { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'b: 'c, 'c>(&'b self, mut out: AstPass<'_, 'c, Mysql>) -> QueryResult<()> { match (self.limit.as_ref(), self.offset.as_ref()) { (Some(limit), Some(offset)) => { limit.walk_ast(out.reborrow())?; diff --git a/diesel/src/mysql/query_builder/query_fragment_impls.rs b/diesel/src/mysql/query_builder/query_fragment_impls.rs index 89036ec5d292..dd1c72009235 100644 --- a/diesel/src/mysql/query_builder/query_fragment_impls.rs +++ b/diesel/src/mysql/query_builder/query_fragment_impls.rs @@ -4,41 +4,41 @@ use crate::query_builder::{AstPass, DefaultValues, QueryFragment}; use crate::result::QueryResult; impl QueryFragment for ForUpdate { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { out.push_sql(" FOR UPDATE"); Ok(()) } } impl QueryFragment for ForShare { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { out.push_sql(" FOR SHARE"); Ok(()) } } impl QueryFragment for NoModifier { - fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, _out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { Ok(()) } } impl QueryFragment for SkipLocked { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { out.push_sql(" SKIP LOCKED"); Ok(()) } } impl QueryFragment for NoWait { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { out.push_sql(" NOWAIT"); Ok(()) } } impl QueryFragment for DefaultValues { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { out.push_sql("() VALUES ()"); Ok(()) } diff --git a/diesel/src/mysql/types/date_and_time.rs b/diesel/src/mysql/types/date_and_time.rs index 79cfd80a9366..be8d71ab54b6 100644 --- a/diesel/src/mysql/types/date_and_time.rs +++ b/diesel/src/mysql/types/date_and_time.rs @@ -106,7 +106,7 @@ impl MysqlTimestampType { macro_rules! mysql_time_impls { ($ty:ty) => { impl ToSql<$ty, Mysql> for MysqlTime { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { let bytes = unsafe { let bytes_ptr = self as *const MysqlTime as *const u8; slice::from_raw_parts(bytes_ptr, mem::size_of::()) @@ -131,7 +131,7 @@ mysql_time_impls!(Date); #[cfg(feature = "chrono")] impl ToSql for NaiveDateTime { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { >::to_sql(self, out) } } @@ -145,7 +145,7 @@ impl FromSql for NaiveDateTime { #[cfg(feature = "chrono")] impl ToSql for NaiveDateTime { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { let mysql_time = MysqlTime { year: self.year() as libc::c_uint, month: self.month() as libc::c_uint, @@ -159,7 +159,7 @@ impl ToSql for NaiveDateTime { time_zone_displacement: 0, }; - >::to_sql(&mysql_time, out) + >::to_sql(&mysql_time, &mut out.reborrow()) } } @@ -187,7 +187,10 @@ impl FromSql for NaiveDateTime { #[cfg(feature = "chrono")] impl ToSql for NaiveTime { - fn to_sql(&self, out: &mut serialize::Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>( + &'a self, + out: &mut serialize::Output<'b, '_, Mysql>, + ) -> serialize::Result { let mysql_time = MysqlTime { hour: self.hour() as libc::c_uint, minute: self.minute() as libc::c_uint, @@ -201,7 +204,7 @@ impl ToSql for NaiveTime { time_zone_displacement: 0, }; - >::to_sql(&mysql_time, out) + >::to_sql(&mysql_time, &mut out.reborrow()) } } @@ -220,7 +223,7 @@ impl FromSql for NaiveTime { #[cfg(feature = "chrono")] impl ToSql for NaiveDate { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { let mysql_time = MysqlTime { year: self.year() as libc::c_uint, month: self.month() as libc::c_uint, @@ -234,7 +237,7 @@ impl ToSql for NaiveDate { time_zone_displacement: 0, }; - >::to_sql(&mysql_time, out) + >::to_sql(&mysql_time, &mut out.reborrow()) } } diff --git a/diesel/src/mysql/types/json.rs b/diesel/src/mysql/types/json.rs index 8d67636995c2..cc3b877eeb8e 100644 --- a/diesel/src/mysql/types/json.rs +++ b/diesel/src/mysql/types/json.rs @@ -2,7 +2,6 @@ use crate::deserialize::{self, FromSql}; use crate::mysql::{Mysql, MysqlValue}; use crate::serialize::{self, IsNull, Output, ToSql}; use crate::sql_types; -use std::io::prelude::*; impl FromSql for serde_json::Value { fn from_sql(value: MysqlValue<'_>) -> deserialize::Result { @@ -11,7 +10,7 @@ impl FromSql for serde_json::Value { } impl ToSql for serde_json::Value { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { serde_json::to_writer(out, self) .map(|_| IsNull::No) .map_err(Into::into) @@ -20,10 +19,11 @@ impl ToSql for serde_json::Value { #[test] fn json_to_sql() { - let mut bytes = Output::test(); + let mut buffer = Vec::new(); + let mut bytes = Output::test(&mut buffer); let test_json = serde_json::Value::Bool(true); ToSql::::to_sql(&test_json, &mut bytes).unwrap(); - assert_eq!(bytes, b"true"); + assert_eq!(buffer, b"true"); } #[test] diff --git a/diesel/src/mysql/types/mod.rs b/diesel/src/mysql/types/mod.rs index cb22e58aa139..6c301ae9d216 100644 --- a/diesel/src/mysql/types/mod.rs +++ b/diesel/src/mysql/types/mod.rs @@ -6,20 +6,18 @@ mod json; mod numeric; mod primitives; -use byteorder::WriteBytesExt; -use std::io::Write; - use crate::deserialize::{self, FromSql}; use crate::mysql::{Mysql, MysqlType, MysqlValue}; use crate::query_builder::QueryId; use crate::serialize::{self, IsNull, Output, ToSql}; -use crate::sql_types::ops::*; use crate::sql_types::*; +use crate::sql_types::{self, ops::*}; +use byteorder::{NativeEndian, WriteBytesExt}; pub use date_and_time::{MysqlTime, MysqlTimestampType}; impl ToSql for i8 { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { out.write_i8(*self).map(|_| IsNull::No).map_err(Into::into) } } @@ -68,8 +66,8 @@ where } impl ToSql, Mysql> for u8 { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - ToSql::::to_sql(&(*self as i8), out) + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + ToSql::::to_sql(&(*self as i8), &mut out.reborrow()) } } @@ -81,8 +79,8 @@ impl FromSql, Mysql> for u8 { } impl ToSql, Mysql> for u16 { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - ToSql::::to_sql(&(*self as i16), out) + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + ToSql::::to_sql(&(*self as i16), &mut out.reborrow()) } } @@ -94,8 +92,8 @@ impl FromSql, Mysql> for u16 { } impl ToSql, Mysql> for u32 { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - ToSql::::to_sql(&(*self as i32), out) + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + ToSql::::to_sql(&(*self as i32), &mut out.reborrow()) } } @@ -107,8 +105,8 @@ impl FromSql, Mysql> for u32 { } impl ToSql, Mysql> for u64 { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - ToSql::::to_sql(&(*self as i64), out) + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + ToSql::::to_sql(&(*self as i64), &mut out.reborrow()) } } @@ -120,9 +118,9 @@ impl FromSql, Mysql> for u64 { } impl ToSql for bool { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { let int_value = if *self { 1 } else { 0 }; - >::to_sql(&int_value, out) + >::to_sql(&int_value, &mut out.reborrow()) } } @@ -132,6 +130,46 @@ impl FromSql for bool { } } +impl ToSql for i16 { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + out.write_i16::(*self) + .map(|_| IsNull::No) + .map_err(|e| Box::new(e) as Box<_>) + } +} + +impl ToSql for i32 { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + out.write_i32::(*self) + .map(|_| IsNull::No) + .map_err(|e| Box::new(e) as Box<_>) + } +} + +impl ToSql for i64 { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + out.write_i64::(*self) + .map(|_| IsNull::No) + .map_err(|e| Box::new(e) as Box<_>) + } +} + +impl ToSql for f64 { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + out.write_f64::(*self) + .map(|_| IsNull::No) + .map_err(|e| Box::new(e) as Box<_>) + } +} + +impl ToSql for f32 { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { + out.write_f32::(*self) + .map(|_| IsNull::No) + .map_err(|e| Box::new(e) as Box<_>) + } +} + impl HasSqlType> for Mysql { fn metadata(_lookup: &mut ()) -> MysqlType { MysqlType::UnsignedTiny diff --git a/diesel/src/mysql/types/numeric.rs b/diesel/src/mysql/types/numeric.rs index bfdd7e16e4e7..36e74dbd2404 100644 --- a/diesel/src/mysql/types/numeric.rs +++ b/diesel/src/mysql/types/numeric.rs @@ -11,7 +11,7 @@ pub mod bigdecimal { use crate::sql_types::Numeric; impl ToSql for BigDecimal { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Mysql>) -> serialize::Result { write!(out, "{}", *self) .map(|_| IsNull::No) .map_err(Into::into) diff --git a/diesel/src/pg/backend.rs b/diesel/src/pg/backend.rs index c9e9b14bfb3e..1cb038cd04dc 100644 --- a/diesel/src/pg/backend.rs +++ b/diesel/src/pg/backend.rs @@ -1,7 +1,5 @@ //! The PostgreSQL backend -use byteorder::NetworkEndian; - use super::query_builder::PgQueryBuilder; use super::{PgMetadataLookup, PgValue}; use crate::backend::*; @@ -105,8 +103,10 @@ impl PgTypeMetadata { impl Backend for Pg { type QueryBuilder = PgQueryBuilder; +} + +impl<'a> HasBindCollector<'a> for Pg { type BindCollector = RawBytesBindCollector; - type ByteOrder = NetworkEndian; } impl<'a> HasRawValue<'a> for Pg { diff --git a/diesel/src/pg/expression/array.rs b/diesel/src/pg/expression/array.rs index c135d9f01825..b0066e86c242 100644 --- a/diesel/src/pg/expression/array.rs +++ b/diesel/src/pg/expression/array.rs @@ -67,11 +67,14 @@ where impl QueryFragment for ArrayLiteral where DB: Backend, - for<'a> &'a T: QueryFragment, + T: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> crate::result::QueryResult<()> { + fn walk_ast<'a: 'b, 'b>( + &'a self, + mut out: AstPass<'_, 'b, DB>, + ) -> crate::result::QueryResult<()> { out.push_sql("ARRAY["); - QueryFragment::walk_ast(&&self.elements, out.reborrow())?; + QueryFragment::walk_ast(&self.elements, out.reborrow())?; out.push_sql("]"); Ok(()) } diff --git a/diesel/src/pg/expression/array_comparison.rs b/diesel/src/pg/expression/array_comparison.rs index 30cce1cdca50..2733cf0948d3 100644 --- a/diesel/src/pg/expression/array_comparison.rs +++ b/diesel/src/pg/expression/array_comparison.rs @@ -86,7 +86,7 @@ impl QueryFragment for Any where Expr: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql("ANY("); self.expr.walk_ast(out.reborrow())?; out.push_sql(")"); @@ -120,7 +120,7 @@ impl QueryFragment for All where Expr: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql("ALL("); self.expr.walk_ast(out.reborrow())?; out.push_sql(")"); diff --git a/diesel/src/pg/expression/date_and_time.rs b/diesel/src/pg/expression/date_and_time.rs index 94ef7362ae46..532a7ebdcc2f 100644 --- a/diesel/src/pg/expression/date_and_time.rs +++ b/diesel/src/pg/expression/date_and_time.rs @@ -39,7 +39,7 @@ where Ts: QueryFragment, Tz: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { self.timestamp.walk_ast(out.reborrow())?; out.push_sql(" AT TIME ZONE "); self.timezone.walk_ast(out.reborrow())?; diff --git a/diesel/src/pg/query_builder/distinct_on.rs b/diesel/src/pg/query_builder/distinct_on.rs index d92bb063ae34..df9b45fd1240 100644 --- a/diesel/src/pg/query_builder/distinct_on.rs +++ b/diesel/src/pg/query_builder/distinct_on.rs @@ -1,12 +1,14 @@ use crate::expression::SelectableExpression; use crate::pg::Pg; use crate::query_builder::order_clause::{NoOrderClause, OrderClause}; -use crate::query_builder::{AstPass, QueryFragment, QueryId, SelectQuery, SelectStatement}; +use crate::query_builder::{ + AstPass, FromClause, QueryFragment, QueryId, SelectQuery, SelectStatement, +}; use crate::query_dsl::methods::DistinctOnDsl; use crate::query_dsl::order_dsl::ValidOrderingForDistinct; use crate::result::QueryResult; use crate::sql_types::SingleValue; -use crate::Expression; +use crate::{Expression, QuerySource}; /// Represents `DISTINCT ON (...)` #[derive(Debug, Clone, Copy, QueryId)] @@ -25,7 +27,7 @@ impl QueryFragment for DistinctOnClause where T: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql("DISTINCT ON ("); self.0.walk_ast(out.reborrow())?; out.push_sql(")"); @@ -34,15 +36,17 @@ where } impl DistinctOnDsl - for SelectStatement + for SelectStatement, S, D, W, O, LOf, G, H> where + F: QuerySource, Selection: SelectableExpression, Selection::SqlType: SingleValue, Self: SelectQuery, O: ValidOrderingForDistinct>, - SelectStatement, W, O, LOf, G, H>: SelectQuery, + SelectStatement, S, DistinctOnClause, W, O, LOf, G, H>: + SelectQuery, { - type Output = SelectStatement, W, O, LOf, G, H>; + type Output = SelectStatement, S, DistinctOnClause, W, O, LOf, G, H>; fn distinct_on(self, selection: Selection) -> Self::Output { SelectStatement::new( diff --git a/diesel/src/pg/query_builder/limit_offset.rs b/diesel/src/pg/query_builder/limit_offset.rs index ad4c61ece819..5b1a21e2f2f8 100644 --- a/diesel/src/pg/query_builder/limit_offset.rs +++ b/diesel/src/pg/query_builder/limit_offset.rs @@ -19,7 +19,7 @@ where } impl<'a> QueryFragment for BoxedLimitOffsetClause<'a, Pg> { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'b: 'c, 'c>(&'b self, mut out: AstPass<'_, 'c, Pg>) -> QueryResult<()> { if let Some(ref limit) = self.limit { limit.walk_ast(out.reborrow())?; } @@ -35,7 +35,7 @@ where L: QueryFragment, O: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { self.limit_clause.walk_ast(out.reborrow())?; self.offset_clause.walk_ast(out.reborrow())?; Ok(()) diff --git a/diesel/src/pg/query_builder/on_constraint.rs b/diesel/src/pg/query_builder/on_constraint.rs index deef01e3e108..2dc465f6ca51 100644 --- a/diesel/src/pg/query_builder/on_constraint.rs +++ b/diesel/src/pg/query_builder/on_constraint.rs @@ -59,7 +59,7 @@ impl<'a> QueryId for OnConstraint<'a> { } impl<'a> QueryFragment for ConflictTarget> { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'b: 'c, 'c>(&'b self, mut out: AstPass<'_, 'c, Pg>) -> QueryResult<()> { out.unsafe_to_cache_prepared(); out.push_sql(" ON CONSTRAINT "); out.push_identifier(self.0.constraint_name)?; diff --git a/diesel/src/pg/query_builder/query_fragment_impls.rs b/diesel/src/pg/query_builder/query_fragment_impls.rs index 38c6ae5196af..579f1cef2a00 100644 --- a/diesel/src/pg/query_builder/query_fragment_impls.rs +++ b/diesel/src/pg/query_builder/query_fragment_impls.rs @@ -1,5 +1,4 @@ use crate::expression::array_comparison::{In, Many, MaybeEmpty, NotIn}; -use crate::expression::AsExpression; use crate::pg::backend::PgStyleArrayComparision; use crate::pg::types::sql_types::Array; use crate::pg::Pg; @@ -13,48 +12,48 @@ use crate::serialize::ToSql; use crate::sql_types::{HasSqlType, SingleValue}; impl QueryFragment for ForUpdate { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql(" FOR UPDATE"); Ok(()) } } impl QueryFragment for ForNoKeyUpdate { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql(" FOR NO KEY UPDATE"); Ok(()) } } impl QueryFragment for ForShare { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql(" FOR SHARE"); Ok(()) } } impl QueryFragment for ForKeyShare { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql(" FOR KEY SHARE"); Ok(()) } } impl QueryFragment for NoModifier { - fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, _out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { Ok(()) } } impl QueryFragment for SkipLocked { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql(" SKIP LOCKED"); Ok(()) } } impl QueryFragment for NoWait { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql(" NOWAIT"); Ok(()) } @@ -65,7 +64,7 @@ where T: QueryFragment, U: QueryFragment + MaybeEmpty, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { self.left.walk_ast(out.reborrow())?; out.push_sql(" = ANY("); self.values.walk_ast(out.reborrow())?; @@ -79,7 +78,7 @@ where T: QueryFragment, U: QueryFragment + MaybeEmpty, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { self.left.walk_ast(out.reborrow())?; out.push_sql(" != ALL("); self.values.walk_ast(out.reborrow())?; @@ -91,12 +90,11 @@ where impl QueryFragment for Many where ST: SingleValue, - for<'a> &'a [I]: ToSql, Pg>, + Vec: ToSql, Pg>, Pg: HasSqlType, { - fn walk_ast(&self, out: AstPass) -> QueryResult<()> { - let values = &self.0 as &[I]; - <_ as AsExpression>>::as_expression(values).walk_ast(out) + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { + out.push_bind_param::, Vec>(&self.0) } } @@ -106,7 +104,7 @@ where T: QueryFragment, U: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { self.target.walk_ast(out.reborrow())?; self.where_clause.walk_ast(out.reborrow())?; Ok(()) diff --git a/diesel/src/pg/serialize/write_tuple.rs b/diesel/src/pg/serialize/write_tuple.rs index 5c2f55cdcbcc..3f98078a5bf9 100644 --- a/diesel/src/pg/serialize/write_tuple.rs +++ b/diesel/src/pg/serialize/write_tuple.rs @@ -1,5 +1,3 @@ -use std::io::Write; - use crate::pg::Pg; use crate::serialize::{self, Output}; @@ -21,7 +19,6 @@ use crate::serialize::{self, Output}; /// # use diesel::pg::Pg; /// # use diesel::serialize::{self, ToSql, Output, WriteTuple}; /// # use diesel::sql_types::{Integer, Text, SqlType}; -/// # use std::io::Write; /// # /// #[derive(SqlType)] /// #[postgres(type_name = "my_type")] @@ -31,10 +28,10 @@ use crate::serialize::{self, Output}; /// struct MyStruct<'a>(i32, &'a str); /// /// impl<'a> ToSql for MyStruct<'a> { -/// fn to_sql(&self, out: &mut Output) -> serialize::Result { +/// fn to_sql<'b: 'c, 'c>(&'b self, out: &mut Output<'c, '_, Pg>) -> serialize::Result { /// WriteTuple::<(Integer, Text)>::write_tuple( /// &(self.0, self.1), -/// out, +/// &mut out.reborrow(), /// ) /// } /// } @@ -43,5 +40,5 @@ use crate::serialize::{self, Output}; /// ``` pub trait WriteTuple { /// See trait documentation. - fn write_tuple(&self, out: &mut Output) -> serialize::Result; + fn write_tuple(&self, out: &mut Output) -> serialize::Result; } diff --git a/diesel/src/pg/transaction.rs b/diesel/src/pg/transaction.rs index 2a6b554497b6..e9058ba025fc 100644 --- a/diesel/src/pg/transaction.rs +++ b/diesel/src/pg/transaction.rs @@ -293,7 +293,7 @@ where } impl<'a, C> QueryFragment for TransactionBuilder<'a, C> { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'b: 'c, 'c>(&'b self, mut out: AstPass<'_, 'c, Pg>) -> QueryResult<()> { out.push_sql("BEGIN TRANSACTION"); if let Some(ref isolation_level) = self.isolation_level { isolation_level.walk_ast(out.reborrow())?; @@ -316,7 +316,7 @@ enum IsolationLevel { } impl QueryFragment for IsolationLevel { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql(" ISOLATION LEVEL "); match *self { IsolationLevel::ReadCommitted => out.push_sql("READ COMMITTED"), @@ -334,7 +334,7 @@ enum ReadMode { } impl QueryFragment for ReadMode { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { match *self { ReadMode::ReadOnly => out.push_sql(" READ ONLY"), ReadMode::ReadWrite => out.push_sql(" READ WRITE"), @@ -350,7 +350,7 @@ enum Deferrable { } impl QueryFragment for Deferrable { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { match *self { Deferrable::Deferrable => out.push_sql(" DEFERRABLE"), Deferrable::NotDeferrable => out.push_sql(" NOT DEFERRABLE"), diff --git a/diesel/src/pg/types/array.rs b/diesel/src/pg/types/array.rs index b6315eb60a1e..6ea6f50f3646 100644 --- a/diesel/src/pg/types/array.rs +++ b/diesel/src/pg/types/array.rs @@ -86,7 +86,7 @@ where Pg: HasSqlType, T: ToSql, { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { let num_dimensions = 1; out.write_i32::(num_dimensions)?; let flags = 0; @@ -103,9 +103,8 @@ where for elem in self.iter() { let is_null = { - let mut temp_buffer = Output::new(buffer, out.metadata_lookup()); + let mut temp_buffer = Output::new(&mut buffer, out.metadata_lookup()); let is_null = elem.to_sql(&mut temp_buffer)?; - buffer = temp_buffer.into_inner(); is_null }; @@ -128,7 +127,7 @@ where [T]: ToSql, Pg>, ST: 'static, { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { ToSql::, Pg>::to_sql(self, out) } } @@ -139,7 +138,7 @@ where [T]: ToSql, Pg>, T: fmt::Debug, { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { (self as &[T]).to_sql(out) } } @@ -149,7 +148,7 @@ where ST: 'static, Vec: ToSql, Pg>, { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { ToSql::, Pg>::to_sql(self, out) } } diff --git a/diesel/src/pg/types/date_and_time/chrono.rs b/diesel/src/pg/types/date_and_time/chrono.rs index 0846edc40264..2e1458e85a98 100644 --- a/diesel/src/pg/types/date_and_time/chrono.rs +++ b/diesel/src/pg/types/date_and_time/chrono.rs @@ -5,7 +5,6 @@ extern crate chrono; use self::chrono::naive::MAX_DATE; use self::chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; -use std::io::Write; use super::{PgDate, PgTime, PgTimestamp}; use crate::deserialize::{self, FromSql}; @@ -32,7 +31,7 @@ impl FromSql for NaiveDateTime { } impl ToSql for NaiveDateTime { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { let time = match (self.signed_duration_since(pg_epoch())).num_microseconds() { Some(time) => time, None => { @@ -41,7 +40,7 @@ impl ToSql for NaiveDateTime { return Err(error_message.into()); } }; - ToSql::::to_sql(&PgTimestamp(time), out) + ToSql::::to_sql(&PgTimestamp(time), &mut out.reborrow()) } } @@ -52,7 +51,7 @@ impl FromSql for NaiveDateTime { } impl ToSql for NaiveDateTime { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { ToSql::::to_sql(self, out) } } @@ -72,8 +71,8 @@ impl FromSql for DateTime { } impl ToSql for DateTime { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - ToSql::::to_sql(&self.naive_utc(), out) + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + ToSql::::to_sql(&self.naive_utc(), &mut out.reborrow()) } } @@ -82,10 +81,10 @@ fn midnight() -> NaiveTime { } impl ToSql for NaiveTime { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { let duration = self.signed_duration_since(midnight()); match duration.num_microseconds() { - Some(offset) => ToSql::::to_sql(&PgTime(offset), out), + Some(offset) => ToSql::::to_sql(&PgTime(offset), &mut out.reborrow()), None => unreachable!(), } } @@ -104,9 +103,9 @@ fn pg_epoch_date() -> NaiveDate { } impl ToSql for NaiveDate { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { let days_since_epoch = self.signed_duration_since(pg_epoch_date()).num_days(); - ToSql::::to_sql(&PgDate(days_since_epoch as i32), out) + ToSql::::to_sql(&PgDate(days_since_epoch as i32), &mut out.reborrow()) } } diff --git a/diesel/src/pg/types/date_and_time/mod.rs b/diesel/src/pg/types/date_and_time/mod.rs index 578055234bae..cbe00efcabc6 100644 --- a/diesel/src/pg/types/date_and_time/mod.rs +++ b/diesel/src/pg/types/date_and_time/mod.rs @@ -1,4 +1,3 @@ -use std::io::Write; use std::ops::Add; use crate::deserialize::{self, FromSql, FromSqlRow}; @@ -82,7 +81,7 @@ impl PgInterval { } impl ToSql for PgTimestamp { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { ToSql::::to_sql(&self.0, out) } } @@ -94,7 +93,7 @@ impl FromSql for PgTimestamp { } impl ToSql for PgTimestamp { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { ToSql::::to_sql(self, out) } } @@ -106,7 +105,7 @@ impl FromSql for PgTimestamp { } impl ToSql for PgDate { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { ToSql::::to_sql(&self.0, out) } } @@ -118,7 +117,7 @@ impl FromSql for PgDate { } impl ToSql for PgTime { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { ToSql::::to_sql(&self.0, out) } } @@ -130,7 +129,7 @@ impl FromSql for PgTime { } impl ToSql for PgInterval { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { ToSql::::to_sql(&self.microseconds, out)?; ToSql::::to_sql(&self.days, out)?; ToSql::::to_sql(&self.months, out)?; diff --git a/diesel/src/pg/types/date_and_time/std_time.rs b/diesel/src/pg/types/date_and_time/std_time.rs index 793968e8c469..b29ff5995a28 100644 --- a/diesel/src/pg/types/date_and_time/std_time.rs +++ b/diesel/src/pg/types/date_and_time/std_time.rs @@ -1,4 +1,3 @@ -use std::io::Write; use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::deserialize::{self, FromSql}; @@ -12,7 +11,7 @@ fn pg_epoch() -> SystemTime { } impl ToSql for SystemTime { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { let (before_epoch, duration) = match self.duration_since(pg_epoch()) { Ok(duration) => (false, duration), Err(time_err) => (true, time_err.duration()), @@ -22,7 +21,7 @@ impl ToSql for SystemTime { } else { duration_to_usecs(duration) as i64 }; - ToSql::::to_sql(&time_since_epoch, out) + ToSql::::to_sql(&time_since_epoch, &mut out.reborrow()) } } diff --git a/diesel/src/pg/types/floats/mod.rs b/diesel/src/pg/types/floats/mod.rs index aa1387946ce3..d12c9ed3ea1f 100644 --- a/diesel/src/pg/types/floats/mod.rs +++ b/diesel/src/pg/types/floats/mod.rs @@ -1,12 +1,10 @@ -use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; -use std::error::Error; -use std::io::prelude::*; - use crate::deserialize::{self, FromSql, FromSqlRow}; use crate::expression::AsExpression; use crate::pg::{Pg, PgValue}; use crate::serialize::{self, IsNull, Output, ToSql}; use crate::sql_types; +use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; +use std::error::Error; #[cfg(feature = "quickcheck")] mod quickcheck_impls; @@ -79,7 +77,7 @@ impl FromSql for PgNumeric { } impl ToSql for PgNumeric { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { let sign = match *self { PgNumeric::Positive { .. } => 0, PgNumeric::Negative { .. } => 0x4000, @@ -111,3 +109,47 @@ impl ToSql for PgNumeric { Ok(IsNull::No) } } + +impl FromSql for f32 { + fn from_sql(value: PgValue<'_>) -> deserialize::Result { + let mut bytes = value.as_bytes(); + debug_assert!( + bytes.len() <= 4, + "Received more than 4 bytes while decoding \ + an f32. Was a double accidentally marked as float?" + ); + bytes + .read_f32::() + .map_err(|e| Box::new(e) as Box) + } +} + +impl FromSql for f64 { + fn from_sql(value: PgValue<'_>) -> deserialize::Result { + let mut bytes = value.as_bytes(); + debug_assert!( + bytes.len() <= 8, + "Received more than 8 bytes while decoding \ + an f64. Was a numeric accidentally marked as double?" + ); + bytes + .read_f64::() + .map_err(|e| Box::new(e) as Box) + } +} + +impl ToSql for f32 { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + out.write_f32::(*self) + .map(|_| IsNull::No) + .map_err(|e| Box::new(e) as Box) + } +} + +impl ToSql for f64 { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + out.write_f64::(*self) + .map(|_| IsNull::No) + .map_err(|e| Box::new(e) as Box) + } +} diff --git a/diesel/src/pg/types/integers.rs b/diesel/src/pg/types/integers.rs index bc59ae8f51f4..71769ae5d804 100644 --- a/diesel/src/pg/types/integers.rs +++ b/diesel/src/pg/types/integers.rs @@ -1,10 +1,8 @@ -use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; -use std::io::prelude::*; - use crate::deserialize::{self, FromSql}; use crate::pg::{Pg, PgValue}; use crate::serialize::{self, IsNull, Output, ToSql}; use crate::sql_types; +use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; impl FromSql for u32 { fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { @@ -14,39 +12,124 @@ impl FromSql for u32 { } impl ToSql for u32 { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { out.write_u32::(*self) .map(|_| IsNull::No) .map_err(Into::into) } } +impl FromSql for i16 { + fn from_sql(value: PgValue<'_>) -> deserialize::Result { + let mut bytes = value.as_bytes(); + debug_assert!( + bytes.len() <= 2, + "Received more than 2 bytes decoding i16. \ + Was an Integer expression accidentally identified as SmallInt?" + ); + debug_assert!( + bytes.len() >= 2, + "Received fewer than 2 bytes decoding i16. \ + Was an expression of a different type accidentally identified \ + as SmallInt?" + ); + bytes + .read_i16::() + .map_err(|e| Box::new(e) as Box<_>) + } +} + +impl FromSql for i32 { + fn from_sql(value: PgValue<'_>) -> deserialize::Result { + let mut bytes = value.as_bytes(); + debug_assert!( + bytes.len() <= 4, + "Received more than 4 bytes decoding i32. \ + Was a BigInt expression accidentally identified as Integer?" + ); + debug_assert!( + bytes.len() >= 4, + "Received fewer than 4 bytes decoding i32. \ + Was a SmallInt expression accidentally identified as Integer?" + ); + bytes + .read_i32::() + .map_err(|e| Box::new(e) as Box<_>) + } +} + +impl FromSql for i64 { + fn from_sql(value: PgValue<'_>) -> deserialize::Result { + let mut bytes = value.as_bytes(); + debug_assert!( + bytes.len() <= 8, + "Received more than 8 bytes decoding i64. \ + Was an expression of a different type misidentified as BigInt?" + ); + debug_assert!( + bytes.len() >= 8, + "Received fewer than 8 bytes decoding i64. \ + Was an Integer expression misidentified as BigInt?" + ); + bytes + .read_i64::() + .map_err(|e| Box::new(e) as Box<_>) + } +} + +impl ToSql for i16 { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + out.write_i16::(*self) + .map(|_| IsNull::No) + .map_err(|e| Box::new(e) as Box<_>) + } +} + +impl ToSql for i32 { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + out.write_i32::(*self) + .map(|_| IsNull::No) + .map_err(|e| Box::new(e) as Box<_>) + } +} + +impl ToSql for i64 { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { + out.write_i64::(*self) + .map(|_| IsNull::No) + .map_err(|e| Box::new(e) as Box<_>) + } +} + #[test] fn i16_to_sql() { - let mut bytes = Output::test(); + let mut buffer = Vec::new(); + let mut bytes = Output::test(&mut buffer); ToSql::::to_sql(&1i16, &mut bytes).unwrap(); ToSql::::to_sql(&0i16, &mut bytes).unwrap(); ToSql::::to_sql(&-1i16, &mut bytes).unwrap(); - assert_eq!(bytes, vec![0, 1, 0, 0, 255, 255]); + assert_eq!(buffer, vec![0, 1, 0, 0, 255, 255]); } #[test] fn i32_to_sql() { - let mut bytes = Output::test(); + let mut buffer = Vec::new(); + let mut bytes = Output::test(&mut buffer); ToSql::::to_sql(&1i32, &mut bytes).unwrap(); ToSql::::to_sql(&0i32, &mut bytes).unwrap(); ToSql::::to_sql(&-1i32, &mut bytes).unwrap(); - assert_eq!(bytes, vec![0, 0, 0, 1, 0, 0, 0, 0, 255, 255, 255, 255]); + assert_eq!(buffer, vec![0, 0, 0, 1, 0, 0, 0, 0, 255, 255, 255, 255]); } #[test] fn i64_to_sql() { - let mut bytes = Output::test(); + let mut buffer = Vec::new(); + let mut bytes = Output::test(&mut buffer); ToSql::::to_sql(&1i64, &mut bytes).unwrap(); ToSql::::to_sql(&0i64, &mut bytes).unwrap(); ToSql::::to_sql(&-1i64, &mut bytes).unwrap(); assert_eq!( - bytes, + buffer, vec![ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, ] diff --git a/diesel/src/pg/types/json.rs b/diesel/src/pg/types/json.rs index e483501cb295..71fcffd4346c 100644 --- a/diesel/src/pg/types/json.rs +++ b/diesel/src/pg/types/json.rs @@ -16,7 +16,7 @@ impl FromSql for serde_json::Value { } impl ToSql for serde_json::Value { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { serde_json::to_writer(out, self) .map(|_| IsNull::No) .map_err(Into::into) @@ -34,7 +34,7 @@ impl FromSql for serde_json::Value { } impl ToSql for serde_json::Value { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { out.write_all(&[1])?; serde_json::to_writer(out, self) .map(|_| IsNull::No) @@ -44,10 +44,11 @@ impl ToSql for serde_json::Value { #[test] fn json_to_sql() { - let mut bytes = Output::test(); + let mut buffer = Vec::new(); + let mut bytes = Output::test(&mut buffer); let test_json = serde_json::Value::Bool(true); ToSql::::to_sql(&test_json, &mut bytes).unwrap(); - assert_eq!(bytes, b"true"); + assert_eq!(buffer, b"true"); } #[test] @@ -77,10 +78,11 @@ fn no_json_from_sql() { #[test] fn jsonb_to_sql() { - let mut bytes = Output::test(); + let mut buffer = Vec::new(); + let mut bytes = Output::test(&mut buffer); let test_json = serde_json::Value::Bool(true); ToSql::::to_sql(&test_json, &mut bytes).unwrap(); - assert_eq!(bytes, b"\x01true"); + assert_eq!(buffer, b"\x01true"); } #[test] diff --git a/diesel/src/pg/types/mac_addr.rs b/diesel/src/pg/types/mac_addr.rs index d7230f99878b..31b82b946b40 100644 --- a/diesel/src/pg/types/mac_addr.rs +++ b/diesel/src/pg/types/mac_addr.rs @@ -28,7 +28,7 @@ impl FromSql for [u8; 6] { } impl ToSql for [u8; 6] { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { out.write_all(&self[..]) .map(|_| IsNull::No) .map_err(Into::into) @@ -37,9 +37,10 @@ impl ToSql for [u8; 6] { #[test] fn macaddr_roundtrip() { - let mut bytes = Output::test(); + let mut buffer = Vec::new(); + let mut bytes = Output::test(&mut buffer); let input_address = [0x52, 0x54, 0x00, 0xfb, 0xc6, 0x16]; ToSql::::to_sql(&input_address, &mut bytes).unwrap(); - let output_address: [u8; 6] = FromSql::from_sql(PgValue::for_test(bytes.as_ref())).unwrap(); + let output_address: [u8; 6] = FromSql::from_sql(PgValue::for_test(&buffer)).unwrap(); assert_eq!(input_address, output_address); } diff --git a/diesel/src/pg/types/money.rs b/diesel/src/pg/types/money.rs index b6f3ebb7d6d0..75336cb0f889 100644 --- a/diesel/src/pg/types/money.rs +++ b/diesel/src/pg/types/money.rs @@ -1,6 +1,4 @@ //! Support for Money values under PostgreSQL. - -use std::io::prelude::*; use std::ops::{Add, AddAssign, Sub, SubAssign}; use crate::deserialize::{self, FromSql, FromSqlRow}; @@ -31,7 +29,7 @@ impl FromSql for PgMoney { } impl ToSql for PgMoney { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { ToSql::::to_sql(&self.0, out) } } diff --git a/diesel/src/pg/types/network_address.rs b/diesel/src/pg/types/network_address.rs index 115811455c80..ae887d898ece 100644 --- a/diesel/src/pg/types/network_address.rs +++ b/diesel/src/pg/types/network_address.rs @@ -95,7 +95,7 @@ macro_rules! impl_Sql { } impl ToSql<$ty, Pg> for IpNetwork { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { use self::ipnetwork::IpNetwork::*; let net_type = $net_type; match *self { @@ -138,11 +138,14 @@ impl_Sql!(Cidr, 1); fn v4address_to_sql() { macro_rules! test_to_sql { ($ty:ty, $net_type:expr) => { - let mut bytes = Output::test(); - let test_address = - IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap()); - ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap(); - assert_eq!(bytes, vec![PGSQL_AF_INET, 32, $net_type, 4, 127, 0, 0, 1]); + let mut buffer = Vec::new(); + { + let mut bytes = Output::test(&mut buffer); + let test_address = + IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap()); + ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap(); + } + assert_eq!(buffer, vec![PGSQL_AF_INET, 32, $net_type, 4, 127, 0, 0, 1]); }; } @@ -156,10 +159,12 @@ fn some_v4address_from_sql() { ($ty:tt) => { let input_address = IpNetwork::V4(Ipv4Network::new(Ipv4Addr::new(127, 0, 0, 1), 32).unwrap()); - let mut bytes = Output::test(); - ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap(); - let output_address = - FromSql::<$ty, Pg>::from_sql(PgValue::for_test(bytes.as_ref())).unwrap(); + let mut buffer = Vec::new(); + { + let mut bytes = Output::test(&mut buffer); + ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap(); + } + let output_address = FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap(); assert_eq!(input_address, output_address); }; } @@ -172,12 +177,16 @@ fn some_v4address_from_sql() { fn v6address_to_sql() { macro_rules! test_to_sql { ($ty:ty, $net_type:expr) => { - let mut bytes = Output::test(); - let test_address = - IpNetwork::V6(Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 64).unwrap()); - ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap(); + let mut buffer = Vec::new(); + { + let mut bytes = Output::test(&mut buffer); + let test_address = IpNetwork::V6( + Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 64).unwrap(), + ); + ToSql::<$ty, Pg>::to_sql(&test_address, &mut bytes).unwrap(); + } assert_eq!( - bytes, + buffer, vec![ PGSQL_AF_INET6, 64, @@ -214,10 +223,12 @@ fn some_v6address_from_sql() { ($ty:tt) => { let input_address = IpNetwork::V6(Ipv6Network::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 64).unwrap()); - let mut bytes = Output::test(); - ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap(); - let output_address = - FromSql::<$ty, Pg>::from_sql(PgValue::for_test(bytes.as_ref())).unwrap(); + let mut buffer = Vec::new(); + { + let mut bytes = Output::test(&mut buffer); + ToSql::<$ty, Pg>::to_sql(&input_address, &mut bytes).unwrap(); + } + let output_address = FromSql::<$ty, Pg>::from_sql(PgValue::for_test(&buffer)).unwrap(); assert_eq!(input_address, output_address); }; } diff --git a/diesel/src/pg/types/numeric.rs b/diesel/src/pg/types/numeric.rs index 75c4d8a6fffc..d453f89b7bf2 100644 --- a/diesel/src/pg/types/numeric.rs +++ b/diesel/src/pg/types/numeric.rs @@ -9,7 +9,6 @@ mod bigdecimal { use self::num_bigint::{BigInt, BigUint, Sign}; use self::num_integer::Integer; use self::num_traits::{Signed, ToPrimitive, Zero}; - use std::io::prelude::*; use crate::deserialize::{self, FromSql}; use crate::pg::data_types::PgNumeric; @@ -145,9 +144,9 @@ mod bigdecimal { } impl ToSql for BigDecimal { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { let numeric = PgNumeric::from(self); - ToSql::::to_sql(&numeric, out) + ToSql::::to_sql(&numeric, &mut out.reborrow()) } } diff --git a/diesel/src/pg/types/primitives.rs b/diesel/src/pg/types/primitives.rs index 58dc2dc93fd1..78fd9bc92b92 100644 --- a/diesel/src/pg/types/primitives.rs +++ b/diesel/src/pg/types/primitives.rs @@ -12,19 +12,44 @@ impl FromSql for bool { } impl ToSql for bool { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { out.write_all(&[*self as u8]) .map(|_| IsNull::No) .map_err(Into::into) } } +/// The returned pointer is *only* valid for the lifetime to the argument of +/// `from_sql`. This impl is intended for uses where you want to write a new +/// impl in terms of `String`, but don't want to allocate. We have to return a +/// raw pointer instead of a reference with a lifetime due to the structure of +/// `FromSql` +impl FromSql for *const str { + fn from_sql(value: PgValue<'_>) -> deserialize::Result { + use std::str; + let string = str::from_utf8(value.as_bytes())?; + Ok(string as *const _) + } +} + +/// The returned pointer is *only* valid for the lifetime to the argument of +/// `from_sql`. This impl is intended for uses where you want to write a new +/// impl in terms of `Vec`, but don't want to allocate. We have to return a +/// raw pointer instead of a reference with a lifetime due to the structure of +/// `FromSql` +impl FromSql for *const [u8] { + fn from_sql(value: PgValue<'_>) -> deserialize::Result { + Ok(value.as_bytes() as *const _) + } +} + #[test] fn bool_to_sql() { - let mut bytes = Output::test(); + let mut buffer = Vec::new(); + let mut bytes = Output::test(&mut buffer); ToSql::::to_sql(&true, &mut bytes).unwrap(); ToSql::::to_sql(&false, &mut bytes).unwrap(); - assert_eq!(bytes, vec![1u8, 0u8]); + assert_eq!(buffer, vec![1u8, 0u8]); } #[test] diff --git a/diesel/src/pg/types/ranges.rs b/diesel/src/pg/types/ranges.rs index 4dc986eb094f..1382c4084d5f 100644 --- a/diesel/src/pg/types/ranges.rs +++ b/diesel/src/pg/types/ranges.rs @@ -108,7 +108,7 @@ impl ToSql, Pg> for (Bound, Bound) where T: ToSql, { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { let mut flags = match self.0 { Bound::Included(_) => RangeFlags::LB_INC, Bound::Excluded(_) => RangeFlags::empty(), @@ -128,9 +128,8 @@ where match self.0 { Bound::Included(ref value) | Bound::Excluded(ref value) => { { - let mut inner_buffer = Output::new(buffer, out.metadata_lookup()); + let mut inner_buffer = Output::new(&mut buffer, out.metadata_lookup()); value.to_sql(&mut inner_buffer)?; - buffer = inner_buffer.into_inner(); } out.write_u32::(buffer.len() as u32)?; out.write_all(&buffer)?; @@ -142,9 +141,8 @@ where match self.1 { Bound::Included(ref value) | Bound::Excluded(ref value) => { { - let mut inner_buffer = Output::new(buffer, out.metadata_lookup()); + let mut inner_buffer = Output::new(&mut buffer, out.metadata_lookup()); value.to_sql(&mut inner_buffer)?; - buffer = inner_buffer.into_inner(); } out.write_u32::(buffer.len() as u32)?; out.write_all(&buffer)?; @@ -161,7 +159,7 @@ where ST: 'static, (Bound, Bound): ToSql, Pg>, { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { ToSql::, Pg>::to_sql(self, out) } } diff --git a/diesel/src/pg/types/record.rs b/diesel/src/pg/types/record.rs index 0b5e9186e7c5..47028a6ecb68 100644 --- a/diesel/src/pg/types/record.rs +++ b/diesel/src/pg/types/record.rs @@ -98,7 +98,7 @@ macro_rules! tuple_impls { $($T: ToSql<$ST, Pg>,)+ $(Pg: HasSqlType<$ST>),+ { - fn write_tuple<_W: Write>(&self, out: &mut Output<_W, Pg>) -> serialize::Result { + fn write_tuple(&self, out: &mut Output) -> serialize::Result { let mut buffer = Vec::new(); out.write_i32::($Tuple)?; @@ -106,9 +106,8 @@ macro_rules! tuple_impls { let oid = >::metadata(out.metadata_lookup()).oid()?; out.write_u32::(oid)?; let is_null = { - let mut temp_buffer = Output::new(buffer, out.metadata_lookup()); + let mut temp_buffer = Output::new(&mut buffer, out.metadata_lookup()); let is_null = self.$idx.to_sql(&mut temp_buffer)?; - buffer = temp_buffer.into_inner(); is_null }; @@ -136,7 +135,7 @@ impl QueryFragment for PgTuple where T: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql("("); self.0.walk_ast(out.reborrow())?; out.push_sql(")"); @@ -230,7 +229,7 @@ mod tests { struct MyStruct<'a>(i32, &'a str); impl<'a> ToSql for MyStruct<'a> { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'b: 'c, 'c>(&'b self, out: &mut Output<'c, '_, Pg>) -> serialize::Result { WriteTuple::<(Integer, Text)>::write_tuple(&(self.0, self.1), out) } } diff --git a/diesel/src/pg/types/uuid.rs b/diesel/src/pg/types/uuid.rs index d2577cebde8a..0530dfa449f1 100644 --- a/diesel/src/pg/types/uuid.rs +++ b/diesel/src/pg/types/uuid.rs @@ -19,7 +19,7 @@ impl FromSql for uuid::Uuid { } impl ToSql for uuid::Uuid { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { out.write_all(self.as_bytes()) .map(|_| IsNull::No) .map_err(Into::into) @@ -28,10 +28,11 @@ impl ToSql for uuid::Uuid { #[test] fn uuid_to_sql() { - let mut bytes = Output::test(); + let mut buffer = Vec::new(); let test_uuid = uuid::Uuid::from_fields(0xFFFF_FFFF, 0xFFFF, 0xFFFF, b"abcdef12").unwrap(); + let mut bytes = Output::test(&mut buffer); ToSql::::to_sql(&test_uuid, &mut bytes).unwrap(); - assert_eq!(bytes, test_uuid.as_bytes()); + assert_eq!(buffer, test_uuid.as_bytes()); } #[test] diff --git a/diesel/src/pg/value.rs b/diesel/src/pg/value.rs index 12081a6eda3c..ba5594412df7 100644 --- a/diesel/src/pg/value.rs +++ b/diesel/src/pg/value.rs @@ -1,5 +1,3 @@ -use super::Pg; -use crate::backend::BinaryRawValue; use std::num::NonZeroU32; use std::ops::Range; @@ -37,12 +35,6 @@ impl TypeOidLookup for NonZeroU32 { } } -impl<'a> BinaryRawValue<'a> for Pg { - fn as_bytes(value: PgValue<'a>) -> &'a [u8] { - value.raw_value - } -} - impl<'a> PgValue<'a> { #[cfg(test)] pub(crate) fn for_test(raw_value: &'a [u8]) -> Self { diff --git a/diesel/src/query_builder/ast_pass.rs b/diesel/src/query_builder/ast_pass.rs index 58afd03a0efc..72b1797792e5 100644 --- a/diesel/src/query_builder/ast_pass.rs +++ b/diesel/src/query_builder/ast_pass.rs @@ -1,6 +1,6 @@ -use std::{fmt, mem}; +use std::fmt; -use crate::backend::Backend; +use crate::backend::{Backend, HasBindCollector}; use crate::query_builder::{BindCollector, QueryBuilder}; use crate::result::QueryResult; use crate::serialize::ToSql; @@ -21,19 +21,21 @@ use crate::sql_types::HasSqlType; /// you to find out what the current pass is. You should simply call the /// relevant methods and trust that they will be a no-op if they're not relevant /// to the current pass. -pub struct AstPass<'a, DB> +pub struct AstPass<'a, 'b, DB> where DB: Backend, DB::QueryBuilder: 'a, - DB::BindCollector: 'a, + >::BindCollector: 'a, DB::MetadataLookup: 'a, + 'b: 'a, { - internals: AstPassInternals<'a, DB>, + internals: AstPassInternals<'a, 'b, DB>, } -impl<'a, DB> AstPass<'a, DB> +impl<'a, 'b, DB> AstPass<'a, 'b, DB> where DB: Backend, + 'b: 'a, { #[doc(hidden)] #[allow(clippy::wrong_self_convention)] @@ -45,7 +47,7 @@ where #[doc(hidden)] pub fn collect_binds( - collector: &'a mut DB::BindCollector, + collector: &'a mut >::BindCollector, metadata_lookup: &'a mut DB::MetadataLookup, ) -> Self { AstPass { @@ -64,7 +66,7 @@ where } #[doc(hidden)] - pub fn debug_binds(formatter: &'a mut fmt::DebugList<'a, 'a>) -> Self { + pub fn debug_binds(formatter: &'a mut fmt::DebugList<'b, 'b>) -> Self { AstPass { internals: AstPassInternals::DebugBinds(formatter), } @@ -89,9 +91,7 @@ where /// done implicitly for references. For structs with lifetimes it must be /// done explicitly. This method matches the semantics of what Rust would do /// implicitly if you were passing a mutable reference - // Clippy is wrong, this cannot be expressed with pointer casting - #[allow(clippy::transmute_ptr_to_ptr)] - pub fn reborrow(&mut self) -> AstPass { + pub fn reborrow(&'_ mut self) -> AstPass<'_, 'b, DB> { let internals = match self.internals { AstPassInternals::ToSql(ref mut builder) => AstPassInternals::ToSql(&mut **builder), AstPassInternals::CollectBinds { @@ -104,11 +104,7 @@ where AstPassInternals::IsSafeToCachePrepared(ref mut result) => { AstPassInternals::IsSafeToCachePrepared(&mut **result) } - AstPassInternals::DebugBinds(ref mut f) => { - // Safe because the lifetime is always being shortened. - let f_with_shorter_lifetime = unsafe { mem::transmute(&mut **f) }; - AstPassInternals::DebugBinds(f_with_shorter_lifetime) - } + AstPassInternals::DebugBinds(ref mut f) => AstPassInternals::DebugBinds(&mut **f), AstPassInternals::IsNoop(ref mut result) => AstPassInternals::IsNoop(&mut **result), }; AstPass { internals } @@ -155,7 +151,7 @@ where /// Left: QueryFragment, /// Right: QueryFragment, /// { - /// fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + /// fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { /// self.left.walk_ast(out.reborrow())?; /// out.push_sql(" AND "); /// self.right.walk_ast(out.reborrow())?; @@ -190,7 +186,7 @@ where /// This method affects multiple AST passes. It should be called at the /// point in the query where you'd want the parameter placeholder (`$1` on /// PG, `?` on other backends) to be inserted. - pub fn push_bind_param(&mut self, bind: &U) -> QueryResult<()> + pub fn push_bind_param(&mut self, bind: &'b U) -> QueryResult<()> where DB: HasSqlType, U: ToSql, @@ -211,7 +207,7 @@ where } #[doc(hidden)] - pub fn push_bind_param_value_only(&mut self, bind: &U) -> QueryResult<()> + pub fn push_bind_param_value_only(&mut self, bind: &'b U) -> QueryResult<()> where DB: HasSqlType, U: ToSql, @@ -234,19 +230,20 @@ where /// usage of the methods provided rather than matching on the enum directly. /// This essentially mimics the capabilities that would be available if /// `AstPass` were a trait. -enum AstPassInternals<'a, DB> +enum AstPassInternals<'a, 'b, DB> where DB: Backend, DB::QueryBuilder: 'a, - DB::BindCollector: 'a, + >::BindCollector: 'a, DB::MetadataLookup: 'a, + 'b: 'a, { ToSql(&'a mut DB::QueryBuilder), CollectBinds { - collector: &'a mut DB::BindCollector, + collector: &'a mut >::BindCollector, metadata_lookup: &'a mut DB::MetadataLookup, }, IsSafeToCachePrepared(&'a mut bool), - DebugBinds(&'a mut fmt::DebugList<'a, 'a>), + DebugBinds(&'a mut fmt::DebugList<'b, 'b>), IsNoop(&'a mut bool), } diff --git a/diesel/src/query_builder/bind_collector.rs b/diesel/src/query_builder/bind_collector.rs index 362d248cbe82..7103b5beef7a 100644 --- a/diesel/src/query_builder/bind_collector.rs +++ b/diesel/src/query_builder/bind_collector.rs @@ -13,16 +13,19 @@ use crate::sql_types::{HasSqlType, TypeMetadata}; /// the query builder will use [`AstPass::push_bind_param`] instead. /// /// [`AstPass::push_bind_param`]: crate::query_builder::AstPass::push_bind_param() -pub trait BindCollector { +pub trait BindCollector<'a, DB: TypeMetadata>: Sized { + /// The internal buffer type used by this bind collector + type Buffer; + /// Serializes the given bind value, and collects the result. fn push_bound_value( &mut self, - bind: &U, + bind: &'a U, metadata_lookup: &mut DB::MetadataLookup, ) -> QueryResult<()> where - DB: HasSqlType, - U: ToSql; + DB: Backend + HasSqlType, + U: ToSql + 'a; } #[derive(Debug)] @@ -51,9 +54,18 @@ impl RawBytesBindCollector { binds: Vec::new(), } } + + pub(crate) fn reborrow_buffer<'a: 'b, 'b>(b: &'a mut Vec) -> &'b mut Vec { + b + } } -impl BindCollector for RawBytesBindCollector { +impl<'a, DB> BindCollector<'a, DB> for RawBytesBindCollector +where + DB: Backend + TypeMetadata, +{ + type Buffer = &'a mut Vec; + fn push_bound_value( &mut self, bind: &U, @@ -63,11 +75,12 @@ impl BindCollector for RawBytesBindCollector DB: HasSqlType, U: ToSql, { - let mut to_sql_output = Output::new(Vec::new(), metadata_lookup); - let is_null = bind - .to_sql(&mut to_sql_output) - .map_err(SerializationError)?; - let bytes = to_sql_output.into_inner(); + let mut bytes = Vec::new(); + let is_null = { + let mut to_sql_output = Output::new(&mut bytes, metadata_lookup); + bind.to_sql(&mut to_sql_output) + .map_err(SerializationError)? + }; let metadata = >::metadata(metadata_lookup); match is_null { IsNull::No => self.binds.push(Some(bytes)), diff --git a/diesel/src/query_builder/clause_macro.rs b/diesel/src/query_builder/clause_macro.rs index 6f867533fb27..9d88c44ec04a 100644 --- a/diesel/src/query_builder/clause_macro.rs +++ b/diesel/src/query_builder/clause_macro.rs @@ -34,7 +34,9 @@ macro_rules! simple_clause { pub struct $no_clause; impl QueryFragment for $no_clause { - fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> + where 'a: 'b + { Ok(()) } } @@ -47,7 +49,9 @@ macro_rules! simple_clause { DB: Backend $(+ $backend_bounds)*, Expr: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where 'a: 'b + { out.push_sql($sql); self.0.walk_ast(out.reborrow())?; Ok(()) diff --git a/diesel/src/query_builder/combination_clause.rs b/diesel/src/query_builder/combination_clause.rs index e39055c4293c..5d76407e0b95 100644 --- a/diesel/src/query_builder/combination_clause.rs +++ b/diesel/src/query_builder/combination_clause.rs @@ -11,7 +11,10 @@ use crate::{CombineDsl, Insertable, QueryResult, RunQueryDsl, Table}; pub struct NoCombinationClause; impl QueryFragment for NoCombinationClause { - fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { Ok(()) } } @@ -130,7 +133,10 @@ where RhsParenthesisWrapper: QueryFragment, DB: Backend + SupportsCombinationClause, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { self.source.walk_ast(out.reborrow())?; self.combinator.walk_ast(out.reborrow())?; self.duplicate_rule.walk_ast(out.reborrow())?; @@ -143,7 +149,10 @@ where pub struct Union; impl QueryFragment for Union { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql(" UNION "); Ok(()) } @@ -154,7 +163,10 @@ impl QueryFragment for Union { pub struct Intersect; impl QueryFragment for Intersect { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql(" INTERSECT "); Ok(()) } @@ -165,7 +177,10 @@ impl QueryFragment for Intersect { pub struct Except; impl QueryFragment for Except { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql(" EXCEPT "); Ok(()) } @@ -176,7 +191,10 @@ impl QueryFragment for Except { pub struct Distinct; impl QueryFragment for Distinct { - fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, _out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { Ok(()) } } @@ -186,7 +204,10 @@ impl QueryFragment for Distinct { pub struct All; impl QueryFragment for All { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("ALL "); Ok(()) } @@ -207,7 +228,10 @@ mod postgres { use crate::QueryResult; impl> QueryFragment for RhsParenthesisWrapper { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("("); self.0.walk_ast(out.reborrow())?; out.push_sql(")"); @@ -231,7 +255,10 @@ mod mysql { use crate::QueryResult; impl> QueryFragment for RhsParenthesisWrapper { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("("); self.0.walk_ast(out.reborrow())?; out.push_sql(")"); @@ -251,7 +278,10 @@ mod sqlite { use crate::QueryResult; impl> QueryFragment for RhsParenthesisWrapper { - fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> + where + 'a: 'b, + { self.0.walk_ast(out) // SQLite does not support parenthesis around Ths } } diff --git a/diesel/src/query_builder/delete_statement/mod.rs b/diesel/src/query_builder/delete_statement/mod.rs index 71803c0ac3f5..d6486b570af2 100644 --- a/diesel/src/query_builder/delete_statement/mod.rs +++ b/diesel/src/query_builder/delete_statement/mod.rs @@ -3,13 +3,14 @@ use crate::dsl::{Filter, IntoBoxed}; use crate::expression::{AppearsOnTable, SelectableExpression}; use crate::query_builder::returning_clause::*; use crate::query_builder::where_clause::*; -use crate::query_builder::*; use crate::query_dsl::methods::{BoxedDsl, FilterDsl}; use crate::query_dsl::RunQueryDsl; use crate::query_source::Table; use crate::result::QueryResult; +use crate::{query_builder::*, QuerySource}; + +use super::from_clause::FromClause; -#[derive(Debug, Clone, Copy, QueryId)] #[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] /// Represents a SQL `DELETE` statement. /// @@ -21,21 +22,65 @@ use crate::result::QueryResult; /// - `Ret`: The `RETURNING` clause of this query. The exact types used to /// represent this are private. You can safely rely on the default type /// representing the lack of a `RETURNING` clause. -pub struct DeleteStatement { - table: T, +pub struct DeleteStatement { + from_clause: FromClause, where_clause: U, returning: Ret, } +impl Clone for DeleteStatement +where + T: QuerySource, + FromClause: Clone, + U: Clone, + Ret: Clone, +{ + fn clone(&self) -> Self { + Self { + from_clause: self.from_clause.clone(), + where_clause: self.where_clause.clone(), + returning: self.returning.clone(), + } + } +} + +impl std::fmt::Debug for DeleteStatement +where + T: QuerySource, + FromClause: std::fmt::Debug, + U: std::fmt::Debug, + Ret: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DeleteStatement") + .field("from_clause", &self.from_clause) + .field("where_clause", &self.where_clause) + .field("returning", &self.returning) + .finish() + } +} + +impl QueryId for DeleteStatement +where + T: QuerySource + QueryId + 'static, + U: QueryId, + Ret: QueryId, +{ + type QueryId = DeleteStatement; + + const HAS_STATIC_QUERY_ID: bool = + T::HAS_STATIC_QUERY_ID && U::HAS_STATIC_QUERY_ID && Ret::HAS_STATIC_QUERY_ID; +} + /// A `DELETE` statement with a boxed `WHERE` clause pub type BoxedDeleteStatement<'a, DB, T, Ret = NoReturningClause> = DeleteStatement, Ret>; -impl DeleteStatement { +impl DeleteStatement { pub(crate) fn new(table: T, where_clause: U) -> Self { DeleteStatement { - table: table, - where_clause: where_clause, + from_clause: FromClause::new(table), + where_clause, returning: NoReturningClause, } } @@ -129,12 +174,13 @@ impl FilterDsl for DeleteStatement where U: WhereAnd, Predicate: AppearsOnTable, + T: QuerySource, { type Output = DeleteStatement; fn filter(self, predicate: Predicate) -> Self::Output { DeleteStatement { - table: self.table, + from_clause: self.from_clause, where_clause: self.where_clause.and(predicate), returning: self.returning, } @@ -144,14 +190,15 @@ where impl<'a, T, U, Ret, DB> BoxedDsl<'a, DB> for DeleteStatement where U: Into>, + T: QuerySource, { type Output = BoxedDeleteStatement<'a, DB, T, Ret>; fn internal_into_boxed(self) -> Self::Output { DeleteStatement { - table: self.table, where_clause: self.where_clause.into(), returning: self.returning, + from_clause: self.from_clause, } } } @@ -160,13 +207,16 @@ impl QueryFragment for DeleteStatement where DB: Backend, T: Table, - T::FromClause: QueryFragment, + FromClause: QueryFragment, U: QueryFragment, Ret: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { - out.push_sql("DELETE FROM "); - self.table.from_clause().walk_ast(out.reborrow())?; + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { + out.push_sql("DELETE "); + self.from_clause.walk_ast(out.reborrow())?; self.where_clause.walk_ast(out.reborrow())?; self.returning.walk_ast(out.reborrow())?; Ok(()) @@ -195,9 +245,9 @@ where type SqlType = Ret::SqlType; } -impl RunQueryDsl for DeleteStatement {} +impl RunQueryDsl for DeleteStatement {} -impl DeleteStatement { +impl DeleteStatement { /// Specify what expression is returned after execution of the `delete`. /// /// # Examples @@ -225,8 +275,8 @@ impl DeleteStatement { DeleteStatement>: Query, { DeleteStatement { - table: self.table, where_clause: self.where_clause, + from_clause: self.from_clause, returning: ReturningClause(returns), } } diff --git a/diesel/src/query_builder/distinct_clause.rs b/diesel/src/query_builder/distinct_clause.rs index 077cb34bd130..c369011e967f 100644 --- a/diesel/src/query_builder/distinct_clause.rs +++ b/diesel/src/query_builder/distinct_clause.rs @@ -9,13 +9,19 @@ pub struct NoDistinctClause; pub struct DistinctClause; impl QueryFragment for NoDistinctClause { - fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { Ok(()) } } impl QueryFragment for DistinctClause { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("DISTINCT "); Ok(()) } diff --git a/diesel/src/query_builder/from_clause.rs b/diesel/src/query_builder/from_clause.rs new file mode 100644 index 000000000000..f37e6d9174b9 --- /dev/null +++ b/diesel/src/query_builder/from_clause.rs @@ -0,0 +1,139 @@ +use super::{AstPass, QueryFragment, QueryId}; +use crate::backend::Backend; +use crate::query_source::AppearsInFromClause; +use crate::{QueryResult, QuerySource}; + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoFromClause; + +impl QueryFragment for NoFromClause +where + Self: QueryFragment, + DB: Backend, +{ + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { + >::walk_ast(self, pass) + } +} + +impl QueryFragment + for NoFromClause +where + DB: Backend, +{ + fn walk_ast<'a, 'b>(&'a self, _pass: AstPass<'_, 'b, DB>) -> QueryResult<()> where 'a: 'b { + Ok(()) + } +} + +pub trait AsQuerySource { + type QuerySource: QuerySource; + + fn as_query_source(&self) -> &Self::QuerySource; +} + +impl AsQuerySource for QS +where + QS: QuerySource, +{ + type QuerySource = Self; + + fn as_query_source(&self) -> &Self::QuerySource { + self + } +} + +#[doc(hidden)] +pub struct FromClause { + pub(crate) source: F, + pub(crate) from_clause: F::FromClause, +} + +impl AsQuerySource for FromClause +where + F: QuerySource, +{ + type QuerySource = F; + + fn as_query_source(&self) -> &Self::QuerySource { + &self.source + } +} + +impl QueryId for FromClause +where + F: QuerySource + QueryId, +{ + type QueryId = F::QueryId; + + const HAS_STATIC_QUERY_ID: bool = F::HAS_STATIC_QUERY_ID; +} + +impl Copy for FromClause +where + F: QuerySource + Copy, + F::FromClause: Copy, +{ +} + +impl Clone for FromClause +where + F: QuerySource + Clone, + F::FromClause: Clone, +{ + fn clone(&self) -> Self { + Self { + source: self.source.clone(), + from_clause: self.from_clause.clone(), + } + } +} + +impl std::fmt::Debug for FromClause +where + F: QuerySource, + F::FromClause: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FromClause") + .field("from_clause", &self.from_clause) + .finish() + } +} + +impl FromClause { + pub(crate) fn new(qs: F) -> Self { + Self { + from_clause: qs.from_clause(), + source: qs, + } + } +} + +impl QueryFragment for FromClause +where + F: QuerySource, + DB: Backend, + F::FromClause: QueryFragment, +{ + fn walk_ast<'a, 'b>(&'a self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { + pass.push_sql(" FROM "); + self.from_clause.walk_ast(pass) + } +} + +impl AppearsInFromClause for FromClause +where + QS1: QuerySource, + QS2: QuerySource, + QS2: AppearsInFromClause, +{ + type Count = >::Count; +} diff --git a/diesel/src/query_builder/functions.rs b/diesel/src/query_builder/functions.rs index c9c58b55c5fb..3bc29d4d5d51 100644 --- a/diesel/src/query_builder/functions.rs +++ b/diesel/src/query_builder/functions.rs @@ -1,10 +1,13 @@ use super::delete_statement::DeleteStatement; use super::insert_statement::{Insert, InsertOrIgnore, Replace}; +use super::select_clause::SelectClause; use super::{ - IncompleteInsertStatement, IntoUpdateTarget, SelectStatement, SqlQuery, UpdateStatement, + AsQuery, IncompleteInsertStatement, IntoUpdateTarget, SelectStatement, SqlQuery, + UpdateStatement, }; use crate::expression::Expression; -use crate::query_dsl::methods::SelectDsl; +use crate::query_builder::distinct_clause::NoDistinctClause; +use crate::Table; /// Creates an `UPDATE` statement. /// @@ -399,7 +402,7 @@ pub fn delete(source: T) -> DeleteStatement(target: T) -> IncompleteInsertStatement { +pub fn insert_into(target: T) -> IncompleteInsertStatement { IncompleteInsertStatement::new(target, Insert) } @@ -450,7 +453,7 @@ pub fn insert_into(target: T) -> IncompleteInsertStatement { /// # Ok(()) /// # } /// ``` -pub fn insert_or_ignore_into(target: T) -> IncompleteInsertStatement { +pub fn insert_or_ignore_into(target: T) -> IncompleteInsertStatement { IncompleteInsertStatement::new(target, InsertOrIgnore) } @@ -460,9 +463,22 @@ pub fn insert_or_ignore_into(target: T) -> IncompleteInsertStatement(expression: T) -> crate::dsl::BareSelect where T: Expression, - SelectStatement: SelectDsl, + crate::dsl::BareSelect: AsQuery, { - SelectStatement::simple(crate::query_builder::select_statement::NoFromClause).select(expression) + SelectStatement::new( + SelectClause(expression), + super::NoFromClause, + NoDistinctClause, + super::where_clause::NoWhereClause, + super::order_clause::NoOrderClause, + super::LimitOffsetClause { + limit_clause: super::NoLimitClause, + offset_clause: super::NoOffsetClause, + }, + super::group_by_clause::NoGroupByClause, + super::having_clause::NoHavingClause, + super::locking_clause::NoLockingClause, + ) } /// Creates a `REPLACE` statement. @@ -500,7 +516,7 @@ where /// assert_eq!(Ok(vec!["Jim".into(), "Tess".into()]), names); /// # } /// # #[cfg(feature = "postgres")] fn main() {} -pub fn replace_into(target: T) -> IncompleteInsertStatement { +pub fn replace_into(target: T) -> IncompleteInsertStatement { IncompleteInsertStatement::new(target, Replace) } diff --git a/diesel/src/query_builder/insert_statement/batch_insert.rs b/diesel/src/query_builder/insert_statement/batch_insert.rs index b8fc15c4cb45..4da68325e61d 100644 --- a/diesel/src/query_builder/insert_statement/batch_insert.rs +++ b/diesel/src/query_builder/insert_statement/batch_insert.rs @@ -2,7 +2,7 @@ use super::ValuesClause; use crate::backend::{sql_dialect, Backend, SqlDialect}; use crate::insertable::CanInsertInSingleQuery; use crate::query_builder::{AstPass, QueryFragment, QueryId}; -use crate::{Insertable, QueryResult}; +use crate::QueryResult; use std::marker::PhantomData; #[doc(hidden)] @@ -76,207 +76,47 @@ where } } -#[doc(hidden)] -pub trait AsValueIterator { - type Item; - - // we return a boxed iterator as - // a plain iterator may involve lifetimes - // and the trait itself cannot have an attached lifetime - // therefor it wouldn't be possible to name the type correctly - // FIXME: This allocation can be removed if GAT's are stable - fn as_value_iter<'a>( - &'a self, - ) -> Box>::Values> + 'a> - where - Self::Item: 'a, - &'a Self::Item: Insertable; -} - -// https://github.com/rust-lang/rust-clippy/issues/7497 -#[allow(clippy::redundant_closure)] -impl AsValueIterator for [T; N] { - type Item = T; - - fn as_value_iter<'a>( - &'a self, - ) -> Box>::Values> + 'a> - where - Self::Item: 'a, - &'a T: Insertable, - { - Box::new(IntoIterator::into_iter(self).map(|v| Insertable::values(v))) - } -} - -// https://github.com/rust-lang/rust-clippy/issues/7497 -#[allow(clippy::redundant_closure)] -impl<'b, T, Tab, const N: usize> AsValueIterator for &'b [T; N] { - type Item = T; - - fn as_value_iter<'a>( - &'a self, - ) -> Box>::Values> + 'a> - where - Self::Item: 'a, - &'a T: Insertable, - { - Box::new(IntoIterator::into_iter(*self).map(|v| Insertable::values(v))) - } -} - -// https://github.com/rust-lang/rust-clippy/issues/7497 -#[allow(clippy::redundant_closure)] -impl AsValueIterator for Box<[T; N]> { - type Item = T; - - fn as_value_iter<'a>( - &'a self, - ) -> Box>::Values> + 'a> - where - Self::Item: 'a, - &'a T: Insertable, - { - Box::new(IntoIterator::into_iter(&**self).map(|v| Insertable::values(v))) - } -} - -// https://github.com/rust-lang/rust-clippy/issues/7497 -#[allow(clippy::redundant_closure)] -impl AsValueIterator for Vec { - type Item = T; - - fn as_value_iter<'a>( - &'a self, - ) -> Box>::Values> + 'a> - where - Self::Item: 'a, - &'a T: Insertable, - { - Box::new(IntoIterator::into_iter(self).map(|v| Insertable::values(v))) - } -} - -// https://github.com/rust-lang/rust-clippy/issues/7497 -#[allow(clippy::redundant_closure)] -impl<'b, T, Tab> AsValueIterator for &'b [T] { - type Item = T; - - fn as_value_iter<'a>( - &'a self, - ) -> Box>::Values> + 'a> - where - Self::Item: 'a, - &'a T: Insertable, - { - Box::new(IntoIterator::into_iter(*self).map(|v| Insertable::values(v))) - } -} - -// This trait is a workaround to issues with hrtb (for<'a>) in rust -// Rustc refuses to unify associated types pulled out of some trait -// bound that involves a hrtb. As a workaround we create this -// intermediate private trait and hide all "advanced" restrictions -// behind this trait -// For this case this is essentially only that `Self::Values` implement `QueryFragment` -// This allows us to unify all that behind a single trait bound in the -// `QueryFragment` impl below -#[doc(hidden)] -pub trait InsertableQueryfragment -where - Self: Insertable, - DB: Backend, -{ - fn walk_ast_helper_with_value_clause(values: Self::Values, out: AstPass) - -> QueryResult<()>; - - fn walk_ast_helper_without_value_clause( - values: Self::Values, - out: AstPass, - ) -> QueryResult<()>; -} - -impl<'a, Tab, DB, T> InsertableQueryfragment for &'a T -where - Self: Insertable, - <&'a T as Insertable>::Values: QueryFragment + IsValuesClause, - DB: Backend, -{ - fn walk_ast_helper_with_value_clause( - values: Self::Values, - out: AstPass, - ) -> QueryResult<()> { - values.walk_ast(out) - } - - fn walk_ast_helper_without_value_clause( - values: Self::Values, - out: AstPass, - ) -> QueryResult<()> { - values.values().walk_ast(out) - } -} - -#[doc(hidden)] -pub trait IsValuesClause { - type Inner: QueryFragment; - - fn values(&self) -> &Self::Inner; -} - -impl IsValuesClause for ValuesClause -where - DB: Backend, - Inner: QueryFragment, -{ - type Inner = Inner; - - fn values(&self) -> &Self::Inner { - &self.values - } -} - impl QueryFragment for BatchInsert where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } -impl +impl QueryFragment - for BatchInsert + for BatchInsert>, Tab, QId, HAS_STATIC_QUERY_ID> where DB: Backend + SqlDialect< BatchInsertSupport = sql_dialect::batch_insert_support::PostgresLikeBatchInsertSupport, >, DB::InsertWithDefaultKeyword: sql_dialect::default_keyword_for_insert::SupportsDefaultKeyword, - V: AsValueIterator, - for<'a> &'a T: InsertableQueryfragment, + ValuesClause: QueryFragment, + V: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { if !HAS_STATIC_QUERY_ID { out.unsafe_to_cache_prepared(); } - let mut values = self.values.as_value_iter(); + let mut values = self.values.iter(); if let Some(value) = values.next() { - <&T as InsertableQueryfragment>::walk_ast_helper_with_value_clause( - value, - out.reborrow(), - )?; + value.walk_ast(out.reborrow())?; } for value in values { out.push_sql(", ("); - <&T as InsertableQueryfragment>::walk_ast_helper_without_value_clause( - value, - out.reborrow(), - )?; + value.values.walk_ast(out.reborrow())?; out.push_sql(")"); } Ok(()) diff --git a/diesel/src/query_builder/insert_statement/insert_from_select.rs b/diesel/src/query_builder/insert_statement/insert_from_select.rs index 26754606c773..dd5f0060156b 100644 --- a/diesel/src/query_builder/insert_statement/insert_from_select.rs +++ b/diesel/src/query_builder/insert_statement/insert_from_select.rs @@ -49,7 +49,10 @@ where Columns: ColumnList + Expression, Select: Query + QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("("); self.columns.walk_ast(out.reborrow())?; out.push_sql(") "); diff --git a/diesel/src/query_builder/insert_statement/insert_with_default_for_sqlite.rs b/diesel/src/query_builder/insert_statement/insert_with_default_for_sqlite.rs index d896f6e62224..5d3b0fab9646 100644 --- a/diesel/src/query_builder/insert_statement/insert_with_default_for_sqlite.rs +++ b/diesel/src/query_builder/insert_statement/insert_with_default_for_sqlite.rs @@ -1,80 +1,43 @@ use super::{BatchInsert, InsertStatement}; use crate::connection::Connection; -use crate::insertable::Insertable; +use crate::insertable::InsertValues; use crate::insertable::{CanInsertInSingleQuery, ColumnInsertValue, DefaultableColumnInsertValue}; use crate::prelude::*; -use crate::query_builder::returning_clause::NoReturningClause; -use crate::query_builder::{ - AsValueIterator, AstPass, InsertableQueryfragment, QueryId, ValuesClause, -}; +use crate::query_builder::{AstPass, QueryId, ValuesClause}; use crate::query_builder::{DebugQuery, QueryFragment}; use crate::query_dsl::methods::ExecuteDsl; use crate::sqlite::Sqlite; use crate::{QueryResult, Table}; -use std::fmt::{self, Debug, Display, Write}; - -pub trait SqliteInsertableQueryfragment -where - Self: Insertable, -{ - fn execute_single_record( - v: Self::Values, - conn: &mut C, - target: Tab, - op: Op, - ) -> QueryResult; - - fn write_debug_query(v: Self::Values, out: &mut impl Write, target: Tab, op: Op) - -> fmt::Result; -} - -impl<'a, Tab, T, Op, C> SqliteInsertableQueryfragment for &'a T -where - Self: Insertable, - Tab: Table, - C: Connection, - InsertStatement>::Values, Op>: - ExecuteDsl + QueryFragment, -{ - fn execute_single_record( - v: Self::Values, - conn: &mut C, - target: Tab, - op: Op, - ) -> QueryResult { - ExecuteDsl::execute(InsertStatement::new(target, v, op, NoReturningClause), conn) - } - - fn write_debug_query( - v: Self::Values, - out: &mut impl Write, - target: Tab, - op: Op, - ) -> fmt::Result { - let stmt = InsertStatement::new(target, v, op, NoReturningClause); - write!(out, "{}", crate::debug_query::(&stmt)) - } -} +use std::fmt::{self, Debug, Display}; pub trait DebugQueryHelper { fn fmt_debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result; fn fmt_display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result; } -impl<'a, T, V, QId, Op, const STATIC_QUERY_ID: bool> DebugQueryHelper - for DebugQuery<'a, InsertStatement, Op>, Sqlite> +impl<'a, T, V, QId, Op, Ret, const STATIC_QUERY_ID: bool> DebugQueryHelper + for DebugQuery< + 'a, + InsertStatement>, T, QId, STATIC_QUERY_ID>, Op, Ret>, + Sqlite, + > where - V: AsValueIterator, - for<'b> &'b V::Item: SqliteInsertableQueryfragment, - T: Copy, + V: QueryFragment, + T: Copy + QuerySource, Op: Copy, + Ret: Copy, + for<'b> InsertStatement, Op, Ret>: QueryFragment, { fn fmt_debug(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut statements = vec![String::from("BEGIN")]; - for record in self.query.records.values.as_value_iter() { - let mut out = String::new(); - <&V::Item as SqliteInsertableQueryfragment>::write_debug_query(record, &mut out, self.query.target, self.query.operator)?; - statements.push(out); + for record in self.query.records.values.iter() { + let stmt = InsertStatement::new( + self.query.target, + record, + self.query.operator, + self.query.returning, + ); + statements.push(crate::debug_query(&stmt).to_string()); } statements.push("COMMIT".into()); @@ -86,9 +49,14 @@ where fn fmt_display(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { writeln!(f, "BEGIN;")?; - for record in self.query.records.values.as_value_iter() { - <&V::Item as SqliteInsertableQueryfragment>::write_debug_query(record, f, self.query.target, self.query.operator)?; - writeln!(f)?; + for record in self.query.records.values.iter() { + let stmt = InsertStatement::new( + self.query.target, + record, + self.query.operator, + self.query.returning, + ); + writeln!(f, "{}", crate::debug_query(&stmt))?; } writeln!(f, "COMMIT;")?; Ok(()) @@ -98,7 +66,7 @@ where impl<'a, T, V, QId, Op, const STATIC_QUERY_ID: bool> DebugQueryHelper for DebugQuery<'a, InsertStatement, Op>, Sqlite> where - T: Copy, + T: Copy + QuerySource, Op: Copy, DebugQuery< 'a, @@ -138,11 +106,14 @@ where } impl<'a, T, V, QId, Op, O, const STATIC_QUERY_ID: bool> Display - for DebugQuery<'a, InsertStatement, Op>, Sqlite> + for DebugQuery< + 'a, + InsertStatement>, T, QId, STATIC_QUERY_ID>, Op>, + Sqlite, + > where - V: AsValueIterator, - V::Item: Insertable, - >::Values: ContainsDefaultableValue, + T: QuerySource, + V: ContainsDefaultableValue, Self: DebugQueryHelper, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -151,11 +122,14 @@ where } impl<'a, T, V, QId, Op, O, const STATIC_QUERY_ID: bool> Debug - for DebugQuery<'a, InsertStatement, Op>, Sqlite> + for DebugQuery< + 'a, + InsertStatement>, T, QId, STATIC_QUERY_ID>, Op>, + Sqlite, + > where - V: AsValueIterator, - V::Item: Insertable, - >::Values: ContainsDefaultableValue, + T: QuerySource, + V: ContainsDefaultableValue, Self: DebugQueryHelper, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -235,12 +209,11 @@ where } impl ExecuteDsl - for InsertStatement, Op> + for InsertStatement>, T, QId, STATIC_QUERY_ID>, Op> where + T: QuerySource, C: Connection, - V: AsValueIterator, - V::Item: Insertable, - >::Values: ContainsDefaultableValue, + V: ContainsDefaultableValue, O: Default, (O, Self): ExecuteDsl, { @@ -252,27 +225,22 @@ where impl ExecuteDsl for ( Yes, - InsertStatement, Op>, + InsertStatement>, T, QId, STATIC_QUERY_ID>, Op>, ) where C: Connection, - V: AsValueIterator, - for<'a> &'a V::Item: SqliteInsertableQueryfragment, - T: Table + Copy + QueryId, + T: Table + Copy + QueryId + 'static, T::FromClause: QueryFragment, - Op: Copy + QueryId, + Op: Copy + QueryId + QueryFragment, + V: InsertValues + CanInsertInSingleQuery + QueryId, { fn execute((Yes, query): Self, conn: &mut C) -> QueryResult { conn.transaction(|conn| { let mut result = 0; - for record in query.records.values.as_value_iter() { - result += - <&V::Item as SqliteInsertableQueryfragment>::execute_single_record( - record, - conn, - query.target, - query.operator, - )?; + for record in &query.records.values { + let stmt = + InsertStatement::new(query.target, record, query.operator, query.returning); + result += stmt.execute(conn)?; } Ok(result) }) @@ -285,30 +253,27 @@ pub struct SqliteBatchInsertWrapper( BatchInsert, ); -impl QueryFragment - for SqliteBatchInsertWrapper +impl QueryFragment + for SqliteBatchInsertWrapper>, Tab, QId, STATIC_QUERY_ID> where - V: AsValueIterator, - for<'a> &'a T: InsertableQueryfragment, + ValuesClause: QueryFragment, + V: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> + where + 'a: 'b, + { if !STATIC_QUERY_ID { out.unsafe_to_cache_prepared(); } - let mut values = self.0.values.as_value_iter(); + let mut values = self.0.values.iter(); if let Some(value) = values.next() { - <&T as InsertableQueryfragment>::walk_ast_helper_with_value_clause( - value, - out.reborrow(), - )?; + value.walk_ast(out.reborrow())?; } for value in values { out.push_sql(", ("); - <&T as InsertableQueryfragment>::walk_ast_helper_without_value_clause( - value, - out.reborrow(), - )?; + value.values.walk_ast(out.reborrow())?; out.push_sql(")"); } Ok(()) @@ -320,58 +285,16 @@ where pub struct SqliteCanInsertInSingleQueryHelper(T); impl CanInsertInSingleQuery - for SqliteBatchInsertWrapper + for SqliteBatchInsertWrapper>, T, QId, STATIC_QUERY_ID> where // We constrain that here on an internal helper type // to make sure that this does not accidently leak // so that noone does really implement normal batch // insert for inserts with default values here SqliteCanInsertInSingleQueryHelper: CanInsertInSingleQuery, - V: AsValueIterator, - V::Item: Insertable, - >::Values: ContainsDefaultableValue, { fn rows_to_insert(&self) -> Option { - let values = &self.0.values; - let values = unsafe { - // This cast is safe as `SqliteCanInsertInSingleQueryHelper` is #[repr(transparent)] - &*(values as *const V as *const SqliteCanInsertInSingleQueryHelper) - }; - values.rows_to_insert() - } -} - -impl CanInsertInSingleQuery - for SqliteCanInsertInSingleQueryHelper<[T; N]> -{ - fn rows_to_insert(&self) -> Option { - Some(N) - } -} - -impl CanInsertInSingleQuery - for SqliteCanInsertInSingleQueryHelper> -{ - fn rows_to_insert(&self) -> Option { - Some(N) - } -} - -impl CanInsertInSingleQuery for SqliteCanInsertInSingleQueryHelper<[T]> { - fn rows_to_insert(&self) -> Option { - Some(self.0.len()) - } -} - -impl<'a, T> CanInsertInSingleQuery for SqliteCanInsertInSingleQueryHelper<&'a [T]> { - fn rows_to_insert(&self) -> Option { - Some(self.0.len()) - } -} - -impl CanInsertInSingleQuery for SqliteCanInsertInSingleQueryHelper> { - fn rows_to_insert(&self) -> Option { - Some(self.0.len()) + Some(self.0.values.len()) } } @@ -402,7 +325,7 @@ impl ExecuteDsl ) where C: Connection, - T: Table + QueryId, + T: Table + QueryId + 'static, T::FromClause: QueryFragment, Op: QueryFragment + QueryId, SqliteBatchInsertWrapper: @@ -414,6 +337,7 @@ where operator: query.operator, target: query.target, returning: query.returning, + into_clause: query.into_clause, }; query.execute(conn) } diff --git a/diesel/src/query_builder/insert_statement/mod.rs b/diesel/src/query_builder/insert_statement/mod.rs index 187174fb4c66..040e100cc5cb 100644 --- a/diesel/src/query_builder/insert_statement/mod.rs +++ b/diesel/src/query_builder/insert_statement/mod.rs @@ -2,9 +2,7 @@ mod batch_insert; mod column_list; mod insert_from_select; -pub use self::batch_insert::{ - AsValueIterator, BatchInsert, InsertableQueryfragment, IsValuesClause, -}; +pub use self::batch_insert::BatchInsert; pub(crate) use self::column_list::ColumnList; pub(crate) use self::insert_from_select::InsertFromSelect; @@ -15,7 +13,6 @@ use crate::backend::{sql_dialect, Backend, SqlDialect}; use crate::expression::grouped::Grouped; use crate::expression::operators::Eq; use crate::expression::{Expression, NonAggregate, SelectableExpression}; -use crate::insertable::*; #[cfg(feature = "mysql")] use crate::mysql::Mysql; use crate::query_builder::*; @@ -24,6 +21,7 @@ use crate::query_source::{Column, Table}; use crate::result::QueryResult; #[cfg(feature = "sqlite")] use crate::sqlite::Sqlite; +use crate::{insertable::*, QuerySource}; #[cfg(feature = "sqlite")] mod insert_with_default_for_sqlite; @@ -43,7 +41,7 @@ pub struct IncompleteInsertStatement { operator: Op, } -impl IncompleteInsertStatement { +impl IncompleteInsertStatement { pub(crate) fn new(target: T, operator: Op) -> Self { IncompleteInsertStatement { target, operator } } @@ -112,7 +110,7 @@ impl IncompleteInsertStatement { } } -#[derive(Debug, Copy, Clone, QueryId)] +#[derive(Debug, Copy, Clone)] #[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] /// A fully constructed insert statement. /// @@ -126,20 +124,37 @@ impl IncompleteInsertStatement { /// - `Ret`: The `RETURNING` clause of the query. The specific types used to /// represent this are private. You can safely rely on the default type /// representing a query without a `RETURNING` clause. -pub struct InsertStatement { +pub struct InsertStatement { operator: Op, target: T, records: U, returning: Ret, + into_clause: T::FromClause, +} + +impl QueryId for InsertStatement +where + T: QuerySource + QueryId + 'static, + U: QueryId, + Op: QueryId, + Ret: QueryId, +{ + type QueryId = InsertStatement; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID + && U::HAS_STATIC_QUERY_ID + && Op::HAS_STATIC_QUERY_ID + && Ret::HAS_STATIC_QUERY_ID; } -impl InsertStatement { +impl InsertStatement { fn new(target: T, records: U, operator: Op, returning: Ret) -> Self { InsertStatement { - operator: operator, - target: target, - records: records, - returning: returning, + into_clause: target.from_clause(), + operator, + target, + records, + returning, } } @@ -151,7 +166,7 @@ impl InsertStatement { } } -impl InsertStatement, Op, Ret> { +impl InsertStatement, Op, Ret> { /// Set the column list when inserting from a select statement /// /// See the documentation for [`insert_into`] for usage examples. @@ -183,17 +198,20 @@ where Op: QueryFragment, Ret: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { if self.records.rows_to_insert() == Some(0) { out.push_sql("SELECT 1 FROM "); - self.target.from_clause().walk_ast(out.reborrow())?; + self.into_clause.walk_ast(out.reborrow())?; out.push_sql(" WHERE 1=0"); return Ok(()); } self.operator.walk_ast(out.reborrow())?; out.push_sql(" INTO "); - self.target.from_clause().walk_ast(out.reborrow())?; + self.into_clause.walk_ast(out.reborrow())?; out.push_sql(" "); self.records.walk_ast(out.reborrow())?; self.returning.walk_ast(out.reborrow())?; @@ -216,14 +234,15 @@ where impl Query for InsertStatement> where + T: QuerySource, Ret: Expression + SelectableExpression + NonAggregate, { type SqlType = Ret::SqlType; } -impl RunQueryDsl for InsertStatement {} +impl RunQueryDsl for InsertStatement {} -impl InsertStatement { +impl InsertStatement { /// Specify what expression is returned after execution of the `insert`. /// # Examples /// @@ -263,7 +282,10 @@ impl InsertStatement { pub struct Insert; impl QueryFragment for Insert { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("INSERT"); Ok(()) } @@ -275,7 +297,10 @@ pub struct InsertOrIgnore; #[cfg(feature = "sqlite")] impl QueryFragment for InsertOrIgnore { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("INSERT OR IGNORE"); Ok(()) } @@ -283,7 +308,10 @@ impl QueryFragment for InsertOrIgnore { #[cfg(feature = "mysql")] impl QueryFragment for InsertOrIgnore { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("INSERT IGNORE"); Ok(()) } @@ -295,7 +323,10 @@ pub struct Replace; #[cfg(feature = "sqlite")] impl QueryFragment for Replace { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("REPLACE"); Ok(()) } @@ -303,7 +334,10 @@ impl QueryFragment for Replace { #[cfg(feature = "mysql")] impl QueryFragment for Replace { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("REPLACE"); Ok(()) } @@ -392,7 +426,10 @@ where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } @@ -405,7 +442,10 @@ where DefaultValueClauseForInsert = sql_dialect::default_value_clause::AnsiDefaultValueClause, >, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("DEFAULT VALUES"); Ok(()) } @@ -450,7 +490,10 @@ where T: InsertValues, DefaultValues: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { if self.values.is_noop()? { DefaultValues.walk_ast(out)?; } else { diff --git a/diesel/src/query_builder/locking_clause.rs b/diesel/src/query_builder/locking_clause.rs index 8605ac930366..712dae8f4c5f 100644 --- a/diesel/src/query_builder/locking_clause.rs +++ b/diesel/src/query_builder/locking_clause.rs @@ -6,7 +6,10 @@ use crate::result::QueryResult; pub struct NoLockingClause; impl QueryFragment for NoLockingClause { - fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { Ok(()) } } @@ -29,7 +32,10 @@ impl LockingClause { impl, M: QueryFragment> QueryFragment for LockingClause { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { self.lock_mode.walk_ast(out.reborrow())?; self.modifier.walk_ast(out.reborrow()) } diff --git a/diesel/src/query_builder/mod.rs b/diesel/src/query_builder/mod.rs index 4f45893eb5aa..10c348b89222 100644 --- a/diesel/src/query_builder/mod.rs +++ b/diesel/src/query_builder/mod.rs @@ -15,6 +15,7 @@ pub(crate) mod combination_clause; mod debug_query; mod delete_statement; mod distinct_clause; +mod from_clause; #[doc(hidden)] pub mod functions; mod group_by_clause; @@ -40,20 +41,16 @@ pub use self::bind_collector::BindCollector; pub use self::debug_query::DebugQuery; pub use self::delete_statement::{BoxedDeleteStatement, DeleteStatement}; #[doc(hidden)] -pub use self::insert_statement::{ - AsValueIterator, BatchInsert, DefaultValues, InsertableQueryfragment, IsValuesClause, -}; +pub use self::insert_statement::{BatchInsert, DefaultValues}; #[doc(inline)] pub use self::insert_statement::{ IncompleteInsertStatement, InsertStatement, UndecoratedInsertRecord, ValuesClause, }; pub use self::query_id::QueryId; #[doc(inline)] -pub use self::select_clause::{ - IntoBoxedSelectClause, SelectClauseExpression, SelectClauseQueryFragment, -}; +pub use self::select_clause::SelectClauseExpression; #[doc(hidden)] -pub use self::select_statement::{BoxedSelectStatement, NoFromClause, SelectStatement}; +pub use self::select_statement::{BoxedSelectStatement, SelectStatement}; pub use self::sql_query::{BoxedSqlQuery, SqlQuery}; #[doc(inline)] pub use self::update_statement::{ @@ -61,12 +58,15 @@ pub use self::update_statement::{ }; pub use self::upsert::on_conflict_target_decorations::DecoratableTarget; +#[doc(hidden)] +pub use self::from_clause::{FromClause, NoFromClause}; pub use self::limit_clause::{LimitClause, NoLimitClause}; pub use self::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; pub use self::offset_clause::{NoOffsetClause, OffsetClause}; - #[doc(hidden)] pub use self::returning_clause::ReturningClause; +#[doc(hidden)] +pub use self::select_clause::DefaultSelectClause; pub(crate) use self::insert_statement::ColumnList; @@ -75,7 +75,7 @@ pub use crate::pg::query_builder::only_clause::Only; use std::error::Error; -use crate::backend::Backend; +use crate::backend::{Backend, HasBindCollector}; use crate::result::QueryResult; mod private { @@ -168,7 +168,7 @@ pub trait QueryFragment { /// This method will contain the behavior required for all possible AST /// passes. See [`AstPass`] for more details. /// - fn walk_ast(&self, pass: AstPass) -> QueryResult<()>; + fn walk_ast<'a: 'b, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()>; /// Converts this `QueryFragment` to its SQL representation. /// @@ -183,11 +183,14 @@ pub trait QueryFragment { /// itself. It is represented in SQL with a placeholder such as `?` or `$1`. /// /// This method should only be called by implementations of `Connection`. - fn collect_binds( - &self, - out: &mut DB::BindCollector, - metadata_lookup: &mut DB::MetadataLookup, - ) -> QueryResult<()> { + fn collect_binds<'a, 'b, 'c>( + &'c self, + out: &'a mut >::BindCollector, + metadata_lookup: &'a mut DB::MetadataLookup, + ) -> QueryResult<()> + where + 'c: 'b, + { self.walk_ast(AstPass::collect_binds(out, metadata_lookup)) } @@ -226,7 +229,10 @@ where DB: Backend, T: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { QueryFragment::walk_ast(&**self, pass) } } @@ -236,13 +242,19 @@ where DB: Backend, T: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'b, 'c>(&'b self, pass: AstPass<'_, 'c, DB>) -> QueryResult<()> + where + 'b: 'c, + { QueryFragment::walk_ast(&**self, pass) } } impl QueryFragment for () { - fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { Ok(()) } } @@ -252,7 +264,10 @@ where DB: Backend, T: QueryFragment, { - fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { match *self { Some(ref c) => c.walk_ast(out), None => Ok(()), diff --git a/diesel/src/query_builder/nodes/mod.rs b/diesel/src/query_builder/nodes/mod.rs index 7a1787095203..e576b7e37dd8 100644 --- a/diesel/src/query_builder/nodes/mod.rs +++ b/diesel/src/query_builder/nodes/mod.rs @@ -1,42 +1,76 @@ +use std::marker::PhantomData; + use crate::backend::Backend; use crate::query_builder::*; use crate::result::QueryResult; +pub trait StaticQueryFragment { + type Component: 'static; + const STATIC_COMPONENT: &'static Self::Component; +} + +#[derive(Debug, Copy, Clone)] +pub struct StaticQueryFragmentInstance(PhantomData); + +impl StaticQueryFragmentInstance { + pub const fn new() -> Self { + Self(PhantomData) + } +} + +impl QueryFragment for StaticQueryFragmentInstance +where + DB: Backend, + T: StaticQueryFragment, + T::Component: QueryFragment, +{ + fn walk_ast<'a: 'b, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> { + T::STATIC_COMPONENT.walk_ast(pass) + } +} + #[derive(Debug, Copy, Clone)] pub struct Identifier<'a>(pub &'a str); impl<'a, DB: Backend> QueryFragment for Identifier<'a> { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'b: 'c, 'c>(&'b self, mut out: AstPass<'_, 'c, DB>) -> QueryResult<()> { out.push_identifier(self.0) } } +pub trait MiddleFragment { + fn push_sql(&self, pass: AstPass); +} + +impl<'a, DB: Backend> MiddleFragment for &'a str { + fn push_sql(&self, mut pass: AstPass) { + pass.push_sql(self); + } +} + #[derive(Debug, Copy, Clone)] -pub struct InfixNode<'a, T, U> { +pub struct InfixNode { lhs: T, rhs: U, - middle: &'a str, + middle: M, } -impl<'a, T, U> InfixNode<'a, T, U> { - pub fn new(lhs: T, rhs: U, middle: &'a str) -> Self { - InfixNode { - lhs: lhs, - rhs: rhs, - middle: middle, - } +impl InfixNode { + pub const fn new(lhs: T, rhs: U, middle: M) -> Self { + InfixNode { lhs, rhs, middle } } } -impl<'a, T, U, DB> QueryFragment for InfixNode<'a, T, U> +impl QueryFragment for InfixNode where DB: Backend, T: QueryFragment, U: QueryFragment, + M: MiddleFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'b: 'c, 'c>(&'b self, mut out: AstPass<'_, 'c, DB>) -> QueryResult<()> { self.lhs.walk_ast(out.reborrow())?; - out.push_sql(self.middle); + self.middle.push_sql(out.reborrow()); self.rhs.walk_ast(out.reborrow())?; Ok(()) } diff --git a/diesel/src/query_builder/returning_clause.rs b/diesel/src/query_builder/returning_clause.rs index 2fbaca353523..f7977f676d54 100644 --- a/diesel/src/query_builder/returning_clause.rs +++ b/diesel/src/query_builder/returning_clause.rs @@ -7,7 +7,10 @@ use crate::result::QueryResult; pub struct NoReturningClause; impl QueryFragment for NoReturningClause { - fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { Ok(()) } } @@ -21,7 +24,10 @@ where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } @@ -35,7 +41,10 @@ where >, Expr: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql(" RETURNING "); self.0.walk_ast(out.reborrow())?; Ok(()) diff --git a/diesel/src/query_builder/select_clause.rs b/diesel/src/query_builder/select_clause.rs index 31e3821fb014..95c60497201c 100644 --- a/diesel/src/query_builder/select_clause.rs +++ b/diesel/src/query_builder/select_clause.rs @@ -1,10 +1,64 @@ +use super::from_clause::AsQuerySource; use crate::backend::Backend; use crate::expression::{Expression, SelectableExpression}; use crate::query_builder::*; use crate::query_source::QuerySource; -#[derive(Debug, Clone, Copy, QueryId)] -pub struct DefaultSelectClause; +#[doc(hidden)] +pub struct DefaultSelectClause { + default_seletion: ::DefaultSelection, +} + +impl std::fmt::Debug for DefaultSelectClause +where + QS: AsQuerySource, + ::DefaultSelection: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DefaultSelectClause") + .field("default_seletion", &self.default_seletion) + .finish() + } +} + +impl Clone for DefaultSelectClause +where + QS: AsQuerySource, + ::DefaultSelection: Clone, +{ + fn clone(&self) -> Self { + Self { + default_seletion: self.default_seletion.clone(), + } + } +} + +impl Copy for DefaultSelectClause +where + QS: AsQuerySource, + ::DefaultSelection: Copy, +{ +} + +impl DefaultSelectClause { + pub(crate) fn new(qs: &QS) -> Self { + Self { + default_seletion: qs.as_query_source().default_selection(), + } + } +} + +impl QueryId for DefaultSelectClause +where + QS: AsQuerySource, + ::DefaultSelection: QueryId, +{ + type QueryId = <::DefaultSelection as QueryId>::QueryId; + + const HAS_STATIC_QUERY_ID: bool = + <::DefaultSelection as QueryId>::HAS_STATIC_QUERY_ID; +} + #[derive(Debug, Clone, Copy, QueryId)] pub struct SelectClause(pub T); @@ -14,96 +68,59 @@ pub struct SelectClause(pub T); /// generic type parameter. This allows to access the query source in generic code. pub trait SelectClauseExpression { /// The expression represented by the given select clause - type Selection: SelectableExpression; + type Selection; /// SQL type of the select clause type SelectClauseSqlType; } -impl SelectClauseExpression for SelectClause +impl SelectClauseExpression> for SelectClause where + QS: QuerySource, T: SelectableExpression, { type Selection = T; type SelectClauseSqlType = T::SqlType; } -impl SelectClauseExpression for DefaultSelectClause +impl SelectClauseExpression for SelectClause where - QS: QuerySource, + T: SelectableExpression, { - type Selection = QS::DefaultSelection; - type SelectClauseSqlType = ::SqlType; -} - -/// Specialised variant of `QueryFragment` for select clause types -/// -/// The difference to the normal `QueryFragment` trait is the query source (`QS`) -/// generic type parameter. -pub trait SelectClauseQueryFragment { - /// Walk over this `SelectClauseQueryFragment` for all passes. - /// - /// This method is where the actual behavior of an select clause is implemented. - /// This method will contain the behavior required for all possible AST - /// passes. See [`AstPass`] for more details. - /// - fn walk_ast(&self, source: &QS, pass: AstPass) -> QueryResult<()>; -} - -impl SelectClauseQueryFragment for SelectClause -where - DB: Backend, - T: QueryFragment, -{ - fn walk_ast(&self, _: &QS, pass: AstPass) -> QueryResult<()> { - self.0.walk_ast(pass) - } + type Selection = T; + type SelectClauseSqlType = T::SqlType; } -impl SelectClauseQueryFragment for DefaultSelectClause +impl SelectClauseExpression> for DefaultSelectClause> where - DB: Backend, QS: QuerySource, - QS::DefaultSelection: QueryFragment, { - fn walk_ast(&self, source: &QS, pass: AstPass) -> QueryResult<()> { - source.default_selection().walk_ast(pass) - } -} - -/// An internal helper trait to convert different select clauses -/// into their boxed counter part. -/// -/// You normally don't need this trait, at least as long as you -/// don't implement your own select clause representation -pub trait IntoBoxedSelectClause<'a, DB, QS> { - /// The sql type of the select clause - type SqlType; - - /// Convert the select clause into a the boxed representation - fn into_boxed(self, source: &QS) -> Box + Send + 'a>; + type Selection = QS::DefaultSelection; + type SelectClauseSqlType = ::SqlType; } -impl<'a, DB, T, QS> IntoBoxedSelectClause<'a, DB, QS> for SelectClause +impl QueryFragment for SelectClause where - T: QueryFragment + SelectableExpression + Send + 'a, DB: Backend, + T: QueryFragment, { - type SqlType = T::SqlType; - - fn into_boxed(self, _source: &QS) -> Box + Send + 'a> { - Box::new(self.0) + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { + self.0.walk_ast(pass) } } -impl<'a, DB, QS> IntoBoxedSelectClause<'a, DB, QS> for DefaultSelectClause +impl QueryFragment for DefaultSelectClause where - QS: QuerySource, - QS::DefaultSelection: QueryFragment + Send + 'a, DB: Backend, + QS: AsQuerySource, + ::DefaultSelection: QueryFragment, { - type SqlType = ::SqlType; - - fn into_boxed(self, source: &QS) -> Box + Send + 'a> { - Box::new(source.default_selection()) + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { + self.default_seletion.walk_ast(pass) } } diff --git a/diesel/src/query_builder/select_statement/boxed.rs b/diesel/src/query_builder/select_statement/boxed.rs index 197c776d8153..5bd1181dd28c 100644 --- a/diesel/src/query_builder/select_statement/boxed.rs +++ b/diesel/src/query_builder/select_statement/boxed.rs @@ -7,6 +7,7 @@ use crate::expression::*; use crate::insertable::Insertable; use crate::query_builder::combination_clause::*; use crate::query_builder::distinct_clause::DistinctClause; +use crate::query_builder::from_clause::FromClause; use crate::query_builder::group_by_clause::ValidGroupByClause; use crate::query_builder::having_clause::HavingClause; use crate::query_builder::insert_statement::InsertFromSelect; @@ -36,11 +37,11 @@ pub struct BoxedSelectStatement<'a, ST, QS, DB, GB = ()> { _marker: PhantomData<(ST, GB)>, } -impl<'a, ST, QS, DB, GB> BoxedSelectStatement<'a, ST, QS, DB, GB> { +impl<'a, ST, QS: QuerySource, DB, GB> BoxedSelectStatement<'a, ST, FromClause, DB, GB> { #[allow(clippy::too_many_arguments)] - pub fn new( + pub(crate) fn new( select: S, - from: QS, + from: FromClause, distinct: Box + Send + 'a>, where_clause: BoxedWhereClause<'a, DB>, order: Option + Send + 'a>>, @@ -51,11 +52,49 @@ impl<'a, ST, QS, DB, GB> BoxedSelectStatement<'a, ST, QS, DB, GB> { where DB: Backend, G: ValidGroupByClause + QueryFragment + Send + 'a, - S: IntoBoxedSelectClause<'a, DB, QS> + SelectClauseExpression, + S: SelectClauseExpression, SelectClauseSqlType = ST> + + QueryFragment + + Send + + 'a, S::Selection: ValidGrouping, { BoxedSelectStatement { - select: select.into_boxed(&from), + select: Box::new(select), + from, + distinct, + where_clause, + order, + limit_offset, + group_by: Box::new(group_by), + having, + _marker: PhantomData, + } + } +} + +impl<'a, ST, DB, GB> BoxedSelectStatement<'a, ST, NoFromClause, DB, GB> { + #[allow(clippy::too_many_arguments)] + pub(crate) fn new_no_from_clause( + select: S, + from: NoFromClause, + distinct: Box + Send + 'a>, + where_clause: BoxedWhereClause<'a, DB>, + order: Option + Send + 'a>>, + limit_offset: BoxedLimitOffsetClause<'a, DB>, + group_by: G, + having: Box + Send + 'a>, + ) -> Self + where + DB: Backend, + G: ValidGroupByClause + QueryFragment + Send + 'a, + S: SelectClauseExpression + + QueryFragment + + Send + + 'a, + S::Selection: ValidGrouping, + { + BoxedSelectStatement { + select: Box::new(select), from, distinct, where_clause, @@ -69,22 +108,24 @@ impl<'a, ST, QS, DB, GB> BoxedSelectStatement<'a, ST, QS, DB, GB> { } impl<'a, ST, QS, DB, GB> BoxedSelectStatement<'a, ST, QS, DB, GB> { - pub(crate) fn build_query( - &self, - mut out: AstPass, - where_clause_handler: impl Fn(&BoxedWhereClause<'a, DB>, AstPass) -> QueryResult<()>, + pub(crate) fn build_query<'b, 'c>( + &'b self, + mut out: AstPass<'_, 'c, DB>, + where_clause_handler: impl Fn( + &'b BoxedWhereClause<'a, DB>, + AstPass<'_, 'c, DB>, + ) -> QueryResult<()>, ) -> QueryResult<()> where DB: Backend, - QS: QuerySource, - QS::FromClause: QueryFragment, + QS: QueryFragment, BoxedLimitOffsetClause<'a, DB>: QueryFragment, + 'b: 'c, { out.push_sql("SELECT "); self.distinct.walk_ast(out.reborrow())?; self.select.walk_ast(out.reborrow())?; - out.push_sql(" FROM "); - self.from.from_clause().walk_ast(out.reborrow())?; + self.from.walk_ast(out.reborrow())?; where_clause_handler(&self.where_clause, out.reborrow())?; self.group_by.walk_ast(out.reborrow())?; self.having.walk_ast(out.reborrow())?; @@ -120,35 +161,17 @@ impl<'a, ST, QS, QS2, DB, GB> ValidSubselect for BoxedSelectStatement<'a, S impl<'a, ST, QS, DB, GB> QueryFragment for BoxedSelectStatement<'a, ST, QS, DB, GB> where DB: Backend, - QS: QuerySource, - QS::FromClause: QueryFragment, + QS: QueryFragment, BoxedLimitOffsetClause<'a, DB>: QueryFragment, { - fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + fn walk_ast<'b, 'c>(&'b self, out: AstPass<'_, 'c, DB>) -> QueryResult<()> + where + 'b: 'c, + { self.build_query(out, |where_clause, out| where_clause.walk_ast(out)) } } -impl<'a, ST, DB, GB> QueryFragment for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB> -where - DB: Backend, - NoFromClause: QueryFragment, - BoxedLimitOffsetClause<'a, DB>: QueryFragment, -{ - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { - out.push_sql("SELECT "); - self.distinct.walk_ast(out.reborrow())?; - self.select.walk_ast(out.reborrow())?; - self.from.walk_ast(out.reborrow())?; - self.where_clause.walk_ast(out.reborrow())?; - self.group_by.walk_ast(out.reborrow())?; - self.having.walk_ast(out.reborrow())?; - self.order.walk_ast(out.reborrow())?; - self.limit_offset.walk_ast(out.reborrow())?; - Ok(()) - } -} - impl<'a, ST, QS, DB, GB> QueryId for BoxedSelectStatement<'a, ST, QS, DB, GB> { type QueryId = (); @@ -156,16 +179,19 @@ impl<'a, ST, QS, DB, GB> QueryId for BoxedSelectStatement<'a, ST, QS, DB, GB> { } impl<'a, ST, QS, DB, Rhs, Kind, On, GB> InternalJoinDsl - for BoxedSelectStatement<'a, ST, QS, DB, GB> + for BoxedSelectStatement<'a, ST, FromClause, DB, GB> where - BoxedSelectStatement<'a, ST, JoinOn, On>, DB, GB>: AsQuery, + QS: QuerySource, + Rhs: QuerySource, + JoinOn, On>: QuerySource, + BoxedSelectStatement<'a, ST, FromClause, On>>, DB, GB>: AsQuery, { - type Output = BoxedSelectStatement<'a, ST, JoinOn, On>, DB, GB>; + type Output = BoxedSelectStatement<'a, ST, FromClause, On>>, DB, GB>; fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { BoxedSelectStatement { select: self.select, - from: Join::new(self.from, rhs, kind).on(on), + from: FromClause::new(Join::new(self.from.source, rhs, kind).on(on)), distinct: self.distinct, where_clause: self.where_clause, order: self.order, @@ -191,12 +217,37 @@ where } impl<'a, ST, QS, DB, Selection, GB> SelectDsl - for BoxedSelectStatement<'a, ST, QS, DB, GB> + for BoxedSelectStatement<'a, ST, FromClause, DB, GB> where DB: Backend, + QS: QuerySource, Selection: SelectableExpression + QueryFragment + ValidGrouping + Send + 'a, { - type Output = BoxedSelectStatement<'a, Selection::SqlType, QS, DB, GB>; + type Output = BoxedSelectStatement<'a, Selection::SqlType, FromClause, DB, GB>; + + fn select(self, selection: Selection) -> Self::Output { + BoxedSelectStatement { + select: Box::new(selection), + from: self.from, + distinct: self.distinct, + where_clause: self.where_clause, + order: self.order, + limit_offset: self.limit_offset, + group_by: self.group_by, + having: self.having, + _marker: PhantomData, + } + } +} + +impl<'a, ST, DB, Selection, GB> SelectDsl + for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB> +where + DB: Backend, + Selection: + SelectableExpression + QueryFragment + ValidGrouping + Send + 'a, +{ + type Output = BoxedSelectStatement<'a, Selection::SqlType, NoFromClause, DB, GB>; fn select(self, selection: Selection) -> Self::Output { BoxedSelectStatement { @@ -214,8 +265,9 @@ where } impl<'a, ST, QS, DB, Predicate, GB> FilterDsl - for BoxedSelectStatement<'a, ST, QS, DB, GB> + for BoxedSelectStatement<'a, ST, FromClause, DB, GB> where + QS: QuerySource, BoxedWhereClause<'a, DB>: WhereAnd>, Predicate: AppearsOnTable + NonAggregate, Predicate::SqlType: BoolOrNullableBool, @@ -228,9 +280,25 @@ where } } +impl<'a, ST, DB, Predicate, GB> FilterDsl + for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB> +where + BoxedWhereClause<'a, DB>: WhereAnd>, + Predicate: AppearsOnTable + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = Self; + + fn filter(mut self, predicate: Predicate) -> Self::Output { + self.where_clause = self.where_clause.and(predicate); + self + } +} + impl<'a, ST, QS, DB, Predicate, GB> OrFilterDsl - for BoxedSelectStatement<'a, ST, QS, DB, GB> + for BoxedSelectStatement<'a, ST, FromClause, DB, GB> where + QS: QuerySource, BoxedWhereClause<'a, DB>: WhereOr>, Predicate: AppearsOnTable + NonAggregate, Predicate::SqlType: BoolOrNullableBool, @@ -243,6 +311,21 @@ where } } +impl<'a, ST, DB, Predicate, GB> OrFilterDsl + for BoxedSelectStatement<'a, ST, NoFromClause, DB, GB> +where + BoxedWhereClause<'a, DB>: WhereOr>, + Predicate: AppearsOnTable + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = Self; + + fn or_filter(mut self, predicate: Predicate) -> Self::Output { + self.where_clause = self.where_clause.or(predicate); + self + } +} + impl<'a, ST, QS, DB, GB> LimitDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> where DB: Backend, @@ -298,11 +381,11 @@ where } } -impl<'a, ST, QS, DB, Rhs> JoinTo for BoxedSelectStatement<'a, ST, QS, DB, ()> +impl<'a, ST, QS, DB, Rhs> JoinTo for BoxedSelectStatement<'a, ST, FromClause, DB, ()> where - QS: JoinTo, + QS: JoinTo + QuerySource, { - type FromClause = QS::FromClause; + type FromClause = >::FromClause; type OnClause = QS::OnClause; fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) { diff --git a/diesel/src/query_builder/select_statement/dsl_impls.rs b/diesel/src/query_builder/select_statement/dsl_impls.rs index 804f33998439..a19668367a25 100644 --- a/diesel/src/query_builder/select_statement/dsl_impls.rs +++ b/diesel/src/query_builder/select_statement/dsl_impls.rs @@ -7,6 +7,8 @@ use crate::expression::*; use crate::insertable::Insertable; use crate::query_builder::combination_clause::*; use crate::query_builder::distinct_clause::*; +use crate::query_builder::from_clause::AsQuerySource; +use crate::query_builder::from_clause::FromClause; use crate::query_builder::group_by_clause::*; use crate::query_builder::insert_statement::InsertFromSelect; use crate::query_builder::limit_clause::*; @@ -17,6 +19,7 @@ use crate::query_builder::order_clause::*; use crate::query_builder::select_clause::*; use crate::query_builder::update_statement::*; use crate::query_builder::where_clause::*; +use crate::query_builder::NoFromClause; use crate::query_builder::{ AsQuery, IntoBoxedClause, Query, QueryFragment, SelectQuery, SelectStatement, }; @@ -28,17 +31,86 @@ use crate::query_source::joins::{Join, JoinOn, JoinTo}; use crate::query_source::QuerySource; use crate::sql_types::{BigInt, BoolOrNullableBool}; +impl InternalJoinDsl + for SelectStatement, DefaultSelectClause>, D, W, O, LOf, G, H, LC> +where + F: QuerySource, + Rhs: QuerySource, + JoinOn, On>: QuerySource, + SelectStatement< + FromClause, On>>, + DefaultSelectClause, On>>>, + D, + W, + O, + LOf, + G, + H, + LC, + >: AsQuery, +{ + type Output = SelectStatement< + FromClause, On>>, + DefaultSelectClause, On>>>, + D, + W, + O, + LOf, + G, + H, + LC, + >; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + let from = FromClause::new(Join::new(self.from.source, rhs, kind).on(on)); + SelectStatement::new( + DefaultSelectClause::new(&from), + from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + impl InternalJoinDsl - for SelectStatement + for SelectStatement, SelectClause, D, W, O, LOf, G, H, LC> where - SelectStatement, On>, S, D, W, O, LOf, G, H, LC>: AsQuery, + F: QuerySource, + Rhs: QuerySource, + JoinOn, On>: QuerySource, + SelectStatement< + FromClause, On>>, + SelectClause, + D, + W, + O, + LOf, + G, + H, + LC, + >: AsQuery, { - type Output = SelectStatement, On>, S, D, W, O, LOf, G, H, LC>; + type Output = SelectStatement< + FromClause, On>>, + SelectClause, + D, + W, + O, + LOf, + G, + H, + LC, + >; fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { SelectStatement::new( self.select, - Join::new(self.from, rhs, kind).on(on), + FromClause::new(Join::new(self.from.source, rhs, kind).on(on)), self.distinct, self.where_clause, self.order, @@ -51,13 +123,38 @@ where } impl SelectDsl - for SelectStatement + for SelectStatement, S, D, W, O, LOf, G, H, LC> where G: ValidGroupByClause, + F: QuerySource, Selection: SelectableExpression + ValidGrouping, - SelectStatement, D, W, O, LOf, G, H, LC>: SelectQuery, + SelectStatement, SelectClause, D, W, O, LOf, G, H, LC>: SelectQuery, +{ + type Output = SelectStatement, SelectClause, D, W, O, LOf, G, H, LC>; + + fn select(self, selection: Selection) -> Self::Output { + SelectStatement::new( + SelectClause(selection), + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.having, + self.locking, + ) + } +} + +impl SelectDsl + for SelectStatement +where + G: ValidGroupByClause, + Selection: SelectableExpression + ValidGrouping, + SelectStatement, D, W, O, LOf, G, H, LC>: SelectQuery, { - type Output = SelectStatement, D, W, O, LOf, G, H, LC>; + type Output = SelectStatement, D, W, O, LOf, G, H, LC>; fn select(self, selection: Selection) -> Self::Output { SelectStatement::new( @@ -149,7 +246,8 @@ use crate::expression_methods::EqAll; use crate::query_builder::having_clause::{HavingClause, NoHavingClause}; use crate::query_source::Table; -impl FindDsl for SelectStatement +impl FindDsl + for SelectStatement, S, D, W, O, LOf, G, H, LC> where F: Table, F::PrimaryKey: EqAll, @@ -158,7 +256,7 @@ where type Output = Filter>::Output>; fn find(self, id: PK) -> Self::Output { - let primary_key = self.from.primary_key(); + let primary_key = self.from.source.primary_key(); FilterDsl::filter(self, primary_key.eq_all(id)) } } @@ -361,11 +459,12 @@ impl ModifyLockDsl } impl<'a, F, S, D, W, O, LOf, G, H, DB> BoxedDsl<'a, DB> - for SelectStatement + for SelectStatement, S, D, W, O, LOf, G, H> where Self: AsQuery, DB: Backend, - S: IntoBoxedSelectClause<'a, DB, F> + SelectClauseExpression, + F: QuerySource, + S: SelectClauseExpression> + QueryFragment + Send + 'a, S::Selection: ValidGrouping, D: QueryFragment + Send + 'a, W: Into>, @@ -374,7 +473,8 @@ where G: ValidGroupByClause + QueryFragment + Send + 'a, H: QueryFragment + Send + 'a, { - type Output = BoxedSelectStatement<'a, S::SqlType, F, DB, G::Expressions>; + type Output = + BoxedSelectStatement<'a, S::SelectClauseSqlType, FromClause, DB, G::Expressions>; fn internal_into_boxed(self) -> Self::Output { BoxedSelectStatement::new( @@ -390,9 +490,41 @@ where } } -impl HasTable for SelectStatement +impl<'a, S, D, W, O, LOf, G, H, DB> BoxedDsl<'a, DB> + for SelectStatement where - F: HasTable, + Self: AsQuery, + DB: Backend, + S: SelectClauseExpression + QueryFragment + Send + 'a, + S::Selection: ValidGrouping, + D: QueryFragment + Send + 'a, + W: Into>, + O: Into + Send + 'a>>>, + LOf: IntoBoxedClause<'a, DB, BoxedClause = BoxedLimitOffsetClause<'a, DB>>, + G: ValidGroupByClause + QueryFragment + Send + 'a, + H: QueryFragment + Send + 'a, +{ + type Output = + BoxedSelectStatement<'a, S::SelectClauseSqlType, NoFromClause, DB, G::Expressions>; + + fn internal_into_boxed(self) -> Self::Output { + BoxedSelectStatement::new_no_from_clause( + self.select, + self.from, + Box::new(self.distinct), + self.where_clause.into(), + self.order.into(), + self.limit_offset.into_boxed(), + self.group_by, + Box::new(self.having), + ) + } +} + +impl HasTable + for SelectStatement, S, D, W, O, LOf, G, H, LC> +where + F: HasTable + QuerySource, { type Table = F::Table; @@ -401,9 +533,11 @@ where } } -impl IntoUpdateTarget for SelectStatement +impl IntoUpdateTarget + for SelectStatement, DefaultSelectClause>, NoDistinctClause, W> where - SelectStatement: HasTable, + F: QuerySource, + Self: HasTable, W: ValidWhereClause, { type WhereClause = W; @@ -420,11 +554,11 @@ where // any other query methods where a join no longer has the same semantics as // joining on just the table? impl JoinTo - for SelectStatement + for SelectStatement, S, D, W, O, LOf, G, H, LC> where - F: JoinTo, + F: JoinTo + QuerySource, { - type FromClause = F::FromClause; + type FromClause = >::FromClause; type OnClause = F::OnClause; fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) { @@ -490,16 +624,26 @@ impl<'a, F, S, D, W, O, LOf, G, H> SelectNullableDsl } impl<'a, F, D, W, O, LOf, G, H> SelectNullableDsl - for SelectStatement + for SelectStatement, D, W, O, LOf, G, H> where - F: QuerySource, + F: AsQuerySource, { - type Output = - SelectStatement>, D, W, O, LOf, G, H>; + type Output = SelectStatement< + F, + SelectClause::DefaultSelection>>, + D, + W, + O, + LOf, + G, + H, + >; fn nullable(self) -> Self::Output { SelectStatement::new( - SelectClause(Nullable::new(self.from.default_selection())), + SelectClause(Nullable::new( + self.from.as_query_source().default_selection(), + )), self.from, self.distinct, self.where_clause, diff --git a/diesel/src/query_builder/select_statement/mod.rs b/diesel/src/query_builder/select_statement/mod.rs index dd8a96b251d3..bd6eb23fdda3 100644 --- a/diesel/src/query_builder/select_statement/mod.rs +++ b/diesel/src/query_builder/select_statement/mod.rs @@ -18,6 +18,8 @@ mod dsl_impls; pub use self::boxed::BoxedSelectStatement; use super::distinct_clause::NoDistinctClause; +use super::from_clause::AsQuerySource; +use super::from_clause::FromClause; use super::group_by_clause::*; use super::limit_clause::NoLimitClause; use super::locking_clause::NoLockingClause; @@ -25,6 +27,7 @@ use super::offset_clause::NoOffsetClause; use super::order_clause::NoOrderClause; use super::select_clause::*; use super::where_clause::*; +use super::NoFromClause; use super::{AstPass, Query, QueryFragment}; use crate::backend::Backend; use crate::expression::subselect::ValidSubselect; @@ -37,25 +40,12 @@ use crate::query_source::joins::{AppendSelection, Inner, Join}; use crate::query_source::*; use crate::result::QueryResult; -#[derive(Debug, Clone, Copy, QueryId)] -pub struct NoFromClause; - -impl QueryFragment - for NoFromClause -where - DB: Backend, -{ - fn walk_ast(&self, _pass: AstPass) -> QueryResult<()> { - Ok(()) - } -} - #[derive(Debug, Clone, Copy, QueryId)] #[doc(hidden)] #[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] pub struct SelectStatement< From, - Select = DefaultSelectClause, + Select = DefaultSelectClause, Distinct = NoDistinctClause, Where = NoWhereClause, Order = NoOrderClause, @@ -102,10 +92,11 @@ impl SelectStatement } } -impl SelectStatement { +impl SelectStatement> { pub fn simple(from: F) -> Self { + let from = FromClause::new(from); SelectStatement::new( - DefaultSelectClause, + DefaultSelectClause::new(&from), from, NoDistinctClause, NoWhereClause, @@ -143,9 +134,8 @@ impl QueryFragment for SelectStatement where DB: Backend, - S: SelectClauseQueryFragment, - F: QuerySource, - F::FromClause: QueryFragment, + S: QueryFragment, + F: QueryFragment, D: QueryFragment, W: QueryFragment, O: QueryFragment, @@ -154,12 +144,14 @@ where H: QueryFragment, LC: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("SELECT "); self.distinct.walk_ast(out.reborrow())?; - self.select.walk_ast(&self.from, out.reborrow())?; - out.push_sql(" FROM "); - self.from.from_clause().walk_ast(out.reborrow())?; + self.select.walk_ast(out.reborrow())?; + self.from.walk_ast(out.reborrow())?; self.where_clause.walk_ast(out.reborrow())?; self.group_by.walk_ast(out.reborrow())?; self.having.walk_ast(out.reborrow())?; @@ -170,40 +162,40 @@ where } } -impl QueryFragment +impl ValidSubselect + for SelectStatement, S, D, W, O, LOf, G, H, LC> +where + Self: SelectQuery, + F: QuerySource, + QS: QuerySource, + Join: QuerySource, + W: ValidWhereClause>>, +{ +} + +impl ValidSubselect for SelectStatement where - DB: Backend, - S: SelectClauseQueryFragment, - NoFromClause: QueryFragment, - D: QueryFragment, - W: QueryFragment, - O: QueryFragment, - LOf: QueryFragment, - G: QueryFragment, - H: QueryFragment, - LC: QueryFragment, + Self: SelectQuery, + W: ValidWhereClause, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { - out.push_sql("SELECT "); - self.distinct.walk_ast(out.reborrow())?; - self.select.walk_ast(&NoFromClause, out.reborrow())?; - self.from.walk_ast(out.reborrow())?; - self.where_clause.walk_ast(out.reborrow())?; - self.group_by.walk_ast(out.reborrow())?; - self.having.walk_ast(out.reborrow())?; - self.order.walk_ast(out.reborrow())?; - self.limit_offset.walk_ast(out.reborrow())?; - self.locking.walk_ast(out.reborrow())?; - Ok(()) - } } -impl ValidSubselect - for SelectStatement +impl ValidSubselect + for SelectStatement, S, D, W, O, LOf, G, H, LC> +where + Self: SelectQuery, + F: QuerySource, + W: ValidWhereClause>, +{ +} + +impl ValidSubselect + for SelectStatement where Self: SelectQuery, - W: ValidWhereClause>, + QS: QuerySource, + W: ValidWhereClause, { } @@ -211,35 +203,37 @@ where /// no other query methods have been called on it impl AppearsInFromClause for SelectStatement where - From: AppearsInFromClause, + From: AsQuerySource, + From::QuerySource: AppearsInFromClause + QuerySource, { - type Count = From::Count; + type Count = >::Count; } impl QuerySource for SelectStatement where - From: QuerySource, - From::DefaultSelection: SelectableExpression, + From: AsQuerySource, + ::DefaultSelection: SelectableExpression, { - type FromClause = From::FromClause; - type DefaultSelection = From::DefaultSelection; + type FromClause = ::FromClause; + type DefaultSelection = ::DefaultSelection; - fn from_clause(&self) -> Self::FromClause { - self.from.from_clause() + fn from_clause(&self) -> ::FromClause { + self.from.as_query_source().from_clause() } fn default_selection(&self) -> Self::DefaultSelection { - self.from.default_selection() + self.from.as_query_source().default_selection() } } impl AppendSelection for SelectStatement where - From: AppendSelection, + From: AsQuerySource, + From::QuerySource: AppendSelection, { - type Output = From::Output; + type Output = >::Output; fn append_selection(&self, selection: Selection) -> Self::Output { - self.from.append_selection(selection) + self.from.as_query_source().append_selection(selection) } } diff --git a/diesel/src/query_builder/sql_query.rs b/diesel/src/query_builder/sql_query.rs index c682bcd7c8f9..e2a6bfb1064c 100644 --- a/diesel/src/query_builder/sql_query.rs +++ b/diesel/src/query_builder/sql_query.rs @@ -102,7 +102,10 @@ where DB: Backend, Inner: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.unsafe_to_cache_prepared(); self.inner.walk_ast(out.reborrow())?; out.push_sql(&self.query); @@ -229,7 +232,10 @@ where Query: QueryFragment, Value: ToSql, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { self.query.walk_ast(out.reborrow())?; out.push_bind_param_value_only(&self.value)?; Ok(()) @@ -250,7 +256,25 @@ impl RunQueryDsl for UncheckedBind { query: Query, sql: String, - binds: Vec) -> QueryResult<()> + 'f>>, + binds: Vec + 'f>>, +} + +struct RawBind { + value: U, + p: PhantomData, +} + +impl QueryFragment for RawBind +where + DB: Backend + HasSqlType, + U: ToSql, +{ + fn walk_ast<'a, 'b>(&'a self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { + pass.push_bind_param_value_only(&self.value) + } } impl<'f, DB: Backend, Query> BoxedSqlQuery<'f, DB, Query> { @@ -269,9 +293,12 @@ impl<'f, DB: Backend, Query> BoxedSqlQuery<'f, DB, Query> { where DB: HasSqlType, Value: ToSql + 'f, + BindSt: 'f, { - self.binds - .push(Box::new(move |mut out| out.push_bind_param_value_only(&b))); + self.binds.push(Box::new(RawBind { + value: b, + p: PhantomData, + }) as Box<_>); self } @@ -289,13 +316,16 @@ where DB: Backend, Query: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.unsafe_to_cache_prepared(); self.query.walk_ast(out.reborrow())?; out.push_sql(&self.sql); for b in &self.binds { - b(out.reborrow())?; + b.walk_ast(out.reborrow())?; } Ok(()) } diff --git a/diesel/src/query_builder/update_statement/changeset.rs b/diesel/src/query_builder/update_statement/changeset.rs index 77c8c49c74d9..5dee070daa28 100644 --- a/diesel/src/query_builder/update_statement/changeset.rs +++ b/diesel/src/query_builder/update_statement/changeset.rs @@ -77,7 +77,10 @@ where T: Column, U: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_identifier(T::NAME)?; out.push_sql(" = "); QueryFragment::walk_ast(&self.expr, out) diff --git a/diesel/src/query_builder/update_statement/mod.rs b/diesel/src/query_builder/update_statement/mod.rs index 4090c1465ca9..55b2967538f8 100644 --- a/diesel/src/query_builder/update_statement/mod.rs +++ b/diesel/src/query_builder/update_statement/mod.rs @@ -11,17 +11,17 @@ use crate::expression::{ }; use crate::query_builder::returning_clause::*; use crate::query_builder::where_clause::*; -use crate::query_builder::*; use crate::query_dsl::methods::{BoxedDsl, FilterDsl}; use crate::query_dsl::RunQueryDsl; use crate::query_source::Table; use crate::result::Error::QueryBuilderError; use crate::result::QueryResult; +use crate::{query_builder::*, QuerySource}; -impl UpdateStatement { +impl UpdateStatement { pub(crate) fn new(target: UpdateTarget) -> Self { UpdateStatement { - table: target.table, + from_clause: target.table.from_clause(), where_clause: target.where_clause, values: SetNotCalled, returning: NoReturningClause, @@ -40,7 +40,7 @@ impl UpdateStatement { UpdateStatement: AsQuery, { UpdateStatement { - table: self.table, + from_clause: self.from_clause, where_clause: self.where_clause, values: values.as_changeset(), returning: self.returning, @@ -48,15 +48,15 @@ impl UpdateStatement { } } -#[derive(Debug, Copy, Clone)] +#[derive(Clone, Debug)] #[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] /// Represents a complete `UPDATE` statement. /// /// See [`update`](crate::update()) for usage examples, or [the update /// guide](https://diesel.rs/guides/all-about-updates/) for a more exhaustive /// set of examples. -pub struct UpdateStatement { - table: T, +pub struct UpdateStatement { + from_clause: T::FromClause, where_clause: U, values: V, returning: Ret, @@ -66,7 +66,7 @@ pub struct UpdateStatement { pub type BoxedUpdateStatement<'a, DB, T, V = SetNotCalled, Ret = NoReturningClause> = UpdateStatement, V, Ret>; -impl UpdateStatement { +impl UpdateStatement { /// Adds the given predicate to the `WHERE` clause of the statement being /// constructed. /// @@ -156,6 +156,7 @@ impl UpdateStatement { impl FilterDsl for UpdateStatement where + T: QuerySource, U: WhereAnd, Predicate: AppearsOnTable, { @@ -163,7 +164,7 @@ where fn filter(self, predicate: Predicate) -> Self::Output { UpdateStatement { - table: self.table, + from_clause: self.from_clause, where_clause: self.where_clause.and(predicate), values: self.values, returning: self.returning, @@ -173,13 +174,14 @@ where impl<'a, T, U, V, Ret, DB> BoxedDsl<'a, DB> for UpdateStatement where + T: QuerySource, U: Into>, { type Output = BoxedUpdateStatement<'a, DB, T, V, Ret>; fn internal_into_boxed(self) -> Self::Output { UpdateStatement { - table: self.table, + from_clause: self.from_clause, where_clause: self.where_clause.into(), values: self.values, returning: self.returning, @@ -196,7 +198,10 @@ where V: QueryFragment, Ret: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { if self.values.is_noop()? { return Err(QueryBuilderError( "There are no changes to save. This query cannot be built".into(), @@ -205,7 +210,7 @@ where out.unsafe_to_cache_prepared(); out.push_sql("UPDATE "); - self.table.from_clause().walk_ast(out.reborrow())?; + self.from_clause.walk_ast(out.reborrow())?; out.push_sql(" SET "); self.values.walk_ast(out.reborrow())?; self.where_clause.walk_ast(out.reborrow())?; @@ -214,7 +219,10 @@ where } } -impl QueryId for UpdateStatement { +impl QueryId for UpdateStatement +where + T: QuerySource, +{ type QueryId = (); const HAS_STATIC_QUERY_ID: bool = false; @@ -245,9 +253,9 @@ where type SqlType = Ret::SqlType; } -impl RunQueryDsl for UpdateStatement {} +impl RunQueryDsl for UpdateStatement {} -impl UpdateStatement { +impl UpdateStatement { /// Specify what expression is returned after execution of the `update`. /// # Examples /// @@ -275,7 +283,7 @@ impl UpdateStatement { UpdateStatement>: Query, { UpdateStatement { - table: self.table, + from_clause: self.from_clause, where_clause: self.where_clause, values: self.values, returning: ReturningClause(returns), diff --git a/diesel/src/query_builder/upsert/into_conflict_clause.rs b/diesel/src/query_builder/upsert/into_conflict_clause.rs index 62c083423b80..9c6bcede45bc 100644 --- a/diesel/src/query_builder/upsert/into_conflict_clause.rs +++ b/diesel/src/query_builder/upsert/into_conflict_clause.rs @@ -28,7 +28,10 @@ impl QueryFragment for OnConflictSelectWrapper where S: QueryFragment, { - fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, out: AstPass<'_, 'b, crate::pg::Pg>) -> QueryResult<()> + where + 'a: 'b, + { self.0.walk_ast(out) } } @@ -42,7 +45,10 @@ where SelectStatement, O, LOf, G, H, LC>: QueryFragment, { - fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, out: AstPass<'_, 'b, crate::sqlite::Sqlite>) -> QueryResult<()> + where + 'a: 'b, + { self.0.walk_ast(out) } } @@ -53,10 +59,12 @@ impl<'a, ST, QS, GB> QueryFragment where BoxedSelectStatement<'a, ST, QS, crate::sqlite::Sqlite, GB>: QueryFragment, - QS: crate::query_source::QuerySource, - QS::FromClause: QueryFragment, + QS: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'b, 'c>(&'b self, pass: AstPass<'_, 'c, crate::sqlite::Sqlite>) -> QueryResult<()> + where + 'b: 'c, + { // https://www.sqlite.org/lang_UPSERT.html (Parsing Ambiguity) self.0.build_query(pass, |where_clause, mut pass| { match where_clause { diff --git a/diesel/src/query_builder/upsert/on_conflict_actions.rs b/diesel/src/query_builder/upsert/on_conflict_actions.rs index d1e2771603dd..b1a8107d38b0 100644 --- a/diesel/src/query_builder/upsert/on_conflict_actions.rs +++ b/diesel/src/query_builder/upsert/on_conflict_actions.rs @@ -13,7 +13,10 @@ where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } @@ -23,7 +26,10 @@ where DB: Backend, T: sql_dialect::on_conflict_clause::SupportsOnConflictClause, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql(" DO NOTHING"); Ok(()) } @@ -46,7 +52,10 @@ where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } @@ -57,7 +66,10 @@ where SP: sql_dialect::on_conflict_clause::SupportsOnConflictClause, T: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.unsafe_to_cache_prepared(); if self.changeset.is_noop()? { out.push_sql(" DO NOTHING"); @@ -84,7 +96,10 @@ where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } @@ -95,7 +110,10 @@ where SP: sql_dialect::on_conflict_clause::SupportsOnConflictClause, T: Column, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql("excluded."); out.push_identifier(T::NAME)?; Ok(()) diff --git a/diesel/src/query_builder/upsert/on_conflict_clause.rs b/diesel/src/query_builder/upsert/on_conflict_clause.rs index d1c44a055f90..d056d00ec487 100644 --- a/diesel/src/query_builder/upsert/on_conflict_clause.rs +++ b/diesel/src/query_builder/upsert/on_conflict_clause.rs @@ -56,7 +56,10 @@ where Target: QueryFragment, Action: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { self.values.walk_ast(out.reborrow())?; out.push_sql(" ON CONFLICT"); self.target.walk_ast(out.reborrow())?; diff --git a/diesel/src/query_builder/upsert/on_conflict_target.rs b/diesel/src/query_builder/upsert/on_conflict_target.rs index 651a9e9ed8b0..1bd1179dc684 100644 --- a/diesel/src/query_builder/upsert/on_conflict_target.rs +++ b/diesel/src/query_builder/upsert/on_conflict_target.rs @@ -16,7 +16,10 @@ where DB: Backend, DB::OnConflictClause: sql_dialect::on_conflict_clause::SupportsOnConflictClause, { - fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { Ok(()) } } @@ -32,7 +35,10 @@ where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } @@ -43,7 +49,10 @@ where SP: sql_dialect::on_conflict_clause::SupportsOnConflictClause, T: Column, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql(" ("); out.push_identifier(T::NAME)?; out.push_sql(")"); @@ -59,7 +68,10 @@ where SP: sql_dialect::on_conflict_clause::SupportsOnConflictClause, SqlLiteral: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql(" "); self.0.walk_ast(out.reborrow())?; Ok(()) @@ -74,7 +86,10 @@ where SP: sql_dialect::on_conflict_clause::SupportsOnConflictClause, T: Column, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql(" ("); out.push_identifier(T::NAME)?; out.push_sql(")"); @@ -97,7 +112,9 @@ macro_rules! on_conflict_tuples { _T: Column, $($T: Column,)* { - fn walk_ast(&self, mut out: AstPass<_DB>) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, _DB>) -> QueryResult<()> + where 'a: 'b + { out.push_sql(" ("); out.push_identifier(_T::NAME)?; $( diff --git a/diesel/src/query_builder/upsert/on_conflict_target_decorations.rs b/diesel/src/query_builder/upsert/on_conflict_target_decorations.rs index 3c34b58f3756..6fdf01e0731c 100644 --- a/diesel/src/query_builder/upsert/on_conflict_target_decorations.rs +++ b/diesel/src/query_builder/upsert/on_conflict_target_decorations.rs @@ -62,7 +62,10 @@ where DB: Backend, Self: QueryFragment, { - fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { >::walk_ast(self, pass) } } diff --git a/diesel/src/query_builder/where_clause.rs b/diesel/src/query_builder/where_clause.rs index c43a2c38e610..507f2a4b5f0b 100644 --- a/diesel/src/query_builder/where_clause.rs +++ b/diesel/src/query_builder/where_clause.rs @@ -1,3 +1,4 @@ +use super::from_clause::AsQuerySource; use super::*; use crate::backend::Backend; use crate::expression::grouped::Grouped; @@ -31,7 +32,10 @@ pub trait WhereOr { pub struct NoWhereClause; impl QueryFragment for NoWhereClause { - fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, _: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { Ok(()) } } @@ -75,7 +79,10 @@ where DB: Backend, Expr: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql(" WHERE "); self.0.walk_ast(out.reborrow())?; Ok(()) @@ -126,7 +133,17 @@ pub trait ValidWhereClause {} impl ValidWhereClause for NoWhereClause {} -impl ValidWhereClause for WhereClause where Expr: AppearsOnTable {} +impl ValidWhereClause for WhereClause +where + Expr: AppearsOnTable, + QS: AsQuerySource, +{ +} + +impl ValidWhereClause for WhereClause where + Expr: AppearsOnTable +{ +} #[allow(missing_debug_implementations)] // We can't... pub enum BoxedWhereClause<'a, DB> { @@ -138,7 +155,10 @@ impl<'a, DB> QueryFragment for BoxedWhereClause<'a, DB> where DB: Backend, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'b, 'c>(&'b self, mut out: AstPass<'_, 'c, DB>) -> QueryResult<()> + where + 'b: 'c, + { match *self { BoxedWhereClause::Where(ref where_clause) => { out.push_sql(" WHERE "); diff --git a/diesel/src/query_dsl/boxed_dsl.rs b/diesel/src/query_dsl/boxed_dsl.rs index 75a5c44d3083..072e7a3f7425 100644 --- a/diesel/src/query_dsl/boxed_dsl.rs +++ b/diesel/src/query_dsl/boxed_dsl.rs @@ -2,6 +2,7 @@ use crate::dsl; use crate::expression::TypedExpressionType; use crate::expression::ValidGrouping; use crate::query_builder::AsQuery; +use crate::query_builder::FromClause; use crate::query_builder::SelectStatement; use crate::query_source::Table; use crate::Expression; @@ -23,12 +24,12 @@ pub trait BoxedDsl<'a, DB> { impl<'a, T, DB> BoxedDsl<'a, DB> for T where - T: Table + AsQuery>, - SelectStatement: BoxedDsl<'a, DB>, + T: Table + AsQuery>>, + SelectStatement>: BoxedDsl<'a, DB>, T::DefaultSelection: Expression + ValidGrouping<()>, T::SqlType: TypedExpressionType, { - type Output = dsl::IntoBoxed<'a, SelectStatement, DB>; + type Output = dsl::IntoBoxed<'a, SelectStatement>, DB>; fn internal_into_boxed(self) -> Self::Output { self.as_query().internal_into_boxed() diff --git a/diesel/src/query_dsl/distinct_dsl.rs b/diesel/src/query_dsl/distinct_dsl.rs index bde855fb5582..3b0b90a112c3 100644 --- a/diesel/src/query_dsl/distinct_dsl.rs +++ b/diesel/src/query_dsl/distinct_dsl.rs @@ -3,6 +3,7 @@ use crate::dsl; use crate::expression::SelectableExpression; use crate::expression::TypedExpressionType; use crate::expression::ValidGrouping; +use crate::query_builder::FromClause; use crate::query_builder::{AsQuery, SelectStatement}; use crate::query_source::Table; use crate::Expression; @@ -24,13 +25,13 @@ pub trait DistinctDsl { impl DistinctDsl for T where - T: Table + AsQuery>, + T: Table + AsQuery>>, T::DefaultSelection: Expression + ValidGrouping<()>, T::SqlType: TypedExpressionType, { - type Output = dsl::Distinct>; + type Output = dsl::Distinct>>; - fn distinct(self) -> dsl::Distinct> { + fn distinct(self) -> dsl::Distinct>> { self.as_query().distinct() } } @@ -55,12 +56,13 @@ pub trait DistinctOnDsl { impl DistinctOnDsl for T where Selection: SelectableExpression, - T: Table + AsQuery>, - SelectStatement: DistinctOnDsl, + Selection::SqlType: crate::sql_types::SingleValue, + T: Table + AsQuery>>, + SelectStatement>: DistinctOnDsl, T::DefaultSelection: Expression + ValidGrouping<()>, T::SqlType: TypedExpressionType, { - type Output = dsl::DistinctOn, Selection>; + type Output = dsl::DistinctOn>, Selection>; fn distinct_on(self, selection: Selection) -> dsl::DistinctOn { self.as_query().distinct_on(selection) diff --git a/diesel/src/query_dsl/group_by_dsl.rs b/diesel/src/query_dsl/group_by_dsl.rs index eab72ed2f268..1c62c6885974 100644 --- a/diesel/src/query_dsl/group_by_dsl.rs +++ b/diesel/src/query_dsl/group_by_dsl.rs @@ -2,6 +2,7 @@ use crate::dsl; use crate::expression::Expression; use crate::expression::TypedExpressionType; use crate::expression::ValidGrouping; +use crate::query_builder::FromClause; use crate::query_builder::{AsQuery, SelectStatement}; use crate::query_source::Table; @@ -23,11 +24,11 @@ pub trait GroupByDsl { impl GroupByDsl for T where Expr: Expression, - T: Table + AsQuery>, + T: Table + AsQuery>>, T::DefaultSelection: Expression + ValidGrouping<()>, T::SqlType: TypedExpressionType, { - type Output = dsl::GroupBy, Expr>; + type Output = dsl::GroupBy>, Expr>; fn group_by(self, expr: Expr) -> dsl::GroupBy { self.as_query().group_by(expr) diff --git a/diesel/src/query_dsl/locking_dsl.rs b/diesel/src/query_dsl/locking_dsl.rs index 8f97beb2a882..6ad6d93311ee 100644 --- a/diesel/src/query_dsl/locking_dsl.rs +++ b/diesel/src/query_dsl/locking_dsl.rs @@ -1,6 +1,7 @@ use crate::expression::TypedExpressionType; use crate::expression::ValidGrouping; use crate::query_builder::AsQuery; +use crate::query_builder::FromClause; use crate::query_builder::SelectStatement; use crate::query_source::Table; use crate::Expression; @@ -25,11 +26,11 @@ pub trait LockingDsl { impl LockingDsl for T where - T: Table + AsQuery>, + T: Table + AsQuery>>, T::DefaultSelection: Expression + ValidGrouping<()>, T::SqlType: TypedExpressionType, { - type Output = as LockingDsl>::Output; + type Output = > as LockingDsl>::Output; fn with_lock(self, lock: Lock) -> Self::Output { self.as_query().with_lock(lock) diff --git a/diesel/src/query_dsl/mod.rs b/diesel/src/query_dsl/mod.rs index fd8696d6378e..62b25632cd7e 100644 --- a/diesel/src/query_dsl/mod.rs +++ b/diesel/src/query_dsl/mod.rs @@ -1247,6 +1247,8 @@ pub trait QueryDsl: Sized { /// } /// } /// + /// allow_tables_to_appear_in_same_query!(users, posts); + /// /// # let _: Vec<(i32, Option)> = /// posts::table.filter( /// posts::by_user.eq_any(users::table.select(users::name).nullable()) diff --git a/diesel/src/query_dsl/positional_order_dsl.rs b/diesel/src/query_dsl/positional_order_dsl.rs index 15c1364b0f7f..ae288da366b4 100644 --- a/diesel/src/query_dsl/positional_order_dsl.rs +++ b/diesel/src/query_dsl/positional_order_dsl.rs @@ -44,13 +44,15 @@ impl QueryFragment for PositionalOrderClause where DB: Backend, Source: QueryFragment, - Expr: Order, - Expr::Fragment: QueryFragment, + Expr: QueryFragment, { - fn walk_ast(&self, mut pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { self.source.walk_ast(pass.reborrow())?; pass.push_sql(" ORDER BY "); - self.expr.into_fragment().walk_ast(pass) + self.expr.walk_ast(pass) } } @@ -58,7 +60,10 @@ where pub struct OrderColumn(u32); impl QueryFragment for OrderColumn { - fn walk_ast(&self, mut pass: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut pass: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { pass.push_sql(&self.0.to_string()); Ok(()) } diff --git a/diesel/src/query_source/joins.rs b/diesel/src/query_source/joins.rs index 36c3a30997e7..02598bbcac56 100644 --- a/diesel/src/query_source/joins.rs +++ b/diesel/src/query_source/joins.rs @@ -10,14 +10,69 @@ use crate::result::QueryResult; use crate::sql_types::BoolOrNullableBool; use crate::util::TupleAppend; -#[derive(Debug, Clone, Copy, QueryId)] /// A query source representing the join between two tables -pub struct Join { - left: Left, - right: Right, +pub struct Join { + left: FromClause, + right: FromClause, kind: Kind, } +impl Clone for Join +where + Left: QuerySource, + FromClause: Clone, + Right: QuerySource, + FromClause: Clone, + Kind: Clone, +{ + fn clone(&self) -> Self { + Self { + left: self.left.clone(), + right: self.right.clone(), + kind: self.kind.clone(), + } + } +} + +impl Copy for Join +where + Left: QuerySource, + FromClause: Copy, + Right: QuerySource, + FromClause: Copy, + Kind: Copy, +{ +} + +impl std::fmt::Debug for Join +where + Left: QuerySource, + FromClause: std::fmt::Debug, + Right: QuerySource, + FromClause: std::fmt::Debug, + Kind: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Join") + .field("left", &self.left) + .field("right", &self.right) + .field("kind", &self.kind) + .finish() + } +} + +impl QueryId for Join +where + Left: QueryId + QuerySource + 'static, + Right: QueryId + QuerySource + 'static, + Kind: QueryId, +{ + type QueryId = Join; + + const HAS_STATIC_QUERY_ID: bool = + Left::HAS_STATIC_QUERY_ID && Right::HAS_STATIC_QUERY_ID && Kind::HAS_STATIC_QUERY_ID; +} + #[derive(Debug, Clone, Copy, QueryId)] #[doc(hidden)] /// A query source representing the join between two tables with an explicit @@ -28,12 +83,16 @@ pub struct JoinOn { on: On, } -impl Join { +impl Join +where + Left: QuerySource, + Right: QuerySource, +{ pub fn new(left: Left, right: Right, kind: Kind) -> Self { Join { - left: left, - right: right, - kind: kind, + left: FromClause::new(left), + right: FromClause::new(right), + kind, } } @@ -58,7 +117,9 @@ where } fn default_selection(&self) -> Self::DefaultSelection { - self.left.append_selection(self.right.default_selection()) + self.left + .source + .append_selection(self.right.source.default_selection()) } } @@ -78,7 +139,17 @@ where fn default_selection(&self) -> Self::DefaultSelection { self.left - .append_selection(self.right.default_selection().nullable()) + .source + .append_selection(self.right.source.default_selection().nullable()) + } +} + +#[derive(Debug, Clone, Copy)] +pub struct OnKeyword; + +impl nodes::MiddleFragment for OnKeyword { + fn push_sql(&self, mut pass: AstPass) { + pass.push_sql(" ON "); } } @@ -89,14 +160,14 @@ where On::SqlType: BoolOrNullableBool, Join::DefaultSelection: SelectableExpression, { - type FromClause = Grouped>; + type FromClause = Grouped>; type DefaultSelection = Join::DefaultSelection; fn from_clause(&self) -> Self::FromClause { Grouped(nodes::InfixNode::new( self.join.from_clause(), self.on.clone(), - " ON ", + OnKeyword, )) } @@ -114,11 +185,14 @@ where Right::FromClause: QueryFragment, Kind: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { - self.left.from_clause().walk_ast(out.reborrow())?; + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { + self.left.from_clause.walk_ast(out.reborrow())?; self.kind.walk_ast(out.reborrow())?; out.push_sql(" JOIN "); - self.right.from_clause().walk_ast(out.reborrow())?; + self.right.from_clause.walk_ast(out.reborrow())?; Ok(()) } } @@ -166,6 +240,8 @@ impl AppendSelection for T { impl AppendSelection for Join where + Left: QuerySource, + Mid: QuerySource, Self: QuerySource, ::DefaultSelection: TupleAppend, { @@ -192,7 +268,10 @@ where pub struct Inner; impl QueryFragment for Inner { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql(" INNER"); Ok(()) } @@ -203,7 +282,10 @@ impl QueryFragment for Inner { pub struct LeftOuter; impl QueryFragment for LeftOuter { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.push_sql(" LEFT OUTER"); Ok(()) } @@ -211,9 +293,10 @@ impl QueryFragment for LeftOuter { impl JoinTo for Join where - Left: JoinTo, + Left: JoinTo + QuerySource, + Mid: QuerySource, { - type FromClause = Left::FromClause; + type FromClause = >::FromClause; type OnClause = Left::OnClause; fn join_target(rhs: Right) -> (Self::FromClause, Self::OnClause) { @@ -235,8 +318,8 @@ where impl AppearsInFromClause for Join where - Left: AppearsInFromClause, - Right: AppearsInFromClause, + Left: AppearsInFromClause + QuerySource, + Right: AppearsInFromClause + QuerySource, Left::Count: Plus, { type Count = >::Output; @@ -316,8 +399,10 @@ pub trait ToInnerJoin { impl ToInnerJoin for Join where - Left: ToInnerJoin, - Right: ToInnerJoin, + Left: ToInnerJoin + QuerySource, + Left::InnerJoin: QuerySource, + Right: ToInnerJoin + QuerySource, + Right::InnerJoin: QuerySource, { type InnerJoin = Join; } @@ -329,11 +414,12 @@ where type InnerJoin = JoinOn; } -impl ToInnerJoin for SelectStatement +impl ToInnerJoin for SelectStatement> where - From: ToInnerJoin, + From: ToInnerJoin + QuerySource, + From::InnerJoin: QuerySource, { - type InnerJoin = SelectStatement; + type InnerJoin = SelectStatement>; } impl ToInnerJoin for T { diff --git a/diesel/src/serialize.rs b/diesel/src/serialize.rs index 9111f02d0521..4f197e1ea566 100644 --- a/diesel/src/serialize.rs +++ b/diesel/src/serialize.rs @@ -3,11 +3,11 @@ use std::error::Error; use std::fmt; use std::io::{self, Write}; -use std::ops::{Deref, DerefMut}; use std::result; -use crate::backend::Backend; -use crate::sql_types::TypeMetadata; +use crate::backend::{Backend, HasBindCollector}; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::query_builder::BindCollector; #[cfg(feature = "postgres_backend")] pub use crate::pg::serialize::*; @@ -31,26 +31,31 @@ pub enum IsNull { /// Wraps a buffer to be written by `ToSql` with additional backend specific /// utilities. -pub struct Output<'a, T, DB> +pub struct Output<'a, 'b, DB> where - DB: TypeMetadata, + DB: Backend, DB::MetadataLookup: 'a, { - out: T, - metadata_lookup: Option<&'a mut DB::MetadataLookup>, + out: as BindCollector<'a, DB>>::Buffer, + metadata_lookup: Option<&'b mut DB::MetadataLookup>, } -impl<'a, T, DB: TypeMetadata> Output<'a, T, DB> { +impl<'a, 'b, DB: Backend> Output<'a, 'b, DB> { /// Construct a new `Output` - pub fn new(out: T, metadata_lookup: &'a mut DB::MetadataLookup) -> Self { + pub fn new( + out: as BindCollector<'a, DB>>::Buffer, + metadata_lookup: &'b mut DB::MetadataLookup, + ) -> Self { Output { out, metadata_lookup: Some(metadata_lookup), } } - /// Return the raw buffer this type is wrapping - pub fn into_inner(self) -> T { + #[cfg(feature = "sqlite")] + pub(crate) fn into_inner( + self, + ) -> as BindCollector<'a, DB>>::Buffer { self.out } @@ -61,19 +66,64 @@ impl<'a, T, DB: TypeMetadata> Output<'a, T, DB> { } } +#[cfg(feature = "sqlite")] +impl<'a, 'b> Output<'a, 'b, crate::sqlite::Sqlite> { + pub(crate) fn set_small_int(&mut self, i: i16) { + self.out = crate::sqlite::query_builder::SqliteBindValue::SmallInt(i); + } + + pub(crate) fn set_int(&mut self, i: i32) { + self.out = crate::sqlite::query_builder::SqliteBindValue::Integer(i); + } + + pub(crate) fn set_big_int(&mut self, i: i64) { + self.out = crate::sqlite::query_builder::SqliteBindValue::BigInt(i); + } + + pub(crate) fn set_float(&mut self, i: f32) { + self.out = crate::sqlite::query_builder::SqliteBindValue::Float(i); + } + + pub(crate) fn set_double(&mut self, i: f64) { + self.out = crate::sqlite::query_builder::SqliteBindValue::Double(i); + } + + pub(crate) fn set_borrowed_string(&mut self, s: &'a str) { + self.out = crate::sqlite::query_builder::SqliteBindValue::BorrowedString(s); + } + + pub(crate) fn set_borrowed_binary(&mut self, s: &'a [u8]) { + self.out = crate::sqlite::query_builder::SqliteBindValue::BorrowedBinary(s); + } + + // this can be unused depending on the enabled features + #[allow(dead_code)] + pub(crate) fn set_owned_string(&mut self, s: String) { + self.out = crate::sqlite::query_builder::SqliteBindValue::String(s.into_boxed_str()); + } + + // This can be unused depending on the enabled features + #[allow(dead_code)] + pub(crate) fn set_owned_binary(&mut self, b: Vec) { + self.out = crate::sqlite::query_builder::SqliteBindValue::Binary(b.into_boxed_slice()); + } +} + #[cfg(test)] -impl Output<'static, Vec, DB> { +impl<'a, DB: Backend> Output<'a, 'static, DB> { /// Returns a `Output` suitable for testing `ToSql` implementations. /// Unsafe to use for testing types which perform dynamic metadata lookup. - pub fn test() -> Self { + pub fn test( + buffer: as BindCollector<'a, DB>>::Buffer, + ) -> Self { Self { - out: Vec::new(), + out: buffer, metadata_lookup: None, } } } -impl<'a, T: Write, DB: TypeMetadata> Write for Output<'a, T, DB> { +impl<'a, 'b, DB: Backend>> Write for Output<'a, 'b, DB> { fn write(&mut self, buf: &[u8]) -> io::Result { self.out.write(buf) } @@ -91,34 +141,34 @@ impl<'a, T: Write, DB: TypeMetadata> Write for Output<'a, T, DB> { } } -impl<'a, T, DB: TypeMetadata> Deref for Output<'a, T, DB> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.out - } -} - -impl<'a, T, DB: TypeMetadata> DerefMut for Output<'a, T, DB> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.out - } -} - -impl<'a, T, U, DB> PartialEq for Output<'a, T, DB> -where - DB: TypeMetadata, - T: PartialEq, -{ - fn eq(&self, rhs: &U) -> bool { - self.out == *rhs +impl<'a, 'b, DB: Backend>> Output<'a, 'b, DB> { + /// Call this method whenever you pass an instance of `Output` by value. + /// + /// Effectively copies `self`, with a narrower lifetime. When passing a + /// reference or a mutable reference, this is normally done by rust + /// implicitly. This is why you can pass `&mut Foo` to multiple functions, + /// even though mutable references are not `Copy`. However, this is only + /// done implicitly for references. For structs with lifetimes it must be + /// done explicitly. This method matches the semantics of what Rust would do + /// implicitly if you were passing a mutable reference + pub fn reborrow<'c>(&'c mut self) -> Output<'c, 'c, DB> + where + 'a: 'c, + { + Output { + out: RawBytesBindCollector::::reborrow_buffer(self.out), + metadata_lookup: match &mut self.metadata_lookup { + None => None, + Some(m) => Some(&mut **m), + }, + } } } -impl<'a, T, DB> fmt::Debug for Output<'a, T, DB> +impl<'a, 'b, DB> fmt::Debug for Output<'a, 'b, DB> where - T: fmt::Debug, - DB: TypeMetadata, + <>::BindCollector as BindCollector<'a, DB>>::Buffer: fmt::Debug, + DB: Backend, { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.out.fmt(f) @@ -175,14 +225,17 @@ where /// DB: Backend, /// i32: ToSql, /// { -/// fn to_sql(&self, out: &mut Output) -> serialize::Result { -/// (*self as i32).to_sql(out) +/// fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, DB>) -> serialize::Result { +/// match self { +/// MyEnum::A => 1.to_sql(out), +/// MyEnum::B => 2.to_sql(out), +/// } /// } /// } /// ``` pub trait ToSql: fmt::Debug { /// See the trait documentation. - fn to_sql(&self, out: &mut Output) -> Result; + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, DB>) -> Result; } impl<'a, A, T, DB> ToSql for &'a T @@ -190,7 +243,10 @@ where DB: Backend, T: ToSql + ?Sized, { - fn to_sql(&self, out: &mut Output) -> Result { + fn to_sql<'b, 'c, 'd>(&'b self, out: &mut Output<'c, 'd, DB>) -> Result + where + 'b: 'c, + { (*self).to_sql(out) } } diff --git a/diesel/src/sqlite/backend.rs b/diesel/src/sqlite/backend.rs index 66724a0e46d2..eda70c111364 100644 --- a/diesel/src/sqlite/backend.rs +++ b/diesel/src/sqlite/backend.rs @@ -1,10 +1,8 @@ //! The SQLite backend -use byteorder::NativeEndian; use super::connection::SqliteValue; -use super::query_builder::SqliteQueryBuilder; +use super::query_builder::{SqliteBindCollector, SqliteQueryBuilder}; use crate::backend::*; -use crate::query_builder::bind_collector::RawBytesBindCollector; use crate::sql_types::TypeMetadata; /// The SQLite backend @@ -40,8 +38,10 @@ pub enum SqliteType { impl Backend for Sqlite { type QueryBuilder = SqliteQueryBuilder; - type BindCollector = RawBytesBindCollector; - type ByteOrder = NativeEndian; +} + +impl<'a> HasBindCollector<'a> for Sqlite { + type BindCollector = SqliteBindCollector<'a>; } impl<'a> HasRawValue<'a> for Sqlite { diff --git a/diesel/src/sqlite/connection/functions.rs b/diesel/src/sqlite/connection/functions.rs index d2fe1eb59775..f125e279dbe0 100644 --- a/diesel/src/sqlite/connection/functions.rs +++ b/diesel/src/sqlite/connection/functions.rs @@ -2,7 +2,6 @@ extern crate libsqlite3_sys as ffi; use super::raw::RawConnection; use super::row::PrivateSqliteRow; -use super::serialized_value::SerializedValue; use super::{Sqlite, SqliteAggregateFunction}; use crate::deserialize::{FromSqlRow, StaticallySizedRow}; use crate::result::{DatabaseErrorKind, Error, QueryResult}; @@ -10,6 +9,7 @@ use crate::row::{Field, PartialRow, Row, RowGatWorkaround, RowIndex}; use crate::serialize::{IsNull, Output, ToSql}; use crate::sql_types::HasSqlType; use crate::sqlite::connection::sqlite_value::OwnedSqliteValue; +use crate::sqlite::query_builder::SqliteBindValue; use crate::sqlite::SqliteValue; use std::cell::{Ref, RefCell}; use std::marker::PhantomData; @@ -40,9 +40,7 @@ where conn.register_sql_function(fn_name, fields_needed, deterministic, move |conn, args| { let args = build_sql_function_args::(args)?; - let result = f(conn, args); - - process_sql_function_result::(result) + Ok(f(conn, args)) })?; Ok(()) } @@ -58,10 +56,7 @@ where Ret: ToSql, Sqlite: HasSqlType, { - conn.register_sql_function(fn_name, 0, deterministic, move |_, _| { - let result = f(); - process_sql_function_result::(result) - })?; + conn.register_sql_function(fn_name, 0, deterministic, move |_, _| Ok(f()))?; Ok(()) } @@ -102,26 +97,21 @@ where } pub(crate) fn process_sql_function_result( - result: Ret, -) -> QueryResult + result: &'_ Ret, +) -> QueryResult> where Ret: ToSql, Sqlite: HasSqlType, { let mut metadata_lookup = (); - let mut buf = Output::new(Vec::new(), &mut metadata_lookup); + let mut buf = Output::new(SqliteBindValue::Null, &mut metadata_lookup); let is_null = result.to_sql(&mut buf).map_err(Error::SerializationError)?; - let bytes = if let IsNull::Yes = is_null { - None + if let IsNull::Yes = is_null { + Ok(SqliteBindValue::Null) } else { - Some(buf.into_inner()) - }; - - Ok(SerializedValue { - ty: Sqlite::metadata(&mut ()), - data: bytes, - }) + Ok(buf.into_inner()) + } } struct FunctionRow<'a> { diff --git a/diesel/src/sqlite/connection/mod.rs b/diesel/src/sqlite/connection/mod.rs index 195de331eeec..0c479433367b 100644 --- a/diesel/src/sqlite/connection/mod.rs +++ b/diesel/src/sqlite/connection/mod.rs @@ -4,7 +4,6 @@ mod functions; #[doc(hidden)] pub mod raw; mod row; -mod serialized_value; mod sqlite_value; mod statement_iterator; mod stmt; @@ -20,7 +19,6 @@ use super::SqliteAggregateFunction; use crate::connection::*; use crate::deserialize::{FromSqlRow, StaticallySizedRow}; use crate::expression::QueryMetadata; -use crate::query_builder::bind_collector::RawBytesBindCollector; use crate::query_builder::*; use crate::result::*; use crate::serialize::ToSql; @@ -213,11 +211,11 @@ impl SqliteConnection { Statement::prepare(raw_connection, sql, is_cached) })?; - let mut bind_collector = RawBytesBindCollector::::new(); + let mut bind_collector = crate::sqlite::query_builder::SqliteBindCollector::new(); source.collect_binds(&mut bind_collector, &mut ())?; - let metadata = bind_collector.metadata; - let binds = bind_collector.binds; - for (tpe, value) in metadata.into_iter().zip(binds) { + let metadata = &bind_collector.metadata; + let binds = &bind_collector.binds; + for (tpe, value) in metadata.iter().zip(binds) { statement.bind(tpe, value)?; } diff --git a/diesel/src/sqlite/connection/raw.rs b/diesel/src/sqlite/connection/raw.rs index 20babb0bb34f..6cccf8043195 100644 --- a/diesel/src/sqlite/connection/raw.rs +++ b/diesel/src/sqlite/connection/raw.rs @@ -7,7 +7,6 @@ use std::ptr::NonNull; use std::{mem, ptr, slice, str}; use super::functions::{build_sql_function_args, process_sql_function_result}; -use super::serialized_value::SerializedValue; use super::stmt::ensure_sqlite_ok; use super::{Sqlite, SqliteAggregateFunction}; use crate::deserialize::FromSqlRow; @@ -83,7 +82,7 @@ impl RawConnection { unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) as usize } } - pub fn register_sql_function( + pub fn register_sql_function( &self, fn_name: &str, num_args: usize, @@ -91,10 +90,12 @@ impl RawConnection { f: F, ) -> QueryResult<()> where - F: FnMut(&Self, &mut [*mut ffi::sqlite3_value]) -> QueryResult + F: FnMut(&Self, &mut [*mut ffi::sqlite3_value]) -> QueryResult + std::panic::UnwindSafe + Send + 'static, + Ret: ToSql, + Sqlite: HasSqlType, { let callback_fn = Box::into_raw(Box::new(CustomFunctionUserPtr { callback: f, @@ -110,7 +111,7 @@ impl RawConnection { num_args as _, flags, callback_fn as *mut _, - Some(run_custom_function::), + Some(run_custom_function::), None, None, Some(destroy_boxed::>), @@ -264,15 +265,17 @@ struct CustomFunctionUserPtr { } #[allow(warnings)] -extern "C" fn run_custom_function( +extern "C" fn run_custom_function( ctx: *mut ffi::sqlite3_context, num_args: libc::c_int, value_ptr: *mut *mut ffi::sqlite3_value, ) where - F: FnMut(&RawConnection, &mut [*mut ffi::sqlite3_value]) -> QueryResult + F: FnMut(&RawConnection, &mut [*mut ffi::sqlite3_value]) -> QueryResult + std::panic::UnwindSafe + Send + 'static, + Ret: ToSql, + Sqlite: HasSqlType, { use std::ops::Deref; static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen."; @@ -307,7 +310,13 @@ extern "C" fn run_custom_function( let result = std::panic::catch_unwind(move || { let args = unsafe { slice::from_raw_parts_mut(value_ptr, num_args as _) }; - Ok((callback.0)(&*conn, args)?) + let res = (callback.0)(&*conn, args)?; + let value = process_sql_function_result(&res)?; + // We've checked already that ctx is not null + unsafe { + value.result_of(&mut *ctx); + } + Ok(()) }) .unwrap_or_else(|p| { Err(SqliteCallbackError::Panic( @@ -315,12 +324,8 @@ extern "C" fn run_custom_function( data_ptr.function_name.clone(), )) }); - match result { - Ok(value) => value.result_of(ctx), - Err(e) => { - e.emit(ctx); - return; - } + if let Err(e) = result { + e.emit(ctx); } } @@ -455,7 +460,12 @@ extern "C" fn run_aggregator_final_function(res)?) + let value = process_sql_function_result(&res)?; + // We've checked already that ctx is not null + unsafe { + value.result_of(&mut *ctx); + } + Ok(()) }) .unwrap_or_else(|e| { Err(SqliteCallbackError::Panic( @@ -463,10 +473,9 @@ extern "C" fn run_aggregator_final_function()), )) }); - - match result { - Ok(value) => value.result_of(ctx), - Err(e) => e.emit(ctx), + if let Err(e) = result { + e.emit(ctx); + return; } } diff --git a/diesel/src/sqlite/connection/serialized_value.rs b/diesel/src/sqlite/connection/serialized_value.rs deleted file mode 100644 index 5c38f22bebc8..000000000000 --- a/diesel/src/sqlite/connection/serialized_value.rs +++ /dev/null @@ -1,113 +0,0 @@ -extern crate libsqlite3_sys as ffi; - -use std::os::raw as libc; -use std::ptr::{self, NonNull}; - -use crate::sqlite::SqliteType; - -pub struct SerializedValue { - pub ty: SqliteType, - pub data: Option>, -} - -impl SerializedValue { - // We are always reading potentially misaligned pointers with - // `ptr::read_unaligned` - #[allow(clippy::cast_ptr_alignment)] - pub(crate) fn bind_to(self, stmt: NonNull, idx: libc::c_int) -> libc::c_int { - // This unsafe block assumes the following invariants: - // - // - `stmt` points to valid memory - // - If `self.ty` is anything other than `Binary` or `Text`, the appropriate - // number of bytes were written to `value` for an integer of the - // corresponding size. - unsafe { - match (self.ty, self.data) { - (_, None) => ffi::sqlite3_bind_null(stmt.as_ptr(), idx), - (SqliteType::Binary, Some(bytes)) => ffi::sqlite3_bind_blob( - stmt.as_ptr(), - idx, - bytes.as_ptr() as *const libc::c_void, - bytes.len() as libc::c_int, - ffi::SQLITE_TRANSIENT(), - ), - (SqliteType::Text, Some(bytes)) => ffi::sqlite3_bind_text( - stmt.as_ptr(), - idx, - bytes.as_ptr() as *const libc::c_char, - bytes.len() as libc::c_int, - ffi::SQLITE_TRANSIENT(), - ), - (SqliteType::Float, Some(bytes)) => { - let value = ptr::read_unaligned(bytes.as_ptr() as *const f32); - ffi::sqlite3_bind_double(stmt.as_ptr(), idx, libc::c_double::from(value)) - } - (SqliteType::Double, Some(bytes)) => { - let value = ptr::read_unaligned(bytes.as_ptr() as *const f64); - ffi::sqlite3_bind_double(stmt.as_ptr(), idx, value as libc::c_double) - } - (SqliteType::SmallInt, Some(bytes)) => { - let value = ptr::read_unaligned(bytes.as_ptr() as *const i16); - ffi::sqlite3_bind_int(stmt.as_ptr(), idx, libc::c_int::from(value)) - } - (SqliteType::Integer, Some(bytes)) => { - let value = ptr::read_unaligned(bytes.as_ptr() as *const i32); - ffi::sqlite3_bind_int(stmt.as_ptr(), idx, value as libc::c_int) - } - (SqliteType::Long, Some(bytes)) => { - let value = ptr::read_unaligned(bytes.as_ptr() as *const i64); - ffi::sqlite3_bind_int64(stmt.as_ptr(), idx, value) - } - } - } - } - - // We are always reading potentially misaligned pointers with - // `ptr::read_unaligned` - #[allow(clippy::cast_ptr_alignment)] - pub fn result_of(self, ctx: *mut ffi::sqlite3_context) { - // This unsafe block assumes the following invariants: - // - // - `ctx` points to valid memory - // - If `self.ty` is anything other than `Binary` or `Text`, the appropriate - // number of bytes were written to `self.data` for an integer of the - // corresponding size. - unsafe { - match (self.ty, self.data) { - (_, None) => ffi::sqlite3_result_null(ctx), - (SqliteType::Binary, Some(bytes)) => ffi::sqlite3_result_blob( - ctx, - bytes.as_ptr() as *const libc::c_void, - bytes.len() as libc::c_int, - ffi::SQLITE_TRANSIENT(), - ), - (SqliteType::Text, Some(bytes)) => ffi::sqlite3_result_text( - ctx, - bytes.as_ptr() as *const libc::c_char, - bytes.len() as libc::c_int, - ffi::SQLITE_TRANSIENT(), - ), - (SqliteType::Float, Some(bytes)) => { - let value = ptr::read_unaligned(bytes.as_ptr() as *const f32); - ffi::sqlite3_result_double(ctx, libc::c_double::from(value)) - } - (SqliteType::Double, Some(bytes)) => { - let value = ptr::read_unaligned(bytes.as_ptr() as *const f64); - ffi::sqlite3_result_double(ctx, value as libc::c_double) - } - (SqliteType::SmallInt, Some(bytes)) => { - let value = ptr::read_unaligned(bytes.as_ptr() as *const i16); - ffi::sqlite3_result_int(ctx, libc::c_int::from(value)) - } - (SqliteType::Integer, Some(bytes)) => { - let value = ptr::read_unaligned(bytes.as_ptr() as *const i32); - ffi::sqlite3_result_int(ctx, value as libc::c_int) - } - (SqliteType::Long, Some(bytes)) => { - let value = ptr::read_unaligned(bytes.as_ptr() as *const i64); - ffi::sqlite3_result_int64(ctx, value) - } - } - } - } -} diff --git a/diesel/src/sqlite/connection/stmt.rs b/diesel/src/sqlite/connection/stmt.rs index b3d284d39894..c3ee6e618499 100644 --- a/diesel/src/sqlite/connection/stmt.rs +++ b/diesel/src/sqlite/connection/stmt.rs @@ -1,11 +1,11 @@ extern crate libsqlite3_sys as ffi; use super::raw::RawConnection; -use super::serialized_value::SerializedValue; use super::sqlite_value::OwnedSqliteValue; use crate::connection::{MaybeCached, PrepareForCache}; use crate::result::Error::DatabaseError; use crate::result::*; +use crate::sqlite::query_builder::SqliteBindValue; use crate::sqlite::SqliteType; use crate::util::OnceCell; use std::ffi::{CStr, CString}; @@ -49,14 +49,75 @@ impl Statement { }) } - pub fn bind(&mut self, tpe: SqliteType, value: Option>) -> QueryResult<()> { + pub fn bind(&mut self, tpe: &SqliteType, value: &SqliteBindValue) -> QueryResult<()> { self.bind_index += 1; - let value = SerializedValue { - ty: tpe, - data: value, + // This unsafe block assumes the following invariants: + // + // - `stmt` points to valid memory + // - If `self.ty` is anything other than `Binary` or `Text`, the appropriate + // number of bytes were written to `value` for an integer of the + // corresponding size. + let result = unsafe { + match (tpe, value) { + (_, SqliteBindValue::Null) => { + ffi::sqlite3_bind_null(self.inner_statement.as_ptr(), self.bind_index) + } + (SqliteType::Binary, SqliteBindValue::BorrowedBinary(bytes)) => { + ffi::sqlite3_bind_blob( + self.inner_statement.as_ptr(), + self.bind_index, + bytes.as_ptr() as *const libc::c_void, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ) + } + (SqliteType::Binary, SqliteBindValue::Binary(bytes)) => ffi::sqlite3_bind_blob( + self.inner_statement.as_ptr(), + self.bind_index, + bytes.as_ptr() as *const libc::c_void, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + (SqliteType::Text, SqliteBindValue::BorrowedString(bytes)) => { + ffi::sqlite3_bind_text( + self.inner_statement.as_ptr(), + self.bind_index, + bytes.as_ptr() as *const libc::c_char, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ) + } + (SqliteType::Text, SqliteBindValue::String(bytes)) => ffi::sqlite3_bind_text( + self.inner_statement.as_ptr(), + self.bind_index, + bytes.as_ptr() as *const libc::c_char, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + (SqliteType::Float, SqliteBindValue::Float(value)) => ffi::sqlite3_bind_double( + self.inner_statement.as_ptr(), + self.bind_index, + libc::c_double::from(*value), + ), + (SqliteType::Double, SqliteBindValue::Double(value)) => ffi::sqlite3_bind_double( + self.inner_statement.as_ptr(), + self.bind_index, + *value as libc::c_double, + ), + (SqliteType::SmallInt, SqliteBindValue::SmallInt(value)) => ffi::sqlite3_bind_int( + self.inner_statement.as_ptr(), + self.bind_index, + libc::c_int::from(*value), + ), + (SqliteType::Integer, SqliteBindValue::Integer(value)) => { + ffi::sqlite3_bind_int(self.inner_statement.as_ptr(), self.bind_index, *value) + } + (SqliteType::Long, SqliteBindValue::BigInt(value)) => { + ffi::sqlite3_bind_int64(self.inner_statement.as_ptr(), self.bind_index, *value) + } + _ => unreachable!(), + } }; - let result = value.bind_to(self.inner_statement, self.bind_index); - ensure_sqlite_ok(result, self.raw_connection()) } diff --git a/diesel/src/sqlite/query_builder/limit_offset.rs b/diesel/src/sqlite/query_builder/limit_offset.rs index 8d713a3ecb8d..6a3985dfd02c 100644 --- a/diesel/src/sqlite/query_builder/limit_offset.rs +++ b/diesel/src/sqlite/query_builder/limit_offset.rs @@ -6,7 +6,10 @@ use crate::result::QueryResult; use crate::sqlite::Sqlite; impl QueryFragment for LimitOffsetClause { - fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, _out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> + where + 'a: 'b, + { Ok(()) } } @@ -15,7 +18,10 @@ impl QueryFragment for LimitOffsetClause, NoOffsetClau where LimitClause: QueryFragment, { - fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> + where + 'a: 'b, + { self.limit_clause.walk_ast(out)?; Ok(()) } @@ -25,7 +31,10 @@ impl QueryFragment for LimitOffsetClause: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> + where + 'a: 'b, + { // Sqlite requires a limit clause in front of any offset clause // using `LIMIT -1` is the same as not having any limit clause // https://sqlite.org/lang_select.html @@ -40,7 +49,10 @@ where LimitClause: QueryFragment, OffsetClause: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> + where + 'a: 'b, + { self.limit_clause.walk_ast(out.reborrow())?; self.offset_clause.walk_ast(out.reborrow())?; Ok(()) @@ -48,7 +60,10 @@ where } impl<'a> QueryFragment for BoxedLimitOffsetClause<'a, Sqlite> { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'b, 'c>(&'b self, mut out: AstPass<'_, 'c, Sqlite>) -> QueryResult<()> + where + 'b: 'c, + { match (self.limit.as_ref(), self.offset.as_ref()) { (Some(limit), Some(offset)) => { limit.walk_ast(out.reborrow())?; diff --git a/diesel/src/sqlite/query_builder/mod.rs b/diesel/src/sqlite/query_builder/mod.rs index 546c963445d6..c373c7f65cdf 100644 --- a/diesel/src/sqlite/query_builder/mod.rs +++ b/diesel/src/sqlite/query_builder/mod.rs @@ -1,8 +1,11 @@ //! The SQLite query builder use super::backend::Sqlite; -use crate::query_builder::QueryBuilder; +use super::SqliteType; +use crate::query_builder::{BindCollector, QueryBuilder}; use crate::result::QueryResult; +use crate::serialize::{IsNull, Output}; +use crate::sql_types::HasSqlType; mod limit_offset; @@ -40,3 +43,109 @@ impl QueryBuilder for SqliteQueryBuilder { self.sql } } + +#[doc(hidden)] +#[derive(Debug)] +pub struct SqliteBindCollector<'a> { + pub(in crate::sqlite) binds: Vec>, + pub(in crate::sqlite) metadata: Vec, +} + +impl SqliteBindCollector<'_> { + pub(in crate::sqlite) fn new() -> Self { + Self { + binds: Vec::new(), + metadata: Vec::new(), + } + } +} + +#[doc(hidden)] +#[derive(Debug)] +pub enum SqliteBindValue<'a> { + BorrowedString(&'a str), + String(Box), + BorrowedBinary(&'a [u8]), + Binary(Box<[u8]>), + SmallInt(i16), + Integer(i32), + BigInt(i64), + Float(f32), + Double(f64), + Null, +} + +impl SqliteBindValue<'_> { + pub(in crate::sqlite) fn result_of(self, ctx: &mut libsqlite3_sys::sqlite3_context) { + use libsqlite3_sys as ffi; + use std::os::raw as libc; + // This unsafe block assumes the following invariants: + // + // - `ctx` points to valid memory + unsafe { + match self { + SqliteBindValue::BorrowedString(s) => ffi::sqlite3_result_text( + ctx, + s.as_ptr() as *const libc::c_char, + s.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + SqliteBindValue::String(s) => ffi::sqlite3_result_text( + ctx, + s.as_ptr() as *const libc::c_char, + s.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + SqliteBindValue::Binary(b) => ffi::sqlite3_result_blob( + ctx, + b.as_ptr() as *const libc::c_void, + b.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + SqliteBindValue::BorrowedBinary(b) => ffi::sqlite3_result_blob( + ctx, + b.as_ptr() as *const libc::c_void, + b.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + SqliteBindValue::SmallInt(s) => ffi::sqlite3_result_int(ctx, libc::c_int::from(s)), + SqliteBindValue::Integer(i) => ffi::sqlite3_result_int(ctx, i as libc::c_int), + SqliteBindValue::BigInt(l) => ffi::sqlite3_result_int64(ctx, l), + SqliteBindValue::Float(f) => { + ffi::sqlite3_result_double(ctx, libc::c_double::from(f)) + } + SqliteBindValue::Double(d) => ffi::sqlite3_result_double(ctx, d as libc::c_double), + SqliteBindValue::Null => ffi::sqlite3_result_null(ctx), + } + } + } +} + +impl<'a> BindCollector<'a, Sqlite> for SqliteBindCollector<'a> { + type Buffer = SqliteBindValue<'a>; + + fn push_bound_value(&mut self, bind: &'a U, metadata_lookup: &mut ()) -> QueryResult<()> + where + Sqlite: crate::sql_types::HasSqlType, + U: crate::serialize::ToSql, + { + let mut to_sql_output = Output::new(SqliteBindValue::Null, metadata_lookup); + let is_null = bind + .to_sql(&mut to_sql_output) + .map_err(crate::result::Error::SerializationError)?; + let bind = to_sql_output.into_inner(); + let metadata = Sqlite::metadata(metadata_lookup); + match is_null { + IsNull::No => self.binds.push(bind), + IsNull::Yes => self.binds.push(SqliteBindValue::Null), + } + self.metadata.push(metadata); + Ok(()) + } +} + +// impl<'a> Output<'a, Sqlite> { +// pub(in crate::sqlite) fn ref_mut<'b>(&'b mut self) -> &'b mut SqliteBindValue<'a> { +// &mut self.out +// } +// } diff --git a/diesel/src/sqlite/types/date_and_time/chrono.rs b/diesel/src/sqlite/types/date_and_time/chrono.rs index fa8867a031cc..dc4103ae8344 100644 --- a/diesel/src/sqlite/types/date_and_time/chrono.rs +++ b/diesel/src/sqlite/types/date_and_time/chrono.rs @@ -1,13 +1,9 @@ -extern crate chrono; - -use self::chrono::{NaiveDate, NaiveDateTime, NaiveTime}; -use std::io::Write; - use crate::backend; use crate::deserialize::{self, FromSql}; -use crate::serialize::{self, Output, ToSql}; -use crate::sql_types::{Date, Text, Time, Timestamp}; +use crate::serialize::{self, IsNull, Output, ToSql}; +use crate::sql_types::{Date, Time, Timestamp}; use crate::sqlite::Sqlite; +use chrono::{NaiveDate, NaiveDateTime, NaiveTime}; const SQLITE_DATE_FORMAT: &str = "%F"; @@ -20,9 +16,13 @@ impl FromSql for NaiveDate { } impl ToSql for NaiveDate { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { let s = self.format(SQLITE_DATE_FORMAT).to_string(); - ToSql::::to_sql(&s, out) + out.set_owned_string(s); + Ok(IsNull::No) } } @@ -47,9 +47,13 @@ impl FromSql for NaiveTime { } impl ToSql for NaiveTime { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { let s = self.format("%T%.f").to_string(); - ToSql::::to_sql(&s, out) + out.set_owned_string(s); + Ok(IsNull::No) } } @@ -96,9 +100,13 @@ impl FromSql for NaiveDateTime { } impl ToSql for NaiveDateTime { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { let s = self.format("%F %T%.f").to_string(); - ToSql::::to_sql(&s, out) + out.set_owned_string(s); + Ok(IsNull::No) } } diff --git a/diesel/src/sqlite/types/date_and_time/mod.rs b/diesel/src/sqlite/types/date_and_time/mod.rs index 563a68ef83d6..19802070b8ac 100644 --- a/diesel/src/sqlite/types/date_and_time/mod.rs +++ b/diesel/src/sqlite/types/date_and_time/mod.rs @@ -1,5 +1,3 @@ -use std::io::Write; - use crate::deserialize::{self, FromSql}; use crate::serialize::{self, Output, ToSql}; use crate::sql_types; @@ -16,14 +14,20 @@ impl FromSql for String { } impl ToSql for str { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { ToSql::::to_sql(self, out) } } impl ToSql for String { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - <&str as ToSql>::to_sql(&&**self, out) + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { + >::to_sql(self as &str, out) } } @@ -34,14 +38,20 @@ impl FromSql for String { } impl ToSql for str { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { ToSql::::to_sql(self, out) } } impl ToSql for String { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - <&str as ToSql>::to_sql(&&**self, out) + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { + >::to_sql(self as &str, out) } } @@ -52,13 +62,19 @@ impl FromSql for String { } impl ToSql for str { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { ToSql::::to_sql(self, out) } } impl ToSql for String { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - <&str as ToSql>::to_sql(&&**self, out) + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { + >::to_sql(self as &str, out) } } diff --git a/diesel/src/sqlite/types/mod.rs b/diesel/src/sqlite/types/mod.rs index 3d96ae2c6206..46cbe1314dfc 100644 --- a/diesel/src/sqlite/types/mod.rs +++ b/diesel/src/sqlite/types/mod.rs @@ -1,12 +1,10 @@ mod date_and_time; mod numeric; -use std::io::prelude::*; - use super::connection::SqliteValue; use super::Sqlite; use crate::deserialize::{self, FromSql}; -use crate::serialize::{self, Output, ToSql}; +use crate::serialize::{self, IsNull, Output, ToSql}; use crate::sql_types; /// The returned pointer is *only* valid for the lifetime to the argument of @@ -70,8 +68,83 @@ impl FromSql for f64 { } impl ToSql for bool { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - let int_value = if *self { 1 } else { 0 }; - >::to_sql(&int_value, out) + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { + const ZERO: i32 = 0; + const ONE: i32 = 1; + let int_value = if *self { &ONE } else { &ZERO }; + >::to_sql(int_value, out) + } +} + +impl ToSql for str { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { + out.set_borrowed_string(self); + Ok(IsNull::No) + } +} + +impl ToSql for [u8] { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { + out.set_borrowed_binary(self); + Ok(IsNull::No) + } +} + +impl ToSql for i16 { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { + out.set_small_int(*self); + Ok(IsNull::No) + } +} + +impl ToSql for i32 { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { + out.set_int(*self); + Ok(IsNull::No) + } +} + +impl ToSql for i64 { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { + out.set_big_int(*self); + Ok(IsNull::No) + } +} + +impl ToSql for f32 { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { + out.set_float(*self); + Ok(IsNull::No) + } +} + +impl ToSql for f64 { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, Sqlite>) -> serialize::Result + where + 'a: 'b, + { + out.set_double(*self); + Ok(IsNull::No) } } diff --git a/diesel/src/type_impls/floats.rs b/diesel/src/type_impls/floats.rs index 3fd60e680502..8b137891791f 100644 --- a/diesel/src/type_impls/floats.rs +++ b/diesel/src/type_impls/floats.rs @@ -1,58 +1 @@ -use byteorder::{ReadBytesExt, WriteBytesExt}; -use std::error::Error; -use std::io::prelude::*; -use crate::backend::{Backend, BinaryRawValue}; -use crate::deserialize::{self, FromSql}; -use crate::serialize::{self, IsNull, Output, ToSql}; -use crate::sql_types; - -impl FromSql for f32 -where - DB: Backend + for<'a> BinaryRawValue<'a>, -{ - fn from_sql(value: crate::backend::RawValue) -> deserialize::Result { - let mut bytes = DB::as_bytes(value); - debug_assert!( - bytes.len() <= 4, - "Received more than 4 bytes while decoding \ - an f32. Was a double accidentally marked as float?" - ); - bytes - .read_f32::() - .map_err(|e| Box::new(e) as Box) - } -} - -impl ToSql for f32 { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - out.write_f32::(*self) - .map(|_| IsNull::No) - .map_err(|e| Box::new(e) as Box) - } -} - -impl FromSql for f64 -where - DB: Backend + for<'a> BinaryRawValue<'a>, -{ - fn from_sql(value: crate::backend::RawValue) -> deserialize::Result { - let mut bytes = DB::as_bytes(value); - debug_assert!( - bytes.len() <= 8, - "Received more than 8 bytes while decoding \ - an f64. Was a numeric accidentally marked as double?" - ); - bytes - .read_f64::() - .map_err(|e| Box::new(e) as Box) - } -} - -impl ToSql for f64 { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - out.write_f64::(*self) - .map(|_| IsNull::No) - .map_err(|e| Box::new(e) as Box) - } -} diff --git a/diesel/src/type_impls/integers.rs b/diesel/src/type_impls/integers.rs index bf08ce1878cb..c849d14f5564 100644 --- a/diesel/src/type_impls/integers.rs +++ b/diesel/src/type_impls/integers.rs @@ -1,99 +1,7 @@ -use byteorder::{ReadBytesExt, WriteBytesExt}; +use byteorder::WriteBytesExt; use std::error::Error; use std::io::prelude::*; -use crate::backend::{Backend, BinaryRawValue}; -use crate::deserialize::{self, FromSql}; +use crate::backend::Backend; use crate::serialize::{self, IsNull, Output, ToSql}; use crate::sql_types; - -impl FromSql for i16 -where - DB: Backend + for<'a> BinaryRawValue<'a>, -{ - fn from_sql(value: crate::backend::RawValue) -> deserialize::Result { - let mut bytes = DB::as_bytes(value); - debug_assert!( - bytes.len() <= 2, - "Received more than 2 bytes decoding i16. \ - Was an Integer expression accidentally identified as SmallInt?" - ); - debug_assert!( - bytes.len() >= 2, - "Received fewer than 2 bytes decoding i16. \ - Was an expression of a different type accidentally identified \ - as SmallInt?" - ); - bytes - .read_i16::() - .map_err(|e| Box::new(e) as Box) - } -} - -impl ToSql for i16 { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - out.write_i16::(*self) - .map(|_| IsNull::No) - .map_err(|e| Box::new(e) as Box) - } -} - -impl FromSql for i32 -where - DB: Backend + for<'a> BinaryRawValue<'a>, -{ - fn from_sql(value: crate::backend::RawValue) -> deserialize::Result { - let mut bytes = DB::as_bytes(value); - debug_assert!( - bytes.len() <= 4, - "Received more than 4 bytes decoding i32. \ - Was a BigInt expression accidentally identified as Integer?" - ); - debug_assert!( - bytes.len() >= 4, - "Received fewer than 4 bytes decoding i32. \ - Was a SmallInt expression accidentally identified as Integer?" - ); - bytes - .read_i32::() - .map_err(|e| Box::new(e) as Box) - } -} - -impl ToSql for i32 { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - out.write_i32::(*self) - .map(|_| IsNull::No) - .map_err(|e| Box::new(e) as Box) - } -} - -impl FromSql for i64 -where - DB: Backend + for<'a> BinaryRawValue<'a>, -{ - fn from_sql(value: crate::backend::RawValue) -> deserialize::Result { - let mut bytes = DB::as_bytes(value); - debug_assert!( - bytes.len() <= 8, - "Received more than 8 bytes decoding i64. \ - Was an expression of a different type misidentified as BigInt?" - ); - debug_assert!( - bytes.len() >= 8, - "Received fewer than 8 bytes decoding i64. \ - Was an Integer expression misidentified as BigInt?" - ); - bytes - .read_i64::() - .map_err(|e| Box::new(e) as Box) - } -} - -impl ToSql for i64 { - fn to_sql(&self, out: &mut Output) -> serialize::Result { - out.write_i64::(*self) - .map(|_| IsNull::No) - .map_err(|e| Box::new(e) as Box) - } -} diff --git a/diesel/src/type_impls/mod.rs b/diesel/src/type_impls/mod.rs index 6b8421951bf3..28d4fb342623 100644 --- a/diesel/src/type_impls/mod.rs +++ b/diesel/src/type_impls/mod.rs @@ -1,7 +1,7 @@ mod date_and_time; mod decimal; -pub mod floats; -mod integers; +//pub mod floats; +//mod integers; #[cfg(all(feature = "serde_json", any(feature = "postgres", feature = "mysql")))] mod json; pub mod option; diff --git a/diesel/src/type_impls/option.rs b/diesel/src/type_impls/option.rs index 2abcce4fb5b6..34b9016a01b9 100644 --- a/diesel/src/type_impls/option.rs +++ b/diesel/src/type_impls/option.rs @@ -1,5 +1,3 @@ -use std::io::Write; - use crate::backend::{self, Backend}; use crate::deserialize::{self, FromSql, Queryable, QueryableByName}; use crate::expression::bound::Bound; @@ -51,7 +49,10 @@ where DB: Backend, ST: SqlType, { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, DB>) -> serialize::Result + where + 'a: 'b, + { if let Some(ref value) = *self { value.to_sql(out) } else { @@ -120,18 +121,27 @@ use crate::sql_types; #[cfg(feature = "postgres")] fn option_to_sql() { type Type = sql_types::Nullable; - let mut bytes = Output::test(); - let is_null = ToSql::::to_sql(&None::, &mut bytes).unwrap(); + let mut buffer = Vec::new(); + let is_null = { + let mut bytes = Output::test(&mut buffer); + ToSql::::to_sql(&None::, &mut bytes).unwrap() + }; assert_eq!(IsNull::Yes, is_null); - assert!(bytes.is_empty()); + assert!(buffer.is_empty()); - let is_null = ToSql::::to_sql(&Some(""), &mut bytes).unwrap(); + let is_null = { + let mut bytes = Output::test(&mut buffer); + ToSql::::to_sql(&Some(""), &mut bytes).unwrap() + }; assert_eq!(IsNull::No, is_null); - assert!(bytes.is_empty()); + assert!(buffer.is_empty()); - let is_null = ToSql::::to_sql(&Some("Sean"), &mut bytes).unwrap(); + let is_null = { + let mut bytes = Output::test(&mut buffer); + ToSql::::to_sql(&Some("Sean"), &mut bytes).unwrap() + }; let expectd_bytes = b"Sean".to_vec(); assert_eq!(IsNull::No, is_null); - assert_eq!(bytes, expectd_bytes); + assert_eq!(buffer, expectd_bytes); } diff --git a/diesel/src/type_impls/primitives.rs b/diesel/src/type_impls/primitives.rs index 5661f3e96162..9898470cc1c1 100644 --- a/diesel/src/type_impls/primitives.rs +++ b/diesel/src/type_impls/primitives.rs @@ -1,8 +1,9 @@ use std::error::Error; use std::io::Write; -use crate::backend::{self, Backend, BinaryRawValue}; +use crate::backend::{self, Backend}; use crate::deserialize::{self, FromSql, Queryable}; +use crate::query_builder::bind_collector::RawBytesBindCollector; use crate::serialize::{self, IsNull, Output, ToSql}; use crate::sql_types::{ self, BigInt, Binary, Bool, Double, Float, Integer, SingleValue, SmallInt, Text, @@ -125,24 +126,14 @@ where } } -/// The returned pointer is *only* valid for the lifetime to the argument of -/// `from_sql`. This impl is intended for uses where you want to write a new -/// impl in terms of `String`, but don't want to allocate. We have to return a -/// raw pointer instead of a reference with a lifetime due to the structure of -/// `FromSql` -impl FromSql for *const str +impl ToSql for str where - DB: Backend + for<'a> BinaryRawValue<'a>, + DB: Backend>, { - fn from_sql(value: crate::backend::RawValue) -> deserialize::Result { - use std::str; - let string = str::from_utf8(DB::as_bytes(value))?; - Ok(string as *const _) - } -} - -impl ToSql for str { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, DB>) -> serialize::Result + where + 'a: 'b, + { out.write_all(self.as_bytes()) .map(|_| IsNull::No) .map_err(|e| Box::new(e) as Box) @@ -154,7 +145,10 @@ where DB: Backend, str: ToSql, { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, DB>) -> serialize::Result + where + 'a: 'b, + { (self as &str).to_sql(out) } } @@ -172,32 +166,27 @@ where } } -/// The returned pointer is *only* valid for the lifetime to the argument of -/// `from_sql`. This impl is intended for uses where you want to write a new -/// impl in terms of `Vec`, but don't want to allocate. We have to return a -/// raw pointer instead of a reference with a lifetime due to the structure of -/// `FromSql` -impl FromSql for *const [u8] -where - DB: Backend + for<'a> BinaryRawValue<'a>, -{ - fn from_sql(bytes: backend::RawValue) -> deserialize::Result { - Ok(DB::as_bytes(bytes) as *const _) - } -} - impl ToSql for Vec where DB: Backend, [u8]: ToSql, { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, DB>) -> serialize::Result + where + 'a: 'b, + { (self as &[u8]).to_sql(out) } } -impl ToSql for [u8] { - fn to_sql(&self, out: &mut Output) -> serialize::Result { +impl ToSql for [u8] +where + DB: Backend>, +{ + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, DB>) -> serialize::Result + where + 'a: 'b, + { out.write_all(self) .map(|_| IsNull::No) .map_err(|e| Box::new(e) as Box) @@ -212,7 +201,10 @@ where DB: Backend, Self: fmt::Debug, { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'b, 'c, 'd>(&'b self, out: &mut Output<'c, 'd, DB>) -> serialize::Result + where + 'b: 'c, + { ToSql::::to_sql(&**self, out) } } diff --git a/diesel/src/type_impls/tuples.rs b/diesel/src/type_impls/tuples.rs index 27ff56981bff..a48932fe200a 100644 --- a/diesel/src/type_impls/tuples.rs +++ b/diesel/src/type_impls/tuples.rs @@ -75,7 +75,10 @@ macro_rules! tuple_impls { impl<$($T: QueryFragment<__DB>),+, __DB: Backend> QueryFragment<__DB> for ($($T,)+) { #[allow(unused_assignments)] - fn walk_ast(&self, mut out: AstPass<__DB>) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, __DB>) -> QueryResult<()> + where + 'a: 'b + { let mut needs_comma = false; $( if !self.$idx.is_noop()? { diff --git a/diesel/src/upsert/on_conflict_extension.rs b/diesel/src/upsert/on_conflict_extension.rs index f0fae6a62972..f328fe5e5686 100644 --- a/diesel/src/upsert/on_conflict_extension.rs +++ b/diesel/src/upsert/on_conflict_extension.rs @@ -10,6 +10,7 @@ use crate::sql_types::BoolOrNullableBool; impl InsertStatement where + T: QuerySource, U: UndecoratedInsertRecord + IntoConflictValueClause, { /// Adds `ON CONFLICT DO NOTHING` to the insert statement, without @@ -224,7 +225,9 @@ pub struct IncompleteOnConflict { target: Target, } -impl IncompleteOnConflict, Target> { +impl + IncompleteOnConflict, Target> +{ /// Creates a query with `ON CONFLICT (target) DO NOTHING` /// /// If you want to do nothing when *any* constraint conflicts, use @@ -371,7 +374,9 @@ pub struct IncompleteDoUpdate { target: Target, } -impl IncompleteDoUpdate, Target> { +impl + IncompleteDoUpdate, Target> +{ /// See [`do_update`] for usage examples. /// /// [`do_update`]: IncompleteOnConflict::do_update() diff --git a/diesel_cli/src/query_helper.rs b/diesel_cli/src/query_helper.rs index 8aba0801a114..64d66a70ba9b 100644 --- a/diesel_cli/src/query_helper.rs +++ b/diesel_cli/src/query_helper.rs @@ -26,7 +26,7 @@ impl DropDatabaseStatement { } impl QueryFragment for DropDatabaseStatement { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { out.push_sql("DROP DATABASE "); if self.if_exists { out.push_sql("IF EXISTS "); @@ -58,7 +58,7 @@ impl CreateDatabaseStatement { } impl QueryFragment for CreateDatabaseStatement { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> { out.push_sql("CREATE DATABASE "); out.push_identifier(&self.db_name)?; Ok(()) diff --git a/diesel_compile_tests/tests/fail/any_is_only_selectable_if_inner_expr_is_selectable.stderr b/diesel_compile_tests/tests/fail/any_is_only_selectable_if_inner_expr_is_selectable.stderr index 2eea89b58533..d74975a436fc 100644 --- a/diesel_compile_tests/tests/fail/any_is_only_selectable_if_inner_expr_is_selectable.stderr +++ b/diesel_compile_tests/tests/fail/any_is_only_selectable_if_inner_expr_is_selectable.stderr @@ -17,6 +17,6 @@ error[E0277]: the trait bound `stuff::table: AppearsInFromClause` for `more_stuff::columns::names` = note: 3 redundant requirements hidden = note: required because of the requirements on the impl of `AppearsOnTable` for `Grouped>>` - = note: required because of the requirements on the impl of `diesel::query_builder::where_clause::ValidWhereClause` for `diesel::query_builder::where_clause::WhereClause>>>` - = note: required because of the requirements on the impl of `Query` for `SelectStatement>>>>` - = note: required because of the requirements on the impl of `LoadQuery<_, _>` for `SelectStatement>>>>` + = note: required because of the requirements on the impl of `diesel::query_builder::where_clause::ValidWhereClause>` for `diesel::query_builder::where_clause::WhereClause>>>` + = note: required because of the requirements on the impl of `Query` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>>` + = note: required because of the requirements on the impl of `LoadQuery<_, _>` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>>` diff --git a/diesel_compile_tests/tests/fail/array_expressions_must_be_correct_type.stderr b/diesel_compile_tests/tests/fail/array_expressions_must_be_correct_type.stderr index 9de7857ab514..d7b4b256b817 100644 --- a/diesel_compile_tests/tests/fail/array_expressions_must_be_correct_type.stderr +++ b/diesel_compile_tests/tests/fail/array_expressions_must_be_correct_type.stderr @@ -6,13 +6,15 @@ error[E0277]: the trait bound `f64: SelectableExpression` is not s | ::: $DIESEL/src/query_builder/functions.rs | - | SelectStatement: SelectDsl, - | ------------ required by this bound in `diesel::select` + | crate::dsl::BareSelect: AsQuery, + | ------- required by this bound in `diesel::select` | = note: required because of the requirements on the impl of `SelectableExpression` for `(f64, f64)` = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `SelectableExpression` for `diesel::pg::expression::array::ArrayLiteral<(f64, f64), diesel::sql_types::Integer>` - = note: required because of the requirements on the impl of `SelectDsl>` for `SelectStatement` + = note: required because of the requirements on the impl of `SelectClauseExpression` for `diesel::query_builder::select_clause::SelectClause>` + = note: required because of the requirements on the impl of `Query` for `SelectStatement>>` + = note: required because of the requirements on the impl of `AsQuery` for `SelectStatement>>` error[E0277]: the trait bound `f64: ValidGrouping<()>` is not satisfied --> $DIR/array_expressions_must_be_correct_type.rs:9:5 @@ -22,13 +24,14 @@ error[E0277]: the trait bound `f64: ValidGrouping<()>` is not satisfied | ::: $DIESEL/src/query_builder/functions.rs | - | SelectStatement: SelectDsl, - | ------------ required by this bound in `diesel::select` + | crate::dsl::BareSelect: AsQuery, + | ------- required by this bound in `diesel::select` | = note: required because of the requirements on the impl of `ValidGrouping<()>` for `(f64, f64)` = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::pg::expression::array::ArrayLiteral<(f64, f64), diesel::sql_types::Integer>` - = note: required because of the requirements on the impl of `SelectDsl>` for `SelectStatement` + = note: required because of the requirements on the impl of `Query` for `SelectStatement>>` + = note: required because of the requirements on the impl of `AsQuery` for `SelectStatement>>` error[E0277]: the trait bound `f64: SelectableExpression` is not satisfied --> $DIR/array_expressions_must_be_correct_type.rs:9:33 @@ -55,29 +58,26 @@ error[E0277]: the trait bound `f64: ValidGrouping<()>` is not satisfied = note: required because of the requirements on the impl of `Query` for `SelectStatement>>` = note: required because of the requirements on the impl of `LoadQuery<_, Vec>` for `SelectStatement>>` -error[E0277]: the trait bound `f64: QueryId` is not satisfied +error[E0277]: the trait bound `f64: QueryFragment` is not satisfied --> $DIR/array_expressions_must_be_correct_type.rs:9:33 | 9 | select(array((1f64, 3f64))).get_result::>(&mut connection); - | ^^^^^^^^^^ the trait `QueryId` is not implemented for `f64` + | ^^^^^^^^^^ the trait `QueryFragment` is not implemented for `f64` | - = note: required because of the requirements on the impl of `QueryId` for `(f64, f64)` + = note: required because of the requirements on the impl of `QueryFragment` for `(f64, f64)` = note: 3 redundant requirements hidden - = note: required because of the requirements on the impl of `QueryId` for `SelectStatement>>` + = note: required because of the requirements on the impl of `QueryFragment` for `SelectStatement>>` = note: required because of the requirements on the impl of `LoadQuery<_, Vec>` for `SelectStatement>>` -error[E0277]: the trait bound `f64: QueryFragment` is not satisfied +error[E0277]: the trait bound `f64: QueryId` is not satisfied --> $DIR/array_expressions_must_be_correct_type.rs:9:33 | 9 | select(array((1f64, 3f64))).get_result::>(&mut connection); - | ^^^^^^^^^^ the trait `QueryFragment` is not implemented for `f64` + | ^^^^^^^^^^ the trait `QueryId` is not implemented for `f64` | - = note: required because of the requirements on the impl of `QueryFragment` for `(f64, f64)` - = note: 2 redundant requirements hidden - = note: required because of the requirements on the impl of `QueryFragment` for `diesel::pg::expression::array::ArrayLiteral<(f64, f64), diesel::sql_types::Integer>` - = note: required because of the requirements on the impl of `SelectClauseQueryFragment` for `diesel::query_builder::select_clause::SelectClause>` - = note: 1 redundant requirements hidden - = note: required because of the requirements on the impl of `QueryFragment` for `SelectStatement>>` + = note: required because of the requirements on the impl of `QueryId` for `(f64, f64)` + = note: 3 redundant requirements hidden + = note: required because of the requirements on the impl of `QueryId` for `SelectStatement>>` = note: required because of the requirements on the impl of `LoadQuery<_, Vec>` for `SelectStatement>>` error[E0277]: the trait bound `f64: diesel::Expression` is not satisfied @@ -93,12 +93,3 @@ error[E0277]: the trait bound `f64: diesel::Expression` is not satisfied | = note: required because of the requirements on the impl of `AsExpression` for `f64` = note: required because of the requirements on the impl of `AsExpressionList` for `(f64, f64)` - -error[E0277]: the trait bound `SelectStatement: SelectDsl>` is not satisfied - --> $DIR/array_expressions_must_be_correct_type.rs:9:5 - | -9 | select(array((1f64, 3f64))).get_result::>(&mut connection); - | ^^^^^^ the trait `SelectDsl>` is not implemented for `SelectStatement` - | - = help: the following implementations were found: - as SelectDsl> diff --git a/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr b/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr index 45568ef8282d..94e7e4bce66c 100644 --- a/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr +++ b/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr @@ -193,7 +193,7 @@ error[E0277]: the trait bound `{integer}: QueryId` is not satisfied = note: required because of the requirements on the impl of `LoadQuery<_, Vec>` for `SelectStatement), diesel::sql_types::Double>>>` error[E0277]: the trait bound `{integer}: QueryFragment` is not satisfied - --> tests/fail/array_expressions_must_be_same_type.rs:12:30 + --> $DIR/array_expressions_must_be_same_type.rs:12:30 | 12 | select(array((1, 3f64))).get_result::>(&mut connection).unwrap(); | ^^^^^^^^^^ the trait `QueryFragment` is not implemented for `{integer}` @@ -203,7 +203,7 @@ error[E0277]: the trait bound `{integer}: QueryFragment` is not satisfied <() as QueryFragment> <(A, B) as QueryFragment<__DB>> <(A, B, C) as QueryFragment<__DB>> - and 243 others + and 242 others = note: required because of the requirements on the impl of `QueryFragment` for `({integer}, diesel::expression::bound::Bound)` = note: 2 redundant requirements hidden = note: required because of the requirements on the impl of `QueryFragment` for `diesel::pg::expression::array::ArrayLiteral<({integer}, diesel::expression::bound::Bound), diesel::sql_types::Double>` @@ -233,7 +233,7 @@ error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied = note: required because of the requirements on the impl of `AsExpressionList` for `({integer}, f64)` error[E0277]: the trait bound `SelectStatement: SelectDsl, f64), diesel::sql_types::Integer>>` is not satisfied - --> tests/fail/array_expressions_must_be_same_type.rs:11:5 + --> $DIR/array_expressions_must_be_same_type.rs:11:5 | 11 | select(array((1, 3f64))).get_result::>(&mut connection).unwrap(); | ^^^^^^ the trait `SelectDsl, f64), diesel::sql_types::Integer>>` is not implemented for `SelectStatement` @@ -242,7 +242,7 @@ error[E0277]: the trait bound `SelectStatement: SelectDsl as SelectDsl> error[E0277]: the trait bound `SelectStatement: SelectDsl), diesel::sql_types::Double>>` is not satisfied - --> tests/fail/array_expressions_must_be_same_type.rs:12:5 + --> $DIR/array_expressions_must_be_same_type.rs:12:5 | 12 | select(array((1, 3f64))).get_result::>(&mut connection).unwrap(); | ^^^^^^ the trait `SelectDsl), diesel::sql_types::Double>>` is not implemented for `SelectStatement` diff --git a/diesel_compile_tests/tests/fail/boxed_queries_and_group_by.stderr b/diesel_compile_tests/tests/fail/boxed_queries_and_group_by.stderr index 164b99db9688..bdf5f96324d1 100644 --- a/diesel_compile_tests/tests/fail/boxed_queries_and_group_by.stderr +++ b/diesel_compile_tests/tests/fail/boxed_queries_and_group_by.stderr @@ -1,11 +1,12 @@ -error[E0277]: the trait bound `SelectStatement, diesel::query_builder::group_by_clause::GroupByClause>: BoxedDsl<'_, _>` is not satisfied +error[E0277]: the trait bound `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause, diesel::query_builder::group_by_clause::GroupByClause>: BoxedDsl<'_, _>` is not satisfied --> $DIR/boxed_queries_and_group_by.rs:55:40 | 55 | users::table.group_by(users::name).into_boxed(); - | ^^^^^^^^^^ the trait `BoxedDsl<'_, _>` is not implemented for `SelectStatement, diesel::query_builder::group_by_clause::GroupByClause>` + | ^^^^^^^^^^ the trait `BoxedDsl<'_, _>` is not implemented for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause, diesel::query_builder::group_by_clause::GroupByClause>` | = help: the following implementations were found: - as BoxedDsl<'a, DB>> + , S, D, W, O, LOf, G, H> as BoxedDsl<'a, DB>> + as BoxedDsl<'a, DB>> error[E0271]: type mismatch resolving `>::Output == diesel::expression::is_contained_in_group_by::Yes` --> $DIR/boxed_queries_and_group_by.rs:59:10 @@ -14,16 +15,17 @@ error[E0271]: type mismatch resolving `` for `users::columns::id` - = note: required because of the requirements on the impl of `SelectDsl` for `SelectStatement, diesel::query_builder::group_by_clause::GroupByClause>` + = note: required because of the requirements on the impl of `SelectDsl` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause, diesel::query_builder::group_by_clause::GroupByClause>` -error[E0277]: the trait bound `SelectStatement, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause, diesel::query_builder::group_by_clause::GroupByClause>: BoxedDsl<'_, _>` is not satisfied +error[E0277]: the trait bound `SelectStatement, diesel::query_builder::select_clause::SelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause, diesel::query_builder::group_by_clause::GroupByClause>: BoxedDsl<'_, _>` is not satisfied --> $DIR/boxed_queries_and_group_by.rs:60:10 | 60 | .into_boxed(); - | ^^^^^^^^^^ the trait `BoxedDsl<'_, _>` is not implemented for `SelectStatement, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause, diesel::query_builder::group_by_clause::GroupByClause>` + | ^^^^^^^^^^ the trait `BoxedDsl<'_, _>` is not implemented for `SelectStatement, diesel::query_builder::select_clause::SelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause, diesel::query_builder::group_by_clause::GroupByClause>` | = help: the following implementations were found: - as BoxedDsl<'a, DB>> + , S, D, W, O, LOf, G, H> as BoxedDsl<'a, DB>> + as BoxedDsl<'a, DB>> error[E0271]: type mismatch resolving `>::Output == diesel::expression::is_contained_in_group_by::Yes` --> $DIR/boxed_queries_and_group_by.rs:66:10 @@ -32,16 +34,16 @@ error[E0271]: type mismatch resolving `` for `users::columns::id` - = note: required because of the requirements on the impl of `SelectDsl` for `BoxedSelectStatement<'_, diesel::sql_types::Text, users::table, _, users::columns::name>` + = note: required because of the requirements on the impl of `SelectDsl` for `BoxedSelectStatement<'_, diesel::sql_types::Text, FromClause, _, users::columns::name>` -error[E0277]: the trait bound `BoxedSelectStatement<'_, diesel::sql_types::Text, users::table, _, users::columns::name>: Table` is not satisfied +error[E0277]: the trait bound `BoxedSelectStatement<'_, diesel::sql_types::Text, FromClause, _, users::columns::name>: Table` is not satisfied --> $DIR/boxed_queries_and_group_by.rs:73:10 | 73 | .inner_join(posts::table) - | ^^^^^^^^^^ the trait `Table` is not implemented for `BoxedSelectStatement<'_, diesel::sql_types::Text, users::table, _, users::columns::name>` + | ^^^^^^^^^^ the trait `Table` is not implemented for `BoxedSelectStatement<'_, diesel::sql_types::Text, FromClause, _, users::columns::name>` | - = note: required because of the requirements on the impl of `JoinTo>` for `BoxedSelectStatement<'_, diesel::sql_types::Text, users::table, _, users::columns::name>` - = note: required because of the requirements on the impl of `JoinWithImplicitOnClause, Inner>` for `BoxedSelectStatement<'_, diesel::sql_types::Text, users::table, _, users::columns::name>` + = note: required because of the requirements on the impl of `JoinTo>` for `BoxedSelectStatement<'_, diesel::sql_types::Text, FromClause, _, users::columns::name>` + = note: required because of the requirements on the impl of `JoinWithImplicitOnClause, Inner>` for `BoxedSelectStatement<'_, diesel::sql_types::Text, FromClause, _, users::columns::name>` error[E0308]: mismatched types --> $DIR/boxed_queries_and_group_by.rs:73:21 @@ -61,29 +63,29 @@ error[E0308]: mismatched types = note: expected struct `BoxedSelectStatement<'_, _, _, _, ()>` found struct `BoxedSelectStatement<'_, _, _, _, users::columns::id>` -error[E0271]: type mismatch resolving ` as AsQuery>::Query == SelectStatement>` +error[E0271]: type mismatch resolving `, _> as AsQuery>::Query == SelectStatement, _>>>` --> $DIR/boxed_queries_and_group_by.rs:84:10 | 84 | .group_by(users::id) | ^^^^^^^^ expected struct `BoxedSelectStatement`, found struct `SelectStatement` | - = note: expected type `BoxedSelectStatement<'_, _, _, _, _>` - found struct `SelectStatement>` - = note: required because of the requirements on the impl of `GroupByDsl<_>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), users::table, _>` + = note: expected type `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), FromClause, _>` + found struct `SelectStatement, _>>>` + = note: required because of the requirements on the impl of `GroupByDsl<_>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), FromClause, _>` -error[E0277]: the trait bound `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), users::table, _>: Table` is not satisfied +error[E0277]: the trait bound `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), FromClause, _>: Table` is not satisfied --> $DIR/boxed_queries_and_group_by.rs:84:10 | 84 | .group_by(users::id) - | ^^^^^^^^ the trait `Table` is not implemented for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), users::table, _>` + | ^^^^^^^^ the trait `Table` is not implemented for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), FromClause, _>` | - = note: required because of the requirements on the impl of `GroupByDsl<_>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), users::table, _>` + = note: required because of the requirements on the impl of `GroupByDsl<_>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), FromClause, _>` -error[E0277]: the trait bound `SelectStatement>: GroupByDsl<_>` is not satisfied +error[E0277]: the trait bound `SelectStatement, _>>>: GroupByDsl<_>` is not satisfied --> $DIR/boxed_queries_and_group_by.rs:84:10 | 84 | .group_by(users::id) - | ^^^^^^^^ the trait `GroupByDsl<_>` is not implemented for `SelectStatement>` + | ^^^^^^^^ the trait `GroupByDsl<_>` is not implemented for `SelectStatement, _>>>` | = help: the following implementations were found: as GroupByDsl> diff --git a/diesel_compile_tests/tests/fail/boxed_queries_must_be_used_with_proper_connection.stderr b/diesel_compile_tests/tests/fail/boxed_queries_must_be_used_with_proper_connection.stderr index ad3e10d79118..eaaee038ff71 100644 --- a/diesel_compile_tests/tests/fail/boxed_queries_must_be_used_with_proper_connection.stderr +++ b/diesel_compile_tests/tests/fail/boxed_queries_must_be_used_with_proper_connection.stderr @@ -4,4 +4,4 @@ error[E0271]: type mismatch resolving `().load::<(i32,)>(&mut connection); | ^^^^ expected struct `Sqlite`, found struct `Pg` | - = note: required because of the requirements on the impl of `LoadQuery` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer,), users::table, Pg>` + = note: required because of the requirements on the impl of `LoadQuery` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer,), FromClause, Pg>` diff --git a/diesel_compile_tests/tests/fail/boxed_queries_require_selectable_expression_for_filter.stderr b/diesel_compile_tests/tests/fail/boxed_queries_require_selectable_expression_for_filter.stderr index 11ca748dfc70..4011b782266c 100644 --- a/diesel_compile_tests/tests/fail/boxed_queries_require_selectable_expression_for_filter.stderr +++ b/diesel_compile_tests/tests/fail/boxed_queries_require_selectable_expression_for_filter.stderr @@ -9,4 +9,4 @@ error[E0277]: the trait bound `users::table: AppearsInFromClause` = note: required because of the requirements on the impl of `AppearsOnTable` for `posts::columns::title` = note: 2 redundant requirements hidden = note: required because of the requirements on the impl of `AppearsOnTable` for `Grouped>>` - = note: required because of the requirements on the impl of `FilterDsl>>>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), users::table, Pg>` + = note: required because of the requirements on the impl of `FilterDsl>>>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), FromClause, Pg>` diff --git a/diesel_compile_tests/tests/fail/boxed_queries_require_selectable_expression_for_order.stderr b/diesel_compile_tests/tests/fail/boxed_queries_require_selectable_expression_for_order.stderr index 5cea87dbc641..244fc11716df 100644 --- a/diesel_compile_tests/tests/fail/boxed_queries_require_selectable_expression_for_order.stderr +++ b/diesel_compile_tests/tests/fail/boxed_queries_require_selectable_expression_for_order.stderr @@ -6,7 +6,8 @@ error[E0277]: the trait bound `users::table: AppearsInFromClause` | = help: the following implementations were found: > - = note: required because of the requirements on the impl of `AppearsOnTable` for `posts::columns::title` + = note: required because of the requirements on the impl of `AppearsInFromClause` for `FromClause` + = note: required because of the requirements on the impl of `AppearsOnTable>` for `posts::columns::title` = note: 1 redundant requirements hidden - = note: required because of the requirements on the impl of `AppearsOnTable` for `Desc` - = note: required because of the requirements on the impl of `OrderDsl>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), users::table, Pg>` + = note: required because of the requirements on the impl of `AppearsOnTable>` for `Desc` + = note: required because of the requirements on the impl of `OrderDsl>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), FromClause, Pg>` diff --git a/diesel_compile_tests/tests/fail/cannot_join_to_non_joinable_table.stderr b/diesel_compile_tests/tests/fail/cannot_join_to_non_joinable_table.stderr index 41b6cc03f16f..3a8934e96aa7 100644 --- a/diesel_compile_tests/tests/fail/cannot_join_to_non_joinable_table.stderr +++ b/diesel_compile_tests/tests/fail/cannot_join_to_non_joinable_table.stderr @@ -5,9 +5,9 @@ error[E0277]: the trait bound `users::table: JoinTo` is not satisf | ^^^^^^^^^^ the trait `JoinTo` is not implemented for `users::table` | = help: the following implementations were found: - >> + , ST, DB>>> >> - >> + , S, D, W, O, L, Of, G>>> >> = note: required because of the requirements on the impl of `JoinWithImplicitOnClause` for `users::table` @@ -18,9 +18,9 @@ error[E0277]: the trait bound `users::table: JoinTo` is not satisf | ^^^^^^^^^^^^^^^ the trait `JoinTo` is not implemented for `users::table` | = help: the following implementations were found: - >> + , ST, DB>>> >> - >> + , S, D, W, O, L, Of, G>>> >> = note: required because of the requirements on the impl of `JoinWithImplicitOnClause` for `users::table` @@ -31,10 +31,10 @@ error[E0277]: the trait bound `posts::table: JoinTo` is not satisf | ^^^^ the trait `JoinTo` is not implemented for `posts::table` | = help: the following implementations were found: - >> + , ST, DB>>> >> - >> + , S, D, W, O, L, Of, G>>> > >> = note: required because of the requirements on the impl of `JoinTo` for `diesel::query_source::joins::Join` - = note: required because of the requirements on the impl of `JoinWithImplicitOnClause, Grouped, diesel::expression::nullable::Nullable>>>>, Inner>` for `users::table` + = note: required because of the requirements on the impl of `JoinWithImplicitOnClause, Grouped, diesel::expression::nullable::Nullable>>>>>, Inner>` for `users::table` diff --git a/diesel_compile_tests/tests/fail/cannot_load_default_select_with_group_by.stderr b/diesel_compile_tests/tests/fail/cannot_load_default_select_with_group_by.stderr index 0c596812cc95..1941f56e400f 100644 --- a/diesel_compile_tests/tests/fail/cannot_load_default_select_with_group_by.stderr +++ b/diesel_compile_tests/tests/fail/cannot_load_default_select_with_group_by.stderr @@ -7,5 +7,5 @@ error[E0271]: type mismatch resolving `` for `columns::id` = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `ValidGrouping` for `(columns::id, columns::name)` - = note: required because of the requirements on the impl of `Query` for `SelectStatement, diesel::query_builder::group_by_clause::GroupByClause>` - = note: required because of the requirements on the impl of `LoadQuery<_, (i32, std::string::String)>` for `SelectStatement, diesel::query_builder::group_by_clause::GroupByClause>` + = note: required because of the requirements on the impl of `Query` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause, diesel::query_builder::group_by_clause::GroupByClause>` + = note: required because of the requirements on the impl of `LoadQuery<_, (i32, std::string::String)>` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause, diesel::query_builder::group_by_clause::GroupByClause>` diff --git a/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.stderr b/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.stderr index 6c13d1d7bc0c..5d9691924e1a 100644 --- a/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.stderr +++ b/diesel_compile_tests/tests/fail/cannot_mix_aggregate_and_non_aggregate_selects.stderr @@ -8,7 +8,7 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggreg > > = note: required because of the requirements on the impl of `ValidGrouping<()>` for `(columns::id, CountStar)` - = note: required because of the requirements on the impl of `SelectDsl<(columns::id, CountStar)>` for `SelectStatement` + = note: required because of the requirements on the impl of `SelectDsl<(columns::id, CountStar)>` for `SelectStatement>` error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggregates` is not satisfied --> $DIR/cannot_mix_aggregate_and_non_aggregate_selects.rs:22:24 @@ -20,7 +20,7 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggreg > > = note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::expression::ops::Add, columns::nullable_int_col>>` - = note: required because of the requirements on the impl of `SelectDsl, columns::nullable_int_col>>>` for `SelectStatement` + = note: required because of the requirements on the impl of `SelectDsl, columns::nullable_int_col>>>` for `SelectStatement>` error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggregates` is not satisfied --> $DIR/cannot_mix_aggregate_and_non_aggregate_selects.rs:24:24 @@ -34,4 +34,4 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::No: MixedAggreg = note: required because of the requirements on the impl of `ValidGrouping<()>` for `__Derived, columns::nullable_int_col>>` = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `ValidGrouping<()>` for `f::f, columns::nullable_int_col>>` - = note: required because of the requirements on the impl of `SelectDsl, columns::nullable_int_col>>>` for `SelectStatement` + = note: required because of the requirements on the impl of `SelectDsl, columns::nullable_int_col>>>` for `SelectStatement>` diff --git a/diesel_compile_tests/tests/fail/cannot_pass_aggregate_to_where.stderr b/diesel_compile_tests/tests/fail/cannot_pass_aggregate_to_where.stderr index 21ccb4124955..871a8db73c71 100644 --- a/diesel_compile_tests/tests/fail/cannot_pass_aggregate_to_where.stderr +++ b/diesel_compile_tests/tests/fail/cannot_pass_aggregate_to_where.stderr @@ -7,4 +7,4 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::Yes: MixedAggre = help: the following implementations were found: > > - = note: required because of the requirements on the impl of `FilterDsl, diesel::expression::bound::Bound>>>` for `SelectStatement` + = note: required because of the requirements on the impl of `FilterDsl, diesel::expression::bound::Bound>>>` for `SelectStatement>` diff --git a/diesel_compile_tests/tests/fail/cannot_update_target_with_methods_other_than_filter_called.stderr b/diesel_compile_tests/tests/fail/cannot_update_target_with_methods_other_than_filter_called.stderr index 97f1bbf0c298..c3b6924243c0 100644 --- a/diesel_compile_tests/tests/fail/cannot_update_target_with_methods_other_than_filter_called.stderr +++ b/diesel_compile_tests/tests/fail/cannot_update_target_with_methods_other_than_filter_called.stderr @@ -1,25 +1,25 @@ -error[E0277]: the trait bound `SelectStatement>: Identifiable` is not satisfied +error[E0277]: the trait bound `SelectStatement, diesel::query_builder::select_clause::SelectClause>: Identifiable` is not satisfied --> $DIR/cannot_update_target_with_methods_other_than_filter_called.rs:15:26 | 15 | let command = update(users.select(id)).set(id.eq(1)); - | ^^^^^^^^^^^^^^^^ the trait `Identifiable` is not implemented for `SelectStatement>` + | ^^^^^^^^^^^^^^^^ the trait `Identifiable` is not implemented for `SelectStatement, diesel::query_builder::select_clause::SelectClause>` | ::: $DIESEL/src/query_builder/functions.rs | | pub fn update(source: T) -> UpdateStatement { | ---------------- required by this bound in `diesel::update` | - = note: required because of the requirements on the impl of `IntoUpdateTarget` for `SelectStatement>` + = note: required because of the requirements on the impl of `IntoUpdateTarget` for `SelectStatement, diesel::query_builder::select_clause::SelectClause>` -error[E0277]: the trait bound `SelectStatement>: Identifiable` is not satisfied +error[E0277]: the trait bound `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::OrderClause>: Identifiable` is not satisfied --> $DIR/cannot_update_target_with_methods_other_than_filter_called.rs:16:26 | 16 | let command = update(users.order(id)).set(id.eq(1)); - | ^^^^^^^^^^^^^^^ the trait `Identifiable` is not implemented for `SelectStatement>` + | ^^^^^^^^^^^^^^^ the trait `Identifiable` is not implemented for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::OrderClause>` | ::: $DIESEL/src/query_builder/functions.rs | | pub fn update(source: T) -> UpdateStatement { | ---------------- required by this bound in `diesel::update` | - = note: required because of the requirements on the impl of `IntoUpdateTarget` for `SelectStatement>` + = note: required because of the requirements on the impl of `IntoUpdateTarget` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::OrderClause>` diff --git a/diesel_compile_tests/tests/fail/distinct_on_clause_only_supported_for_pg.stderr b/diesel_compile_tests/tests/fail/distinct_on_clause_only_supported_for_pg.stderr index 1b3948876a70..09a4f7ce3115 100644 --- a/diesel_compile_tests/tests/fail/distinct_on_clause_only_supported_for_pg.stderr +++ b/diesel_compile_tests/tests/fail/distinct_on_clause_only_supported_for_pg.stderr @@ -4,7 +4,7 @@ error[E0271]: type mismatch resolving `` for `SelectStatement>` + = note: required because of the requirements on the impl of `LoadQuery` for `SelectStatement, DefaultSelectClause>, DistinctOnClause>` error[E0271]: type mismatch resolving `::Backend == Pg` --> $DIR/distinct_on_clause_only_supported_for_pg.rs:20:29 @@ -12,4 +12,4 @@ error[E0271]: type mismatch resolving `` for `SelectStatement>` + = note: required because of the requirements on the impl of `LoadQuery` for `SelectStatement, DefaultSelectClause>, DistinctOnClause>` diff --git a/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.stderr b/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.stderr index a83d351cfa16..ac11106341bd 100644 --- a/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.stderr +++ b/diesel_compile_tests/tests/fail/distinct_on_requires_matching_order_clause.stderr @@ -22,7 +22,7 @@ error[E0277]: the trait bound `columns::id: query_dsl::order_dsl::ValidOrderingF 63 | let _ = users::table.distinct_on(users::name).order_by(users::id); | ^^^^^^^^^ the trait `query_dsl::order_dsl::ValidOrderingForDistinct>` is not implemented for `columns::id` | - = note: required because of the requirements on the impl of `OrderDsl` for `SelectStatement>` + = note: required because of the requirements on the impl of `OrderDsl` for `SelectStatement, DefaultSelectClause>, DistinctOnClause>` error[E0277]: the trait bound `diesel::query_builder::order_clause::OrderClause: query_dsl::order_dsl::ValidOrderingForDistinct>` is not satisfied --> $DIR/distinct_on_requires_matching_order_clause.rs:63:51 @@ -36,8 +36,8 @@ error[E0277]: the trait bound `diesel::query_builder::order_clause::OrderClause< as query_dsl::order_dsl::ValidOrderingForDistinct>> as query_dsl::order_dsl::ValidOrderingForDistinct>> and 14 others - = note: required because of the requirements on the impl of `SelectQuery` for `SelectStatement, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::OrderClause>` - = note: required because of the requirements on the impl of `OrderDsl` for `SelectStatement>` + = note: required because of the requirements on the impl of `SelectQuery` for `SelectStatement, DefaultSelectClause>, DistinctOnClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::OrderClause>` + = note: required because of the requirements on the impl of `OrderDsl` for `SelectStatement, DefaultSelectClause>, DistinctOnClause>` error[E0308]: mismatched types --> $DIR/distinct_on_requires_matching_order_clause.rs:66:58 @@ -63,7 +63,7 @@ error[E0277]: the trait bound `columns::id: query_dsl::order_dsl::ValidOrderingF 84 | .order_by(users::id) | ^^^^^^^^^ the trait `query_dsl::order_dsl::ValidOrderingForDistinct>` is not implemented for `columns::id` | - = note: required because of the requirements on the impl of `OrderDsl` for `SelectStatement>` + = note: required because of the requirements on the impl of `OrderDsl` for `SelectStatement, DefaultSelectClause>, DistinctOnClause>` error[E0277]: the trait bound `diesel::query_builder::order_clause::OrderClause: query_dsl::order_dsl::ValidOrderingForDistinct>` is not satisfied --> $DIR/distinct_on_requires_matching_order_clause.rs:84:10 @@ -77,5 +77,5 @@ error[E0277]: the trait bound `diesel::query_builder::order_clause::OrderClause< as query_dsl::order_dsl::ValidOrderingForDistinct>> as query_dsl::order_dsl::ValidOrderingForDistinct>> and 14 others - = note: required because of the requirements on the impl of `SelectQuery` for `SelectStatement, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::OrderClause>` - = note: required because of the requirements on the impl of `OrderDsl` for `SelectStatement>` + = note: required because of the requirements on the impl of `SelectQuery` for `SelectStatement, DefaultSelectClause>, DistinctOnClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::OrderClause>` + = note: required because of the requirements on the impl of `OrderDsl` for `SelectStatement, DefaultSelectClause>, DistinctOnClause>` diff --git a/diesel_compile_tests/tests/fail/exists_can_only_take_subselects.stderr b/diesel_compile_tests/tests/fail/exists_can_only_take_subselects.stderr index 489a2fde58f0..abf40f19ace1 100644 --- a/diesel_compile_tests/tests/fail/exists_can_only_take_subselects.stderr +++ b/diesel_compile_tests/tests/fail/exists_can_only_take_subselects.stderr @@ -7,7 +7,7 @@ error[E0277]: the trait bound `bool: SelectQuery` is not satisfied = note: required because of the requirements on the impl of `diesel::Expression` for `Subselect` = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `diesel::Expression` for `Exists` - = note: required because of the requirements on the impl of `FilterDsl>` for `SelectStatement` + = note: required because of the requirements on the impl of `FilterDsl>` for `SelectStatement>` error[E0277]: the trait bound `users::columns::id: diesel::Expression` is not satisfied --> $DIR/exists_can_only_take_subselects.rs:24:25 @@ -21,4 +21,4 @@ error[E0277]: the trait bound `users::columns::id: diesel::Expression` is not sa = note: required because of the requirements on the impl of `diesel::Expression` for `Subselect` = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `diesel::Expression` for `Exists` - = note: required because of the requirements on the impl of `FilterDsl>` for `SelectStatement` + = note: required because of the requirements on the impl of `FilterDsl>` for `SelectStatement>` diff --git a/diesel_compile_tests/tests/fail/filter_cannot_take_comparison_for_columns_from_another_table.stderr b/diesel_compile_tests/tests/fail/filter_cannot_take_comparison_for_columns_from_another_table.stderr index f6c8f2179b59..60ada61cf9b7 100644 --- a/diesel_compile_tests/tests/fail/filter_cannot_take_comparison_for_columns_from_another_table.stderr +++ b/diesel_compile_tests/tests/fail/filter_cannot_take_comparison_for_columns_from_another_table.stderr @@ -7,9 +7,9 @@ error[E0271]: type mismatch resolving `` for `posts::columns::id` = note: 2 redundant requirements hidden = note: required because of the requirements on the impl of `AppearsOnTable` for `Grouped>>` - = note: required because of the requirements on the impl of `diesel::query_builder::where_clause::ValidWhereClause` for `diesel::query_builder::where_clause::WhereClause>>>` - = note: required because of the requirements on the impl of `Query` for `SelectStatement>>>>` - = note: required because of the requirements on the impl of `LoadQuery<_, User>` for `SelectStatement>>>>` + = note: required because of the requirements on the impl of `diesel::query_builder::where_clause::ValidWhereClause>` for `diesel::query_builder::where_clause::WhereClause>>>` + = note: required because of the requirements on the impl of `Query` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>>` + = note: required because of the requirements on the impl of `LoadQuery<_, User>` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>>` error[E0271]: type mismatch resolving `>::Count == diesel::query_source::Once` --> $DIR/filter_cannot_take_comparison_for_columns_from_another_table.rs:36:10 @@ -20,16 +20,17 @@ error[E0271]: type mismatch resolving `` for `posts::columns::id` = note: 2 redundant requirements hidden = note: required because of the requirements on the impl of `AppearsOnTable` for `Grouped>>` - = note: required because of the requirements on the impl of `FilterDsl>>>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), users::table, Pg>` + = note: required because of the requirements on the impl of `FilterDsl>>>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), FromClause, Pg>` -error[E0277]: the trait bound `SelectStatement>>>>: BoxedDsl<'_, Pg>` is not satisfied +error[E0277]: the trait bound `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>>: BoxedDsl<'_, Pg>` is not satisfied --> $DIR/filter_cannot_take_comparison_for_columns_from_another_table.rs:39:10 | 39 | .into_boxed::(); - | ^^^^^^^^^^ the trait `BoxedDsl<'_, Pg>` is not implemented for `SelectStatement>>>>` + | ^^^^^^^^^^ the trait `BoxedDsl<'_, Pg>` is not implemented for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>>` | = help: the following implementations were found: - as BoxedDsl<'a, DB>> + , S, D, W, O, LOf, G, H> as BoxedDsl<'a, DB>> + as BoxedDsl<'a, DB>> error[E0271]: type mismatch resolving `>::Count == diesel::query_source::Once` --> $DIR/filter_cannot_take_comparison_for_columns_from_another_table.rs:43:10 @@ -40,9 +41,9 @@ error[E0271]: type mismatch resolving `` for `posts::columns::title` = note: 2 redundant requirements hidden = note: required because of the requirements on the impl of `AppearsOnTable` for `Grouped>` - = note: required because of the requirements on the impl of `diesel::query_builder::where_clause::ValidWhereClause` for `diesel::query_builder::where_clause::WhereClause>>` - = note: required because of the requirements on the impl of `Query` for `SelectStatement>>>` - = note: required because of the requirements on the impl of `LoadQuery<_, User>` for `SelectStatement>>>` + = note: required because of the requirements on the impl of `diesel::query_builder::where_clause::ValidWhereClause>` for `diesel::query_builder::where_clause::WhereClause>>` + = note: required because of the requirements on the impl of `Query` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>` + = note: required because of the requirements on the impl of `LoadQuery<_, User>` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>` error[E0271]: type mismatch resolving `>::Count == diesel::query_source::Once` --> $DIR/filter_cannot_take_comparison_for_columns_from_another_table.rs:47:10 @@ -53,13 +54,14 @@ error[E0271]: type mismatch resolving `` for `posts::columns::title` = note: 2 redundant requirements hidden = note: required because of the requirements on the impl of `AppearsOnTable` for `Grouped>` - = note: required because of the requirements on the impl of `FilterDsl>>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), users::table, Pg>` + = note: required because of the requirements on the impl of `FilterDsl>>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), FromClause, Pg>` -error[E0277]: the trait bound `SelectStatement>>>: BoxedDsl<'_, Pg>` is not satisfied +error[E0277]: the trait bound `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>: BoxedDsl<'_, Pg>` is not satisfied --> $DIR/filter_cannot_take_comparison_for_columns_from_another_table.rs:51:10 | 51 | .into_boxed::(); - | ^^^^^^^^^^ the trait `BoxedDsl<'_, Pg>` is not implemented for `SelectStatement>>>` + | ^^^^^^^^^^ the trait `BoxedDsl<'_, Pg>` is not implemented for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>` | = help: the following implementations were found: - as BoxedDsl<'a, DB>> + , S, D, W, O, LOf, G, H> as BoxedDsl<'a, DB>> + as BoxedDsl<'a, DB>> diff --git a/diesel_compile_tests/tests/fail/filter_requires_bool_nonaggregate_expression.stderr b/diesel_compile_tests/tests/fail/filter_requires_bool_nonaggregate_expression.stderr index af91df852740..e536febb2968 100644 --- a/diesel_compile_tests/tests/fail/filter_requires_bool_nonaggregate_expression.stderr +++ b/diesel_compile_tests/tests/fail/filter_requires_bool_nonaggregate_expression.stderr @@ -4,7 +4,7 @@ error[E0277]: the trait bound `diesel::sql_types::Text: BoolOrNullableBool` is n 15 | let _ = users::table.filter(users::name); | ^^^^^^ the trait `BoolOrNullableBool` is not implemented for `diesel::sql_types::Text` | - = note: required because of the requirements on the impl of `FilterDsl` for `SelectStatement` + = note: required because of the requirements on the impl of `FilterDsl` for `SelectStatement>` error[E0277]: the trait bound `diesel::expression::is_aggregate::Yes: MixedAggregates` is not satisfied --> $DIR/filter_requires_bool_nonaggregate_expression.rs:16:26 @@ -15,4 +15,4 @@ error[E0277]: the trait bound `diesel::expression::is_aggregate::Yes: MixedAggre = help: the following implementations were found: > > - = note: required because of the requirements on the impl of `FilterDsl, diesel::expression::bound::Bound, i64>>>>` for `SelectStatement` + = note: required because of the requirements on the impl of `FilterDsl, diesel::expression::bound::Bound, i64>>>>` for `SelectStatement>` diff --git a/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr b/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr index bbd61b0fcea7..11a4c89d83c5 100644 --- a/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr +++ b/diesel_compile_tests/tests/fail/find_requires_correct_type.stderr @@ -7,7 +7,7 @@ error[E0277]: the trait bound `str: diesel::Expression` is not satisfied = note: required because of the requirements on the impl of `diesel::Expression` for `&str` = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `diesel::Expression` for `diesel::expression::operators::Eq` - = note: required because of the requirements on the impl of `FilterDsl>>` for `SelectStatement` + = note: required because of the requirements on the impl of `FilterDsl>>` for `SelectStatement>` error[E0277]: the trait bound `str: ValidGrouping<()>` is not satisfied --> $DIR/find_requires_correct_type.rs:20:33 @@ -18,7 +18,7 @@ error[E0277]: the trait bound `str: ValidGrouping<()>` is not satisfied = note: required because of the requirements on the impl of `ValidGrouping<()>` for `&str` = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::expression::operators::Eq` - = note: required because of the requirements on the impl of `FilterDsl>>` for `SelectStatement` + = note: required because of the requirements on the impl of `FilterDsl>>` for `SelectStatement>` error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied --> $DIR/find_requires_correct_type.rs:22:36 @@ -33,7 +33,7 @@ error[E0277]: the trait bound `{integer}: diesel::Expression` is not satisfied <(A, B, C, D) as diesel::Expression> and 110 others = note: required because of the requirements on the impl of `diesel::Expression` for `diesel::expression::operators::Eq` - = note: required because of the requirements on the impl of `FilterDsl>>` for `SelectStatement` + = note: required because of the requirements on the impl of `FilterDsl>>` for `SelectStatement>` error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied --> $DIR/find_requires_correct_type.rs:22:36 @@ -48,4 +48,4 @@ error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied <(A, B, C, D) as ValidGrouping<__GroupByClause>> and 131 others = note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::expression::operators::Eq` - = note: required because of the requirements on the impl of `FilterDsl>>` for `SelectStatement` + = note: required because of the requirements on the impl of `FilterDsl>>` for `SelectStatement>` diff --git a/diesel_compile_tests/tests/fail/having_cant_be_used_without_group_by.stderr b/diesel_compile_tests/tests/fail/having_cant_be_used_without_group_by.stderr index 2d7e3b3b62e4..74d73ed23bfa 100644 --- a/diesel_compile_tests/tests/fail/having_cant_be_used_without_group_by.stderr +++ b/diesel_compile_tests/tests/fail/having_cant_be_used_without_group_by.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `SelectStatement>: HavingDsl<_>` is not satisfied +error[E0277]: the trait bound `SelectStatement, diesel::query_builder::select_clause::SelectClause>: HavingDsl<_>` is not satisfied --> $DIR/having_cant_be_used_without_group_by.rs:26:38 | 26 | users::table.select(users::name).having(users::id.gt(1)).load(&mut conn); - | ^^^^^^ the trait `HavingDsl<_>` is not implemented for `SelectStatement>` + | ^^^^^^ the trait `HavingDsl<_>` is not implemented for `SelectStatement, diesel::query_builder::select_clause::SelectClause>` | = help: the following implementations were found: , H> as HavingDsl> @@ -13,26 +13,26 @@ error[E0277]: the trait bound `(): diesel::Expression` is not satisfied 28 | users::table.into_boxed().having(users::id.gt(1)).load(&mut conn); | ^^^^^^ the trait `diesel::Expression` is not implemented for `()` | - = note: required because of the requirements on the impl of `HavingDsl<_>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), users::table, _>` + = note: required because of the requirements on the impl of `HavingDsl<_>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer, diesel::sql_types::Text), FromClause, _>` -error[E0271]: type mismatch resolving `>::Count == diesel::query_source::Once` +error[E0271]: type mismatch resolving ` as AppearsInFromClause>::Count == diesel::query_source::Once` --> $DIR/having_cant_be_used_without_group_by.rs:30:58 | 30 | users::table.select(users::name).group_by(users::id).having(posts::id.eq(42)).load(&mut conn); | ^^^^^^ expected struct `diesel::query_source::Never`, found struct `diesel::query_source::Once` | - = note: required because of the requirements on the impl of `AppearsOnTable` for `posts::columns::id` + = note: required because of the requirements on the impl of `AppearsOnTable>` for `posts::columns::id` = note: 2 redundant requirements hidden - = note: required because of the requirements on the impl of `AppearsOnTable` for `Grouped>>` - = note: required because of the requirements on the impl of `HavingDsl>>>` for `SelectStatement, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause, diesel::query_builder::group_by_clause::GroupByClause>` + = note: required because of the requirements on the impl of `AppearsOnTable>` for `Grouped>>` + = note: required because of the requirements on the impl of `HavingDsl>>>` for `SelectStatement, diesel::query_builder::select_clause::SelectClause, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::NoWhereClause, diesel::query_builder::order_clause::NoOrderClause, LimitOffsetClause, diesel::query_builder::group_by_clause::GroupByClause>` -error[E0271]: type mismatch resolving `>::Count == diesel::query_source::Once` +error[E0271]: type mismatch resolving ` as AppearsInFromClause>::Count == diesel::query_source::Once` --> $DIR/having_cant_be_used_without_group_by.rs:32:71 | 32 | users::table.select(users::name).group_by(users::id).into_boxed().having(posts::id.eq(42)).load(&mut conn); | ^^^^^^ expected struct `diesel::query_source::Never`, found struct `diesel::query_source::Once` | - = note: required because of the requirements on the impl of `AppearsOnTable` for `posts::columns::id` + = note: required because of the requirements on the impl of `AppearsOnTable>` for `posts::columns::id` = note: 2 redundant requirements hidden - = note: required because of the requirements on the impl of `AppearsOnTable` for `Grouped>>` - = note: required because of the requirements on the impl of `HavingDsl>>>` for `BoxedSelectStatement<'_, diesel::sql_types::Text, users::table, _, users::columns::id>` + = note: required because of the requirements on the impl of `AppearsOnTable>` for `Grouped>>` + = note: required because of the requirements on the impl of `HavingDsl>>>` for `BoxedSelectStatement<'_, diesel::sql_types::Text, FromClause, _, users::columns::id>` diff --git a/diesel_compile_tests/tests/fail/insert_from_select_cant_be_used_with_tuples_or_arrays.stderr b/diesel_compile_tests/tests/fail/insert_from_select_cant_be_used_with_tuples_or_arrays.stderr index 8e83aaad1b1c..21315cbb3b27 100644 --- a/diesel_compile_tests/tests/fail/insert_from_select_cant_be_used_with_tuples_or_arrays.stderr +++ b/diesel_compile_tests/tests/fail/insert_from_select_cant_be_used_with_tuples_or_arrays.stderr @@ -13,6 +13,6 @@ error[E0271]: type mismatch resolving `, (posts::columns::user_id, posts::columns::title, posts::columns::body)>` + = note: expected struct `diesel::query_builder::insert_statement::insert_from_select::InsertFromSelect>, (posts::columns::user_id, posts::columns::title, posts::columns::body)>` found struct `ValuesClause<_, posts::table>` = note: required because of the requirements on the impl of `diesel::Insertable` for `(users::table, users::table)` diff --git a/diesel_compile_tests/tests/fail/insert_from_select_requires_valid_column_list.stderr b/diesel_compile_tests/tests/fail/insert_from_select_requires_valid_column_list.stderr index 6c42d0a26eab..cdd38d7c6240 100644 --- a/diesel_compile_tests/tests/fail/insert_from_select_requires_valid_column_list.stderr +++ b/diesel_compile_tests/tests/fail/insert_from_select_requires_valid_column_list.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving `> as Query>::SqlType == (diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable)` +error[E0271]: type mismatch resolving `, diesel::query_builder::select_clause::SelectClause<(users::columns::name, users::columns::hair_color)>> as Query>::SqlType == (diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable)` --> $DIR/insert_from_select_requires_valid_column_list.rs:57:10 | 57 | .execute(&mut conn) @@ -6,10 +6,10 @@ error[E0271]: type mismatch resolving `)` found tuple `(diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Nullable)` - = note: required because of the requirements on the impl of `QueryFragment<_>` for `diesel::query_builder::insert_statement::insert_from_select::InsertFromSelect>, (posts::columns::user_id, posts::columns::title, posts::columns::body)>` + = note: required because of the requirements on the impl of `QueryFragment<_>` for `diesel::query_builder::insert_statement::insert_from_select::InsertFromSelect, diesel::query_builder::select_clause::SelectClause<(users::columns::name, users::columns::hair_color)>>, (posts::columns::user_id, posts::columns::title, posts::columns::body)>` = note: 1 redundant requirements hidden - = note: required because of the requirements on the impl of `QueryFragment<_>` for `InsertStatement>, (posts::columns::user_id, posts::columns::title, posts::columns::body)>>` - = note: required because of the requirements on the impl of `ExecuteDsl<_, _>` for `InsertStatement>, (posts::columns::user_id, posts::columns::title, posts::columns::body)>>` + = note: required because of the requirements on the impl of `QueryFragment<_>` for `InsertStatement, diesel::query_builder::select_clause::SelectClause<(users::columns::name, users::columns::hair_color)>>, (posts::columns::user_id, posts::columns::title, posts::columns::body)>>` + = note: required because of the requirements on the impl of `ExecuteDsl<_, _>` for `InsertStatement, diesel::query_builder::select_clause::SelectClause<(users::columns::name, users::columns::hair_color)>>, (posts::columns::user_id, posts::columns::title, posts::columns::body)>>` error[E0271]: type mismatch resolving `::Table == posts::table` --> $DIR/insert_from_select_requires_valid_column_list.rs:63:10 diff --git a/diesel_compile_tests/tests/fail/join_with_explicit_on_requires_valid_boolean_expression.stderr b/diesel_compile_tests/tests/fail/join_with_explicit_on_requires_valid_boolean_expression.stderr index 409dbdf658b6..f1c9585e921d 100644 --- a/diesel_compile_tests/tests/fail/join_with_explicit_on_requires_valid_boolean_expression.stderr +++ b/diesel_compile_tests/tests/fail/join_with_explicit_on_requires_valid_boolean_expression.stderr @@ -8,9 +8,7 @@ error[E0271]: type mismatch resolving `QueryFragment for AutoIncrementwhere Col: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, Sqlite>) -> QueryResult<()> + where + 'a: 'b, + { out.unsafe_to_cache_prepared(); self.0.walk_ast(out.reborrow())?; out.push_sql(" AUTOINCREMENT"); @@ -162,7 +174,7 @@ implQueryFragment for AutoIncrementwhere Col: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Mysql>) -> QueryResult<()> { out.unsafe_to_cache_prepared(); self.0.walk_ast(out.reborrow())?; out.push_sql(" AUTO_INCREMENT"); @@ -178,7 +190,7 @@ implQueryId for AutoIncrement{ #[cfg(feature = "postgres")] impl<'a> QueryFragment for AutoIncrement>> { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'b: 'c, 'c>(&'b self, mut out: AstPass<'_, 'c, Pg>) -> QueryResult<()> { out.unsafe_to_cache_prepared(); out.push_identifier((self.0).0.name)?; out.push_sql(" SERIAL PRIMARY KEY"); @@ -191,7 +203,10 @@ where DB: Backend, Col: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a, 'b>(&'a self, mut out: AstPass<'_, 'b, DB>) -> QueryResult<()> + where + 'a: 'b, + { out.unsafe_to_cache_prepared(); self.0.walk_ast(out.reborrow())?; out.push_sql(" NOT NULL"); @@ -210,7 +225,10 @@ where DB: Backend, Col: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'b, 'c>(&'b self, mut out: AstPass<'_, 'c, DB>) -> QueryResult<()> + where + 'b: 'c, + { out.unsafe_to_cache_prepared(); self.column.walk_ast(out.reborrow())?; out.push_sql(" DEFAULT "); diff --git a/diesel_tests/tests/serialize_as.rs b/diesel_tests/tests/serialize_as.rs index 93ae5048b168..609c97874ba5 100644 --- a/diesel_tests/tests/serialize_as.rs +++ b/diesel_tests/tests/serialize_as.rs @@ -2,7 +2,6 @@ use crate::schema::*; use diesel::backend::Backend; use diesel::serialize::{Output, ToSql}; use diesel::*; -use std::io::Write; #[derive(Debug, FromSqlRow, AsExpression)] #[sql_type = "sql_types::Text"] @@ -19,7 +18,10 @@ where DB: Backend, String: ToSql, { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a, 'b, 'c>(&'a self, out: &mut Output<'b, 'c, DB>) -> serialize::Result + where + 'a: 'b, + { self.0.to_sql(out) } } diff --git a/examples/postgres/advanced-blog-cli/src/pagination.rs b/examples/postgres/advanced-blog-cli/src/pagination.rs index b761b0c484a4..bcf30d8fd87e 100644 --- a/examples/postgres/advanced-blog-cli/src/pagination.rs +++ b/examples/postgres/advanced-blog-cli/src/pagination.rs @@ -14,6 +14,7 @@ impl Paginate for T { query: self, per_page: DEFAULT_PER_PAGE, page, + offset: (page - 1) * DEFAULT_PER_PAGE, } } } @@ -25,11 +26,16 @@ pub struct Paginated { query: T, page: i64, per_page: i64, + offset: i64, } impl Paginated { pub fn per_page(self, per_page: i64) -> Self { - Paginated { per_page, ..self } + Paginated { + per_page, + offset: (self.page - 1) * per_page, + ..self + } } pub fn load_and_count_pages(self, conn: &mut PgConnection) -> QueryResult<(Vec, i64)> @@ -55,14 +61,13 @@ impl QueryFragment for Paginated where T: QueryFragment, { - fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + fn walk_ast<'a: 'b, 'b>(&'a self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> { out.push_sql("SELECT *, COUNT(*) OVER () FROM ("); self.query.walk_ast(out.reborrow())?; out.push_sql(") t LIMIT "); out.push_bind_param::(&self.per_page)?; out.push_sql(" OFFSET "); - let offset = (self.page - 1) * self.per_page; - out.push_bind_param::(&offset)?; + out.push_bind_param::(&self.offset)?; Ok(()) } } diff --git a/examples/postgres/custom_types/src/model.rs b/examples/postgres/custom_types/src/model.rs index 3648b3bc8d94..e40e65d5e703 100644 --- a/examples/postgres/custom_types/src/model.rs +++ b/examples/postgres/custom_types/src/model.rs @@ -13,7 +13,7 @@ pub enum Language { } impl ToSql for Language { - fn to_sql(&self, out: &mut Output) -> serialize::Result { + fn to_sql<'a: 'b, 'b>(&'a self, out: &mut Output<'b, '_, Pg>) -> serialize::Result { match *self { Language::En => out.write_all(b"en")?, Language::Ru => out.write_all(b"ru")?, From c6cb5e0464eb2405f694eec240ea4253ebd210f8 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Wed, 3 Nov 2021 14:09:35 +0100 Subject: [PATCH 02/18] Improve the sqlite bind collector to skip even more copies This improves the sqlite bind collector to skip even more data copies, by setting the sqlite bind mode to `STATIC`. This prevents that sqlite creates a internal copy of the bind data, which means we must ensure that the data live at least as long as the buffer is bound to the statement. This requires some unsafe hackery to ensure that all data remain avaible. We ensure this by binding all information to `StatementUse` itself, which lives as long as the user can access the executed query. If `StatementUse` is dropped we cleanup the corresponding buffers. --- diesel/src/sqlite/connection/mod.rs | 27 +-- diesel/src/sqlite/connection/stmt.rs | 259 ++++++++++++++++++------- diesel/src/sqlite/query_builder/mod.rs | 24 ++- diesel_tests/tests/internal_details.rs | 16 ++ 4 files changed, 230 insertions(+), 96 deletions(-) diff --git a/diesel/src/sqlite/connection/mod.rs b/diesel/src/sqlite/connection/mod.rs index 0c479433367b..70503eca8fb4 100644 --- a/diesel/src/sqlite/connection/mod.rs +++ b/diesel/src/sqlite/connection/mod.rs @@ -97,9 +97,8 @@ impl Connection for SqliteConnection { T::Query: QueryFragment + QueryId, Self::Backend: QueryMetadata, { - let stmt = self.prepared_query(&source.as_query())?; + let statement_use = self.prepared_query(source.as_query())?; - let statement_use = StatementUse::new(stmt); Ok(StatementIterator::new(statement_use)) } @@ -108,9 +107,7 @@ impl Connection for SqliteConnection { where T: QueryFragment + QueryId, { - let stmt = self.prepared_query(source)?; - - let statement_use = StatementUse::new(stmt); + let statement_use = self.prepared_query(source)?; statement_use.run()?; Ok(self.raw_connection.rows_affected_by_last_query()) @@ -201,25 +198,17 @@ impl SqliteConnection { } } - fn prepared_query<'a, T: QueryFragment + QueryId>( - &'a mut self, - source: &'_ T, - ) -> QueryResult> { + fn prepared_query + QueryId>( + &mut self, + source: T, + ) -> QueryResult { let raw_connection = &self.raw_connection; let cache = &mut self.statement_cache; - let mut statement = cache.cached_statement(source, &[], |sql, is_cached| { + let statement = cache.cached_statement(&source, &[], |sql, is_cached| { Statement::prepare(raw_connection, sql, is_cached) })?; - let mut bind_collector = crate::sqlite::query_builder::SqliteBindCollector::new(); - source.collect_binds(&mut bind_collector, &mut ())?; - let metadata = &bind_collector.metadata; - let binds = &bind_collector.binds; - for (tpe, value) in metadata.iter().zip(binds) { - statement.bind(tpe, value)?; - } - - Ok(statement) + StatementUse::bind(statement, source) } #[doc(hidden)] diff --git a/diesel/src/sqlite/connection/stmt.rs b/diesel/src/sqlite/connection/stmt.rs index c3ee6e618499..57dbb3472a4d 100644 --- a/diesel/src/sqlite/connection/stmt.rs +++ b/diesel/src/sqlite/connection/stmt.rs @@ -3,19 +3,19 @@ extern crate libsqlite3_sys as ffi; use super::raw::RawConnection; use super::sqlite_value::OwnedSqliteValue; use crate::connection::{MaybeCached, PrepareForCache}; +use crate::query_builder::{QueryFragment, QueryId}; use crate::result::Error::DatabaseError; use crate::result::*; -use crate::sqlite::query_builder::SqliteBindValue; -use crate::sqlite::SqliteType; +use crate::sqlite::query_builder::{SqliteBindCollector, SqliteBindValue}; +use crate::sqlite::{Sqlite, SqliteType}; use crate::util::OnceCell; use std::ffi::{CStr, CString}; use std::io::{stderr, Write}; use std::os::raw as libc; use std::ptr::{self, NonNull}; - -pub struct Statement { +#[allow(missing_debug_implementations)] +pub(in crate::sqlite) struct Statement { inner_statement: NonNull, - bind_index: libc::c_int, } impl Statement { @@ -44,85 +44,85 @@ impl Statement { ensure_sqlite_ok(prepare_result, raw_connection.internal_connection.as_ptr()).map(|_| { Statement { inner_statement: unsafe { NonNull::new_unchecked(stmt) }, - bind_index: 0, } }) } - pub fn bind(&mut self, tpe: &SqliteType, value: &SqliteBindValue) -> QueryResult<()> { - self.bind_index += 1; + unsafe fn bind( + &mut self, + tpe: SqliteType, + value: &SqliteBindValue, + bind_index: i32, + ) -> QueryResult<()> { // This unsafe block assumes the following invariants: // // - `stmt` points to valid memory // - If `self.ty` is anything other than `Binary` or `Text`, the appropriate // number of bytes were written to `value` for an integer of the // corresponding size. - let result = unsafe { - match (tpe, value) { - (_, SqliteBindValue::Null) => { - ffi::sqlite3_bind_null(self.inner_statement.as_ptr(), self.bind_index) - } - (SqliteType::Binary, SqliteBindValue::BorrowedBinary(bytes)) => { - ffi::sqlite3_bind_blob( - self.inner_statement.as_ptr(), - self.bind_index, - bytes.as_ptr() as *const libc::c_void, - bytes.len() as libc::c_int, - ffi::SQLITE_TRANSIENT(), - ) - } - (SqliteType::Binary, SqliteBindValue::Binary(bytes)) => ffi::sqlite3_bind_blob( - self.inner_statement.as_ptr(), - self.bind_index, - bytes.as_ptr() as *const libc::c_void, - bytes.len() as libc::c_int, - ffi::SQLITE_TRANSIENT(), - ), - (SqliteType::Text, SqliteBindValue::BorrowedString(bytes)) => { - ffi::sqlite3_bind_text( - self.inner_statement.as_ptr(), - self.bind_index, - bytes.as_ptr() as *const libc::c_char, - bytes.len() as libc::c_int, - ffi::SQLITE_TRANSIENT(), - ) - } - (SqliteType::Text, SqliteBindValue::String(bytes)) => ffi::sqlite3_bind_text( - self.inner_statement.as_ptr(), - self.bind_index, - bytes.as_ptr() as *const libc::c_char, - bytes.len() as libc::c_int, - ffi::SQLITE_TRANSIENT(), - ), - (SqliteType::Float, SqliteBindValue::Float(value)) => ffi::sqlite3_bind_double( - self.inner_statement.as_ptr(), - self.bind_index, - libc::c_double::from(*value), - ), - (SqliteType::Double, SqliteBindValue::Double(value)) => ffi::sqlite3_bind_double( - self.inner_statement.as_ptr(), - self.bind_index, - *value as libc::c_double, - ), - (SqliteType::SmallInt, SqliteBindValue::SmallInt(value)) => ffi::sqlite3_bind_int( - self.inner_statement.as_ptr(), - self.bind_index, - libc::c_int::from(*value), - ), - (SqliteType::Integer, SqliteBindValue::Integer(value)) => { - ffi::sqlite3_bind_int(self.inner_statement.as_ptr(), self.bind_index, *value) - } - (SqliteType::Long, SqliteBindValue::BigInt(value)) => { - ffi::sqlite3_bind_int64(self.inner_statement.as_ptr(), self.bind_index, *value) - } - _ => unreachable!(), + let result = match (tpe, value) { + (_, SqliteBindValue::Null) => { + ffi::sqlite3_bind_null(self.inner_statement.as_ptr(), bind_index) + } + (SqliteType::Binary, SqliteBindValue::BorrowedBinary(bytes)) => ffi::sqlite3_bind_blob( + self.inner_statement.as_ptr(), + bind_index, + bytes.as_ptr() as *const libc::c_void, + bytes.len() as libc::c_int, + ffi::SQLITE_STATIC(), + ), + (SqliteType::Binary, SqliteBindValue::Binary(bytes)) => ffi::sqlite3_bind_blob( + self.inner_statement.as_ptr(), + bind_index, + bytes.as_ptr() as *const libc::c_void, + bytes.len() as libc::c_int, + ffi::SQLITE_STATIC(), + ), + (SqliteType::Text, SqliteBindValue::BorrowedString(bytes)) => ffi::sqlite3_bind_text( + self.inner_statement.as_ptr(), + bind_index, + bytes.as_ptr() as *const libc::c_char, + bytes.len() as libc::c_int, + ffi::SQLITE_STATIC(), + ), + (SqliteType::Text, SqliteBindValue::String(bytes)) => ffi::sqlite3_bind_text( + self.inner_statement.as_ptr(), + bind_index, + bytes.as_ptr() as *const libc::c_char, + bytes.len() as libc::c_int, + ffi::SQLITE_STATIC(), + ), + (SqliteType::Float, SqliteBindValue::Float(value)) => ffi::sqlite3_bind_double( + self.inner_statement.as_ptr(), + bind_index, + libc::c_double::from(*value), + ), + (SqliteType::Double, SqliteBindValue::Double(value)) => ffi::sqlite3_bind_double( + self.inner_statement.as_ptr(), + bind_index, + *value as libc::c_double, + ), + (SqliteType::SmallInt, SqliteBindValue::SmallInt(value)) => ffi::sqlite3_bind_int( + self.inner_statement.as_ptr(), + bind_index, + libc::c_int::from(*value), + ), + (SqliteType::Integer, SqliteBindValue::Integer(value)) => { + ffi::sqlite3_bind_int(self.inner_statement.as_ptr(), bind_index, *value) + } + (SqliteType::Long, SqliteBindValue::BigInt(value)) => { + ffi::sqlite3_bind_int64(self.inner_statement.as_ptr(), bind_index, *value) + } + (t, b) => { + return Err(Error::DeserializationError( + format!("Type missmatch: Expected {:?}, got {}", t, b).into(), + )) } }; ensure_sqlite_ok(result, self.raw_connection()) } fn reset(&mut self) { - self.bind_index = 0; unsafe { ffi::sqlite3_reset(self.inner_statement.as_ptr()) }; } @@ -191,14 +191,90 @@ impl Drop for Statement { pub struct StatementUse<'a> { statement: MaybeCached<'a, Statement>, column_names: OnceCell>, + binds_to_free: Vec, + owned_strings_to_free: Vec<&'static mut str>, + owned_slices_to_free: Vec<&'static mut [u8]>, + query: NonNull, + drop_query: Box, } impl<'a> StatementUse<'a> { - pub(in crate::sqlite::connection) fn new(statement: MaybeCached<'a, Statement>) -> Self { - StatementUse { + pub(super) fn bind(mut statement: MaybeCached<'a, Statement>, query: T) -> QueryResult + where + T: QueryFragment + QueryId, + { + let mut bind_collector = SqliteBindCollector::new(); + query.collect_binds(&mut bind_collector, &mut ())?; + + let SqliteBindCollector { metadata, binds } = bind_collector; + let mut owned_strings_to_free = Vec::new(); + let mut owned_slices_to_free = Vec::new(); + let mut binds_to_free = Vec::new(); + + for (idx, (bind, tpe)) in binds.into_iter().zip(metadata).enumerate() { + // Sqlite starts to count by 1 + let bind_idx = idx as i32 + 1; + // It's safe to call bind here as: + // * The type and value matches + // * We ensure that corresponding buffers lives long enough below + // * The statement is not used yet by `step` or anything else + unsafe { + statement.bind(tpe, &bind, bind_idx)?; + } + + match bind { + SqliteBindValue::String(s) => { + // We leak owned strings here + // so that they we can be sure that + // the buffer remains at its location + // till we unbind it from sqlite. + // For the unbinding we need to know the + // bind index_number, so we collect that here + // + // At that point we need to free the memory there, + // so we collect the corresponding pointer here + binds_to_free.push(bind_idx); + let ptr = Box::leak(s); + owned_strings_to_free.push(ptr); + } + SqliteBindValue::Binary(b) => { + // Same as for Strings + binds_to_free.push(bind_idx); + let ptr = Box::leak(b); + owned_slices_to_free.push(ptr); + } + SqliteBindValue::BorrowedString(_) | SqliteBindValue::BorrowedBinary(_) => { + // We want to unbind the buffers later to ensure + // that sqlite does not access uninitilized memory + binds_to_free.push(bind_idx); + } + _ => {} + } + } + let query = Box::new(query); + let query_ptr = Box::leak(query) as *mut T as *mut libc::c_void; + let query_ptr = unsafe { + // This is safe because we got the ptr from the box above. + NonNull::new_unchecked(query_ptr) + }; + + // we provide a callback to free the query data here + // to prevent poluting the signature of `StatementUse` with + // the generic type of the query + let free_query = Box::new(|ptr: *mut libc::c_void| { + let b = unsafe { Box::from_raw(ptr as *mut T) }; + std::mem::drop(b); + }) as Box; + + Ok(Self { statement, column_names: OnceCell::new(), - } + binds_to_free, + owned_strings_to_free, + owned_slices_to_free, + query: query_ptr, + drop_query: free_query, + }) } pub(in crate::sqlite::connection) fn run(self) -> QueryResult<()> { @@ -279,6 +355,47 @@ impl<'a> StatementUse<'a> { impl<'a> Drop for StatementUse<'a> { fn drop(&mut self) { + // First reset the statement, otherwise the bind calls + // below will fails self.statement.reset(); + + // Reset the binds that may point to memory that will be/needs to be freed + for idx in &self.binds_to_free { + unsafe { + // It's always safe to bind null values + self.statement + .bind(SqliteType::Text, &SqliteBindValue::Null, *idx) + .expect("Binding nulls shouldn't ever fail"); + } + } + + // After we rebound all string/binary values we can free the corresponding memory + for string_to_free in std::mem::replace(&mut self.owned_strings_to_free, Vec::new()) { + let len = string_to_free.len(); + let ptr = string_to_free.as_mut_ptr(); + let s = unsafe { + // This is sound because: + // * ptr points to a string + // * len is the lenght of the string (as returned by the string itself above) + // * len == capacity, as `.into_boxed_str()` shrinks the allocation + String::from_raw_parts(ptr, len, len) + }; + std::mem::drop(s); + } + for slice_to_free in std::mem::replace(&mut self.owned_slices_to_free, Vec::new()) { + let len = slice_to_free.len(); + let ptr = slice_to_free.as_mut_ptr(); + let v = unsafe { + // This is sound because: + // * ptr points to a Vec + // * len is the lenght of the Vec (as returned by the slice itself above) + // * len == capacity, as `.into_boxed_slice()` shrinks the allocation + Vec::from_raw_parts(ptr, len, len) + }; + std::mem::drop(v); + } + + // drop the query + (self.drop_query)(self.query.as_ptr()) } } diff --git a/diesel/src/sqlite/query_builder/mod.rs b/diesel/src/sqlite/query_builder/mod.rs index c373c7f65cdf..690d9a703be4 100644 --- a/diesel/src/sqlite/query_builder/mod.rs +++ b/diesel/src/sqlite/query_builder/mod.rs @@ -1,5 +1,7 @@ //! The SQLite query builder +use std::fmt::Display; + use super::backend::Sqlite; use super::SqliteType; use crate::query_builder::{BindCollector, QueryBuilder}; @@ -75,6 +77,22 @@ pub enum SqliteBindValue<'a> { Null, } +impl Display for SqliteBindValue<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let n = match self { + SqliteBindValue::BorrowedString(_) | SqliteBindValue::String(_) => "Text", + SqliteBindValue::BorrowedBinary(_) | SqliteBindValue::Binary(_) => "Binary", + SqliteBindValue::SmallInt(_) => "SmallInt", + SqliteBindValue::Integer(_) => "Integer", + SqliteBindValue::BigInt(_) => "BigInt", + SqliteBindValue::Float(_) => "Float", + SqliteBindValue::Double(_) => "Double", + SqliteBindValue::Null => "Null", + }; + f.write_str(n) + } +} + impl SqliteBindValue<'_> { pub(in crate::sqlite) fn result_of(self, ctx: &mut libsqlite3_sys::sqlite3_context) { use libsqlite3_sys as ffi; @@ -143,9 +161,3 @@ impl<'a> BindCollector<'a, Sqlite> for SqliteBindCollector<'a> { Ok(()) } } - -// impl<'a> Output<'a, Sqlite> { -// pub(in crate::sqlite) fn ref_mut<'b>(&'b mut self) -> &'b mut SqliteBindValue<'a> { -// &mut self.out -// } -// } diff --git a/diesel_tests/tests/internal_details.rs b/diesel_tests/tests/internal_details.rs index a4ad2b708e63..842511eb6069 100644 --- a/diesel_tests/tests/internal_details.rs +++ b/diesel_tests/tests/internal_details.rs @@ -51,3 +51,19 @@ fn empty_query_gives_proper_error_instead_of_panicking() { Err(_) => panic!("We got back the wrong kind of error. This test is invalid."), } } + +#[test] +fn ensure_sqlite_does_not_access_dropped_buffers() { + let connection = &mut connection(); + + let buf: Vec = vec![0, 1, 2]; + + let query = diesel::select((&buf as &[u8]).into_sql::()); + + let mut iter = Connection::load(connection, query).unwrap(); + + std::mem::drop(buf); + + assert_eq!(iter.next().is_some(), true); + assert_eq!(iter.next().is_none(), true); +} From 99b9a24cf1c1114efc85c088af836d7453d9c279 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Thu, 4 Nov 2021 11:52:48 +0100 Subject: [PATCH 03/18] Improve unsafe code usage in sqlites StatementUse implementation This commit improves the unsafe code usage in the implementation of StatementUse by introducing the correct lifetimes to bind the StatementUse instance to the query itself. This prevents dropping the query (and any bound value) before dropping the StatementUse containing the corresponding binds. --- diesel/src/connection/mod.rs | 12 +- diesel/src/mysql/connection/mod.rs | 10 +- diesel/src/pg/connection/mod.rs | 9 +- diesel/src/query_dsl/load_dsl.rs | 26 ++--- diesel/src/query_dsl/mod.rs | 24 ++-- diesel/src/query_dsl/save_changes_dsl.rs | 12 +- diesel/src/r2d2.rs | 14 +-- diesel/src/sqlite/backend.rs | 2 +- diesel/src/sqlite/connection/functions.rs | 4 +- diesel/src/sqlite/connection/mod.rs | 22 ++-- diesel/src/sqlite/connection/row.rs | 26 ++--- diesel/src/sqlite/connection/sqlite_value.rs | 10 +- .../sqlite/connection/statement_iterator.rs | 18 +-- diesel/src/sqlite/connection/stmt.rs | 108 ++++-------------- diesel/src/sqlite/types/date_and_time/mod.rs | 6 +- diesel/src/sqlite/types/mod.rs | 16 +-- diesel/src/sqlite/types/numeric.rs | 2 +- ...ite_cannot_access_memory_of_droped_bind.rs | 69 +++++++++++ ...cannot_access_memory_of_droped_bind.stderr | 11 ++ diesel_migrations/src/migration_harness.rs | 4 +- diesel_tests/tests/internal_details.rs | 2 - 21 files changed, 214 insertions(+), 193 deletions(-) create mode 100644 diesel_compile_tests/tests/fail/ensure_sqlite_cannot_access_memory_of_droped_bind.rs create mode 100644 diesel_compile_tests/tests/fail/ensure_sqlite_cannot_access_memory_of_droped_bind.stderr diff --git a/diesel/src/connection/mod.rs b/diesel/src/connection/mod.rs index 86ee9cc760c7..6d1a4400f3c2 100644 --- a/diesel/src/connection/mod.rs +++ b/diesel/src/connection/mod.rs @@ -29,7 +29,7 @@ pub trait SimpleConnection { /// implementation. This trait is only useful in combination with [`Connection`]. /// /// Implementation wise this is a workaround for GAT's -pub trait ConnectionGatWorkaround<'a, DB: Backend> { +pub trait ConnectionGatWorkaround<'a, 'b, DB: Backend> { /// The cursor type returned by [`Connection::load`] /// /// Users should handle this as opaque type that implements [`Iterator`] @@ -42,7 +42,7 @@ pub trait ConnectionGatWorkaround<'a, DB: Backend> { /// A connection to a database pub trait Connection: SimpleConnection + Sized + Send where - Self: for<'a> ConnectionGatWorkaround<'a, ::Backend>, + Self: for<'a, 'b> ConnectionGatWorkaround<'a, 'b, ::Backend>, { /// The backend this type connects to type Backend: Backend; @@ -192,13 +192,13 @@ where fn execute(&mut self, query: &str) -> QueryResult; #[doc(hidden)] - fn load( - &mut self, + fn load<'a, 'b, T>( + &'a mut self, source: T, - ) -> QueryResult<>::Cursor> + ) -> QueryResult<>::Cursor> where T: AsQuery, - T::Query: QueryFragment + QueryId, + T::Query: QueryFragment + QueryId + 'b, Self::Backend: QueryMetadata; #[doc(hidden)] diff --git a/diesel/src/mysql/connection/mod.rs b/diesel/src/mysql/connection/mod.rs index 8656c06e08ac..1894f310d1f3 100644 --- a/diesel/src/mysql/connection/mod.rs +++ b/diesel/src/mysql/connection/mod.rs @@ -32,7 +32,7 @@ impl SimpleConnection for MysqlConnection { } } -impl<'a> ConnectionGatWorkaround<'a, Mysql> for MysqlConnection { +impl<'a, 'b> ConnectionGatWorkaround<'a, 'b, Mysql> for MysqlConnection { type Cursor = self::stmt::iterator::StatementIterator<'a>; type Row = self::stmt::iterator::MysqlRow; } @@ -65,13 +65,13 @@ impl Connection for MysqlConnection { } #[doc(hidden)] - fn load( - &mut self, + fn load<'a, 'b, T>( + &'a mut self, source: T, - ) -> QueryResult<>::Cursor> + ) -> QueryResult<>::Cursor> where T: AsQuery, - T::Query: QueryFragment + QueryId, + T::Query: QueryFragment + QueryId + 'b, Self::Backend: QueryMetadata, { let stmt = self.prepared_query(&source.as_query())?; diff --git a/diesel/src/pg/connection/mod.rs b/diesel/src/pg/connection/mod.rs index db7ed60201a9..5f2cea062200 100644 --- a/diesel/src/pg/connection/mod.rs +++ b/diesel/src/pg/connection/mod.rs @@ -43,7 +43,7 @@ impl SimpleConnection for PgConnection { } } -impl<'a> ConnectionGatWorkaround<'a, Pg> for PgConnection { +impl<'a, 'b> ConnectionGatWorkaround<'a, 'b, Pg> for PgConnection { type Cursor = Cursor; type Row = self::row::PgRow; } @@ -72,10 +72,13 @@ impl Connection for PgConnection { } #[doc(hidden)] - fn load(&mut self, source: T) -> QueryResult<>::Cursor> + fn load<'a, 'b, T>( + &'a mut self, + source: T, + ) -> QueryResult<>::Cursor> where T: AsQuery, - T::Query: QueryFragment + QueryId, + T::Query: QueryFragment + QueryId + 'b, Self::Backend: QueryMetadata, { self.with_prepared_query(&source.as_query(), |stmt, params, conn| { diff --git a/diesel/src/query_dsl/load_dsl.rs b/diesel/src/query_dsl/load_dsl.rs index 35a1ee464ab3..1b09768784db 100644 --- a/diesel/src/query_dsl/load_dsl.rs +++ b/diesel/src/query_dsl/load_dsl.rs @@ -13,18 +13,18 @@ use crate::result::QueryResult; /// to call `load` from generic code. /// /// [`RunQueryDsl`]: crate::RunQueryDsl -pub trait LoadQuery: RunQueryDsl +pub trait LoadQuery<'b, Conn, U>: RunQueryDsl where - for<'a> Self: LoadQueryGatWorkaround<'a, Conn, U>, + for<'a> Self: LoadQueryGatWorkaround<'a, 'b, Conn, U>, { /// Load this query - fn internal_load( + fn internal_load<'a>( self, - conn: &mut Conn, - ) -> QueryResult<>::Ret>; + conn: &'a mut Conn, + ) -> QueryResult<>::Ret>; } -pub trait LoadQueryGatWorkaround<'a, Conn, U> { +pub trait LoadQueryGatWorkaround<'a, 'b, Conn, U> { type Ret: Iterator>; } @@ -69,7 +69,7 @@ pub struct LoadIter<'a, U, C, ST, DB> { _marker: std::marker::PhantomData<&'a (ST, U, DB)>, } -impl<'a, Conn, T, U, DB> LoadQueryGatWorkaround<'a, Conn, U> for T +impl<'a, 'b, Conn, T, U, DB> LoadQueryGatWorkaround<'a, 'b, Conn, U> for T where Conn: Connection, T: AsQuery + RunQueryDsl, @@ -82,26 +82,26 @@ where type Ret = LoadIter< 'a, U, - >::Cursor, + >::Cursor, >::SqlType, DB, >; } -impl LoadQuery for T +impl<'b, Conn, T, U, DB> LoadQuery<'b, Conn, U> for T where Conn: Connection, T: AsQuery + RunQueryDsl, - T::Query: QueryFragment + QueryId, + T::Query: QueryFragment + QueryId + 'b, T::SqlType: CompatibleType, DB: Backend + QueryMetadata + 'static, U: FromSqlRow<>::SqlType, DB> + 'static, >::SqlType: 'static, { - fn internal_load( + fn internal_load<'a>( self, - conn: &mut Conn, - ) -> QueryResult<>::Ret> { + conn: &'a mut Conn, + ) -> QueryResult<>::Ret> { Ok(LoadIter { cursor: conn.load(self)?, _marker: Default::default(), diff --git a/diesel/src/query_dsl/mod.rs b/diesel/src/query_dsl/mod.rs index 62b25632cd7e..dc88594739aa 100644 --- a/diesel/src/query_dsl/mod.rs +++ b/diesel/src/query_dsl/mod.rs @@ -1400,9 +1400,9 @@ pub trait RunQueryDsl: Sized { /// # Ok(()) /// # } /// ``` - fn load(self, conn: &mut Conn) -> QueryResult> + fn load<'b, U>(self, conn: &mut Conn) -> QueryResult> where - Self: LoadQuery, + Self: LoadQuery<'b, Conn, U>, { self.internal_load(conn)?.collect() } @@ -1505,13 +1505,15 @@ pub trait RunQueryDsl: Sized { /// # Ok(()) /// # } /// ``` - fn load_iter<'a, U>( + fn load_iter<'a, 'b, 'c, U>( self, conn: &'a mut Conn, - ) -> QueryResult> + 'a>> + ) -> QueryResult> + 'c>> where + 'a: 'c, + 'b: 'c, U: 'a, - Self: LoadQuery + 'a, + Self: LoadQuery<'b, Conn, U> + 'a, { self.internal_load(conn).map(|i| Box::new(i) as Box<_>) } @@ -1561,9 +1563,9 @@ pub trait RunQueryDsl: Sized { /// # Ok(()) /// # } /// ``` - fn get_result(self, conn: &mut Conn) -> QueryResult + fn get_result<'b, U>(self, conn: &mut Conn) -> QueryResult where - Self: LoadQuery, + Self: LoadQuery<'b, Conn, U>, { match self.internal_load(conn)?.next() { Some(v) => v, @@ -1577,9 +1579,9 @@ pub trait RunQueryDsl: Sized { /// sense for insert, update, and delete statements. /// /// [`load`]: crate::query_dsl::RunQueryDsl::load() - fn get_results(self, conn: &mut Conn) -> QueryResult> + fn get_results<'b, U>(self, conn: &mut Conn) -> QueryResult> where - Self: LoadQuery, + Self: LoadQuery<'b, Conn, U>, { self.load(conn) } @@ -1617,10 +1619,10 @@ pub trait RunQueryDsl: Sized { /// # Ok(()) /// # } /// ``` - fn first(self, conn: &mut Conn) -> QueryResult + fn first<'b, U>(self, conn: &mut Conn) -> QueryResult where Self: methods::LimitDsl, - Limit: LoadQuery, + Limit: LoadQuery<'b, Conn, U>, { methods::LimitDsl::limit(self, 1).get_result(conn) } diff --git a/diesel/src/query_dsl/save_changes_dsl.rs b/diesel/src/query_dsl/save_changes_dsl.rs index 9dc7c2cac8cc..f13fbd9fcf2d 100644 --- a/diesel/src/query_dsl/save_changes_dsl.rs +++ b/diesel/src/query_dsl/save_changes_dsl.rs @@ -36,10 +36,10 @@ pub trait UpdateAndFetchResults: Connection { use crate::pg::PgConnection; #[cfg(feature = "postgres")] -impl UpdateAndFetchResults for PgConnection +impl<'b, Changes, Output> UpdateAndFetchResults for PgConnection where Changes: Copy + AsChangeset::Table> + IntoUpdateTarget, - Update: LoadQuery, + Update: LoadQuery<'b, PgConnection, Output>, ::AllColumns: ValidGrouping<()>, <::AllColumns as ValidGrouping<()>>::IsAggregate: MixedAggregates, @@ -53,13 +53,13 @@ where use crate::sqlite::SqliteConnection; #[cfg(feature = "sqlite")] -impl UpdateAndFetchResults for SqliteConnection +impl<'b, Changes, Output> UpdateAndFetchResults for SqliteConnection where Changes: Copy + Identifiable, Changes: AsChangeset::Table> + IntoUpdateTarget, Changes::Table: FindDsl, Update: ExecuteDsl, - Find: LoadQuery, + Find: LoadQuery<'b, SqliteConnection, Output>, ::AllColumns: ValidGrouping<()>, <::AllColumns as ValidGrouping<()>>::IsAggregate: MixedAggregates, @@ -74,13 +74,13 @@ where use crate::mysql::MysqlConnection; #[cfg(feature = "mysql")] -impl UpdateAndFetchResults for MysqlConnection +impl<'b, Changes, Output> UpdateAndFetchResults for MysqlConnection where Changes: Copy + Identifiable, Changes: AsChangeset::Table> + IntoUpdateTarget, Changes::Table: FindDsl, Update: ExecuteDsl, - Find: LoadQuery, + Find: LoadQuery<'b, MysqlConnection, Output>, ::AllColumns: ValidGrouping<()>, <::AllColumns as ValidGrouping<()>>::IsAggregate: MixedAggregates, diff --git a/diesel/src/r2d2.rs b/diesel/src/r2d2.rs index 3f0cd3201b4a..048e86a1e0e8 100644 --- a/diesel/src/r2d2.rs +++ b/diesel/src/r2d2.rs @@ -130,14 +130,14 @@ where } } -impl<'a, DB, M> ConnectionGatWorkaround<'a, DB> for PooledConnection +impl<'a, 'b, DB, M> ConnectionGatWorkaround<'a, 'b, DB> for PooledConnection where M: ManageConnection, M::Connection: Connection, DB: Backend, { - type Cursor = >::Cursor; - type Row = >::Row; + type Cursor = >::Cursor; + type Row = >::Row; } impl Connection for PooledConnection @@ -159,13 +159,13 @@ where (&mut **self).execute(query) } - fn load( - &mut self, + fn load<'a, 'b, T>( + &'a mut self, source: T, - ) -> QueryResult<>::Cursor> + ) -> QueryResult<>::Cursor> where T: AsQuery, - T::Query: QueryFragment + QueryId, + T::Query: QueryFragment + QueryId + 'b, Self::Backend: QueryMetadata, { (&mut **self).load(source) diff --git a/diesel/src/sqlite/backend.rs b/diesel/src/sqlite/backend.rs index eda70c111364..b73d4f9814d9 100644 --- a/diesel/src/sqlite/backend.rs +++ b/diesel/src/sqlite/backend.rs @@ -45,7 +45,7 @@ impl<'a> HasBindCollector<'a> for Sqlite { } impl<'a> HasRawValue<'a> for Sqlite { - type RawValue = SqliteValue<'a, 'a>; + type RawValue = SqliteValue<'a, 'a, 'a>; } impl TypeMetadata for Sqlite { diff --git a/diesel/src/sqlite/connection/functions.rs b/diesel/src/sqlite/connection/functions.rs index f125e279dbe0..b497b7c4cdfe 100644 --- a/diesel/src/sqlite/connection/functions.rs +++ b/diesel/src/sqlite/connection/functions.rs @@ -117,7 +117,7 @@ where struct FunctionRow<'a> { // we use `ManuallyDrop` to prevent dropping the content of the internal vector // as this buffer is owned by sqlite not by diesel - args: Rc>>>, + args: Rc>>>, field_count: usize, marker: PhantomData<&'a ffi::sqlite3_value>, } @@ -223,7 +223,7 @@ impl<'a, 'b> RowIndex<&'a str> for FunctionRow<'b> { } struct FunctionArgument<'a> { - args: Ref<'a, ManuallyDrop>>, + args: Ref<'a, ManuallyDrop>>, col_idx: i32, } diff --git a/diesel/src/sqlite/connection/mod.rs b/diesel/src/sqlite/connection/mod.rs index 70503eca8fb4..9e3c731555e7 100644 --- a/diesel/src/sqlite/connection/mod.rs +++ b/diesel/src/sqlite/connection/mod.rs @@ -52,9 +52,9 @@ impl SimpleConnection for SqliteConnection { } } -impl<'a> ConnectionGatWorkaround<'a, Sqlite> for SqliteConnection { - type Cursor = StatementIterator<'a>; - type Row = self::row::SqliteRow<'a>; +impl<'a, 'b> ConnectionGatWorkaround<'a, 'b, Sqlite> for SqliteConnection { + type Cursor = StatementIterator<'a, 'b>; + type Row = self::row::SqliteRow<'a, 'b>; } impl Connection for SqliteConnection { @@ -88,13 +88,13 @@ impl Connection for SqliteConnection { } #[doc(hidden)] - fn load( - &mut self, + fn load<'a, 'b, T>( + &'a mut self, source: T, - ) -> QueryResult<>::Cursor> + ) -> QueryResult<>::Cursor> where T: AsQuery, - T::Query: QueryFragment + QueryId, + T::Query: QueryFragment + QueryId + 'b, Self::Backend: QueryMetadata, { let statement_use = self.prepared_query(source.as_query())?; @@ -198,10 +198,10 @@ impl SqliteConnection { } } - fn prepared_query + QueryId>( - &mut self, - source: T, - ) -> QueryResult { + fn prepared_query<'a, 'b, T>(&'a mut self, source: T) -> QueryResult> + where + T: QueryFragment + QueryId + 'b, + { let raw_connection = &self.raw_connection; let cache = &mut self.statement_cache; let statement = cache.cached_statement(&source, &[], |sql, is_cached| { diff --git a/diesel/src/sqlite/connection/row.rs b/diesel/src/sqlite/connection/row.rs index 52f2928e5d47..375638c85ee0 100644 --- a/diesel/src/sqlite/connection/row.rs +++ b/diesel/src/sqlite/connection/row.rs @@ -9,13 +9,13 @@ use crate::sqlite::Sqlite; use crate::util::OnceCell; #[allow(missing_debug_implementations)] -pub struct SqliteRow<'a> { - pub(super) inner: Rc>>, +pub struct SqliteRow<'a, 'b> { + pub(super) inner: Rc>>, pub(super) field_count: usize, } -pub(super) enum PrivateSqliteRow<'a> { - Direct(StatementUse<'a>), +pub(super) enum PrivateSqliteRow<'a, 'b> { + Direct(StatementUse<'a, 'b>), Duplicated { values: Vec>, column_names: Rc<[Option]>, @@ -23,7 +23,7 @@ pub(super) enum PrivateSqliteRow<'a> { TemporaryEmpty, } -impl<'a> PrivateSqliteRow<'a> { +impl<'a, 'b> PrivateSqliteRow<'a, 'b> { pub(super) fn duplicate(&mut self, column_names: &mut Option]>>) -> Self { match self { PrivateSqliteRow::Direct(stmt) => { @@ -60,11 +60,11 @@ impl<'a> PrivateSqliteRow<'a> { } } -impl<'a, 'b> RowGatWorkaround<'a, Sqlite> for SqliteRow<'b> { - type Field = SqliteField<'a>; +impl<'a, 'b, 'c> RowGatWorkaround<'a, Sqlite> for SqliteRow<'b, 'c> { + type Field = SqliteField<'a, 'a>; } -impl<'a> Row<'a, Sqlite> for SqliteRow<'a> { +impl<'a, 'c> Row<'a, Sqlite> for SqliteRow<'a, 'c> { type InnerPartialRow = Self; fn field_count(&self) -> usize { @@ -89,7 +89,7 @@ impl<'a> Row<'a, Sqlite> for SqliteRow<'a> { } } -impl<'a> RowIndex for SqliteRow<'a> { +impl<'a, 'b> RowIndex for SqliteRow<'a, 'b> { fn idx(&self, idx: usize) -> Option { if idx < self.field_count { Some(idx) @@ -99,7 +99,7 @@ impl<'a> RowIndex for SqliteRow<'a> { } } -impl<'a, 'd> RowIndex<&'d str> for SqliteRow<'a> { +impl<'a, 'd, 'c> RowIndex<&'d str> for SqliteRow<'a, 'c> { fn idx(&self, field_name: &'d str) -> Option { match &mut *self.inner.borrow_mut() { PrivateSqliteRow::Direct(stmt) => stmt.index_for_column_name(field_name), @@ -121,13 +121,13 @@ impl<'a, 'd> RowIndex<&'d str> for SqliteRow<'a> { } #[allow(missing_debug_implementations)] -pub struct SqliteField<'a> { - pub(super) row: Ref<'a, PrivateSqliteRow<'a>>, +pub struct SqliteField<'a, 'b> { + pub(super) row: Ref<'a, PrivateSqliteRow<'a, 'b>>, pub(super) col_idx: i32, field_name: OnceCell>, } -impl<'a> Field<'a, Sqlite> for SqliteField<'a> { +impl<'a, 'b> Field<'a, Sqlite> for SqliteField<'a, 'b> { fn field_name(&self) -> Option<&str> { self.field_name .get_or_init(|| match &*self.row { diff --git a/diesel/src/sqlite/connection/sqlite_value.rs b/diesel/src/sqlite/connection/sqlite_value.rs index 7707e95e64d6..721225c93f7b 100644 --- a/diesel/src/sqlite/connection/sqlite_value.rs +++ b/diesel/src/sqlite/connection/sqlite_value.rs @@ -13,11 +13,11 @@ use super::row::PrivateSqliteRow; /// Use existing `FromSql` implementations to convert this into /// rust values #[allow(missing_debug_implementations, missing_copy_implementations)] -pub struct SqliteValue<'a, 'b> { +pub struct SqliteValue<'a, 'b, 'c> { // This field exists to ensure that nobody // can modify the underlying row while we are // holding a reference to some row value here - _row: Ref<'a, PrivateSqliteRow<'b>>, + _row: Ref<'a, PrivateSqliteRow<'b, 'c>>, // we extract the raw value pointer as part of the constructor // to safe the match statements for each method // Acconding to benchmarks this leads to a ~20-30% speedup @@ -39,8 +39,8 @@ impl Drop for OwnedSqliteValue { } } -impl<'a, 'b> SqliteValue<'a, 'b> { - pub(super) fn new(row: Ref<'a, PrivateSqliteRow<'b>>, col_idx: i32) -> Option { +impl<'a, 'b, 'c> SqliteValue<'a, 'b, 'c> { + pub(super) fn new(row: Ref<'a, PrivateSqliteRow<'b, 'c>>, col_idx: i32) -> Option { let value = match &*row { PrivateSqliteRow::Direct(stmt) => stmt.column_value(col_idx)?, PrivateSqliteRow::Duplicated { values, .. } => { @@ -66,7 +66,7 @@ impl<'a, 'b> SqliteValue<'a, 'b> { } } - pub(crate) fn parse_string<'c, R>(&'c self, f: impl FnOnce(&'c str) -> R) -> R { + pub(crate) fn parse_string<'d, R>(&'d self, f: impl FnOnce(&'d str) -> R) -> R { let s = unsafe { let ptr = ffi::sqlite3_value_text(self.value.as_ptr()); let len = ffi::sqlite3_value_bytes(self.value.as_ptr()); diff --git a/diesel/src/sqlite/connection/statement_iterator.rs b/diesel/src/sqlite/connection/statement_iterator.rs index ae51cbe52a64..1fcaaf928d8e 100644 --- a/diesel/src/sqlite/connection/statement_iterator.rs +++ b/diesel/src/sqlite/connection/statement_iterator.rs @@ -6,20 +6,20 @@ use super::stmt::StatementUse; use crate::result::QueryResult; #[allow(missing_debug_implementations)] -pub struct StatementIterator<'a> { - inner: PrivateStatementIterator<'a>, +pub struct StatementIterator<'a, 'b> { + inner: PrivateStatementIterator<'a, 'b>, column_names: Option]>>, field_count: usize, } -enum PrivateStatementIterator<'a> { - NotStarted(StatementUse<'a>), - Started(Rc>>), +enum PrivateStatementIterator<'a, 'b> { + NotStarted(StatementUse<'a, 'b>), + Started(Rc>>), TemporaryEmpty, } -impl<'a> StatementIterator<'a> { - pub fn new(stmt: StatementUse<'a>) -> Self { +impl<'a, 'b> StatementIterator<'a, 'b> { + pub fn new(stmt: StatementUse<'a, 'b>) -> Self { Self { inner: PrivateStatementIterator::NotStarted(stmt), column_names: None, @@ -28,8 +28,8 @@ impl<'a> StatementIterator<'a> { } } -impl<'a> Iterator for StatementIterator<'a> { - type Item = QueryResult>; +impl<'a, 'b> Iterator for StatementIterator<'a, 'b> { + type Item = QueryResult>; fn next(&mut self) -> Option { use PrivateStatementIterator::{NotStarted, Started, TemporaryEmpty}; diff --git a/diesel/src/sqlite/connection/stmt.rs b/diesel/src/sqlite/connection/stmt.rs index 57dbb3472a4d..c0e571b0d7c8 100644 --- a/diesel/src/sqlite/connection/stmt.rs +++ b/diesel/src/sqlite/connection/stmt.rs @@ -188,92 +188,59 @@ impl Drop for Statement { } #[allow(missing_debug_implementations)] -pub struct StatementUse<'a> { +pub struct StatementUse<'a, 'b> { statement: MaybeCached<'a, Statement>, column_names: OnceCell>, - binds_to_free: Vec, - owned_strings_to_free: Vec<&'static mut str>, - owned_slices_to_free: Vec<&'static mut [u8]>, - query: NonNull, - drop_query: Box, + // we need to store the query here to ensure noone does + // drop it till the end ot the statement + // We use a boxed queryfragment here just to erase the + // generic type + _query: Box + 'b>, + binds_to_free: Vec<(i32, Option>)>, } -impl<'a> StatementUse<'a> { +impl<'a, 'b> StatementUse<'a, 'b> { pub(super) fn bind(mut statement: MaybeCached<'a, Statement>, query: T) -> QueryResult where - T: QueryFragment + QueryId, + T: QueryFragment + QueryId + 'b, { let mut bind_collector = SqliteBindCollector::new(); query.collect_binds(&mut bind_collector, &mut ())?; let SqliteBindCollector { metadata, binds } = bind_collector; - let mut owned_strings_to_free = Vec::new(); - let mut owned_slices_to_free = Vec::new(); let mut binds_to_free = Vec::new(); for (idx, (bind, tpe)) in binds.into_iter().zip(metadata).enumerate() { // Sqlite starts to count by 1 let bind_idx = idx as i32 + 1; + // It's safe to call bind here as: // * The type and value matches // * We ensure that corresponding buffers lives long enough below // * The statement is not used yet by `step` or anything else - unsafe { - statement.bind(tpe, &bind, bind_idx)?; - } + unsafe { statement.bind(tpe, &bind, bind_idx)? }; + // We want to unbind the buffers later to ensure + // that sqlite does not access uninitilized memory match bind { - SqliteBindValue::String(s) => { - // We leak owned strings here - // so that they we can be sure that - // the buffer remains at its location - // till we unbind it from sqlite. - // For the unbinding we need to know the - // bind index_number, so we collect that here - // - // At that point we need to free the memory there, - // so we collect the corresponding pointer here - binds_to_free.push(bind_idx); - let ptr = Box::leak(s); - owned_strings_to_free.push(ptr); + SqliteBindValue::BorrowedString(_) | SqliteBindValue::BorrowedBinary(_) => { + binds_to_free.push((bind_idx, None)); } SqliteBindValue::Binary(b) => { - // Same as for Strings - binds_to_free.push(bind_idx); - let ptr = Box::leak(b); - owned_slices_to_free.push(ptr); + binds_to_free.push((bind_idx, Some(SqliteBindValue::Binary(b)))); } - SqliteBindValue::BorrowedString(_) | SqliteBindValue::BorrowedBinary(_) => { - // We want to unbind the buffers later to ensure - // that sqlite does not access uninitilized memory - binds_to_free.push(bind_idx); + SqliteBindValue::String(b) => { + binds_to_free.push((bind_idx, Some(SqliteBindValue::String(b)))); } - _ => {} + _ => (), } } - let query = Box::new(query); - let query_ptr = Box::leak(query) as *mut T as *mut libc::c_void; - let query_ptr = unsafe { - // This is safe because we got the ptr from the box above. - NonNull::new_unchecked(query_ptr) - }; - - // we provide a callback to free the query data here - // to prevent poluting the signature of `StatementUse` with - // the generic type of the query - let free_query = Box::new(|ptr: *mut libc::c_void| { - let b = unsafe { Box::from_raw(ptr as *mut T) }; - std::mem::drop(b); - }) as Box; Ok(Self { statement, column_names: OnceCell::new(), binds_to_free, - owned_strings_to_free, - owned_slices_to_free, - query: query_ptr, - drop_query: free_query, + _query: Box::new(query) as Box<_>, }) } @@ -353,49 +320,20 @@ impl<'a> StatementUse<'a> { } } -impl<'a> Drop for StatementUse<'a> { +impl<'a, 'b> Drop for StatementUse<'a, 'b> { fn drop(&mut self) { // First reset the statement, otherwise the bind calls // below will fails self.statement.reset(); // Reset the binds that may point to memory that will be/needs to be freed - for idx in &self.binds_to_free { + for (idx, _buffer) in std::mem::take(&mut self.binds_to_free) { unsafe { // It's always safe to bind null values self.statement - .bind(SqliteType::Text, &SqliteBindValue::Null, *idx) + .bind(SqliteType::Text, &SqliteBindValue::Null, idx) .expect("Binding nulls shouldn't ever fail"); } } - - // After we rebound all string/binary values we can free the corresponding memory - for string_to_free in std::mem::replace(&mut self.owned_strings_to_free, Vec::new()) { - let len = string_to_free.len(); - let ptr = string_to_free.as_mut_ptr(); - let s = unsafe { - // This is sound because: - // * ptr points to a string - // * len is the lenght of the string (as returned by the string itself above) - // * len == capacity, as `.into_boxed_str()` shrinks the allocation - String::from_raw_parts(ptr, len, len) - }; - std::mem::drop(s); - } - for slice_to_free in std::mem::replace(&mut self.owned_slices_to_free, Vec::new()) { - let len = slice_to_free.len(); - let ptr = slice_to_free.as_mut_ptr(); - let v = unsafe { - // This is sound because: - // * ptr points to a Vec - // * len is the lenght of the Vec (as returned by the slice itself above) - // * len == capacity, as `.into_boxed_slice()` shrinks the allocation - Vec::from_raw_parts(ptr, len, len) - }; - std::mem::drop(v); - } - - // drop the query - (self.drop_query)(self.query.as_ptr()) } } diff --git a/diesel/src/sqlite/types/date_and_time/mod.rs b/diesel/src/sqlite/types/date_and_time/mod.rs index 19802070b8ac..e9e5a6933508 100644 --- a/diesel/src/sqlite/types/date_and_time/mod.rs +++ b/diesel/src/sqlite/types/date_and_time/mod.rs @@ -8,7 +8,7 @@ use crate::sqlite::Sqlite; mod chrono; impl FromSql for String { - fn from_sql(value: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { FromSql::::from_sql(value) } } @@ -32,7 +32,7 @@ impl ToSql for String { } impl FromSql for String { - fn from_sql(value: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { FromSql::::from_sql(value) } } @@ -56,7 +56,7 @@ impl ToSql for String { } impl FromSql for String { - fn from_sql(value: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { FromSql::::from_sql(value) } } diff --git a/diesel/src/sqlite/types/mod.rs b/diesel/src/sqlite/types/mod.rs index 46cbe1314dfc..5e3fda0fa115 100644 --- a/diesel/src/sqlite/types/mod.rs +++ b/diesel/src/sqlite/types/mod.rs @@ -13,7 +13,7 @@ use crate::sql_types; /// raw pointer instead of a reference with a lifetime due to the structure of /// `FromSql` impl FromSql for *const str { - fn from_sql(value: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { let text = value.read_text(); Ok(text as *const _) } @@ -25,44 +25,44 @@ impl FromSql for *const str { /// raw pointer instead of a reference with a lifetime due to the structure of /// `FromSql` impl FromSql for *const [u8] { - fn from_sql(bytes: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(bytes: SqliteValue<'_, '_, '_>) -> deserialize::Result { let bytes = bytes.read_blob(); Ok(bytes as *const _) } } impl FromSql for i16 { - fn from_sql(value: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { Ok(value.read_integer() as i16) } } impl FromSql for i32 { - fn from_sql(value: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { Ok(value.read_integer()) } } impl FromSql for bool { - fn from_sql(value: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { Ok(value.read_integer() != 0) } } impl FromSql for i64 { - fn from_sql(value: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { Ok(value.read_long()) } } impl FromSql for f32 { - fn from_sql(value: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { Ok(value.read_double() as f32) } } impl FromSql for f64 { - fn from_sql(value: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(value: SqliteValue<'_, '_, '_>) -> deserialize::Result { Ok(value.read_double()) } } diff --git a/diesel/src/sqlite/types/numeric.rs b/diesel/src/sqlite/types/numeric.rs index aaf2683544dc..5d34b1d10b07 100644 --- a/diesel/src/sqlite/types/numeric.rs +++ b/diesel/src/sqlite/types/numeric.rs @@ -8,7 +8,7 @@ use crate::sqlite::connection::SqliteValue; use crate::sqlite::Sqlite; impl FromSql for BigDecimal { - fn from_sql(bytes: SqliteValue<'_, '_>) -> deserialize::Result { + fn from_sql(bytes: SqliteValue<'_, '_, '_>) -> deserialize::Result { let x = >::from_sql(bytes)?; BigDecimal::from_f64(x).ok_or_else(|| format!("{} is not valid decimal number ", x).into()) } diff --git a/diesel_compile_tests/tests/fail/ensure_sqlite_cannot_access_memory_of_droped_bind.rs b/diesel_compile_tests/tests/fail/ensure_sqlite_cannot_access_memory_of_droped_bind.rs new file mode 100644 index 000000000000..21f1105912e5 --- /dev/null +++ b/diesel_compile_tests/tests/fail/ensure_sqlite_cannot_access_memory_of_droped_bind.rs @@ -0,0 +1,69 @@ +use diesel::prelude::*; +use diesel::sql_types; + +fn main() { + { + let mut connection = SqliteConnection::establish("").unwrap(); + + let buf: Vec = vec![0, 1, 2]; + + let query = diesel::select((&buf as &[u8]).into_sql::()); + + let mut iter = Connection::load(&mut connection, query).unwrap(); + + // Sqlite borrows the buffer internally, so droping it here is not allowed + // while the statement is still alive. + std::mem::drop(buf); + + assert_eq!(iter.next().is_some(), true); + assert_eq!(iter.next().is_none(), true); + } + + // Everything else is allowed + { + let mut connection = PgConnection::establish("").unwrap(); + + let buf: Vec = vec![0, 1, 2]; + + let query = diesel::select((&buf as &[u8]).into_sql::()); + + let mut iter = Connection::load(&mut connection, query).unwrap(); + + std::mem::drop(buf); + + assert_eq!(iter.next().is_some(), true); + assert_eq!(iter.next().is_none(), true); + } + + { + let mut connection = MysqlConnection::establish("").unwrap(); + + let buf: Vec = vec![0, 1, 2]; + + let query = diesel::select((&buf as &[u8]).into_sql::()); + + let mut iter = Connection::load(&mut connection, query).unwrap(); + + std::mem::drop(buf); + + assert_eq!(iter.next().is_some(), true); + assert_eq!(iter.next().is_none(), true); + } + + { + let mut connection = SqliteConnection::establish("").unwrap(); + + let buf: Vec = vec![0, 1, 2]; + + let query = diesel::select((&buf as &[u8]).into_sql::()); + + let mut iter = Connection::load(&mut connection, query).unwrap(); + + assert_eq!(iter.next().is_some(), true); + assert_eq!(iter.next().is_none(), true); + + std::mem::drop(iter); + std::mem::drop(buf); + } + +} diff --git a/diesel_compile_tests/tests/fail/ensure_sqlite_cannot_access_memory_of_droped_bind.stderr b/diesel_compile_tests/tests/fail/ensure_sqlite_cannot_access_memory_of_droped_bind.stderr new file mode 100644 index 000000000000..b4144034b914 --- /dev/null +++ b/diesel_compile_tests/tests/fail/ensure_sqlite_cannot_access_memory_of_droped_bind.stderr @@ -0,0 +1,11 @@ +error[E0505]: cannot move out of `buf` because it is borrowed + --> $DIR/ensure_sqlite_cannot_access_memory_of_droped_bind.rs:16:24 + | +10 | let query = diesel::select((&buf as &[u8]).into_sql::()); + | ---- borrow of `buf` occurs here +... +16 | std::mem::drop(buf); + | ^^^ move out of `buf` occurs here +17 | +18 | assert_eq!(iter.next().is_some(), true); + | ---- borrow later used here diff --git a/diesel_migrations/src/migration_harness.rs b/diesel_migrations/src/migration_harness.rs index ee079c9eb7b1..b60dd7c0d79b 100644 --- a/diesel_migrations/src/migration_harness.rs +++ b/diesel_migrations/src/migration_harness.rs @@ -154,14 +154,14 @@ pub trait MigrationHarness { fn applied_migrations(&mut self) -> Result>>; } -impl MigrationHarness for C +impl<'b, C, DB> MigrationHarness for C where DB: Backend, C: Connection + MigrationConnection + 'static, dsl::Order< dsl::Select<__diesel_schema_migrations::table, __diesel_schema_migrations::version>, dsl::Desc<__diesel_schema_migrations::version>, - >: LoadQuery>, + >: LoadQuery<'b, C, MigrationVersion<'static>>, for<'a> InsertStatement< __diesel_schema_migrations::table, ValuesClause< diff --git a/diesel_tests/tests/internal_details.rs b/diesel_tests/tests/internal_details.rs index 842511eb6069..80f3cc33d7d0 100644 --- a/diesel_tests/tests/internal_details.rs +++ b/diesel_tests/tests/internal_details.rs @@ -62,8 +62,6 @@ fn ensure_sqlite_does_not_access_dropped_buffers() { let mut iter = Connection::load(connection, query).unwrap(); - std::mem::drop(buf); - assert_eq!(iter.next().is_some(), true); assert_eq!(iter.next().is_none(), true); } From dd37c5331cdd3201e8fe948338035274f166d966 Mon Sep 17 00:00:00 2001 From: Georg Semmler Date: Thu, 4 Nov 2021 11:55:06 +0100 Subject: [PATCH 04/18] Update compile tests to include the newly introduced lifetime --- ...ectable_if_inner_expr_is_selectable.stderr | 2 +- ...ay_expressions_must_be_correct_type.stderr | 8 ++-- ...array_expressions_must_be_same_type.stderr | 17 ++++++++ .../fail/array_only_usable_with_pg.stderr | 4 +- ...must_be_used_with_proper_connection.stderr | 2 +- ...t_load_default_select_with_group_by.stderr | 2 +- ...support_returning_methods_on_sqlite.stderr | 4 +- ...nct_on_clause_only_supported_for_pg.stderr | 4 +- ...ison_for_columns_from_another_table.stderr | 4 +- ...support_returning_methods_on_sqlite.stderr | 4 +- ...es_not_support_offset_without_limit.stderr | 2 +- ...ions_cant_be_used_in_a_sqlite_query.stderr | 6 +-- .../fail/queryable_type_missmatch.stderr | 8 ++-- ...ct_carries_correct_result_type_info.stderr | 4 +- ...for_update_cannot_be_used_on_sqlite.stderr | 4 +- ...te_no_wait_cannot_be_used_on_sqlite.stderr | 4 +- ...kip_locked_cannot_be_used_on_sqlite.stderr | 4 +- ...elect_sql_still_ensures_result_type.stderr | 2 +- .../tests/fail/selectable.stderr | 42 +++++++++---------- ...sql_do_not_allow_multiple_iterators.stderr | 26 ++++++++++++ ...lect_cannot_reference_random_tables.stderr | 6 +-- ...support_returning_methods_on_sqlite.stderr | 4 +- ...nctions_follow_same_selection_rules.stderr | 2 +- ...alid_grouping_and_boxed_expressions.stderr | 4 +- 24 files changed, 106 insertions(+), 63 deletions(-) diff --git a/diesel_compile_tests/tests/fail/any_is_only_selectable_if_inner_expr_is_selectable.stderr b/diesel_compile_tests/tests/fail/any_is_only_selectable_if_inner_expr_is_selectable.stderr index d74975a436fc..718927a68545 100644 --- a/diesel_compile_tests/tests/fail/any_is_only_selectable_if_inner_expr_is_selectable.stderr +++ b/diesel_compile_tests/tests/fail/any_is_only_selectable_if_inner_expr_is_selectable.stderr @@ -19,4 +19,4 @@ error[E0277]: the trait bound `stuff::table: AppearsInFromClause` for `Grouped>>` = note: required because of the requirements on the impl of `diesel::query_builder::where_clause::ValidWhereClause>` for `diesel::query_builder::where_clause::WhereClause>>>` = note: required because of the requirements on the impl of `Query` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>>` - = note: required because of the requirements on the impl of `LoadQuery<_, _>` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>>` + = note: required because of the requirements on the impl of `LoadQuery<'_, _, _>` for `SelectStatement, DefaultSelectClause>, diesel::query_builder::distinct_clause::NoDistinctClause, diesel::query_builder::where_clause::WhereClause>>>>` diff --git a/diesel_compile_tests/tests/fail/array_expressions_must_be_correct_type.stderr b/diesel_compile_tests/tests/fail/array_expressions_must_be_correct_type.stderr index d7b4b256b817..bd8b89a7ef99 100644 --- a/diesel_compile_tests/tests/fail/array_expressions_must_be_correct_type.stderr +++ b/diesel_compile_tests/tests/fail/array_expressions_must_be_correct_type.stderr @@ -44,7 +44,7 @@ error[E0277]: the trait bound `f64: SelectableExpression` is not s = note: required because of the requirements on the impl of `SelectableExpression` for `diesel::pg::expression::array::ArrayLiteral<(f64, f64), diesel::sql_types::Integer>` = note: required because of the requirements on the impl of `SelectClauseExpression` for `diesel::query_builder::select_clause::SelectClause>` = note: required because of the requirements on the impl of `Query` for `SelectStatement>>` - = note: required because of the requirements on the impl of `LoadQuery<_, Vec>` for `SelectStatement>>` + = note: required because of the requirements on the impl of `LoadQuery<'_, _, Vec>` for `SelectStatement>>` error[E0277]: the trait bound `f64: ValidGrouping<()>` is not satisfied --> $DIR/array_expressions_must_be_correct_type.rs:9:33 @@ -56,7 +56,7 @@ error[E0277]: the trait bound `f64: ValidGrouping<()>` is not satisfied = note: 1 redundant requirements hidden = note: required because of the requirements on the impl of `ValidGrouping<()>` for `diesel::pg::expression::array::ArrayLiteral<(f64, f64), diesel::sql_types::Integer>` = note: required because of the requirements on the impl of `Query` for `SelectStatement>>` - = note: required because of the requirements on the impl of `LoadQuery<_, Vec>` for `SelectStatement>>` + = note: required because of the requirements on the impl of `LoadQuery<'_, _, Vec>` for `SelectStatement>>` error[E0277]: the trait bound `f64: QueryFragment` is not satisfied --> $DIR/array_expressions_must_be_correct_type.rs:9:33 @@ -67,7 +67,7 @@ error[E0277]: the trait bound `f64: QueryFragment` is not satisfied = note: required because of the requirements on the impl of `QueryFragment` for `(f64, f64)` = note: 3 redundant requirements hidden = note: required because of the requirements on the impl of `QueryFragment` for `SelectStatement>>` - = note: required because of the requirements on the impl of `LoadQuery<_, Vec>` for `SelectStatement>>` + = note: required because of the requirements on the impl of `LoadQuery<'_, _, Vec>` for `SelectStatement>>` error[E0277]: the trait bound `f64: QueryId` is not satisfied --> $DIR/array_expressions_must_be_correct_type.rs:9:33 @@ -78,7 +78,7 @@ error[E0277]: the trait bound `f64: QueryId` is not satisfied = note: required because of the requirements on the impl of `QueryId` for `(f64, f64)` = note: 3 redundant requirements hidden = note: required because of the requirements on the impl of `QueryId` for `SelectStatement>>` - = note: required because of the requirements on the impl of `LoadQuery<_, Vec>` for `SelectStatement>>` + = note: required because of the requirements on the impl of `LoadQuery<'_, _, Vec>` for `SelectStatement>>` error[E0277]: the trait bound `f64: diesel::Expression` is not satisfied --> $DIR/array_expressions_must_be_correct_type.rs:9:12 diff --git a/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr b/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr index 94e7e4bce66c..7f83b4fa929a 100644 --- a/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr +++ b/diesel_compile_tests/tests/fail/array_expressions_must_be_same_type.stderr @@ -175,6 +175,23 @@ error[E0277]: the trait bound `{integer}: ValidGrouping<()>` is not satisfied = note: required because of the requirements on the impl of `Query` for `SelectStatement), diesel::sql_types::Double>>>` = note: required because of the requirements on the impl of `LoadQuery<_, Vec>` for `SelectStatement), diesel::sql_types::Double>>>` +error[E0277]: the trait bound `{integer}: QueryFragment` is not satisfied + --> $DIR/array_expressions_must_be_same_type.rs:12:30 + | +12 | select(array((1, 3f64))).get_result::>(&mut connection).unwrap(); + | ^^^^^^^^^^ the trait `QueryFragment` is not implemented for `{integer}` + | + = help: the following implementations were found: + <&'a T as QueryFragment> + <() as QueryFragment> + <(A, B) as QueryFragment<__DB>> + <(A, B, C) as QueryFragment<__DB>> + and 246 others + = note: required because of the requirements on the impl of `QueryFragment` for `({integer}, diesel::expression::bound::Bound)` + = note: 3 redundant requirements hidden + = note: required because of the requirements on the impl of `QueryFragment` for `SelectStatement), diesel::sql_types::Double>>>` + = note: required because of the requirements on the impl of `LoadQuery<_, Vec>` for `SelectStatement), diesel::sql_types::Double>>>` + error[E0277]: the trait bound `{integer}: QueryId` is not satisfied --> tests/fail/array_expressions_must_be_same_type.rs:12:30 | diff --git a/diesel_compile_tests/tests/fail/array_only_usable_with_pg.stderr b/diesel_compile_tests/tests/fail/array_only_usable_with_pg.stderr index 96c598114085..28a2fc6d5d2c 100644 --- a/diesel_compile_tests/tests/fail/array_only_usable_with_pg.stderr +++ b/diesel_compile_tests/tests/fail/array_only_usable_with_pg.stderr @@ -4,7 +4,7 @@ error[E0271]: type mismatch resolving `>(&mut connection); | ^^^^^^^^^^ expected struct `Sqlite`, found struct `Pg` | - = note: required because of the requirements on the impl of `LoadQuery>` for `SelectStatement,), diesel::sql_types::Integer>>>` + = note: required because of the requirements on the impl of `LoadQuery<'_, diesel::SqliteConnection, Vec>` for `SelectStatement,), diesel::sql_types::Integer>>>` error[E0271]: type mismatch resolving `::Backend == Pg` --> $DIR/array_only_usable_with_pg.rs:11:25 @@ -12,4 +12,4 @@ error[E0271]: type mismatch resolving `>(&mut connection); | ^^^^^^^^^^ expected struct `Mysql`, found struct `Pg` | - = note: required because of the requirements on the impl of `LoadQuery>` for `SelectStatement,), diesel::sql_types::Integer>>>` + = note: required because of the requirements on the impl of `LoadQuery<'_, diesel::MysqlConnection, Vec>` for `SelectStatement,), diesel::sql_types::Integer>>>` diff --git a/diesel_compile_tests/tests/fail/boxed_queries_must_be_used_with_proper_connection.stderr b/diesel_compile_tests/tests/fail/boxed_queries_must_be_used_with_proper_connection.stderr index eaaee038ff71..02c419ef4bf8 100644 --- a/diesel_compile_tests/tests/fail/boxed_queries_must_be_used_with_proper_connection.stderr +++ b/diesel_compile_tests/tests/fail/boxed_queries_must_be_used_with_proper_connection.stderr @@ -4,4 +4,4 @@ error[E0271]: type mismatch resolving `().load::<(i32,)>(&mut connection); | ^^^^ expected struct `Sqlite`, found struct `Pg` | - = note: required because of the requirements on the impl of `LoadQuery` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer,), FromClause, Pg>` + = note: required because of the requirements on the impl of `LoadQuery<'_, diesel::SqliteConnection, (i32,)>` for `BoxedSelectStatement<'_, (diesel::sql_types::Integer,), FromClause, Pg>` diff --git a/diesel_compile_tests/tests/fail/cannot_load_default_select_with_group_by.stderr b/diesel_compile_tests/tests/fail/cannot_load_default_select_with_group_by.stderr index 1941f56e400f..45e38a84c9d7 100644 --- a/diesel_compile_tests/tests/fail/cannot_load_default_select_with_group_by.stderr +++ b/diesel_compile_tests/tests/fail/cannot_load_default_select_with_group_by.stderr @@ -8,4 +8,4 @@ error[E0271]: type mismatch resolving `