iceberg_rust/materialized_view/
mod.rs

1//! Materialized view functionality for Apache Iceberg
2//!
3//! This module provides the implementation of materialized views in Apache Iceberg.
4//! Materialized views maintain a physical copy of query results that can be refreshed
5//! to stay in sync with source tables.
6//!
7//! Key components:
8//! - [`MaterializedView`]: The main type representing a materialized view
9//! - [`Transaction`]: Provides ACID guarantees for view operations
10//! - [`StorageTable`]: Manages the physical storage of materialized data
11//!
12//! # Example
13//! ```no_run
14//! # use iceberg_rust::materialized_view::MaterializedView;
15//! # async fn example() {
16//! let mut view = MaterializedView::builder()
17//!     .name("event_counts")
18//!     .build()
19//!     .await
20//!     .unwrap();
21//!
22//! // Perform operations in a transaction
23//! view.new_transaction(None)
24//!     .full_refresh(vec![], refresh_state)
25//!     .unwrap()
26//!     .commit()
27//!     .await
28//!     .unwrap();
29//! # }
30//! ```
31
32use std::sync::Arc;
33
34use iceberg_rust_spec::spec::{
35    materialized_view_metadata::MaterializedViewMetadata, schema::Schema,
36};
37
38use crate::{
39    catalog::{
40        create::CreateMaterializedViewBuilder, identifier::Identifier, tabular::Tabular, Catalog,
41    },
42    error::Error,
43};
44
45use self::{storage_table::StorageTable, transaction::Transaction as MaterializedViewTransaction};
46
47mod storage_table;
48pub mod transaction;
49
50/// Default postfix for the storage table identifier
51pub static STORAGE_TABLE_POSTFIX: &str = "__storage";
52/// Flag to mark a table as a storage table
53pub static STORAGE_TABLE_FLAG: &str = "materialize.storage_table";
54
55#[derive(Debug, Clone)]
56/// A materialized view in Apache Iceberg that maintains a physical copy of query results
57/// in a storage table. The view provides ACID guarantees and can be refreshed to
58/// stay in sync with changes in the source tables.
59///
60/// The materialized view consists of:
61/// * A view definition (SQL or other representation)
62/// * A storage table containing the materialized data
63/// * Metadata tracking the freshness state relative to source tables
64pub struct MaterializedView {
65    /// Type of the View, either filesystem or metastore.
66    identifier: Identifier,
67    /// Metadata for the iceberg view according to the iceberg view spec
68    metadata: MaterializedViewMetadata,
69    /// Catalog of the table
70    catalog: Arc<dyn Catalog>,
71}
72
73/// Storage table states
74#[derive(Debug)]
75pub enum SourceTableState {
76    /// Data in storage table is fresh
77    Fresh,
78    /// Data in storage table is outdated
79    Outdated(i64),
80    /// Data in storage table is invalid
81    Invalid,
82}
83
84/// Public interface of the table.
85impl MaterializedView {
86    /// Creates a new builder for configuring and creating a materialized view
87    ///
88    /// Returns a `CreateMaterializedViewBuilder` that provides a fluent interface for:
89    /// - Setting the view name and location
90    /// - Configuring view properties
91    /// - Defining the view schema and SQL representation
92    /// - Specifying the catalog and storage table settings
93    pub fn builder() -> CreateMaterializedViewBuilder {
94        CreateMaterializedViewBuilder::default()
95    }
96
97    /// Creates a new materialized view instance with the given identifier, catalog and metadata
98    ///
99    /// # Arguments
100    /// * `identifier` - The unique identifier for this view in the catalog
101    /// * `catalog` - The catalog that will store this view's metadata
102    /// * `metadata` - The view metadata containing schema, properties, etc.
103    ///
104    /// # Returns
105    /// * `Result<MaterializedView, Error>` - The created materialized view or an error
106    pub async fn new(
107        identifier: Identifier,
108        catalog: Arc<dyn Catalog>,
109        metadata: MaterializedViewMetadata,
110    ) -> Result<Self, Error> {
111        Ok(MaterializedView {
112            identifier,
113            metadata,
114            catalog,
115        })
116    }
117    /// Returns the unique identifier for this materialized view in the catalog
118    ///
119    /// The identifier contains the namespace and name that uniquely identify
120    /// this view within its catalog
121    pub fn identifier(&self) -> &Identifier {
122        &self.identifier
123    }
124    /// Returns a reference to the catalog that stores this materialized view
125    ///
126    /// The catalog manages the view's metadata and provides ACID guarantees
127    /// for operations on the view
128    pub fn catalog(&self) -> Arc<dyn Catalog> {
129        self.catalog.clone()
130    }
131    /// Returns the current schema for this materialized view
132    ///
133    /// # Arguments
134    /// * `branch` - Optional branch name to get the schema for. If None, returns the main branch schema
135    ///
136    /// # Returns
137    /// * `Result<&Schema, Error>` - The current schema or an error if it cannot be found
138    pub fn current_schema(&self, branch: Option<&str>) -> Result<&Schema, Error> {
139        self.metadata.current_schema(branch).map_err(Error::from)
140    }
141    /// Returns a reference to this materialized view's metadata
142    ///
143    /// The metadata contains the view's schema, properties, version history,
144    /// and other configuration details as defined by the Apache Iceberg spec
145    pub fn metadata(&self) -> &MaterializedViewMetadata {
146        &self.metadata
147    }
148    /// Creates a new transaction for performing atomic operations on this materialized view
149    ///
150    /// # Arguments
151    /// * `branch` - Optional branch name to perform the transaction on. If None, uses the main branch
152    ///
153    /// # Returns
154    /// A new transaction that can be used to perform multiple operations atomically
155    pub fn new_transaction(&mut self, branch: Option<&str>) -> MaterializedViewTransaction<'_> {
156        MaterializedViewTransaction::new(self, branch)
157    }
158    /// Returns the storage table that contains the materialized data for this view
159    ///
160    /// The storage table is a regular Iceberg table that stores the physical data
161    /// for this materialized view. It is managed internally by the view and should
162    /// not be modified directly.
163    ///
164    /// # Returns
165    /// * `Result<StorageTable, Error>` - The storage table or an error if it cannot be loaded
166    pub async fn storage_table(&self) -> Result<StorageTable, Error> {
167        let identifier = self.metadata().current_version(None)?.storage_table();
168        if let Tabular::Table(table) = self.catalog().load_tabular(&identifier.into()).await? {
169            Ok(StorageTable::new(table))
170        } else {
171            Err(Error::InvalidFormat("storage table".to_string()))
172        }
173    }
174}