From 0243cdc7515cdb4c630fb10e2dd57b4d21197bd3 Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Wed, 21 Dec 2022 12:10:41 -0800 Subject: [PATCH 1/7] add parameters resources + ds --- docs/data-sources/parameters.md | 42 + docs/resources/account_parameter.md | 45 + docs/resources/object_parameter.md | 60 ++ docs/resources/session_parameter.md | 40 + .../snowflake_account_parameter/import.sh | 1 + .../snowflake_account_parameter/resource.tf | 9 + .../snowflake_object_parameter/import.sh | 1 + .../snowflake_object_parameter/resource.tf | 22 + .../snowflake_session_parameter/import.sh | 1 + .../snowflake_session_parameter/resource.tf | 4 + pkg/datasources/parameters.go | 149 ++++ pkg/datasources/parameters_acceptance_test.go | 28 + pkg/provider/provider.go | 6 +- pkg/resources/account_parameter.go | 120 +++ .../account_parameter_acceptance_test.go | 34 + pkg/resources/object_parameter.go | 152 ++++ .../object_parameter_acceptance_test.go | 42 + pkg/resources/session_parameter.go | 120 +++ .../session_parameter_acceptance_test.go | 34 + pkg/snowflake/parameters.go | 824 ++++++++++++++++++ pkg/snowflake/snowflake.go | 21 + pkg/snowflake/validation.go | 137 +++ 22 files changed, 1891 insertions(+), 1 deletion(-) create mode 100644 docs/data-sources/parameters.md create mode 100644 docs/resources/account_parameter.md create mode 100644 docs/resources/object_parameter.md create mode 100644 docs/resources/session_parameter.md create mode 100644 examples/resources/snowflake_account_parameter/import.sh create mode 100644 examples/resources/snowflake_account_parameter/resource.tf create mode 100644 examples/resources/snowflake_object_parameter/import.sh create mode 100644 examples/resources/snowflake_object_parameter/resource.tf create mode 100644 examples/resources/snowflake_session_parameter/import.sh create mode 100644 examples/resources/snowflake_session_parameter/resource.tf create mode 100644 pkg/datasources/parameters.go create mode 100644 pkg/datasources/parameters_acceptance_test.go create mode 100644 pkg/resources/account_parameter.go create mode 100644 pkg/resources/account_parameter_acceptance_test.go create mode 100644 pkg/resources/object_parameter.go create mode 100644 pkg/resources/object_parameter_acceptance_test.go create mode 100644 pkg/resources/session_parameter.go create mode 100644 pkg/resources/session_parameter_acceptance_test.go create mode 100644 pkg/snowflake/parameters.go create mode 100644 pkg/snowflake/snowflake.go diff --git a/docs/data-sources/parameters.md b/docs/data-sources/parameters.md new file mode 100644 index 0000000000..2cec6ee767 --- /dev/null +++ b/docs/data-sources/parameters.md @@ -0,0 +1,42 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "snowflake_parameters Data Source - terraform-provider-snowflake" +subcategory: "" +description: |- + +--- + +# snowflake_parameters (Data Source) + + + + + + +## Schema + +### Optional + +- `object_name` (String) If parameter_type is set to "OBJECT" then object_name is the name of the object to display object parameters for. +- `object_type` (String) If parameter_type is set to "OBJECT" then object_type is the type of object to display object parameters for. Valid values are any object supported by the IN clause of the [SHOW PARAMETERS](https://docs.snowflake.com/en/sql-reference/sql/show-parameters.html#parameters) statement, including: WAREHOUSE | DATABASE | SCHEMA | TASK | TABLE +- `parameter_type` (String) The type of parameter to filter by. Valid values are: "ACCOUNT", "SESSION", "OBJECT". +- `pattern` (String) Allows limiting the list of parameters by name using LIKE clause. Refer to [Limiting the List of Parameters by Name](https://docs.snowflake.com/en/sql-reference/parameters.html#limiting-the-list-of-parameters-by-name) + +### Read-Only + +- `id` (String) The ID of this resource. +- `parameters` (List of Object) The pipes in the schema (see [below for nested schema](#nestedatt--parameters)) + + +### Nested Schema for `parameters` + +Read-Only: + +- `default` (String) +- `description` (String) +- `key` (String) +- `level` (String) +- `type` (String) +- `value` (String) + + diff --git a/docs/resources/account_parameter.md b/docs/resources/account_parameter.md new file mode 100644 index 0000000000..782b33b77f --- /dev/null +++ b/docs/resources/account_parameter.md @@ -0,0 +1,45 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "snowflake_account_parameter Resource - terraform-provider-snowflake" +subcategory: "" +description: |- + +--- + +# snowflake_account_parameter (Resource) + + + +## Example Usage + +```terraform +resource "snowflake_account_parameter" "p" { + key = "ALLOW_ID_TOKEN" + value = "true" +} + +resource "snowflake_account_parameter" "p2" { + key = "CLIENT_ENCRYPTION_KEY_SIZE" + value = "256" +} +``` + + +## Schema + +### Required + +- `key` (String) Name of account parameter. Valid values are those in [account parameters](https://docs.snowflake.com/en/sql-reference/parameters.html#account-parameters). +- `value` (String) Value of account parameter, as a string. Constraints are the same as those for the parameters in Snowflake documentation. + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import snowflake_account_parameter.p +``` diff --git a/docs/resources/object_parameter.md b/docs/resources/object_parameter.md new file mode 100644 index 0000000000..70d72a4bf3 --- /dev/null +++ b/docs/resources/object_parameter.md @@ -0,0 +1,60 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "snowflake_object_parameter Resource - terraform-provider-snowflake" +subcategory: "" +description: |- + +--- + +# snowflake_object_parameter (Resource) + + + +## Example Usage + +```terraform +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" + object_type = "DATABASE" + object_name = snowflake_database.d.name +} + +resource "snowflake_object_parameter" "o2" { + key = "PIPE_EXECUTION_PAUSED" + value = "false" + object_type = "SCHEMA" + object_name = "${snowflake_database.d.name}.${snowflake_schema.s.name}" +} +``` + + +## Schema + +### 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_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. + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import snowflake_object_parameter.s ❄️❄️ +``` diff --git a/docs/resources/session_parameter.md b/docs/resources/session_parameter.md new file mode 100644 index 0000000000..5ad236adf3 --- /dev/null +++ b/docs/resources/session_parameter.md @@ -0,0 +1,40 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "snowflake_session_parameter Resource - terraform-provider-snowflake" +subcategory: "" +description: |- + +--- + +# snowflake_session_parameter (Resource) + + + +## Example Usage + +```terraform +resource "snowflake_session_parameter" "s" { + key = "AUTOCOMMIT" + value = "false" +} +``` + + +## Schema + +### Required + +- `key` (String) Name of session parameter. Valid values are those in [session parameters](https://docs.snowflake.com/en/sql-reference/parameters.html#session-parameters). +- `value` (String) Value of session parameter, as a string. Constraints are the same as those for the parameters in Snowflake documentation. + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +Import is supported using the following syntax: + +```shell +terraform import snowflake_session_parameter.s +``` diff --git a/examples/resources/snowflake_account_parameter/import.sh b/examples/resources/snowflake_account_parameter/import.sh new file mode 100644 index 0000000000..c1dd2640d1 --- /dev/null +++ b/examples/resources/snowflake_account_parameter/import.sh @@ -0,0 +1 @@ +terraform import snowflake_account_parameter.p diff --git a/examples/resources/snowflake_account_parameter/resource.tf b/examples/resources/snowflake_account_parameter/resource.tf new file mode 100644 index 0000000000..263643b111 --- /dev/null +++ b/examples/resources/snowflake_account_parameter/resource.tf @@ -0,0 +1,9 @@ +resource "snowflake_account_parameter" "p" { + key = "ALLOW_ID_TOKEN" + value = "true" +} + +resource "snowflake_account_parameter" "p2" { + key = "CLIENT_ENCRYPTION_KEY_SIZE" + value = "256" +} diff --git a/examples/resources/snowflake_object_parameter/import.sh b/examples/resources/snowflake_object_parameter/import.sh new file mode 100644 index 0000000000..0af314c406 --- /dev/null +++ b/examples/resources/snowflake_object_parameter/import.sh @@ -0,0 +1 @@ +terraform import snowflake_object_parameter.s ❄️❄️ diff --git a/examples/resources/snowflake_object_parameter/resource.tf b/examples/resources/snowflake_object_parameter/resource.tf new file mode 100644 index 0000000000..3aefbd28d8 --- /dev/null +++ b/examples/resources/snowflake_object_parameter/resource.tf @@ -0,0 +1,22 @@ +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" + object_type = "DATABASE" + object_name = snowflake_database.d.name +} + +resource "snowflake_object_parameter" "o2" { + key = "PIPE_EXECUTION_PAUSED" + value = "false" + object_type = "SCHEMA" + object_name = "${snowflake_database.d.name}.${snowflake_schema.s.name}" +} diff --git a/examples/resources/snowflake_session_parameter/import.sh b/examples/resources/snowflake_session_parameter/import.sh new file mode 100644 index 0000000000..387e43c1ab --- /dev/null +++ b/examples/resources/snowflake_session_parameter/import.sh @@ -0,0 +1 @@ +terraform import snowflake_session_parameter.s diff --git a/examples/resources/snowflake_session_parameter/resource.tf b/examples/resources/snowflake_session_parameter/resource.tf new file mode 100644 index 0000000000..1f399e8642 --- /dev/null +++ b/examples/resources/snowflake_session_parameter/resource.tf @@ -0,0 +1,4 @@ +resource "snowflake_session_parameter" "s" { + key = "AUTOCOMMIT" + value = "false" +} diff --git a/pkg/datasources/parameters.go b/pkg/datasources/parameters.go new file mode 100644 index 0000000000..d5df907bad --- /dev/null +++ b/pkg/datasources/parameters.go @@ -0,0 +1,149 @@ +package datasources + +import ( + "database/sql" + "errors" + "log" + "strings" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" +) + +var parametersSchema = map[string]*schema.Schema{ + "parameter_type": { + Type: schema.TypeString, + Optional: true, + Default: "SESSION", + Description: "The type of parameter to filter by. Valid values are: \"ACCOUNT\", \"SESSION\", \"OBJECT\".", + ValidateFunc: validation.StringInSlice([]string{"ACCOUNT", "SESSION", "OBJECT"}, true), + }, + "pattern": { + Type: schema.TypeString, + Optional: true, + Description: "Allows limiting the list of parameters by name using LIKE clause. Refer to [Limiting the List of Parameters by Name](https://docs.snowflake.com/en/sql-reference/parameters.html#limiting-the-list-of-parameters-by-name)", + }, + "object_type": { + Type: schema.TypeString, + Optional: true, + Description: "If parameter_type is set to \"OBJECT\" then object_type is the type of object to display object parameters for. Valid values are any object supported by the IN clause of the [SHOW PARAMETERS](https://docs.snowflake.com/en/sql-reference/sql/show-parameters.html#parameters) statement, including: WAREHOUSE | DATABASE | SCHEMA | TASK | TABLE", + ValidateFunc: validation.StringInSlice(snowflake.GetParameterObjectTypeSetAsStrings(), false), + }, + "object_name": { + Type: schema.TypeString, + Optional: true, + Description: "If parameter_type is set to \"OBJECT\" then object_name is the name of the object to display object parameters for.", + }, + "parameters": { + Type: schema.TypeList, + Computed: true, + Description: "The pipes in the schema", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Computed: true, + Description: "The name of the parameter", + }, + "value": { + Type: schema.TypeString, + Computed: true, + Description: "The value of the parameter", + }, + "default": { + Type: schema.TypeString, + Computed: true, + Description: "The default value of the parameter", + }, + "level": { + Type: schema.TypeString, + Computed: true, + Description: "The level of the parameter", + }, + "description": { + Type: schema.TypeString, + Computed: true, + Description: "The description of the parameter", + }, + "type": { + Type: schema.TypeString, + Computed: true, + Description: "The type of the parameter", + }, + }, + }, + }, +} + +func Parameters() *schema.Resource { + return &schema.Resource{ + Read: ReadParameters, + Schema: parametersSchema, + } +} + +func ReadParameters(d *schema.ResourceData, meta interface{}) error { + db := meta.(*sql.DB) + p, ok := d.GetOk("pattern") + pattern := "" + if ok { + pattern = p.(string) + } + parameterType := snowflake.ParameterType(strings.ToUpper(d.Get("parameter_type").(string))) + if parameterType == snowflake.ParameterTypeObject { + oType := d.Get("object_type").(string) + objectType := snowflake.ObjectType(oType) + objectName := d.Get("object_name").(string) + parameters, err := snowflake.ListObjectParameters(db, objectType, objectName, 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 occured during read: %v", err.Error()) + return err + } + d.SetId("parameters") + 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 + + 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 occured during read: %v", err.Error()) + return err + } + d.SetId("parameters") + + 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 + + params = append(params, paramMap) + } + return d.Set("parameters", params) + } +} diff --git a/pkg/datasources/parameters_acceptance_test.go b/pkg/datasources/parameters_acceptance_test.go new file mode 100644 index 0000000000..ee485aa93d --- /dev/null +++ b/pkg/datasources/parameters_acceptance_test.go @@ -0,0 +1,28 @@ +package datasources_test + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAcc_Parameters(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + Providers: providers(), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: parameters(), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttrSet("data.snowflake_parameters.p", "warehouses.#"), + resource.TestCheckResourceAttrSet("data.snowflake_parameters.p", "parameters.0.key"), + resource.TestCheckResourceAttrSet("data.snowflake_parameters.p", "parameters.0.value"), + ), + }, + }, + }) +} + +func parameters() string { + return `data "snowflake_parameters" "p" {}` +} diff --git a/pkg/provider/provider.go b/pkg/provider/provider.go index ba5bd56dab..1f5379877f 100644 --- a/pkg/provider/provider.go +++ b/pkg/provider/provider.go @@ -208,6 +208,7 @@ func GetGrantResources() resources.TerraformGrantResources { func getResources() map[string]*schema.Resource { // NOTE(): do not add grant resources here others := map[string]*schema.Resource{ + "snowflake_account_parameter": resources.AccountParameter(), "snowflake_api_integration": resources.APIIntegration(), "snowflake_database": resources.Database(), "snowflake_external_function": resources.ExternalFunction(), @@ -220,6 +221,7 @@ func getResources() map[string]*schema.Resource { "snowflake_network_policy_attachment": resources.NetworkPolicyAttachment(), "snowflake_network_policy": resources.NetworkPolicy(), "snowflake_oauth_integration": resources.OAuthIntegration(), + "snowflake_object_parameter": resources.ObjectParameter(), "snowflake_external_oauth_integration": resources.ExternalOauthIntegration(), "snowflake_pipe": resources.Pipe(), "snowflake_procedure": resources.Procedure(), @@ -232,6 +234,7 @@ func getResources() map[string]*schema.Resource { "snowflake_schema": resources.Schema(), "snowflake_scim_integration": resources.SCIMIntegration(), "snowflake_sequence": resources.Sequence(), + "snowflake_session_parameter": resources.SessionParameter(), "snowflake_share": resources.Share(), "snowflake_stage": resources.Stage(), "snowflake_storage_integration": resources.StorageIntegration(), @@ -273,7 +276,6 @@ func getDataSources() map[string]*schema.Resource { "snowflake_sequences": datasources.Sequences(), "snowflake_streams": datasources.Streams(), "snowflake_tasks": datasources.Tasks(), - "snowflake_pipes": datasources.Pipes(), "snowflake_masking_policies": datasources.MaskingPolicies(), "snowflake_external_functions": datasources.ExternalFunctions(), "snowflake_external_tables": datasources.ExternalTables(), @@ -282,6 +284,8 @@ func getDataSources() map[string]*schema.Resource { "snowflake_storage_integrations": datasources.StorageIntegrations(), "snowflake_row_access_policies": datasources.RowAccessPolicies(), "snowflake_functions": datasources.Functions(), + "snowflake_parameters": datasources.Parameters(), + "snowflake_pipes": datasources.Pipes(), "snowflake_procedures": datasources.Procedures(), "snowflake_databases": datasources.Databases(), "snowflake_database": datasources.Database(), diff --git a/pkg/resources/account_parameter.go b/pkg/resources/account_parameter.go new file mode 100644 index 0000000000..9c575886c1 --- /dev/null +++ b/pkg/resources/account_parameter.go @@ -0,0 +1,120 @@ +package resources + +import ( + "database/sql" + "fmt" + "reflect" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "golang.org/x/exp/maps" +) + +var accountParameterSchema = map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + 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), + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: "Value of account parameter, as a string. Constraints are the same as those for the parameters in Snowflake documentation.", + }, +} + +func AccountParameter() *schema.Resource { + return &schema.Resource{ + Create: CreateAccountParameter, + Read: ReadAccountParameter, + Update: UpdateAccountParameter, + Delete: DeleteAccountParameter, + + Schema: accountParameterSchema, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +// CreateAccountParameter implements schema.CreateFunc. +func CreateAccountParameter(d *schema.ResourceData, meta interface{}) error { + db := meta.(*sql.DB) + key := d.Get("key").(string) + value := d.Get("value").(string) + + parameterDefault := snowflake.GetParameters(snowflake.ParameterTypeAccount)[key] + if parameterDefault.Validate != nil { + if err := parameterDefault.Validate(value); err != nil { + return err + } + } + + // add quotes to value if it is a string + typeString := reflect.TypeOf("") + if reflect.TypeOf(parameterDefault.DefaultValue) == typeString { + value = fmt.Sprintf("'%s'", snowflake.EscapeString(value)) + } + + builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeAccount, db) + err := builder.SetParameter() + if err != nil { + return fmt.Errorf("error creating account parameter err = %v", err) + } + + d.SetId(key) + p, err := snowflake.ShowParameter(db, key, snowflake.ParameterTypeAccount) + if err != nil { + return fmt.Errorf("error reading account parameter err = %v", err) + } + d.Set("value", p.Value.String) + return nil +} + +// ReadAccountParameter implements schema.ReadFunc. +func ReadAccountParameter(d *schema.ResourceData, meta interface{}) error { + db := meta.(*sql.DB) + key := d.Id() + p, err := snowflake.ShowParameter(db, key, snowflake.ParameterTypeAccount) + if err != nil { + return fmt.Errorf("error reading account parameter err = %v", err) + } + d.Set("value", p.Value.String) + return nil +} + +// UpdateAccountParameter implements schema.UpdateFunc. +func UpdateAccountParameter(d *schema.ResourceData, meta interface{}) error { + return CreateAccountParameter(d, meta) +} + +// DeleteAccountParameter implements schema.DeleteFunc. +func DeleteAccountParameter(d *schema.ResourceData, meta interface{}) error { + db := meta.(*sql.DB) + key := d.Get("key").(string) + + parameterDefault := snowflake.GetParameters(snowflake.ParameterTypeAccount)[key] + defaultValue := parameterDefault.DefaultValue + value := fmt.Sprintf("%v", defaultValue) + + // add quotes to value if it is a string + typeString := reflect.TypeOf("") + if reflect.TypeOf(parameterDefault.DefaultValue) == typeString { + value = fmt.Sprintf("'%s'", value) + } + builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeAccount, db) + err := builder.SetParameter() + if err != nil { + return fmt.Errorf("error creating account parameter err = %v", err) + } + _, err = snowflake.ShowParameter(db, key, snowflake.ParameterTypeAccount) + if err != nil { + return fmt.Errorf("error reading account parameter err = %v", err) + } + + d.SetId("") + return nil +} diff --git a/pkg/resources/account_parameter_acceptance_test.go b/pkg/resources/account_parameter_acceptance_test.go new file mode 100644 index 0000000000..28953318c1 --- /dev/null +++ b/pkg/resources/account_parameter_acceptance_test.go @@ -0,0 +1,34 @@ +package resources_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAcc_AccountParameter(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + Providers: providers(), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: accountParameterBasic("ALLOW_ID_TOKEN", "true"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_account_parameter.p", "key", "ALLOW_ID_TOKEN"), + resource.TestCheckResourceAttr("snowflake_account_parameter.p", "value", "true"), + ), + }, + }, + }) +} + +func accountParameterBasic(key, value string) string { + s := ` +resource "snowflake_account_parameter" "p" { + key = "%s" + value = "%s" +} +` + return fmt.Sprintf(s, key, value) +} diff --git a/pkg/resources/object_parameter.go b/pkg/resources/object_parameter.go new file mode 100644 index 0000000000..68d5184e06 --- /dev/null +++ b/pkg/resources/object_parameter.go @@ -0,0 +1,152 @@ +package resources + +import ( + "database/sql" + "fmt" + "reflect" + "strings" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" +) + +var objectParameterSchema = map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + 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), + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: "Value of object parameter, as a string. Constraints are the same as those for the parameters in Snowflake documentation.", + }, + "object_type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + 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, + Required: true, + ForceNew: true, + Description: "Name of object to which the parameter applies.", + }, +} + +func ObjectParameter() *schema.Resource { + return &schema.Resource{ + Create: CreateObjectParameter, + Read: ReadObjectParameter, + Update: UpdateObjectParameter, + Delete: DeleteObjectParameter, + + Schema: objectParameterSchema, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +// CreateObjectParameter implements schema.CreateFunc. +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] + if parameterDefault.Validate != nil { + if err := parameterDefault.Validate(value); err != nil { + return err + } + } + if ok := slices.Contains(parameterDefault.AllowedObjectTypes, objectType); !ok { + return fmt.Errorf("object_type '%v' is not allowed for parameter '%v'", objectType, key) + } + + // add quotes to value if it is a string + typeString := reflect.TypeOf("") + if reflect.TypeOf(parameterDefault.DefaultValue) == typeString { + value = fmt.Sprintf("'%s'", snowflake.EscapeString(value)) + } + + builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeObject, db) + builder.WithObjectName(objectName) + builder.WithObjectType(objectType) + err := builder.SetParameter() + if err != nil { + return fmt.Errorf("error creating object parameter err = %v", err) + } + id := fmt.Sprintf("%v❄️%v❄️%v", key, objectType, objectName) + d.SetId(id) + p, err := snowflake.ShowObjectParameter(db, key, objectType, objectName) + if err != nil { + return fmt.Errorf("error reading object parameter err = %v", err) + } + d.Set("value", p.Value.String) + return nil +} + +// ReadObjectParameter implements schema.ReadFunc. +func ReadObjectParameter(d *schema.ResourceData, meta interface{}) error { + db := meta.(*sql.DB) + 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) + } + key := parts[0] + objectType := snowflake.ObjectType(parts[1]) + objectName := parts[2] + p, err := snowflake.ShowObjectParameter(db, key, objectType, objectName) + if err != nil { + return fmt.Errorf("error reading object parameter err = %v", err) + } + d.Set("value", p.Value.String) + return nil +} + +// UpdateObjectParameter implements schema.UpdateFunc. +func UpdateObjectParameter(d *schema.ResourceData, meta interface{}) error { + return CreateObjectParameter(d, meta) +} + +// DeleteObjectParameter implements schema.DeleteFunc. +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) + + parameterDefault := snowflake.GetParameters(snowflake.ParameterTypeObject)[key] + defaultValue := parameterDefault.DefaultValue + value := fmt.Sprintf("%v", defaultValue) + + // add quotes to value if it is a string + typeString := reflect.TypeOf("") + if reflect.TypeOf(parameterDefault.DefaultValue) == typeString { + value = fmt.Sprintf("'%s'", value) + } + builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeObject, db) + builder.WithObjectName(objectName) + builder.WithObjectType(objectType) + err := builder.SetParameter() + if err != nil { + return fmt.Errorf("error restoring default for object parameter err = %v", err) + } + _, err = snowflake.ShowObjectParameter(db, key, objectType, objectName) + if err != nil { + return fmt.Errorf("error reading object parameter err = %v", err) + } + + d.SetId("") + return nil +} diff --git a/pkg/resources/object_parameter_acceptance_test.go b/pkg/resources/object_parameter_acceptance_test.go new file mode 100644 index 0000000000..ff3eb811be --- /dev/null +++ b/pkg/resources/object_parameter_acceptance_test.go @@ -0,0 +1,42 @@ +package resources_test + +import ( + "fmt" + "strings" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAcc_ObjectParameter(t *testing.T) { + prefix := "tst-terraform" + strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + resource.ParallelTest(t, resource.TestCase{ + Providers: providers(), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: objectParameterBasic(prefix, "ENABLE_STREAM_TASK_REPLICATION", "true"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_object_parameter.p", "key", "ENABLE_STREAM_TASK_REPLICATION"), + resource.TestCheckResourceAttr("snowflake_object_parameter.p", "value", "true"), + ), + }, + }, + }) +} + +func objectParameterBasic(prefix, key, value string) string { + s := ` +resource "snowflake_database" "d" { + name = "%s" +} +resource "snowflake_object_parameter" "p" { + key = "%s" + value = "%s" + object_type = "DATABASE" + object_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 new file mode 100644 index 0000000000..5ee1233f19 --- /dev/null +++ b/pkg/resources/session_parameter.go @@ -0,0 +1,120 @@ +package resources + +import ( + "database/sql" + "fmt" + "reflect" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/snowflake" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + "golang.org/x/exp/maps" +) + +var sessionParameterSchema = map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + 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), + }, + "value": { + Type: schema.TypeString, + Required: true, + Description: "Value of session parameter, as a string. Constraints are the same as those for the parameters in Snowflake documentation.", + }, +} + +func SessionParameter() *schema.Resource { + return &schema.Resource{ + Create: CreateSessionParameter, + Read: ReadSessionParameter, + Update: UpdateSessionParameter, + Delete: DeleteSessionParameter, + + Schema: sessionParameterSchema, + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +// CreateSessionParameter implements schema.CreateFunc. +func CreateSessionParameter(d *schema.ResourceData, meta interface{}) error { + db := meta.(*sql.DB) + key := d.Get("key").(string) + value := d.Get("value").(string) + + parameterDefault := snowflake.GetParameters(snowflake.ParameterTypeSession)[key] + if parameterDefault.Validate != nil { + if err := parameterDefault.Validate(value); err != nil { + return err + } + } + + // add quotes to value if it is a string + typeString := reflect.TypeOf("") + if reflect.TypeOf(parameterDefault.DefaultValue) == typeString { + value = fmt.Sprintf("'%s'", snowflake.EscapeString(value)) + } + + builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeSession, db) + err := builder.SetParameter() + if err != nil { + return fmt.Errorf("error creating session parameter err = %v", err) + } + + d.SetId(key) + p, err := snowflake.ShowParameter(db, key, snowflake.ParameterTypeSession) + if err != nil { + return fmt.Errorf("error reading session parameter err = %v", err) + } + d.Set("value", p.Value.String) + return nil +} + +// ReadSessionParameter implements schema.ReadFunc. +func ReadSessionParameter(d *schema.ResourceData, meta interface{}) error { + db := meta.(*sql.DB) + key := d.Id() + p, err := snowflake.ShowParameter(db, key, snowflake.ParameterTypeSession) + if err != nil { + return fmt.Errorf("error reading session parameter err = %v", err) + } + d.Set("value", p.Value.String) + return nil +} + +// UpdateSessionParameter implements schema.UpdateFunc. +func UpdateSessionParameter(d *schema.ResourceData, meta interface{}) error { + return CreateSessionParameter(d, meta) +} + +// DeleteSessionParameter implements schema.DeleteFunc. +func DeleteSessionParameter(d *schema.ResourceData, meta interface{}) error { + db := meta.(*sql.DB) + key := d.Get("key").(string) + + parameterDefault := snowflake.GetParameters(snowflake.ParameterTypeSession)[key] + defaultValue := parameterDefault.DefaultValue + value := fmt.Sprintf("%v", defaultValue) + + // add quotes to value if it is a string + typeString := reflect.TypeOf("") + if reflect.TypeOf(parameterDefault.DefaultValue) == typeString { + value = fmt.Sprintf("'%s'", value) + } + builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeSession, db) + err := builder.SetParameter() + if err != nil { + return fmt.Errorf("error creating account parameter err = %v", err) + } + _, err = snowflake.ShowParameter(db, key, snowflake.ParameterTypeSession) + if err != nil { + return fmt.Errorf("error reading a parameter err = %v", err) + } + + d.SetId("") + return nil +} diff --git a/pkg/resources/session_parameter_acceptance_test.go b/pkg/resources/session_parameter_acceptance_test.go new file mode 100644 index 0000000000..21067c4c88 --- /dev/null +++ b/pkg/resources/session_parameter_acceptance_test.go @@ -0,0 +1,34 @@ +package resources_test + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAcc_SessionParameter(t *testing.T) { + resource.ParallelTest(t, resource.TestCase{ + Providers: providers(), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: sessionParameterBasic("AUTOCOMMIT", "false"), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_session_parameter.p", "key", "AUTOCOMMIT"), + resource.TestCheckResourceAttr("snowflake_session_parameter.p", "value", "false"), + ), + }, + }, + }) +} + +func sessionParameterBasic(key, value string) string { + s := ` +resource "snowflake_session_parameter" "p" { + key = "%s" + value = "%s" +} +` + return fmt.Sprintf(s, key, value) +} diff --git a/pkg/snowflake/parameters.go b/pkg/snowflake/parameters.go new file mode 100644 index 0000000000..93e143714b --- /dev/null +++ b/pkg/snowflake/parameters.go @@ -0,0 +1,824 @@ +package snowflake + +import ( + "database/sql" + "fmt" + "log" + "reflect" + "strconv" + "time" + + "github.com/jmoiron/sqlx" + "golang.org/x/exp/maps" + "golang.org/x/exp/slices" +) + +// ParameterType is the type of parameter. +type ParameterType string + +const ( + ParameterTypeAccount ParameterType = "ACCOUNT" + ParameterTypeSession ParameterType = "SESSION" + ParameterTypeObject ParameterType = "OBJECT" +) + +// Parameter is a parameter that can be set on an account, session, or object. +type Parameter struct { + TypeSet []ParameterType + DefaultValue interface{} + ValueType reflect.Type + Validate func(string) error + AllowedObjectTypes []ObjectType +} + +// ParameterDefaults returns a map of default values for all parameters. +func ParameterDefaults() map[string]Parameter { + validateBoolFunc := func(value string) (err error) { + _, err = strconv.ParseBool(value) + if err != nil { + return fmt.Errorf("%v is an invalid value. Boolean value (\"true\"/\"false\") expected", value) + } + return nil + } + + return map[string]Parameter{ + "ALLOW_CLIENT_MFA_CACHING": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "ALLOW_ID_TOKEN": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "CLIENT_ENCRYPTION_KEY_SIZE": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: 128, + Validate: func(value string) (err error) { + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return fmt.Errorf("%v cannot be cast to an integer", value) + } + if v != 128 && v != 256 { + return fmt.Errorf("%v is not a valid value for CLIENT_ENCRYPTION_KEY_SIZE", value) + } + return nil + }, + }, + "ENABLE_INTERNAL_STAGES_PRIVATELINK": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "ENFORCE_SESSION_POLICY": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "EXTERNAL_OAUTH_ADD_PRIVILEGED_ROLES_TO_BLOCKED_LIST": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: true, + Validate: validateBoolFunc, + }, + "INITIAL_REPLICATION_SIZE_LIMIT_IN_TB": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: 10.0, + Validate: func(value string) (err error) { + v, err := strconv.ParseFloat(value, 32) + if err != nil { + return fmt.Errorf("%v cannot be cast to a float", value) + } + if v < 0.0 || (v < 0.0 && v < 1.0) { + return fmt.Errorf("%v must be 0.0 and above with a scale of at least 1 (e.g. 20.5, 32.25, 33.333, etc.)", v) + } + return nil + }, + }, + "MIN_DATA_RETENTION_TIME_IN_DAYS": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: 0, + Validate: func(value string) (err error) { + v, err := strconv.ParseInt(value, 10, 64) + if err != nil { + return fmt.Errorf("%v cannot be cast to an integer", value) + } + if v < 0 || v > 90 { + return fmt.Errorf("%v must be 0 or 1 for Standard Edition, or between 0 and 90 for Enterprise Edition or higher", v) + } + return nil + }, + }, + "NETWORK_POLICY": { + TypeSet: []ParameterType{ParameterTypeAccount, ParameterTypeObject}, + DefaultValue: "none", + Validate: func(value string) (err error) { + if len(value) <= 0 { + return fmt.Errorf("NETWORK_POLICY cannot be empty") + } + _, errs := ValidateIdentifier(value, []string{}) + if len(errs) > 0 { + return fmt.Errorf("NETWORK_POLICY %v is not a valid identifier", value) + } + return nil + }, + AllowedObjectTypes: []ObjectType{ + ObjectTypeUser, + }, + }, + "PERIODIC_DATA_REKEYING": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "PREVENT_UNLOAD_TO_INLINE_URL": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "REQUIRE_STORAGE_INTEGRATION_FOR_STAGE_CREATION": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "SSO_LOGIN_PAGE": { + TypeSet: []ParameterType{ParameterTypeAccount}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "ABORT_DETACHED_QUERY": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "AUTOCOMMIT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: true, + Validate: validateBoolFunc, + }, + "BINARY_INPUT_FORMAT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "HEX", + Validate: func(value string) (err error) { + validFormats := []string{"HEX", "BASE64", "UTF8", "UTF-8"} + if !slices.Contains(validFormats, value) { + return fmt.Errorf("%v is not a valid value for BINARY_INPUT_FORMAT", value) + } + return nil + }, + }, + "BINARY_OUTPUT_FORMAT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "HEX", + Validate: func(value string) (err error) { + validFormats := []string{"HEX", "BASE64"} + if !slices.Contains(validFormats, value) { + return fmt.Errorf("%v is not a valid value for BINARY_OUTPUT_FORMAT", value) + } + return nil + }, + }, + "DATE_INPUT_FORMAT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "auto", + Validate: func(value string) (err error) { + validFormats := getValidDateFormats(DateFormatAny, true) + if !slices.Contains(validFormats, value) { + return fmt.Errorf("%v is not a valid value for DATE_INPUT_FORMAT", value) + } + return nil + }, + }, + "DATE_OUTPUT_FORMAT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "YYYY-MM-DD", + Validate: func(value string) (err error) { + validFormats := getValidDateFormats(DateFormatAny, false) + if !slices.Contains(validFormats, value) { + return fmt.Errorf("%v is not a valid value for DATE_INPUT_FORMAT", value) + } + return nil + }, + }, + "ERROR_ON_NONDETERMINISTIC_MERGE": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: true, + Validate: validateBoolFunc, + }, + "ERROR_ON_NONDETERMINISTIC_UPDATE": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "JSON_INDENT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: 2, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for JSON_INDENT, must be an integer between 0 and 16", value) + } + if v < 0 || v > 16 { + return fmt.Errorf("%v is not a valid value for JSON_INDENT, must be an integer between 0 and 16", value) + } + return nil + }, + }, + "LOCK_TIMEOUT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: 43200, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for LOCK_TIMEOUT, must be an integer", value) + } + if v < 0 { + return fmt.Errorf("%v is not a valid value for LOCK_TIMEOUT, must be an integer greater than 0", value) + } + return nil + }, + }, + "QUERY_TAG": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "", + Validate: func(value string) (err error) { + if len(value) > 2000 { + return fmt.Errorf("%v is not a valid value for QUERY_TAG, must be 2000 characters or less", value) + } + return nil + }, + }, + "ROWS_PER_RESULTSET": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: 0, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for LOCK_TIMEOUT, must be an integer", value) + } + if v < 0 { + return fmt.Errorf("%v is not a valid value for LOCK_TIMEOUT, must be an integer greater than 0", value) + } + return nil + }, + }, + "SIMULATED_DATA_SHARING_CONSUMER": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "", + Validate: nil, + }, + "STATEMENT_TIMEOUT_IN_SECONDS": { + TypeSet: []ParameterType{ParameterTypeSession, ParameterTypeObject}, + DefaultValue: 172800, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for STATEMENT_TIMEOUT_IN_SECONDS, must be an integer", value) + } + if v < 0 || v > 604800 { + return fmt.Errorf("%v is not a valid value for STATEMENT_TIMEOUT_IN_SECONDS, must be an integer between 0 and 604800", value) + } + return nil + }, + AllowedObjectTypes: []ObjectType{ + ObjectTypeWarehouse, + }, + }, + "STRICT_JSON_OUTPUT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "TIMESTAMP_DAY_IS_ALWAYS_24H": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: false, + Validate: validateBoolFunc, + }, + "TIMESTAMP_INPUT_FORMAT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "auto", + Validate: func(value string) (err error) { + formats := getValidTimeStampFormats(TimeStampFormatAny, true) + if !slices.Contains(formats, value) { + return fmt.Errorf("%v is not a valid value for TIMESTAMP_INPUT_FORMAT, must be one of %v", value, formats) + } + return nil + }, + }, + "TIMESTAMP_LTZ_OUTPUT_FORMAT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + Validate: func(value string) (err error) { + formats := getValidTimeStampFormats(TimeStampFormatAny, false) + if !slices.Contains(formats, value) { + return fmt.Errorf("%v is not a valid value for TIMESTAMP_LTZ_OUTPUT_FORMAT, must be one of %v", value, formats) + } + return nil + }, + }, + "TIMESTAMP_NTZ_OUTPUT_FORMAT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "YYYY-MM-DD HH24:MI:SS.FF3", + Validate: func(value string) (err error) { + formats := getValidTimeStampFormats(TimeStampFormatAny, false) + if !slices.Contains(formats, value) { + return fmt.Errorf("%v is not a valid value for TIMESTAMP_NTZ_OUTPUT_FORMAT, must be one of %v", value, formats) + } + return nil + }, + }, + "TIMESTAMP_OUTPUT_FORMAT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "YYYY-MM-DD HH24:MI:SS.FF3 TZHTZM", + Validate: func(value string) (err error) { + formats := getValidTimeStampFormats(TimeStampFormatAny, false) + if !slices.Contains(formats, value) { + return fmt.Errorf("%v is not a valid value for TIMESTAMP_OUTPUT_FORMAT, must be one of %v", value, formats) + } + return nil + }, + }, + "TIMESTAMP_TYPE_MAPPING": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "TIMESTAMP_NTZ", + Validate: func(value string) (err error) { + if !slices.Contains([]string{"TIMESTAMP_NTZ", "TIMESTAMP_LTZ", "TIMESTAMP_TZ"}, value) { + return fmt.Errorf("%v is not a valid value for TIMESTAMP_TYPE_MAPPING, must be one of TIMESTAMP_NTZ, TIMESTAMP_LTZ, TIMESTAMP_TZ", value) + } + return nil + }, + }, + "TIMESTAMP_TZ_OUTPUT_FORMAT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "", + Validate: func(value string) (err error) { + formats := getValidTimeStampFormats(TimeStampFormatAny, false) + if !slices.Contains(formats, value) { + return fmt.Errorf("%v is not a valid value for TIMESTAMP_TZ_OUTPUT_FORMAT, must be one of %v", value, formats) + } + return nil + }, + }, + "TIMEZONE": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "America/Los_Angeles", + Validate: func(value string) (err error) { + _, err = time.LoadLocation(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for TIMEZONE, must be a valid timezone", value) + } + return nil + }, + }, + "TIME_INPUT_FORMAT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "auto", + Validate: func(value string) (err error) { + formats := getValidTimeFormats(TimeFormatAny, true) + if !slices.Contains(formats, value) { + return fmt.Errorf("%v is not a valid value for TIME_INPUT_FORMAT, must be one of %v", value, formats) + } + return nil + }, + }, + "TIME_OUTPUT_FORMAT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "HH24:MI:SS", + Validate: func(value string) (err error) { + formats := getValidTimeFormats(TimeFormatAny, false) + if !slices.Contains(formats, value) { + return fmt.Errorf("%v is not a valid value for TIME_OUTPUT_FORMAT, must be one of %v", value, formats) + } + return nil + }, + }, + "TRANSACTION_DEFAULT_ISOLATION_LEVEL": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "READ_COMMITTED", + Validate: func(value string) (err error) { + if !slices.Contains([]string{"READ_COMMITTED"}, value) { + return fmt.Errorf("%v is not a valid value for TRANSACTION_DEFAULT_ISOLATION_LEVEL, must be one of READ_UNCOMMITTED, READ_COMMITTED, REPEATABLE_READ, SERIALIZABLE", value) + } + return nil + }, + }, + "TWO_DIGIT_CENTURY_START": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: 1970, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for TWO_DIGIT_CENTURY_START, must be an integer", value) + } + if v < 1900 || v > 2100 { + return fmt.Errorf("%v is not a valid value for TWO_DIGIT_CENTURY_START, must be between 1900 and 2100", value) + } + return nil + }, + }, + "UNSUPPORTED_DDL_ACTION": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: "IGNORE", + Validate: func(value string) (err error) { + if !slices.Contains([]string{"IGNORE", "FAIL"}, value) { + return fmt.Errorf("%v is not a valid value for UNSUPPORTED_DDL_ACTION, must be one of IGNORE, FAIL", value) + } + return nil + }, + }, + "USE_CACHED_RESULT": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: true, + Validate: validateBoolFunc, + }, + "WEEK_OF_YEAR_POLICY": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: 0, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for WEEK_OF_YEAR_POLICY, must be an integer", value) + } + if v < 0 || v > 1 { + return fmt.Errorf("%v is not a valid value for WEEK_OF_YEAR_POLICY, must be 0 or 1", value) + } + return nil + }, + }, + "WEEK_START": { + TypeSet: []ParameterType{ParameterTypeSession}, + DefaultValue: 0, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for WEEK_START, must be an integer", value) + } + if v < 0 || v > 7 { + return fmt.Errorf("%v is not a valid value for WEEK_START, must be between 0 and 7", value) + } + return nil + }, + }, + "DATA_RETENTION_TIME_IN_DAYS": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: 1, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for DATA_RETENTION_TIME_IN_DAYS, must be an integer", value) + } + if v < 0 || v > 90 { + return fmt.Errorf("%v is not a valid value for DATA_RETENTION_TIME_IN_DAYS, must be between 0 and 90", value) + } + return nil + }, + AllowedObjectTypes: []ObjectType{ + ObjectTypeDatabase, + ObjectTypeSchema, + ObjectTypeTable, + }, + }, + "DEFAULT_DDL_COLLATION": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: "", + Validate: func(value string) (err error) { + // todo: validate collation. + if len(value) < 1 { + return fmt.Errorf("%v is not a valid value for DEFAULT_DDL_COLLATION, must be a valid collation", value) + } + return nil + }, + AllowedObjectTypes: []ObjectType{ + ObjectTypeDatabase, + ObjectTypeSchema, + ObjectTypeTable, + }, + }, + "ENABLE_STREAM_TASK_REPLICATION": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: false, + Validate: validateBoolFunc, + AllowedObjectTypes: []ObjectType{ + ObjectTypeDatabase, + ObjectTypeReplicationGroup, + ObjectTypeFailoverGroup, + }, + }, + "MAX_CONCURRENCY_LEVEL": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: 0, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for MAX_CONCURRENCY_LEVEL, must be an integer", value) + } + if v < 0 { + return fmt.Errorf("%v is not a valid value for MAX_CONCURRENCY_LEVEL, must be an integer greater than 0", value) + } + return nil + }, + AllowedObjectTypes: []ObjectType{ + ObjectTypeWarehouse, + }, + }, + "MAX_DATA_EXTENSION_TIME_IN_DAYS": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: 14, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for MAX_DATA_EXTENSION_TIME_IN_DAYS, must be an integer", value) + } + if v < 0 || v > 90 { + return fmt.Errorf("%v is not a valid value for MAX_DATA_EXTENSION_TIME_IN_DAYS, must be between 0 and 90", value) + } + return nil + }, + }, + "PIPE_EXECUTION_PAUSED": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: false, + Validate: validateBoolFunc, + AllowedObjectTypes: []ObjectType{ + ObjectTypeSchema, + ObjectTypePipe, + }, + }, + "PREVENT_UNLOAD_TO_INTERNAL_STAGES": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: false, + Validate: validateBoolFunc, + AllowedObjectTypes: []ObjectType{ + ObjectTypeUser, + }, + }, + "STATEMENT_QUEUED_TIMEOUT_IN_SECONDS": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: 0, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for STATEMENT_QUEUED_TIMEOUT_IN_SECONDS, must be an integer", value) + } + if v < 0 { + return fmt.Errorf("%v is not a valid value for STATEMENT_QUEUED_TIMEOUT_IN_SECONDS, must be an integer greater than 0", value) + } + return nil + }, + AllowedObjectTypes: []ObjectType{ + ObjectTypeWarehouse, + }, + }, + "SHARE_RESTRICTIONS": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: true, + Validate: validateBoolFunc, + AllowedObjectTypes: []ObjectType{ + ObjectTypeShare, + }, + }, + "SUSPEND_TASK_AFTER_NUM_FAILURES": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: 0, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for SUSPEND_TASK_AFTER_NUM_FAILURES, must be an integer", value) + } + if v < 0 { + return fmt.Errorf("%v is not a valid value for SUSPEND_TASK_AFTER_NUM_FAILURES, must be an integer greater than 0", value) + } + return nil + }, + AllowedObjectTypes: []ObjectType{ + ObjectTypeDatabase, + ObjectTypeSchema, + ObjectTypeTask, + }, + }, + "USER_TASK_MANAGED_INITIAL_WAREHOUSE_SIZE": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: "MEDIUM", + Validate: func(value string) (err error) { + if !slices.Contains([]string{"X-SMALL", "SMALL", "MEDIUM", "LARGE", "X-LARGE", "2X-LARGE", "3X-LARGE", "4X-LARGE", "5X-LARGE", "6X-LARGE"}, value) { + return fmt.Errorf("%v is not a valid value for USER_TASK_MANAGED_INITIAL_WAREHOUSE_SIZE, must be a valid warehouse size, such as \"SMALL\", \"MEDIUM\" or \"LARGE\"", value) + } + return nil + }, + AllowedObjectTypes: []ObjectType{ + ObjectTypeDatabase, + ObjectTypeSchema, + ObjectTypeTask, + }, + }, + "USER_TASK_TIMEOUT_MS": { + TypeSet: []ParameterType{ParameterTypeObject}, + DefaultValue: 3600000, + Validate: func(value string) (err error) { + v, err := strconv.Atoi(value) + if err != nil { + return fmt.Errorf("%v is not a valid value for USER_TASK_TIMEOUT_MS, must be an integer", value) + } + if v < 0 || v > 86400000 { + return fmt.Errorf("%v is not a valid value for USER_TASK_TIMEOUT_MS, must be an integer greater than 0 and less than 86400000 (1 day)", value) + } + return nil + }, + AllowedObjectTypes: []ObjectType{ + ObjectTypeDatabase, + ObjectTypeSchema, + ObjectTypeTask, + }, + }, + } +} + +// GetParameterObjectTypeSetAsStrings returns a slice of all object types that can have parameters +func GetParameterObjectTypeSetAsStrings() []string { + objectTypeSet := []ObjectType{ + ObjectTypeDatabase, + ObjectTypeSchema, + ObjectTypePipe, + ObjectTypeUser, + ObjectTypeShare, + ObjectTypeWarehouse, + ObjectTypeTask, + ObjectTypeReplicationGroup, + ObjectTypeFailoverGroup, + ObjectTypeTable, + } + result := make([]string, 0, len(objectTypeSet)) + for _, v := range objectTypeSet { + result = append(result, string(v)) + } + return result +} + +// GetParameters returns a map of parameters that match the given type (e.g. Account, Session, Object) +func GetParameters(t ParameterType) map[string]Parameter { + parameters := ParameterDefaults() + keys := maps.Keys(parameters) + for _, key := range keys { + typeSet := parameters[key].TypeSet + if !slices.Contains(typeSet, t) { + delete(parameters, key) + } + } + return parameters +} + +// GetParameter returns a parameter by key +func GetParameter(key string) Parameter { + 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 +} + +func NewParameter(key, value string, parameterType ParameterType, db *sql.DB) *ParameterBuilder { + return &ParameterBuilder{ + key: key, + value: value, + parameterType: parameterType, + db: db, + } +} + +func (v *ParameterBuilder) WithObjectType(objectType ObjectType) *ParameterBuilder { + v.objectType = objectType + return v +} + +func (v *ParameterBuilder) WithObjectName(objectName string) *ParameterBuilder { + v.objectName = objectName + return v +} + +func (v *ParameterBuilder) SetParameter() error { + if v.parameterType == ParameterTypeAccount || v.parameterType == ParameterTypeSession { + // prepared statements do not work here for some reason. We already validate inputs so its okay + stmt := fmt.Sprintf("ALTER ACCOUNT SET %s = %s", v.key, v.value) + _, err := v.db.Exec(stmt) + if err != nil { + return err + } + } else if v.parameterType == ParameterTypeObject { + stmt := fmt.Sprintf("ALTER %s %s SET %s = %s", v.objectType, v.objectName, v.key, v.value) + _, err := v.db.Exec(stmt) + if err != nil { + return err + } + } + return nil +} + +type snowflakeParameter struct { + Key sql.NullString `db:"key"` + Value sql.NullString `db:"value"` + Default sql.NullString `db:"default"` + Level sql.NullString`db:"level"` + Description sql.NullString `db:"description"` + PType sql.NullString `db:"type"` +} + +func ShowParameter(db *sql.DB, key string, parameterType ParameterType) (*snowflakeParameter, error) { + var value snowflakeParameter + var stmt string + if parameterType == ParameterTypeAccount || parameterType == ParameterTypeSession { + stmt = fmt.Sprintf("SHOW PARAMETERS LIKE '%s' IN ACCOUNT", key) + } else { + return nil, fmt.Errorf("unsupported parameter type %s", parameterType) + } + rows, err := db.Query(stmt) + if err != nil { + return nil, err + } + defer rows.Close() + params := []snowflakeParameter{} + if err := sqlx.StructScan(rows, ¶ms); err != nil { + if err == sql.ErrNoRows { + return nil, nil + } + return nil, fmt.Errorf("unable to scan row for %s err = %w", stmt, err) + } + value = params[0] + + 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) + rows, err := db.Query(stmt) + if err != nil { + return nil, err + } + defer rows.Close() + params := []snowflakeParameter{} + if err := sqlx.StructScan(rows, ¶ms); err != nil { + if err == sql.ErrNoRows { + return nil, nil + } + return nil, fmt.Errorf("unable to scan row for %s err = %w", stmt, err) + } + value = params[0] + + return &value, nil +} + +func ListParameters(db *sql.DB, parameterType ParameterType, pattern string) ([]snowflakeParameter, error) { + var stmt string + if parameterType == ParameterTypeAccount || parameterType == ParameterTypeSession{ + if pattern != "" { + stmt = fmt.Sprintf("SHOW PARAMETERS LIKE '%s' IN %v", pattern, parameterType) + } else { + stmt = fmt.Sprintf("SHOW PARAMETERS IN %v",parameterType) + } + } else { + return nil, fmt.Errorf("unsupported parameter type %s", parameterType) + } + log.Printf("[DEBUG] query = %s", stmt) + rows, err := db.Query(stmt) + if err != nil { + return nil, err + } + defer rows.Close() + params := []snowflakeParameter{} + if err := sqlx.StructScan(rows, ¶ms); err != nil { + if err == sql.ErrNoRows { + return nil, nil + } + return nil, fmt.Errorf("unable to scan row for %s err = %w", stmt, err) + } + return params, nil +} + +func ListObjectParameters(db *sql.DB, objectType ObjectType, objectName, pattern string) ([]snowflakeParameter, error) { + var stmt string + if pattern != "" { + stmt = fmt.Sprintf("SHOW PARAMETERS LIKE '%s' IN %s %s", pattern, objectType.String(), objectName) + } else { + stmt = fmt.Sprintf("SHOW PARAMETERS IN %s %s", objectType.String(), objectName) + } + log.Printf("[DEBUG] query = %s", stmt) + rows, err := db.Query(stmt) + if err != nil { + return nil, err + } + defer rows.Close() + params := []snowflakeParameter{} + if err := sqlx.StructScan(rows, ¶ms); err != nil { + if err == sql.ErrNoRows { + return nil, nil + } + return nil, fmt.Errorf("unable to scan row for %s err = %w", stmt, err) + } + return params, nil +} diff --git a/pkg/snowflake/snowflake.go b/pkg/snowflake/snowflake.go new file mode 100644 index 0000000000..8d4143c2d0 --- /dev/null +++ b/pkg/snowflake/snowflake.go @@ -0,0 +1,21 @@ +package snowflake + +// ObjectType is the type of object. +type ObjectType string + +const ( + ObjectTypeDatabase ObjectType = "DATABASE" + ObjectTypeSchema ObjectType = "SCHEMA" + ObjectTypeTable ObjectType = "TABLE" + ObjectTypeReplicationGroup ObjectType = "REPLICATION GROUP" + ObjectTypeFailoverGroup ObjectType = "FAILOVER GROUP" + ObjectTypeWarehouse ObjectType = "WAREHOUSE" + ObjectTypePipe ObjectType = "PIPE" + ObjectTypeUser ObjectType = "USER" + ObjectTypeShare ObjectType = "SHARE" + ObjectTypeTask ObjectType = "TASK" +) + +func (o ObjectType) String() string { + return string(o) +} diff --git a/pkg/snowflake/validation.go b/pkg/snowflake/validation.go index 9b7ff47fda..0a104726e1 100644 --- a/pkg/snowflake/validation.go +++ b/pkg/snowflake/validation.go @@ -51,3 +51,140 @@ func isInitialIdentifierRune(r rune) bool { (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z')) } + +type DateFormat string + +const ( + DateFormatISO DateFormat = "DateFormatISO" + DateFormatOther DateFormat = "DateFormatOther" + DateFormatAny DateFormat = "DateFormatAny" +) + +func getValidDateFormats(f DateFormat, includeAuto bool) []string { + ISODateFormats := []string{ + "YYYY-MM-DD", + } + OtherDateFormats := []string{ + "DD-MON-YYYY", + "MM/DD/YYYY", + } + AnyDateFormats := append(ISODateFormats, OtherDateFormats...) + var formats []string + + switch f { + case DateFormatISO: + formats = ISODateFormats + case DateFormatOther: + formats = OtherDateFormats + case DateFormatAny: + formats = AnyDateFormats + } + if includeAuto { + formats= append(formats, "auto") + } + return formats +} + +type TimeFormat string + +const ( + TimeFormatISO TimeFormat = "TimeFormatISO" + TimeFormatRFC TimeFormat = "TimeFormatRFC" + TimeFormatAny TimeFormat = "TimeFormatAny" +) + +func getValidTimeFormats(f TimeFormat, includeAuto bool) []string { + ISOTimeFormats := []string{ + "HH24:MI:SS.FFTZH:TZM", + "HH24:MI:SS.FF", + "HH24:MI:SS", + "HH24:MI", + } + RFCTimeFormats := []string{ + "HH12:MI:SS.FF AM", + "HH12:MI:SS AM", + "HH12:MI AM", + } + AnyTimeFormats := append(ISOTimeFormats, RFCTimeFormats...) + var formats []string + + switch f { + case TimeFormatISO: + formats = ISOTimeFormats + case TimeFormatRFC: + formats = RFCTimeFormats + case TimeFormatAny: + formats = AnyTimeFormats + } + if includeAuto { + formats= append(formats, "auto") + } + return formats +} + +type TimeStampFormat string + +const ( + TimeStampFormatISO TimeStampFormat = "TimeStampFormatISO" + TimeStampFormatRFC TimeStampFormat = "TimeStampFormatRFC" + TimeStampFormatOther TimeStampFormat = "TimeStampFormatOther" + TimeStampFormatAny TimeStampFormat = "TimeStampFormatAny" +) + +func getValidTimeStampFormats(f TimeStampFormat, includeAuto bool) []string { + + ISOTimeStampFormats := []string{ + "YYYY-MM-DD\"T\"HH24:MI:SS.FFTZH:TZM", + "YYYY-MM-DD HH24:MI:SS.FFTZH:TZM", + "YYYY-MM-DD HH24:MI:SS.FFTZH", + "YYYY-MM-DD HH24:MI:SS.FF TZH:TZM", + "YYYY-MM-DD HH24:MI:SS.FF TZHTZM", + "YYYY-MM-DD HH24:MI:SS TZH:TZM", + "YYYY-MM-DD HH24:MI:SS TZHTZM", + "YYYY-MM-DD\"T\"HH24:MI:SS.FF", + "YYYY-MM-DD HH24:MI:SS.FF", + "YYYY-MM-DD\"T\"HH24:MI:SS", + "YYYY-MM-DD HH24:MI:SS", + "YYYY-MM-DD\"T\"HH24:MI", + "YYYY-MM-DD HH24:MI", + "YYYY-MM-DD\"T\"HH24", + "YYYY-MM-DD HH24", + "YYYY-MM-DD\"T\"HH24:MI:SSTZH:TZM", + "YYYY-MM-DD HH24:MI:SSTZH:TZM", + "YYYY-MM-DD HH24:MI:SSTZH", + "YYYY-MM-DD\"T\"HH24:MITZH:TZM", + "YYYY-MM-DD HH24:MITZH:TZM", + } + RFCTimeStampFormats := []string{ + "DY, DD MON YYYY HH24:MI:SS TZHTZM", + "DY, DD MON YYYY HH24:MI:SS.FF TZHTZM", + "DY, DD MON YYYY HH12:MI:SS AM TZHTZM", + "DY, DD MON YYYY HH12:MI:SS.FF AM TZHTZM", + "DY, DD MON YYYY HH24:MI:SS", + "DY, DD MON YYYY HH24:MI:SS.FF", + "DY, DD MON YYYY HH12:MI:SS AM", + "DY, DD MON YYYY HH12:MI:SS.FF AM", + } + OtherTimeStampFormats := []string{ + "MM/DD/YYYY HH24:MI:SS", + "DY MON DD HH24:MI:SS TZHTZM YYYY", + } + AnyTimeStampFormats := append(ISOTimeStampFormats, append(RFCTimeStampFormats, OtherTimeStampFormats...)...) + var formats []string + switch f { + case TimeStampFormatISO: + formats = ISOTimeStampFormats + case TimeStampFormatRFC: + formats = RFCTimeStampFormats + case TimeStampFormatOther: + formats = OtherTimeStampFormats + case TimeStampFormatAny: + formats = AnyTimeStampFormats + } + + if includeAuto { + formats = append(formats, "auto") + } + return formats +} + From 0e82223d109a48d22c2bdabb1bb8fa8982365454 Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Wed, 21 Dec 2022 12:16:24 -0800 Subject: [PATCH 2/7] add parameters example --- .../snowflake_parameters/data-source.tf | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 examples/data-sources/snowflake_parameters/data-source.tf diff --git a/examples/data-sources/snowflake_parameters/data-source.tf b/examples/data-sources/snowflake_parameters/data-source.tf new file mode 100644 index 0000000000..72b0607e06 --- /dev/null +++ b/examples/data-sources/snowflake_parameters/data-source.tf @@ -0,0 +1,22 @@ +resource "snowflake_database" "d" { + name = "TEST_DB" +} + +// read all object parameters in database TEST_DB +data "snowflake_parameters" "p" { + parameter_type = "OBJECT" + object_type = "DATABASE" + object_name = snowflake_database.d.name +} + +// read all account parameters with the pattern '%TIMESTAMP%' +data "snowflake_parameters" "p2" { + parameter_type = "ACCOUNT" + pattern = "%TIMESTAMP%" +} + +// read the exact session parameter ROWS_PER_RESULTSET +data "snowflake_parameters" "p3" { + parameter_type = "SESSION" + pattern = "ROWS_PER_RESULTSET" +} From 8c044e9413b508fb0fd1b34178db9d68b50f0c69 Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Wed, 21 Dec 2022 13:42:01 -0800 Subject: [PATCH 3/7] fix linting issues --- docs/data-sources/parameters.md | 25 ++++++++++++++ pkg/datasources/parameters.go | 10 +++--- pkg/datasources/parameters_acceptance_test.go | 2 +- pkg/resources/account_parameter.go | 18 ++++++---- pkg/resources/object_parameter.go | 5 ++- .../object_parameter_acceptance_test.go | 2 +- pkg/snowflake/parameters.go | 21 ++++++------ pkg/snowflake/snowflake.go | 18 +++++----- pkg/snowflake/validation.go | 34 +++++++++++-------- 9 files changed, 88 insertions(+), 47 deletions(-) diff --git a/docs/data-sources/parameters.md b/docs/data-sources/parameters.md index 2cec6ee767..b6f0eea2b2 100644 --- a/docs/data-sources/parameters.md +++ b/docs/data-sources/parameters.md @@ -10,7 +10,32 @@ description: |- +## Example Usage +```terraform +resource "snowflake_database" "d" { + name = "TEST_DB" +} + +// read all object parameters in database TEST_DB +data "snowflake_parameters" "p" { + parameter_type = "OBJECT" + object_type = "DATABASE" + object_name = snowflake_database.d.name +} + +// read all account parameters with the pattern '%TIMESTAMP%' +data "snowflake_parameters" "p2" { + parameter_type = "ACCOUNT" + pattern = "%TIMESTAMP%" +} + +// read the exact session parameter ROWS_PER_RESULTSET +data "snowflake_parameters" "p3" { + parameter_type = "SESSION" + pattern = "ROWS_PER_RESULTSET" +} +``` ## Schema diff --git a/pkg/datasources/parameters.go b/pkg/datasources/parameters.go index d5df907bad..dd26e12e88 100644 --- a/pkg/datasources/parameters.go +++ b/pkg/datasources/parameters.go @@ -31,9 +31,9 @@ var parametersSchema = map[string]*schema.Schema{ ValidateFunc: validation.StringInSlice(snowflake.GetParameterObjectTypeSetAsStrings(), false), }, "object_name": { - Type: schema.TypeString, - Optional: true, - Description: "If parameter_type is set to \"OBJECT\" then object_name is the name of the object to display object parameters for.", + Type: schema.TypeString, + Optional: true, + Description: "If parameter_type is set to \"OBJECT\" then object_name is the name of the object to display object parameters for.", }, "parameters": { Type: schema.TypeList, @@ -101,7 +101,7 @@ func ReadParameters(d *schema.ResourceData, meta interface{}) error { d.SetId("") return nil } else if err != nil { - log.Printf("[DEBUG] error occured during read: %v", err.Error()) + log.Printf("[DEBUG] error occurred during read: %v", err.Error()) return err } d.SetId("parameters") @@ -126,7 +126,7 @@ func ReadParameters(d *schema.ResourceData, meta interface{}) error { d.SetId("") return nil } else if err != nil { - log.Printf("[DEBUG] error occured during read: %v", err.Error()) + log.Printf("[DEBUG] error occurred during read: %v", err.Error()) return err } d.SetId("parameters") diff --git a/pkg/datasources/parameters_acceptance_test.go b/pkg/datasources/parameters_acceptance_test.go index ee485aa93d..b4b68df8b4 100644 --- a/pkg/datasources/parameters_acceptance_test.go +++ b/pkg/datasources/parameters_acceptance_test.go @@ -14,7 +14,7 @@ func TestAcc_Parameters(t *testing.T) { { Config: parameters(), Check: resource.ComposeTestCheckFunc( - resource.TestCheckResourceAttrSet("data.snowflake_parameters.p", "warehouses.#"), + resource.TestCheckResourceAttrSet("data.snowflake_parameters.p", "parameters.#"), resource.TestCheckResourceAttrSet("data.snowflake_parameters.p", "parameters.0.key"), resource.TestCheckResourceAttrSet("data.snowflake_parameters.p", "parameters.0.value"), ), diff --git a/pkg/resources/account_parameter.go b/pkg/resources/account_parameter.go index 9c575886c1..9cdf305493 100644 --- a/pkg/resources/account_parameter.go +++ b/pkg/resources/account_parameter.go @@ -56,21 +56,24 @@ func CreateAccountParameter(d *schema.ResourceData, meta interface{}) error { // add quotes to value if it is a string typeString := reflect.TypeOf("") if reflect.TypeOf(parameterDefault.DefaultValue) == typeString { - value = fmt.Sprintf("'%s'", snowflake.EscapeString(value)) + value = fmt.Sprintf("'%s'", snowflake.EscapeString(value)) } builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeAccount, db) err := builder.SetParameter() if err != nil { - return fmt.Errorf("error creating account parameter err = %v", err) + return fmt.Errorf("error creating account parameter err = %w", err) } d.SetId(key) p, err := snowflake.ShowParameter(db, key, snowflake.ParameterTypeAccount) if err != nil { - return fmt.Errorf("error reading account parameter err = %v", err) + return fmt.Errorf("error reading account parameter err = %w", err) + } + err = d.Set("value", p.Value.String) + if err != nil { + return fmt.Errorf("error setting account parameter value err = %w", err) } - d.Set("value", p.Value.String) return nil } @@ -80,9 +83,12 @@ func ReadAccountParameter(d *schema.ResourceData, meta interface{}) error { key := d.Id() p, err := snowflake.ShowParameter(db, key, snowflake.ParameterTypeAccount) if err != nil { - return fmt.Errorf("error reading account parameter err = %v", err) + return fmt.Errorf("error reading account parameter err = %w", err) + } + err = d.Set("value", p.Value.String) + if err != nil { + return fmt.Errorf("error setting account parameter value err = %w", err) } - d.Set("value", p.Value.String) return nil } diff --git a/pkg/resources/object_parameter.go b/pkg/resources/object_parameter.go index 68d5184e06..853b6dc24b 100644 --- a/pkg/resources/object_parameter.go +++ b/pkg/resources/object_parameter.go @@ -91,7 +91,10 @@ func CreateObjectParameter(d *schema.ResourceData, meta interface{}) error { if err != nil { return fmt.Errorf("error reading object parameter err = %v", err) } - d.Set("value", p.Value.String) + err = d.Set("value", p.Value.String) + if err != nil { + return err + } return nil } diff --git a/pkg/resources/object_parameter_acceptance_test.go b/pkg/resources/object_parameter_acceptance_test.go index ff3eb811be..0f9ca92642 100644 --- a/pkg/resources/object_parameter_acceptance_test.go +++ b/pkg/resources/object_parameter_acceptance_test.go @@ -38,5 +38,5 @@ resource "snowflake_object_parameter" "p" { object_name = snowflake_database.d.name } ` - return fmt.Sprintf(s,prefix, key, value) + return fmt.Sprintf(s, prefix, key, value) } diff --git a/pkg/snowflake/parameters.go b/pkg/snowflake/parameters.go index 93e143714b..9b98923af1 100644 --- a/pkg/snowflake/parameters.go +++ b/pkg/snowflake/parameters.go @@ -2,6 +2,7 @@ package snowflake import ( "database/sql" + "errors" "fmt" "log" "reflect" @@ -113,7 +114,7 @@ func ParameterDefaults() map[string]Parameter { TypeSet: []ParameterType{ParameterTypeAccount, ParameterTypeObject}, DefaultValue: "none", Validate: func(value string) (err error) { - if len(value) <= 0 { + if len(value) == 0 { return fmt.Errorf("NETWORK_POLICY cannot be empty") } _, errs := ValidateIdentifier(value, []string{}) @@ -633,7 +634,7 @@ func ParameterDefaults() map[string]Parameter { } } -// GetParameterObjectTypeSetAsStrings returns a slice of all object types that can have parameters +// GetParameterObjectTypeSetAsStrings returns a slice of all object types that can have parameters. func GetParameterObjectTypeSetAsStrings() []string { objectTypeSet := []ObjectType{ ObjectTypeDatabase, @@ -654,7 +655,7 @@ func GetParameterObjectTypeSetAsStrings() []string { return result } -// GetParameters returns a map of parameters that match the given type (e.g. Account, Session, Object) +// GetParameters returns a map of parameters that match the given type (e.g. Account, Session, Object). func GetParameters(t ParameterType) map[string]Parameter { parameters := ParameterDefaults() keys := maps.Keys(parameters) @@ -667,7 +668,7 @@ func GetParameters(t ParameterType) map[string]Parameter { return parameters } -// GetParameter returns a parameter by key +// GetParameter returns a parameter by key. func GetParameter(key string) Parameter { return ParameterDefaults()[key] } @@ -723,7 +724,7 @@ type snowflakeParameter struct { Key sql.NullString `db:"key"` Value sql.NullString `db:"value"` Default sql.NullString `db:"default"` - Level sql.NullString`db:"level"` + Level sql.NullString `db:"level"` Description sql.NullString `db:"description"` PType sql.NullString `db:"type"` } @@ -743,7 +744,7 @@ func ShowParameter(db *sql.DB, key string, parameterType ParameterType) (*snowfl defer rows.Close() params := []snowflakeParameter{} if err := sqlx.StructScan(rows, ¶ms); err != nil { - if err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return nil, nil } return nil, fmt.Errorf("unable to scan row for %s err = %w", stmt, err) @@ -763,7 +764,7 @@ func ShowObjectParameter(db *sql.DB, key string, objectType ObjectType, objectNa defer rows.Close() params := []snowflakeParameter{} if err := sqlx.StructScan(rows, ¶ms); err != nil { - if err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return nil, nil } return nil, fmt.Errorf("unable to scan row for %s err = %w", stmt, err) @@ -775,11 +776,11 @@ func ShowObjectParameter(db *sql.DB, key string, objectType ObjectType, objectNa func ListParameters(db *sql.DB, parameterType ParameterType, pattern string) ([]snowflakeParameter, error) { var stmt string - if parameterType == ParameterTypeAccount || parameterType == ParameterTypeSession{ + if parameterType == ParameterTypeAccount || parameterType == ParameterTypeSession { if pattern != "" { stmt = fmt.Sprintf("SHOW PARAMETERS LIKE '%s' IN %v", pattern, parameterType) } else { - stmt = fmt.Sprintf("SHOW PARAMETERS IN %v",parameterType) + stmt = fmt.Sprintf("SHOW PARAMETERS IN %v", parameterType) } } else { return nil, fmt.Errorf("unsupported parameter type %s", parameterType) @@ -792,7 +793,7 @@ func ListParameters(db *sql.DB, parameterType ParameterType, pattern string) ([] defer rows.Close() params := []snowflakeParameter{} if err := sqlx.StructScan(rows, ¶ms); err != nil { - if err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return nil, nil } return nil, fmt.Errorf("unable to scan row for %s err = %w", stmt, err) diff --git a/pkg/snowflake/snowflake.go b/pkg/snowflake/snowflake.go index 8d4143c2d0..ff746df78a 100644 --- a/pkg/snowflake/snowflake.go +++ b/pkg/snowflake/snowflake.go @@ -4,16 +4,16 @@ package snowflake type ObjectType string const ( - ObjectTypeDatabase ObjectType = "DATABASE" - ObjectTypeSchema ObjectType = "SCHEMA" - ObjectTypeTable ObjectType = "TABLE" + ObjectTypeDatabase ObjectType = "DATABASE" + ObjectTypeSchema ObjectType = "SCHEMA" + ObjectTypeTable ObjectType = "TABLE" ObjectTypeReplicationGroup ObjectType = "REPLICATION GROUP" - ObjectTypeFailoverGroup ObjectType = "FAILOVER GROUP" - ObjectTypeWarehouse ObjectType = "WAREHOUSE" - ObjectTypePipe ObjectType = "PIPE" - ObjectTypeUser ObjectType = "USER" - ObjectTypeShare ObjectType = "SHARE" - ObjectTypeTask ObjectType = "TASK" + ObjectTypeFailoverGroup ObjectType = "FAILOVER GROUP" + ObjectTypeWarehouse ObjectType = "WAREHOUSE" + ObjectTypePipe ObjectType = "PIPE" + ObjectTypeUser ObjectType = "USER" + ObjectTypeShare ObjectType = "SHARE" + ObjectTypeTask ObjectType = "TASK" ) func (o ObjectType) String() string { diff --git a/pkg/snowflake/validation.go b/pkg/snowflake/validation.go index 0a104726e1..f26c748878 100644 --- a/pkg/snowflake/validation.go +++ b/pkg/snowflake/validation.go @@ -55,9 +55,9 @@ func isInitialIdentifierRune(r rune) bool { type DateFormat string const ( - DateFormatISO DateFormat = "DateFormatISO" + DateFormatISO DateFormat = "DateFormatISO" DateFormatOther DateFormat = "DateFormatOther" - DateFormatAny DateFormat = "DateFormatAny" + DateFormatAny DateFormat = "DateFormatAny" ) func getValidDateFormats(f DateFormat, includeAuto bool) []string { @@ -68,7 +68,9 @@ func getValidDateFormats(f DateFormat, includeAuto bool) []string { "DD-MON-YYYY", "MM/DD/YYYY", } - AnyDateFormats := append(ISODateFormats, OtherDateFormats...) + AnyDateFormats := make([]string, 0, len(ISODateFormats)+len(OtherDateFormats)) + AnyDateFormats = append(AnyDateFormats, ISODateFormats...) + AnyDateFormats = append(AnyDateFormats, OtherDateFormats...) var formats []string switch f { @@ -80,7 +82,7 @@ func getValidDateFormats(f DateFormat, includeAuto bool) []string { formats = AnyDateFormats } if includeAuto { - formats= append(formats, "auto") + formats = append(formats, "auto") } return formats } @@ -90,7 +92,7 @@ type TimeFormat string const ( TimeFormatISO TimeFormat = "TimeFormatISO" TimeFormatRFC TimeFormat = "TimeFormatRFC" - TimeFormatAny TimeFormat = "TimeFormatAny" + TimeFormatAny TimeFormat = "TimeFormatAny" ) func getValidTimeFormats(f TimeFormat, includeAuto bool) []string { @@ -105,7 +107,9 @@ func getValidTimeFormats(f TimeFormat, includeAuto bool) []string { "HH12:MI:SS AM", "HH12:MI AM", } - AnyTimeFormats := append(ISOTimeFormats, RFCTimeFormats...) + AnyTimeFormats := make([]string, 0, len(ISOTimeFormats)+len(RFCTimeFormats)) + AnyTimeFormats = append(AnyTimeFormats, ISOTimeFormats...) + AnyTimeFormats = append(AnyTimeFormats, RFCTimeFormats...) var formats []string switch f { @@ -115,9 +119,9 @@ func getValidTimeFormats(f TimeFormat, includeAuto bool) []string { formats = RFCTimeFormats case TimeFormatAny: formats = AnyTimeFormats - } + } if includeAuto { - formats= append(formats, "auto") + formats = append(formats, "auto") } return formats } @@ -125,14 +129,13 @@ func getValidTimeFormats(f TimeFormat, includeAuto bool) []string { type TimeStampFormat string const ( - TimeStampFormatISO TimeStampFormat = "TimeStampFormatISO" - TimeStampFormatRFC TimeStampFormat = "TimeStampFormatRFC" + TimeStampFormatISO TimeStampFormat = "TimeStampFormatISO" + TimeStampFormatRFC TimeStampFormat = "TimeStampFormatRFC" TimeStampFormatOther TimeStampFormat = "TimeStampFormatOther" - TimeStampFormatAny TimeStampFormat = "TimeStampFormatAny" + TimeStampFormatAny TimeStampFormat = "TimeStampFormatAny" ) func getValidTimeStampFormats(f TimeStampFormat, includeAuto bool) []string { - ISOTimeStampFormats := []string{ "YYYY-MM-DD\"T\"HH24:MI:SS.FFTZH:TZM", "YYYY-MM-DD HH24:MI:SS.FFTZH:TZM", @@ -169,7 +172,11 @@ func getValidTimeStampFormats(f TimeStampFormat, includeAuto bool) []string { "MM/DD/YYYY HH24:MI:SS", "DY MON DD HH24:MI:SS TZHTZM YYYY", } - AnyTimeStampFormats := append(ISOTimeStampFormats, append(RFCTimeStampFormats, OtherTimeStampFormats...)...) + AnyTimeStampFormats := make([]string, 0, len(ISOTimeStampFormats)+len(RFCTimeStampFormats)+len(OtherTimeStampFormats)) + AnyTimeStampFormats = append(AnyTimeStampFormats, ISOTimeStampFormats...) + AnyTimeStampFormats = append(AnyTimeStampFormats, RFCTimeStampFormats...) + AnyTimeStampFormats = append(AnyTimeStampFormats, OtherTimeStampFormats...) + var formats []string switch f { case TimeStampFormatISO: @@ -187,4 +194,3 @@ func getValidTimeStampFormats(f TimeStampFormat, includeAuto bool) []string { } return formats } - From 5e1dfbbeb77e73e83c7cff02609b01bfd177d2a4 Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Wed, 21 Dec 2022 13:48:57 -0800 Subject: [PATCH 4/7] fix linting issues --- pkg/resources/account_parameter.go | 4 ++-- pkg/resources/object_parameter.go | 9 ++++++--- pkg/resources/session_parameter.go | 10 ++++++++-- pkg/snowflake/parameters.go | 2 +- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/pkg/resources/account_parameter.go b/pkg/resources/account_parameter.go index 9cdf305493..c080b03b6a 100644 --- a/pkg/resources/account_parameter.go +++ b/pkg/resources/account_parameter.go @@ -114,11 +114,11 @@ func DeleteAccountParameter(d *schema.ResourceData, meta interface{}) error { builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeAccount, db) err := builder.SetParameter() if err != nil { - return fmt.Errorf("error creating account parameter err = %v", err) + return fmt.Errorf("error creating account parameter err = %w", err) } _, err = snowflake.ShowParameter(db, key, snowflake.ParameterTypeAccount) if err != nil { - return fmt.Errorf("error reading account parameter err = %v", err) + return fmt.Errorf("error reading account parameter err = %w", err) } d.SetId("") diff --git a/pkg/resources/object_parameter.go b/pkg/resources/object_parameter.go index 853b6dc24b..b1c12f893b 100644 --- a/pkg/resources/object_parameter.go +++ b/pkg/resources/object_parameter.go @@ -83,13 +83,13 @@ func CreateObjectParameter(d *schema.ResourceData, meta interface{}) error { builder.WithObjectType(objectType) err := builder.SetParameter() if err != nil { - return fmt.Errorf("error creating object parameter err = %v", err) + return fmt.Errorf("error creating object parameter err = %w", err) } id := fmt.Sprintf("%v❄️%v❄️%v", key, objectType, objectName) d.SetId(id) p, err := snowflake.ShowObjectParameter(db, key, objectType, objectName) if err != nil { - return fmt.Errorf("error reading object parameter err = %v", err) + return fmt.Errorf("error reading object parameter err = %w", err) } err = d.Set("value", p.Value.String) if err != nil { @@ -113,7 +113,10 @@ func ReadObjectParameter(d *schema.ResourceData, meta interface{}) error { if err != nil { return fmt.Errorf("error reading object parameter err = %v", err) } - d.Set("value", p.Value.String) + err = d.Set("value", p.Value.String) + if err != nil { + return err + } return nil } diff --git a/pkg/resources/session_parameter.go b/pkg/resources/session_parameter.go index 5ee1233f19..9b2fdad50a 100644 --- a/pkg/resources/session_parameter.go +++ b/pkg/resources/session_parameter.go @@ -70,7 +70,10 @@ func CreateSessionParameter(d *schema.ResourceData, meta interface{}) error { if err != nil { return fmt.Errorf("error reading session parameter err = %v", err) } - d.Set("value", p.Value.String) + err = d.Set("value", p.Value.String) + if err != nil { + return fmt.Errorf("error setting session parameter err = %v", err) + } return nil } @@ -82,7 +85,10 @@ func ReadSessionParameter(d *schema.ResourceData, meta interface{}) error { if err != nil { return fmt.Errorf("error reading session parameter err = %v", err) } - d.Set("value", p.Value.String) + err = d.Set("value", p.Value.String) + if err != nil { + return fmt.Errorf("error setting session parameter err = %v", err) + } return nil } diff --git a/pkg/snowflake/parameters.go b/pkg/snowflake/parameters.go index 9b98923af1..a6b946c706 100644 --- a/pkg/snowflake/parameters.go +++ b/pkg/snowflake/parameters.go @@ -816,7 +816,7 @@ func ListObjectParameters(db *sql.DB, objectType ObjectType, objectName, pattern defer rows.Close() params := []snowflakeParameter{} if err := sqlx.StructScan(rows, ¶ms); err != nil { - if err == sql.ErrNoRows { + if errors.Is(err, sql.ErrNoRows) { return nil, nil } return nil, fmt.Errorf("unable to scan row for %s err = %w", stmt, err) From f20106be92e4fbe6367b4637a7d73fa8233a21aa Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Wed, 21 Dec 2022 13:57:04 -0800 Subject: [PATCH 5/7] fix linting issues --- pkg/resources/object_parameter.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/resources/object_parameter.go b/pkg/resources/object_parameter.go index b1c12f893b..bbc1dd0a16 100644 --- a/pkg/resources/object_parameter.go +++ b/pkg/resources/object_parameter.go @@ -111,7 +111,7 @@ func ReadObjectParameter(d *schema.ResourceData, meta interface{}) error { objectName := parts[2] p, err := snowflake.ShowObjectParameter(db, key, objectType, objectName) if err != nil { - return fmt.Errorf("error reading object parameter err = %v", err) + return fmt.Errorf("error reading object parameter err = %w", err) } err = d.Set("value", p.Value.String) if err != nil { @@ -146,11 +146,11 @@ func DeleteObjectParameter(d *schema.ResourceData, meta interface{}) error { builder.WithObjectType(objectType) err := builder.SetParameter() if err != nil { - return fmt.Errorf("error restoring default for object parameter err = %v", err) + return fmt.Errorf("error restoring default for object parameter err = %w", err) } _, err = snowflake.ShowObjectParameter(db, key, objectType, objectName) if err != nil { - return fmt.Errorf("error reading object parameter err = %v", err) + return fmt.Errorf("error reading object parameter err = %w", err) } d.SetId("") From f8672a54251d54ae4632766908d18d9ce3c6113d Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Wed, 21 Dec 2022 14:10:11 -0800 Subject: [PATCH 6/7] fix linting issues --- pkg/resources/session_parameter.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkg/resources/session_parameter.go b/pkg/resources/session_parameter.go index 9b2fdad50a..d7dbdc9dca 100644 --- a/pkg/resources/session_parameter.go +++ b/pkg/resources/session_parameter.go @@ -62,17 +62,17 @@ func CreateSessionParameter(d *schema.ResourceData, meta interface{}) error { builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeSession, db) err := builder.SetParameter() if err != nil { - return fmt.Errorf("error creating session parameter err = %v", err) + return fmt.Errorf("error creating session parameter err = %w", err) } d.SetId(key) p, err := snowflake.ShowParameter(db, key, snowflake.ParameterTypeSession) if err != nil { - return fmt.Errorf("error reading session parameter err = %v", err) + return fmt.Errorf("error reading session parameter err = %w", err) } err = d.Set("value", p.Value.String) if err != nil { - return fmt.Errorf("error setting session parameter err = %v", err) + return fmt.Errorf("error setting session parameter err = %w", err) } return nil } @@ -83,11 +83,11 @@ func ReadSessionParameter(d *schema.ResourceData, meta interface{}) error { key := d.Id() p, err := snowflake.ShowParameter(db, key, snowflake.ParameterTypeSession) if err != nil { - return fmt.Errorf("error reading session parameter err = %v", err) + return fmt.Errorf("error reading session parameter err = %w", err) } err = d.Set("value", p.Value.String) if err != nil { - return fmt.Errorf("error setting session parameter err = %v", err) + return fmt.Errorf("error setting session parameter err = %w", err) } return nil } @@ -114,11 +114,11 @@ func DeleteSessionParameter(d *schema.ResourceData, meta interface{}) error { builder := snowflake.NewParameter(key, value, snowflake.ParameterTypeSession, db) err := builder.SetParameter() if err != nil { - return fmt.Errorf("error creating account parameter err = %v", err) + return fmt.Errorf("error creating account parameter err = %w", err) } _, err = snowflake.ShowParameter(db, key, snowflake.ParameterTypeSession) if err != nil { - return fmt.Errorf("error reading a parameter err = %v", err) + return fmt.Errorf("error reading a parameter err = %w", err) } d.SetId("") From bbe5dbba749d5d3643b6353e5ebaaf18121c8d02 Mon Sep 17 00:00:00 2001 From: Scott Winkler Date: Wed, 21 Dec 2022 14:38:12 -0800 Subject: [PATCH 7/7] add double quotes --- pkg/snowflake/parameters.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/snowflake/parameters.go b/pkg/snowflake/parameters.go index a6b946c706..487f0a4ed8 100644 --- a/pkg/snowflake/parameters.go +++ b/pkg/snowflake/parameters.go @@ -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.objectName, v.key, v.value) _, err := v.db.Exec(stmt) if err != nil { return err @@ -756,7 +756,7 @@ func ShowParameter(db *sql.DB, key string, parameterType ParameterType) (*snowfl 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) + stmt := fmt.Sprintf("SHOW PARAMETERS LIKE '%s' IN %s \"%s\"", key, objectType.String(), objectName) rows, err := db.Query(stmt) if err != nil { return nil, err @@ -804,7 +804,7 @@ func ListParameters(db *sql.DB, parameterType ParameterType, pattern string) ([] func ListObjectParameters(db *sql.DB, objectType ObjectType, objectName, pattern string) ([]snowflakeParameter, 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(), objectName) } else { stmt = fmt.Sprintf("SHOW PARAMETERS IN %s %s", objectType.String(), objectName) }