diesel/pg/query_builder/
tablesample.rs

1use crate::expression::{Expression, ValidGrouping};
2use crate::pg::Pg;
3use crate::query_builder::{AsQuery, AstPass, FromClause, QueryFragment, QueryId, SelectStatement};
4use crate::query_source::private::PlainQuerySource;
5use crate::query_source::{QueryRelation, QuerySource, TableNotEqual};
6use crate::result::QueryResult;
7use crate::sql_types::{Double, SmallInt};
8use crate::{JoinTo, SelectableExpression, Table};
9use std::marker::PhantomData;
10
11#[doc(hidden)]
12pub trait TablesampleMethod: Clone {
13    fn method_name_sql() -> &'static str;
14}
15
16#[derive(#[automatically_derived]
impl ::core::clone::Clone for BernoulliMethod {
    #[inline]
    fn clone(&self) -> BernoulliMethod { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for BernoulliMethod { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for BernoulliMethod {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "BernoulliMethod")
    }
}Debug)]
17/// Used to specify the `BERNOULLI` sampling method.
18pub struct BernoulliMethod;
19
20impl TablesampleMethod for BernoulliMethod {
21    fn method_name_sql() -> &'static str {
22        "BERNOULLI"
23    }
24}
25
26#[derive(#[automatically_derived]
impl ::core::clone::Clone for SystemMethod {
    #[inline]
    fn clone(&self) -> SystemMethod { *self }
}Clone, #[automatically_derived]
impl ::core::marker::Copy for SystemMethod { }Copy, #[automatically_derived]
impl ::core::fmt::Debug for SystemMethod {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::write_str(f, "SystemMethod")
    }
}Debug)]
27/// Used to specify the `SYSTEM` sampling method.
28pub struct SystemMethod;
29
30impl TablesampleMethod for SystemMethod {
31    fn method_name_sql() -> &'static str {
32        "SYSTEM"
33    }
34}
35
36/// Represents a query with a `TABLESAMPLE` clause.
37#[derive(#[automatically_derived]
impl<S: ::core::fmt::Debug, TSM: ::core::fmt::Debug> ::core::fmt::Debug for
    Tablesample<S, TSM> where TSM: TablesampleMethod {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field4_finish(f, "Tablesample",
            "source", &self.source, "method", &self.method, "portion",
            &self.portion, "seed", &&self.seed)
    }
}Debug, #[automatically_derived]
impl<S: ::core::clone::Clone, TSM: ::core::clone::Clone> ::core::clone::Clone
    for Tablesample<S, TSM> where TSM: TablesampleMethod {
    #[inline]
    fn clone(&self) -> Tablesample<S, TSM> {
        Tablesample {
            source: ::core::clone::Clone::clone(&self.source),
            method: ::core::clone::Clone::clone(&self.method),
            portion: ::core::clone::Clone::clone(&self.portion),
            seed: ::core::clone::Clone::clone(&self.seed),
        }
    }
}Clone, #[automatically_derived]
impl<S: ::core::marker::Copy, TSM: ::core::marker::Copy> ::core::marker::Copy
    for Tablesample<S, TSM> where TSM: TablesampleMethod {
}Copy)]
38pub struct Tablesample<S, TSM>
39where
40    TSM: TablesampleMethod,
41{
42    source: S,
43    method: PhantomData<TSM>,
44    portion: i16,
45    seed: Option<f64>,
46}
47
48impl<S, TSM> Tablesample<S, TSM>
49where
50    TSM: TablesampleMethod,
51{
52    pub(crate) fn new(source: S, portion: i16) -> Tablesample<S, TSM> {
53        Tablesample {
54            source,
55            method: PhantomData,
56            portion,
57            seed: None,
58        }
59    }
60
61    /// This method allows you to specify the random number generator seed to use in the sampling
62    /// method. This allows you to obtain repeatable results.
63    pub fn with_seed(self, seed: f64) -> Tablesample<S, TSM> {
64        Tablesample {
65            source: self.source,
66            method: self.method,
67            portion: self.portion,
68            seed: Some(seed),
69        }
70    }
71}
72
73#[diagnostic::do_not_recommend]
74impl<T1, T2, TSM> TableNotEqual<T1> for Tablesample<T2, TSM>
75where
76    T1: QueryRelation,
77    T2: TableNotEqual<T1>,
78    TSM: TablesampleMethod,
79    Self: Table,
80{
81}
82
83#[diagnostic::do_not_recommend]
84impl<T1, T2, TSM> TableNotEqual<Tablesample<T1, TSM>> for T2
85where
86    T1: QueryRelation,
87    T2: PlainQuerySource + TableNotEqual<T1>,
88    Tablesample<T1, TSM>: Table,
89    TSM: TablesampleMethod,
90{
91}
92
93impl<S, TSM> QueryId for Tablesample<S, TSM>
94where
95    S: QueryId,
96    TSM: TablesampleMethod,
97{
98    type QueryId = ();
99    const HAS_STATIC_QUERY_ID: bool = false;
100}
101
102impl<S, TSM> QuerySource for Tablesample<S, TSM>
103where
104    S: Table + Clone,
105    TSM: TablesampleMethod,
106    <S as QuerySource>::DefaultSelection:
107        ValidGrouping<()> + SelectableExpression<Tablesample<S, TSM>>,
108{
109    type FromClause = Self;
110    type DefaultSelection = <S as QuerySource>::DefaultSelection;
111
112    fn from_clause(&self) -> Self::FromClause {
113        self.clone()
114    }
115
116    fn default_selection(&self) -> Self::DefaultSelection {
117        self.source.default_selection()
118    }
119}
120
121impl<S, TSM> QueryFragment<Pg> for Tablesample<S, TSM>
122where
123    S: QueryFragment<Pg>,
124    TSM: TablesampleMethod,
125{
126    fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
127        self.source.walk_ast(out.reborrow())?;
128        out.push_sql(" TABLESAMPLE ");
129        out.push_sql(TSM::method_name_sql());
130        out.push_sql("(");
131        out.push_bind_param::<SmallInt, _>(&self.portion)?;
132        out.push_sql(")");
133        if let Some(f) = &self.seed {
134            out.push_sql(" REPEATABLE(");
135            out.push_bind_param::<Double, _>(f)?;
136            out.push_sql(")");
137        }
138        Ok(())
139    }
140}
141
142impl<S, TSM> AsQuery for Tablesample<S, TSM>
143where
144    S: Table + Clone,
145    TSM: TablesampleMethod,
146    <S as QuerySource>::DefaultSelection:
147        ValidGrouping<()> + SelectableExpression<Tablesample<S, TSM>>,
148{
149    type SqlType = <<Self as QuerySource>::DefaultSelection as Expression>::SqlType;
150    type Query = SelectStatement<FromClause<Self>>;
151    fn as_query(self) -> Self::Query {
152        SelectStatement::simple(self)
153    }
154}
155
156impl<S, T, TSM> JoinTo<T> for Tablesample<S, TSM>
157where
158    S: JoinTo<T>,
159    T: Table,
160    S: Table,
161    TSM: TablesampleMethod,
162{
163    type FromClause = <S as JoinTo<T>>::FromClause;
164    type OnClause = <S as JoinTo<T>>::OnClause;
165
166    fn join_target(rhs: T) -> (Self::FromClause, Self::OnClause) {
167        <S as JoinTo<T>>::join_target(rhs)
168    }
169}
170
171impl<S, TSM> Table for Tablesample<S, TSM>
172where
173    S: Table + Clone + AsQuery,
174    TSM: TablesampleMethod,
175
176    <S as Table>::PrimaryKey: SelectableExpression<Tablesample<S, TSM>>,
177    <S as Table>::AllColumns: SelectableExpression<Tablesample<S, TSM>>,
178    <S as QuerySource>::DefaultSelection:
179        ValidGrouping<()> + SelectableExpression<Tablesample<S, TSM>>,
180{
181    type PrimaryKey = <S as Table>::PrimaryKey;
182    type AllColumns = <S as Table>::AllColumns;
183
184    fn primary_key(&self) -> Self::PrimaryKey {
185        self.source.primary_key()
186    }
187
188    fn all_columns() -> Self::AllColumns {
189        S::all_columns()
190    }
191}
192
193#[cfg(test)]
194mod test {
195    use super::*;
196    use crate::backend::Backend;
197    use crate::query_builder::QueryBuilder;
198    use diesel::dsl::*;
199    use diesel::*;
200
201    macro_rules! assert_sql {
202        ($query:expr, $sql:expr) => {
203            let mut query_builder = <Pg as Backend>::QueryBuilder::default();
204            $query.to_sql(&mut query_builder, &Pg).unwrap();
205            let sql = query_builder.finish();
206            assert_eq!(sql, $sql);
207        };
208    }
209
210    table! {
211        users {
212            id -> Integer,
213            name -> VarChar,
214        }
215    }
216
217    #[diesel_test_helper::test]
218    fn test_generated_tablesample_sql() {
219        assert_sql!(
220            users::table.tablesample_bernoulli(10),
221            "\"users\" TABLESAMPLE BERNOULLI($1)"
222        );
223
224        assert_sql!(
225            users::table.tablesample_system(10),
226            "\"users\" TABLESAMPLE SYSTEM($1)"
227        );
228
229        assert_sql!(
230            users::table.tablesample_system(10).with_seed(42.0),
231            "\"users\" TABLESAMPLE SYSTEM($1) REPEATABLE($2)"
232        );
233    }
234}