Skip to content

Commit ad41d9b

Browse files
committed
Add a macro for defining sql functions compatible with from clauses
1 parent 3404747 commit ad41d9b

File tree

2 files changed

+213
-0
lines changed

2 files changed

+213
-0
lines changed

src/diesel_ext.rs

+211
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
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+
}

src/main.rs

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ mod model;
1616
mod pagination;
1717
#[allow(unused_imports)]
1818
mod schema;
19+
#[macro_use]
20+
mod diesel_ext;
1921

2022
use self::graphql::{Mutation, Query};
2123

0 commit comments

Comments
 (0)