use crate::values::PostgresValue;
use core::marker::PhantomData;
use drizzle_core::traits::{SQLTableInfo, SQLViewInfo};
use drizzle_core::{SQL, ToSQL, Token};
#[derive(Debug, Clone, Copy, Default)]
pub struct RefreshInitial;
#[derive(Debug, Clone, Copy, Default)]
pub struct RefreshConcurrently;
#[derive(Debug, Clone, Copy, Default)]
pub struct RefreshWithNoData;
#[derive(Debug, Clone)]
pub struct RefreshMaterializedView<'a, State = RefreshInitial> {
sql: SQL<'a, PostgresValue<'a>>,
_state: PhantomData<State>,
}
impl<'a> RefreshMaterializedView<'a, RefreshInitial> {
#[must_use]
pub fn new<V: SQLViewInfo>(view: &'a V) -> Self {
let schema = SQLTableInfo::schema(view).unwrap_or("public");
let name = view.name();
let sql = SQL::from_iter([Token::REFRESH, Token::MATERIALIZED, Token::VIEW])
.append(SQL::ident(schema))
.push(Token::DOT)
.append(SQL::ident(name));
Self {
sql,
_state: PhantomData,
}
}
#[must_use]
pub fn concurrently(self) -> RefreshMaterializedView<'a, RefreshConcurrently> {
let chunks = self.sql.chunks;
let schema_name_start = 3;
let mut new_sql = SQL::from_iter([
Token::REFRESH,
Token::MATERIALIZED,
Token::VIEW,
Token::CONCURRENTLY,
]);
for chunk in chunks.into_iter().skip(schema_name_start) {
new_sql = new_sql.push(chunk);
}
RefreshMaterializedView {
sql: new_sql,
_state: PhantomData,
}
}
#[must_use]
pub fn with_no_data(self) -> RefreshMaterializedView<'a, RefreshWithNoData> {
RefreshMaterializedView {
sql: self.sql.push(Token::WITH).push(Token::NO).push(Token::DATA),
_state: PhantomData,
}
}
#[must_use]
pub fn with_data(self) -> Self {
Self {
sql: self.sql.push(Token::WITH).push(Token::DATA),
_state: PhantomData,
}
}
}
impl<'a, State> ToSQL<'a, PostgresValue<'a>> for RefreshMaterializedView<'a, State> {
fn to_sql(&self) -> SQL<'a, PostgresValue<'a>> {
self.sql.clone()
}
}
pub fn refresh_materialized_view<V: SQLViewInfo>(
view: &V,
) -> RefreshMaterializedView<'_, RefreshInitial> {
RefreshMaterializedView::new(view)
}
#[cfg(test)]
mod tests {
use super::*;
struct TestView;
impl drizzle_core::traits::SQLTableInfo for TestView {
fn name(&self) -> &'static str {
"user_stats"
}
fn schema(&self) -> Option<&'static str> {
Some("public")
}
}
impl SQLViewInfo for TestView {
fn definition_sql(&self) -> std::borrow::Cow<'static, str> {
"SELECT * FROM users".into()
}
fn is_materialized(&self) -> bool {
true
}
}
#[test]
fn test_basic_refresh() {
let view = TestView;
let refresh = RefreshMaterializedView::new(&view);
let sql = refresh.to_sql();
assert_eq!(
sql.sql(),
r#"REFRESH MATERIALIZED VIEW "public"."user_stats""#
);
}
#[test]
fn test_concurrent_refresh() {
let view = TestView;
let refresh = RefreshMaterializedView::new(&view).concurrently();
let sql = refresh.to_sql();
assert_eq!(
sql.sql(),
r#"REFRESH MATERIALIZED VIEW CONCURRENTLY "public"."user_stats""#
);
}
#[test]
fn test_refresh_with_no_data() {
let view = TestView;
let refresh = RefreshMaterializedView::new(&view).with_no_data();
let sql = refresh.to_sql();
assert_eq!(
sql.sql(),
r#"REFRESH MATERIALIZED VIEW "public"."user_stats" WITH NO DATA"#
);
}
#[test]
fn test_refresh_with_data() {
let view = TestView;
let refresh = RefreshMaterializedView::new(&view).with_data();
let sql = refresh.to_sql();
assert_eq!(
sql.sql(),
r#"REFRESH MATERIALIZED VIEW "public"."user_stats" WITH DATA"#
);
}
#[test]
fn test_helper_function() {
let view = TestView;
let refresh = refresh_materialized_view(&view);
let sql = refresh.to_sql();
assert_eq!(
sql.sql(),
r#"REFRESH MATERIALIZED VIEW "public"."user_stats""#
);
}
}