From 66cc80a8cac24f4b7967986032b7da9e20bd4eab Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Thu, 7 Dec 2023 23:24:36 -0800 Subject: [PATCH] feat: add event tables to sdk (#2215) * add event tables to sdk * fix event tables * update event tables * update * update URL --- pkg/sdk/client.go | 2 + pkg/sdk/event_tables_def.go | 166 +++++++++ pkg/sdk/event_tables_dto_builders_gen.go | 314 +++++++++++++++++ pkg/sdk/event_tables_dto_gen.go | 104 ++++++ pkg/sdk/event_tables_gen.go | 159 +++++++++ pkg/sdk/event_tables_gen_test.go | 323 ++++++++++++++++++ pkg/sdk/event_tables_impl_gen.go | 202 +++++++++++ pkg/sdk/event_tables_validations_gen.go | 89 +++++ pkg/sdk/poc/main.go | 1 + .../testint/event_tables_integration_test.go | 277 +++++++++++++++ 10 files changed, 1637 insertions(+) create mode 100644 pkg/sdk/event_tables_def.go create mode 100644 pkg/sdk/event_tables_dto_builders_gen.go create mode 100644 pkg/sdk/event_tables_dto_gen.go create mode 100644 pkg/sdk/event_tables_gen.go create mode 100644 pkg/sdk/event_tables_gen_test.go create mode 100644 pkg/sdk/event_tables_impl_gen.go create mode 100644 pkg/sdk/event_tables_validations_gen.go create mode 100644 pkg/sdk/testint/event_tables_integration_test.go diff --git a/pkg/sdk/client.go b/pkg/sdk/client.go index ac448aabc4..94c0636287 100644 --- a/pkg/sdk/client.go +++ b/pkg/sdk/client.go @@ -36,6 +36,7 @@ type Client struct { Databases Databases DynamicTables DynamicTables ExternalTables ExternalTables + EventTables EventTables FailoverGroups FailoverGroups FileFormats FileFormats Grants Grants @@ -163,6 +164,7 @@ func (c *Client) initialize() { c.Databases = &databases{client: c} c.DynamicTables = &dynamicTables{client: c} c.ExternalTables = &externalTables{client: c} + c.EventTables = &eventTables{client: c} c.FailoverGroups = &failoverGroups{client: c} c.FileFormats = &fileFormats{client: c} c.Grants = &grants{client: c} diff --git a/pkg/sdk/event_tables_def.go b/pkg/sdk/event_tables_def.go new file mode 100644 index 0000000000..6345c411b5 --- /dev/null +++ b/pkg/sdk/event_tables_def.go @@ -0,0 +1,166 @@ +package sdk + +import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/generator" + +//go:generate go run ./poc/main.go + +var eventTableSet = g.NewQueryStruct("EventTableSet"). + OptionalNumberAssignment("DATA_RETENTION_TIME_IN_DAYS", g.ParameterOptions()). + OptionalNumberAssignment("MAX_DATA_EXTENSION_TIME_IN_DAYS", g.ParameterOptions()). + OptionalBooleanAssignment("CHANGE_TRACKING", g.ParameterOptions()). + OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()) + +var eventTableUnset = g.NewQueryStruct("EventTableUnset"). + OptionalSQL("DATA_RETENTION_TIME_IN_DAYS"). + OptionalSQL("MAX_DATA_EXTENSION_TIME_IN_DAYS"). + OptionalSQL("CHANGE_TRACKING"). + OptionalSQL("COMMENT") + +var eventTableAddRowAccessPolicy = g.NewQueryStruct("EventTableAddRowAccessPolicy"). + SQL("ADD"). + 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") + +var eventTableDropRowAccessPolicy = g.NewQueryStruct("EventTableDropRowAccessPolicy"). + SQL("DROP"). + Identifier("RowAccessPolicy", g.KindOfT[SchemaObjectIdentifier](), g.IdentifierOptions().SQL("ROW ACCESS POLICY").Required()). + WithValidation(g.ValidIdentifier, "RowAccessPolicy") + +var eventTableDropAndAddRowAccessPolicy = g.NewQueryStruct("EventTableDropAndAddRowAccessPolicy"). + QueryStructField("Drop", eventTableDropRowAccessPolicy, g.KeywordOptions().Required()). + QueryStructField("Add", eventTableAddRowAccessPolicy, g.KeywordOptions().Required()) + +var eventTableClusteringAction = g.NewQueryStruct("EventTableClusteringAction"). + PredefinedQueryStructField("ClusterBy", "*[]string", g.KeywordOptions().Parentheses().SQL("CLUSTER BY")). + OptionalSQL("SUSPEND RECLUSTER"). + OptionalSQL("RESUME RECLUSTER"). + OptionalSQL("DROP CLUSTERING KEY") + +var searchOptimization = g.NewQueryStruct("SearchOptimization"). + SQL("SEARCH OPTIMIZATION"). + PredefinedQueryStructField("On", "[]string", g.KeywordOptions().SQL("ON")) + +var eventTableSearchOptimizationAction = g.NewQueryStruct("EventTableSearchOptimizationAction"). + OptionalQueryStructField( + "Add", + searchOptimization, + g.KeywordOptions().SQL("ADD"), + ). + OptionalQueryStructField( + "Drop", + searchOptimization, + g.KeywordOptions().SQL("DROP"), + ) + +var EventTablesDef = g.NewInterface( + "EventTables", + "EventTable", + g.KindOfT[SchemaObjectIdentifier](), +).CreateOperation( + "https://docs.snowflake.com/en/sql-reference/sql/create-event-table", + g.NewQueryStruct("CreateEventTable"). + Create(). + OrReplace(). + SQL("EVENT TABLE"). + IfNotExists(). + Name(). + NamedListWithParens("CLUSTER BY", g.KindOfT[string](), g.KeywordOptions()). + OptionalNumberAssignment("DATA_RETENTION_TIME_IN_DAYS", g.ParameterOptions()). + OptionalNumberAssignment("MAX_DATA_EXTENSION_TIME_IN_DAYS", g.ParameterOptions()). + OptionalBooleanAssignment("CHANGE_TRACKING", g.ParameterOptions()). + OptionalTextAssignment("DEFAULT_DDL_COLLATION", g.ParameterOptions().SingleQuotes()). + OptionalSQL("COPY GRANTS"). + OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). + PredefinedQueryStructField("RowAccessPolicy", "*RowAccessPolicy", g.KeywordOptions()). + OptionalTags(). + WithValidation(g.ValidIdentifier, "name"). + WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), +).ShowOperation( + "https://docs.snowflake.com/en/sql-reference/sql/show-event-tables", + g.DbStruct("eventTableRow"). + Field("created_on", "time.Time"). + Field("name", "string"). + Field("database_name", "string"). + Field("schema_name", "string"). + Field("owner", "sql.NullString"). + Field("comment", "sql.NullString"). + Field("owner_role_type", "sql.NullString"), + g.PlainStruct("EventTable"). + Field("CreatedOn", "time.Time"). + Field("Name", "string"). + Field("DatabaseName", "string"). + Field("SchemaName", "string"). + Field("Owner", "string"). + Field("Comment", "string"). + Field("OwnerRoleType", "string"), + g.NewQueryStruct("ShowEventTables"). + Show(). + Terse(). + SQL("EVENT TABLES"). + OptionalLike(). + OptionalIn(). + OptionalStartsWith(). + OptionalLimit(), +).ShowByIdOperation().DescribeOperation( + g.DescriptionMappingKindSingleValue, + "https://docs.snowflake.com/en/sql-reference/sql/desc-event-table", + g.DbStruct("eventTableDetailsRow"). + Field("name", "string"). + Field("kind", "string"). + Field("comment", "string"), + g.PlainStruct("EventTableDetails"). + Field("Name", "string"). + Field("Kind", "string"). + Field("Comment", "string"), + g.NewQueryStruct("DescribeEventTable"). + Describe(). + SQL("EVENT TABLE"). + Name(). + WithValidation(g.ValidIdentifier, "name"), +).DropOperation( + "https://docs.snowflake.com/en/sql-reference/sql/drop-table", + g.NewQueryStruct("DropEventTable"). + Drop(). + SQL("TABLE"). + IfExists(). + Name(). + OptionalSQL("RESTRICT"). // CASCADE or RESTRICT, and CASCADE for Default + WithValidation(g.ValidIdentifier, "name"), +).AlterOperation( + "https://docs.snowflake.com/en/sql-reference/sql/alter-table-event-table", + g.NewQueryStruct("AlterEventTable"). + Alter(). + SQL("TABLE"). + IfNotExists(). + Name(). + OptionalQueryStructField( + "Set", + eventTableSet, + g.KeywordOptions().SQL("SET"), + ). + OptionalQueryStructField( + "Unset", + eventTableUnset, + g.KeywordOptions().SQL("UNSET"), + ). + OptionalQueryStructField("AddRowAccessPolicy", eventTableAddRowAccessPolicy, g.KeywordOptions()). + OptionalQueryStructField("DropRowAccessPolicy", eventTableDropRowAccessPolicy, g.KeywordOptions()). + OptionalQueryStructField("DropAndAddRowAccessPolicy", eventTableDropAndAddRowAccessPolicy, g.ListOptions().NoParentheses()). + OptionalSQL("DROP ALL ROW ACCESS POLICIES"). + OptionalQueryStructField( + "ClusteringAction", + eventTableClusteringAction, + g.KeywordOptions(), + ). + OptionalQueryStructField( + "SearchOptimizationAction", + eventTableSearchOptimizationAction, + g.KeywordOptions(), + ). + OptionalSetTags(). + OptionalUnsetTags(). + Identifier("RenameTo", g.KindOfTPointer[SchemaObjectIdentifier](), g.IdentifierOptions().SQL("RENAME TO")). + WithValidation(g.ValidIdentifier, "name"). + WithValidation(g.ExactlyOneValueSet, "RenameTo", "Set", "Unset", "SetTags", "UnsetTags", "AddRowAccessPolicy", "DropRowAccessPolicy", "DropAndAddRowAccessPolicy", "DropAllRowAccessPolicies", "ClusteringAction", "SearchOptimizationAction"), +) diff --git a/pkg/sdk/event_tables_dto_builders_gen.go b/pkg/sdk/event_tables_dto_builders_gen.go new file mode 100644 index 0000000000..9d25168bbc --- /dev/null +++ b/pkg/sdk/event_tables_dto_builders_gen.go @@ -0,0 +1,314 @@ +// Code generated by dto builder generator; DO NOT EDIT. + +package sdk + +import () + +func NewCreateEventTableRequest( + name SchemaObjectIdentifier, +) *CreateEventTableRequest { + s := CreateEventTableRequest{} + s.name = name + return &s +} + +func (s *CreateEventTableRequest) WithOrReplace(OrReplace *bool) *CreateEventTableRequest { + s.OrReplace = OrReplace + return s +} + +func (s *CreateEventTableRequest) WithIfNotExists(IfNotExists *bool) *CreateEventTableRequest { + s.IfNotExists = IfNotExists + return s +} + +func (s *CreateEventTableRequest) WithClusterBy(ClusterBy []string) *CreateEventTableRequest { + s.ClusterBy = ClusterBy + return s +} + +func (s *CreateEventTableRequest) WithDataRetentionTimeInDays(DataRetentionTimeInDays *int) *CreateEventTableRequest { + s.DataRetentionTimeInDays = DataRetentionTimeInDays + return s +} + +func (s *CreateEventTableRequest) WithMaxDataExtensionTimeInDays(MaxDataExtensionTimeInDays *int) *CreateEventTableRequest { + s.MaxDataExtensionTimeInDays = MaxDataExtensionTimeInDays + return s +} + +func (s *CreateEventTableRequest) WithChangeTracking(ChangeTracking *bool) *CreateEventTableRequest { + s.ChangeTracking = ChangeTracking + return s +} + +func (s *CreateEventTableRequest) WithDefaultDdlCollation(DefaultDdlCollation *string) *CreateEventTableRequest { + s.DefaultDdlCollation = DefaultDdlCollation + return s +} + +func (s *CreateEventTableRequest) WithCopyGrants(CopyGrants *bool) *CreateEventTableRequest { + s.CopyGrants = CopyGrants + return s +} + +func (s *CreateEventTableRequest) WithComment(Comment *string) *CreateEventTableRequest { + s.Comment = Comment + return s +} + +func (s *CreateEventTableRequest) WithRowAccessPolicy(RowAccessPolicy *RowAccessPolicy) *CreateEventTableRequest { + s.RowAccessPolicy = RowAccessPolicy + return s +} + +func (s *CreateEventTableRequest) WithTag(Tag []TagAssociation) *CreateEventTableRequest { + s.Tag = Tag + return s +} + +func NewShowEventTableRequest() *ShowEventTableRequest { + return &ShowEventTableRequest{} +} + +func (s *ShowEventTableRequest) WithTerse(Terse *bool) *ShowEventTableRequest { + s.Terse = Terse + return s +} + +func (s *ShowEventTableRequest) WithLike(Like *Like) *ShowEventTableRequest { + s.Like = Like + return s +} + +func (s *ShowEventTableRequest) WithIn(In *In) *ShowEventTableRequest { + s.In = In + return s +} + +func (s *ShowEventTableRequest) WithStartsWith(StartsWith *string) *ShowEventTableRequest { + s.StartsWith = StartsWith + return s +} + +func (s *ShowEventTableRequest) WithLimit(Limit *LimitFrom) *ShowEventTableRequest { + s.Limit = Limit + return s +} + +func NewDescribeEventTableRequest( + name SchemaObjectIdentifier, +) *DescribeEventTableRequest { + s := DescribeEventTableRequest{} + s.name = name + return &s +} + +func NewDropEventTableRequest( + name SchemaObjectIdentifier, +) *DropEventTableRequest { + s := DropEventTableRequest{} + s.name = name + return &s +} + +func (s *DropEventTableRequest) WithIfExists(IfExists *bool) *DropEventTableRequest { + s.IfExists = IfExists + return s +} + +func (s *DropEventTableRequest) WithRestrict(Restrict *bool) *DropEventTableRequest { + s.Restrict = Restrict + return s +} + +func NewAlterEventTableRequest( + name SchemaObjectIdentifier, +) *AlterEventTableRequest { + s := AlterEventTableRequest{} + s.name = name + return &s +} + +func (s *AlterEventTableRequest) WithIfNotExists(IfNotExists *bool) *AlterEventTableRequest { + s.IfNotExists = IfNotExists + return s +} + +func (s *AlterEventTableRequest) WithSet(Set *EventTableSetRequest) *AlterEventTableRequest { + s.Set = Set + return s +} + +func (s *AlterEventTableRequest) WithUnset(Unset *EventTableUnsetRequest) *AlterEventTableRequest { + s.Unset = Unset + return s +} + +func (s *AlterEventTableRequest) WithAddRowAccessPolicy(AddRowAccessPolicy *EventTableAddRowAccessPolicyRequest) *AlterEventTableRequest { + s.AddRowAccessPolicy = AddRowAccessPolicy + return s +} + +func (s *AlterEventTableRequest) WithDropRowAccessPolicy(DropRowAccessPolicy *EventTableDropRowAccessPolicyRequest) *AlterEventTableRequest { + s.DropRowAccessPolicy = DropRowAccessPolicy + return s +} + +func (s *AlterEventTableRequest) WithDropAndAddRowAccessPolicy(DropAndAddRowAccessPolicy *EventTableDropAndAddRowAccessPolicyRequest) *AlterEventTableRequest { + s.DropAndAddRowAccessPolicy = DropAndAddRowAccessPolicy + return s +} + +func (s *AlterEventTableRequest) WithDropAllRowAccessPolicies(DropAllRowAccessPolicies *bool) *AlterEventTableRequest { + s.DropAllRowAccessPolicies = DropAllRowAccessPolicies + return s +} + +func (s *AlterEventTableRequest) WithClusteringAction(ClusteringAction *EventTableClusteringActionRequest) *AlterEventTableRequest { + s.ClusteringAction = ClusteringAction + return s +} + +func (s *AlterEventTableRequest) WithSearchOptimizationAction(SearchOptimizationAction *EventTableSearchOptimizationActionRequest) *AlterEventTableRequest { + s.SearchOptimizationAction = SearchOptimizationAction + return s +} + +func (s *AlterEventTableRequest) WithSetTags(SetTags []TagAssociation) *AlterEventTableRequest { + s.SetTags = SetTags + return s +} + +func (s *AlterEventTableRequest) WithUnsetTags(UnsetTags []ObjectIdentifier) *AlterEventTableRequest { + s.UnsetTags = UnsetTags + return s +} + +func (s *AlterEventTableRequest) WithRenameTo(RenameTo *SchemaObjectIdentifier) *AlterEventTableRequest { + s.RenameTo = RenameTo + return s +} + +func NewEventTableSetRequest() *EventTableSetRequest { + return &EventTableSetRequest{} +} + +func (s *EventTableSetRequest) WithDataRetentionTimeInDays(DataRetentionTimeInDays *int) *EventTableSetRequest { + s.DataRetentionTimeInDays = DataRetentionTimeInDays + return s +} + +func (s *EventTableSetRequest) WithMaxDataExtensionTimeInDays(MaxDataExtensionTimeInDays *int) *EventTableSetRequest { + s.MaxDataExtensionTimeInDays = MaxDataExtensionTimeInDays + return s +} + +func (s *EventTableSetRequest) WithChangeTracking(ChangeTracking *bool) *EventTableSetRequest { + s.ChangeTracking = ChangeTracking + return s +} + +func (s *EventTableSetRequest) WithComment(Comment *string) *EventTableSetRequest { + s.Comment = Comment + return s +} + +func NewEventTableUnsetRequest() *EventTableUnsetRequest { + return &EventTableUnsetRequest{} +} + +func (s *EventTableUnsetRequest) WithDataRetentionTimeInDays(DataRetentionTimeInDays *bool) *EventTableUnsetRequest { + s.DataRetentionTimeInDays = DataRetentionTimeInDays + return s +} + +func (s *EventTableUnsetRequest) WithMaxDataExtensionTimeInDays(MaxDataExtensionTimeInDays *bool) *EventTableUnsetRequest { + s.MaxDataExtensionTimeInDays = MaxDataExtensionTimeInDays + return s +} + +func (s *EventTableUnsetRequest) WithChangeTracking(ChangeTracking *bool) *EventTableUnsetRequest { + s.ChangeTracking = ChangeTracking + return s +} + +func (s *EventTableUnsetRequest) WithComment(Comment *bool) *EventTableUnsetRequest { + s.Comment = Comment + return s +} + +func NewEventTableAddRowAccessPolicyRequest( + RowAccessPolicy SchemaObjectIdentifier, + On []string, +) *EventTableAddRowAccessPolicyRequest { + s := EventTableAddRowAccessPolicyRequest{} + s.RowAccessPolicy = RowAccessPolicy + s.On = On + return &s +} + +func NewEventTableDropRowAccessPolicyRequest( + RowAccessPolicy SchemaObjectIdentifier, +) *EventTableDropRowAccessPolicyRequest { + s := EventTableDropRowAccessPolicyRequest{} + s.RowAccessPolicy = RowAccessPolicy + return &s +} + +func NewEventTableDropAndAddRowAccessPolicyRequest( + Drop EventTableDropRowAccessPolicyRequest, + Add EventTableAddRowAccessPolicyRequest, +) *EventTableDropAndAddRowAccessPolicyRequest { + s := EventTableDropAndAddRowAccessPolicyRequest{} + s.Drop = Drop + s.Add = Add + return &s +} + +func NewEventTableClusteringActionRequest() *EventTableClusteringActionRequest { + return &EventTableClusteringActionRequest{} +} + +func (s *EventTableClusteringActionRequest) WithClusterBy(ClusterBy *[]string) *EventTableClusteringActionRequest { + s.ClusterBy = ClusterBy + return s +} + +func (s *EventTableClusteringActionRequest) WithSuspendRecluster(SuspendRecluster *bool) *EventTableClusteringActionRequest { + s.SuspendRecluster = SuspendRecluster + return s +} + +func (s *EventTableClusteringActionRequest) WithResumeRecluster(ResumeRecluster *bool) *EventTableClusteringActionRequest { + s.ResumeRecluster = ResumeRecluster + return s +} + +func (s *EventTableClusteringActionRequest) WithDropClusteringKey(DropClusteringKey *bool) *EventTableClusteringActionRequest { + s.DropClusteringKey = DropClusteringKey + return s +} + +func NewEventTableSearchOptimizationActionRequest() *EventTableSearchOptimizationActionRequest { + return &EventTableSearchOptimizationActionRequest{} +} + +func (s *EventTableSearchOptimizationActionRequest) WithAdd(Add *SearchOptimizationRequest) *EventTableSearchOptimizationActionRequest { + s.Add = Add + return s +} + +func (s *EventTableSearchOptimizationActionRequest) WithDrop(Drop *SearchOptimizationRequest) *EventTableSearchOptimizationActionRequest { + s.Drop = Drop + return s +} + +func NewSearchOptimizationRequest() *SearchOptimizationRequest { + return &SearchOptimizationRequest{} +} + +func (s *SearchOptimizationRequest) WithOn(On []string) *SearchOptimizationRequest { + s.On = On + return s +} diff --git a/pkg/sdk/event_tables_dto_gen.go b/pkg/sdk/event_tables_dto_gen.go new file mode 100644 index 0000000000..c043c86b21 --- /dev/null +++ b/pkg/sdk/event_tables_dto_gen.go @@ -0,0 +1,104 @@ +package sdk + +//go:generate go run ./dto-builder-generator/main.go + +var ( + _ optionsProvider[CreateEventTableOptions] = new(CreateEventTableRequest) + _ optionsProvider[ShowEventTableOptions] = new(ShowEventTableRequest) + _ optionsProvider[DescribeEventTableOptions] = new(DescribeEventTableRequest) + _ optionsProvider[DropEventTableOptions] = new(DropEventTableRequest) + _ optionsProvider[AlterEventTableOptions] = new(AlterEventTableRequest) +) + +type CreateEventTableRequest struct { + OrReplace *bool + IfNotExists *bool + name SchemaObjectIdentifier // required + ClusterBy []string + DataRetentionTimeInDays *int + MaxDataExtensionTimeInDays *int + ChangeTracking *bool + DefaultDdlCollation *string + CopyGrants *bool + Comment *string + RowAccessPolicy *RowAccessPolicy + Tag []TagAssociation +} + +type ShowEventTableRequest struct { + Terse *bool + Like *Like + In *In + StartsWith *string + Limit *LimitFrom +} + +type DescribeEventTableRequest struct { + name SchemaObjectIdentifier // required +} + +type DropEventTableRequest struct { + IfExists *bool + name SchemaObjectIdentifier // required + Restrict *bool +} + +type AlterEventTableRequest struct { + IfNotExists *bool + name SchemaObjectIdentifier // required + Set *EventTableSetRequest + Unset *EventTableUnsetRequest + AddRowAccessPolicy *EventTableAddRowAccessPolicyRequest + DropRowAccessPolicy *EventTableDropRowAccessPolicyRequest + DropAndAddRowAccessPolicy *EventTableDropAndAddRowAccessPolicyRequest + DropAllRowAccessPolicies *bool + ClusteringAction *EventTableClusteringActionRequest + SearchOptimizationAction *EventTableSearchOptimizationActionRequest + SetTags []TagAssociation + UnsetTags []ObjectIdentifier + RenameTo *SchemaObjectIdentifier +} + +type EventTableSetRequest struct { + DataRetentionTimeInDays *int + MaxDataExtensionTimeInDays *int + ChangeTracking *bool + Comment *string +} + +type EventTableUnsetRequest struct { + DataRetentionTimeInDays *bool + MaxDataExtensionTimeInDays *bool + ChangeTracking *bool + Comment *bool +} + +type EventTableAddRowAccessPolicyRequest struct { + RowAccessPolicy SchemaObjectIdentifier // required + On []string // required +} + +type EventTableDropRowAccessPolicyRequest struct { + RowAccessPolicy SchemaObjectIdentifier // required +} + +type EventTableDropAndAddRowAccessPolicyRequest struct { + Drop EventTableDropRowAccessPolicyRequest // required + Add EventTableAddRowAccessPolicyRequest // required +} + +type EventTableClusteringActionRequest struct { + ClusterBy *[]string + SuspendRecluster *bool + ResumeRecluster *bool + DropClusteringKey *bool +} + +type EventTableSearchOptimizationActionRequest struct { + Add *SearchOptimizationRequest + Drop *SearchOptimizationRequest +} + +type SearchOptimizationRequest struct { + On []string +} diff --git a/pkg/sdk/event_tables_gen.go b/pkg/sdk/event_tables_gen.go new file mode 100644 index 0000000000..b4e641a5a9 --- /dev/null +++ b/pkg/sdk/event_tables_gen.go @@ -0,0 +1,159 @@ +package sdk + +import ( + "context" + "database/sql" + "time" +) + +type EventTables interface { + Create(ctx context.Context, request *CreateEventTableRequest) error + Show(ctx context.Context, request *ShowEventTableRequest) ([]EventTable, error) + ShowByID(ctx context.Context, id SchemaObjectIdentifier) (*EventTable, error) + Describe(ctx context.Context, id SchemaObjectIdentifier) (*EventTableDetails, error) + Drop(ctx context.Context, request *DropEventTableRequest) error + Alter(ctx context.Context, request *AlterEventTableRequest) error +} + +// CreateEventTableOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-event-table. +type CreateEventTableOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + eventTable bool `ddl:"static" sql:"EVENT TABLE"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + ClusterBy []string `ddl:"keyword,parentheses" sql:"CLUSTER BY"` + DataRetentionTimeInDays *int `ddl:"parameter" sql:"DATA_RETENTION_TIME_IN_DAYS"` + MaxDataExtensionTimeInDays *int `ddl:"parameter" sql:"MAX_DATA_EXTENSION_TIME_IN_DAYS"` + ChangeTracking *bool `ddl:"parameter" sql:"CHANGE_TRACKING"` + DefaultDdlCollation *string `ddl:"parameter,single_quotes" sql:"DEFAULT_DDL_COLLATION"` + CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` + RowAccessPolicy *RowAccessPolicy `ddl:"keyword"` + Tag []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` +} + +// ShowEventTableOptions is based on https://docs.snowflake.com/en/sql-reference/sql/show-event-tables. +type ShowEventTableOptions struct { + show bool `ddl:"static" sql:"SHOW"` + Terse *bool `ddl:"keyword" sql:"TERSE"` + eventTables bool `ddl:"static" sql:"EVENT TABLES"` + Like *Like `ddl:"keyword" sql:"LIKE"` + In *In `ddl:"keyword" sql:"IN"` + StartsWith *string `ddl:"parameter,single_quotes,no_equals" sql:"STARTS WITH"` + Limit *LimitFrom `ddl:"keyword" sql:"LIMIT"` +} + +type eventTableRow struct { + CreatedOn time.Time `db:"created_on"` + Name string `db:"name"` + DatabaseName string `db:"database_name"` + SchemaName string `db:"schema_name"` + Owner sql.NullString `db:"owner"` + Comment sql.NullString `db:"comment"` + OwnerRoleType sql.NullString `db:"owner_role_type"` +} + +type EventTable struct { + CreatedOn time.Time + Name string + DatabaseName string + SchemaName string + Owner string + Comment string + OwnerRoleType string +} + +// DescribeEventTableOptions is based on https://docs.snowflake.com/en/sql-reference/sql/desc-event-table. +type DescribeEventTableOptions struct { + describe bool `ddl:"static" sql:"DESCRIBE"` + eventTable bool `ddl:"static" sql:"EVENT TABLE"` + name SchemaObjectIdentifier `ddl:"identifier"` +} + +type eventTableDetailsRow struct { + Name string `db:"name"` + Kind string `db:"kind"` + Comment string `db:"comment"` +} + +type EventTableDetails struct { + Name string + Kind string + Comment string +} + +// DropEventTableOptions is based on https://docs.snowflake.com/en/sql-reference/sql/drop-table. +type DropEventTableOptions struct { + drop bool `ddl:"static" sql:"DROP"` + table bool `ddl:"static" sql:"TABLE"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + Restrict *bool `ddl:"keyword" sql:"RESTRICT"` +} + +// AlterEventTableOptions is based on https://docs.snowflake.com/en/sql-reference/sql/alter-table-event-table. +type AlterEventTableOptions struct { + alter bool `ddl:"static" sql:"ALTER"` + table bool `ddl:"static" sql:"TABLE"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + Set *EventTableSet `ddl:"keyword" sql:"SET"` + Unset *EventTableUnset `ddl:"keyword" sql:"UNSET"` + AddRowAccessPolicy *EventTableAddRowAccessPolicy `ddl:"keyword"` + DropRowAccessPolicy *EventTableDropRowAccessPolicy `ddl:"keyword"` + DropAndAddRowAccessPolicy *EventTableDropAndAddRowAccessPolicy `ddl:"list,no_parentheses"` + DropAllRowAccessPolicies *bool `ddl:"keyword" sql:"DROP ALL ROW ACCESS POLICIES"` + ClusteringAction *EventTableClusteringAction `ddl:"keyword"` + SearchOptimizationAction *EventTableSearchOptimizationAction `ddl:"keyword"` + SetTags []TagAssociation `ddl:"keyword" sql:"SET TAG"` + UnsetTags []ObjectIdentifier `ddl:"keyword" sql:"UNSET TAG"` + RenameTo *SchemaObjectIdentifier `ddl:"identifier" sql:"RENAME TO"` +} + +type EventTableSet struct { + DataRetentionTimeInDays *int `ddl:"parameter" sql:"DATA_RETENTION_TIME_IN_DAYS"` + MaxDataExtensionTimeInDays *int `ddl:"parameter" sql:"MAX_DATA_EXTENSION_TIME_IN_DAYS"` + ChangeTracking *bool `ddl:"parameter" sql:"CHANGE_TRACKING"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} + +type EventTableUnset struct { + DataRetentionTimeInDays *bool `ddl:"keyword" sql:"DATA_RETENTION_TIME_IN_DAYS"` + MaxDataExtensionTimeInDays *bool `ddl:"keyword" sql:"MAX_DATA_EXTENSION_TIME_IN_DAYS"` + ChangeTracking *bool `ddl:"keyword" sql:"CHANGE_TRACKING"` + Comment *bool `ddl:"keyword" sql:"COMMENT"` +} + +type EventTableAddRowAccessPolicy struct { + add bool `ddl:"static" sql:"ADD"` + RowAccessPolicy SchemaObjectIdentifier `ddl:"identifier" sql:"ROW ACCESS POLICY"` + On []string `ddl:"keyword,parentheses" sql:"ON"` +} + +type EventTableDropRowAccessPolicy struct { + drop bool `ddl:"static" sql:"DROP"` + RowAccessPolicy SchemaObjectIdentifier `ddl:"identifier" sql:"ROW ACCESS POLICY"` +} + +type EventTableDropAndAddRowAccessPolicy struct { + Drop EventTableDropRowAccessPolicy `ddl:"keyword"` + Add EventTableAddRowAccessPolicy `ddl:"keyword"` +} + +type EventTableClusteringAction struct { + ClusterBy *[]string `ddl:"keyword,parentheses" sql:"CLUSTER BY"` + SuspendRecluster *bool `ddl:"keyword" sql:"SUSPEND RECLUSTER"` + ResumeRecluster *bool `ddl:"keyword" sql:"RESUME RECLUSTER"` + DropClusteringKey *bool `ddl:"keyword" sql:"DROP CLUSTERING KEY"` +} + +type EventTableSearchOptimizationAction struct { + Add *SearchOptimization `ddl:"keyword" sql:"ADD"` + Drop *SearchOptimization `ddl:"keyword" sql:"DROP"` +} + +type SearchOptimization struct { + searchOptimization bool `ddl:"static" sql:"SEARCH OPTIMIZATION"` + On []string `ddl:"keyword" sql:"ON"` +} diff --git a/pkg/sdk/event_tables_gen_test.go b/pkg/sdk/event_tables_gen_test.go new file mode 100644 index 0000000000..006cb2cd98 --- /dev/null +++ b/pkg/sdk/event_tables_gen_test.go @@ -0,0 +1,323 @@ +package sdk + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/random" +) + +func TestEventTables_Create(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + defaultOpts := func() *CreateEventTableOptions { + return &CreateEventTableOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *CreateEventTableOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + opts.OrReplace = Bool(true) + opts.ClusterBy = []string{"a", "b"} + opts.DataRetentionTimeInDays = Int(1) + opts.MaxDataExtensionTimeInDays = Int(2) + opts.ChangeTracking = Bool(true) + opts.DefaultDdlCollation = String("en_US") + opts.CopyGrants = Bool(true) + opts.Comment = String("test") + pn := NewSchemaObjectIdentifier(random.StringN(4), random.StringN(4), random.StringN(4)) + opts.RowAccessPolicy = &RowAccessPolicy{ + Name: pn, + On: []string{"c1", "c2"}, + } + tn := NewSchemaObjectIdentifier(random.StringN(4), random.StringN(4), random.StringN(4)) + opts.Tag = []TagAssociation{ + { + Name: tn, + Value: "v1", + }, + } + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE EVENT TABLE %s CLUSTER BY (a, b) DATA_RETENTION_TIME_IN_DAYS = 1 MAX_DATA_EXTENSION_TIME_IN_DAYS = 2 CHANGE_TRACKING = true DEFAULT_DDL_COLLATION = 'en_US' COPY GRANTS COMMENT = 'test' ROW ACCESS POLICY %s ON (c1, c2) TAG (%s = 'v1')`, id.FullyQualifiedName(), pn.FullyQualifiedName(), tn.FullyQualifiedName()) + }) +} + +func TestEventTables_Show(t *testing.T) { + id := RandomSchemaObjectIdentifier() + defaultOpts := func() *ShowEventTableOptions { + return &ShowEventTableOptions{} + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *ShowEventTableOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("show with in", func(t *testing.T) { + opts := defaultOpts() + opts.In = &In{ + Database: NewAccountObjectIdentifier("database"), + } + assertOptsValidAndSQLEquals(t, opts, `SHOW EVENT TABLES IN DATABASE "database"`) + }) + + t.Run("show with like", func(t *testing.T) { + opts := defaultOpts() + opts.Like = &Like{ + Pattern: String(id.Name()), + } + assertOptsValidAndSQLEquals(t, opts, `SHOW EVENT TABLES LIKE '%s'`, id.Name()) + }) + + t.Run("show with like and in", func(t *testing.T) { + opts := defaultOpts() + opts.Terse = Bool(true) + opts.Like = &Like{ + Pattern: String(id.Name()), + } + opts.In = &In{ + Database: NewAccountObjectIdentifier("database"), + } + assertOptsValidAndSQLEquals(t, opts, `SHOW TERSE EVENT TABLES LIKE '%s' IN DATABASE "database"`, id.Name()) + }) +} + +func TestEventTables_Describe(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + defaultOpts := func() *DescribeEventTableOptions { + return &DescribeEventTableOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + opts := (*DescribeEventTableOptions)(nil) + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("all options", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, `DESCRIBE EVENT TABLE %s`, id.FullyQualifiedName()) + }) +} + +func TestEventTables_Alter(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + defaultOpts := func() *AlterEventTableOptions { + return &AlterEventTableOptions{ + name: id, + IfNotExists: Bool(true), + } + } + + t.Run("validation: nil options", func(t *testing.T) { + opts := (*AlterEventTableOptions)(nil) + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("validation: exactly one field should be present", func(t *testing.T) { + opts := defaultOpts() + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterEventTableOptions", "RenameTo", "Set", "Unset", "SetTags", "UnsetTags", "AddRowAccessPolicy", "DropRowAccessPolicy", "DropAndAddRowAccessPolicy", "DropAllRowAccessPolicies", "ClusteringAction", "SearchOptimizationAction")) + }) + + t.Run("validation: exactly one field should be present", func(t *testing.T) { + opts := defaultOpts() + opts.DropAllRowAccessPolicies = Bool(true) + opts.Set = &EventTableSet{ + DataRetentionTimeInDays: Int(1), + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterEventTableOptions", "RenameTo", "Set", "Unset", "SetTags", "UnsetTags", "AddRowAccessPolicy", "DropRowAccessPolicy", "DropAndAddRowAccessPolicy", "DropAllRowAccessPolicies", "ClusteringAction", "SearchOptimizationAction")) + }) + + t.Run("alter: rename to", func(t *testing.T) { + opts := defaultOpts() + target := NewSchemaObjectIdentifier(id.DatabaseName(), id.SchemaName(), random.StringN(12)) + opts.RenameTo = &target + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s RENAME TO %s`, id.FullyQualifiedName(), target.FullyQualifiedName()) + }) + + t.Run("alter: clustering action", func(t *testing.T) { + opts := defaultOpts() + cluster := []string{"a", "b"} + opts.ClusteringAction = &EventTableClusteringAction{ + ClusterBy: &cluster, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s CLUSTER BY (a, b)`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.ClusteringAction = &EventTableClusteringAction{ + SuspendRecluster: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s SUSPEND RECLUSTER`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.ClusteringAction = &EventTableClusteringAction{ + ResumeRecluster: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s RESUME RECLUSTER`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.ClusteringAction = &EventTableClusteringAction{ + DropClusteringKey: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s DROP CLUSTERING KEY`, id.FullyQualifiedName()) + }) + + t.Run("alter: search optimization action", func(t *testing.T) { + opts := defaultOpts() + opts.SearchOptimizationAction = &EventTableSearchOptimizationAction{ + Add: &SearchOptimization{ + On: []string{"EQUALITY(*)", "SUBSTRING(*)"}, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s ADD SEARCH OPTIMIZATION ON EQUALITY(*), SUBSTRING(*)`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.SearchOptimizationAction = &EventTableSearchOptimizationAction{ + Drop: &SearchOptimization{ + On: []string{"EQUALITY(*)", "SUBSTRING(*)"}, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s DROP SEARCH OPTIMIZATION ON EQUALITY(*), SUBSTRING(*)`, id.FullyQualifiedName()) + }) + + t.Run("alter: set", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &EventTableSet{ + DataRetentionTimeInDays: Int(1), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s SET DATA_RETENTION_TIME_IN_DAYS = 1`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.Set = &EventTableSet{ + MaxDataExtensionTimeInDays: Int(1), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s SET MAX_DATA_EXTENSION_TIME_IN_DAYS = 1`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.Set = &EventTableSet{ + ChangeTracking: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s SET CHANGE_TRACKING = true`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.Set = &EventTableSet{ + Comment: String("comment"), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s SET COMMENT = 'comment'`, id.FullyQualifiedName()) + }) + + t.Run("alter: unset", func(t *testing.T) { + opts := defaultOpts() + opts.Unset = &EventTableUnset{ + DataRetentionTimeInDays: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s UNSET DATA_RETENTION_TIME_IN_DAYS`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.Unset = &EventTableUnset{ + MaxDataExtensionTimeInDays: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s UNSET MAX_DATA_EXTENSION_TIME_IN_DAYS`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.Unset = &EventTableUnset{ + ChangeTracking: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s UNSET CHANGE_TRACKING`, id.FullyQualifiedName()) + + opts = defaultOpts() + opts.Unset = &EventTableUnset{ + Comment: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s UNSET COMMENT`, id.FullyQualifiedName()) + }) + + t.Run("alter: set tags", func(t *testing.T) { + opts := defaultOpts() + opts.SetTags = []TagAssociation{ + { + Name: NewAccountObjectIdentifier("tag1"), + Value: "value1", + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s SET TAG "tag1" = 'value1'`, id.FullyQualifiedName()) + }) + + t.Run("alter: unset tags", func(t *testing.T) { + opts := defaultOpts() + opts.UnsetTags = []ObjectIdentifier{ + NewAccountObjectIdentifier("tag1"), + NewAccountObjectIdentifier("tag2"), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s UNSET TAG "tag1", "tag2"`, id.FullyQualifiedName()) + }) + + t.Run("alter: add row access policy", func(t *testing.T) { + rowAccessPolicyId := RandomSchemaObjectIdentifier() + + opts := defaultOpts() + opts.AddRowAccessPolicy = &EventTableAddRowAccessPolicy{ + RowAccessPolicy: rowAccessPolicyId, + On: []string{"a", "b"}, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE IF NOT EXISTS %s ADD ROW ACCESS POLICY %s ON (a, b)", id.FullyQualifiedName(), rowAccessPolicyId.FullyQualifiedName()) + }) + + t.Run("alter: drop row access policy", func(t *testing.T) { + rowAccessPolicyId := RandomSchemaObjectIdentifier() + + opts := defaultOpts() + opts.DropRowAccessPolicy = &EventTableDropRowAccessPolicy{ + RowAccessPolicy: rowAccessPolicyId, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE IF NOT EXISTS %s DROP ROW ACCESS POLICY %s", id.FullyQualifiedName(), rowAccessPolicyId.FullyQualifiedName()) + }) + + t.Run("alter: drop and add row access policy", func(t *testing.T) { + rowAccessPolicy1Id := RandomSchemaObjectIdentifier() + rowAccessPolicy2Id := RandomSchemaObjectIdentifier() + + opts := defaultOpts() + opts.DropAndAddRowAccessPolicy = &EventTableDropAndAddRowAccessPolicy{ + Drop: EventTableDropRowAccessPolicy{ + RowAccessPolicy: rowAccessPolicy1Id, + }, + Add: EventTableAddRowAccessPolicy{ + RowAccessPolicy: rowAccessPolicy2Id, + On: []string{"a", "b"}, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE IF NOT EXISTS %s DROP ROW ACCESS POLICY %s, ADD ROW ACCESS POLICY %s ON (a, b)", id.FullyQualifiedName(), rowAccessPolicy1Id.FullyQualifiedName(), rowAccessPolicy2Id.FullyQualifiedName()) + }) + + t.Run("alter: drop all row access policies", func(t *testing.T) { + opts := defaultOpts() + opts.DropAllRowAccessPolicies = Bool(true) + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE IF NOT EXISTS %s DROP ALL ROW ACCESS POLICIES`, id.FullyQualifiedName()) + }) +} diff --git a/pkg/sdk/event_tables_impl_gen.go b/pkg/sdk/event_tables_impl_gen.go new file mode 100644 index 0000000000..8da92bd29c --- /dev/null +++ b/pkg/sdk/event_tables_impl_gen.go @@ -0,0 +1,202 @@ +package sdk + +import ( + "context" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" +) + +var _ EventTables = (*eventTables)(nil) + +type eventTables struct { + client *Client +} + +func (v *eventTables) Create(ctx context.Context, request *CreateEventTableRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *eventTables) Show(ctx context.Context, request *ShowEventTableRequest) ([]EventTable, error) { + opts := request.toOpts() + dbRows, err := validateAndQuery[eventTableRow](v.client, ctx, opts) + if err != nil { + return nil, err + } + resultList := convertRows[eventTableRow, EventTable](dbRows) + return resultList, nil +} + +func (v *eventTables) ShowByID(ctx context.Context, id SchemaObjectIdentifier) (*EventTable, error) { + request := NewShowEventTableRequest().WithIn(&In{Database: NewAccountObjectIdentifier(id.DatabaseName())}).WithLike(&Like{String(id.Name())}) + eventTables, err := v.Show(ctx, request) + if err != nil { + return nil, err + } + return collections.FindOne(eventTables, func(r EventTable) bool { return r.Name == id.Name() }) +} + +func (v *eventTables) Describe(ctx context.Context, id SchemaObjectIdentifier) (*EventTableDetails, error) { + opts := &DescribeEventTableOptions{ + name: id, + } + result, err := validateAndQueryOne[eventTableDetailsRow](v.client, ctx, opts) + if err != nil { + return nil, err + } + return result.convert(), nil +} + +func (v *eventTables) Drop(ctx context.Context, request *DropEventTableRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *eventTables) Alter(ctx context.Context, request *AlterEventTableRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (r *CreateEventTableRequest) toOpts() *CreateEventTableOptions { + opts := &CreateEventTableOptions{ + OrReplace: r.OrReplace, + IfNotExists: r.IfNotExists, + name: r.name, + ClusterBy: r.ClusterBy, + DataRetentionTimeInDays: r.DataRetentionTimeInDays, + MaxDataExtensionTimeInDays: r.MaxDataExtensionTimeInDays, + ChangeTracking: r.ChangeTracking, + DefaultDdlCollation: r.DefaultDdlCollation, + CopyGrants: r.CopyGrants, + Comment: r.Comment, + RowAccessPolicy: r.RowAccessPolicy, + Tag: r.Tag, + } + return opts +} + +func (r *ShowEventTableRequest) toOpts() *ShowEventTableOptions { + opts := &ShowEventTableOptions{ + Terse: r.Terse, + Like: r.Like, + In: r.In, + StartsWith: r.StartsWith, + Limit: r.Limit, + } + return opts +} + +func (r eventTableRow) convert() *EventTable { + t := &EventTable{ + CreatedOn: r.CreatedOn, + Name: r.Name, + DatabaseName: r.DatabaseName, + SchemaName: r.SchemaName, + } + if r.Owner.Valid { + t.Owner = r.Owner.String + } + if r.Comment.Valid { + t.Comment = r.Comment.String + } + if r.OwnerRoleType.Valid { + t.OwnerRoleType = r.OwnerRoleType.String + } + return t +} + +func (r *DescribeEventTableRequest) toOpts() *DescribeEventTableOptions { + opts := &DescribeEventTableOptions{ + name: r.name, + } + return opts +} + +func (r eventTableDetailsRow) convert() *EventTableDetails { + return &EventTableDetails{ + Name: r.Name, + Kind: r.Kind, + Comment: r.Comment, + } +} + +func (r *DropEventTableRequest) toOpts() *DropEventTableOptions { + opts := &DropEventTableOptions{ + IfExists: r.IfExists, + name: r.name, + Restrict: r.Restrict, + } + return opts +} + +func (r *AlterEventTableRequest) toOpts() *AlterEventTableOptions { + opts := &AlterEventTableOptions{ + IfNotExists: r.IfNotExists, + name: r.name, + + DropAllRowAccessPolicies: r.DropAllRowAccessPolicies, + + SetTags: r.SetTags, + UnsetTags: r.UnsetTags, + RenameTo: r.RenameTo, + } + if r.Set != nil { + opts.Set = &EventTableSet{ + DataRetentionTimeInDays: r.Set.DataRetentionTimeInDays, + MaxDataExtensionTimeInDays: r.Set.MaxDataExtensionTimeInDays, + ChangeTracking: r.Set.ChangeTracking, + Comment: r.Set.Comment, + } + } + if r.Unset != nil { + opts.Unset = &EventTableUnset{ + DataRetentionTimeInDays: r.Unset.DataRetentionTimeInDays, + MaxDataExtensionTimeInDays: r.Unset.MaxDataExtensionTimeInDays, + ChangeTracking: r.Unset.ChangeTracking, + Comment: r.Unset.Comment, + } + } + if r.AddRowAccessPolicy != nil { + opts.AddRowAccessPolicy = &EventTableAddRowAccessPolicy{ + RowAccessPolicy: r.AddRowAccessPolicy.RowAccessPolicy, + On: r.AddRowAccessPolicy.On, + } + } + if r.DropRowAccessPolicy != nil { + opts.DropRowAccessPolicy = &EventTableDropRowAccessPolicy{ + RowAccessPolicy: r.DropRowAccessPolicy.RowAccessPolicy, + } + } + if r.DropAndAddRowAccessPolicy != nil { + opts.DropAndAddRowAccessPolicy = &EventTableDropAndAddRowAccessPolicy{} + opts.DropAndAddRowAccessPolicy.Drop = EventTableDropRowAccessPolicy{ + RowAccessPolicy: r.DropAndAddRowAccessPolicy.Drop.RowAccessPolicy, + } + opts.DropAndAddRowAccessPolicy.Add = EventTableAddRowAccessPolicy{ + RowAccessPolicy: r.DropAndAddRowAccessPolicy.Add.RowAccessPolicy, + On: r.DropAndAddRowAccessPolicy.Add.On, + } + } + if r.ClusteringAction != nil { + opts.ClusteringAction = &EventTableClusteringAction{ + ClusterBy: r.ClusteringAction.ClusterBy, + SuspendRecluster: r.ClusteringAction.SuspendRecluster, + ResumeRecluster: r.ClusteringAction.ResumeRecluster, + DropClusteringKey: r.ClusteringAction.DropClusteringKey, + } + } + if r.SearchOptimizationAction != nil { + opts.SearchOptimizationAction = &EventTableSearchOptimizationAction{} + if r.SearchOptimizationAction.Add != nil { + opts.SearchOptimizationAction.Add = &SearchOptimization{ + On: r.SearchOptimizationAction.Add.On, + } + } + if r.SearchOptimizationAction.Drop != nil { + opts.SearchOptimizationAction.Drop = &SearchOptimization{ + On: r.SearchOptimizationAction.Drop.On, + } + } + } + return opts +} diff --git a/pkg/sdk/event_tables_validations_gen.go b/pkg/sdk/event_tables_validations_gen.go new file mode 100644 index 0000000000..1a586565d8 --- /dev/null +++ b/pkg/sdk/event_tables_validations_gen.go @@ -0,0 +1,89 @@ +package sdk + +var ( + _ validatable = new(CreateEventTableOptions) + _ validatable = new(ShowEventTableOptions) + _ validatable = new(DescribeEventTableOptions) + _ validatable = new(DropEventTableOptions) + _ validatable = new(AlterEventTableOptions) +) + +func (opts *CreateEventTableOptions) 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("CreateEventTableOptions", "OrReplace", "IfNotExists")) + } + return JoinErrors(errs...) +} + +func (opts *ShowEventTableOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + return JoinErrors(errs...) +} + +func (opts *DescribeEventTableOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + return JoinErrors(errs...) +} + +func (opts *DropEventTableOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + return JoinErrors(errs...) +} + +func (opts *AlterEventTableOptions) validate() error { + if opts == nil { + return ErrNilOptions + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + if !exactlyOneValueSet(opts.RenameTo, opts.Set, opts.Unset, opts.SetTags, opts.UnsetTags, opts.AddRowAccessPolicy, opts.DropRowAccessPolicy, opts.DropAndAddRowAccessPolicy, opts.DropAllRowAccessPolicies, opts.ClusteringAction, opts.SearchOptimizationAction) { + errs = append(errs, errExactlyOneOf("AlterEventTableOptions", "RenameTo", "Set", "Unset", "SetTags", "UnsetTags", "AddRowAccessPolicy", "DropRowAccessPolicy", "DropAndAddRowAccessPolicy", "DropAllRowAccessPolicies", "ClusteringAction", "SearchOptimizationAction")) + } + if valueSet(opts.AddRowAccessPolicy) { + if !ValidObjectIdentifier(opts.AddRowAccessPolicy.RowAccessPolicy) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + } + if valueSet(opts.DropRowAccessPolicy) { + if !ValidObjectIdentifier(opts.DropRowAccessPolicy.RowAccessPolicy) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + } + if valueSet(opts.DropAndAddRowAccessPolicy) { + if valueSet(opts.DropAndAddRowAccessPolicy.Drop) { + if !ValidObjectIdentifier(opts.DropAndAddRowAccessPolicy.Drop.RowAccessPolicy) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + } + if valueSet(opts.DropAndAddRowAccessPolicy.Add) { + if !ValidObjectIdentifier(opts.DropAndAddRowAccessPolicy.Add.RowAccessPolicy) { + errs = append(errs, ErrInvalidObjectIdentifier) + } + } + } + return JoinErrors(errs...) +} diff --git a/pkg/sdk/poc/main.go b/pkg/sdk/poc/main.go index 817f5a115a..6d0115847e 100644 --- a/pkg/sdk/poc/main.go +++ b/pkg/sdk/poc/main.go @@ -24,6 +24,7 @@ var definitionMapping = map[string]*generator.Interface{ "application_roles_def.go": sdk.ApplicationRolesDef, "views_def.go": sdk.ViewsDef, "stages_def.go": sdk.StagesDef, + "event_tables_def.go": sdk.EventTablesDef, } func main() { diff --git a/pkg/sdk/testint/event_tables_integration_test.go b/pkg/sdk/testint/event_tables_integration_test.go new file mode 100644 index 0000000000..d871d9ec4d --- /dev/null +++ b/pkg/sdk/testint/event_tables_integration_test.go @@ -0,0 +1,277 @@ +package testint + +import ( + "context" + "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" +) + +func TestInt_EventTables(t *testing.T) { + client := testClient(t) + ctx := context.Background() + + databaseTest, schemaTest := testDb(t), testSchema(t) + tagTest, tagCleaup := createTag(t, client, databaseTest, schemaTest) + t.Cleanup(tagCleaup) + + assertEventTableHandle := func(t *testing.T, et *sdk.EventTable, expectedName string, expectedComment string, expectedAllowedValues []string) { + t.Helper() + assert.NotEmpty(t, et.CreatedOn) + assert.Equal(t, expectedName, et.Name) + assert.Equal(t, "ACCOUNTADMIN", et.Owner) + assert.Equal(t, expectedComment, et.Comment) + } + + cleanupTableHandle := func(t *testing.T, id sdk.SchemaObjectIdentifier) func() { + t.Helper() + return func() { + err := client.EventTables.Drop(ctx, sdk.NewDropEventTableRequest(id).WithIfExists(sdk.Bool(true))) + require.NoError(t, err) + } + } + + createEventTableHandle := func(t *testing.T) *sdk.EventTable { + t.Helper() + + id := sdk.NewSchemaObjectIdentifier(databaseTest.Name, schemaTest.Name, random.StringN(4)) + err := client.EventTables.Create(ctx, sdk.NewCreateEventTableRequest(id)) + require.NoError(t, err) + t.Cleanup(cleanupTableHandle(t, id)) + + et, err := client.EventTables.ShowByID(ctx, id) + require.NoError(t, err) + return et + } + + t.Run("create event tables: all options", func(t *testing.T) { + name := random.StringN(4) + id := sdk.NewSchemaObjectIdentifier(databaseTest.Name, schemaTest.Name, name) + + request := sdk.NewCreateEventTableRequest(id). + WithChangeTracking(sdk.Bool(true)). + WithDefaultDdlCollation(sdk.String("en_US")). + WithDataRetentionTimeInDays(sdk.Int(1)). + WithMaxDataExtensionTimeInDays(sdk.Int(2)). + WithComment(sdk.String("test")). + WithIfNotExists(sdk.Bool(true)). + WithTag([]sdk.TagAssociation{ + { + Name: tagTest.ID(), + Value: "v1", + }, + }) + err := client.EventTables.Create(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupTableHandle(t, id)) + }) + + t.Run("show event table: without like", func(t *testing.T) { + et1 := createEventTableHandle(t) + et2 := createEventTableHandle(t) + + tables, err := client.EventTables.Show(ctx, sdk.NewShowEventTableRequest()) + require.NoError(t, err) + + assert.Equal(t, 2, len(tables)) + assert.Contains(t, tables, *et1) + assert.Contains(t, tables, *et2) + }) + + t.Run("show event table: with like", func(t *testing.T) { + et1 := createEventTableHandle(t) + et2 := createEventTableHandle(t) + + tables, err := client.EventTables.Show(ctx, sdk.NewShowEventTableRequest().WithLike(&sdk.Like{Pattern: &et1.Name})) + require.NoError(t, err) + assert.Equal(t, 1, len(tables)) + assert.Contains(t, tables, *et1) + assert.NotContains(t, tables, *et2) + assert.Equal(t, et1.Name, tables[0].Name) + assert.Equal(t, et1.DatabaseName, tables[0].DatabaseName) + assert.Equal(t, et1.SchemaName, tables[0].SchemaName) + assert.Equal(t, et1.Owner, tables[0].Owner) + assert.Equal(t, et1.Comment, tables[0].Comment) + assert.Equal(t, et1.OwnerRoleType, tables[0].OwnerRoleType) + }) + + t.Run("show event table: no matches", func(t *testing.T) { + tables, err := client.EventTables.Show(ctx, sdk.NewShowEventTableRequest().WithLike(&sdk.Like{Pattern: sdk.String("non-existent")})) + require.NoError(t, err) + assert.Equal(t, 0, len(tables)) + }) + + t.Run("describe event table", func(t *testing.T) { + dt := createEventTableHandle(t) + id := sdk.NewSchemaObjectIdentifier(databaseTest.Name, schemaTest.Name, dt.Name) + + details, err := client.EventTables.Describe(ctx, id) + require.NoError(t, err) + assert.Equal(t, "TIMESTAMP", details.Name) + assert.NotEmpty(t, details.Kind) + }) + + t.Run("alter event table: set and unset comment", func(t *testing.T) { + dt := createEventTableHandle(t) + id := sdk.NewSchemaObjectIdentifier(databaseTest.Name, schemaTest.Name, dt.Name) + + comment := random.Comment() + set := sdk.NewEventTableSetRequest().WithComment(&comment) + err := client.EventTables.Alter(ctx, sdk.NewAlterEventTableRequest(id).WithSet(set)) + require.NoError(t, err) + + et, err := client.EventTables.ShowByID(ctx, id) + require.NoError(t, err) + assertEventTableHandle(t, et, dt.Name, comment, nil) + + unset := sdk.NewEventTableUnsetRequest().WithComment(sdk.Bool(true)) + err = client.EventTables.Alter(ctx, sdk.NewAlterEventTableRequest(id).WithUnset(unset)) + require.NoError(t, err) + + et, err = client.EventTables.ShowByID(ctx, id) + require.NoError(t, err) + assertEventTableHandle(t, et, dt.Name, "", nil) + }) + + t.Run("alter event table: set and unset change tacking", func(t *testing.T) { + dt := createEventTableHandle(t) + id := sdk.NewSchemaObjectIdentifier(databaseTest.Name, schemaTest.Name, dt.Name) + + set := sdk.NewEventTableSetRequest().WithChangeTracking(sdk.Bool(true)) + err := client.EventTables.Alter(ctx, sdk.NewAlterEventTableRequest(id).WithSet(set)) + require.NoError(t, err) + + unset := sdk.NewEventTableUnsetRequest().WithChangeTracking(sdk.Bool(true)) + err = client.EventTables.Alter(ctx, sdk.NewAlterEventTableRequest(id).WithUnset(unset)) + require.NoError(t, err) + }) + + t.Run("alter event table: set and unset tag", func(t *testing.T) { + dt := createEventTableHandle(t) + id := sdk.NewSchemaObjectIdentifier(databaseTest.Name, schemaTest.Name, dt.Name) + + set := []sdk.TagAssociation{ + { + Name: tagTest.ID(), + Value: "v1", + }, + } + err := client.EventTables.Alter(ctx, sdk.NewAlterEventTableRequest(id).WithSetTags(set)) + require.NoError(t, err) + + unset := []sdk.ObjectIdentifier{tagTest.ID()} + err = client.EventTables.Alter(ctx, sdk.NewAlterEventTableRequest(id).WithUnsetTags(unset)) + require.NoError(t, err) + }) + + t.Run("alter event table: rename", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(databaseTest.Name, schemaTest.Name, name) + + err := client.EventTables.Create(ctx, sdk.NewCreateEventTableRequest(id)) + require.NoError(t, err) + + nid := sdk.NewSchemaObjectIdentifier(databaseTest.Name, schemaTest.Name, random.String()) + err = client.EventTables.Alter(ctx, sdk.NewAlterEventTableRequest(id).WithRenameTo(&nid)) + if err != nil { + t.Cleanup(cleanupTableHandle(t, id)) + } else { + t.Cleanup(cleanupTableHandle(t, nid)) + } + require.NoError(t, err) + + _, err = client.EventTables.ShowByID(ctx, id) + assert.ErrorIs(t, err, collections.ErrObjectNotFound) + + _, err = client.EventTables.ShowByID(ctx, nid) + require.NoError(t, err) + }) + + t.Run("alter event table: clustering action with drop", func(t *testing.T) { + dt := createEventTableHandle(t) + id := sdk.NewSchemaObjectIdentifier(databaseTest.Name, schemaTest.Name, dt.Name) + + action := sdk.NewEventTableClusteringActionRequest().WithDropClusteringKey(sdk.Bool(true)) + err := client.EventTables.Alter(ctx, sdk.NewAlterEventTableRequest(id).WithClusteringAction(action)) + require.NoError(t, err) + }) + + t.Run("alter event table: search optimization action", func(t *testing.T) { + dt := createEventTableHandle(t) + id := sdk.NewSchemaObjectIdentifier(databaseTest.Name, schemaTest.Name, dt.Name) + + action := sdk.NewEventTableSearchOptimizationActionRequest().WithAdd(sdk.NewSearchOptimizationRequest().WithOn([]string{"SUBSTRING(*)"})) + err := client.EventTables.Alter(ctx, sdk.NewAlterEventTableRequest(id).WithSearchOptimizationAction(action)) + require.NoError(t, err) + + action = sdk.NewEventTableSearchOptimizationActionRequest().WithDrop(sdk.NewSearchOptimizationRequest().WithOn([]string{"SUBSTRING(*)"})) + err = client.EventTables.Alter(ctx, sdk.NewAlterEventTableRequest(id).WithSearchOptimizationAction(action)) + require.NoError(t, err) + }) + + // alter view: add and drop row access policies + t.Run("alter event table: add and drop row access policies", func(t *testing.T) { + rowAccessPolicyId, rowAccessPolicyCleanup := createRowAccessPolicy(t, client, schemaTest) + t.Cleanup(rowAccessPolicyCleanup) + rowAccessPolicy2Id, rowAccessPolicy2Cleanup := createRowAccessPolicy(t, client, schemaTest) + t.Cleanup(rowAccessPolicy2Cleanup) + + table, tableCleanup := createTable(t, client, databaseTest, schemaTest) + t.Cleanup(tableCleanup) + id := sdk.NewSchemaObjectIdentifier(table.DatabaseName, table.SchemaName, table.Name) + + // add policy + alterRequest := sdk.NewAlterEventTableRequest(id).WithAddRowAccessPolicy(sdk.NewEventTableAddRowAccessPolicyRequest(rowAccessPolicyId, []string{"id"})) + err := client.EventTables.Alter(ctx, alterRequest) + require.NoError(t, err) + + e, err := getRowAccessPolicyFor(t, client, table.ID(), sdk.ObjectTypeTable) + require.NoError(t, err) + assert.Equal(t, rowAccessPolicyId.Name(), e.PolicyName) + assert.Equal(t, "ROW_ACCESS_POLICY", e.PolicyKind) + assert.Equal(t, table.ID().Name(), e.RefEntityName) + assert.Equal(t, "TABLE", e.RefEntityDomain) + assert.Equal(t, "ACTIVE", e.PolicyStatus) + + // remove policy + alterRequest = sdk.NewAlterEventTableRequest(id).WithDropRowAccessPolicy(sdk.NewEventTableDropRowAccessPolicyRequest(rowAccessPolicyId)) + err = client.EventTables.Alter(ctx, alterRequest) + require.NoError(t, err) + + _, err = getRowAccessPolicyFor(t, client, table.ID(), sdk.ObjectTypeTable) + require.Error(t, err, "no rows in result set") + + // add policy again + alterRequest = sdk.NewAlterEventTableRequest(id).WithAddRowAccessPolicy(sdk.NewEventTableAddRowAccessPolicyRequest(rowAccessPolicyId, []string{"id"})) + err = client.EventTables.Alter(ctx, alterRequest) + require.NoError(t, err) + + e, err = getRowAccessPolicyFor(t, client, table.ID(), sdk.ObjectTypeTable) + require.NoError(t, err) + assert.Equal(t, rowAccessPolicyId.Name(), e.PolicyName) + + // drop and add other policy simultaneously + alterRequest = sdk.NewAlterEventTableRequest(id).WithDropAndAddRowAccessPolicy(sdk.NewEventTableDropAndAddRowAccessPolicyRequest( + *sdk.NewEventTableDropRowAccessPolicyRequest(rowAccessPolicyId), + *sdk.NewEventTableAddRowAccessPolicyRequest(rowAccessPolicy2Id, []string{"id"}), + )) + err = client.EventTables.Alter(ctx, alterRequest) + require.NoError(t, err) + + e, err = getRowAccessPolicyFor(t, client, table.ID(), sdk.ObjectTypeTable) + require.NoError(t, err) + assert.Equal(t, rowAccessPolicy2Id.Name(), e.PolicyName) + + // drop all policies + alterRequest = sdk.NewAlterEventTableRequest(id).WithDropAllRowAccessPolicies(sdk.Bool(true)) + err = client.EventTables.Alter(ctx, alterRequest) + require.NoError(t, err) + + _, err = getRowAccessPolicyFor(t, client, table.ID(), sdk.ObjectTypeView) + require.Error(t, err, "no rows in result set") + }) +}