From a5ce69920328cce899260249d319ff7726ae3911 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Wed, 24 Jan 2024 10:50:11 +0100 Subject: [PATCH] feat: Add materialized view to the SDK (#2403) Add materialized view to the SDK Differences with the docs (I will add them on doc-discuss): - set secure / comment cannot be used simultaneously - the same with unset - there are missing options compared to views, I did not check them yet, will ask about them after checking (row access policy, masking column policy, tags in alter; terse, start with, limit in show) - comment on column possible (it is in regular view doc, but it is not added in materialized view one; there is a test proving it works: https://github.com/Snowflake-Labs/terraform-provider-snowflake/pull/2403/files#diff-7a8a3cc00d490b622d152a5bdf42e0915fff497c2eb4554f491dfaaaa3d51025R124)? --- pkg/sdk/client.go | 2 + pkg/sdk/materialized_views_def.go | 189 ++++++++++ .../materialized_views_dto_builders_gen.go | 241 ++++++++++++ pkg/sdk/materialized_views_dto_gen.go | 92 +++++ pkg/sdk/materialized_views_gen.go | 192 ++++++++++ pkg/sdk/materialized_views_gen_test.go | 315 ++++++++++++++++ pkg/sdk/materialized_views_impl_gen.go | 225 +++++++++++ pkg/sdk/materialized_views_validations_gen.go | 95 +++++ pkg/sdk/poc/generator/db_struct.go | 8 + pkg/sdk/poc/generator/plain_struct.go | 8 + pkg/sdk/poc/main.go | 1 + ...materialized_views_gen_integration_test.go | 357 ++++++++++++++++++ pkg/sdk/testint/views_gen_integration_test.go | 4 +- 13 files changed, 1727 insertions(+), 2 deletions(-) create mode 100644 pkg/sdk/materialized_views_def.go create mode 100644 pkg/sdk/materialized_views_dto_builders_gen.go create mode 100644 pkg/sdk/materialized_views_dto_gen.go create mode 100644 pkg/sdk/materialized_views_gen.go create mode 100644 pkg/sdk/materialized_views_gen_test.go create mode 100644 pkg/sdk/materialized_views_impl_gen.go create mode 100644 pkg/sdk/materialized_views_validations_gen.go create mode 100644 pkg/sdk/testint/materialized_views_gen_integration_test.go diff --git a/pkg/sdk/client.go b/pkg/sdk/client.go index d9a510b465..3934cfb776 100644 --- a/pkg/sdk/client.go +++ b/pkg/sdk/client.go @@ -55,6 +55,7 @@ type Client struct { Grants Grants ManagedAccounts ManagedAccounts MaskingPolicies MaskingPolicies + MaterializedViews MaterializedViews NetworkPolicies NetworkPolicies Parameters Parameters PasswordPolicies PasswordPolicies @@ -202,6 +203,7 @@ func (c *Client) initialize() { c.Grants = &grants{client: c} c.ManagedAccounts = &managedAccounts{client: c} c.MaskingPolicies = &maskingPolicies{client: c} + c.MaterializedViews = &materializedViews{client: c} c.NetworkPolicies = &networkPolicies{client: c} c.Parameters = ¶meters{client: c} c.PasswordPolicies = &passwordPolicies{client: c} diff --git a/pkg/sdk/materialized_views_def.go b/pkg/sdk/materialized_views_def.go new file mode 100644 index 0000000000..fedef1d829 --- /dev/null +++ b/pkg/sdk/materialized_views_def.go @@ -0,0 +1,189 @@ +package sdk + +import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator" + +//go:generate go run ./poc/main.go + +var materializedViewColumn = g.NewQueryStruct("MaterializedViewColumn"). + Text("Name", g.KeywordOptions().DoubleQuotes().Required()). + OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes().NoEquals()) + +var materializedViewColumnMaskingPolicy = g.NewQueryStruct("MaterializedViewColumnMaskingPolicy"). + Text("Name", g.KeywordOptions().Required()). + Identifier("MaskingPolicy", g.KindOfT[SchemaObjectIdentifier](), g.IdentifierOptions().SQL("MASKING POLICY").Required()). + NamedListWithParens("USING", g.KindOfT[string](), nil). // TODO: double quotes here? + OptionalTags() + +var materializedViewRowAccessPolicy = g.NewQueryStruct("MaterializedViewRowAccessPolicy"). + Identifier("RowAccessPolicy", g.KindOfT[SchemaObjectIdentifier](), g.IdentifierOptions().SQL("ROW ACCESS POLICY").Required()). + NamedListWithParens("ON", g.KindOfT[string](), g.KeywordOptions().Required()). // TODO: double quotes here? + WithValidation(g.ValidIdentifier, "RowAccessPolicy"). + WithValidation(g.ValidateValueSet, "On") + +var materializedViewClusterByExpression = g.NewQueryStruct("MaterializedViewClusterByExpression"). + Text("Name", g.KeywordOptions().DoubleQuotes().Required()) + +var materializedViewClusterBy = g.NewQueryStruct("MaterializedViewClusterBy"). + SQL("CLUSTER BY"). + ListQueryStructField("Expressions", materializedViewClusterByExpression, g.ListOptions().Parentheses()). + WithValidation(g.ValidateValueSet, "Expressions") + +var materializedViewSet = g.NewQueryStruct("MaterializedViewSet"). + OptionalSQL("SECURE"). + OptionalComment(). + WithValidation(g.ExactlyOneValueSet, "Secure", "Comment") + +var materializedViewUnset = g.NewQueryStruct("MaterializedViewUnset"). + OptionalSQL("SECURE"). + OptionalSQL("COMMENT"). + WithValidation(g.ExactlyOneValueSet, "Secure", "Comment") + +var materializedViewDbRow = g.DbStruct("materializedViewDBRow"). + Text("created_on"). + Text("name"). + OptionalText("reserved"). + Text("database_name"). + Text("schema_name"). + OptionalText("cluster_by"). + Number("rows"). + Number("bytes"). + Text("source_database_name"). + Text("source_schema_name"). + Text("source_table_name"). + Time("refreshed_on"). + Time("compacted_on"). + Text("owner"). + Bool("invalid"). + OptionalText("invalid_reason"). + Text("behind_by"). + OptionalText("comment"). + Text("text"). + Bool("is_secure"). + Text("automatic_clustering"). + OptionalText("owner_role_type"). + OptionalText("budget") + +var materializedView = g.PlainStruct("MaterializedView"). + Text("CreatedOn"). + Text("Name"). + OptionalText("Reserved"). + Text("DatabaseName"). + Text("SchemaName"). + Text("ClusterBy"). + Number("Rows"). + Number("Bytes"). + Text("SourceDatabaseName"). + Text("SourceSchemaName"). + Text("SourceTableName"). + Time("RefreshedOn"). + Time("CompactedOn"). + Text("Owner"). + Bool("Invalid"). + Text("InvalidReason"). + Text("BehindBy"). + Text("Comment"). + Text("Text"). + Bool("IsSecure"). + Bool("AutomaticClustering"). + Text("OwnerRoleType"). + Text("Budget") + +var materializedViewDetailsDbRow = g.DbStruct("materializedViewDetailsRow"). + Text("name"). + Field("type", "DataType"). + Text("kind"). + Text("null"). + OptionalText("default"). + Text("primary key"). + Text("unique key"). + OptionalText("check"). + OptionalText("expression"). + OptionalText("comment") + +var materializedViewDetails = g.PlainStruct("MaterializedViewDetails"). + Text("Name"). + Field("Type", "DataType"). + Text("Kind"). + Bool("IsNullable"). + OptionalText("Default"). + Bool("IsPrimary"). + Bool("IsUnique"). + OptionalBool("Check"). + OptionalText("Expression"). + OptionalText("Comment") + +var MaterializedViewsDef = g.NewInterface( + "MaterializedViews", + "MaterializedView", + g.KindOfT[SchemaObjectIdentifier](), +). + CreateOperation( + "https://docs.snowflake.com/en/sql-reference/sql/create-materialized-view", + g.NewQueryStruct("CreateMaterializedView"). + Create(). + OrReplace(). + OptionalSQL("SECURE"). + SQL("MATERIALIZED VIEW"). + IfNotExists(). + Name(). + OptionalCopyGrants(). + ListQueryStructField("Columns", materializedViewColumn, g.ListOptions().Parentheses()). + ListQueryStructField("ColumnsMaskingPolicies", materializedViewColumnMaskingPolicy, g.ListOptions().NoParentheses().NoEquals()). + OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). + OptionalQueryStructField("RowAccessPolicy", materializedViewRowAccessPolicy, g.KeywordOptions()). + OptionalTags(). + OptionalQueryStructField("ClusterBy", materializedViewClusterBy, g.KeywordOptions()). + SQL("AS"). + Text("sql", g.KeywordOptions().NoQuotes().Required()). + WithValidation(g.ValidIdentifier, "name"). + WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), + ). + AlterOperation( + "https://docs.snowflake.com/en/sql-reference/sql/alter-materialized-view", + g.NewQueryStruct("AlterMaterializedView"). + Alter(). + SQL("MATERIALIZED VIEW"). + Name(). + OptionalIdentifier("RenameTo", g.KindOfT[SchemaObjectIdentifier](), g.IdentifierOptions().SQL("RENAME TO")). + OptionalQueryStructField("ClusterBy", materializedViewClusterBy, g.KeywordOptions()). + OptionalSQL("DROP CLUSTERING KEY"). + OptionalSQL("SUSPEND RECLUSTER"). + OptionalSQL("RESUME RECLUSTER"). + OptionalSQL("SUSPEND"). + OptionalSQL("RESUME"). + OptionalQueryStructField("Set", materializedViewSet, g.KeywordOptions().SQL("SET")). + OptionalQueryStructField("Unset", materializedViewUnset, g.KeywordOptions().SQL("UNSET")). + WithValidation(g.ValidIdentifier, "name"). + WithValidation(g.ExactlyOneValueSet, "RenameTo", "ClusterBy", "DropClusteringKey", "SuspendRecluster", "ResumeRecluster", "Suspend", "Resume", "Set", "Unset"), + ). + DropOperation( + "https://docs.snowflake.com/en/sql-reference/sql/drop-materialized-view", + g.NewQueryStruct("DropMaterializedView"). + Drop(). + SQL("MATERIALIZED VIEW"). + IfExists(). + Name(). + WithValidation(g.ValidIdentifier, "name"), + ). + ShowOperation( + "https://docs.snowflake.com/en/sql-reference/sql/show-materialized-views", + materializedViewDbRow, + materializedView, + g.NewQueryStruct("ShowMaterializedViews"). + Show(). + SQL("MATERIALIZED VIEWS"). + OptionalLike(). + OptionalIn(), + ). + ShowByIdOperation(). + DescribeOperation( + g.DescriptionMappingKindSlice, + "https://docs.snowflake.com/en/sql-reference/sql/desc-materialized-view", + materializedViewDetailsDbRow, + materializedViewDetails, + g.NewQueryStruct("DescribeMaterializedView"). + Describe(). + SQL("MATERIALIZED VIEW"). + Name(). + WithValidation(g.ValidIdentifier, "name"), + ) diff --git a/pkg/sdk/materialized_views_dto_builders_gen.go b/pkg/sdk/materialized_views_dto_builders_gen.go new file mode 100644 index 0000000000..5f8852b106 --- /dev/null +++ b/pkg/sdk/materialized_views_dto_builders_gen.go @@ -0,0 +1,241 @@ +// Code generated by dto builder generator; DO NOT EDIT. + +package sdk + +import () + +func NewCreateMaterializedViewRequest( + name SchemaObjectIdentifier, + sql string, +) *CreateMaterializedViewRequest { + s := CreateMaterializedViewRequest{} + s.name = name + s.sql = sql + return &s +} + +func (s *CreateMaterializedViewRequest) WithOrReplace(OrReplace *bool) *CreateMaterializedViewRequest { + s.OrReplace = OrReplace + return s +} + +func (s *CreateMaterializedViewRequest) WithSecure(Secure *bool) *CreateMaterializedViewRequest { + s.Secure = Secure + return s +} + +func (s *CreateMaterializedViewRequest) WithIfNotExists(IfNotExists *bool) *CreateMaterializedViewRequest { + s.IfNotExists = IfNotExists + return s +} + +func (s *CreateMaterializedViewRequest) WithCopyGrants(CopyGrants *bool) *CreateMaterializedViewRequest { + s.CopyGrants = CopyGrants + return s +} + +func (s *CreateMaterializedViewRequest) WithColumns(Columns []MaterializedViewColumnRequest) *CreateMaterializedViewRequest { + s.Columns = Columns + return s +} + +func (s *CreateMaterializedViewRequest) WithColumnsMaskingPolicies(ColumnsMaskingPolicies []MaterializedViewColumnMaskingPolicyRequest) *CreateMaterializedViewRequest { + s.ColumnsMaskingPolicies = ColumnsMaskingPolicies + return s +} + +func (s *CreateMaterializedViewRequest) WithComment(Comment *string) *CreateMaterializedViewRequest { + s.Comment = Comment + return s +} + +func (s *CreateMaterializedViewRequest) WithRowAccessPolicy(RowAccessPolicy *MaterializedViewRowAccessPolicyRequest) *CreateMaterializedViewRequest { + s.RowAccessPolicy = RowAccessPolicy + return s +} + +func (s *CreateMaterializedViewRequest) WithTag(Tag []TagAssociation) *CreateMaterializedViewRequest { + s.Tag = Tag + return s +} + +func (s *CreateMaterializedViewRequest) WithClusterBy(ClusterBy *MaterializedViewClusterByRequest) *CreateMaterializedViewRequest { + s.ClusterBy = ClusterBy + return s +} + +func NewMaterializedViewColumnRequest( + Name string, +) *MaterializedViewColumnRequest { + s := MaterializedViewColumnRequest{} + s.Name = Name + return &s +} + +func (s *MaterializedViewColumnRequest) WithComment(Comment *string) *MaterializedViewColumnRequest { + s.Comment = Comment + return s +} + +func NewMaterializedViewColumnMaskingPolicyRequest( + Name string, + MaskingPolicy SchemaObjectIdentifier, +) *MaterializedViewColumnMaskingPolicyRequest { + s := MaterializedViewColumnMaskingPolicyRequest{} + s.Name = Name + s.MaskingPolicy = MaskingPolicy + return &s +} + +func (s *MaterializedViewColumnMaskingPolicyRequest) WithUsing(Using []string) *MaterializedViewColumnMaskingPolicyRequest { + s.Using = Using + return s +} + +func (s *MaterializedViewColumnMaskingPolicyRequest) WithTag(Tag []TagAssociation) *MaterializedViewColumnMaskingPolicyRequest { + s.Tag = Tag + return s +} + +func NewMaterializedViewRowAccessPolicyRequest( + RowAccessPolicy SchemaObjectIdentifier, + On []string, +) *MaterializedViewRowAccessPolicyRequest { + s := MaterializedViewRowAccessPolicyRequest{} + s.RowAccessPolicy = RowAccessPolicy + s.On = On + return &s +} + +func NewMaterializedViewClusterByRequest() *MaterializedViewClusterByRequest { + return &MaterializedViewClusterByRequest{} +} + +func (s *MaterializedViewClusterByRequest) WithExpressions(Expressions []MaterializedViewClusterByExpressionRequest) *MaterializedViewClusterByRequest { + s.Expressions = Expressions + return s +} + +func NewMaterializedViewClusterByExpressionRequest( + Name string, +) *MaterializedViewClusterByExpressionRequest { + s := MaterializedViewClusterByExpressionRequest{} + s.Name = Name + return &s +} + +func NewAlterMaterializedViewRequest( + name SchemaObjectIdentifier, +) *AlterMaterializedViewRequest { + s := AlterMaterializedViewRequest{} + s.name = name + return &s +} + +func (s *AlterMaterializedViewRequest) WithRenameTo(RenameTo *SchemaObjectIdentifier) *AlterMaterializedViewRequest { + s.RenameTo = RenameTo + return s +} + +func (s *AlterMaterializedViewRequest) WithClusterBy(ClusterBy *MaterializedViewClusterByRequest) *AlterMaterializedViewRequest { + s.ClusterBy = ClusterBy + return s +} + +func (s *AlterMaterializedViewRequest) WithDropClusteringKey(DropClusteringKey *bool) *AlterMaterializedViewRequest { + s.DropClusteringKey = DropClusteringKey + return s +} + +func (s *AlterMaterializedViewRequest) WithSuspendRecluster(SuspendRecluster *bool) *AlterMaterializedViewRequest { + s.SuspendRecluster = SuspendRecluster + return s +} + +func (s *AlterMaterializedViewRequest) WithResumeRecluster(ResumeRecluster *bool) *AlterMaterializedViewRequest { + s.ResumeRecluster = ResumeRecluster + return s +} + +func (s *AlterMaterializedViewRequest) WithSuspend(Suspend *bool) *AlterMaterializedViewRequest { + s.Suspend = Suspend + return s +} + +func (s *AlterMaterializedViewRequest) WithResume(Resume *bool) *AlterMaterializedViewRequest { + s.Resume = Resume + return s +} + +func (s *AlterMaterializedViewRequest) WithSet(Set *MaterializedViewSetRequest) *AlterMaterializedViewRequest { + s.Set = Set + return s +} + +func (s *AlterMaterializedViewRequest) WithUnset(Unset *MaterializedViewUnsetRequest) *AlterMaterializedViewRequest { + s.Unset = Unset + return s +} + +func NewMaterializedViewSetRequest() *MaterializedViewSetRequest { + return &MaterializedViewSetRequest{} +} + +func (s *MaterializedViewSetRequest) WithSecure(Secure *bool) *MaterializedViewSetRequest { + s.Secure = Secure + return s +} + +func (s *MaterializedViewSetRequest) WithComment(Comment *string) *MaterializedViewSetRequest { + s.Comment = Comment + return s +} + +func NewMaterializedViewUnsetRequest() *MaterializedViewUnsetRequest { + return &MaterializedViewUnsetRequest{} +} + +func (s *MaterializedViewUnsetRequest) WithSecure(Secure *bool) *MaterializedViewUnsetRequest { + s.Secure = Secure + return s +} + +func (s *MaterializedViewUnsetRequest) WithComment(Comment *bool) *MaterializedViewUnsetRequest { + s.Comment = Comment + return s +} + +func NewDropMaterializedViewRequest( + name SchemaObjectIdentifier, +) *DropMaterializedViewRequest { + s := DropMaterializedViewRequest{} + s.name = name + return &s +} + +func (s *DropMaterializedViewRequest) WithIfExists(IfExists *bool) *DropMaterializedViewRequest { + s.IfExists = IfExists + return s +} + +func NewShowMaterializedViewRequest() *ShowMaterializedViewRequest { + return &ShowMaterializedViewRequest{} +} + +func (s *ShowMaterializedViewRequest) WithLike(Like *Like) *ShowMaterializedViewRequest { + s.Like = Like + return s +} + +func (s *ShowMaterializedViewRequest) WithIn(In *In) *ShowMaterializedViewRequest { + s.In = In + return s +} + +func NewDescribeMaterializedViewRequest( + name SchemaObjectIdentifier, +) *DescribeMaterializedViewRequest { + s := DescribeMaterializedViewRequest{} + s.name = name + return &s +} diff --git a/pkg/sdk/materialized_views_dto_gen.go b/pkg/sdk/materialized_views_dto_gen.go new file mode 100644 index 0000000000..bcfb821926 --- /dev/null +++ b/pkg/sdk/materialized_views_dto_gen.go @@ -0,0 +1,92 @@ +package sdk + +//go:generate go run ./dto-builder-generator/main.go + +var ( + _ optionsProvider[CreateMaterializedViewOptions] = new(CreateMaterializedViewRequest) + _ optionsProvider[AlterMaterializedViewOptions] = new(AlterMaterializedViewRequest) + _ optionsProvider[DropMaterializedViewOptions] = new(DropMaterializedViewRequest) + _ optionsProvider[ShowMaterializedViewOptions] = new(ShowMaterializedViewRequest) + _ optionsProvider[DescribeMaterializedViewOptions] = new(DescribeMaterializedViewRequest) +) + +type CreateMaterializedViewRequest struct { + OrReplace *bool + Secure *bool + IfNotExists *bool + name SchemaObjectIdentifier // required + CopyGrants *bool + Columns []MaterializedViewColumnRequest + ColumnsMaskingPolicies []MaterializedViewColumnMaskingPolicyRequest + Comment *string + RowAccessPolicy *MaterializedViewRowAccessPolicyRequest + Tag []TagAssociation + ClusterBy *MaterializedViewClusterByRequest + sql string // required +} + +func (r *CreateMaterializedViewRequest) GetName() SchemaObjectIdentifier { + return r.name +} + +type MaterializedViewColumnRequest struct { + Name string // required + Comment *string +} + +type MaterializedViewColumnMaskingPolicyRequest struct { + Name string // required + MaskingPolicy SchemaObjectIdentifier // required + Using []string + Tag []TagAssociation +} + +type MaterializedViewRowAccessPolicyRequest struct { + RowAccessPolicy SchemaObjectIdentifier // required + On []string // required +} + +type MaterializedViewClusterByRequest struct { + Expressions []MaterializedViewClusterByExpressionRequest +} + +type MaterializedViewClusterByExpressionRequest struct { + Name string // required +} + +type AlterMaterializedViewRequest struct { + name SchemaObjectIdentifier // required + RenameTo *SchemaObjectIdentifier + ClusterBy *MaterializedViewClusterByRequest + DropClusteringKey *bool + SuspendRecluster *bool + ResumeRecluster *bool + Suspend *bool + Resume *bool + Set *MaterializedViewSetRequest + Unset *MaterializedViewUnsetRequest +} + +type MaterializedViewSetRequest struct { + Secure *bool + Comment *string +} + +type MaterializedViewUnsetRequest struct { + Secure *bool + Comment *bool +} + +type DropMaterializedViewRequest struct { + IfExists *bool + name SchemaObjectIdentifier // required +} + +type ShowMaterializedViewRequest struct { + Like *Like + In *In +} + +type DescribeMaterializedViewRequest struct { + name SchemaObjectIdentifier // required +} diff --git a/pkg/sdk/materialized_views_gen.go b/pkg/sdk/materialized_views_gen.go new file mode 100644 index 0000000000..a4cf204577 --- /dev/null +++ b/pkg/sdk/materialized_views_gen.go @@ -0,0 +1,192 @@ +package sdk + +import ( + "context" + "database/sql" + "time" +) + +type MaterializedViews interface { + Create(ctx context.Context, request *CreateMaterializedViewRequest) error + Alter(ctx context.Context, request *AlterMaterializedViewRequest) error + Drop(ctx context.Context, request *DropMaterializedViewRequest) error + Show(ctx context.Context, request *ShowMaterializedViewRequest) ([]MaterializedView, error) + ShowByID(ctx context.Context, id SchemaObjectIdentifier) (*MaterializedView, error) + Describe(ctx context.Context, id SchemaObjectIdentifier) ([]MaterializedViewDetails, error) +} + +// CreateMaterializedViewOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-materialized-view. +type CreateMaterializedViewOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + Secure *bool `ddl:"keyword" sql:"SECURE"` + materializedView bool `ddl:"static" sql:"MATERIALIZED VIEW"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` + Columns []MaterializedViewColumn `ddl:"list,parentheses"` + ColumnsMaskingPolicies []MaterializedViewColumnMaskingPolicy `ddl:"list,no_parentheses,no_equals"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` + RowAccessPolicy *MaterializedViewRowAccessPolicy `ddl:"keyword"` + Tag []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` + ClusterBy *MaterializedViewClusterBy `ddl:"keyword"` + as bool `ddl:"static" sql:"AS"` + sql string `ddl:"keyword,no_quotes"` +} + +type MaterializedViewColumn struct { + Name string `ddl:"keyword,double_quotes"` + Comment *string `ddl:"parameter,single_quotes,no_equals" sql:"COMMENT"` +} + +type MaterializedViewColumnMaskingPolicy struct { + Name string `ddl:"keyword"` + MaskingPolicy SchemaObjectIdentifier `ddl:"identifier" sql:"MASKING POLICY"` + Using []string `ddl:"keyword,parentheses" sql:"USING"` + Tag []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` +} + +type MaterializedViewRowAccessPolicy struct { + RowAccessPolicy SchemaObjectIdentifier `ddl:"identifier" sql:"ROW ACCESS POLICY"` + On []string `ddl:"keyword,parentheses" sql:"ON"` +} + +type MaterializedViewClusterBy struct { + clusterBy bool `ddl:"static" sql:"CLUSTER BY"` + Expressions []MaterializedViewClusterByExpression `ddl:"list,parentheses"` +} + +type MaterializedViewClusterByExpression struct { + Name string `ddl:"keyword,double_quotes"` +} + +// AlterMaterializedViewOptions is based on https://docs.snowflake.com/en/sql-reference/sql/alter-materialized-view. +type AlterMaterializedViewOptions struct { + alter bool `ddl:"static" sql:"ALTER"` + materializedView bool `ddl:"static" sql:"MATERIALIZED VIEW"` + name SchemaObjectIdentifier `ddl:"identifier"` + RenameTo *SchemaObjectIdentifier `ddl:"identifier" sql:"RENAME TO"` + ClusterBy *MaterializedViewClusterBy `ddl:"keyword"` + DropClusteringKey *bool `ddl:"keyword" sql:"DROP CLUSTERING KEY"` + SuspendRecluster *bool `ddl:"keyword" sql:"SUSPEND RECLUSTER"` + ResumeRecluster *bool `ddl:"keyword" sql:"RESUME RECLUSTER"` + Suspend *bool `ddl:"keyword" sql:"SUSPEND"` + Resume *bool `ddl:"keyword" sql:"RESUME"` + Set *MaterializedViewSet `ddl:"keyword" sql:"SET"` + Unset *MaterializedViewUnset `ddl:"keyword" sql:"UNSET"` +} + +type MaterializedViewSet struct { + Secure *bool `ddl:"keyword" sql:"SECURE"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} + +type MaterializedViewUnset struct { + Secure *bool `ddl:"keyword" sql:"SECURE"` + Comment *bool `ddl:"keyword" sql:"COMMENT"` +} + +// DropMaterializedViewOptions is based on https://docs.snowflake.com/en/sql-reference/sql/drop-materialized-view. +type DropMaterializedViewOptions struct { + drop bool `ddl:"static" sql:"DROP"` + materializedView bool `ddl:"static" sql:"MATERIALIZED VIEW"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` +} + +// ShowMaterializedViewOptions is based on https://docs.snowflake.com/en/sql-reference/sql/show-materialized-views. +type ShowMaterializedViewOptions struct { + show bool `ddl:"static" sql:"SHOW"` + materializedViews bool `ddl:"static" sql:"MATERIALIZED VIEWS"` + Like *Like `ddl:"keyword" sql:"LIKE"` + In *In `ddl:"keyword" sql:"IN"` +} + +type materializedViewDBRow struct { + CreatedOn string `db:"created_on"` + Name string `db:"name"` + Reserved sql.NullString `db:"reserved"` + DatabaseName string `db:"database_name"` + SchemaName string `db:"schema_name"` + ClusterBy sql.NullString `db:"cluster_by"` + Rows int `db:"rows"` + Bytes int `db:"bytes"` + SourceDatabaseName string `db:"source_database_name"` + SourceSchemaName string `db:"source_schema_name"` + SourceTableName string `db:"source_table_name"` + RefreshedOn time.Time `db:"refreshed_on"` + CompactedOn time.Time `db:"compacted_on"` + Owner string `db:"owner"` + Invalid bool `db:"invalid"` + InvalidReason sql.NullString `db:"invalid_reason"` + BehindBy string `db:"behind_by"` + Comment sql.NullString `db:"comment"` + Text string `db:"text"` + IsSecure bool `db:"is_secure"` + AutomaticClustering string `db:"automatic_clustering"` + OwnerRoleType sql.NullString `db:"owner_role_type"` + Budget sql.NullString `db:"budget"` +} + +type MaterializedView struct { + CreatedOn string + Name string + Reserved *string + DatabaseName string + SchemaName string + ClusterBy string + Rows int + Bytes int + SourceDatabaseName string + SourceSchemaName string + SourceTableName string + RefreshedOn time.Time + CompactedOn time.Time + Owner string + Invalid bool + InvalidReason string + BehindBy string + Comment string + Text string + IsSecure bool + AutomaticClustering bool + OwnerRoleType string + Budget string +} + +func (v *MaterializedView) ID() SchemaObjectIdentifier { + return NewSchemaObjectIdentifier(v.DatabaseName, v.SchemaName, v.Name) +} + +// DescribeMaterializedViewOptions is based on https://docs.snowflake.com/en/sql-reference/sql/desc-materialized-view. +type DescribeMaterializedViewOptions struct { + describe bool `ddl:"static" sql:"DESCRIBE"` + materializedView bool `ddl:"static" sql:"MATERIALIZED VIEW"` + name SchemaObjectIdentifier `ddl:"identifier"` +} + +type materializedViewDetailsRow struct { + Name string `db:"name"` + Type DataType `db:"type"` + Kind string `db:"kind"` + Null string `db:"null?"` + Default sql.NullString `db:"default"` + PrimaryKey string `db:"primary key"` + UniqueKey string `db:"unique key"` + Check sql.NullString `db:"check"` + Expression sql.NullString `db:"expression"` + Comment sql.NullString `db:"comment"` +} + +type MaterializedViewDetails struct { + Name string + Type DataType + Kind string + IsNullable bool + Default *string + IsPrimary bool + IsUnique bool + Check *bool + Expression *string + Comment *string +} diff --git a/pkg/sdk/materialized_views_gen_test.go b/pkg/sdk/materialized_views_gen_test.go new file mode 100644 index 0000000000..0ab9d645d9 --- /dev/null +++ b/pkg/sdk/materialized_views_gen_test.go @@ -0,0 +1,315 @@ +package sdk + +import "testing" + +func TestMaterializedViews_Create(t *testing.T) { + id := RandomSchemaObjectIdentifier() + sql := "SELECT id FROM t" + + // Minimal valid CreateMaterializedViewOptions + defaultOpts := func() *CreateMaterializedViewOptions { + return &CreateMaterializedViewOptions{ + name: id, + sql: sql, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *CreateMaterializedViewOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { + opts := defaultOpts() + opts.OrReplace = Bool(true) + opts.IfNotExists = Bool(true) + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateMaterializedViewOptions", "OrReplace", "IfNotExists")) + }) + + t.Run("validation: valid identifier for [opts.RowAccessPolicy.RowAccessPolicy]", func(t *testing.T) { + opts := defaultOpts() + opts.RowAccessPolicy = &MaterializedViewRowAccessPolicy{ + RowAccessPolicy: NewSchemaObjectIdentifier("", "", ""), + } + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: [opts.RowAccessPolicy.On] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.RowAccessPolicy = &MaterializedViewRowAccessPolicy{ + RowAccessPolicy: RandomSchemaObjectIdentifier(), + On: []string{}, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateMaterializedViewOptions.RowAccessPolicy", "On")) + }) + + t.Run("validation: [opts.ClusterBy.Expressions] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.ClusterBy = &MaterializedViewClusterBy{ + Expressions: []MaterializedViewClusterByExpression{}, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateMaterializedViewOptions.ClusterBy", "Expressions")) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, "CREATE MATERIALIZED VIEW %s AS %s", id.FullyQualifiedName(), sql) + }) + + t.Run("all options", func(t *testing.T) { + rowAccessPolicyId := RandomSchemaObjectIdentifier() + tag1Id := RandomSchemaObjectIdentifier() + tag2Id := RandomSchemaObjectIdentifier() + maskingPolicy1Id := RandomSchemaObjectIdentifier() + maskingPolicy2Id := RandomSchemaObjectIdentifier() + + req := NewCreateMaterializedViewRequest(id, sql). + WithOrReplace(Bool(true)). + WithSecure(Bool(true)). + WithColumns([]MaterializedViewColumnRequest{ + *NewMaterializedViewColumnRequest("column_without_comment"), + *NewMaterializedViewColumnRequest("column_with_comment").WithComment(String("column 2 comment")), + }). + WithColumnsMaskingPolicies([]MaterializedViewColumnMaskingPolicyRequest{ + *NewMaterializedViewColumnMaskingPolicyRequest("column", maskingPolicy1Id). + WithUsing([]string{"a", "b"}). + WithTag([]TagAssociation{{ + Name: tag1Id, + Value: "v1", + }}), + *NewMaterializedViewColumnMaskingPolicyRequest("column 2", maskingPolicy2Id), + }). + WithCopyGrants(Bool(true)). + WithComment(String("comment")). + WithRowAccessPolicy(NewMaterializedViewRowAccessPolicyRequest(rowAccessPolicyId, []string{"c", "d"})). + WithTag([]TagAssociation{{ + Name: tag2Id, + Value: "v2", + }}). + WithClusterBy(NewMaterializedViewClusterByRequest().WithExpressions([]MaterializedViewClusterByExpressionRequest{{"column_without_comment"}, {"column_with_comment"}})) + + assertOptsValidAndSQLEquals(t, req.toOpts(), `CREATE OR REPLACE SECURE MATERIALIZED VIEW %s COPY GRANTS ("column_without_comment", "column_with_comment" COMMENT 'column 2 comment') column MASKING POLICY %s USING (a, b) TAG (%s = 'v1'), column 2 MASKING POLICY %s COMMENT = 'comment' ROW ACCESS POLICY %s ON (c, d) TAG (%s = 'v2') CLUSTER BY ("column_without_comment", "column_with_comment") AS %s`, id.FullyQualifiedName(), maskingPolicy1Id.FullyQualifiedName(), tag1Id.FullyQualifiedName(), maskingPolicy2Id.FullyQualifiedName(), rowAccessPolicyId.FullyQualifiedName(), tag2Id.FullyQualifiedName(), sql) + }) +} + +func TestMaterializedViews_Alter(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + // Minimal valid AlterMaterializedViewOptions + defaultOpts := func() *AlterMaterializedViewOptions { + return &AlterMaterializedViewOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *AlterMaterializedViewOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: exactly one field from [opts.RenameTo opts.ClusterBy opts.DropClusteringKey opts.SuspendRecluster opts.ResumeRecluster opts.Suspend opts.Resume opts.Set opts.Unset] should be present", func(t *testing.T) { + opts := defaultOpts() + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterMaterializedViewOptions", "RenameTo", "ClusterBy", "DropClusteringKey", "SuspendRecluster", "ResumeRecluster", "Suspend", "Resume", "Set", "Unset")) + }) + + t.Run("validation: exactly one field from [opts.RenameTo opts.ClusterBy opts.DropClusteringKey opts.SuspendRecluster opts.ResumeRecluster opts.Suspend opts.Resume opts.Set opts.Unset] should be present - more present", func(t *testing.T) { + opts := defaultOpts() + opts.SuspendRecluster = Bool(true) + opts.Suspend = Bool(true) + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterMaterializedViewOptions", "RenameTo", "ClusterBy", "DropClusteringKey", "SuspendRecluster", "ResumeRecluster", "Suspend", "Resume", "Set", "Unset")) + }) + + t.Run("validation: [opts.ClusterBy.Expressions] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.ClusterBy = &MaterializedViewClusterBy{ + Expressions: []MaterializedViewClusterByExpression{}, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("AlterMaterializedViewOptions.ClusterBy", "Expressions")) + }) + + t.Run("validation: exactly one field from [opts.Set.Secure opts.Set.Comment] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &MaterializedViewSet{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterMaterializedViewOptions.Set", "Secure", "Comment")) + }) + + t.Run("validation: exactly one field from [opts.Set.Secure opts.Set.Comment] should be set - more present", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &MaterializedViewSet{ + Secure: Bool(true), + Comment: String("comment"), + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterMaterializedViewOptions.Set", "Secure", "Comment")) + }) + + t.Run("validation: exactly one field from [opts.Unset.Secure opts.Unset.Comment] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Unset = &MaterializedViewUnset{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterMaterializedViewOptions.Unset", "Secure", "Comment")) + }) + + t.Run("validation: exactly one field from [opts.Unset.Secure opts.Unset.Comment] should be set - more present", func(t *testing.T) { + opts := defaultOpts() + opts.Unset = &MaterializedViewUnset{ + Secure: Bool(true), + Comment: Bool(true), + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterMaterializedViewOptions.Unset", "Secure", "Comment")) + }) + + t.Run("rename", func(t *testing.T) { + newId := RandomSchemaObjectIdentifier() + + opts := defaultOpts() + opts.RenameTo = &newId + assertOptsValidAndSQLEquals(t, opts, "ALTER MATERIALIZED VIEW %s RENAME TO %s", id.FullyQualifiedName(), newId.FullyQualifiedName()) + }) + + t.Run("cluster by", func(t *testing.T) { + opts := defaultOpts() + opts.ClusterBy = &MaterializedViewClusterBy{ + Expressions: []MaterializedViewClusterByExpression{{"column"}}, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER MATERIALIZED VIEW %s CLUSTER BY ("column")`, id.FullyQualifiedName()) + }) + + t.Run("drop clustering key", func(t *testing.T) { + opts := defaultOpts() + opts.DropClusteringKey = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "ALTER MATERIALIZED VIEW %s DROP CLUSTERING KEY", id.FullyQualifiedName()) + }) + + t.Run("suspend recluster", func(t *testing.T) { + opts := defaultOpts() + opts.SuspendRecluster = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "ALTER MATERIALIZED VIEW %s SUSPEND RECLUSTER", id.FullyQualifiedName()) + }) + + t.Run("resume recluster", func(t *testing.T) { + opts := defaultOpts() + opts.ResumeRecluster = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "ALTER MATERIALIZED VIEW %s RESUME RECLUSTER", id.FullyQualifiedName()) + }) + + t.Run("suspend ", func(t *testing.T) { + opts := defaultOpts() + opts.Suspend = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "ALTER MATERIALIZED VIEW %s SUSPEND", id.FullyQualifiedName()) + }) + + t.Run("resume", func(t *testing.T) { + opts := defaultOpts() + opts.Resume = Bool(true) + assertOptsValidAndSQLEquals(t, opts, "ALTER MATERIALIZED VIEW %s RESUME", id.FullyQualifiedName()) + }) + + t.Run("set single", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &MaterializedViewSet{ + Secure: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, "ALTER MATERIALIZED VIEW %s SET SECURE", id.FullyQualifiedName()) + }) + + t.Run("unset single", func(t *testing.T) { + opts := defaultOpts() + opts.Unset = &MaterializedViewUnset{ + Secure: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, "ALTER MATERIALIZED VIEW %s UNSET SECURE", id.FullyQualifiedName()) + }) +} + +func TestMaterializedViews_Drop(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + // Minimal valid DropMaterializedViewOptions + defaultOpts := func() *DropMaterializedViewOptions { + return &DropMaterializedViewOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *DropMaterializedViewOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, "DROP MATERIALIZED VIEW %s", id.FullyQualifiedName()) + }) +} + +func TestMaterializedViews_Show(t *testing.T) { + // Minimal valid ShowMaterializedViewOptions + defaultOpts := func() *ShowMaterializedViewOptions { + return &ShowMaterializedViewOptions{} + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *ShowMaterializedViewOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, "SHOW MATERIALIZED VIEWS") + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + opts.Like = &Like{ + Pattern: String("myaccount"), + } + opts.In = &In{ + Account: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, "SHOW MATERIALIZED VIEWS LIKE 'myaccount' IN ACCOUNT") + }) +} + +func TestMaterializedViews_Describe(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + // Minimal valid DescribeMaterializedViewOptions + defaultOpts := func() *DescribeMaterializedViewOptions { + return &DescribeMaterializedViewOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *DescribeMaterializedViewOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, "DESCRIBE MATERIALIZED VIEW %s", id.FullyQualifiedName()) + }) +} diff --git a/pkg/sdk/materialized_views_impl_gen.go b/pkg/sdk/materialized_views_impl_gen.go new file mode 100644 index 0000000000..1914973bf3 --- /dev/null +++ b/pkg/sdk/materialized_views_impl_gen.go @@ -0,0 +1,225 @@ +package sdk + +import ( + "context" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" +) + +var _ MaterializedViews = (*materializedViews)(nil) + +type materializedViews struct { + client *Client +} + +func (v *materializedViews) Create(ctx context.Context, request *CreateMaterializedViewRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *materializedViews) Alter(ctx context.Context, request *AlterMaterializedViewRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *materializedViews) Drop(ctx context.Context, request *DropMaterializedViewRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *materializedViews) Show(ctx context.Context, request *ShowMaterializedViewRequest) ([]MaterializedView, error) { + opts := request.toOpts() + dbRows, err := validateAndQuery[materializedViewDBRow](v.client, ctx, opts) + if err != nil { + return nil, err + } + resultList := convertRows[materializedViewDBRow, MaterializedView](dbRows) + return resultList, nil +} + +func (v *materializedViews) ShowByID(ctx context.Context, id SchemaObjectIdentifier) (*MaterializedView, error) { + request := NewShowMaterializedViewRequest().WithIn(&In{Schema: NewDatabaseObjectIdentifier(id.DatabaseName(), id.SchemaName())}).WithLike(&Like{String(id.Name())}) + materializedViews, err := v.Show(ctx, request) + if err != nil { + return nil, err + } + return collections.FindOne(materializedViews, func(r MaterializedView) bool { return r.Name == id.Name() }) +} + +func (v *materializedViews) Describe(ctx context.Context, id SchemaObjectIdentifier) ([]MaterializedViewDetails, error) { + opts := &DescribeMaterializedViewOptions{ + name: id, + } + rows, err := validateAndQuery[materializedViewDetailsRow](v.client, ctx, opts) + if err != nil { + return nil, err + } + return convertRows[materializedViewDetailsRow, MaterializedViewDetails](rows), nil +} + +func (r *CreateMaterializedViewRequest) toOpts() *CreateMaterializedViewOptions { + opts := &CreateMaterializedViewOptions{ + OrReplace: r.OrReplace, + Secure: r.Secure, + IfNotExists: r.IfNotExists, + name: r.name, + CopyGrants: r.CopyGrants, + Comment: r.Comment, + Tag: r.Tag, + sql: r.sql, + } + if r.Columns != nil { + s := make([]MaterializedViewColumn, len(r.Columns)) + for i, v := range r.Columns { + s[i] = MaterializedViewColumn(v) + } + opts.Columns = s + } + if r.ColumnsMaskingPolicies != nil { + s := make([]MaterializedViewColumnMaskingPolicy, len(r.ColumnsMaskingPolicies)) + for i, v := range r.ColumnsMaskingPolicies { + s[i] = MaterializedViewColumnMaskingPolicy(v) + } + opts.ColumnsMaskingPolicies = s + } + if r.RowAccessPolicy != nil { + opts.RowAccessPolicy = &MaterializedViewRowAccessPolicy{ + RowAccessPolicy: r.RowAccessPolicy.RowAccessPolicy, + On: r.RowAccessPolicy.On, + } + } + if r.ClusterBy != nil { + opts.ClusterBy = &MaterializedViewClusterBy{} + if r.ClusterBy.Expressions != nil { + s := make([]MaterializedViewClusterByExpression, len(r.ClusterBy.Expressions)) + for i, v := range r.ClusterBy.Expressions { + s[i] = MaterializedViewClusterByExpression(v) + } + opts.ClusterBy.Expressions = s + } + } + return opts +} + +func (r *AlterMaterializedViewRequest) toOpts() *AlterMaterializedViewOptions { + opts := &AlterMaterializedViewOptions{ + name: r.name, + RenameTo: r.RenameTo, + DropClusteringKey: r.DropClusteringKey, + SuspendRecluster: r.SuspendRecluster, + ResumeRecluster: r.ResumeRecluster, + Suspend: r.Suspend, + Resume: r.Resume, + } + if r.ClusterBy != nil { + opts.ClusterBy = &MaterializedViewClusterBy{} + if r.ClusterBy.Expressions != nil { + s := make([]MaterializedViewClusterByExpression, len(r.ClusterBy.Expressions)) + for i, v := range r.ClusterBy.Expressions { + s[i] = MaterializedViewClusterByExpression(v) + } + opts.ClusterBy.Expressions = s + } + } + if r.Set != nil { + opts.Set = &MaterializedViewSet{ + Secure: r.Set.Secure, + Comment: r.Set.Comment, + } + } + if r.Unset != nil { + opts.Unset = &MaterializedViewUnset{ + Secure: r.Unset.Secure, + Comment: r.Unset.Comment, + } + } + return opts +} + +func (r *DropMaterializedViewRequest) toOpts() *DropMaterializedViewOptions { + opts := &DropMaterializedViewOptions{ + IfExists: r.IfExists, + name: r.name, + } + return opts +} + +func (r *ShowMaterializedViewRequest) toOpts() *ShowMaterializedViewOptions { + opts := &ShowMaterializedViewOptions{ + Like: r.Like, + In: r.In, + } + return opts +} + +func (r materializedViewDBRow) convert() *MaterializedView { + materializedView := MaterializedView{ + CreatedOn: r.CreatedOn, + Name: r.Name, + DatabaseName: r.DatabaseName, + SchemaName: r.SchemaName, + Rows: r.Rows, + Bytes: r.Bytes, + SourceDatabaseName: r.SourceDatabaseName, + SourceSchemaName: r.SourceSchemaName, + SourceTableName: r.SourceTableName, + RefreshedOn: r.RefreshedOn, + CompactedOn: r.CompactedOn, + Owner: r.Owner, + Invalid: r.Invalid, + BehindBy: r.BehindBy, + Text: r.Text, + IsSecure: r.IsSecure, + } + if r.Reserved.Valid { + materializedView.Reserved = &r.Reserved.String + } + if r.ClusterBy.Valid { + materializedView.ClusterBy = r.ClusterBy.String + } + if r.InvalidReason.Valid { + materializedView.InvalidReason = r.InvalidReason.String + } + if r.Comment.Valid { + materializedView.Comment = r.Comment.String + } + materializedView.AutomaticClustering = r.AutomaticClustering == "ON" + if r.OwnerRoleType.Valid { + materializedView.OwnerRoleType = r.OwnerRoleType.String + } + if r.Budget.Valid { + materializedView.Budget = r.Budget.String + } + return &materializedView +} + +func (r *DescribeMaterializedViewRequest) toOpts() *DescribeMaterializedViewOptions { + opts := &DescribeMaterializedViewOptions{ + name: r.name, + } + return opts +} + +func (r materializedViewDetailsRow) convert() *MaterializedViewDetails { + details := &MaterializedViewDetails{ + Name: r.Name, + Type: r.Type, + Kind: r.Kind, + IsNullable: r.Null == "Y", + IsPrimary: r.PrimaryKey == "Y", + IsUnique: r.UniqueKey == "Y", + } + if r.Default.Valid { + details.Default = String(r.Default.String) + } + if r.Check.Valid { + details.Check = Bool(r.Check.String == "Y") + } + if r.Expression.Valid { + details.Expression = String(r.Expression.String) + } + if r.Comment.Valid { + details.Comment = String(r.Comment.String) + } + return details +} diff --git a/pkg/sdk/materialized_views_validations_gen.go b/pkg/sdk/materialized_views_validations_gen.go new file mode 100644 index 0000000000..2b03c2c231 --- /dev/null +++ b/pkg/sdk/materialized_views_validations_gen.go @@ -0,0 +1,95 @@ +package sdk + +var ( + _ validatable = new(CreateMaterializedViewOptions) + _ validatable = new(AlterMaterializedViewOptions) + _ validatable = new(DropMaterializedViewOptions) + _ validatable = new(ShowMaterializedViewOptions) + _ validatable = new(DescribeMaterializedViewOptions) +) + +func (opts *CreateMaterializedViewOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if everyValueSet(opts.OrReplace, opts.IfNotExists) { + errs = append(errs, errOneOf("CreateMaterializedViewOptions", "OrReplace", "IfNotExists")) + } + if valueSet(opts.RowAccessPolicy) { + if !ValidObjectIdentifier(opts.RowAccessPolicy.RowAccessPolicy) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if !valueSet(opts.RowAccessPolicy.On) { + errs = append(errs, errNotSet("CreateMaterializedViewOptions.RowAccessPolicy", "On")) + } + } + if valueSet(opts.ClusterBy) { + if !valueSet(opts.ClusterBy.Expressions) { + errs = append(errs, errNotSet("CreateMaterializedViewOptions.ClusterBy", "Expressions")) + } + } + return JoinErrors(errs...) +} + +func (opts *AlterMaterializedViewOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if !exactlyOneValueSet(opts.RenameTo, opts.ClusterBy, opts.DropClusteringKey, opts.SuspendRecluster, opts.ResumeRecluster, opts.Suspend, opts.Resume, opts.Set, opts.Unset) { + errs = append(errs, errExactlyOneOf("AlterMaterializedViewOptions", "RenameTo", "ClusterBy", "DropClusteringKey", "SuspendRecluster", "ResumeRecluster", "Suspend", "Resume", "Set", "Unset")) + } + if valueSet(opts.ClusterBy) { + if !valueSet(opts.ClusterBy.Expressions) { + errs = append(errs, errNotSet("AlterMaterializedViewOptions.ClusterBy", "Expressions")) + } + } + if valueSet(opts.Set) { + if !exactlyOneValueSet(opts.Set.Secure, opts.Set.Comment) { + errs = append(errs, errExactlyOneOf("AlterMaterializedViewOptions.Set", "Secure", "Comment")) + } + } + if valueSet(opts.Unset) { + if !exactlyOneValueSet(opts.Unset.Secure, opts.Unset.Comment) { + errs = append(errs, errExactlyOneOf("AlterMaterializedViewOptions.Unset", "Secure", "Comment")) + } + } + return JoinErrors(errs...) +} + +func (opts *DropMaterializedViewOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + return JoinErrors(errs...) +} + +func (opts *ShowMaterializedViewOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + return JoinErrors(errs...) +} + +func (opts *DescribeMaterializedViewOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + return JoinErrors(errs...) +} diff --git a/pkg/sdk/poc/generator/db_struct.go b/pkg/sdk/poc/generator/db_struct.go index 8e1a53f379..64e93033b2 100644 --- a/pkg/sdk/poc/generator/db_struct.go +++ b/pkg/sdk/poc/generator/db_struct.go @@ -45,6 +45,14 @@ func (v *dbStruct) OptionalBool(dbName string) *dbStruct { return v.Field(dbName, "sql.NullBool") } +func (v *dbStruct) Number(dbName string) *dbStruct { + return v.Field(dbName, "int") +} + +func (v *dbStruct) OptionalNumber(dbName string) *dbStruct { + return v.Field(dbName, "sql.NullInt64") +} + func (v *dbStruct) IntoField() *Field { f := NewField(v.name, v.name, nil, nil) for _, field := range v.fields { diff --git a/pkg/sdk/poc/generator/plain_struct.go b/pkg/sdk/poc/generator/plain_struct.go index d0e529b38d..facb90f81a 100644 --- a/pkg/sdk/poc/generator/plain_struct.go +++ b/pkg/sdk/poc/generator/plain_struct.go @@ -45,6 +45,14 @@ func (v *plainStruct) OptionalBool(name string) *plainStruct { return v.Field(name, "*bool") } +func (v *plainStruct) Number(dbName string) *plainStruct { + return v.Field(dbName, "int") +} + +func (v *plainStruct) OptionalNumber(dbName string) *plainStruct { + return v.Field(dbName, "*int") +} + func (v *plainStruct) IntoField() *Field { f := NewField(v.name, v.name, nil, nil) for _, field := range v.fields { diff --git a/pkg/sdk/poc/main.go b/pkg/sdk/poc/main.go index 959e963e8f..484c2adcf9 100644 --- a/pkg/sdk/poc/main.go +++ b/pkg/sdk/poc/main.go @@ -33,6 +33,7 @@ var definitionMapping = map[string]*generator.Interface{ "row_access_policies_def.go": sdk.RowAccessPoliciesDef, "applications_def.go": sdk.ApplicationsDef, "sequences_def.go": sdk.SequencesDef, + "materialized_views_def.go": sdk.MaterializedViewsDef, } func main() { diff --git a/pkg/sdk/testint/materialized_views_gen_integration_test.go b/pkg/sdk/testint/materialized_views_gen_integration_test.go new file mode 100644 index 0000000000..09f162879d --- /dev/null +++ b/pkg/sdk/testint/materialized_views_gen_integration_test.go @@ -0,0 +1,357 @@ +package testint + +import ( + "fmt" + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/random" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TODO [SNOW-1016430]: add tests for setting masking policy on creation +func TestInt_MaterializedViews(t *testing.T) { + client := testClient(t) + ctx := testContext(t) + + table, tableCleanup := createTable(t, client, testDb(t), testSchema(t)) + t.Cleanup(tableCleanup) + + sql := fmt.Sprintf("SELECT id FROM %s", table.ID().FullyQualifiedName()) + + assertMaterializedViewWithOptions := func(t *testing.T, view *sdk.MaterializedView, id sdk.SchemaObjectIdentifier, isSecure bool, comment string, clusterBy string) { + t.Helper() + assert.NotEmpty(t, view.CreatedOn) + assert.Equal(t, id.Name(), view.Name) + assert.Empty(t, view.Reserved) + assert.Equal(t, testDb(t).Name, view.DatabaseName) + assert.Equal(t, testSchema(t).Name, view.SchemaName) + assert.Equal(t, clusterBy, view.ClusterBy) + assert.Equal(t, 0, view.Rows) + assert.Equal(t, 0, view.Bytes) + assert.Equal(t, testDb(t).Name, view.SourceDatabaseName) + assert.Equal(t, testSchema(t).Name, view.SourceSchemaName) + assert.Equal(t, table.Name, view.SourceTableName) + assert.NotEmpty(t, view.RefreshedOn) + assert.NotEmpty(t, view.CompactedOn) + assert.Equal(t, "ACCOUNTADMIN", view.Owner) + assert.Equal(t, false, view.Invalid) + assert.Equal(t, "", view.InvalidReason) + assert.NotEmpty(t, view.BehindBy) + assert.Equal(t, comment, view.Comment) + assert.NotEmpty(t, view.Text) + assert.Equal(t, isSecure, view.IsSecure) + assert.Equal(t, clusterBy != "", view.AutomaticClustering) + assert.Equal(t, "ROLE", view.OwnerRoleType) + assert.Equal(t, "", view.Budget) + } + + assertMaterializedView := func(t *testing.T, view *sdk.MaterializedView, id sdk.SchemaObjectIdentifier) { + t.Helper() + assertMaterializedViewWithOptions(t, view, id, false, "", "") + } + + assertViewDetailsRow := func(t *testing.T, materializedViewDetails *sdk.MaterializedViewDetails) { + t.Helper() + assert.Equal(t, sdk.MaterializedViewDetails{ + Name: "ID", + Type: "NUMBER(38,0)", + Kind: "COLUMN", + IsNullable: true, + Default: nil, + IsPrimary: false, + IsUnique: false, + Check: nil, + Expression: nil, + Comment: nil, + }, *materializedViewDetails) + } + + cleanupMaterializedViewProvider := func(id sdk.SchemaObjectIdentifier) func() { + return func() { + err := client.MaterializedViews.Drop(ctx, sdk.NewDropMaterializedViewRequest(id)) + require.NoError(t, err) + } + } + + createMaterializedViewBasicRequest := func(t *testing.T) *sdk.CreateMaterializedViewRequest { + t.Helper() + name := random.String() + id := sdk.NewSchemaObjectIdentifier(testDb(t).Name, testSchema(t).Name, name) + + return sdk.NewCreateMaterializedViewRequest(id, sql) + } + + createMaterializedViewWithRequest := func(t *testing.T, request *sdk.CreateMaterializedViewRequest) *sdk.MaterializedView { + t.Helper() + id := request.GetName() + + err := client.MaterializedViews.Create(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupMaterializedViewProvider(id)) + + materializedView, err := client.MaterializedViews.ShowByID(ctx, id) + require.NoError(t, err) + + return materializedView + } + + createMaterializedView := func(t *testing.T) *sdk.MaterializedView { + t.Helper() + return createMaterializedViewWithRequest(t, createMaterializedViewBasicRequest(t)) + } + + t.Run("create materialized view: no optionals", func(t *testing.T) { + request := createMaterializedViewBasicRequest(t) + + view := createMaterializedViewWithRequest(t, request) + + assertMaterializedView(t, view, request.GetName()) + }) + + t.Run("create materialized view: almost complete case", func(t *testing.T) { + rowAccessPolicyId, rowAccessPolicyCleanup := createRowAccessPolicy(t, client, testSchema(t)) + t.Cleanup(rowAccessPolicyCleanup) + + tag, tagCleanup := createTag(t, client, testDb(t), testSchema(t)) + t.Cleanup(tagCleanup) + + request := createMaterializedViewBasicRequest(t). + WithOrReplace(sdk.Bool(true)). + WithSecure(sdk.Bool(true)). + WithColumns([]sdk.MaterializedViewColumnRequest{ + *sdk.NewMaterializedViewColumnRequest("COLUMN_WITH_COMMENT").WithComment(sdk.String("column comment")), + }). + WithCopyGrants(sdk.Bool(true)). + WithComment(sdk.String("comment")). + WithRowAccessPolicy(sdk.NewMaterializedViewRowAccessPolicyRequest(rowAccessPolicyId, []string{"column_with_comment"})). + WithTag([]sdk.TagAssociation{{ + Name: tag.ID(), + Value: "v2", + }}). + WithClusterBy(sdk.NewMaterializedViewClusterByRequest().WithExpressions([]sdk.MaterializedViewClusterByExpressionRequest{{Name: "COLUMN_WITH_COMMENT"}})) + + id := request.GetName() + + view := createMaterializedViewWithRequest(t, request) + + assertMaterializedViewWithOptions(t, view, id, true, "comment", fmt.Sprintf(`LINEAR("%s")`, "COLUMN_WITH_COMMENT")) + rowAccessPolicyReference, err := getRowAccessPolicyFor(t, client, view.ID(), sdk.ObjectTypeView) + require.NoError(t, err) + assert.Equal(t, rowAccessPolicyId.Name(), rowAccessPolicyReference.PolicyName) + assert.Equal(t, "ROW_ACCESS_POLICY", rowAccessPolicyReference.PolicyKind) + assert.Equal(t, view.ID().Name(), rowAccessPolicyReference.RefEntityName) + assert.Equal(t, "MATERIALIZED_VIEW", rowAccessPolicyReference.RefEntityDomain) + assert.Equal(t, "ACTIVE", rowAccessPolicyReference.PolicyStatus) + }) + + t.Run("drop materialized view: existing", func(t *testing.T) { + request := createMaterializedViewBasicRequest(t) + id := request.GetName() + + err := client.MaterializedViews.Create(ctx, request) + require.NoError(t, err) + + err = client.MaterializedViews.Drop(ctx, sdk.NewDropMaterializedViewRequest(id)) + require.NoError(t, err) + + _, err = client.MaterializedViews.ShowByID(ctx, id) + assert.ErrorIs(t, err, collections.ErrObjectNotFound) + }) + + t.Run("drop view: non-existing", func(t *testing.T) { + id := sdk.NewSchemaObjectIdentifier(testDb(t).Name, testSchema(t).Name, "does_not_exist") + + err := client.MaterializedViews.Drop(ctx, sdk.NewDropMaterializedViewRequest(id)) + assert.ErrorIs(t, err, sdk.ErrObjectNotExistOrAuthorized) + }) + + t.Run("alter materialized view: rename", func(t *testing.T) { + createRequest := createMaterializedViewBasicRequest(t) + id := createRequest.GetName() + + err := client.MaterializedViews.Create(ctx, createRequest) + require.NoError(t, err) + + newName := random.String() + newId := sdk.NewSchemaObjectIdentifier(testDb(t).Name, testSchema(t).Name, newName) + alterRequest := sdk.NewAlterMaterializedViewRequest(id).WithRenameTo(&newId) + + err = client.MaterializedViews.Alter(ctx, alterRequest) + if err != nil { + t.Cleanup(cleanupMaterializedViewProvider(id)) + } else { + t.Cleanup(cleanupMaterializedViewProvider(newId)) + } + require.NoError(t, err) + + _, err = client.MaterializedViews.ShowByID(ctx, id) + assert.ErrorIs(t, err, collections.ErrObjectNotFound) + + view, err := client.MaterializedViews.ShowByID(ctx, newId) + require.NoError(t, err) + + assertMaterializedView(t, view, newId) + }) + + t.Run("alter materialized view: set cluster by", func(t *testing.T) { + view := createMaterializedView(t) + id := view.ID() + + alterRequest := sdk.NewAlterMaterializedViewRequest(id).WithClusterBy(sdk.NewMaterializedViewClusterByRequest().WithExpressions([]sdk.MaterializedViewClusterByExpressionRequest{{Name: "ID"}})) + err := client.MaterializedViews.Alter(ctx, alterRequest) + require.NoError(t, err) + + alteredView, err := client.MaterializedViews.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, fmt.Sprintf(`LINEAR("%s")`, "ID"), alteredView.ClusterBy) + }) + + t.Run("alter materialized view: recluster suspend and resume", func(t *testing.T) { + request := createMaterializedViewBasicRequest(t).WithClusterBy(sdk.NewMaterializedViewClusterByRequest().WithExpressions([]sdk.MaterializedViewClusterByExpressionRequest{{Name: "ID"}})) + view := createMaterializedViewWithRequest(t, request) + id := view.ID() + + assert.Equal(t, true, view.AutomaticClustering) + + alterRequest := sdk.NewAlterMaterializedViewRequest(id).WithSuspendRecluster(sdk.Bool(true)) + err := client.MaterializedViews.Alter(ctx, alterRequest) + require.NoError(t, err) + + alteredView, err := client.MaterializedViews.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, false, alteredView.AutomaticClustering) + + alterRequest = sdk.NewAlterMaterializedViewRequest(id).WithResumeRecluster(sdk.Bool(true)) + err = client.MaterializedViews.Alter(ctx, alterRequest) + require.NoError(t, err) + + alteredView, err = client.MaterializedViews.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, true, alteredView.AutomaticClustering) + }) + + t.Run("alter materialized view: suspend and resume", func(t *testing.T) { + view := createMaterializedView(t) + id := view.ID() + + assert.Equal(t, false, view.Invalid) + + alterRequest := sdk.NewAlterMaterializedViewRequest(id).WithSuspend(sdk.Bool(true)) + err := client.MaterializedViews.Alter(ctx, alterRequest) + require.NoError(t, err) + + alteredView, err := client.MaterializedViews.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, true, alteredView.Invalid) + + alterRequest = sdk.NewAlterMaterializedViewRequest(id).WithResume(sdk.Bool(true)) + err = client.MaterializedViews.Alter(ctx, alterRequest) + require.NoError(t, err) + + alteredView, err = client.MaterializedViews.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, false, alteredView.Invalid) + }) + + t.Run("alter materialized view: set and unset values", func(t *testing.T) { + view := createMaterializedView(t) + id := view.ID() + + alterRequest := sdk.NewAlterMaterializedViewRequest(id).WithSet( + sdk.NewMaterializedViewSetRequest().WithSecure(sdk.Bool(true)), + ) + err := client.MaterializedViews.Alter(ctx, alterRequest) + require.NoError(t, err) + + alteredView, err := client.MaterializedViews.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, true, alteredView.IsSecure) + + alterRequest = sdk.NewAlterMaterializedViewRequest(id).WithSet( + sdk.NewMaterializedViewSetRequest().WithComment(sdk.String("comment")), + ) + err = client.MaterializedViews.Alter(ctx, alterRequest) + require.NoError(t, err) + + alteredView, err = client.MaterializedViews.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, "comment", alteredView.Comment) + + alterRequest = sdk.NewAlterMaterializedViewRequest(id).WithUnset( + sdk.NewMaterializedViewUnsetRequest().WithComment(sdk.Bool(true)), + ) + err = client.MaterializedViews.Alter(ctx, alterRequest) + require.NoError(t, err) + + alteredView, err = client.MaterializedViews.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, "", alteredView.Comment) + + alterRequest = sdk.NewAlterMaterializedViewRequest(id).WithUnset( + sdk.NewMaterializedViewUnsetRequest().WithSecure(sdk.Bool(true)), + ) + err = client.MaterializedViews.Alter(ctx, alterRequest) + require.NoError(t, err) + + alteredView, err = client.MaterializedViews.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, false, alteredView.IsSecure) + }) + + t.Run("show materialized view: default", func(t *testing.T) { + view1 := createMaterializedView(t) + view2 := createMaterializedView(t) + + showRequest := sdk.NewShowMaterializedViewRequest() + returnedViews, err := client.MaterializedViews.Show(ctx, showRequest) + require.NoError(t, err) + + assert.Equal(t, 2, len(returnedViews)) + assert.Contains(t, returnedViews, *view1) + assert.Contains(t, returnedViews, *view2) + }) + + t.Run("show materialized view: with options", func(t *testing.T) { + view1 := createMaterializedView(t) + view2 := createMaterializedView(t) + + showRequest := sdk.NewShowMaterializedViewRequest(). + WithLike(&sdk.Like{Pattern: &view1.Name}). + WithIn(&sdk.In{Schema: sdk.NewDatabaseObjectIdentifier(testDb(t).Name, testSchema(t).Name)}) + returnedViews, err := client.MaterializedViews.Show(ctx, showRequest) + + require.NoError(t, err) + assert.Equal(t, 1, len(returnedViews)) + assert.Contains(t, returnedViews, *view1) + assert.NotContains(t, returnedViews, *view2) + }) + + t.Run("describe materialized view", func(t *testing.T) { + view := createMaterializedView(t) + + returnedViewDetails, err := client.MaterializedViews.Describe(ctx, view.ID()) + require.NoError(t, err) + + assert.Equal(t, 1, len(returnedViewDetails)) + assertViewDetailsRow(t, &returnedViewDetails[0]) + }) + + t.Run("describe materialized view: non-existing", func(t *testing.T) { + id := sdk.NewSchemaObjectIdentifier(testDb(t).Name, testSchema(t).Name, "does_not_exist") + + _, err := client.MaterializedViews.Describe(ctx, id) + assert.ErrorIs(t, err, sdk.ErrObjectNotExistOrAuthorized) + }) +} diff --git a/pkg/sdk/testint/views_gen_integration_test.go b/pkg/sdk/testint/views_gen_integration_test.go index 39e250facf..c7c7d441dd 100644 --- a/pkg/sdk/testint/views_gen_integration_test.go +++ b/pkg/sdk/testint/views_gen_integration_test.go @@ -11,8 +11,8 @@ import ( "github.com/stretchr/testify/require" ) -// TODO: add tests for setting masking policy on creation -// TODO: add tests for setting recursive on creation +// TODO [SNOW-1016430]: add tests for setting masking policy on creation +// TODO [SNOW-1016430]: add tests for setting recursive on creation func TestInt_Views(t *testing.T) { client := testClient(t) ctx := testContext(t)