diff --git a/docs/data-sources/current_role.md b/docs/data-sources/current_role.md index d33642b700..7477345c01 100644 --- a/docs/data-sources/current_role.md +++ b/docs/data-sources/current_role.md @@ -9,17 +9,7 @@ description: |- # snowflake_current_role (Data Source) -## Example Usage -```terraform -data "snowflake_current_role" "this" {} - -resource "aws_ssm_parameter" "snowflake_account_role" { - name = "/snowflake/account_role" - type = "String" - value = data.snowflake_current_account.this.name -} -``` @@ -27,7 +17,7 @@ resource "aws_ssm_parameter" "snowflake_account_role" { ### Read-Only -- `name` (String) - `id` (String) The ID of this resource. +- `name` (String) The name of the [primary role](https://docs.snowflake.com/en/user-guide/security-access-control-overview.html#label-access-control-role-enforcement) in use for the current session. diff --git a/docs/resources/object_parameter.md b/docs/resources/object_parameter.md index 70d72a4bf3..ca314b9ab1 100644 --- a/docs/resources/object_parameter.md +++ b/docs/resources/object_parameter.md @@ -17,23 +17,49 @@ resource "snowflake_database" "d" { name = "TEST_DB" } -resource "snowflake_schema" "s" { - name = "TEST_SCHEMA" - database = snowflake_database.d.name -} - resource "snowflake_object_parameter" "o" { - key = "ENABLE_STREAM_TASK_REPLICATION" - value = "true" + key = "SUSPEND_TASK_AFTER_NUM_FAILURES" + value = "33" object_type = "DATABASE" - object_name = snowflake_database.d.name + object_identifier { + name = snowflake_database.d.name + } +} + +resource "snowflake_schema" "s" { + name = "TEST_SCHEMA" + database = snowflake_database.d.name } resource "snowflake_object_parameter" "o2" { - key = "PIPE_EXECUTION_PAUSED" - value = "false" + key = "USER_TASK_TIMEOUT_MS" + value = "500" object_type = "SCHEMA" - object_name = "${snowflake_database.d.name}.${snowflake_schema.s.name}" + object_identifier { + database = snowflake_database.d.name + name = snowflake_schema.s.name + } +} + +resource "snowflake_table" "t" { + name = "TEST_TABLE" + database = snowflake_database.d.name + schema = snowflake_schema.s.name + column { + name = "id" + type = "NUMBER" + } +} + +resource "snowflake_object_parameter" "o3" { + key = "DATA_RETENTION_TIME_IN_DAYS" + value = "89" + object_type = "TABLE" + object_identifier { + database = snowflake_database.d.name + schema = snowflake_schema.s.name + name = snowflake_table.t.name + } } ``` @@ -43,7 +69,7 @@ resource "snowflake_object_parameter" "o2" { ### Required - `key` (String) Name of object parameter. Valid values are those in [object parameters](https://docs.snowflake.com/en/sql-reference/parameters.html#object-parameters). -- `object_name` (String) Name of object to which the parameter applies. +- `object_identifier` (Block List, Min: 1) Specifies the object identifier for the object parameter. (see [below for nested schema](#nestedblock--object_identifier)) - `object_type` (String) Type of object to which the parameter applies. Valid values are those in [object types](https://docs.snowflake.com/en/sql-reference/parameters.html#object-types). - `value` (String) Value of object parameter, as a string. Constraints are the same as those for the parameters in Snowflake documentation. @@ -51,10 +77,22 @@ resource "snowflake_object_parameter" "o2" { - `id` (String) The ID of this resource. + +### Nested Schema for `object_identifier` + +Required: + +- `name` (String) Name of the object to set the parameter for. + +Optional: + +- `database` (String) Name of the database that the object was created in. +- `schema` (String) Name of the schema that the object was created in. + ## Import Import is supported using the following syntax: ```shell -terraform import snowflake_object_parameter.s ❄️❄️ +terraform import snowflake_object_parameter.s ❄️❄️ ``` diff --git a/docs/resources/table.md b/docs/resources/table.md index 914dcf7b28..8ded6d446a 100644 --- a/docs/resources/table.md +++ b/docs/resources/table.md @@ -94,7 +94,7 @@ resource "snowflake_table" "table" { - `change_tracking` (Boolean) Specifies whether to enable change tracking on the table. Default false. - `cluster_by` (List of String) A list of one or more table columns/expressions to be used as clustering key(s) for the table - `comment` (String) Specifies a comment for the table. -- `data_retention_days` (Number) Specifies the retention period for the table so that Time Travel actions (SELECT, CLONE, UNDROP) can be performed on historical data in the table. Default value is 1, if you wish to inherit the parent schema setting then pass in the schema attribute to this argument. +- `data_retention_days` (Number, Deprecated) Specifies the retention period for the table so that Time Travel actions (SELECT, CLONE, UNDROP) can be performed on historical data in the table. Default value is 1, if you wish to inherit the parent schema setting then pass in the schema attribute to this argument. - `primary_key` (Block List, Max: 1, Deprecated) Definitions of primary key constraint to create on table (see [below for nested schema](#nestedblock--primary_key)) - `tag` (Block List, Deprecated) Definitions of a tag to associate with the resource. (see [below for nested schema](#nestedblock--tag)) diff --git a/docs/resources/view_grant.md b/docs/resources/view_grant.md index ae96dc5925..6d79cea975 100644 --- a/docs/resources/view_grant.md +++ b/docs/resources/view_grant.md @@ -26,6 +26,19 @@ resource "snowflake_view_grant" "grant" { on_future = false with_grant_option = false } + +/* +Snowflake view grant is an object level grant, not a schema level grant. To add schema level +grants, use the `snowflake_schema_grant` resource +*/ + +resource "snowflake_schema_grant" "grant" { + database_name = "database" + schema_name = "schema" + + privilege = "USAGE" + roles = ["role1", "role2"] +} ``` diff --git a/examples/resources/snowflake_object_parameter/import.sh b/examples/resources/snowflake_object_parameter/import.sh index 0af314c406..c24122c7d6 100644 --- a/examples/resources/snowflake_object_parameter/import.sh +++ b/examples/resources/snowflake_object_parameter/import.sh @@ -1 +1 @@ -terraform import snowflake_object_parameter.s ❄️❄️ +terraform import snowflake_object_parameter.s ❄️❄️ diff --git a/examples/resources/snowflake_object_parameter/resource.tf b/examples/resources/snowflake_object_parameter/resource.tf index 3aefbd28d8..1e81c49cb9 100644 --- a/examples/resources/snowflake_object_parameter/resource.tf +++ b/examples/resources/snowflake_object_parameter/resource.tf @@ -2,21 +2,47 @@ resource "snowflake_database" "d" { name = "TEST_DB" } -resource "snowflake_schema" "s" { - name = "TEST_SCHEMA" - database = snowflake_database.d.name -} - resource "snowflake_object_parameter" "o" { - key = "ENABLE_STREAM_TASK_REPLICATION" - value = "true" + key = "SUSPEND_TASK_AFTER_NUM_FAILURES" + value = "33" object_type = "DATABASE" - object_name = snowflake_database.d.name + object_identifier { + name = snowflake_database.d.name + } +} + +resource "snowflake_schema" "s" { + name = "TEST_SCHEMA" + database = snowflake_database.d.name } resource "snowflake_object_parameter" "o2" { - key = "PIPE_EXECUTION_PAUSED" - value = "false" + key = "USER_TASK_TIMEOUT_MS" + value = "500" object_type = "SCHEMA" - object_name = "${snowflake_database.d.name}.${snowflake_schema.s.name}" + object_identifier { + database = snowflake_database.d.name + name = snowflake_schema.s.name + } +} + +resource "snowflake_table" "t" { + name = "TEST_TABLE" + database = snowflake_database.d.name + schema = snowflake_schema.s.name + column { + name = "id" + type = "NUMBER" + } +} + +resource "snowflake_object_parameter" "o3" { + key = "DATA_RETENTION_TIME_IN_DAYS" + value = "89" + object_type = "TABLE" + object_identifier { + database = snowflake_database.d.name + schema = snowflake_schema.s.name + name = snowflake_table.t.name + } } diff --git a/examples/resources/snowflake_view_grant/resource.tf b/examples/resources/snowflake_view_grant/resource.tf index cdbdd3c9f5..38afaaa078 100644 --- a/examples/resources/snowflake_view_grant/resource.tf +++ b/examples/resources/snowflake_view_grant/resource.tf @@ -11,3 +11,16 @@ resource "snowflake_view_grant" "grant" { on_future = false with_grant_option = false } + +/* +Snowflake view grant is an object level grant, not a schema level grant. To add schema level +grants, use the `snowflake_schema_grant` resource +*/ + +resource "snowflake_schema_grant" "grant" { + database_name = "database" + schema_name = "schema" + + privilege = "USAGE" + roles = ["role1", "role2"] +} diff --git a/pkg/datasources/current_role.go b/pkg/datasources/current_role.go index 8536e77288..8104a48f12 100644 --- a/pkg/datasources/current_role.go +++ b/pkg/datasources/current_role.go @@ -11,8 +11,8 @@ import ( var currentRoleSchema = map[string]*schema.Schema{ "name": { - Type: schema.TypeString, - Computed: true, + Type: schema.TypeString, + Computed: true, Description: "The name of the [primary role](https://docs.snowflake.com/en/user-guide/security-access-control-overview.html#label-access-control-role-enforcement) in use for the current session.", }, } @@ -27,7 +27,6 @@ func CurrentRole() *schema.Resource { func ReadCurrentRole(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) role, err := snowflake.ReadCurrentRole(db) - if err != nil { log.Printf("[DEBUG] current_role failed to decode") d.SetId("") @@ -35,7 +34,9 @@ func ReadCurrentRole(d *schema.ResourceData, meta interface{}) error { } d.SetId(fmt.Sprintf(role.Role)) - d.Set("name", role.Role) - + err = d.Set("name", role.Role) + if err != nil { + return err + } return nil } diff --git a/pkg/datasources/parameters.go b/pkg/datasources/parameters.go index dd26e12e88..31740ea7ad 100644 --- a/pkg/datasources/parameters.go +++ b/pkg/datasources/parameters.go @@ -119,31 +119,30 @@ func ReadParameters(d *schema.ResourceData, meta interface{}) error { params = append(params, paramMap) } return d.Set("parameters", params) - } else { - parameters, err := snowflake.ListParameters(db, parameterType, pattern) - if errors.Is(err, sql.ErrNoRows) { - log.Printf("[DEBUG] parameters not found") - d.SetId("") - return nil - } else if err != nil { - log.Printf("[DEBUG] error occurred during read: %v", err.Error()) - return err - } - d.SetId("parameters") + } + parameters, err := snowflake.ListParameters(db, parameterType, pattern) + if errors.Is(err, sql.ErrNoRows) { + log.Printf("[DEBUG] parameters not found") + d.SetId("") + return nil + } else if err != nil { + log.Printf("[DEBUG] error occurred during read: %v", err.Error()) + return err + } + d.SetId("parameters") - params := []map[string]interface{}{} - for _, param := range parameters { - paramMap := map[string]interface{}{} + params := []map[string]interface{}{} + for _, param := range parameters { + paramMap := map[string]interface{}{} - paramMap["key"] = param.Key.String - paramMap["value"] = param.Value.String - paramMap["default"] = param.Default.String - paramMap["level"] = param.Level.String - paramMap["description"] = param.Description.String - paramMap["type"] = param.PType.String + paramMap["key"] = param.Key.String + paramMap["value"] = param.Value.String + paramMap["default"] = param.Default.String + paramMap["level"] = param.Level.String + paramMap["description"] = param.Description.String + paramMap["type"] = param.PType.String - params = append(params, paramMap) - } - return d.Set("parameters", params) + params = append(params, paramMap) } + return d.Set("parameters", params) } diff --git a/pkg/resources/account_parameter.go b/pkg/resources/account_parameter.go index c080b03b6a..59ca09819f 100644 --- a/pkg/resources/account_parameter.go +++ b/pkg/resources/account_parameter.go @@ -17,7 +17,7 @@ var accountParameterSchema = map[string]*schema.Schema{ Required: true, ForceNew: true, Description: "Name of account parameter. Valid values are those in [account parameters](https://docs.snowflake.com/en/sql-reference/parameters.html#account-parameters).", - ValidateFunc: validation.StringInSlice(maps.Keys(snowflake.GetParameters(snowflake.ParameterTypeAccount)), false), + ValidateFunc: validation.StringInSlice(maps.Keys(snowflake.GetParameterDefaults(snowflake.ParameterTypeAccount)), false), }, "value": { Type: schema.TypeString, @@ -46,7 +46,7 @@ func CreateAccountParameter(d *schema.ResourceData, meta interface{}) error { key := d.Get("key").(string) value := d.Get("value").(string) - parameterDefault := snowflake.GetParameters(snowflake.ParameterTypeAccount)[key] + parameterDefault := snowflake.GetParameterDefaults(snowflake.ParameterTypeAccount)[key] if parameterDefault.Validate != nil { if err := parameterDefault.Validate(value); err != nil { return err @@ -102,7 +102,7 @@ func DeleteAccountParameter(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) key := d.Get("key").(string) - parameterDefault := snowflake.GetParameters(snowflake.ParameterTypeAccount)[key] + parameterDefault := snowflake.GetParameterDefaults(snowflake.ParameterTypeAccount)[key] defaultValue := parameterDefault.DefaultValue value := fmt.Sprintf("%v", defaultValue) diff --git a/pkg/resources/list_expansion.go b/pkg/resources/helper_expansion.go similarity index 53% rename from pkg/resources/list_expansion.go rename to pkg/resources/helper_expansion.go index c3aef588f8..188b96fed4 100644 --- a/pkg/resources/list_expansion.go +++ b/pkg/resources/helper_expansion.go @@ -22,3 +22,17 @@ func expandStringList(configured []interface{}) []string { } return vs } + +func expandObjectIdentifier(objectIdentifier interface{}) (string, string, string) { + objectIdentifierMap := objectIdentifier.([]interface{})[0].(map[string]interface{}) + objectName := objectIdentifierMap["name"].(string) + var objectSchema string + if v := objectIdentifierMap["schema"]; v != nil { + objectSchema = v.(string) + } + var objectDatabase string + if v := objectIdentifierMap["database"]; v != nil { + objectDatabase = v.(string) + } + return objectDatabase, objectSchema, objectName +} diff --git a/pkg/resources/list_expansion_internal_test.go b/pkg/resources/helper_expansion_internal_test.go similarity index 100% rename from pkg/resources/list_expansion_internal_test.go rename to pkg/resources/helper_expansion_internal_test.go diff --git a/pkg/resources/object_parameter.go b/pkg/resources/object_parameter.go index bbc1dd0a16..bc5774a3af 100644 --- a/pkg/resources/object_parameter.go +++ b/pkg/resources/object_parameter.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + snowflakeValidation "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/validation" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "golang.org/x/exp/maps" @@ -19,7 +20,7 @@ var objectParameterSchema = map[string]*schema.Schema{ Required: true, ForceNew: true, Description: "Name of object parameter. Valid values are those in [object parameters](https://docs.snowflake.com/en/sql-reference/parameters.html#object-parameters).", - ValidateFunc: validation.StringInSlice(maps.Keys(snowflake.GetParameters(snowflake.ParameterTypeObject)), false), + ValidateFunc: validation.StringInSlice(maps.Keys(snowflake.GetParameterDefaults(snowflake.ParameterTypeObject)), false), }, "value": { Type: schema.TypeString, @@ -33,11 +34,33 @@ var objectParameterSchema = map[string]*schema.Schema{ Description: "Type of object to which the parameter applies. Valid values are those in [object types](https://docs.snowflake.com/en/sql-reference/parameters.html#object-types).", ValidateFunc: validation.StringInSlice(snowflake.GetParameterObjectTypeSetAsStrings(), false), }, - "object_name": { - Type: schema.TypeString, + "object_identifier": { + Type: schema.TypeList, Required: true, - ForceNew: true, - Description: "Name of object to which the parameter applies.", + MinItems: 1, + Description: "Specifies the object identifier for the object parameter.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Name of the object to set the parameter for.", + }, + "database": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "Name of the database that the object was created in.", + }, + "schema": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: "Name of the schema that the object was created in.", + }, + }, + }, }, } @@ -60,9 +83,11 @@ func CreateObjectParameter(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) key := d.Get("key").(string) value := d.Get("value").(string) - objectName := d.Get("object_name").(string) objectType := snowflake.ObjectType(d.Get("object_type").(string)) - parameterDefault := snowflake.GetParameters(snowflake.ParameterTypeObject)[key] + objectDatabase, objectSchema, objectName := expandObjectIdentifier(d.Get("object_identifier")) + fullyQualifierObjectIdentifier := snowflakeValidation.FormatFullyQualifiedObjectID(objectDatabase, objectSchema, objectName) + + parameterDefault := snowflake.GetParameterDefaults(snowflake.ParameterTypeObject)[key] if parameterDefault.Validate != nil { if err := parameterDefault.Validate(value); err != nil { return err @@ -79,15 +104,15 @@ func CreateObjectParameter(d *schema.ResourceData, meta interface{}) error { } builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeObject, db) - builder.WithObjectName(objectName) + builder.WithObjectIdentifier(fullyQualifierObjectIdentifier) builder.WithObjectType(objectType) err := builder.SetParameter() if err != nil { return fmt.Errorf("error creating object parameter err = %w", err) } - id := fmt.Sprintf("%v❄️%v❄️%v", key, objectType, objectName) + id := fmt.Sprintf("%v❄️%v❄️%v", key, objectType, fullyQualifierObjectIdentifier) d.SetId(id) - p, err := snowflake.ShowObjectParameter(db, key, objectType, objectName) + p, err := snowflake.ShowObjectParameter(db, key, objectType, fullyQualifierObjectIdentifier) if err != nil { return fmt.Errorf("error reading object parameter err = %w", err) } @@ -104,12 +129,12 @@ func ReadObjectParameter(d *schema.ResourceData, meta interface{}) error { id := d.Id() parts := strings.Split(id, "❄️") if len(parts) != 3 { - return fmt.Errorf("unexpected format of ID (%v), expected key❄️object_type❄️object_name", id) + return fmt.Errorf("unexpected format of ID (%v), expected key❄️object_type❄️object_identifier", id) } key := parts[0] objectType := snowflake.ObjectType(parts[1]) - objectName := parts[2] - p, err := snowflake.ShowObjectParameter(db, key, objectType, objectName) + objectIdentifier := parts[2] + p, err := snowflake.ShowObjectParameter(db, key, objectType, objectIdentifier) if err != nil { return fmt.Errorf("error reading object parameter err = %w", err) } @@ -130,9 +155,10 @@ func DeleteObjectParameter(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) key := d.Get("key").(string) objectType := snowflake.ObjectType(d.Get("object_type").(string)) - objectName := d.Get("object_name").(string) + objectDatabase, objectSchema, objectName := expandObjectIdentifier(d.Get("object_identifier")) + fullyQualifierObjectIdentifier := snowflakeValidation.FormatFullyQualifiedObjectID(objectDatabase, objectSchema, objectName) - parameterDefault := snowflake.GetParameters(snowflake.ParameterTypeObject)[key] + parameterDefault := snowflake.GetParameterDefaults(snowflake.ParameterTypeObject)[key] defaultValue := parameterDefault.DefaultValue value := fmt.Sprintf("%v", defaultValue) @@ -142,13 +168,13 @@ func DeleteObjectParameter(d *schema.ResourceData, meta interface{}) error { value = fmt.Sprintf("'%s'", value) } builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeObject, db) - builder.WithObjectName(objectName) + builder.WithObjectIdentifier(fullyQualifierObjectIdentifier) builder.WithObjectType(objectType) err := builder.SetParameter() if err != nil { return fmt.Errorf("error restoring default for object parameter err = %w", err) } - _, err = snowflake.ShowObjectParameter(db, key, objectType, objectName) + _, err = snowflake.ShowObjectParameter(db, key, objectType, fullyQualifierObjectIdentifier) if err != nil { return fmt.Errorf("error reading object parameter err = %w", err) } diff --git a/pkg/resources/object_parameter_acceptance_test.go b/pkg/resources/object_parameter_acceptance_test.go index 0f9ca92642..f277e024d3 100644 --- a/pkg/resources/object_parameter_acceptance_test.go +++ b/pkg/resources/object_parameter_acceptance_test.go @@ -35,7 +35,9 @@ resource "snowflake_object_parameter" "p" { key = "%s" value = "%s" object_type = "DATABASE" - object_name = snowflake_database.d.name + object_identifier { + name = snowflake_database.d.name + } } ` return fmt.Sprintf(s, prefix, key, value) diff --git a/pkg/resources/session_parameter.go b/pkg/resources/session_parameter.go index d7dbdc9dca..d1612efac9 100644 --- a/pkg/resources/session_parameter.go +++ b/pkg/resources/session_parameter.go @@ -17,7 +17,7 @@ var sessionParameterSchema = map[string]*schema.Schema{ Required: true, ForceNew: true, Description: "Name of session parameter. Valid values are those in [session parameters](https://docs.snowflake.com/en/sql-reference/parameters.html#session-parameters).", - ValidateFunc: validation.StringInSlice(maps.Keys(snowflake.GetParameters(snowflake.ParameterTypeSession)), false), + ValidateFunc: validation.StringInSlice(maps.Keys(snowflake.GetParameterDefaults(snowflake.ParameterTypeSession)), false), }, "value": { Type: schema.TypeString, @@ -46,7 +46,7 @@ func CreateSessionParameter(d *schema.ResourceData, meta interface{}) error { key := d.Get("key").(string) value := d.Get("value").(string) - parameterDefault := snowflake.GetParameters(snowflake.ParameterTypeSession)[key] + parameterDefault := snowflake.GetParameterDefaults(snowflake.ParameterTypeSession)[key] if parameterDefault.Validate != nil { if err := parameterDefault.Validate(value); err != nil { return err @@ -102,7 +102,7 @@ func DeleteSessionParameter(d *schema.ResourceData, meta interface{}) error { db := meta.(*sql.DB) key := d.Get("key").(string) - parameterDefault := snowflake.GetParameters(snowflake.ParameterTypeSession)[key] + parameterDefault := snowflake.GetParameterDefaults(snowflake.ParameterTypeSession)[key] defaultValue := parameterDefault.DefaultValue value := fmt.Sprintf("%v", defaultValue) diff --git a/pkg/resources/table.go b/pkg/resources/table.go index 6168fc5a9d..34d97c7a84 100644 --- a/pkg/resources/table.go +++ b/pkg/resources/table.go @@ -182,6 +182,7 @@ var tableSchema = map[string]*schema.Schema{ Default: 1, Description: "Specifies the retention period for the table so that Time Travel actions (SELECT, CLONE, UNDROP) can be performed on historical data in the table. Default value is 1, if you wish to inherit the parent schema setting then pass in the schema attribute to this argument.", ValidateFunc: validation.IntBetween(0, 90), + Deprecated: "Use snowflake_object_parameter instead", }, "change_tracking": { Type: schema.TypeBool, diff --git a/pkg/resources/tag_association.go b/pkg/resources/tag_association.go index 6f74428eaf..2dab2af2b9 100644 --- a/pkg/resources/tag_association.go +++ b/pkg/resources/tag_association.go @@ -6,7 +6,6 @@ import ( "errors" "fmt" "log" - "strings" "time" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" @@ -109,13 +108,14 @@ func CreateTagAssociation(d *schema.ResourceData, meta interface{}) error { tagID := d.Get("tag_id").(string) objectType := d.Get("object_type").(string) tagValue := d.Get("tag_value").(string) - objectIdentifier := d.Get("object_identifier").([]interface{})[0].(map[string]interface{}) - fullyQualifierObjectIdentifier := tagAssociationFullyQualifiedIdentifier(objectIdentifier, objectType) + objectDatabase, objectSchema, objectName := expandObjectIdentifier(d.Get("object_identifier")) + fullyQualifierObjectIdentifier := snowflakeValidation.FormatFullyQualifiedObjectID(objectDatabase, objectSchema, objectName) + builder := snowflake.NewTagAssociationBuilder(tagID).WithObjectIdentifier(fullyQualifierObjectIdentifier).WithObjectType(objectType).WithTagValue(tagValue) q := builder.Create() if err := snowflake.Exec(db, q); err != nil { - return fmt.Errorf("error associating tag to object: [%v] with command: [%v], tag_id [%v]", objectIdentifier, q, tagID) + return fmt.Errorf("error associating tag to object: [%v] with command: [%v], tag_id [%v]", fullyQualifierObjectIdentifier, q, tagID) } skipValidate := d.Get("skip_validation").(bool) @@ -157,8 +157,9 @@ func ReadTagAssociation(d *schema.ResourceData, meta interface{}) error { tagID := d.Get("tag_id").(string) objectType := d.Get("object_type").(string) - objectIdentifier := d.Get("object_identifier").([]interface{})[0].(map[string]interface{}) - fullyQualifierObjectIdentifier := tagAssociationFullyQualifiedIdentifier(objectIdentifier, objectType) + objectDatabase, objectSchema, objectName := expandObjectIdentifier(d.Get("object_identifier")) + fullyQualifierObjectIdentifier := snowflakeValidation.FormatFullyQualifiedObjectID(objectDatabase, objectSchema, objectName) + q := snowflake.NewTagAssociationBuilder(tagID).WithObjectIdentifier(fullyQualifierObjectIdentifier).WithObjectType(objectType).Show() row := snowflake.QueryRow(db, q) @@ -186,8 +187,9 @@ func UpdateTagAssociation(d *schema.ResourceData, meta interface{}) error { tagID := d.Get("tag_id").(string) objectType := d.Get("object_type").(string) - objectIdentifier := d.Get("object_identifier").([]interface{})[0].(map[string]interface{}) - fullyQualifierObjectIdentifier := tagAssociationFullyQualifiedIdentifier(objectIdentifier, objectType) + objectDatabase, objectSchema, objectName := expandObjectIdentifier(d.Get("object_identifier")) + fullyQualifierObjectIdentifier := snowflakeValidation.FormatFullyQualifiedObjectID(objectDatabase, objectSchema, objectName) + builder := snowflake.NewTagAssociationBuilder(tagID).WithObjectIdentifier(fullyQualifierObjectIdentifier).WithObjectType(objectType) if d.HasChange("skip_validation") { @@ -205,7 +207,7 @@ func UpdateTagAssociation(d *schema.ResourceData, meta interface{}) error { } err := snowflake.Exec(db, q) if err != nil { - return fmt.Errorf("error updating tag association value for object [%v]", objectIdentifier) + return fmt.Errorf("error updating tag association value for object [%v]", fullyQualifierObjectIdentifier) } } @@ -218,46 +220,15 @@ func DeleteTagAssociation(d *schema.ResourceData, meta interface{}) error { tagID := d.Get("tag_id").(string) objectType := d.Get("object_type").(string) - objectIdentifier := d.Get("object_identifier").([]interface{})[0].(map[string]interface{}) - fullyQualifierObjectIdentifier := tagAssociationFullyQualifiedIdentifier(objectIdentifier, objectType) + objectDatabase, objectSchema, objectName := expandObjectIdentifier(d.Get("object_identifier")) + fullyQualifierObjectIdentifier := snowflakeValidation.FormatFullyQualifiedObjectID(objectDatabase, objectSchema, objectName) q := snowflake.NewTagAssociationBuilder(tagID).WithObjectIdentifier(fullyQualifierObjectIdentifier).WithObjectType(objectType).Drop() if err := snowflake.Exec(db, q); err != nil { - log.Printf("[DEBUG] error is %v", err.Error()) - return fmt.Errorf("error deleting tag association for object [%v]", objectIdentifier) + return fmt.Errorf("error deleting tag association for object id [%s]: %w", tagID, err) } d.SetId("") return nil } - -func tagAssociationFullyQualifiedIdentifier(objectIdentifier map[string]interface{}, objectType string) string { - if strings.ToUpper(objectType) == "SCHEMA" { - objectSchema := objectIdentifier["name"].(string) - obd := objectIdentifier["database"] - objectDatabase := "" - if obd != nil { - objectDatabase = obd.(string) - } - /* - objectIdentifier["schema"] is ignored - objectIdentifier["name"] is schema name (as it's a required parameter) - //TODO add validation to detect situations where both schema and name parameters are specified for object type SCHEMA - - */ - return snowflakeValidation.FormatFullyQualifiedObjectID("", objectDatabase, objectSchema) // db_name.schema_name - } - objectName := objectIdentifier["name"].(string) - objectSchema := "" - obs := objectIdentifier["schema"] - if obs != nil { - objectSchema = obs.(string) - } - objectDatabase := "" - obd := objectIdentifier["database"] - if obd != nil { - objectDatabase = obd.(string) - } - return snowflakeValidation.FormatFullyQualifiedObjectID(objectDatabase, objectSchema, objectName) -} diff --git a/pkg/snowflake/current_role.go b/pkg/snowflake/current_role.go index 155a69fa8f..93ca4f2e94 100644 --- a/pkg/snowflake/current_role.go +++ b/pkg/snowflake/current_role.go @@ -10,16 +10,17 @@ func SelectCurrentRole() string { return `SELECT CURRENT_ROLE() AS "currentRole";` } -type currentRole struct { +type CurrentRole struct { Role string `db:"currentRole"` } -func ScanCurrentRole(row *sqlx.Row) (*currentRole, error) { - role := ¤tRole{} +func ScanCurrentRole(row *sqlx.Row) (*CurrentRole, error) { + role := &CurrentRole{} err := row.StructScan(role) return role, err } -func ReadCurrentRole(db *sql.DB) (*currentRole, error) { + +func ReadCurrentRole(db *sql.DB) (*CurrentRole, error) { row := QueryRow(db, SelectCurrentRole()) return ScanCurrentRole(row) } diff --git a/pkg/snowflake/parameters.go b/pkg/snowflake/parameters.go index 487f0a4ed8..adcd151e54 100644 --- a/pkg/snowflake/parameters.go +++ b/pkg/snowflake/parameters.go @@ -23,8 +23,8 @@ const ( ParameterTypeObject ParameterType = "OBJECT" ) -// Parameter is a parameter that can be set on an account, session, or object. -type Parameter struct { +// ParameterDefault is a parameter that can be set on an account, session, or object. +type ParameterDefault struct { TypeSet []ParameterType DefaultValue interface{} ValueType reflect.Type @@ -33,7 +33,7 @@ type Parameter struct { } // ParameterDefaults returns a map of default values for all parameters. -func ParameterDefaults() map[string]Parameter { +func ParameterDefaults() map[string]ParameterDefault { validateBoolFunc := func(value string) (err error) { _, err = strconv.ParseBool(value) if err != nil { @@ -42,7 +42,7 @@ func ParameterDefaults() map[string]Parameter { return nil } - return map[string]Parameter{ + return map[string]ParameterDefault{ "ALLOW_CLIENT_MFA_CACHING": { TypeSet: []ParameterType{ParameterTypeAccount}, DefaultValue: false, @@ -656,7 +656,7 @@ func GetParameterObjectTypeSetAsStrings() []string { } // GetParameters returns a map of parameters that match the given type (e.g. Account, Session, Object). -func GetParameters(t ParameterType) map[string]Parameter { +func GetParameterDefaults(t ParameterType) map[string]ParameterDefault { parameters := ParameterDefaults() keys := maps.Keys(parameters) for _, key := range keys { @@ -669,18 +669,18 @@ func GetParameters(t ParameterType) map[string]Parameter { } // GetParameter returns a parameter by key. -func GetParameter(key string) Parameter { +func GetParameterDefault(key string) ParameterDefault { return ParameterDefaults()[key] } // ParameterBuilder abstracts the creation of SQL queries for Snowflake parameters. type ParameterBuilder struct { - key string - value string - parameterType ParameterType - objectType ObjectType - objectName string - db *sql.DB + key string + value string + parameterType ParameterType + objectType ObjectType + objectIdentifier string + db *sql.DB } func NewParameter(key, value string, parameterType ParameterType, db *sql.DB) *ParameterBuilder { @@ -697,8 +697,8 @@ func (v *ParameterBuilder) WithObjectType(objectType ObjectType) *ParameterBuild return v } -func (v *ParameterBuilder) WithObjectName(objectName string) *ParameterBuilder { - v.objectName = objectName +func (v *ParameterBuilder) WithObjectIdentifier(objectIdentifier string) *ParameterBuilder { + v.objectIdentifier = objectIdentifier return v } @@ -711,7 +711,7 @@ func (v *ParameterBuilder) SetParameter() error { return err } } else if v.parameterType == ParameterTypeObject { - stmt := fmt.Sprintf("ALTER %s \"%s\" SET %s = %s", v.objectType, v.objectName, v.key, v.value) + stmt := fmt.Sprintf("ALTER %s %s SET %s = %s", v.objectType, v.objectIdentifier, v.key, v.value) _, err := v.db.Exec(stmt) if err != nil { return err @@ -720,7 +720,7 @@ func (v *ParameterBuilder) SetParameter() error { return nil } -type snowflakeParameter struct { +type Parameter struct { Key sql.NullString `db:"key"` Value sql.NullString `db:"value"` Default sql.NullString `db:"default"` @@ -729,8 +729,8 @@ type snowflakeParameter struct { PType sql.NullString `db:"type"` } -func ShowParameter(db *sql.DB, key string, parameterType ParameterType) (*snowflakeParameter, error) { - var value snowflakeParameter +func ShowParameter(db *sql.DB, key string, parameterType ParameterType) (*Parameter, error) { + var value Parameter var stmt string if parameterType == ParameterTypeAccount || parameterType == ParameterTypeSession { stmt = fmt.Sprintf("SHOW PARAMETERS LIKE '%s' IN ACCOUNT", key) @@ -742,7 +742,7 @@ func ShowParameter(db *sql.DB, key string, parameterType ParameterType) (*snowfl return nil, err } defer rows.Close() - params := []snowflakeParameter{} + params := []Parameter{} if err := sqlx.StructScan(rows, ¶ms); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil @@ -754,15 +754,15 @@ func ShowParameter(db *sql.DB, key string, parameterType ParameterType) (*snowfl return &value, nil } -func ShowObjectParameter(db *sql.DB, key string, objectType ObjectType, objectName string) (*snowflakeParameter, error) { - var value snowflakeParameter - stmt := fmt.Sprintf("SHOW PARAMETERS LIKE '%s' IN %s \"%s\"", key, objectType.String(), objectName) +func ShowObjectParameter(db *sql.DB, key string, objectType ObjectType, objectIdentifier string) (*Parameter, error) { + var value Parameter + stmt := fmt.Sprintf("SHOW PARAMETERS LIKE '%s' IN %s %s", key, objectType.String(), objectIdentifier) rows, err := db.Query(stmt) if err != nil { return nil, err } defer rows.Close() - params := []snowflakeParameter{} + params := []Parameter{} if err := sqlx.StructScan(rows, ¶ms); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil @@ -774,7 +774,7 @@ func ShowObjectParameter(db *sql.DB, key string, objectType ObjectType, objectNa return &value, nil } -func ListParameters(db *sql.DB, parameterType ParameterType, pattern string) ([]snowflakeParameter, error) { +func ListParameters(db *sql.DB, parameterType ParameterType, pattern string) ([]Parameter, error) { var stmt string if parameterType == ParameterTypeAccount || parameterType == ParameterTypeSession { if pattern != "" { @@ -791,7 +791,7 @@ func ListParameters(db *sql.DB, parameterType ParameterType, pattern string) ([] return nil, err } defer rows.Close() - params := []snowflakeParameter{} + params := []Parameter{} if err := sqlx.StructScan(rows, ¶ms); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil @@ -801,12 +801,12 @@ func ListParameters(db *sql.DB, parameterType ParameterType, pattern string) ([] return params, nil } -func ListObjectParameters(db *sql.DB, objectType ObjectType, objectName, pattern string) ([]snowflakeParameter, error) { +func ListObjectParameters(db *sql.DB, objectType ObjectType, objectIdentifier, pattern string) ([]Parameter, error) { var stmt string if pattern != "" { - stmt = fmt.Sprintf("SHOW PARAMETERS LIKE '%s' IN %s \"%s\"", pattern, objectType.String(), objectName) + stmt = fmt.Sprintf("SHOW PARAMETERS LIKE '%s' IN %s %s", pattern, objectType.String(), objectIdentifier) } else { - stmt = fmt.Sprintf("SHOW PARAMETERS IN %s %s", objectType.String(), objectName) + stmt = fmt.Sprintf("SHOW PARAMETERS IN %s %s", objectType.String(), objectIdentifier) } log.Printf("[DEBUG] query = %s", stmt) rows, err := db.Query(stmt) @@ -814,7 +814,7 @@ func ListObjectParameters(db *sql.DB, objectType ObjectType, objectName, pattern return nil, err } defer rows.Close() - params := []snowflakeParameter{} + params := []Parameter{} if err := sqlx.StructScan(rows, ¶ms); err != nil { if errors.Is(err, sql.ErrNoRows) { return nil, nil diff --git a/pkg/validation/validation.go b/pkg/validation/validation.go index f740d8bd0a..9653a46351 100644 --- a/pkg/validation/validation.go +++ b/pkg/validation/validation.go @@ -115,20 +115,34 @@ func ValidateFullyQualifiedObjectID(i interface{}, k string) (s []string, errors func FormatFullyQualifiedObjectID(dbName, schemaName, objectName string) string { var n strings.Builder - if dbName != "" && schemaName != "" { - n.WriteString(fmt.Sprintf(`"%v"."%v".`, dbName, schemaName)) - } - - if dbName != "" && schemaName == "" { - n.WriteString(fmt.Sprintf(`"%v"..`, dbName)) - } - - if dbName == "" && schemaName != "" { - n.WriteString(fmt.Sprintf(`"%v".`, schemaName)) + if dbName == "" { + if schemaName == "" { + if objectName == "" { + return n.String() + } + n.WriteString(fmt.Sprintf(`"%v"`, objectName)) + return n.String() + } + n.WriteString(fmt.Sprintf(`"%v"`, schemaName)) + if objectName == "" { + return n.String() + } + n.WriteString(fmt.Sprintf(`."%v"`, objectName)) + return n.String() + } // dbName != "" + n.WriteString(fmt.Sprintf(`"%v"`, dbName)) + if schemaName == "" { + if objectName == "" { + return n.String() + } + n.WriteString(fmt.Sprintf(`."%v"`, objectName)) + return n.String() + } // schemaName != "" + n.WriteString(fmt.Sprintf(`."%v"`, schemaName)) + if objectName == "" { + return n.String() } - - n.WriteString(fmt.Sprintf(`"%v"`, objectName)) - + n.WriteString(fmt.Sprintf(`."%v"`, objectName)) return n.String() }