diff --git a/crates/diesel_models/src/query.rs b/crates/diesel_models/src/query.rs index 468b4441a8..9a886c473c 100644 --- a/crates/diesel_models/src/query.rs +++ b/crates/diesel_models/src/query.rs @@ -44,3 +44,4 @@ pub mod user; pub mod user_authentication_method; pub mod user_key_store; pub mod user_role; +mod utils; diff --git a/crates/diesel_models/src/query/generics.rs b/crates/diesel_models/src/query/generics.rs index bf3238ab4f..e23f4026cc 100644 --- a/crates/diesel_models/src/query/generics.rs +++ b/crates/diesel_models/src/query/generics.rs @@ -4,7 +4,7 @@ use async_bb8_diesel::AsyncRunQueryDsl; use diesel::{ associations::HasTable, debug_query, - dsl::{Find, Limit}, + dsl::{Find, IsNotNull, Limit}, helper_types::{Filter, IntoBoxed}, insertable::CanInsertInSingleQuery, pg::{Pg, PgConnection}, @@ -17,12 +17,12 @@ use diesel::{ LoadQuery, RunQueryDsl, }, result::Error as DieselError, - Expression, Insertable, QueryDsl, QuerySource, Table, + Expression, ExpressionMethods, Insertable, QueryDsl, QuerySource, Table, }; use error_stack::{report, ResultExt}; use router_env::logger; -use crate::{errors, PgPooledConn, StorageResult}; +use crate::{errors, query::utils::GetPrimaryKey, PgPooledConn, StorageResult}; pub mod db_metrics { #[derive(Debug)] @@ -401,7 +401,7 @@ where to_optional(generic_find_one_core::(conn, predicate).await) } -pub async fn generic_filter( +pub(super) async fn generic_filter( conn: &PgPooledConn, predicate: P, limit: Option, @@ -409,8 +409,9 @@ pub async fn generic_filter( order: Option, ) -> StorageResult> where - T: HasTable + Table + BoxedDsl<'static, Pg> + 'static, + T: HasTable
+ Table + BoxedDsl<'static, Pg> + GetPrimaryKey + 'static, IntoBoxed<'static, T, Pg>: FilterDsl> + + FilterDsl, Output = IntoBoxed<'static, T, Pg>> + LimitDsl> + OffsetDsl> + OrderDsl> @@ -421,8 +422,9 @@ where R: Send + 'static, { let mut query = T::table().into_boxed(); - query = query.filter(predicate); - + query = query + .filter(predicate) + .filter(T::table().get_primary_key().is_not_null()); if let Some(limit) = limit { query = query.limit(limit); } diff --git a/crates/diesel_models/src/query/utils.rs b/crates/diesel_models/src/query/utils.rs new file mode 100644 index 0000000000..fe2f43d419 --- /dev/null +++ b/crates/diesel_models/src/query/utils.rs @@ -0,0 +1,144 @@ +use diesel; + +use crate::{schema, schema_v2}; + +/// This trait will return a single column as primary key even in case of composite primary key. +/// +/// In case of composite key, it will return the column that is used as local unique. +pub(super) trait GetPrimaryKey: diesel::Table { + type PK: diesel::ExpressionMethods; + fn get_primary_key(&self) -> Self::PK; +} + +/// This trait must be implemented for all composite keys. +pub(super) trait CompositeKey { + type UK; + /// It will return the local unique key of the composite key. + /// + /// If `(attempt_id, merchant_id)` is the composite key for `payment_attempt` table, then it will return `attempt_id`. + fn get_local_unique_key(&self) -> Self::UK; +} + +/// implementation of `CompositeKey` trait for all the composite keys must be done here. +mod composite_key { + use super::{schema, schema_v2, CompositeKey}; + impl CompositeKey for ::PrimaryKey { + type UK = schema::payment_attempt::dsl::attempt_id; + fn get_local_unique_key(&self) -> Self::UK { + self.0 + } + } + impl CompositeKey for ::PrimaryKey { + type UK = schema::refund::dsl::refund_id; + fn get_local_unique_key(&self) -> Self::UK { + self.1 + } + } + impl CompositeKey for ::PrimaryKey { + type UK = schema::customers::dsl::customer_id; + fn get_local_unique_key(&self) -> Self::UK { + self.0 + } + } + impl CompositeKey for ::PrimaryKey { + type UK = schema::blocklist::dsl::fingerprint_id; + fn get_local_unique_key(&self) -> Self::UK { + self.1 + } + } + impl CompositeKey for ::PrimaryKey { + type UK = schema::incremental_authorization::dsl::authorization_id; + fn get_local_unique_key(&self) -> Self::UK { + self.0 + } + } + impl CompositeKey for ::PrimaryKey { + type UK = schema_v2::incremental_authorization::dsl::authorization_id; + fn get_local_unique_key(&self) -> Self::UK { + self.0 + } + } + impl CompositeKey for ::PrimaryKey { + type UK = schema_v2::blocklist::dsl::fingerprint_id; + fn get_local_unique_key(&self) -> Self::UK { + self.1 + } + } +} + +/// This macro will implement the `GetPrimaryKey` trait for all the tables with single primary key. +macro_rules! impl_get_primary_key { + ($($table:ty),*) => { + $( + impl GetPrimaryKey for $table + { + type PK = <$table as diesel::Table>::PrimaryKey; + fn get_primary_key(&self) -> Self::PK { + ::primary_key(self) + } + } + )* + }; +} +impl_get_primary_key!( + // v1 tables + schema::dashboard_metadata::table, + schema::merchant_connector_account::table, + schema::merchant_key_store::table, + schema::payment_methods::table, + schema::user_authentication_methods::table, + schema::user_key_store::table, + schema::users::table, + schema::api_keys::table, + schema::captures::table, + schema::business_profile::table, + schema::mandate::dsl::mandate, + schema::dispute::table, + schema::events::table, + schema::merchant_account::table, + schema::process_tracker::table, + // v2 tables + schema_v2::dashboard_metadata::table, + schema_v2::merchant_connector_account::table, + schema_v2::merchant_key_store::table, + schema_v2::payment_methods::table, + schema_v2::user_authentication_methods::table, + schema_v2::user_key_store::table, + schema_v2::users::table, + schema_v2::api_keys::table, + schema_v2::captures::table, + schema_v2::business_profile::table, + schema_v2::mandate::table, + schema_v2::dispute::table, + schema_v2::events::table, + schema_v2::merchant_account::table, + schema_v2::process_tracker::table, + schema_v2::refund::table, + schema_v2::customers::table, + schema_v2::payment_attempt::table +); + +/// This macro will implement the `GetPrimaryKey` trait for all the tables with composite key. +macro_rules! impl_get_primary_key_for_composite { + ($($table:ty),*) => { + $( + impl GetPrimaryKey for $table + { + type PK = <<$table as diesel::Table>::PrimaryKey as CompositeKey>::UK; + fn get_primary_key(&self) -> Self::PK { + ::primary_key(self).get_local_unique_key() + } + } + )* + }; +} + +impl_get_primary_key_for_composite!( + schema::payment_attempt::table, + schema::refund::table, + schema::customers::table, + schema::blocklist::table, + schema::incremental_authorization::table, + schema_v2::incremental_authorization::table, + schema_v2::blocklist::table +);