Skip to main content

reinhardt_query/query/materialized_view/
refresh_materialized_view.rs

1//! REFRESH MATERIALIZED VIEW statement builder
2//!
3//! This module provides the `RefreshMaterializedViewStatement` type for building
4//! SQL REFRESH MATERIALIZED VIEW queries.
5
6use crate::backend::QueryBuilder;
7use crate::types::{DynIden, IntoIden};
8
9use crate::query::traits::{QueryBuilderTrait, QueryStatementBuilder, QueryStatementWriter};
10
11/// REFRESH MATERIALIZED VIEW statement builder
12///
13/// This struct provides a fluent API for constructing REFRESH MATERIALIZED VIEW queries.
14///
15/// # Examples
16///
17/// ```rust,ignore
18/// use reinhardt_query::prelude::*;
19///
20/// // Basic refresh
21/// let query = Query::refresh_materialized_view()
22///     .name("my_mv");
23///
24/// // Refresh concurrently
25/// let query = Query::refresh_materialized_view()
26///     .name("my_mv")
27///     .concurrently();
28///
29/// // Refresh with data
30/// let query = Query::refresh_materialized_view()
31///     .name("my_mv")
32///     .with_data(true);
33/// ```
34#[derive(Debug, Clone)]
35pub struct RefreshMaterializedViewStatement {
36	pub(crate) name: Option<DynIden>,
37	pub(crate) concurrently: bool,
38	pub(crate) with_data: Option<bool>,
39}
40
41impl RefreshMaterializedViewStatement {
42	/// Create a new REFRESH MATERIALIZED VIEW statement
43	pub fn new() -> Self {
44		Self {
45			name: None,
46			concurrently: false,
47			with_data: None,
48		}
49	}
50
51	/// Take the ownership of data in the current statement
52	pub fn take(&mut self) -> Self {
53		Self {
54			name: self.name.take(),
55			concurrently: self.concurrently,
56			with_data: self.with_data,
57		}
58	}
59
60	/// Set the materialized view name
61	///
62	/// # Examples
63	///
64	/// ```rust,ignore
65	/// use reinhardt_query::prelude::*;
66	///
67	/// let query = Query::refresh_materialized_view()
68	///     .name("my_mv");
69	/// ```
70	pub fn name<N>(&mut self, name: N) -> &mut Self
71	where
72		N: IntoIden,
73	{
74		self.name = Some(name.into_iden());
75		self
76	}
77
78	/// Add CONCURRENTLY clause
79	///
80	/// Allows the materialized view to be refreshed without blocking concurrent selects.
81	/// Requires a unique index on the materialized view.
82	///
83	/// # Examples
84	///
85	/// ```rust,ignore
86	/// use reinhardt_query::prelude::*;
87	///
88	/// let query = Query::refresh_materialized_view()
89	///     .name("my_mv")
90	///     .concurrently();
91	/// ```
92	pub fn concurrently(&mut self) -> &mut Self {
93		self.concurrently = true;
94		self
95	}
96
97	/// Set WITH DATA or WITH NO DATA clause
98	///
99	/// # Examples
100	///
101	/// ```rust,ignore
102	/// use reinhardt_query::prelude::*;
103	///
104	/// // WITH DATA (default behavior)
105	/// let query = Query::refresh_materialized_view()
106	///     .name("my_mv")
107	///     .with_data(true);
108	///
109	/// // WITH NO DATA (clears the materialized view)
110	/// let query = Query::refresh_materialized_view()
111	///     .name("my_mv")
112	///     .with_data(false);
113	/// ```
114	pub fn with_data(&mut self, with_data: bool) -> &mut Self {
115		self.with_data = Some(with_data);
116		self
117	}
118}
119
120impl Default for RefreshMaterializedViewStatement {
121	fn default() -> Self {
122		Self::new()
123	}
124}
125
126impl QueryStatementBuilder for RefreshMaterializedViewStatement {
127	fn build_any(&self, query_builder: &dyn QueryBuilderTrait) -> (String, crate::value::Values) {
128		// Downcast to concrete QueryBuilder type
129		use std::any::Any;
130		if let Some(builder) =
131			(query_builder as &dyn Any).downcast_ref::<crate::backend::PostgresQueryBuilder>()
132		{
133			return builder.build_refresh_materialized_view(self);
134		}
135		if let Some(builder) =
136			(query_builder as &dyn Any).downcast_ref::<crate::backend::CockroachDBQueryBuilder>()
137		{
138			return builder.build_refresh_materialized_view(self);
139		}
140		if let Some(_builder) =
141			(query_builder as &dyn Any).downcast_ref::<crate::backend::MySqlQueryBuilder>()
142		{
143			panic!("MySQL does not support materialized views");
144		}
145		if let Some(_builder) =
146			(query_builder as &dyn Any).downcast_ref::<crate::backend::SqliteQueryBuilder>()
147		{
148			panic!("SQLite does not support materialized views");
149		}
150		panic!("Unsupported query builder type");
151	}
152}
153
154impl QueryStatementWriter for RefreshMaterializedViewStatement {}
155
156#[cfg(test)]
157mod tests {
158	use super::*;
159	use rstest::*;
160
161	#[rstest]
162	fn test_refresh_materialized_view_basic() {
163		let mut stmt = RefreshMaterializedViewStatement::new();
164		stmt.name("my_mv");
165		assert_eq!(stmt.name.as_ref().unwrap().to_string(), "my_mv");
166		assert!(!stmt.concurrently);
167		assert!(stmt.with_data.is_none());
168	}
169
170	#[rstest]
171	fn test_refresh_materialized_view_concurrently() {
172		let mut stmt = RefreshMaterializedViewStatement::new();
173		stmt.name("my_mv").concurrently();
174		assert!(stmt.concurrently);
175	}
176
177	#[rstest]
178	fn test_refresh_materialized_view_with_data() {
179		let mut stmt = RefreshMaterializedViewStatement::new();
180		stmt.name("my_mv").with_data(true);
181		assert_eq!(stmt.with_data, Some(true));
182	}
183
184	#[rstest]
185	fn test_refresh_materialized_view_with_no_data() {
186		let mut stmt = RefreshMaterializedViewStatement::new();
187		stmt.name("my_mv").with_data(false);
188		assert_eq!(stmt.with_data, Some(false));
189	}
190
191	#[rstest]
192	fn test_refresh_materialized_view_all_options() {
193		let mut stmt = RefreshMaterializedViewStatement::new();
194		stmt.name("my_mv").concurrently().with_data(true);
195		assert_eq!(stmt.name.as_ref().unwrap().to_string(), "my_mv");
196		assert!(stmt.concurrently);
197		assert_eq!(stmt.with_data, Some(true));
198	}
199
200	#[rstest]
201	fn test_refresh_materialized_view_default() {
202		let stmt = RefreshMaterializedViewStatement::default();
203		assert!(stmt.name.is_none());
204		assert!(!stmt.concurrently);
205		assert!(stmt.with_data.is_none());
206	}
207
208	#[rstest]
209	fn test_refresh_materialized_view_take() {
210		let mut stmt = RefreshMaterializedViewStatement::new();
211		stmt.name("my_mv").concurrently();
212		let taken = stmt.take();
213		assert_eq!(taken.name.as_ref().unwrap().to_string(), "my_mv");
214		assert!(taken.concurrently);
215		assert!(stmt.name.is_none());
216	}
217}