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 {
Self {
sql: SQL::from_iter([Token::REFRESH, Token::MATERIALIZED, Token::VIEW])
.append(qualified_view_name(view)),
_state: PhantomData,
}
}
#[must_use]
pub fn concurrently(self) -> RefreshMaterializedView<'a, RefreshConcurrently> {
let mut sql = SQL::from_iter([
Token::REFRESH,
Token::MATERIALIZED,
Token::VIEW,
Token::CONCURRENTLY,
]);
for chunk in self.sql.chunks.into_iter().skip(3) {
sql = sql.push(chunk);
}
RefreshMaterializedView {
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,
}
}
}
fn qualified_view_name<'a, V: SQLViewInfo>(view: &V) -> SQL<'a, PostgresValue<'a>> {
let name = SQL::ident(view.name());
match SQLTableInfo::schema(view) {
Some(schema) if schema != "public" => SQL::ident(schema).push(Token::DOT).append(name),
_ => name,
}
}
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 "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 "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 "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 "user_stats" WITH DATA"#
);
}
struct ExplicitSchemaView;
impl drizzle_core::traits::SQLTableInfo for ExplicitSchemaView {
fn name(&self) -> &'static str {
"user_stats"
}
fn schema(&self) -> Option<&'static str> {
Some("analytics")
}
}
impl SQLViewInfo for ExplicitSchemaView {
fn definition_sql(&self) -> std::borrow::Cow<'static, str> {
"SELECT * FROM users".into()
}
fn is_materialized(&self) -> bool {
true
}
}
#[test]
fn test_non_public_schema_is_qualified() {
let view = ExplicitSchemaView;
assert_eq!(
RefreshMaterializedView::new(&view).to_sql().sql(),
r#"REFRESH MATERIALIZED VIEW "analytics"."user_stats""#
);
assert_eq!(
RefreshMaterializedView::new(&view)
.concurrently()
.to_sql()
.sql(),
r#"REFRESH MATERIALIZED VIEW CONCURRENTLY "analytics"."user_stats""#
);
}
#[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 "user_stats""#);
}
}