nastra commented on code in PR #331: URL: https://github.com/apache/iceberg-rust/pull/331#discussion_r1675578086
########## crates/iceberg/src/spec/view_metadata.rs: ########## @@ -0,0 +1,682 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//! Defines the [view metadata](https://iceberg.apache.org/view-spec/#view-metadata). +//! The main struct here is [ViewMetadata] which defines the data for a view. + +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; +use std::cmp::Ordering; +use std::fmt::{Display, Formatter}; +use std::{collections::HashMap, sync::Arc}; +use uuid::Uuid; + +use super::{ + view_version::{ViewVersion, ViewVersionRef}, + SchemaId, SchemaRef, +}; +use crate::catalog::ViewCreation; +use crate::error::Result; + +use _serde::ViewMetadataEnum; + +use chrono::{DateTime, MappedLocalTime, TimeZone, Utc}; + +/// Reference to [`ViewMetadata`]. +pub type ViewMetadataRef = Arc<ViewMetadata>; + +#[derive(Debug, PartialEq, Deserialize, Eq, Clone)] +#[serde(try_from = "ViewMetadataEnum", into = "ViewMetadataEnum")] +/// Fields for the version 1 of the view metadata. +/// +/// We assume that this data structure is always valid, so we will panic when invalid error happens. +/// We check the validity of this data structure when constructing. +pub struct ViewMetadata { + /// Integer Version for the format. + pub(crate) format_version: ViewFormatVersion, + /// A UUID that identifies the view, generated when the view is created. + pub(crate) view_uuid: Uuid, + /// The view's base location; used to create metadata file locations + pub(crate) location: String, + /// ID of the current version of the view (version-id) + pub(crate) current_version_id: i64, + /// A list of known versions of the view + pub(crate) versions: HashMap<i64, ViewVersionRef>, + /// A list of version log entries with the timestamp and version-id for every + /// change to current-version-id + pub(crate) version_log: Vec<ViewVersionLog>, Review Comment: in java we call this `history` ########## crates/iceberg/src/spec/view_version.rs: ########## @@ -0,0 +1,347 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/*! + * View Versions! +*/ +use crate::error::Result; +use chrono::{DateTime, MappedLocalTime, TimeZone, Utc}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::sync::Arc; +use typed_builder::TypedBuilder; + +use super::view_metadata::ViewVersionLog; +use crate::catalog::NamespaceIdent; +use crate::spec::{SchemaId, SchemaRef, ViewMetadata}; +use crate::{Error, ErrorKind}; +use _serde::ViewVersionV1; + +/// Reference to [`ViewVersion`]. +pub type ViewVersionRef = Arc<ViewVersion>; + +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, TypedBuilder)] +#[serde(from = "ViewVersionV1", into = "ViewVersionV1")] +#[builder(field_defaults(setter(prefix = "with_")))] +/// A view versions represents the definition of a view at a specific point in time. +pub struct ViewVersion { + /// A unique long ID + version_id: i64, + /// ID of the schema for the view version + schema_id: SchemaId, + /// Timestamp when the version was created (ms from epoch) + timestamp_ms: i64, + /// A string to string map of summary metadata about the version + summary: HashMap<String, String>, + /// A list of representations for the view definition. + representations: ViewRepresentations, + /// Catalog name to use when a reference in the SELECT does not contain a catalog + #[builder(default = None)] + default_catalog: Option<String>, + /// Namespace to use when a reference in the SELECT is a single identifier + default_namespace: NamespaceIdent, +} + +impl ViewVersion { + /// Get the version id of this view version. + #[inline] + pub fn version_id(&self) -> i64 { + self.version_id + } + + /// Get the schema id of this view version. + #[inline] + pub fn schema_id(&self) -> SchemaId { + self.schema_id + } + + /// Get the timestamp of when the view version was created + #[inline] + pub fn timestamp(&self) -> MappedLocalTime<DateTime<Utc>> { + Utc.timestamp_millis_opt(self.timestamp_ms) + } + + /// Get the timestamp of when the view version was created in milliseconds since epoch + #[inline] + pub fn timestamp_ms(&self) -> i64 { + self.timestamp_ms + } + + /// Get summary of the view version + #[inline] + pub fn summary(&self) -> &HashMap<String, String> { + &self.summary + } + + /// Get this views representations + #[inline] + pub fn representations(&self) -> &ViewRepresentations { + &self.representations + } + + /// Get the default catalog for this view version + #[inline] + pub fn default_catalog(&self) -> Option<&String> { + self.default_catalog.as_ref() + } + + /// Get the default namespace to use when a reference in the SELECT is a single identifier + #[inline] + pub fn default_namespace(&self) -> &NamespaceIdent { + &self.default_namespace + } + + /// Get the schema of this snapshot. + pub fn schema(&self, view_metadata: &ViewMetadata) -> Result<SchemaRef> { + let r = view_metadata + .schema_by_id(self.schema_id()) + .ok_or_else(|| { + Error::new( + ErrorKind::DataInvalid, + format!("Schema with id {} not found", self.schema_id()), + ) + }) + .cloned(); + r + } + + pub(crate) fn log(&self) -> ViewVersionLog { + ViewVersionLog::new(self.version_id, self.timestamp_ms) + } +} + +/// A list of view representations. +pub type ViewRepresentations = Vec<ViewRepresentation>; Review Comment: is it normal in rust to create a separate type for a list of X? ########## crates/iceberg/src/spec/view_version.rs: ########## @@ -0,0 +1,347 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/*! + * View Versions! +*/ +use crate::error::Result; +use chrono::{DateTime, MappedLocalTime, TimeZone, Utc}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::sync::Arc; +use typed_builder::TypedBuilder; + +use super::view_metadata::ViewVersionLog; +use crate::catalog::NamespaceIdent; +use crate::spec::{SchemaId, SchemaRef, ViewMetadata}; +use crate::{Error, ErrorKind}; +use _serde::ViewVersionV1; + +/// Reference to [`ViewVersion`]. +pub type ViewVersionRef = Arc<ViewVersion>; + +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, TypedBuilder)] +#[serde(from = "ViewVersionV1", into = "ViewVersionV1")] +#[builder(field_defaults(setter(prefix = "with_")))] +/// A view versions represents the definition of a view at a specific point in time. +pub struct ViewVersion { + /// A unique long ID + version_id: i64, + /// ID of the schema for the view version + schema_id: SchemaId, + /// Timestamp when the version was created (ms from epoch) + timestamp_ms: i64, + /// A string to string map of summary metadata about the version + summary: HashMap<String, String>, + /// A list of representations for the view definition. + representations: ViewRepresentations, + /// Catalog name to use when a reference in the SELECT does not contain a catalog + #[builder(default = None)] + default_catalog: Option<String>, + /// Namespace to use when a reference in the SELECT is a single identifier + default_namespace: NamespaceIdent, +} + +impl ViewVersion { + /// Get the version id of this view version. + #[inline] + pub fn version_id(&self) -> i64 { + self.version_id + } + + /// Get the schema id of this view version. + #[inline] + pub fn schema_id(&self) -> SchemaId { + self.schema_id + } + + /// Get the timestamp of when the view version was created + #[inline] + pub fn timestamp(&self) -> MappedLocalTime<DateTime<Utc>> { + Utc.timestamp_millis_opt(self.timestamp_ms) + } + + /// Get the timestamp of when the view version was created in milliseconds since epoch + #[inline] + pub fn timestamp_ms(&self) -> i64 { + self.timestamp_ms + } + + /// Get summary of the view version + #[inline] + pub fn summary(&self) -> &HashMap<String, String> { + &self.summary + } + + /// Get this views representations + #[inline] + pub fn representations(&self) -> &ViewRepresentations { + &self.representations + } + + /// Get the default catalog for this view version + #[inline] + pub fn default_catalog(&self) -> Option<&String> { + self.default_catalog.as_ref() + } + + /// Get the default namespace to use when a reference in the SELECT is a single identifier + #[inline] + pub fn default_namespace(&self) -> &NamespaceIdent { + &self.default_namespace + } + + /// Get the schema of this snapshot. + pub fn schema(&self, view_metadata: &ViewMetadata) -> Result<SchemaRef> { + let r = view_metadata + .schema_by_id(self.schema_id()) + .ok_or_else(|| { + Error::new( + ErrorKind::DataInvalid, + format!("Schema with id {} not found", self.schema_id()), + ) + }) + .cloned(); + r + } + + pub(crate) fn log(&self) -> ViewVersionLog { + ViewVersionLog::new(self.version_id, self.timestamp_ms) + } +} + +/// A list of view representations. +pub type ViewRepresentations = Vec<ViewRepresentation>; + +/// A builder for [`ViewRepresentations`]. +pub struct ViewRepresentationsBuilder(ViewRepresentations); + +impl ViewRepresentationsBuilder { + /// Create a new builder. + pub fn new() -> Self { + Self(Vec::new()) + } + + /// Add a representation to the list. + /// + /// SQL representations dialects must be unique. If a representation with the same Review Comment: does this make sure to ignore the case of the dialect when comparing two dialects? If no, then it should ########## crates/iceberg/testdata/view_metadata/ViewMetadataV2Valid.json: ########## @@ -0,0 +1,58 @@ +{ Review Comment: > However, there isn't much in https://github.com/apache/iceberg/tree/5ea78e3fbe5d8c9c846a1b86bcd2b77d13a31acd/core/src/test/java/org/apache/iceberg/view we can use for now. In the java code we typically don't construct the full JSON just to test something, but there are plenty of tests in `TestViewMetadata` to make sure schema / view version re-assignment / de-duplication happens or SQL dialect deduplication behaves correctly. > I just want to start with the foundation before heading into the more controversial builder bits. I was merely asked to look at this view implementation since I did the Java implementation, but I'm not involved in iceberg-rust, so I'll let others decide how to proceed with adding views to iceberg-rust and what to include in this PR vs in follow-ups. Regarding follow-ups, I think a few more things would be needed for the view metadata builder, which I mentioned above. `TestViewMetadata´ gives a good overview over how the view metadata / builder should behave. Thanks for working on this btw 💯 ########## crates/iceberg/src/spec/view_metadata.rs: ########## @@ -0,0 +1,682 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//! Defines the [view metadata](https://iceberg.apache.org/view-spec/#view-metadata). +//! The main struct here is [ViewMetadata] which defines the data for a view. + +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; +use std::cmp::Ordering; +use std::fmt::{Display, Formatter}; +use std::{collections::HashMap, sync::Arc}; +use uuid::Uuid; + +use super::{ + view_version::{ViewVersion, ViewVersionRef}, + SchemaId, SchemaRef, +}; +use crate::catalog::ViewCreation; +use crate::error::Result; + +use _serde::ViewMetadataEnum; + +use chrono::{DateTime, MappedLocalTime, TimeZone, Utc}; + +/// Reference to [`ViewMetadata`]. +pub type ViewMetadataRef = Arc<ViewMetadata>; + +#[derive(Debug, PartialEq, Deserialize, Eq, Clone)] +#[serde(try_from = "ViewMetadataEnum", into = "ViewMetadataEnum")] +/// Fields for the version 1 of the view metadata. +/// +/// We assume that this data structure is always valid, so we will panic when invalid error happens. +/// We check the validity of this data structure when constructing. +pub struct ViewMetadata { + /// Integer Version for the format. + pub(crate) format_version: ViewFormatVersion, + /// A UUID that identifies the view, generated when the view is created. + pub(crate) view_uuid: Uuid, + /// The view's base location; used to create metadata file locations + pub(crate) location: String, + /// ID of the current version of the view (version-id) + pub(crate) current_version_id: i64, + /// A list of known versions of the view + pub(crate) versions: HashMap<i64, ViewVersionRef>, + /// A list of version log entries with the timestamp and version-id for every + /// change to current-version-id + pub(crate) version_log: Vec<ViewVersionLog>, + /// A list of schemas, stored as objects with schema-id. + pub(crate) schemas: HashMap<i32, SchemaRef>, + /// A string to string map of view properties. + /// Properties are used for metadata such as comment and for settings that + /// affect view maintenance. This is not intended to be used for arbitrary metadata. + pub(crate) properties: HashMap<String, String>, +} + +impl ViewMetadata { + /// Returns format version of this metadata. + #[inline] + pub fn format_version(&self) -> ViewFormatVersion { + self.format_version + } + + /// Returns uuid of current view. + #[inline] + pub fn uuid(&self) -> Uuid { + self.view_uuid + } + + /// Returns view location. + #[inline] + pub fn location(&self) -> &str { + self.location.as_str() + } + + /// Returns the current version id. + #[inline] + pub fn current_version_id(&self) -> i64 { + self.current_version_id + } + + /// Returns all view versions. + #[inline] + pub fn versions(&self) -> impl Iterator<Item = &ViewVersionRef> { + self.versions.values() + } + + /// Lookup a view version by id. + #[inline] + pub fn version_by_id(&self, version_id: i64) -> Option<&ViewVersionRef> { + self.versions.get(&version_id) + } + + /// Returns the current view version. + #[inline] + pub fn current_version(&self) -> &ViewVersionRef { + self.versions + .get(&self.current_version_id) + .expect("Current version id set, but not found in view versions") + } + + /// Returns schemas + #[inline] + pub fn schemas_iter(&self) -> impl Iterator<Item = &SchemaRef> { + self.schemas.values() + } + + /// Lookup schema by id. + #[inline] + pub fn schema_by_id(&self, schema_id: SchemaId) -> Option<&SchemaRef> { + self.schemas.get(&schema_id) + } + + /// Get current schema + #[inline] + pub fn current_schema(&self) -> &SchemaRef { + let schema_id = self.current_version().schema_id(); + self.schema_by_id(schema_id) + .expect("Current schema id set, but not found in view metadata") + } + + /// Returns properties of the view. + #[inline] + pub fn properties(&self) -> &HashMap<String, String> { + &self.properties + } + + /// Append view version to view + pub fn append_version(&mut self, view_version: ViewVersion) { + self.current_version_id = view_version.version_id(); + + self.version_log.push(view_version.log()); + self.versions + .insert(view_version.version_id(), Arc::new(view_version)); + } + + /// Returns view history. + #[inline] + pub fn history(&self) -> &[ViewVersionLog] { + &self.version_log + } +} + +/// Manipulating view metadata. +pub struct ViewMetadataBuilder(ViewMetadata); + +impl ViewMetadataBuilder { + /// Creates a new view metadata builder from the given view metadata. + pub fn new(origin: ViewMetadata) -> Self { + Self(origin) + } + + /// Creates a new view metadata builder from the given view creation. + pub fn from_view_creation(view_creation: ViewCreation) -> Result<Self> { + let ViewCreation { + location, + schema, + properties, + name: _, + representations, + default_catalog, + default_namespace, + summary, + } = view_creation; + let initial_version_id = super::INITIAL_SEQUENCE_NUMBER; + let version = ViewVersion::builder() + .with_default_catalog(default_catalog) + .with_default_namespace(default_namespace) + .with_representations(representations.into_iter().collect::<Vec<_>>()) + .with_schema_id(schema.schema_id()) + .with_summary(summary) + .with_timestamp_ms(Utc::now().timestamp_millis()) + .with_version_id(initial_version_id) + .build(); + + let versions = HashMap::from_iter(vec![(initial_version_id, version.into())]); + + let view_metadata = ViewMetadata { + format_version: ViewFormatVersion::V1, + view_uuid: Uuid::now_v7(), + location, + current_version_id: initial_version_id, + versions, + version_log: Vec::new(), + schemas: HashMap::from_iter(vec![(schema.schema_id(), Arc::new(schema))]), + properties, + }; + + Ok(Self(view_metadata)) + } + + /// Changes uuid of view metadata. + pub fn assign_uuid(mut self, uuid: Uuid) -> Result<Self> { + self.0.view_uuid = uuid; + Ok(self) + } + + /// Returns the new view metadata after changes. + pub fn build(self) -> Result<ViewMetadata> { + Ok(self.0) + } +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] +#[serde(rename_all = "kebab-case")] +/// A log of when each snapshot was made. +pub struct ViewVersionLog { Review Comment: in java this is called `ViewHistoryEntry`, maybe worth naming it here similarly? ########## crates/iceberg/src/spec/view_metadata.rs: ########## @@ -0,0 +1,682 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +//! Defines the [view metadata](https://iceberg.apache.org/view-spec/#view-metadata). +//! The main struct here is [ViewMetadata] which defines the data for a view. + +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; +use std::cmp::Ordering; +use std::fmt::{Display, Formatter}; +use std::{collections::HashMap, sync::Arc}; +use uuid::Uuid; + +use super::{ + view_version::{ViewVersion, ViewVersionRef}, + SchemaId, SchemaRef, +}; +use crate::catalog::ViewCreation; +use crate::error::Result; + +use _serde::ViewMetadataEnum; + +use chrono::{DateTime, MappedLocalTime, TimeZone, Utc}; + +/// Reference to [`ViewMetadata`]. +pub type ViewMetadataRef = Arc<ViewMetadata>; + +#[derive(Debug, PartialEq, Deserialize, Eq, Clone)] +#[serde(try_from = "ViewMetadataEnum", into = "ViewMetadataEnum")] +/// Fields for the version 1 of the view metadata. +/// +/// We assume that this data structure is always valid, so we will panic when invalid error happens. +/// We check the validity of this data structure when constructing. +pub struct ViewMetadata { + /// Integer Version for the format. + pub(crate) format_version: ViewFormatVersion, + /// A UUID that identifies the view, generated when the view is created. + pub(crate) view_uuid: Uuid, + /// The view's base location; used to create metadata file locations + pub(crate) location: String, + /// ID of the current version of the view (version-id) + pub(crate) current_version_id: i64, + /// A list of known versions of the view + pub(crate) versions: HashMap<i64, ViewVersionRef>, + /// A list of version log entries with the timestamp and version-id for every + /// change to current-version-id + pub(crate) version_log: Vec<ViewVersionLog>, + /// A list of schemas, stored as objects with schema-id. + pub(crate) schemas: HashMap<i32, SchemaRef>, + /// A string to string map of view properties. + /// Properties are used for metadata such as comment and for settings that + /// affect view maintenance. This is not intended to be used for arbitrary metadata. + pub(crate) properties: HashMap<String, String>, +} + +impl ViewMetadata { + /// Returns format version of this metadata. + #[inline] + pub fn format_version(&self) -> ViewFormatVersion { + self.format_version + } + + /// Returns uuid of current view. + #[inline] + pub fn uuid(&self) -> Uuid { + self.view_uuid + } + + /// Returns view location. + #[inline] + pub fn location(&self) -> &str { + self.location.as_str() + } + + /// Returns the current version id. + #[inline] + pub fn current_version_id(&self) -> i64 { + self.current_version_id + } + + /// Returns all view versions. + #[inline] + pub fn versions(&self) -> impl Iterator<Item = &ViewVersionRef> { + self.versions.values() + } + + /// Lookup a view version by id. + #[inline] + pub fn version_by_id(&self, version_id: i64) -> Option<&ViewVersionRef> { + self.versions.get(&version_id) + } + + /// Returns the current view version. + #[inline] + pub fn current_version(&self) -> &ViewVersionRef { + self.versions + .get(&self.current_version_id) + .expect("Current version id set, but not found in view versions") + } + + /// Returns schemas + #[inline] + pub fn schemas_iter(&self) -> impl Iterator<Item = &SchemaRef> { + self.schemas.values() + } + + /// Lookup schema by id. + #[inline] + pub fn schema_by_id(&self, schema_id: SchemaId) -> Option<&SchemaRef> { + self.schemas.get(&schema_id) + } + + /// Get current schema + #[inline] + pub fn current_schema(&self) -> &SchemaRef { + let schema_id = self.current_version().schema_id(); + self.schema_by_id(schema_id) + .expect("Current schema id set, but not found in view metadata") + } + + /// Returns properties of the view. + #[inline] + pub fn properties(&self) -> &HashMap<String, String> { + &self.properties + } + + /// Append view version to view + pub fn append_version(&mut self, view_version: ViewVersion) { + self.current_version_id = view_version.version_id(); Review Comment: I feel that things like view version id re-assignment and view version de-duplication are missing in the builder. The same applies for schema id re-assignment / schema de-duplication. There are a bunch of tests in the java ref impl in `TestViewMetadata` that make sure these things behave correctly ########## crates/iceberg/src/spec/view_version.rs: ########## @@ -0,0 +1,347 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +/*! + * View Versions! +*/ +use crate::error::Result; +use chrono::{DateTime, MappedLocalTime, TimeZone, Utc}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::sync::Arc; +use typed_builder::TypedBuilder; + +use super::view_metadata::ViewVersionLog; +use crate::catalog::NamespaceIdent; +use crate::spec::{SchemaId, SchemaRef, ViewMetadata}; +use crate::{Error, ErrorKind}; +use _serde::ViewVersionV1; + +/// Reference to [`ViewVersion`]. +pub type ViewVersionRef = Arc<ViewVersion>; + +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, TypedBuilder)] +#[serde(from = "ViewVersionV1", into = "ViewVersionV1")] +#[builder(field_defaults(setter(prefix = "with_")))] +/// A view versions represents the definition of a view at a specific point in time. +pub struct ViewVersion { + /// A unique long ID + version_id: i64, + /// ID of the schema for the view version + schema_id: SchemaId, + /// Timestamp when the version was created (ms from epoch) + timestamp_ms: i64, + /// A string to string map of summary metadata about the version + summary: HashMap<String, String>, + /// A list of representations for the view definition. + representations: ViewRepresentations, + /// Catalog name to use when a reference in the SELECT does not contain a catalog + #[builder(default = None)] + default_catalog: Option<String>, + /// Namespace to use when a reference in the SELECT is a single identifier + default_namespace: NamespaceIdent, +} + +impl ViewVersion { + /// Get the version id of this view version. + #[inline] + pub fn version_id(&self) -> i64 { + self.version_id + } + + /// Get the schema id of this view version. + #[inline] + pub fn schema_id(&self) -> SchemaId { + self.schema_id + } + + /// Get the timestamp of when the view version was created + #[inline] + pub fn timestamp(&self) -> MappedLocalTime<DateTime<Utc>> { + Utc.timestamp_millis_opt(self.timestamp_ms) + } + + /// Get the timestamp of when the view version was created in milliseconds since epoch + #[inline] + pub fn timestamp_ms(&self) -> i64 { + self.timestamp_ms + } + + /// Get summary of the view version + #[inline] + pub fn summary(&self) -> &HashMap<String, String> { + &self.summary + } + + /// Get this views representations + #[inline] + pub fn representations(&self) -> &ViewRepresentations { + &self.representations + } + + /// Get the default catalog for this view version + #[inline] + pub fn default_catalog(&self) -> Option<&String> { + self.default_catalog.as_ref() + } + + /// Get the default namespace to use when a reference in the SELECT is a single identifier + #[inline] + pub fn default_namespace(&self) -> &NamespaceIdent { + &self.default_namespace + } + + /// Get the schema of this snapshot. + pub fn schema(&self, view_metadata: &ViewMetadata) -> Result<SchemaRef> { + let r = view_metadata + .schema_by_id(self.schema_id()) + .ok_or_else(|| { + Error::new( + ErrorKind::DataInvalid, + format!("Schema with id {} not found", self.schema_id()), + ) + }) + .cloned(); + r + } + + pub(crate) fn log(&self) -> ViewVersionLog { + ViewVersionLog::new(self.version_id, self.timestamp_ms) + } +} + +/// A list of view representations. +pub type ViewRepresentations = Vec<ViewRepresentation>; + +/// A builder for [`ViewRepresentations`]. +pub struct ViewRepresentationsBuilder(ViewRepresentations); + +impl ViewRepresentationsBuilder { + /// Create a new builder. + pub fn new() -> Self { + Self(Vec::new()) + } + + /// Add a representation to the list. + /// + /// SQL representations dialects must be unique. If a representation with the same + /// dialect already exists, it will be overwritten. + pub fn add_or_overwrite_representation(mut self, representation: ViewRepresentation) -> Self { + let dialect = match &representation { + ViewRepresentation::SqlViewRepresentation(sql) => &sql.dialect, + }; + self.0.retain(|r| { + let ViewRepresentation::SqlViewRepresentation(sql) = r; + sql.dialect != *dialect + }); + self.0.push(representation); + self + } + + /// Add a SQL representation to the list. + /// + /// SQL representations dialects must be unique. If a representation with the same + /// dialect already exists, it will be overwritten. Review Comment: why is it better to overwrite a dialect rather than throw an error saying that there are duplicate dialects? -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: issues-unsubscr...@iceberg.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: issues-unsubscr...@iceberg.apache.org For additional commands, e-mail: issues-h...@iceberg.apache.org