From b5d567370f570afcfd7d9d59274e3ef1efea457f Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 13:23:05 +0100 Subject: [PATCH 01/63] functions SDK update starts here From 4e6f7ad4ab3257632bf204a6773e19de0c58ac98 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 13:23:28 +0100 Subject: [PATCH 02/63] Add TODOs for create --- pkg/sdk/functions_gen.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index ab7ca62170..d81ed74673 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -22,6 +22,18 @@ type Functions interface { } // CreateForJavaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#java-handler. +// TODO [SNOW-1348103 - this PR]: test secret (= missing, multiple secretes) + add helper for secret +// TODO [SNOW-1348103 - this PR]: test secure (for each type, with owner and underprivileged role), read https://docs.snowflake.com/en/developer-guide/secure-udf-procedure +// TODO [SNOW-1348103 - this PR]: test setting the paths for all types (like imports, target paths) +// TODO [SNOW-1348103 - next PRs]: check data type mappings https://docs.snowflake.com/en/sql-reference/sql/create-function#all-languages (signature + returns) +// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 11.x, 17.x supported, 11.x being the default) +// TODO [SNOW-1348103 - this PR]: packages: package_name:version_number; do we validate? - check SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'java'; +// TODO [SNOW-1348103 - next PRs]: add to the resource docs https://docs.snowflake.com/en/sql-reference/sql/create-function#access-control-requirements +// TODO [SNOW-1348103 - this PR]: what delimiter do we use for : ' versus $? +// TODO [SNOW-1348103 - this PR]: escaping single quotes test +// TODO [SNOW-1348103 - this PR]: validation of JAR (check https://docs.snowflake.com/en/sql-reference/sql/create-function#id6) +// TODO [SNOW-1348103 - next PRs]: active warehouse vs validations +// TODO [SNOW-1348103 - this PR]: check creation of all functions (using examples and more) type CreateForJavaFunctionOptions struct { create bool `ddl:"static" sql:"CREATE"` OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` @@ -103,6 +115,11 @@ type CreateForJavascriptFunctionOptions struct { } // CreateForPythonFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#python-handler. +// TODO [SNOW-1348103 - this PR]: add aggregate (PuPr) +// TODO [SNOW-1348103 - this PR]: what about [==] - SDK level or resource level? check also: SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'python'; +// TODO [SNOW-1348103 - this PR]: what about preview feature >= ? +// TODO [SNOW-1348103 - this PR]: what about '.' for non-inline functions? +// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 3.8, 3.9, 3.10, 3.11 supported, which one is a default?) type CreateForPythonFunctionOptions struct { create bool `ddl:"static" sql:"CREATE"` OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` @@ -129,6 +146,7 @@ type CreateForPythonFunctionOptions struct { } // CreateForScalaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#scala-handler. +// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 2.12 supported, which is the default) type CreateForScalaFunctionOptions struct { create bool `ddl:"static" sql:"CREATE"` OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` From 70f697f993ea74952318b0418ce32e2ca1bb05da Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 14:51:03 +0100 Subject: [PATCH 03/63] Modify opts impl template in generator to reduce whitespace --- .../templates/sub_templates/to_opts_mapping.tmpl | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pkg/sdk/poc/generator/templates/sub_templates/to_opts_mapping.tmpl b/pkg/sdk/poc/generator/templates/sub_templates/to_opts_mapping.tmpl index d304292cc5..5444230288 100644 --- a/pkg/sdk/poc/generator/templates/sub_templates/to_opts_mapping.tmpl +++ b/pkg/sdk/poc/generator/templates/sub_templates/to_opts_mapping.tmpl @@ -10,13 +10,13 @@ {{- range .Fields }} {{- if .ShouldBeInDto }} {{- if .IsStruct }} - {{ if or .IsPointer .IsSlice }} + {{- if or .IsPointer .IsSlice }} if r{{ .Path }} != nil { - {{ end }} + {{- end -}} {{- if not .IsSlice }} opts{{ .Path }} = {{ template "toOptsMapping" . -}}{{/* Recursive call */}} - {{- else }} + {{- else -}} s := make({{ .Kind }}, len(r{{ .Path }})) for i, v := range r{{ .Path }} { s[i] = {{ .KindNoSlice }}{ @@ -26,11 +26,11 @@ } } opts{{ .Path }} = s - {{ end -}} + {{- end -}} - {{ if or .IsPointer .IsSlice }} + {{- if or .IsPointer .IsSlice -}} } - {{ end }} + {{- end -}} {{- end -}} - {{ end -}} -{{ end }} + {{- end }} +{{- end }} From 42fb76801341961d996582cbc609f0df5071edbe Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 14:54:57 +0100 Subject: [PATCH 04/63] Regenerate to replace struct creation --- pkg/sdk/functions_impl_gen.go | 83 ++++++++++++++++++++++++++++------- 1 file changed, 68 insertions(+), 15 deletions(-) diff --git a/pkg/sdk/functions_impl_gen.go b/pkg/sdk/functions_impl_gen.go index ca17781139..bd3b29fae9 100644 --- a/pkg/sdk/functions_impl_gen.go +++ b/pkg/sdk/functions_impl_gen.go @@ -103,7 +103,12 @@ func (r *CreateForJavaFunctionRequest) toOpts() *CreateForJavaFunctionOptions { if r.Arguments != nil { s := make([]FunctionArgument, len(r.Arguments)) for i, v := range r.Arguments { - s[i] = FunctionArgument(v) + s[i] = FunctionArgument{ + ArgName: v.ArgName, + ArgDataTypeOld: v.ArgDataTypeOld, + ArgDataType: v.ArgDataType, + DefaultValue: v.DefaultValue, + } } opts.Arguments = s } @@ -119,7 +124,11 @@ func (r *CreateForJavaFunctionRequest) toOpts() *CreateForJavaFunctionOptions { if r.Returns.Table.Columns != nil { s := make([]FunctionColumn, len(r.Returns.Table.Columns)) for i, v := range r.Returns.Table.Columns { - s[i] = FunctionColumn(v) + s[i] = FunctionColumn{ + ColumnName: v.ColumnName, + ColumnDataTypeOld: v.ColumnDataTypeOld, + ColumnDataType: v.ColumnDataType, + } } opts.Returns.Table.Columns = s } @@ -127,14 +136,18 @@ func (r *CreateForJavaFunctionRequest) toOpts() *CreateForJavaFunctionOptions { if r.Imports != nil { s := make([]FunctionImport, len(r.Imports)) for i, v := range r.Imports { - s[i] = FunctionImport(v) + s[i] = FunctionImport{ + Import: v.Import, + } } opts.Imports = s } if r.Packages != nil { s := make([]FunctionPackage, len(r.Packages)) for i, v := range r.Packages { - s[i] = FunctionPackage(v) + s[i] = FunctionPackage{ + Package: v.Package, + } } opts.Packages = s } @@ -159,7 +172,12 @@ func (r *CreateForJavascriptFunctionRequest) toOpts() *CreateForJavascriptFuncti if r.Arguments != nil { s := make([]FunctionArgument, len(r.Arguments)) for i, v := range r.Arguments { - s[i] = FunctionArgument(v) + s[i] = FunctionArgument{ + ArgName: v.ArgName, + ArgDataTypeOld: v.ArgDataTypeOld, + ArgDataType: v.ArgDataType, + DefaultValue: v.DefaultValue, + } } opts.Arguments = s } @@ -175,7 +193,11 @@ func (r *CreateForJavascriptFunctionRequest) toOpts() *CreateForJavascriptFuncti if r.Returns.Table.Columns != nil { s := make([]FunctionColumn, len(r.Returns.Table.Columns)) for i, v := range r.Returns.Table.Columns { - s[i] = FunctionColumn(v) + s[i] = FunctionColumn{ + ColumnName: v.ColumnName, + ColumnDataTypeOld: v.ColumnDataTypeOld, + ColumnDataType: v.ColumnDataType, + } } opts.Returns.Table.Columns = s } @@ -207,7 +229,12 @@ func (r *CreateForPythonFunctionRequest) toOpts() *CreateForPythonFunctionOption if r.Arguments != nil { s := make([]FunctionArgument, len(r.Arguments)) for i, v := range r.Arguments { - s[i] = FunctionArgument(v) + s[i] = FunctionArgument{ + ArgName: v.ArgName, + ArgDataTypeOld: v.ArgDataTypeOld, + ArgDataType: v.ArgDataType, + DefaultValue: v.DefaultValue, + } } opts.Arguments = s } @@ -223,7 +250,11 @@ func (r *CreateForPythonFunctionRequest) toOpts() *CreateForPythonFunctionOption if r.Returns.Table.Columns != nil { s := make([]FunctionColumn, len(r.Returns.Table.Columns)) for i, v := range r.Returns.Table.Columns { - s[i] = FunctionColumn(v) + s[i] = FunctionColumn{ + ColumnName: v.ColumnName, + ColumnDataTypeOld: v.ColumnDataTypeOld, + ColumnDataType: v.ColumnDataType, + } } opts.Returns.Table.Columns = s } @@ -231,14 +262,18 @@ func (r *CreateForPythonFunctionRequest) toOpts() *CreateForPythonFunctionOption if r.Imports != nil { s := make([]FunctionImport, len(r.Imports)) for i, v := range r.Imports { - s[i] = FunctionImport(v) + s[i] = FunctionImport{ + Import: v.Import, + } } opts.Imports = s } if r.Packages != nil { s := make([]FunctionPackage, len(r.Packages)) for i, v := range r.Packages { - s[i] = FunctionPackage(v) + s[i] = FunctionPackage{ + Package: v.Package, + } } opts.Packages = s } @@ -269,21 +304,30 @@ func (r *CreateForScalaFunctionRequest) toOpts() *CreateForScalaFunctionOptions if r.Arguments != nil { s := make([]FunctionArgument, len(r.Arguments)) for i, v := range r.Arguments { - s[i] = FunctionArgument(v) + s[i] = FunctionArgument{ + ArgName: v.ArgName, + ArgDataTypeOld: v.ArgDataTypeOld, + ArgDataType: v.ArgDataType, + DefaultValue: v.DefaultValue, + } } opts.Arguments = s } if r.Imports != nil { s := make([]FunctionImport, len(r.Imports)) for i, v := range r.Imports { - s[i] = FunctionImport(v) + s[i] = FunctionImport{ + Import: v.Import, + } } opts.Imports = s } if r.Packages != nil { s := make([]FunctionPackage, len(r.Packages)) for i, v := range r.Packages { - s[i] = FunctionPackage(v) + s[i] = FunctionPackage{ + Package: v.Package, + } } opts.Packages = s } @@ -308,7 +352,12 @@ func (r *CreateForSQLFunctionRequest) toOpts() *CreateForSQLFunctionOptions { if r.Arguments != nil { s := make([]FunctionArgument, len(r.Arguments)) for i, v := range r.Arguments { - s[i] = FunctionArgument(v) + s[i] = FunctionArgument{ + ArgName: v.ArgName, + ArgDataTypeOld: v.ArgDataTypeOld, + ArgDataType: v.ArgDataType, + DefaultValue: v.DefaultValue, + } } opts.Arguments = s } @@ -324,7 +373,11 @@ func (r *CreateForSQLFunctionRequest) toOpts() *CreateForSQLFunctionOptions { if r.Returns.Table.Columns != nil { s := make([]FunctionColumn, len(r.Returns.Table.Columns)) for i, v := range r.Returns.Table.Columns { - s[i] = FunctionColumn(v) + s[i] = FunctionColumn{ + ColumnName: v.ColumnName, + ColumnDataTypeOld: v.ColumnDataTypeOld, + ColumnDataType: v.ColumnDataType, + } } opts.Returns.Table.Columns = s } From 5211af81928c25917397932201a26b1cd876616a Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 18:44:03 +0100 Subject: [PATCH 05/63] Update TODOs --- pkg/sdk/functions_gen.go | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index d81ed74673..6e667fbe84 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -22,15 +22,16 @@ type Functions interface { } // CreateForJavaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#java-handler. -// TODO [SNOW-1348103 - this PR]: test secret (= missing, multiple secretes) + add helper for secret // TODO [SNOW-1348103 - this PR]: test secure (for each type, with owner and underprivileged role), read https://docs.snowflake.com/en/developer-guide/secure-udf-procedure // TODO [SNOW-1348103 - this PR]: test setting the paths for all types (like imports, target paths) +// TODO [SNOW-1348103 - this PR]: test weird names for arg name +// TODO [SNOW-1348103 - this PR]: test two types of creation for each func // TODO [SNOW-1348103 - next PRs]: check data type mappings https://docs.snowflake.com/en/sql-reference/sql/create-function#all-languages (signature + returns) // TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 11.x, 17.x supported, 11.x being the default) // TODO [SNOW-1348103 - this PR]: packages: package_name:version_number; do we validate? - check SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'java'; // TODO [SNOW-1348103 - next PRs]: add to the resource docs https://docs.snowflake.com/en/sql-reference/sql/create-function#access-control-requirements -// TODO [SNOW-1348103 - this PR]: what delimiter do we use for : ' versus $? -// TODO [SNOW-1348103 - this PR]: escaping single quotes test +// TODO [SNOW-1348103 - this PR]: what delimiter do we use for : ' versus $$? - we use $$ as tasks +// TODO [SNOW-1348103 - this PR]: escaping single quotes test - don't have to do this with $$ // TODO [SNOW-1348103 - this PR]: validation of JAR (check https://docs.snowflake.com/en/sql-reference/sql/create-function#id6) // TODO [SNOW-1348103 - next PRs]: active warehouse vs validations // TODO [SNOW-1348103 - this PR]: check creation of all functions (using examples and more) @@ -115,7 +116,7 @@ type CreateForJavascriptFunctionOptions struct { } // CreateForPythonFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#python-handler. -// TODO [SNOW-1348103 - this PR]: add aggregate (PuPr) +// TODO [SNOW-1348103 - this PR]: test aggregate func creation // TODO [SNOW-1348103 - this PR]: what about [==] - SDK level or resource level? check also: SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'python'; // TODO [SNOW-1348103 - this PR]: what about preview feature >= ? // TODO [SNOW-1348103 - this PR]: what about '.' for non-inline functions? @@ -192,6 +193,10 @@ type CreateForSQLFunctionOptions struct { } // AlterFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/alter-function. +// TODO [this PR]: can we run multiple sets/unsets? +// TODO [this PR]: add setting EXTERNAL_ACCESS_INTEGRATIONS/SECRETS +// TODO [this PR]: unset EXTERNAL_ACCESS_INTEGRATIONS or SECRETS? +// TODO [this PR]: EXTERNAL_ACCESS_INTEGRATIONS or SECRETS in Javascript or SQL type AlterFunctionOptions struct { alter bool `ddl:"static" sql:"ALTER"` function bool `ddl:"static" sql:"FUNCTION"` @@ -219,6 +224,10 @@ type DropFunctionOptions struct { } // ShowFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/show-user-functions. +// TODO [this PR]: extended in +// TODO [this PR]: check mapping for is_ columns +// TODO [this PR]: add is_data_metric +// TODO [this PR]: example does not list is_data_metric type ShowFunctionOptions struct { show bool `ddl:"static" sql:"SHOW"` userFunctions bool `ddl:"static" sql:"USER FUNCTIONS"` @@ -268,6 +277,8 @@ type Function struct { } // DescribeFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/desc-function. +// TODO [this PR]: create details struct similar to the one in user +// TODO [this PR]: list properties for all types of functions type DescribeFunctionOptions struct { describe bool `ddl:"static" sql:"DESCRIBE"` function bool `ddl:"static" sql:"FUNCTION"` From 8a94f3c09e425b03c35390edc8bc69ce0307702a Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 18:44:51 +0100 Subject: [PATCH 06/63] Remove single quotes from AS and add AGGREGATE for python (WIP) --- pkg/sdk/functions_def.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index 825c1d2551..ff68c82bca 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -87,7 +87,7 @@ var FunctionsDef = g.NewInterface( ListAssignment("EXTERNAL_ACCESS_INTEGRATIONS", "AccountObjectIdentifier", g.ParameterOptions().Parentheses()). ListAssignment("SECRETS", "SecretReference", g.ParameterOptions().Parentheses()). OptionalTextAssignment("TARGET_PATH", g.ParameterOptions().SingleQuotes()). - PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SingleQuotes().SQL("AS")). + PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SQL("AS")). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ValidateValueSet, "Handler"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), @@ -116,7 +116,7 @@ var FunctionsDef = g.NewInterface( PredefinedQueryStructField("NullInputBehavior", "*NullInputBehavior", g.KeywordOptions()). PredefinedQueryStructField("ReturnResultsBehavior", "*ReturnResultsBehavior", g.KeywordOptions()). OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). - PredefinedQueryStructField("FunctionDefinition", "string", g.ParameterOptions().NoEquals().SingleQuotes().SQL("AS").Required()). + PredefinedQueryStructField("FunctionDefinition", "string", g.ParameterOptions().NoEquals().SQL("AS").Required()). WithValidation(g.ValidateValueSet, "FunctionDefinition"). WithValidation(g.ValidIdentifier, "name"), ).CustomOperation( @@ -127,6 +127,7 @@ var FunctionsDef = g.NewInterface( OrReplace(). OptionalSQL("TEMPORARY"). OptionalSQL("SECURE"). + OptionalSQL("AGGREGATE"). SQL("FUNCTION"). IfNotExists(). Identifier("name", g.KindOfT[SchemaObjectIdentifier](), g.IdentifierOptions().Required()). @@ -159,7 +160,7 @@ var FunctionsDef = g.NewInterface( TextAssignment("HANDLER", g.ParameterOptions().SingleQuotes().Required()). ListAssignment("EXTERNAL_ACCESS_INTEGRATIONS", "AccountObjectIdentifier", g.ParameterOptions().Parentheses()). ListAssignment("SECRETS", "SecretReference", g.ParameterOptions().Parentheses()). - PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SingleQuotes().SQL("AS")). + PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SQL("AS")). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ValidateValueSet, "RuntimeVersion"). WithValidation(g.ValidateValueSet, "Handler"). @@ -201,7 +202,7 @@ var FunctionsDef = g.NewInterface( ). TextAssignment("HANDLER", g.ParameterOptions().SingleQuotes().Required()). OptionalTextAssignment("TARGET_PATH", g.ParameterOptions().SingleQuotes()). - PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SingleQuotes().SQL("AS")). + PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SQL("AS")). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ValidateValueSet, "Handler"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"). @@ -230,7 +231,7 @@ var FunctionsDef = g.NewInterface( PredefinedQueryStructField("ReturnResultsBehavior", "*ReturnResultsBehavior", g.KeywordOptions()). OptionalSQL("MEMOIZABLE"). OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). - PredefinedQueryStructField("FunctionDefinition", "string", g.ParameterOptions().NoEquals().SingleQuotes().SQL("AS").Required()). + PredefinedQueryStructField("FunctionDefinition", "string", g.ParameterOptions().NoEquals().SQL("AS").Required()). WithValidation(g.ValidateValueSet, "FunctionDefinition"). WithValidation(g.ValidIdentifier, "name"), ).AlterOperation( From fec412b4ee13fb9457dd5add518c6d88c2b385ee Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 19:03:05 +0100 Subject: [PATCH 07/63] Add mapping from function detail row to properties and start function details struct --- pkg/sdk/functions_ext.go | 80 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 4fe8a9524d..bbd3cdc4bd 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -1,5 +1,85 @@ package sdk +import "strconv" + func (v *Function) ID() SchemaObjectIdentifierWithArguments { return NewSchemaObjectIdentifierWithArguments(v.CatalogName, v.SchemaName, v.Name, v.ArgumentsOld...) } + +// FunctionDetails contains aggregated describe results for the given function. +// TODO [this PR]: fill out +type FunctionDetails struct { + A *StringProperty + B *IntProperty + C *FloatProperty + D *BoolProperty +} + +func functionDetailsFromRows(rows []functionDetailRow) *FunctionDetails { + v := &FunctionDetails{} + for _, row := range rows { + switch row.Property { + case "A": + v.A = row.toStringProperty() + case "B": + v.B = row.toIntProperty() + case "C": + v.C = row.toFloatProperty() + case "D": + v.D = row.toBoolProperty() + } + } + return v +} + +func (r functionDetailRow) toStringProperty() *StringProperty { + prop := &StringProperty{} + if r.Value.Valid { + prop.Value = r.Value.String + } + return prop +} + +func (r functionDetailRow) toIntProperty() *IntProperty { + prop := &IntProperty{} + if r.Value.Valid { + var value *int + v, err := strconv.Atoi(r.Value.String) + if err == nil { + value = &v + } else { + value = nil + } + prop.Value = value + } + return prop +} + +func (r functionDetailRow) toFloatProperty() *FloatProperty { + prop := &FloatProperty{} + if r.Value.Valid { + var value *float64 + v, err := strconv.ParseFloat(r.Value.String, 64) + if err == nil { + value = &v + } else { + value = nil + } + prop.Value = value + } + return prop +} + +func (r functionDetailRow) toBoolProperty() *BoolProperty { + prop := &BoolProperty{} + if r.Value.Valid { + var value bool + if r.Value.String != "" && r.Value.String != "null" { + value = ToBool(r.Value.String) + } else { + value = false + } + prop.Value = value + } + return prop +} From a53949d5cedb2cb7333343f4e8083fb8613edce4 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 19:13:29 +0100 Subject: [PATCH 08/63] Add describe details func --- pkg/sdk/functions_ext.go | 91 +++++++++++++++++++++------------------- pkg/sdk/functions_gen.go | 3 ++ 2 files changed, 52 insertions(+), 42 deletions(-) diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index bbd3cdc4bd..3010287433 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -1,6 +1,9 @@ package sdk -import "strconv" +import ( + "context" + "strconv" +) func (v *Function) ID() SchemaObjectIdentifierWithArguments { return NewSchemaObjectIdentifierWithArguments(v.CatalogName, v.SchemaName, v.Name, v.ArgumentsOld...) @@ -15,7 +18,7 @@ type FunctionDetails struct { D *BoolProperty } -func functionDetailsFromRows(rows []functionDetailRow) *FunctionDetails { +func functionDetailsFromRows(rows []FunctionDetail) *FunctionDetails { v := &FunctionDetails{} for _, row := range rows { switch row.Property { @@ -32,54 +35,58 @@ func functionDetailsFromRows(rows []functionDetailRow) *FunctionDetails { return v } -func (r functionDetailRow) toStringProperty() *StringProperty { - prop := &StringProperty{} - if r.Value.Valid { - prop.Value = r.Value.String +func (v *functions) DescribeDetails(ctx context.Context, id SchemaObjectIdentifierWithArguments) (*FunctionDetails, error) { + rows, err := v.Describe(ctx, id) + if err != nil { + return nil, err } - return prop + return functionDetailsFromRows(rows), nil } -func (r functionDetailRow) toIntProperty() *IntProperty { - prop := &IntProperty{} - if r.Value.Valid { - var value *int - v, err := strconv.Atoi(r.Value.String) - if err == nil { - value = &v - } else { - value = nil - } - prop.Value = value +func (d *FunctionDetail) toStringProperty() *StringProperty { + return &StringProperty{ + Value: d.Value, + Description: d.Property, } - return prop } -func (r functionDetailRow) toFloatProperty() *FloatProperty { - prop := &FloatProperty{} - if r.Value.Valid { - var value *float64 - v, err := strconv.ParseFloat(r.Value.String, 64) - if err == nil { - value = &v - } else { - value = nil - } - prop.Value = value +func (d *FunctionDetail) toIntProperty() *IntProperty { + var value *int + v, err := strconv.Atoi(d.Value) + if err == nil { + value = &v + } else { + value = nil + } + return &IntProperty{ + Value: value, + Description: d.Property, } - return prop } -func (r functionDetailRow) toBoolProperty() *BoolProperty { - prop := &BoolProperty{} - if r.Value.Valid { - var value bool - if r.Value.String != "" && r.Value.String != "null" { - value = ToBool(r.Value.String) - } else { - value = false - } - prop.Value = value +func (d *FunctionDetail) toFloatProperty() *FloatProperty { + var value *float64 + v, err := strconv.ParseFloat(d.Value, 64) + if err == nil { + value = &v + } else { + value = nil + } + return &FloatProperty{ + Value: value, + Description: d.Property, + } +} + +func (d *FunctionDetail) toBoolProperty() *BoolProperty { + var value bool + if d.Value != "" && d.Value != "null" { + value = ToBool(d.Value) + } else { + value = false + } + return &BoolProperty{ + Value: value, + Description: d.Property, } - return prop } diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index 6e667fbe84..b2a6ad4633 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -19,6 +19,9 @@ type Functions interface { Show(ctx context.Context, request *ShowFunctionRequest) ([]Function, error) ShowByID(ctx context.Context, id SchemaObjectIdentifierWithArguments) (*Function, error) Describe(ctx context.Context, id SchemaObjectIdentifierWithArguments) ([]FunctionDetail, error) + + // DescribeDetails is added manually; it returns aggregated describe results for the given function. + DescribeDetails(ctx context.Context, id SchemaObjectIdentifierWithArguments) (*FunctionDetails, error) } // CreateForJavaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#java-handler. From 2459bc3baab5b63c6b76cd889f115fb7374fdb43 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 19:36:22 +0100 Subject: [PATCH 09/63] Generate snowflake object assertions for functions (in current shape) --- .../objectassert/function_snowflake_gen.go | 232 ++++++++++++++++++ .../assert/objectassert/gen/sdk_object_def.go | 5 + pkg/acceptance/helpers/function_client.go | 7 + 3 files changed, 244 insertions(+) create mode 100644 pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go new file mode 100644 index 0000000000..0f0c6dd130 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go @@ -0,0 +1,232 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package objectassert + +// imports modified manually +import ( + "fmt" + "slices" + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" +) + +type FunctionAssert struct { + *assert.SnowflakeObjectAssert[sdk.Function, sdk.SchemaObjectIdentifierWithArguments] +} + +func Function(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments) *FunctionAssert { + t.Helper() + return &FunctionAssert{ + assert.NewSnowflakeObjectAssertWithProvider(sdk.ObjectTypeFunction, id, acc.TestClient().Function.Show), + } +} + +func FunctionFromObject(t *testing.T, function *sdk.Function) *FunctionAssert { + t.Helper() + return &FunctionAssert{ + assert.NewSnowflakeObjectAssertWithObject(sdk.ObjectTypeFunction, function.ID(), function), + } +} + +func (f *FunctionAssert) HasCreatedOn(expected string) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.CreatedOn != expected { + return fmt.Errorf("expected created on: %v; got: %v", expected, o.CreatedOn) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasName(expected string) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.Name != expected { + return fmt.Errorf("expected name: %v; got: %v", expected, o.Name) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasSchemaName(expected string) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.SchemaName != expected { + return fmt.Errorf("expected schema name: %v; got: %v", expected, o.SchemaName) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasIsBuiltin(expected bool) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.IsBuiltin != expected { + return fmt.Errorf("expected is builtin: %v; got: %v", expected, o.IsBuiltin) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasIsAggregate(expected bool) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.IsAggregate != expected { + return fmt.Errorf("expected is aggregate: %v; got: %v", expected, o.IsAggregate) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasIsAnsi(expected bool) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.IsAnsi != expected { + return fmt.Errorf("expected is ansi: %v; got: %v", expected, o.IsAnsi) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasMinNumArguments(expected int) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.MinNumArguments != expected { + return fmt.Errorf("expected min num arguments: %v; got: %v", expected, o.MinNumArguments) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasMaxNumArguments(expected int) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.MaxNumArguments != expected { + return fmt.Errorf("expected max num arguments: %v; got: %v", expected, o.MaxNumArguments) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasArgumentsOld(expected []sdk.DataType) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + // edited manually + if !slices.Equal(o.ArgumentsOld, expected) { + return fmt.Errorf("expected arguments old: %v; got: %v", expected, o.ArgumentsOld) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasArgumentsRaw(expected string) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.ArgumentsRaw != expected { + return fmt.Errorf("expected arguments raw: %v; got: %v", expected, o.ArgumentsRaw) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasDescription(expected string) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.Description != expected { + return fmt.Errorf("expected description: %v; got: %v", expected, o.Description) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasCatalogName(expected string) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.CatalogName != expected { + return fmt.Errorf("expected catalog name: %v; got: %v", expected, o.CatalogName) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasIsTableFunction(expected bool) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.IsTableFunction != expected { + return fmt.Errorf("expected is table function: %v; got: %v", expected, o.IsTableFunction) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasValidForClustering(expected bool) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.ValidForClustering != expected { + return fmt.Errorf("expected valid for clustering: %v; got: %v", expected, o.ValidForClustering) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasIsSecure(expected bool) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.IsSecure != expected { + return fmt.Errorf("expected is secure: %v; got: %v", expected, o.IsSecure) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasIsExternalFunction(expected bool) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.IsExternalFunction != expected { + return fmt.Errorf("expected is external function: %v; got: %v", expected, o.IsExternalFunction) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasLanguage(expected string) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.Language != expected { + return fmt.Errorf("expected language: %v; got: %v", expected, o.Language) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasIsMemoizable(expected bool) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.IsMemoizable != expected { + return fmt.Errorf("expected is memoizable: %v; got: %v", expected, o.IsMemoizable) + } + return nil + }) + return f +} diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go b/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go index 636fb40c8e..3f5a88e827 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go @@ -92,6 +92,11 @@ var allStructs = []SdkObjectDef{ ObjectType: sdk.ObjectTypeAccount, ObjectStruct: sdk.Account{}, }, + { + IdType: "sdk.SchemaObjectIdentifierWithArguments", + ObjectType: sdk.ObjectTypeFunction, + ObjectStruct: sdk.Function{}, + }, } func GetSdkObjectDetails() []genhelpers.SdkObjectDetails { diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 3e6fe5a294..15040c5ec8 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -81,3 +81,10 @@ func (c *FunctionClient) DropFunctionFunc(t *testing.T, id sdk.SchemaObjectIdent require.NoError(t, err) } } + +func (c *FunctionClient) Show(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments) (*sdk.Function, error) { + t.Helper() + ctx := context.Background() + + return c.client().ShowByID(ctx, id) +} From 32eea232647fa94e739c7f28e4aa8a30213c141d Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 19:44:57 +0100 Subject: [PATCH 10/63] Extract sample function definitions in different languages --- pkg/acceptance/helpers/function_client.go | 55 +++++++++++++++++++ pkg/sdk/testint/functions_integration_test.go | 42 +++----------- 2 files changed, 63 insertions(+), 34 deletions(-) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 15040c5ec8..cba2264df1 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -88,3 +88,58 @@ func (c *FunctionClient) Show(t *testing.T, id sdk.SchemaObjectIdentifierWithArg return c.client().ShowByID(ctx, id) } + +func (c *FunctionClient) SampleJavaDefinition(t *testing.T) string { + t.Helper() + + return ` + class TestFunc { + public static String echoVarchar(String x) { + return x; + } + } +` +} + +func (c *FunctionClient) SampleJavaScriptDefinition(t *testing.T) string { + t.Helper() + + return ` + if (D <= 0) { + return 1; + } else { + var result = 1; + for (var i = 2; i <= D; i++) { + result = result * i; + } + return result; + } +` +} + +func (c *FunctionClient) SamplePythonDefinition(t *testing.T) string { + t.Helper() + + return ` + def dump(i): + print("Hello World!") +` +} + +func (c *FunctionClient) SampleScalaDefinition(t *testing.T) string { + t.Helper() + + return ` + class Echo { + def echoVarchar(x : String): String = { + return x + } + } +` +} + +func (c *FunctionClient) SampleSqlDefinition(t *testing.T) string { + t.Helper() + + return "3.141592654::FLOAT" +} diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 5c19d66af4..937632a467 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -37,12 +37,7 @@ func TestInt_CreateFunctions(t *testing.T) { t.Run("create function for Java", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) - definition := ` - class TestFunc { - public static String echoVarchar(String x) { - return x; - } - }` + definition := testClientHelper().Function.SampleJavaDefinition(t) target := fmt.Sprintf("@~/tf-%d.jar", time.Now().Unix()) dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeVARCHAR) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) @@ -66,17 +61,7 @@ func TestInt_CreateFunctions(t *testing.T) { t.Run("create function for Javascript", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) - definition := ` - if (D <= 0) { - return 1; - } else { - var result = 1; - for (var i = 2; i <= D; i++) { - result = result * i; - } - return result; - }` - + definition := testClientHelper().Function.SampleJavaScriptDefinition(t) dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) argument := sdk.NewFunctionArgumentRequest("d", nil).WithArgDataTypeOld(sdk.DataTypeFloat) @@ -97,9 +82,7 @@ func TestInt_CreateFunctions(t *testing.T) { t.Run("create function for Python", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeNumber) - definition := ` -def dump(i): - print("Hello World!")` + definition := testClientHelper().Function.SamplePythonDefinition(t) dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeVariant) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) argument := sdk.NewFunctionArgumentRequest("i", nil).WithArgDataTypeOld(sdk.DataTypeNumber) @@ -120,13 +103,7 @@ def dump(i): t.Run("create function for Scala", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) - definition := ` - class Echo { - def echoVarchar(x : String): String = { - return x - } - }` - + definition := testClientHelper().Function.SampleScalaDefinition(t) argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeVARCHAR) request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), nil, "Echo.echoVarchar"). WithResultDataTypeOld(sdk.DataTypeVARCHAR). @@ -147,8 +124,7 @@ def dump(i): t.Run("create function for SQL", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) - definition := "3.141592654::FLOAT" - + definition := testClientHelper().Function.SampleSqlDefinition(t) dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeFloat) @@ -169,8 +145,7 @@ def dump(i): t.Run("create function for SQL with no arguments", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments() - definition := "3.141592654::FLOAT" - + definition := testClientHelper().Function.SampleSqlDefinition(t) dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition). @@ -240,8 +215,7 @@ func TestInt_OtherFunctions(t *testing.T) { id = testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments() } - definition := "3.141592654::FLOAT" - + definition := testClientHelper().Function.SampleSqlDefinition(t) dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition). @@ -438,7 +412,7 @@ func TestInt_FunctionsShowByID(t *testing.T) { createFunctionForSQLHandle := func(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments) { t.Helper() - definition := "3.141592654::FLOAT" + definition := testClientHelper().Function.SampleSqlDefinition(t) dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition).WithOrReplace(true) From 9d48ab6f2644f7f03ecc5da9c312d4437fdcf077 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 19:48:43 +0100 Subject: [PATCH 11/63] Extract identity function --- pkg/acceptance/helpers/function_client.go | 7 +++++++ pkg/sdk/testint/functions_integration_test.go | 5 +++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index cba2264df1..921701912e 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -2,6 +2,7 @@ package helpers import ( "context" + "fmt" "testing" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" @@ -143,3 +144,9 @@ func (c *FunctionClient) SampleSqlDefinition(t *testing.T) string { return "3.141592654::FLOAT" } + +func (c *FunctionClient) PythonIdentityDefinition(t *testing.T, funcName string, argName string) string { + t.Helper() + + return fmt.Sprintf("def %[1]s(%[2]s): %[2]s", funcName, argName) +} diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 937632a467..ab6812ce5c 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -567,6 +567,7 @@ func TestInt_FunctionsShowByID(t *testing.T) { t.Run(fmt.Sprintf("function returns non detailed data types of arguments for %s", tc), func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifier() argName := "A" + funcName := "identity" dataType, err := datatypes.ParseDataType(tc) require.NoError(t, err) args := []sdk.FunctionArgumentRequest{ @@ -577,10 +578,10 @@ func TestInt_FunctionsShowByID(t *testing.T) { id, *sdk.NewFunctionReturnsRequest().WithResultDataType(*sdk.NewFunctionReturnsResultDataTypeRequest(dataType)), "3.8", - "add", + funcName, ). WithArguments(args). - WithFunctionDefinition(fmt.Sprintf("def add(%[1]s): %[1]s", argName)), + WithFunctionDefinition(testClientHelper().Function.PythonIdentityDefinition(t, funcName, argName)), ) require.NoError(t, err) From d00002b7cd25f5043b8a01a4dca1f9cfaeb8cf55 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Thu, 5 Dec 2024 19:51:21 +0100 Subject: [PATCH 12/63] Add minimal set of creation tests to implement --- pkg/sdk/testint/functions_integration_test.go | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index ab6812ce5c..dc0e4381e7 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -34,6 +34,24 @@ func TestInt_CreateFunctions(t *testing.T) { } } + t.Run("create function for Java - inline minimal", func(t *testing.T) {}) + t.Run("create function for Java - inline full", func(t *testing.T) {}) + t.Run("create function for Java - staged", func(t *testing.T) {}) + + t.Run("create function for JavaScript - inline minimal", func(t *testing.T) {}) + t.Run("create function for JavaScript - inline full", func(t *testing.T) {}) + + t.Run("create function for Python - inline minimal", func(t *testing.T) {}) + t.Run("create function for Python - inline full", func(t *testing.T) {}) + t.Run("create function for Python - staged", func(t *testing.T) {}) + + t.Run("create function for Scala - inline minimal", func(t *testing.T) {}) + t.Run("create function for Scala - inline full", func(t *testing.T) {}) + t.Run("create function for Scala - staged", func(t *testing.T) {}) + + t.Run("create function for SQL - inline minimal", func(t *testing.T) {}) + t.Run("create function for SQL - inline full", func(t *testing.T) {}) + t.Run("create function for Java", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) From 39926341cb857e8d30268793466f7edbe13cbbae Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 11:05:11 +0100 Subject: [PATCH 13/63] Remove TODO --- pkg/sdk/testint/functions_integration_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index dc0e4381e7..660597bc81 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -17,7 +17,6 @@ import ( /* todo: add tests for: - creating functions with different languages (java, javascript, python, scala, sql) from stages using [ TARGET_PATH = '' ] - - execute and execute-immediate for scripting https://docs.snowflake.com/en/sql-reference/sql/execute-immediate */ func TestInt_CreateFunctions(t *testing.T) { From 14e5136c265ca3fd1003ce1ed3a471bce00208f8 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 13:02:54 +0100 Subject: [PATCH 14/63] Fix python procedure --- pkg/acceptance/helpers/function_client.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 921701912e..27e06e72db 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -122,7 +122,7 @@ func (c *FunctionClient) SamplePythonDefinition(t *testing.T) string { t.Helper() return ` - def dump(i): +def dump(i): print("Hello World!") ` } From 25f8ab8920c76e02a5eb6ed5146277f93b85ab4b Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 13:25:03 +0100 Subject: [PATCH 15/63] Go through existing TODOs in definition with manual tests --- pkg/sdk/functions_def.go | 8 +++++--- pkg/sdk/functions_gen.go | 4 ---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index ff68c82bca..9a7f641a74 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -282,7 +282,8 @@ var FunctionsDef = g.NewInterface( Field("is_secure", "sql.NullString"). Field("is_external_function", "string"). Field("language", "string"). - Field("is_memoizable", "sql.NullString"), + Field("is_memoizable", "sql.NullString"). + Field("is_data_metric", "sql.NullString"), g.PlainStruct("Function"). Field("CreatedOn", "string"). Field("Name", "string"). @@ -300,12 +301,13 @@ var FunctionsDef = g.NewInterface( Field("IsSecure", "bool"). Field("IsExternalFunction", "bool"). Field("Language", "string"). - Field("IsMemoizable", "bool"), + Field("IsMemoizable", "bool"). + Field("IsDataMetric", "bool"), g.NewQueryStruct("ShowFunctions"). Show(). SQL("USER FUNCTIONS"). OptionalLike(). - OptionalIn(), + OptionalExtendedIn(), ).ShowByIdOperation().DescribeOperation( g.DescriptionMappingKindSlice, "https://docs.snowflake.com/en/sql-reference/sql/desc-function", diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index b2a6ad4633..c46b02eb2b 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -227,10 +227,6 @@ type DropFunctionOptions struct { } // ShowFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/show-user-functions. -// TODO [this PR]: extended in -// TODO [this PR]: check mapping for is_ columns -// TODO [this PR]: add is_data_metric -// TODO [this PR]: example does not list is_data_metric type ShowFunctionOptions struct { show bool `ddl:"static" sql:"SHOW"` userFunctions bool `ddl:"static" sql:"USER FUNCTIONS"` From bbac3734b6d2d15b98dd1e020c6b554a94b953ea Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 15:10:44 +0100 Subject: [PATCH 16/63] Add target --- pkg/sdk/testint/functions_integration_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 660597bc81..6758bae72a 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -120,12 +120,14 @@ func TestInt_CreateFunctions(t *testing.T) { t.Run("create function for Scala", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) + target := fmt.Sprintf("@~/tf-%d.jar", time.Now().Unix()) definition := testClientHelper().Function.SampleScalaDefinition(t) argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeVARCHAR) request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), nil, "Echo.echoVarchar"). WithResultDataTypeOld(sdk.DataTypeVARCHAR). WithOrReplace(true). WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithTargetPath(target). WithRuntimeVersion("2.12"). WithFunctionDefinition(definition) err := client.Functions.CreateForScala(ctx, request) From 2a1f7074ecfbfeb7b2a41fdf5a87d6d90ab50e5e Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 15:11:42 +0100 Subject: [PATCH 17/63] Returned details for different types of functions --- pkg/sdk/functions_ext.go | 60 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 3010287433..8f904057c3 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -90,3 +90,63 @@ func (d *FunctionDetail) toBoolProperty() *BoolProperty { Description: d.Property, } } + +//python function describe: +//- signature +//- returns +//- language +//- null handling +//- volatility +//- [hidden for secure] body +//- external_access_integrations +//- secrets +//- [hidden for secure] imports +//- [hidden for secure] handler +//- [hidden for secure] runtime_version +//- [hidden for secure] packages +//- [hidden for secure] installed_packages +//- is_aggregate +// +//SQL function describe: +//- signature +//- returns +//- language +//- [hidden for secure] body +// +//scala function describe: +//- signature +//- returns +//- language +//- null handling +//- volatility +//- [hidden for secure] body +//- [hidden for secure] imports +//- [hidden for secure] handler +//- [hidden for secure] target_path - test with target path set +//- [hidden for secure] runtime_version +//- [hidden for secure] packages +//- external_access_integrations +//- secrets +// +//java: +//- signature +//- returns +//- language +//- null handling +//- volatility +//- [hidden for secure] body +//- [hidden for secure] imports +//- [hidden for secure] handler +//- [hidden for secure] target_path +//- [hidden for secure] runtime_version +//- [hidden for secure] packages +//- external_access_integrations +//- secrets +// +//javascript: +//- signature +//- returns +//- language +//- null handling +//- volatility +//- [hidden for secure] body From 3a005779a3a74766a9528745fcede6148f2848b7 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 15:14:59 +0100 Subject: [PATCH 18/63] Update a few comments with knowledge --- pkg/sdk/functions_gen.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index c46b02eb2b..54941cb15c 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -25,7 +25,6 @@ type Functions interface { } // CreateForJavaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#java-handler. -// TODO [SNOW-1348103 - this PR]: test secure (for each type, with owner and underprivileged role), read https://docs.snowflake.com/en/developer-guide/secure-udf-procedure // TODO [SNOW-1348103 - this PR]: test setting the paths for all types (like imports, target paths) // TODO [SNOW-1348103 - this PR]: test weird names for arg name // TODO [SNOW-1348103 - this PR]: test two types of creation for each func @@ -196,10 +195,10 @@ type CreateForSQLFunctionOptions struct { } // AlterFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/alter-function. -// TODO [this PR]: can we run multiple sets/unsets? +// TODO [this PR]: can we run multiple sets/unsets? - yes, parameters + all besides SECURE // TODO [this PR]: add setting EXTERNAL_ACCESS_INTEGRATIONS/SECRETS -// TODO [this PR]: unset EXTERNAL_ACCESS_INTEGRATIONS or SECRETS? -// TODO [this PR]: EXTERNAL_ACCESS_INTEGRATIONS or SECRETS in Javascript or SQL +// TODO [this PR]: unset EXTERNAL_ACCESS_INTEGRATIONS or SECRETS? - works for external access integrations, passes for secrets but does nothing SET to () works for secrets +// TODO [this PR]: EXTERNAL_ACCESS_INTEGRATIONS or SECRETS in Javascript or SQL - not working, working in SCALA though type AlterFunctionOptions struct { alter bool `ddl:"static" sql:"ALTER"` function bool `ddl:"static" sql:"FUNCTION"` From 287ffc509d20567d17e19c6fb555117fad7bbdc0 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 15:21:34 +0100 Subject: [PATCH 19/63] Check weird names --- pkg/sdk/functions_gen.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index 54941cb15c..e363a08f98 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -26,8 +26,7 @@ type Functions interface { // CreateForJavaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#java-handler. // TODO [SNOW-1348103 - this PR]: test setting the paths for all types (like imports, target paths) -// TODO [SNOW-1348103 - this PR]: test weird names for arg name -// TODO [SNOW-1348103 - this PR]: test two types of creation for each func +// TODO [SNOW-1348103 - this PR]: test weird names for arg name - lower/upper if used with double quotes, to upper without quotes, dots, spaces, and both quotes not permitted // TODO [SNOW-1348103 - next PRs]: check data type mappings https://docs.snowflake.com/en/sql-reference/sql/create-function#all-languages (signature + returns) // TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 11.x, 17.x supported, 11.x being the default) // TODO [SNOW-1348103 - this PR]: packages: package_name:version_number; do we validate? - check SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'java'; From a80bdc438e847b8ea2553f3e90a8265bbbb82599 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 15:57:59 +0100 Subject: [PATCH 20/63] Set double quotes in argument names --- pkg/sdk/functions_def.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index 9a7f641a74..c92e84b2cb 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -5,14 +5,14 @@ import g "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/poc/gen //go:generate go run ./poc/main.go var functionArgument = g.NewQueryStruct("FunctionArgument"). - Text("ArgName", g.KeywordOptions().NoQuotes().Required()). + Text("ArgName", g.KeywordOptions().DoubleQuotes().Required()). PredefinedQueryStructField("ArgDataTypeOld", "DataType", g.KeywordOptions().NoQuotes()). PredefinedQueryStructField("ArgDataType", "datatypes.DataType", g.ParameterOptions().NoQuotes().NoEquals().Required()). PredefinedQueryStructField("DefaultValue", "*string", g.ParameterOptions().NoEquals().SQL("DEFAULT")). WithValidation(g.ExactlyOneValueSet, "ArgDataTypeOld", "ArgDataType") var functionColumn = g.NewQueryStruct("FunctionColumn"). - Text("ColumnName", g.KeywordOptions().NoQuotes().Required()). + Text("ColumnName", g.KeywordOptions().DoubleQuotes().Required()). PredefinedQueryStructField("ColumnDataTypeOld", "DataType", g.KeywordOptions().NoQuotes()). PredefinedQueryStructField("ColumnDataType", "datatypes.DataType", g.ParameterOptions().NoQuotes().NoEquals().Required()). WithValidation(g.ExactlyOneValueSet, "ColumnDataTypeOld", "ColumnDataType") From f82e434e601e6b5cfcf9fdc00da90616e6b0ec66 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 16:20:48 +0100 Subject: [PATCH 21/63] Add mapping to describe options --- pkg/sdk/functions_ext.go | 68 ++++++++++++++++++++++++++++++---------- pkg/sdk/functions_gen.go | 2 -- 2 files changed, 51 insertions(+), 19 deletions(-) diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 8f904057c3..ec3994a43c 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -10,29 +10,63 @@ func (v *Function) ID() SchemaObjectIdentifierWithArguments { } // FunctionDetails contains aggregated describe results for the given function. -// TODO [this PR]: fill out +// TODO [this PR]: do we keep *Property or types directly? -> types type FunctionDetails struct { - A *StringProperty - B *IntProperty - C *FloatProperty - D *BoolProperty + Signature *StringProperty + Returns *StringProperty + Language *StringProperty + NullHandling *StringProperty + Volatility *StringProperty + Body *StringProperty + ExternalAccessIntegrations *StringProperty // list + Secrets *StringProperty // map + Imports *StringProperty // list + Handler *StringProperty + RuntimeVersion *StringProperty + Packages *StringProperty // list + InstalledPackages *StringProperty // list + IsAggregate *BoolProperty + TargetPath *StringProperty } -func functionDetailsFromRows(rows []FunctionDetail) *FunctionDetails { +// TODO [this PR]: handle errors +func functionDetailsFromRows(rows []FunctionDetail) (*FunctionDetails, error) { v := &FunctionDetails{} for _, row := range rows { switch row.Property { - case "A": - v.A = row.toStringProperty() - case "B": - v.B = row.toIntProperty() - case "C": - v.C = row.toFloatProperty() - case "D": - v.D = row.toBoolProperty() + case "signature": + v.Signature = row.toStringProperty() + case "returns": + v.Returns = row.toStringProperty() + case "language": + v.Language = row.toStringProperty() + case "null handling": + v.NullHandling = row.toStringProperty() + case "volatility": + v.Volatility = row.toStringProperty() + case "body": + v.Body = row.toStringProperty() + case "external_access_integrations": + v.ExternalAccessIntegrations = row.toStringProperty() + case "secrets": + v.Secrets = row.toStringProperty() + case "imports": + v.Imports = row.toStringProperty() + case "handler": + v.Handler = row.toStringProperty() + case "runtime_version": + v.RuntimeVersion = row.toStringProperty() + case "packages": + v.Packages = row.toStringProperty() + case "installed_packages": + v.InstalledPackages = row.toStringProperty() + case "is_aggregate": + v.IsAggregate = row.toBoolProperty() + case "targetPath": + v.TargetPath = row.toStringProperty() } } - return v + return v, nil } func (v *functions) DescribeDetails(ctx context.Context, id SchemaObjectIdentifierWithArguments) (*FunctionDetails, error) { @@ -40,7 +74,7 @@ func (v *functions) DescribeDetails(ctx context.Context, id SchemaObjectIdentifi if err != nil { return nil, err } - return functionDetailsFromRows(rows), nil + return functionDetailsFromRows(rows) } func (d *FunctionDetail) toStringProperty() *StringProperty { @@ -122,7 +156,7 @@ func (d *FunctionDetail) toBoolProperty() *BoolProperty { //- [hidden for secure] body //- [hidden for secure] imports //- [hidden for secure] handler -//- [hidden for secure] target_path - test with target path set +//- [hidden for secure] target_path //- [hidden for secure] runtime_version //- [hidden for secure] packages //- external_access_integrations diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index e363a08f98..3e76a89a3f 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -274,8 +274,6 @@ type Function struct { } // DescribeFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/desc-function. -// TODO [this PR]: create details struct similar to the one in user -// TODO [this PR]: list properties for all types of functions type DescribeFunctionOptions struct { describe bool `ddl:"static" sql:"DESCRIBE"` function bool `ddl:"static" sql:"FUNCTION"` From a59b407e78d11173b33287269ac4a4955f29d0d3 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 16:23:55 +0100 Subject: [PATCH 22/63] Move TODOs before regeneration --- pkg/sdk/functions_ext.go | 29 +++++++++++++++++++++++++++++ pkg/sdk/functions_gen.go | 21 --------------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index ec3994a43c..5b05f5d85d 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -184,3 +184,32 @@ func (d *FunctionDetail) toBoolProperty() *BoolProperty { //- null handling //- volatility //- [hidden for secure] body + +// CreateForJavaFunctionOptions +// TODO [SNOW-1348103 - this PR]: test setting the paths for all types (like imports, target paths) +// TODO [SNOW-1348103 - this PR]: test weird names for arg name - lower/upper if used with double quotes, to upper without quotes, dots, spaces, and both quotes not permitted +// TODO [SNOW-1348103 - next PRs]: check data type mappings https://docs.snowflake.com/en/sql-reference/sql/create-function#all-languages (signature + returns) +// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 11.x, 17.x supported, 11.x being the default) +// TODO [SNOW-1348103 - this PR]: packages: package_name:version_number; do we validate? - check SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'java'; +// TODO [SNOW-1348103 - next PRs]: add to the resource docs https://docs.snowflake.com/en/sql-reference/sql/create-function#access-control-requirements +// TODO [SNOW-1348103 - this PR]: what delimiter do we use for : ' versus $$? - we use $$ as tasks +// TODO [SNOW-1348103 - this PR]: escaping single quotes test - don't have to do this with $$ +// TODO [SNOW-1348103 - this PR]: validation of JAR (check https://docs.snowflake.com/en/sql-reference/sql/create-function#id6) +// TODO [SNOW-1348103 - next PRs]: active warehouse vs validations +// TODO [SNOW-1348103 - this PR]: check creation of all functions (using examples and more) + +// CreateForPythonFunctionOptions +// TODO [SNOW-1348103 - this PR]: test aggregate func creation +// TODO [SNOW-1348103 - this PR]: what about [==] - SDK level or resource level? check also: SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'python'; +// TODO [SNOW-1348103 - this PR]: what about preview feature >= ? +// TODO [SNOW-1348103 - this PR]: what about '.' for non-inline functions? +// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 3.8, 3.9, 3.10, 3.11 supported, which one is a default?) + +// CreateForScalaFunctionOptions +// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 2.12 supported, which is the default) + +// AlterFunctionOptions +// TODO [this PR]: can we run multiple sets/unsets? - yes, parameters + all besides SECURE +// TODO [this PR]: add setting EXTERNAL_ACCESS_INTEGRATIONS/SECRETS +// TODO [this PR]: unset EXTERNAL_ACCESS_INTEGRATIONS or SECRETS? - works for external access integrations, passes for secrets but does nothing SET to () works for secrets +// TODO [this PR]: EXTERNAL_ACCESS_INTEGRATIONS or SECRETS in Javascript or SQL - not working, working in SCALA though diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index 3e76a89a3f..bbd46364e3 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -25,17 +25,6 @@ type Functions interface { } // CreateForJavaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#java-handler. -// TODO [SNOW-1348103 - this PR]: test setting the paths for all types (like imports, target paths) -// TODO [SNOW-1348103 - this PR]: test weird names for arg name - lower/upper if used with double quotes, to upper without quotes, dots, spaces, and both quotes not permitted -// TODO [SNOW-1348103 - next PRs]: check data type mappings https://docs.snowflake.com/en/sql-reference/sql/create-function#all-languages (signature + returns) -// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 11.x, 17.x supported, 11.x being the default) -// TODO [SNOW-1348103 - this PR]: packages: package_name:version_number; do we validate? - check SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'java'; -// TODO [SNOW-1348103 - next PRs]: add to the resource docs https://docs.snowflake.com/en/sql-reference/sql/create-function#access-control-requirements -// TODO [SNOW-1348103 - this PR]: what delimiter do we use for : ' versus $$? - we use $$ as tasks -// TODO [SNOW-1348103 - this PR]: escaping single quotes test - don't have to do this with $$ -// TODO [SNOW-1348103 - this PR]: validation of JAR (check https://docs.snowflake.com/en/sql-reference/sql/create-function#id6) -// TODO [SNOW-1348103 - next PRs]: active warehouse vs validations -// TODO [SNOW-1348103 - this PR]: check creation of all functions (using examples and more) type CreateForJavaFunctionOptions struct { create bool `ddl:"static" sql:"CREATE"` OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` @@ -117,11 +106,6 @@ type CreateForJavascriptFunctionOptions struct { } // CreateForPythonFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#python-handler. -// TODO [SNOW-1348103 - this PR]: test aggregate func creation -// TODO [SNOW-1348103 - this PR]: what about [==] - SDK level or resource level? check also: SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'python'; -// TODO [SNOW-1348103 - this PR]: what about preview feature >= ? -// TODO [SNOW-1348103 - this PR]: what about '.' for non-inline functions? -// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 3.8, 3.9, 3.10, 3.11 supported, which one is a default?) type CreateForPythonFunctionOptions struct { create bool `ddl:"static" sql:"CREATE"` OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` @@ -148,7 +132,6 @@ type CreateForPythonFunctionOptions struct { } // CreateForScalaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#scala-handler. -// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 2.12 supported, which is the default) type CreateForScalaFunctionOptions struct { create bool `ddl:"static" sql:"CREATE"` OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` @@ -194,10 +177,6 @@ type CreateForSQLFunctionOptions struct { } // AlterFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/alter-function. -// TODO [this PR]: can we run multiple sets/unsets? - yes, parameters + all besides SECURE -// TODO [this PR]: add setting EXTERNAL_ACCESS_INTEGRATIONS/SECRETS -// TODO [this PR]: unset EXTERNAL_ACCESS_INTEGRATIONS or SECRETS? - works for external access integrations, passes for secrets but does nothing SET to () works for secrets -// TODO [this PR]: EXTERNAL_ACCESS_INTEGRATIONS or SECRETS in Javascript or SQL - not working, working in SCALA though type AlterFunctionOptions struct { alter bool `ddl:"static" sql:"ALTER"` function bool `ddl:"static" sql:"FUNCTION"` From 485726d06b7d0d35678d6f162c8700d995b9e20e Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 17:02:38 +0100 Subject: [PATCH 23/63] Update alter for functions --- pkg/sdk/common_types.go | 24 ++++++++++++++++++++++ pkg/sdk/common_types_test.go | 33 +++++++++++++++++++++++++++++ pkg/sdk/functions_def.go | 40 ++++++++++++++++++++++++++++-------- pkg/sdk/functions_ext.go | 6 ------ 4 files changed, 88 insertions(+), 15 deletions(-) diff --git a/pkg/sdk/common_types.go b/pkg/sdk/common_types.go index 7a4975a78e..39ab08e1a8 100644 --- a/pkg/sdk/common_types.go +++ b/pkg/sdk/common_types.go @@ -259,6 +259,7 @@ func ReturnNullValuesPointer(v ReturnNullValues) *ReturnNullValues { return &v } +// TODO [this PR]: Name -> Identifier? type SecretReference struct { VariableName string `ddl:"keyword,single_quotes"` Name string `ddl:"parameter,no_quotes"` @@ -356,6 +357,29 @@ var AllTraceLevels = []TraceLevel{ TraceLevelOff, } +type MetricLevel string + +const ( + MetricLevelAll MetricLevel = "ALL" + MetricLevelNone MetricLevel = "NONE" +) + +func ToMetricLevel(value string) (MetricLevel, error) { + switch strings.ToUpper(value) { + case string(MetricLevelAll): + return MetricLevelAll, nil + case string(MetricLevelNone): + return MetricLevelNone, nil + default: + return "", fmt.Errorf("unknown metric level: %s", value) + } +} + +var AllMetricLevels = []MetricLevel{ + MetricLevelAll, + MetricLevelNone, +} + // StringAllowEmpty is a wrapper on string to allow using empty strings in SQL. type StringAllowEmpty struct { Value string `ddl:"keyword,single_quotes"` diff --git a/pkg/sdk/common_types_test.go b/pkg/sdk/common_types_test.go index 1c0e785a88..1b5e5ecf9f 100644 --- a/pkg/sdk/common_types_test.go +++ b/pkg/sdk/common_types_test.go @@ -294,3 +294,36 @@ func TestToTraceLevel(t *testing.T) { }) } } + +func Test_ToMetricLevel(t *testing.T) { + testCases := []struct { + Name string + Input string + Expected MetricLevel + ExpectedError string + }{ + {Input: string(MetricLevelAll), Expected: MetricLevelAll}, + {Input: string(MetricLevelNone), Expected: MetricLevelNone}, + {Name: "validation: incorrect metric level", Input: "incorrect", ExpectedError: "unknown metric level: incorrect"}, + {Name: "validation: empty input", Input: "", ExpectedError: "unknown metric level: "}, + {Name: "validation: lower case input", Input: "all", Expected: MetricLevelAll}, + } + + for _, tc := range testCases { + tc := tc + name := tc.Name + if name == "" { + name = fmt.Sprintf("%v metric level", tc.Input) + } + t.Run(name, func(t *testing.T) { + value, err := ToMetricLevel(tc.Input) + if tc.ExpectedError != "" { + assert.Empty(t, value) + assert.ErrorContains(t, err, tc.ExpectedError) + } else { + assert.NoError(t, err) + assert.Equal(t, tc.Expected, value) + } + }) + } +} diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index c92e84b2cb..b983a9f00c 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -38,8 +38,10 @@ var functionReturns = g.NewQueryStruct("FunctionReturns"). ).WithValidation(g.ExactlyOneValueSet, "ResultDataType", "Table") var ( - functionImports = g.NewQueryStruct("FunctionImport").Text("Import", g.KeywordOptions().SingleQuotes()) - functionPackages = g.NewQueryStruct("FunctionPackage").Text("Package", g.KeywordOptions().SingleQuotes()) + functionImports = g.NewQueryStruct("FunctionImport").Text("Import", g.KeywordOptions().SingleQuotes()) + functionPackages = g.NewQueryStruct("FunctionPackage").Text("Package", g.KeywordOptions().SingleQuotes()) + functionSecretsListWrapper = g.NewQueryStruct("SecretsList"). + List("SecretsList", "SecretReference", g.ListOptions().Required().MustParentheses()) ) var FunctionsDef = g.NewInterface( @@ -242,19 +244,39 @@ var FunctionsDef = g.NewInterface( IfExists(). Name(). Identifier("RenameTo", g.KindOfTPointer[SchemaObjectIdentifier](), g.IdentifierOptions().SQL("RENAME TO")). - OptionalTextAssignment("SET COMMENT", g.ParameterOptions().SingleQuotes()). - OptionalTextAssignment("SET LOG_LEVEL", g.ParameterOptions().SingleQuotes()). - OptionalTextAssignment("SET TRACE_LEVEL", g.ParameterOptions().SingleQuotes()). + OptionalQueryStructField( + "Set", + g.NewQueryStruct("FunctionSet"). + OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). + ListAssignment("EXTERNAL_ACCESS_INTEGRATIONS", "AccountObjectIdentifier", g.ParameterOptions().Parentheses()). + OptionalQueryStructField("SecretsList", functionSecretsListWrapper, g.ParameterOptions().SQL("SECRETS").Parentheses()). + OptionalBooleanAssignment("ENABLE_CONSOLE_OUTPUT", nil). + OptionalAssignment("LOG_LEVEL", g.KindOfTPointer[LogLevel](), g.ParameterOptions()). + OptionalAssignment("METRIC_LEVEL", g.KindOfTPointer[MetricLevel](), g.ParameterOptions()). + OptionalAssignment("TRACE_LEVEL", g.KindOfTPointer[TraceLevel](), g.ParameterOptions()). + // TODO [this PR]: test setting secrets to empty (nil versus empty list) + WithValidation(g.AtLeastOneValueSet, "Comment", "ExternalAccessIntegrations", "SecretsList", "EnableConsoleOutput", "LogLevel", "MetricLevel", "TraceLevel"), + g.ListOptions().SQL("SET"), + ). + OptionalQueryStructField( + "Unset", + g.NewQueryStruct("FunctionUnset"). + OptionalSQL("COMMENT"). + OptionalSQL("EXTERNAL_ACCESS_INTEGRATIONS"). + OptionalSQL("ENABLE_CONSOLE_OUTPUT"). + OptionalSQL("LOG_LEVEL"). + OptionalSQL("METRIC_LEVEL"). + OptionalSQL("TRACE_LEVEL"). + WithValidation(g.AtLeastOneValueSet, "Comment", "ExternalAccessIntegrations", "EnableConsoleOutput", "LogLevel", "MetricLevel", "TraceLevel"), + g.ListOptions().SQL("UNSET"), + ). OptionalSQL("SET SECURE"). OptionalSQL("UNSET SECURE"). - OptionalSQL("UNSET LOG_LEVEL"). - OptionalSQL("UNSET TRACE_LEVEL"). - OptionalSQL("UNSET COMMENT"). OptionalSetTags(). OptionalUnsetTags(). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ValidIdentifierIfSet, "RenameTo"). - WithValidation(g.ExactlyOneValueSet, "RenameTo", "SetComment", "SetLogLevel", "SetTraceLevel", "SetSecure", "UnsetLogLevel", "UnsetTraceLevel", "UnsetSecure", "UnsetComment", "SetTags", "UnsetTags"), + WithValidation(g.ExactlyOneValueSet, "RenameTo", "Set", "Unset", "SetSecure", "UnsetSecure", "SetTags", "UnsetTags"), ).DropOperation( "https://docs.snowflake.com/en/sql-reference/sql/drop-function", g.NewQueryStruct("DropFunction"). diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 5b05f5d85d..8b0b09e6c3 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -207,9 +207,3 @@ func (d *FunctionDetail) toBoolProperty() *BoolProperty { // CreateForScalaFunctionOptions // TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 2.12 supported, which is the default) - -// AlterFunctionOptions -// TODO [this PR]: can we run multiple sets/unsets? - yes, parameters + all besides SECURE -// TODO [this PR]: add setting EXTERNAL_ACCESS_INTEGRATIONS/SECRETS -// TODO [this PR]: unset EXTERNAL_ACCESS_INTEGRATIONS or SECRETS? - works for external access integrations, passes for secrets but does nothing SET to () works for secrets -// TODO [this PR]: EXTERNAL_ACCESS_INTEGRATIONS or SECRETS in Javascript or SQL - not working, working in SCALA though From 36f01e7ed7a415810e3b92f307afc807285ef8aa Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 17:20:19 +0100 Subject: [PATCH 24/63] Regenerate functions (WIP - no test/compilation) --- pkg/sdk/functions_dto_builders_gen.go | 106 +++++++++++++++++++++----- pkg/sdk/functions_dto_gen.go | 48 ++++++++---- pkg/sdk/functions_gen.go | 74 +++++++++++------- pkg/sdk/functions_impl_gen.go | 52 +++++++++---- pkg/sdk/functions_validations_gen.go | 14 +++- 5 files changed, 218 insertions(+), 76 deletions(-) diff --git a/pkg/sdk/functions_dto_builders_gen.go b/pkg/sdk/functions_dto_builders_gen.go index 3bb40dfd0e..1a9a47ff60 100644 --- a/pkg/sdk/functions_dto_builders_gen.go +++ b/pkg/sdk/functions_dto_builders_gen.go @@ -279,6 +279,11 @@ func (s *CreateForPythonFunctionRequest) WithSecure(Secure bool) *CreateForPytho return s } +func (s *CreateForPythonFunctionRequest) WithAggregate(Aggregate bool) *CreateForPythonFunctionRequest { + s.Aggregate = &Aggregate + return s +} + func (s *CreateForPythonFunctionRequest) WithIfNotExists(IfNotExists bool) *CreateForPythonFunctionRequest { s.IfNotExists = &IfNotExists return s @@ -506,18 +511,13 @@ func (s *AlterFunctionRequest) WithRenameTo(RenameTo SchemaObjectIdentifier) *Al return s } -func (s *AlterFunctionRequest) WithSetComment(SetComment string) *AlterFunctionRequest { - s.SetComment = &SetComment +func (s *AlterFunctionRequest) WithSet(Set FunctionSetRequest) *AlterFunctionRequest { + s.Set = &Set return s } -func (s *AlterFunctionRequest) WithSetLogLevel(SetLogLevel string) *AlterFunctionRequest { - s.SetLogLevel = &SetLogLevel - return s -} - -func (s *AlterFunctionRequest) WithSetTraceLevel(SetTraceLevel string) *AlterFunctionRequest { - s.SetTraceLevel = &SetTraceLevel +func (s *AlterFunctionRequest) WithUnset(Unset FunctionUnsetRequest) *AlterFunctionRequest { + s.Unset = &Unset return s } @@ -531,28 +531,94 @@ func (s *AlterFunctionRequest) WithUnsetSecure(UnsetSecure bool) *AlterFunctionR return s } -func (s *AlterFunctionRequest) WithUnsetLogLevel(UnsetLogLevel bool) *AlterFunctionRequest { - s.UnsetLogLevel = &UnsetLogLevel +func (s *AlterFunctionRequest) WithSetTags(SetTags []TagAssociation) *AlterFunctionRequest { + s.SetTags = SetTags return s } -func (s *AlterFunctionRequest) WithUnsetTraceLevel(UnsetTraceLevel bool) *AlterFunctionRequest { - s.UnsetTraceLevel = &UnsetTraceLevel +func (s *AlterFunctionRequest) WithUnsetTags(UnsetTags []ObjectIdentifier) *AlterFunctionRequest { + s.UnsetTags = UnsetTags return s } -func (s *AlterFunctionRequest) WithUnsetComment(UnsetComment bool) *AlterFunctionRequest { - s.UnsetComment = &UnsetComment +func NewFunctionSetRequest() *FunctionSetRequest { + return &FunctionSetRequest{} +} + +func (s *FunctionSetRequest) WithComment(Comment string) *FunctionSetRequest { + s.Comment = &Comment return s } -func (s *AlterFunctionRequest) WithSetTags(SetTags []TagAssociation) *AlterFunctionRequest { - s.SetTags = SetTags +func (s *FunctionSetRequest) WithExternalAccessIntegrations(ExternalAccessIntegrations []AccountObjectIdentifier) *FunctionSetRequest { + s.ExternalAccessIntegrations = ExternalAccessIntegrations return s } -func (s *AlterFunctionRequest) WithUnsetTags(UnsetTags []ObjectIdentifier) *AlterFunctionRequest { - s.UnsetTags = UnsetTags +func (s *FunctionSetRequest) WithSecretsList(SecretsList SecretsListRequest) *FunctionSetRequest { + s.SecretsList = &SecretsList + return s +} + +func (s *FunctionSetRequest) WithEnableConsoleOutput(EnableConsoleOutput bool) *FunctionSetRequest { + s.EnableConsoleOutput = &EnableConsoleOutput + return s +} + +func (s *FunctionSetRequest) WithLogLevel(LogLevel LogLevel) *FunctionSetRequest { + s.LogLevel = &LogLevel + return s +} + +func (s *FunctionSetRequest) WithMetricLevel(MetricLevel MetricLevel) *FunctionSetRequest { + s.MetricLevel = &MetricLevel + return s +} + +func (s *FunctionSetRequest) WithTraceLevel(TraceLevel TraceLevel) *FunctionSetRequest { + s.TraceLevel = &TraceLevel + return s +} + +func NewSecretsListRequest( + SecretsList []SecretReference, +) *SecretsListRequest { + s := SecretsListRequest{} + s.SecretsList = SecretsList + return &s +} + +func NewFunctionUnsetRequest() *FunctionUnsetRequest { + return &FunctionUnsetRequest{} +} + +func (s *FunctionUnsetRequest) WithComment(Comment bool) *FunctionUnsetRequest { + s.Comment = &Comment + return s +} + +func (s *FunctionUnsetRequest) WithExternalAccessIntegrations(ExternalAccessIntegrations bool) *FunctionUnsetRequest { + s.ExternalAccessIntegrations = &ExternalAccessIntegrations + return s +} + +func (s *FunctionUnsetRequest) WithEnableConsoleOutput(EnableConsoleOutput bool) *FunctionUnsetRequest { + s.EnableConsoleOutput = &EnableConsoleOutput + return s +} + +func (s *FunctionUnsetRequest) WithLogLevel(LogLevel bool) *FunctionUnsetRequest { + s.LogLevel = &LogLevel + return s +} + +func (s *FunctionUnsetRequest) WithMetricLevel(MetricLevel bool) *FunctionUnsetRequest { + s.MetricLevel = &MetricLevel + return s +} + +func (s *FunctionUnsetRequest) WithTraceLevel(TraceLevel bool) *FunctionUnsetRequest { + s.TraceLevel = &TraceLevel return s } @@ -578,7 +644,7 @@ func (s *ShowFunctionRequest) WithLike(Like Like) *ShowFunctionRequest { return s } -func (s *ShowFunctionRequest) WithIn(In In) *ShowFunctionRequest { +func (s *ShowFunctionRequest) WithIn(In ExtendedIn) *ShowFunctionRequest { s.In = &In return s } diff --git a/pkg/sdk/functions_dto_gen.go b/pkg/sdk/functions_dto_gen.go index 4ff74dcd73..9def0d3a85 100644 --- a/pkg/sdk/functions_dto_gen.go +++ b/pkg/sdk/functions_dto_gen.go @@ -93,6 +93,7 @@ type CreateForPythonFunctionRequest struct { OrReplace *bool Temporary *bool Secure *bool + Aggregate *bool IfNotExists *bool name SchemaObjectIdentifier // required Arguments []FunctionArgumentRequest @@ -149,19 +150,38 @@ type CreateForSQLFunctionRequest struct { } type AlterFunctionRequest struct { - IfExists *bool - name SchemaObjectIdentifierWithArguments // required - RenameTo *SchemaObjectIdentifier - SetComment *string - SetLogLevel *string - SetTraceLevel *string - SetSecure *bool - UnsetSecure *bool - UnsetLogLevel *bool - UnsetTraceLevel *bool - UnsetComment *bool - SetTags []TagAssociation - UnsetTags []ObjectIdentifier + IfExists *bool + name SchemaObjectIdentifierWithArguments // required + RenameTo *SchemaObjectIdentifier + Set *FunctionSetRequest + Unset *FunctionUnsetRequest + SetSecure *bool + UnsetSecure *bool + SetTags []TagAssociation + UnsetTags []ObjectIdentifier +} + +type FunctionSetRequest struct { + Comment *string + ExternalAccessIntegrations []AccountObjectIdentifier + SecretsList *SecretsListRequest + EnableConsoleOutput *bool + LogLevel *LogLevel + MetricLevel *MetricLevel + TraceLevel *TraceLevel +} + +type SecretsListRequest struct { + SecretsList []SecretReference // required +} + +type FunctionUnsetRequest struct { + Comment *bool + ExternalAccessIntegrations *bool + EnableConsoleOutput *bool + LogLevel *bool + MetricLevel *bool + TraceLevel *bool } type DropFunctionRequest struct { @@ -171,7 +191,7 @@ type DropFunctionRequest struct { type ShowFunctionRequest struct { Like *Like - In *In + In *ExtendedIn } type DescribeFunctionRequest struct { diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index bbd46364e3..192825db01 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -48,11 +48,11 @@ type CreateForJavaFunctionOptions struct { ExternalAccessIntegrations []AccountObjectIdentifier `ddl:"parameter,parentheses" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` Secrets []SecretReference `ddl:"parameter,parentheses" sql:"SECRETS"` TargetPath *string `ddl:"parameter,single_quotes" sql:"TARGET_PATH"` - FunctionDefinition *string `ddl:"parameter,single_quotes,no_equals" sql:"AS"` + FunctionDefinition *string `ddl:"parameter,no_equals" sql:"AS"` } type FunctionArgument struct { - ArgName string `ddl:"keyword,no_quotes"` + ArgName string `ddl:"keyword,double_quotes"` ArgDataTypeOld DataType `ddl:"keyword,no_quotes"` ArgDataType datatypes.DataType `ddl:"parameter,no_quotes,no_equals"` DefaultValue *string `ddl:"parameter,no_equals" sql:"DEFAULT"` @@ -73,7 +73,7 @@ type FunctionReturnsTable struct { } type FunctionColumn struct { - ColumnName string `ddl:"keyword,no_quotes"` + ColumnName string `ddl:"keyword,double_quotes"` ColumnDataTypeOld DataType `ddl:"keyword,no_quotes"` ColumnDataType datatypes.DataType `ddl:"parameter,no_quotes,no_equals"` } @@ -102,7 +102,7 @@ type CreateForJavascriptFunctionOptions struct { NullInputBehavior *NullInputBehavior `ddl:"keyword"` ReturnResultsBehavior *ReturnResultsBehavior `ddl:"keyword"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` - FunctionDefinition string `ddl:"parameter,single_quotes,no_equals" sql:"AS"` + FunctionDefinition string `ddl:"parameter,no_equals" sql:"AS"` } // CreateForPythonFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#python-handler. @@ -111,6 +111,7 @@ type CreateForPythonFunctionOptions struct { OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` Temporary *bool `ddl:"keyword" sql:"TEMPORARY"` Secure *bool `ddl:"keyword" sql:"SECURE"` + Aggregate *bool `ddl:"keyword" sql:"AGGREGATE"` function bool `ddl:"static" sql:"FUNCTION"` IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` name SchemaObjectIdentifier `ddl:"identifier"` @@ -128,7 +129,7 @@ type CreateForPythonFunctionOptions struct { Handler string `ddl:"parameter,single_quotes" sql:"HANDLER"` ExternalAccessIntegrations []AccountObjectIdentifier `ddl:"parameter,parentheses" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` Secrets []SecretReference `ddl:"parameter,parentheses" sql:"SECRETS"` - FunctionDefinition *string `ddl:"parameter,single_quotes,no_equals" sql:"AS"` + FunctionDefinition *string `ddl:"parameter,no_equals" sql:"AS"` } // CreateForScalaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#scala-handler. @@ -155,7 +156,7 @@ type CreateForScalaFunctionOptions struct { Packages []FunctionPackage `ddl:"parameter,parentheses" sql:"PACKAGES"` Handler string `ddl:"parameter,single_quotes" sql:"HANDLER"` TargetPath *string `ddl:"parameter,single_quotes" sql:"TARGET_PATH"` - FunctionDefinition *string `ddl:"parameter,single_quotes,no_equals" sql:"AS"` + FunctionDefinition *string `ddl:"parameter,no_equals" sql:"AS"` } // CreateForSQLFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#sql-handler. @@ -173,26 +174,45 @@ type CreateForSQLFunctionOptions struct { ReturnResultsBehavior *ReturnResultsBehavior `ddl:"keyword"` Memoizable *bool `ddl:"keyword" sql:"MEMOIZABLE"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` - FunctionDefinition string `ddl:"parameter,single_quotes,no_equals" sql:"AS"` + FunctionDefinition string `ddl:"parameter,no_equals" sql:"AS"` } // AlterFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/alter-function. type AlterFunctionOptions struct { - alter bool `ddl:"static" sql:"ALTER"` - function bool `ddl:"static" sql:"FUNCTION"` - IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` - name SchemaObjectIdentifierWithArguments `ddl:"identifier"` - RenameTo *SchemaObjectIdentifier `ddl:"identifier" sql:"RENAME TO"` - SetComment *string `ddl:"parameter,single_quotes" sql:"SET COMMENT"` - SetLogLevel *string `ddl:"parameter,single_quotes" sql:"SET LOG_LEVEL"` - SetTraceLevel *string `ddl:"parameter,single_quotes" sql:"SET TRACE_LEVEL"` - SetSecure *bool `ddl:"keyword" sql:"SET SECURE"` - UnsetSecure *bool `ddl:"keyword" sql:"UNSET SECURE"` - UnsetLogLevel *bool `ddl:"keyword" sql:"UNSET LOG_LEVEL"` - UnsetTraceLevel *bool `ddl:"keyword" sql:"UNSET TRACE_LEVEL"` - UnsetComment *bool `ddl:"keyword" sql:"UNSET COMMENT"` - SetTags []TagAssociation `ddl:"keyword" sql:"SET TAG"` - UnsetTags []ObjectIdentifier `ddl:"keyword" sql:"UNSET TAG"` + alter bool `ddl:"static" sql:"ALTER"` + function bool `ddl:"static" sql:"FUNCTION"` + IfExists *bool `ddl:"keyword" sql:"IF EXISTS"` + name SchemaObjectIdentifierWithArguments `ddl:"identifier"` + RenameTo *SchemaObjectIdentifier `ddl:"identifier" sql:"RENAME TO"` + Set *FunctionSet `ddl:"list" sql:"SET"` + Unset *FunctionUnset `ddl:"list" sql:"UNSET"` + SetSecure *bool `ddl:"keyword" sql:"SET SECURE"` + UnsetSecure *bool `ddl:"keyword" sql:"UNSET SECURE"` + SetTags []TagAssociation `ddl:"keyword" sql:"SET TAG"` + UnsetTags []ObjectIdentifier `ddl:"keyword" sql:"UNSET TAG"` +} + +type FunctionSet struct { + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` + ExternalAccessIntegrations []AccountObjectIdentifier `ddl:"parameter,parentheses" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` + SecretsList *SecretsList `ddl:"parameter,parentheses" sql:"SECRETS"` + EnableConsoleOutput *bool `ddl:"parameter" sql:"ENABLE_CONSOLE_OUTPUT"` + LogLevel *LogLevel `ddl:"parameter" sql:"LOG_LEVEL"` + MetricLevel *MetricLevel `ddl:"parameter" sql:"METRIC_LEVEL"` + TraceLevel *TraceLevel `ddl:"parameter" sql:"TRACE_LEVEL"` +} + +type SecretsList struct { + SecretsList []SecretReference `ddl:"list,must_parentheses"` +} + +type FunctionUnset struct { + Comment *bool `ddl:"keyword" sql:"COMMENT"` + ExternalAccessIntegrations *bool `ddl:"keyword" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` + EnableConsoleOutput *bool `ddl:"keyword" sql:"ENABLE_CONSOLE_OUTPUT"` + LogLevel *bool `ddl:"keyword" sql:"LOG_LEVEL"` + MetricLevel *bool `ddl:"keyword" sql:"METRIC_LEVEL"` + TraceLevel *bool `ddl:"keyword" sql:"TRACE_LEVEL"` } // DropFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/drop-function. @@ -205,10 +225,10 @@ type DropFunctionOptions struct { // ShowFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/show-user-functions. type ShowFunctionOptions struct { - show bool `ddl:"static" sql:"SHOW"` - userFunctions bool `ddl:"static" sql:"USER FUNCTIONS"` - Like *Like `ddl:"keyword" sql:"LIKE"` - In *In `ddl:"keyword" sql:"IN"` + show bool `ddl:"static" sql:"SHOW"` + userFunctions bool `ddl:"static" sql:"USER FUNCTIONS"` + Like *Like `ddl:"keyword" sql:"LIKE"` + In *ExtendedIn `ddl:"keyword" sql:"IN"` } type functionRow struct { @@ -229,6 +249,7 @@ type functionRow struct { IsExternalFunction string `db:"is_external_function"` Language string `db:"language"` IsMemoizable sql.NullString `db:"is_memoizable"` + IsDataMetric sql.NullString `db:"is_data_metric"` } type Function struct { @@ -250,6 +271,7 @@ type Function struct { IsExternalFunction bool Language string IsMemoizable bool + IsDataMetric bool } // DescribeFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/desc-function. diff --git a/pkg/sdk/functions_impl_gen.go b/pkg/sdk/functions_impl_gen.go index bd3b29fae9..901d3e0d14 100644 --- a/pkg/sdk/functions_impl_gen.go +++ b/pkg/sdk/functions_impl_gen.go @@ -60,7 +60,7 @@ func (v *functions) Show(ctx context.Context, request *ShowFunctionRequest) ([]F } func (v *functions) ShowByID(ctx context.Context, id SchemaObjectIdentifierWithArguments) (*Function, error) { - functions, err := v.Show(ctx, NewShowFunctionRequest().WithIn(In{Schema: id.SchemaId()}).WithLike(Like{String(id.Name())})) + functions, err := v.Show(ctx, NewShowFunctionRequest().WithIn(ExtendedIn{In: In{Schema: id.SchemaId()}}).WithLike(Like{String(id.Name())})) if err != nil { return nil, err } @@ -210,6 +210,7 @@ func (r *CreateForPythonFunctionRequest) toOpts() *CreateForPythonFunctionOption OrReplace: r.OrReplace, Temporary: r.Temporary, Secure: r.Secure, + Aggregate: r.Aggregate, IfNotExists: r.IfNotExists, name: r.name, @@ -387,19 +388,39 @@ func (r *CreateForSQLFunctionRequest) toOpts() *CreateForSQLFunctionOptions { func (r *AlterFunctionRequest) toOpts() *AlterFunctionOptions { opts := &AlterFunctionOptions{ - IfExists: r.IfExists, - name: r.name, - RenameTo: r.RenameTo, - SetComment: r.SetComment, - SetLogLevel: r.SetLogLevel, - SetTraceLevel: r.SetTraceLevel, - SetSecure: r.SetSecure, - UnsetSecure: r.UnsetSecure, - UnsetLogLevel: r.UnsetLogLevel, - UnsetTraceLevel: r.UnsetTraceLevel, - UnsetComment: r.UnsetComment, - SetTags: r.SetTags, - UnsetTags: r.UnsetTags, + IfExists: r.IfExists, + name: r.name, + RenameTo: r.RenameTo, + SetSecure: r.SetSecure, + UnsetSecure: r.UnsetSecure, + SetTags: r.SetTags, + UnsetTags: r.UnsetTags, + } + if r.Set != nil { + opts.Set = &FunctionSet{ + Comment: r.Set.Comment, + ExternalAccessIntegrations: r.Set.ExternalAccessIntegrations, + + EnableConsoleOutput: r.Set.EnableConsoleOutput, + LogLevel: r.Set.LogLevel, + MetricLevel: r.Set.MetricLevel, + TraceLevel: r.Set.TraceLevel, + } + if r.Set.SecretsList != nil { + opts.Set.SecretsList = &SecretsList{ + SecretsList: r.Set.SecretsList.SecretsList, + } + } + } + if r.Unset != nil { + opts.Unset = &FunctionUnset{ + Comment: r.Unset.Comment, + ExternalAccessIntegrations: r.Unset.ExternalAccessIntegrations, + EnableConsoleOutput: r.Unset.EnableConsoleOutput, + LogLevel: r.Unset.LogLevel, + MetricLevel: r.Unset.MetricLevel, + TraceLevel: r.Unset.TraceLevel, + } } return opts } @@ -453,6 +474,9 @@ func (r functionRow) convert() *Function { if r.IsMemoizable.Valid { e.IsMemoizable = r.IsMemoizable.String == "Y" } + if r.IsDataMetric.Valid { + e.IsDataMetric = r.IsDataMetric.String == "Y" + } return e } diff --git a/pkg/sdk/functions_validations_gen.go b/pkg/sdk/functions_validations_gen.go index 78970158e8..5ffb52ce1d 100644 --- a/pkg/sdk/functions_validations_gen.go +++ b/pkg/sdk/functions_validations_gen.go @@ -258,8 +258,18 @@ func (opts *AlterFunctionOptions) validate() error { if opts.RenameTo != nil && !ValidObjectIdentifier(opts.RenameTo) { errs = append(errs, ErrInvalidObjectIdentifier) } - if !exactlyOneValueSet(opts.RenameTo, opts.SetComment, opts.SetLogLevel, opts.SetTraceLevel, opts.SetSecure, opts.UnsetLogLevel, opts.UnsetTraceLevel, opts.UnsetSecure, opts.UnsetComment, opts.SetTags, opts.UnsetTags) { - errs = append(errs, errExactlyOneOf("AlterFunctionOptions", "RenameTo", "SetComment", "SetLogLevel", "SetTraceLevel", "SetSecure", "UnsetLogLevel", "UnsetTraceLevel", "UnsetSecure", "UnsetComment", "SetTags", "UnsetTags")) + if !exactlyOneValueSet(opts.RenameTo, opts.Set, opts.Unset, opts.SetSecure, opts.UnsetSecure, opts.SetTags, opts.UnsetTags) { + errs = append(errs, errExactlyOneOf("AlterFunctionOptions", "RenameTo", "Set", "Unset", "SetSecure", "UnsetSecure", "SetTags", "UnsetTags")) + } + if valueSet(opts.Set) { + if !anyValueSet(opts.Set.Comment, opts.Set.ExternalAccessIntegrations, opts.Set.SecretsList, opts.Set.EnableConsoleOutput, opts.Set.LogLevel, opts.Set.MetricLevel, opts.Set.TraceLevel) { + errs = append(errs, errAtLeastOneOf("AlterFunctionOptions.Set", "Comment", "ExternalAccessIntegrations", "SecretsList", "EnableConsoleOutput", "LogLevel", "MetricLevel", "TraceLevel")) + } + } + if valueSet(opts.Unset) { + if !anyValueSet(opts.Unset.Comment, opts.Unset.ExternalAccessIntegrations, opts.Unset.EnableConsoleOutput, opts.Unset.LogLevel, opts.Unset.MetricLevel, opts.Unset.TraceLevel) { + errs = append(errs, errAtLeastOneOf("AlterFunctionOptions.Unset", "Comment", "ExternalAccessIntegrations", "EnableConsoleOutput", "LogLevel", "MetricLevel", "TraceLevel")) + } } return JoinErrors(errs...) } From db226a6e8d0d17696f20bc027c5cf90887dc3941 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 18:03:10 +0100 Subject: [PATCH 25/63] Reorganize, Rename, and Pass unit tests --- pkg/sdk/functions_def.go | 6 +- pkg/sdk/functions_ext.go | 2 + pkg/sdk/functions_gen.go | 6 +- pkg/sdk/functions_gen_test.go | 438 +++++++++++++++++++--------------- 4 files changed, 252 insertions(+), 200 deletions(-) diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index b983a9f00c..e248dd6746 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -251,9 +251,9 @@ var FunctionsDef = g.NewInterface( ListAssignment("EXTERNAL_ACCESS_INTEGRATIONS", "AccountObjectIdentifier", g.ParameterOptions().Parentheses()). OptionalQueryStructField("SecretsList", functionSecretsListWrapper, g.ParameterOptions().SQL("SECRETS").Parentheses()). OptionalBooleanAssignment("ENABLE_CONSOLE_OUTPUT", nil). - OptionalAssignment("LOG_LEVEL", g.KindOfTPointer[LogLevel](), g.ParameterOptions()). - OptionalAssignment("METRIC_LEVEL", g.KindOfTPointer[MetricLevel](), g.ParameterOptions()). - OptionalAssignment("TRACE_LEVEL", g.KindOfTPointer[TraceLevel](), g.ParameterOptions()). + OptionalAssignment("LOG_LEVEL", g.KindOfTPointer[LogLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("METRIC_LEVEL", g.KindOfTPointer[MetricLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("TRACE_LEVEL", g.KindOfTPointer[TraceLevel](), g.ParameterOptions().SingleQuotes()). // TODO [this PR]: test setting secrets to empty (nil versus empty list) WithValidation(g.AtLeastOneValueSet, "Comment", "ExternalAccessIntegrations", "SecretsList", "EnableConsoleOutput", "LogLevel", "MetricLevel", "TraceLevel"), g.ListOptions().SQL("SET"), diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 8b0b09e6c3..2a4aae2fc7 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -185,6 +185,8 @@ func (d *FunctionDetail) toBoolProperty() *BoolProperty { //- volatility //- [hidden for secure] body +// TODO [SNOW-1348103 - this PR]: test creation with specifying parameters in the same query + // CreateForJavaFunctionOptions // TODO [SNOW-1348103 - this PR]: test setting the paths for all types (like imports, target paths) // TODO [SNOW-1348103 - this PR]: test weird names for arg name - lower/upper if used with double quotes, to upper without quotes, dots, spaces, and both quotes not permitted diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index 192825db01..eea83e59dd 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -197,9 +197,9 @@ type FunctionSet struct { ExternalAccessIntegrations []AccountObjectIdentifier `ddl:"parameter,parentheses" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` SecretsList *SecretsList `ddl:"parameter,parentheses" sql:"SECRETS"` EnableConsoleOutput *bool `ddl:"parameter" sql:"ENABLE_CONSOLE_OUTPUT"` - LogLevel *LogLevel `ddl:"parameter" sql:"LOG_LEVEL"` - MetricLevel *MetricLevel `ddl:"parameter" sql:"METRIC_LEVEL"` - TraceLevel *TraceLevel `ddl:"parameter" sql:"TRACE_LEVEL"` + LogLevel *LogLevel `ddl:"parameter,single_quotes" sql:"LOG_LEVEL"` + MetricLevel *MetricLevel `ddl:"parameter,single_quotes" sql:"METRIC_LEVEL"` + TraceLevel *TraceLevel `ddl:"parameter,single_quotes" sql:"TRACE_LEVEL"` } type SecretsList struct { diff --git a/pkg/sdk/functions_gen_test.go b/pkg/sdk/functions_gen_test.go index 95c21d9204..30cb1cf3b3 100644 --- a/pkg/sdk/functions_gen_test.go +++ b/pkg/sdk/functions_gen_test.go @@ -1,9 +1,14 @@ package sdk import ( + "fmt" "testing" ) +func wrapFunctionDefinition(def string) string { + return fmt.Sprintf(`$$%s$$`, def) +} + func TestFunctions_CreateForJava(t *testing.T) { id := randomSchemaObjectIdentifier() @@ -18,12 +23,29 @@ func TestFunctions_CreateForJava(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: incorrect identifier", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) + t.Run("validation: [opts.Handler] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Returns = FunctionReturns{ + ResultDataType: &FunctionReturnsResultDataType{ + ResultDataType: dataTypeVarchar, + }, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForJavaFunctionOptions", "Handler")) + }) + + t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { + opts := defaultOpts() + opts.OrReplace = Bool(true) + opts.IfNotExists = Bool(true) + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateForJavaFunctionOptions", "OrReplace", "IfNotExists")) + }) + t.Run("validation: exactly one field from [opts.Arguments.ArgDataTypeOld opts.Arguments.ArgDataType] should be present", func(t *testing.T) { opts := defaultOpts() opts.Arguments = []FunctionArgument{ @@ -49,7 +71,7 @@ func TestFunctions_CreateForJava(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForJavaFunctionOptions.Arguments", "ArgDataTypeOld", "ArgDataType")) }) - t.Run("validation: returns", func(t *testing.T) { + t.Run("validation: exactly one field from [opts.Returns.ResultDataType opts.Returns.Table] should be present", func(t *testing.T) { opts := defaultOpts() opts.Returns = FunctionReturns{} assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForJavaFunctionOptions.Returns", "ResultDataType", "Table")) @@ -124,16 +146,6 @@ func TestFunctions_CreateForJava(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, NewError("IMPORTS must not be empty when AS is nil")) }) - t.Run("validation: options are missing", func(t *testing.T) { - opts := defaultOpts() - opts.Returns = FunctionReturns{ - ResultDataType: &FunctionReturnsResultDataType{ - ResultDataType: dataTypeVarchar, - }, - } - assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForJavaFunctionOptions", "Handler")) - }) - // TODO [SNOW-1348103]: remove with old function removal for V1 t.Run("all options - old data types", func(t *testing.T) { opts := defaultOpts() @@ -196,8 +208,8 @@ func TestFunctions_CreateForJava(t *testing.T) { }, } opts.TargetPath = String("@~/testfunc.jar") - opts.FunctionDefinition = String("return id + name;") - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s (id NUMBER, name VARCHAR DEFAULT 'test') COPY GRANTS RETURNS TABLE (country_code VARCHAR, country_name VARCHAR) NOT NULL LANGUAGE JAVA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@~/my_decrement_udf_package_dir/my_decrement_udf_jar.jar') PACKAGES = ('com.snowflake:snowpark:1.2.0') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) TARGET_PATH = '@~/testfunc.jar' AS 'return id + name;'`, id.FullyQualifiedName()) + opts.FunctionDefinition = String(wrapFunctionDefinition("return id + name;")) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("id" NUMBER, "name" VARCHAR DEFAULT 'test') COPY GRANTS RETURNS TABLE ("country_code" VARCHAR, "country_name" VARCHAR) NOT NULL LANGUAGE JAVA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@~/my_decrement_udf_package_dir/my_decrement_udf_jar.jar') PACKAGES = ('com.snowflake:snowpark:1.2.0') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) TARGET_PATH = '@~/testfunc.jar' AS $$return id + name;$$`, id.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { @@ -261,8 +273,8 @@ func TestFunctions_CreateForJava(t *testing.T) { }, } opts.TargetPath = String("@~/testfunc.jar") - opts.FunctionDefinition = String("return id + name;") - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s (id NUMBER(36, 2), name VARCHAR(100) DEFAULT 'test') COPY GRANTS RETURNS TABLE (country_code VARCHAR(100), country_name VARCHAR(100)) NOT NULL LANGUAGE JAVA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@~/my_decrement_udf_package_dir/my_decrement_udf_jar.jar') PACKAGES = ('com.snowflake:snowpark:1.2.0') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) TARGET_PATH = '@~/testfunc.jar' AS 'return id + name;'`, id.FullyQualifiedName()) + opts.FunctionDefinition = String(wrapFunctionDefinition("return id + name;")) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("id" NUMBER(36, 2), "name" VARCHAR(100) DEFAULT 'test') COPY GRANTS RETURNS TABLE ("country_code" VARCHAR(100), "country_name" VARCHAR(100)) NOT NULL LANGUAGE JAVA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@~/my_decrement_udf_package_dir/my_decrement_udf_jar.jar') PACKAGES = ('com.snowflake:snowpark:1.2.0') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) TARGET_PATH = '@~/testfunc.jar' AS $$return id + name;$$`, id.FullyQualifiedName()) }) } @@ -280,7 +292,17 @@ func TestFunctions_CreateForJavascript(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: incorrect identifier", func(t *testing.T) { + t.Run("validation: [opts.FunctionDefinition] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Returns = FunctionReturns{ + ResultDataType: &FunctionReturnsResultDataType{ + ResultDataType: dataTypeVarchar, + }, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForJavascriptFunctionOptions", "FunctionDefinition")) + }) + + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) @@ -311,6 +333,21 @@ func TestFunctions_CreateForJavascript(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForJavascriptFunctionOptions.Arguments", "ArgDataTypeOld", "ArgDataType")) }) + t.Run("validation: exactly one field from [opts.Returns.ResultDataType opts.Returns.Table] should be present", func(t *testing.T) { + opts := defaultOpts() + opts.Returns = FunctionReturns{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForJavascriptFunctionOptions.Returns", "ResultDataType", "Table")) + }) + + t.Run("validation: exactly one field from [opts.Returns.ResultDataType opts.Returns.Table] should be present - two present", func(t *testing.T) { + opts := defaultOpts() + opts.Returns = FunctionReturns{ + ResultDataType: &FunctionReturnsResultDataType{}, + Table: &FunctionReturnsTable{}, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForJavascriptFunctionOptions.Returns", "ResultDataType", "Table")) + }) + t.Run("validation: exactly one field from [opts.Returns.ResultDataType.ResultDataTypeOld opts.Returns.ResultDataType.ResultDataType] should be present", func(t *testing.T) { opts := defaultOpts() opts.Returns = FunctionReturns{ @@ -367,22 +404,6 @@ func TestFunctions_CreateForJavascript(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForSQLFunctionOptions.Returns.Table.Columns", "ColumnDataTypeOld", "ColumnDataType")) }) - t.Run("validation: returns", func(t *testing.T) { - opts := defaultOpts() - opts.Returns = FunctionReturns{} - assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForJavascriptFunctionOptions.Returns", "ResultDataType", "Table")) - }) - - t.Run("validation: options are missing", func(t *testing.T) { - opts := defaultOpts() - opts.Returns = FunctionReturns{ - ResultDataType: &FunctionReturnsResultDataType{ - ResultDataType: dataTypeVarchar, - }, - } - assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForJavascriptFunctionOptions", "FunctionDefinition")) - }) - // TODO [SNOW-1348103]: remove with old function removal for V1 t.Run("all options - old data types", func(t *testing.T) { opts := defaultOpts() @@ -406,8 +427,8 @@ func TestFunctions_CreateForJavascript(t *testing.T) { opts.NullInputBehavior = NullInputBehaviorPointer(NullInputBehaviorCalledOnNullInput) opts.ReturnResultsBehavior = ReturnResultsBehaviorPointer(ReturnResultsBehaviorImmutable) opts.Comment = String("comment") - opts.FunctionDefinition = "return 1;" - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s (d FLOAT DEFAULT 1.0) COPY GRANTS RETURNS FLOAT NOT NULL LANGUAGE JAVASCRIPT CALLED ON NULL INPUT IMMUTABLE COMMENT = 'comment' AS 'return 1;'`, id.FullyQualifiedName()) + opts.FunctionDefinition = wrapFunctionDefinition("return 1;") + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("d" FLOAT DEFAULT 1.0) COPY GRANTS RETURNS FLOAT NOT NULL LANGUAGE JAVASCRIPT CALLED ON NULL INPUT IMMUTABLE COMMENT = 'comment' AS $$return 1;$$`, id.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { @@ -432,8 +453,8 @@ func TestFunctions_CreateForJavascript(t *testing.T) { opts.NullInputBehavior = NullInputBehaviorPointer(NullInputBehaviorCalledOnNullInput) opts.ReturnResultsBehavior = ReturnResultsBehaviorPointer(ReturnResultsBehaviorImmutable) opts.Comment = String("comment") - opts.FunctionDefinition = "return 1;" - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s (d FLOAT DEFAULT 1.0) COPY GRANTS RETURNS FLOAT NOT NULL LANGUAGE JAVASCRIPT CALLED ON NULL INPUT IMMUTABLE COMMENT = 'comment' AS 'return 1;'`, id.FullyQualifiedName()) + opts.FunctionDefinition = wrapFunctionDefinition("return 1;") + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("d" FLOAT DEFAULT 1.0) COPY GRANTS RETURNS FLOAT NOT NULL LANGUAGE JAVASCRIPT CALLED ON NULL INPUT IMMUTABLE COMMENT = 'comment' AS $$return 1;$$`, id.FullyQualifiedName()) }) } @@ -451,12 +472,39 @@ func TestFunctions_CreateForPython(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: incorrect identifier", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) + t.Run("validation: [opts.RuntimeVersion] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Returns = FunctionReturns{ + ResultDataType: &FunctionReturnsResultDataType{ + ResultDataType: dataTypeVarchar, + }, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForPythonFunctionOptions", "RuntimeVersion")) + }) + + t.Run("validation: [opts.Handler] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Returns = FunctionReturns{ + ResultDataType: &FunctionReturnsResultDataType{ + ResultDataType: dataTypeVarchar, + }, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForPythonFunctionOptions", "Handler")) + }) + + t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { + opts := defaultOpts() + opts.OrReplace = Bool(true) + opts.IfNotExists = Bool(true) + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateForPythonFunctionOptions", "OrReplace", "IfNotExists")) + }) + t.Run("validation: exactly one field from [opts.Arguments.ArgDataTypeOld opts.Arguments.ArgDataType] should be present", func(t *testing.T) { opts := defaultOpts() opts.Arguments = []FunctionArgument{ @@ -482,6 +530,21 @@ func TestFunctions_CreateForPython(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForPythonFunctionOptions.Arguments", "ArgDataTypeOld", "ArgDataType")) }) + t.Run("validation: exactly one field from [opts.Returns.ResultDataType opts.Returns.Table] should be present", func(t *testing.T) { + opts := defaultOpts() + opts.Returns = FunctionReturns{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForPythonFunctionOptions.Returns", "ResultDataType", "Table")) + }) + + t.Run("validation: exactly one field from [opts.Returns.ResultDataType opts.Returns.Table] should be present - two present", func(t *testing.T) { + opts := defaultOpts() + opts.Returns = FunctionReturns{ + ResultDataType: &FunctionReturnsResultDataType{}, + Table: &FunctionReturnsTable{}, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForPythonFunctionOptions.Returns", "ResultDataType", "Table")) + }) + t.Run("validation: exactly one field from [opts.Returns.ResultDataType.ResultDataTypeOld opts.Returns.ResultDataType.ResultDataType] should be present", func(t *testing.T) { opts := defaultOpts() opts.Returns = FunctionReturns{ @@ -538,23 +601,6 @@ func TestFunctions_CreateForPython(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForSQLFunctionOptions.Returns.Table.Columns", "ColumnDataTypeOld", "ColumnDataType")) }) - t.Run("validation: returns", func(t *testing.T) { - opts := defaultOpts() - opts.Returns = FunctionReturns{} - assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForPythonFunctionOptions.Returns", "ResultDataType", "Table")) - }) - - t.Run("validation: options are missing", func(t *testing.T) { - opts := defaultOpts() - opts.Returns = FunctionReturns{ - ResultDataType: &FunctionReturnsResultDataType{ - ResultDataType: dataTypeVarchar, - }, - } - assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForPythonFunctionOptions", "RuntimeVersion")) - assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForPythonFunctionOptions", "Handler")) - }) - t.Run("validation: function definition", func(t *testing.T) { opts := defaultOpts() opts.Packages = []FunctionPackage{ @@ -619,8 +665,8 @@ func TestFunctions_CreateForPython(t *testing.T) { Name: "name2", }, } - opts.FunctionDefinition = String("import numpy as np") - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s (i NUMBER DEFAULT 1) COPY GRANTS RETURNS VARIANT NOT NULL LANGUAGE PYTHON CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '3.8' COMMENT = 'comment' IMPORTS = ('numpy', 'pandas') PACKAGES = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) AS 'import numpy as np'`, id.FullyQualifiedName()) + opts.FunctionDefinition = String(wrapFunctionDefinition("import numpy as np")) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("i" NUMBER DEFAULT 1) COPY GRANTS RETURNS VARIANT NOT NULL LANGUAGE PYTHON CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '3.8' COMMENT = 'comment' IMPORTS = ('numpy', 'pandas') PACKAGES = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) AS $$import numpy as np$$`, id.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { @@ -676,8 +722,8 @@ func TestFunctions_CreateForPython(t *testing.T) { Name: "name2", }, } - opts.FunctionDefinition = String("import numpy as np") - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s (i NUMBER(36, 2) DEFAULT 1) COPY GRANTS RETURNS VARIANT NOT NULL LANGUAGE PYTHON CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '3.8' COMMENT = 'comment' IMPORTS = ('numpy', 'pandas') PACKAGES = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) AS 'import numpy as np'`, id.FullyQualifiedName()) + opts.FunctionDefinition = String(wrapFunctionDefinition("import numpy as np")) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("i" NUMBER(36, 2) DEFAULT 1) COPY GRANTS RETURNS VARIANT NOT NULL LANGUAGE PYTHON CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '3.8' COMMENT = 'comment' IMPORTS = ('numpy', 'pandas') PACKAGES = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) AS $$import numpy as np$$`, id.FullyQualifiedName()) }) } @@ -695,12 +741,37 @@ func TestFunctions_CreateForScala(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: incorrect identifier", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) + t.Run("validation: [opts.Handler] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.ResultDataType = dataTypeVarchar + assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForScalaFunctionOptions", "Handler")) + }) + + t.Run("validation: conflicting fields for [opts.OrReplace opts.IfNotExists]", func(t *testing.T) { + opts := defaultOpts() + opts.OrReplace = Bool(true) + opts.IfNotExists = Bool(true) + assertOptsInvalidJoinedErrors(t, opts, errOneOf("CreateForScalaFunctionOptions", "OrReplace", "IfNotExists")) + }) + + t.Run("validation: exactly one field from [opts.ResultDataTypeOld opts.ResultDataType] should be present", func(t *testing.T) { + opts := defaultOpts() + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForScalaFunctionOptions", "ResultDataTypeOld", "ResultDataType")) + }) + + t.Run("validation: exactly one field from [opts.ResultDataTypeOld opts.ResultDataType] should be present - two present", func(t *testing.T) { + opts := defaultOpts() + opts.ResultDataTypeOld = DataTypeFloat + opts.ResultDataType = dataTypeFloat + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForScalaFunctionOptions", "ResultDataTypeOld", "ResultDataType")) + }) + t.Run("validation: exactly one field from [opts.Arguments.ArgDataTypeOld opts.Arguments.ArgDataType] should be present", func(t *testing.T) { opts := defaultOpts() opts.Arguments = []FunctionArgument{ @@ -726,18 +797,6 @@ func TestFunctions_CreateForScala(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForScalaFunctionOptions.Arguments", "ArgDataTypeOld", "ArgDataType")) }) - t.Run("validation: exactly one field from [opts.ResultDataTypeOld opts.ResultDataType] should be present", func(t *testing.T) { - opts := defaultOpts() - assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForScalaFunctionOptions", "ResultDataTypeOld", "ResultDataType")) - }) - - t.Run("validation: exactly one field from [opts.ResultDataTypeOld opts.ResultDataType] should be present - two present", func(t *testing.T) { - opts := defaultOpts() - opts.ResultDataTypeOld = DataTypeFloat - opts.ResultDataType = dataTypeFloat - assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForScalaFunctionOptions", "ResultDataTypeOld", "ResultDataType")) - }) - t.Run("validation: function definition", func(t *testing.T) { opts := defaultOpts() opts.TargetPath = String("@~/testfunc.jar") @@ -751,12 +810,6 @@ func TestFunctions_CreateForScala(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, NewError("IMPORTS must not be empty when AS is nil")) }) - t.Run("validation: options are missing", func(t *testing.T) { - opts := defaultOpts() - opts.ResultDataType = dataTypeVarchar - assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForScalaFunctionOptions", "Handler")) - }) - // TODO [SNOW-1348103]: remove with old function removal for V1 t.Run("all options - old data types", func(t *testing.T) { opts := defaultOpts() @@ -783,8 +836,8 @@ func TestFunctions_CreateForScala(t *testing.T) { }, } opts.Handler = "Echo.echoVarchar" - opts.FunctionDefinition = String("return x") - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s (x VARCHAR DEFAULT 'test') COPY GRANTS RETURNS VARCHAR NOT NULL LANGUAGE SCALA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@udf_libs/echohandler.jar') HANDLER = 'Echo.echoVarchar' AS 'return x'`, id.FullyQualifiedName()) + opts.FunctionDefinition = String(wrapFunctionDefinition("return x")) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("x" VARCHAR DEFAULT 'test') COPY GRANTS RETURNS VARCHAR NOT NULL LANGUAGE SCALA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@udf_libs/echohandler.jar') HANDLER = 'Echo.echoVarchar' AS $$return x$$`, id.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { @@ -812,8 +865,8 @@ func TestFunctions_CreateForScala(t *testing.T) { }, } opts.Handler = "Echo.echoVarchar" - opts.FunctionDefinition = String("return x") - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s (x VARCHAR(100) DEFAULT 'test') COPY GRANTS RETURNS VARCHAR(100) NOT NULL LANGUAGE SCALA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@udf_libs/echohandler.jar') HANDLER = 'Echo.echoVarchar' AS 'return x'`, id.FullyQualifiedName()) + opts.FunctionDefinition = String(wrapFunctionDefinition("return x")) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("x" VARCHAR(100) DEFAULT 'test') COPY GRANTS RETURNS VARCHAR(100) NOT NULL LANGUAGE SCALA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@udf_libs/echohandler.jar') HANDLER = 'Echo.echoVarchar' AS $$return x$$`, id.FullyQualifiedName()) }) } @@ -831,7 +884,17 @@ func TestFunctions_CreateForSQL(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: incorrect identifier", func(t *testing.T) { + t.Run("validation: [opts.FunctionDefinition] should be set", func(t *testing.T) { + opts := defaultOpts() + opts.Returns = FunctionReturns{ + ResultDataType: &FunctionReturnsResultDataType{ + ResultDataType: dataTypeVarchar, + }, + } + assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForSQLFunctionOptions", "FunctionDefinition")) + }) + + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() opts.name = emptySchemaObjectIdentifier assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) @@ -862,6 +925,21 @@ func TestFunctions_CreateForSQL(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForSQLFunctionOptions.Arguments", "ArgDataTypeOld", "ArgDataType")) }) + t.Run("validation: exactly one field from [opts.Returns.ResultDataType opts.Returns.Table] should be present", func(t *testing.T) { + opts := defaultOpts() + opts.Returns = FunctionReturns{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForSQLFunctionOptions.Returns", "ResultDataType", "Table")) + }) + + t.Run("validation: exactly one field from [opts.Returns.ResultDataType opts.Returns.Table] should be present - two present", func(t *testing.T) { + opts := defaultOpts() + opts.Returns = FunctionReturns{ + ResultDataType: &FunctionReturnsResultDataType{}, + Table: &FunctionReturnsTable{}, + } + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForSQLFunctionOptions.Returns", "ResultDataType", "Table")) + }) + t.Run("validation: exactly one field from [opts.Returns.ResultDataType.ResultDataTypeOld opts.Returns.ResultDataType.ResultDataType] should be present", func(t *testing.T) { opts := defaultOpts() opts.Returns = FunctionReturns{ @@ -918,22 +996,6 @@ func TestFunctions_CreateForSQL(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForSQLFunctionOptions.Returns.Table.Columns", "ColumnDataTypeOld", "ColumnDataType")) }) - t.Run("validation: returns", func(t *testing.T) { - opts := defaultOpts() - opts.Returns = FunctionReturns{} - assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("CreateForSQLFunctionOptions.Returns", "ResultDataType", "Table")) - }) - - t.Run("validation: options are missing", func(t *testing.T) { - opts := defaultOpts() - opts.Returns = FunctionReturns{ - ResultDataType: &FunctionReturnsResultDataType{ - ResultDataType: dataTypeVarchar, - }, - } - assertOptsInvalidJoinedErrors(t, opts, errNotSet("CreateForSQLFunctionOptions", "FunctionDefinition")) - }) - t.Run("create with no arguments", func(t *testing.T) { opts := defaultOpts() opts.Returns = FunctionReturns{ @@ -941,8 +1003,8 @@ func TestFunctions_CreateForSQL(t *testing.T) { ResultDataType: dataTypeFloat, }, } - opts.FunctionDefinition = "3.141592654::FLOAT" - assertOptsValidAndSQLEquals(t, opts, `CREATE FUNCTION %s () RETURNS FLOAT AS '3.141592654::FLOAT'`, id.FullyQualifiedName()) + opts.FunctionDefinition = wrapFunctionDefinition("3.141592654::FLOAT") + assertOptsValidAndSQLEquals(t, opts, `CREATE FUNCTION %s () RETURNS FLOAT AS $$3.141592654::FLOAT$$`, id.FullyQualifiedName()) }) // TODO [SNOW-1348103]: remove with old function removal for V1 @@ -968,8 +1030,8 @@ func TestFunctions_CreateForSQL(t *testing.T) { opts.ReturnResultsBehavior = ReturnResultsBehaviorPointer(ReturnResultsBehaviorImmutable) opts.Memoizable = Bool(true) opts.Comment = String("comment") - opts.FunctionDefinition = "3.141592654::FLOAT" - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s (message VARCHAR DEFAULT 'test') COPY GRANTS RETURNS FLOAT NOT NULL IMMUTABLE MEMOIZABLE COMMENT = 'comment' AS '3.141592654::FLOAT'`, id.FullyQualifiedName()) + opts.FunctionDefinition = wrapFunctionDefinition("3.141592654::FLOAT") + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("message" VARCHAR DEFAULT 'test') COPY GRANTS RETURNS FLOAT NOT NULL IMMUTABLE MEMOIZABLE COMMENT = 'comment' AS $$3.141592654::FLOAT$$`, id.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { @@ -994,50 +1056,13 @@ func TestFunctions_CreateForSQL(t *testing.T) { opts.ReturnResultsBehavior = ReturnResultsBehaviorPointer(ReturnResultsBehaviorImmutable) opts.Memoizable = Bool(true) opts.Comment = String("comment") - opts.FunctionDefinition = "3.141592654::FLOAT" - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s (message VARCHAR(100) DEFAULT 'test') COPY GRANTS RETURNS FLOAT NOT NULL IMMUTABLE MEMOIZABLE COMMENT = 'comment' AS '3.141592654::FLOAT'`, id.FullyQualifiedName()) - }) -} - -func TestFunctions_Drop(t *testing.T) { - noArgsId := randomSchemaObjectIdentifierWithArguments() - id := randomSchemaObjectIdentifierWithArguments(DataTypeVARCHAR, DataTypeNumber) - - defaultOpts := func() *DropFunctionOptions { - return &DropFunctionOptions{ - name: id, - } - } - - t.Run("validation: nil options", func(t *testing.T) { - opts := (*DropFunctionOptions)(nil) - assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) - }) - - t.Run("validation: incorrect identifier", func(t *testing.T) { - opts := defaultOpts() - opts.name = emptySchemaObjectIdentifierWithArguments - assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) - }) - - t.Run("no arguments", func(t *testing.T) { - opts := defaultOpts() - opts.name = noArgsId - assertOptsValidAndSQLEquals(t, opts, `DROP FUNCTION %s`, noArgsId.FullyQualifiedName()) - }) - - t.Run("all options", func(t *testing.T) { - opts := &DropFunctionOptions{ - name: id, - } - opts.IfExists = Bool(true) - assertOptsValidAndSQLEquals(t, opts, `DROP FUNCTION IF EXISTS %s`, id.FullyQualifiedName()) + opts.FunctionDefinition = wrapFunctionDefinition("3.141592654::FLOAT") + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("message" VARCHAR(100) DEFAULT 'test') COPY GRANTS RETURNS FLOAT NOT NULL IMMUTABLE MEMOIZABLE COMMENT = 'comment' AS $$3.141592654::FLOAT$$`, id.FullyQualifiedName()) }) } func TestFunctions_Alter(t *testing.T) { id := randomSchemaObjectIdentifierWithArguments(DataTypeVARCHAR, DataTypeNumber) - noArgsId := randomSchemaObjectIdentifierWithArguments() defaultOpts := func() *AlterFunctionOptions { return &AlterFunctionOptions{ @@ -1051,72 +1076,72 @@ func TestFunctions_Alter(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: incorrect identifier", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() opts.name = emptySchemaObjectIdentifierWithArguments assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) - t.Run("validation: exactly one field should be present", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.RenameTo] if set", func(t *testing.T) { opts := defaultOpts() - assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterFunctionOptions", "RenameTo", "SetComment", "SetLogLevel", "SetTraceLevel", "SetSecure", "UnsetLogLevel", "UnsetTraceLevel", "UnsetSecure", "UnsetComment", "SetTags", "UnsetTags")) - }) - - t.Run("validation: exactly one field should be present", func(t *testing.T) { - opts := defaultOpts() - opts.SetLogLevel = String("DEBUG") - opts.UnsetComment = Bool(true) - assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterFunctionOptions", "RenameTo", "SetComment", "SetLogLevel", "SetTraceLevel", "SetSecure", "UnsetLogLevel", "UnsetTraceLevel", "UnsetSecure", "UnsetComment", "SetTags", "UnsetTags")) + target := emptySchemaObjectIdentifier + opts.RenameTo = &target + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) - t.Run("alter: rename to", func(t *testing.T) { + t.Run("validation: exactly one field from [opts.RenameTo opts.Set opts.Unset opts.SetSecure opts.UnsetSecure opts.SetTags opts.UnsetTags] should be present", func(t *testing.T) { opts := defaultOpts() - target := randomSchemaObjectIdentifierInSchema(id.SchemaId()) - opts.RenameTo = &target - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s RENAME TO %s`, id.FullyQualifiedName(), opts.RenameTo.FullyQualifiedName()) + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterFunctionOptions", "RenameTo", "Set", "Unset", "SetSecure", "UnsetSecure", "SetTags", "UnsetTags")) }) - t.Run("alter: set log level with no arguments", func(t *testing.T) { + t.Run("validation: exactly one field from [opts.RenameTo opts.Set opts.Unset opts.SetSecure opts.UnsetSecure opts.SetTags opts.UnsetTags] should be present - two present", func(t *testing.T) { opts := defaultOpts() - opts.name = noArgsId - opts.SetLogLevel = String("DEBUG") - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET LOG_LEVEL = 'DEBUG'`, noArgsId.FullyQualifiedName()) + opts.Set = &FunctionSet{} + opts.Unset = &FunctionUnset{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterFunctionOptions", "RenameTo", "Set", "Unset", "SetSecure", "UnsetSecure", "SetTags", "UnsetTags")) }) - t.Run("alter: set log level", func(t *testing.T) { + t.Run("validation: at least one of the fields [opts.Set.Comment opts.Set.ExternalAccessIntegrations opts.Set.SecretsList opts.Set.EnableConsoleOutput opts.Set.LogLevel opts.Set.MetricLevel opts.Set.TraceLevel] should be set", func(t *testing.T) { opts := defaultOpts() - opts.SetLogLevel = String("DEBUG") - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET LOG_LEVEL = 'DEBUG'`, id.FullyQualifiedName()) + opts.Set = &FunctionSet{} + assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterFunctionOptions.Set", "Comment", "ExternalAccessIntegrations", "SecretsList", "EnableConsoleOutput", "LogLevel", "MetricLevel", "TraceLevel")) }) - t.Run("alter: set trace level", func(t *testing.T) { + t.Run("validation: at least one of the fields [opts.Unset.Comment opts.Unset.ExternalAccessIntegrations opts.Unset.EnableConsoleOutput opts.Unset.LogLevel opts.Unset.MetricLevel opts.Unset.TraceLevel] should be set", func(t *testing.T) { opts := defaultOpts() - opts.SetTraceLevel = String("DEBUG") - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET TRACE_LEVEL = 'DEBUG'`, id.FullyQualifiedName()) + opts.Unset = &FunctionUnset{} + assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterFunctionOptions.Unset", "Comment", "ExternalAccessIntegrations", "EnableConsoleOutput", "LogLevel", "MetricLevel", "TraceLevel")) }) - t.Run("alter: set comment", func(t *testing.T) { + t.Run("alter: rename to", func(t *testing.T) { opts := defaultOpts() - opts.SetComment = String("comment") - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET COMMENT = 'comment'`, id.FullyQualifiedName()) + target := randomSchemaObjectIdentifierInSchema(id.SchemaId()) + opts.RenameTo = &target + assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s RENAME TO %s`, id.FullyQualifiedName(), opts.RenameTo.FullyQualifiedName()) }) - t.Run("alter: set secure", func(t *testing.T) { + t.Run("alter: set", func(t *testing.T) { opts := defaultOpts() - opts.SetSecure = Bool(true) - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET SECURE`, id.FullyQualifiedName()) + opts.Set = &FunctionSet{ + Comment: String("comment"), + TraceLevel: Pointer(TraceLevelOff), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET COMMENT = 'comment', TRACE_LEVEL = 'OFF'`, id.FullyQualifiedName()) }) - t.Run("alter: unset log level", func(t *testing.T) { + t.Run("alter: unset", func(t *testing.T) { opts := defaultOpts() - opts.UnsetLogLevel = Bool(true) - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s UNSET LOG_LEVEL`, id.FullyQualifiedName()) + opts.Unset = &FunctionUnset{ + Comment: Bool(true), + TraceLevel: Bool(true), + } + assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s UNSET COMMENT, TRACE_LEVEL`, id.FullyQualifiedName()) }) - t.Run("alter: unset trace level", func(t *testing.T) { + t.Run("alter: set secure", func(t *testing.T) { opts := defaultOpts() - opts.UnsetTraceLevel = Bool(true) - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s UNSET TRACE_LEVEL`, id.FullyQualifiedName()) + opts.SetSecure = Bool(true) + assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET SECURE`, id.FullyQualifiedName()) }) t.Run("alter: unset secure", func(t *testing.T) { @@ -1125,12 +1150,6 @@ func TestFunctions_Alter(t *testing.T) { assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s UNSET SECURE`, id.FullyQualifiedName()) }) - t.Run("alter: unset comment", func(t *testing.T) { - opts := defaultOpts() - opts.UnsetComment = Bool(true) - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s UNSET COMMENT`, id.FullyQualifiedName()) - }) - t.Run("alter: set tags", func(t *testing.T) { opts := defaultOpts() opts.SetTags = []TagAssociation{ @@ -1152,6 +1171,42 @@ func TestFunctions_Alter(t *testing.T) { }) } +func TestFunctions_Drop(t *testing.T) { + noArgsId := randomSchemaObjectIdentifierWithArguments() + id := randomSchemaObjectIdentifierWithArguments(DataTypeVARCHAR, DataTypeNumber) + + defaultOpts := func() *DropFunctionOptions { + return &DropFunctionOptions{ + name: id, + } + } + + t.Run("validation: nil options", func(t *testing.T) { + opts := (*DropFunctionOptions)(nil) + assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + }) + + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { + opts := defaultOpts() + opts.name = emptySchemaObjectIdentifierWithArguments + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + }) + + t.Run("basic", func(t *testing.T) { + opts := defaultOpts() + opts.name = noArgsId + assertOptsValidAndSQLEquals(t, opts, `DROP FUNCTION %s`, noArgsId.FullyQualifiedName()) + }) + + t.Run("all options", func(t *testing.T) { + opts := &DropFunctionOptions{ + name: id, + } + opts.IfExists = Bool(true) + assertOptsValidAndSQLEquals(t, opts, `DROP FUNCTION IF EXISTS %s`, id.FullyQualifiedName()) + }) +} + func TestFunctions_Show(t *testing.T) { defaultOpts := func() *ShowFunctionOptions { return &ShowFunctionOptions{} @@ -1177,8 +1232,10 @@ func TestFunctions_Show(t *testing.T) { t.Run("show with in", func(t *testing.T) { opts := defaultOpts() - opts.In = &In{ - Account: Bool(true), + opts.In = &ExtendedIn{ + In: In{ + Account: Bool(true), + }, } assertOptsValidAndSQLEquals(t, opts, `SHOW USER FUNCTIONS IN ACCOUNT`) }) @@ -1186,7 +1243,6 @@ func TestFunctions_Show(t *testing.T) { func TestFunctions_Describe(t *testing.T) { id := randomSchemaObjectIdentifierWithArguments(DataTypeVARCHAR, DataTypeNumber) - noArgsId := randomSchemaObjectIdentifierWithArguments() defaultOpts := func() *DescribeFunctionOptions { return &DescribeFunctionOptions{ @@ -1199,18 +1255,12 @@ func TestFunctions_Describe(t *testing.T) { assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) }) - t.Run("validation: incorrect identifier", func(t *testing.T) { + t.Run("validation: valid identifier for [opts.name]", func(t *testing.T) { opts := defaultOpts() opts.name = emptySchemaObjectIdentifierWithArguments assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) - t.Run("no arguments", func(t *testing.T) { - opts := defaultOpts() - opts.name = noArgsId - assertOptsValidAndSQLEquals(t, opts, `DESCRIBE FUNCTION %s`, noArgsId.FullyQualifiedName()) - }) - t.Run("all options", func(t *testing.T) { opts := defaultOpts() assertOptsValidAndSQLEquals(t, opts, `DESCRIBE FUNCTION %s`, id.FullyQualifiedName()) From a56e64440c60ac41e95ab29beffebd87507a7832 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Fri, 6 Dec 2024 18:07:44 +0100 Subject: [PATCH 26/63] Test empty and non-empty secrets --- pkg/sdk/functions_def.go | 1 - pkg/sdk/functions_gen_test.go | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index e248dd6746..a1e7eb4d1e 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -254,7 +254,6 @@ var FunctionsDef = g.NewInterface( OptionalAssignment("LOG_LEVEL", g.KindOfTPointer[LogLevel](), g.ParameterOptions().SingleQuotes()). OptionalAssignment("METRIC_LEVEL", g.KindOfTPointer[MetricLevel](), g.ParameterOptions().SingleQuotes()). OptionalAssignment("TRACE_LEVEL", g.KindOfTPointer[TraceLevel](), g.ParameterOptions().SingleQuotes()). - // TODO [this PR]: test setting secrets to empty (nil versus empty list) WithValidation(g.AtLeastOneValueSet, "Comment", "ExternalAccessIntegrations", "SecretsList", "EnableConsoleOutput", "LogLevel", "MetricLevel", "TraceLevel"), g.ListOptions().SQL("SET"), ). diff --git a/pkg/sdk/functions_gen_test.go b/pkg/sdk/functions_gen_test.go index 30cb1cf3b3..dcd50f8e6f 100644 --- a/pkg/sdk/functions_gen_test.go +++ b/pkg/sdk/functions_gen_test.go @@ -1129,6 +1129,26 @@ func TestFunctions_Alter(t *testing.T) { assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET COMMENT = 'comment', TRACE_LEVEL = 'OFF'`, id.FullyQualifiedName()) }) + t.Run("alter: set empty secrets", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &FunctionSet{ + SecretsList: &SecretsList{}, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET SECRETS = ()`, id.FullyQualifiedName()) + }) + + t.Run("alter: set non-empty secrets", func(t *testing.T) { + opts := defaultOpts() + opts.Set = &FunctionSet{ + SecretsList: &SecretsList{ + []SecretReference{ + {VariableName: "abc", Name: "secret_name"}, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET SECRETS = ('abc' = secret_name)`, id.FullyQualifiedName()) + }) + t.Run("alter: unset", func(t *testing.T) { opts := defaultOpts() opts.Unset = &FunctionUnset{ From 2b3ce4b5d1cb6666f66c7c0cc31fc2f71a538917 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sat, 7 Dec 2024 14:29:26 +0100 Subject: [PATCH 27/63] Fix secret reference --- pkg/sdk/common_types.go | 6 +++--- pkg/sdk/functions_gen_test.go | 33 +++++++++++++++++++-------------- pkg/sdk/procedures_gen_test.go | 28 ++++++++++++++++------------ 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/pkg/sdk/common_types.go b/pkg/sdk/common_types.go index 39ab08e1a8..e14ae3e644 100644 --- a/pkg/sdk/common_types.go +++ b/pkg/sdk/common_types.go @@ -259,10 +259,10 @@ func ReturnNullValuesPointer(v ReturnNullValues) *ReturnNullValues { return &v } -// TODO [this PR]: Name -> Identifier? type SecretReference struct { - VariableName string `ddl:"keyword,single_quotes"` - Name string `ddl:"parameter,no_quotes"` + VariableName string `ddl:"keyword,single_quotes"` + equals bool `ddl:"static" sql:"="` + Name SchemaObjectIdentifier `ddl:"identifier"` } type ValuesBehavior string diff --git a/pkg/sdk/functions_gen_test.go b/pkg/sdk/functions_gen_test.go index dcd50f8e6f..07b0eae432 100644 --- a/pkg/sdk/functions_gen_test.go +++ b/pkg/sdk/functions_gen_test.go @@ -11,6 +11,8 @@ func wrapFunctionDefinition(def string) string { func TestFunctions_CreateForJava(t *testing.T) { id := randomSchemaObjectIdentifier() + secretId := randomSchemaObjectIdentifier() + secretId2 := randomSchemaObjectIdentifier() defaultOpts := func() *CreateForJavaFunctionOptions { return &CreateForJavaFunctionOptions{ @@ -200,16 +202,16 @@ func TestFunctions_CreateForJava(t *testing.T) { opts.Secrets = []SecretReference{ { VariableName: "variable1", - Name: "name1", + Name: secretId, }, { VariableName: "variable2", - Name: "name2", + Name: secretId2, }, } opts.TargetPath = String("@~/testfunc.jar") opts.FunctionDefinition = String(wrapFunctionDefinition("return id + name;")) - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("id" NUMBER, "name" VARCHAR DEFAULT 'test') COPY GRANTS RETURNS TABLE ("country_code" VARCHAR, "country_name" VARCHAR) NOT NULL LANGUAGE JAVA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@~/my_decrement_udf_package_dir/my_decrement_udf_jar.jar') PACKAGES = ('com.snowflake:snowpark:1.2.0') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) TARGET_PATH = '@~/testfunc.jar' AS $$return id + name;$$`, id.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("id" NUMBER, "name" VARCHAR DEFAULT 'test') COPY GRANTS RETURNS TABLE ("country_code" VARCHAR, "country_name" VARCHAR) NOT NULL LANGUAGE JAVA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@~/my_decrement_udf_package_dir/my_decrement_udf_jar.jar') PACKAGES = ('com.snowflake:snowpark:1.2.0') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = %s, 'variable2' = %s) TARGET_PATH = '@~/testfunc.jar' AS $$return id + name;$$`, id.FullyQualifiedName(), secretId.FullyQualifiedName(), secretId2.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { @@ -265,16 +267,16 @@ func TestFunctions_CreateForJava(t *testing.T) { opts.Secrets = []SecretReference{ { VariableName: "variable1", - Name: "name1", + Name: secretId, }, { VariableName: "variable2", - Name: "name2", + Name: secretId2, }, } opts.TargetPath = String("@~/testfunc.jar") opts.FunctionDefinition = String(wrapFunctionDefinition("return id + name;")) - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("id" NUMBER(36, 2), "name" VARCHAR(100) DEFAULT 'test') COPY GRANTS RETURNS TABLE ("country_code" VARCHAR(100), "country_name" VARCHAR(100)) NOT NULL LANGUAGE JAVA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@~/my_decrement_udf_package_dir/my_decrement_udf_jar.jar') PACKAGES = ('com.snowflake:snowpark:1.2.0') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) TARGET_PATH = '@~/testfunc.jar' AS $$return id + name;$$`, id.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("id" NUMBER(36, 2), "name" VARCHAR(100) DEFAULT 'test') COPY GRANTS RETURNS TABLE ("country_code" VARCHAR(100), "country_name" VARCHAR(100)) NOT NULL LANGUAGE JAVA CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '2.0' COMMENT = 'comment' IMPORTS = ('@~/my_decrement_udf_package_dir/my_decrement_udf_jar.jar') PACKAGES = ('com.snowflake:snowpark:1.2.0') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = %s, 'variable2' = %s) TARGET_PATH = '@~/testfunc.jar' AS $$return id + name;$$`, id.FullyQualifiedName(), secretId.FullyQualifiedName(), secretId2.FullyQualifiedName()) }) } @@ -460,6 +462,8 @@ func TestFunctions_CreateForJavascript(t *testing.T) { func TestFunctions_CreateForPython(t *testing.T) { id := randomSchemaObjectIdentifier() + secretId := randomSchemaObjectIdentifier() + secretId2 := randomSchemaObjectIdentifier() defaultOpts := func() *CreateForPythonFunctionOptions { return &CreateForPythonFunctionOptions{ @@ -658,15 +662,15 @@ func TestFunctions_CreateForPython(t *testing.T) { opts.Secrets = []SecretReference{ { VariableName: "variable1", - Name: "name1", + Name: secretId, }, { VariableName: "variable2", - Name: "name2", + Name: secretId2, }, } opts.FunctionDefinition = String(wrapFunctionDefinition("import numpy as np")) - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("i" NUMBER DEFAULT 1) COPY GRANTS RETURNS VARIANT NOT NULL LANGUAGE PYTHON CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '3.8' COMMENT = 'comment' IMPORTS = ('numpy', 'pandas') PACKAGES = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) AS $$import numpy as np$$`, id.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("i" NUMBER DEFAULT 1) COPY GRANTS RETURNS VARIANT NOT NULL LANGUAGE PYTHON CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '3.8' COMMENT = 'comment' IMPORTS = ('numpy', 'pandas') PACKAGES = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = %s, 'variable2' = %s) AS $$import numpy as np$$`, id.FullyQualifiedName(), secretId.FullyQualifiedName(), secretId2.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { @@ -715,15 +719,15 @@ func TestFunctions_CreateForPython(t *testing.T) { opts.Secrets = []SecretReference{ { VariableName: "variable1", - Name: "name1", + Name: secretId, }, { VariableName: "variable2", - Name: "name2", + Name: secretId2, }, } opts.FunctionDefinition = String(wrapFunctionDefinition("import numpy as np")) - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("i" NUMBER(36, 2) DEFAULT 1) COPY GRANTS RETURNS VARIANT NOT NULL LANGUAGE PYTHON CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '3.8' COMMENT = 'comment' IMPORTS = ('numpy', 'pandas') PACKAGES = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) AS $$import numpy as np$$`, id.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE TEMPORARY SECURE FUNCTION %s ("i" NUMBER(36, 2) DEFAULT 1) COPY GRANTS RETURNS VARIANT NOT NULL LANGUAGE PYTHON CALLED ON NULL INPUT IMMUTABLE RUNTIME_VERSION = '3.8' COMMENT = 'comment' IMPORTS = ('numpy', 'pandas') PACKAGES = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = %s, 'variable2' = %s) AS $$import numpy as np$$`, id.FullyQualifiedName(), secretId.FullyQualifiedName(), secretId2.FullyQualifiedName()) }) } @@ -1063,6 +1067,7 @@ func TestFunctions_CreateForSQL(t *testing.T) { func TestFunctions_Alter(t *testing.T) { id := randomSchemaObjectIdentifierWithArguments(DataTypeVARCHAR, DataTypeNumber) + secretId := randomSchemaObjectIdentifier() defaultOpts := func() *AlterFunctionOptions { return &AlterFunctionOptions{ @@ -1142,11 +1147,11 @@ func TestFunctions_Alter(t *testing.T) { opts.Set = &FunctionSet{ SecretsList: &SecretsList{ []SecretReference{ - {VariableName: "abc", Name: "secret_name"}, + {VariableName: "abc", Name: secretId}, }, }, } - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET SECRETS = ('abc' = secret_name)`, id.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET SECRETS = ('abc' = %s)`, id.FullyQualifiedName(), secretId.FullyQualifiedName()) }) t.Run("alter: unset", func(t *testing.T) { diff --git a/pkg/sdk/procedures_gen_test.go b/pkg/sdk/procedures_gen_test.go index 994181b59b..12a3d030c4 100644 --- a/pkg/sdk/procedures_gen_test.go +++ b/pkg/sdk/procedures_gen_test.go @@ -6,6 +6,8 @@ import ( func TestProcedures_CreateForJava(t *testing.T) { id := randomSchemaObjectIdentifier() + secretId := randomSchemaObjectIdentifier() + secretId2 := randomSchemaObjectIdentifier() defaultOpts := func() *CreateForJavaProcedureOptions { return &CreateForJavaProcedureOptions{ @@ -192,11 +194,11 @@ func TestProcedures_CreateForJava(t *testing.T) { opts.Secrets = []SecretReference{ { VariableName: "variable1", - Name: "name1", + Name: secretId, }, { VariableName: "variable2", - Name: "name2", + Name: secretId2, }, } opts.TargetPath = String("@~/testfunc.jar") @@ -204,7 +206,7 @@ func TestProcedures_CreateForJava(t *testing.T) { opts.Comment = String("test comment") opts.ExecuteAs = ExecuteAsPointer(ExecuteAsCaller) opts.ProcedureDefinition = String("return id + name;") - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE SECURE PROCEDURE %s (id NUMBER, name VARCHAR DEFAULT 'test') COPY GRANTS RETURNS TABLE (country_code VARCHAR) LANGUAGE JAVA RUNTIME_VERSION = '1.8' PACKAGES = ('com.snowflake:snowpark:1.2.0') IMPORTS = ('test_jar.jar') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) TARGET_PATH = '@~/testfunc.jar' STRICT COMMENT = 'test comment' EXECUTE AS CALLER AS 'return id + name;'`, id.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE SECURE PROCEDURE %s (id NUMBER, name VARCHAR DEFAULT 'test') COPY GRANTS RETURNS TABLE (country_code VARCHAR) LANGUAGE JAVA RUNTIME_VERSION = '1.8' PACKAGES = ('com.snowflake:snowpark:1.2.0') IMPORTS = ('test_jar.jar') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = %s, 'variable2' = %s) TARGET_PATH = '@~/testfunc.jar' STRICT COMMENT = 'test comment' EXECUTE AS CALLER AS 'return id + name;'`, id.FullyQualifiedName(), secretId.FullyQualifiedName(), secretId2.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { @@ -251,11 +253,11 @@ func TestProcedures_CreateForJava(t *testing.T) { opts.Secrets = []SecretReference{ { VariableName: "variable1", - Name: "name1", + Name: secretId, }, { VariableName: "variable2", - Name: "name2", + Name: secretId2, }, } opts.TargetPath = String("@~/testfunc.jar") @@ -263,7 +265,7 @@ func TestProcedures_CreateForJava(t *testing.T) { opts.Comment = String("test comment") opts.ExecuteAs = ExecuteAsPointer(ExecuteAsCaller) opts.ProcedureDefinition = String("return id + name;") - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE SECURE PROCEDURE %s (id NUMBER(36, 2), name VARCHAR(100) DEFAULT 'test') COPY GRANTS RETURNS TABLE (country_code VARCHAR(100)) LANGUAGE JAVA RUNTIME_VERSION = '1.8' PACKAGES = ('com.snowflake:snowpark:1.2.0') IMPORTS = ('test_jar.jar') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) TARGET_PATH = '@~/testfunc.jar' STRICT COMMENT = 'test comment' EXECUTE AS CALLER AS 'return id + name;'`, id.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE SECURE PROCEDURE %s (id NUMBER(36, 2), name VARCHAR(100) DEFAULT 'test') COPY GRANTS RETURNS TABLE (country_code VARCHAR(100)) LANGUAGE JAVA RUNTIME_VERSION = '1.8' PACKAGES = ('com.snowflake:snowpark:1.2.0') IMPORTS = ('test_jar.jar') HANDLER = 'TestFunc.echoVarchar' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = %s, 'variable2' = %s) TARGET_PATH = '@~/testfunc.jar' STRICT COMMENT = 'test comment' EXECUTE AS CALLER AS 'return id + name;'`, id.FullyQualifiedName(), secretId.FullyQualifiedName(), secretId2.FullyQualifiedName()) }) } @@ -377,6 +379,8 @@ func TestProcedures_CreateForJavaScript(t *testing.T) { func TestProcedures_CreateForPython(t *testing.T) { id := randomSchemaObjectIdentifier() + secretId := randomSchemaObjectIdentifier() + secretId2 := randomSchemaObjectIdentifier() defaultOpts := func() *CreateForPythonProcedureOptions { return &CreateForPythonProcedureOptions{ @@ -550,18 +554,18 @@ func TestProcedures_CreateForPython(t *testing.T) { opts.Secrets = []SecretReference{ { VariableName: "variable1", - Name: "name1", + Name: secretId, }, { VariableName: "variable2", - Name: "name2", + Name: secretId2, }, } opts.NullInputBehavior = NullInputBehaviorPointer(NullInputBehaviorStrict) opts.Comment = String("test comment") opts.ExecuteAs = ExecuteAsPointer(ExecuteAsCaller) opts.ProcedureDefinition = String("import numpy as np") - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE SECURE PROCEDURE %s (i int DEFAULT 1) COPY GRANTS RETURNS VARIANT NULL LANGUAGE PYTHON RUNTIME_VERSION = '3.8' PACKAGES = ('numpy', 'pandas') IMPORTS = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) STRICT COMMENT = 'test comment' EXECUTE AS CALLER AS 'import numpy as np'`, id.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE SECURE PROCEDURE %s (i int DEFAULT 1) COPY GRANTS RETURNS VARIANT NULL LANGUAGE PYTHON RUNTIME_VERSION = '3.8' PACKAGES = ('numpy', 'pandas') IMPORTS = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = %s, 'variable2' = %s) STRICT COMMENT = 'test comment' EXECUTE AS CALLER AS 'import numpy as np'`, id.FullyQualifiedName(), secretId.FullyQualifiedName(), secretId2.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { @@ -606,18 +610,18 @@ func TestProcedures_CreateForPython(t *testing.T) { opts.Secrets = []SecretReference{ { VariableName: "variable1", - Name: "name1", + Name: secretId, }, { VariableName: "variable2", - Name: "name2", + Name: secretId2, }, } opts.NullInputBehavior = NullInputBehaviorPointer(NullInputBehaviorStrict) opts.Comment = String("test comment") opts.ExecuteAs = ExecuteAsPointer(ExecuteAsCaller) opts.ProcedureDefinition = String("import numpy as np") - assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE SECURE PROCEDURE %s (i NUMBER(36, 2) DEFAULT 1) COPY GRANTS RETURNS VARIANT NULL LANGUAGE PYTHON RUNTIME_VERSION = '3.8' PACKAGES = ('numpy', 'pandas') IMPORTS = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = name1, 'variable2' = name2) STRICT COMMENT = 'test comment' EXECUTE AS CALLER AS 'import numpy as np'`, id.FullyQualifiedName()) + assertOptsValidAndSQLEquals(t, opts, `CREATE OR REPLACE SECURE PROCEDURE %s (i NUMBER(36, 2) DEFAULT 1) COPY GRANTS RETURNS VARIANT NULL LANGUAGE PYTHON RUNTIME_VERSION = '3.8' PACKAGES = ('numpy', 'pandas') IMPORTS = ('numpy', 'pandas') HANDLER = 'udf' EXTERNAL_ACCESS_INTEGRATIONS = ("ext_integration") SECRETS = ('variable1' = %s, 'variable2' = %s) STRICT COMMENT = 'test comment' EXECUTE AS CALLER AS 'import numpy as np'`, id.FullyQualifiedName(), secretId.FullyQualifiedName(), secretId2.FullyQualifiedName()) }) } From 0b7d036b103d1d608298b11b248418b87cbd42ec Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sat, 7 Dec 2024 15:17:44 +0100 Subject: [PATCH 28/63] Finish describe mapping --- pkg/sdk/functions_def.go | 4 +- pkg/sdk/functions_ext.go | 182 +++++++++------------------------- pkg/sdk/functions_gen.go | 2 +- pkg/sdk/functions_impl_gen.go | 4 +- 4 files changed, 53 insertions(+), 139 deletions(-) diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index a1e7eb4d1e..54016018c0 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -336,8 +336,8 @@ var FunctionsDef = g.NewInterface( Field("property", "string"). Field("value", "sql.NullString"), g.PlainStruct("FunctionDetail"). - Field("Property", "string"). - Field("Value", "string"), + Text("Property"). + OptionalText("Value"), g.NewQueryStruct("DescribeFunction"). Describe(). SQL("FUNCTION"). diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 2a4aae2fc7..078e2d07a6 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -2,6 +2,8 @@ package sdk import ( "context" + "errors" + "fmt" "strconv" ) @@ -10,63 +12,62 @@ func (v *Function) ID() SchemaObjectIdentifierWithArguments { } // FunctionDetails contains aggregated describe results for the given function. -// TODO [this PR]: do we keep *Property or types directly? -> types type FunctionDetails struct { - Signature *StringProperty - Returns *StringProperty - Language *StringProperty - NullHandling *StringProperty - Volatility *StringProperty - Body *StringProperty - ExternalAccessIntegrations *StringProperty // list - Secrets *StringProperty // map - Imports *StringProperty // list - Handler *StringProperty - RuntimeVersion *StringProperty - Packages *StringProperty // list - InstalledPackages *StringProperty // list - IsAggregate *BoolProperty - TargetPath *StringProperty + Signature string // present for all function types + Returns string // present for all function types + Language string // present for all function types + Body *string // present for all function types (hidden when SECURE) + NullHandling *string // present for all function types but SQL + Volatility *string // present for all function types but SQL + ExternalAccessIntegrations *string // list present for python, java, and scala + Secrets *string // map present for python, java, and scala + Imports *string // list present for python, java, and scala (hidden when SECURE) + Handler *string // present for python, java, and scala (hidden when SECURE) + RuntimeVersion *string // present for python, java, and scala (hidden when SECURE) + Packages *string // list // present for python, java, and scala + TargetPath *string // list present for scala and java (hidden when SECURE) + InstalledPackages *string // list present for python (hidden when SECURE) + IsAggregate *bool // present for python } -// TODO [this PR]: handle errors func functionDetailsFromRows(rows []FunctionDetail) (*FunctionDetails, error) { v := &FunctionDetails{} + var errs []error for _, row := range rows { switch row.Property { case "signature": - v.Signature = row.toStringProperty() + errs = append(errs, row.setStringValueOrError("signature", &v.Signature)) case "returns": - v.Returns = row.toStringProperty() + errs = append(errs, row.setStringValueOrError("returns", &v.Returns)) case "language": - v.Language = row.toStringProperty() + errs = append(errs, row.setStringValueOrError("language", &v.Language)) case "null handling": - v.NullHandling = row.toStringProperty() + v.NullHandling = row.Value case "volatility": - v.Volatility = row.toStringProperty() + v.Volatility = row.Value case "body": - v.Body = row.toStringProperty() + v.Body = row.Value case "external_access_integrations": - v.ExternalAccessIntegrations = row.toStringProperty() + v.ExternalAccessIntegrations = row.Value case "secrets": - v.Secrets = row.toStringProperty() + v.Secrets = row.Value case "imports": - v.Imports = row.toStringProperty() + v.Imports = row.Value case "handler": - v.Handler = row.toStringProperty() + v.Handler = row.Value case "runtime_version": - v.RuntimeVersion = row.toStringProperty() + v.RuntimeVersion = row.Value case "packages": - v.Packages = row.toStringProperty() + v.Packages = row.Value case "installed_packages": - v.InstalledPackages = row.toStringProperty() + v.InstalledPackages = row.Value case "is_aggregate": - v.IsAggregate = row.toBoolProperty() + errs = append(errs, row.setOptionalBoolValueOrError("is_aggregate", &v.IsAggregate)) case "targetPath": - v.TargetPath = row.toStringProperty() + v.TargetPath = row.Value } } - return v, nil + return v, errors.Join(errs...) } func (v *functions) DescribeDetails(ctx context.Context, id SchemaObjectIdentifierWithArguments) (*FunctionDetails, error) { @@ -77,114 +78,27 @@ func (v *functions) DescribeDetails(ctx context.Context, id SchemaObjectIdentifi return functionDetailsFromRows(rows) } -func (d *FunctionDetail) toStringProperty() *StringProperty { - return &StringProperty{ - Value: d.Value, - Description: d.Property, - } -} - -func (d *FunctionDetail) toIntProperty() *IntProperty { - var value *int - v, err := strconv.Atoi(d.Value) - if err == nil { - value = &v +func (d *FunctionDetail) setStringValueOrError(property string, field *string) error { + if d.Value == nil { + return fmt.Errorf("value expected for field %s", property) } else { - value = nil - } - return &IntProperty{ - Value: value, - Description: d.Property, + *field = *d.Value } + return nil } -func (d *FunctionDetail) toFloatProperty() *FloatProperty { - var value *float64 - v, err := strconv.ParseFloat(d.Value, 64) - if err == nil { - value = &v - } else { - value = nil - } - return &FloatProperty{ - Value: value, - Description: d.Property, - } -} - -func (d *FunctionDetail) toBoolProperty() *BoolProperty { - var value bool - if d.Value != "" && d.Value != "null" { - value = ToBool(d.Value) - } else { - value = false - } - return &BoolProperty{ - Value: value, - Description: d.Property, +func (d *FunctionDetail) setOptionalBoolValueOrError(property string, field **bool) error { + if d.Value != nil && *d.Value != "" { + v, err := strconv.ParseBool(*d.Value) + if err != nil { + return fmt.Errorf("invalid value for field %s, err: %w", property, err) + } else { + *field = Bool(v) + } } + return nil } -//python function describe: -//- signature -//- returns -//- language -//- null handling -//- volatility -//- [hidden for secure] body -//- external_access_integrations -//- secrets -//- [hidden for secure] imports -//- [hidden for secure] handler -//- [hidden for secure] runtime_version -//- [hidden for secure] packages -//- [hidden for secure] installed_packages -//- is_aggregate -// -//SQL function describe: -//- signature -//- returns -//- language -//- [hidden for secure] body -// -//scala function describe: -//- signature -//- returns -//- language -//- null handling -//- volatility -//- [hidden for secure] body -//- [hidden for secure] imports -//- [hidden for secure] handler -//- [hidden for secure] target_path -//- [hidden for secure] runtime_version -//- [hidden for secure] packages -//- external_access_integrations -//- secrets -// -//java: -//- signature -//- returns -//- language -//- null handling -//- volatility -//- [hidden for secure] body -//- [hidden for secure] imports -//- [hidden for secure] handler -//- [hidden for secure] target_path -//- [hidden for secure] runtime_version -//- [hidden for secure] packages -//- external_access_integrations -//- secrets -// -//javascript: -//- signature -//- returns -//- language -//- null handling -//- volatility -//- [hidden for secure] body - // TODO [SNOW-1348103 - this PR]: test creation with specifying parameters in the same query // CreateForJavaFunctionOptions diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index eea83e59dd..06f2005450 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -288,5 +288,5 @@ type functionDetailRow struct { type FunctionDetail struct { Property string - Value string + Value *string } diff --git a/pkg/sdk/functions_impl_gen.go b/pkg/sdk/functions_impl_gen.go index 901d3e0d14..36ba25d6a9 100644 --- a/pkg/sdk/functions_impl_gen.go +++ b/pkg/sdk/functions_impl_gen.go @@ -491,8 +491,8 @@ func (r functionDetailRow) convert() *FunctionDetail { e := &FunctionDetail{ Property: r.Property, } - if r.Value.Valid { - e.Value = r.Value.String + if r.Value.Valid && r.Value.String != "null" { + e.Value = String(r.Value.String) } return e } From 92b0f1d0babf2acf5493607c5a6d229a3335f300 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sat, 7 Dec 2024 15:21:10 +0100 Subject: [PATCH 29/63] Add parameters to each create --- pkg/sdk/functions_def.go | 20 ++++++ pkg/sdk/functions_dto_builders_gen.go | 100 ++++++++++++++++++++++++++ pkg/sdk/functions_dto_gen.go | 20 ++++++ pkg/sdk/functions_ext.go | 2 - pkg/sdk/functions_gen.go | 24 ++++++- 5 files changed, 162 insertions(+), 4 deletions(-) diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index 54016018c0..2231922afa 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -89,6 +89,10 @@ var FunctionsDef = g.NewInterface( ListAssignment("EXTERNAL_ACCESS_INTEGRATIONS", "AccountObjectIdentifier", g.ParameterOptions().Parentheses()). ListAssignment("SECRETS", "SecretReference", g.ParameterOptions().Parentheses()). OptionalTextAssignment("TARGET_PATH", g.ParameterOptions().SingleQuotes()). + OptionalBooleanAssignment("ENABLE_CONSOLE_OUTPUT", nil). + OptionalAssignment("LOG_LEVEL", g.KindOfTPointer[LogLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("METRIC_LEVEL", g.KindOfTPointer[MetricLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("TRACE_LEVEL", g.KindOfTPointer[TraceLevel](), g.ParameterOptions().SingleQuotes()). PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SQL("AS")). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ValidateValueSet, "Handler"). @@ -118,6 +122,10 @@ var FunctionsDef = g.NewInterface( PredefinedQueryStructField("NullInputBehavior", "*NullInputBehavior", g.KeywordOptions()). PredefinedQueryStructField("ReturnResultsBehavior", "*ReturnResultsBehavior", g.KeywordOptions()). OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). + OptionalBooleanAssignment("ENABLE_CONSOLE_OUTPUT", nil). + OptionalAssignment("LOG_LEVEL", g.KindOfTPointer[LogLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("METRIC_LEVEL", g.KindOfTPointer[MetricLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("TRACE_LEVEL", g.KindOfTPointer[TraceLevel](), g.ParameterOptions().SingleQuotes()). PredefinedQueryStructField("FunctionDefinition", "string", g.ParameterOptions().NoEquals().SQL("AS").Required()). WithValidation(g.ValidateValueSet, "FunctionDefinition"). WithValidation(g.ValidIdentifier, "name"), @@ -162,6 +170,10 @@ var FunctionsDef = g.NewInterface( TextAssignment("HANDLER", g.ParameterOptions().SingleQuotes().Required()). ListAssignment("EXTERNAL_ACCESS_INTEGRATIONS", "AccountObjectIdentifier", g.ParameterOptions().Parentheses()). ListAssignment("SECRETS", "SecretReference", g.ParameterOptions().Parentheses()). + OptionalBooleanAssignment("ENABLE_CONSOLE_OUTPUT", nil). + OptionalAssignment("LOG_LEVEL", g.KindOfTPointer[LogLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("METRIC_LEVEL", g.KindOfTPointer[MetricLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("TRACE_LEVEL", g.KindOfTPointer[TraceLevel](), g.ParameterOptions().SingleQuotes()). PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SQL("AS")). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ValidateValueSet, "RuntimeVersion"). @@ -204,6 +216,10 @@ var FunctionsDef = g.NewInterface( ). TextAssignment("HANDLER", g.ParameterOptions().SingleQuotes().Required()). OptionalTextAssignment("TARGET_PATH", g.ParameterOptions().SingleQuotes()). + OptionalBooleanAssignment("ENABLE_CONSOLE_OUTPUT", nil). + OptionalAssignment("LOG_LEVEL", g.KindOfTPointer[LogLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("METRIC_LEVEL", g.KindOfTPointer[MetricLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("TRACE_LEVEL", g.KindOfTPointer[TraceLevel](), g.ParameterOptions().SingleQuotes()). PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SQL("AS")). WithValidation(g.ValidIdentifier, "name"). WithValidation(g.ValidateValueSet, "Handler"). @@ -233,6 +249,10 @@ var FunctionsDef = g.NewInterface( PredefinedQueryStructField("ReturnResultsBehavior", "*ReturnResultsBehavior", g.KeywordOptions()). OptionalSQL("MEMOIZABLE"). OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). + OptionalBooleanAssignment("ENABLE_CONSOLE_OUTPUT", nil). + OptionalAssignment("LOG_LEVEL", g.KindOfTPointer[LogLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("METRIC_LEVEL", g.KindOfTPointer[MetricLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("TRACE_LEVEL", g.KindOfTPointer[TraceLevel](), g.ParameterOptions().SingleQuotes()). PredefinedQueryStructField("FunctionDefinition", "string", g.ParameterOptions().NoEquals().SQL("AS").Required()). WithValidation(g.ValidateValueSet, "FunctionDefinition"). WithValidation(g.ValidIdentifier, "name"), diff --git a/pkg/sdk/functions_dto_builders_gen.go b/pkg/sdk/functions_dto_builders_gen.go index 1a9a47ff60..acf9768fb4 100644 --- a/pkg/sdk/functions_dto_builders_gen.go +++ b/pkg/sdk/functions_dto_builders_gen.go @@ -99,6 +99,26 @@ func (s *CreateForJavaFunctionRequest) WithTargetPath(TargetPath string) *Create return s } +func (s *CreateForJavaFunctionRequest) WithEnableConsoleOutput(EnableConsoleOutput bool) *CreateForJavaFunctionRequest { + s.EnableConsoleOutput = &EnableConsoleOutput + return s +} + +func (s *CreateForJavaFunctionRequest) WithLogLevel(LogLevel LogLevel) *CreateForJavaFunctionRequest { + s.LogLevel = &LogLevel + return s +} + +func (s *CreateForJavaFunctionRequest) WithMetricLevel(MetricLevel MetricLevel) *CreateForJavaFunctionRequest { + s.MetricLevel = &MetricLevel + return s +} + +func (s *CreateForJavaFunctionRequest) WithTraceLevel(TraceLevel TraceLevel) *CreateForJavaFunctionRequest { + s.TraceLevel = &TraceLevel + return s +} + func (s *CreateForJavaFunctionRequest) WithFunctionDefinition(FunctionDefinition string) *CreateForJavaFunctionRequest { s.FunctionDefinition = &FunctionDefinition return s @@ -250,6 +270,26 @@ func (s *CreateForJavascriptFunctionRequest) WithComment(Comment string) *Create return s } +func (s *CreateForJavascriptFunctionRequest) WithEnableConsoleOutput(EnableConsoleOutput bool) *CreateForJavascriptFunctionRequest { + s.EnableConsoleOutput = &EnableConsoleOutput + return s +} + +func (s *CreateForJavascriptFunctionRequest) WithLogLevel(LogLevel LogLevel) *CreateForJavascriptFunctionRequest { + s.LogLevel = &LogLevel + return s +} + +func (s *CreateForJavascriptFunctionRequest) WithMetricLevel(MetricLevel MetricLevel) *CreateForJavascriptFunctionRequest { + s.MetricLevel = &MetricLevel + return s +} + +func (s *CreateForJavascriptFunctionRequest) WithTraceLevel(TraceLevel TraceLevel) *CreateForJavascriptFunctionRequest { + s.TraceLevel = &TraceLevel + return s +} + func NewCreateForPythonFunctionRequest( name SchemaObjectIdentifier, Returns FunctionReturnsRequest, @@ -339,6 +379,26 @@ func (s *CreateForPythonFunctionRequest) WithSecrets(Secrets []SecretReference) return s } +func (s *CreateForPythonFunctionRequest) WithEnableConsoleOutput(EnableConsoleOutput bool) *CreateForPythonFunctionRequest { + s.EnableConsoleOutput = &EnableConsoleOutput + return s +} + +func (s *CreateForPythonFunctionRequest) WithLogLevel(LogLevel LogLevel) *CreateForPythonFunctionRequest { + s.LogLevel = &LogLevel + return s +} + +func (s *CreateForPythonFunctionRequest) WithMetricLevel(MetricLevel MetricLevel) *CreateForPythonFunctionRequest { + s.MetricLevel = &MetricLevel + return s +} + +func (s *CreateForPythonFunctionRequest) WithTraceLevel(TraceLevel TraceLevel) *CreateForPythonFunctionRequest { + s.TraceLevel = &TraceLevel + return s +} + func (s *CreateForPythonFunctionRequest) WithFunctionDefinition(FunctionDefinition string) *CreateForPythonFunctionRequest { s.FunctionDefinition = &FunctionDefinition return s @@ -431,6 +491,26 @@ func (s *CreateForScalaFunctionRequest) WithTargetPath(TargetPath string) *Creat return s } +func (s *CreateForScalaFunctionRequest) WithEnableConsoleOutput(EnableConsoleOutput bool) *CreateForScalaFunctionRequest { + s.EnableConsoleOutput = &EnableConsoleOutput + return s +} + +func (s *CreateForScalaFunctionRequest) WithLogLevel(LogLevel LogLevel) *CreateForScalaFunctionRequest { + s.LogLevel = &LogLevel + return s +} + +func (s *CreateForScalaFunctionRequest) WithMetricLevel(MetricLevel MetricLevel) *CreateForScalaFunctionRequest { + s.MetricLevel = &MetricLevel + return s +} + +func (s *CreateForScalaFunctionRequest) WithTraceLevel(TraceLevel TraceLevel) *CreateForScalaFunctionRequest { + s.TraceLevel = &TraceLevel + return s +} + func (s *CreateForScalaFunctionRequest) WithFunctionDefinition(FunctionDefinition string) *CreateForScalaFunctionRequest { s.FunctionDefinition = &FunctionDefinition return s @@ -493,6 +573,26 @@ func (s *CreateForSQLFunctionRequest) WithComment(Comment string) *CreateForSQLF return s } +func (s *CreateForSQLFunctionRequest) WithEnableConsoleOutput(EnableConsoleOutput bool) *CreateForSQLFunctionRequest { + s.EnableConsoleOutput = &EnableConsoleOutput + return s +} + +func (s *CreateForSQLFunctionRequest) WithLogLevel(LogLevel LogLevel) *CreateForSQLFunctionRequest { + s.LogLevel = &LogLevel + return s +} + +func (s *CreateForSQLFunctionRequest) WithMetricLevel(MetricLevel MetricLevel) *CreateForSQLFunctionRequest { + s.MetricLevel = &MetricLevel + return s +} + +func (s *CreateForSQLFunctionRequest) WithTraceLevel(TraceLevel TraceLevel) *CreateForSQLFunctionRequest { + s.TraceLevel = &TraceLevel + return s +} + func NewAlterFunctionRequest( name SchemaObjectIdentifierWithArguments, ) *AlterFunctionRequest { diff --git a/pkg/sdk/functions_dto_gen.go b/pkg/sdk/functions_dto_gen.go index 9def0d3a85..c7c09bb8f1 100644 --- a/pkg/sdk/functions_dto_gen.go +++ b/pkg/sdk/functions_dto_gen.go @@ -36,6 +36,10 @@ type CreateForJavaFunctionRequest struct { ExternalAccessIntegrations []AccountObjectIdentifier Secrets []SecretReference TargetPath *string + EnableConsoleOutput *bool + LogLevel *LogLevel + MetricLevel *MetricLevel + TraceLevel *TraceLevel FunctionDefinition *string } @@ -86,6 +90,10 @@ type CreateForJavascriptFunctionRequest struct { NullInputBehavior *NullInputBehavior ReturnResultsBehavior *ReturnResultsBehavior Comment *string + EnableConsoleOutput *bool + LogLevel *LogLevel + MetricLevel *MetricLevel + TraceLevel *TraceLevel FunctionDefinition string // required } @@ -109,6 +117,10 @@ type CreateForPythonFunctionRequest struct { Handler string // required ExternalAccessIntegrations []AccountObjectIdentifier Secrets []SecretReference + EnableConsoleOutput *bool + LogLevel *LogLevel + MetricLevel *MetricLevel + TraceLevel *TraceLevel FunctionDefinition *string } @@ -131,6 +143,10 @@ type CreateForScalaFunctionRequest struct { Packages []FunctionPackageRequest Handler string // required TargetPath *string + EnableConsoleOutput *bool + LogLevel *LogLevel + MetricLevel *MetricLevel + TraceLevel *TraceLevel FunctionDefinition *string } @@ -146,6 +162,10 @@ type CreateForSQLFunctionRequest struct { ReturnResultsBehavior *ReturnResultsBehavior Memoizable *bool Comment *string + EnableConsoleOutput *bool + LogLevel *LogLevel + MetricLevel *MetricLevel + TraceLevel *TraceLevel FunctionDefinition string // required } diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 078e2d07a6..b21388b10c 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -99,8 +99,6 @@ func (d *FunctionDetail) setOptionalBoolValueOrError(property string, field **bo return nil } -// TODO [SNOW-1348103 - this PR]: test creation with specifying parameters in the same query - // CreateForJavaFunctionOptions // TODO [SNOW-1348103 - this PR]: test setting the paths for all types (like imports, target paths) // TODO [SNOW-1348103 - this PR]: test weird names for arg name - lower/upper if used with double quotes, to upper without quotes, dots, spaces, and both quotes not permitted diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index 06f2005450..d457a27b57 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -48,6 +48,10 @@ type CreateForJavaFunctionOptions struct { ExternalAccessIntegrations []AccountObjectIdentifier `ddl:"parameter,parentheses" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` Secrets []SecretReference `ddl:"parameter,parentheses" sql:"SECRETS"` TargetPath *string `ddl:"parameter,single_quotes" sql:"TARGET_PATH"` + EnableConsoleOutput *bool `ddl:"parameter" sql:"ENABLE_CONSOLE_OUTPUT"` + LogLevel *LogLevel `ddl:"parameter,single_quotes" sql:"LOG_LEVEL"` + MetricLevel *MetricLevel `ddl:"parameter,single_quotes" sql:"METRIC_LEVEL"` + TraceLevel *TraceLevel `ddl:"parameter,single_quotes" sql:"TRACE_LEVEL"` FunctionDefinition *string `ddl:"parameter,no_equals" sql:"AS"` } @@ -102,6 +106,10 @@ type CreateForJavascriptFunctionOptions struct { NullInputBehavior *NullInputBehavior `ddl:"keyword"` ReturnResultsBehavior *ReturnResultsBehavior `ddl:"keyword"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` + EnableConsoleOutput *bool `ddl:"parameter" sql:"ENABLE_CONSOLE_OUTPUT"` + LogLevel *LogLevel `ddl:"parameter,single_quotes" sql:"LOG_LEVEL"` + MetricLevel *MetricLevel `ddl:"parameter,single_quotes" sql:"METRIC_LEVEL"` + TraceLevel *TraceLevel `ddl:"parameter,single_quotes" sql:"TRACE_LEVEL"` FunctionDefinition string `ddl:"parameter,no_equals" sql:"AS"` } @@ -129,6 +137,10 @@ type CreateForPythonFunctionOptions struct { Handler string `ddl:"parameter,single_quotes" sql:"HANDLER"` ExternalAccessIntegrations []AccountObjectIdentifier `ddl:"parameter,parentheses" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` Secrets []SecretReference `ddl:"parameter,parentheses" sql:"SECRETS"` + EnableConsoleOutput *bool `ddl:"parameter" sql:"ENABLE_CONSOLE_OUTPUT"` + LogLevel *LogLevel `ddl:"parameter,single_quotes" sql:"LOG_LEVEL"` + MetricLevel *MetricLevel `ddl:"parameter,single_quotes" sql:"METRIC_LEVEL"` + TraceLevel *TraceLevel `ddl:"parameter,single_quotes" sql:"TRACE_LEVEL"` FunctionDefinition *string `ddl:"parameter,no_equals" sql:"AS"` } @@ -155,8 +167,12 @@ type CreateForScalaFunctionOptions struct { Imports []FunctionImport `ddl:"parameter,parentheses" sql:"IMPORTS"` Packages []FunctionPackage `ddl:"parameter,parentheses" sql:"PACKAGES"` Handler string `ddl:"parameter,single_quotes" sql:"HANDLER"` - TargetPath *string `ddl:"parameter,single_quotes" sql:"TARGET_PATH"` - FunctionDefinition *string `ddl:"parameter,no_equals" sql:"AS"` + TargetPath *string + EnableConsoleOutput *bool `ddl:"parameter" sql:"ENABLE_CONSOLE_OUTPUT"` + LogLevel *LogLevel `ddl:"parameter,single_quotes" sql:"LOG_LEVEL"` + MetricLevel *MetricLevel `ddl:"parameter,single_quotes" sql:"METRIC_LEVEL"` + TraceLevel *TraceLevel `ddl:"parameter,single_quotes" sql:"TRACE_LEVEL"` + FunctionDefinition *string `ddl:"parameter,no_equals" sql:"AS"` } // CreateForSQLFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#sql-handler. @@ -174,6 +190,10 @@ type CreateForSQLFunctionOptions struct { ReturnResultsBehavior *ReturnResultsBehavior `ddl:"keyword"` Memoizable *bool `ddl:"keyword" sql:"MEMOIZABLE"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` + EnableConsoleOutput *bool `ddl:"parameter" sql:"ENABLE_CONSOLE_OUTPUT"` + LogLevel *LogLevel `ddl:"parameter,single_quotes" sql:"LOG_LEVEL"` + MetricLevel *MetricLevel `ddl:"parameter,single_quotes" sql:"METRIC_LEVEL"` + TraceLevel *TraceLevel `ddl:"parameter,single_quotes" sql:"TRACE_LEVEL"` FunctionDefinition string `ddl:"parameter,no_equals" sql:"AS"` } From d5487fb69e312f90f0d03be32aa5534b581742a2 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sat, 7 Dec 2024 16:02:37 +0100 Subject: [PATCH 30/63] Pass first integration tests --- pkg/acceptance/helpers/function_client.go | 10 +- pkg/acceptance/testdatatypes/testdatatypes.go | 10 ++ pkg/datasources/functions.go | 2 +- pkg/resources/external_function.go | 8 +- pkg/resources/function.go | 16 +-- pkg/sdk/functions_ext.go | 39 ++++++ pkg/sdk/testint/functions_integration_test.go | 125 +++++++++++------- 7 files changed, 143 insertions(+), 67 deletions(-) create mode 100644 pkg/acceptance/testdatatypes/testdatatypes.go diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 27e06e72db..956cd2a7c1 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -90,16 +90,16 @@ func (c *FunctionClient) Show(t *testing.T, id sdk.SchemaObjectIdentifierWithArg return c.client().ShowByID(ctx, id) } -func (c *FunctionClient) SampleJavaDefinition(t *testing.T) string { +func (c *FunctionClient) SampleJavaDefinition(t *testing.T, className string, funcName string, argName string) string { t.Helper() - return ` - class TestFunc { - public static String echoVarchar(String x) { + return fmt.Sprintf(` + class %s { + public static String %s(String %s) { return x; } } -` +`, className, funcName, argName) } func (c *FunctionClient) SampleJavaScriptDefinition(t *testing.T) string { diff --git a/pkg/acceptance/testdatatypes/testdatatypes.go b/pkg/acceptance/testdatatypes/testdatatypes.go new file mode 100644 index 0000000000..dc11e82f0e --- /dev/null +++ b/pkg/acceptance/testdatatypes/testdatatypes.go @@ -0,0 +1,10 @@ +package testdatatypes + +import "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" + +var ( + DataTypeNumber_36_2, _ = datatypes.ParseDataType("NUMBER(36, 2)") + DataTypeVarchar_100, _ = datatypes.ParseDataType("VARCHAR(100)") + DataTypeFloat, _ = datatypes.ParseDataType("FLOAT") + DataTypeVariant, _ = datatypes.ParseDataType("VARIANT") +) diff --git a/pkg/datasources/functions.go b/pkg/datasources/functions.go index 480ed24b6f..8dc158b406 100644 --- a/pkg/datasources/functions.go +++ b/pkg/datasources/functions.go @@ -77,7 +77,7 @@ func ReadContextFunctions(ctx context.Context, d *schema.ResourceData, meta inte schemaName := d.Get("schema").(string) request := sdk.NewShowFunctionRequest() - request.WithIn(sdk.In{Schema: sdk.NewDatabaseObjectIdentifier(databaseName, schemaName)}) + request.WithIn(sdk.ExtendedIn{In: sdk.In{Schema: sdk.NewDatabaseObjectIdentifier(databaseName, schemaName)}}) functions, err := client.Functions.Show(ctx, request) if err != nil { id := d.Id() diff --git a/pkg/resources/external_function.go b/pkg/resources/external_function.go index 2580fb6141..7ff5270ae2 100644 --- a/pkg/resources/external_function.go +++ b/pkg/resources/external_function.go @@ -501,11 +501,11 @@ func UpdateContextExternalFunction(ctx context.Context, d *schema.ResourceData, req := sdk.NewAlterFunctionRequest(id) if d.HasChange("comment") { - _, new := d.GetChange("comment") - if new == "" { - req.UnsetComment = sdk.Bool(true) + _, newComment := d.GetChange("comment") + if newComment.(string) == "" { + req.WithUnset(*sdk.NewFunctionUnsetRequest().WithComment(true)) } else { - req.SetComment = sdk.String(new.(string)) + req.WithSet(*sdk.NewFunctionSetRequest().WithComment(newComment.(string))) } err := client.Functions.Alter(ctx, req) if err != nil { diff --git a/pkg/resources/function.go b/pkg/resources/function.go index ba91184217..e37d44a58f 100644 --- a/pkg/resources/function.go +++ b/pkg/resources/function.go @@ -571,7 +571,7 @@ func ReadContextFunction(ctx context.Context, d *schema.ResourceData, meta inter switch desc.Property { case "signature": // Format in Snowflake DB is: (argName argType, argName argType, ...) - value := strings.ReplaceAll(strings.ReplaceAll(desc.Value, "(", ""), ")", "") + value := strings.ReplaceAll(strings.ReplaceAll(*desc.Value, "(", ""), ")", "") if value != "" { // Do nothing for functions without arguments pairs := strings.Split(value, ", ") @@ -602,8 +602,8 @@ func ReadContextFunction(ctx context.Context, d *schema.ResourceData, meta inter case "returns": // Format in Snowflake DB is returnType() re := regexp.MustCompile(`^(.*)\([0-9]*\)$`) - match := re.FindStringSubmatch(desc.Value) - rt := desc.Value + rt := *desc.Value + match := re.FindStringSubmatch(rt) if match != nil { rt = match[1] } @@ -611,7 +611,7 @@ func ReadContextFunction(ctx context.Context, d *schema.ResourceData, meta inter diag.FromErr(err) } case "language": - if snowflake.Contains(languages, strings.ToLower(desc.Value)) { + if snowflake.Contains(languages, strings.ToLower(*desc.Value)) { if err := d.Set("language", desc.Value); err != nil { diag.FromErr(err) } @@ -619,7 +619,7 @@ func ReadContextFunction(ctx context.Context, d *schema.ResourceData, meta inter log.Printf("[INFO] Unexpected language for function %v returned from Snowflake", desc.Value) } case "packages": - value := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(desc.Value, "[", ""), "]", ""), "'", "") + value := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(*desc.Value, "[", ""), "]", ""), "'", "") if value != "" { // Do nothing for Java / Python functions without packages packages := strings.Split(value, ",") if err := d.Set("packages", packages); err != nil { @@ -627,7 +627,7 @@ func ReadContextFunction(ctx context.Context, d *schema.ResourceData, meta inter } } case "imports": - value := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(desc.Value, "[", ""), "]", ""), "'", "") + value := strings.ReplaceAll(strings.ReplaceAll(strings.ReplaceAll(*desc.Value, "[", ""), "]", ""), "'", "") if value != "" { // Do nothing for Java functions without imports imports := strings.Split(value, ",") if err := d.Set("imports", imports); err != nil { @@ -702,11 +702,11 @@ func UpdateContextFunction(ctx context.Context, d *schema.ResourceData, meta int if d.HasChange("comment") { comment := d.Get("comment") if comment != "" { - if err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSetComment(comment.(string))); err != nil { + if err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSet(*sdk.NewFunctionSetRequest().WithComment(comment.(string)))); err != nil { return diag.FromErr(err) } } else { - if err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnsetComment(true)); err != nil { + if err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnset(*sdk.NewFunctionUnsetRequest().WithComment(true))); err != nil { return diag.FromErr(err) } } diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index b21388b10c..a6dfe1fda2 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -99,6 +99,45 @@ func (d *FunctionDetail) setOptionalBoolValueOrError(property string, field **bo return nil } +func (s *CreateForJavaFunctionRequest) WithFunctionDefinitionWrapped(FunctionDefinition string) *CreateForJavaFunctionRequest { + s.FunctionDefinition = String(fmt.Sprintf(`$$%s$$`, FunctionDefinition)) + return s +} + +func (s *CreateForPythonFunctionRequest) WithFunctionDefinitionWrapped(FunctionDefinition string) *CreateForPythonFunctionRequest { + s.FunctionDefinition = String(fmt.Sprintf(`$$%s$$`, FunctionDefinition)) + return s +} + +func (s *CreateForScalaFunctionRequest) WithFunctionDefinitionWrapped(FunctionDefinition string) *CreateForScalaFunctionRequest { + s.FunctionDefinition = String(fmt.Sprintf(`$$%s$$`, FunctionDefinition)) + return s +} + +func NewCreateForSQLFunctionRequestDefinitionWrapped( + name SchemaObjectIdentifier, + Returns FunctionReturnsRequest, + FunctionDefinition string, +) *CreateForSQLFunctionRequest { + s := CreateForSQLFunctionRequest{} + s.name = name + s.Returns = Returns + s.FunctionDefinition = fmt.Sprintf(`$$%s$$`, FunctionDefinition) + return &s +} + +func NewCreateForJavascriptFunctionRequestDefinitionWrapped( + name SchemaObjectIdentifier, + Returns FunctionReturnsRequest, + FunctionDefinition string, +) *CreateForJavascriptFunctionRequest { + s := CreateForJavascriptFunctionRequest{} + s.name = name + s.Returns = Returns + s.FunctionDefinition = fmt.Sprintf(`$$%s$$`, FunctionDefinition) + return &s +} + // CreateForJavaFunctionOptions // TODO [SNOW-1348103 - this PR]: test setting the paths for all types (like imports, target paths) // TODO [SNOW-1348103 - this PR]: test weird names for arg name - lower/upper if used with double quotes, to upper without quotes, dots, spaces, and both quotes not permitted diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 6758bae72a..b8e8cf6e5d 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" @@ -14,60 +15,67 @@ import ( "github.com/stretchr/testify/require" ) -/* -todo: add tests for: - - creating functions with different languages (java, javascript, python, scala, sql) from stages using [ TARGET_PATH = '' ] -*/ - func TestInt_CreateFunctions(t *testing.T) { client := testClient(t) ctx := context.Background() - cleanupFunctionHandle := func(id sdk.SchemaObjectIdentifierWithArguments) func() { - return func() { - err := client.Functions.Drop(ctx, sdk.NewDropFunctionRequest(id)) - if errors.Is(err, sdk.ErrObjectNotExistOrAuthorized) { - return - } - require.NoError(t, err) - } - } + t.Run("create function for Java - inline minimal", func(t *testing.T) { + className := "TestFunc" + funcName := "echoVarchar" + argName := "x" - t.Run("create function for Java - inline minimal", func(t *testing.T) {}) - t.Run("create function for Java - inline full", func(t *testing.T) {}) - t.Run("create function for Java - staged", func(t *testing.T) {}) + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) + argument := sdk.NewFunctionArgumentRequest(argName, testdatatypes.DataTypeVarchar_100) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(testdatatypes.DataTypeVarchar_100) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + handler := fmt.Sprintf("%s.%s", className, funcName) + definition := testClientHelper().Function.SampleJavaDefinition(t, className, funcName, argName) - t.Run("create function for JavaScript - inline minimal", func(t *testing.T) {}) - t.Run("create function for JavaScript - inline full", func(t *testing.T) {}) + request := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithFunctionDefinitionWrapped(definition) - t.Run("create function for Python - inline minimal", func(t *testing.T) {}) - t.Run("create function for Python - inline full", func(t *testing.T) {}) - t.Run("create function for Python - staged", func(t *testing.T) {}) + err := client.Functions.CreateForJava(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) - t.Run("create function for Scala - inline minimal", func(t *testing.T) {}) - t.Run("create function for Scala - inline full", func(t *testing.T) {}) - t.Run("create function for Scala - staged", func(t *testing.T) {}) + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + require.Equal(t, id.Name(), function.Name) + require.Equal(t, "JAVA", function.Language) + }) - t.Run("create function for SQL - inline minimal", func(t *testing.T) {}) - t.Run("create function for SQL - inline full", func(t *testing.T) {}) + t.Run("create function for Java - inline full", func(t *testing.T) { + className := "TestFunc" + funcName := "echoVarchar" + argName := "x" - t.Run("create function for Java", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) + argument := sdk.NewFunctionArgumentRequest(argName, testdatatypes.DataTypeVarchar_100) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(testdatatypes.DataTypeVarchar_100) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + handler := fmt.Sprintf("%s.%s", className, funcName) + definition := testClientHelper().Function.SampleJavaDefinition(t, className, funcName, argName) - definition := testClientHelper().Function.SampleJavaDefinition(t) target := fmt.Sprintf("@~/tf-%d.jar", time.Now().Unix()) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeVARCHAR) - returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - argument := sdk.NewFunctionArgumentRequest("x", nil).WithDefaultValue("'abc'").WithArgDataTypeOld(sdk.DataTypeVARCHAR) - request := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, "TestFunc.echoVarchar"). + request := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). WithOrReplace(true). WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithCopyGrants(true). WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorCalledOnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithRuntimeVersion("11"). + WithComment("comment"). + //WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport("lang.*")}). + WithPackages([]sdk.FunctionPackageRequest{*sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:latest")}). + //WithExternalAccessIntegrations(). + //WithSecrets(). WithTargetPath(target). - WithFunctionDefinition(definition) + WithFunctionDefinitionWrapped(definition) + err := client.Functions.CreateForJava(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupFunctionHandle(id)) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) @@ -75,6 +83,25 @@ func TestInt_CreateFunctions(t *testing.T) { require.Equal(t, "JAVA", function.Language) }) + t.Run("create function for Java - staged minimal", func(t *testing.T) {}) + t.Run("create function for Java - staged full", func(t *testing.T) {}) + + t.Run("create function for JavaScript - inline minimal", func(t *testing.T) {}) + t.Run("create function for JavaScript - inline full", func(t *testing.T) {}) + + t.Run("create function for Python - inline minimal", func(t *testing.T) {}) + t.Run("create function for Python - inline full", func(t *testing.T) {}) + t.Run("create function for Python - staged minimal", func(t *testing.T) {}) + t.Run("create function for Python - staged full", func(t *testing.T) {}) + + t.Run("create function for Scala - inline minimal", func(t *testing.T) {}) + t.Run("create function for Scala - inline full", func(t *testing.T) {}) + t.Run("create function for Scala - staged minimal", func(t *testing.T) {}) + t.Run("create function for Scala - staged full", func(t *testing.T) {}) + + t.Run("create function for SQL - inline minimal", func(t *testing.T) {}) + t.Run("create function for SQL - inline full", func(t *testing.T) {}) + t.Run("create function for Javascript", func(t *testing.T) { id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) @@ -88,7 +115,7 @@ func TestInt_CreateFunctions(t *testing.T) { WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorCalledOnNullInput)) err := client.Functions.CreateForJavascript(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupFunctionHandle(id)) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) @@ -109,7 +136,7 @@ func TestInt_CreateFunctions(t *testing.T) { WithFunctionDefinition(definition) err := client.Functions.CreateForPython(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupFunctionHandle(id)) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) @@ -132,7 +159,7 @@ func TestInt_CreateFunctions(t *testing.T) { WithFunctionDefinition(definition) err := client.Functions.CreateForScala(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupFunctionHandle(id)) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) @@ -153,7 +180,7 @@ func TestInt_CreateFunctions(t *testing.T) { WithComment("comment") err := client.Functions.CreateForSQL(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupFunctionHandle(id)) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) @@ -172,7 +199,7 @@ func TestInt_CreateFunctions(t *testing.T) { WithComment("comment") err := client.Functions.CreateForSQL(ctx, request) require.NoError(t, err) - t.Cleanup(cleanupFunctionHandle(id)) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) @@ -278,7 +305,7 @@ func TestInt_OtherFunctions(t *testing.T) { f := createFunctionForSQLHandle(t, true, true) id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSetLogLevel(string(sdk.LogLevelDebug))) + err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSet(*sdk.NewFunctionSetRequest().WithLogLevel(sdk.LogLevelDebug))) require.NoError(t, err) assertFunction(t, id, false, true) }) @@ -287,7 +314,7 @@ func TestInt_OtherFunctions(t *testing.T) { f := createFunctionForSQLHandle(t, true, true) id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnsetLogLevel(true)) + err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnset(*sdk.NewFunctionUnsetRequest().WithLogLevel(true))) require.NoError(t, err) assertFunction(t, id, false, true) }) @@ -296,7 +323,7 @@ func TestInt_OtherFunctions(t *testing.T) { f := createFunctionForSQLHandle(t, true, true) id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSetTraceLevel(string(sdk.TraceLevelAlways))) + err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSet(*sdk.NewFunctionSetRequest().WithTraceLevel(sdk.TraceLevelAlways))) require.NoError(t, err) assertFunction(t, id, false, true) }) @@ -305,7 +332,7 @@ func TestInt_OtherFunctions(t *testing.T) { f := createFunctionForSQLHandle(t, true, true) id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnsetTraceLevel(true)) + err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnset(*sdk.NewFunctionUnsetRequest().WithTraceLevel(true))) require.NoError(t, err) assertFunction(t, id, false, true) }) @@ -314,7 +341,7 @@ func TestInt_OtherFunctions(t *testing.T) { f := createFunctionForSQLHandle(t, true, true) id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSetComment("test comment")) + err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSet(*sdk.NewFunctionSetRequest().WithComment("test comment"))) require.NoError(t, err) assertFunction(t, id, false, true) }) @@ -323,7 +350,7 @@ func TestInt_OtherFunctions(t *testing.T) { f := createFunctionForSQLHandle(t, true, true) id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnsetComment(true)) + err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnset(*sdk.NewFunctionUnsetRequest().WithComment(true))) require.NoError(t, err) assertFunction(t, id, false, true) }) @@ -390,7 +417,7 @@ func TestInt_OtherFunctions(t *testing.T) { require.NoError(t, err) pairs := make(map[string]string) for _, detail := range details { - pairs[detail.Property] = detail.Value + pairs[detail.Property] = *detail.Value } require.Equal(t, "SQL", pairs["language"]) require.Equal(t, "FLOAT", pairs["returns"]) @@ -405,7 +432,7 @@ func TestInt_OtherFunctions(t *testing.T) { require.NoError(t, err) pairs := make(map[string]string) for _, detail := range details { - pairs[detail.Property] = detail.Value + pairs[detail.Property] = *detail.Value } require.Equal(t, "SQL", pairs["language"]) require.Equal(t, "FLOAT", pairs["returns"]) @@ -616,7 +643,7 @@ func TestInt_FunctionsShowByID(t *testing.T) { require.NoError(t, err) pairs := make(map[string]string) for _, detail := range details { - pairs[detail.Property] = detail.Value + pairs[detail.Property] = *detail.Value } assert.Equal(t, fmt.Sprintf("(%s %s)", argName, oldDataType), pairs["signature"]) assert.Equal(t, dataType.Canonical(), pairs["returns"]) From ea9b33e64a0cc6e365ae0b5e0882c9d512319a48 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sat, 7 Dec 2024 16:58:42 +0100 Subject: [PATCH 31/63] Extend java inline function creation --- .../objectassert/function_snowflake_ext.go | 19 +++++ .../objectassert/function_snowflake_gen.go | 11 +++ .../external_access_integration_client.go | 20 ++++-- pkg/sdk/functions_ext.go | 2 + pkg/sdk/testint/functions_integration_test.go | 69 +++++++++++++++++-- 5 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_ext.go diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_ext.go b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_ext.go new file mode 100644 index 0000000000..b73b07faef --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_ext.go @@ -0,0 +1,19 @@ +package objectassert + +import ( + "fmt" + "testing" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" +) + +func (a *FunctionAssert) HasCreatedOnNotEmpty() *FunctionAssert { + a.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.CreatedOn == "" { + return fmt.Errorf("expected create_on to be not empty") + } + return nil + }) + return a +} diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go index 0f0c6dd130..62e9b961cc 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go @@ -230,3 +230,14 @@ func (f *FunctionAssert) HasIsMemoizable(expected bool) *FunctionAssert { }) return f } + +func (f *FunctionAssert) HasIsDataMetric(expected bool) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.IsDataMetric != expected { + return fmt.Errorf("expected is data metric: %v; got: %v", expected, o.IsDataMetric) + } + return nil + }) + return f +} diff --git a/pkg/acceptance/helpers/external_access_integration_client.go b/pkg/acceptance/helpers/external_access_integration_client.go index 0e7496e85e..d5a6a59b91 100644 --- a/pkg/acceptance/helpers/external_access_integration_client.go +++ b/pkg/acceptance/helpers/external_access_integration_client.go @@ -26,22 +26,32 @@ func (c *ExternalAccessIntegrationClient) client() *sdk.Client { return c.context.client } -func (c *ExternalAccessIntegrationClient) CreateExternalAccessIntegration(t *testing.T, networkRuleId sdk.SchemaObjectIdentifier) (sdk.SchemaObjectIdentifier, func()) { +func (c *ExternalAccessIntegrationClient) CreateExternalAccessIntegration(t *testing.T, networkRuleId sdk.SchemaObjectIdentifier) (sdk.AccountObjectIdentifier, func()) { t.Helper() ctx := context.Background() - id := c.ids.RandomSchemaObjectIdentifier() - _, err := c.client().ExecForTests(ctx, fmt.Sprintf(`CREATE EXTERNAL ACCESS INTEGRATION %s ALLOWED_NETWORK_RULES = (%s) ENABLED = TRUE`, id.Name(), networkRuleId.Name())) + id := c.ids.RandomAccountObjectIdentifier() + _, err := c.client().ExecForTests(ctx, fmt.Sprintf(`CREATE EXTERNAL ACCESS INTEGRATION %s ALLOWED_NETWORK_RULES = (%s) ENABLED = TRUE`, id.FullyQualifiedName(), networkRuleId.FullyQualifiedName())) require.NoError(t, err) return id, c.DropExternalAccessIntegrationFunc(t, id) } -func (c *ExternalAccessIntegrationClient) DropExternalAccessIntegrationFunc(t *testing.T, id sdk.SchemaObjectIdentifier) func() { +func (c *ExternalAccessIntegrationClient) CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t *testing.T, networkRuleId sdk.SchemaObjectIdentifier, secretId sdk.SchemaObjectIdentifier) (sdk.AccountObjectIdentifier, func()) { + t.Helper() + ctx := context.Background() + + id := c.ids.RandomAccountObjectIdentifier() + _, err := c.client().ExecForTests(ctx, fmt.Sprintf(`CREATE EXTERNAL ACCESS INTEGRATION %s ALLOWED_NETWORK_RULES = (%s) ALLOWED_AUTHENTICATION_SECRETS = (%s) ENABLED = TRUE`, id.FullyQualifiedName(), networkRuleId.FullyQualifiedName(), secretId.FullyQualifiedName())) + require.NoError(t, err) + return id, c.DropExternalAccessIntegrationFunc(t, id) +} + +func (c *ExternalAccessIntegrationClient) DropExternalAccessIntegrationFunc(t *testing.T, id sdk.AccountObjectIdentifier) func() { t.Helper() ctx := context.Background() return func() { - _, err := c.client().ExecForTests(ctx, fmt.Sprintf(`DROP EXTERNAL ACCESS INTEGRATION IF EXISTS %s`, id.Name())) + _, err := c.client().ExecForTests(ctx, fmt.Sprintf(`DROP EXTERNAL ACCESS INTEGRATION IF EXISTS %s`, id.FullyQualifiedName())) require.NoError(t, err) } } diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index a6dfe1fda2..2ec7d548e1 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -7,6 +7,8 @@ import ( "strconv" ) +const DefaultFunctionComment = "user-defined function" + func (v *Function) ID() SchemaObjectIdentifierWithArguments { return NewSchemaObjectIdentifierWithArguments(v.CatalogName, v.SchemaName, v.Name, v.ArgumentsOld...) } diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index b8e8cf6e5d..4d0cd92c8d 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -7,6 +7,9 @@ import ( "testing" "time" + assertions "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/objectassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" @@ -15,9 +18,23 @@ import ( "github.com/stretchr/testify/require" ) +// TODO [next PR]: schemaName and catalog name are quoted (because we use lowercase) +// TODO [next PR]: HasArgumentsRawFrom(functionId, arguments, return) +// TODO [next PR]: WithImports - creation and cleanup on stage needed +// TODO [next PR]: extract show assertions with commons fields func TestInt_CreateFunctions(t *testing.T) { client := testClient(t) ctx := context.Background() + secretId := testClientHelper().Ids.RandomSchemaObjectIdentifier() + + networkRule, networkRuleCleanup := testClientHelper().NetworkRule.Create(t) + t.Cleanup(networkRuleCleanup) + + secret, secretCleanup := testClientHelper().Secret.CreateWithGenericString(t, secretId, "test_secret_string") + t.Cleanup(secretCleanup) + + externalAccessIntegration, externalAccessIntegrationCleanup := testClientHelper().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret.ID()) + t.Cleanup(externalAccessIntegrationCleanup) t.Run("create function for Java - inline minimal", func(t *testing.T) { className := "TestFunc" @@ -41,8 +58,28 @@ func TestInt_CreateFunctions(t *testing.T) { function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) - require.Equal(t, id.Name(), function.Name) - require.Equal(t, "JAVA", function.Language) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.DataTypeVARCHAR}). + HasArgumentsRaw(fmt.Sprintf(`%s(VARCHAR) RETURN VARCHAR`, function.ID().Name())). + HasDescription(sdk.DefaultFunctionComment). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("JAVA"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) }) t.Run("create function for Java - inline full", func(t *testing.T) { @@ -68,8 +105,8 @@ func TestInt_CreateFunctions(t *testing.T) { WithComment("comment"). //WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport("lang.*")}). WithPackages([]sdk.FunctionPackageRequest{*sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:latest")}). - //WithExternalAccessIntegrations(). - //WithSecrets(). + WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). + WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}). WithTargetPath(target). WithFunctionDefinitionWrapped(definition) @@ -79,8 +116,28 @@ func TestInt_CreateFunctions(t *testing.T) { function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) - require.Equal(t, id.Name(), function.Name) - require.Equal(t, "JAVA", function.Language) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.DataTypeVARCHAR}). + HasArgumentsRaw(fmt.Sprintf(`%s(VARCHAR) RETURN VARCHAR`, function.ID().Name())). + HasDescription("comment"). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("JAVA"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) }) t.Run("create function for Java - staged minimal", func(t *testing.T) {}) From 8a177496c293e74b7feb77670afd8d7d4f733a50 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sat, 7 Dec 2024 20:09:49 +0100 Subject: [PATCH 32/63] Add describe assertions --- .../function_describe_snowflake_ext.go | 356 ++++++++++++++++++ pkg/acceptance/helpers/function_client.go | 7 + pkg/sdk/common_types.go | 2 +- pkg/sdk/functions_ext.go | 2 +- pkg/sdk/testint/functions_integration_test.go | 54 ++- 5 files changed, 414 insertions(+), 7 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go b/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go new file mode 100644 index 0000000000..0b8ca9312d --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go @@ -0,0 +1,356 @@ +package objectassert + +import ( + "fmt" + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" +) + +// TODO [SNOW-1501905]: this file should be fully regenerated when adding and option to assert the results of describe +type FunctionDetailsAssert struct { + *assert.SnowflakeObjectAssert[sdk.FunctionDetails, sdk.SchemaObjectIdentifierWithArguments] +} + +func FunctionDetails(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments) *FunctionDetailsAssert { + t.Helper() + return &FunctionDetailsAssert{ + assert.NewSnowflakeObjectAssertWithProvider(sdk.ObjectType("FUNCTION_DETAILS"), id, acc.TestClient().Function.DescribeDetails), + } +} + +func (f *FunctionDetailsAssert) HasSignature(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Signature != expected { + return fmt.Errorf("expected signature: %v; got: %v", expected, o.Signature) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasReturns(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Returns != expected { + return fmt.Errorf("expected returns: %v; got: %v", expected, o.Returns) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasLanguage(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Language != expected { + return fmt.Errorf("expected language: %v; got: %v", expected, o.Language) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasBody(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Body == nil { + return fmt.Errorf("expected body to have value; got: nil") + } + if *o.Body != expected { + return fmt.Errorf("expected body: %v; got: %v", expected, *o.Body) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasNullHandling(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.NullHandling == nil { + return fmt.Errorf("expected null handling to have value; got: nil") + } + if *o.NullHandling != expected { + return fmt.Errorf("expected null handling: %v; got: %v", expected, *o.NullHandling) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasVolatility(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Volatility == nil { + return fmt.Errorf("expected volatility to have value; got: nil") + } + if *o.Volatility != expected { + return fmt.Errorf("expected volatility: %v; got: %v", expected, *o.Volatility) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasExternalAccessIntegrations(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.ExternalAccessIntegrations == nil { + return fmt.Errorf("expected external access integrations to have value; got: nil") + } + if *o.ExternalAccessIntegrations != expected { + return fmt.Errorf("expected external access integrations: %v; got: %v", expected, *o.ExternalAccessIntegrations) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasSecrets(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Secrets == nil { + return fmt.Errorf("expected secrets to have value; got: nil") + } + if *o.Secrets != expected { + return fmt.Errorf("expected secrets: %v; got: %v", expected, *o.Secrets) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasImports(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Imports == nil { + return fmt.Errorf("expected imports to have value; got: nil") + } + if *o.Imports != expected { + return fmt.Errorf("expected imports: %v; got: %v", expected, *o.Imports) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasHandler(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Handler == nil { + return fmt.Errorf("expected handler to have value; got: nil") + } + if *o.Handler != expected { + return fmt.Errorf("expected handler: %v; got: %v", expected, *o.Handler) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasRuntimeVersion(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.RuntimeVersion == nil { + return fmt.Errorf("expected runtime version to have value; got: nil") + } + if *o.RuntimeVersion != expected { + return fmt.Errorf("expected runtime version: %v; got: %v", expected, *o.RuntimeVersion) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasPackages(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Packages == nil { + return fmt.Errorf("expected packages to have value; got: nil") + } + if *o.Packages != expected { + return fmt.Errorf("expected packages: %v; got: %v", expected, *o.Packages) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasTargetPath(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.TargetPath == nil { + return fmt.Errorf("expected target path to have value; got: nil") + } + if *o.TargetPath != expected { + return fmt.Errorf("expected target path: %v; got: %v", expected, *o.TargetPath) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasInstalledPackages(expected string) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.InstalledPackages == nil { + return fmt.Errorf("expected installed packages to have value; got: nil") + } + if *o.InstalledPackages != expected { + return fmt.Errorf("expected installed packages: %v; got: %v", expected, *o.InstalledPackages) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasIsAggregate(expected bool) *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.IsAggregate == nil { + return fmt.Errorf("expected is aggregate to have value; got: nil") + } + if *o.IsAggregate != expected { + return fmt.Errorf("expected is aggregate: %v; got: %v", expected, *o.IsAggregate) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasBodyNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Body != nil { + return fmt.Errorf("expected body to be nil, was %v", o.Body) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasNullHandlingNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.NullHandling != nil { + return fmt.Errorf("expected null handling to be nil, was %v", o.NullHandling) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasVolatilityNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Volatility != nil { + return fmt.Errorf("expected volatility to be nil, was %v", o.Volatility) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasExternalAccessIntegrationsNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.ExternalAccessIntegrations != nil { + return fmt.Errorf("expected external access integrations to be nil, was %v", o.ExternalAccessIntegrations) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasSecretsNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Secrets != nil { + return fmt.Errorf("expected secrets to be nil, was %v", o.Secrets) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasImportsNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Imports != nil { + return fmt.Errorf("expected imports to be nil, was %v", o.Imports) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasHandlerNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Handler != nil { + return fmt.Errorf("expected handler to be nil, was %v", o.Handler) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasRuntimeVersionNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.RuntimeVersion != nil { + return fmt.Errorf("expected runtime version to be nil, was %v", o.RuntimeVersion) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasPackagesNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.Packages != nil { + return fmt.Errorf("expected packages to be nil, was %v", o.Packages) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasTargetPathNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.TargetPath != nil { + return fmt.Errorf("expected target path to be nil, was %v", o.TargetPath) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasInstalledPackagesNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.InstalledPackages != nil { + return fmt.Errorf("expected installed packages to be nil, was %v", o.InstalledPackages) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasIsAggregateNil() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.IsAggregate != nil { + return fmt.Errorf("expected is aggregate to be nil, was %v", o.IsAggregate) + } + return nil + }) + return f +} diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 956cd2a7c1..04dfdeabcf 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -90,6 +90,13 @@ func (c *FunctionClient) Show(t *testing.T, id sdk.SchemaObjectIdentifierWithArg return c.client().ShowByID(ctx, id) } +func (c *FunctionClient) DescribeDetails(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments) (*sdk.FunctionDetails, error) { + t.Helper() + ctx := context.Background() + + return c.client().DescribeDetails(ctx, id) +} + func (c *FunctionClient) SampleJavaDefinition(t *testing.T, className string, funcName string, argName string) string { t.Helper() diff --git a/pkg/sdk/common_types.go b/pkg/sdk/common_types.go index e14ae3e644..ca2bc6b9b3 100644 --- a/pkg/sdk/common_types.go +++ b/pkg/sdk/common_types.go @@ -233,7 +233,7 @@ func NullInputBehaviorPointer(v NullInputBehavior) *NullInputBehavior { const ( NullInputBehaviorCalledOnNullInput NullInputBehavior = "CALLED ON NULL INPUT" - NullInputBehaviorReturnNullInput NullInputBehavior = "RETURN NULL ON NULL INPUT" + NullInputBehaviorReturnNullInput NullInputBehavior = "RETURNS NULL ON NULL INPUT" NullInputBehaviorStrict NullInputBehavior = "STRICT" ) diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 2ec7d548e1..fefdabc25e 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -65,7 +65,7 @@ func functionDetailsFromRows(rows []FunctionDetail) (*FunctionDetails, error) { v.InstalledPackages = row.Value case "is_aggregate": errs = append(errs, row.setOptionalBoolValueOrError("is_aggregate", &v.IsAggregate)) - case "targetPath": + case "target_path": v.TargetPath = row.Value } } diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 4d0cd92c8d..167d954ee4 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -80,6 +80,26 @@ func TestInt_CreateFunctions(t *testing.T) { HasIsMemoizable(false). HasIsDataMetric(false), ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, testdatatypes.DataTypeVarchar_100.ToLegacyDataTypeSql())). + HasReturns(testdatatypes.DataTypeVarchar_100.ToSql()). + HasLanguage("JAVA"). + HasBody(definition). + HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImports(`[]`). + HasHandler(handler). + HasRuntimeVersionNil(). + HasPackages(`[]`). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + // TODO [this PR]: check function parameters }) t.Run("create function for Java - inline full", func(t *testing.T) { @@ -93,21 +113,25 @@ func TestInt_CreateFunctions(t *testing.T) { returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) handler := fmt.Sprintf("%s.%s", className, funcName) definition := testClientHelper().Function.SampleJavaDefinition(t, className, funcName, argName) + targetPath := fmt.Sprintf("@~/tf-%d.jar", time.Now().Unix()) - target := fmt.Sprintf("@~/tf-%d.jar", time.Now().Unix()) request := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). WithOrReplace(true). WithArguments([]sdk.FunctionArgumentRequest{*argument}). WithCopyGrants(true). - WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorCalledOnNullInput)). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). WithRuntimeVersion("11"). WithComment("comment"). - //WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport("lang.*")}). - WithPackages([]sdk.FunctionPackageRequest{*sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:latest")}). + // TODO [this PR]: test imports + // WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport("lang.*")}). + WithPackages([]sdk.FunctionPackageRequest{ + *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:1.14.0"), + *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:telemetry:0.1.0"), + }). WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}). - WithTargetPath(target). + WithTargetPath(targetPath). WithFunctionDefinitionWrapped(definition) err := client.Functions.CreateForJava(ctx, request) @@ -138,6 +162,26 @@ func TestInt_CreateFunctions(t *testing.T) { HasIsMemoizable(false). HasIsDataMetric(false), ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, testdatatypes.DataTypeVarchar_100.ToLegacyDataTypeSql())). + HasReturns(testdatatypes.DataTypeVarchar_100.ToSql()). + HasLanguage("JAVA"). + HasBody(definition). + HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + // TODO [this PR]: parse to identifier list + // TODO [this PR]: check multiple secrets (to know how to parse) + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). + HasImports(`[]`). + HasHandler(handler). + HasRuntimeVersion("11"). + HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasTargetPath(targetPath). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) }) t.Run("create function for Java - staged minimal", func(t *testing.T) {}) From c3a3b596d645f34f35eab3faeb0b8561dd7b0da0 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sat, 7 Dec 2024 20:14:47 +0100 Subject: [PATCH 33/63] Add function parameters definition --- .../gen/object_parameters_def.go | 11 +++++++++++ pkg/sdk/parameters.go | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/pkg/acceptance/bettertestspoc/assert/objectparametersassert/gen/object_parameters_def.go b/pkg/acceptance/bettertestspoc/assert/objectparametersassert/gen/object_parameters_def.go index 67a6b4e7a0..7db9f72c07 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectparametersassert/gen/object_parameters_def.go +++ b/pkg/acceptance/bettertestspoc/assert/objectparametersassert/gen/object_parameters_def.go @@ -205,4 +205,15 @@ var allObjectsParameters = []SnowflakeObjectParameters{ // TODO(SNOW-1348092 - next prs): Add parameters }, }, + { + Name: "Function", + IdType: "sdk.SchemaObjectIdentifierWithArguments", + Level: sdk.ParameterTypeFunction, + Parameters: []SnowflakeParameter{ + {ParameterName: string(sdk.FunctionParameterEnableConsoleOutput), ParameterType: "bool", DefaultValue: "false", DefaultLevel: "sdk.ParameterTypeSnowflakeDefault"}, + {ParameterName: string(sdk.FunctionParameterLogLevel), ParameterType: "sdk.LogLevel", DefaultValue: "sdk.LogLevelOff", DefaultLevel: "sdk.ParameterTypeSnowflakeDefault"}, + {ParameterName: string(sdk.FunctionParameterMetricLevel), ParameterType: "sdk.MetricLevel", DefaultValue: "sdk.MetricLevelNone", DefaultLevel: "sdk.ParameterTypeSnowflakeDefault"}, + {ParameterName: string(sdk.FunctionParameterTraceLevel), ParameterType: "sdk.TraceLevel", DefaultValue: "sdk.TraceLevelOff", DefaultLevel: "sdk.ParameterTypeSnowflakeDefault"}, + }, + }, } diff --git a/pkg/sdk/parameters.go b/pkg/sdk/parameters.go index cf29fa1da4..5abfc165be 100644 --- a/pkg/sdk/parameters.go +++ b/pkg/sdk/parameters.go @@ -833,6 +833,15 @@ const ( DatabaseParameterEnableConsoleOutput DatabaseParameter = "ENABLE_CONSOLE_OUTPUT" ) +type FunctionParameter string + +const ( + FunctionParameterEnableConsoleOutput FunctionParameter = "ENABLE_CONSOLE_OUTPUT" + FunctionParameterLogLevel FunctionParameter = "LOG_LEVEL" + FunctionParameterMetricLevel FunctionParameter = "METRIC_LEVEL" + FunctionParameterTraceLevel FunctionParameter = "TRACE_LEVEL" +) + // AccountParameters is based on https://docs.snowflake.com/en/sql-reference/parameters#account-parameters. type AccountParameters struct { // Account Parameters @@ -1370,6 +1379,7 @@ const ( ParameterTypeDatabase ParameterType = "DATABASE" ParameterTypeSchema ParameterType = "SCHEMA" ParameterTypeTask ParameterType = "TASK" + ParameterTypeFunction ParameterType = "FUNCTION" ) type Parameter struct { From bc6ebfef278a41ecf48e751c39989db4da18aa16 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sat, 7 Dec 2024 20:21:59 +0100 Subject: [PATCH 34/63] Add function parameters assertions and helpers --- .../function_parameters_snowflake_gen.go | 170 ++++++++++++++++++ pkg/acceptance/helpers/parameter_client.go | 11 ++ pkg/sdk/functions_ext.go | 8 + pkg/sdk/functions_gen.go | 1 + pkg/sdk/parameters.go | 24 +-- 5 files changed, 204 insertions(+), 10 deletions(-) create mode 100644 pkg/acceptance/bettertestspoc/assert/objectparametersassert/function_parameters_snowflake_gen.go diff --git a/pkg/acceptance/bettertestspoc/assert/objectparametersassert/function_parameters_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectparametersassert/function_parameters_snowflake_gen.go new file mode 100644 index 0000000000..fac494a123 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/objectparametersassert/function_parameters_snowflake_gen.go @@ -0,0 +1,170 @@ +// Code generated by assertions generator; DO NOT EDIT. + +package objectparametersassert + +import ( + "testing" + + acc "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" +) + +type FunctionParametersAssert struct { + *assert.SnowflakeParametersAssert[sdk.SchemaObjectIdentifierWithArguments] +} + +func FunctionParameters(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments) *FunctionParametersAssert { + t.Helper() + return &FunctionParametersAssert{ + assert.NewSnowflakeParametersAssertWithProvider(id, sdk.ObjectTypeFunction, acc.TestClient().Parameter.ShowFunctionParameters), + } +} + +func FunctionParametersPrefetched(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments, parameters []*sdk.Parameter) *FunctionParametersAssert { + t.Helper() + return &FunctionParametersAssert{ + assert.NewSnowflakeParametersAssertWithParameters(id, sdk.ObjectTypeFunction, parameters), + } +} + +////////////////////////////// +// Generic parameter checks // +////////////////////////////// + +func (f *FunctionParametersAssert) HasBoolParameterValue(parameterName sdk.FunctionParameter, expected bool) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterBoolValueSet(parameterName, expected)) + return f +} + +func (f *FunctionParametersAssert) HasIntParameterValue(parameterName sdk.FunctionParameter, expected int) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterIntValueSet(parameterName, expected)) + return f +} + +func (f *FunctionParametersAssert) HasStringParameterValue(parameterName sdk.FunctionParameter, expected string) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterValueSet(parameterName, expected)) + return f +} + +func (f *FunctionParametersAssert) HasDefaultParameterValue(parameterName sdk.FunctionParameter) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterDefaultValueSet(parameterName)) + return f +} + +func (f *FunctionParametersAssert) HasDefaultParameterValueOnLevel(parameterName sdk.FunctionParameter, parameterType sdk.ParameterType) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterDefaultValueOnLevelSet(parameterName, parameterType)) + return f +} + +/////////////////////////////// +// Aggregated generic checks // +/////////////////////////////// + +// HasAllDefaults checks if all the parameters: +// - have a default value by comparing current value of the sdk.Parameter with its default +// - have an expected level +func (f *FunctionParametersAssert) HasAllDefaults() *FunctionParametersAssert { + return f. + HasDefaultParameterValueOnLevel(sdk.FunctionParameterEnableConsoleOutput, sdk.ParameterTypeSnowflakeDefault). + HasDefaultParameterValueOnLevel(sdk.FunctionParameterLogLevel, sdk.ParameterTypeSnowflakeDefault). + HasDefaultParameterValueOnLevel(sdk.FunctionParameterMetricLevel, sdk.ParameterTypeSnowflakeDefault). + HasDefaultParameterValueOnLevel(sdk.FunctionParameterTraceLevel, sdk.ParameterTypeSnowflakeDefault) +} + +func (f *FunctionParametersAssert) HasAllDefaultsExplicit() *FunctionParametersAssert { + return f. + HasDefaultEnableConsoleOutputValueExplicit(). + HasDefaultLogLevelValueExplicit(). + HasDefaultMetricLevelValueExplicit(). + HasDefaultTraceLevelValueExplicit() +} + +//////////////////////////// +// Parameter value checks // +//////////////////////////// + +func (f *FunctionParametersAssert) HasEnableConsoleOutput(expected bool) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterBoolValueSet(sdk.FunctionParameterEnableConsoleOutput, expected)) + return f +} + +func (f *FunctionParametersAssert) HasLogLevel(expected sdk.LogLevel) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterStringUnderlyingValueSet(sdk.FunctionParameterLogLevel, expected)) + return f +} + +func (f *FunctionParametersAssert) HasMetricLevel(expected sdk.MetricLevel) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterStringUnderlyingValueSet(sdk.FunctionParameterMetricLevel, expected)) + return f +} + +func (f *FunctionParametersAssert) HasTraceLevel(expected sdk.TraceLevel) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterStringUnderlyingValueSet(sdk.FunctionParameterTraceLevel, expected)) + return f +} + +//////////////////////////// +// Parameter level checks // +//////////////////////////// + +func (f *FunctionParametersAssert) HasEnableConsoleOutputLevel(expected sdk.ParameterType) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterLevelSet(sdk.FunctionParameterEnableConsoleOutput, expected)) + return f +} + +func (f *FunctionParametersAssert) HasLogLevelLevel(expected sdk.ParameterType) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterLevelSet(sdk.FunctionParameterLogLevel, expected)) + return f +} + +func (f *FunctionParametersAssert) HasMetricLevelLevel(expected sdk.ParameterType) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterLevelSet(sdk.FunctionParameterMetricLevel, expected)) + return f +} + +func (f *FunctionParametersAssert) HasTraceLevelLevel(expected sdk.ParameterType) *FunctionParametersAssert { + f.AddAssertion(assert.SnowflakeParameterLevelSet(sdk.FunctionParameterTraceLevel, expected)) + return f +} + +//////////////////////////////////// +// Parameter default value checks // +//////////////////////////////////// + +func (f *FunctionParametersAssert) HasDefaultEnableConsoleOutputValue() *FunctionParametersAssert { + return f.HasDefaultParameterValue(sdk.FunctionParameterEnableConsoleOutput) +} + +func (f *FunctionParametersAssert) HasDefaultLogLevelValue() *FunctionParametersAssert { + return f.HasDefaultParameterValue(sdk.FunctionParameterLogLevel) +} + +func (f *FunctionParametersAssert) HasDefaultMetricLevelValue() *FunctionParametersAssert { + return f.HasDefaultParameterValue(sdk.FunctionParameterMetricLevel) +} + +func (f *FunctionParametersAssert) HasDefaultTraceLevelValue() *FunctionParametersAssert { + return f.HasDefaultParameterValue(sdk.FunctionParameterTraceLevel) +} + +///////////////////////////////////////////// +// Parameter explicit default value checks // +///////////////////////////////////////////// + +func (f *FunctionParametersAssert) HasDefaultEnableConsoleOutputValueExplicit() *FunctionParametersAssert { + return f.HasEnableConsoleOutput(false) +} + +func (f *FunctionParametersAssert) HasDefaultLogLevelValueExplicit() *FunctionParametersAssert { + return f.HasLogLevel(sdk.LogLevelOff) +} + +func (f *FunctionParametersAssert) HasDefaultMetricLevelValueExplicit() *FunctionParametersAssert { + return f.HasMetricLevel(sdk.MetricLevelNone) +} + +func (f *FunctionParametersAssert) HasDefaultTraceLevelValueExplicit() *FunctionParametersAssert { + return f.HasTraceLevel(sdk.TraceLevelOff) +} diff --git a/pkg/acceptance/helpers/parameter_client.go b/pkg/acceptance/helpers/parameter_client.go index 70321379b1..902201d2ca 100644 --- a/pkg/acceptance/helpers/parameter_client.go +++ b/pkg/acceptance/helpers/parameter_client.go @@ -91,6 +91,17 @@ func (c *ParameterClient) ShowTaskParameters(t *testing.T, id sdk.SchemaObjectId return params } +func (c *ParameterClient) ShowFunctionParameters(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments) []*sdk.Parameter { + t.Helper() + params, err := c.client().ShowParameters(context.Background(), &sdk.ShowParametersOptions{ + In: &sdk.ParametersIn{ + Function: id, + }, + }) + require.NoError(t, err) + return params +} + func (c *ParameterClient) UpdateAccountParameterTemporarily(t *testing.T, parameter sdk.AccountParameter, newValue string) func() { t.Helper() ctx := context.Background() diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index fefdabc25e..151e5d39c2 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -80,6 +80,14 @@ func (v *functions) DescribeDetails(ctx context.Context, id SchemaObjectIdentifi return functionDetailsFromRows(rows) } +func (v *functions) ShowParameters(ctx context.Context, id SchemaObjectIdentifierWithArguments) ([]*Parameter, error) { + return v.client.Parameters.ShowParameters(ctx, &ShowParametersOptions{ + In: &ParametersIn{ + Function: id, + }, + }) +} + func (d *FunctionDetail) setStringValueOrError(property string, field *string) error { if d.Value == nil { return fmt.Errorf("value expected for field %s", property) diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index d457a27b57..218bcf4d14 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -22,6 +22,7 @@ type Functions interface { // DescribeDetails is added manually; it returns aggregated describe results for the given function. DescribeDetails(ctx context.Context, id SchemaObjectIdentifierWithArguments) (*FunctionDetails, error) + ShowParameters(ctx context.Context, id SchemaObjectIdentifierWithArguments) ([]*Parameter, error) } // CreateForJavaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#java-handler. diff --git a/pkg/sdk/parameters.go b/pkg/sdk/parameters.go index 5abfc165be..f4748fb450 100644 --- a/pkg/sdk/parameters.go +++ b/pkg/sdk/parameters.go @@ -1350,19 +1350,20 @@ func (opts *ShowParametersOptions) validate() error { } type ParametersIn struct { - Session *bool `ddl:"keyword" sql:"SESSION"` - Account *bool `ddl:"keyword" sql:"ACCOUNT"` - User AccountObjectIdentifier `ddl:"identifier" sql:"USER"` - Warehouse AccountObjectIdentifier `ddl:"identifier" sql:"WAREHOUSE"` - Database AccountObjectIdentifier `ddl:"identifier" sql:"DATABASE"` - Schema DatabaseObjectIdentifier `ddl:"identifier" sql:"SCHEMA"` - Task SchemaObjectIdentifier `ddl:"identifier" sql:"TASK"` - Table SchemaObjectIdentifier `ddl:"identifier" sql:"TABLE"` + Session *bool `ddl:"keyword" sql:"SESSION"` + Account *bool `ddl:"keyword" sql:"ACCOUNT"` + User AccountObjectIdentifier `ddl:"identifier" sql:"USER"` + Warehouse AccountObjectIdentifier `ddl:"identifier" sql:"WAREHOUSE"` + Database AccountObjectIdentifier `ddl:"identifier" sql:"DATABASE"` + Schema DatabaseObjectIdentifier `ddl:"identifier" sql:"SCHEMA"` + Task SchemaObjectIdentifier `ddl:"identifier" sql:"TASK"` + Table SchemaObjectIdentifier `ddl:"identifier" sql:"TABLE"` + Function SchemaObjectIdentifierWithArguments `ddl:"identifier" sql:"FUNCTION"` } func (v *ParametersIn) validate() error { - if !anyValueSet(v.Session, v.Account, v.User, v.Warehouse, v.Database, v.Schema, v.Task, v.Table) { - return errors.Join(errAtLeastOneOf("Session", "Account", "User", "Warehouse", "Database", "Schema", "Task", "Table")) + if !anyValueSet(v.Session, v.Account, v.User, v.Warehouse, v.Database, v.Schema, v.Task, v.Table, v.Function) { + return errors.Join(errAtLeastOneOf("Session", "Account", "User", "Warehouse", "Database", "Schema", "Task", "Table", "Function")) } return nil } @@ -1488,6 +1489,7 @@ func (v *parameters) ShowUserParameter(ctx context.Context, parameter UserParame return parameters[0], nil } +// TODO [this PR]: test for functions func (v *parameters) ShowObjectParameter(ctx context.Context, parameter ObjectParameter, object Object) (*Parameter, error) { opts := &ShowParametersOptions{ Like: &Like{ @@ -1508,6 +1510,8 @@ func (v *parameters) ShowObjectParameter(ctx context.Context, parameter ObjectPa opts.In.Table = object.Name.(SchemaObjectIdentifier) case ObjectTypeUser: opts.In.User = object.Name.(AccountObjectIdentifier) + case ObjectTypeFunction: + opts.In.Function = object.Name.(SchemaObjectIdentifierWithArguments) default: return nil, fmt.Errorf("unsupported object type %s", object.Name) } From 9b0b55d1d8db99c6f06c972ee4d87e827599f13e Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sat, 7 Dec 2024 20:39:02 +0100 Subject: [PATCH 35/63] Check function parameters in tests --- pkg/sdk/functions_impl_gen.go | 26 +++++++++++++-- pkg/sdk/testint/functions_integration_test.go | 33 ++++++++++++++++++- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/pkg/sdk/functions_impl_gen.go b/pkg/sdk/functions_impl_gen.go index 36ba25d6a9..af4f013e3a 100644 --- a/pkg/sdk/functions_impl_gen.go +++ b/pkg/sdk/functions_impl_gen.go @@ -98,6 +98,10 @@ func (r *CreateForJavaFunctionRequest) toOpts() *CreateForJavaFunctionOptions { ExternalAccessIntegrations: r.ExternalAccessIntegrations, Secrets: r.Secrets, TargetPath: r.TargetPath, + EnableConsoleOutput: r.EnableConsoleOutput, + LogLevel: r.LogLevel, + MetricLevel: r.MetricLevel, + TraceLevel: r.TraceLevel, FunctionDefinition: r.FunctionDefinition, } if r.Arguments != nil { @@ -167,6 +171,10 @@ func (r *CreateForJavascriptFunctionRequest) toOpts() *CreateForJavascriptFuncti NullInputBehavior: r.NullInputBehavior, ReturnResultsBehavior: r.ReturnResultsBehavior, Comment: r.Comment, + EnableConsoleOutput: r.EnableConsoleOutput, + LogLevel: r.LogLevel, + MetricLevel: r.MetricLevel, + TraceLevel: r.TraceLevel, FunctionDefinition: r.FunctionDefinition, } if r.Arguments != nil { @@ -225,6 +233,10 @@ func (r *CreateForPythonFunctionRequest) toOpts() *CreateForPythonFunctionOption Handler: r.Handler, ExternalAccessIntegrations: r.ExternalAccessIntegrations, Secrets: r.Secrets, + EnableConsoleOutput: r.EnableConsoleOutput, + LogLevel: r.LogLevel, + MetricLevel: r.MetricLevel, + TraceLevel: r.TraceLevel, FunctionDefinition: r.FunctionDefinition, } if r.Arguments != nil { @@ -298,9 +310,13 @@ func (r *CreateForScalaFunctionRequest) toOpts() *CreateForScalaFunctionOptions RuntimeVersion: r.RuntimeVersion, Comment: r.Comment, - Handler: r.Handler, - TargetPath: r.TargetPath, - FunctionDefinition: r.FunctionDefinition, + Handler: r.Handler, + TargetPath: r.TargetPath, + EnableConsoleOutput: r.EnableConsoleOutput, + LogLevel: r.LogLevel, + MetricLevel: r.MetricLevel, + TraceLevel: r.TraceLevel, + FunctionDefinition: r.FunctionDefinition, } if r.Arguments != nil { s := make([]FunctionArgument, len(r.Arguments)) @@ -348,6 +364,10 @@ func (r *CreateForSQLFunctionRequest) toOpts() *CreateForSQLFunctionOptions { ReturnResultsBehavior: r.ReturnResultsBehavior, Memoizable: r.Memoizable, Comment: r.Comment, + EnableConsoleOutput: r.EnableConsoleOutput, + LogLevel: r.LogLevel, + MetricLevel: r.MetricLevel, + TraceLevel: r.TraceLevel, FunctionDefinition: r.FunctionDefinition, } if r.Arguments != nil { diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 167d954ee4..d87e0a85f2 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -10,6 +10,7 @@ import ( assertions "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/objectassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/objectparametersassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" @@ -36,6 +37,15 @@ func TestInt_CreateFunctions(t *testing.T) { externalAccessIntegration, externalAccessIntegrationCleanup := testClientHelper().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret.ID()) t.Cleanup(externalAccessIntegrationCleanup) + //assertParametersSet := func(t *testing.T, functionParametersAssert *objectparametersassert.FunctionParametersAssert) { + // assertions.AssertThatObject(t, functionParametersAssert. + // HasEnableConsoleOutput(true). + // HasLogLevel(sdk.LogLevelWarn). + // HasMetricLevel(sdk.MetricLevelAll). + // HasTraceLevel(sdk.TraceLevelAlways), + // ) + //} + t.Run("create function for Java - inline minimal", func(t *testing.T) { className := "TestFunc" funcName := "echoVarchar" @@ -99,7 +109,10 @@ func TestInt_CreateFunctions(t *testing.T) { HasIsAggregateNil(), ) - // TODO [this PR]: check function parameters + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) }) t.Run("create function for Java - inline full", func(t *testing.T) { @@ -132,6 +145,10 @@ func TestInt_CreateFunctions(t *testing.T) { WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}). WithTargetPath(targetPath). + WithEnableConsoleOutput(true). + WithLogLevel(sdk.LogLevelWarn). + WithMetricLevel(sdk.MetricLevelAll). + WithTraceLevel(sdk.TraceLevelAlways). WithFunctionDefinitionWrapped(definition) err := client.Functions.CreateForJava(ctx, request) @@ -182,6 +199,20 @@ func TestInt_CreateFunctions(t *testing.T) { HasInstalledPackagesNil(). HasIsAggregateNil(), ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + + // TODO [this PR]: will check after alter + // TODO [this PR]: add a test documenting that we can't set parameters in create (and revert adding these parametrs directly in object...) + //assertParametersSet(t, objectparametersassert.FunctionParameters(t, id)) + // + //// check that ShowParameters works too + //parameters, err := client.Functions.ShowParameters(ctx, id) + //require.NoError(t, err) + //assertParametersSet(t, objectparametersassert.FunctionParametersPrefetched(t, id, parameters)) }) t.Run("create function for Java - staged minimal", func(t *testing.T) {}) From 93f7e7786c3d543b632b084b30a7b851e9061fc3 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sat, 7 Dec 2024 22:55:52 +0100 Subject: [PATCH 36/63] Add minimal test for staged java func --- pkg/acceptance/helpers/stage_client.go | 15 ++++ pkg/sdk/testint/functions_integration_test.go | 89 ++++++++++++++++++- 2 files changed, 102 insertions(+), 2 deletions(-) diff --git a/pkg/acceptance/helpers/stage_client.go b/pkg/acceptance/helpers/stage_client.go index 41eb0aea36..6f918bfca1 100644 --- a/pkg/acceptance/helpers/stage_client.go +++ b/pkg/acceptance/helpers/stage_client.go @@ -96,6 +96,21 @@ func (c *StageClient) PutOnStage(t *testing.T, id sdk.SchemaObjectIdentifier, fi require.NoError(t, err) } +func (c *StageClient) RemoveFromUserStage(t *testing.T, pathOnStage string) { + t.Helper() + ctx := context.Background() + + _, err := c.context.client.ExecForTests(ctx, fmt.Sprintf(`REMOVE @~/%s`, pathOnStage)) + require.NoError(t, err) +} + +func (c *StageClient) RemoveFromUserStageFunc(t *testing.T, pathOnStage string) func() { + t.Helper() + return func() { + c.RemoveFromUserStage(t, pathOnStage) + } +} + func (c *StageClient) PutOnStageWithContent(t *testing.T, id sdk.SchemaObjectIdentifier, filename string, content string) { t.Helper() ctx := context.Background() diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index d87e0a85f2..269b83d2f1 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -11,6 +11,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/objectassert" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert/objectparametersassert" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/internal/collections" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" @@ -126,7 +127,8 @@ func TestInt_CreateFunctions(t *testing.T) { returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) handler := fmt.Sprintf("%s.%s", className, funcName) definition := testClientHelper().Function.SampleJavaDefinition(t, className, funcName, argName) - targetPath := fmt.Sprintf("@~/tf-%d.jar", time.Now().Unix()) + jarName := fmt.Sprintf("tf-%d-%s.jar", time.Now().Unix(), random.AlphaN(5)) + targetPath := fmt.Sprintf("@~/%s", jarName) request := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). WithOrReplace(true). @@ -215,7 +217,90 @@ func TestInt_CreateFunctions(t *testing.T) { //assertParametersSet(t, objectparametersassert.FunctionParametersPrefetched(t, id, parameters)) }) - t.Run("create function for Java - staged minimal", func(t *testing.T) {}) + // TODO [next PR]: create jar for java func before all tests and reuse where needed; also, move it to helper + t.Run("create function for Java - staged minimal", func(t *testing.T) { + className := "TestFunc" + funcName := "echoVarchar" + argName := "x" + + id1 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) + argument := sdk.NewFunctionArgumentRequest(argName, testdatatypes.DataTypeVarchar_100) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(testdatatypes.DataTypeVarchar_100) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + handler := fmt.Sprintf("%s.%s", className, funcName) + definition := testClientHelper().Function.SampleJavaDefinition(t, className, funcName, argName) + jarName := fmt.Sprintf("tf-%d-%s.jar", time.Now().Unix(), random.AlphaN(5)) + targetPath := fmt.Sprintf("@~/%s", jarName) + + request := sdk.NewCreateForJavaFunctionRequest(id1.SchemaObjectId(), *returns, handler). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithTargetPath(targetPath). + WithFunctionDefinitionWrapped(definition) + + err := client.Functions.CreateForJava(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id1)) + + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) + + requestStaged := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(targetPath)}) + + err = client.Functions.CreateForJava(ctx, requestStaged) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + t.Cleanup(testClientHelper().Stage.RemoveFromUserStageFunc(t, jarName)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.DataTypeVARCHAR}). + HasArgumentsRaw(fmt.Sprintf(`%s(VARCHAR) RETURN VARCHAR`, function.ID().Name())). + HasDescription(sdk.DefaultFunctionComment). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("JAVA"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, testdatatypes.DataTypeVarchar_100.ToLegacyDataTypeSql())). + HasReturns(testdatatypes.DataTypeVarchar_100.ToSql()). + HasLanguage("JAVA"). + HasBodyNil(). + HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImports(fmt.Sprintf(`[%s]`, targetPath)). + HasHandler(handler). + HasRuntimeVersionNil(). + HasPackages(`[]`). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + t.Run("create function for Java - staged full", func(t *testing.T) {}) t.Run("create function for JavaScript - inline minimal", func(t *testing.T) {}) From 414be6cc2fb74a14a657e254420aca46bcbc15e8 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 00:13:53 +0100 Subject: [PATCH 37/63] Extract function stored in jar setup --- pkg/acceptance/helpers/function_client.go | 7 +- .../helpers/function_setup_helpers.go | 70 +++++++++++++++++++ .../helpers/random/random_helpers.go | 4 ++ pkg/sdk/testint/functions_integration_test.go | 42 ++++------- 4 files changed, 92 insertions(+), 31 deletions(-) create mode 100644 pkg/acceptance/helpers/function_setup_helpers.go diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 04dfdeabcf..22bd36e5bb 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -42,6 +42,7 @@ func (c *FunctionClient) CreateWithIdentifier(t *testing.T, id sdk.SchemaObjectI ) } +// TODO [next PR]: improve this helper (all other types creation) func (c *FunctionClient) CreateSecure(t *testing.T, arguments ...sdk.DataType) *sdk.Function { t.Helper() id := c.ids.RandomSchemaObjectIdentifierWithArguments(arguments...) @@ -101,9 +102,9 @@ func (c *FunctionClient) SampleJavaDefinition(t *testing.T, className string, fu t.Helper() return fmt.Sprintf(` - class %s { - public static String %s(String %s) { - return x; + class %[1]s { + public static String %[2]s(String %[3]s) { + return %[3]s; } } `, className, funcName, argName) diff --git a/pkg/acceptance/helpers/function_setup_helpers.go b/pkg/acceptance/helpers/function_setup_helpers.go new file mode 100644 index 0000000000..6c936fb37a --- /dev/null +++ b/pkg/acceptance/helpers/function_setup_helpers.go @@ -0,0 +1,70 @@ +package helpers + +import ( + "context" + "fmt" + "testing" + "time" + + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/helpers/random" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" + "github.com/stretchr/testify/require" +) + +// TODO [SNOW-1827324]: add TestClient ref to each specific client, so that we enhance specific client and not the base one +func (c *TestClient) CreateSampleJavaFunctionAndJar(t *testing.T) *TmpFunction { + t.Helper() + ctx := context.Background() + + className := fmt.Sprintf("TestClassAbc%s", random.AlphaLowerN(3)) + funcName := fmt.Sprintf("echoVarchar%s", random.AlphaLowerN(3)) + argName := fmt.Sprintf("arg%s", random.AlphaLowerN(3)) + dataType := testdatatypes.DataTypeVarchar_100 + + id := c.Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + argument := sdk.NewFunctionArgumentRequest(argName, testdatatypes.DataTypeVarchar_100) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(testdatatypes.DataTypeVarchar_100) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + handler := fmt.Sprintf("%s.%s", className, funcName) + definition := c.Function.SampleJavaDefinition(t, className, funcName, argName) + jarName := fmt.Sprintf("tf-%d-%s.jar", time.Now().Unix(), random.AlphaN(5)) + targetPath := fmt.Sprintf("@~/%s", jarName) + + request := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithTargetPath(targetPath). + WithFunctionDefinitionWrapped(definition) + + err := c.context.client.Functions.CreateForJava(ctx, request) + require.NoError(t, err) + t.Cleanup(c.Function.DropFunctionFunc(t, id)) + t.Cleanup(c.Stage.RemoveFromUserStageFunc(t, jarName)) + + return &TmpFunction{ + FunctionId: id, + ClassName: className, + FuncName: funcName, + ArgName: argName, + ArgType: dataType, + JarName: jarName, + } +} + +type TmpFunction struct { + FunctionId sdk.SchemaObjectIdentifierWithArguments + ClassName string + FuncName string + ArgName string + ArgType datatypes.DataType + JarName string +} + +func (f *TmpFunction) JarLocation() string { + return fmt.Sprintf("@~/%s", f.JarName) +} + +func (f *TmpFunction) Handler() string { + return fmt.Sprintf("%s.%s", f.ClassName, f.FuncName) +} diff --git a/pkg/acceptance/helpers/random/random_helpers.go b/pkg/acceptance/helpers/random/random_helpers.go index fcfb5b7208..5a9e270b13 100644 --- a/pkg/acceptance/helpers/random/random_helpers.go +++ b/pkg/acceptance/helpers/random/random_helpers.go @@ -45,6 +45,10 @@ func AlphaN(num int) string { return gofakeit.Password(true, true, false, false, false, num) } +func AlphaLowerN(num int) string { + return gofakeit.Password(true, false, false, false, false, num) +} + func Email() string { return gofakeit.Email() } diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 269b83d2f1..d807274a2a 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -38,6 +38,8 @@ func TestInt_CreateFunctions(t *testing.T) { externalAccessIntegration, externalAccessIntegrationCleanup := testClientHelper().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret.ID()) t.Cleanup(externalAccessIntegrationCleanup) + tmpFunction := testClientHelper().CreateSampleJavaFunctionAndJar(t) + //assertParametersSet := func(t *testing.T, functionParametersAssert *objectparametersassert.FunctionParametersAssert) { // assertions.AssertThatObject(t, functionParametersAssert. // HasEnableConsoleOutput(true). @@ -217,40 +219,24 @@ func TestInt_CreateFunctions(t *testing.T) { //assertParametersSet(t, objectparametersassert.FunctionParametersPrefetched(t, id, parameters)) }) - // TODO [next PR]: create jar for java func before all tests and reuse where needed; also, move it to helper t.Run("create function for Java - staged minimal", func(t *testing.T) { - className := "TestFunc" - funcName := "echoVarchar" - argName := "x" + dataType := tmpFunction.ArgType + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) - id1 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) - argument := sdk.NewFunctionArgumentRequest(argName, testdatatypes.DataTypeVarchar_100) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(testdatatypes.DataTypeVarchar_100) + argName := "x" + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - handler := fmt.Sprintf("%s.%s", className, funcName) - definition := testClientHelper().Function.SampleJavaDefinition(t, className, funcName, argName) - jarName := fmt.Sprintf("tf-%d-%s.jar", time.Now().Unix(), random.AlphaN(5)) - targetPath := fmt.Sprintf("@~/%s", jarName) - - request := sdk.NewCreateForJavaFunctionRequest(id1.SchemaObjectId(), *returns, handler). - WithArguments([]sdk.FunctionArgumentRequest{*argument}). - WithTargetPath(targetPath). - WithFunctionDefinitionWrapped(definition) - - err := client.Functions.CreateForJava(ctx, request) - require.NoError(t, err) - t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id1)) - - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) + handler := tmpFunction.Handler() + targetPath := tmpFunction.JarLocation() requestStaged := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). WithArguments([]sdk.FunctionArgumentRequest{*argument}). WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(targetPath)}) - err = client.Functions.CreateForJava(ctx, requestStaged) + err := client.Functions.CreateForJava(ctx, requestStaged) require.NoError(t, err) t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) - t.Cleanup(testClientHelper().Stage.RemoveFromUserStageFunc(t, jarName)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) @@ -264,8 +250,8 @@ func TestInt_CreateFunctions(t *testing.T) { HasIsAnsi(false). HasMinNumArguments(1). HasMaxNumArguments(1). - HasArgumentsOld([]sdk.DataType{sdk.DataTypeVARCHAR}). - HasArgumentsRaw(fmt.Sprintf(`%s(VARCHAR) RETURN VARCHAR`, function.ID().Name())). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). HasDescription(sdk.DefaultFunctionComment). HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). HasIsTableFunction(false). @@ -278,8 +264,8 @@ func TestInt_CreateFunctions(t *testing.T) { ) assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). - HasSignature(fmt.Sprintf(`(%s %s)`, argName, testdatatypes.DataTypeVarchar_100.ToLegacyDataTypeSql())). - HasReturns(testdatatypes.DataTypeVarchar_100.ToSql()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(dataType.ToSql()). HasLanguage("JAVA"). HasBodyNil(). HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). From 77145bad28a4323c005f847dc558d5a8756be4a9 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 00:16:44 +0100 Subject: [PATCH 38/63] Check import in inline function too --- pkg/sdk/testint/functions_integration_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index d807274a2a..2c89071b7c 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -140,8 +140,7 @@ func TestInt_CreateFunctions(t *testing.T) { WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). WithRuntimeVersion("11"). WithComment("comment"). - // TODO [this PR]: test imports - // WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport("lang.*")}). + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpFunction.JarLocation())}). WithPackages([]sdk.FunctionPackageRequest{ *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:1.14.0"), *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:telemetry:0.1.0"), @@ -195,7 +194,7 @@ func TestInt_CreateFunctions(t *testing.T) { // TODO [this PR]: parse to identifier list // TODO [this PR]: check multiple secrets (to know how to parse) HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). - HasImports(`[]`). + HasImports(fmt.Sprintf(`[%s]`, tmpFunction.JarLocation())). HasHandler(handler). HasRuntimeVersion("11"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). From e267caaa560ad4bbadd2566e08a84678f25f0859 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 09:38:59 +0100 Subject: [PATCH 39/63] Test java full staged --- pkg/sdk/functions_gen_test.go | 1 - pkg/sdk/functions_validations_gen.go | 3 - pkg/sdk/testint/functions_integration_test.go | 85 ++++++++++++++++++- 3 files changed, 81 insertions(+), 8 deletions(-) diff --git a/pkg/sdk/functions_gen_test.go b/pkg/sdk/functions_gen_test.go index 07b0eae432..c4087ac0c6 100644 --- a/pkg/sdk/functions_gen_test.go +++ b/pkg/sdk/functions_gen_test.go @@ -144,7 +144,6 @@ func TestFunctions_CreateForJava(t *testing.T) { }, } assertOptsInvalidJoinedErrors(t, opts, NewError("TARGET_PATH must be nil when AS is nil")) - assertOptsInvalidJoinedErrors(t, opts, NewError("PACKAGES must be empty when AS is nil")) assertOptsInvalidJoinedErrors(t, opts, NewError("IMPORTS must not be empty when AS is nil")) }) diff --git a/pkg/sdk/functions_validations_gen.go b/pkg/sdk/functions_validations_gen.go index 5ffb52ce1d..aee4d814bf 100644 --- a/pkg/sdk/functions_validations_gen.go +++ b/pkg/sdk/functions_validations_gen.go @@ -59,9 +59,6 @@ func (opts *CreateForJavaFunctionOptions) validate() error { if opts.TargetPath != nil { errs = append(errs, NewError("TARGET_PATH must be nil when AS is nil")) } - if len(opts.Packages) > 0 { - errs = append(errs, NewError("PACKAGES must be empty when AS is nil")) - } if len(opts.Imports) == 0 { errs = append(errs, NewError("IMPORTS must not be empty when AS is nil")) } diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 2c89071b7c..ef3d9abfff 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -227,11 +227,11 @@ func TestInt_CreateFunctions(t *testing.T) { dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) handler := tmpFunction.Handler() - targetPath := tmpFunction.JarLocation() + importPath := tmpFunction.JarLocation() requestStaged := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). WithArguments([]sdk.FunctionArgumentRequest{*argument}). - WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(targetPath)}) + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(importPath)}) err := client.Functions.CreateForJava(ctx, requestStaged) require.NoError(t, err) @@ -271,7 +271,7 @@ func TestInt_CreateFunctions(t *testing.T) { HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). HasExternalAccessIntegrationsNil(). HasSecretsNil(). - HasImports(fmt.Sprintf(`[%s]`, targetPath)). + HasImports(fmt.Sprintf(`[%s]`, importPath)). HasHandler(handler). HasRuntimeVersionNil(). HasPackages(`[]`). @@ -286,7 +286,84 @@ func TestInt_CreateFunctions(t *testing.T) { ) }) - t.Run("create function for Java - staged full", func(t *testing.T) {}) + t.Run("create function for Java - staged full", func(t *testing.T) { + dataType := tmpFunction.ArgType + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + argName := "x" + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + handler := tmpFunction.Handler() + + requestStaged := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). + WithOrReplace(true). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithCopyGrants(true). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithRuntimeVersion("11"). + WithComment("comment"). + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpFunction.JarLocation())}). + WithPackages([]sdk.FunctionPackageRequest{ + *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:1.14.0"), + *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:telemetry:0.1.0"), + }). + WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). + WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}) + + err := client.Functions.CreateForJava(ctx, requestStaged) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription("comment"). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("JAVA"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(dataType.ToSql()). + HasLanguage("JAVA"). + HasBodyNil(). + HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). + HasImports(fmt.Sprintf(`[%s]`, tmpFunction.JarLocation())). + HasHandler(handler). + HasRuntimeVersion("11"). + HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) t.Run("create function for JavaScript - inline minimal", func(t *testing.T) {}) t.Run("create function for JavaScript - inline full", func(t *testing.T) {}) From b811ab8583bec0cd2d6bbabe07a2c7f68d2f36ba Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 10:04:56 +0100 Subject: [PATCH 40/63] Pass javascript function creation tests --- pkg/acceptance/helpers/function_client.go | 10 +- pkg/sdk/testint/functions_integration_test.go | 318 ++++++++++++------ 2 files changed, 222 insertions(+), 106 deletions(-) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 22bd36e5bb..25fd407790 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -110,20 +110,20 @@ func (c *FunctionClient) SampleJavaDefinition(t *testing.T, className string, fu `, className, funcName, argName) } -func (c *FunctionClient) SampleJavaScriptDefinition(t *testing.T) string { +func (c *FunctionClient) SampleJavascriptDefinition(t *testing.T, argName string) string { t.Helper() - return ` - if (D <= 0) { + return fmt.Sprintf(` + if (%[1]s <= 0) { return 1; } else { var result = 1; - for (var i = 2; i <= D; i++) { + for (var i = 2; i <= %[1]s; i++) { result = result * i; } return result; } -` +`, argName) } func (c *FunctionClient) SamplePythonDefinition(t *testing.T) string { diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index ef3d9abfff..16629bedb8 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -122,10 +122,11 @@ func TestInt_CreateFunctions(t *testing.T) { className := "TestFunc" funcName := "echoVarchar" argName := "x" + dataType := testdatatypes.DataTypeVarchar_100 - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) - argument := sdk.NewFunctionArgumentRequest(argName, testdatatypes.DataTypeVarchar_100) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(testdatatypes.DataTypeVarchar_100) + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) handler := fmt.Sprintf("%s.%s", className, funcName) definition := testClientHelper().Function.SampleJavaDefinition(t, className, funcName, argName) @@ -138,6 +139,7 @@ func TestInt_CreateFunctions(t *testing.T) { WithCopyGrants(true). WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). WithRuntimeVersion("11"). WithComment("comment"). WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpFunction.JarLocation())}). @@ -170,8 +172,8 @@ func TestInt_CreateFunctions(t *testing.T) { HasIsAnsi(false). HasMinNumArguments(1). HasMaxNumArguments(1). - HasArgumentsOld([]sdk.DataType{sdk.DataTypeVARCHAR}). - HasArgumentsRaw(fmt.Sprintf(`%s(VARCHAR) RETURN VARCHAR`, function.ID().Name())). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). HasDescription("comment"). HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). HasIsTableFunction(false). @@ -184,8 +186,8 @@ func TestInt_CreateFunctions(t *testing.T) { ) assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). - HasSignature(fmt.Sprintf(`(%s %s)`, argName, testdatatypes.DataTypeVarchar_100.ToLegacyDataTypeSql())). - HasReturns(testdatatypes.DataTypeVarchar_100.ToSql()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(fmt.Sprintf(`%s NOT NULL`, dataType.ToSql())). HasLanguage("JAVA"). HasBody(definition). HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). @@ -302,6 +304,7 @@ func TestInt_CreateFunctions(t *testing.T) { WithCopyGrants(true). WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). WithRuntimeVersion("11"). WithComment("comment"). WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpFunction.JarLocation())}). @@ -343,7 +346,7 @@ func TestInt_CreateFunctions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). - HasReturns(dataType.ToSql()). + HasReturns(fmt.Sprintf(`%s NOT NULL`, dataType.ToSql())). HasLanguage("JAVA"). HasBodyNil(). HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). @@ -365,126 +368,239 @@ func TestInt_CreateFunctions(t *testing.T) { ) }) - t.Run("create function for JavaScript - inline minimal", func(t *testing.T) {}) - t.Run("create function for JavaScript - inline full", func(t *testing.T) {}) - - t.Run("create function for Python - inline minimal", func(t *testing.T) {}) - t.Run("create function for Python - inline full", func(t *testing.T) {}) - t.Run("create function for Python - staged minimal", func(t *testing.T) {}) - t.Run("create function for Python - staged full", func(t *testing.T) {}) - - t.Run("create function for Scala - inline minimal", func(t *testing.T) {}) - t.Run("create function for Scala - inline full", func(t *testing.T) {}) - t.Run("create function for Scala - staged minimal", func(t *testing.T) {}) - t.Run("create function for Scala - staged full", func(t *testing.T) {}) - - t.Run("create function for SQL - inline minimal", func(t *testing.T) {}) - t.Run("create function for SQL - inline full", func(t *testing.T) {}) - - t.Run("create function for Javascript", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) + t.Run("create function for Javascript - inline minimal", func(t *testing.T) { + dataType := testdatatypes.DataTypeFloat + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) - definition := testClientHelper().Function.SampleJavaScriptDefinition(t) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) + argName := "d" + definition := testClientHelper().Function.SampleJavascriptDefinition(t, argName) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - argument := sdk.NewFunctionArgumentRequest("d", nil).WithArgDataTypeOld(sdk.DataTypeFloat) - request := sdk.NewCreateForJavascriptFunctionRequest(id.SchemaObjectId(), *returns, definition). - WithOrReplace(true). - WithArguments([]sdk.FunctionArgumentRequest{*argument}). - WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorCalledOnNullInput)) - err := client.Functions.CreateForJavascript(ctx, request) - require.NoError(t, err) - t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) - - function, err := client.Functions.ShowByID(ctx, id) - require.NoError(t, err) - require.Equal(t, id.Name(), function.Name) - require.Equal(t, "JAVASCRIPT", function.Language) - }) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) - t.Run("create function for Python", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeNumber) + request := sdk.NewCreateForJavascriptFunctionRequestDefinitionWrapped(id.SchemaObjectId(), *returns, definition). + WithArguments([]sdk.FunctionArgumentRequest{*argument}) - definition := testClientHelper().Function.SamplePythonDefinition(t) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeVariant) - returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - argument := sdk.NewFunctionArgumentRequest("i", nil).WithArgDataTypeOld(sdk.DataTypeNumber) - request := sdk.NewCreateForPythonFunctionRequest(id.SchemaObjectId(), *returns, "3.8", "dump"). - WithOrReplace(true). - WithArguments([]sdk.FunctionArgumentRequest{*argument}). - WithFunctionDefinition(definition) - err := client.Functions.CreateForPython(ctx, request) + err := client.Functions.CreateForJavascript(ctx, request) require.NoError(t, err) t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) - require.Equal(t, id.Name(), function.Name) - require.Equal(t, "PYTHON", function.Language) - }) - t.Run("create function for Scala", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription(sdk.DefaultFunctionComment). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("JAVASCRIPT"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) - target := fmt.Sprintf("@~/tf-%d.jar", time.Now().Unix()) - definition := testClientHelper().Function.SampleScalaDefinition(t) - argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeVARCHAR) - request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), nil, "Echo.echoVarchar"). - WithResultDataTypeOld(sdk.DataTypeVARCHAR). - WithOrReplace(true). - WithArguments([]sdk.FunctionArgumentRequest{*argument}). - WithTargetPath(target). - WithRuntimeVersion("2.12"). - WithFunctionDefinition(definition) - err := client.Functions.CreateForScala(ctx, request) - require.NoError(t, err) - t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(dataType.ToSql()). + HasLanguage("JAVASCRIPT"). + HasBody(definition). + HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImportsNil(). + HasHandlerNil(). + HasRuntimeVersionNil(). + HasPackagesNil(). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) - function, err := client.Functions.ShowByID(ctx, id) - require.NoError(t, err) - require.Equal(t, id.Name(), function.Name) - require.Equal(t, "SCALA", function.Language) + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) }) - t.Run("create function for SQL", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) + t.Run("create function for Javascript - inline full", func(t *testing.T) { + dataType := testdatatypes.DataTypeFloat + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) - definition := testClientHelper().Function.SampleSqlDefinition(t) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) + argName := "d" + definition := testClientHelper().Function.SampleJavascriptDefinition(t, argName) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeFloat) - request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition). - WithArguments([]sdk.FunctionArgumentRequest{*argument}). + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + request := sdk.NewCreateForJavascriptFunctionRequestDefinitionWrapped(id.SchemaObjectId(), *returns, definition). WithOrReplace(true). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithCopyGrants(true). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). WithComment("comment") - err := client.Functions.CreateForSQL(ctx, request) + + err := client.Functions.CreateForJavascript(ctx, request) require.NoError(t, err) t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) - require.Equal(t, id.Name(), function.Name) - require.Equal(t, "SQL", function.Language) - }) - t.Run("create function for SQL with no arguments", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments() + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription("comment"). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("JAVASCRIPT"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) - definition := testClientHelper().Function.SampleSqlDefinition(t) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) - returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition). - WithOrReplace(true). - WithComment("comment") - err := client.Functions.CreateForSQL(ctx, request) - require.NoError(t, err) - t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(fmt.Sprintf(`%s NOT NULL`, dataType.ToSql())). + HasLanguage("JAVASCRIPT"). + HasBody(definition). + HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImportsNil(). + HasHandlerNil(). + HasRuntimeVersionNil(). + HasPackagesNil(). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) - function, err := client.Functions.ShowByID(ctx, id) - require.NoError(t, err) - require.Equal(t, id.Name(), function.Name) - require.Equal(t, "SQL", function.Language) + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) }) + + //t.Run("create function for Python - inline minimal", func(t *testing.T) {}) + //t.Run("create function for Python - inline full", func(t *testing.T) {}) + //t.Run("create function for Python - staged minimal", func(t *testing.T) {}) + //t.Run("create function for Python - staged full", func(t *testing.T) {}) + // + //t.Run("create function for Scala - inline minimal", func(t *testing.T) {}) + //t.Run("create function for Scala - inline full", func(t *testing.T) {}) + //t.Run("create function for Scala - staged minimal", func(t *testing.T) {}) + //t.Run("create function for Scala - staged full", func(t *testing.T) {}) + // + //t.Run("create function for SQL - inline minimal", func(t *testing.T) {}) + //t.Run("create function for SQL - inline full", func(t *testing.T) {}) + + //t.Run("create function for Python", func(t *testing.T) { + // id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeNumber) + // + // definition := testClientHelper().Function.SamplePythonDefinition(t) + // dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeVariant) + // returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + // argument := sdk.NewFunctionArgumentRequest("i", nil).WithArgDataTypeOld(sdk.DataTypeNumber) + // request := sdk.NewCreateForPythonFunctionRequest(id.SchemaObjectId(), *returns, "3.8", "dump"). + // WithOrReplace(true). + // WithArguments([]sdk.FunctionArgumentRequest{*argument}). + // WithFunctionDefinition(definition) + // err := client.Functions.CreateForPython(ctx, request) + // require.NoError(t, err) + // t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + // + // function, err := client.Functions.ShowByID(ctx, id) + // require.NoError(t, err) + // require.Equal(t, id.Name(), function.Name) + // require.Equal(t, "PYTHON", function.Language) + //}) + // + //t.Run("create function for Scala", func(t *testing.T) { + // id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) + // + // target := fmt.Sprintf("@~/tf-%d.jar", time.Now().Unix()) + // definition := testClientHelper().Function.SampleScalaDefinition(t) + // argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeVARCHAR) + // request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), nil, "Echo.echoVarchar"). + // WithResultDataTypeOld(sdk.DataTypeVARCHAR). + // WithOrReplace(true). + // WithArguments([]sdk.FunctionArgumentRequest{*argument}). + // WithTargetPath(target). + // WithRuntimeVersion("2.12"). + // WithFunctionDefinition(definition) + // err := client.Functions.CreateForScala(ctx, request) + // require.NoError(t, err) + // t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + // + // function, err := client.Functions.ShowByID(ctx, id) + // require.NoError(t, err) + // require.Equal(t, id.Name(), function.Name) + // require.Equal(t, "SCALA", function.Language) + //}) + // + //t.Run("create function for SQL", func(t *testing.T) { + // id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) + // + // definition := testClientHelper().Function.SampleSqlDefinition(t) + // dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) + // returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + // argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeFloat) + // request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition). + // WithArguments([]sdk.FunctionArgumentRequest{*argument}). + // WithOrReplace(true). + // WithComment("comment") + // err := client.Functions.CreateForSQL(ctx, request) + // require.NoError(t, err) + // t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + // + // function, err := client.Functions.ShowByID(ctx, id) + // require.NoError(t, err) + // require.Equal(t, id.Name(), function.Name) + // require.Equal(t, "SQL", function.Language) + //}) + + //t.Run("create function for SQL with no arguments", func(t *testing.T) { + // id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments() + // + // definition := testClientHelper().Function.SampleSqlDefinition(t) + // dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) + // returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + // request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition). + // WithOrReplace(true). + // WithComment("comment") + // err := client.Functions.CreateForSQL(ctx, request) + // require.NoError(t, err) + // t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + // + // function, err := client.Functions.ShowByID(ctx, id) + // require.NoError(t, err) + // require.Equal(t, id.Name(), function.Name) + // require.Equal(t, "SQL", function.Language) + //}) } func TestInt_OtherFunctions(t *testing.T) { From fbbdd6eae6c82eeca926163abded3c44e5a81bd3 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 10:10:53 +0100 Subject: [PATCH 41/63] Provide a quick way to speed up local testing --- .../testenvs/testing_environment_variables.go | 2 + pkg/sdk/testint/setup_test.go | 94 ++++++++++--------- 2 files changed, 51 insertions(+), 45 deletions(-) diff --git a/pkg/acceptance/testenvs/testing_environment_variables.go b/pkg/acceptance/testenvs/testing_environment_variables.go index 07a69e6a34..22ffdeb072 100644 --- a/pkg/acceptance/testenvs/testing_environment_variables.go +++ b/pkg/acceptance/testenvs/testing_environment_variables.go @@ -35,6 +35,8 @@ const ( ConfigureClientOnce env = "SF_TF_ACC_TEST_CONFIGURE_CLIENT_ONCE" TestObjectsSuffix env = "TEST_SF_TF_TEST_OBJECT_SUFFIX" RequireTestObjectsSuffix env = "TEST_SF_TF_REQUIRE_TEST_OBJECT_SUFFIX" + + SimplifiedIntegrationTestsSetup env = "TEST_SF_TF_SIMPLIFIED_INTEGRATION_TESTS_SETUP" ) func GetOrSkipTest(t *testing.T, envName Env) string { diff --git a/pkg/sdk/testint/setup_test.go b/pkg/sdk/testint/setup_test.go index ee949d3d67..2980b992b8 100644 --- a/pkg/sdk/testint/setup_test.go +++ b/pkg/sdk/testint/setup_test.go @@ -160,65 +160,69 @@ func (itc *integrationTestContext) initialize() error { } itc.warehouse = wh - config, err := sdk.ProfileConfig(testprofiles.Secondary) - if err != nil { - return err - } + itc.testClient = helpers.NewTestClient(c, TestDatabaseName, TestSchemaName, TestWarehouseName, random.IntegrationTestsSuffix) - if config.Account == defaultConfig.Account { - log.Println("[WARN] default and secondary configs are set to the same account; it may cause problems in tests requiring multiple accounts") - } + // TODO [next PR]: improve setup; this is a quick workaround for faster local testing + if os.Getenv(string(testenvs.SimplifiedIntegrationTestsSetup)) == "" { + config, err := sdk.ProfileConfig(testprofiles.Secondary) + if err != nil { + return err + } - secondaryClient, err := sdk.NewClient(config) - if err != nil { - return err - } - itc.secondaryClient = secondaryClient - itc.secondaryCtx = context.Background() + if config.Account == defaultConfig.Account { + log.Println("[WARN] default and secondary configs are set to the same account; it may cause problems in tests requiring multiple accounts") + } - secondaryDb, secondaryDbCleanup, err := createDb(itc.secondaryClient, itc.secondaryCtx, true) - itc.secondaryDatabaseCleanup = secondaryDbCleanup - if err != nil { - return err - } - itc.secondaryDatabase = secondaryDb + secondaryClient, err := sdk.NewClient(config) + if err != nil { + return err + } + itc.secondaryClient = secondaryClient + itc.secondaryCtx = context.Background() - secondarySchema, secondarySchemaCleanup, err := createSc(itc.secondaryClient, itc.secondaryCtx, itc.database, true) - itc.secondarySchemaCleanup = secondarySchemaCleanup - if err != nil { - return err - } - itc.secondarySchema = secondarySchema + secondaryDb, secondaryDbCleanup, err := createDb(itc.secondaryClient, itc.secondaryCtx, true) + itc.secondaryDatabaseCleanup = secondaryDbCleanup + if err != nil { + return err + } + itc.secondaryDatabase = secondaryDb - secondaryWarehouse, secondaryWarehouseCleanup, err := createWh(itc.secondaryClient, itc.secondaryCtx, true) - itc.secondaryWarehouseCleanup = secondaryWarehouseCleanup - if err != nil { - return err - } - itc.secondaryWarehouse = secondaryWarehouse + secondarySchema, secondarySchemaCleanup, err := createSc(itc.secondaryClient, itc.secondaryCtx, itc.database, true) + itc.secondarySchemaCleanup = secondarySchemaCleanup + if err != nil { + return err + } + itc.secondarySchema = secondarySchema - itc.testClient = helpers.NewTestClient(c, TestDatabaseName, TestSchemaName, TestWarehouseName, random.IntegrationTestsSuffix) - itc.secondaryTestClient = helpers.NewTestClient(secondaryClient, TestDatabaseName, TestSchemaName, TestWarehouseName, random.IntegrationTestsSuffix) + secondaryWarehouse, secondaryWarehouseCleanup, err := createWh(itc.secondaryClient, itc.secondaryCtx, true) + itc.secondaryWarehouseCleanup = secondaryWarehouseCleanup + if err != nil { + return err + } + itc.secondaryWarehouse = secondaryWarehouse - err = helpers.EnsureQuotedIdentifiersIgnoreCaseIsSetToFalse(itc.client, itc.ctx) - if err != nil { - return err - } - err = helpers.EnsureQuotedIdentifiersIgnoreCaseIsSetToFalse(itc.secondaryClient, itc.secondaryCtx) - if err != nil { - return err - } + itc.secondaryTestClient = helpers.NewTestClient(secondaryClient, TestDatabaseName, TestSchemaName, TestWarehouseName, random.IntegrationTestsSuffix) - // TODO(SNOW-1842271): Adjust test setup to work properly with Accountadmin role for object tests and Orgadmin for account tests - if os.Getenv(string(testenvs.TestAccountCreate)) == "" { - err = helpers.EnsureScimProvisionerRolesExist(itc.client, itc.ctx) + err = helpers.EnsureQuotedIdentifiersIgnoreCaseIsSetToFalse(itc.client, itc.ctx) if err != nil { return err } - err = helpers.EnsureScimProvisionerRolesExist(itc.secondaryClient, itc.secondaryCtx) + err = helpers.EnsureQuotedIdentifiersIgnoreCaseIsSetToFalse(itc.secondaryClient, itc.secondaryCtx) if err != nil { return err } + + // TODO(SNOW-1842271): Adjust test setup to work properly with Accountadmin role for object tests and Orgadmin for account tests + if os.Getenv(string(testenvs.TestAccountCreate)) == "" { + err = helpers.EnsureScimProvisionerRolesExist(itc.client, itc.ctx) + if err != nil { + return err + } + err = helpers.EnsureScimProvisionerRolesExist(itc.secondaryClient, itc.secondaryCtx) + if err != nil { + return err + } + } } return nil From 03c048f896d97c7fed38358239685a3f95e9350b Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 10:23:42 +0100 Subject: [PATCH 42/63] Pass python inline minimal --- .../function_describe_snowflake_ext.go | 38 ++++++--- pkg/acceptance/helpers/function_client.go | 8 +- pkg/sdk/testint/functions_integration_test.go | 84 +++++++++++++++++-- 3 files changed, 106 insertions(+), 24 deletions(-) diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go b/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go index 0b8ca9312d..c1b241aa1d 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go @@ -227,7 +227,7 @@ func (f *FunctionDetailsAssert) HasBodyNil() *FunctionDetailsAssert { f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.Body != nil { - return fmt.Errorf("expected body to be nil, was %v", o.Body) + return fmt.Errorf("expected body to be nil, was %v", *o.Body) } return nil }) @@ -238,7 +238,7 @@ func (f *FunctionDetailsAssert) HasNullHandlingNil() *FunctionDetailsAssert { f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.NullHandling != nil { - return fmt.Errorf("expected null handling to be nil, was %v", o.NullHandling) + return fmt.Errorf("expected null handling to be nil, was %v", *o.NullHandling) } return nil }) @@ -249,7 +249,7 @@ func (f *FunctionDetailsAssert) HasVolatilityNil() *FunctionDetailsAssert { f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.Volatility != nil { - return fmt.Errorf("expected volatility to be nil, was %v", o.Volatility) + return fmt.Errorf("expected volatility to be nil, was %v", *o.Volatility) } return nil }) @@ -260,7 +260,7 @@ func (f *FunctionDetailsAssert) HasExternalAccessIntegrationsNil() *FunctionDeta f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.ExternalAccessIntegrations != nil { - return fmt.Errorf("expected external access integrations to be nil, was %v", o.ExternalAccessIntegrations) + return fmt.Errorf("expected external access integrations to be nil, was %v", *o.ExternalAccessIntegrations) } return nil }) @@ -271,7 +271,7 @@ func (f *FunctionDetailsAssert) HasSecretsNil() *FunctionDetailsAssert { f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.Secrets != nil { - return fmt.Errorf("expected secrets to be nil, was %v", o.Secrets) + return fmt.Errorf("expected secrets to be nil, was %v", *o.Secrets) } return nil }) @@ -282,7 +282,7 @@ func (f *FunctionDetailsAssert) HasImportsNil() *FunctionDetailsAssert { f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.Imports != nil { - return fmt.Errorf("expected imports to be nil, was %v", o.Imports) + return fmt.Errorf("expected imports to be nil, was %v", *o.Imports) } return nil }) @@ -293,7 +293,7 @@ func (f *FunctionDetailsAssert) HasHandlerNil() *FunctionDetailsAssert { f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.Handler != nil { - return fmt.Errorf("expected handler to be nil, was %v", o.Handler) + return fmt.Errorf("expected handler to be nil, was %v", *o.Handler) } return nil }) @@ -304,7 +304,7 @@ func (f *FunctionDetailsAssert) HasRuntimeVersionNil() *FunctionDetailsAssert { f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.RuntimeVersion != nil { - return fmt.Errorf("expected runtime version to be nil, was %v", o.RuntimeVersion) + return fmt.Errorf("expected runtime version to be nil, was %v", *o.RuntimeVersion) } return nil }) @@ -315,7 +315,7 @@ func (f *FunctionDetailsAssert) HasPackagesNil() *FunctionDetailsAssert { f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.Packages != nil { - return fmt.Errorf("expected packages to be nil, was %v", o.Packages) + return fmt.Errorf("expected packages to be nil, was %v", *o.Packages) } return nil }) @@ -326,7 +326,7 @@ func (f *FunctionDetailsAssert) HasTargetPathNil() *FunctionDetailsAssert { f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.TargetPath != nil { - return fmt.Errorf("expected target path to be nil, was %v", o.TargetPath) + return fmt.Errorf("expected target path to be nil, was %v", *o.TargetPath) } return nil }) @@ -337,7 +337,7 @@ func (f *FunctionDetailsAssert) HasInstalledPackagesNil() *FunctionDetailsAssert f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.InstalledPackages != nil { - return fmt.Errorf("expected installed packages to be nil, was %v", o.InstalledPackages) + return fmt.Errorf("expected installed packages to be nil, was %v", *o.InstalledPackages) } return nil }) @@ -348,7 +348,21 @@ func (f *FunctionDetailsAssert) HasIsAggregateNil() *FunctionDetailsAssert { f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { t.Helper() if o.IsAggregate != nil { - return fmt.Errorf("expected is aggregate to be nil, was %v", o.IsAggregate) + return fmt.Errorf("expected is aggregate to be nil, was %v", *o.IsAggregate) + } + return nil + }) + return f +} + +func (f *FunctionDetailsAssert) HasInstalledPackagesNotEmpty() *FunctionDetailsAssert { + f.AddAssertion(func(t *testing.T, o *sdk.FunctionDetails) error { + t.Helper() + if o.InstalledPackages == nil { + return fmt.Errorf("expected installed packages to not be nil") + } + if *o.InstalledPackages == "" { + return fmt.Errorf("expected installed packages to not be empty") } return nil }) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 25fd407790..88b9ce116f 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -126,13 +126,13 @@ func (c *FunctionClient) SampleJavascriptDefinition(t *testing.T, argName string `, argName) } -func (c *FunctionClient) SamplePythonDefinition(t *testing.T) string { +func (c *FunctionClient) SamplePythonDefinition(t *testing.T, funcName string, argName string) string { t.Helper() - return ` -def dump(i): + return fmt.Sprintf(` +def %[1]s(%[2]s): print("Hello World!") -` +`, funcName, argName) } func (c *FunctionClient) SampleScalaDefinition(t *testing.T) string { diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 16629bedb8..3b3a791b58 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strings" "testing" "time" @@ -53,10 +54,11 @@ func TestInt_CreateFunctions(t *testing.T) { className := "TestFunc" funcName := "echoVarchar" argName := "x" + dataType := testdatatypes.DataTypeVarchar_100 - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) - argument := sdk.NewFunctionArgumentRequest(argName, testdatatypes.DataTypeVarchar_100) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(testdatatypes.DataTypeVarchar_100) + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) handler := fmt.Sprintf("%s.%s", className, funcName) definition := testClientHelper().Function.SampleJavaDefinition(t, className, funcName, argName) @@ -81,8 +83,8 @@ func TestInt_CreateFunctions(t *testing.T) { HasIsAnsi(false). HasMinNumArguments(1). HasMaxNumArguments(1). - HasArgumentsOld([]sdk.DataType{sdk.DataTypeVARCHAR}). - HasArgumentsRaw(fmt.Sprintf(`%s(VARCHAR) RETURN VARCHAR`, function.ID().Name())). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). HasDescription(sdk.DefaultFunctionComment). HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). HasIsTableFunction(false). @@ -95,8 +97,8 @@ func TestInt_CreateFunctions(t *testing.T) { ) assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). - HasSignature(fmt.Sprintf(`(%s %s)`, argName, testdatatypes.DataTypeVarchar_100.ToLegacyDataTypeSql())). - HasReturns(testdatatypes.DataTypeVarchar_100.ToSql()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(dataType.ToSql()). HasLanguage("JAVA"). HasBody(definition). HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). @@ -505,7 +507,73 @@ func TestInt_CreateFunctions(t *testing.T) { ) }) - //t.Run("create function for Python - inline minimal", func(t *testing.T) {}) + t.Run("create function for Python - inline minimal", func(t *testing.T) { + dataType := testdatatypes.DataTypeNumber_36_2 + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + argName := "i" + funcName := "dump" + definition := testClientHelper().Function.SamplePythonDefinition(t, funcName, argName) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + request := sdk.NewCreateForPythonFunctionRequest(id.SchemaObjectId(), *returns, "3.8", funcName). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithFunctionDefinitionWrapped(definition) + + err := client.Functions.CreateForPython(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription(sdk.DefaultFunctionComment). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("PYTHON"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). //TODO [this PR]: do we care about this whitespace? + HasLanguage("PYTHON"). + HasBody(definition). + HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImports(`[]`). + HasHandler(funcName). + HasRuntimeVersion("3.8"). + HasPackages(`[]`). + HasTargetPathNil(). + HasInstalledPackagesNotEmpty(). + HasIsAggregate(false), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + //t.Run("create function for Python - inline full", func(t *testing.T) {}) //t.Run("create function for Python - staged minimal", func(t *testing.T) {}) //t.Run("create function for Python - staged full", func(t *testing.T) {}) From 443bf802c8bb91b588fa39a4a2d3cd9945bd45e8 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 10:47:33 +0100 Subject: [PATCH 43/63] Pass python inline full --- pkg/sdk/testint/functions_integration_test.go | 103 ++++++++++++++---- 1 file changed, 81 insertions(+), 22 deletions(-) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 3b3a791b58..1b63873516 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -25,6 +25,7 @@ import ( // TODO [next PR]: HasArgumentsRawFrom(functionId, arguments, return) // TODO [next PR]: WithImports - creation and cleanup on stage needed // TODO [next PR]: extract show assertions with commons fields +// TODO [this PR]: python aggregate func func TestInt_CreateFunctions(t *testing.T) { client := testClient(t) ctx := context.Background() @@ -574,7 +575,86 @@ func TestInt_CreateFunctions(t *testing.T) { ) }) - //t.Run("create function for Python - inline full", func(t *testing.T) {}) + t.Run("create function for Python - inline full", func(t *testing.T) { + dataType := testdatatypes.DataTypeNumber_36_2 + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + argName := "i" + funcName := "dump" + definition := testClientHelper().Function.SamplePythonDefinition(t, funcName, argName) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + request := sdk.NewCreateForPythonFunctionRequest(id.SchemaObjectId(), *returns, "3.8", funcName). + WithOrReplace(true). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithCopyGrants(true). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithComment("comment"). + //WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpFunction.JarLocation())}). // TODO [this PR] + WithPackages([]sdk.FunctionPackageRequest{ + *sdk.NewFunctionPackageRequest().WithPackage("absl-py==0.10.0"), + *sdk.NewFunctionPackageRequest().WithPackage("about-time==4.2.1"), + }). + WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). + WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}). + WithFunctionDefinitionWrapped(definition) + + err := client.Functions.CreateForPython(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription("comment"). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("PYTHON"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")+" NOT NULL"). //TODO [this PR]: do we care about this whitespace? + HasLanguage("PYTHON"). + HasBody(definition). + HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). + HasImports(`[]`). + HasHandler(funcName). + HasRuntimeVersion("3.8"). + HasPackages(`['absl-py==0.10.0','about-time==4.2.1']`). + HasTargetPathNil(). + HasInstalledPackagesNotEmpty(). + HasIsAggregate(false), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + //t.Run("create function for Python - staged minimal", func(t *testing.T) {}) //t.Run("create function for Python - staged full", func(t *testing.T) {}) // @@ -585,27 +665,6 @@ func TestInt_CreateFunctions(t *testing.T) { // //t.Run("create function for SQL - inline minimal", func(t *testing.T) {}) //t.Run("create function for SQL - inline full", func(t *testing.T) {}) - - //t.Run("create function for Python", func(t *testing.T) { - // id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeNumber) - // - // definition := testClientHelper().Function.SamplePythonDefinition(t) - // dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeVariant) - // returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - // argument := sdk.NewFunctionArgumentRequest("i", nil).WithArgDataTypeOld(sdk.DataTypeNumber) - // request := sdk.NewCreateForPythonFunctionRequest(id.SchemaObjectId(), *returns, "3.8", "dump"). - // WithOrReplace(true). - // WithArguments([]sdk.FunctionArgumentRequest{*argument}). - // WithFunctionDefinition(definition) - // err := client.Functions.CreateForPython(ctx, request) - // require.NoError(t, err) - // t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) - // - // function, err := client.Functions.ShowByID(ctx, id) - // require.NoError(t, err) - // require.Equal(t, id.Name(), function.Name) - // require.Equal(t, "PYTHON", function.Language) - //}) // //t.Run("create function for Scala", func(t *testing.T) { // id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) From 7da9a7ffd8dd124b657a02fcd7e6887f46bc86cd Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 12:36:13 +0100 Subject: [PATCH 44/63] Pass python inline full with import --- pkg/acceptance/helpers/function_client.go | 5 +- .../helpers/function_setup_helpers.go | 57 ++++++++++++++++++- pkg/acceptance/helpers/stage_client.go | 15 +++++ pkg/sdk/testint/functions_integration_test.go | 26 ++++----- 4 files changed, 86 insertions(+), 17 deletions(-) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 88b9ce116f..d9bbfc5b82 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -131,7 +131,10 @@ func (c *FunctionClient) SamplePythonDefinition(t *testing.T, funcName string, a return fmt.Sprintf(` def %[1]s(%[2]s): - print("Hello World!") + result = "" + for a in range(5): + result += %[2]s + return result `, funcName, argName) } diff --git a/pkg/acceptance/helpers/function_setup_helpers.go b/pkg/acceptance/helpers/function_setup_helpers.go index 6c936fb37a..ca4d29dd86 100644 --- a/pkg/acceptance/helpers/function_setup_helpers.go +++ b/pkg/acceptance/helpers/function_setup_helpers.go @@ -3,6 +3,8 @@ package helpers import ( "context" "fmt" + "path/filepath" + "strings" "testing" "time" @@ -24,8 +26,8 @@ func (c *TestClient) CreateSampleJavaFunctionAndJar(t *testing.T) *TmpFunction { dataType := testdatatypes.DataTypeVarchar_100 id := c.Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) - argument := sdk.NewFunctionArgumentRequest(argName, testdatatypes.DataTypeVarchar_100) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(testdatatypes.DataTypeVarchar_100) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) handler := fmt.Sprintf("%s.%s", className, funcName) definition := c.Function.SampleJavaDefinition(t, className, funcName, argName) @@ -52,9 +54,46 @@ func (c *TestClient) CreateSampleJavaFunctionAndJar(t *testing.T) *TmpFunction { } } +func (c *TestClient) CreateSamplePythonFunctionAndModule(t *testing.T) *TmpFunction { + t.Helper() + ctx := context.Background() + + funcName := fmt.Sprintf("echo%s", random.AlphaLowerN(3)) + argName := fmt.Sprintf("arg%s", random.AlphaLowerN(3)) + dataType := testdatatypes.DataTypeVarchar_100 + + id := c.Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + definition := c.Function.SamplePythonDefinition(t, funcName, argName) + + request := sdk.NewCreateForPythonFunctionRequest(id.SchemaObjectId(), *returns, "3.8", funcName). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithFunctionDefinitionWrapped(definition) + + err := c.context.client.Functions.CreateForPython(ctx, request) + require.NoError(t, err) + t.Cleanup(c.Function.DropFunctionFunc(t, id)) + + // using os.CreateTemp underneath - last * in pattern is replaced with random string + modulePattern := fmt.Sprintf("example*%s.py", random.AlphaLowerN(3)) + modulePath := c.Stage.PutOnUserStageWithContent(t, modulePattern, definition) + moduleFileName := filepath.Base(modulePath) + + return &TmpFunction{ + FunctionId: id, + ModuleName: strings.ReplaceAll(moduleFileName, ".py", ""), + FuncName: funcName, + ArgName: argName, + ArgType: dataType, + } +} + type TmpFunction struct { FunctionId sdk.SchemaObjectIdentifierWithArguments ClassName string + ModuleName string FuncName string ArgName string ArgType datatypes.DataType @@ -65,6 +104,18 @@ func (f *TmpFunction) JarLocation() string { return fmt.Sprintf("@~/%s", f.JarName) } -func (f *TmpFunction) Handler() string { +func (f *TmpFunction) PythonModuleLocation() string { + return fmt.Sprintf("@~/%s", f.PythonFileName()) +} + +func (f *TmpFunction) PythonFileName() string { + return fmt.Sprintf("%s.py", f.ModuleName) +} + +func (f *TmpFunction) JavaHandler() string { return fmt.Sprintf("%s.%s", f.ClassName, f.FuncName) } + +func (f *TmpFunction) PythonHandler() string { + return fmt.Sprintf("%s.%s", f.ModuleName, f.FuncName) +} diff --git a/pkg/acceptance/helpers/stage_client.go b/pkg/acceptance/helpers/stage_client.go index 6f918bfca1..60bac47c90 100644 --- a/pkg/acceptance/helpers/stage_client.go +++ b/pkg/acceptance/helpers/stage_client.go @@ -8,6 +8,7 @@ import ( "testing" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/testhelpers" "github.com/stretchr/testify/require" ) @@ -96,6 +97,20 @@ func (c *StageClient) PutOnStage(t *testing.T, id sdk.SchemaObjectIdentifier, fi require.NoError(t, err) } +func (c *StageClient) PutOnUserStageWithContent(t *testing.T, filename string, content string) string { + t.Helper() + ctx := context.Background() + + path := testhelpers.TestFile(t, filename, []byte(content)) + + _, err := c.context.client.ExecForTests(ctx, fmt.Sprintf(`PUT file://%s @~/ AUTO_COMPRESS = FALSE OVERWRITE = TRUE`, path)) + require.NoError(t, err) + + t.Cleanup(c.RemoveFromUserStageFunc(t, path)) + + return path +} + func (c *StageClient) RemoveFromUserStage(t *testing.T, pathOnStage string) { t.Helper() ctx := context.Background() diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 1b63873516..b32ee548f3 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -23,7 +23,6 @@ import ( // TODO [next PR]: schemaName and catalog name are quoted (because we use lowercase) // TODO [next PR]: HasArgumentsRawFrom(functionId, arguments, return) -// TODO [next PR]: WithImports - creation and cleanup on stage needed // TODO [next PR]: extract show assertions with commons fields // TODO [this PR]: python aggregate func func TestInt_CreateFunctions(t *testing.T) { @@ -40,7 +39,8 @@ func TestInt_CreateFunctions(t *testing.T) { externalAccessIntegration, externalAccessIntegrationCleanup := testClientHelper().ExternalAccessIntegration.CreateExternalAccessIntegrationWithNetworkRuleAndSecret(t, networkRule.ID(), secret.ID()) t.Cleanup(externalAccessIntegrationCleanup) - tmpFunction := testClientHelper().CreateSampleJavaFunctionAndJar(t) + tmpJavaFunction := testClientHelper().CreateSampleJavaFunctionAndJar(t) + tmpPythonFunction := testClientHelper().CreateSamplePythonFunctionAndModule(t) //assertParametersSet := func(t *testing.T, functionParametersAssert *objectparametersassert.FunctionParametersAssert) { // assertions.AssertThatObject(t, functionParametersAssert. @@ -145,7 +145,7 @@ func TestInt_CreateFunctions(t *testing.T) { WithReturnNullValues(sdk.ReturnNullValuesNotNull). WithRuntimeVersion("11"). WithComment("comment"). - WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpFunction.JarLocation())}). + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpJavaFunction.JarLocation())}). WithPackages([]sdk.FunctionPackageRequest{ *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:1.14.0"), *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:telemetry:0.1.0"), @@ -199,7 +199,7 @@ func TestInt_CreateFunctions(t *testing.T) { // TODO [this PR]: parse to identifier list // TODO [this PR]: check multiple secrets (to know how to parse) HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). - HasImports(fmt.Sprintf(`[%s]`, tmpFunction.JarLocation())). + HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasHandler(handler). HasRuntimeVersion("11"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). @@ -224,15 +224,15 @@ func TestInt_CreateFunctions(t *testing.T) { }) t.Run("create function for Java - staged minimal", func(t *testing.T) { - dataType := tmpFunction.ArgType + dataType := tmpJavaFunction.ArgType id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) argName := "x" argument := sdk.NewFunctionArgumentRequest(argName, dataType) dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - handler := tmpFunction.Handler() - importPath := tmpFunction.JarLocation() + handler := tmpJavaFunction.JavaHandler() + importPath := tmpJavaFunction.JarLocation() requestStaged := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). WithArguments([]sdk.FunctionArgumentRequest{*argument}). @@ -292,14 +292,14 @@ func TestInt_CreateFunctions(t *testing.T) { }) t.Run("create function for Java - staged full", func(t *testing.T) { - dataType := tmpFunction.ArgType + dataType := tmpJavaFunction.ArgType id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) argName := "x" argument := sdk.NewFunctionArgumentRequest(argName, dataType) dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - handler := tmpFunction.Handler() + handler := tmpJavaFunction.JavaHandler() requestStaged := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). WithOrReplace(true). @@ -310,7 +310,7 @@ func TestInt_CreateFunctions(t *testing.T) { WithReturnNullValues(sdk.ReturnNullValuesNotNull). WithRuntimeVersion("11"). WithComment("comment"). - WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpFunction.JarLocation())}). + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpJavaFunction.JarLocation())}). WithPackages([]sdk.FunctionPackageRequest{ *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:1.14.0"), *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:telemetry:0.1.0"), @@ -356,7 +356,7 @@ func TestInt_CreateFunctions(t *testing.T) { HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). - HasImports(fmt.Sprintf(`[%s]`, tmpFunction.JarLocation())). + HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasHandler(handler). HasRuntimeVersion("11"). HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). @@ -593,7 +593,7 @@ func TestInt_CreateFunctions(t *testing.T) { WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). WithComment("comment"). - //WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpFunction.JarLocation())}). // TODO [this PR] + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpPythonFunction.PythonModuleLocation())}). WithPackages([]sdk.FunctionPackageRequest{ *sdk.NewFunctionPackageRequest().WithPackage("absl-py==0.10.0"), *sdk.NewFunctionPackageRequest().WithPackage("about-time==4.2.1"), @@ -640,7 +640,7 @@ func TestInt_CreateFunctions(t *testing.T) { HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). - HasImports(`[]`). + HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). HasHandler(funcName). HasRuntimeVersion("3.8"). HasPackages(`['absl-py==0.10.0','about-time==4.2.1']`). From 29d371800c7a205835df7fea71b0058bbff1f929 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 12:44:46 +0100 Subject: [PATCH 45/63] Test python staged (both options) --- pkg/sdk/testint/functions_integration_test.go | 145 +++++++++++++++++- 1 file changed, 142 insertions(+), 3 deletions(-) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index b32ee548f3..d251c22bb2 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -655,9 +655,148 @@ func TestInt_CreateFunctions(t *testing.T) { ) }) - //t.Run("create function for Python - staged minimal", func(t *testing.T) {}) - //t.Run("create function for Python - staged full", func(t *testing.T) {}) - // + t.Run("create function for Python - staged minimal", func(t *testing.T) { + dataType := testdatatypes.DataTypeVarchar_100 + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + argName := "i" + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + request := sdk.NewCreateForPythonFunctionRequest(id.SchemaObjectId(), *returns, "3.8", tmpPythonFunction.PythonHandler()). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpPythonFunction.PythonModuleLocation())}) + + err := client.Functions.CreateForPython(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription(sdk.DefaultFunctionComment). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("PYTHON"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). //TODO [this PR]: do we care about this whitespace? + HasLanguage("PYTHON"). + HasBodyNil(). + HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). + HasHandler(tmpPythonFunction.PythonHandler()). + HasRuntimeVersion("3.8"). + HasPackages(`[]`). + HasTargetPathNil(). + HasInstalledPackagesNotEmpty(). + HasIsAggregate(false), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + + t.Run("create function for Python - staged full", func(t *testing.T) { + dataType := testdatatypes.DataTypeVarchar_100 + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + argName := "i" + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + request := sdk.NewCreateForPythonFunctionRequest(id.SchemaObjectId(), *returns, "3.8", tmpPythonFunction.PythonHandler()). + WithOrReplace(true). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithCopyGrants(true). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithComment("comment"). + WithPackages([]sdk.FunctionPackageRequest{ + *sdk.NewFunctionPackageRequest().WithPackage("absl-py==0.10.0"), + *sdk.NewFunctionPackageRequest().WithPackage("about-time==4.2.1"), + }). + WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). + WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}). + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpPythonFunction.PythonModuleLocation())}) + + err := client.Functions.CreateForPython(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription("comment"). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("PYTHON"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")+" NOT NULL"). //TODO [this PR]: do we care about this whitespace? + HasLanguage("PYTHON"). + HasBodyNil(). + HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). + HasImports(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). + HasHandler(tmpPythonFunction.PythonHandler()). + HasRuntimeVersion("3.8"). + HasPackages(`['absl-py==0.10.0','about-time==4.2.1']`). + HasTargetPathNil(). + HasInstalledPackagesNotEmpty(). + HasIsAggregate(false), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + //t.Run("create function for Scala - inline minimal", func(t *testing.T) {}) //t.Run("create function for Scala - inline full", func(t *testing.T) {}) //t.Run("create function for Scala - staged minimal", func(t *testing.T) {}) From ee5886aeab338824c3ac4116eaffbe74aff6ce37 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 13:01:47 +0100 Subject: [PATCH 46/63] Test scala inline (both options) --- pkg/acceptance/helpers/function_client.go | 12 +- pkg/sdk/functions_def.go | 1 + pkg/sdk/functions_gen.go | 12 +- pkg/sdk/testint/functions_integration_test.go | 160 +++++++++++++++++- 4 files changed, 171 insertions(+), 14 deletions(-) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index d9bbfc5b82..7fdbf66dc8 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -138,16 +138,16 @@ def %[1]s(%[2]s): `, funcName, argName) } -func (c *FunctionClient) SampleScalaDefinition(t *testing.T) string { +func (c *FunctionClient) SampleScalaDefinition(t *testing.T, className string, funcName string, argName string) string { t.Helper() - return ` - class Echo { - def echoVarchar(x : String): String = { - return x + return fmt.Sprintf(` + class %[1]s { + def %[2]s(%[3]s : String): String = { + return %[3]s } } -` +`, className, funcName, argName) } func (c *FunctionClient) SampleSqlDefinition(t *testing.T) string { diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index 2231922afa..cc909bf5cb 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -180,6 +180,7 @@ var FunctionsDef = g.NewInterface( WithValidation(g.ValidateValueSet, "Handler"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), ).CustomOperation( + // TODO [this PR]: runtime version required "CreateForScala", "https://docs.snowflake.com/en/sql-reference/sql/create-function#scala-handler", g.NewQueryStruct("CreateForScala"). diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index 218bcf4d14..4c6a0c039f 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -168,12 +168,12 @@ type CreateForScalaFunctionOptions struct { Imports []FunctionImport `ddl:"parameter,parentheses" sql:"IMPORTS"` Packages []FunctionPackage `ddl:"parameter,parentheses" sql:"PACKAGES"` Handler string `ddl:"parameter,single_quotes" sql:"HANDLER"` - TargetPath *string - EnableConsoleOutput *bool `ddl:"parameter" sql:"ENABLE_CONSOLE_OUTPUT"` - LogLevel *LogLevel `ddl:"parameter,single_quotes" sql:"LOG_LEVEL"` - MetricLevel *MetricLevel `ddl:"parameter,single_quotes" sql:"METRIC_LEVEL"` - TraceLevel *TraceLevel `ddl:"parameter,single_quotes" sql:"TRACE_LEVEL"` - FunctionDefinition *string `ddl:"parameter,no_equals" sql:"AS"` + TargetPath *string `ddl:"parameter,single_quotes" sql:"TARGET_PATH"` + EnableConsoleOutput *bool `ddl:"parameter" sql:"ENABLE_CONSOLE_OUTPUT"` + LogLevel *LogLevel `ddl:"parameter,single_quotes" sql:"LOG_LEVEL"` + MetricLevel *MetricLevel `ddl:"parameter,single_quotes" sql:"METRIC_LEVEL"` + TraceLevel *TraceLevel `ddl:"parameter,single_quotes" sql:"TRACE_LEVEL"` + FunctionDefinition *string `ddl:"parameter,no_equals" sql:"AS"` } // CreateForSQLFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#sql-handler. diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index d251c22bb2..13290a5373 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -797,8 +797,164 @@ func TestInt_CreateFunctions(t *testing.T) { ) }) - //t.Run("create function for Scala - inline minimal", func(t *testing.T) {}) - //t.Run("create function for Scala - inline full", func(t *testing.T) {}) + t.Run("create function for Scala - inline minimal", func(t *testing.T) { + className := "TestFunc" + funcName := "echoVarchar" + argName := "x" + dataType := testdatatypes.DataTypeVarchar_100 + + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + definition := testClientHelper().Function.SampleScalaDefinition(t, className, funcName, argName) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + handler := fmt.Sprintf("%s.%s", className, funcName) + request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithRuntimeVersion("2.12"). + WithFunctionDefinitionWrapped(definition) + + err := client.Functions.CreateForScala(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription(sdk.DefaultFunctionComment). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("SCALA"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(dataType.ToSql()). + HasLanguage("SCALA"). + HasBody(definition). + HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImports(`[]`). + HasHandler(handler). + HasRuntimeVersion("2.12"). + HasPackages(`[]`). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + + t.Run("create function for Scala - inline full", func(t *testing.T) { + className := "TestFunc" + funcName := "echoVarchar" + argName := "x" + dataType := testdatatypes.DataTypeVarchar_100 + + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + definition := testClientHelper().Function.SampleScalaDefinition(t, className, funcName, argName) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + handler := fmt.Sprintf("%s.%s", className, funcName) + jarName := fmt.Sprintf("tf-%d-%s.jar", time.Now().Unix(), random.AlphaN(5)) + targetPath := fmt.Sprintf("@~/%s", jarName) + request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler). + WithOrReplace(true). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithCopyGrants(true). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + WithRuntimeVersion("2.12"). + WithComment("comment"). + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpJavaFunction.JarLocation())}). + WithPackages([]sdk.FunctionPackageRequest{ + *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:1.14.0"), + *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:telemetry:0.1.0"), + }). + WithTargetPath(targetPath). + //WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). // TODO [this PR]: add + //WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}). // TODO [this PR]: add + WithEnableConsoleOutput(true). + WithLogLevel(sdk.LogLevelWarn). + WithMetricLevel(sdk.MetricLevelAll). + WithTraceLevel(sdk.TraceLevelAlways). + WithFunctionDefinitionWrapped(definition) + + err := client.Functions.CreateForScala(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription("comment"). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("SCALA"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(fmt.Sprintf(`%s NOT NULL`, dataType.ToSql())). + HasLanguage("SCALA"). + HasBody(definition). + HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + //HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + //HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). + HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). + HasHandler(handler). + HasRuntimeVersion("2.12"). + HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasTargetPath(targetPath). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + //t.Run("create function for Scala - staged minimal", func(t *testing.T) {}) //t.Run("create function for Scala - staged full", func(t *testing.T) {}) // From 616e0c3ff3404da3fac842db8e088aa613fecfcb Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 13:09:01 +0100 Subject: [PATCH 47/63] Test scala staged (both options) --- pkg/sdk/functions_gen_test.go | 1 - pkg/sdk/functions_validations_gen.go | 3 - pkg/sdk/testint/functions_integration_test.go | 171 +++++++++++++++--- 3 files changed, 145 insertions(+), 30 deletions(-) diff --git a/pkg/sdk/functions_gen_test.go b/pkg/sdk/functions_gen_test.go index c4087ac0c6..56778f14a8 100644 --- a/pkg/sdk/functions_gen_test.go +++ b/pkg/sdk/functions_gen_test.go @@ -809,7 +809,6 @@ func TestFunctions_CreateForScala(t *testing.T) { }, } assertOptsInvalidJoinedErrors(t, opts, NewError("TARGET_PATH must be nil when AS is nil")) - assertOptsInvalidJoinedErrors(t, opts, NewError("PACKAGES must be empty when AS is nil")) assertOptsInvalidJoinedErrors(t, opts, NewError("IMPORTS must not be empty when AS is nil")) }) diff --git a/pkg/sdk/functions_validations_gen.go b/pkg/sdk/functions_validations_gen.go index aee4d814bf..651515e1cf 100644 --- a/pkg/sdk/functions_validations_gen.go +++ b/pkg/sdk/functions_validations_gen.go @@ -192,9 +192,6 @@ func (opts *CreateForScalaFunctionOptions) validate() error { if opts.TargetPath != nil { errs = append(errs, NewError("TARGET_PATH must be nil when AS is nil")) } - if len(opts.Packages) > 0 { - errs = append(errs, NewError("PACKAGES must be empty when AS is nil")) - } if len(opts.Imports) == 0 { errs = append(errs, NewError("IMPORTS must not be empty when AS is nil")) } diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 13290a5373..af684292fc 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -955,35 +955,154 @@ func TestInt_CreateFunctions(t *testing.T) { ) }) - //t.Run("create function for Scala - staged minimal", func(t *testing.T) {}) - //t.Run("create function for Scala - staged full", func(t *testing.T) {}) - // + t.Run("create function for Scala - staged minimal", func(t *testing.T) { + dataType := tmpJavaFunction.ArgType + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + argName := "x" + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + handler := tmpJavaFunction.JavaHandler() + importPath := tmpJavaFunction.JarLocation() + + requestStaged := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithRuntimeVersion("2.12"). + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(importPath)}) + + err := client.Functions.CreateForScala(ctx, requestStaged) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription(sdk.DefaultFunctionComment). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("SCALA"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(dataType.ToSql()). + HasLanguage("SCALA"). + HasBodyNil(). + HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImports(fmt.Sprintf(`[%s]`, importPath)). + HasHandler(handler). + HasRuntimeVersion("2.12"). + HasPackages(`[]`). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + + t.Run("create function for Scala - staged full", func(t *testing.T) { + dataType := tmpJavaFunction.ArgType + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + argName := "x" + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + handler := tmpJavaFunction.JavaHandler() + + requestStaged := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler). + WithOrReplace(true). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithCopyGrants(true). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + WithRuntimeVersion("2.12"). + WithComment("comment"). + WithPackages([]sdk.FunctionPackageRequest{ + *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:1.14.0"), + *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:telemetry:0.1.0"), + }). + //WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). + //WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}) + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpJavaFunction.JarLocation())}) + + err := client.Functions.CreateForScala(ctx, requestStaged) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription("comment"). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("SCALA"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(fmt.Sprintf(`%s NOT NULL`, dataType.ToSql())). + HasLanguage("SCALA"). + HasBodyNil(). + HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + //HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + //HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). + HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). + HasHandler(handler). + HasRuntimeVersion("2.12"). + HasPackages(`[com.snowflake:snowpark:1.14.0,com.snowflake:telemetry:0.1.0]`). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + //t.Run("create function for SQL - inline minimal", func(t *testing.T) {}) //t.Run("create function for SQL - inline full", func(t *testing.T) {}) // - //t.Run("create function for Scala", func(t *testing.T) { - // id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) - // - // target := fmt.Sprintf("@~/tf-%d.jar", time.Now().Unix()) - // definition := testClientHelper().Function.SampleScalaDefinition(t) - // argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeVARCHAR) - // request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), nil, "Echo.echoVarchar"). - // WithResultDataTypeOld(sdk.DataTypeVARCHAR). - // WithOrReplace(true). - // WithArguments([]sdk.FunctionArgumentRequest{*argument}). - // WithTargetPath(target). - // WithRuntimeVersion("2.12"). - // WithFunctionDefinition(definition) - // err := client.Functions.CreateForScala(ctx, request) - // require.NoError(t, err) - // t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) - // - // function, err := client.Functions.ShowByID(ctx, id) - // require.NoError(t, err) - // require.Equal(t, id.Name(), function.Name) - // require.Equal(t, "SCALA", function.Language) - //}) - // //t.Run("create function for SQL", func(t *testing.T) { // id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) // From 497128723e433ded9665e851f98f3d0c319bac9b Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 13:12:51 +0100 Subject: [PATCH 48/63] Add secrets and external access integrations to scala functions --- pkg/sdk/functions_def.go | 2 + pkg/sdk/functions_dto_builders_gen.go | 10 ++++ pkg/sdk/functions_dto_gen.go | 48 ++++++++-------- pkg/sdk/functions_gen.go | 56 ++++++++++--------- pkg/sdk/functions_impl_gen.go | 16 +++--- pkg/sdk/testint/functions_integration_test.go | 16 +++--- 6 files changed, 83 insertions(+), 65 deletions(-) diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index cc909bf5cb..9da2f947d8 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -216,6 +216,8 @@ var FunctionsDef = g.NewInterface( g.ParameterOptions().Parentheses().SQL("PACKAGES"), ). TextAssignment("HANDLER", g.ParameterOptions().SingleQuotes().Required()). + ListAssignment("EXTERNAL_ACCESS_INTEGRATIONS", "AccountObjectIdentifier", g.ParameterOptions().Parentheses()). + ListAssignment("SECRETS", "SecretReference", g.ParameterOptions().Parentheses()). OptionalTextAssignment("TARGET_PATH", g.ParameterOptions().SingleQuotes()). OptionalBooleanAssignment("ENABLE_CONSOLE_OUTPUT", nil). OptionalAssignment("LOG_LEVEL", g.KindOfTPointer[LogLevel](), g.ParameterOptions().SingleQuotes()). diff --git a/pkg/sdk/functions_dto_builders_gen.go b/pkg/sdk/functions_dto_builders_gen.go index acf9768fb4..d983ac9e30 100644 --- a/pkg/sdk/functions_dto_builders_gen.go +++ b/pkg/sdk/functions_dto_builders_gen.go @@ -486,6 +486,16 @@ func (s *CreateForScalaFunctionRequest) WithPackages(Packages []FunctionPackageR return s } +func (s *CreateForScalaFunctionRequest) WithExternalAccessIntegrations(ExternalAccessIntegrations []AccountObjectIdentifier) *CreateForScalaFunctionRequest { + s.ExternalAccessIntegrations = ExternalAccessIntegrations + return s +} + +func (s *CreateForScalaFunctionRequest) WithSecrets(Secrets []SecretReference) *CreateForScalaFunctionRequest { + s.Secrets = Secrets + return s +} + func (s *CreateForScalaFunctionRequest) WithTargetPath(TargetPath string) *CreateForScalaFunctionRequest { s.TargetPath = &TargetPath return s diff --git a/pkg/sdk/functions_dto_gen.go b/pkg/sdk/functions_dto_gen.go index c7c09bb8f1..b5930cf6a2 100644 --- a/pkg/sdk/functions_dto_gen.go +++ b/pkg/sdk/functions_dto_gen.go @@ -125,29 +125,31 @@ type CreateForPythonFunctionRequest struct { } type CreateForScalaFunctionRequest struct { - OrReplace *bool - Temporary *bool - Secure *bool - IfNotExists *bool - name SchemaObjectIdentifier // required - Arguments []FunctionArgumentRequest - CopyGrants *bool - ResultDataTypeOld DataType - ResultDataType datatypes.DataType // required - ReturnNullValues *ReturnNullValues - NullInputBehavior *NullInputBehavior - ReturnResultsBehavior *ReturnResultsBehavior - RuntimeVersion *string - Comment *string - Imports []FunctionImportRequest - Packages []FunctionPackageRequest - Handler string // required - TargetPath *string - EnableConsoleOutput *bool - LogLevel *LogLevel - MetricLevel *MetricLevel - TraceLevel *TraceLevel - FunctionDefinition *string + OrReplace *bool + Temporary *bool + Secure *bool + IfNotExists *bool + name SchemaObjectIdentifier // required + Arguments []FunctionArgumentRequest + CopyGrants *bool + ResultDataTypeOld DataType + ResultDataType datatypes.DataType // required + ReturnNullValues *ReturnNullValues + NullInputBehavior *NullInputBehavior + ReturnResultsBehavior *ReturnResultsBehavior + RuntimeVersion *string + Comment *string + Imports []FunctionImportRequest + Packages []FunctionPackageRequest + Handler string // required + ExternalAccessIntegrations []AccountObjectIdentifier + Secrets []SecretReference + TargetPath *string + EnableConsoleOutput *bool + LogLevel *LogLevel + MetricLevel *MetricLevel + TraceLevel *TraceLevel + FunctionDefinition *string } type CreateForSQLFunctionRequest struct { diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index 4c6a0c039f..225485be5c 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -147,33 +147,35 @@ type CreateForPythonFunctionOptions struct { // CreateForScalaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#scala-handler. type CreateForScalaFunctionOptions struct { - create bool `ddl:"static" sql:"CREATE"` - OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` - Temporary *bool `ddl:"keyword" sql:"TEMPORARY"` - Secure *bool `ddl:"keyword" sql:"SECURE"` - function bool `ddl:"static" sql:"FUNCTION"` - IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` - name SchemaObjectIdentifier `ddl:"identifier"` - Arguments []FunctionArgument `ddl:"list,must_parentheses"` - CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` - returns bool `ddl:"static" sql:"RETURNS"` - ResultDataTypeOld DataType `ddl:"parameter,no_equals"` - ResultDataType datatypes.DataType `ddl:"parameter,no_quotes,no_equals"` - ReturnNullValues *ReturnNullValues `ddl:"keyword"` - languageScala bool `ddl:"static" sql:"LANGUAGE SCALA"` - NullInputBehavior *NullInputBehavior `ddl:"keyword"` - ReturnResultsBehavior *ReturnResultsBehavior `ddl:"keyword"` - RuntimeVersion *string `ddl:"parameter,single_quotes" sql:"RUNTIME_VERSION"` - Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` - Imports []FunctionImport `ddl:"parameter,parentheses" sql:"IMPORTS"` - Packages []FunctionPackage `ddl:"parameter,parentheses" sql:"PACKAGES"` - Handler string `ddl:"parameter,single_quotes" sql:"HANDLER"` - TargetPath *string `ddl:"parameter,single_quotes" sql:"TARGET_PATH"` - EnableConsoleOutput *bool `ddl:"parameter" sql:"ENABLE_CONSOLE_OUTPUT"` - LogLevel *LogLevel `ddl:"parameter,single_quotes" sql:"LOG_LEVEL"` - MetricLevel *MetricLevel `ddl:"parameter,single_quotes" sql:"METRIC_LEVEL"` - TraceLevel *TraceLevel `ddl:"parameter,single_quotes" sql:"TRACE_LEVEL"` - FunctionDefinition *string `ddl:"parameter,no_equals" sql:"AS"` + create bool `ddl:"static" sql:"CREATE"` + OrReplace *bool `ddl:"keyword" sql:"OR REPLACE"` + Temporary *bool `ddl:"keyword" sql:"TEMPORARY"` + Secure *bool `ddl:"keyword" sql:"SECURE"` + function bool `ddl:"static" sql:"FUNCTION"` + IfNotExists *bool `ddl:"keyword" sql:"IF NOT EXISTS"` + name SchemaObjectIdentifier `ddl:"identifier"` + Arguments []FunctionArgument `ddl:"list,must_parentheses"` + CopyGrants *bool `ddl:"keyword" sql:"COPY GRANTS"` + returns bool `ddl:"static" sql:"RETURNS"` + ResultDataTypeOld DataType `ddl:"parameter,no_equals"` + ResultDataType datatypes.DataType `ddl:"parameter,no_quotes,no_equals"` + ReturnNullValues *ReturnNullValues `ddl:"keyword"` + languageScala bool `ddl:"static" sql:"LANGUAGE SCALA"` + NullInputBehavior *NullInputBehavior `ddl:"keyword"` + ReturnResultsBehavior *ReturnResultsBehavior `ddl:"keyword"` + RuntimeVersion *string `ddl:"parameter,single_quotes" sql:"RUNTIME_VERSION"` + Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` + Imports []FunctionImport `ddl:"parameter,parentheses" sql:"IMPORTS"` + Packages []FunctionPackage `ddl:"parameter,parentheses" sql:"PACKAGES"` + Handler string `ddl:"parameter,single_quotes" sql:"HANDLER"` + ExternalAccessIntegrations []AccountObjectIdentifier `ddl:"parameter,parentheses" sql:"EXTERNAL_ACCESS_INTEGRATIONS"` + Secrets []SecretReference `ddl:"parameter,parentheses" sql:"SECRETS"` + TargetPath *string `ddl:"parameter,single_quotes" sql:"TARGET_PATH"` + EnableConsoleOutput *bool `ddl:"parameter" sql:"ENABLE_CONSOLE_OUTPUT"` + LogLevel *LogLevel `ddl:"parameter,single_quotes" sql:"LOG_LEVEL"` + MetricLevel *MetricLevel `ddl:"parameter,single_quotes" sql:"METRIC_LEVEL"` + TraceLevel *TraceLevel `ddl:"parameter,single_quotes" sql:"TRACE_LEVEL"` + FunctionDefinition *string `ddl:"parameter,no_equals" sql:"AS"` } // CreateForSQLFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#sql-handler. diff --git a/pkg/sdk/functions_impl_gen.go b/pkg/sdk/functions_impl_gen.go index af4f013e3a..4a122ee2df 100644 --- a/pkg/sdk/functions_impl_gen.go +++ b/pkg/sdk/functions_impl_gen.go @@ -310,13 +310,15 @@ func (r *CreateForScalaFunctionRequest) toOpts() *CreateForScalaFunctionOptions RuntimeVersion: r.RuntimeVersion, Comment: r.Comment, - Handler: r.Handler, - TargetPath: r.TargetPath, - EnableConsoleOutput: r.EnableConsoleOutput, - LogLevel: r.LogLevel, - MetricLevel: r.MetricLevel, - TraceLevel: r.TraceLevel, - FunctionDefinition: r.FunctionDefinition, + Handler: r.Handler, + ExternalAccessIntegrations: r.ExternalAccessIntegrations, + Secrets: r.Secrets, + TargetPath: r.TargetPath, + EnableConsoleOutput: r.EnableConsoleOutput, + LogLevel: r.LogLevel, + MetricLevel: r.MetricLevel, + TraceLevel: r.TraceLevel, + FunctionDefinition: r.FunctionDefinition, } if r.Arguments != nil { s := make([]FunctionArgument, len(r.Arguments)) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index af684292fc..efc54ef611 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -894,8 +894,8 @@ func TestInt_CreateFunctions(t *testing.T) { *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:telemetry:0.1.0"), }). WithTargetPath(targetPath). - //WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). // TODO [this PR]: add - //WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}). // TODO [this PR]: add + WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). + WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}). WithEnableConsoleOutput(true). WithLogLevel(sdk.LogLevelWarn). WithMetricLevel(sdk.MetricLevelAll). @@ -938,8 +938,8 @@ func TestInt_CreateFunctions(t *testing.T) { HasBody(definition). HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). - //HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). - //HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasHandler(handler). HasRuntimeVersion("2.12"). @@ -1043,8 +1043,8 @@ func TestInt_CreateFunctions(t *testing.T) { *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:1.14.0"), *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:telemetry:0.1.0"), }). - //WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). - //WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}) + WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). + WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}). WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpJavaFunction.JarLocation())}) err := client.Functions.CreateForScala(ctx, requestStaged) @@ -1083,8 +1083,8 @@ func TestInt_CreateFunctions(t *testing.T) { HasBodyNil(). HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). - //HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). - //HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasHandler(handler). HasRuntimeVersion("2.12"). From e3c6e36f481b8ff4a9838fc9f66b43938b58a31f Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 13:19:55 +0100 Subject: [PATCH 49/63] Test create SQL func minimal --- pkg/sdk/testint/functions_integration_test.go | 67 ++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index efc54ef611..b74fb074ce 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -1100,7 +1100,72 @@ func TestInt_CreateFunctions(t *testing.T) { ) }) - //t.Run("create function for SQL - inline minimal", func(t *testing.T) {}) + t.Run("create function for SQL - inline minimal", func(t *testing.T) { + argName := "x" + dataType := testdatatypes.DataTypeFloat + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + definition := testClientHelper().Function.SampleSqlDefinition(t) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + request := sdk.NewCreateForSQLFunctionRequestDefinitionWrapped(id.SchemaObjectId(), *returns, definition). + WithArguments([]sdk.FunctionArgumentRequest{*argument}) + + err := client.Functions.CreateForSQL(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription(sdk.DefaultFunctionComment). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("SQL"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(dataType.ToSql()). + HasLanguage("SQL"). + HasBody(definition). + HasNullHandlingNil(). + HasVolatilityNil(). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImportsNil(). + HasHandlerNil(). + HasRuntimeVersionNil(). + HasPackagesNil(). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + + // TODO [this PR]: volatility is not returned and is present in create syntax //t.Run("create function for SQL - inline full", func(t *testing.T) {}) // //t.Run("create function for SQL", func(t *testing.T) { From 22935c287facb6f8ab742558ed17be61f3bd188a Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 14:04:38 +0100 Subject: [PATCH 50/63] Test create SQL func full --- pkg/sdk/testint/functions_integration_test.go | 95 ++++++++++++++----- 1 file changed, 72 insertions(+), 23 deletions(-) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index b74fb074ce..1a86d09cce 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -1165,29 +1165,78 @@ func TestInt_CreateFunctions(t *testing.T) { ) }) - // TODO [this PR]: volatility is not returned and is present in create syntax - //t.Run("create function for SQL - inline full", func(t *testing.T) {}) - // - //t.Run("create function for SQL", func(t *testing.T) { - // id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) - // - // definition := testClientHelper().Function.SampleSqlDefinition(t) - // dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) - // returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - // argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeFloat) - // request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition). - // WithArguments([]sdk.FunctionArgumentRequest{*argument}). - // WithOrReplace(true). - // WithComment("comment") - // err := client.Functions.CreateForSQL(ctx, request) - // require.NoError(t, err) - // t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) - // - // function, err := client.Functions.ShowByID(ctx, id) - // require.NoError(t, err) - // require.Equal(t, id.Name(), function.Name) - // require.Equal(t, "SQL", function.Language) - //}) + t.Run("create function for SQL - inline full", func(t *testing.T) { + argName := "x" + dataType := testdatatypes.DataTypeFloat + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + definition := testClientHelper().Function.SampleSqlDefinition(t) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + request := sdk.NewCreateForSQLFunctionRequestDefinitionWrapped(id.SchemaObjectId(), *returns, definition). + WithOrReplace(true). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithCopyGrants(true). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithMemoizable(true). + WithComment("comment") + + err := client.Functions.CreateForSQL(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(1). + HasMaxNumArguments(1). + HasArgumentsOld([]sdk.DataType{sdk.LegacyDataTypeFrom(dataType)}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s(%[2]s) RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription("comment"). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("SQL"). + HasIsMemoizable(true). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). + HasReturns(fmt.Sprintf(`%s NOT NULL`, dataType.ToSql())). + HasLanguage("SQL"). + HasBody(definition). + HasNullHandlingNil(). + // TODO [next PR]: volatility is not returned and is present in create syntax + //HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + HasVolatilityNil(). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImportsNil(). + HasHandlerNil(). + HasRuntimeVersionNil(). + HasPackagesNil(). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) //t.Run("create function for SQL with no arguments", func(t *testing.T) { // id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments() From 905ecf38b46fe30632e3e5c74987175636958bbe Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 14:11:34 +0100 Subject: [PATCH 51/63] Test create SQL without args --- pkg/sdk/testint/functions_integration_test.go | 81 ++++++++++++++----- 1 file changed, 62 insertions(+), 19 deletions(-) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 1a86d09cce..384b07def0 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -25,7 +25,7 @@ import ( // TODO [next PR]: HasArgumentsRawFrom(functionId, arguments, return) // TODO [next PR]: extract show assertions with commons fields // TODO [this PR]: python aggregate func -func TestInt_CreateFunctions(t *testing.T) { +func TestInt_Functions(t *testing.T) { client := testClient(t) ctx := context.Background() secretId := testClientHelper().Ids.RandomSchemaObjectIdentifier() @@ -1238,24 +1238,67 @@ func TestInt_CreateFunctions(t *testing.T) { ) }) - //t.Run("create function for SQL with no arguments", func(t *testing.T) { - // id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments() - // - // definition := testClientHelper().Function.SampleSqlDefinition(t) - // dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) - // returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - // request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition). - // WithOrReplace(true). - // WithComment("comment") - // err := client.Functions.CreateForSQL(ctx, request) - // require.NoError(t, err) - // t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) - // - // function, err := client.Functions.ShowByID(ctx, id) - // require.NoError(t, err) - // require.Equal(t, id.Name(), function.Name) - // require.Equal(t, "SQL", function.Language) - //}) + t.Run("create function for SQL - no arguments", func(t *testing.T) { + dataType := testdatatypes.DataTypeFloat + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments() + + definition := testClientHelper().Function.SampleSqlDefinition(t) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + request := sdk.NewCreateForSQLFunctionRequestDefinitionWrapped(id.SchemaObjectId(), *returns, definition) + + err := client.Functions.CreateForSQL(ctx, request) + require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + + function, err := client.Functions.ShowByID(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, function). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasSchemaName(fmt.Sprintf(`"%s"`, id.SchemaName())). + HasIsBuiltin(false). + HasIsAggregate(false). + HasIsAnsi(false). + HasMinNumArguments(0). + HasMaxNumArguments(0). + HasArgumentsOld([]sdk.DataType{}). + HasArgumentsRaw(fmt.Sprintf(`%[1]s() RETURN %[2]s`, function.ID().Name(), dataType.ToLegacyDataTypeSql())). + HasDescription(sdk.DefaultFunctionComment). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasIsExternalFunction(false). + HasLanguage("SQL"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). + HasSignature("()"). + HasReturns(dataType.ToSql()). + HasLanguage("SQL"). + HasBody(definition). + HasNullHandlingNil(). + HasVolatilityNil(). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImportsNil(). + HasHandlerNil(). + HasRuntimeVersionNil(). + HasPackagesNil(). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) } func TestInt_OtherFunctions(t *testing.T) { From cb51f3e66fe1f8ade7c8129eb17ffb408392761b Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 14:12:02 +0100 Subject: [PATCH 52/63] Add a TODO --- pkg/sdk/testint/functions_integration_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 384b07def0..2539a5f51f 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -25,6 +25,7 @@ import ( // TODO [next PR]: HasArgumentsRawFrom(functionId, arguments, return) // TODO [next PR]: extract show assertions with commons fields // TODO [this PR]: python aggregate func +// TODO [this PR]: clean stage in tests with targetPath func TestInt_Functions(t *testing.T) { client := testClient(t) ctx := context.Background() From 9d5ff09e38dd8d468f5a8b3cac3e5c12e4bffb52 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 14:48:15 +0100 Subject: [PATCH 53/63] Start testing alters --- pkg/acceptance/helpers/function_client.go | 54 ++++ pkg/sdk/functions_gen.go | 1 + pkg/sdk/testint/functions_integration_test.go | 234 +++++++++++------- 3 files changed, 193 insertions(+), 96 deletions(-) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 7fdbf66dc8..e75516237d 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -5,6 +5,7 @@ import ( "fmt" "testing" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" "github.com/stretchr/testify/require" ) @@ -56,6 +57,59 @@ func (c *FunctionClient) CreateSecure(t *testing.T, arguments ...sdk.DataType) * ) } +func (c *FunctionClient) CreateSql(t *testing.T) (*sdk.Function, func()) { + t.Helper() + ctx := context.Background() + + argName := "x" + dataType := testdatatypes.DataTypeFloat + id := c.ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + definition := c.SampleSqlDefinition(t) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + request := sdk.NewCreateForSQLFunctionRequestDefinitionWrapped(id.SchemaObjectId(), *returns, definition). + WithArguments([]sdk.FunctionArgumentRequest{*argument}) + + err := c.client().CreateForSQL(ctx, request) + require.NoError(t, err) + + function, err := c.client().ShowByID(ctx, id) + require.NoError(t, err) + + return function, c.DropFunctionFunc(t, id) +} + +func (c *FunctionClient) CreateJava(t *testing.T) (*sdk.Function, func()) { + t.Helper() + ctx := context.Background() + + className := "TestFunc" + funcName := "echoVarchar" + argName := "x" + dataType := testdatatypes.DataTypeVarchar_100 + + id := c.ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + handler := fmt.Sprintf("%s.%s", className, funcName) + definition := c.SampleJavaDefinition(t, className, funcName, argName) + + request := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithFunctionDefinitionWrapped(definition) + + err := c.client().CreateForJava(ctx, request) + require.NoError(t, err) + + function, err := c.client().ShowByID(ctx, id) + require.NoError(t, err) + + return function, c.DropFunctionFunc(t, id) +} + func (c *FunctionClient) CreateWithRequest(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments, req *sdk.CreateForSQLFunctionRequest) *sdk.Function { t.Helper() ctx := context.Background() diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index 225485be5c..a51e65a227 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -291,6 +291,7 @@ type Function struct { IsTableFunction bool ValidForClustering bool IsSecure bool + // TODO [this PR]: eai + secrets IsExternalFunction bool Language string IsMemoizable bool diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 2539a5f51f..6a91000d34 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -43,14 +43,14 @@ func TestInt_Functions(t *testing.T) { tmpJavaFunction := testClientHelper().CreateSampleJavaFunctionAndJar(t) tmpPythonFunction := testClientHelper().CreateSamplePythonFunctionAndModule(t) - //assertParametersSet := func(t *testing.T, functionParametersAssert *objectparametersassert.FunctionParametersAssert) { - // assertions.AssertThatObject(t, functionParametersAssert. - // HasEnableConsoleOutput(true). - // HasLogLevel(sdk.LogLevelWarn). - // HasMetricLevel(sdk.MetricLevelAll). - // HasTraceLevel(sdk.TraceLevelAlways), - // ) - //} + assertParametersSet := func(t *testing.T, functionParametersAssert *objectparametersassert.FunctionParametersAssert) { + assertions.AssertThatObject(t, functionParametersAssert. + HasEnableConsoleOutput(true). + HasLogLevel(sdk.LogLevelWarn). + HasMetricLevel(sdk.MetricLevelAll). + HasTraceLevel(sdk.TraceLevelAlways), + ) + } t.Run("create function for Java - inline minimal", func(t *testing.T) { className := "TestFunc" @@ -213,15 +213,6 @@ func TestInt_Functions(t *testing.T) { HasAllDefaults(). HasAllDefaultsExplicit(), ) - - // TODO [this PR]: will check after alter - // TODO [this PR]: add a test documenting that we can't set parameters in create (and revert adding these parametrs directly in object...) - //assertParametersSet(t, objectparametersassert.FunctionParameters(t, id)) - // - //// check that ShowParameters works too - //parameters, err := client.Functions.ShowParameters(ctx, id) - //require.NoError(t, err) - //assertParametersSet(t, objectparametersassert.FunctionParametersPrefetched(t, id, parameters)) }) t.Run("create function for Java - staged minimal", func(t *testing.T) { @@ -1300,11 +1291,6 @@ func TestInt_Functions(t *testing.T) { HasAllDefaultsExplicit(), ) }) -} - -func TestInt_OtherFunctions(t *testing.T) { - client := testClient(t) - ctx := testContext(t) assertFunction := func(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments, secure bool, withArguments bool) { t.Helper() @@ -1336,56 +1322,21 @@ func TestInt_OtherFunctions(t *testing.T) { assert.Equal(t, false, function.IsMemoizable) } - cleanupFunctionHandle := func(id sdk.SchemaObjectIdentifierWithArguments) func() { - return func() { - err := client.Functions.Drop(ctx, sdk.NewDropFunctionRequest(id)) - if errors.Is(err, sdk.ErrObjectNotExistOrAuthorized) { - return - } - require.NoError(t, err) - } - } - createFunctionForSQLHandle := func(t *testing.T, cleanup bool, withArguments bool) *sdk.Function { - t.Helper() - var id sdk.SchemaObjectIdentifierWithArguments - if withArguments { - id = testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) - } else { - id = testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments() - } - - definition := testClientHelper().Function.SampleSqlDefinition(t) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) - returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition). - WithOrReplace(true) - if withArguments { - argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeFloat) - request = request.WithArguments([]sdk.FunctionArgumentRequest{*argument}) - } - err := client.Functions.CreateForSQL(ctx, request) - require.NoError(t, err) - if cleanup { - t.Cleanup(cleanupFunctionHandle(id)) - } - function, err := client.Functions.ShowByID(ctx, id) - require.NoError(t, err) - return function + f, fCleanup := testClientHelper().Function.CreateSql(t) + t.Cleanup(fCleanup) + return f } t.Run("alter function: rename", func(t *testing.T) { - f := createFunctionForSQLHandle(t, false, true) + f, fCleanup := testClientHelper().Function.CreateSql(t) + t.Cleanup(fCleanup) id := f.ID() - nid := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) + nid := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(id.ArgumentDataTypes()...) err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithRenameTo(nid.SchemaObjectId())) - if err != nil { - t.Cleanup(cleanupFunctionHandle(id)) - } else { - t.Cleanup(cleanupFunctionHandle(nid)) - } require.NoError(t, err) + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, nid)) _, err = client.Functions.ShowByID(ctx, id) assert.ErrorIs(t, err, collections.ErrObjectNotFound) @@ -1395,58 +1346,149 @@ func TestInt_OtherFunctions(t *testing.T) { require.Equal(t, nid.Name(), e.Name) }) - t.Run("alter function: set log level", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) + t.Run("alter function: set and unset all for Java", func(t *testing.T) { + f, fCleanup := testClientHelper().Function.CreateJava(t) + t.Cleanup(fCleanup) id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSet(*sdk.NewFunctionSetRequest().WithLogLevel(sdk.LogLevelDebug))) + + assertions.AssertThatObject(t, objectassert.Function(t, id). + HasName(id.Name()). + HasDescription(sdk.DefaultFunctionComment), + ) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + + request := sdk.NewAlterFunctionRequest(id).WithSet(*sdk.NewFunctionSetRequest(). + WithEnableConsoleOutput(true). + WithExternalAccessIntegrations([]sdk.AccountObjectIdentifier{externalAccessIntegration}). + WithSecretsList(*sdk.NewSecretsListRequest([]sdk.SecretReference{{VariableName: "abc", Name: secretId}})). + WithLogLevel(sdk.LogLevelWarn). + WithMetricLevel(sdk.MetricLevelAll). + WithTraceLevel(sdk.TraceLevelAlways). + WithComment("new comment"), + ) + + err := client.Functions.Alter(ctx, request) require.NoError(t, err) - assertFunction(t, id, false, true) - }) - t.Run("alter function: unset log level", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) + assertions.AssertThatObject(t, objectassert.Function(t, id). + HasName(id.Name()). + HasDescription("new comment"), + ) - id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnset(*sdk.NewFunctionUnsetRequest().WithLogLevel(true))) + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())), + ) + + assertParametersSet(t, objectparametersassert.FunctionParameters(t, id)) + + unsetRequest := sdk.NewAlterFunctionRequest(id).WithUnset(*sdk.NewFunctionUnsetRequest(). + WithEnableConsoleOutput(true). + WithExternalAccessIntegrations(true). + WithEnableConsoleOutput(true). + WithLogLevel(true). + WithMetricLevel(true). + WithTraceLevel(true). + WithComment(true), + ) + + err = client.Functions.Alter(ctx, unsetRequest) require.NoError(t, err) - assertFunction(t, id, false, true) - }) - t.Run("alter function: set trace level", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) + assertions.AssertThatObject(t, objectassert.Function(t, id). + HasName(id.Name()). + HasDescription(sdk.DefaultFunctionComment), + ) - id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSet(*sdk.NewFunctionSetRequest().WithTraceLevel(sdk.TraceLevelAlways))) + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). + HasExternalAccessIntegrationsNil(). + // TODO [next PR]: apparently UNSET external access integrations cleans out secrets in the describe but leaves it in SHOW + HasSecretsNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + + unsetSecretsRequest := sdk.NewAlterFunctionRequest(id).WithSet(*sdk.NewFunctionSetRequest(). + WithSecretsList(*sdk.NewSecretsListRequest([]sdk.SecretReference{})), + ) + + err = client.Functions.Alter(ctx, unsetSecretsRequest) require.NoError(t, err) - assertFunction(t, id, false, true) + + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). + HasSecretsNil(), + ) }) - t.Run("alter function: unset trace level", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) + t.Run("alter function: set and unset all for SQL", func(t *testing.T) { + f, fCleanup := testClientHelper().Function.CreateSql(t) + t.Cleanup(fCleanup) id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnset(*sdk.NewFunctionUnsetRequest().WithTraceLevel(true))) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + + request := sdk.NewAlterFunctionRequest(id).WithSet(*sdk.NewFunctionSetRequest(). + WithEnableConsoleOutput(true). + WithLogLevel(sdk.LogLevelWarn). + WithMetricLevel(sdk.MetricLevelAll). + WithTraceLevel(sdk.TraceLevelAlways). + WithComment("new comment"), + ) + + err := client.Functions.Alter(ctx, request) require.NoError(t, err) - assertFunction(t, id, false, true) - }) - t.Run("alter function: set comment", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) + assertions.AssertThatObject(t, objectassert.Function(t, id). + HasName(id.Name()). + HasDescription("new comment"), + ) - id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSet(*sdk.NewFunctionSetRequest().WithComment("test comment"))) + // TODO [this PR]: add a test documenting that we can't set parameters in create (and revert adding these parameters directly in object...) + assertParametersSet(t, objectparametersassert.FunctionParameters(t, id)) + + // check that ShowParameters works too + parameters, err := client.Functions.ShowParameters(ctx, id) require.NoError(t, err) - assertFunction(t, id, false, true) - }) + assertParametersSet(t, objectparametersassert.FunctionParametersPrefetched(t, id, parameters)) - t.Run("alter function: unset comment", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) + unsetRequest := sdk.NewAlterFunctionRequest(id).WithUnset(*sdk.NewFunctionUnsetRequest(). + WithEnableConsoleOutput(true). + WithLogLevel(true). + WithMetricLevel(true). + WithTraceLevel(true). + WithComment(true), + ) - id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnset(*sdk.NewFunctionUnsetRequest().WithComment(true))) + err = client.Functions.Alter(ctx, unsetRequest) require.NoError(t, err) - assertFunction(t, id, false, true) + + assertions.AssertThatObject(t, objectassert.Function(t, id). + HasCreatedOnNotEmpty(). + HasName(id.Name()). + HasDescription(sdk.DefaultFunctionComment), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) }) t.Run("alter function: set secure", func(t *testing.T) { From e24a38d16553edc7ba160bf9b14d5ad2fcaa8b79 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 15:18:21 +0100 Subject: [PATCH 54/63] Add describe assertions --- pkg/acceptance/helpers/function_client.go | 22 +++ pkg/sdk/testint/functions_integration_test.go | 158 +++++++++--------- 2 files changed, 102 insertions(+), 78 deletions(-) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index e75516237d..ebaedddc3c 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -81,6 +81,28 @@ func (c *FunctionClient) CreateSql(t *testing.T) (*sdk.Function, func()) { return function, c.DropFunctionFunc(t, id) } +func (c *FunctionClient) CreateSqlNoArgs(t *testing.T) (*sdk.Function, func()) { + t.Helper() + ctx := context.Background() + + dataType := testdatatypes.DataTypeFloat + id := c.ids.RandomSchemaObjectIdentifierWithArguments() + + definition := c.SampleSqlDefinition(t) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + request := sdk.NewCreateForSQLFunctionRequestDefinitionWrapped(id.SchemaObjectId(), *returns, definition) + + err := c.client().CreateForSQL(ctx, request) + require.NoError(t, err) + t.Cleanup(c.DropFunctionFunc(t, id)) + + function, err := c.client().ShowByID(ctx, id) + require.NoError(t, err) + + return function, c.DropFunctionFunc(t, id) +} + func (c *FunctionClient) CreateJava(t *testing.T) (*sdk.Function, func()) { t.Helper() ctx := context.Background() diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 6a91000d34..79375982e2 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -1292,47 +1292,11 @@ func TestInt_Functions(t *testing.T) { ) }) - assertFunction := func(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments, secure bool, withArguments bool) { - t.Helper() - - function, err := client.Functions.ShowByID(ctx, id) - require.NoError(t, err) - - assert.NotEmpty(t, function.CreatedOn) - assert.Equal(t, id.Name(), function.Name) - assert.Equal(t, false, function.IsBuiltin) - assert.Equal(t, false, function.IsAggregate) - assert.Equal(t, false, function.IsAnsi) - if withArguments { - assert.Equal(t, 1, function.MinNumArguments) - assert.Equal(t, 1, function.MaxNumArguments) - } else { - assert.Equal(t, 0, function.MinNumArguments) - assert.Equal(t, 0, function.MaxNumArguments) - } - assert.NotEmpty(t, function.ArgumentsRaw) - assert.NotEmpty(t, function.ArgumentsOld) - assert.NotEmpty(t, function.Description) - assert.NotEmpty(t, function.CatalogName) - assert.Equal(t, false, function.IsTableFunction) - assert.Equal(t, false, function.ValidForClustering) - assert.Equal(t, secure, function.IsSecure) - assert.Equal(t, false, function.IsExternalFunction) - assert.Equal(t, "SQL", function.Language) - assert.Equal(t, false, function.IsMemoizable) - } - - createFunctionForSQLHandle := func(t *testing.T, cleanup bool, withArguments bool) *sdk.Function { - f, fCleanup := testClientHelper().Function.CreateSql(t) - t.Cleanup(fCleanup) - return f - } - t.Run("alter function: rename", func(t *testing.T) { f, fCleanup := testClientHelper().Function.CreateSql(t) t.Cleanup(fCleanup) - id := f.ID() + nid := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(id.ArgumentDataTypes()...) err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithRenameTo(nid.SchemaObjectId())) require.NoError(t, err) @@ -1349,7 +1313,6 @@ func TestInt_Functions(t *testing.T) { t.Run("alter function: set and unset all for Java", func(t *testing.T) { f, fCleanup := testClientHelper().Function.CreateJava(t) t.Cleanup(fCleanup) - id := f.ID() assertions.AssertThatObject(t, objectassert.Function(t, id). @@ -1436,7 +1399,6 @@ func TestInt_Functions(t *testing.T) { t.Run("alter function: set and unset all for SQL", func(t *testing.T) { f, fCleanup := testClientHelper().Function.CreateSql(t) t.Cleanup(fCleanup) - id := f.ID() assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). @@ -1491,35 +1453,36 @@ func TestInt_Functions(t *testing.T) { ) }) - t.Run("alter function: set secure", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) - + t.Run("alter function: set and unset secure", func(t *testing.T) { + f, fCleanup := testClientHelper().Function.CreateSql(t) + t.Cleanup(fCleanup) id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSetSecure(true)) - require.NoError(t, err) - assertFunction(t, id, true, true) - }) - t.Run("alter function: set secure with no arguments", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) - id := f.ID() + assertions.AssertThatObject(t, objectassert.FunctionFromObject(t, f). + HasIsSecure(false), + ) + err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSetSecure(true)) require.NoError(t, err) - assertFunction(t, id, true, true) - }) - t.Run("alter function: unset secure", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) + assertions.AssertThatObject(t, objectassert.Function(t, id). + HasIsSecure(true), + ) - id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnsetSecure(true)) + err = client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnsetSecure(true)) require.NoError(t, err) - assertFunction(t, id, false, true) + + assertions.AssertThatObject(t, objectassert.Function(t, id). + HasIsSecure(false), + ) }) - t.Run("show function for SQL: without like", func(t *testing.T) { - f1 := createFunctionForSQLHandle(t, true, true) - f2 := createFunctionForSQLHandle(t, true, true) + t.Run("show function: without like", func(t *testing.T) { + f1, fCleanup := testClientHelper().Function.CreateSql(t) + t.Cleanup(fCleanup) + + f2, fCleanup2 := testClientHelper().Function.CreateSql(t) + t.Cleanup(fCleanup2) functions, err := client.Functions.Show(ctx, sdk.NewShowFunctionRequest()) require.NoError(t, err) @@ -1528,9 +1491,12 @@ func TestInt_Functions(t *testing.T) { require.Contains(t, functions, *f2) }) - t.Run("show function for SQL: with like", func(t *testing.T) { - f1 := createFunctionForSQLHandle(t, true, true) - f2 := createFunctionForSQLHandle(t, true, true) + t.Run("show function: with like", func(t *testing.T) { + f1, fCleanup := testClientHelper().Function.CreateSql(t) + t.Cleanup(fCleanup) + + f2, fCleanup2 := testClientHelper().Function.CreateSql(t) + t.Cleanup(fCleanup2) functions, err := client.Functions.Show(ctx, sdk.NewShowFunctionRequest().WithLike(sdk.Like{Pattern: &f1.Name})) require.NoError(t, err) @@ -1540,40 +1506,76 @@ func TestInt_Functions(t *testing.T) { require.NotContains(t, functions, *f2) }) - t.Run("show function for SQL: no matches", func(t *testing.T) { - functions, err := client.Functions.Show(ctx, sdk.NewShowFunctionRequest().WithLike(sdk.Like{Pattern: sdk.String("non-existing-id-pattern")})) + t.Run("show function: no matches", func(t *testing.T) { + functions, err := client.Functions.Show(ctx, sdk.NewShowFunctionRequest(). + WithIn(sdk.ExtendedIn{In: sdk.In{Schema: testClientHelper().Ids.SchemaId()}}). + WithLike(sdk.Like{Pattern: sdk.String(NonExistingSchemaObjectIdentifier.Name())})) require.NoError(t, err) require.Equal(t, 0, len(functions)) }) - t.Run("describe function for SQL", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) + t.Run("describe function: for Java - minimal", func(t *testing.T) { + f, fCleanup := testClientHelper().Function.CreateJava(t) + t.Cleanup(fCleanup) + id := f.ID() + + details, err := client.Functions.Describe(ctx, id) + require.NoError(t, err) + assert.Len(t, details, 11) + + pairs := make(map[string]*string) + for _, detail := range details { + pairs[detail.Property] = detail.Value + } + assert.Equal(t, "(x VARCHAR)", *pairs["signature"]) + assert.Equal(t, "VARCHAR(100)", *pairs["returns"]) + assert.Equal(t, "JAVA", *pairs["language"]) + assert.NotEmpty(t, *pairs["body"]) + assert.Equal(t, string(sdk.NullInputBehaviorCalledOnNullInput), *pairs["null handling"]) + assert.Equal(t, string(sdk.VolatileTableKind), *pairs["volatility"]) + assert.Nil(t, pairs["external_access_integration"]) + assert.Nil(t, pairs["secrets"]) + assert.Equal(t, "[]", *pairs["imports"]) + assert.Equal(t, "TestFunc.echoVarchar", *pairs["handler"]) + assert.Nil(t, pairs["runtime_version"]) + }) + + t.Run("describe function: for SQL - with arguments", func(t *testing.T) { + f, fCleanup := testClientHelper().Function.CreateSql(t) + t.Cleanup(fCleanup) + id := f.ID() - details, err := client.Functions.Describe(ctx, f.ID()) + details, err := client.Functions.Describe(ctx, id) require.NoError(t, err) + assert.Len(t, details, 4) + pairs := make(map[string]string) for _, detail := range details { pairs[detail.Property] = *detail.Value } - require.Equal(t, "SQL", pairs["language"]) - require.Equal(t, "FLOAT", pairs["returns"]) - require.Equal(t, "3.141592654::FLOAT", pairs["body"]) - require.Equal(t, "(X FLOAT)", pairs["signature"]) + assert.Equal(t, "(x FLOAT)", pairs["signature"]) + assert.Equal(t, "FLOAT", pairs["returns"]) + assert.Equal(t, "SQL", pairs["language"]) + assert.Equal(t, "3.141592654::FLOAT", pairs["body"]) }) - t.Run("describe function for SQL: no arguments", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, false) + t.Run("describe function: for SQL - no arguments", func(t *testing.T) { + f, fCleanup := testClientHelper().Function.CreateSqlNoArgs(t) + t.Cleanup(fCleanup) + id := f.ID() - details, err := client.Functions.Describe(ctx, f.ID()) + details, err := client.Functions.Describe(ctx, id) require.NoError(t, err) + assert.Len(t, details, 4) + pairs := make(map[string]string) for _, detail := range details { pairs[detail.Property] = *detail.Value } - require.Equal(t, "SQL", pairs["language"]) - require.Equal(t, "FLOAT", pairs["returns"]) - require.Equal(t, "3.141592654::FLOAT", pairs["body"]) - require.Equal(t, "()", pairs["signature"]) + assert.Equal(t, "()", pairs["signature"]) + assert.Equal(t, "FLOAT", pairs["returns"]) + assert.Equal(t, "SQL", pairs["language"]) + assert.Equal(t, "3.141592654::FLOAT", pairs["body"]) }) } From 1bb7685d8bb55f56cecb6de85a13b1d62afa2877 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 15:43:42 +0100 Subject: [PATCH 55/63] Pass function integration tests --- pkg/acceptance/helpers/function_client.go | 21 ++++--- pkg/sdk/testint/functions_integration_test.go | 62 ++++--------------- 2 files changed, 26 insertions(+), 57 deletions(-) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index ebaedddc3c..3d7d62f7f8 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -7,6 +7,7 @@ import ( "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/testdatatypes" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk" + "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" "github.com/stretchr/testify/require" ) @@ -35,7 +36,7 @@ func (c *FunctionClient) CreateWithIdentifier(t *testing.T, id sdk.SchemaObjectI t.Helper() return c.CreateWithRequest(t, id, - sdk.NewCreateForSQLFunctionRequest( + sdk.NewCreateForSQLFunctionRequestDefinitionWrapped( id.SchemaObjectId(), *sdk.NewFunctionReturnsRequest().WithResultDataType(*sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeInt)), "SELECT 1", @@ -49,7 +50,7 @@ func (c *FunctionClient) CreateSecure(t *testing.T, arguments ...sdk.DataType) * id := c.ids.RandomSchemaObjectIdentifierWithArguments(arguments...) return c.CreateWithRequest(t, id, - sdk.NewCreateForSQLFunctionRequest( + sdk.NewCreateForSQLFunctionRequestDefinitionWrapped( id.SchemaObjectId(), *sdk.NewFunctionReturnsRequest().WithResultDataType(*sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeInt)), "SELECT 1", @@ -58,27 +59,31 @@ func (c *FunctionClient) CreateSecure(t *testing.T, arguments ...sdk.DataType) * } func (c *FunctionClient) CreateSql(t *testing.T) (*sdk.Function, func()) { + dataType := testdatatypes.DataTypeFloat + id := c.ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + return c.CreateSqlWithIdentifierAndArgument(t, id.SchemaObjectId(), dataType) +} + +func (c *FunctionClient) CreateSqlWithIdentifierAndArgument(t *testing.T, id sdk.SchemaObjectIdentifier, dataType datatypes.DataType) (*sdk.Function, func()) { t.Helper() ctx := context.Background() + idWithArgs := sdk.NewSchemaObjectIdentifierWithArgumentsInSchema(id.SchemaId(), id.Name(), sdk.LegacyDataTypeFrom(dataType)) argName := "x" - dataType := testdatatypes.DataTypeFloat - id := c.ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) - definition := c.SampleSqlDefinition(t) dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) argument := sdk.NewFunctionArgumentRequest(argName, dataType) - request := sdk.NewCreateForSQLFunctionRequestDefinitionWrapped(id.SchemaObjectId(), *returns, definition). + request := sdk.NewCreateForSQLFunctionRequestDefinitionWrapped(id, *returns, definition). WithArguments([]sdk.FunctionArgumentRequest{*argument}) err := c.client().CreateForSQL(ctx, request) require.NoError(t, err) - function, err := c.client().ShowByID(ctx, id) + function, err := c.client().ShowByID(ctx, idWithArgs) require.NoError(t, err) - return function, c.DropFunctionFunc(t, id) + return function, c.DropFunctionFunc(t, idWithArgs) } func (c *FunctionClient) CreateSqlNoArgs(t *testing.T) (*sdk.Function, func()) { diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 79375982e2..bfa5b95b74 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -2,7 +2,6 @@ package testint import ( "context" - "errors" "fmt" "strings" "testing" @@ -1577,46 +1576,19 @@ func TestInt_Functions(t *testing.T) { assert.Equal(t, "SQL", pairs["language"]) assert.Equal(t, "3.141592654::FLOAT", pairs["body"]) }) -} - -func TestInt_FunctionsShowByID(t *testing.T) { - client := testClient(t) - ctx := testContext(t) - - cleanupFunctionHandle := func(id sdk.SchemaObjectIdentifierWithArguments) func() { - return func() { - err := client.Functions.Drop(ctx, sdk.NewDropFunctionRequest(id)) - if errors.Is(err, sdk.ErrObjectNotExistOrAuthorized) { - return - } - require.NoError(t, err) - } - } - - createFunctionForSQLHandle := func(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments) { - t.Helper() - - definition := testClientHelper().Function.SampleSqlDefinition(t) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) - returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition).WithOrReplace(true) - - argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeFloat) - request = request.WithArguments([]sdk.FunctionArgumentRequest{*argument}) - err := client.Functions.CreateForSQL(ctx, request) - require.NoError(t, err) - t.Cleanup(cleanupFunctionHandle(id)) - } t.Run("show by id - same name in different schemas", func(t *testing.T) { schema, schemaCleanup := testClientHelper().Schema.CreateSchema(t) t.Cleanup(schemaCleanup) - id1 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) - id2 := testClientHelper().Ids.NewSchemaObjectIdentifierWithArgumentsInSchema(id1.Name(), schema.ID(), sdk.DataTypeFloat) + dataType := testdatatypes.DataTypeFloat + id1 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + id2 := testClientHelper().Ids.NewSchemaObjectIdentifierWithArgumentsInSchema(id1.Name(), schema.ID(), sdk.LegacyDataTypeFrom(dataType)) - createFunctionForSQLHandle(t, id1) - createFunctionForSQLHandle(t, id2) + _, fCleanup1 := testClientHelper().Function.CreateSqlWithIdentifierAndArgument(t, id1.SchemaObjectId(), dataType) + t.Cleanup(fCleanup1) + _, fCleanup2 := testClientHelper().Function.CreateSqlWithIdentifierAndArgument(t, id2.SchemaObjectId(), dataType) + t.Cleanup(fCleanup2) e1, err := client.Functions.ShowByID(ctx, id1) require.NoError(t, err) @@ -1633,21 +1605,13 @@ func TestInt_FunctionsShowByID(t *testing.T) { require.Equal(t, id2, e2Id) }) - t.Run("show function by id - different name, same arguments", func(t *testing.T) { - id1 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR) - id2 := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR) - e := testClientHelper().Function.CreateWithIdentifier(t, id1) - testClientHelper().Function.CreateWithIdentifier(t, id2) - - es, err := client.Functions.ShowByID(ctx, id1) - require.NoError(t, err) - require.Equal(t, *e, *es) - }) - t.Run("show function by id - same name, different arguments", func(t *testing.T) { + dataType := testdatatypes.DataTypeFloat name := testClientHelper().Ids.Alpha() - id1 := testClientHelper().Ids.NewSchemaObjectIdentifierWithArgumentsInSchema(name, testClientHelper().Ids.SchemaId(), sdk.DataTypeInt, sdk.DataTypeFloat, sdk.DataTypeVARCHAR) + + id1 := testClientHelper().Ids.NewSchemaObjectIdentifierWithArgumentsInSchema(name, testClientHelper().Ids.SchemaId(), sdk.LegacyDataTypeFrom(dataType)) id2 := testClientHelper().Ids.NewSchemaObjectIdentifierWithArgumentsInSchema(name, testClientHelper().Ids.SchemaId(), sdk.DataTypeInt, sdk.DataTypeVARCHAR) + e := testClientHelper().Function.CreateWithIdentifier(t, id1) testClientHelper().Function.CreateWithIdentifier(t, id2) @@ -1696,7 +1660,7 @@ func TestInt_FunctionsShowByID(t *testing.T) { "add", ). WithArguments(args). - WithFunctionDefinition("def add(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, R, S, T, U, V, W, X, Y, Z): A + A"), + WithFunctionDefinitionWrapped("def add(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, R, S, T, U, V, W, X, Y, Z): A + A"), ) require.NoError(t, err) @@ -1765,7 +1729,7 @@ func TestInt_FunctionsShowByID(t *testing.T) { funcName, ). WithArguments(args). - WithFunctionDefinition(testClientHelper().Function.PythonIdentityDefinition(t, funcName, argName)), + WithFunctionDefinitionWrapped(testClientHelper().Function.PythonIdentityDefinition(t, funcName, argName)), ) require.NoError(t, err) From 3b686f4357d270cbc376ec02895a8d0a05eb6c83 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 15:55:25 +0100 Subject: [PATCH 56/63] Fix runtime version for scala and add missing show outputs --- pkg/resources/function.go | 10 ++- pkg/sdk/functions_def.go | 7 +- pkg/sdk/functions_dto_builders_gen.go | 7 +- pkg/sdk/functions_dto_gen.go | 2 +- pkg/sdk/functions_gen.go | 81 ++++++++++--------- pkg/sdk/functions_impl_gen.go | 6 ++ pkg/sdk/testint/functions_integration_test.go | 13 ++- 7 files changed, 67 insertions(+), 59 deletions(-) diff --git a/pkg/resources/function.go b/pkg/resources/function.go index e37d44a58f..aab7456cdb 100644 --- a/pkg/resources/function.go +++ b/pkg/resources/function.go @@ -310,8 +310,13 @@ func createScalaFunction(ctx context.Context, d *schema.ResourceData, meta inter } functionDefinition := d.Get("statement").(string) handler := d.Get("handler").(string) + var runtimeVersion string + if v, ok := d.GetOk("runtime_version"); ok { + runtimeVersion = v.(string) + } + // create request with required - request := sdk.NewCreateForScalaFunctionRequest(id, nil, handler).WithResultDataTypeOld(sdk.LegacyDataTypeFrom(returnDataType)) + request := sdk.NewCreateForScalaFunctionRequest(id, nil, handler, runtimeVersion).WithResultDataTypeOld(sdk.LegacyDataTypeFrom(returnDataType)) request.WithFunctionDefinition(functionDefinition) // Set optionals @@ -331,9 +336,6 @@ func createScalaFunction(ctx context.Context, d *schema.ResourceData, meta inter if v, ok := d.GetOk("return_behavior"); ok { request.WithReturnResultsBehavior(sdk.ReturnResultsBehavior(v.(string))) } - if v, ok := d.GetOk("runtime_version"); ok { - request.WithRuntimeVersion(v.(string)) - } if v, ok := d.GetOk("comment"); ok { request.WithComment(v.(string)) } diff --git a/pkg/sdk/functions_def.go b/pkg/sdk/functions_def.go index 9da2f947d8..1c92cd4078 100644 --- a/pkg/sdk/functions_def.go +++ b/pkg/sdk/functions_def.go @@ -180,7 +180,6 @@ var FunctionsDef = g.NewInterface( WithValidation(g.ValidateValueSet, "Handler"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), ).CustomOperation( - // TODO [this PR]: runtime version required "CreateForScala", "https://docs.snowflake.com/en/sql-reference/sql/create-function#scala-handler", g.NewQueryStruct("CreateForScala"). @@ -203,7 +202,7 @@ var FunctionsDef = g.NewInterface( SQL("LANGUAGE SCALA"). PredefinedQueryStructField("NullInputBehavior", "*NullInputBehavior", g.KeywordOptions()). PredefinedQueryStructField("ReturnResultsBehavior", "*ReturnResultsBehavior", g.KeywordOptions()). - OptionalTextAssignment("RUNTIME_VERSION", g.ParameterOptions().SingleQuotes()). + TextAssignment("RUNTIME_VERSION", g.ParameterOptions().SingleQuotes().Required()). OptionalTextAssignment("COMMENT", g.ParameterOptions().SingleQuotes()). ListQueryStructField( "Imports", @@ -324,6 +323,8 @@ var FunctionsDef = g.NewInterface( Field("is_table_function", "string"). Field("valid_for_clustering", "string"). Field("is_secure", "sql.NullString"). + OptionalText("secrets"). + OptionalText("external_access_integrations"). Field("is_external_function", "string"). Field("language", "string"). Field("is_memoizable", "sql.NullString"). @@ -343,6 +344,8 @@ var FunctionsDef = g.NewInterface( Field("IsTableFunction", "bool"). Field("ValidForClustering", "bool"). Field("IsSecure", "bool"). + OptionalText("Secrets"). + OptionalText("ExternalAccessIntegrations"). Field("IsExternalFunction", "bool"). Field("Language", "string"). Field("IsMemoizable", "bool"). diff --git a/pkg/sdk/functions_dto_builders_gen.go b/pkg/sdk/functions_dto_builders_gen.go index d983ac9e30..7d0a49180a 100644 --- a/pkg/sdk/functions_dto_builders_gen.go +++ b/pkg/sdk/functions_dto_builders_gen.go @@ -408,11 +408,13 @@ func NewCreateForScalaFunctionRequest( name SchemaObjectIdentifier, ResultDataType datatypes.DataType, Handler string, + RuntimeVersion string, ) *CreateForScalaFunctionRequest { s := CreateForScalaFunctionRequest{} s.name = name s.ResultDataType = ResultDataType s.Handler = Handler + s.RuntimeVersion = RuntimeVersion return &s } @@ -466,11 +468,6 @@ func (s *CreateForScalaFunctionRequest) WithReturnResultsBehavior(ReturnResultsB return s } -func (s *CreateForScalaFunctionRequest) WithRuntimeVersion(RuntimeVersion string) *CreateForScalaFunctionRequest { - s.RuntimeVersion = &RuntimeVersion - return s -} - func (s *CreateForScalaFunctionRequest) WithComment(Comment string) *CreateForScalaFunctionRequest { s.Comment = &Comment return s diff --git a/pkg/sdk/functions_dto_gen.go b/pkg/sdk/functions_dto_gen.go index b5930cf6a2..14e86f6260 100644 --- a/pkg/sdk/functions_dto_gen.go +++ b/pkg/sdk/functions_dto_gen.go @@ -137,7 +137,7 @@ type CreateForScalaFunctionRequest struct { ReturnNullValues *ReturnNullValues NullInputBehavior *NullInputBehavior ReturnResultsBehavior *ReturnResultsBehavior - RuntimeVersion *string + RuntimeVersion string // required Comment *string Imports []FunctionImportRequest Packages []FunctionPackageRequest diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index a51e65a227..ad2ec72844 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -163,7 +163,7 @@ type CreateForScalaFunctionOptions struct { languageScala bool `ddl:"static" sql:"LANGUAGE SCALA"` NullInputBehavior *NullInputBehavior `ddl:"keyword"` ReturnResultsBehavior *ReturnResultsBehavior `ddl:"keyword"` - RuntimeVersion *string `ddl:"parameter,single_quotes" sql:"RUNTIME_VERSION"` + RuntimeVersion string `ddl:"parameter,single_quotes" sql:"RUNTIME_VERSION"` Comment *string `ddl:"parameter,single_quotes" sql:"COMMENT"` Imports []FunctionImport `ddl:"parameter,parentheses" sql:"IMPORTS"` Packages []FunctionPackage `ddl:"parameter,parentheses" sql:"PACKAGES"` @@ -255,47 +255,50 @@ type ShowFunctionOptions struct { } type functionRow struct { - CreatedOn string `db:"created_on"` - Name string `db:"name"` - SchemaName string `db:"schema_name"` - IsBuiltin string `db:"is_builtin"` - IsAggregate string `db:"is_aggregate"` - IsAnsi string `db:"is_ansi"` - MinNumArguments int `db:"min_num_arguments"` - MaxNumArguments int `db:"max_num_arguments"` - Arguments string `db:"arguments"` - Description string `db:"description"` - CatalogName string `db:"catalog_name"` - IsTableFunction string `db:"is_table_function"` - ValidForClustering string `db:"valid_for_clustering"` - IsSecure sql.NullString `db:"is_secure"` - IsExternalFunction string `db:"is_external_function"` - Language string `db:"language"` - IsMemoizable sql.NullString `db:"is_memoizable"` - IsDataMetric sql.NullString `db:"is_data_metric"` + CreatedOn string `db:"created_on"` + Name string `db:"name"` + SchemaName string `db:"schema_name"` + IsBuiltin string `db:"is_builtin"` + IsAggregate string `db:"is_aggregate"` + IsAnsi string `db:"is_ansi"` + MinNumArguments int `db:"min_num_arguments"` + MaxNumArguments int `db:"max_num_arguments"` + Arguments string `db:"arguments"` + Description string `db:"description"` + CatalogName string `db:"catalog_name"` + IsTableFunction string `db:"is_table_function"` + ValidForClustering string `db:"valid_for_clustering"` + IsSecure sql.NullString `db:"is_secure"` + Secrets sql.NullString `db:"secrets"` + ExternalAccessIntegrations sql.NullString `db:"external_access_integrations"` + IsExternalFunction string `db:"is_external_function"` + Language string `db:"language"` + IsMemoizable sql.NullString `db:"is_memoizable"` + IsDataMetric sql.NullString `db:"is_data_metric"` } type Function struct { - CreatedOn string - Name string - SchemaName string - IsBuiltin bool - IsAggregate bool - IsAnsi bool - MinNumArguments int - MaxNumArguments int - ArgumentsOld []DataType - ArgumentsRaw string - Description string - CatalogName string - IsTableFunction bool - ValidForClustering bool - IsSecure bool - // TODO [this PR]: eai + secrets - IsExternalFunction bool - Language string - IsMemoizable bool - IsDataMetric bool + CreatedOn string + Name string + SchemaName string + IsBuiltin bool + IsAggregate bool + IsAnsi bool + MinNumArguments int + MaxNumArguments int + ArgumentsOld []DataType + ArgumentsRaw string + Description string + CatalogName string + IsTableFunction bool + ValidForClustering bool + IsSecure bool + Secrets *string + ExternalAccessIntegrations *string + IsExternalFunction bool + Language string + IsMemoizable bool + IsDataMetric bool } // DescribeFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/desc-function. diff --git a/pkg/sdk/functions_impl_gen.go b/pkg/sdk/functions_impl_gen.go index 4a122ee2df..8d9dbd1606 100644 --- a/pkg/sdk/functions_impl_gen.go +++ b/pkg/sdk/functions_impl_gen.go @@ -493,6 +493,12 @@ func (r functionRow) convert() *Function { if r.IsSecure.Valid { e.IsSecure = r.IsSecure.String == "Y" } + if r.Secrets.Valid { + e.Secrets = String(r.Secrets.String) + } + if r.ExternalAccessIntegrations.Valid { + e.ExternalAccessIntegrations = String(r.ExternalAccessIntegrations.String) + } if r.IsMemoizable.Valid { e.IsMemoizable = r.IsMemoizable.String == "Y" } diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index bfa5b95b74..e200ffecf5 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -23,6 +23,7 @@ import ( // TODO [next PR]: schemaName and catalog name are quoted (because we use lowercase) // TODO [next PR]: HasArgumentsRawFrom(functionId, arguments, return) // TODO [next PR]: extract show assertions with commons fields +// TODO [next PR]: test confirming that runtime version is required for Scala function // TODO [this PR]: python aggregate func // TODO [this PR]: clean stage in tests with targetPath func TestInt_Functions(t *testing.T) { @@ -799,9 +800,8 @@ func TestInt_Functions(t *testing.T) { definition := testClientHelper().Function.SampleScalaDefinition(t, className, funcName, argName) argument := sdk.NewFunctionArgumentRequest(argName, dataType) handler := fmt.Sprintf("%s.%s", className, funcName) - request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler). + request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler, "2.12"). WithArguments([]sdk.FunctionArgumentRequest{*argument}). - WithRuntimeVersion("2.12"). WithFunctionDefinitionWrapped(definition) err := client.Functions.CreateForScala(ctx, request) @@ -870,14 +870,13 @@ func TestInt_Functions(t *testing.T) { handler := fmt.Sprintf("%s.%s", className, funcName) jarName := fmt.Sprintf("tf-%d-%s.jar", time.Now().Unix(), random.AlphaN(5)) targetPath := fmt.Sprintf("@~/%s", jarName) - request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler). + request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler, "2.12"). WithOrReplace(true). WithArguments([]sdk.FunctionArgumentRequest{*argument}). WithCopyGrants(true). WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). WithReturnNullValues(sdk.ReturnNullValuesNotNull). - WithRuntimeVersion("2.12"). WithComment("comment"). WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(tmpJavaFunction.JarLocation())}). WithPackages([]sdk.FunctionPackageRequest{ @@ -955,9 +954,8 @@ func TestInt_Functions(t *testing.T) { handler := tmpJavaFunction.JavaHandler() importPath := tmpJavaFunction.JarLocation() - requestStaged := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler). + requestStaged := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler, "2.12"). WithArguments([]sdk.FunctionArgumentRequest{*argument}). - WithRuntimeVersion("2.12"). WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(importPath)}) err := client.Functions.CreateForScala(ctx, requestStaged) @@ -1021,14 +1019,13 @@ func TestInt_Functions(t *testing.T) { argument := sdk.NewFunctionArgumentRequest(argName, dataType) handler := tmpJavaFunction.JavaHandler() - requestStaged := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler). + requestStaged := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler, "2.12"). WithOrReplace(true). WithArguments([]sdk.FunctionArgumentRequest{*argument}). WithCopyGrants(true). WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). WithReturnNullValues(sdk.ReturnNullValuesNotNull). - WithRuntimeVersion("2.12"). WithComment("comment"). WithPackages([]sdk.FunctionPackageRequest{ *sdk.NewFunctionPackageRequest().WithPackage("com.snowflake:snowpark:1.14.0"), From 3f37bd9fbd3ff1e573c64fbc1ad4f85d188af972 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 16:28:34 +0100 Subject: [PATCH 57/63] Solve TODOs --- .../objectassert/function_snowflake_ext.go | 11 +++ .../objectassert/function_snowflake_gen.go | 28 ++++++ pkg/sdk/parameters.go | 1 - pkg/sdk/testint/functions_integration_test.go | 95 ++++++++++++++++--- 4 files changed, 120 insertions(+), 15 deletions(-) diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_ext.go b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_ext.go index b73b07faef..66e4253d20 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_ext.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_ext.go @@ -17,3 +17,14 @@ func (a *FunctionAssert) HasCreatedOnNotEmpty() *FunctionAssert { }) return a } + +func (a *FunctionAssert) HasExternalAccessIntegrationsNil() *FunctionAssert { + a.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.ExternalAccessIntegrations != nil { + return fmt.Errorf("expected external_access_integrations to be nil but was: %v", *o.ExternalAccessIntegrations) + } + return nil + }) + return a +} diff --git a/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go index 62e9b961cc..8b6f674aa8 100644 --- a/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go @@ -198,6 +198,34 @@ func (f *FunctionAssert) HasIsSecure(expected bool) *FunctionAssert { return f } +func (f *FunctionAssert) HasSecrets(expected string) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.Secrets == nil { + return fmt.Errorf("expected secrets to have value; got: nil") + } + if *o.Secrets != expected { + return fmt.Errorf("expected secrets: %v; got: %v", expected, *o.Secrets) + } + return nil + }) + return f +} + +func (f *FunctionAssert) HasExternalAccessIntegrations(expected string) *FunctionAssert { + f.AddAssertion(func(t *testing.T, o *sdk.Function) error { + t.Helper() + if o.ExternalAccessIntegrations == nil { + return fmt.Errorf("expected external access integrations to have value; got: nil") + } + if *o.ExternalAccessIntegrations != expected { + return fmt.Errorf("expected external access integrations: %v; got: %v", expected, *o.ExternalAccessIntegrations) + } + return nil + }) + return f +} + func (f *FunctionAssert) HasIsExternalFunction(expected bool) *FunctionAssert { f.AddAssertion(func(t *testing.T, o *sdk.Function) error { t.Helper() diff --git a/pkg/sdk/parameters.go b/pkg/sdk/parameters.go index f4748fb450..b651b673f3 100644 --- a/pkg/sdk/parameters.go +++ b/pkg/sdk/parameters.go @@ -1489,7 +1489,6 @@ func (v *parameters) ShowUserParameter(ctx context.Context, parameter UserParame return parameters[0], nil } -// TODO [this PR]: test for functions func (v *parameters) ShowObjectParameter(ctx context.Context, parameter ObjectParameter, object Object) (*Parameter, error) { opts := &ShowParametersOptions{ Like: &Like{ diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index e200ffecf5..64ed0fe9f2 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -24,8 +24,14 @@ import ( // TODO [next PR]: HasArgumentsRawFrom(functionId, arguments, return) // TODO [next PR]: extract show assertions with commons fields // TODO [next PR]: test confirming that runtime version is required for Scala function -// TODO [this PR]: python aggregate func +// TODO [next PR]: test create or replace with name change, args change +// TODO [next PR]: test rename more (arg stays, can't change arg, rename to different schema) +// TODO [next PR]: add test documenting that UNSET SECRETS does not work +// TODO [next PR]: add test documenting [JAVA]: 391516 (42601): SQL compilation error: Cannot specify TARGET_PATH without a function BODY. +// TODO [next PR]: test secure +// TODO [next PR]: python aggregate func (100357 (P0000): Could not find accumulate method in function CVVEMHIT_06547800_08D6_DBCA_1AC7_5E422AFF8B39 with handler dump) // TODO [this PR]: clean stage in tests with targetPath +// TODO [next PR]: add a test documenting that we can't set parameters in create (and revert adding these parameters directly in object...) func TestInt_Functions(t *testing.T) { client := testClient(t) ctx := context.Background() @@ -92,6 +98,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). HasIsExternalFunction(false). HasLanguage("JAVA"). HasIsMemoizable(false). @@ -183,6 +191,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). HasIsExternalFunction(false). HasLanguage("JAVA"). HasIsMemoizable(false). @@ -197,8 +207,8 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). - // TODO [this PR]: parse to identifier list - // TODO [this PR]: check multiple secrets (to know how to parse) + // TODO [next PR]: parse to identifier list + // TODO [next PR]: check multiple secrets (to know how to parse) HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasHandler(handler). @@ -253,6 +263,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). HasIsExternalFunction(false). HasLanguage("JAVA"). HasIsMemoizable(false). @@ -333,6 +345,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). HasIsExternalFunction(false). HasLanguage("JAVA"). HasIsMemoizable(false). @@ -399,6 +413,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). HasIsExternalFunction(false). HasLanguage("JAVASCRIPT"). HasIsMemoizable(false). @@ -470,6 +486,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). HasIsExternalFunction(false). HasLanguage("JAVASCRIPT"). HasIsMemoizable(false). @@ -537,6 +555,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). HasIsExternalFunction(false). HasLanguage("PYTHON"). HasIsMemoizable(false). @@ -545,7 +565,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). - HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). //TODO [this PR]: do we care about this whitespace? + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). //TODO [next PR]: do we care about this whitespace? HasLanguage("PYTHON"). HasBody(definition). HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). @@ -617,6 +637,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). HasIsExternalFunction(false). HasLanguage("PYTHON"). HasIsMemoizable(false). @@ -625,7 +647,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). - HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")+" NOT NULL"). //TODO [this PR]: do we care about this whitespace? + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")+" NOT NULL"). //TODO [next PR]: do we care about this whitespace? HasLanguage("PYTHON"). HasBody(definition). HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). @@ -682,6 +704,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). HasIsExternalFunction(false). HasLanguage("PYTHON"). HasIsMemoizable(false). @@ -690,7 +714,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). - HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). //TODO [this PR]: do we care about this whitespace? + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). //TODO [next PR]: do we care about this whitespace? HasLanguage("PYTHON"). HasBodyNil(). HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). @@ -759,6 +783,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). HasIsExternalFunction(false). HasLanguage("PYTHON"). HasIsMemoizable(false). @@ -767,7 +793,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). - HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")+" NOT NULL"). //TODO [this PR]: do we care about this whitespace? + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")+" NOT NULL"). //TODO [next PR]: do we care about this whitespace? HasLanguage("PYTHON"). HasBodyNil(). HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). @@ -827,6 +853,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). HasIsExternalFunction(false). HasLanguage("SCALA"). HasIsMemoizable(false). @@ -915,6 +943,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). HasIsExternalFunction(false). HasLanguage("SCALA"). HasIsMemoizable(false). @@ -981,6 +1011,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). HasIsExternalFunction(false). HasLanguage("SCALA"). HasIsMemoizable(false). @@ -1059,6 +1091,8 @@ func TestInt_Functions(t *testing.T) { HasValidForClustering(false). HasIsSecure(false). HasIsExternalFunction(false). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). HasLanguage("SCALA"). HasIsMemoizable(false). HasIsDataMetric(false), @@ -1123,6 +1157,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). HasIsExternalFunction(false). HasLanguage("SQL"). HasIsMemoizable(false). @@ -1194,6 +1230,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). HasIsExternalFunction(false). HasLanguage("SQL"). HasIsMemoizable(true). @@ -1258,6 +1296,8 @@ func TestInt_Functions(t *testing.T) { HasIsTableFunction(false). HasValidForClustering(false). HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). HasIsExternalFunction(false). HasLanguage("SQL"). HasIsMemoizable(false). @@ -1288,6 +1328,37 @@ func TestInt_Functions(t *testing.T) { ) }) + t.Run("show parameters", func(t *testing.T) { + f, fCleanup := testClientHelper().Function.CreateSql(t) + t.Cleanup(fCleanup) + id := f.ID() + + param, err := client.Parameters.ShowObjectParameter(ctx, sdk.ObjectParameterLogLevel, sdk.Object{ObjectType: sdk.ObjectTypeFunction, Name: id}) + require.NoError(t, err) + assert.Equal(t, string(sdk.LogLevelOff), param.Value) + + parameters, err := client.Parameters.ShowParameters(ctx, &sdk.ShowParametersOptions{ + In: &sdk.ParametersIn{ + Function: id, + }, + }) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParametersPrefetched(t, id, parameters). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + + // check that ShowParameters on function level works too + parameters, err = client.Functions.ShowParameters(ctx, id) + require.NoError(t, err) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParametersPrefetched(t, id, parameters). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + t.Run("alter function: rename", func(t *testing.T) { f, fCleanup := testClientHelper().Function.CreateSql(t) t.Cleanup(fCleanup) @@ -1366,7 +1437,9 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.Function(t, id). HasName(id.Name()). - HasDescription(sdk.DefaultFunctionComment), + HasDescription(sdk.DefaultFunctionComment). + HasExternalAccessIntegrations("[]"). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())), ) assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). @@ -1418,14 +1491,8 @@ func TestInt_Functions(t *testing.T) { HasDescription("new comment"), ) - // TODO [this PR]: add a test documenting that we can't set parameters in create (and revert adding these parameters directly in object...) assertParametersSet(t, objectparametersassert.FunctionParameters(t, id)) - // check that ShowParameters works too - parameters, err := client.Functions.ShowParameters(ctx, id) - require.NoError(t, err) - assertParametersSet(t, objectparametersassert.FunctionParametersPrefetched(t, id, parameters)) - unsetRequest := sdk.NewAlterFunctionRequest(id).WithUnset(*sdk.NewFunctionUnsetRequest(). WithEnableConsoleOutput(true). WithLogLevel(true). From 474e42de4668bea0d8412ea9260a92d49abe0ba1 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 16:30:09 +0100 Subject: [PATCH 58/63] Remove target path in appropriate tests --- pkg/sdk/testint/functions_integration_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 64ed0fe9f2..79fdd2922d 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -30,7 +30,6 @@ import ( // TODO [next PR]: add test documenting [JAVA]: 391516 (42601): SQL compilation error: Cannot specify TARGET_PATH without a function BODY. // TODO [next PR]: test secure // TODO [next PR]: python aggregate func (100357 (P0000): Could not find accumulate method in function CVVEMHIT_06547800_08D6_DBCA_1AC7_5E422AFF8B39 with handler dump) -// TODO [this PR]: clean stage in tests with targetPath // TODO [next PR]: add a test documenting that we can't set parameters in create (and revert adding these parameters directly in object...) func TestInt_Functions(t *testing.T) { client := testClient(t) @@ -171,6 +170,7 @@ func TestInt_Functions(t *testing.T) { err := client.Functions.CreateForJava(ctx, request) require.NoError(t, err) t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + t.Cleanup(testClientHelper().Stage.RemoveFromUserStageFunc(t, targetPath)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) @@ -923,6 +923,7 @@ func TestInt_Functions(t *testing.T) { err := client.Functions.CreateForScala(ctx, request) require.NoError(t, err) t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + t.Cleanup(testClientHelper().Stage.RemoveFromUserStageFunc(t, targetPath)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) From b5123b1a8890ce580b6a161d7153e7038a117747 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 16:41:21 +0100 Subject: [PATCH 59/63] Clean up TODOs --- pkg/acceptance/helpers/function_client.go | 2 +- pkg/sdk/functions_ext.go | 23 ----------- pkg/sdk/testint/functions_integration_test.go | 40 ++++++++++--------- pkg/sdk/testint/setup_test.go | 2 +- 4 files changed, 23 insertions(+), 44 deletions(-) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 3d7d62f7f8..06c008b66e 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -44,7 +44,7 @@ func (c *FunctionClient) CreateWithIdentifier(t *testing.T, id sdk.SchemaObjectI ) } -// TODO [next PR]: improve this helper (all other types creation) +// TODO [SNOW-1850370]: improve this helper (all other types creation) func (c *FunctionClient) CreateSecure(t *testing.T, arguments ...sdk.DataType) *sdk.Function { t.Helper() id := c.ids.RandomSchemaObjectIdentifierWithArguments(arguments...) diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 151e5d39c2..7e16a54c03 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -147,26 +147,3 @@ func NewCreateForJavascriptFunctionRequestDefinitionWrapped( s.FunctionDefinition = fmt.Sprintf(`$$%s$$`, FunctionDefinition) return &s } - -// CreateForJavaFunctionOptions -// TODO [SNOW-1348103 - this PR]: test setting the paths for all types (like imports, target paths) -// TODO [SNOW-1348103 - this PR]: test weird names for arg name - lower/upper if used with double quotes, to upper without quotes, dots, spaces, and both quotes not permitted -// TODO [SNOW-1348103 - next PRs]: check data type mappings https://docs.snowflake.com/en/sql-reference/sql/create-function#all-languages (signature + returns) -// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 11.x, 17.x supported, 11.x being the default) -// TODO [SNOW-1348103 - this PR]: packages: package_name:version_number; do we validate? - check SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'java'; -// TODO [SNOW-1348103 - next PRs]: add to the resource docs https://docs.snowflake.com/en/sql-reference/sql/create-function#access-control-requirements -// TODO [SNOW-1348103 - this PR]: what delimiter do we use for : ' versus $$? - we use $$ as tasks -// TODO [SNOW-1348103 - this PR]: escaping single quotes test - don't have to do this with $$ -// TODO [SNOW-1348103 - this PR]: validation of JAR (check https://docs.snowflake.com/en/sql-reference/sql/create-function#id6) -// TODO [SNOW-1348103 - next PRs]: active warehouse vs validations -// TODO [SNOW-1348103 - this PR]: check creation of all functions (using examples and more) - -// CreateForPythonFunctionOptions -// TODO [SNOW-1348103 - this PR]: test aggregate func creation -// TODO [SNOW-1348103 - this PR]: what about [==] - SDK level or resource level? check also: SELECT * FROM INFORMATION_SCHEMA.PACKAGES WHERE LANGUAGE = 'python'; -// TODO [SNOW-1348103 - this PR]: what about preview feature >= ? -// TODO [SNOW-1348103 - this PR]: what about '.' for non-inline functions? -// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 3.8, 3.9, 3.10, 3.11 supported, which one is a default?) - -// CreateForScalaFunctionOptions -// TODO [SNOW-1348103 - this PR]: setting RUNTIME_VERSION (only 2.12 supported, which is the default) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 79fdd2922d..b18d3f9e17 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -20,17 +20,19 @@ import ( "github.com/stretchr/testify/require" ) -// TODO [next PR]: schemaName and catalog name are quoted (because we use lowercase) -// TODO [next PR]: HasArgumentsRawFrom(functionId, arguments, return) -// TODO [next PR]: extract show assertions with commons fields -// TODO [next PR]: test confirming that runtime version is required for Scala function -// TODO [next PR]: test create or replace with name change, args change -// TODO [next PR]: test rename more (arg stays, can't change arg, rename to different schema) -// TODO [next PR]: add test documenting that UNSET SECRETS does not work -// TODO [next PR]: add test documenting [JAVA]: 391516 (42601): SQL compilation error: Cannot specify TARGET_PATH without a function BODY. -// TODO [next PR]: test secure -// TODO [next PR]: python aggregate func (100357 (P0000): Could not find accumulate method in function CVVEMHIT_06547800_08D6_DBCA_1AC7_5E422AFF8B39 with handler dump) -// TODO [next PR]: add a test documenting that we can't set parameters in create (and revert adding these parameters directly in object...) +// TODO [SNOW-1348103]: schemaName and catalog name are quoted (because we use lowercase) +// TODO [SNOW-1850370]: HasArgumentsRawFrom(functionId, arguments, return) +// TODO [SNOW-1850370]: extract show assertions with commons fields +// TODO [SNOW-1850370]: test confirming that runtime version is required for Scala function +// TODO [SNOW-1348103 or SNOW-1850370]: test create or replace with name change, args change +// TODO [SNOW-1348103]: test rename more (arg stays, can't change arg, rename to different schema) +// TODO [SNOW-1348103]: test weird names for arg name - lower/upper if used with double quotes, to upper without quotes, dots, spaces, and both quotes not permitted +// TODO [SNOW-1850370]: add test documenting that UNSET SECRETS does not work +// TODO [SNOW-1850370]: add test documenting [JAVA]: 391516 (42601): SQL compilation error: Cannot specify TARGET_PATH without a function BODY. +// TODO [SNOW-1348103 or SNOW-1850370]: test secure +// TODO [SNOW-1348103]: python aggregate func (100357 (P0000): Could not find accumulate method in function CVVEMHIT_06547800_08D6_DBCA_1AC7_5E422AFF8B39 with handler dump) +// TODO [SNOW-1348103]: add a test documenting that we can't set parameters in create (and revert adding these parameters directly in object...) +// TODO [SNOW-1850370]: active warehouse vs validations func TestInt_Functions(t *testing.T) { client := testClient(t) ctx := context.Background() @@ -207,8 +209,8 @@ func TestInt_Functions(t *testing.T) { HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). - // TODO [next PR]: parse to identifier list - // TODO [next PR]: check multiple secrets (to know how to parse) + // TODO [SNOW-1348103]: parse to identifier list + // TODO [SNOW-1348103]: check multiple secrets (to know how to parse) HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())). HasImports(fmt.Sprintf(`[%s]`, tmpJavaFunction.JarLocation())). HasHandler(handler). @@ -565,7 +567,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). - HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). //TODO [next PR]: do we care about this whitespace? + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). //TODO [SNOW-1348103]: do we care about this whitespace? HasLanguage("PYTHON"). HasBody(definition). HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). @@ -647,7 +649,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). - HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")+" NOT NULL"). //TODO [next PR]: do we care about this whitespace? + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")+" NOT NULL"). // TODO [SNOW-1348103]: do we care about this whitespace? HasLanguage("PYTHON"). HasBody(definition). HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). @@ -714,7 +716,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). - HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). //TODO [next PR]: do we care about this whitespace? + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). HasLanguage("PYTHON"). HasBodyNil(). HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). @@ -793,7 +795,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). - HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")+" NOT NULL"). //TODO [next PR]: do we care about this whitespace? + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")+" NOT NULL"). HasLanguage("PYTHON"). HasBodyNil(). HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). @@ -1245,7 +1247,7 @@ func TestInt_Functions(t *testing.T) { HasLanguage("SQL"). HasBody(definition). HasNullHandlingNil(). - // TODO [next PR]: volatility is not returned and is present in create syntax + // TODO [SNOW-1348103]: volatility is not returned and is present in create syntax //HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasVolatilityNil(). HasExternalAccessIntegrationsNil(). @@ -1445,7 +1447,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). HasExternalAccessIntegrationsNil(). - // TODO [next PR]: apparently UNSET external access integrations cleans out secrets in the describe but leaves it in SHOW + // TODO [SNOW-1850370]: apparently UNSET external access integrations cleans out secrets in the describe but leaves it in SHOW HasSecretsNil(), ) diff --git a/pkg/sdk/testint/setup_test.go b/pkg/sdk/testint/setup_test.go index 2980b992b8..c5b60802a4 100644 --- a/pkg/sdk/testint/setup_test.go +++ b/pkg/sdk/testint/setup_test.go @@ -162,7 +162,7 @@ func (itc *integrationTestContext) initialize() error { itc.testClient = helpers.NewTestClient(c, TestDatabaseName, TestSchemaName, TestWarehouseName, random.IntegrationTestsSuffix) - // TODO [next PR]: improve setup; this is a quick workaround for faster local testing + // TODO [SNOW-1763603]: improve setup; this is a quick workaround for faster local testing if os.Getenv(string(testenvs.SimplifiedIntegrationTestsSetup)) == "" { config, err := sdk.ProfileConfig(testprofiles.Secondary) if err != nil { From 086e202f2ecf1cc3589d73d209959ea007f54b55 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 16:47:13 +0100 Subject: [PATCH 60/63] Run pre-push --- pkg/acceptance/helpers/function_client.go | 1 + pkg/sdk/functions_ext.go | 28 +++++++++---------- pkg/sdk/functions_gen_test.go | 4 +-- pkg/sdk/testint/functions_integration_test.go | 5 ++-- 4 files changed, 20 insertions(+), 18 deletions(-) diff --git a/pkg/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 06c008b66e..ef8fde637b 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -59,6 +59,7 @@ func (c *FunctionClient) CreateSecure(t *testing.T, arguments ...sdk.DataType) * } func (c *FunctionClient) CreateSql(t *testing.T) (*sdk.Function, func()) { + t.Helper() dataType := testdatatypes.DataTypeFloat id := c.ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) return c.CreateSqlWithIdentifierAndArgument(t, id.SchemaObjectId(), dataType) diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 7e16a54c03..531ddfd9fa 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -109,41 +109,41 @@ func (d *FunctionDetail) setOptionalBoolValueOrError(property string, field **bo return nil } -func (s *CreateForJavaFunctionRequest) WithFunctionDefinitionWrapped(FunctionDefinition string) *CreateForJavaFunctionRequest { - s.FunctionDefinition = String(fmt.Sprintf(`$$%s$$`, FunctionDefinition)) +func (s *CreateForJavaFunctionRequest) WithFunctionDefinitionWrapped(functionDefinition string) *CreateForJavaFunctionRequest { + s.FunctionDefinition = String(fmt.Sprintf(`$$%s$$`, functionDefinition)) return s } -func (s *CreateForPythonFunctionRequest) WithFunctionDefinitionWrapped(FunctionDefinition string) *CreateForPythonFunctionRequest { - s.FunctionDefinition = String(fmt.Sprintf(`$$%s$$`, FunctionDefinition)) +func (s *CreateForPythonFunctionRequest) WithFunctionDefinitionWrapped(functionDefinition string) *CreateForPythonFunctionRequest { + s.FunctionDefinition = String(fmt.Sprintf(`$$%s$$`, functionDefinition)) return s } -func (s *CreateForScalaFunctionRequest) WithFunctionDefinitionWrapped(FunctionDefinition string) *CreateForScalaFunctionRequest { - s.FunctionDefinition = String(fmt.Sprintf(`$$%s$$`, FunctionDefinition)) +func (s *CreateForScalaFunctionRequest) WithFunctionDefinitionWrapped(functionDefinition string) *CreateForScalaFunctionRequest { + s.FunctionDefinition = String(fmt.Sprintf(`$$%s$$`, functionDefinition)) return s } func NewCreateForSQLFunctionRequestDefinitionWrapped( name SchemaObjectIdentifier, - Returns FunctionReturnsRequest, - FunctionDefinition string, + returns FunctionReturnsRequest, + functionDefinition string, ) *CreateForSQLFunctionRequest { s := CreateForSQLFunctionRequest{} s.name = name - s.Returns = Returns - s.FunctionDefinition = fmt.Sprintf(`$$%s$$`, FunctionDefinition) + s.Returns = returns + s.FunctionDefinition = fmt.Sprintf(`$$%s$$`, functionDefinition) return &s } func NewCreateForJavascriptFunctionRequestDefinitionWrapped( name SchemaObjectIdentifier, - Returns FunctionReturnsRequest, - FunctionDefinition string, + returns FunctionReturnsRequest, + functionDefinition string, ) *CreateForJavascriptFunctionRequest { s := CreateForJavascriptFunctionRequest{} s.name = name - s.Returns = Returns - s.FunctionDefinition = fmt.Sprintf(`$$%s$$`, FunctionDefinition) + s.Returns = returns + s.FunctionDefinition = fmt.Sprintf(`$$%s$$`, functionDefinition) return &s } diff --git a/pkg/sdk/functions_gen_test.go b/pkg/sdk/functions_gen_test.go index 56778f14a8..7d8d8d9a79 100644 --- a/pkg/sdk/functions_gen_test.go +++ b/pkg/sdk/functions_gen_test.go @@ -830,7 +830,7 @@ func TestFunctions_CreateForScala(t *testing.T) { opts.ReturnNullValues = ReturnNullValuesPointer(ReturnNullValuesNotNull) opts.NullInputBehavior = NullInputBehaviorPointer(NullInputBehaviorCalledOnNullInput) opts.ReturnResultsBehavior = ReturnResultsBehaviorPointer(ReturnResultsBehaviorImmutable) - opts.RuntimeVersion = String("2.0") + opts.RuntimeVersion = "2.0" opts.Comment = String("comment") opts.Imports = []FunctionImport{ { @@ -859,7 +859,7 @@ func TestFunctions_CreateForScala(t *testing.T) { opts.ReturnNullValues = ReturnNullValuesPointer(ReturnNullValuesNotNull) opts.NullInputBehavior = NullInputBehaviorPointer(NullInputBehaviorCalledOnNullInput) opts.ReturnResultsBehavior = ReturnResultsBehaviorPointer(ReturnResultsBehaviorImmutable) - opts.RuntimeVersion = String("2.0") + opts.RuntimeVersion = "2.0" opts.Comment = String("comment") opts.Imports = []FunctionImport{ { diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index b18d3f9e17..ce2e539d4c 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -51,6 +51,7 @@ func TestInt_Functions(t *testing.T) { tmpPythonFunction := testClientHelper().CreateSamplePythonFunctionAndModule(t) assertParametersSet := func(t *testing.T, functionParametersAssert *objectparametersassert.FunctionParametersAssert) { + t.Helper() assertions.AssertThatObject(t, functionParametersAssert. HasEnableConsoleOutput(true). HasLogLevel(sdk.LogLevelWarn). @@ -567,7 +568,7 @@ func TestInt_Functions(t *testing.T) { assertions.AssertThatObject(t, objectassert.FunctionDetails(t, function.ID()). HasSignature(fmt.Sprintf(`(%s %s)`, argName, dataType.ToLegacyDataTypeSql())). - HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). //TODO [SNOW-1348103]: do we care about this whitespace? + HasReturns(strings.ReplaceAll(dataType.ToSql(), " ", "")). // TODO [SNOW-1348103]: do we care about this whitespace? HasLanguage("PYTHON"). HasBody(definition). HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). @@ -1248,7 +1249,7 @@ func TestInt_Functions(t *testing.T) { HasBody(definition). HasNullHandlingNil(). // TODO [SNOW-1348103]: volatility is not returned and is present in create syntax - //HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + // HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). HasVolatilityNil(). HasExternalAccessIntegrationsNil(). HasSecretsNil(). From e97df58c923d269261474b7c544b08e03e9fff0b Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 16:53:31 +0100 Subject: [PATCH 61/63] Fix cleanup --- pkg/sdk/testint/functions_integration_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index ce2e539d4c..fa5196dc95 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -173,7 +173,7 @@ func TestInt_Functions(t *testing.T) { err := client.Functions.CreateForJava(ctx, request) require.NoError(t, err) t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) - t.Cleanup(testClientHelper().Stage.RemoveFromUserStageFunc(t, targetPath)) + t.Cleanup(testClientHelper().Stage.RemoveFromUserStageFunc(t, jarName)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) @@ -926,7 +926,7 @@ func TestInt_Functions(t *testing.T) { err := client.Functions.CreateForScala(ctx, request) require.NoError(t, err) t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) - t.Cleanup(testClientHelper().Stage.RemoveFromUserStageFunc(t, targetPath)) + t.Cleanup(testClientHelper().Stage.RemoveFromUserStageFunc(t, jarName)) function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) From 503e095147e2c18268c2b8e4e132455b8c4aeb18 Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Sun, 8 Dec 2024 19:26:46 +0100 Subject: [PATCH 62/63] Fix function resource --- pkg/resources/function.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/resources/function.go b/pkg/resources/function.go index aab7456cdb..a0440d33f2 100644 --- a/pkg/resources/function.go +++ b/pkg/resources/function.go @@ -240,7 +240,7 @@ func createJavaFunction(ctx context.Context, d *schema.ResourceData, meta interf // create request with required request := sdk.NewCreateForJavaFunctionRequest(id, *returns, handler) functionDefinition := d.Get("statement").(string) - request.WithFunctionDefinition(functionDefinition) + request.WithFunctionDefinitionWrapped(functionDefinition) // Set optionals if v, ok := d.GetOk("is_secure"); ok { @@ -317,7 +317,7 @@ func createScalaFunction(ctx context.Context, d *schema.ResourceData, meta inter // create request with required request := sdk.NewCreateForScalaFunctionRequest(id, nil, handler, runtimeVersion).WithResultDataTypeOld(sdk.LegacyDataTypeFrom(returnDataType)) - request.WithFunctionDefinition(functionDefinition) + request.WithFunctionDefinitionWrapped(functionDefinition) // Set optionals if v, ok := d.GetOk("is_secure"); ok { @@ -383,7 +383,7 @@ func createSQLFunction(ctx context.Context, d *schema.ResourceData, meta interfa } functionDefinition := d.Get("statement").(string) // create request with required - request := sdk.NewCreateForSQLFunctionRequest(id, *returns, functionDefinition) + request := sdk.NewCreateForSQLFunctionRequestDefinitionWrapped(id, *returns, functionDefinition) // Set optionals if v, ok := d.GetOk("is_secure"); ok { @@ -432,7 +432,7 @@ func createPythonFunction(ctx context.Context, d *schema.ResourceData, meta inte handler := d.Get("handler").(string) // create request with required request := sdk.NewCreateForPythonFunctionRequest(id, *returns, version, handler) - request.WithFunctionDefinition(functionDefinition) + request.WithFunctionDefinitionWrapped(functionDefinition) // Set optionals if v, ok := d.GetOk("is_secure"); ok { @@ -496,7 +496,7 @@ func createJavascriptFunction(ctx context.Context, d *schema.ResourceData, meta } functionDefinition := d.Get("statement").(string) // create request with required - request := sdk.NewCreateForJavascriptFunctionRequest(id, *returns, functionDefinition) + request := sdk.NewCreateForJavascriptFunctionRequestDefinitionWrapped(id, *returns, functionDefinition) // Set optionals if v, ok := d.GetOk("is_secure"); ok { From 7a392ecb0819a0dd6cf36439c7a8d32a93d53a3f Mon Sep 17 00:00:00 2001 From: Artur Sawicki Date: Mon, 9 Dec 2024 12:45:23 +0100 Subject: [PATCH 63/63] Fix acceptance test --- pkg/resources/function_acceptance_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/resources/function_acceptance_test.go b/pkg/resources/function_acceptance_test.go index 52d60e3717..df8bb28014 100644 --- a/pkg/resources/function_acceptance_test.go +++ b/pkg/resources/function_acceptance_test.go @@ -142,7 +142,7 @@ func TestAcc_Function_complex(t *testing.T) { resource.TestCheckResourceAttr(resourceName, "comment", "Terraform acceptance test"), resource.TestCheckResourceAttr(resourceName, "statement", statement), resource.TestCheckResourceAttr(resourceName, "arguments.#", "1"), - resource.TestCheckResourceAttr(resourceName, "arguments.0.name", "D"), + resource.TestCheckResourceAttr(resourceName, "arguments.0.name", "d"), resource.TestCheckResourceAttr(resourceName, "arguments.0.type", "FLOAT"), resource.TestCheckResourceAttr(resourceName, "return_behavior", "VOLATILE"), resource.TestCheckResourceAttr(resourceName, "return_type", "FLOAT"),