Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add refresh_mode and initialize to dynamic tables #2437

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions docs/resources/dynamic_table.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
page_title: "snowflake_dynamic_table Resource - terraform-provider-snowflake"
subcategory: ""
description: |-

---

# snowflake_dynamic_table (Resource)
Expand Down Expand Up @@ -43,6 +43,8 @@ resource "snowflake_dynamic_table" "dt" {

- `comment` (String) Specifies a comment for the dynamic table.
- `or_replace` (Boolean) Specifies whether to replace the dynamic table if it already exists.
- `refresh_mode` (String) INCREMENTAL if the dynamic table will use incremental refreshes, or FULL if it will recompute the whole table on every refresh. Specify AUTO to let Snowflake decide. The default is AUTO.
- `initialize` (String) Specifies the behavior of the initial refresh of the dynamic table. This property cannot be altered after you create the dynamic table. Specify ON_CREATE to initialize the dynamic table immeidately, or ON_SCHEDULE to have it initialize at the next tick after creation. The default os ON_CREATE.

### Read-Only

Expand All @@ -55,7 +57,6 @@ resource "snowflake_dynamic_table" "dt" {
- `is_replica` (Boolean) TRUE if the dynamic table is a replica. else FALSE.
- `last_suspended_on` (String) Timestamp of last suspension.
- `owner` (String) Role that owns the dynamic table.
- `refresh_mode` (String) INCREMENTAL if the dynamic table will use incremental refreshes, or FULL if it will recompute the whole table on every refresh.
- `refresh_mode_reason` (String) Explanation for why FULL refresh mode was chosen. NULL if refresh mode is not FULL.
- `rows` (Number) Number of rows in the table.
- `scheduling_state` (String) Displays RUNNING for dynamic tables that are actively scheduling refreshes and SUSPENDED for suspended dynamic tables.
Expand Down
58 changes: 50 additions & 8 deletions pkg/resources/dynamic_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ import (
"context"
"database/sql"
"log"
"regexp"
"strings"
"time"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/helpers"
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk"
"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 refreshModePattern = regexp.MustCompile(`refresh_mode = '(\w+)'`)

var dynamicTableSchema = map[string]*schema.Schema{
"or_replace": {
Type: schema.TypeBool,
Expand Down Expand Up @@ -74,6 +79,27 @@ var dynamicTableSchema = map[string]*schema.Schema{
Optional: true,
Description: "Specifies a comment for the dynamic table.",
},
"refresh_mode": {
sfc-gh-asawicki marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeString,
Optional: true,
Default: sdk.DynamicTableRefreshModeAuto,
Description: "INCREMENTAL to use incremental refreshes, FULL to recompute the whole table on every refresh, or AUTO to let Snowflake decide.",
ValidateFunc: validation.StringInSlice(sdk.AsStringList(sdk.AllDynamicRefreshModes), true),
ForceNew: true,
},
"initialize": {
sfc-gh-asawicki marked this conversation as resolved.
Show resolved Hide resolved
Type: schema.TypeString,
Optional: true,
Default: sdk.DynamicTableInitializeOnCreate,
Description: "Initialize trigger for the dynamic table. Can only be set on creation.",
ValidateFunc: validation.StringInSlice(sdk.AsStringList(sdk.AllDynamicTableInitializes), true),
ForceNew: true,
},
"created_on": {
Type: schema.TypeString,
Description: "Time when this dynamic table was created.",
Computed: true,
},
"cluster_by": {
Type: schema.TypeString,
Description: "The clustering key for the dynamic table.",
Expand All @@ -94,11 +120,6 @@ var dynamicTableSchema = map[string]*schema.Schema{
Description: "Role that owns the dynamic table.",
Computed: true,
},
"refresh_mode": {
Type: schema.TypeString,
Description: "INCREMENTAL if the dynamic table will use incremental refreshes, or FULL if it will recompute the whole table on every refresh.",
Computed: true,
},
"refresh_mode_reason": {
Type: schema.TypeString,
Description: "Explanation for why FULL refresh mode was chosen. NULL if refresh mode is not FULL.",
Expand Down Expand Up @@ -199,6 +220,24 @@ func ReadDynamicTable(d *schema.ResourceData, meta interface{}) error {
return err
}
}
if strings.Contains(dynamicTable.Text, "initialize = 'ON_CREATE'") {
sfc-gh-asawicki marked this conversation as resolved.
Show resolved Hide resolved
if err := d.Set("initialize", "ON_CREATE"); err != nil {
return err
}
} else if strings.Contains(dynamicTable.Text, "initialize = 'ON_SCHEDULE'") {
if err := d.Set("initialize", "ON_SCHEDULE"); err != nil {
return err
}
}
m := refreshModePattern.FindStringSubmatch(dynamicTable.Text)
if len(m) > 1 {
if err := d.Set("refresh_mode", m[1]); err != nil {
return err
}
}
if err := d.Set("created_on", dynamicTable.CreatedOn.Format(time.RFC3339)); err != nil {
return err
}
if err := d.Set("cluster_by", dynamicTable.ClusterBy); err != nil {
return err
}
Expand All @@ -211,9 +250,6 @@ func ReadDynamicTable(d *schema.ResourceData, meta interface{}) error {
if err := d.Set("owner", dynamicTable.Owner); err != nil {
return err
}
if err := d.Set("refresh_mode", string(dynamicTable.RefreshMode)); err != nil {
return err
}
if err := d.Set("refresh_mode_reason", dynamicTable.RefreshModeReason); err != nil {
return err
}
Expand Down Expand Up @@ -288,6 +324,12 @@ func CreateDynamicTable(d *schema.ResourceData, meta interface{}) error {
if v, ok := d.GetOk("or_replace"); ok && v.(bool) {
request.WithOrReplace(true)
}
if v, ok := d.GetOk("refresh_mode"); ok {
request.WithRefreshMode(sdk.DynamicTableRefreshMode(v.(string)))
}
if v, ok := d.GetOk("initialize"); ok {
request.WithInitialize(sdk.DynamicTableInitialize(v.(string)))
}
if err := client.DynamicTables.Create(context.Background(), request); err != nil {
return err
}
Expand Down
57 changes: 56 additions & 1 deletion pkg/resources/dynamic_table_acceptance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ func TestAcc_DynamicTable_basic(t *testing.T) {
variableSet2["warehouse"] = config.StringVariable(acc.TestWarehouseName2)
variableSet2["comment"] = config.StringVariable("Terraform acceptance test - updated")

variableSet3 := m()
variableSet3["initialize"] = config.StringVariable(string(sdk.DynamicTableInitializeOnSchedule))

variableSet4 := m()
variableSet4["initialize"] = config.StringVariable(string(sdk.DynamicTableInitializeOnSchedule)) // keep the same setting from set 3
variableSet4["refresh_mode"] = config.StringVariable(string(sdk.DynamicTableRefreshModeFull))

// used to check whether a dynamic table was replaced
var createdOn string

resource.Test(t, resource.TestCase{
ProtoV6ProviderFactories: acc.TestAccProtoV6ProviderFactories,
PreCheck: func() { acc.TestAccPreCheck(t) },
Expand All @@ -53,6 +63,8 @@ func TestAcc_DynamicTable_basic(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "database", acc.TestDatabaseName),
resource.TestCheckResourceAttr(resourceName, "schema", acc.TestSchemaName),
resource.TestCheckResourceAttr(resourceName, "warehouse", acc.TestWarehouseName),
resource.TestCheckResourceAttr(resourceName, "initialize", string(sdk.DynamicTableInitializeOnCreate)),
resource.TestCheckResourceAttr(resourceName, "refresh_mode", string(sdk.DynamicTableRefreshModeAuto)),
resource.TestCheckResourceAttr(resourceName, "target_lag.#", "1"),
resource.TestCheckResourceAttr(resourceName, "target_lag.0.maximum_duration", "2 minutes"),
resource.TestCheckResourceAttr(resourceName, "query", fmt.Sprintf("select \"id\" from \"%v\".\"%v\".\"%v\"", acc.TestDatabaseName, acc.TestSchemaName, tableName)),
Expand All @@ -65,14 +77,18 @@ func TestAcc_DynamicTable_basic(t *testing.T) {
resource.TestCheckResourceAttrSet(resourceName, "rows"),
resource.TestCheckResourceAttrSet(resourceName, "bytes"),
resource.TestCheckResourceAttrSet(resourceName, "owner"),
resource.TestCheckResourceAttrSet(resourceName, "refresh_mode"),
// - not used at this time
// resource.TestCheckResourceAttrSet(resourceName, "automatic_clustering"),
resource.TestCheckResourceAttrSet(resourceName, "scheduling_state"),
resource.TestCheckResourceAttrSet(resourceName, "last_suspended_on"),
resource.TestCheckResourceAttrSet(resourceName, "is_clone"),
resource.TestCheckResourceAttrSet(resourceName, "is_replica"),
resource.TestCheckResourceAttrSet(resourceName, "data_timestamp"),

resource.TestCheckResourceAttrWith(resourceName, "created_on", func(value string) error {
createdOn = value
return nil
}),
),
},
// test target lag to downstream and change comment
Expand All @@ -88,6 +104,45 @@ func TestAcc_DynamicTable_basic(t *testing.T) {
resource.TestCheckResourceAttr(resourceName, "target_lag.#", "1"),
resource.TestCheckResourceAttr(resourceName, "target_lag.0.downstream", "true"),
resource.TestCheckResourceAttr(resourceName, "comment", "Terraform acceptance test - updated"),

resource.TestCheckResourceAttrWith(resourceName, "created_on", func(value string) error {
if value != createdOn {
return fmt.Errorf("created_on changed from %v to %v", createdOn, value)
}
return nil
}),
),
},
// test changing initialize setting
{
ConfigDirectory: config.TestStepDirectory(),
ConfigVariables: variableSet3,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "initialize", string(sdk.DynamicTableInitializeOnSchedule)),

resource.TestCheckResourceAttrWith(resourceName, "created_on", func(value string) error {
if value == createdOn {
return fmt.Errorf("expected created_on to change but was not changed")
}
createdOn = value
return nil
}),
),
},
// test changing refresh_mode setting
{
ConfigDirectory: config.TestStepDirectory(),
ConfigVariables: variableSet4,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(resourceName, "initialize", string(sdk.DynamicTableInitializeOnSchedule)),
resource.TestCheckResourceAttr(resourceName, "refresh_mode", string(sdk.DynamicTableRefreshModeFull)),

resource.TestCheckResourceAttrWith(resourceName, "created_on", func(value string) error {
if value == createdOn {
return fmt.Errorf("expected created_on to change but was not changed")
}
return nil
}),
),
},
// test import
Expand Down
7 changes: 4 additions & 3 deletions pkg/resources/testdata/TestAcc_DynamicTable_basic/3/test.tf
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ resource "snowflake_dynamic_table" "dt" {
target_lag {
downstream = true
}
warehouse = var.warehouse
query = var.query
comment = var.comment
warehouse = var.warehouse
query = var.query
comment = var.comment
initialize = var.initialize
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ variable "comment" {
type = string
}

variable "initialize" {
type = string
}

variable "table_name" {
type = string
}
27 changes: 27 additions & 0 deletions pkg/resources/testdata/TestAcc_DynamicTable_basic/4/test.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@


resource "snowflake_table" "t" {
database = var.database
schema = var.schema
name = var.table_name
change_tracking = true
column {
name = "id"
type = "NUMBER(38,0)"
}
}

resource "snowflake_dynamic_table" "dt" {
depends_on = [snowflake_table.t]
name = var.name
database = var.database
schema = var.schema
target_lag {
downstream = true
}
warehouse = var.warehouse
query = var.query
comment = var.comment
refresh_mode = var.refresh_mode
initialize = var.initialize
}
37 changes: 37 additions & 0 deletions pkg/resources/testdata/TestAcc_DynamicTable_basic/4/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@


variable "name" {
type = string
}

variable "database" {
type = string
}

variable "schema" {
type = string
}

variable "warehouse" {
type = string
}

variable "query" {
type = string
}

variable "comment" {
type = string
}

variable "refresh_mode" {
type = string
}

variable "initialize" {
type = string
}

variable "table_name" {
type = string
}
25 changes: 25 additions & 0 deletions pkg/resources/testdata/TestAcc_DynamicTable_basic/5/test.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@


resource "snowflake_table" "t" {
database = var.database
schema = var.schema
name = var.table_name
change_tracking = true
column {
name = "id"
type = "NUMBER(38,0)"
}
}

resource "snowflake_dynamic_table" "dt" {
depends_on = [snowflake_table.t]
name = var.name
database = var.database
schema = var.schema
target_lag {
downstream = true
}
warehouse = var.warehouse
query = var.query
comment = var.comment
}
29 changes: 29 additions & 0 deletions pkg/resources/testdata/TestAcc_DynamicTable_basic/5/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@


variable "name" {
type = string
}

variable "database" {
type = string
}

variable "schema" {
type = string
}

variable "warehouse" {
type = string
}

variable "query" {
type = string
}

variable "comment" {
type = string
}

variable "table_name" {
type = string
}
Loading
Loading