Skip to content

Commit

Permalink
feat: Support for selecting language in snowflake_procedure (#1010)
Browse files Browse the repository at this point in the history
* Add support for selecting language in snowflake_procedure

Implements #948

The previous behaviour was always setting `JAVASCRIPT` as language.
The implementation in this PR breaks the "backwards compatibility" with that rule to honor the Snowflake default: `SQL`.

* Update documentation for `procedure_grant` and `function_grant` as requested

* Generate docs with `make check-docs`

* Update docs

Co-authored-by: Allison Doami <adoami@chanzuckerberg.com>
Co-authored-by: Scott Winkler <scott.winkler@snowflake.com>
  • Loading branch information
3 people authored May 26, 2022
1 parent e7dcfd4 commit 3161827
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 46 deletions.
18 changes: 8 additions & 10 deletions docs/resources/function_grant.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@ resource snowflake_function_grant grant {
schema_name = "schema"
function_name = "function"
arguments = [
{
"name": "a",
"type": "array"
},
{
"name": "b",
"type": "string"
}
]
arguments {
name = "a"
type = "array"
}
arguments {
name = "b"
type = "string"
}
return_type = "string"
privilege = "USAGE"
Expand Down
2 changes: 2 additions & 0 deletions docs/resources/procedure.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ resource "snowflake_procedure" "proc" {
name = "SAMPLEPROC"
database = snowflake_database.db.name
schema = snowflake_schema.schema.name
language = "JAVASCRIPT"
arguments {
name = "arg1"
type = "varchar"
Expand Down Expand Up @@ -64,6 +65,7 @@ EOT
- `arguments` (Block List) List of the arguments for the procedure (see [below for nested schema](#nestedblock--arguments))
- `comment` (String) Specifies a comment for the procedure.
- `execute_as` (String) Sets execute context - see caller's rights and owner's rights
- `language` (String) Specifies the language of the stored procedure code.
- `null_input_behavior` (String) Specifies the behavior of the procedure when called with null inputs.
- `return_behavior` (String) Specifies the behavior of the function when returning results

Expand Down
18 changes: 8 additions & 10 deletions docs/resources/procedure_grant.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,14 @@ resource snowflake_procedure_grant grant {
schema_name = "schema"
procedure_name = "procedure"
arguments = [
{
"name": "a",
"type": "array"
},
{
"name": "b",
"type": "string"
}
]
arguments {
name = "a"
type = "array"
}
arguments {
name = "b"
type = "string"
}
return_type = "string"
privilege = "select"
Expand Down
18 changes: 8 additions & 10 deletions examples/resources/snowflake_function_grant/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ resource snowflake_function_grant grant {
schema_name = "schema"
function_name = "function"

arguments = [
{
"name": "a",
"type": "array"
},
{
"name": "b",
"type": "string"
}
]
arguments {
name = "a"
type = "array"
}
arguments {
name = "b"
type = "string"
}
return_type = "string"

privilege = "USAGE"
Expand Down
1 change: 1 addition & 0 deletions examples/resources/snowflake_procedure/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ resource "snowflake_procedure" "proc" {
name = "SAMPLEPROC"
database = snowflake_database.db.name
schema = snowflake_schema.schema.name
language = "JAVASCRIPT"
arguments {
name = "arg1"
type = "varchar"
Expand Down
18 changes: 8 additions & 10 deletions examples/resources/snowflake_procedure_grant/resource.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,14 @@ resource snowflake_procedure_grant grant {
schema_name = "schema"
procedure_name = "procedure"

arguments = [
{
"name": "a",
"type": "array"
},
{
"name": "b",
"type": "string"
}
]
arguments {
name = "a"
type = "array"
}
arguments {
name = "b"
type = "string"
}
return_type = "string"

privilege = "select"
Expand Down
20 changes: 19 additions & 1 deletion pkg/resources/procedure.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/pkg/errors"
)

var procedureLanguages = []string{"JAVASCRIPT", "JAVA", "SCALA", "SQL"}

var procedureSchema = map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Expand Down Expand Up @@ -70,6 +72,13 @@ var procedureSchema = map[string]*schema.Schema{
ForceNew: true,
DiffSuppressFunc: DiffSuppressStatement,
},
"language": {
Type: schema.TypeString,
Optional: true,
Default: "SQL",
ValidateFunc: validation.StringInSlice(procedureLanguages, false),
Description: "Specifies the language of the stored procedure code.",
},
"execute_as": {
Type: schema.TypeString,
Optional: true,
Expand Down Expand Up @@ -159,6 +168,11 @@ func CreateProcedure(d *schema.ResourceData, meta interface{}) error {
builder.WithExecuteAs(v.(string))
}

// Set optionals, default is SQL
if v, ok := d.GetOk("language"); ok {
builder.WithLanguage(v.(string))
}

if v, ok := d.GetOk("comment"); ok {
builder.WithComment(v.(string))
}
Expand Down Expand Up @@ -259,7 +273,11 @@ func ReadProcedure(d *schema.ResourceData, meta interface{}) error {
return err
}
case "language":
// To ignore
if snowflake.Contains(languages, desc.Value.String) {
if err = d.Set("language", desc.Value.String); err != nil {
return err
}
}
default:
log.Printf("[WARN] unexpected procedure property %v returned from Snowflake", desc.Property.String)
}
Expand Down
6 changes: 4 additions & 2 deletions pkg/resources/procedure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ func prepDummyProcedureResource(t *testing.T) *schema.ResourceData {
"schema": "my_schema",
"arguments": []interface{}{argument1, argument2},
"return_type": "varchar",
"language": "SCALA",
"comment": "mock comment",
"return_behavior": "IMMUTABLE",
"statement": procedureBody, //var message = DATA + DATA;return message
Expand All @@ -43,7 +44,7 @@ func TestProcedureCreate(t *testing.T) {
d := prepDummyProcedureResource(t)

WithMockDb(t, func(db *sql.DB, mock sqlmock.Sqlmock) {
mock.ExpectExec(`CREATE OR REPLACE PROCEDURE "my_db"."my_schema"."my_proc"\(data VARCHAR, event_dt DATE\) RETURNS VARCHAR LANGUAGE javascript CALLED ON NULL INPUT IMMUTABLE COMMENT = 'mock comment' EXECUTE AS OWNER AS \$\$hi\$\$`).WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectExec(`CREATE OR REPLACE PROCEDURE "my_db"."my_schema"."my_proc"\(data VARCHAR, event_dt DATE\) RETURNS VARCHAR LANGUAGE SCALA CALLED ON NULL INPUT IMMUTABLE COMMENT = 'mock comment' EXECUTE AS OWNER AS \$\$hi\$\$`).WillReturnResult(sqlmock.NewResult(1, 1))
expectProcedureRead(mock)
err := resources.CreateProcedure(d, db)
r.NoError(err)
Expand All @@ -61,7 +62,7 @@ func expectProcedureRead(mock sqlmock.Sqlmock) {
describeRows := sqlmock.NewRows([]string{"property", "value"}).
AddRow("signature", "(data VARCHAR, event_dt DATE)").
AddRow("returns", "VARCHAR(123456789)"). // This is how return type is stored in Snowflake DB
AddRow("language", "JAVASCRIPT").
AddRow("language", "SQL").
AddRow("null handling", "CALLED ON NULL INPUT").
AddRow("volatility", "IMMUTABLE").
AddRow("execute as", "CALLER").
Expand All @@ -85,6 +86,7 @@ func TestProcedureRead(t *testing.T) {
r.Equal("MY_SCHEMA", d.Get("schema").(string))
r.Equal("mock comment", d.Get("comment").(string))
r.Equal("VARCHAR", d.Get("return_type").(string))
r.Equal("SQL", d.Get("language").(string))
r.Equal("IMMUTABLE", d.Get("return_behavior").(string))
r.Equal(procedureBody, d.Get("statement").(string))

Expand Down
11 changes: 10 additions & 1 deletion pkg/snowflake/procedure.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type ProcedureBuilder struct {
args []map[string]string
returnBehavior string // VOLATILE, IMMUTABLE
nullInputBehavior string // "CALLED ON NULL INPUT" or "RETURNS NULL ON NULL INPUT"
language string // SQL, JAVASCRIPT, JAVA, SCALA
returnType string
executeAs string
comment string
Expand Down Expand Up @@ -83,6 +84,12 @@ func (pb *ProcedureBuilder) WithExecuteAs(s string) *ProcedureBuilder {
return pb
}

// WithLanguage sets the language to SQL, JAVA, SCALA or JAVASCRIPT
func (pb *ProcedureBuilder) WithLanguage(s string) *ProcedureBuilder {
pb.language = s
return pb
}

// WithComment adds a comment to the ProcedureBuilder
func (pb *ProcedureBuilder) WithComment(c string) *ProcedureBuilder {
pb.comment = c
Expand Down Expand Up @@ -141,7 +148,9 @@ func (pb *ProcedureBuilder) Create() (string, error) {
q.WriteString(`)`)

q.WriteString(fmt.Sprintf(" RETURNS %v", pb.returnType))
q.WriteString(" LANGUAGE javascript")
if pb.language != "" {
q.WriteString(fmt.Sprintf(" LANGUAGE %v", EscapeString(pb.language)))
}
if pb.nullInputBehavior != "" {
q.WriteString(fmt.Sprintf(` %v`, EscapeString(pb.nullInputBehavior)))
}
Expand Down
5 changes: 3 additions & 2 deletions pkg/snowflake/procedure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestProcedureCreate(t *testing.T) {
r.Equal([]string{"VARCHAR", "DATE"}, s.ArgTypes())
createStmnt, _ := s.Create()
expected := `CREATE OR REPLACE PROCEDURE "test_db"."test_schema"."test_proc"` +
`(user VARCHAR, eventdt DATE) RETURNS VARCHAR LANGUAGE javascript EXECUTE AS CALLER AS $$` +
`(user VARCHAR, eventdt DATE) RETURNS VARCHAR EXECUTE AS CALLER AS $$` +
`var message = "Hi"` + "\nreturn message$$"
r.Equal(expected, createStmnt)
}
Expand All @@ -45,10 +45,11 @@ func TestProcedureCreateWithOptionalParams(t *testing.T) {
s := getProcedure(true)
s.WithNullInputBehavior("RETURNS NULL ON NULL INPUT")
s.WithReturnBehavior("IMMUTABLE")
s.WithLanguage("JAVASCRIPT")
s.WithComment("this is cool proc!")
createStmnt, _ := s.Create()
expected := `CREATE OR REPLACE PROCEDURE "test_db"."test_schema"."test_proc"` +
`(user VARCHAR, eventdt DATE) RETURNS VARCHAR LANGUAGE javascript RETURNS NULL ON NULL INPUT` +
`(user VARCHAR, eventdt DATE) RETURNS VARCHAR LANGUAGE JAVASCRIPT RETURNS NULL ON NULL INPUT` +
` IMMUTABLE COMMENT = 'this is cool proc!' EXECUTE AS CALLER AS $$` +
`var message = "Hi"` + "\nreturn message$$"
r.Equal(expected, createStmnt)
Expand Down

0 comments on commit 3161827

Please sign in to comment.