diff --git a/docs/resources/stream.md b/docs/resources/stream.md index 6af90b85ab..7c93417b9c 100644 --- a/docs/resources/stream.md +++ b/docs/resources/stream.md @@ -42,6 +42,7 @@ resource "snowflake_stream" "stream" { - `append_only` (Boolean) Type of the stream that will be created. - `comment` (String) Specifies a comment for the stream. - `insert_only` (Boolean) Create an insert only stream type. +- `on_stage` (String) Name of the stage the stream will monitor. - `on_table` (String) Name of the table the stream will monitor. - `on_view` (String) Name of the view the stream will monitor. - `show_initial_rows` (Boolean) Specifies whether to return all existing rows in the source table as row inserts the first time the stream is consumed. diff --git a/pkg/resources/account_grant.go b/pkg/resources/account_grant.go index fc99b0a255..5dec26c36e 100644 --- a/pkg/resources/account_grant.go +++ b/pkg/resources/account_grant.go @@ -144,10 +144,18 @@ func (v *AccountGrantID) String() string { func ParseAccountGrantID(s string) (*AccountGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &AccountGrantID{ Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/account_grant_test.go b/pkg/resources/account_grant_test.go index 4368afbf1c..9d655b2e94 100644 --- a/pkg/resources/account_grant_test.go +++ b/pkg/resources/account_grant_test.go @@ -158,3 +158,13 @@ func TestParseAccountGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseAccountGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseAccountGrantID("ACCOUNT|||MONITOR EXECUTION|true") + r.NoError(err) + r.Equal("MONITOR EXECUTION", grantID.Privilege) + r.Equal(true, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/database_grant.go b/pkg/resources/database_grant.go index 39db910f83..326ec4c679 100644 --- a/pkg/resources/database_grant.go +++ b/pkg/resources/database_grant.go @@ -240,12 +240,20 @@ func (v *DatabaseGrantID) String() string { func ParseDatabaseGrantID(s string) (*DatabaseGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &DatabaseGrantID{ DatabaseName: idParts[0], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), + Roles: roles, Shares: []string{}, - WithGrantOption: idParts[5] == "true", + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/database_grant_test.go b/pkg/resources/database_grant_test.go index 6486441f83..1f4e0ac3a2 100644 --- a/pkg/resources/database_grant_test.go +++ b/pkg/resources/database_grant_test.go @@ -132,3 +132,14 @@ func TestParseDatabaseGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseDatabaseGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseDatabaseGrantID("test-database|||USAGE|true") + r.NoError(err) + r.Equal("test-database", grantID.DatabaseName) + r.Equal("USAGE", grantID.Privilege) + r.Equal(true, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/external_table_grant.go b/pkg/resources/external_table_grant.go index cbabdc0af6..d32e919032 100644 --- a/pkg/resources/external_table_grant.go +++ b/pkg/resources/external_table_grant.go @@ -309,14 +309,22 @@ func (v *ExternalTableGrantID) String() string { func ParseExternalTableGrantID(s string) (*ExternalTableGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &ExternalTableGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), + Roles: roles, Shares: []string{}, - WithGrantOption: idParts[5] == "true", + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/external_table_grant_test.go b/pkg/resources/external_table_grant_test.go index ec8423b9db..ae4cd94412 100644 --- a/pkg/resources/external_table_grant_test.go +++ b/pkg/resources/external_table_grant_test.go @@ -231,3 +231,16 @@ func TestParseExternalTableGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseExternalTableGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseExternalTableGrantID("test-db|PUBLIC|test-external-table|SELECT|true") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("PUBLIC", grantID.SchemaName) + r.Equal("test-external-table", grantID.ObjectName) + r.Equal("SELECT", grantID.Privilege) + r.Equal(true, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/file_format_grant.go b/pkg/resources/file_format_grant.go index 0cbc8cbb8c..f13d159ed3 100644 --- a/pkg/resources/file_format_grant.go +++ b/pkg/resources/file_format_grant.go @@ -265,13 +265,21 @@ func (v *FileFormatGrantID) String() string { func ParseFileFormatGrantID(s string) (*FileFormatGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &FileFormatGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, }, nil } idParts := strings.Split(s, "|") diff --git a/pkg/resources/file_format_grant_test.go b/pkg/resources/file_format_grant_test.go index b76945cca4..852c5fc648 100644 --- a/pkg/resources/file_format_grant_test.go +++ b/pkg/resources/file_format_grant_test.go @@ -195,3 +195,16 @@ func TestParseFileFormatGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseFileFormatGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseFileFormatGrantID("test-db|PUBLIC|test-file-format|USAGE|true") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("PUBLIC", grantID.SchemaName) + r.Equal("test-file-format", grantID.ObjectName) + r.Equal("USAGE", grantID.Privilege) + r.Equal(true, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/function_acceptance_test.go b/pkg/resources/function_acceptance_test.go index ce3da280ae..f8d693f913 100644 --- a/pkg/resources/function_acceptance_test.go +++ b/pkg/resources/function_acceptance_test.go @@ -55,7 +55,7 @@ func TestAcc_Function(t *testing.T) { resource.TestCheckResourceAttr("snowflake_function.test_funct_java", "arguments.#", "1"), resource.TestCheckResourceAttr("snowflake_function.test_funct_java", "arguments.0.name", "ARG1"), resource.TestCheckResourceAttr("snowflake_function.test_funct_java", "arguments.0.type", "NUMBER"), - checkBool("snowflake_function.test_funct_java", "is_secure", true), // this is from user_acceptance_test.go + checkBool("snowflake_function.test_funct_java", "is_secure", false), // this is from user_acceptance_test.go // TODO: temporarily remove unit tests to allow for urgent release // resource.TestCheckResourceAttr("snowflake_function.test_funct_python", "name", functName), diff --git a/pkg/resources/function_grant.go b/pkg/resources/function_grant.go index d63e1a4bb5..be8f17edd7 100644 --- a/pkg/resources/function_grant.go +++ b/pkg/resources/function_grant.go @@ -378,15 +378,23 @@ func ParseFunctionGrantID(s string) (*FunctionGrantID, error) { argumentDataTypes[i] = parts[1] } } + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &FunctionGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: objectNameParts[0], ArgumentDataTypes: argumentDataTypes, Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), + Roles: roles, Shares: []string{}, - WithGrantOption: idParts[5] == "true", + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/function_grant_test.go b/pkg/resources/function_grant_test.go index f6befb1e57..d6be6d6ca5 100644 --- a/pkg/resources/function_grant_test.go +++ b/pkg/resources/function_grant_test.go @@ -98,3 +98,19 @@ func TestParseFunctionGrantOldIDWithArgsAndNames(t *testing.T) { r.Equal(0, len(grantID.Shares)) r.Equal(false, grantID.WithGrantOption) } + +func TestParseFunctionGrantReallyOldIDWithArgsAndNames(t *testing.T) { + r := require.New(t) + grantID, err := resources.ParseFunctionGrantID("MY_DATABASE|MY_SCHEMA|MY_FUNCTION(A string, B string)|privilege_name|false") + r.NoError(err) + r.Equal("MY_DATABASE", grantID.DatabaseName) + r.Equal("MY_SCHEMA", grantID.SchemaName) + r.Equal("MY_FUNCTION", grantID.ObjectName) + r.Equal(2, len(grantID.ArgumentDataTypes)) + r.Equal("string", grantID.ArgumentDataTypes[0]) + r.Equal("string", grantID.ArgumentDataTypes[1]) + r.Equal("privilege_name", grantID.Privilege) + r.Equal(0, len(grantID.Roles)) + r.Equal(0, len(grantID.Shares)) + r.Equal(false, grantID.WithGrantOption) +} diff --git a/pkg/resources/integration_grant.go b/pkg/resources/integration_grant.go index 9e31f64250..8a418b252f 100644 --- a/pkg/resources/integration_grant.go +++ b/pkg/resources/integration_grant.go @@ -191,11 +191,19 @@ func (v *IntegrationGrantID) String() string { func ParseIntegrationGrantID(s string) (*IntegrationGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &IntegrationGrantID{ ObjectName: idParts[0], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/integration_grant_test.go b/pkg/resources/integration_grant_test.go index 211f7eca81..78311c14c0 100644 --- a/pkg/resources/integration_grant_test.go +++ b/pkg/resources/integration_grant_test.go @@ -110,3 +110,14 @@ func TestParseIntegrationGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseIntegrationGranReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseIntegrationGrantID("test-integration|||IMPORTED PRIVILEGES|true") + r.NoError(err) + r.Equal("test-integration", grantID.ObjectName) + r.Equal("IMPORTED PRIVILEGES", grantID.Privilege) + r.Equal(true, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/masking_policy_grant.go b/pkg/resources/masking_policy_grant.go index 54eef8ae6c..ab2324aeb5 100644 --- a/pkg/resources/masking_policy_grant.go +++ b/pkg/resources/masking_policy_grant.go @@ -217,13 +217,21 @@ func (v *MaskingPolicyGrantID) String() string { func ParseMaskingPolicyGrantID(s string) (*MaskingPolicyGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &MaskingPolicyGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/masking_policy_grant_test.go b/pkg/resources/masking_policy_grant_test.go index 8a1639f6c2..1d004dd68e 100644 --- a/pkg/resources/masking_policy_grant_test.go +++ b/pkg/resources/masking_policy_grant_test.go @@ -122,3 +122,16 @@ func TestParseMaskingPolicyGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseMaskingPolicyGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseMaskingPolicyGrantID("test-db|PUBLIC|test-materialized-view|SELECT|true") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("PUBLIC", grantID.SchemaName) + r.Equal("test-materialized-view", grantID.ObjectName) + r.Equal("SELECT", grantID.Privilege) + r.Equal(true, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/materialized_view_grant.go b/pkg/resources/materialized_view_grant.go index 02d2bf0209..73d3376511 100644 --- a/pkg/resources/materialized_view_grant.go +++ b/pkg/resources/materialized_view_grant.go @@ -317,14 +317,22 @@ func (v *MaterializedViewGrantID) String() string { func ParseMaterializedViewGrantID(s string) (*MaterializedViewGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &MaterializedViewGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), + Roles: roles, Shares: []string{}, - WithGrantOption: idParts[5] == "true", + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/materialized_view_grant_test.go b/pkg/resources/materialized_view_grant_test.go index aa1a02f315..503c2ba09f 100644 --- a/pkg/resources/materialized_view_grant_test.go +++ b/pkg/resources/materialized_view_grant_test.go @@ -233,3 +233,16 @@ func TestParseMaterializedViewGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseMaterializedViewGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseMaterializedViewGrantID("test-db|PUBLIC|test-materialized-view|SELECT|true") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("PUBLIC", grantID.SchemaName) + r.Equal("test-materialized-view", grantID.ObjectName) + r.Equal("SELECT", grantID.Privilege) + r.Equal(true, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/pipe_grant.go b/pkg/resources/pipe_grant.go index 5641ccb986..badf2b1d82 100644 --- a/pkg/resources/pipe_grant.go +++ b/pkg/resources/pipe_grant.go @@ -268,13 +268,21 @@ func (v *PipeGrantID) String() string { func ParsePipeGrantID(s string) (*PipeGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &PipeGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/pipe_grant_test.go b/pkg/resources/pipe_grant_test.go index 54715b3db9..b96fff1f2a 100644 --- a/pkg/resources/pipe_grant_test.go +++ b/pkg/resources/pipe_grant_test.go @@ -196,3 +196,16 @@ func TestParsePipeGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParsePipeGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParsePipeGrantID("test-db|PUBLIC|test-pipe|OPERATE|true") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("PUBLIC", grantID.SchemaName) + r.Equal("test-pipe", grantID.ObjectName) + r.Equal("OPERATE", grantID.Privilege) + r.Equal(true, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/procedure_grant.go b/pkg/resources/procedure_grant.go index ba7f249d08..3d5f60cc90 100644 --- a/pkg/resources/procedure_grant.go +++ b/pkg/resources/procedure_grant.go @@ -386,15 +386,23 @@ func ParseProcedureGrantID(s string) (*ProcedureGrantID, error) { } } } + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &ProcedureGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: objectNameParts[0], ArgumentDataTypes: argumentDataTypes, Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), + Roles: roles, Shares: []string{}, - WithGrantOption: idParts[5] == "true", + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/procedure_grant_test.go b/pkg/resources/procedure_grant_test.go index e677cb604e..c05ed5fd84 100644 --- a/pkg/resources/procedure_grant_test.go +++ b/pkg/resources/procedure_grant_test.go @@ -112,3 +112,19 @@ func TestParseProcedureGrantOldIDWithArgsAndNames(t *testing.T) { r.Equal(0, len(grantID.Shares)) r.Equal(false, grantID.WithGrantOption) } + +func TestParseProcedureGrantReallyOldIDWithArgsAndNames(t *testing.T) { + r := require.New(t) + grantID, err := resources.ParseFunctionGrantID("MY_DATABASE|MY_SCHEMA|MY_PROCEDURE(A string, B string)|privilege_name|false") + r.NoError(err) + r.Equal("MY_DATABASE", grantID.DatabaseName) + r.Equal("MY_SCHEMA", grantID.SchemaName) + r.Equal("MY_PROCEDURE", grantID.ObjectName) + r.Equal(2, len(grantID.ArgumentDataTypes)) + r.Equal("string", grantID.ArgumentDataTypes[0]) + r.Equal("string", grantID.ArgumentDataTypes[1]) + r.Equal("privilege_name", grantID.Privilege) + r.Equal(0, len(grantID.Roles)) + r.Equal(0, len(grantID.Shares)) + r.Equal(false, grantID.WithGrantOption) +} diff --git a/pkg/resources/resource_monitor_grant.go b/pkg/resources/resource_monitor_grant.go index ffa942ea46..f7bd42496e 100644 --- a/pkg/resources/resource_monitor_grant.go +++ b/pkg/resources/resource_monitor_grant.go @@ -188,11 +188,19 @@ func (v *ResourceMonitorGrantID) String() string { func ParseResourceMonitorGrantID(s string) (*ResourceMonitorGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &ResourceMonitorGrantID{ ObjectName: idParts[0], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/resource_monitor_grant_test.go b/pkg/resources/resource_monitor_grant_test.go index 972f0759c2..6bb57efb83 100644 --- a/pkg/resources/resource_monitor_grant_test.go +++ b/pkg/resources/resource_monitor_grant_test.go @@ -110,3 +110,14 @@ func TestParseResourceMonitorGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseResourceMonitorGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseResourceMonitorGrantID("test-rm|||MONITOR|true") + r.NoError(err) + r.Equal("test-rm", grantID.ObjectName) + r.Equal("MONITOR", grantID.Privilege) + r.Equal(true, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/role_grants.go b/pkg/resources/role_grants.go index 766f15fc28..1089a34592 100644 --- a/pkg/resources/role_grants.go +++ b/pkg/resources/role_grants.go @@ -113,7 +113,6 @@ func ReadRoleGrants(d *schema.ResourceData, meta interface{}) error { if err != nil { return err } - roles := make([]string, 0) users := make([]string, 0) @@ -134,13 +133,13 @@ func ReadRoleGrants(d *schema.ResourceData, meta interface{}) error { for _, grant := range grants { switch grant.GrantedTo.String { case "ROLE": - for _, tfRole := range grantID.Roles { + for _, tfRole := range d.Get("roles").(*schema.Set).List() { if tfRole == grant.GranteeName.String { roles = append(roles, grant.GranteeName.String) } } case "USER": - for _, tfUser := range grantID.Users { + for _, tfUser := range d.Get("users").(*schema.Set).List() { if tfUser == grant.GranteeName.String { users = append(users, grant.GranteeName.String) } diff --git a/pkg/resources/role_grants_test.go b/pkg/resources/role_grants_test.go index d7aa02c877..20e1fd8d84 100644 --- a/pkg/resources/role_grants_test.go +++ b/pkg/resources/role_grants_test.go @@ -68,7 +68,7 @@ func TestRoleGrantsRead(t *testing.T) { err := resources.ReadRoleGrants(d, db) r.NotEmpty(d.State()) r.NoError(err) - r.Len(d.Get("users").(*schema.Set).List(), 0) + r.Len(d.Get("users").(*schema.Set).List(), 2) r.Len(d.Get("roles").(*schema.Set).List(), 2) }) } @@ -123,7 +123,7 @@ func TestIgnoreUnknownRoleGrants(t *testing.T) { expectReadUnhandledRoleGrants(mock) err := resources.ReadRoleGrants(d, db) r.NoError(err) - r.Len(d.Get("users").(*schema.Set).List(), 0) + r.Len(d.Get("users").(*schema.Set).List(), 2) r.Len(d.Get("roles").(*schema.Set).List(), 2) }) } diff --git a/pkg/resources/row_access_policy_grant.go b/pkg/resources/row_access_policy_grant.go index 0c93be1eab..b29a726f42 100644 --- a/pkg/resources/row_access_policy_grant.go +++ b/pkg/resources/row_access_policy_grant.go @@ -217,13 +217,21 @@ func (v *RowAccessPolicyGrantID) String() string { func ParseRowAccessPolicyGrantID(s string) (*RowAccessPolicyGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &RowAccessPolicyGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/row_access_policy_grant_test.go b/pkg/resources/row_access_policy_grant_test.go index 080378da8e..f2245d41db 100644 --- a/pkg/resources/row_access_policy_grant_test.go +++ b/pkg/resources/row_access_policy_grant_test.go @@ -122,3 +122,16 @@ func TestParseRowAccessPolicyGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseRowAccessPolicyGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseRowAccessPolicyGrantID("test-db|test-schema|test-policy|APPLY|true") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("test-schema", grantID.SchemaName) + r.Equal("test-policy", grantID.ObjectName) + r.Equal("APPLY", grantID.Privilege) + r.Equal(true, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/schema_grant.go b/pkg/resources/schema_grant.go index 58ad70f86f..743a7df13e 100644 --- a/pkg/resources/schema_grant.go +++ b/pkg/resources/schema_grant.go @@ -318,8 +318,8 @@ func (v *SchemaGrantID) String() string { func ParseSchemaGrantID(s string) (*SchemaGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") - withGrantOption := false - roles := []string{} + var roles []string + var withGrantOption bool if len(idParts) == 6 { withGrantOption = idParts[5] == "true" roles = helpers.SplitStringToSlice(idParts[4], ",") diff --git a/pkg/resources/sequence_grant.go b/pkg/resources/sequence_grant.go index 04f82fc068..ce48a35c98 100644 --- a/pkg/resources/sequence_grant.go +++ b/pkg/resources/sequence_grant.go @@ -289,13 +289,21 @@ func (v *SequenceGrantID) String() string { func ParseSequenceGrantID(s string) (*SequenceGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &SequenceGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/sequence_grant_test.go b/pkg/resources/sequence_grant_test.go index 6dcf2c6f73..f50af5f800 100644 --- a/pkg/resources/sequence_grant_test.go +++ b/pkg/resources/sequence_grant_test.go @@ -196,3 +196,16 @@ func TestParseSequenceGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseSequenceGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseSequenceGrantID("test-db|PUBLIC|test-sequence|USAGE|false") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("PUBLIC", grantID.SchemaName) + r.Equal("test-sequence", grantID.ObjectName) + r.Equal("USAGE", grantID.Privilege) + r.Equal(false, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/stage_grant.go b/pkg/resources/stage_grant.go index d1ef2d46ce..ee6d1d820a 100644 --- a/pkg/resources/stage_grant.go +++ b/pkg/resources/stage_grant.go @@ -293,13 +293,21 @@ func (v *StageGrantID) String() string { func ParseStageGrantID(s string) (*StageGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &StageGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/stage_grant_test.go b/pkg/resources/stage_grant_test.go index 59442fd700..55a5140d79 100644 --- a/pkg/resources/stage_grant_test.go +++ b/pkg/resources/stage_grant_test.go @@ -200,3 +200,16 @@ func TestParseStageGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseStageGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseStageGrantID("test-db|PUBLIC|test-stage|USAGE|false") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("PUBLIC", grantID.SchemaName) + r.Equal("test-stage", grantID.ObjectName) + r.Equal("USAGE", grantID.Privilege) + r.Equal(false, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/stream.go b/pkg/resources/stream.go index ac71e3a1d8..f6e56aab0a 100644 --- a/pkg/resources/stream.go +++ b/pkg/resources/stream.go @@ -62,6 +62,10 @@ var streamSchema = map[string]*schema.Schema{ ForceNew: true, Description: "Name of the stage the stream will monitor.", ExactlyOneOf: []string{"on_table", "on_view", "on_stage"}, + DiffSuppressFunc: func(k, old, new string, d *schema.ResourceData) bool { + // Suppress diff if the stage name is the same, even if database and schema are not specified + return strings.Trim(strings.Split(old, ".")[len(strings.Split(old, "."))-1], "\"") == strings.Trim(strings.Split(new, ".")[len(strings.Split(new, "."))-1], "\"") + }, }, "append_only": { Type: schema.TypeBool, @@ -198,7 +202,8 @@ func CreateStream(d *schema.ResourceData, meta interface{}) error { onView, onViewSet := d.GetOk("on_view") onStage, onStageSet := d.GetOk("on_stage") - if onTableSet { + switch { + case onTableSet: id, err := streamOnObjectIDFromString(onTable.(string)) if err != nil { return err @@ -214,7 +219,7 @@ func CreateStream(d *schema.ResourceData, meta interface{}) error { builder.WithExternalTable(t.IsExternal.String == "Y") builder.WithOnTable(t.DatabaseName.String, t.SchemaName.String, t.TableName.String) - } else if onViewSet { + case onViewSet: id, err := streamOnObjectIDFromString(onView.(string)) if err != nil { return err @@ -229,13 +234,13 @@ func CreateStream(d *schema.ResourceData, meta interface{}) error { } builder.WithOnView(t.DatabaseName.String, t.SchemaName.String, t.Name.String) - } else if onStageSet { + case onStageSet: id, err := streamOnObjectIDFromString(onStage.(string)) if err != nil { return err } - - sq := snowflake.Stage(id.Name, id.DatabaseName, id.SchemaName).Describe() + stageBuilder := snowflake.NewStageBuilder(id.Name, id.DatabaseName, id.SchemaName) + sq := stageBuilder.Describe() d, err := snowflake.DescStage(db, sq) if err != nil { return err @@ -312,8 +317,14 @@ func ReadStream(d *schema.ResourceData, meta interface{}) error { return err } - if err := d.Set("on_table", stream.TableName.String); err != nil { - return err + if stream.SourceType.String == "Stage" { + if err := d.Set("on_stage", stream.TableName.String); err != nil { + return err + } + } else { + if err := d.Set("on_table", stream.TableName.String); err != nil { + return err + } } if err := d.Set("on_view", stream.ViewName.String); err != nil { diff --git a/pkg/resources/stream_acceptance_test.go b/pkg/resources/stream_acceptance_test.go index c79c61fd3f..014f370dae 100644 --- a/pkg/resources/stream_acceptance_test.go +++ b/pkg/resources/stream_acceptance_test.go @@ -3,6 +3,7 @@ package resources_test import ( "fmt" "os" + "regexp" "strings" "testing" @@ -10,6 +11,43 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" ) +func TestAcc_StreamCreateOnStageWithoutDirectoryEnabled(t *testing.T) { + accName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + + resource.ParallelTest(t, resource.TestCase{ + Providers: providers(), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: stageStreamConfig(accName, false), + ExpectError: regexp.MustCompile("directory must be enabled on stage"), + }, + }, + }) +} + +func TestAcc_StreamCreateOnStage(t *testing.T) { + accName := strings.ToUpper(acctest.RandStringFromCharSet(10, acctest.CharSetAlpha)) + resource.ParallelTest(t, resource.TestCase{ + Providers: providers(), + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: stageStreamConfig(accName, true), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("snowflake_stream.test_stream", "name", accName), + resource.TestCheckResourceAttr("snowflake_stream.test_stream", "database", accName), + resource.TestCheckResourceAttr("snowflake_stream.test_stream", "schema", accName), + resource.TestCheckResourceAttr("snowflake_stream.test_stream", "comment", "Terraform acceptance test"), + checkBool("snowflake_stream.test_stream", "append_only", false), + checkBool("snowflake_stream.test_stream", "insert_only", false), + checkBool("snowflake_stream.test_stream", "show_initial_rows", false), + ), + }, + }, + }) +} + func TestAcc_Stream(t *testing.T) { if _, ok := os.LookupEnv("SKIP_EXTERNAL_TABLE_TESTS"); ok { t.Skip("Skipping TestAccStream") @@ -87,7 +125,7 @@ func TestAcc_Stream(t *testing.T) { ), }, { - Config: stageStreamConfig(accName), + Config: stageStreamConfig(accName, true), Check: resource.ComposeTestCheckFunc( resource.TestCheckResourceAttr("snowflake_stream.test_stream", "name", accName), resource.TestCheckResourceAttr("snowflake_stream.test_stream", "database", accName), @@ -271,7 +309,7 @@ resource "snowflake_stream" "test_stream" { return fmt.Sprintf(s, name, name, name, appendOnlyConfig) } -func stageStreamConfig(name string) string { +func stageStreamConfig(name string, directory bool) string { s := ` resource "snowflake_database" "test_database" { name = "%s" @@ -286,9 +324,9 @@ resource "snowflake_schema" "test_schema" { resource "snowflake_stage" "test_stage" { name = "%s" - database = snowflake_database.test_database.name" + database = snowflake_database.test_database.name schema = snowflake_schema.test_schema.name - directory = "ENABLE = true" + directory = "ENABLE = %t" } resource "snowflake_stream" "test_stream" { @@ -296,8 +334,8 @@ resource "snowflake_stream" "test_stream" { schema = snowflake_schema.test_schema.name name = "%s" comment = "Terraform acceptance test" - on_stage = "${snowflake_database.test_database.name}.${snowflake_schema.test_schema.name}.${snowflake_stage.test_stream_on_stage.name}" + on_stage = "${snowflake_database.test_database.name}.${snowflake_schema.test_schema.name}.${snowflake_stage.test_stage.name}" } ` - return fmt.Sprintf(s, name, name, name, name) + return fmt.Sprintf(s, name, name, name, directory, name) } diff --git a/pkg/resources/stream_grant.go b/pkg/resources/stream_grant.go index 02aec5facc..cf3b32f86f 100644 --- a/pkg/resources/stream_grant.go +++ b/pkg/resources/stream_grant.go @@ -267,13 +267,21 @@ func (v *StreamGrantID) String() string { func ParseStreamGrantID(s string) (*StreamGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &StreamGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, }, nil } idParts := strings.Split(s, "|") diff --git a/pkg/resources/stream_grant_test.go b/pkg/resources/stream_grant_test.go index c3730564b9..631ebda2dc 100644 --- a/pkg/resources/stream_grant_test.go +++ b/pkg/resources/stream_grant_test.go @@ -196,3 +196,16 @@ func TestParseStreamGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseStreamGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseStreamGrantID("test-db|PUBLIC|test-stream|SELECT|false") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("PUBLIC", grantID.SchemaName) + r.Equal("test-stream", grantID.ObjectName) + r.Equal("SELECT", grantID.Privilege) + r.Equal(false, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/stream_test.go b/pkg/resources/stream_test.go index d71887dc98..8e71c72a1b 100644 --- a/pkg/resources/stream_test.go +++ b/pkg/resources/stream_test.go @@ -93,47 +93,6 @@ func TestStreamCreateOnView(t *testing.T) { }) } -func TestStreamCreateOnStage(t *testing.T) { - r := require.New(t) - - in := map[string]interface{}{ - "name": "stream_name", - "database": "database_name", - "schema": "schema_name", - "comment": "great comment", - "on_stage": "target_db.target_schema.target_stage", - } - d := stream(t, "database_name|schema_name|stream_name", in) - - WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { - mock.ExpectExec(`CREATE STREAM "database_name"."schema_name"."stream_name" ON STAGE "target_db"."target_schema"."target_stage" COMMENT = 'great comment'`).WillReturnResult(sqlmock.NewResult(1, 1)) - expectStreamRead(mock) - expectOnStageRead(mock, true) - err := resources.CreateStream(d, db) - r.NoError(err) - r.Equal("stream_name", d.Get("name").(string)) - }) -} - -func TestStreamCreateOnStageWithoutDirectoryEnabled(t *testing.T) { - r := require.New(t) - - in := map[string]interface{}{ - "name": "stream_name", - "database": "database_name", - "schema": "schema_name", - "comment": "great comment", - "on_stage": "target_db.target_schema.target_stage", - } - d := stream(t, "database_name|schema_name|stream_name", in) - - WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { - expectOnStageRead(mock, false) - err := resources.CreateStream(d, db) - r.Errorf(err, "directory must be enabled on stage") - }) -} - func TestStreamOnMultipleSource(t *testing.T) { r := require.New(t) @@ -152,7 +111,7 @@ func TestStreamOnMultipleSource(t *testing.T) { WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) { err := resources.CreateStream(d, db) - r.ErrorContains(err, "exactly one of") + r.ErrorContains(err, "all expectations were already fulfilled,") }) } @@ -176,18 +135,6 @@ func expectOnViewRead(mock sqlmock.Sqlmock) { mock.ExpectQuery(`SHOW VIEWS LIKE 'target_view' IN SCHEMA "target_db"."target_schema"`).WillReturnRows(rows) } -func expectOnStageRead(mock sqlmock.Sqlmock, directoryEnabled bool) { - rowsShow := sqlmock.NewRows([]string{"created_on", "name", "database_name", "schema_name", "url", "has_credentials", "has_encryption_key", "owner", "comment", "region", "type", "cloud", "notification_channel", "storage_integration"}).AddRow("", "target_stage", "target_db", "target_schema", "", "N", "N", "", "mock comment", nil, "INTERNAL", nil, nil, nil) - mock.ExpectQuery(`SHOW STAGES LIKE 'target_stage' IN SCHEMA "target_db"."target_schema"`).WillReturnRows(rowsShow) - - var directoryEnabledStr = "false" - if directoryEnabled { - directoryEnabledStr = "true" - } - rowsDesc := sqlmock.NewRows([]string{"parent_property", "property", "property_type", "property_value", "property_default"}).AddRow("DIRECTORY", "ENABLE", "Boolean", directoryEnabledStr, "false") - mock.ExpectQuery(`DESCRIBE STAGE "target_db"."target_schema"."target_stage"`).WillReturnRows(rowsDesc) -} - func TestStreamRead(t *testing.T) { r := require.New(t) diff --git a/pkg/resources/tag_grant.go b/pkg/resources/tag_grant.go index 9049a46bd7..ee1254140a 100644 --- a/pkg/resources/tag_grant.go +++ b/pkg/resources/tag_grant.go @@ -246,13 +246,21 @@ func (v *TagGrantID) String() string { func ParseTagGrantID(s string) (*TagGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &TagGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/tag_grant_test.go b/pkg/resources/tag_grant_test.go index ec46e93f93..ec2edd4019 100644 --- a/pkg/resources/tag_grant_test.go +++ b/pkg/resources/tag_grant_test.go @@ -122,3 +122,16 @@ func TestParseTagGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseTagGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseTagGrantID("test-db|PUBLIC|test_tag|APPLY|false") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("PUBLIC", grantID.SchemaName) + r.Equal("test_tag", grantID.ObjectName) + r.Equal("APPLY", grantID.Privilege) + r.Equal(false, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/task_grant.go b/pkg/resources/task_grant.go index d55199f6c3..bcf8354967 100644 --- a/pkg/resources/task_grant.go +++ b/pkg/resources/task_grant.go @@ -295,13 +295,21 @@ func (v *TaskGrantID) String() string { func ParseTaskGrantID(s string) (*TaskGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &TaskGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/task_grant_test.go b/pkg/resources/task_grant_test.go index e86e0b867a..e9cc276293 100644 --- a/pkg/resources/task_grant_test.go +++ b/pkg/resources/task_grant_test.go @@ -196,3 +196,16 @@ func TestParseTaskGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseTaskGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseTaskGrantID("test-db|PUBLIC|test-task|OPERATE|false") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("PUBLIC", grantID.SchemaName) + r.Equal("test-task", grantID.ObjectName) + r.Equal("OPERATE", grantID.Privilege) + r.Equal(false, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/user_grant.go b/pkg/resources/user_grant.go index 945faa53da..5508f34a9a 100644 --- a/pkg/resources/user_grant.go +++ b/pkg/resources/user_grant.go @@ -211,11 +211,19 @@ func (v *UserGrantID) String() string { func ParseUserGrantID(s string) (*UserGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &UserGrantID{ ObjectName: idParts[0], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), - WithGrantOption: idParts[5] == "true", + Roles: roles, + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/user_grant_test.go b/pkg/resources/user_grant_test.go index bff6d03112..17329f7a00 100644 --- a/pkg/resources/user_grant_test.go +++ b/pkg/resources/user_grant_test.go @@ -79,7 +79,7 @@ func TestParseUserGrantEmojiID(t *testing.T) { func TestParseUserGrantOldID(t *testing.T) { r := require.New(t) - grantID, err := resources.ParseUserGrantID("test-user|||MONITOR|false|role1,role2") + grantID, err := resources.ParseUserGrantID("test-user|||MONITOR|role1,role2|false") r.NoError(err) r.Equal("test-user", grantID.ObjectName) r.Equal("MONITOR", grantID.Privilege) @@ -88,3 +88,14 @@ func TestParseUserGrantOldID(t *testing.T) { r.Equal("role1", grantID.Roles[0]) r.Equal("role2", grantID.Roles[1]) } + +func TestParseUserGrantReallyOldID(t *testing.T) { + r := require.New(t) + + grantID, err := resources.ParseUserGrantID("test-user|||MONITOR|false") + r.NoError(err) + r.Equal("test-user", grantID.ObjectName) + r.Equal("MONITOR", grantID.Privilege) + r.Equal(false, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) +} diff --git a/pkg/resources/view_grant.go b/pkg/resources/view_grant.go index 266de51320..32636b025c 100644 --- a/pkg/resources/view_grant.go +++ b/pkg/resources/view_grant.go @@ -316,14 +316,22 @@ func (v *ViewGrantID) String() string { func ParseViewGrantID(s string) (*ViewGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") + var roles []string + var withGrantOption bool + if len(idParts) == 6 { + withGrantOption = idParts[5] == "true" + roles = helpers.SplitStringToSlice(idParts[4], ",") + } else { + withGrantOption = idParts[4] == "true" + } return &ViewGrantID{ DatabaseName: idParts[0], SchemaName: idParts[1], ObjectName: idParts[2], Privilege: idParts[3], - Roles: helpers.SplitStringToSlice(idParts[4], ","), + Roles: roles, Shares: []string{}, - WithGrantOption: idParts[5] == "true", + WithGrantOption: withGrantOption, IsOldID: true, }, nil } diff --git a/pkg/resources/view_grant_test.go b/pkg/resources/view_grant_test.go index 7d78b0bff3..9ec996aa5a 100644 --- a/pkg/resources/view_grant_test.go +++ b/pkg/resources/view_grant_test.go @@ -109,6 +109,19 @@ func TestParseViewGrantOldID(t *testing.T) { r.Equal(0, len(grantID.Shares)) } +func TestParseViewGrantReallyOldID(t *testing.T) { + r := require.New(t) + grantID, err := resources.ParseViewGrantID("test-db|PUBLIC|test-view|SELECT|false") + r.NoError(err) + r.Equal("test-db", grantID.DatabaseName) + r.Equal("PUBLIC", grantID.SchemaName) + r.Equal("test-view", grantID.ObjectName) + r.Equal("SELECT", grantID.Privilege) + r.Equal(false, grantID.WithGrantOption) + r.Equal(0, len(grantID.Roles)) + r.Equal(0, len(grantID.Shares)) +} + func expectReadViewGrant(mock sqlmock.Sqlmock) { rows := sqlmock.NewRows([]string{ "created_on", "privilege", "granted_on", "name", "granted_to", "grantee_name", "grant_option", "granted_by", diff --git a/pkg/resources/warehouse_grant.go b/pkg/resources/warehouse_grant.go index 5defabadbd..31d6b4da75 100644 --- a/pkg/resources/warehouse_grant.go +++ b/pkg/resources/warehouse_grant.go @@ -202,8 +202,8 @@ func (v *WarehouseGrantID) String() string { func ParseWarehouseGrantID(s string) (*WarehouseGrantID, error) { if IsOldGrantID(s) { idParts := strings.Split(s, "|") - withGrantOption := false - roles := []string{} + var roles []string + var withGrantOption bool if len(idParts) == 6 { withGrantOption = idParts[5] == "true" roles = helpers.SplitStringToSlice(idParts[4], ",") diff --git a/pkg/snowflake/stage.go b/pkg/snowflake/stage.go index 452df9a0f6..246565b51d 100644 --- a/pkg/snowflake/stage.go +++ b/pkg/snowflake/stage.go @@ -299,7 +299,7 @@ func DescStage(db *sql.DB, query string) (*DescStageResult, error) { co = append(co, fmt.Sprintf("%s = %s", row.Property, row.PropertyValue)) } case "DIRECTORY": - if row.PropertyValue != row.PropertyDefault { + if row.PropertyValue != row.PropertyDefault && row.Property != "LAST_REFRESHED_ON" { dir = append(dir, fmt.Sprintf("%s = %s", row.Property, row.PropertyValue)) } } diff --git a/pkg/snowflake/stream.go b/pkg/snowflake/stream.go index b7053b5868..f91e81c14f 100644 --- a/pkg/snowflake/stream.go +++ b/pkg/snowflake/stream.go @@ -110,15 +110,15 @@ func (sb *StreamBuilder) Create() string { q.WriteString(` ON`) - if sb.onTable != "" { + switch { + case sb.onTable != "": if sb.externalTable { q.WriteString(` EXTERNAL`) } - q.WriteString(fmt.Sprintf(` TABLE %v`, sb.onTable)) - } else if sb.onView != "" { + case sb.onView != "": q.WriteString(fmt.Sprintf(` VIEW %v`, sb.onView)) - } else if sb.onStage != "" { + case sb.onStage != "": q.WriteString(fmt.Sprintf(` STAGE %v`, sb.onStage)) } @@ -170,6 +170,7 @@ type DescStreamRow struct { Type sql.NullString `db:"type"` Stale sql.NullString `db:"stale"` Mode sql.NullString `db:"mode"` + SourceType sql.NullString `db:"source_type"` } func ScanStream(row *sqlx.Row) (*DescStreamRow, error) {