drizzle_postgres/builder/
refresh.rs1use crate::values::PostgresValue;
23use core::marker::PhantomData;
24use drizzle_core::traits::{SQLTableInfo, SQLViewInfo};
25use drizzle_core::{SQL, ToSQL, Token};
26
27#[derive(Debug, Clone, Copy, Default)]
33pub struct RefreshInitial;
34
35#[derive(Debug, Clone, Copy, Default)]
37pub struct RefreshConcurrently;
38
39#[derive(Debug, Clone, Copy, Default)]
41pub struct RefreshWithNoData;
42
43#[derive(Debug, Clone)]
57pub struct RefreshMaterializedView<'a, State = RefreshInitial> {
58 sql: SQL<'a, PostgresValue<'a>>,
59 _state: PhantomData<State>,
60}
61
62impl<'a> RefreshMaterializedView<'a, RefreshInitial> {
63 #[must_use]
65 pub fn new<V: SQLViewInfo>(view: &'a V) -> Self {
66 Self {
67 sql: SQL::from_iter([Token::REFRESH, Token::MATERIALIZED, Token::VIEW])
68 .append(qualified_view_name(view)),
69 _state: PhantomData,
70 }
71 }
72
73 #[must_use]
80 pub fn concurrently(self) -> RefreshMaterializedView<'a, RefreshConcurrently> {
81 let mut sql = SQL::from_iter([
84 Token::REFRESH,
85 Token::MATERIALIZED,
86 Token::VIEW,
87 Token::CONCURRENTLY,
88 ]);
89 for chunk in self.sql.chunks.into_iter().skip(3) {
90 sql = sql.push(chunk);
91 }
92
93 RefreshMaterializedView {
94 sql,
95 _state: PhantomData,
96 }
97 }
98
99 #[must_use]
106 pub fn with_no_data(self) -> RefreshMaterializedView<'a, RefreshWithNoData> {
107 RefreshMaterializedView {
108 sql: self.sql.push(Token::WITH).push(Token::NO).push(Token::DATA),
109 _state: PhantomData,
110 }
111 }
112
113 #[must_use]
115 pub fn with_data(self) -> Self {
116 Self {
117 sql: self.sql.push(Token::WITH).push(Token::DATA),
118 _state: PhantomData,
119 }
120 }
121}
122
123fn qualified_view_name<'a, V: SQLViewInfo>(view: &V) -> SQL<'a, PostgresValue<'a>> {
127 let name = SQL::ident(view.name());
128 match SQLTableInfo::schema(view) {
129 Some(schema) if schema != "public" => SQL::ident(schema).push(Token::DOT).append(name),
130 _ => name,
131 }
132}
133
134impl<'a, State> ToSQL<'a, PostgresValue<'a>> for RefreshMaterializedView<'a, State> {
139 fn to_sql(&self) -> SQL<'a, PostgresValue<'a>> {
140 self.sql.clone()
141 }
142}
143
144pub fn refresh_materialized_view<V: SQLViewInfo>(
150 view: &V,
151) -> RefreshMaterializedView<'_, RefreshInitial> {
152 RefreshMaterializedView::new(view)
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 struct TestView;
161
162 impl drizzle_core::traits::SQLTableInfo for TestView {
163 fn name(&self) -> &'static str {
164 "user_stats"
165 }
166
167 fn schema(&self) -> Option<&'static str> {
168 Some("public")
169 }
170 }
171
172 impl SQLViewInfo for TestView {
173 fn definition_sql(&self) -> std::borrow::Cow<'static, str> {
174 "SELECT * FROM users".into()
175 }
176
177 fn is_materialized(&self) -> bool {
178 true
179 }
180 }
181
182 #[test]
183 fn test_basic_refresh() {
184 let view = TestView;
185 let refresh = RefreshMaterializedView::new(&view);
186 let sql = refresh.to_sql();
187
188 assert_eq!(sql.sql(), r#"REFRESH MATERIALIZED VIEW "user_stats""#);
189 }
190
191 #[test]
192 fn test_concurrent_refresh() {
193 let view = TestView;
194 let refresh = RefreshMaterializedView::new(&view).concurrently();
195 let sql = refresh.to_sql();
196
197 assert_eq!(
198 sql.sql(),
199 r#"REFRESH MATERIALIZED VIEW CONCURRENTLY "user_stats""#
200 );
201 }
202
203 #[test]
204 fn test_refresh_with_no_data() {
205 let view = TestView;
206 let refresh = RefreshMaterializedView::new(&view).with_no_data();
207 let sql = refresh.to_sql();
208
209 assert_eq!(
210 sql.sql(),
211 r#"REFRESH MATERIALIZED VIEW "user_stats" WITH NO DATA"#
212 );
213 }
214
215 #[test]
216 fn test_refresh_with_data() {
217 let view = TestView;
218 let refresh = RefreshMaterializedView::new(&view).with_data();
219 let sql = refresh.to_sql();
220
221 assert_eq!(
222 sql.sql(),
223 r#"REFRESH MATERIALIZED VIEW "user_stats" WITH DATA"#
224 );
225 }
226
227 struct ExplicitSchemaView;
228
229 impl drizzle_core::traits::SQLTableInfo for ExplicitSchemaView {
230 fn name(&self) -> &'static str {
231 "user_stats"
232 }
233
234 fn schema(&self) -> Option<&'static str> {
235 Some("analytics")
236 }
237 }
238
239 impl SQLViewInfo for ExplicitSchemaView {
240 fn definition_sql(&self) -> std::borrow::Cow<'static, str> {
241 "SELECT * FROM users".into()
242 }
243
244 fn is_materialized(&self) -> bool {
245 true
246 }
247 }
248
249 #[test]
250 fn test_non_public_schema_is_qualified() {
251 let view = ExplicitSchemaView;
252 assert_eq!(
253 RefreshMaterializedView::new(&view).to_sql().sql(),
254 r#"REFRESH MATERIALIZED VIEW "analytics"."user_stats""#
255 );
256 assert_eq!(
257 RefreshMaterializedView::new(&view)
258 .concurrently()
259 .to_sql()
260 .sql(),
261 r#"REFRESH MATERIALIZED VIEW CONCURRENTLY "analytics"."user_stats""#
262 );
263 }
264
265 #[test]
266 fn test_helper_function() {
267 let view = TestView;
268 let refresh = refresh_materialized_view(&view);
269 let sql = refresh.to_sql();
270
271 assert_eq!(sql.sql(), r#"REFRESH MATERIALIZED VIEW "user_stats""#);
272 }
273}