diff --git a/Makefile b/Makefile index 0dd62fbd35..555e4d3375 100644 --- a/Makefile +++ b/Makefile @@ -68,6 +68,9 @@ test: ## run unit and integration tests test-acceptance: ## run acceptance tests TF_ACC=1 go test -run "^TestAcc_" -v -cover -timeout=30m ./... +test-integration: ## run SDK integration tests + go test -run "^TestInt_" -v -cover -timeout=20m ./... + test-architecture: ## check architecture constraints between packages go test ./pkg/architests/... -v diff --git a/pkg/resources/file_format.go b/pkg/resources/file_format.go index 1433783155..2cd56e4d43 100644 --- a/pkg/resources/file_format.go +++ b/pkg/resources/file_format.go @@ -428,7 +428,7 @@ func CreateFileFormat(d *schema.ResourceData, meta interface{}) error { } nullIf = append(nullIf, sdk.NullString{S: s.(string)}) } - opts.JSONNullIf = &nullIf + opts.JSONNullIf = nullIf } if v, ok := d.GetOk("file_extension"); ok { opts.JSONFileExtension = sdk.String(v.(string)) @@ -649,7 +649,7 @@ func ReadFileFormat(d *schema.ResourceData, meta interface{}) error { return err } nullIf := []string{} - for _, s := range *fileFormat.Options.JSONNullIf { + for _, s := range fileFormat.Options.JSONNullIf { nullIf = append(nullIf, s.S) } if err := d.Set("null_if", nullIf); err != nil { @@ -939,7 +939,7 @@ func UpdateFileFormat(d *schema.ResourceData, meta interface{}) error { } nullIf = append(nullIf, sdk.NullString{S: s.(string)}) } - opts.Set.JSONNullIf = &nullIf + opts.Set.JSONNullIf = nullIf runSet = true } if d.HasChange("file_extension") { diff --git a/pkg/sdk/client.go b/pkg/sdk/client.go index 94c0636287..d333c965f2 100644 --- a/pkg/sdk/client.go +++ b/pkg/sdk/client.go @@ -53,6 +53,7 @@ type Client struct { Shares Shares Stages Stages Streams Streams + Tables Tables Tags Tags Tasks Tasks Users Users @@ -183,6 +184,7 @@ func (c *Client) initialize() { c.Stages = &stages{client: c} c.Streams = &streams{client: c} c.SystemFunctions = &systemFunctions{client: c} + c.Tables = &tables{client: c} c.Tags = &tags{client: c} c.Tasks = &tasks{client: c} c.Users = &users{client: c} diff --git a/pkg/sdk/common_table_types.go b/pkg/sdk/common_table_types.go new file mode 100644 index 0000000000..0334095411 --- /dev/null +++ b/pkg/sdk/common_table_types.go @@ -0,0 +1,115 @@ +package sdk + +import "errors" + +type TableRowAccessPolicy struct { + rowAccessPolicy bool `ddl:"static" sql:"ROW ACCESS POLICY"` + Name SchemaObjectIdentifier `ddl:"identifier"` + On []string `ddl:"keyword,parentheses" sql:"ON"` +} + +// ColumnInlineConstraint is based on https://docs.snowflake.com/en/sql-reference/sql/create-table-constraint#inline-unique-primary-foreign-key. +type ColumnInlineConstraint struct { + Name *string `ddl:"parameter,no_equals" sql:"CONSTRAINT"` + Type ColumnConstraintType `ddl:"keyword"` + ForeignKey *InlineForeignKey `ddl:"keyword" sql:"FOREIGN KEY"` + + // optional + Enforced *bool `ddl:"keyword" sql:"ENFORCED"` + NotEnforced *bool `ddl:"keyword" sql:"NOT ENFORCED"` + Deferrable *bool `ddl:"keyword" sql:"DEFERRABLE"` + NotDeferrable *bool `ddl:"keyword" sql:"NOT DEFERRABLE"` + InitiallyDeferred *bool `ddl:"keyword" sql:"INITIALLY DEFERRED"` + InitiallyImmediate *bool `ddl:"keyword" sql:"INITIALLY IMMEDIATE"` + Enable *bool `ddl:"keyword" sql:"ENABLE"` + Disable *bool `ddl:"keyword" sql:"DISABLE"` + Validate *bool `ddl:"keyword" sql:"VALIDATE"` + NoValidate *bool `ddl:"keyword" sql:"NOVALIDATE"` + Rely *bool `ddl:"keyword" sql:"RELY"` + NoRely *bool `ddl:"keyword" sql:"NORELY"` +} + +func (v *ColumnInlineConstraint) validate() error { + var errs []error + switch v.Type { + case ColumnConstraintTypeForeignKey: + if !valueSet(v.ForeignKey) { + errs = append(errs, errNotSet("ColumnInlineConstraint", "ForeignKey")) + } else { + if err := v.ForeignKey.validate(); err != nil { + errs = append(errs, err) + } + } + case ColumnConstraintTypeUnique, ColumnConstraintTypePrimaryKey: + if valueSet(v.ForeignKey) { + errs = append(errs, errSet("ColumnInlineConstraint", "ForeignKey")) + } + default: + errs = append(errs, errInvalidValue("ColumnInlineConstraint", "Type", string(v.Type))) + } + if moreThanOneValueSet(v.Enforced, v.NotEnforced) { + errs = append(errs, errMoreThanOneOf("ColumnInlineConstraint", "Enforced", "NotEnforced")) + } + if moreThanOneValueSet(v.Deferrable, v.NotDeferrable) { + errs = append(errs, errMoreThanOneOf("ColumnInlineConstraint", "Deferrable", "NotDeferrable")) + } + if moreThanOneValueSet(v.InitiallyDeferred, v.InitiallyImmediate) { + errs = append(errs, errMoreThanOneOf("ColumnInlineConstraint", "InitiallyDeferred", "InitiallyImmediate")) + } + if moreThanOneValueSet(v.Enable, v.Disable) { + errs = append(errs, errMoreThanOneOf("ColumnInlineConstraint", "Enable", "Disable")) + } + if moreThanOneValueSet(v.Validate, v.NoValidate) { + errs = append(errs, errMoreThanOneOf("ColumnInlineConstraint", "Validate", "Novalidate")) + } + if moreThanOneValueSet(v.Rely, v.NoRely) { + errs = append(errs, errMoreThanOneOf("ColumnInlineConstraint", "Rely", "Norely")) + } + return errors.Join(errs...) +} + +type ColumnConstraintType string + +const ( + ColumnConstraintTypeUnique ColumnConstraintType = "UNIQUE" + ColumnConstraintTypePrimaryKey ColumnConstraintType = "PRIMARY KEY" + ColumnConstraintTypeForeignKey ColumnConstraintType = "FOREIGN KEY" +) + +type InlineForeignKey struct { + TableName string `ddl:"keyword" sql:"REFERENCES"` + ColumnName []string `ddl:"keyword,parentheses"` + Match *MatchType `ddl:"keyword" sql:"MATCH"` + On *ForeignKeyOnAction `ddl:"keyword" sql:"ON"` +} + +func (v *InlineForeignKey) validate() error { + var errs []error + if !valueSet(v.TableName) { + errs = append(errs, errNotSet("InlineForeignKey", "TableName")) + } + return errors.Join(errs...) +} + +type MatchType string + +var ( + FullMatchType MatchType = "FULL" + SimpleMatchType MatchType = "SIMPLE" + PartialMatchType MatchType = "PARTIAL" +) + +type ForeignKeyOnAction struct { + OnUpdate *ForeignKeyAction `ddl:"parameter,no_equals" sql:"ON UPDATE"` + OnDelete *ForeignKeyAction `ddl:"parameter,no_equals" sql:"ON DELETE"` +} + +type ForeignKeyAction string + +const ( + ForeignKeyCascadeAction ForeignKeyAction = "CASCADE" + ForeignKeySetNullAction ForeignKeyAction = "SET NULL" + ForeignKeySetDefaultAction ForeignKeyAction = "SET DEFAULT" + ForeignKeyRestrictAction ForeignKeyAction = "RESTRICT" + ForeignKeyNoAction ForeignKeyAction = "NO ACTION" +) diff --git a/pkg/sdk/common_types.go b/pkg/sdk/common_types.go index 57a4f862e0..d7703a4ba1 100644 --- a/pkg/sdk/common_types.go +++ b/pkg/sdk/common_types.go @@ -129,65 +129,6 @@ func (row *propertyRow) toIntProperty() *IntProperty { } } -type RowAccessPolicy struct { - rowAccessPolicy bool `ddl:"static" sql:"ROW ACCESS POLICY"` - Name SchemaObjectIdentifier `ddl:"identifier"` - On []string `ddl:"keyword,parentheses" sql:"ON"` -} - -type ColumnInlineConstraint struct { - NotNull *bool `ddl:"keyword" sql:"NOT NULL"` - Name *string `ddl:"parameter,no_equals" sql:"CONSTRAINT"` - Type *ColumnConstraintType `ddl:"keyword"` - ForeignKey *InlineForeignKey `ddl:"keyword" sql:"FOREIGN KEY"` - - // optional - Enforced *bool `ddl:"keyword" sql:"ENFORCED"` - NotEnforced *bool `ddl:"keyword" sql:"NOT ENFORCED"` - Deferrable *bool `ddl:"keyword" sql:"DEFERRABLE"` - NotDeferrable *bool `ddl:"keyword" sql:"NOT DEFERRABLE"` - InitiallyDeferred *bool `ddl:"keyword" sql:"INITIALLY DEFERRED"` - InitiallyImmediate *bool `ddl:"keyword" sql:"INITIALLY IMMEDIATE"` - Enable *bool `ddl:"keyword" sql:"ENABLE"` - Disable *bool `ddl:"keyword" sql:"DISABLE"` - Validate *bool `ddl:"keyword" sql:"VALIDATE"` - NoValidate *bool `ddl:"keyword" sql:"NOVALIDATE"` - Rely *bool `ddl:"keyword" sql:"RELY"` - NoRely *bool `ddl:"keyword" sql:"NORELY"` -} - -type ColumnConstraintType string - -var ( - ColumnConstraintTypeUnique ColumnConstraintType = "UNIQUE" - ColumnConstraintTypePrimaryKey ColumnConstraintType = "PRIMARY KEY" - ColumnConstraintTypeForeignKey ColumnConstraintType = "FOREIGN KEY" -) - -type InlineForeignKey struct { - TableName string `ddl:"keyword" sql:"REFERENCES"` - ColumnName []string `ddl:"keyword,parentheses"` - Match *MatchType `ddl:"keyword" sql:"MATCH"` - On *ForeignKeyOnAction `ddl:"keyword" sql:"ON"` -} - -func (v *InlineForeignKey) validate() error { - return nil -} - -type MatchType string - -var ( - FullMatchType MatchType = "FULL" - SimpleMatchType MatchType = "SIMPLE" - PartialMatchType MatchType = "PARTIAL" -) - -type ForeignKeyOnAction struct { - OnUpdate *bool `ddl:"parameter,no_equals" sql:"ON UPDATE"` - OnDelete *bool `ddl:"parameter,no_equals" sql:"ON DELETE"` -} - func (row *propertyRow) toBoolProperty() *BoolProperty { var value bool if row.Value != "" && row.Value != "null" { diff --git a/pkg/sdk/data_types.go b/pkg/sdk/data_types.go index 132f2a94d5..8ec5d77def 100644 --- a/pkg/sdk/data_types.go +++ b/pkg/sdk/data_types.go @@ -7,6 +7,7 @@ import ( "golang.org/x/exp/slices" ) +// DataType is based on https://docs.snowflake.com/en/sql-reference/intro-summary-data-types. type DataType string const ( diff --git a/pkg/sdk/errors.go b/pkg/sdk/errors.go index 4ca6475cc2..2d7bbc6089 100644 --- a/pkg/sdk/errors.go +++ b/pkg/sdk/errors.go @@ -52,6 +52,10 @@ func errNotSet(structName string, fieldNames ...string) error { return newError(fmt.Sprintf("%v fields: %v should be set", structName, fieldNames), 2) } +func errSet(structName string, fieldNames ...string) error { + return newError(fmt.Sprintf("%v fields: %v should not be set", structName, fieldNames), 2) +} + func errExactlyOneOf(structName string, fieldNames ...string) error { return newError(fmt.Sprintf("exactly one of %s fields %v must be set", structName, fieldNames), 2) } @@ -60,6 +64,14 @@ func errAtLeastOneOf(structName string, fieldNames ...string) error { return newError(fmt.Sprintf("at least one of %s fields %v must be set", structName, fieldNames), 2) } +func errMoreThanOneOf(structName string, fieldNames ...string) error { + return newError(fmt.Sprintf("more than one field (%v) of %s cannot be set", fieldNames, structName), 2) +} + +func errInvalidValue(structName string, fieldName string, invalidValue string) error { + return newError(fmt.Sprintf("invalid value %s of struct %s field: %s", invalidValue, structName, fieldName), 2) +} + func decodeDriverError(err error) error { if err == nil { return nil diff --git a/pkg/sdk/external_tables.go b/pkg/sdk/external_tables.go index eea89fc9bd..97752dea73 100644 --- a/pkg/sdk/external_tables.go +++ b/pkg/sdk/external_tables.go @@ -17,6 +17,7 @@ type ExternalTables interface { CreateWithManualPartitioning(ctx context.Context, req *CreateWithManualPartitioningExternalTableRequest) error CreateDeltaLake(ctx context.Context, req *CreateDeltaLakeExternalTableRequest) error CreateUsingTemplate(ctx context.Context, req *CreateExternalTableUsingTemplateRequest) error + // TODO: Add alter options from https://docs.snowflake.com/en/sql-reference/sql/alter-table#external-table-column-actions-exttablecolumnaction (for better UX) Alter(ctx context.Context, req *AlterExternalTableRequest) error AlterPartitions(ctx context.Context, req *AlterExternalTablePartitionRequest) error Drop(ctx context.Context, req *DropExternalTableRequest) error @@ -74,7 +75,7 @@ type CreateExternalTableOptions struct { AwsSnsTopic *string `ddl:"parameter,single_quotes" sql:"AWS_SNS_TOPIC"` CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` - RowAccessPolicy *RowAccessPolicy `ddl:"keyword"` + RowAccessPolicy *TableRowAccessPolicy `ddl:"keyword"` Tag []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` } @@ -82,6 +83,7 @@ type ExternalTableColumn struct { Name string `ddl:"keyword"` Type DataType `ddl:"keyword"` AsExpression []string `ddl:"keyword,parentheses" sql:"AS"` + NotNull *bool `ddl:"keyword" sql:"NOT NULL"` InlineConstraint *ColumnInlineConstraint } @@ -205,7 +207,7 @@ type CreateWithManualPartitioningExternalTableOptions struct { FileFormat []ExternalTableFileFormat `ddl:"parameter,parentheses" sql:"FILE_FORMAT"` CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` - RowAccessPolicy *RowAccessPolicy `ddl:"keyword"` + RowAccessPolicy *TableRowAccessPolicy `ddl:"keyword"` Tag []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` } @@ -227,7 +229,7 @@ type CreateDeltaLakeExternalTableOptions struct { DeltaTableFormat *bool `ddl:"keyword" sql:"TABLE_FORMAT = DELTA"` CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` - RowAccessPolicy *RowAccessPolicy `ddl:"keyword"` + RowAccessPolicy *TableRowAccessPolicy `ddl:"keyword"` Tag []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` } @@ -248,7 +250,7 @@ type CreateExternalTableUsingTemplateOptions struct { FileFormat []ExternalTableFileFormat `ddl:"parameter,parentheses" sql:"FILE_FORMAT"` AwsSnsTopic *string `ddl:"parameter,single_quotes" sql:"AWS_SNS_TOPIC"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` - RowAccessPolicy *RowAccessPolicy `ddl:"keyword"` + RowAccessPolicy *TableRowAccessPolicy `ddl:"keyword"` Tag []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` } diff --git a/pkg/sdk/external_tables_dto.go b/pkg/sdk/external_tables_dto.go index 3bcc01103f..f266ce3c69 100644 --- a/pkg/sdk/external_tables_dto.go +++ b/pkg/sdk/external_tables_dto.go @@ -42,6 +42,7 @@ type ExternalTableColumnRequest struct { name string // required dataType DataType // required asExpression string // required + notNull *bool inlineConstraint *ColumnInlineConstraintRequest } @@ -55,16 +56,16 @@ func (v ExternalTableColumnRequest) toOpts() ExternalTableColumn { Name: v.name, Type: v.dataType, AsExpression: []string{v.asExpression}, + NotNull: v.notNull, InlineConstraint: inlineConstraint, } } func (v *ColumnInlineConstraintRequest) toOpts() *ColumnInlineConstraint { return &ColumnInlineConstraint{ - NotNull: v.notNull, - Name: &v.name, - Type: &v.constraintType, - ForeignKey: v.foreignKey, + Name: &v.Name, + Type: v.type_, + ForeignKey: v.foreignKey.toOpts(), Enforced: v.enforced, NotEnforced: v.notEnforced, Deferrable: v.deferrable, @@ -80,37 +81,16 @@ func (v *ColumnInlineConstraintRequest) toOpts() *ColumnInlineConstraint { } } -type ColumnInlineConstraintRequest struct { - notNull *bool - name string // required - constraintType ColumnConstraintType // required - foreignKey *InlineForeignKey - - // optional - enforced *bool - notEnforced *bool - deferrable *bool - notDeferrable *bool - initiallyDeferred *bool - initiallyImmediate *bool - enable *bool - disable *bool - validate *bool - noValidate *bool - rely *bool - noRely *bool -} - -type InlineForeignKeyRequest struct { - tableName string // required - columnName []string - match *MatchType - on *ForeignKeyOnActionRequest -} - -type ForeignKeyOnActionRequest struct { - onUpdate *bool - onDelete *bool +func (v *InlineForeignKeyRequest) toOpts() *InlineForeignKey { + if v == nil { + return nil + } + return &InlineForeignKey{ + TableName: v.TableName, + ColumnName: v.ColumnName, + Match: v.Match, + On: v.On, + } } type CloudProviderParamsRequest struct { @@ -233,25 +213,15 @@ func (v NullStringRequest) toOpts() NullString { } } -type RowAccessPolicyRequest struct { - name SchemaObjectIdentifier // required - on []string // required -} - -func (v *RowAccessPolicyRequest) toOpts() *RowAccessPolicy { - return nil -} - -type TagAssociationRequest struct { - name ObjectIdentifier // required - value string // required +func (v *RowAccessPolicyRequest) toOpts() *TableRowAccessPolicy { + return &TableRowAccessPolicy{ + Name: v.Name, + On: v.On, + } } func (v TagAssociationRequest) toOpts() TagAssociation { - return TagAssociation{ - Name: v.name, - Value: v.value, - } + return TagAssociation(v) } func (s *CreateExternalTableRequest) toOpts() *CreateExternalTableOptions { @@ -272,7 +242,7 @@ func (s *CreateExternalTableRequest) toOpts() *CreateExternalTableOptions { cloudProviderParams = s.cloudProviderParams.toOpts() } - var rowAccessPolicy *RowAccessPolicy + var rowAccessPolicy *TableRowAccessPolicy if s.rowAccessPolicy != nil { rowAccessPolicy = s.rowAccessPolicy.toOpts() } @@ -337,7 +307,7 @@ func (v *CreateWithManualPartitioningExternalTableRequest) toOpts() *CreateWithM fileFormat = []ExternalTableFileFormat{v.fileFormat.toOpts()} } - var rowAccessPolicy *RowAccessPolicy + var rowAccessPolicy *TableRowAccessPolicy if v.rowAccessPolicy != nil { rowAccessPolicy = v.rowAccessPolicy.toOpts() } @@ -403,7 +373,7 @@ func (v *CreateDeltaLakeExternalTableRequest) toOpts() *CreateDeltaLakeExternalT fileFormat = []ExternalTableFileFormat{v.fileFormat.toOpts()} } - var rowAccessPolicy *RowAccessPolicy + var rowAccessPolicy *TableRowAccessPolicy if v.rowAccessPolicy != nil { rowAccessPolicy = v.rowAccessPolicy.toOpts() } @@ -464,7 +434,7 @@ func (v *CreateExternalTableUsingTemplateRequest) toOpts() *CreateExternalTableU fileFormat = []ExternalTableFileFormat{v.fileFormat.toOpts()} } - var rowAccessPolicy *RowAccessPolicy + var rowAccessPolicy *TableRowAccessPolicy if v.rowAccessPolicy != nil { rowAccessPolicy = v.rowAccessPolicy.toOpts() } @@ -645,11 +615,6 @@ func (v *ShowExternalTableInRequest) toOpts() *In { } } -type LimitFromRequest struct { - rows *int - from *string -} - func (v *LimitFromRequest) toOpts() *LimitFrom { return &LimitFrom{ Rows: v.rows, diff --git a/pkg/sdk/external_tables_dto_builders_gen.go b/pkg/sdk/external_tables_dto_builders_gen.go index cad2cf1097..9a08c82078 100644 --- a/pkg/sdk/external_tables_dto_builders_gen.go +++ b/pkg/sdk/external_tables_dto_builders_gen.go @@ -91,125 +91,13 @@ func NewExternalTableColumnRequest( return &s } -func (s *ExternalTableColumnRequest) WithInlineConstraint(inlineConstraint *ColumnInlineConstraintRequest) *ExternalTableColumnRequest { - s.inlineConstraint = inlineConstraint - return s -} - -func NewColumnInlineConstraintRequest( - name string, - constraintType ColumnConstraintType, -) *ColumnInlineConstraintRequest { - s := ColumnInlineConstraintRequest{} - s.name = name - s.constraintType = constraintType - return &s -} - -func (s *ColumnInlineConstraintRequest) WithNotNull(notNull *bool) *ColumnInlineConstraintRequest { - s.notNull = notNull - return s -} - -func (s *ColumnInlineConstraintRequest) WithForeignKey(foreignKey *InlineForeignKey) *ColumnInlineConstraintRequest { - s.foreignKey = foreignKey - return s -} - -func (s *ColumnInlineConstraintRequest) WithEnforced(enforced *bool) *ColumnInlineConstraintRequest { - s.enforced = enforced - return s -} - -func (s *ColumnInlineConstraintRequest) WithNotEnforced(notEnforced *bool) *ColumnInlineConstraintRequest { - s.notEnforced = notEnforced - return s -} - -func (s *ColumnInlineConstraintRequest) WithDeferrable(deferrable *bool) *ColumnInlineConstraintRequest { - s.deferrable = deferrable - return s -} - -func (s *ColumnInlineConstraintRequest) WithNotDeferrable(notDeferrable *bool) *ColumnInlineConstraintRequest { - s.notDeferrable = notDeferrable - return s -} - -func (s *ColumnInlineConstraintRequest) WithInitiallyDeferred(initiallyDeferred *bool) *ColumnInlineConstraintRequest { - s.initiallyDeferred = initiallyDeferred - return s -} - -func (s *ColumnInlineConstraintRequest) WithInitiallyImmediate(initiallyImmediate *bool) *ColumnInlineConstraintRequest { - s.initiallyImmediate = initiallyImmediate - return s -} - -func (s *ColumnInlineConstraintRequest) WithEnable(enable *bool) *ColumnInlineConstraintRequest { - s.enable = enable - return s -} - -func (s *ColumnInlineConstraintRequest) WithDisable(disable *bool) *ColumnInlineConstraintRequest { - s.disable = disable - return s -} - -func (s *ColumnInlineConstraintRequest) WithValidate(validate *bool) *ColumnInlineConstraintRequest { - s.validate = validate - return s -} - -func (s *ColumnInlineConstraintRequest) WithNoValidate(noValidate *bool) *ColumnInlineConstraintRequest { - s.noValidate = noValidate - return s -} - -func (s *ColumnInlineConstraintRequest) WithRely(rely *bool) *ColumnInlineConstraintRequest { - s.rely = rely - return s -} - -func (s *ColumnInlineConstraintRequest) WithNoRely(noRely *bool) *ColumnInlineConstraintRequest { - s.noRely = noRely - return s -} - -func NewInlineForeignKeyRequest( - tableName string, -) *InlineForeignKeyRequest { - s := InlineForeignKeyRequest{} - s.tableName = tableName - return &s -} - -func (s *InlineForeignKeyRequest) WithColumnName(columnName []string) *InlineForeignKeyRequest { - s.columnName = columnName - return s -} - -func (s *InlineForeignKeyRequest) WithMatch(match *MatchType) *InlineForeignKeyRequest { - s.match = match - return s -} - -func (s *InlineForeignKeyRequest) WithOn(on *ForeignKeyOnActionRequest) *InlineForeignKeyRequest { - s.on = on - return s -} - -func NewForeignKeyOnActionRequest() *ForeignKeyOnActionRequest { - return &ForeignKeyOnActionRequest{} -} - -func (s *ForeignKeyOnActionRequest) WithOnUpdate(onUpdate *bool) *ForeignKeyOnActionRequest { - s.onUpdate = onUpdate +func (s *ExternalTableColumnRequest) WithNotNull() *ExternalTableColumnRequest { + s.notNull = Bool(true) return s } -func (s *ForeignKeyOnActionRequest) WithOnDelete(onDelete *bool) *ForeignKeyOnActionRequest { - s.onDelete = onDelete +func (s *ExternalTableColumnRequest) WithInlineConstraint(inlineConstraint *ColumnInlineConstraintRequest) *ExternalTableColumnRequest { + s.inlineConstraint = inlineConstraint return s } @@ -384,18 +272,8 @@ func NewRowAccessPolicyRequest( on []string, ) *RowAccessPolicyRequest { s := RowAccessPolicyRequest{} - s.name = name - s.on = on - return &s -} - -func NewTagAssociationRequest( - name ObjectIdentifier, - value string, -) *TagAssociationRequest { - s := TagAssociationRequest{} - s.name = name - s.value = value + s.Name = name + s.On = on return &s } @@ -787,20 +665,6 @@ func (s *ShowExternalTableInRequest) WithSchema(schema DatabaseObjectIdentifier) return s } -func NewLimitFromRequest() *LimitFromRequest { - return &LimitFromRequest{} -} - -func (s *LimitFromRequest) WithRows(rows *int) *LimitFromRequest { - s.rows = rows - return s -} - -func (s *LimitFromRequest) WithFrom(from *string) *LimitFromRequest { - s.from = from - return s -} - func NewShowExternalTableByIDRequest( id SchemaObjectIdentifier, ) *ShowExternalTableByIDRequest { diff --git a/pkg/sdk/external_tables_test.go b/pkg/sdk/external_tables_test.go index e6ed3fb29a..3a3fccf57f 100644 --- a/pkg/sdk/external_tables_test.go +++ b/pkg/sdk/external_tables_test.go @@ -14,10 +14,10 @@ func TestExternalTablesCreate(t *testing.T) { Name: "column", Type: "varchar", AsExpression: []string{"value::column::varchar"}, + NotNull: Bool(true), InlineConstraint: &ColumnInlineConstraint{ - Name: String("my_constraint"), - NotNull: Bool(true), - Type: &ColumnConstraintTypeUnique, + Name: String("my_constraint"), + Type: ColumnConstraintTypeUnique, }, }, }, @@ -43,10 +43,10 @@ func TestExternalTablesCreate(t *testing.T) { Name: "column", Type: "varchar", AsExpression: []string{"value::column::varchar"}, + NotNull: Bool(true), InlineConstraint: &ColumnInlineConstraint{ - Name: String("my_constraint"), - NotNull: Bool(true), - Type: &ColumnConstraintTypeUnique, + Name: String("my_constraint"), + Type: ColumnConstraintTypeUnique, }, }, }, @@ -61,7 +61,7 @@ func TestExternalTablesCreate(t *testing.T) { }, AwsSnsTopic: String("aws_sns_topic"), CopyGrants: Bool(true), - RowAccessPolicy: &RowAccessPolicy{ + RowAccessPolicy: &TableRowAccessPolicy{ Name: NewSchemaObjectIdentifier("db", "schema", "row_access_policy"), On: []string{"value1", "value2"}, }, @@ -106,10 +106,10 @@ func TestExternalTablesCreateWithManualPartitioning(t *testing.T) { Name: "column", Type: "varchar", AsExpression: []string{"value::column::varchar"}, + NotNull: Bool(true), InlineConstraint: &ColumnInlineConstraint{ - Name: String("my_constraint"), - NotNull: Bool(true), - Type: &ColumnConstraintTypeUnique, + Name: String("my_constraint"), + Type: ColumnConstraintTypeUnique, }, }, }, @@ -123,7 +123,7 @@ func TestExternalTablesCreateWithManualPartitioning(t *testing.T) { }, }, CopyGrants: Bool(true), - RowAccessPolicy: &RowAccessPolicy{ + RowAccessPolicy: &TableRowAccessPolicy{ Name: NewSchemaObjectIdentifier("db", "schema", "row_access_policy"), On: []string{"value1", "value2"}, }, @@ -183,7 +183,7 @@ func TestExternalTablesCreateDeltaLake(t *testing.T) { }, DeltaTableFormat: Bool(true), CopyGrants: Bool(true), - RowAccessPolicy: &RowAccessPolicy{ + RowAccessPolicy: &TableRowAccessPolicy{ Name: NewSchemaObjectIdentifier("db", "schema", "row_access_policy"), On: []string{"value1", "value2"}, }, @@ -236,7 +236,7 @@ func TestExternalTableUsingTemplateOpts(t *testing.T) { }, }, Comment: String("some_comment"), - RowAccessPolicy: &RowAccessPolicy{ + RowAccessPolicy: &TableRowAccessPolicy{ Name: NewSchemaObjectIdentifier("db", "schema", "row_access_policy"), On: []string{"value1", "value2"}, }, diff --git a/pkg/sdk/file_format.go b/pkg/sdk/file_format.go index aef6cb18c3..59ccbecdaa 100644 --- a/pkg/sdk/file_format.go +++ b/pkg/sdk/file_format.go @@ -161,7 +161,7 @@ func (row FileFormatRow) convert() *FileFormat { ff.Options.JSONTimestampFormat = &inputOptions.TimestampFormat ff.Options.JSONBinaryFormat = (*BinaryFormat)(&inputOptions.BinaryFormat) ff.Options.JSONTrimSpace = &inputOptions.TrimSpace - ff.Options.JSONNullIf = &newNullIf + ff.Options.JSONNullIf = newNullIf ff.Options.JSONFileExtension = &inputOptions.FileExtension ff.Options.JSONEnableOctal = &inputOptions.EnableOctal ff.Options.JSONAllowDuplicate = &inputOptions.AllowDuplicate @@ -429,7 +429,7 @@ type FileFormatTypeOptions struct { JSONTimestampFormat *string `ddl:"parameter,single_quotes" sql:"TIMESTAMP_FORMAT"` JSONBinaryFormat *BinaryFormat `ddl:"parameter" sql:"BINARY_FORMAT"` JSONTrimSpace *bool `ddl:"parameter" sql:"TRIM_SPACE"` - JSONNullIf *[]NullString `ddl:"parameter,parentheses" sql:"NULL_IF"` + JSONNullIf []NullString `ddl:"parameter,parentheses" sql:"NULL_IF"` JSONFileExtension *string `ddl:"parameter,single_quotes" sql:"FILE_EXTENSION"` JSONEnableOctal *bool `ddl:"parameter" sql:"ENABLE_OCTAL"` JSONAllowDuplicate *bool `ddl:"parameter" sql:"ALLOW_DUPLICATE"` @@ -835,7 +835,7 @@ func (v *fileFormats) Describe(ctx context.Context, id SchemaObjectIdentifier) ( for _, s := range strings.Split(strings.Trim(v, "[]"), ", ") { newNullIf = append(newNullIf, NullString{s}) } - details.Options.JSONNullIf = &newNullIf + details.Options.JSONNullIf = newNullIf case "COMPRESSION": comp := JSONCompression(v) details.Options.JSONCompression = &comp diff --git a/pkg/sdk/file_format_test.go b/pkg/sdk/file_format_test.go index 8807f97e9c..f364636c66 100644 --- a/pkg/sdk/file_format_test.go +++ b/pkg/sdk/file_format_test.go @@ -65,7 +65,7 @@ func TestFileFormatsCreate(t *testing.T) { JSONTimestampFormat: String("aze"), JSONBinaryFormat: &BinaryFormatHex, JSONTrimSpace: Bool(true), - JSONNullIf: &[]NullString{ + JSONNullIf: []NullString{ {"c1"}, {"c2"}, }, diff --git a/pkg/sdk/integration_test_imports.go b/pkg/sdk/integration_test_imports.go index b109ec8aa6..aaf396b739 100644 --- a/pkg/sdk/integration_test_imports.go +++ b/pkg/sdk/integration_test_imports.go @@ -27,6 +27,13 @@ func (c *Client) QueryOneForTests(ctx context.Context, dest interface{}, sql str return decodeDriverError(c.db.GetContext(ctx, dest, sql)) } +// QueryForTests is an exact copy of query (that is unexported), that some integration tests/helpers were using +// TODO: remove after introducing all resources using this +func (c *Client) QueryForTests(ctx context.Context, dest interface{}, sql string) error { + ctx = context.WithValue(ctx, snowflakeAccountLocatorContextKey, c.accountLocator) + return decodeDriverError(c.db.SelectContext(ctx, dest, sql)) +} + func ErrorsEqual(t *testing.T, expected error, actual error) { t.Helper() var expectedErr *Error diff --git a/pkg/sdk/parameters.go b/pkg/sdk/parameters.go index 25304c5851..76b5f6a8f2 100644 --- a/pkg/sdk/parameters.go +++ b/pkg/sdk/parameters.go @@ -266,12 +266,12 @@ func (parameters *parameters) SetObjectParameterOnAccount(ctx context.Context, p } type setParameterOnObject struct { - alter bool `ddl:"static" sql:"ALTER"` //lint:ignore U1000 This is used in the ddl tag + alter bool `ddl:"static" sql:"ALTER"` objectType ObjectType `ddl:"keyword"` objectIdentifier ObjectIdentifier `ddl:"identifier"` - set bool `ddl:"static" sql:"SET"` //lint:ignore U1000 This is used in the ddl tag + set bool `ddl:"static" sql:"SET"` parameterKey ObjectParameter `ddl:"keyword"` - equals bool `ddl:"static" sql:"="` //lint:ignore U1000 This is used in the ddl tag + equals bool `ddl:"static" sql:"="` parameterValue string `ddl:"keyword"` } @@ -829,8 +829,8 @@ type UserParametersUnset struct { // ShowParametersOptions is based on https://docs.snowflake.com/en/sql-reference/sql/show-parameters. type ShowParametersOptions struct { - show bool `ddl:"static" sql:"SHOW"` //lint:ignore U1000 This is used in the ddl tag - parameters bool `ddl:"static" sql:"PARAMETERS"` //lint:ignore U1000 This is used in the ddl tag + show bool `ddl:"static" sql:"SHOW"` + parameters bool `ddl:"static" sql:"PARAMETERS"` Like *Like `ddl:"keyword" sql:"LIKE"` In *ParametersIn `ddl:"keyword" sql:"IN"` } diff --git a/pkg/sdk/stages_dto_builders_gen.go b/pkg/sdk/stages_dto_builders_gen.go index 34f47fd699..9aec793e27 100644 --- a/pkg/sdk/stages_dto_builders_gen.go +++ b/pkg/sdk/stages_dto_builders_gen.go @@ -2,7 +2,9 @@ package sdk -import () +import ( + "fmt" +) func NewCreateInternalStageRequest( name SchemaObjectIdentifier, @@ -93,7 +95,7 @@ func (s *StageFileFormatRequest) WithType(Type *FileFormatType) *StageFileFormat return s } -func (s *StageFileFormatRequest) WithOptions(Options *FileFormatTypeOptions) *StageFileFormatRequest { +func (s *StageFileFormatRequest) WithOptions(Options *FileFormatTypeOptionsRequest) *StageFileFormatRequest { s.Options = Options return s } @@ -151,8 +153,18 @@ func (s *StageCopyOnErrorOptionsRequest) WithContinue(Continue *bool) *StageCopy return s } -func (s *StageCopyOnErrorOptionsRequest) WithSkipFile(SkipFile *bool) *StageCopyOnErrorOptionsRequest { - s.SkipFile = SkipFile +func (s *StageCopyOnErrorOptionsRequest) WithSkipFile() *StageCopyOnErrorOptionsRequest { + s.SkipFile = String("SKIP_FILE") + return s +} + +func (s *StageCopyOnErrorOptionsRequest) WithSkipFileX(x int) *StageCopyOnErrorOptionsRequest { + s.SkipFile = String(fmt.Sprintf("SKIP_FILE_%d", x)) + return s +} + +func (s *StageCopyOnErrorOptionsRequest) WithSkipFileXPercent(x int) *StageCopyOnErrorOptionsRequest { + s.SkipFile = String(fmt.Sprintf("'SKIP_FILE_%d%%'", x)) return s } diff --git a/pkg/sdk/stages_dto_gen.go b/pkg/sdk/stages_dto_gen.go index c3212497d7..5330c66fdc 100644 --- a/pkg/sdk/stages_dto_gen.go +++ b/pkg/sdk/stages_dto_gen.go @@ -44,7 +44,7 @@ type InternalDirectoryTableOptionsRequest struct { type StageFileFormatRequest struct { FormatName *string Type *FileFormatType - Options *FileFormatTypeOptions + Options *FileFormatTypeOptionsRequest } type StageCopyOptionsRequest struct { @@ -60,7 +60,7 @@ type StageCopyOptionsRequest struct { type StageCopyOnErrorOptionsRequest struct { Continue *bool - SkipFile *bool + SkipFile *string AbortStatement *bool } diff --git a/pkg/sdk/stages_gen.go b/pkg/sdk/stages_gen.go index 037d68f6c8..b83be95246 100644 --- a/pkg/sdk/stages_gen.go +++ b/pkg/sdk/stages_gen.go @@ -67,9 +67,9 @@ type StageCopyOptions struct { } type StageCopyOnErrorOptions struct { - Continue *bool `ddl:"keyword" sql:"CONTINUE"` - SkipFile *bool `ddl:"keyword" sql:"SKIP_FILE"` - AbortStatement *bool `ddl:"keyword" sql:"ABORT_STATEMENT"` + Continue *bool `ddl:"keyword" sql:"CONTINUE"` + SkipFile *string `ddl:"keyword"` + AbortStatement *bool `ddl:"keyword" sql:"ABORT_STATEMENT"` } // CreateOnS3StageOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-stage. diff --git a/pkg/sdk/stages_impl_gen.go b/pkg/sdk/stages_impl_gen.go index 9d2e99839e..90a4f83f20 100644 --- a/pkg/sdk/stages_impl_gen.go +++ b/pkg/sdk/stages_impl_gen.go @@ -129,11 +129,7 @@ func (r *CreateInternalStageRequest) toOpts() *CreateInternalStageOptions { } } if r.FileFormat != nil { - opts.FileFormat = &StageFileFormat{ - FormatName: r.FileFormat.FormatName, - Type: r.FileFormat.Type, - Options: r.FileFormat.Options, - } + opts.FileFormat = r.FileFormat.toOpts() } if r.CopyOptions != nil { opts.CopyOptions = &StageCopyOptions{ @@ -198,7 +194,7 @@ func (r *CreateOnS3StageRequest) toOpts() *CreateOnS3StageOptions { opts.FileFormat = &StageFileFormat{ FormatName: r.FileFormat.FormatName, Type: r.FileFormat.Type, - Options: r.FileFormat.Options, + Options: r.FileFormat.Options.toOpts(), } } if r.CopyOptions != nil { @@ -256,7 +252,7 @@ func (r *CreateOnGCSStageRequest) toOpts() *CreateOnGCSStageOptions { opts.FileFormat = &StageFileFormat{ FormatName: r.FileFormat.FormatName, Type: r.FileFormat.Type, - Options: r.FileFormat.Options, + Options: r.FileFormat.Options.toOpts(), } } if r.CopyOptions != nil { @@ -319,7 +315,7 @@ func (r *CreateOnAzureStageRequest) toOpts() *CreateOnAzureStageOptions { opts.FileFormat = &StageFileFormat{ FormatName: r.FileFormat.FormatName, Type: r.FileFormat.Type, - Options: r.FileFormat.Options, + Options: r.FileFormat.Options.toOpts(), } } if r.CopyOptions != nil { @@ -372,7 +368,7 @@ func (r *CreateOnS3CompatibleStageRequest) toOpts() *CreateOnS3CompatibleStageOp opts.FileFormat = &StageFileFormat{ FormatName: r.FileFormat.FormatName, Type: r.FileFormat.Type, - Options: r.FileFormat.Options, + Options: r.FileFormat.Options.toOpts(), } } if r.CopyOptions != nil { @@ -418,7 +414,7 @@ func (r *AlterInternalStageStageRequest) toOpts() *AlterInternalStageStageOption opts.FileFormat = &StageFileFormat{ FormatName: r.FileFormat.FormatName, Type: r.FileFormat.Type, - Options: r.FileFormat.Options, + Options: r.FileFormat.Options.toOpts(), } } if r.CopyOptions != nil { @@ -474,7 +470,7 @@ func (r *AlterExternalS3StageStageRequest) toOpts() *AlterExternalS3StageStageOp opts.FileFormat = &StageFileFormat{ FormatName: r.FileFormat.FormatName, Type: r.FileFormat.Type, - Options: r.FileFormat.Options, + Options: r.FileFormat.Options.toOpts(), } } if r.CopyOptions != nil { @@ -521,7 +517,7 @@ func (r *AlterExternalGCSStageStageRequest) toOpts() *AlterExternalGCSStageStage opts.FileFormat = &StageFileFormat{ FormatName: r.FileFormat.FormatName, Type: r.FileFormat.Type, - Options: r.FileFormat.Options, + Options: r.FileFormat.Options.toOpts(), } } if r.CopyOptions != nil { @@ -573,7 +569,7 @@ func (r *AlterExternalAzureStageStageRequest) toOpts() *AlterExternalAzureStageS opts.FileFormat = &StageFileFormat{ FormatName: r.FileFormat.FormatName, Type: r.FileFormat.Type, - Options: r.FileFormat.Options, + Options: r.FileFormat.Options.toOpts(), } } if r.CopyOptions != nil { diff --git a/pkg/sdk/tables.go b/pkg/sdk/tables.go index 29c881939e..e43079cfa2 100644 --- a/pkg/sdk/tables.go +++ b/pkg/sdk/tables.go @@ -1,12 +1,610 @@ package sdk -// placeholder for the real implementation. -type CreateTableOptions struct{} +import ( + "context" + "database/sql" + "fmt" +) + +var _ convertibleRow[Table] = new(tableDBRow) + +type Tables interface { + Create(ctx context.Context, req *CreateTableRequest) error + CreateAsSelect(ctx context.Context, req *CreateTableAsSelectRequest) error + CreateUsingTemplate(ctx context.Context, req *CreateTableUsingTemplateRequest) error + CreateLike(ctx context.Context, req *CreateTableLikeRequest) error + CreateClone(ctx context.Context, req *CreateTableCloneRequest) error + Alter(ctx context.Context, req *AlterTableRequest) error + Drop(ctx context.Context, req *DropTableRequest) error + Show(ctx context.Context, req *ShowTableRequest) ([]Table, error) + ShowByID(ctx context.Context, id SchemaObjectIdentifier) (*Table, error) + DescribeColumns(ctx context.Context, req *DescribeTableColumnsRequest) ([]TableColumnDetails, error) + DescribeStage(ctx context.Context, req *DescribeTableStageRequest) ([]TableStageDetails, error) +} + +// TODO: check if [...] in the docs (like in https://docs.snowflake.com/en/sql-reference/sql/create-table#create-table-using-template) mean that we can reuse all parameters from "normal" createTableOptions +type createTableAsSelectOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + table bool `ddl:"static" sql:"TABLE"` + name SchemaObjectIdentifier `ddl:"identifier"` + Columns []TableAsSelectColumn `ddl:"list,parentheses"` + ClusterBy []string `ddl:"keyword,parentheses" sql:"CLUSTER BY"` + CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` + RowAccessPolicy *TableRowAccessPolicy `ddl:"keyword"` + Query string `ddl:"parameter,no_equals" sql:"AS"` +} + +type TableAsSelectColumn struct { + Name string `ddl:"keyword"` + Type *DataType `ddl:"keyword"` + MaskingPolicy *TableAsSelectColumnMaskingPolicy `ddl:"keyword"` +} + +type TableAsSelectColumnMaskingPolicy struct { + maskingPolicy bool `ddl:"static" sql:"MASKING POLICY"` + Name SchemaObjectIdentifier `ddl:"identifier"` +} + +type createTableUsingTemplateOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + table bool `ddl:"static" sql:"TABLE"` + name SchemaObjectIdentifier `ddl:"identifier"` + CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` + Query []string `ddl:"parameter,no_equals,parentheses" sql:"USING TEMPLATE"` +} + +type createTableLikeOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + table bool `ddl:"static" sql:"TABLE"` + name SchemaObjectIdentifier `ddl:"identifier"` + like bool `ddl:"static" sql:"LIKE"` + SourceTable SchemaObjectIdentifier `ddl:"identifier"` + ClusterBy []string `ddl:"keyword,parentheses" sql:"CLUSTER BY"` + CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` +} + +type createTableCloneOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + table bool `ddl:"static" sql:"TABLE"` + name SchemaObjectIdentifier `ddl:"identifier"` + clone bool `ddl:"static" sql:"CLONE"` + SourceTable SchemaObjectIdentifier `ddl:"identifier"` + ClonePoint *ClonePoint `ddl:"keyword"` + CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` +} + +type ClonePoint struct { + Moment CloneMoment `ddl:"parameter,no_equals"` + At TimeTravel `ddl:"list,parentheses,no_comma"` +} + +type CloneMoment string + +const ( + CloneMomentAt CloneMoment = "AT" + CloneMomentBefore CloneMoment = "BEFORE" +) + +type createTableOptions struct { + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + Scope *TableScope `ddl:"keyword"` + Kind *TableKind `ddl:"keyword"` + table bool `ddl:"static" sql:"TABLE"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + ColumnsAndConstraints CreateTableColumnsAndConstraints `ddl:"list,parentheses"` + ClusterBy []string `ddl:"keyword,parentheses" sql:"CLUSTER BY"` + EnableSchemaEvolution *bool `ddl:"parameter" sql:"ENABLE_SCHEMA_EVOLUTION"` + StageFileFormat *StageFileFormat `ddl:"list,parentheses,no_comma" sql:"STAGE_FILE_FORMAT ="` + StageCopyOptions *StageCopyOptions `ddl:"list,parentheses,no_comma" sql:"STAGE_COPY_OPTIONS ="` + 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"` + RowAccessPolicy *TableRowAccessPolicy `ddl:"keyword"` + Tags []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} + +type CreateTableColumnsAndConstraints struct { + Columns []TableColumn `ddl:"keyword"` + OutOfLineConstraint []OutOfLineConstraint `ddl:"list,no_parentheses"` +} + +type TableScope string + +const ( + GlobalTableScope TableScope = "GLOBAL" + LocalTableScope TableScope = "LOCAL" +) + +type TableKind string + +const ( + TemporaryTableKind TableKind = "TEMPORARY" + VolatileTableKind TableKind = "VOLATILE" + TransientTableKind TableKind = "TRANSIENT" +) + +type TableColumn struct { + Name string `ddl:"keyword"` + Type DataType `ddl:"keyword"` + InlineConstraint *ColumnInlineConstraint `ddl:"keyword"` + NotNull *bool `ddl:"keyword" sql:"NOT NULL"` + Collate *string `ddl:"parameter,no_equals,single_quotes" sql:"COLLATE"` + DefaultValue *ColumnDefaultValue `ddl:"keyword"` + MaskingPolicy *ColumnMaskingPolicy `ddl:"keyword"` + Tags []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` + Comment *string `ddl:"parameter,no_equals,single_quotes" sql:"COMMENT"` +} + +type ColumnDefaultValue struct { + // One of + Expression *string `ddl:"parameter,no_equals" sql:"DEFAULT"` + Identity *ColumnIdentity `ddl:"keyword" sql:"IDENTITY"` +} +type ColumnIdentity struct { + Start int `ddl:"parameter,no_quotes,no_equals" sql:"START"` + Increment int `ddl:"parameter,no_quotes,no_equals" sql:"INCREMENT"` + Order *bool `ddl:"keyword" sql:"ORDER"` + Noorder *bool `ddl:"keyword" sql:"NOORDER"` +} + +type ColumnMaskingPolicy struct { + With *bool `ddl:"keyword" sql:"WITH"` + maskingPolicy bool `ddl:"static" sql:"MASKING POLICY"` + Name SchemaObjectIdentifier `ddl:"identifier"` + Using []string `ddl:"keyword,parentheses" sql:"USING"` +} + +// OutOfLineConstraint is based on https://docs.snowflake.com/en/sql-reference/sql/create-table-constraint#out-of-line-unique-primary-foreign-key. +type OutOfLineConstraint struct { + Name string `ddl:"parameter,no_equals" sql:"CONSTRAINT"` + Type ColumnConstraintType `ddl:"keyword"` + Columns []string `ddl:"keyword,parentheses"` + ForeignKey *OutOfLineForeignKey `ddl:"keyword"` + + // optional + Enforced *bool `ddl:"keyword" sql:"ENFORCED"` + NotEnforced *bool `ddl:"keyword" sql:"NOT ENFORCED"` + Deferrable *bool `ddl:"keyword" sql:"DEFERRABLE"` + NotDeferrable *bool `ddl:"keyword" sql:"NOT DEFERRABLE"` + InitiallyDeferred *bool `ddl:"keyword" sql:"INITIALLY DEFERRED"` + InitiallyImmediate *bool `ddl:"keyword" sql:"INITIALLY IMMEDIATE"` + Enable *bool `ddl:"keyword" sql:"ENABLE"` + Disable *bool `ddl:"keyword" sql:"DISABLE"` + Validate *bool `ddl:"keyword" sql:"VALIDATE"` + NoValidate *bool `ddl:"keyword" sql:"NOVALIDATE"` + Rely *bool `ddl:"keyword" sql:"RELY"` + NoRely *bool `ddl:"keyword" sql:"NORELY"` +} + +type OutOfLineForeignKey struct { + references bool `ddl:"static" sql:"REFERENCES"` + TableName SchemaObjectIdentifier `ddl:"identifier"` + ColumnNames []string `ddl:"parameter,no_equals,parentheses"` + Match *MatchType `ddl:"parameter,no_equals" sql:"MATCH"` + On *ForeignKeyOnAction `ddl:"keyword"` +} + +type alterTableOptions struct { + alter bool `ddl:"static" sql:"ALTER"` + table bool `ddl:"static" sql:"TABLE"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + + // One of + NewName *SchemaObjectIdentifier `ddl:"identifier" sql:"RENAME TO"` + SwapWith *SchemaObjectIdentifier `ddl:"identifier" sql:"SWAP WITH"` + ClusteringAction *TableClusteringAction `ddl:"keyword"` + ColumnAction *TableColumnAction `ddl:"keyword"` + ConstraintAction *TableConstraintAction `ddl:"keyword"` + ExternalTableAction *TableExternalTableAction `ddl:"keyword"` + SearchOptimizationAction *TableSearchOptimizationAction `ddl:"keyword"` + Set *TableSet `ddl:"keyword" sql:"SET"` + SetTags []TagAssociation `ddl:"parameter,no_equals" sql:"SET TAG"` + UnsetTags []ObjectIdentifier `ddl:"keyword" sql:"UNSET TAG"` + Unset *TableUnset `ddl:"keyword" sql:"UNSET"` + AddRowAccessPolicy *TableAddRowAccessPolicy `ddl:"keyword"` + DropRowAccessPolicy *TableDropRowAccessPolicy `ddl:"keyword"` + DropAndAddRowAccessPolicy *TableDropAndAddRowAccessPolicy `ddl:"list,no_parentheses"` + DropAllAccessRowPolicies *bool `ddl:"keyword" sql:"DROP ALL ROW ACCESS POLICIES"` +} + +type TableClusteringAction struct { + // one of + ClusterBy []string `ddl:"keyword,parentheses" sql:"CLUSTER BY"` + Recluster *TableReclusterAction `ddl:"keyword" sql:"RECLUSTER"` + ChangeReclusterState *TableReclusterChangeState `ddl:"keyword"` + DropClusteringKey *bool `ddl:"keyword" sql:"DROP CLUSTERING KEY"` +} + +type TableReclusterAction struct { + MaxSize *int `ddl:"parameter" sql:"MAX_SIZE"` + Condition *string `ddl:"parameter,no_equals" sql:"WHERE"` +} + +type TableReclusterChangeState struct { + State *ReclusterState `ddl:"keyword"` + recluster bool `ddl:"static" sql:"RECLUSTER"` +} + +type ReclusterState string + +const ( + ReclusterStateResume ReclusterState = "RESUME" + ReclusterStateSuspend ReclusterState = "SUSPEND" +) + +type TableColumnAction struct { + // One of + Add *TableColumnAddAction `ddl:"keyword" sql:"ADD"` + Rename *TableColumnRenameAction `ddl:"keyword"` + Alter []TableColumnAlterAction `ddl:"keyword" sql:"ALTER"` + SetMaskingPolicy *TableColumnAlterSetMaskingPolicyAction `ddl:"keyword"` + UnsetMaskingPolicy *TableColumnAlterUnsetMaskingPolicyAction `ddl:"keyword"` + SetTags *TableColumnAlterSetTagsAction `ddl:"keyword"` + UnsetTags *TableColumnAlterUnsetTagsAction `ddl:"keyword"` + DropColumns *TableColumnAlterDropColumns `ddl:"keyword"` +} + +type TableColumnAddAction struct { + column bool `ddl:"static" sql:"COLUMN"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + Name string `ddl:"keyword"` + Type DataType `ddl:"keyword"` + DefaultValue *ColumnDefaultValue `ddl:"keyword"` + InlineConstraint *TableColumnAddInlineConstraint `ddl:"keyword"` + MaskingPolicy *ColumnMaskingPolicy `ddl:"keyword"` + Tags []TagAssociation `ddl:"keyword,parentheses" sql:"TAG"` +} + +type TableColumnAddInlineConstraint struct { + NotNull *bool `ddl:"keyword" sql:"NOT NULL"` + Name string `ddl:"parameter,no_equals" sql:"CONSTRAINT"` + Type ColumnConstraintType `ddl:"keyword"` + ForeignKey *ColumnAddForeignKey `ddl:"keyword"` +} + +type ColumnAddForeignKey struct { + TableName string `ddl:"keyword" sql:"REFERENCES"` + ColumnName string `ddl:"keyword,parentheses"` +} + +type TableColumnRenameAction struct { + OldName string `ddl:"parameter,no_equals" sql:"RENAME COLUMN"` + NewName string `ddl:"parameter,no_equals" sql:"TO"` +} + +type TableColumnAlterAction struct { + column bool `ddl:"static" sql:"COLUMN"` + Name string `ddl:"keyword"` + + // One of + DropDefault *bool `ddl:"keyword" sql:"DROP DEFAULT"` + SetDefault *SequenceName `ddl:"parameter,no_equals" sql:"SET DEFAULT"` + NotNullConstraint *TableColumnNotNullConstraint + Type *DataType `ddl:"parameter,no_equals" sql:"SET DATA TYPE"` + Comment *string `ddl:"parameter,no_equals,single_quotes" sql:"COMMENT"` + UnsetComment *bool `ddl:"keyword" sql:"UNSET COMMENT"` +} + +type TableColumnAlterSetMaskingPolicyAction struct { + alter bool `ddl:"static" sql:"ALTER COLUMN"` + ColumnName string `ddl:"keyword"` + setMaskingPolicy bool `ddl:"static" sql:"SET MASKING POLICY"` + MaskingPolicyName SchemaObjectIdentifier `ddl:"identifier"` + Using []string `ddl:"keyword,parentheses" sql:"USING"` + Force *bool `ddl:"keyword" sql:"FORCE"` +} + +type TableColumnAlterUnsetMaskingPolicyAction struct { + alter bool `ddl:"static" sql:"ALTER COLUMN"` + ColumnName string `ddl:"keyword"` + setMaskingPolicy bool `ddl:"static" sql:"UNSET MASKING POLICY"` +} + +type TableColumnAlterSetTagsAction struct { + alter bool `ddl:"static" sql:"ALTER COLUMN"` + ColumnName string `ddl:"keyword"` + set bool `ddl:"static" sql:"SET"` + Tags []TagAssociation `ddl:"keyword" sql:"TAG"` +} + +type TableColumnAlterUnsetTagsAction struct { + alter bool `ddl:"static" sql:"ALTER COLUMN"` + ColumnName string `ddl:"keyword"` + unset bool `ddl:"static" sql:"UNSET"` + Tags []ObjectIdentifier `ddl:"keyword" sql:"TAG"` +} + +type TableColumnAlterDropColumns struct { + dropColumn bool `ddl:"static" sql:"DROP COLUMN"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + Columns []string `ddl:"keyword"` +} + +type TableColumnAlterSequenceName interface { + String() string +} + +type SequenceName string + +func (sn SequenceName) String() string { + return fmt.Sprintf("%s.NEXTVAL", string(sn)) +} + +type TableColumnNotNullConstraint struct { + Set *bool `ddl:"keyword" sql:"SET NOT NULL"` + Drop *bool `ddl:"keyword" sql:"DROP NOT NULL"` +} + +type TableConstraintAction struct { + Add *OutOfLineConstraint `ddl:"keyword" sql:"ADD"` + Rename *TableConstraintRenameAction `ddl:"keyword" sql:"RENAME CONSTRAINT"` + Alter *TableConstraintAlterAction `ddl:"keyword" sql:"ALTER"` + Drop *TableConstraintDropAction `ddl:"keyword" sql:"DROP"` +} + +type TableConstraintRenameAction struct { + OldName string `ddl:"keyword"` + NewName string `ddl:"parameter,no_equals" sql:"TO"` +} + +type TableConstraintAlterAction struct { + // One of + ConstraintName *string `ddl:"parameter,no_equals" sql:"CONSTRAINT"` + PrimaryKey *bool `ddl:"keyword" sql:"PRIMARY KEY"` + Unique *bool `ddl:"keyword" sql:"UNIQUE"` + ForeignKey *bool `ddl:"keyword" sql:"FOREIGN KEY"` + + Columns []string `ddl:"keyword,parentheses"` + + // Optional + Enforced *bool `ddl:"keyword" sql:"ENFORCED"` + NotEnforced *bool `ddl:"keyword" sql:"NOT ENFORCED"` + Validate *bool `ddl:"keyword" sql:"VALIDATE"` + NoValidate *bool `ddl:"keyword" sql:"NOVALIDATE"` + Rely *bool `ddl:"keyword" sql:"RELY"` + NoRely *bool `ddl:"keyword" sql:"NORELY"` +} + +type TableConstraintDropAction struct { + // One of + ConstraintName *string `ddl:"parameter,no_equals" sql:"CONSTRAINT"` + PrimaryKey *bool `ddl:"keyword" sql:"PRIMARY KEY"` + Unique *bool `ddl:"keyword" sql:"UNIQUE"` + ForeignKey *bool `ddl:"keyword" sql:"FOREIGN KEY"` + + Columns []string `ddl:"keyword,parentheses"` + + // Optional + Cascade *bool `ddl:"keyword" sql:"CASCADE"` + Restrict *bool `ddl:"keyword" sql:"RESTRICT"` +} + +type TableUnsetTags struct { + Tag []ObjectIdentifier `ddl:"keyword"` +} + +type TableExternalTableAction struct { + // One of + Add *TableExternalTableColumnAddAction `ddl:"keyword"` + Rename *TableExternalTableColumnRenameAction `ddl:"keyword"` + Drop *TableExternalTableColumnDropAction `ddl:"keyword"` +} + +type TableExternalTableColumnAddAction struct { + addColumn bool `ddl:"static" sql:"ADD COLUMN"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + Name string `ddl:"keyword"` + Type DataType `ddl:"keyword"` + Expression []string `ddl:"parameter,no_equals,parentheses" sql:"AS"` +} + +type TableExternalTableColumnRenameAction struct { + OldName string `ddl:"parameter,no_equals" sql:"RENAME COLUMN"` + NewName string `ddl:"parameter,no_equals" sql:"TO"` +} + +type TableExternalTableColumnDropAction struct { + columns bool `ddl:"static" sql:"DROP COLUMN"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + Names []string `ddl:"keyword"` +} + +type TableSearchOptimizationAction struct { + // One of + Add *AddSearchOptimization `ddl:"keyword"` + Drop *DropSearchOptimization `ddl:"keyword"` +} + +type AddSearchOptimization struct { + addSearchOptimization bool `ddl:"static" sql:"ADD SEARCH OPTIMIZATION"` + // Optional + On []string `ddl:"keyword" sql:"ON"` +} + +type DropSearchOptimization struct { + dropSearchOptimization bool `ddl:"static" sql:"DROP SEARCH OPTIMIZATION"` + // Optional + On []string `ddl:"keyword" sql:"ON"` +} + +type TableSet struct { + // Optional + EnableSchemaEvolution *bool `ddl:"parameter" sql:"ENABLE_SCHEMA_EVOLUTION"` + StageFileFormat *StageFileFormat `ddl:"list,parentheses" sql:"STAGE_FILE_FORMAT ="` + StageCopyOptions *StageCopyOptions `ddl:"list,parentheses" sql:"STAGE_COPY_OPTIONS ="` + 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"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` +} + +type TableUnset 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"` + DefaultDDLCollation *bool `ddl:"keyword" sql:"DEFAULT_DDL_COLLATION"` + EnableSchemaEvolution *bool `ddl:"keyword" sql:"ENABLE_SCHEMA_EVOLUTION"` + Comment *bool `ddl:"keyword" sql:"COMMENT"` +} + +type TableAddRowAccessPolicy struct { + add bool `ddl:"static" sql:"ADD"` + RowAccessPolicy SchemaObjectIdentifier `ddl:"identifier" sql:"ROW ACCESS POLICY"` + On []string `ddl:"keyword,parentheses" sql:"ON"` +} + +type TableDropRowAccessPolicy struct { + drop bool `ddl:"static" sql:"DROP"` + RowAccessPolicy SchemaObjectIdentifier `ddl:"identifier" sql:"ROW ACCESS POLICY"` +} + +type TableDropAndAddRowAccessPolicy struct { + Drop TableDropRowAccessPolicy `ddl:"keyword"` + Add TableAddRowAccessPolicy `ddl:"keyword"` +} + +// dropTableOptions is based on https://docs.snowflake.com/en/sql-reference/sql/drop-table +type dropTableOptions struct { + drop bool `ddl:"static" sql:"DROP"` + table bool `ddl:"static" sql:"TABLE"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + + // One of + Cascade *bool `ddl:"keyword" sql:"CASCADE"` + Restrict *bool `ddl:"keyword" sql:"RESTRICT"` +} + +type showTableOptions struct { + show bool `ddl:"static" sql:"SHOW"` + Terse *bool `ddl:"keyword" sql:"TERSE"` + tables bool `ddl:"static" sql:"TABLES"` + History *bool `ddl:"keyword" sql:"HISTORY"` + Like *Like `ddl:"keyword" sql:"LIKE"` + In *In `ddl:"keyword" sql:"IN"` + StartsWith *string `ddl:"parameter,single_quotes,no_equals" sql:"STARTS WITH"` + LimitFrom *LimitFrom `ddl:"keyword" sql:"LIMIT"` +} + +type tableDBRow struct { + CreatedOn string `db:"created_on"` + Name string `db:"name"` + SchemaName string `db:"schema_name"` + DatabaseName string `db:"database_name"` + Kind string `db:"kind"` + Comment sql.NullString `db:"comment"` + ClusterBy sql.NullString `db:"cluster_by"` + Rows sql.NullInt64 `db:"rows"` + Bytes sql.NullInt64 `db:"bytes"` + Owner string `db:"owner"` + RetentionTime sql.NullInt64 `db:"retention_time"` + DroppedOn sql.NullString `db:"dropped_on"` + AutomaticClustering sql.NullString `db:"automatic_clustering"` + ChangeTracking sql.NullString `db:"change_tracking"` + SearchOptimization sql.NullString `db:"search_optimization"` + SearchOptimizationProgress sql.NullString `db:"search_optimization_progress"` + SearchOptimizationBytes sql.NullInt64 `db:"search_optimization_bytes"` + IsExternal sql.NullString `db:"is_external"` + EnableSchemaEvolution sql.NullString `db:"enable_schema_evolution"` + OwnerRoleType sql.NullString `db:"owner_role_type"` + IsEvent sql.NullString `db:"is_event"` + Budget sql.NullString `db:"budget"` +} type Table struct { - DatabaseName string - SchemaName string - Name string + CreatedOn string + Name string + DatabaseName string + SchemaName string + Kind string + Comment string + ClusterBy string + Rows int + Bytes *int + Owner string + RetentionTime int + DroppedOn *string + AutomaticClustering bool + ChangeTracking bool + SearchOptimization bool + SearchOptimizationProgress string + SearchOptimizationBytes *int + IsExternal bool + EnableSchemaEvolution bool + OwnerRoleType string + IsEvent bool + Budget *string +} + +func (row tableDBRow) convert() *Table { + table := Table{ + CreatedOn: row.CreatedOn, + Name: row.Name, + SchemaName: row.SchemaName, + DatabaseName: row.DatabaseName, + Owner: row.Owner, + Kind: row.Kind, + } + if row.Rows.Valid { + table.Rows = int(row.Rows.Int64) + } + if row.Bytes.Valid { + table.Bytes = Int(int(row.Bytes.Int64)) + } + if row.RetentionTime.Valid { + table.RetentionTime = int(row.RetentionTime.Int64) + } + if row.DroppedOn.Valid { + table.DroppedOn = String(row.DroppedOn.String) + } + if row.AutomaticClustering.Valid { + table.AutomaticClustering = row.AutomaticClustering.String == "ON" + } + if row.ChangeTracking.Valid { + table.ChangeTracking = row.ChangeTracking.String == "ON" + } + if row.SearchOptimization.Valid { + table.SearchOptimization = row.SearchOptimization.String == "ON" + } + if row.SearchOptimizationProgress.Valid { + table.SearchOptimizationProgress = row.SearchOptimizationProgress.String + } + if row.SearchOptimizationBytes.Valid { + table.SearchOptimizationBytes = Int(int(row.SearchOptimizationBytes.Int64)) + } + if row.IsExternal.Valid { + table.IsExternal = row.IsExternal.String == "Y" + } + if row.IsEvent.Valid { + table.IsEvent = row.IsEvent.String == "Y" + } + if row.EnableSchemaEvolution.Valid { + table.EnableSchemaEvolution = row.EnableSchemaEvolution.String == "Y" + } + if row.Comment.Valid { + table.Comment = row.Comment.String + } + if row.ClusterBy.Valid { + table.ClusterBy = row.ClusterBy.String + } + if row.OwnerRoleType.Valid { + table.OwnerRoleType = row.OwnerRoleType.String + } + if row.Budget.Valid { + table.Budget = String(row.Budget.String) + } + return &table } func (v *Table) ID() SchemaObjectIdentifier { @@ -16,3 +614,98 @@ func (v *Table) ID() SchemaObjectIdentifier { func (v *Table) ObjectType() ObjectType { return ObjectTypeTable } + +// describeExternalTableColumnsOptions based on https://docs.snowflake.com/en/sql-reference/sql/desc-table +type describeTableColumnsOptions struct { + describeTable bool `ddl:"static" sql:"DESCRIBE TABLE"` + name SchemaObjectIdentifier `ddl:"identifier"` + columnsType bool `ddl:"static" sql:"TYPE = COLUMNS"` +} + +type TableColumnDetails struct { + Name string + Type DataType + Kind string + IsNullable bool + Default *string + IsPrimary bool + IsUnique bool + Check *bool + Expression *string + Comment *string + PolicyName *string +} + +// tableColumnDetailsRow based on https://docs.snowflake.com/en/sql-reference/sql/desc-table +type tableColumnDetailsRow struct { + Name string `db:"name"` + Type DataType `db:"type"` + Kind string `db:"kind"` + IsNullable string `db:"null?"` + Default sql.NullString `db:"default"` + IsPrimary string `db:"primary key"` + IsUnique string `db:"unique key"` + Check sql.NullString `db:"check"` + Expression sql.NullString `db:"expression"` + Comment sql.NullString `db:"comment"` + PolicyName sql.NullString `db:"policy name"` +} + +func (r tableColumnDetailsRow) convert() *TableColumnDetails { + details := &TableColumnDetails{ + Name: r.Name, + Type: r.Type, + Kind: r.Kind, + IsNullable: r.IsNullable == "Y", + IsPrimary: r.IsPrimary == "Y", + IsUnique: r.IsUnique == "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) + } + if r.PolicyName.Valid { + details.PolicyName = String(r.PolicyName.String) + } + return details +} + +type describeTableStageOptions struct { + describeTable bool `ddl:"static" sql:"DESCRIBE TABLE"` + name SchemaObjectIdentifier `ddl:"identifier"` + stageType bool `ddl:"static" sql:"TYPE = STAGE"` +} + +type TableStageDetails struct { + ParentProperty string + Property string + PropertyType string + PropertyValue string + PropertyDefault string +} + +type tableStageDetailsRow struct { + ParentProperty string `db:"parent_property"` + Property string `db:"property"` + PropertyType string `db:"property_type"` + PropertyValue string `db:"property_value"` + PropertyDefault string `db:"property_default"` +} + +func (r tableStageDetailsRow) convert() *TableStageDetails { + return &TableStageDetails{ + ParentProperty: r.ParentProperty, + Property: r.Property, + PropertyType: r.PropertyType, + PropertyValue: r.PropertyValue, + PropertyDefault: r.PropertyDefault, + } +} diff --git a/pkg/sdk/tables_dto.go b/pkg/sdk/tables_dto.go new file mode 100644 index 0000000000..c490558deb --- /dev/null +++ b/pkg/sdk/tables_dto.go @@ -0,0 +1,541 @@ +package sdk + +import "time" + +//go:generate go run ./dto-builder-generator/main.go + +type CreateTableAsSelectRequest struct { + orReplace *bool + name SchemaObjectIdentifier // required + columns []TableAsSelectColumnRequest // required + query string // required +} + +type TableAsSelectColumnRequest struct { + orReplace *bool + name string // required + type_ *DataType + maskingPolicyName *SchemaObjectIdentifier + clusterBy []string + copyGrants *bool +} + +type CreateTableUsingTemplateRequest struct { + orReplace *bool + name SchemaObjectIdentifier // required + copyGrants *bool + Query string // required +} + +type CreateTableLikeRequest struct { + orReplace *bool + name SchemaObjectIdentifier // required + sourceTable SchemaObjectIdentifier // required + clusterBy []string + copyGrants *bool +} + +type CreateTableCloneRequest struct { + orReplace *bool + name SchemaObjectIdentifier // required + sourceTable SchemaObjectIdentifier // required + copyGrants *bool + ClonePoint *ClonePointRequest +} + +type ClonePointRequest struct { + Moment CloneMoment + At TimeTravelRequest +} + +type TimeTravelRequest struct { + Timestamp *time.Time + Offset *int + Statement *string +} + +type CreateTableRequest struct { + orReplace *bool + ifNotExists *bool + scope *TableScope + kind *TableKind + name SchemaObjectIdentifier // required + columns []TableColumnRequest // required + OutOfLineConstraints []OutOfLineConstraintRequest + clusterBy []string + enableSchemaEvolution *bool + stageFileFormat *StageFileFormatRequest + stageCopyOptions *StageCopyOptionsRequest + DataRetentionTimeInDays *int + MaxDataExtensionTimeInDays *int + ChangeTracking *bool + DefaultDDLCollation *string + CopyGrants *bool + RowAccessPolicy *RowAccessPolicyRequest + Tags []TagAssociationRequest + Comment *string +} + +type RowAccessPolicyRequest struct { + Name SchemaObjectIdentifier // required + On []string // required +} + +type TableColumnRequest struct { + name string // required + type_ DataType // required + collate *string + comment *string + defaultValue *ColumnDefaultValueRequest + notNull *bool + maskingPolicy *ColumnMaskingPolicyRequest + with *bool + tags []TagAssociation + inlineConstraint *ColumnInlineConstraintRequest +} + +type ColumnDefaultValueRequest struct { + // One of + expression *string + identity *ColumnIdentityRequest +} + +type ColumnIdentityRequest struct { + Start int // required + Increment int // required + Order *bool + Noorder *bool +} + +type ColumnMaskingPolicyRequest struct { + with *bool + name SchemaObjectIdentifier // required + using []string +} + +type ColumnInlineConstraintRequest struct { + Name string // required + type_ ColumnConstraintType // required + foreignKey *InlineForeignKeyRequest + enforced *bool + notEnforced *bool + deferrable *bool + notDeferrable *bool + initiallyDeferred *bool + initiallyImmediate *bool + enable *bool + disable *bool + validate *bool + noValidate *bool + rely *bool + noRely *bool +} + +type OutOfLineConstraintRequest struct { + Name string // required + Type ColumnConstraintType // required + Columns []string + ForeignKey *OutOfLineForeignKeyRequest + + // Optional + Enforced *bool + NotEnforced *bool + Deferrable *bool + NotDeferrable *bool + InitiallyDeferred *bool + InitiallyImmediate *bool + Enable *bool + Disable *bool + Validate *bool + NoValidate *bool + Rely *bool + NoRely *bool +} + +type InlineForeignKeyRequest struct { + TableName string // required + ColumnName []string + Match *MatchType + On *ForeignKeyOnAction +} + +type OutOfLineForeignKeyRequest struct { + TableName SchemaObjectIdentifier // required + ColumnNames []string // required + Match *MatchType + On *ForeignKeyOnAction +} + +type AlterTableRequest struct { + IfExists *bool + name SchemaObjectIdentifier // required + NewName *SchemaObjectIdentifier + SwapWith *SchemaObjectIdentifier + ClusteringAction *TableClusteringActionRequest + ColumnAction *TableColumnActionRequest + ConstraintAction *TableConstraintActionRequest + ExternalTableAction *TableExternalTableActionRequest + SearchOptimizationAction *TableSearchOptimizationActionRequest + Set *TableSetRequest + SetTags []TagAssociationRequest + UnsetTags []ObjectIdentifier + Unset *TableUnsetRequest + AddRowAccessPolicy *TableAddRowAccessPolicyRequest + DropRowAccessPolicy *TableDropRowAccessPolicyRequest + DropAndAddRowAccessPolicy *TableDropAndAddRowAccessPolicy + DropAllAccessRowPolicies *bool +} + +type DropTableRequest struct { + IfExists *bool + Name SchemaObjectIdentifier // required + // One of + Cascade *bool + Restrict *bool +} + +func (s *DropTableRequest) toOpts() *dropTableOptions { + return &dropTableOptions{ + IfExists: s.IfExists, + name: s.Name, + Cascade: s.Cascade, + Restrict: s.Restrict, + } +} + +func (s *ShowTableRequest) toOpts() *showTableOptions { + var like *Like + if s.likePattern != "" { + like = &Like{ + Pattern: &s.likePattern, + } + } + var limitFrom *LimitFrom + if s.limitFrom != nil { + limitFrom = &LimitFrom{ + Rows: s.limitFrom.Rows, + From: s.limitFrom.From, + } + } + return &showTableOptions{ + Terse: s.terse, + History: s.history, + Like: like, + StartsWith: s.startsWith, + LimitFrom: limitFrom, + In: s.in, + } +} + +type TableAddRowAccessPolicyRequest struct { + RowAccessPolicy SchemaObjectIdentifier // required + On []string // required +} + +type TableDropRowAccessPolicyRequest struct { + RowAccessPolicy SchemaObjectIdentifier // required +} + +type TableDropAndAddRowAccessPolicyRequest struct { + Drop TableDropRowAccessPolicyRequest // required + Add TableAddRowAccessPolicyRequest // required +} + +type TableUnsetRequest struct { + DataRetentionTimeInDays bool + MaxDataExtensionTimeInDays bool + ChangeTracking bool + DefaultDDLCollation bool + EnableSchemaEvolution bool + Comment bool +} + +type AddRowAccessPolicyRequest struct { + PolicyName string // required + ColumnName []string // required +} + +type TagAssociationRequest struct { + Name ObjectIdentifier // required + Value string // required +} + +type FileFormatTypeOptionsRequest struct { + CSVCompression *CSVCompression + CSVRecordDelimiter *string + CSVFieldDelimiter *string + CSVFileExtension *string + CSVParseHeader *bool + CSVSkipHeader *int + CSVSkipBlankLines *bool + CSVDateFormat *string + CSVTimeFormat *string + CSVTimestampFormat *string + CSVBinaryFormat *BinaryFormat + CSVEscape *string + CSVEscapeUnenclosedField *string + CSVTrimSpace *bool + CSVFieldOptionallyEnclosedBy *string + CSVNullIf *[]NullString + CSVErrorOnColumnCountMismatch *bool + CSVReplaceInvalidCharacters *bool + CSVEmptyFieldAsNull *bool + CSVSkipByteOrderMark *bool + CSVEncoding *CSVEncoding + + // JSON type options + JSONCompression *JSONCompression + JSONDateFormat *string + JSONTimeFormat *string + JSONTimestampFormat *string + JSONBinaryFormat *BinaryFormat + JSONTrimSpace *bool + JSONNullIf []NullString + JSONFileExtension *string + JSONEnableOctal *bool + JSONAllowDuplicate *bool + JSONStripOuterArray *bool + JSONStripNullValues *bool + JSONReplaceInvalidCharacters *bool + JSONIgnoreUTF8Errors *bool + JSONSkipByteOrderMark *bool + + // AVRO type options + AvroCompression *AvroCompression + AvroTrimSpace *bool + AvroReplaceInvalidCharacters *bool + AvroNullIf *[]NullString + + // ORC type options + ORCTrimSpace *bool + ORCReplaceInvalidCharacters *bool + ORCNullIf *[]NullString + + // PARQUET type options + ParquetCompression *ParquetCompression + ParquetSnappyCompression *bool + ParquetBinaryAsText *bool + ParquetTrimSpace *bool + ParquetReplaceInvalidCharacters *bool + ParquetNullIf *[]NullString + + // XML type options + XMLCompression *XMLCompression + XMLIgnoreUTF8Errors *bool + XMLPreserveSpace *bool + XMLStripOuterElement *bool + XMLDisableSnowflakeData *bool + XMLDisableAutoConvert *bool + XMLReplaceInvalidCharacters *bool + XMLSkipByteOrderMark *bool + + Comment *string +} + +type TableClusteringActionRequest struct { + // One of + ClusterBy []string + Recluster *TableReclusterActionRequest + ChangeReclusterState *ReclusterState + DropClusteringKey *bool +} + +type TableReclusterActionRequest struct { + MaxSize *int + Condition *string +} + +type TableReclusterChangeStateRequest struct { + State ReclusterState +} + +type TableColumnActionRequest struct { + Add *TableColumnAddActionRequest + Rename *TableColumnRenameActionRequest + Alter []TableColumnAlterActionRequest + SetMaskingPolicy *TableColumnAlterSetMaskingPolicyActionRequest + UnsetMaskingPolicy *TableColumnAlterUnsetMaskingPolicyActionRequest + SetTags *TableColumnAlterSetTagsActionRequest + UnsetTags *TableColumnAlterUnsetTagsActionRequest + DropColumnsIfExists *bool + DropColumns []string +} + +type TableColumnAddActionRequest struct { + IfNotExists *bool + Name string // required + Type DataType // required + DefaultValue *ColumnDefaultValueRequest + InlineConstraint *TableColumnAddInlineConstraintRequest + MaskingPolicy *ColumnMaskingPolicyRequest + With *bool + Tags []TagAssociation +} + +type TableColumnAddInlineConstraintRequest struct { + NotNull *bool + Name string + Type ColumnConstraintType + ForeignKey *ColumnAddForeignKey +} + +type ColumnAddForeignKeyRequest struct { + TableName string + ColumnName string +} + +type TableColumnRenameActionRequest struct { + OldName string // required + NewName string // required +} + +type TableColumnAlterActionRequest struct { + Column bool // required + Name string // required + + // One of + DropDefault *bool + SetDefault *SequenceName + NotNullConstraint *TableColumnNotNullConstraintRequest + Type *DataType + Comment *string + UnsetComment *bool +} + +type TableColumnAlterSetMaskingPolicyActionRequest struct { + ColumnName string // required + MaskingPolicyName SchemaObjectIdentifier // required + Using []string // required + Force *bool +} + +type TableColumnAlterUnsetMaskingPolicyActionRequest struct { + ColumnName string // required +} + +type TableColumnAlterSetTagsActionRequest struct { + ColumnName string // required + Tags []TagAssociation // required +} + +type TableColumnAlterUnsetTagsActionRequest struct { + ColumnName string // required + Tags []ObjectIdentifier // required +} + +type TableColumnNotNullConstraintRequest struct { + Set *bool + Drop *bool +} + +type TableConstraintActionRequest struct { + Add *OutOfLineConstraintRequest + Rename *TableConstraintRenameActionRequest + Alter *TableConstraintAlterActionRequest + Drop *TableConstraintDropActionRequest +} + +type TableConstraintRenameActionRequest struct { + OldName string + NewName string +} + +type TableConstraintAlterActionRequest struct { + // One of + ConstraintName *string + PrimaryKey *bool + Unique *bool + ForeignKey *bool + + Columns []string // required + // Optional + Enforced *bool + NotEnforced *bool + Validate *bool + NoValidate *bool + Rely *bool + NoRely *bool +} + +type TableConstraintDropActionRequest struct { + // One of + ConstraintName *string + PrimaryKey *bool + Unique *bool + ForeignKey *bool + + Columns []string // required + + // Optional + Cascade *bool + Restrict *bool +} + +type TableExternalTableActionRequest struct { + // One of + Add *TableExternalTableColumnAddActionRequest + Rename *TableExternalTableColumnRenameActionRequest + Drop *TableExternalTableColumnDropActionRequest +} + +type TableSearchOptimizationActionRequest struct { + // One of + AddSearchOptimizationOn []string + DropSearchOptimizationOn []string +} + +type TableSetRequest struct { + EnableSchemaEvolution *bool + StageFileFormat *StageFileFormatRequest + StageCopyOptions *StageCopyOptionsRequest + DataRetentionTimeInDays *int + MaxDataExtensionTimeInDays *int + ChangeTracking *bool + DefaultDDLCollation *string + Comment *string +} + +type TableExternalTableColumnAddActionRequest struct { + IfNotExists *bool + Name string + Type DataType + Expression string +} + +type TableExternalTableColumnRenameActionRequest struct { + OldName string + NewName string +} + +type TableExternalTableColumnDropActionRequest struct { + Columns []string //required + IfExists *bool +} + +type ShowTableRequest struct { + terse *bool + history *bool + likePattern string + in *In + startsWith *string + limitFrom *LimitFrom +} + +type ShowTableInRequest struct { + Account *bool + Database AccountObjectIdentifier + Schema DatabaseObjectIdentifier +} + +type LimitFromRequest struct { + rows *int + from *string +} + +type DescribeTableColumnsRequest struct { + id SchemaObjectIdentifier // required +} + +type DescribeTableStageRequest struct { + id SchemaObjectIdentifier // required +} diff --git a/pkg/sdk/tables_dto_generated.go b/pkg/sdk/tables_dto_generated.go new file mode 100644 index 0000000000..ed88b35cb3 --- /dev/null +++ b/pkg/sdk/tables_dto_generated.go @@ -0,0 +1,1652 @@ +package sdk + +import ( + "time" +) + +func NewCreateTableAsSelectRequest( + name SchemaObjectIdentifier, + columns []TableAsSelectColumnRequest, + query string, +) *CreateTableAsSelectRequest { + s := CreateTableAsSelectRequest{} + s.name = name + s.columns = columns + s.query = query + return &s +} + +func (s *CreateTableAsSelectRequest) WithOrReplace(orReplace *bool) *CreateTableAsSelectRequest { + s.orReplace = orReplace + return s +} + +func NewTableAsSelectColumnRequest( + name string, +) *TableAsSelectColumnRequest { + s := TableAsSelectColumnRequest{} + s.name = name + return &s +} + +func (s *TableAsSelectColumnRequest) WithOrReplace(orReplace *bool) *TableAsSelectColumnRequest { + s.orReplace = orReplace + return s +} + +func (s *TableAsSelectColumnRequest) WithType_(type_ *DataType) *TableAsSelectColumnRequest { + s.type_ = type_ + return s +} + +func (s *TableAsSelectColumnRequest) WithMaskingPolicyName(maskingPolicyName *SchemaObjectIdentifier) *TableAsSelectColumnRequest { + s.maskingPolicyName = maskingPolicyName + return s +} + +func (s *TableAsSelectColumnRequest) WithClusterBy(clusterBy []string) *TableAsSelectColumnRequest { + s.clusterBy = clusterBy + return s +} + +func (s *TableAsSelectColumnRequest) WithCopyGrants(copyGrants *bool) *TableAsSelectColumnRequest { + s.copyGrants = copyGrants + return s +} + +func NewCreateTableUsingTemplateRequest( + name SchemaObjectIdentifier, + query string, +) *CreateTableUsingTemplateRequest { + s := CreateTableUsingTemplateRequest{} + s.name = name + s.Query = query + return &s +} + +func (s *CreateTableUsingTemplateRequest) WithOrReplace(orReplace *bool) *CreateTableUsingTemplateRequest { + s.orReplace = orReplace + return s +} + +func (s *CreateTableUsingTemplateRequest) WithCopyGrants(copyGrants *bool) *CreateTableUsingTemplateRequest { + s.copyGrants = copyGrants + return s +} + +func NewCreateTableLikeRequest( + name SchemaObjectIdentifier, + sourceTable SchemaObjectIdentifier, +) *CreateTableLikeRequest { + s := CreateTableLikeRequest{} + s.name = name + s.sourceTable = sourceTable + return &s +} + +func (s *CreateTableLikeRequest) WithOrReplace(orReplace *bool) *CreateTableLikeRequest { + s.orReplace = orReplace + return s +} + +func (s *CreateTableLikeRequest) WithClusterBy(clusterBy []string) *CreateTableLikeRequest { + s.clusterBy = clusterBy + return s +} + +func (s *CreateTableLikeRequest) WithCopyGrants(copyGrants *bool) *CreateTableLikeRequest { + s.copyGrants = copyGrants + return s +} + +func NewCreateTableCloneRequest( + name SchemaObjectIdentifier, + sourceTable SchemaObjectIdentifier, +) *CreateTableCloneRequest { + s := CreateTableCloneRequest{} + s.name = name + s.sourceTable = sourceTable + return &s +} + +func (s *CreateTableCloneRequest) WithOrReplace(orReplace *bool) *CreateTableCloneRequest { + s.orReplace = orReplace + return s +} + +func (s *CreateTableCloneRequest) WithCopyGrants(copyGrants *bool) *CreateTableCloneRequest { + s.copyGrants = copyGrants + return s +} + +func (s *CreateTableCloneRequest) WithClonePoint(clonePoint *ClonePointRequest) *CreateTableCloneRequest { + s.ClonePoint = clonePoint + return s +} + +func NewClonePointRequest() *ClonePointRequest { + return &ClonePointRequest{} +} + +func (s *ClonePointRequest) WithMoment(moment CloneMoment) *ClonePointRequest { + s.Moment = moment + return s +} + +func (s *ClonePointRequest) WithAt(at TimeTravelRequest) *ClonePointRequest { + s.At = at + return s +} + +func NewTimeTravelRequest() *TimeTravelRequest { + return &TimeTravelRequest{} +} + +func (s *TimeTravelRequest) WithTimestamp(timestamp *time.Time) *TimeTravelRequest { + s.Timestamp = timestamp + return s +} + +func (s *TimeTravelRequest) WithOffset(offset *int) *TimeTravelRequest { + s.Offset = offset + return s +} + +func (s *TimeTravelRequest) WithStatement(statement *string) *TimeTravelRequest { + s.Statement = statement + return s +} + +func NewCreateTableRequest( + name SchemaObjectIdentifier, + columns []TableColumnRequest, +) *CreateTableRequest { + s := CreateTableRequest{} + s.name = name + s.columns = columns + return &s +} + +func (s *CreateTableRequest) WithOrReplace(orReplace *bool) *CreateTableRequest { + s.orReplace = orReplace + return s +} + +func (s *CreateTableRequest) WithIfNotExists(ifNotExists *bool) *CreateTableRequest { + s.ifNotExists = ifNotExists + return s +} + +func (s *CreateTableRequest) WithScope(scope *TableScope) *CreateTableRequest { + s.scope = scope + return s +} + +func (s *CreateTableRequest) WithKind(kind *TableKind) *CreateTableRequest { + s.kind = kind + return s +} + +func (s *CreateTableRequest) WithOutOfLineConstraint(outOfLineConstraint OutOfLineConstraintRequest) *CreateTableRequest { + s.OutOfLineConstraints = append(s.OutOfLineConstraints, outOfLineConstraint) + return s +} + +func (s *CreateTableRequest) WithClusterBy(clusterBy []string) *CreateTableRequest { + s.clusterBy = clusterBy + return s +} + +func (s *CreateTableRequest) WithEnableSchemaEvolution(enableSchemaEvolution *bool) *CreateTableRequest { + s.enableSchemaEvolution = enableSchemaEvolution + return s +} + +func (s *CreateTableRequest) WithStageFileFormat(stageFileFormat StageFileFormatRequest) *CreateTableRequest { + s.stageFileFormat = &stageFileFormat + return s +} + +func (s *CreateTableRequest) WithStageCopyOptions(stageCopyOptions StageCopyOptionsRequest) *CreateTableRequest { + s.stageCopyOptions = &stageCopyOptions + return s +} + +func (s *CreateTableRequest) WithDataRetentionTimeInDays(dataRetentionTimeInDays *int) *CreateTableRequest { + s.DataRetentionTimeInDays = dataRetentionTimeInDays + return s +} + +func (s *CreateTableRequest) WithMaxDataExtensionTimeInDays(maxDataExtensionTimeInDays *int) *CreateTableRequest { + s.MaxDataExtensionTimeInDays = maxDataExtensionTimeInDays + return s +} + +func (s *CreateTableRequest) WithChangeTracking(changeTracking *bool) *CreateTableRequest { + s.ChangeTracking = changeTracking + return s +} + +func (s *CreateTableRequest) WithDefaultDDLCollation(defaultDDLCollation *string) *CreateTableRequest { + s.DefaultDDLCollation = defaultDDLCollation + return s +} + +func (s *CreateTableRequest) WithCopyGrants(copyGrants *bool) *CreateTableRequest { + s.CopyGrants = copyGrants + return s +} + +func (s *CreateTableRequest) WithRowAccessPolicy(rowAccessPolicy *RowAccessPolicyRequest) *CreateTableRequest { + s.RowAccessPolicy = rowAccessPolicy + return s +} + +func (s *CreateTableRequest) WithTags(tags []TagAssociationRequest) *CreateTableRequest { + s.Tags = tags + return s +} + +func (s *CreateTableRequest) WithComment(comment *string) *CreateTableRequest { + s.Comment = comment + return s +} + +func NewTableColumnRequest( + name string, + type_ DataType, +) *TableColumnRequest { + s := TableColumnRequest{} + s.name = name + s.type_ = type_ + return &s +} + +func (s *TableColumnRequest) WithCollate(collate *string) *TableColumnRequest { + s.collate = collate + return s +} + +func (s *TableColumnRequest) WithComment(comment *string) *TableColumnRequest { + s.comment = comment + return s +} + +func (s *TableColumnRequest) WithDefaultValue(defaultValue *ColumnDefaultValueRequest) *TableColumnRequest { + s.defaultValue = defaultValue + return s +} + +func (s *TableColumnRequest) WithNotNull(notNull *bool) *TableColumnRequest { + s.notNull = notNull + return s +} + +func (s *TableColumnRequest) WithMaskingPolicy(maskingPolicy *ColumnMaskingPolicyRequest) *TableColumnRequest { + s.maskingPolicy = maskingPolicy + return s +} + +func (s *TableColumnRequest) WithTags(tags []TagAssociation) *TableColumnRequest { + s.tags = tags + return s +} + +func (s *TableColumnRequest) WithInlineConstraint(inlineConstraint *ColumnInlineConstraintRequest) *TableColumnRequest { + s.inlineConstraint = inlineConstraint + return s +} + +func NewColumnDefaultValueRequest() *ColumnDefaultValueRequest { + return &ColumnDefaultValueRequest{} +} + +func (s *ColumnDefaultValueRequest) WithExpression(expression *string) *ColumnDefaultValueRequest { + s.expression = expression + return s +} + +func (s *ColumnDefaultValueRequest) WithIdentity(identity *ColumnIdentityRequest) *ColumnDefaultValueRequest { + s.identity = identity + return s +} + +func NewColumnIdentityRequest( + start int, + increment int, +) *ColumnIdentityRequest { + s := ColumnIdentityRequest{} + s.Start = start + s.Increment = increment + return &s +} + +func (s *ColumnIdentityRequest) WithOrder() *ColumnIdentityRequest { + s.Order = Bool(true) + return s +} + +func (s *ColumnIdentityRequest) WithNoorder() *ColumnIdentityRequest { + s.Noorder = Bool(true) + return s +} + +func NewColumnMaskingPolicyRequest( + name SchemaObjectIdentifier, +) *ColumnMaskingPolicyRequest { + s := ColumnMaskingPolicyRequest{} + s.name = name + return &s +} + +func (s *ColumnMaskingPolicyRequest) WithWith(with *bool) *ColumnMaskingPolicyRequest { + s.with = with + return s +} + +func (s *ColumnMaskingPolicyRequest) WithUsing(using []string) *ColumnMaskingPolicyRequest { + s.using = using + return s +} + +func NewColumnInlineConstraintRequest( + name string, + type_ ColumnConstraintType, +) *ColumnInlineConstraintRequest { + s := ColumnInlineConstraintRequest{} + s.Name = name + s.type_ = type_ + return &s +} + +func (s *ColumnInlineConstraintRequest) WithForeignKey(foreignKey *InlineForeignKeyRequest) *ColumnInlineConstraintRequest { + s.foreignKey = foreignKey + return s +} + +func (s *ColumnInlineConstraintRequest) WithEnforced(enforced *bool) *ColumnInlineConstraintRequest { + s.enforced = enforced + return s +} + +func (s *ColumnInlineConstraintRequest) WithNotEnforced(notEnforced *bool) *ColumnInlineConstraintRequest { + s.notEnforced = notEnforced + return s +} + +func (s *ColumnInlineConstraintRequest) WithDeferrable(deferrable *bool) *ColumnInlineConstraintRequest { + s.deferrable = deferrable + return s +} + +func (s *ColumnInlineConstraintRequest) WithNotDeferrable(notDeferrable *bool) *ColumnInlineConstraintRequest { + s.notDeferrable = notDeferrable + return s +} + +func (s *ColumnInlineConstraintRequest) WithInitiallyDeferred(initiallyDeferred *bool) *ColumnInlineConstraintRequest { + s.initiallyDeferred = initiallyDeferred + return s +} + +func (s *ColumnInlineConstraintRequest) WithInitiallyImmediate(initiallyImmediate *bool) *ColumnInlineConstraintRequest { + s.initiallyImmediate = initiallyImmediate + return s +} + +func (s *ColumnInlineConstraintRequest) WithEnable(enable *bool) *ColumnInlineConstraintRequest { + s.enable = enable + return s +} + +func (s *ColumnInlineConstraintRequest) WithDisable(disable *bool) *ColumnInlineConstraintRequest { + s.disable = disable + return s +} + +func (s *ColumnInlineConstraintRequest) WithValidate(validate *bool) *ColumnInlineConstraintRequest { + s.validate = validate + return s +} + +func (s *ColumnInlineConstraintRequest) WithNoValidate(noValidate *bool) *ColumnInlineConstraintRequest { + s.noValidate = noValidate + return s +} + +func (s *ColumnInlineConstraintRequest) WithRely(rely *bool) *ColumnInlineConstraintRequest { + s.rely = rely + return s +} + +func (s *ColumnInlineConstraintRequest) WithNoRely(noRely *bool) *ColumnInlineConstraintRequest { + s.noRely = noRely + return s +} + +func NewOutOfLineConstraintRequest( + name string, + constraintType ColumnConstraintType, +) *OutOfLineConstraintRequest { + s := OutOfLineConstraintRequest{} + s.Name = name + s.Type = constraintType + return &s +} + +func (s *OutOfLineConstraintRequest) WithColumns(columns []string) *OutOfLineConstraintRequest { + s.Columns = columns + return s +} + +func (s *OutOfLineConstraintRequest) WithForeignKey(foreignKey *OutOfLineForeignKeyRequest) *OutOfLineConstraintRequest { + s.ForeignKey = foreignKey + return s +} + +func (s *OutOfLineConstraintRequest) WithEnforced(enforced *bool) *OutOfLineConstraintRequest { + s.Enforced = enforced + return s +} + +func (s *OutOfLineConstraintRequest) WithNotEnforced(notEnforced *bool) *OutOfLineConstraintRequest { + s.NotEnforced = notEnforced + return s +} + +func (s *OutOfLineConstraintRequest) WithDeferrable(deferrable *bool) *OutOfLineConstraintRequest { + s.Deferrable = deferrable + return s +} + +func (s *OutOfLineConstraintRequest) WithNotDeferrable(notDeferrable *bool) *OutOfLineConstraintRequest { + s.NotDeferrable = notDeferrable + return s +} + +func (s *OutOfLineConstraintRequest) WithInitiallyDeferred(initiallyDeferred *bool) *OutOfLineConstraintRequest { + s.InitiallyDeferred = initiallyDeferred + return s +} + +func (s *OutOfLineConstraintRequest) WithInitiallyImmediate(initiallyImmediate *bool) *OutOfLineConstraintRequest { + s.InitiallyImmediate = initiallyImmediate + return s +} + +func (s *OutOfLineConstraintRequest) WithEnable(enable *bool) *OutOfLineConstraintRequest { + s.Enable = enable + return s +} + +func (s *OutOfLineConstraintRequest) WithDisable(disable *bool) *OutOfLineConstraintRequest { + s.Disable = disable + return s +} + +func (s *OutOfLineConstraintRequest) WithValidate(validate *bool) *OutOfLineConstraintRequest { + s.Validate = validate + return s +} + +func (s *OutOfLineConstraintRequest) WithNoValidate(noValidate *bool) *OutOfLineConstraintRequest { + s.NoValidate = noValidate + return s +} + +func (s *OutOfLineConstraintRequest) WithRely(rely *bool) *OutOfLineConstraintRequest { + s.Rely = rely + return s +} + +func (s *OutOfLineConstraintRequest) WithNoRely(noRely *bool) *OutOfLineConstraintRequest { + s.NoRely = noRely + return s +} + +func NewInlineForeignKeyRequest( + tableName string, +) *InlineForeignKeyRequest { + s := InlineForeignKeyRequest{} + s.TableName = tableName + return &s +} + +func (s *InlineForeignKeyRequest) WithColumnName(columnName []string) *InlineForeignKeyRequest { + s.ColumnName = columnName + return s +} + +func (s *InlineForeignKeyRequest) WithMatch(match *MatchType) *InlineForeignKeyRequest { + s.Match = match + return s +} + +func (s *InlineForeignKeyRequest) WithOn(on *ForeignKeyOnAction) *InlineForeignKeyRequest { + s.On = on + return s +} + +func NewOutOfLineForeignKeyRequest( + tableName SchemaObjectIdentifier, + columnNames []string, +) *OutOfLineForeignKeyRequest { + s := OutOfLineForeignKeyRequest{} + s.TableName = tableName + s.ColumnNames = columnNames + return &s +} + +func (s *OutOfLineForeignKeyRequest) WithMatch(match *MatchType) *OutOfLineForeignKeyRequest { + s.Match = match + return s +} + +func (s *OutOfLineForeignKeyRequest) WithOn(on *ForeignKeyOnAction) *OutOfLineForeignKeyRequest { + s.On = on + return s +} + +func NewForeignKeyOnAction() *ForeignKeyOnAction { + return &ForeignKeyOnAction{} +} + +func (s *ForeignKeyOnAction) WithOnUpdate(onUpdate *ForeignKeyAction) *ForeignKeyOnAction { + s.OnUpdate = onUpdate + return s +} + +func (s *ForeignKeyOnAction) WithOnDelete(onDelete *ForeignKeyAction) *ForeignKeyOnAction { + s.OnDelete = onDelete + return s +} + +func NewAlterTableRequest( + name SchemaObjectIdentifier, +) *AlterTableRequest { + s := AlterTableRequest{} + s.name = name + return &s +} + +func (s *AlterTableRequest) WithIfExists(ifExists *bool) *AlterTableRequest { + s.IfExists = ifExists + return s +} + +func (s *AlterTableRequest) WithNewName(newName *SchemaObjectIdentifier) *AlterTableRequest { + s.NewName = newName + return s +} + +func (s *AlterTableRequest) WithSwapWith(swapWith *SchemaObjectIdentifier) *AlterTableRequest { + s.SwapWith = swapWith + return s +} + +func (s *AlterTableRequest) WithClusteringAction(clusteringAction *TableClusteringActionRequest) *AlterTableRequest { + s.ClusteringAction = clusteringAction + return s +} + +func (s *AlterTableRequest) WithColumnAction(columnAction *TableColumnActionRequest) *AlterTableRequest { + s.ColumnAction = columnAction + return s +} + +func (s *AlterTableRequest) WithConstraintAction(constraintAction *TableConstraintActionRequest) *AlterTableRequest { + s.ConstraintAction = constraintAction + return s +} + +func (s *AlterTableRequest) WithExternalTableAction(externalTableAction *TableExternalTableActionRequest) *AlterTableRequest { + s.ExternalTableAction = externalTableAction + return s +} + +func (s *AlterTableRequest) WithSearchOptimizationAction(searchOptimizationAction *TableSearchOptimizationActionRequest) *AlterTableRequest { + s.SearchOptimizationAction = searchOptimizationAction + return s +} + +func (s *AlterTableRequest) WithSet(set *TableSetRequest) *AlterTableRequest { + s.Set = set + return s +} + +func (s *AlterTableRequest) WithSetTags(setTags []TagAssociationRequest) *AlterTableRequest { + s.SetTags = setTags + return s +} + +func (s *AlterTableRequest) WithUnsetTags(unsetTags []ObjectIdentifier) *AlterTableRequest { + s.UnsetTags = unsetTags + return s +} + +func (s *AlterTableRequest) WithUnset(unset *TableUnsetRequest) *AlterTableRequest { + s.Unset = unset + return s +} + +func (s *AlterTableRequest) WithAddRowAccessPolicy(addRowAccessPolicy *TableAddRowAccessPolicyRequest) *AlterTableRequest { + s.AddRowAccessPolicy = addRowAccessPolicy + return s +} + +func (s *AlterTableRequest) WithDropRowAccessPolicy(dropRowAccessPolicy *TableDropRowAccessPolicyRequest) *AlterTableRequest { + s.DropRowAccessPolicy = dropRowAccessPolicy + return s +} + +func (s *AlterTableRequest) WithDropAndAddRowAccessPolicy(dropAndAddRowAccessPolicy *TableDropAndAddRowAccessPolicy) *AlterTableRequest { + s.DropAndAddRowAccessPolicy = dropAndAddRowAccessPolicy + return s +} + +func (s *AlterTableRequest) WithDropAllAccessRowPolicies(dropAllAccessRowPolicies *bool) *AlterTableRequest { + s.DropAllAccessRowPolicies = dropAllAccessRowPolicies + return s +} + +func NewDropTableRequest( + name SchemaObjectIdentifier, +) *DropTableRequest { + s := DropTableRequest{} + s.Name = name + return &s +} + +func (s *DropTableRequest) WithIfExists(ifExists *bool) *DropTableRequest { + s.IfExists = ifExists + return s +} + +func (s *DropTableRequest) WithCascade(cascade *bool) *DropTableRequest { + s.Cascade = cascade + return s +} + +func (s *DropTableRequest) WithRestrict(restrict *bool) *DropTableRequest { + s.Restrict = restrict + return s +} + +func NewTableAddRowAccessPolicyRequest( + RowAccessPolicy SchemaObjectIdentifier, + On []string, +) *TableAddRowAccessPolicyRequest { + s := TableAddRowAccessPolicyRequest{} + s.RowAccessPolicy = RowAccessPolicy + s.On = On + return &s +} + +func NewTableDropRowAccessPolicyRequest( + RowAccessPolicy SchemaObjectIdentifier, +) *TableDropRowAccessPolicyRequest { + s := TableDropRowAccessPolicyRequest{} + s.RowAccessPolicy = RowAccessPolicy + return &s +} + +func NewTableDropAndAddRowAccessPolicyRequest( + drop TableDropRowAccessPolicyRequest, + add TableAddRowAccessPolicyRequest, +) *TableDropAndAddRowAccessPolicyRequest { + s := TableDropAndAddRowAccessPolicyRequest{} + s.Drop = drop + s.Add = add + return &s +} + +func NewTableUnsetRequest() *TableUnsetRequest { + return &TableUnsetRequest{} +} + +func (s *TableUnsetRequest) WithDataRetentionTimeInDays(dataRetentionTimeInDays bool) *TableUnsetRequest { + s.DataRetentionTimeInDays = dataRetentionTimeInDays + return s +} + +func (s *TableUnsetRequest) WithMaxDataExtensionTimeInDays(maxDataExtensionTimeInDays bool) *TableUnsetRequest { + s.MaxDataExtensionTimeInDays = maxDataExtensionTimeInDays + return s +} + +func (s *TableUnsetRequest) WithChangeTracking(changeTracking bool) *TableUnsetRequest { + s.ChangeTracking = changeTracking + return s +} + +func (s *TableUnsetRequest) WithDefaultDDLCollation(defaultDDLCollation bool) *TableUnsetRequest { + s.DefaultDDLCollation = defaultDDLCollation + return s +} + +func (s *TableUnsetRequest) WithEnableSchemaEvolution(enableSchemaEvolution bool) *TableUnsetRequest { + s.EnableSchemaEvolution = enableSchemaEvolution + return s +} + +func (s *TableUnsetRequest) WithComment(comment bool) *TableUnsetRequest { + s.Comment = comment + return s +} + +func NewAddRowAccessPolicyRequest( + policyName string, + columnName []string, +) *AddRowAccessPolicyRequest { + s := AddRowAccessPolicyRequest{} + s.PolicyName = policyName + s.ColumnName = columnName + return &s +} + +func NewTagAssociationRequest( + name ObjectIdentifier, + value string, +) *TagAssociationRequest { + s := TagAssociationRequest{} + s.Name = name + s.Value = value + return &s +} + +func NewFileFormatTypeOptionsRequest() *FileFormatTypeOptionsRequest { + return &FileFormatTypeOptionsRequest{} +} + +func (s *FileFormatTypeOptionsRequest) WithCSVCompression(csvCompression *CSVCompression) *FileFormatTypeOptionsRequest { + s.CSVCompression = csvCompression + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVRecordDelimiter(csvRecordDelimiter *string) *FileFormatTypeOptionsRequest { + s.CSVRecordDelimiter = csvRecordDelimiter + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVFieldDelimiter(csvFieldDelimiter *string) *FileFormatTypeOptionsRequest { + s.CSVFieldDelimiter = csvFieldDelimiter + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVFileExtension(csvFileExtension *string) *FileFormatTypeOptionsRequest { + s.CSVFileExtension = csvFileExtension + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVParseHeader(csvParseHeader *bool) *FileFormatTypeOptionsRequest { + s.CSVParseHeader = csvParseHeader + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVSkipHeader(csvSkipHeader *int) *FileFormatTypeOptionsRequest { + s.CSVSkipHeader = csvSkipHeader + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVSkipBlankLines(csvSkipBlankLines *bool) *FileFormatTypeOptionsRequest { + s.CSVSkipBlankLines = csvSkipBlankLines + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVDateFormat(csvDateFormat *string) *FileFormatTypeOptionsRequest { + s.CSVDateFormat = csvDateFormat + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVTimeFormat(csvTimeFormat *string) *FileFormatTypeOptionsRequest { + s.CSVTimeFormat = csvTimeFormat + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVTimestampFormat(csvTimestampFormat *string) *FileFormatTypeOptionsRequest { + s.CSVTimestampFormat = csvTimestampFormat + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVBinaryFormat(csvBinaryFormat *BinaryFormat) *FileFormatTypeOptionsRequest { + s.CSVBinaryFormat = csvBinaryFormat + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVEscape(csvEscape *string) *FileFormatTypeOptionsRequest { + s.CSVEscape = csvEscape + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVEscapeUnenclosedField(csvEscapeUnenclosedField *string) *FileFormatTypeOptionsRequest { + s.CSVEscapeUnenclosedField = csvEscapeUnenclosedField + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVTrimSpace(csvTrimSpace *bool) *FileFormatTypeOptionsRequest { + s.CSVTrimSpace = csvTrimSpace + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVFieldOptionallyEnclosedBy(csvFieldOptionallyEnclosedBy *string) *FileFormatTypeOptionsRequest { + s.CSVFieldOptionallyEnclosedBy = csvFieldOptionallyEnclosedBy + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVNullIf(csvNullIf *[]NullString) *FileFormatTypeOptionsRequest { + s.CSVNullIf = csvNullIf + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVErrorOnColumnCountMismatch(csvErrorOnColumnCountMismatch *bool) *FileFormatTypeOptionsRequest { + s.CSVErrorOnColumnCountMismatch = csvErrorOnColumnCountMismatch + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVReplaceInvalidCharacters(csvReplaceInvalidCharacters *bool) *FileFormatTypeOptionsRequest { + s.CSVReplaceInvalidCharacters = csvReplaceInvalidCharacters + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVEmptyFieldAsNull(csvEmptyFieldAsNull *bool) *FileFormatTypeOptionsRequest { + s.CSVEmptyFieldAsNull = csvEmptyFieldAsNull + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVSkipByteOrderMark(csvSkipByteOrderMark *bool) *FileFormatTypeOptionsRequest { + s.CSVSkipByteOrderMark = csvSkipByteOrderMark + return s +} + +func (s *FileFormatTypeOptionsRequest) WithCSVEncoding(csvEncoding *CSVEncoding) *FileFormatTypeOptionsRequest { + s.CSVEncoding = csvEncoding + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONCompression(jsonCompression *JSONCompression) *FileFormatTypeOptionsRequest { + s.JSONCompression = jsonCompression + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONDateFormat(jsonDateFormat *string) *FileFormatTypeOptionsRequest { + s.JSONDateFormat = jsonDateFormat + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONTimeFormat(jsonTimeFormat *string) *FileFormatTypeOptionsRequest { + s.JSONTimeFormat = jsonTimeFormat + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONTimestampFormat(jsonTimestampFormat *string) *FileFormatTypeOptionsRequest { + s.JSONTimestampFormat = jsonTimestampFormat + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONBinaryFormat(jsonBinaryFormat *BinaryFormat) *FileFormatTypeOptionsRequest { + s.JSONBinaryFormat = jsonBinaryFormat + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONTrimSpace(jsonTrimSpace *bool) *FileFormatTypeOptionsRequest { + s.JSONTrimSpace = jsonTrimSpace + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONNullIf(jsonNullIf []NullString) *FileFormatTypeOptionsRequest { + s.JSONNullIf = jsonNullIf + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONFileExtension(jsonFileExtension *string) *FileFormatTypeOptionsRequest { + s.JSONFileExtension = jsonFileExtension + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONEnableOctal(jsonEnableOctal *bool) *FileFormatTypeOptionsRequest { + s.JSONEnableOctal = jsonEnableOctal + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONAllowDuplicate(jsonAllowDuplicate *bool) *FileFormatTypeOptionsRequest { + s.JSONAllowDuplicate = jsonAllowDuplicate + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONStripOuterArray(jsonStripOuterArray *bool) *FileFormatTypeOptionsRequest { + s.JSONStripOuterArray = jsonStripOuterArray + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONStripNullValues(jsonStripNullValues *bool) *FileFormatTypeOptionsRequest { + s.JSONStripNullValues = jsonStripNullValues + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONReplaceInvalidCharacters(jsonReplaceInvalidCharacters *bool) *FileFormatTypeOptionsRequest { + s.JSONReplaceInvalidCharacters = jsonReplaceInvalidCharacters + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONIgnoreUTF8Errors(jsonIgnoreUTF8Errors *bool) *FileFormatTypeOptionsRequest { + s.JSONIgnoreUTF8Errors = jsonIgnoreUTF8Errors + return s +} + +func (s *FileFormatTypeOptionsRequest) WithJSONSkipByteOrderMark(jsonSkipByteOrderMark *bool) *FileFormatTypeOptionsRequest { + s.JSONSkipByteOrderMark = jsonSkipByteOrderMark + return s +} + +func (s *FileFormatTypeOptionsRequest) WithAvroCompression(avroCompression *AvroCompression) *FileFormatTypeOptionsRequest { + s.AvroCompression = avroCompression + return s +} + +func (s *FileFormatTypeOptionsRequest) WithAvroTrimSpace(avroTrimSpace *bool) *FileFormatTypeOptionsRequest { + s.AvroTrimSpace = avroTrimSpace + return s +} + +func (s *FileFormatTypeOptionsRequest) WithAvroReplaceInvalidCharacters(avroReplaceInvalidCharacters *bool) *FileFormatTypeOptionsRequest { + s.AvroReplaceInvalidCharacters = avroReplaceInvalidCharacters + return s +} + +func (s *FileFormatTypeOptionsRequest) WithAvroNullIf(avroNullIf *[]NullString) *FileFormatTypeOptionsRequest { + s.AvroNullIf = avroNullIf + return s +} + +func (s *FileFormatTypeOptionsRequest) WithORCTrimSpace(orcTrimSpace *bool) *FileFormatTypeOptionsRequest { + s.ORCTrimSpace = orcTrimSpace + return s +} + +func (s *FileFormatTypeOptionsRequest) WithORCReplaceInvalidCharacters(orcReplaceInvalidCharacters *bool) *FileFormatTypeOptionsRequest { + s.ORCReplaceInvalidCharacters = orcReplaceInvalidCharacters + return s +} + +func (s *FileFormatTypeOptionsRequest) WithORCNullIf(orcNullIf *[]NullString) *FileFormatTypeOptionsRequest { + s.ORCNullIf = orcNullIf + return s +} + +func (s *FileFormatTypeOptionsRequest) WithParquetCompression(parquetCompression *ParquetCompression) *FileFormatTypeOptionsRequest { + s.ParquetCompression = parquetCompression + return s +} + +func (s *FileFormatTypeOptionsRequest) WithParquetSnappyCompression(parquetSnappyCompression *bool) *FileFormatTypeOptionsRequest { + s.ParquetSnappyCompression = parquetSnappyCompression + return s +} + +func (s *FileFormatTypeOptionsRequest) WithParquetBinaryAsText(parquetBinaryAsText *bool) *FileFormatTypeOptionsRequest { + s.ParquetBinaryAsText = parquetBinaryAsText + return s +} + +func (s *FileFormatTypeOptionsRequest) WithParquetTrimSpace(parquetTrimSpace *bool) *FileFormatTypeOptionsRequest { + s.ParquetTrimSpace = parquetTrimSpace + return s +} + +func (s *FileFormatTypeOptionsRequest) WithParquetReplaceInvalidCharacters(parquetReplaceInvalidCharacters *bool) *FileFormatTypeOptionsRequest { + s.ParquetReplaceInvalidCharacters = parquetReplaceInvalidCharacters + return s +} + +func (s *FileFormatTypeOptionsRequest) WithParquetNullIf(parquetNullIf *[]NullString) *FileFormatTypeOptionsRequest { + s.ParquetNullIf = parquetNullIf + return s +} + +func (s *FileFormatTypeOptionsRequest) WithXMLCompression(xmlCompression *XMLCompression) *FileFormatTypeOptionsRequest { + s.XMLCompression = xmlCompression + return s +} + +func (s *FileFormatTypeOptionsRequest) WithXMLIgnoreUTF8Errors(xmlIgnoreUTF8Errors *bool) *FileFormatTypeOptionsRequest { + s.XMLIgnoreUTF8Errors = xmlIgnoreUTF8Errors + return s +} + +func (s *FileFormatTypeOptionsRequest) WithXMLPreserveSpace(xmlPreserveSpace *bool) *FileFormatTypeOptionsRequest { + s.XMLPreserveSpace = xmlPreserveSpace + return s +} + +func (s *FileFormatTypeOptionsRequest) WithXMLStripOuterElement(xmlStripOuterElement *bool) *FileFormatTypeOptionsRequest { + s.XMLStripOuterElement = xmlStripOuterElement + return s +} + +func (s *FileFormatTypeOptionsRequest) WithXMLDisableSnowflakeData(xmlDisableSnowflakeData *bool) *FileFormatTypeOptionsRequest { + s.XMLDisableSnowflakeData = xmlDisableSnowflakeData + return s +} + +func (s *FileFormatTypeOptionsRequest) WithXMLDisableAutoConvert(xmlDisableAutoConvert *bool) *FileFormatTypeOptionsRequest { + s.XMLDisableAutoConvert = xmlDisableAutoConvert + return s +} + +func (s *FileFormatTypeOptionsRequest) WithXMLReplaceInvalidCharacters(xmlReplaceInvalidCharacters *bool) *FileFormatTypeOptionsRequest { + s.XMLReplaceInvalidCharacters = xmlReplaceInvalidCharacters + return s +} + +func (s *FileFormatTypeOptionsRequest) WithXMLSkipByteOrderMark(xmlSkipByteOrderMark *bool) *FileFormatTypeOptionsRequest { + s.XMLSkipByteOrderMark = xmlSkipByteOrderMark + return s +} + +func (s *FileFormatTypeOptionsRequest) WithComment(comment *string) *FileFormatTypeOptionsRequest { + s.Comment = comment + return s +} + +func NewTableClusteringActionRequest() *TableClusteringActionRequest { + return &TableClusteringActionRequest{} +} + +func (s *TableClusteringActionRequest) WithClusterBy(clusterBy []string) *TableClusteringActionRequest { + s.ClusterBy = clusterBy + return s +} + +func (s *TableClusteringActionRequest) WithRecluster(recluster *TableReclusterActionRequest) *TableClusteringActionRequest { + s.Recluster = recluster + return s +} + +func (s *TableClusteringActionRequest) WithChangeReclusterState(changeReclusterState *ReclusterState) *TableClusteringActionRequest { + s.ChangeReclusterState = changeReclusterState + return s +} + +func (s *TableClusteringActionRequest) WithDropClusteringKey(dropClusteringKey *bool) *TableClusteringActionRequest { + s.DropClusteringKey = dropClusteringKey + return s +} + +func NewTableReclusterActionRequest() *TableReclusterActionRequest { + return &TableReclusterActionRequest{} +} + +func (s *TableReclusterActionRequest) WithMaxSize(maxSize *int) *TableReclusterActionRequest { + s.MaxSize = maxSize + return s +} + +func (s *TableReclusterActionRequest) WithCondition(condition *string) *TableReclusterActionRequest { + s.Condition = condition + return s +} + +func NewTableReclusterChangeStateRequest() *TableReclusterChangeStateRequest { + return &TableReclusterChangeStateRequest{} +} + +func (s *TableReclusterChangeStateRequest) WithState(state ReclusterState) *TableReclusterChangeStateRequest { + s.State = state + return s +} + +func NewTableColumnActionRequest() *TableColumnActionRequest { + return &TableColumnActionRequest{} +} + +func (s *TableColumnActionRequest) WithAdd(add *TableColumnAddActionRequest) *TableColumnActionRequest { + s.Add = add + return s +} + +func (s *TableColumnActionRequest) WithRename(rename *TableColumnRenameActionRequest) *TableColumnActionRequest { + s.Rename = rename + return s +} + +func (s *TableColumnActionRequest) WithAlter(alter []TableColumnAlterActionRequest) *TableColumnActionRequest { + s.Alter = alter + return s +} + +func (s *TableColumnActionRequest) WithSetMaskingPolicy(setMaskingPolicy *TableColumnAlterSetMaskingPolicyActionRequest) *TableColumnActionRequest { + s.SetMaskingPolicy = setMaskingPolicy + return s +} + +func (s *TableColumnActionRequest) WithUnsetMaskingPolicy(unsetMaskingPolicy *TableColumnAlterUnsetMaskingPolicyActionRequest) *TableColumnActionRequest { + s.UnsetMaskingPolicy = unsetMaskingPolicy + return s +} + +func (s *TableColumnActionRequest) WithSetTags(setTags *TableColumnAlterSetTagsActionRequest) *TableColumnActionRequest { + s.SetTags = setTags + return s +} + +func (s *TableColumnActionRequest) WithUnsetTags(unsetTags *TableColumnAlterUnsetTagsActionRequest) *TableColumnActionRequest { + s.UnsetTags = unsetTags + return s +} + +func (s *TableColumnActionRequest) WithDropColumnsIfExists() *TableColumnActionRequest { + s.DropColumnsIfExists = Bool(true) + return s +} + +func (s *TableColumnActionRequest) WithDropColumns(dropColumns []string) *TableColumnActionRequest { + s.DropColumns = dropColumns + return s +} + +func NewTableColumnAddActionRequest( + name string, + dataType DataType, +) *TableColumnAddActionRequest { + s := TableColumnAddActionRequest{} + s.Name = name + s.Type = dataType + return &s +} + +func (s *TableColumnAddActionRequest) WithIfNotExists() *TableColumnAddActionRequest { + s.IfNotExists = Bool(true) + return s +} + +func (s *TableColumnAddActionRequest) WithDefaultValue(defaultValue *ColumnDefaultValueRequest) *TableColumnAddActionRequest { + s.DefaultValue = defaultValue + return s +} + +func (s *TableColumnAddActionRequest) WithInlineConstraint(inlineConstraint *TableColumnAddInlineConstraintRequest) *TableColumnAddActionRequest { + s.InlineConstraint = inlineConstraint + return s +} + +func (s *TableColumnAddActionRequest) WithMaskingPolicy(maskingPolicy *ColumnMaskingPolicyRequest) *TableColumnAddActionRequest { + s.MaskingPolicy = maskingPolicy + return s +} + +func (s *TableColumnAddActionRequest) WithWith(with *bool) *TableColumnAddActionRequest { + s.With = with + return s +} + +func (s *TableColumnAddActionRequest) WithTags(tags []TagAssociation) *TableColumnAddActionRequest { + s.Tags = tags + return s +} + +func NewTableColumnAddInlineConstraintRequest() *TableColumnAddInlineConstraintRequest { + return &TableColumnAddInlineConstraintRequest{} +} + +func (s *TableColumnAddInlineConstraintRequest) WithNotNull(notNull *bool) *TableColumnAddInlineConstraintRequest { + s.NotNull = notNull + return s +} + +func (s *TableColumnAddInlineConstraintRequest) WithName(name string) *TableColumnAddInlineConstraintRequest { + s.Name = name + return s +} + +func (s *TableColumnAddInlineConstraintRequest) WithType(constraintType ColumnConstraintType) *TableColumnAddInlineConstraintRequest { + s.Type = constraintType + return s +} + +func (s *TableColumnAddInlineConstraintRequest) WithForeignKey(foreignKey *ColumnAddForeignKey) *TableColumnAddInlineConstraintRequest { + s.ForeignKey = foreignKey + return s +} + +func NewColumnAddForeignKeyRequest() *ColumnAddForeignKeyRequest { + return &ColumnAddForeignKeyRequest{} +} + +func (s *ColumnAddForeignKeyRequest) WithTableName(tableName string) *ColumnAddForeignKeyRequest { + s.TableName = tableName + return s +} + +func (s *ColumnAddForeignKeyRequest) WithColumnName(columnName string) *ColumnAddForeignKeyRequest { + s.ColumnName = columnName + return s +} + +func NewTableColumnRenameActionRequest( + oldName string, + newName string, +) *TableColumnRenameActionRequest { + s := TableColumnRenameActionRequest{} + s.OldName = oldName + s.NewName = newName + return &s +} + +func NewTableColumnAlterActionRequest( + column bool, + name string, +) *TableColumnAlterActionRequest { + s := TableColumnAlterActionRequest{} + s.Column = column + s.Name = name + return &s +} + +func (s *TableColumnAlterActionRequest) WithDropDefault(dropDefault *bool) *TableColumnAlterActionRequest { + s.DropDefault = dropDefault + return s +} + +func (s *TableColumnAlterActionRequest) WithSetDefault(setDefault *SequenceName) *TableColumnAlterActionRequest { + s.SetDefault = setDefault + return s +} + +func (s *TableColumnAlterActionRequest) WithNotNullConstraint(notNullConstraint *TableColumnNotNullConstraintRequest) *TableColumnAlterActionRequest { + s.NotNullConstraint = notNullConstraint + return s +} + +func (s *TableColumnAlterActionRequest) WithType(dataType *DataType) *TableColumnAlterActionRequest { + s.Type = dataType + return s +} + +func (s *TableColumnAlterActionRequest) WithComment(comment *string) *TableColumnAlterActionRequest { + s.Comment = comment + return s +} + +func (s *TableColumnAlterActionRequest) WithUnsetComment(unsetComment *bool) *TableColumnAlterActionRequest { + s.UnsetComment = unsetComment + return s +} + +func NewTableColumnAlterSetMaskingPolicyActionRequest( + columnName string, + maskingPolicyName SchemaObjectIdentifier, + using []string, +) *TableColumnAlterSetMaskingPolicyActionRequest { + s := TableColumnAlterSetMaskingPolicyActionRequest{} + s.ColumnName = columnName + s.MaskingPolicyName = maskingPolicyName + s.Using = using + return &s +} + +func (s *TableColumnAlterSetMaskingPolicyActionRequest) WithForce(force *bool) *TableColumnAlterSetMaskingPolicyActionRequest { + s.Force = force + return s +} + +func NewTableColumnAlterUnsetMaskingPolicyActionRequest( + columnName string, +) *TableColumnAlterUnsetMaskingPolicyActionRequest { + s := TableColumnAlterUnsetMaskingPolicyActionRequest{} + s.ColumnName = columnName + return &s +} + +func NewTableColumnAlterSetTagsActionRequest( + columnName string, + tags []TagAssociation, +) *TableColumnAlterSetTagsActionRequest { + s := TableColumnAlterSetTagsActionRequest{} + s.ColumnName = columnName + s.Tags = tags + return &s +} + +func NewTableColumnAlterUnsetTagsActionRequest( + columnName string, + tags []ObjectIdentifier, +) *TableColumnAlterUnsetTagsActionRequest { + s := TableColumnAlterUnsetTagsActionRequest{} + s.ColumnName = columnName + s.Tags = tags + return &s +} + +func NewTableColumnNotNullConstraintRequest() *TableColumnNotNullConstraintRequest { + return &TableColumnNotNullConstraintRequest{} +} + +func (s *TableColumnNotNullConstraintRequest) WithSet(set *bool) *TableColumnNotNullConstraintRequest { + s.Set = set + return s +} + +func (s *TableColumnNotNullConstraintRequest) WithDrop(drop *bool) *TableColumnNotNullConstraintRequest { + s.Drop = drop + return s +} + +func NewTableConstraintActionRequest() *TableConstraintActionRequest { + return &TableConstraintActionRequest{} +} + +func (s *TableConstraintActionRequest) WithAdd(add *OutOfLineConstraintRequest) *TableConstraintActionRequest { + s.Add = add + return s +} + +func (s *TableConstraintActionRequest) WithRename(rename *TableConstraintRenameActionRequest) *TableConstraintActionRequest { + s.Rename = rename + return s +} + +func (s *TableConstraintActionRequest) WithAlter(alter *TableConstraintAlterActionRequest) *TableConstraintActionRequest { + s.Alter = alter + return s +} + +func (s *TableConstraintActionRequest) WithDrop(drop *TableConstraintDropActionRequest) *TableConstraintActionRequest { + s.Drop = drop + return s +} + +func NewTableConstraintRenameActionRequest() *TableConstraintRenameActionRequest { + return &TableConstraintRenameActionRequest{} +} + +func (s *TableConstraintRenameActionRequest) WithOldName(oldName string) *TableConstraintRenameActionRequest { + s.OldName = oldName + return s +} + +func (s *TableConstraintRenameActionRequest) WithNewName(newName string) *TableConstraintRenameActionRequest { + s.NewName = newName + return s +} + +func NewTableConstraintAlterActionRequest(columns []string) *TableConstraintAlterActionRequest { + return &TableConstraintAlterActionRequest{ + Columns: columns, + } +} + +func (s *TableConstraintAlterActionRequest) WithConstraintName(constraintName *string) *TableConstraintAlterActionRequest { + s.ConstraintName = constraintName + return s +} + +func (s *TableConstraintAlterActionRequest) WithPrimaryKey(primaryKey *bool) *TableConstraintAlterActionRequest { + s.PrimaryKey = primaryKey + return s +} + +func (s *TableConstraintAlterActionRequest) WithUnique(unique *bool) *TableConstraintAlterActionRequest { + s.Unique = unique + return s +} + +func (s *TableConstraintAlterActionRequest) WithForeignKey(foreignKey *bool) *TableConstraintAlterActionRequest { + s.ForeignKey = foreignKey + return s +} + +func (s *TableConstraintAlterActionRequest) WithEnforced(enforced *bool) *TableConstraintAlterActionRequest { + s.Enforced = enforced + return s +} + +func (s *TableConstraintAlterActionRequest) WithNotEnforced(notEnforced *bool) *TableConstraintAlterActionRequest { + s.NotEnforced = notEnforced + return s +} + +func (s *TableConstraintAlterActionRequest) WithValidate(validate *bool) *TableConstraintAlterActionRequest { + s.Validate = validate + return s +} + +func (s *TableConstraintAlterActionRequest) WithNoValidate(noValidate *bool) *TableConstraintAlterActionRequest { + s.NoValidate = noValidate + return s +} + +func (s *TableConstraintAlterActionRequest) WithRely(rely *bool) *TableConstraintAlterActionRequest { + s.Rely = rely + return s +} + +func (s *TableConstraintAlterActionRequest) WithNoRely(noRely *bool) *TableConstraintAlterActionRequest { + s.NoRely = noRely + return s +} + +func NewTableConstraintDropActionRequest(columns []string) *TableConstraintDropActionRequest { + return &TableConstraintDropActionRequest{ + Columns: columns, + } +} + +func (s *TableConstraintDropActionRequest) WithConstraintName(constraintName *string) *TableConstraintDropActionRequest { + s.ConstraintName = constraintName + return s +} + +func (s *TableConstraintDropActionRequest) WithPrimaryKey(primaryKey *bool) *TableConstraintDropActionRequest { + s.PrimaryKey = primaryKey + return s +} + +func (s *TableConstraintDropActionRequest) WithUnique(unique *bool) *TableConstraintDropActionRequest { + s.Unique = unique + return s +} + +func (s *TableConstraintDropActionRequest) WithForeignKey(foreignKey *bool) *TableConstraintDropActionRequest { + s.ForeignKey = foreignKey + return s +} + +func (s *TableConstraintDropActionRequest) WithCascade(cascade *bool) *TableConstraintDropActionRequest { + s.Cascade = cascade + return s +} + +func (s *TableConstraintDropActionRequest) WithRestrict(restrict *bool) *TableConstraintDropActionRequest { + s.Restrict = restrict + return s +} + +func NewTableExternalTableActionRequest() *TableExternalTableActionRequest { + return &TableExternalTableActionRequest{} +} + +func (s *TableExternalTableActionRequest) WithAdd(add *TableExternalTableColumnAddActionRequest) *TableExternalTableActionRequest { + s.Add = add + return s +} + +func (s *TableExternalTableActionRequest) WithRename(rename *TableExternalTableColumnRenameActionRequest) *TableExternalTableActionRequest { + s.Rename = rename + return s +} + +func (s *TableExternalTableActionRequest) WithDrop(drop *TableExternalTableColumnDropActionRequest) *TableExternalTableActionRequest { + s.Drop = drop + return s +} + +func NewTableSearchOptimizationActionRequest() *TableSearchOptimizationActionRequest { + return &TableSearchOptimizationActionRequest{} +} + +func (s *TableSearchOptimizationActionRequest) WithAddSearchOptimizationOn(addSearchOptimizationOn []string) *TableSearchOptimizationActionRequest { + s.AddSearchOptimizationOn = addSearchOptimizationOn + return s +} + +func (s *TableSearchOptimizationActionRequest) WithDropSearchOptimizationOn(dropSearchOptimizationOn []string) *TableSearchOptimizationActionRequest { + s.DropSearchOptimizationOn = dropSearchOptimizationOn + return s +} + +func NewTableSetRequest() *TableSetRequest { + return &TableSetRequest{} +} + +func (s *TableSetRequest) WithEnableSchemaEvolution(enableSchemaEvolution *bool) *TableSetRequest { + s.EnableSchemaEvolution = enableSchemaEvolution + return s +} + +func (s *TableSetRequest) WithStageFileFormat(stageFileFormat StageFileFormatRequest) *TableSetRequest { + s.StageFileFormat = &stageFileFormat + return s +} + +func (s *TableSetRequest) WithStageCopyOptions(stageCopyOptions StageCopyOptionsRequest) *TableSetRequest { + s.StageCopyOptions = &stageCopyOptions + return s +} + +func (s *TableSetRequest) WithDataRetentionTimeInDays(dataRetentionTimeInDays *int) *TableSetRequest { + s.DataRetentionTimeInDays = dataRetentionTimeInDays + return s +} + +func (s *TableSetRequest) WithMaxDataExtensionTimeInDays(maxDataExtensionTimeInDays *int) *TableSetRequest { + s.MaxDataExtensionTimeInDays = maxDataExtensionTimeInDays + return s +} + +func (s *TableSetRequest) WithChangeTracking(changeTracking *bool) *TableSetRequest { + s.ChangeTracking = changeTracking + return s +} + +func (s *TableSetRequest) WithDefaultDDLCollation(defaultDDLCollation *string) *TableSetRequest { + s.DefaultDDLCollation = defaultDDLCollation + return s +} + +func (s *TableSetRequest) WithComment(comment *string) *TableSetRequest { + s.Comment = comment + return s +} + +func NewTableExternalTableColumnAddActionRequest() *TableExternalTableColumnAddActionRequest { + return &TableExternalTableColumnAddActionRequest{} +} + +func (s *TableExternalTableColumnAddActionRequest) WithIfNotExists() *TableExternalTableColumnAddActionRequest { + s.IfNotExists = Bool(true) + return s +} + +func (s *TableExternalTableColumnAddActionRequest) WithName(name string) *TableExternalTableColumnAddActionRequest { + s.Name = name + return s +} + +func (s *TableExternalTableColumnAddActionRequest) WithType(dataType DataType) *TableExternalTableColumnAddActionRequest { + s.Type = dataType + return s +} + +func (s *TableExternalTableColumnAddActionRequest) WithExpression(expression string) *TableExternalTableColumnAddActionRequest { + s.Expression = expression + return s +} + +func NewTableExternalTableColumnRenameActionRequest() *TableExternalTableColumnRenameActionRequest { + return &TableExternalTableColumnRenameActionRequest{} +} + +func (s *TableExternalTableColumnRenameActionRequest) WithOldName(oldName string) *TableExternalTableColumnRenameActionRequest { + s.OldName = oldName + return s +} + +func (s *TableExternalTableColumnRenameActionRequest) WithNewName(newName string) *TableExternalTableColumnRenameActionRequest { + s.NewName = newName + return s +} + +func NewTableExternalTableColumnDropActionRequest(columns []string) *TableExternalTableColumnDropActionRequest { + return &TableExternalTableColumnDropActionRequest{ + Columns: columns, + } +} + +func (s *TableExternalTableColumnDropActionRequest) WithIfExists() *TableExternalTableColumnDropActionRequest { + s.IfExists = Bool(true) + return s +} + +func NewShowTableRequest() *ShowTableRequest { + return &ShowTableRequest{} +} + +func (s *ShowTableRequest) WithTerse(terse *bool) *ShowTableRequest { + s.terse = terse + return s +} + +func (s *ShowTableRequest) WithHistory(history *bool) *ShowTableRequest { + s.history = history + return s +} + +func (s *ShowTableRequest) WithLikePattern(likePattern string) *ShowTableRequest { + s.likePattern = likePattern + return s +} + +func (s *ShowTableRequest) WithIn(in *In) *ShowTableRequest { + s.in = in + return s +} + +func (s *ShowTableRequest) WithStartsWith(startsWith *string) *ShowTableRequest { + s.startsWith = startsWith + return s +} + +func (s *ShowTableRequest) WithLimitFrom(limitFrom *LimitFrom) *ShowTableRequest { + s.limitFrom = limitFrom + return s +} + +func NewLimitFromRequest() *LimitFromRequest { + return &LimitFromRequest{} +} + +func (s *LimitFromRequest) WithRows(rows *int) *LimitFromRequest { + s.rows = rows + return s +} + +func (s *LimitFromRequest) WithFrom(from *string) *LimitFromRequest { + s.from = from + return s +} + +func NewDescribeTableColumnsRequest( + id SchemaObjectIdentifier, +) *DescribeTableColumnsRequest { + s := DescribeTableColumnsRequest{} + s.id = id + return &s +} + +func NewDescribeTableStageRequest( + id SchemaObjectIdentifier, +) *DescribeTableStageRequest { + s := DescribeTableStageRequest{} + s.id = id + return &s +} diff --git a/pkg/sdk/tables_impl.go b/pkg/sdk/tables_impl.go new file mode 100644 index 0000000000..b3ca029285 --- /dev/null +++ b/pkg/sdk/tables_impl.go @@ -0,0 +1,817 @@ +package sdk + +import ( + "context" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/collections" +) + +var _ Tables = (*tables)(nil) + +var ( + _ optionsProvider[createTableOptions] = new(CreateTableRequest) + _ optionsProvider[createTableAsSelectOptions] = new(CreateTableAsSelectRequest) + _ optionsProvider[createTableUsingTemplateOptions] = new(CreateTableUsingTemplateRequest) + _ optionsProvider[createTableLikeOptions] = new(CreateTableLikeRequest) + _ optionsProvider[createTableCloneOptions] = new(CreateTableCloneRequest) + _ optionsProvider[alterTableOptions] = new(AlterTableRequest) + _ optionsProvider[dropTableOptions] = new(DropTableRequest) + _ optionsProvider[showTableOptions] = new(ShowTableRequest) + _ optionsProvider[describeTableColumnsOptions] = new(DescribeTableColumnsRequest) + _ optionsProvider[describeTableStageOptions] = new(DescribeTableStageRequest) + _ optionsProvider[TableColumnAction] = new(TableColumnActionRequest) + _ optionsProvider[TableConstraintAction] = new(TableConstraintActionRequest) + _ optionsProvider[TableExternalTableAction] = new(TableExternalTableActionRequest) + _ optionsProvider[TableSearchOptimizationAction] = new(TableSearchOptimizationActionRequest) + _ optionsProvider[TableSet] = new(TableSetRequest) +) + +type tables struct { + client *Client +} + +func (v *tables) Create(ctx context.Context, request *CreateTableRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *tables) CreateAsSelect(ctx context.Context, request *CreateTableAsSelectRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *tables) CreateUsingTemplate(ctx context.Context, request *CreateTableUsingTemplateRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *tables) CreateLike(ctx context.Context, request *CreateTableLikeRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *tables) CreateClone(ctx context.Context, request *CreateTableCloneRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *tables) Alter(ctx context.Context, request *AlterTableRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *tables) Drop(ctx context.Context, request *DropTableRequest) error { + opts := request.toOpts() + return validateAndExec(v.client, ctx, opts) +} + +func (v *tables) Show(ctx context.Context, request *ShowTableRequest) ([]Table, error) { + opts := request.toOpts() + dbRows, err := validateAndQuery[tableDBRow](v.client, ctx, opts) + if err != nil { + return nil, err + } + + resultList := convertRows[tableDBRow, Table](dbRows) + + return resultList, nil +} + +func (v *tables) ShowByID(ctx context.Context, id SchemaObjectIdentifier) (*Table, error) { + request := NewShowTableRequest().WithIn(&In{Schema: NewDatabaseObjectIdentifier(id.DatabaseName(), id.SchemaName())}).WithLikePattern(id.Name()) + returnedTables, err := v.Show(ctx, request) + if err != nil { + return nil, err + } + return collections.FindOne(returnedTables, func(r Table) bool { return r.Name == id.Name() }) +} + +func (v *tables) DescribeColumns(ctx context.Context, req *DescribeTableColumnsRequest) ([]TableColumnDetails, error) { + rows, err := validateAndQuery[tableColumnDetailsRow](v.client, ctx, req.toOpts()) + if err != nil { + return nil, err + } + return convertRows[tableColumnDetailsRow, TableColumnDetails](rows), nil +} + +func (v *tables) DescribeStage(ctx context.Context, req *DescribeTableStageRequest) ([]TableStageDetails, error) { + rows, err := validateAndQuery[tableStageDetailsRow](v.client, ctx, req.toOpts()) + if err != nil { + return nil, err + } + return convertRows[tableStageDetailsRow, TableStageDetails](rows), nil +} + +func (s *AlterTableRequest) toOpts() *alterTableOptions { + var clusteringAction *TableClusteringAction + if s.ClusteringAction != nil { + var reclusterAction *TableReclusterAction + if s.ClusteringAction.Recluster != nil { + reclusterAction = &TableReclusterAction{ + MaxSize: s.ClusteringAction.Recluster.MaxSize, + Condition: s.ClusteringAction.Recluster.Condition, + } + } + var changeReclusterChange *TableReclusterChangeState + if s.ClusteringAction.ChangeReclusterState != nil { + changeReclusterChange = &TableReclusterChangeState{State: s.ClusteringAction.ChangeReclusterState} + } + clusteringAction = &TableClusteringAction{ + ClusterBy: s.ClusteringAction.ClusterBy, + Recluster: reclusterAction, + ChangeReclusterState: changeReclusterChange, + DropClusteringKey: s.ClusteringAction.DropClusteringKey, + } + } + var columnAction *TableColumnAction + if s.ColumnAction != nil { + columnAction = s.ColumnAction.toOpts() + } + var constraintAction *TableConstraintAction + if s.ConstraintAction != nil { + constraintAction = s.ConstraintAction.toOpts() + } + var externalTableAction *TableExternalTableAction + if s.ExternalTableAction != nil { + externalTableAction = s.ExternalTableAction.toOpts() + } + var searchOptimizationAction *TableSearchOptimizationAction + if s.SearchOptimizationAction != nil { + searchOptimizationAction = s.SearchOptimizationAction.toOpts() + } + var tableSet *TableSet + if s.Set != nil { + tableSet = s.Set.toOpts() + } + + tagAssociations := make([]TagAssociation, 0, len(s.SetTags)) + for _, tagRequest := range s.SetTags { + tagAssociations = append(tagAssociations, TagAssociation(tagRequest)) + } + var tableUnset *TableUnset + if s.Unset != nil { + tableUnset = &TableUnset{ + DataRetentionTimeInDays: Bool(s.Unset.DataRetentionTimeInDays), + MaxDataExtensionTimeInDays: Bool(s.Unset.MaxDataExtensionTimeInDays), + ChangeTracking: Bool(s.Unset.ChangeTracking), + DefaultDDLCollation: Bool(s.Unset.DefaultDDLCollation), + EnableSchemaEvolution: Bool(s.Unset.EnableSchemaEvolution), + Comment: Bool(s.Unset.Comment), + } + } + var addRowAccessPolicy *TableAddRowAccessPolicy + if s.AddRowAccessPolicy != nil { + addRowAccessPolicy = &TableAddRowAccessPolicy{ + RowAccessPolicy: s.AddRowAccessPolicy.RowAccessPolicy, + On: s.AddRowAccessPolicy.On, + } + } + var dropRowAccessPolicy *TableDropRowAccessPolicy + if s.DropRowAccessPolicy != nil { + dropRowAccessPolicy = &TableDropRowAccessPolicy{ + RowAccessPolicy: s.DropRowAccessPolicy.RowAccessPolicy, + } + } + var dropAndAddRowAccessPolicy *TableDropAndAddRowAccessPolicy + if s.DropAndAddRowAccessPolicy != nil { + add := TableAddRowAccessPolicy{ + RowAccessPolicy: s.DropAndAddRowAccessPolicy.Add.RowAccessPolicy, + On: s.DropAndAddRowAccessPolicy.Add.On, + } + drop := TableDropRowAccessPolicy{ + RowAccessPolicy: s.DropAndAddRowAccessPolicy.Drop.RowAccessPolicy, + } + dropAndAddRowAccessPolicy = &TableDropAndAddRowAccessPolicy{ + Drop: drop, + Add: add, + } + } + + return &alterTableOptions{ + IfExists: s.IfExists, + name: s.name, + NewName: s.NewName, + SwapWith: s.SwapWith, + ClusteringAction: clusteringAction, + ColumnAction: columnAction, + ConstraintAction: constraintAction, + ExternalTableAction: externalTableAction, + SearchOptimizationAction: searchOptimizationAction, + Set: tableSet, + SetTags: tagAssociations, + UnsetTags: s.UnsetTags, + Unset: tableUnset, + AddRowAccessPolicy: addRowAccessPolicy, + DropRowAccessPolicy: dropRowAccessPolicy, + DropAndAddRowAccessPolicy: dropAndAddRowAccessPolicy, + DropAllAccessRowPolicies: s.DropAllAccessRowPolicies, + } +} + +func (s *TableSetRequest) toOpts() *TableSet { + set := &TableSet{ + EnableSchemaEvolution: s.EnableSchemaEvolution, + DataRetentionTimeInDays: s.DataRetentionTimeInDays, + MaxDataExtensionTimeInDays: s.MaxDataExtensionTimeInDays, + ChangeTracking: s.ChangeTracking, + DefaultDDLCollation: s.DefaultDDLCollation, + Comment: s.Comment, + } + + if s.StageCopyOptions != nil { + set.StageCopyOptions = s.StageCopyOptions.toOpts() + } + if s.StageFileFormat != nil { + set.StageFileFormat = s.StageFileFormat.toOpts() + } + + return set +} + +func (s *TableSearchOptimizationActionRequest) toOpts() *TableSearchOptimizationAction { + if len(s.AddSearchOptimizationOn) > 0 { + return &TableSearchOptimizationAction{ + Add: &AddSearchOptimization{ + On: s.AddSearchOptimizationOn, + }, + } + } + if len(s.DropSearchOptimizationOn) > 0 { + return &TableSearchOptimizationAction{ + Drop: &DropSearchOptimization{ + On: s.DropSearchOptimizationOn, + }, + } + } + return nil +} + +func (r *TableExternalTableActionRequest) toOpts() *TableExternalTableAction { + if r.Add != nil { + return &TableExternalTableAction{ + Add: &TableExternalTableColumnAddAction{ + IfNotExists: r.Add.IfNotExists, + Name: r.Add.Name, + Type: r.Add.Type, + Expression: []string{r.Add.Expression}, + }, + } + } + if r.Rename != nil { + return &TableExternalTableAction{ + Rename: &TableExternalTableColumnRenameAction{ + OldName: r.Rename.OldName, + NewName: r.Rename.NewName, + }, + } + } + if r.Drop != nil { + return &TableExternalTableAction{ + Drop: &TableExternalTableColumnDropAction{ + Names: r.Drop.Columns, + IfExists: r.Drop.IfExists, + }, + } + } + return nil +} + +func (r *TableConstraintActionRequest) toOpts() *TableConstraintAction { + if r.Add != nil { + var foreignKey *OutOfLineForeignKey + if r.Add.ForeignKey != nil { + var foreignKeyOnAction *ForeignKeyOnAction + if r.Add.ForeignKey.On != nil { + foreignKeyOnAction = &ForeignKeyOnAction{ + OnUpdate: r.Add.ForeignKey.On.OnUpdate, + OnDelete: r.Add.ForeignKey.On.OnDelete, + } + } + foreignKey = &OutOfLineForeignKey{ + TableName: r.Add.ForeignKey.TableName, + ColumnNames: r.Add.ForeignKey.ColumnNames, + Match: r.Add.ForeignKey.Match, + On: foreignKeyOnAction, + } + } + outOfLineConstraint := OutOfLineConstraint{ + Name: r.Add.Name, + Type: r.Add.Type, + Columns: r.Add.Columns, + ForeignKey: foreignKey, + Enforced: r.Add.Enforced, + NotEnforced: r.Add.NotEnforced, + Deferrable: r.Add.Deferrable, + NotDeferrable: r.Add.NotDeferrable, + InitiallyDeferred: r.Add.InitiallyDeferred, + InitiallyImmediate: r.Add.InitiallyImmediate, + Enable: r.Add.Enable, + Disable: r.Add.Disable, + Validate: r.Add.Validate, + NoValidate: r.Add.NoValidate, + Rely: r.Add.Rely, + NoRely: r.Add.NoRely, + } + return &TableConstraintAction{ + Add: &outOfLineConstraint, + } + } + if r.Rename != nil { + return &TableConstraintAction{ + Rename: &TableConstraintRenameAction{ + OldName: r.Rename.OldName, + NewName: r.Rename.NewName, + }, + } + } + if r.Alter != nil { + return &TableConstraintAction{ + Alter: &TableConstraintAlterAction{ + ConstraintName: r.Alter.ConstraintName, + PrimaryKey: r.Alter.PrimaryKey, + Unique: r.Alter.Unique, + ForeignKey: r.Alter.ForeignKey, + Columns: r.Alter.Columns, + Enforced: r.Alter.Enforced, + NotEnforced: r.Alter.NotEnforced, + Validate: r.Alter.Validate, + NoValidate: r.Alter.NoValidate, + Rely: r.Alter.Rely, + NoRely: r.Alter.NoRely, + }, + } + } + if r.Drop != nil { + return &TableConstraintAction{ + Drop: &TableConstraintDropAction{ + ConstraintName: r.Drop.ConstraintName, + PrimaryKey: r.Drop.PrimaryKey, + Unique: r.Drop.Unique, + ForeignKey: r.Drop.ForeignKey, + Columns: r.Drop.Columns, + Cascade: r.Drop.Cascade, + Restrict: r.Drop.Restrict, + }, + } + } + return nil +} + +func (r *TableColumnActionRequest) toOpts() *TableColumnAction { + if r.Add != nil { + var defaultValue *ColumnDefaultValue + if r.Add.DefaultValue != nil { + defaultValue = &ColumnDefaultValue{ + r.Add.DefaultValue.expression, + &ColumnIdentity{ + Start: r.Add.DefaultValue.identity.Start, + Increment: r.Add.DefaultValue.identity.Increment, + }, + } + } + var inlineConstraint *TableColumnAddInlineConstraint + if r.Add.InlineConstraint != nil { + var foreignKey *ColumnAddForeignKey + if r.Add.InlineConstraint.ForeignKey != nil { + foreignKey = &ColumnAddForeignKey{ + TableName: r.Add.InlineConstraint.ForeignKey.TableName, + ColumnName: r.Add.InlineConstraint.ForeignKey.ColumnName, + } + } + inlineConstraint = &TableColumnAddInlineConstraint{ + NotNull: r.Add.InlineConstraint.NotNull, + Name: r.Add.InlineConstraint.Name, + Type: r.Add.InlineConstraint.Type, + ForeignKey: foreignKey, + } + } + return &TableColumnAction{ + Add: &TableColumnAddAction{ + IfNotExists: r.Add.IfNotExists, + Name: r.Add.Name, + Type: r.Add.Type, + DefaultValue: defaultValue, + InlineConstraint: inlineConstraint, + }, + } + } + if r.Rename != nil { + return &TableColumnAction{ + Rename: &TableColumnRenameAction{ + OldName: r.Rename.OldName, + NewName: r.Rename.NewName, + }, + } + } + if len(r.Alter) > 0 { + var alterActions []TableColumnAlterAction + for _, alterAction := range r.Alter { + var notNullConstraint *TableColumnNotNullConstraint + if alterAction.NotNullConstraint != nil { + notNullConstraint = &TableColumnNotNullConstraint{ + Set: alterAction.NotNullConstraint.Set, + Drop: alterAction.NotNullConstraint.Drop, + } + } + alterActions = append(alterActions, TableColumnAlterAction{ + Name: alterAction.Name, + DropDefault: alterAction.DropDefault, + SetDefault: alterAction.SetDefault, + NotNullConstraint: notNullConstraint, + Type: alterAction.Type, + Comment: alterAction.Comment, + UnsetComment: alterAction.UnsetComment, + }) + } + return &TableColumnAction{ + Alter: alterActions, + } + } + if r.SetMaskingPolicy != nil { + return &TableColumnAction{ + SetMaskingPolicy: &TableColumnAlterSetMaskingPolicyAction{ + ColumnName: r.SetMaskingPolicy.ColumnName, + MaskingPolicyName: r.SetMaskingPolicy.MaskingPolicyName, + Using: r.SetMaskingPolicy.Using, + Force: r.SetMaskingPolicy.Force, + }, + } + } + if r.UnsetMaskingPolicy != nil { + return &TableColumnAction{ + UnsetMaskingPolicy: &TableColumnAlterUnsetMaskingPolicyAction{ + ColumnName: r.UnsetMaskingPolicy.ColumnName, + }, + } + } + if r.SetTags != nil { + return &TableColumnAction{ + SetTags: &TableColumnAlterSetTagsAction{ + ColumnName: r.SetTags.ColumnName, + Tags: r.SetTags.Tags, + }, + } + } + if r.UnsetTags != nil { + return &TableColumnAction{ + UnsetTags: &TableColumnAlterUnsetTagsAction{ + ColumnName: r.UnsetTags.ColumnName, + Tags: r.UnsetTags.Tags, + }, + } + } + if len(r.DropColumns) > 0 { + return &TableColumnAction{ + DropColumns: &TableColumnAlterDropColumns{ + IfExists: r.DropColumnsIfExists, + Columns: r.DropColumns, + }, + } + } + return nil +} + +func (s *CreateTableRequest) toOpts() *createTableOptions { + tagAssociations := make([]TagAssociation, 0, len(s.Tags)) + for _, tagRequest := range s.Tags { + tagAssociations = append(tagAssociations, TagAssociation(tagRequest)) + } + var rowAccessPolicy *TableRowAccessPolicy + if s.RowAccessPolicy != nil { + rowAccessPolicy = &TableRowAccessPolicy{ + Name: s.RowAccessPolicy.Name, + On: s.RowAccessPolicy.On, + } + } + outOfLineConstraints := make([]OutOfLineConstraint, 0) + for _, outOfLineConstraintRequest := range s.OutOfLineConstraints { + var foreignKey *OutOfLineForeignKey + if outOfLineConstraintRequest.ForeignKey != nil { + var foreignKeyOnAction *ForeignKeyOnAction + if outOfLineConstraintRequest.ForeignKey.On != nil { + foreignKeyOnAction = &ForeignKeyOnAction{ + OnUpdate: outOfLineConstraintRequest.ForeignKey.On.OnUpdate, + OnDelete: outOfLineConstraintRequest.ForeignKey.On.OnDelete, + } + } + foreignKey = &OutOfLineForeignKey{ + TableName: outOfLineConstraintRequest.ForeignKey.TableName, + ColumnNames: outOfLineConstraintRequest.ForeignKey.ColumnNames, + Match: outOfLineConstraintRequest.ForeignKey.Match, + On: foreignKeyOnAction, + } + } + outOfLineConstraint := OutOfLineConstraint{ + Name: outOfLineConstraintRequest.Name, + Type: outOfLineConstraintRequest.Type, + Columns: outOfLineConstraintRequest.Columns, + ForeignKey: foreignKey, + Enforced: outOfLineConstraintRequest.Enforced, + NotEnforced: outOfLineConstraintRequest.NotEnforced, + Deferrable: outOfLineConstraintRequest.Deferrable, + NotDeferrable: outOfLineConstraintRequest.NotDeferrable, + InitiallyDeferred: outOfLineConstraintRequest.InitiallyDeferred, + InitiallyImmediate: outOfLineConstraintRequest.InitiallyImmediate, + Enable: outOfLineConstraintRequest.Enable, + Disable: outOfLineConstraintRequest.Disable, + Validate: outOfLineConstraintRequest.Validate, + NoValidate: outOfLineConstraintRequest.NoValidate, + Rely: outOfLineConstraintRequest.Rely, + NoRely: outOfLineConstraintRequest.NoRely, + } + outOfLineConstraints = append(outOfLineConstraints, outOfLineConstraint) + } + + opts := &createTableOptions{ + OrReplace: s.orReplace, + IfNotExists: s.ifNotExists, + Scope: s.scope, + Kind: s.kind, + name: s.name, + ColumnsAndConstraints: CreateTableColumnsAndConstraints{convertColumns(s.columns), outOfLineConstraints}, + ClusterBy: s.clusterBy, + EnableSchemaEvolution: s.enableSchemaEvolution, + DataRetentionTimeInDays: s.DataRetentionTimeInDays, + MaxDataExtensionTimeInDays: s.MaxDataExtensionTimeInDays, + ChangeTracking: s.ChangeTracking, + DefaultDDLCollation: s.DefaultDDLCollation, + CopyGrants: s.CopyGrants, + Tags: tagAssociations, + Comment: s.Comment, + RowAccessPolicy: rowAccessPolicy, + } + + if s.stageCopyOptions != nil { + opts.StageCopyOptions = s.stageCopyOptions.toOpts() + } + if s.stageFileFormat != nil { + opts.StageFileFormat = s.stageFileFormat.toOpts() + } + + return opts +} + +func (s *CreateTableAsSelectRequest) toOpts() *createTableAsSelectOptions { + columns := make([]TableAsSelectColumn, 0, len(s.columns)) + for _, column := range s.columns { + var maskingPolicy *TableAsSelectColumnMaskingPolicy + if column.maskingPolicyName != nil { + maskingPolicy = &TableAsSelectColumnMaskingPolicy{ + Name: *column.maskingPolicyName, + } + } + columns = append(columns, TableAsSelectColumn{ + Name: column.name, + Type: column.type_, + MaskingPolicy: maskingPolicy, + }) + } + return &createTableAsSelectOptions{ + OrReplace: s.orReplace, + name: s.name, + Columns: columns, + Query: s.query, + } +} + +func (s *CreateTableUsingTemplateRequest) toOpts() *createTableUsingTemplateOptions { + return &createTableUsingTemplateOptions{ + OrReplace: s.orReplace, + name: s.name, + CopyGrants: s.copyGrants, + Query: []string{s.Query}, + } +} + +func (s *CreateTableLikeRequest) toOpts() *createTableLikeOptions { + return &createTableLikeOptions{ + OrReplace: s.orReplace, + name: s.name, + CopyGrants: s.copyGrants, + SourceTable: s.sourceTable, + ClusterBy: s.clusterBy, + } +} + +func (s *CreateTableCloneRequest) toOpts() *createTableCloneOptions { + var clonePoint *ClonePoint + if s.ClonePoint != nil { + clonePoint = &ClonePoint{ + Moment: s.ClonePoint.Moment, + At: TimeTravel{ + Timestamp: s.ClonePoint.At.Timestamp, + Offset: s.ClonePoint.At.Offset, + Statement: s.ClonePoint.At.Statement, + }, + } + } + return &createTableCloneOptions{ + OrReplace: s.orReplace, + name: s.name, + CopyGrants: s.copyGrants, + SourceTable: s.sourceTable, + ClonePoint: clonePoint, + } +} + +func (v *StageFileFormatRequest) toOpts() *StageFileFormat { + return &StageFileFormat{ + FormatName: v.FormatName, + Type: v.Type, + Options: v.Options.toOpts(), + } +} + +func (v *StageCopyOptionsRequest) toOpts() *StageCopyOptions { + return &StageCopyOptions{ + OnError: v.OnError.toOpts(), + SizeLimit: v.SizeLimit, + Purge: v.Purge, + ReturnFailedOnly: v.ReturnFailedOnly, + MatchByColumnName: v.MatchByColumnName, + EnforceLength: v.EnforceLength, + Truncatecolumns: v.Truncatecolumns, + Force: v.Force, + } +} + +func (v *StageCopyOnErrorOptionsRequest) toOpts() *StageCopyOnErrorOptions { + return &StageCopyOnErrorOptions{ + Continue: v.Continue, + SkipFile: v.SkipFile, + AbortStatement: v.AbortStatement, + } +} + +func convertStageFileFormatOptions(stageFileFormatRequests []StageFileFormatRequest) []StageFileFormat { + fileFormats := make([]StageFileFormat, 0, len(stageFileFormatRequests)) + for _, request := range stageFileFormatRequests { + var options *FileFormatTypeOptions + if request.Options != nil { + options = request.Options.toOpts() + } + format := StageFileFormat{ + FormatName: request.FormatName, + Type: request.Type, + Options: options, + } + fileFormats = append(fileFormats, format) + } + return fileFormats +} + +func (v *FileFormatTypeOptionsRequest) toOpts() *FileFormatTypeOptions { + if v == nil { + return nil + } + return &FileFormatTypeOptions{ + CSVCompression: v.CSVCompression, + CSVRecordDelimiter: v.CSVRecordDelimiter, + CSVFieldDelimiter: v.CSVFieldDelimiter, + CSVFileExtension: v.CSVFileExtension, + CSVParseHeader: v.CSVParseHeader, + CSVSkipHeader: v.CSVSkipHeader, + CSVSkipBlankLines: v.CSVSkipBlankLines, + CSVDateFormat: v.CSVDateFormat, + CSVTimeFormat: v.CSVTimeFormat, + CSVTimestampFormat: v.CSVTimestampFormat, + CSVBinaryFormat: v.CSVBinaryFormat, + CSVEscape: v.CSVEscape, + CSVEscapeUnenclosedField: v.CSVEscapeUnenclosedField, + CSVTrimSpace: v.CSVTrimSpace, + CSVFieldOptionallyEnclosedBy: v.CSVFieldOptionallyEnclosedBy, + CSVNullIf: v.CSVNullIf, + CSVErrorOnColumnCountMismatch: v.CSVErrorOnColumnCountMismatch, + CSVReplaceInvalidCharacters: v.CSVReplaceInvalidCharacters, + CSVEmptyFieldAsNull: v.CSVEmptyFieldAsNull, + CSVSkipByteOrderMark: v.CSVSkipByteOrderMark, + CSVEncoding: v.CSVEncoding, + JSONCompression: v.JSONCompression, + JSONDateFormat: v.JSONDateFormat, + JSONTimeFormat: v.JSONTimeFormat, + JSONTimestampFormat: v.JSONTimestampFormat, + JSONBinaryFormat: v.JSONBinaryFormat, + JSONTrimSpace: v.JSONTrimSpace, + JSONNullIf: v.JSONNullIf, + JSONFileExtension: v.JSONFileExtension, + JSONEnableOctal: v.JSONEnableOctal, + JSONAllowDuplicate: v.JSONAllowDuplicate, + JSONStripOuterArray: v.JSONStripOuterArray, + JSONStripNullValues: v.JSONStripNullValues, + JSONReplaceInvalidCharacters: v.JSONReplaceInvalidCharacters, + JSONIgnoreUTF8Errors: v.JSONIgnoreUTF8Errors, + JSONSkipByteOrderMark: v.JSONSkipByteOrderMark, + AvroCompression: v.AvroCompression, + AvroTrimSpace: v.AvroTrimSpace, + AvroReplaceInvalidCharacters: v.AvroReplaceInvalidCharacters, + AvroNullIf: v.AvroNullIf, + ORCTrimSpace: v.ORCTrimSpace, + ORCReplaceInvalidCharacters: v.ORCReplaceInvalidCharacters, + ORCNullIf: v.ORCNullIf, + ParquetCompression: v.ParquetCompression, + ParquetSnappyCompression: v.ParquetSnappyCompression, + ParquetBinaryAsText: v.ParquetBinaryAsText, + ParquetTrimSpace: v.ParquetTrimSpace, + ParquetReplaceInvalidCharacters: v.ParquetReplaceInvalidCharacters, + ParquetNullIf: v.ParquetNullIf, + XMLCompression: v.XMLCompression, + XMLIgnoreUTF8Errors: v.XMLIgnoreUTF8Errors, + XMLPreserveSpace: v.XMLPreserveSpace, + XMLStripOuterElement: v.XMLStripOuterElement, + XMLDisableSnowflakeData: v.XMLDisableSnowflakeData, + XMLDisableAutoConvert: v.XMLDisableAutoConvert, + XMLReplaceInvalidCharacters: v.XMLReplaceInvalidCharacters, + XMLSkipByteOrderMark: v.XMLSkipByteOrderMark, + } +} + +func convertColumns(columnRequests []TableColumnRequest) []TableColumn { + columns := make([]TableColumn, 0, len(columnRequests)) + for _, columnRequest := range columnRequests { + columnRequest := columnRequest + var defaultValue *ColumnDefaultValue + if columnRequest.defaultValue != nil { + var columnIdentity *ColumnIdentity + if columnRequest.defaultValue.identity != nil { + columnIdentity = &ColumnIdentity{ + Start: columnRequest.defaultValue.identity.Start, + Increment: columnRequest.defaultValue.identity.Increment, + Order: columnRequest.defaultValue.identity.Order, + Noorder: columnRequest.defaultValue.identity.Noorder, + } + } + defaultValue = &ColumnDefaultValue{ + columnRequest.defaultValue.expression, + columnIdentity, + } + } + var inlineConstraint *ColumnInlineConstraint + if columnRequest.inlineConstraint != nil { + var foreignKey *InlineForeignKey + if columnRequest.inlineConstraint.foreignKey != nil { + var onActionRequest *ForeignKeyOnAction + if columnRequest.inlineConstraint.foreignKey.On != nil { + onActionRequest = &ForeignKeyOnAction{ + OnUpdate: columnRequest.inlineConstraint.foreignKey.On.OnUpdate, + OnDelete: columnRequest.inlineConstraint.foreignKey.On.OnDelete, + } + } + foreignKey = &InlineForeignKey{ + TableName: columnRequest.inlineConstraint.foreignKey.TableName, + ColumnName: columnRequest.inlineConstraint.foreignKey.ColumnName, + Match: columnRequest.inlineConstraint.foreignKey.Match, + On: onActionRequest, + } + } + inlineConstraint = &ColumnInlineConstraint{ + Name: &columnRequest.inlineConstraint.Name, + Type: columnRequest.inlineConstraint.type_, + ForeignKey: foreignKey, + Enforced: columnRequest.inlineConstraint.enforced, + NotEnforced: columnRequest.inlineConstraint.notEnforced, + Deferrable: columnRequest.inlineConstraint.deferrable, + NotDeferrable: columnRequest.inlineConstraint.notDeferrable, + InitiallyDeferred: columnRequest.inlineConstraint.initiallyDeferred, + InitiallyImmediate: columnRequest.inlineConstraint.initiallyImmediate, + Enable: columnRequest.inlineConstraint.enable, + Disable: columnRequest.inlineConstraint.disable, + Validate: columnRequest.inlineConstraint.validate, + NoValidate: columnRequest.inlineConstraint.noValidate, + Rely: columnRequest.inlineConstraint.rely, + NoRely: columnRequest.inlineConstraint.noRely, + } + } + var maskingPolicy *ColumnMaskingPolicy + if columnRequest.maskingPolicy != nil { + maskingPolicy = &ColumnMaskingPolicy{ + With: columnRequest.maskingPolicy.with, + Name: columnRequest.maskingPolicy.name, + Using: columnRequest.maskingPolicy.using, + } + } + columns = append(columns, TableColumn{ + Name: columnRequest.name, + Type: columnRequest.type_, + Collate: columnRequest.collate, + Comment: columnRequest.comment, + DefaultValue: defaultValue, + MaskingPolicy: maskingPolicy, + NotNull: columnRequest.notNull, + Tags: columnRequest.tags, + InlineConstraint: inlineConstraint, + }) + } + return columns +} + +func (v *DescribeTableColumnsRequest) toOpts() *describeTableColumnsOptions { + return &describeTableColumnsOptions{ + name: v.id, + } +} + +func (v *DescribeTableStageRequest) toOpts() *describeTableStageOptions { + return &describeTableStageOptions{ + name: v.id, + } +} diff --git a/pkg/sdk/tables_test.go b/pkg/sdk/tables_test.go new file mode 100644 index 0000000000..27574b9ffe --- /dev/null +++ b/pkg/sdk/tables_test.go @@ -0,0 +1,1573 @@ +package sdk + +import ( + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/internal/random" + "github.com/stretchr/testify/require" +) + +func TestTableCreate(t *testing.T) { + id := RandomSchemaObjectIdentifier() + sampleColumnName := "FIRST_COLUMN" + sampleColumnType := DataTypeVARCHAR + + defaultOpts := func() *createTableOptions { + return &createTableOptions{ + name: id, + } + } + + defaultOptsWithColumnInlineConstraint := func(inlineConstraint *ColumnInlineConstraint) *createTableOptions { + columns := []TableColumn{{ + Name: sampleColumnName, + Type: sampleColumnType, + InlineConstraint: inlineConstraint, + }} + return &createTableOptions{ + name: id, + ColumnsAndConstraints: CreateTableColumnsAndConstraints{Columns: columns}, + } + } + + defaultOptsWithColumnOutOfLineConstraint := func(outOfLineConstraint *OutOfLineConstraint) *createTableOptions { + columns := []TableColumn{{ + Name: sampleColumnName, + Type: sampleColumnType, + }} + return &createTableOptions{ + name: id, + ColumnsAndConstraints: CreateTableColumnsAndConstraints{Columns: columns, OutOfLineConstraint: []OutOfLineConstraint{*outOfLineConstraint}}, + } + } + + t.Run("empty options", func(t *testing.T) { + opts := &createTableOptions{} + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("createTableOptions", "name")) + }) + + t.Run("validation: nil options", func(t *testing.T) { + var opts *createTableOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("createTableOptions", "name")) + }) + + t.Run("validation: no columns", func(t *testing.T) { + opts := defaultOpts() + assertOptsInvalidJoinedErrors(t, opts, errNotSet("createTableOptions", "Columns")) + }) + + t.Run("validation: both expression and identity of a column are present", func(t *testing.T) { + opts := defaultOpts() + opts.name = RandomSchemaObjectIdentifier() + opts.ColumnsAndConstraints = CreateTableColumnsAndConstraints{ + Columns: []TableColumn{{ + Name: "a", + DefaultValue: &ColumnDefaultValue{ + Expression: String("expr"), + Identity: &ColumnIdentity{ + Start: 10, + Increment: 1, + }, + }, + }}, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("DefaultValue", "Expression", "Identity")) + }) + + t.Run("validation: both order and noorder are present for identity", func(t *testing.T) { + opts := defaultOpts() + opts.name = RandomSchemaObjectIdentifier() + opts.ColumnsAndConstraints = CreateTableColumnsAndConstraints{ + Columns: []TableColumn{{ + Name: "a", + DefaultValue: &ColumnDefaultValue{ + Identity: &ColumnIdentity{ + Start: 10, + Increment: 1, + Order: Bool(true), + Noorder: Bool(true), + }, + }, + }}, + } + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("Identity", "Order", "Noorder")) + }) + + t.Run("validation: column masking policy incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = RandomSchemaObjectIdentifier() + opts.ColumnsAndConstraints = CreateTableColumnsAndConstraints{ + Columns: []TableColumn{{ + Name: "a", + MaskingPolicy: &ColumnMaskingPolicy{ + Name: NewSchemaObjectIdentifier("", "", ""), + }, + }}, + } + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("ColumnMaskingPolicy", "Name")) + }) + + t.Run("validation: column tag association's incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = RandomSchemaObjectIdentifier() + opts.ColumnsAndConstraints = CreateTableColumnsAndConstraints{ + Columns: []TableColumn{{ + Name: "a", + Tags: []TagAssociation{ + { + Name: NewSchemaObjectIdentifier("", "", ""), + Value: "v1", + }, + }, + }}, + } + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("TagAssociation", "Name")) + }) + + t.Run("validation: stageFileFormat's both format name and format type are present", func(t *testing.T) { + opts := defaultOpts() + opts.StageFileFormat = &StageFileFormat{ + FormatName: String("some_format"), + Type: Pointer(FileFormatTypeCSV), + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("StageFileFormat", "FormatName", "FormatType")) + }) + + t.Run("validation: rowAccessPolicy's incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.RowAccessPolicy = &TableRowAccessPolicy{ + Name: NewSchemaObjectIdentifier("", "", ""), + } + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("TableRowAccessPolicy", "Name")) + }) + + t.Run("validation: inline constraint - constraint name empty", func(t *testing.T) { + inlineConstraint := ColumnInlineConstraint{ + Type: "", + } + opts := defaultOptsWithColumnInlineConstraint(&inlineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errInvalidValue("ColumnInlineConstraint", "Type", "")) + }) + + t.Run("validation: inline constraint - constraint ", func(t *testing.T) { + inlineConstraint := ColumnInlineConstraint{ + Type: "not existing type", + } + opts := defaultOptsWithColumnInlineConstraint(&inlineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errInvalidValue("ColumnInlineConstraint", "Type", "not existing type")) + }) + + t.Run("validation: inline constraint - foreign key present for foreign key constraint", func(t *testing.T) { + inlineConstraint := ColumnInlineConstraint{ + Type: ColumnConstraintTypeForeignKey, + ForeignKey: nil, + } + opts := defaultOptsWithColumnInlineConstraint(&inlineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errNotSet("ColumnInlineConstraint", "ForeignKey")) + }) + + t.Run("validation: inline constraint - foreign key validation", func(t *testing.T) { + inlineConstraint := ColumnInlineConstraint{ + Type: ColumnConstraintTypeForeignKey, + ForeignKey: &InlineForeignKey{ + TableName: "", + }, + } + opts := defaultOptsWithColumnInlineConstraint(&inlineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errNotSet("InlineForeignKey", "TableName")) + }) + + t.Run("validation: inline constraint - foreign key absent for constraint other than foreign key", func(t *testing.T) { + inlineConstraint := ColumnInlineConstraint{ + Type: ColumnConstraintTypeUnique, + ForeignKey: &InlineForeignKey{ + TableName: "table", + }, + } + opts := defaultOptsWithColumnInlineConstraint(&inlineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errSet("ColumnInlineConstraint", "ForeignKey")) + }) + + t.Run("validation: inline constraint - enforced and not enforced both present", func(t *testing.T) { + inlineConstraint := ColumnInlineConstraint{ + Type: ColumnConstraintTypeUnique, + Enforced: Bool(true), + NotEnforced: Bool(true), + } + opts := defaultOptsWithColumnInlineConstraint(&inlineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("ColumnInlineConstraint", "Enforced", "NotEnforced")) + }) + + t.Run("validation: inline constraint - deferrable and not deferrable both present", func(t *testing.T) { + inlineConstraint := ColumnInlineConstraint{ + Type: ColumnConstraintTypeUnique, + Deferrable: Bool(true), + NotDeferrable: Bool(true), + } + opts := defaultOptsWithColumnInlineConstraint(&inlineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("ColumnInlineConstraint", "Deferrable", "NotDeferrable")) + }) + + t.Run("validation: inline constraint - initially deferred and initially immediate both present", func(t *testing.T) { + inlineConstraint := ColumnInlineConstraint{ + Type: ColumnConstraintTypeUnique, + InitiallyDeferred: Bool(true), + InitiallyImmediate: Bool(true), + } + opts := defaultOptsWithColumnInlineConstraint(&inlineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("ColumnInlineConstraint", "InitiallyDeferred", "InitiallyImmediate")) + }) + + t.Run("validation: inline constraint - enable and disable both present", func(t *testing.T) { + inlineConstraint := ColumnInlineConstraint{ + Type: ColumnConstraintTypeUnique, + Enable: Bool(true), + Disable: Bool(true), + } + opts := defaultOptsWithColumnInlineConstraint(&inlineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("ColumnInlineConstraint", "Enable", "Disable")) + }) + + t.Run("validation: inline constraint - validate and novalidate both present", func(t *testing.T) { + inlineConstraint := ColumnInlineConstraint{ + Type: ColumnConstraintTypeUnique, + Validate: Bool(true), + NoValidate: Bool(true), + } + opts := defaultOptsWithColumnInlineConstraint(&inlineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("ColumnInlineConstraint", "Validate", "Novalidate")) + }) + + t.Run("validation: inline constraint - rely and norely both present", func(t *testing.T) { + inlineConstraint := ColumnInlineConstraint{ + Type: ColumnConstraintTypeUnique, + Rely: Bool(true), + NoRely: Bool(true), + } + opts := defaultOptsWithColumnInlineConstraint(&inlineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("ColumnInlineConstraint", "Rely", "Norely")) + }) + + t.Run("validation: out of line constraint - no columns", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeUnique, + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errNotSet("OutOfLineConstraint", "Columns")) + }) + + t.Run("validation: out of line constraint - constraint name empty", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: "", + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errInvalidValue("OutOfLineConstraint", "Type", "")) + }) + + t.Run("validation: out of line constraint - constraint ", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: "not existing type", + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errInvalidValue("OutOfLineConstraint", "Type", "not existing type")) + }) + + t.Run("validation: out of line constraint - foreign key present for foreign key constraint", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeForeignKey, + ForeignKey: nil, + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errNotSet("OutOfLineConstraint", "ForeignKey")) + }) + + t.Run("validation: out of line constraint - foreign key validation", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeForeignKey, + ForeignKey: &OutOfLineForeignKey{ + TableName: NewSchemaObjectIdentifier("", "", ""), + }, + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errNotSet("OutOfLineForeignKey", "TableName")) + }) + + t.Run("validation: out of line constraint - foreign key absent for constraint other than foreign key", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeUnique, + ForeignKey: &OutOfLineForeignKey{ + TableName: RandomSchemaObjectIdentifier(), + }, + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errSet("OutOfLineConstraint", "ForeignKey")) + }) + + t.Run("validation: out of line constraint - enforced and not enforced both present", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeUnique, + Enforced: Bool(true), + NotEnforced: Bool(true), + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("OutOfLineConstraint", "Enforced", "NotEnforced")) + }) + + t.Run("validation: out of line constraint - deferrable and not deferrable both present", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeUnique, + Deferrable: Bool(true), + NotDeferrable: Bool(true), + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("OutOfLineConstraint", "Deferrable", "NotDeferrable")) + }) + + t.Run("validation: out of line constraint - initially deferred and initially immediate both present", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeUnique, + InitiallyDeferred: Bool(true), + InitiallyImmediate: Bool(true), + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("OutOfLineConstraint", "InitiallyDeferred", "InitiallyImmediate")) + }) + + t.Run("validation: out of line constraint - enable and disable both present", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeUnique, + Enable: Bool(true), + Disable: Bool(true), + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("OutOfLineConstraint", "Enable", "Disable")) + }) + + t.Run("validation: out of line constraint - validate and novalidate both present", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeUnique, + Validate: Bool(true), + NoValidate: Bool(true), + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("OutOfLineConstraint", "Validate", "Novalidate")) + }) + + t.Run("validation: out of line constraint - rely and norely both present", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeUnique, + Rely: Bool(true), + NoRely: Bool(true), + } + opts := defaultOptsWithColumnOutOfLineConstraint(&outOfLineConstraint) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("OutOfLineConstraint", "Rely", "Norely")) + }) + + t.Run("with complete options", func(t *testing.T) { + columnComment := random.String() + tableComment := random.String() + collation := "de" + columnName := "FIRST_COLUMN" + columnType, err := ToDataType("VARCHAR") + maskingPolicy := ColumnMaskingPolicy{ + Name: RandomSchemaObjectIdentifier(), + Using: []string{"FOO", "BAR"}, + } + columnTags := []TagAssociation{ + { + Name: NewSchemaObjectIdentifier("db", "schema", "column_tag1"), + Value: "v1", + }, + { + Name: NewSchemaObjectIdentifier("db", "schema", "column_tag2"), + Value: "v2", + }, + } + + tableTags := []TagAssociation{ + { + Name: NewSchemaObjectIdentifier("db", "schema", "table_tag1"), + Value: "v1", + }, + { + Name: NewSchemaObjectIdentifier("db", "schema", "table_tag2"), + Value: "v2", + }, + } + inlineConstraint := ColumnInlineConstraint{ + Name: String("INLINE_CONSTRAINT"), + Type: ColumnConstraintTypePrimaryKey, + } + require.NoError(t, err) + outOfLineConstraint1 := OutOfLineConstraint{ + Name: "OUT_OF_LINE_CONSTRAINT", + Type: ColumnConstraintTypeForeignKey, + Columns: []string{"COLUMN_1", "COLUMN_2"}, + ForeignKey: &OutOfLineForeignKey{ + TableName: RandomSchemaObjectIdentifier(), + ColumnNames: []string{"COLUMN_3", "COLUMN_4"}, + Match: Pointer(FullMatchType), + On: &ForeignKeyOnAction{ + OnUpdate: Pointer(ForeignKeySetNullAction), + OnDelete: Pointer(ForeignKeyRestrictAction), + }, + }, + } + outOfLineConstraint2 := OutOfLineConstraint{ + Type: ColumnConstraintTypeUnique, + Columns: []string{"COLUMN_1"}, + Enforced: Bool(true), + Deferrable: Bool(true), + InitiallyDeferred: Bool(true), + Enable: Bool(true), + Rely: Bool(true), + } + stageFileFormat := StageFileFormat{ + Type: Pointer(FileFormatTypeCSV), + Options: &FileFormatTypeOptions{ + CSVCompression: Pointer(CSVCompressionAuto), + }, + } + stageCopyOptions := StageCopyOptions{ + OnError: &StageCopyOnErrorOptions{SkipFile: String("SKIP_FILE")}, + } + rowAccessPolicy := TableRowAccessPolicy{ + Name: RandomSchemaObjectIdentifier(), + On: []string{"COLUMN_1", "COLUMN_2"}, + } + columns := []TableColumn{{ + Name: columnName, + Type: columnType, + Collate: &collation, + Comment: &columnComment, + DefaultValue: &ColumnDefaultValue{ + Identity: &ColumnIdentity{ + Start: 10, + Increment: 1, + Order: Bool(true), + }, + }, + NotNull: Bool(true), + MaskingPolicy: &maskingPolicy, + Tags: columnTags, + InlineConstraint: &inlineConstraint, + }} + opts := &createTableOptions{ + name: id, + ColumnsAndConstraints: CreateTableColumnsAndConstraints{columns, []OutOfLineConstraint{outOfLineConstraint1, outOfLineConstraint2}}, + ClusterBy: []string{"COLUMN_1", "COLUMN_2"}, + EnableSchemaEvolution: Bool(true), + StageFileFormat: &stageFileFormat, + StageCopyOptions: &stageCopyOptions, + DataRetentionTimeInDays: Int(10), + MaxDataExtensionTimeInDays: Int(100), + ChangeTracking: Bool(true), + DefaultDDLCollation: String("en"), + CopyGrants: Bool(true), + RowAccessPolicy: &rowAccessPolicy, + Tags: tableTags, + Comment: &tableComment, + } + assertOptsValidAndSQLEquals(t, opts, + `CREATE TABLE %s (%s %s CONSTRAINT INLINE_CONSTRAINT PRIMARY KEY NOT NULL COLLATE 'de' IDENTITY START 10 INCREMENT 1 ORDER MASKING POLICY %s USING (FOO, BAR) TAG ("db"."schema"."column_tag1" = 'v1', "db"."schema"."column_tag2" = 'v2') COMMENT '%s', CONSTRAINT OUT_OF_LINE_CONSTRAINT FOREIGN KEY (COLUMN_1, COLUMN_2) REFERENCES %s (COLUMN_3, COLUMN_4) MATCH FULL ON UPDATE SET NULL ON DELETE RESTRICT, CONSTRAINT UNIQUE (COLUMN_1) ENFORCED DEFERRABLE INITIALLY DEFERRED ENABLE RELY) CLUSTER BY (COLUMN_1, COLUMN_2) ENABLE_SCHEMA_EVOLUTION = true STAGE_FILE_FORMAT = (TYPE = CSV COMPRESSION = AUTO) STAGE_COPY_OPTIONS = (ON_ERROR = SKIP_FILE) DATA_RETENTION_TIME_IN_DAYS = 10 MAX_DATA_EXTENSION_TIME_IN_DAYS = 100 CHANGE_TRACKING = true DEFAULT_DDL_COLLATION = 'en' COPY GRANTS ROW ACCESS POLICY %s ON (COLUMN_1, COLUMN_2) TAG ("db"."schema"."table_tag1" = 'v1', "db"."schema"."table_tag2" = 'v2') COMMENT = '%s'`, + id.FullyQualifiedName(), + columnName, + columnType, + maskingPolicy.Name.FullyQualifiedName(), + columnComment, + outOfLineConstraint1.ForeignKey.TableName.FullyQualifiedName(), + rowAccessPolicy.Name.FullyQualifiedName(), + tableComment, + ) + }) + + t.Run("with skip file x", func(t *testing.T) { + columns := []TableColumnRequest{ + {name: "FIRST_COLUMN", type_: DataTypeVARCHAR}, + } + request := NewCreateTableRequest(id, columns). + WithStageCopyOptions(*NewStageCopyOptionsRequest().WithOnError(NewStageCopyOnErrorOptionsRequest().WithSkipFileX(5))) + assertOptsValidAndSQLEquals(t, request.toOpts(), `CREATE TABLE %s (FIRST_COLUMN VARCHAR) STAGE_COPY_OPTIONS = (ON_ERROR = SKIP_FILE_5)`, id.FullyQualifiedName()) + }) + + t.Run("with skip file x %", func(t *testing.T) { + columns := []TableColumnRequest{ + {name: "FIRST_COLUMN", type_: DataTypeVARCHAR}, + } + request := NewCreateTableRequest(id, columns). + WithStageCopyOptions(*NewStageCopyOptionsRequest().WithOnError(NewStageCopyOnErrorOptionsRequest().WithSkipFileXPercent(10))) + assertOptsValidAndSQLEquals(t, request.toOpts(), `CREATE TABLE %s (FIRST_COLUMN VARCHAR) STAGE_COPY_OPTIONS = (ON_ERROR = 'SKIP_FILE_10%%')`, id.FullyQualifiedName()) + }) +} + +func TestTableCreateAsSelect(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + defaultOpts := func() *createTableAsSelectOptions { + return &createTableAsSelectOptions{ + name: id, + Columns: []TableAsSelectColumn{{Name: "a"}}, + } + } + + t.Run("empty options", func(t *testing.T) { + opts := &createTableAsSelectOptions{} + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("createTableAsSelectOptions", "name")) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("createTableAsSelectOptions", "name")) + }) + + t.Run("validation: no columns", func(t *testing.T) { + opts := defaultOpts() + opts.Columns = []TableAsSelectColumn{} + assertOptsInvalidJoinedErrors(t, opts, errNotSet("createTableAsSelectOptions", "Columns")) + }) + + t.Run("validation: no query", func(t *testing.T) { + opts := defaultOpts() + opts.Query = "" + assertOptsInvalidJoinedErrors(t, opts, errNotSet("createTableAsSelectOptions", "Query")) + }) + + t.Run("with complete options", func(t *testing.T) { + id := RandomSchemaObjectIdentifier() + columnName := "FIRST_COLUMN" + columnType, err := ToDataType("VARCHAR") + require.NoError(t, err) + maskingPolicy := TableAsSelectColumnMaskingPolicy{ + Name: RandomSchemaObjectIdentifier(), + } + rowAccessPolicy := TableRowAccessPolicy{ + Name: RandomSchemaObjectIdentifier(), + On: []string{"COLUMN_1", "COLUMN_2"}, + } + opts := &createTableAsSelectOptions{ + OrReplace: Bool(true), + name: id, + Columns: []TableAsSelectColumn{ + { + Name: columnName, + Type: Pointer(columnType), + MaskingPolicy: &maskingPolicy, + }, + }, + ClusterBy: []string{"COLUMN_1", "COLUMN_2"}, + CopyGrants: Bool(true), + + RowAccessPolicy: &rowAccessPolicy, + Query: "SELECT * FROM ANOTHER_TABLE", + } + assertOptsValidAndSQLEquals(t, opts, "CREATE OR REPLACE TABLE %s (FIRST_COLUMN VARCHAR MASKING POLICY %s) CLUSTER BY (COLUMN_1, COLUMN_2) COPY GRANTS ROW ACCESS POLICY %s ON (COLUMN_1, COLUMN_2) AS SELECT * FROM ANOTHER_TABLE", + id.FullyQualifiedName(), + maskingPolicy.Name.FullyQualifiedName(), + rowAccessPolicy.Name.FullyQualifiedName(), + ) + }) +} + +func TestTableCreateUsingTemplate(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + defaultOpts := func() *createTableUsingTemplateOptions { + return &createTableUsingTemplateOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *createTableUsingTemplateOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("createTableUsingTemplateOptions", "name")) + }) + + t.Run("with complete options", func(t *testing.T) { + id := RandomSchemaObjectIdentifier() + opts := &createTableUsingTemplateOptions{ + OrReplace: Bool(true), + name: id, + CopyGrants: Bool(true), + Query: []string{"sample_data"}, + } + assertOptsValidAndSQLEquals(t, opts, "CREATE OR REPLACE TABLE %s COPY GRANTS USING TEMPLATE (sample_data)", id.FullyQualifiedName()) + }) +} + +func TestTableCreateLike(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + defaultOpts := func() *createTableLikeOptions { + return &createTableLikeOptions{ + name: id, + SourceTable: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *createTableLikeOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("createTableLikeOptions", "name")) + }) + + t.Run("validation: source table's incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.SourceTable = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("createTableLikeOptions", "SourceTable")) + }) + + t.Run("empty options", func(t *testing.T) { + opts := &createTableLikeOptions{} + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("createTableLikeOptions", "name")) + }) + + t.Run("with complete options", func(t *testing.T) { + id := RandomSchemaObjectIdentifier() + sourceTable := RandomSchemaObjectIdentifier() + opts := &createTableLikeOptions{ + OrReplace: Bool(true), + name: id, + SourceTable: sourceTable, + ClusterBy: []string{"date", "id"}, + CopyGrants: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, "CREATE OR REPLACE TABLE %s LIKE %s CLUSTER BY (date, id) COPY GRANTS", id.FullyQualifiedName(), sourceTable.FullyQualifiedName()) + }) +} + +func TestTableCreateClone(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + defaultOpts := func() *createTableCloneOptions { + return &createTableCloneOptions{ + name: id, + SourceTable: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *createTableCloneOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("createTableCloneOptions", "name")) + }) + + t.Run("validation: source table's incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.SourceTable = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("createTableCloneOptions", "SourceTable")) + }) + + t.Run("empty options", func(t *testing.T) { + opts := &createTableCloneOptions{} + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("createTableCloneOptions", "name")) + }) + + t.Run("with complete options", func(t *testing.T) { + id := RandomSchemaObjectIdentifier() + sourceTable := RandomSchemaObjectIdentifier() + clonePoint := ClonePoint{ + Moment: CloneMomentAt, + At: TimeTravel{ + Offset: Int(0), + }, + } + opts := &createTableCloneOptions{ + OrReplace: Bool(true), + name: id, + SourceTable: sourceTable, + ClonePoint: &clonePoint, + CopyGrants: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, "CREATE OR REPLACE TABLE %s CLONE %s AT (OFFSET => 0) COPY GRANTS", id.FullyQualifiedName(), sourceTable.FullyQualifiedName()) + }) +} + +func TestTableAlter(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + defaultOpts := func() *alterTableOptions { + return &alterTableOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *alterTableOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: no action", func(t *testing.T) { + opts := defaultOpts() + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("alterTableOptions", "NewName", "SwapWith", "ClusteringAction", "ColumnAction", "ConstraintAction", "ExternalTableAction", "SearchOptimizationAction", "Set", "SetTags", "UnsetTags", "Unset", "AddRowAccessPolicy", "DropRowAccessPolicy", "DropAndAddRowAccessPolicy", "DropAllAccessRowPolicies")) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("alterTableOptions", "name")) + }) + + t.Run("validation: both NewName and SwapWith are present ", func(t *testing.T) { + opts := defaultOpts() + opts.NewName = Pointer(NewSchemaObjectIdentifier("test", "test", "test")) + opts.SwapWith = Pointer(NewSchemaObjectIdentifier("test", "test", "test")) + + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("alterTableOptions", "NewName", "SwapWith", "ClusteringAction", "ColumnAction", "ConstraintAction", "ExternalTableAction", "SearchOptimizationAction", "Set", "SetTags", "UnsetTags", "Unset", "AddRowAccessPolicy", "DropRowAccessPolicy", "DropAndAddRowAccessPolicy", "DropAllAccessRowPolicies")) + }) + + t.Run("validation: NewName's incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.NewName = Pointer(NewSchemaObjectIdentifier("", "", "")) + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("alterTableOptions", "NewName")) + }) + + t.Run("validation: SwapWith incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.SwapWith = Pointer(NewSchemaObjectIdentifier("", "", "")) + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("alterTableOptions", "SwapWith")) + }) + + t.Run("validation: clustering action - no option present", func(t *testing.T) { + opts := defaultOpts() + opts.ClusteringAction = &TableClusteringAction{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("ClusteringAction", "ClusterBy", "Recluster", "ChangeReclusterState", "DropClusteringKey")) + }) + + t.Run("validation: clustering action - two options present", func(t *testing.T) { + opts := defaultOpts() + opts.ClusteringAction = &TableClusteringAction{ + ClusterBy: []string{"date"}, + Recluster: &TableReclusterAction{ + MaxSize: Int(10), + Condition: String("true"), + }, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("ClusteringAction", "ClusterBy", "Recluster", "ChangeReclusterState", "DropClusteringKey")) + }) + + t.Run("validation: column action - no option present", func(t *testing.T) { + opts := defaultOpts() + opts.ColumnAction = &TableColumnAction{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("ColumnAction", "Add", "Rename", "Alter", "SetMaskingPolicy", "UnsetMaskingPolicy", "SetTags", "UnsetTags", "DropColumns")) + }) + + t.Run("validation: column action - two options present", func(t *testing.T) { + opts := defaultOpts() + opts.ColumnAction = &TableColumnAction{ + Add: &TableColumnAddAction{}, + Rename: &TableColumnRenameAction{ + NewName: "new", + OldName: "old", + }, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("ColumnAction", "Add", "Rename", "Alter", "SetMaskingPolicy", "UnsetMaskingPolicy", "SetTags", "UnsetTags", "DropColumns")) + }) + + t.Run("validation: column action alter - no option present", func(t *testing.T) { + opts := defaultOpts() + opts.ColumnAction = &TableColumnAction{ + Alter: []TableColumnAlterAction{{}}, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("TableColumnAlterAction", "DropDefault", "SetDefault", "NotNullConstraint", "Type", "Comment", "UnsetComment")) + }) + + t.Run("validation: column action alter - two options present", func(t *testing.T) { + opts := defaultOpts() + opts.ColumnAction = &TableColumnAction{ + Alter: []TableColumnAlterAction{{ + DropDefault: Bool(true), + SetDefault: Pointer(SequenceName("sequence")), + }}, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("TableColumnAlterAction", "DropDefault", "SetDefault", "NotNullConstraint", "Type", "Comment", "UnsetComment")) + }) + + t.Run("validation: constraint alter action - no option present", func(t *testing.T) { + opts := defaultOpts() + opts.ConstraintAction = &TableConstraintAction{ + Alter: &TableConstraintAlterAction{}, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("TableConstraintAlterAction", "ConstraintName", "PrimaryKey", "Unique", "ForeignKey", "Columns")) + }) + + t.Run("validation: constraint alter action - no columns", func(t *testing.T) { + opts := defaultOpts() + opts.ConstraintAction = &TableConstraintAction{ + Alter: &TableConstraintAlterAction{ + ConstraintName: String("constraint"), + Columns: []string{}, + }, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("TableConstraintAlterAction", "Columns")) + }) + + t.Run("validation: constraint alter action - two options present", func(t *testing.T) { + opts := defaultOpts() + opts.ConstraintAction = &TableConstraintAction{ + Alter: &TableConstraintAlterAction{ + ConstraintName: String("constraint"), + Unique: Bool(true), + }, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("TableConstraintAlterAction", "ConstraintName", "PrimaryKey", "Unique", "ForeignKey", "Columns")) + }) + + t.Run("validation: constraint drop action - no option present", func(t *testing.T) { + opts := defaultOpts() + opts.ConstraintAction = &TableConstraintAction{ + Drop: &TableConstraintDropAction{}, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("TableConstraintDropAction", "ConstraintName", "PrimaryKey", "Unique", "ForeignKey", "Columns")) + }) + + t.Run("validation: constraint drop action - no columns", func(t *testing.T) { + opts := defaultOpts() + opts.ConstraintAction = &TableConstraintAction{ + Drop: &TableConstraintDropAction{ + ConstraintName: String("constraint"), + Columns: []string{}, + }, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("TableConstraintDropAction", "Columns")) + }) + + t.Run("validation: constraint drop action - two options present", func(t *testing.T) { + opts := defaultOpts() + opts.ConstraintAction = &TableConstraintAction{ + Drop: &TableConstraintDropAction{ + ConstraintName: String("constraint"), + Unique: Bool(true), + }, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("TableConstraintDropAction", "ConstraintName", "PrimaryKey", "Unique", "ForeignKey", "Columns")) + }) + + t.Run("validation: external action - no option present", func(t *testing.T) { + opts := defaultOpts() + opts.ExternalTableAction = &TableExternalTableAction{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("TableExternalTableAction", "Add", "Rename", "Drop")) + }) + + t.Run("validation: external action - two options present", func(t *testing.T) { + opts := defaultOpts() + opts.ExternalTableAction = &TableExternalTableAction{ + Add: &TableExternalTableColumnAddAction{}, + Rename: &TableExternalTableColumnRenameAction{ + OldName: "old_name", + NewName: "new_name", + }, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("TableExternalTableAction", "Add", "Rename", "Drop")) + }) + + t.Run("validation: external action - drop with no columns", func(t *testing.T) { + opts := defaultOpts() + opts.ExternalTableAction = &TableExternalTableAction{ + Drop: &TableExternalTableColumnDropAction{ + Names: []string{}, + }, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("TableExternalTableColumnDropAction", "Names")) + }) + + t.Run("validation: search optimization - no option present", func(t *testing.T) { + opts := defaultOpts() + opts.SearchOptimizationAction = &TableSearchOptimizationAction{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("TableSearchOptimizationAction", "Add", "Drop")) + }) + + t.Run("validation: search optimization - two options present", func(t *testing.T) { + opts := defaultOpts() + opts.SearchOptimizationAction = &TableSearchOptimizationAction{ + Add: &AddSearchOptimization{}, + Drop: &DropSearchOptimization{}, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("TableSearchOptimizationAction", "Add", "Drop")) + }) + + t.Run("empty options", func(t *testing.T) { + opts := &alterTableOptions{} + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("alterTableOptions", "name")) + }) + + t.Run("rename", func(t *testing.T) { + newID := NewSchemaObjectIdentifier(id.databaseName, id.schemaName, random.UUID()) + opts := &alterTableOptions{ + name: id, + NewName: &newID, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s RENAME TO %s", id.FullyQualifiedName(), newID.FullyQualifiedName()) + }) + + t.Run("swap with", func(t *testing.T) { + targetTableId := NewSchemaObjectIdentifier(id.databaseName, id.schemaName, random.UUID()) + opts := &alterTableOptions{ + name: id, + SwapWith: &targetTableId, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s SWAP WITH %s", id.FullyQualifiedName(), targetTableId.FullyQualifiedName()) + }) + + t.Run("cluster by", func(t *testing.T) { + clusterByColumns := []string{"date", "id"} + opts := &alterTableOptions{ + name: id, + ClusteringAction: &TableClusteringAction{ + ClusterBy: clusterByColumns, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s CLUSTER BY (date, id)", id.FullyQualifiedName()) + }) + + t.Run("recluster", func(t *testing.T) { + condition := "name = 'John'" + opts := &alterTableOptions{ + name: id, + ClusteringAction: &TableClusteringAction{ + Recluster: &TableReclusterAction{ + MaxSize: Int(1024), + Condition: &condition, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s RECLUSTER MAX_SIZE = 1024 WHERE name = 'John'", id.FullyQualifiedName()) + }) + + t.Run("suspend recluster", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + ClusteringAction: &TableClusteringAction{ + ChangeReclusterState: &TableReclusterChangeState{ + State: Pointer(ReclusterStateSuspend), + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s SUSPEND RECLUSTER", id.FullyQualifiedName()) + }) + + t.Run("drop clustering key", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + ClusteringAction: &TableClusteringAction{ + DropClusteringKey: Bool(true), + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s DROP CLUSTERING KEY", id.FullyQualifiedName()) + }) + + t.Run("add new column", func(t *testing.T) { + columnName := "NEXT_COLUMN" + opts := &alterTableOptions{ + name: id, + ColumnAction: &TableColumnAction{ + Add: &TableColumnAddAction{ + IfNotExists: Bool(true), + Name: columnName, + Type: DataTypeBoolean, + DefaultValue: &ColumnDefaultValue{ + Identity: &ColumnIdentity{ + Start: 10, + Increment: 1, + }, + }, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s ADD COLUMN IF NOT EXISTS NEXT_COLUMN BOOLEAN IDENTITY START 10 INCREMENT 1", id.FullyQualifiedName()) + }) + + t.Run("rename column", func(t *testing.T) { + oldColumn := "OLD_NAME" + newColumnName := "NEW_NAME" + opts := &alterTableOptions{ + name: id, + ColumnAction: &TableColumnAction{ + Rename: &TableColumnRenameAction{ + OldName: oldColumn, + NewName: newColumnName, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s RENAME COLUMN OLD_NAME TO NEW_NAME", id.FullyQualifiedName()) + }) + + t.Run("alter column", func(t *testing.T) { + // column_1 + columnOneName := "COLUMN_1" + alterActionsForColumnOne := []TableColumnAlterAction{ + { + Name: columnOneName, + DropDefault: Bool(true), + }, + { + Name: columnOneName, + SetDefault: Pointer(SequenceName("SEQUENCE_1")), + }, + { + Name: columnOneName, + UnsetComment: Bool(true), + }, + } + + columnTwoName := "COLUMN_2" + alterActionsForColumnTwo := []TableColumnAlterAction{ + { + Name: columnTwoName, + DropDefault: Bool(true), + }, + { + Name: columnTwoName, + SetDefault: Pointer(SequenceName("SEQUENCE_2")), + }, + { + Name: columnTwoName, + Comment: String("comment"), + }, + { + Name: columnTwoName, + Type: Pointer(DataTypeBoolean), + }, + { + Name: columnTwoName, + NotNullConstraint: &TableColumnNotNullConstraint{Drop: Bool(true)}, + }, + } + var actions []TableColumnAlterAction + actions = append(actions, alterActionsForColumnOne...) + actions = append(actions, alterActionsForColumnTwo...) + + opts := &alterTableOptions{ + name: id, + ColumnAction: &TableColumnAction{ + Alter: actions, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s ALTER COLUMN COLUMN_1 DROP DEFAULT, COLUMN COLUMN_1 SET DEFAULT SEQUENCE_1.NEXTVAL, COLUMN COLUMN_1 UNSET COMMENT, COLUMN COLUMN_2 DROP DEFAULT, COLUMN COLUMN_2 SET DEFAULT SEQUENCE_2.NEXTVAL, COLUMN COLUMN_2 COMMENT 'comment', COLUMN COLUMN_2 SET DATA TYPE BOOLEAN, COLUMN COLUMN_2 DROP NOT NULL", id.FullyQualifiedName()) + }) + + t.Run("alter: set masking policy", func(t *testing.T) { + maskingPolicyName := RandomSchemaObjectIdentifier() + opts := &alterTableOptions{ + name: id, + ColumnAction: &TableColumnAction{ + SetMaskingPolicy: &TableColumnAlterSetMaskingPolicyAction{ + ColumnName: "COLUMN_1", + MaskingPolicyName: maskingPolicyName, + Using: []string{"FOO", "BAR"}, + Force: Bool(true), + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s ALTER COLUMN COLUMN_1 SET MASKING POLICY %s USING (FOO, BAR) FORCE", id.FullyQualifiedName(), maskingPolicyName.FullyQualifiedName()) + }) + + t.Run("alter: unset masking policy", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + ColumnAction: &TableColumnAction{ + UnsetMaskingPolicy: &TableColumnAlterUnsetMaskingPolicyAction{ + ColumnName: "COLUMN_1", + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s ALTER COLUMN COLUMN_1 UNSET MASKING POLICY", id.FullyQualifiedName()) + }) + + t.Run("alter: set tags", func(t *testing.T) { + columnTags := []TagAssociation{ + { + Name: NewSchemaObjectIdentifier("db", "schema", "column_tag1"), + Value: "v1", + }, + { + Name: NewSchemaObjectIdentifier("db", "schema", "column_tag2"), + Value: "v2", + }, + } + opts := &alterTableOptions{ + name: id, + ColumnAction: &TableColumnAction{ + SetTags: &TableColumnAlterSetTagsAction{ + ColumnName: "COLUMN_1", + Tags: columnTags, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE %s ALTER COLUMN COLUMN_1 SET TAG "db"."schema"."column_tag1" = 'v1', "db"."schema"."column_tag2" = 'v2'`, id.FullyQualifiedName()) + }) + + t.Run("alter: unset tags", func(t *testing.T) { + columnTags := []ObjectIdentifier{ + NewSchemaObjectIdentifier("db", "schema", "column_tag1"), + NewSchemaObjectIdentifier("db", "schema", "column_tag2"), + } + opts := &alterTableOptions{ + name: id, + ColumnAction: &TableColumnAction{ + UnsetTags: &TableColumnAlterUnsetTagsAction{ + ColumnName: "COLUMN_1", + Tags: columnTags, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE %s ALTER COLUMN COLUMN_1 UNSET TAG "db"."schema"."column_tag1", "db"."schema"."column_tag2"`, id.FullyQualifiedName()) + }) + + t.Run("alter: drop columns", func(t *testing.T) { + columns := []string{"COLUMN_1", "COLUMN_2"} + opts := &alterTableOptions{ + name: id, + ColumnAction: &TableColumnAction{ + DropColumns: &TableColumnAlterDropColumns{ + IfExists: Bool(true), + Columns: columns, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s DROP COLUMN IF EXISTS COLUMN_1, COLUMN_2", id.FullyQualifiedName()) + }) + + t.Run("validation: alter constraint: no option", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + ConstraintAction: &TableConstraintAction{}, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("ConstraintAction", "Add", "Rename", "Alter", "Drop")) + }) + + t.Run("validation: alter constraint: more than one option", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeUnique, + Columns: []string{"COLUMN_1"}, + } + opts := &alterTableOptions{ + name: id, + ConstraintAction: &TableConstraintAction{ + Add: &outOfLineConstraint, + Rename: &TableConstraintRenameAction{ + OldName: "OLD_NAME_CONSTRAINT", + NewName: "NEW_NAME_CONSTRAINT", + }, + }, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("ConstraintAction", "Add", "Rename", "Alter", "Drop")) + }) + + t.Run("validation: alter constraint: validation", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Type: ColumnConstraintTypeUnique, + } + opts := &alterTableOptions{ + name: id, + ConstraintAction: &TableConstraintAction{ + Add: &outOfLineConstraint, + }, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("OutOfLineConstraint", "Columns")) + }) + + t.Run("alter constraint: add", func(t *testing.T) { + outOfLineConstraint := OutOfLineConstraint{ + Name: "OUT_OF_LINE_CONSTRAINT", + Type: ColumnConstraintTypeForeignKey, + Columns: []string{"COLUMN_1", "COLUMN_2"}, + ForeignKey: &OutOfLineForeignKey{ + TableName: RandomSchemaObjectIdentifier(), + ColumnNames: []string{"COLUMN_3", "COLUMN_4"}, + Match: Pointer(FullMatchType), + On: &ForeignKeyOnAction{ + OnUpdate: Pointer(ForeignKeySetNullAction), + OnDelete: Pointer(ForeignKeyRestrictAction), + }, + }, + } + opts := &alterTableOptions{ + name: id, + ConstraintAction: &TableConstraintAction{ + Add: &outOfLineConstraint, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s ADD CONSTRAINT OUT_OF_LINE_CONSTRAINT FOREIGN KEY (COLUMN_1, COLUMN_2) REFERENCES %s (COLUMN_3, COLUMN_4) MATCH FULL ON UPDATE SET NULL ON DELETE RESTRICT", id.FullyQualifiedName(), outOfLineConstraint.ForeignKey.TableName.FullyQualifiedName()) + }) + + t.Run("alter constraint: rename", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + ConstraintAction: &TableConstraintAction{ + Rename: &TableConstraintRenameAction{ + OldName: "OLD_NAME_CONSTRAINT", + NewName: "NEW_NAME_CONSTRAINT", + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s RENAME CONSTRAINT OLD_NAME_CONSTRAINT TO NEW_NAME_CONSTRAINT", id.FullyQualifiedName()) + }) + + t.Run("alter constraint: alter", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + ConstraintAction: &TableConstraintAction{ + Alter: &TableConstraintAlterAction{ + ConstraintName: String("OUT_OF_LINE_CONSTRAINT"), + Columns: []string{"COLUMN_3", "COLUMN_4"}, + NotEnforced: Bool(true), + Validate: Bool(true), + Rely: Bool(true), + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s ALTER CONSTRAINT OUT_OF_LINE_CONSTRAINT (COLUMN_3, COLUMN_4) NOT ENFORCED VALIDATE RELY", id.FullyQualifiedName()) + }) + + t.Run("alter constraint: drop", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + ConstraintAction: &TableConstraintAction{ + Drop: &TableConstraintDropAction{ + ConstraintName: String("OUT_OF_LINE_CONSTRAINT"), + Columns: []string{"COLUMN_3", "COLUMN_4"}, + Cascade: Bool(true), + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s DROP CONSTRAINT OUT_OF_LINE_CONSTRAINT (COLUMN_3, COLUMN_4) CASCADE", id.FullyQualifiedName()) + }) + + t.Run("external table: add", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + ExternalTableAction: &TableExternalTableAction{ + Add: &TableExternalTableColumnAddAction{ + IfNotExists: Bool(true), + Name: "COLUMN_1", + Type: DataTypeBoolean, + Expression: []string{"SELECT 1"}, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s ADD COLUMN IF NOT EXISTS COLUMN_1 BOOLEAN AS (SELECT 1)", id.FullyQualifiedName()) + }) + + t.Run("external table: rename", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + ExternalTableAction: &TableExternalTableAction{ + Rename: &TableExternalTableColumnRenameAction{ + OldName: "OLD_NAME_COLUMN", + NewName: "NEW_NAME_COLUMN", + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s RENAME COLUMN OLD_NAME_COLUMN TO NEW_NAME_COLUMN", id.FullyQualifiedName()) + }) + + t.Run("external table: drop", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + ExternalTableAction: &TableExternalTableAction{ + Drop: &TableExternalTableColumnDropAction{ + IfExists: Bool(true), + Names: []string{"COLUMN_3", "COLUMN_4"}, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s DROP COLUMN IF EXISTS COLUMN_3, COLUMN_4", id.FullyQualifiedName()) + }) + + t.Run("add search optimization", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + SearchOptimizationAction: &TableSearchOptimizationAction{ + Add: &AddSearchOptimization{ + On: []string{"SUBSTRING(*)", "GEO(*)"}, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s ADD SEARCH OPTIMIZATION ON SUBSTRING(*), GEO(*)", id.FullyQualifiedName()) + }) + + t.Run("drop search optimization", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + SearchOptimizationAction: &TableSearchOptimizationAction{ + Drop: &DropSearchOptimization{ + On: []string{"SUBSTRING(*)", "FOO"}, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s DROP SEARCH OPTIMIZATION ON SUBSTRING(*), FOO", id.FullyQualifiedName()) + }) + + t.Run("drop search optimization", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + SearchOptimizationAction: &TableSearchOptimizationAction{ + Drop: &DropSearchOptimization{ + On: []string{"SUBSTRING(*)", "FOO"}, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, "ALTER TABLE %s DROP SEARCH OPTIMIZATION ON SUBSTRING(*), FOO", id.FullyQualifiedName()) + }) + + t.Run("set: with complete options", func(t *testing.T) { + comment := random.String() + opts := &alterTableOptions{ + name: id, + Set: &TableSet{ + EnableSchemaEvolution: Bool(true), + StageFileFormat: &StageFileFormat{ + Type: Pointer(FileFormatTypeCSV), + }, + StageCopyOptions: &StageCopyOptions{ + OnError: &StageCopyOnErrorOptions{SkipFile: String("SKIP_FILE")}, + }, + DataRetentionTimeInDays: Int(30), + MaxDataExtensionTimeInDays: Int(90), + ChangeTracking: Bool(false), + DefaultDDLCollation: String("us"), + Comment: &comment, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE %s SET ENABLE_SCHEMA_EVOLUTION = true STAGE_FILE_FORMAT = (TYPE = CSV) STAGE_COPY_OPTIONS = (ON_ERROR = SKIP_FILE) DATA_RETENTION_TIME_IN_DAYS = 30 MAX_DATA_EXTENSION_TIME_IN_DAYS = 90 CHANGE_TRACKING = false DEFAULT_DDL_COLLATION = 'us' COMMENT = '%s'`, id.FullyQualifiedName(), comment) + }) + + t.Run("set tags", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + SetTags: []TagAssociation{ + { + Name: NewSchemaObjectIdentifier("db", "schema", "table_tag1"), + Value: "v1", + }, + { + Name: NewSchemaObjectIdentifier("db", "schema", "table_tag2"), + Value: "v2", + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE %s SET TAG "db"."schema"."table_tag1" = 'v1', "db"."schema"."table_tag2" = 'v2'`, id.FullyQualifiedName()) + }) + + t.Run("unset tags", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + UnsetTags: []ObjectIdentifier{ + NewSchemaObjectIdentifier("db", "schema", "table_tag1"), + NewSchemaObjectIdentifier("db", "schema", "table_tag2"), + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE %s UNSET TAG "db"."schema"."table_tag1", "db"."schema"."table_tag2"`, id.FullyQualifiedName()) + }) + + t.Run("unset: complete options", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + Unset: &TableUnset{ + DataRetentionTimeInDays: Bool(true), + MaxDataExtensionTimeInDays: Bool(true), + ChangeTracking: Bool(true), + DefaultDDLCollation: Bool(true), + EnableSchemaEvolution: Bool(true), + Comment: Bool(true), + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE %s UNSET DATA_RETENTION_TIME_IN_DAYS MAX_DATA_EXTENSION_TIME_IN_DAYS CHANGE_TRACKING DEFAULT_DDL_COLLATION ENABLE_SCHEMA_EVOLUTION COMMENT`, id.FullyQualifiedName()) + }) + + t.Run("add row access policy", func(t *testing.T) { + rowAccessPolicyId := RandomSchemaObjectIdentifier() + + opts := &alterTableOptions{ + name: id, + AddRowAccessPolicy: &TableAddRowAccessPolicy{ + RowAccessPolicy: rowAccessPolicyId, + On: []string{"FIRST_COLUMN"}, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE %s ADD ROW ACCESS POLICY %s ON (FIRST_COLUMN)`, id.FullyQualifiedName(), rowAccessPolicyId.FullyQualifiedName()) + }) + + t.Run("drop row access policy", func(t *testing.T) { + rowAccessPolicyId := RandomSchemaObjectIdentifier() + + opts := &alterTableOptions{ + name: id, + DropRowAccessPolicy: &TableDropRowAccessPolicy{ + RowAccessPolicy: rowAccessPolicyId, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE %s DROP ROW ACCESS POLICY %s`, id.FullyQualifiedName(), rowAccessPolicyId.FullyQualifiedName()) + }) + + t.Run("drop and add row access policy", func(t *testing.T) { + rowAccessPolicyId1 := RandomSchemaObjectIdentifier() + rowAccessPolicyId2 := RandomSchemaObjectIdentifier() + + opts := &alterTableOptions{ + name: id, + DropAndAddRowAccessPolicy: &TableDropAndAddRowAccessPolicy{ + Drop: TableDropRowAccessPolicy{ + RowAccessPolicy: rowAccessPolicyId1, + }, + Add: TableAddRowAccessPolicy{ + RowAccessPolicy: rowAccessPolicyId2, + On: []string{"FIRST_COLUMN"}, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE %s DROP ROW ACCESS POLICY %s, ADD ROW ACCESS POLICY %s ON (FIRST_COLUMN)`, id.FullyQualifiedName(), rowAccessPolicyId1.FullyQualifiedName(), rowAccessPolicyId2.FullyQualifiedName()) + }) + + t.Run("drop all row access policies", func(t *testing.T) { + opts := &alterTableOptions{ + name: id, + DropAllAccessRowPolicies: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER TABLE %s DROP ALL ROW ACCESS POLICIES`, id.FullyQualifiedName()) + }) +} + +func TestTableDrop(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + defaultOpts := func() *dropTableOptions { + return &dropTableOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *dropTableOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("dropTableOptions", "name")) + }) + + t.Run("empty options", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, `DROP TABLE %s`, id.FullyQualifiedName()) + }) + + t.Run("with if exists", func(t *testing.T) { + opts := defaultOpts() + opts.IfExists = Bool(true) + assertOptsValidAndSQLEquals(t, opts, `DROP TABLE IF EXISTS %s`, id.FullyQualifiedName()) + }) + + t.Run("validation: both cascade and restrict present", func(t *testing.T) { + opts := defaultOpts() + opts.Cascade = Bool(true) + opts.Restrict = Bool(true) + assertOptsInvalidJoinedErrors(t, opts, errMoreThanOneOf("dropTableOptions", "Cascade", "Restrict")) + }) +} + +func TestTableShow(t *testing.T) { + id := RandomSchemaObjectIdentifier() + + defaultOpts := func() *showTableOptions { + return &showTableOptions{} + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *ShowPipeOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: empty like", func(t *testing.T) { + opts := defaultOpts() + opts.Like = &Like{} + assertOptsInvalidJoinedErrors(t, opts, ErrPatternRequiredForLikeKeyword) + }) + + t.Run("show", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, `SHOW TABLES`) + }) + + t.Run("show with like", func(t *testing.T) { + opts := defaultOpts() + opts.Like = &Like{ + Pattern: String(id.Name()), + } + assertOptsValidAndSQLEquals(t, opts, `SHOW TABLES LIKE '%s'`, id.Name()) + }) +} + +func TestTableDescribeColumns(t *testing.T) { + id := RandomSchemaObjectIdentifier() + defaultOpts := func() *describeTableColumnsOptions { + return &describeTableColumnsOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *describeTableColumnsOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("describeTableColumnsOptions", "name")) + }) + + t.Run("describe", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, `DESCRIBE TABLE %s TYPE = COLUMNS`, id.FullyQualifiedName()) + }) +} + +func TestTableDescribeStage(t *testing.T) { + id := RandomSchemaObjectIdentifier() + defaultOpts := func() *describeTableStageOptions { + return &describeTableStageOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + var opts *describeTableStageOptions = nil + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: incorrect identifier", func(t *testing.T) { + opts := defaultOpts() + opts.name = NewSchemaObjectIdentifier("", "", "") + assertOptsInvalidJoinedErrors(t, opts, errInvalidIdentifier("describeTableStageOptions", "name")) + }) + + t.Run("describe", func(t *testing.T) { + opts := defaultOpts() + assertOptsValidAndSQLEquals(t, opts, `DESCRIBE TABLE %s TYPE = STAGE`, id.FullyQualifiedName()) + }) +} diff --git a/pkg/sdk/tables_validations.go b/pkg/sdk/tables_validations.go new file mode 100644 index 0000000000..6c6211bf9d --- /dev/null +++ b/pkg/sdk/tables_validations.go @@ -0,0 +1,372 @@ +package sdk + +import "errors" + +var ( + _ validatable = new(createTableOptions) + _ validatable = new(createTableAsSelectOptions) + _ validatable = new(createTableLikeOptions) + _ validatable = new(createTableCloneOptions) + _ validatable = new(createTableUsingTemplateOptions) + _ validatable = new(alterTableOptions) + _ validatable = new(dropTableOptions) + _ validatable = new(showTableOptions) + _ validatable = new(describeTableColumnsOptions) + _ validatable = new(describeTableStageOptions) +) + +func (opts *createTableOptions) validate() error { + if opts == nil { + return errors.Join(ErrNilOptions) + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, errInvalidIdentifier("createTableOptions", "name")) + } + if len(opts.ColumnsAndConstraints.Columns) == 0 { + errs = append(errs, errNotSet("createTableOptions", "Columns")) + } + for _, column := range opts.ColumnsAndConstraints.Columns { + if column.InlineConstraint != nil { + if err := column.InlineConstraint.validate(); err != nil { + errs = append(errs, err) + } + } + if column.DefaultValue != nil { + if ok := exactlyOneValueSet( + column.DefaultValue.Expression, + column.DefaultValue.Identity, + ); !ok { + errs = append(errs, errExactlyOneOf("DefaultValue", "Expression", "Identity")) + } + if identity := column.DefaultValue.Identity; valueSet(identity) { + if moreThanOneValueSet(identity.Order, identity.Noorder) { + errs = append(errs, errMoreThanOneOf("Identity", "Order", "Noorder")) + } + } + } + if column.MaskingPolicy != nil { + if !ValidObjectIdentifier(column.MaskingPolicy.Name) { + errs = append(errs, errInvalidIdentifier("ColumnMaskingPolicy", "Name")) + } + } + for _, tag := range column.Tags { + if !ValidObjectIdentifier(tag.Name) { + errs = append(errs, errInvalidIdentifier("TagAssociation", "Name")) + } + } + } + for _, outOfLineConstraint := range opts.ColumnsAndConstraints.OutOfLineConstraint { + if err := outOfLineConstraint.validate(); err != nil { + errs = append(errs, err) + } + } + if stageFileFormat := opts.StageFileFormat; valueSet(stageFileFormat) { + if ok := exactlyOneValueSet( + stageFileFormat.FormatName, + stageFileFormat.Type, + ); !ok { + errs = append(errs, errExactlyOneOf("StageFileFormat", "FormatName", "FormatType")) + } + } + + if opts.RowAccessPolicy != nil { + if !ValidObjectIdentifier(opts.RowAccessPolicy.Name) { + errs = append(errs, errInvalidIdentifier("TableRowAccessPolicy", "Name")) + } + } + + return errors.Join(errs...) +} + +func (opts *createTableAsSelectOptions) validate() error { + if opts == nil { + return errors.Join(ErrNilOptions) + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, errInvalidIdentifier("createTableAsSelectOptions", "name")) + } + if len(opts.Columns) == 0 { + errs = append(errs, errNotSet("createTableAsSelectOptions", "Columns")) + } + if !valueSet(opts.Query) { + errs = append(errs, errNotSet("createTableAsSelectOptions", "Query")) + } + return errors.Join(errs...) +} + +func (opts *createTableLikeOptions) validate() error { + if opts == nil { + return errors.Join(ErrNilOptions) + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, errInvalidIdentifier("createTableLikeOptions", "name")) + } + if !ValidObjectIdentifier(opts.SourceTable) { + errs = append(errs, errInvalidIdentifier("createTableLikeOptions", "SourceTable")) + } + return errors.Join(errs...) +} + +func (opts *createTableUsingTemplateOptions) validate() error { + if opts == nil { + return errors.Join(ErrNilOptions) + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, errInvalidIdentifier("createTableUsingTemplateOptions", "name")) + } + return errors.Join(errs...) +} + +func (opts *createTableCloneOptions) validate() error { + if opts == nil { + return errors.Join(ErrNilOptions) + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, errInvalidIdentifier("createTableCloneOptions", "name")) + } + if !ValidObjectIdentifier(opts.SourceTable) { + errs = append(errs, errInvalidIdentifier("createTableCloneOptions", "SourceTable")) + } + return errors.Join(errs...) +} + +func (opts *alterTableOptions) validate() error { + if opts == nil { + return errors.Join(ErrNilOptions) + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, errInvalidIdentifier("alterTableOptions", "name")) + } + if ok := exactlyOneValueSet( + opts.NewName, + opts.SwapWith, + opts.ClusteringAction, + opts.ColumnAction, + opts.ConstraintAction, + opts.ExternalTableAction, + opts.SearchOptimizationAction, + opts.Set, + opts.SetTags, + opts.UnsetTags, + opts.Unset, + opts.AddRowAccessPolicy, + opts.DropRowAccessPolicy, + opts.DropAndAddRowAccessPolicy, + opts.DropAllAccessRowPolicies, + ); !ok { + errs = append(errs, errExactlyOneOf("alterTableOptions", "NewName", "SwapWith", "ClusteringAction", "ColumnAction", "ConstraintAction", "ExternalTableAction", "SearchOptimizationAction", "Set", "SetTags", "UnsetTags", "Unset", "AddRowAccessPolicy", "DropRowAccessPolicy", "DropAndAddRowAccessPolicy", "DropAllAccessRowPolicies")) + } + if opts.NewName != nil { + if !ValidObjectIdentifier(*opts.NewName) { + errs = append(errs, errInvalidIdentifier("alterTableOptions", "NewName")) + } + } + if opts.SwapWith != nil { + if !ValidObjectIdentifier(*opts.SwapWith) { + errs = append(errs, errInvalidIdentifier("alterTableOptions", "SwapWith")) + } + } + if clusteringAction := opts.ClusteringAction; valueSet(clusteringAction) { + if ok := exactlyOneValueSet( + clusteringAction.ClusterBy, + clusteringAction.Recluster, + clusteringAction.ChangeReclusterState, + clusteringAction.DropClusteringKey, + ); !ok { + errs = append(errs, errExactlyOneOf("ClusteringAction", "ClusterBy", "Recluster", "ChangeReclusterState", "DropClusteringKey")) + } + } + if columnAction := opts.ColumnAction; valueSet(columnAction) { + if ok := exactlyOneValueSet( + columnAction.Add, + columnAction.Rename, + columnAction.Alter, + columnAction.SetMaskingPolicy, + columnAction.UnsetMaskingPolicy, + columnAction.SetTags, + columnAction.UnsetTags, + columnAction.DropColumns, + ); !ok { + errs = append(errs, errExactlyOneOf("ColumnAction", "Add", "Rename", "Alter", "SetMaskingPolicy", "UnsetMaskingPolicy", "SetTags", "UnsetTags", "DropColumns")) + } + for _, alterAction := range columnAction.Alter { + if ok := exactlyOneValueSet( + alterAction.DropDefault, + alterAction.SetDefault, + alterAction.NotNullConstraint, + alterAction.Type, + alterAction.Comment, + alterAction.UnsetComment, + ); !ok { + errs = append(errs, errExactlyOneOf("TableColumnAlterAction", "DropDefault", "SetDefault", "NotNullConstraint", "Type", "Comment", "UnsetComment")) + } + } + } + if constraintAction := opts.ConstraintAction; valueSet(constraintAction) { + if ok := exactlyOneValueSet( + constraintAction.Add, + constraintAction.Rename, + constraintAction.Alter, + constraintAction.Drop, + ); !ok { + errs = append(errs, errExactlyOneOf("ConstraintAction", "Add", "Rename", "Alter", "Drop")) + } + if alterAction := constraintAction.Alter; valueSet(alterAction) { + if ok := exactlyOneValueSet( + alterAction.ConstraintName, + alterAction.PrimaryKey, + alterAction.Unique, + alterAction.ForeignKey, + ); !ok { + errs = append(errs, errExactlyOneOf("TableConstraintAlterAction", "ConstraintName", "PrimaryKey", "Unique", "ForeignKey", "Columns")) + } + if len(alterAction.Columns) == 0 { + errs = append(errs, errNotSet("TableConstraintAlterAction", "Columns")) + } + } + if dropAction := constraintAction.Drop; valueSet(dropAction) { + if ok := exactlyOneValueSet( + dropAction.ConstraintName, + dropAction.PrimaryKey, + dropAction.Unique, + dropAction.ForeignKey, + ); !ok { + errs = append(errs, errExactlyOneOf("TableConstraintDropAction", "ConstraintName", "PrimaryKey", "Unique", "ForeignKey", "Columns")) + } + if len(dropAction.Columns) == 0 { + errs = append(errs, errNotSet("TableConstraintDropAction", "Columns")) + } + } + if addAction := constraintAction.Add; valueSet(addAction) { + if err := addAction.validate(); err != nil { + errs = append(errs, err) + } + } + } + if externalAction := opts.ExternalTableAction; valueSet(externalAction) { + if ok := exactlyOneValueSet( + externalAction.Add, + externalAction.Rename, + externalAction.Drop, + ); !ok { + errs = append(errs, errExactlyOneOf("TableExternalTableAction", "Add", "Rename", "Drop")) + } + if dropAction := externalAction.Drop; valueSet(dropAction) { + if len(dropAction.Names) == 0 { + errs = append(errs, errNotSet("TableExternalTableColumnDropAction", "Names")) + } + } + } + if searchOptimizationAction := opts.SearchOptimizationAction; valueSet(searchOptimizationAction) { + if ok := exactlyOneValueSet( + searchOptimizationAction.Add, + searchOptimizationAction.Drop, + ); !ok { + errs = append(errs, errExactlyOneOf("TableSearchOptimizationAction", "Add", "Drop")) + } + } + return errors.Join(errs...) +} + +func (opts *dropTableOptions) validate() error { + if opts == nil { + return errors.Join(ErrNilOptions) + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, errInvalidIdentifier("dropTableOptions", "name")) + } + if moreThanOneValueSet(opts.Cascade, opts.Restrict) { + errs = append(errs, errMoreThanOneOf("dropTableOptions", "Cascade", "Restrict")) + } + return errors.Join(errs...) +} + +func (opts *showTableOptions) validate() error { + if opts == nil { + return errors.Join(ErrNilOptions) + } + var errs []error + if valueSet(opts.Like) && !valueSet(opts.Like.Pattern) { + errs = append(errs, ErrPatternRequiredForLikeKeyword) + } + return errors.Join(errs...) +} + +func (opts *describeTableColumnsOptions) validate() error { + if opts == nil { + return errors.Join(ErrNilOptions) + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, errInvalidIdentifier("describeTableColumnsOptions", "name")) + } + return errors.Join(errs...) +} + +func (opts *describeTableStageOptions) validate() error { + if opts == nil { + return errors.Join(ErrNilOptions) + } + var errs []error + if !ValidObjectIdentifier(opts.name) { + errs = append(errs, errInvalidIdentifier("describeTableStageOptions", "name")) + } + return errors.Join(errs...) +} + +func (v *OutOfLineConstraint) validate() error { + var errs []error + switch v.Type { + case ColumnConstraintTypeForeignKey: + if !valueSet(v.ForeignKey) { + errs = append(errs, errNotSet("OutOfLineConstraint", "ForeignKey")) + } else { + if err := v.ForeignKey.validate(); err != nil { + errs = append(errs, err) + } + } + case ColumnConstraintTypeUnique, ColumnConstraintTypePrimaryKey: + if valueSet(v.ForeignKey) { + errs = append(errs, errSet("OutOfLineConstraint", "ForeignKey")) + } + default: + errs = append(errs, errInvalidValue("OutOfLineConstraint", "Type", string(v.Type))) + } + if len(v.Columns) == 0 { + errs = append(errs, errNotSet("OutOfLineConstraint", "Columns")) + } + if moreThanOneValueSet(v.Enforced, v.NotEnforced) { + errs = append(errs, errMoreThanOneOf("OutOfLineConstraint", "Enforced", "NotEnforced")) + } + if moreThanOneValueSet(v.Deferrable, v.NotDeferrable) { + errs = append(errs, errMoreThanOneOf("OutOfLineConstraint", "Deferrable", "NotDeferrable")) + } + if moreThanOneValueSet(v.InitiallyDeferred, v.InitiallyImmediate) { + errs = append(errs, errMoreThanOneOf("OutOfLineConstraint", "InitiallyDeferred", "InitiallyImmediate")) + } + if moreThanOneValueSet(v.Enable, v.Disable) { + errs = append(errs, errMoreThanOneOf("OutOfLineConstraint", "Enable", "Disable")) + } + if moreThanOneValueSet(v.Validate, v.NoValidate) { + errs = append(errs, errMoreThanOneOf("OutOfLineConstraint", "Validate", "Novalidate")) + } + if moreThanOneValueSet(v.Rely, v.NoRely) { + errs = append(errs, errMoreThanOneOf("OutOfLineConstraint", "Rely", "Norely")) + } + return errors.Join(errs...) +} + +func (v *OutOfLineForeignKey) validate() error { + var errs []error + if !valueSet(v.TableName) { + errs = append(errs, errNotSet("OutOfLineForeignKey", "TableName")) + } + return errors.Join(errs...) +} diff --git a/pkg/sdk/testint/file_format_integration_test.go b/pkg/sdk/testint/file_format_integration_test.go index 9ecdb347e9..fe96d78818 100644 --- a/pkg/sdk/testint/file_format_integration_test.go +++ b/pkg/sdk/testint/file_format_integration_test.go @@ -113,7 +113,7 @@ func TestInt_FileFormatsCreateAndRead(t *testing.T) { JSONTimestampFormat: sdk.String("c"), JSONBinaryFormat: &sdk.BinaryFormatHex, JSONTrimSpace: sdk.Bool(true), - JSONNullIf: &[]sdk.NullString{{S: "d"}, {S: "e"}}, + JSONNullIf: []sdk.NullString{{S: "d"}, {S: "e"}}, JSONFileExtension: sdk.String("f"), JSONEnableOctal: sdk.Bool(true), JSONAllowDuplicate: sdk.Bool(true), @@ -146,7 +146,7 @@ func TestInt_FileFormatsCreateAndRead(t *testing.T) { assert.Equal(t, "c", *result.Options.JSONTimestampFormat) assert.Equal(t, sdk.BinaryFormatHex, *result.Options.JSONBinaryFormat) assert.Equal(t, true, *result.Options.JSONTrimSpace) - assert.Equal(t, []sdk.NullString{{S: "d"}, {S: "e"}}, *result.Options.JSONNullIf) + assert.Equal(t, []sdk.NullString{{S: "d"}, {S: "e"}}, result.Options.JSONNullIf) assert.Equal(t, "f", *result.Options.JSONFileExtension) assert.Equal(t, true, *result.Options.JSONEnableOctal) assert.Equal(t, true, *result.Options.JSONAllowDuplicate) @@ -164,7 +164,7 @@ func TestInt_FileFormatsCreateAndRead(t *testing.T) { assert.Equal(t, "c", *describeResult.Options.JSONTimestampFormat) assert.Equal(t, sdk.BinaryFormatHex, *describeResult.Options.JSONBinaryFormat) assert.Equal(t, true, *describeResult.Options.JSONTrimSpace) - assert.Equal(t, []sdk.NullString{{S: "d"}, {S: "e"}}, *describeResult.Options.JSONNullIf) + assert.Equal(t, []sdk.NullString{{S: "d"}, {S: "e"}}, describeResult.Options.JSONNullIf) assert.Equal(t, "f", *describeResult.Options.JSONFileExtension) assert.Equal(t, true, *describeResult.Options.JSONEnableOctal) assert.Equal(t, true, *describeResult.Options.JSONAllowDuplicate) diff --git a/pkg/sdk/testint/helpers_test.go b/pkg/sdk/testint/helpers_test.go index 77c6b102ca..861108ef30 100644 --- a/pkg/sdk/testint/helpers_test.go +++ b/pkg/sdk/testint/helpers_test.go @@ -169,19 +169,30 @@ func createUserWithOptions(t *testing.T, client *sdk.Client, id sdk.AccountObjec } func createTable(t *testing.T, client *sdk.Client, database *sdk.Database, schema *sdk.Schema) (*sdk.Table, func()) { + t.Helper() + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("id", sdk.DataTypeNumber), + } + return createTableWithColumns(t, client, database, schema, columns) +} + +func createTableWithColumns(t *testing.T, client *sdk.Client, database *sdk.Database, schema *sdk.Schema, columns []sdk.TableColumnRequest) (*sdk.Table, func()) { t.Helper() name := random.StringRange(8, 28) + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) ctx := context.Background() - _, err := client.ExecForTests(ctx, fmt.Sprintf("CREATE TABLE \"%s\".\"%s\".\"%s\" (id NUMBER)", database.Name, schema.Name, name)) + + dbCreateRequest := sdk.NewCreateTableRequest(id, columns) + err := client.Tables.Create(ctx, dbCreateRequest) require.NoError(t, err) - return &sdk.Table{ - DatabaseName: database.Name, - SchemaName: schema.Name, - Name: name, - }, func() { - _, err := client.ExecForTests(ctx, fmt.Sprintf("DROP TABLE \"%s\".\"%s\".\"%s\"", database.Name, schema.Name, name)) - require.NoError(t, err) - } + + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + return table, func() { + dropErr := client.Tables.Drop(ctx, sdk.NewDropTableRequest(id)) + require.NoError(t, dropErr) + } } func createDynamicTable(t *testing.T, client *sdk.Client) (*sdk.DynamicTable, func()) { @@ -267,6 +278,21 @@ func createStageWithDirectory(t *testing.T, client *sdk.Client, database *sdk.Da } } +func createStageWithName(t *testing.T, client *sdk.Client, name string) (*string, func()) { + t.Helper() + ctx := context.Background() + stageCleanup := func() { + _, err := client.ExecForTests(ctx, fmt.Sprintf("DROP STAGE %s", name)) + require.NoError(t, err) + } + _, err := client.ExecForTests(ctx, fmt.Sprintf("CREATE STAGE %s", name)) + if err != nil { + return nil, stageCleanup + } + require.NoError(t, err) + return &name, stageCleanup +} + func createStage(t *testing.T, client *sdk.Client, database *sdk.Database, schema *sdk.Schema, name string) (*sdk.Stage, func()) { t.Helper() require.NotNil(t, database, "database has to be created") @@ -771,3 +797,63 @@ type policyReference struct { TagName sql.NullString `db:"TAG_NAME"` PolicyStatus string `db:"POLICY_STATUS"` } + +// TODO: extract getting table columns as resource (like getting tag in system functions) +// getTableColumnsFor is based on https://docs.snowflake.com/en/sql-reference/info-schema/columns. +func getTableColumnsFor(t *testing.T, client *sdk.Client, tableId sdk.SchemaObjectIdentifier) []informationSchemaColumns { + t.Helper() + ctx := context.Background() + + var columns []informationSchemaColumns + query := fmt.Sprintf("SELECT * FROM information_schema.columns WHERE table_schema = '%s' AND table_name = '%s' ORDER BY ordinal_position", tableId.SchemaName(), tableId.Name()) + err := client.QueryForTests(ctx, &columns, query) + require.NoError(t, err) + + return columns +} + +type informationSchemaColumns struct { + TableCatalog string `db:"TABLE_CATALOG"` + TableSchema string `db:"TABLE_SCHEMA"` + TableName string `db:"TABLE_NAME"` + ColumnName string `db:"COLUMN_NAME"` + OrdinalPosition string `db:"ORDINAL_POSITION"` + ColumnDefault sql.NullString `db:"COLUMN_DEFAULT"` + IsNullable string `db:"IS_NULLABLE"` + DataType string `db:"DATA_TYPE"` + CharacterMaximumLength sql.NullString `db:"CHARACTER_MAXIMUM_LENGTH"` + CharacterOctetLength sql.NullString `db:"CHARACTER_OCTET_LENGTH"` + NumericPrecision sql.NullString `db:"NUMERIC_PRECISION"` + NumericPrecisionRadix sql.NullString `db:"NUMERIC_PRECISION_RADIX"` + NumericScale sql.NullString `db:"NUMERIC_SCALE"` + DatetimePrecision sql.NullString `db:"DATETIME_PRECISION"` + IntervalType sql.NullString `db:"INTERVAL_TYPE"` + IntervalPrecision sql.NullString `db:"INTERVAL_PRECISION"` + CharacterSetCatalog sql.NullString `db:"CHARACTER_SET_CATALOG"` + CharacterSetSchema sql.NullString `db:"CHARACTER_SET_SCHEMA"` + CharacterSetName sql.NullString `db:"CHARACTER_SET_NAME"` + CollationCatalog sql.NullString `db:"COLLATION_CATALOG"` + CollationSchema sql.NullString `db:"COLLATION_SCHEMA"` + CollationName sql.NullString `db:"COLLATION_NAME"` + DomainCatalog sql.NullString `db:"DOMAIN_CATALOG"` + DomainSchema sql.NullString `db:"DOMAIN_SCHEMA"` + DomainName sql.NullString `db:"DOMAIN_NAME"` + UdtCatalog sql.NullString `db:"UDT_CATALOG"` + UdtSchema sql.NullString `db:"UDT_SCHEMA"` + UdtName sql.NullString `db:"UDT_NAME"` + ScopeCatalog sql.NullString `db:"SCOPE_CATALOG"` + ScopeSchema sql.NullString `db:"SCOPE_SCHEMA"` + ScopeName sql.NullString `db:"SCOPE_NAME"` + MaximumCardinality sql.NullString `db:"MAXIMUM_CARDINALITY"` + DtdIdentifier sql.NullString `db:"DTD_IDENTIFIER"` + IsSelfReferencing string `db:"IS_SELF_REFERENCING"` + IsIdentity string `db:"IS_IDENTITY"` + IdentityGeneration sql.NullString `db:"IDENTITY_GENERATION"` + IdentityStart sql.NullString `db:"IDENTITY_START"` + IdentityIncrement sql.NullString `db:"IDENTITY_INCREMENT"` + IdentityMaximum sql.NullString `db:"IDENTITY_MAXIMUM"` + IdentityMinimum sql.NullString `db:"IDENTITY_MINIMUM"` + IdentityCycle sql.NullString `db:"IDENTITY_CYCLE"` + IdentityOrdered sql.NullString `db:"IDENTITY_ORDERED"` + Comment sql.NullString `db:"COMMENT"` +} diff --git a/pkg/sdk/testint/schemas_integration_test.go b/pkg/sdk/testint/schemas_integration_test.go index f17b55aefa..82a4845e8b 100644 --- a/pkg/sdk/testint/schemas_integration_test.go +++ b/pkg/sdk/testint/schemas_integration_test.go @@ -146,7 +146,8 @@ func TestInt_SchemasAlter(t *testing.T) { table, _ := createTable(t, client, testDb(t), schema) t.Cleanup(func() { - _, err := client.ExecForTests(ctx, fmt.Sprintf("DROP TABLE \"%s\".\"%s\".\"%s\"", testDb(t).Name, swapSchema.Name, table.Name)) + newId := sdk.NewSchemaObjectIdentifier(testDb(t).Name, swapSchema.Name, table.Name) + err := client.Tables.Drop(ctx, sdk.NewDropTableRequest(newId)) require.NoError(t, err) }) diff --git a/pkg/sdk/testint/tables_integration_test.go b/pkg/sdk/testint/tables_integration_test.go new file mode 100644 index 0000000000..a8275d32b9 --- /dev/null +++ b/pkg/sdk/testint/tables_integration_test.go @@ -0,0 +1,952 @@ +package testint + +import ( + "bufio" + "fmt" + "os" + "strings" + "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" +) + +type expectedColumn struct { + Name string + Type sdk.DataType +} + +func TestInt_Table(t *testing.T) { + client := testClient(t) + ctx := testContext(t) + + database := testDb(t) + schema := testSchema(t) + + cleanupTableProvider := func(id sdk.SchemaObjectIdentifier) func() { + return func() { + err := client.Tables.Drop(ctx, sdk.NewDropTableRequest(id)) + require.NoError(t, err) + } + } + tag1, _ := createTag(t, client, database, schema) + tag2, _ := createTag(t, client, database, schema) + + assertColumns := func(t *testing.T, expectedColumns []expectedColumn, createdColumns []informationSchemaColumns) { + t.Helper() + + require.Len(t, createdColumns, len(expectedColumns)) + for i, expectedColumn := range expectedColumns { + assert.Equal(t, strings.ToUpper(expectedColumn.Name), createdColumns[i].ColumnName) + createdColumnDataType, err := sdk.ToDataType(createdColumns[i].DataType) + assert.NoError(t, err) + assert.Equal(t, expectedColumn.Type, createdColumnDataType) + } + } + + assertTable := func(t *testing.T, table *sdk.Table, id sdk.SchemaObjectIdentifier) { + t.Helper() + assert.Equal(t, id, table.ID()) + assert.NotEmpty(t, table.CreatedOn) + assert.Equal(t, id.Name(), table.Name) + assert.Equal(t, testDb(t).Name, table.DatabaseName) + assert.Equal(t, testSchema(t).Name, table.SchemaName) + assert.Equal(t, "TABLE", table.Kind) + assert.Equal(t, 0, table.Rows) + assert.Equal(t, "ACCOUNTADMIN", table.Owner) + } + + assertTableTerse := func(t *testing.T, table *sdk.Table, id sdk.SchemaObjectIdentifier) { + t.Helper() + assert.Equal(t, id, table.ID()) + assert.NotEmpty(t, table.CreatedOn) + assert.Equal(t, id.Name(), table.Name) + assert.Equal(t, testDb(t).Name, table.DatabaseName) + assert.Equal(t, testSchema(t).Name, table.SchemaName) + assert.Equal(t, "TABLE", table.Kind) + assert.Empty(t, table.Rows) + assert.Empty(t, table.Owner) + } + + t.Run("create table: no optionals", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("FIRST_COLUMN", sdk.DataTypeNumber).WithDefaultValue(sdk.NewColumnDefaultValueRequest().WithIdentity(sdk.NewColumnIdentityRequest(1, 1))), + *sdk.NewTableColumnRequest("SECOND_COLUMN", sdk.DataTypeNumber).WithDefaultValue(sdk.NewColumnDefaultValueRequest().WithIdentity(sdk.NewColumnIdentityRequest(1, 1))), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + assertTable(t, table, id) + }) + + t.Run("create table: complete optionals", func(t *testing.T) { + maskingPolicy, _ := createMaskingPolicyWithOptions(t, client, database, schema, []sdk.TableColumnSignature{ + { + Name: "col1", + Type: sdk.DataTypeVARCHAR, + }, + { + Name: "col2", + Type: sdk.DataTypeVARCHAR, + }, + }, sdk.DataTypeVARCHAR, "REPLACE('X', 1, 2)", nil) + table2, _ := createTable(t, client, database, schema) + name := random.String() + comment := random.String() + + columnTags := []sdk.TagAssociation{ + { + Name: sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, tag1.Name), + Value: "v1", + }, + { + Name: sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, tag2.Name), + Value: "v2", + }, + } + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_3", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR). + WithDefaultValue(sdk.NewColumnDefaultValueRequest().WithExpression(sdk.String("'default'"))). + WithMaskingPolicy(sdk.NewColumnMaskingPolicyRequest(sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, maskingPolicy.Name)).WithUsing([]string{"COLUMN_1", "COLUMN_3"})). + WithTags(columnTags). + WithNotNull(sdk.Bool(true)), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeNumber).WithDefaultValue(sdk.NewColumnDefaultValueRequest().WithIdentity(sdk.NewColumnIdentityRequest(1, 1))), + } + outOfLineConstraint := sdk.NewOutOfLineConstraintRequest("OUT_OF_LINE_CONSTRAINT", sdk.ColumnConstraintTypeForeignKey). + WithColumns([]string{"COLUMN_1"}). + WithForeignKey(sdk.NewOutOfLineForeignKeyRequest(sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, table2.Name), []string{"id"}). + WithMatch(sdk.Pointer(sdk.FullMatchType)). + WithOn(sdk.NewForeignKeyOnAction(). + WithOnDelete(sdk.Pointer(sdk.ForeignKeySetNullAction)).WithOnUpdate(sdk.Pointer(sdk.ForeignKeyRestrictAction)))) + stageFileFormat := sdk.NewStageFileFormatRequest(). + WithType(sdk.Pointer(sdk.FileFormatTypeCSV)). + WithOptions(sdk.NewFileFormatTypeOptionsRequest().WithCSVCompression(sdk.Pointer(sdk.CSVCompressionAuto))) + stageCopyOptions := sdk.NewStageCopyOptionsRequest().WithOnError(sdk.NewStageCopyOnErrorOptionsRequest().WithSkipFile()) + request := sdk.NewCreateTableRequest(id, columns). + WithOutOfLineConstraint(*outOfLineConstraint). + WithStageFileFormat(*stageFileFormat). + WithStageCopyOptions(*stageCopyOptions). + WithComment(&comment). + WithDataRetentionTimeInDays(sdk.Int(30)). + WithMaxDataExtensionTimeInDays(sdk.Int(30)) + + err := client.Tables.Create(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + assertTable(t, table, id) + assert.Equal(t, table.Comment, comment) + assert.Equal(t, 30, table.RetentionTime) + + param, err := client.Parameters.ShowObjectParameter(ctx, sdk.ObjectParameterMaxDataExtensionTimeInDays, sdk.Object{ObjectType: sdk.ObjectTypeTable, Name: table.ID()}) + assert.NoError(t, err) + assert.Equal(t, "30", param.Value) + + tableColumns := getTableColumnsFor(t, client, table.ID()) + expectedColumns := []expectedColumn{ + {"COLUMN_3", sdk.DataTypeVARCHAR}, + {"COLUMN_1", sdk.DataTypeVARCHAR}, + {"COLUMN_2", sdk.DataTypeNumber}, + } + assertColumns(t, expectedColumns, tableColumns) + }) + + t.Run("create table as select", func(t *testing.T) { + maskingPolicy, _ := createMaskingPolicyWithOptions(t, client, database, schema, []sdk.TableColumnSignature{ + { + Name: "col1", + Type: sdk.DataTypeVARCHAR, + }, + }, sdk.DataTypeVARCHAR, "REPLACE('X', 1)", nil) + columns := []sdk.TableAsSelectColumnRequest{ + *sdk.NewTableAsSelectColumnRequest("COLUMN_3"). + WithType_(sdk.Pointer(sdk.DataTypeVARCHAR)). + WithCopyGrants(sdk.Bool(true)). + WithOrReplace(sdk.Bool(true)), + *sdk.NewTableAsSelectColumnRequest("COLUMN_1"). + WithType_(sdk.Pointer(sdk.DataTypeVARCHAR)). + WithCopyGrants(sdk.Bool(true)). + WithOrReplace(sdk.Bool(true)), + *sdk.NewTableAsSelectColumnRequest("COLUMN_2"). + WithType_(sdk.Pointer(sdk.DataTypeVARCHAR)). + WithCopyGrants(sdk.Bool(true)). + WithOrReplace(sdk.Bool(true)).WithMaskingPolicyName(sdk.Pointer(sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, maskingPolicy.Name))), + } + + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + query := "SELECT 1, 2, 3" + request := sdk.NewCreateTableAsSelectRequest(id, columns, query) + + err := client.Tables.CreateAsSelect(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + tableColumns := getTableColumnsFor(t, client, table.ID()) + expectedColumns := []expectedColumn{ + {"COLUMN_3", sdk.DataTypeVARCHAR}, + {"COLUMN_1", sdk.DataTypeVARCHAR}, + {"COLUMN_2", sdk.DataTypeVARCHAR}, + } + assertColumns(t, expectedColumns, tableColumns) + }) + + // TODO: fix this test, it should create two integer column but is creating 3 text ones instead + t.Run("create table using template", func(t *testing.T) { + fileFormat, fileFormatCleanup := createFileFormat(t, client, schema.ID()) + t.Cleanup(fileFormatCleanup) + stage, stageCleanup := createStageWithName(t, client, "new_stage") + t.Cleanup(stageCleanup) + + f, err := os.CreateTemp("/tmp", "data.csv") + require.NoError(t, err) + w := bufio.NewWriter(f) + _, err = w.WriteString(` [{"name": "column1", "type" "INTEGER"}, + {"name": "column2", "type" "INTEGER"} ]`) + require.NoError(t, err) + err = w.Flush() + require.NoError(t, err) + _, err = client.ExecForTests(ctx, fmt.Sprintf("PUT file://%s @%s", f.Name(), *stage)) + require.NoError(t, err) + err = os.Remove(f.Name()) + require.NoError(t, err) + + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + query := fmt.Sprintf(`SELECT ARRAY_AGG(OBJECT_CONSTRUCT(*)) WITHIN GROUP (ORDER BY order_id) FROM TABLE (INFER_SCHEMA(location => '@%s', FILE_FORMAT=>'%s', ignore_case => true))`, *stage, fileFormat.ID().FullyQualifiedName()) + request := sdk.NewCreateTableUsingTemplateRequest(id, query) + + err = client.Tables.CreateUsingTemplate(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + returnedTableColumns := getTableColumnsFor(t, client, table.ID()) + expectedColumns := []expectedColumn{ + {"C1", sdk.DataTypeVARCHAR}, + {"C2", sdk.DataTypeVARCHAR}, + {"C3", sdk.DataTypeVARCHAR}, + } + assertColumns(t, expectedColumns, returnedTableColumns) + }) + + t.Run("create table like", func(t *testing.T) { + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("id", "NUMBER"), + *sdk.NewTableColumnRequest("col2", "VARCHAR"), + *sdk.NewTableColumnRequest("col3", "BOOLEAN"), + } + sourceTable, sourceTableCleanup := createTableWithColumns(t, client, database, schema, columns) + t.Cleanup(sourceTableCleanup) + + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + request := sdk.NewCreateTableLikeRequest(id, sourceTable.ID()).WithCopyGrants(sdk.Bool(true)) + + err := client.Tables.CreateLike(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + sourceTableColumns := getTableColumnsFor(t, client, sourceTable.ID()) + expectedColumns := []expectedColumn{ + {"id", sdk.DataTypeNumber}, + {"col2", sdk.DataTypeVARCHAR}, + {"col3", sdk.DataTypeBoolean}, + } + assertColumns(t, expectedColumns, sourceTableColumns) + + likeTable, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + likeTableColumns := getTableColumnsFor(t, client, likeTable.ID()) + assertColumns(t, expectedColumns, likeTableColumns) + }) + + t.Run("create table clone", func(t *testing.T) { + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("id", "NUMBER"), + *sdk.NewTableColumnRequest("col2", "VARCHAR"), + *sdk.NewTableColumnRequest("col3", "BOOLEAN"), + } + sourceTable, sourceTableCleanup := createTableWithColumns(t, client, database, schema, columns) + t.Cleanup(sourceTableCleanup) + + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + request := sdk.NewCreateTableCloneRequest(id, sourceTable.ID()). + WithCopyGrants(sdk.Bool(true)).WithClonePoint(sdk.NewClonePointRequest(). + WithAt(*sdk.NewTimeTravelRequest().WithOffset(sdk.Pointer(0))). + WithMoment(sdk.CloneMomentAt)) + + err := client.Tables.CreateClone(ctx, request) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + sourceTableColumns := getTableColumnsFor(t, client, sourceTable.ID()) + expectedColumns := []expectedColumn{ + {"id", sdk.DataTypeNumber}, + {"col2", sdk.DataTypeVARCHAR}, + {"col3", sdk.DataTypeBoolean}, + } + assertColumns(t, expectedColumns, sourceTableColumns) + + cloneTable, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + cloneTableColumns := getTableColumnsFor(t, client, cloneTable.ID()) + assertColumns(t, expectedColumns, cloneTableColumns) + }) + + t.Run("alter table: rename", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + newName := random.String() + newId := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, newName) + + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_3", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + + alterRequest := sdk.NewAlterTableRequest(id).WithNewName(&newId) + err = client.Tables.Alter(ctx, alterRequest) + if err != nil { + t.Cleanup(cleanupTableProvider(id)) + } else { + t.Cleanup(cleanupTableProvider(newId)) + } + require.NoError(t, err) + + _, err = client.Tables.ShowByID(ctx, id) + assert.ErrorIs(t, err, collections.ErrObjectNotFound) + + table, err := client.Tables.ShowByID(ctx, newId) + require.NoError(t, err) + assertTable(t, table, newId) + }) + + t.Run("alter table: swap with", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + } + + secondTableName := random.String() + secondTableId := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, secondTableName) + secondTableColumns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + err = client.Tables.Create(ctx, sdk.NewCreateTableRequest(secondTableId, secondTableColumns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(secondTableId)) + + alterRequest := sdk.NewAlterTableRequest(id).WithSwapWith(&secondTableId) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + + table, err := client.Tables.ShowByID(ctx, secondTableId) + require.NoError(t, err) + + assertTable(t, table, secondTableId) + + secondTable, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + assertTable(t, secondTable, id) + }) + + t.Run("alter table: cluster by", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + clusterByColumns := []string{"COLUMN_1", "COLUMN_2"} + alterRequest := sdk.NewAlterTableRequest(id).WithClusteringAction(sdk.NewTableClusteringActionRequest().WithClusterBy(clusterByColumns)) + + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + assertTable(t, table, id) + assert.Equal(t, "", table.Comment) + clusterByString := "LINEAR(" + strings.Join(clusterByColumns, ", ") + ")" + assert.Equal(t, clusterByString, table.ClusterBy) + }) + + t.Run("alter table: resume recluster", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + clusterBy := []string{"COLUMN_1", "COLUMN_2"} + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns).WithClusterBy(clusterBy)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithClusteringAction(sdk.NewTableClusteringActionRequest(). + WithChangeReclusterState(sdk.Pointer(sdk.ReclusterStateSuspend))) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + clusterByString := "LINEAR(" + strings.Join(clusterBy, ", ") + ")" + assert.Equal(t, clusterByString, table.ClusterBy) + }) + + t.Run("alter table: drop clustering key", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + clusterBy := []string{"COLUMN_1", "COLUMN_2"} + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns).WithClusterBy(clusterBy)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithClusteringAction(sdk.NewTableClusteringActionRequest(). + WithDropClusteringKey(sdk.Bool(true))) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, "", table.ClusterBy) + }) + + t.Run("alter table: add a column", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + clusterBy := []string{"COLUMN_1", "COLUMN_2"} + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns).WithClusterBy(clusterBy)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithColumnAction(sdk.NewTableColumnActionRequest(). + WithAdd(sdk.NewTableColumnAddActionRequest("COLUMN_3", sdk.DataTypeVARCHAR))) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + currentColumns := getTableColumnsFor(t, client, table.ID()) + expectedColumns := []expectedColumn{ + {"COLUMN_1", sdk.DataTypeVARCHAR}, + {"COLUMN_2", sdk.DataTypeVARCHAR}, + {"COLUMN_3", sdk.DataTypeVARCHAR}, + } + assertColumns(t, expectedColumns, currentColumns) + + assert.Equal(t, "", table.Comment) + }) + + t.Run("alter table: rename column", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithColumnAction(sdk.NewTableColumnActionRequest(). + WithRename(sdk.NewTableColumnRenameActionRequest("COLUMN_1", "COLUMN_3"))) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + currentColumns := getTableColumnsFor(t, client, table.ID()) + expectedColumns := []expectedColumn{ + {"COLUMN_3", sdk.DataTypeVARCHAR}, + {"COLUMN_2", sdk.DataTypeVARCHAR}, + } + assertColumns(t, expectedColumns, currentColumns) + + assert.Equal(t, "", table.Comment) + }) + + t.Run("alter table: unset masking policy", func(t *testing.T) { + maskingPolicy, maskingPolicyCleanup := createMaskingPolicyWithOptions(t, client, database, schema, []sdk.TableColumnSignature{ + { + Name: "col1", + Type: sdk.DataTypeVARCHAR, + }, + }, sdk.DataTypeVARCHAR, "REPLACE('X', 1)", nil) + t.Cleanup(maskingPolicyCleanup) + + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR).WithMaskingPolicy(sdk.NewColumnMaskingPolicyRequest(maskingPolicy.ID())), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + tableDetails, err := client.Tables.DescribeColumns(ctx, sdk.NewDescribeTableColumnsRequest(id)) + require.NoError(t, err) + + require.Equal(t, 2, len(tableDetails)) + assert.Equal(t, maskingPolicy.ID().FullyQualifiedName(), *tableDetails[0].PolicyName) + + alterRequest := sdk.NewAlterTableRequest(id). + WithColumnAction(sdk.NewTableColumnActionRequest().WithUnsetMaskingPolicy(sdk.NewTableColumnAlterUnsetMaskingPolicyActionRequest("COLUMN_1"))) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + + tableDetails, err = client.Tables.DescribeColumns(ctx, sdk.NewDescribeTableColumnsRequest(id)) + require.NoError(t, err) + + require.Equal(t, 2, len(tableDetails)) + assert.Empty(t, tableDetails[0].PolicyName) + }) + + t.Run("alter table: set and unset tags", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + columnTags := []sdk.TagAssociationRequest{ + { + Name: sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, tag1.Name), + Value: "v1", + }, + { + Name: sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, tag2.Name), + Value: "v2", + }, + } + + alterRequest := sdk.NewAlterTableRequest(id).WithSetTags(columnTags) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + + returnedTagValue, err := client.SystemFunctions.GetTag(ctx, tag1.ID(), id, sdk.ObjectTypeTable) + require.NoError(t, err) + + assert.Equal(t, "v1", returnedTagValue) + + returnedTagValue, err = client.SystemFunctions.GetTag(ctx, tag2.ID(), id, sdk.ObjectTypeTable) + require.NoError(t, err) + + assert.Equal(t, "v2", returnedTagValue) + + unsetTags := []sdk.ObjectIdentifier{ + tag1.ID(), + tag2.ID(), + } + alterRequestUnsetTags := sdk.NewAlterTableRequest(id).WithUnsetTags(unsetTags) + + err = client.Tables.Alter(ctx, alterRequestUnsetTags) + require.NoError(t, err) + + _, err = client.SystemFunctions.GetTag(ctx, tag1.ID(), id, sdk.ObjectTypeTable) + require.Error(t, err) + + _, err = client.SystemFunctions.GetTag(ctx, tag2.ID(), id, sdk.ObjectTypeTable) + require.Error(t, err) + }) + + t.Run("alter table: drop columns", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithColumnAction(sdk.NewTableColumnActionRequest().WithDropColumns([]string{"COLUMN_1"})) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + currentColumns := getTableColumnsFor(t, client, table.ID()) + expectedColumns := []expectedColumn{ + {"COLUMN_2", sdk.DataTypeVARCHAR}, + } + assertColumns(t, expectedColumns, currentColumns) + + assert.Equal(t, table.Comment, "") + }) + + // TODO: check added constraints + // Add method similar to getTableColumnsFor based on https://docs.snowflake.com/en/sql-reference/info-schema/table_constraints. + t.Run("alter constraint: add", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + } + + secondTableName := random.String() + secondTableId := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, secondTableName) + secondTableColumns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_3", sdk.DataTypeVARCHAR).WithInlineConstraint(sdk.NewColumnInlineConstraintRequest("pkey", sdk.ColumnConstraintTypePrimaryKey)), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + err = client.Tables.Create(ctx, sdk.NewCreateTableRequest(secondTableId, secondTableColumns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(secondTableId)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithConstraintAction(sdk.NewTableConstraintActionRequest(). + WithAdd(sdk.NewOutOfLineConstraintRequest("OUT_OF_LINE_CONSTRAINT", sdk.ColumnConstraintTypeForeignKey).WithColumns([]string{"COLUMN_1"}). + WithForeignKey(sdk.NewOutOfLineForeignKeyRequest(sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, secondTableName), []string{"COLUMN_3"})))) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + }) + + // TODO: check renamed constraint + t.Run("alter constraint: rename", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + oldConstraintName := "OUT_OF_LINE_CONSTRAINT" + outOfLineConstraint := sdk.NewOutOfLineConstraintRequest(oldConstraintName, sdk.ColumnConstraintTypePrimaryKey).WithColumns([]string{"COLUMN_1"}) + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns).WithOutOfLineConstraint(*outOfLineConstraint)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + newConstraintName := "NEW_OUT_OF_LINE_CONSTRAINT_NAME" + alterRequest := sdk.NewAlterTableRequest(id). + WithConstraintAction(sdk.NewTableConstraintActionRequest(). + WithRename(sdk.NewTableConstraintRenameActionRequest(). + WithOldName(oldConstraintName). + WithNewName(newConstraintName))) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + }) + + // TODO: check altered constraint + t.Run("alter constraint: alter", func(t *testing.T) { + t.Skip("Test is failing: generated statement is not compiling but it is aligned with Snowflake docs https://docs.snowflake.com/en/sql-reference/sql/alter-table#syntax. Requires further investigation.") + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + constraintName := "OUT_OF_LINE_CONSTRAINT" + outOfLineConstraint := sdk.NewOutOfLineConstraintRequest(constraintName, sdk.ColumnConstraintTypePrimaryKey).WithColumns([]string{"COLUMN_1"}) + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns).WithOutOfLineConstraint(*outOfLineConstraint)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithConstraintAction(sdk.NewTableConstraintActionRequest().WithAlter(sdk.NewTableConstraintAlterActionRequest([]string{"COLUMN_1"}).WithConstraintName(sdk.String(constraintName)).WithEnforced(sdk.Bool(true)))) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + }) + + // TODO: check dropped constraint + t.Run("alter constraint: drop", func(t *testing.T) { + t.Skip("Test is failing: generated statement is not compiling but it is aligned with Snowflake docs https://docs.snowflake.com/en/sql-reference/sql/alter-table#syntax. Requires further investigation.") + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + constraintName := "OUT_OF_LINE_CONSTRAINT" + outOfLineConstraint := sdk.NewOutOfLineConstraintRequest(constraintName, sdk.ColumnConstraintTypePrimaryKey).WithColumns([]string{"COLUMN_1"}) + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns).WithOutOfLineConstraint(*outOfLineConstraint)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithConstraintAction(sdk.NewTableConstraintActionRequest().WithDrop(sdk.NewTableConstraintDropActionRequest([]string{"COLUMN_1"}).WithConstraintName(sdk.String(constraintName)))) + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + }) + + t.Run("external table: add", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithExternalTableAction(sdk.NewTableExternalTableActionRequest().WithAdd(sdk.NewTableExternalTableColumnAddActionRequest().WithName("COLUMN_3").WithType(sdk.DataTypeNumber).WithExpression("1 + 1"))) + + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + currentColumns := getTableColumnsFor(t, client, table.ID()) + expectedColumns := []expectedColumn{ + {"COLUMN_1", sdk.DataTypeVARCHAR}, + {"COLUMN_2", sdk.DataTypeVARCHAR}, + {"COLUMN_3", sdk.DataTypeNumber}, + } + assertColumns(t, expectedColumns, currentColumns) + }) + + t.Run("external table: rename", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithExternalTableAction(sdk.NewTableExternalTableActionRequest().WithRename(sdk.NewTableExternalTableColumnRenameActionRequest().WithOldName("COLUMN_1").WithNewName("COLUMN_3"))) + + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, table.Comment, "") + currentColumns := getTableColumnsFor(t, client, table.ID()) + expectedColumns := []expectedColumn{ + {"COLUMN_3", sdk.DataTypeVARCHAR}, + {"COLUMN_2", sdk.DataTypeVARCHAR}, + } + assertColumns(t, expectedColumns, currentColumns) + }) + + t.Run("external table: drop", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithExternalTableAction(sdk.NewTableExternalTableActionRequest().WithDrop(sdk.NewTableExternalTableColumnDropActionRequest([]string{"COLUMN_2"}))) + + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + currentColumns := getTableColumnsFor(t, client, table.ID()) + expectedColumns := []expectedColumn{ + {"COLUMN_1", sdk.DataTypeVARCHAR}, + } + assertColumns(t, expectedColumns, currentColumns) + }) + + // TODO: check search optimization - after adding https://docs.snowflake.com/en/sql-reference/sql/desc-search-optimization + t.Run("add search optimization", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + alterRequest := sdk.NewAlterTableRequest(id). + WithSearchOptimizationAction(sdk.NewTableSearchOptimizationActionRequest().WithAddSearchOptimizationOn([]string{"SUBSTRING(*)", "GEO(*)"})) + + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + }) + + // TODO: try to check more sets (ddl collation, max data extension time in days, etc.) + t.Run("set: with complete options", func(t *testing.T) { + name := random.String() + id := sdk.NewSchemaObjectIdentifier(database.Name, schema.Name, name) + comment := random.String() + columns := []sdk.TableColumnRequest{ + *sdk.NewTableColumnRequest("COLUMN_1", sdk.DataTypeVARCHAR), + *sdk.NewTableColumnRequest("COLUMN_2", sdk.DataTypeVARCHAR), + } + + err := client.Tables.Create(ctx, sdk.NewCreateTableRequest(id, columns)) + require.NoError(t, err) + t.Cleanup(cleanupTableProvider(id)) + + stageFileFormats := sdk.StageFileFormatRequest{ + Type: sdk.Pointer(sdk.FileFormatTypeCSV), + } + stageCopyOptions := sdk.StageCopyOptionsRequest{ + OnError: sdk.NewStageCopyOnErrorOptionsRequest().WithSkipFile(), + } + alterRequest := sdk.NewAlterTableRequest(id). + WithSet(sdk.NewTableSetRequest(). + WithEnableSchemaEvolution(sdk.Bool(true)). + WithStageFileFormat(stageFileFormats). + WithStageCopyOptions(stageCopyOptions). + WithDataRetentionTimeInDays(sdk.Int(30)). + WithMaxDataExtensionTimeInDays(sdk.Int(90)). + WithChangeTracking(sdk.Bool(false)). + WithDefaultDDLCollation(sdk.String("us")). + WithComment(&comment)) + + err = client.Tables.Alter(ctx, alterRequest) + require.NoError(t, err) + table, err := client.Tables.ShowByID(ctx, id) + require.NoError(t, err) + + assert.Equal(t, table.Comment, comment) + assert.Equal(t, table.RetentionTime, 30) + assert.Equal(t, table.ChangeTracking, false) + assert.Equal(t, table.EnableSchemaEvolution, true) + }) + + t.Run("drop table", func(t *testing.T) { + table, tableCleanup := createTable(t, client, database, schema) + err := client.Tables.Drop(ctx, sdk.NewDropTableRequest(table.ID()).WithIfExists(sdk.Bool(true))) + if err != nil { + t.Cleanup(tableCleanup) + } + require.NoError(t, err) + + _, err = client.Tables.ShowByID(ctx, table.ID()) + require.ErrorIs(t, err, collections.ErrObjectNotFound) + }) + + t.Run("show tables", func(t *testing.T) { + table, tableCleanup := createTable(t, client, database, schema) + t.Cleanup(tableCleanup) + table2, table2Cleanup := createTable(t, client, database, schema) + t.Cleanup(table2Cleanup) + + tables, err := client.Tables.Show(ctx, sdk.NewShowTableRequest()) + require.NoError(t, err) + + t1, err := collections.FindOne(tables, func(t sdk.Table) bool { return t.ID().FullyQualifiedName() == table.ID().FullyQualifiedName() }) + require.NoError(t, err) + t2, err := collections.FindOne(tables, func(t sdk.Table) bool { return t.ID().FullyQualifiedName() == table2.ID().FullyQualifiedName() }) + require.NoError(t, err) + + assertTable(t, t1, table.ID()) + assertTable(t, t2, table2.ID()) + }) + + t.Run("with terse", func(t *testing.T) { + table, tableCleanup := createTable(t, client, database, schema) + t.Cleanup(tableCleanup) + + tables, err := client.Tables.Show(ctx, sdk.NewShowTableRequest().WithTerse(sdk.Bool(true)).WithLikePattern(table.ID().Name())) + require.NoError(t, err) + assert.Equal(t, 1, len(tables)) + + assertTableTerse(t, &tables[0], table.ID()) + }) + + t.Run("with starts with", func(t *testing.T) { + table, tableCleanup := createTable(t, client, database, schema) + t.Cleanup(tableCleanup) + + tables, err := client.Tables.Show(ctx, sdk.NewShowTableRequest().WithStartsWith(sdk.String(table.Name))) + require.NoError(t, err) + assert.Equal(t, 1, len(tables)) + + assertTable(t, &tables[0], table.ID()) + }) + + t.Run("when searching a non-existent table", func(t *testing.T) { + tables, err := client.Tables.Show(ctx, sdk.NewShowTableRequest().WithLikePattern("non-existent")) + require.NoError(t, err) + assert.Equal(t, 0, len(tables)) + }) +}