From a2ab4096a1ad8296a63f739fae2329fcb7290e3d Mon Sep 17 00:00:00 2001 From: Billy Chan Date: Tue, 11 Apr 2023 16:01:58 +0800 Subject: [PATCH] feat: implement `PartialEq` for `DynIden`, `SimpleExpr` and related types (#620) * feat: implement `PartialEq` for `SimpleExpr` and its related types * ship our own `SeaRc`; old `SeaRc` is being re-export as `RcOrArc` * fixup * fmt * bad idea to add `any::Any` as the super trait of `Iden` * experiment * assert two `dyn Iden` are of same type by comparing the memory address of vtable * fmt * more tests --- src/expr.rs | 2 +- src/extension/postgres/func.rs | 2 +- src/func.rs | 4 +- src/query/case.rs | 4 +- src/query/condition.rs | 8 +-- src/query/delete.rs | 2 +- src/query/insert.rs | 4 +- src/query/mod.rs | 2 +- src/query/on_conflict.rs | 8 +-- src/query/returning.rs | 2 +- src/query/select.rs | 12 ++-- src/query/update.rs | 2 +- src/query/window.rs | 8 +-- src/query/with.rs | 12 ++-- src/table/column.rs | 4 +- src/types.rs | 105 +++++++++++++++++++++++++++++---- src/value.rs | 4 +- 17 files changed, 134 insertions(+), 51 deletions(-) diff --git a/src/expr.rs b/src/expr.rs index 3773f8952..25d5216b7 100644 --- a/src/expr.rs +++ b/src/expr.rs @@ -19,7 +19,7 @@ pub struct Expr { /// /// [`SimpleExpr`] is a node in the expression tree and can represent identifiers, function calls, /// various operators and sub-queries. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SimpleExpr { Column(ColumnRef), Tuple(Vec), diff --git a/src/extension/postgres/func.rs b/src/extension/postgres/func.rs index cf9866e97..642d08759 100644 --- a/src/extension/postgres/func.rs +++ b/src/extension/postgres/func.rs @@ -3,7 +3,7 @@ use crate::{expr::*, func::*}; /// Functions -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum PgFunction { ToTsquery, ToTsvector, diff --git a/src/func.rs b/src/func.rs index e3f3569b9..38b079c83 100644 --- a/src/func.rs +++ b/src/func.rs @@ -6,7 +6,7 @@ use crate::{expr::*, types::*}; pub use crate::extension::postgres::{PgFunc, PgFunction}; /// Functions -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Function { Max, Min, @@ -29,7 +29,7 @@ pub enum Function { } /// Function call. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct FunctionCall { pub(crate) func: Function, pub(crate) args: Vec, diff --git a/src/query/case.rs b/src/query/case.rs index 5faaa7dd0..22e7833d0 100644 --- a/src/query/case.rs +++ b/src/query/case.rs @@ -1,12 +1,12 @@ use crate::{Condition, IntoCondition, SimpleExpr}; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub(crate) struct CaseStatementCondition { pub(crate) condition: Condition, pub(crate) result: SimpleExpr, } -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct CaseStatement { pub(crate) when: Vec, pub(crate) r#else: Option, diff --git a/src/query/condition.rs b/src/query/condition.rs index c8b148479..826101b33 100644 --- a/src/query/condition.rs +++ b/src/query/condition.rs @@ -7,7 +7,7 @@ pub enum ConditionType { } /// Represents the value of an [`Condition::any`] or [`Condition::all`]: a set of disjunctive or conjunctive conditions. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct Condition { pub(crate) negate: bool, pub(crate) condition_type: ConditionType, @@ -23,13 +23,13 @@ pub type Cond = Condition; /// Represents anything that can be passed to an [`Condition::any`] or [`Condition::all`]'s [`Condition::add`] method. /// /// The arguments are automatically converted to the right enum. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum ConditionExpression { Condition(Condition), SimpleExpr(SimpleExpr), } -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, PartialEq)] pub enum ConditionHolderContents { #[default] Empty, @@ -37,7 +37,7 @@ pub enum ConditionHolderContents { Condition(Condition), } -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, PartialEq)] pub struct ConditionHolder { pub contents: ConditionHolderContents, } diff --git a/src/query/delete.rs b/src/query/delete.rs index 61fb13caf..16b64b615 100644 --- a/src/query/delete.rs +++ b/src/query/delete.rs @@ -37,7 +37,7 @@ use inherent::inherent; /// r#"DELETE FROM "glyph" WHERE "id" < 1 OR "id" > 10"# /// ); /// ``` -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, PartialEq)] pub struct DeleteStatement { pub(crate) table: Option>, pub(crate) r#where: ConditionHolder, diff --git a/src/query/insert.rs b/src/query/insert.rs index c23fa6867..590bc23bc 100644 --- a/src/query/insert.rs +++ b/src/query/insert.rs @@ -9,7 +9,7 @@ use inherent::inherent; /// /// [`InsertValueSource`] is a node in the expression tree and can represent a raw value set /// ('VALUES') or a select query. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub(crate) enum InsertValueSource { Values(Vec>), Select(Box), @@ -42,7 +42,7 @@ pub(crate) enum InsertValueSource { /// r#"INSERT INTO "glyph" ("aspect", "image") VALUES (5.15, '12A'), (4.21, '123')"# /// ); /// ``` -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, PartialEq)] pub struct InsertStatement { pub(crate) replace: bool, pub(crate) table: Option>, diff --git a/src/query/mod.rs b/src/query/mod.rs index 47486f7ee..eb414efee 100644 --- a/src/query/mod.rs +++ b/src/query/mod.rs @@ -46,7 +46,7 @@ pub enum QueryStatement { Delete(DeleteStatement), } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SubQueryStatement { SelectStatement(SelectStatement), InsertStatement(InsertStatement), diff --git a/src/query/on_conflict.rs b/src/query/on_conflict.rs index 2f0498e14..280a8491f 100644 --- a/src/query/on_conflict.rs +++ b/src/query/on_conflict.rs @@ -1,6 +1,6 @@ use crate::{ConditionHolder, DynIden, IntoCondition, IntoIden, SimpleExpr}; -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct OnConflict { pub(crate) target: Option, pub(crate) target_where: ConditionHolder, @@ -9,14 +9,14 @@ pub struct OnConflict { } /// Represents ON CONFLICT (upsert) targets -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum OnConflictTarget { /// A list of columns with unique constraint ConflictColumns(Vec), } /// Represents ON CONFLICT (upsert) actions -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum OnConflictAction { /// Do nothing DoNothing, @@ -25,7 +25,7 @@ pub enum OnConflictAction { } /// Represents strategies to update column in ON CONFLICT (upsert) actions -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum OnConflictUpdate { /// Update column value of existing row with inserting value Column(DynIden), diff --git a/src/query/returning.rs b/src/query/returning.rs index c53039e2b..15724edc3 100644 --- a/src/query/returning.rs +++ b/src/query/returning.rs @@ -7,7 +7,7 @@ use crate::{ColumnRef, IntoColumnRef, SimpleExpr}; /// * SQLite /// - SQLite version >= 3.35.0 /// - **Note that sea-query won't try to enforce either of these constraints** -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum ReturningClause { All, Columns(Vec), diff --git a/src/query/select.rs b/src/query/select.rs index 656fc45f6..b8c11b094 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -39,7 +39,7 @@ use inherent::inherent; /// r#"SELECT "character", "font"."name" FROM "character" LEFT JOIN "font" ON "character"."font_id" = "font"."id" WHERE "size_w" IN (3, 4) AND "character" LIKE 'A%'"# /// ); /// ``` -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, PartialEq)] pub struct SelectStatement { pub(crate) distinct: Option, pub(crate) selects: Vec, @@ -57,7 +57,7 @@ pub struct SelectStatement { } /// List of distinct keywords that can be used in select statement -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SelectDistinct { All, Distinct, @@ -66,7 +66,7 @@ pub enum SelectDistinct { } /// Window type in [`SelectExpr`] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum WindowSelectType { /// Name in [`SelectStatement`] Name(DynIden), @@ -75,7 +75,7 @@ pub enum WindowSelectType { } /// Select expression used in select statement -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct SelectExpr { pub expr: SimpleExpr, pub alias: Option, @@ -83,7 +83,7 @@ pub struct SelectExpr { } /// Join expression used in select statement -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct JoinExpr { pub join: JoinType, pub table: Box, @@ -109,7 +109,7 @@ pub enum LockBehavior { SkipLocked, } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct LockClause { pub(crate) r#type: LockType, pub(crate) tables: Vec, diff --git a/src/query/update.rs b/src/query/update.rs index 93ef78855..b267e813f 100644 --- a/src/query/update.rs +++ b/src/query/update.rs @@ -36,7 +36,7 @@ use inherent::inherent; /// r#"UPDATE "glyph" SET "aspect" = 1.23, "image" = '123' WHERE "id" = 1"# /// ); /// ``` -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, PartialEq)] pub struct UpdateStatement { pub(crate) table: Option>, pub(crate) values: Vec<(DynIden, Box)>, diff --git a/src/query/window.rs b/src/query/window.rs index 4374eeeac..e78667212 100644 --- a/src/query/window.rs +++ b/src/query/window.rs @@ -40,7 +40,7 @@ pub trait OverStatement { } /// frame_start or frame_end clause -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Frame { UnboundedPreceding, Preceding(u32), @@ -50,14 +50,14 @@ pub enum Frame { } /// Frame type -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum FrameType { Range, Rows, } /// Frame clause -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct FrameClause { pub(crate) r#type: FrameType, pub(crate) start: Frame, @@ -71,7 +71,7 @@ pub struct FrameClause { /// 1. /// 2. /// 3. -#[derive(Default, Debug, Clone)] +#[derive(Default, Debug, Clone, PartialEq)] pub struct WindowStatement { pub(crate) partition_by: Vec, pub(crate) order_by: Vec, diff --git a/src/query/with.rs b/src/query/with.rs index d7e5287ba..4b1e4d3fc 100644 --- a/src/query/with.rs +++ b/src/query/with.rs @@ -53,7 +53,7 @@ use std::ops::Deref; /// DELETE FROM table WHERE table.a = cte_name.a) /// /// It is mandatory to set the [Self::table_name] and the [Self::query]. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct CommonTableExpression { pub(crate) table_name: Option, pub(crate) cols: Vec, @@ -193,7 +193,7 @@ impl CommonTableExpression { /// For recursive [WithQuery] [WithClause]s the traversing order can be specified in some databases /// that support this functionality. -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum SearchOrder { /// Breadth first traversal during the execution of the recursive query. BREADTH, @@ -211,7 +211,7 @@ pub enum SearchOrder { /// /// Setting [Self::order] and [Self::expr] is mandatory. The [SelectExpr] used must specify an alias /// which will be the name that you can use to order the result of the [CommonTableExpression]. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct Search { pub(crate) order: Option, pub(crate) expr: Option, @@ -269,7 +269,7 @@ impl Search { /// A query can have both SEARCH and CYCLE clauses. /// /// Setting [Self::set], [Self::expr] and [Self::using] is mandatory. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct Cycle { pub(crate) expr: Option, pub(crate) set_as: Option, @@ -433,7 +433,7 @@ impl Cycle { /// r#"WITH RECURSIVE "cte_traversal" ("id", "depth", "next", "value") AS (SELECT "id", 1, "next", "value" FROM "table" UNION ALL SELECT "id", "depth" + 1, "next", "value" FROM "table" INNER JOIN "cte_traversal" ON "cte_traversal"."next" = "table"."id") SELECT * FROM "cte_traversal""# /// ); /// ``` -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct WithClause { pub(crate) recursive: bool, pub(crate) search: Option, @@ -533,7 +533,7 @@ impl WithClause { /// DELETE FROM table WHERE table.a = cte_name.a) /// /// It is mandatory to set the [Self::cte] and the [Self::query]. -#[derive(Debug, Clone, Default)] +#[derive(Debug, Clone, Default, PartialEq)] pub struct WithQuery { pub(crate) with_clause: WithClause, pub(crate) query: Option>, diff --git a/src/table/column.rs b/src/table/column.rs index 52bdf2c29..4e56121b9 100644 --- a/src/table/column.rs +++ b/src/table/column.rs @@ -48,7 +48,7 @@ pub enum ColumnType { name: DynIden, variants: Vec, }, - Array(SeaRc), + Array(RcOrArc), Cidr, Inet, MacAddr, @@ -555,7 +555,7 @@ impl ColumnDef { /// Set column type as an array with a specified element type. /// This is only supported on Postgres. pub fn array(&mut self, elem_type: ColumnType) -> &mut Self { - self.types = Some(ColumnType::Array(SeaRc::new(elem_type))); + self.types = Some(ColumnType::Array(RcOrArc::new(elem_type))); self } diff --git a/src/types.rs b/src/types.rs index 24e3dac72..cc6618842 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,16 +1,16 @@ //! Base types used throughout sea-query. use crate::{expr::*, query::*, FunctionCall, ValueTuple, Values}; -use std::fmt; +use std::{fmt, mem, ops}; #[cfg(feature = "backend-postgres")] use crate::extension::postgres::PgBinOper; #[cfg(feature = "backend-sqlite")] use crate::extension::sqlite::SqliteBinOper; #[cfg(not(feature = "thread-safe"))] -pub use std::rc::Rc as SeaRc; +pub use std::rc::Rc as RcOrArc; #[cfg(feature = "thread-safe")] -pub use std::sync::Arc as SeaRc; +pub use std::sync::Arc as RcOrArc; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct Quote(pub(crate) u8, pub(crate) u8); @@ -52,6 +52,46 @@ iden_trait!(); pub type DynIden = SeaRc; +#[derive(Debug)] +#[repr(transparent)] +pub struct SeaRc(pub(crate) RcOrArc) +where + I: ?Sized; + +impl ops::Deref for SeaRc { + type Target = RcOrArc; + + fn deref(&self) -> &RcOrArc { + &self.0 + } +} + +impl Clone for SeaRc { + fn clone(&self) -> SeaRc { + SeaRc(RcOrArc::clone(&self.0)) + } +} + +impl PartialEq for SeaRc { + fn eq(&self, other: &Self) -> bool { + let (self_vtable, other_vtable) = unsafe { + let (_, self_vtable) = mem::transmute::<&dyn Iden, (usize, usize)>(&*self.0); + let (_, other_vtable) = mem::transmute::<&dyn Iden, (usize, usize)>(&*other.0); + (self_vtable, other_vtable) + }; + self_vtable == other_vtable && self.to_string() == other.to_string() + } +} + +impl SeaRc { + pub fn new(i: I) -> SeaRc + where + I: Iden + 'static, + { + SeaRc(RcOrArc::new(i)) + } +} + pub trait IntoIden { fn into_iden(self) -> DynIden; } @@ -70,7 +110,7 @@ impl fmt::Debug for dyn Iden { } /// Column references -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum ColumnRef { Column(DynIden), TableColumn(DynIden, DynIden), @@ -85,7 +125,7 @@ pub trait IntoColumnRef { /// Table references #[allow(clippy::large_enum_variant)] -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum TableRef { /// Table identifier without any schema / database prefix Table(DynIden), @@ -152,7 +192,7 @@ pub enum BinOper { } /// Logical chain operator -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum LogicalChainOper { And(SimpleExpr), Or(SimpleExpr), @@ -177,7 +217,7 @@ pub enum NullOrdering { } /// Order expression -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub struct OrderExpr { pub(crate) expr: SimpleExpr, pub(crate) order: Order, @@ -185,7 +225,7 @@ pub struct OrderExpr { } /// Join on types -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum JoinOn { Condition(Box), Columns(Vec), @@ -264,7 +304,7 @@ pub struct NullAlias; pub struct Asterisk; /// SQL Keywords -#[derive(Debug, Clone)] +#[derive(Debug, Clone, PartialEq)] pub enum Keyword { Null, CurrentDate, @@ -285,7 +325,7 @@ pub trait IntoLikeExpr { } /// SubQuery operators -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq)] pub enum SubQueryOper { Exists, Any, @@ -571,8 +611,9 @@ where #[cfg(test)] mod tests { - use crate::*; + pub use crate::{tests_cfg::*, *}; use pretty_assertions::assert_eq; + pub use Character as CharReexport; #[test] fn test_identifier() { @@ -629,4 +670,46 @@ mod tests { r#"SELECT "hel""""lo""# ); } + + #[test] + fn test_cmp_identifier() { + type CharLocal = Character; + + assert_eq!( + ColumnRef::Column(Character::Id.into_iden()), + ColumnRef::Column(Character::Id.into_iden()) + ); + assert_eq!( + ColumnRef::Column(Character::Id.into_iden()), + ColumnRef::Column(Char::Id.into_iden()) + ); + assert_eq!( + ColumnRef::Column(Character::Id.into_iden()), + ColumnRef::Column(CharLocal::Id.into_iden()) + ); + assert_eq!( + ColumnRef::Column(Character::Id.into_iden()), + ColumnRef::Column(CharReexport::Id.into_iden()) + ); + assert_eq!( + ColumnRef::Column(Alias::new("id").into_iden()), + ColumnRef::Column(Alias::new("id").into_iden()) + ); + assert_ne!( + ColumnRef::Column(Alias::new("id").into_iden()), + ColumnRef::Column(Alias::new("id_").into_iden()) + ); + assert_ne!( + ColumnRef::Column(Character::Id.into_iden()), + ColumnRef::Column(Alias::new("id").into_iden()) + ); + assert_ne!( + ColumnRef::Column(Character::Id.into_iden()), + ColumnRef::Column(Character::Table.into_iden()) + ); + assert_ne!( + ColumnRef::Column(Character::Id.into_iden()), + ColumnRef::Column(Font::Id.into_iden()) + ); + } } diff --git a/src/value.rs b/src/value.rs index a17d13e14..95819c5cb 100644 --- a/src/value.rs +++ b/src/value.rs @@ -765,7 +765,7 @@ mod with_mac_address { #[cfg_attr(docsrs, doc(cfg(feature = "postgres-array")))] pub mod with_array { use super::*; - use crate::SeaRc; + use crate::RcOrArc; // We only imlement conversion from Vec to Array when T is not u8. // This is because for u8's case, there is already conversion to Byte defined above. @@ -886,7 +886,7 @@ pub mod with_array { fn column_type() -> ColumnType { use ColumnType::*; - Array(SeaRc::new(T::column_type())) + Array(RcOrArc::new(T::column_type())) } } }