|
| 1 | +#[macro_export] |
| 2 | +macro_rules! from_sql_function { |
| 3 | + ( |
| 4 | + $fn_name: ident ($($arg: ident : $arg_ty: ty)*) { |
| 5 | + $($(#[$($meta: tt)+])* $field_name: ident -> $field_ty: ty,)* |
| 6 | + } |
| 7 | + ) => { |
| 8 | + #[allow(dead_code)] |
| 9 | + mod $fn_name { |
| 10 | + use diesel::query_source::*; |
| 11 | + use diesel::expression::*; |
| 12 | + use diesel::query_builder::*; |
| 13 | + use diesel::sql_types::*; |
| 14 | + use wundergraph::helper::NamedTable; |
| 15 | + |
| 16 | + #[allow(non_camel_case_types)] |
| 17 | + pub struct $fn_name { |
| 18 | + $($arg: std::sync::Arc<dyn diesel::expression::BoxableExpression<(), diesel::pg::Pg, SqlType = $arg_ty>>,)* |
| 19 | + } |
| 20 | + |
| 21 | + #[allow(non_camel_case_types)] |
| 22 | + pub type table = $fn_name; |
| 23 | + |
| 24 | + impl Clone for $fn_name { |
| 25 | + fn clone(&self) -> Self { |
| 26 | + Self { |
| 27 | + $($arg: self.$arg.clone(),)* |
| 28 | + } |
| 29 | + } |
| 30 | + } |
| 31 | + |
| 32 | + impl Table for $fn_name { |
| 33 | + type PrimaryKey = $crate::from_sql_function!(@collect_primary_key [] $($(#[$($meta)*])* $field_name,)*); |
| 34 | + type AllColumns = ($(columns::$field_name,)*); |
| 35 | + |
| 36 | + fn primary_key(&self) -> Self::PrimaryKey { |
| 37 | + $crate::from_sql_function!(@collect_primary_key [] $($(#[$($meta)*])* $field_name,)*) |
| 38 | + } |
| 39 | + |
| 40 | + fn all_columns() -> Self::AllColumns { |
| 41 | + ($(columns::$field_name,)*) |
| 42 | + } |
| 43 | + } |
| 44 | + |
| 45 | + impl QueryId for $fn_name { |
| 46 | + type QueryId = (); |
| 47 | + const HAS_STATIC_QUERY_ID: bool = false; |
| 48 | + } |
| 49 | + |
| 50 | + impl AppearsInFromClause<$fn_name> for $fn_name { |
| 51 | + type Count = Once; |
| 52 | + } |
| 53 | + |
| 54 | + impl AppearsInFromClause<$fn_name> for () { |
| 55 | + type Count = Never; |
| 56 | + } |
| 57 | + |
| 58 | + impl AsQuery for $fn_name { |
| 59 | + type SqlType = <<Self as Table>::AllColumns as Expression>::SqlType; |
| 60 | + type Query = SelectStatement<$fn_name>; |
| 61 | + |
| 62 | + fn as_query(self) -> Self::Query { |
| 63 | + SelectStatement::simple(self) |
| 64 | + } |
| 65 | + } |
| 66 | + |
| 67 | + impl QuerySource for $fn_name { |
| 68 | + type FromClause = Self; |
| 69 | + type DefaultSelection = <Self as Table>::AllColumns; |
| 70 | + |
| 71 | + fn from_clause(&self) -> Self::FromClause { |
| 72 | + self.clone() |
| 73 | + } |
| 74 | + |
| 75 | + fn default_selection(&self) -> Self::DefaultSelection { |
| 76 | + Self::all_columns() |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + impl diesel::associations::HasTable for $fn_name { |
| 81 | + type Table = Self; |
| 82 | + |
| 83 | + fn table() -> Self { |
| 84 | + Self { |
| 85 | + $($arg: std::sync::Arc::new(diesel::dsl::sql("")),)* |
| 86 | + } |
| 87 | + } |
| 88 | + } |
| 89 | + |
| 90 | + impl QueryFragment<diesel::pg::Pg> for $fn_name { |
| 91 | + #[allow(dead_code, unused_assignments)] |
| 92 | + fn walk_ast(&self, mut pass: AstPass<diesel::pg::Pg>) -> diesel::result::QueryResult<()> { |
| 93 | + pass.push_sql(stringify!($fn_name)); |
| 94 | + pass.push_sql("("); |
| 95 | + let mut first = true; |
| 96 | + $( |
| 97 | + if first { |
| 98 | + first = false; |
| 99 | + } else { |
| 100 | + pass.push_sql(", "); |
| 101 | + } |
| 102 | + self.$arg.walk_ast(pass.reborrow())?; |
| 103 | + )* |
| 104 | + pass.push_sql(") AS "); |
| 105 | + pass.push_sql(stringify!($fn_name)); |
| 106 | + Ok(()) |
| 107 | + } |
| 108 | + } |
| 109 | + |
| 110 | + impl NamedTable for $fn_name { |
| 111 | + fn name(&self) -> std::borrow::Cow<'static, str> { |
| 112 | + ::std::borrow::Cow::Borrowed(stringify!($fn_name)) |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + mod columns { |
| 117 | + use diesel::sql_types::*; |
| 118 | + use diesel::prelude::*; |
| 119 | + use diesel::expression::NonAggregate; |
| 120 | + use diesel::query_builder::{QueryFragment, AstPass}; |
| 121 | + |
| 122 | + $( |
| 123 | + #[derive(Debug, Default, Copy, Clone)] |
| 124 | + #[allow(non_camel_case_types)] |
| 125 | + pub struct $field_name; |
| 126 | + |
| 127 | + impl diesel::expression::Expression for $field_name { |
| 128 | + type SqlType = $field_ty; |
| 129 | + } |
| 130 | + |
| 131 | + impl SelectableExpression<super::$fn_name> for $field_name {} |
| 132 | + impl AppearsOnTable<super::$fn_name> for $field_name {} |
| 133 | + impl NonAggregate for $field_name {} |
| 134 | + impl Column for $field_name { |
| 135 | + type Table = super::$fn_name; |
| 136 | + const NAME: &'static str = stringify!($field_name); |
| 137 | + } |
| 138 | + |
| 139 | + impl QueryFragment<diesel::pg::Pg> for $field_name { |
| 140 | + fn walk_ast(&self, mut pass: AstPass<diesel::pg::Pg>) -> diesel::result::QueryResult<()> { |
| 141 | + pass.push_identifier(stringify!($fn_name))?; |
| 142 | + pass.push_sql("."); |
| 143 | + pass.push_identifier(stringify!($field_name))?; |
| 144 | + Ok(()) |
| 145 | + } |
| 146 | + } |
| 147 | + |
| 148 | + impl<T> diesel::EqAll<T> for $field_name where |
| 149 | + T: diesel::expression::AsExpression<$field_ty>, |
| 150 | + diesel::dsl::Eq<$field_name, T>: diesel::Expression<SqlType=diesel::sql_types::Bool>, |
| 151 | + { |
| 152 | + type Output = diesel::dsl::Eq<Self, T>; |
| 153 | + |
| 154 | + fn eq_all(self, rhs: T) -> Self::Output { |
| 155 | + diesel::expression::operators::Eq::new(self, rhs.as_expression()) |
| 156 | + } |
| 157 | + } |
| 158 | + |
| 159 | + |
| 160 | + )* |
| 161 | + } |
| 162 | + |
| 163 | + pub use columns::*; |
| 164 | + |
| 165 | + #[allow(non_camel_case_types)] |
| 166 | + pub(super) mod function { |
| 167 | + use diesel::sql_types::*; |
| 168 | + |
| 169 | + pub fn $fn_name<$($arg,)*>($($arg: $arg,)*) -> super::$fn_name |
| 170 | + where $( |
| 171 | + $arg: diesel::expression::AsExpression<$arg_ty>, |
| 172 | + <$arg as diesel::expression::AsExpression<$arg_ty>>::Expression: diesel::expression::BoxableExpression<(), diesel::pg::Pg, SqlType = $arg_ty> + 'static, |
| 173 | + )* |
| 174 | + { |
| 175 | + super::$fn_name { |
| 176 | + $($arg: std::sync::Arc::new($arg.as_expression()),)* |
| 177 | + } |
| 178 | + } |
| 179 | + } |
| 180 | + } |
| 181 | + |
| 182 | + #[allow(dead_code)] |
| 183 | + pub use self::$fn_name::function::$fn_name; |
| 184 | + |
| 185 | + }; |
| 186 | + |
| 187 | + (@collect_primary_key |
| 188 | + [$($pk: ident,)*] |
| 189 | + $(#[$($meta: tt)*])* #[primary_key] $(#[$($meta2: tt)*])* |
| 190 | + $field: ident, $($rest: tt)* |
| 191 | + ) => { |
| 192 | + $crate::from_sql_function!(@collect_primary_key [$($pk,)* $field] $($rest)*) |
| 193 | + }; |
| 194 | + |
| 195 | + (@collect_primary_key |
| 196 | + [$($pk: ident,)*] |
| 197 | + $(#[$($meta: tt)*])* |
| 198 | + $field: ident, $($rest: tt)* |
| 199 | + ) => { |
| 200 | + $crate::from_sql_function!(@collect_primary_key [$($pk,)*] $($rest)*) |
| 201 | + }; |
| 202 | + (@collect_primary_key [$pk: ident,]) => { |
| 203 | + $pk |
| 204 | + }; |
| 205 | + (@collect_primary_key [$($pk: ident,)+]) => { |
| 206 | + ($($pk,)*) |
| 207 | + }; |
| 208 | + (@collect_primary_key []) => { |
| 209 | + columns::id |
| 210 | + }; |
| 211 | +} |
0 commit comments