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..c1b241aa1d --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_describe_snowflake_ext.go @@ -0,0 +1,370 @@ +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 +} + +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 + }) + return f +} 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..66e4253d20 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_ext.go @@ -0,0 +1,30 @@ +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 +} + +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 new file mode 100644 index 0000000000..8b6f674aa8 --- /dev/null +++ b/pkg/acceptance/bettertestspoc/assert/objectassert/function_snowflake_gen.go @@ -0,0 +1,271 @@ +// 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) 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() + 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 +} + +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/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/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/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/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/acceptance/helpers/function_client.go b/pkg/acceptance/helpers/function_client.go index 3e6fe5a294..ef8fde637b 100644 --- a/pkg/acceptance/helpers/function_client.go +++ b/pkg/acceptance/helpers/function_client.go @@ -2,9 +2,12 @@ package helpers import ( "context" + "fmt" "testing" + "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" ) @@ -33,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", @@ -41,12 +44,13 @@ func (c *FunctionClient) CreateWithIdentifier(t *testing.T, id sdk.SchemaObjectI ) } +// 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...) return c.CreateWithRequest(t, id, - sdk.NewCreateForSQLFunctionRequest( + sdk.NewCreateForSQLFunctionRequestDefinitionWrapped( id.SchemaObjectId(), *sdk.NewFunctionReturnsRequest().WithResultDataType(*sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeInt)), "SELECT 1", @@ -54,6 +58,86 @@ 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) +} + +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" + definition := c.SampleSqlDefinition(t) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + 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, idWithArgs) + require.NoError(t, err) + + return function, c.DropFunctionFunc(t, idWithArgs) +} + +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() + + 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() @@ -81,3 +165,81 @@ 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) +} + +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() + + return fmt.Sprintf(` + class %[1]s { + public static String %[2]s(String %[3]s) { + return %[3]s; + } + } +`, className, funcName, argName) +} + +func (c *FunctionClient) SampleJavascriptDefinition(t *testing.T, argName string) string { + t.Helper() + + return fmt.Sprintf(` + if (%[1]s <= 0) { + return 1; + } else { + var result = 1; + for (var i = 2; i <= %[1]s; i++) { + result = result * i; + } + return result; + } +`, argName) +} + +func (c *FunctionClient) SamplePythonDefinition(t *testing.T, funcName string, argName string) string { + t.Helper() + + return fmt.Sprintf(` +def %[1]s(%[2]s): + result = "" + for a in range(5): + result += %[2]s + return result +`, funcName, argName) +} + +func (c *FunctionClient) SampleScalaDefinition(t *testing.T, className string, funcName string, argName string) string { + t.Helper() + + 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 { + t.Helper() + + 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/acceptance/helpers/function_setup_helpers.go b/pkg/acceptance/helpers/function_setup_helpers.go new file mode 100644 index 0000000000..ca4d29dd86 --- /dev/null +++ b/pkg/acceptance/helpers/function_setup_helpers.go @@ -0,0 +1,121 @@ +package helpers + +import ( + "context" + "fmt" + "path/filepath" + "strings" + "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, 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) + 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, + } +} + +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 + JarName string +} + +func (f *TmpFunction) JarLocation() string { + return fmt.Sprintf("@~/%s", f.JarName) +} + +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/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/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/acceptance/helpers/stage_client.go b/pkg/acceptance/helpers/stage_client.go index 41eb0aea36..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,35 @@ 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() + + _, 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/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/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/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..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 { @@ -310,9 +310,14 @@ 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.WithFunctionDefinition(functionDefinition) + request := sdk.NewCreateForScalaFunctionRequest(id, nil, handler, runtimeVersion).WithResultDataTypeOld(sdk.LegacyDataTypeFrom(returnDataType)) + request.WithFunctionDefinitionWrapped(functionDefinition) // Set optionals if v, ok := d.GetOk("is_secure"); ok { @@ -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)) } @@ -381,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 { @@ -430,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 { @@ -494,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 { @@ -571,7 +573,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 +604,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 +613,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 +621,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 +629,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 +704,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/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"), diff --git a/pkg/sdk/common_types.go b/pkg/sdk/common_types.go index 7a4975a78e..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" ) @@ -260,8 +260,9 @@ func ReturnNullValuesPointer(v ReturnNullValues) *ReturnNullValues { } 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 @@ -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 825c1d2551..1c92cd4078 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") @@ -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( @@ -87,7 +89,11 @@ 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")). + 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"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"), @@ -116,7 +122,11 @@ 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()). + 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"), ).CustomOperation( @@ -127,6 +137,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 +170,11 @@ 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")). + 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"). WithValidation(g.ValidateValueSet, "Handler"). @@ -187,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", @@ -200,8 +215,14 @@ 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()). - PredefinedQueryStructField("FunctionDefinition", "*string", g.ParameterOptions().NoEquals().SingleQuotes().SQL("AS")). + 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"). WithValidation(g.ConflictingFields, "OrReplace", "IfNotExists"). @@ -230,7 +251,11 @@ 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()). + 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"), ).AlterOperation( @@ -241,19 +266,38 @@ 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().SingleQuotes()). + OptionalAssignment("METRIC_LEVEL", g.KindOfTPointer[MetricLevel](), g.ParameterOptions().SingleQuotes()). + OptionalAssignment("TRACE_LEVEL", g.KindOfTPointer[TraceLevel](), g.ParameterOptions().SingleQuotes()). + 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"). @@ -279,9 +323,12 @@ 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"), + Field("is_memoizable", "sql.NullString"). + Field("is_data_metric", "sql.NullString"), g.PlainStruct("Function"). Field("CreatedOn", "string"). Field("Name", "string"). @@ -297,14 +344,17 @@ 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"), + 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", @@ -312,8 +362,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_dto_builders_gen.go b/pkg/sdk/functions_dto_builders_gen.go index 3bb40dfd0e..7d0a49180a 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, @@ -279,6 +319,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 @@ -334,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 @@ -343,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 } @@ -401,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 @@ -421,11 +483,41 @@ 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 } +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 @@ -488,6 +580,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 { @@ -506,18 +618,13 @@ func (s *AlterFunctionRequest) WithRenameTo(RenameTo SchemaObjectIdentifier) *Al return s } -func (s *AlterFunctionRequest) WithSetComment(SetComment string) *AlterFunctionRequest { - s.SetComment = &SetComment - return s -} - -func (s *AlterFunctionRequest) WithSetLogLevel(SetLogLevel string) *AlterFunctionRequest { - s.SetLogLevel = &SetLogLevel +func (s *AlterFunctionRequest) WithSet(Set FunctionSetRequest) *AlterFunctionRequest { + s.Set = &Set 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 +638,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 +751,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..14e86f6260 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 } @@ -93,6 +101,7 @@ type CreateForPythonFunctionRequest struct { OrReplace *bool Temporary *bool Secure *bool + Aggregate *bool IfNotExists *bool name SchemaObjectIdentifier // required Arguments []FunctionArgumentRequest @@ -108,29 +117,39 @@ type CreateForPythonFunctionRequest struct { Handler string // required ExternalAccessIntegrations []AccountObjectIdentifier Secrets []SecretReference + EnableConsoleOutput *bool + LogLevel *LogLevel + MetricLevel *MetricLevel + TraceLevel *TraceLevel FunctionDefinition *string } 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 - 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 // required + 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 { @@ -145,23 +164,46 @@ type CreateForSQLFunctionRequest struct { ReturnResultsBehavior *ReturnResultsBehavior Memoizable *bool Comment *string + EnableConsoleOutput *bool + LogLevel *LogLevel + MetricLevel *MetricLevel + TraceLevel *TraceLevel FunctionDefinition string // required } 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 +213,7 @@ type DropFunctionRequest struct { type ShowFunctionRequest struct { Like *Like - In *In + In *ExtendedIn } type DescribeFunctionRequest struct { diff --git a/pkg/sdk/functions_ext.go b/pkg/sdk/functions_ext.go index 4fe8a9524d..531ddfd9fa 100644 --- a/pkg/sdk/functions_ext.go +++ b/pkg/sdk/functions_ext.go @@ -1,5 +1,149 @@ package sdk +import ( + "context" + "errors" + "fmt" + "strconv" +) + +const DefaultFunctionComment = "user-defined function" + func (v *Function) ID() SchemaObjectIdentifierWithArguments { return NewSchemaObjectIdentifierWithArguments(v.CatalogName, v.SchemaName, v.Name, v.ArgumentsOld...) } + +// FunctionDetails contains aggregated describe results for the given function. +type FunctionDetails struct { + 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 +} + +func functionDetailsFromRows(rows []FunctionDetail) (*FunctionDetails, error) { + v := &FunctionDetails{} + var errs []error + for _, row := range rows { + switch row.Property { + case "signature": + errs = append(errs, row.setStringValueOrError("signature", &v.Signature)) + case "returns": + errs = append(errs, row.setStringValueOrError("returns", &v.Returns)) + case "language": + errs = append(errs, row.setStringValueOrError("language", &v.Language)) + case "null handling": + v.NullHandling = row.Value + case "volatility": + v.Volatility = row.Value + case "body": + v.Body = row.Value + case "external_access_integrations": + v.ExternalAccessIntegrations = row.Value + case "secrets": + v.Secrets = row.Value + case "imports": + v.Imports = row.Value + case "handler": + v.Handler = row.Value + case "runtime_version": + v.RuntimeVersion = row.Value + case "packages": + v.Packages = row.Value + case "installed_packages": + v.InstalledPackages = row.Value + case "is_aggregate": + errs = append(errs, row.setOptionalBoolValueOrError("is_aggregate", &v.IsAggregate)) + case "target_path": + v.TargetPath = row.Value + } + } + return v, errors.Join(errs...) +} + +func (v *functions) DescribeDetails(ctx context.Context, id SchemaObjectIdentifierWithArguments) (*FunctionDetails, error) { + rows, err := v.Describe(ctx, id) + if err != nil { + return nil, err + } + 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) + } else { + *field = *d.Value + } + return nil +} + +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 +} + +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 +} diff --git a/pkg/sdk/functions_gen.go b/pkg/sdk/functions_gen.go index ab7ca62170..ad2ec72844 100644 --- a/pkg/sdk/functions_gen.go +++ b/pkg/sdk/functions_gen.go @@ -19,6 +19,10 @@ 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) + ShowParameters(ctx context.Context, id SchemaObjectIdentifierWithArguments) ([]*Parameter, error) } // CreateForJavaFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#java-handler. @@ -45,11 +49,15 @@ 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"` + 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"` } 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"` @@ -70,7 +78,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"` } @@ -99,7 +107,11 @@ 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"` + 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"` } // CreateForPythonFunctionOptions is based on https://docs.snowflake.com/en/sql-reference/sql/create-function#python-handler. @@ -108,6 +120,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"` @@ -125,34 +138,44 @@ 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"` + 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"` } // 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"` - FunctionDefinition *string `ddl:"parameter,single_quotes,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. @@ -170,26 +193,49 @@ 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"` + 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"` } // 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,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 { + 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. @@ -202,51 +248,57 @@ 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 { - 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"` + 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 - IsExternalFunction bool - Language string - IsMemoizable 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. @@ -263,5 +315,5 @@ type functionDetailRow struct { type FunctionDetail struct { Property string - Value string + Value *string } diff --git a/pkg/sdk/functions_gen_test.go b/pkg/sdk/functions_gen_test.go index 95c21d9204..7d8d8d9a79 100644 --- a/pkg/sdk/functions_gen_test.go +++ b/pkg/sdk/functions_gen_test.go @@ -1,11 +1,18 @@ package sdk import ( + "fmt" "testing" ) +func wrapFunctionDefinition(def string) string { + return fmt.Sprintf(`$$%s$$`, def) +} + func TestFunctions_CreateForJava(t *testing.T) { id := randomSchemaObjectIdentifier() + secretId := randomSchemaObjectIdentifier() + secretId2 := randomSchemaObjectIdentifier() defaultOpts := func() *CreateForJavaFunctionOptions { return &CreateForJavaFunctionOptions{ @@ -18,12 +25,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 +73,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")) @@ -120,20 +144,9 @@ 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")) }) - 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() @@ -188,16 +201,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("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' = %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) { @@ -253,16 +266,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("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' = %s, 'variable2' = %s) TARGET_PATH = '@~/testfunc.jar' AS $$return id + name;$$`, id.FullyQualifiedName(), secretId.FullyQualifiedName(), secretId2.FullyQualifiedName()) }) } @@ -280,7 +293,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 +334,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 +405,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 +428,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,13 +454,15 @@ 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()) }) } func TestFunctions_CreateForPython(t *testing.T) { id := randomSchemaObjectIdentifier() + secretId := randomSchemaObjectIdentifier() + secretId2 := randomSchemaObjectIdentifier() defaultOpts := func() *CreateForPythonFunctionOptions { return &CreateForPythonFunctionOptions{ @@ -451,12 +475,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 +533,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 +604,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{ @@ -612,15 +661,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("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' = %s, 'variable2' = %s) AS $$import numpy as np$$`, id.FullyQualifiedName(), secretId.FullyQualifiedName(), secretId2.FullyQualifiedName()) }) t.Run("all options", func(t *testing.T) { @@ -669,15 +718,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("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' = %s, 'variable2' = %s) AS $$import numpy as np$$`, id.FullyQualifiedName(), secretId.FullyQualifiedName(), secretId2.FullyQualifiedName()) }) } @@ -695,12 +744,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 +800,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") @@ -747,16 +809,9 @@ 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")) }) - 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() @@ -775,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{ { @@ -783,8 +838,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) { @@ -804,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{ { @@ -812,8 +867,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 +886,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 +927,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 +998,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 +1005,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 +1032,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,79 +1058,62 @@ 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()) + 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_Drop(t *testing.T) { - noArgsId := randomSchemaObjectIdentifierWithArguments() +func TestFunctions_Alter(t *testing.T) { id := randomSchemaObjectIdentifierWithArguments(DataTypeVARCHAR, DataTypeNumber) + secretId := randomSchemaObjectIdentifier() - defaultOpts := func() *DropFunctionOptions { - return &DropFunctionOptions{ - name: id, + defaultOpts := func() *AlterFunctionOptions { + return &AlterFunctionOptions{ + name: id, + IfExists: Bool(true), } } t.Run("validation: nil options", func(t *testing.T) { - opts := (*DropFunctionOptions)(nil) + opts := (*AlterFunctionOptions)(nil) 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) { + t.Run("validation: valid identifier for [opts.RenameTo] if set", 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()) + target := emptySchemaObjectIdentifier + opts.RenameTo = &target + assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) }) -} - -func TestFunctions_Alter(t *testing.T) { - id := randomSchemaObjectIdentifierWithArguments(DataTypeVARCHAR, DataTypeNumber) - noArgsId := randomSchemaObjectIdentifierWithArguments() - defaultOpts := func() *AlterFunctionOptions { - return &AlterFunctionOptions{ - name: id, - IfExists: Bool(true), - } - } - - t.Run("validation: nil options", func(t *testing.T) { - opts := (*AlterFunctionOptions)(nil) - assertOptsInvalidJoinedErrors(t, opts, ErrNilOptions) + 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() + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterFunctionOptions", "RenameTo", "Set", "Unset", "SetSecure", "UnsetSecure", "SetTags", "UnsetTags")) }) - t.Run("validation: incorrect identifier", 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 = emptySchemaObjectIdentifierWithArguments - assertOptsInvalidJoinedErrors(t, opts, ErrInvalidObjectIdentifier) + opts.Set = &FunctionSet{} + opts.Unset = &FunctionUnset{} + assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterFunctionOptions", "RenameTo", "Set", "Unset", "SetSecure", "UnsetSecure", "SetTags", "UnsetTags")) }) - t.Run("validation: exactly one field should be present", 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() - assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterFunctionOptions", "RenameTo", "SetComment", "SetLogLevel", "SetTraceLevel", "SetSecure", "UnsetLogLevel", "UnsetTraceLevel", "UnsetSecure", "UnsetComment", "SetTags", "UnsetTags")) + opts.Set = &FunctionSet{} + assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterFunctionOptions.Set", "Comment", "ExternalAccessIntegrations", "SecretsList", "EnableConsoleOutput", "LogLevel", "MetricLevel", "TraceLevel")) }) - t.Run("validation: exactly one field should be present", 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.SetLogLevel = String("DEBUG") - opts.UnsetComment = Bool(true) - assertOptsInvalidJoinedErrors(t, opts, errExactlyOneOf("AlterFunctionOptions", "RenameTo", "SetComment", "SetLogLevel", "SetTraceLevel", "SetSecure", "UnsetLogLevel", "UnsetTraceLevel", "UnsetSecure", "UnsetComment", "SetTags", "UnsetTags")) + opts.Unset = &FunctionUnset{} + assertOptsInvalidJoinedErrors(t, opts, errAtLeastOneOf("AlterFunctionOptions.Unset", "Comment", "ExternalAccessIntegrations", "EnableConsoleOutput", "LogLevel", "MetricLevel", "TraceLevel")) }) t.Run("alter: rename to", func(t *testing.T) { @@ -1076,29 +1123,42 @@ func TestFunctions_Alter(t *testing.T) { assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s RENAME TO %s`, id.FullyQualifiedName(), opts.RenameTo.FullyQualifiedName()) }) - t.Run("alter: set log level with no arguments", func(t *testing.T) { + t.Run("alter: set", 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{ + 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: set log level", func(t *testing.T) { + t.Run("alter: set empty secrets", 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{ + SecretsList: &SecretsList{}, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET SECRETS = ()`, id.FullyQualifiedName()) }) - t.Run("alter: set trace level", func(t *testing.T) { + t.Run("alter: set non-empty secrets", 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.Set = &FunctionSet{ + SecretsList: &SecretsList{ + []SecretReference{ + {VariableName: "abc", Name: secretId}, + }, + }, + } + assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET SECRETS = ('abc' = %s)`, id.FullyQualifiedName(), secretId.FullyQualifiedName()) }) - t.Run("alter: set comment", func(t *testing.T) { + t.Run("alter: unset", func(t *testing.T) { opts := defaultOpts() - opts.SetComment = String("comment") - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET COMMENT = 'comment'`, 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: set secure", func(t *testing.T) { @@ -1107,30 +1167,12 @@ func TestFunctions_Alter(t *testing.T) { assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s SET SECURE`, id.FullyQualifiedName()) }) - t.Run("alter: unset log level", func(t *testing.T) { - opts := defaultOpts() - opts.UnsetLogLevel = Bool(true) - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s UNSET LOG_LEVEL`, id.FullyQualifiedName()) - }) - - t.Run("alter: unset trace level", func(t *testing.T) { - opts := defaultOpts() - opts.UnsetTraceLevel = Bool(true) - assertOptsValidAndSQLEquals(t, opts, `ALTER FUNCTION IF EXISTS %s UNSET TRACE_LEVEL`, id.FullyQualifiedName()) - }) - t.Run("alter: unset secure", func(t *testing.T) { opts := defaultOpts() opts.UnsetSecure = Bool(true) 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 +1194,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 +1255,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 +1266,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 +1278,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()) diff --git a/pkg/sdk/functions_impl_gen.go b/pkg/sdk/functions_impl_gen.go index ca17781139..8d9dbd1606 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 } @@ -98,12 +98,21 @@ 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 { 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 +128,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 +140,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 } @@ -154,12 +171,21 @@ 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 { 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 +201,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 } @@ -188,6 +218,7 @@ func (r *CreateForPythonFunctionRequest) toOpts() *CreateForPythonFunctionOption OrReplace: r.OrReplace, Temporary: r.Temporary, Secure: r.Secure, + Aggregate: r.Aggregate, IfNotExists: r.IfNotExists, name: r.name, @@ -202,12 +233,21 @@ 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 { 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 +263,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 +275,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 } @@ -262,28 +310,43 @@ func (r *CreateForScalaFunctionRequest) toOpts() *CreateForScalaFunctionOptions RuntimeVersion: r.RuntimeVersion, Comment: r.Comment, - Handler: r.Handler, - TargetPath: r.TargetPath, - 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)) 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 } @@ -303,12 +366,21 @@ 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 { 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 +396,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 } @@ -334,19 +410,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 } @@ -397,9 +493,18 @@ 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" } + if r.IsDataMetric.Valid { + e.IsDataMetric = r.IsDataMetric.String == "Y" + } return e } @@ -414,8 +519,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 } diff --git a/pkg/sdk/functions_validations_gen.go b/pkg/sdk/functions_validations_gen.go index 78970158e8..651515e1cf 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")) } @@ -195,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")) } @@ -258,8 +252,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...) } diff --git a/pkg/sdk/parameters.go b/pkg/sdk/parameters.go index cf29fa1da4..b651b673f3 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 @@ -1341,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 } @@ -1370,6 +1380,7 @@ const ( ParameterTypeDatabase ParameterType = "DATABASE" ParameterTypeSchema ParameterType = "SCHEMA" ParameterTypeTask ParameterType = "TASK" + ParameterTypeFunction ParameterType = "FUNCTION" ) type Parameter struct { @@ -1498,6 +1509,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) } 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 57bf0ec3bb..0efd3fde29 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 }} = {{ if .IsPointer }}&{{end}}{{ 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 }} 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()) }) } diff --git a/pkg/sdk/testint/functions_integration_test.go b/pkg/sdk/testint/functions_integration_test.go index 5c19d66af4..fa5196dc95 100644 --- a/pkg/sdk/testint/functions_integration_test.go +++ b/pkg/sdk/testint/functions_integration_test.go @@ -2,11 +2,17 @@ package testint import ( "context" - "errors" "fmt" + "strings" "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/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" "github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/sdk/datatypes" @@ -14,264 +20,1358 @@ 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 = '' ] - - execute and execute-immediate for scripting https://docs.snowflake.com/en/sql-reference/sql/execute-immediate -*/ - -func TestInt_CreateFunctions(t *testing.T) { +// 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() + secretId := testClientHelper().Ids.RandomSchemaObjectIdentifier() - 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) - } + 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) + + tmpJavaFunction := testClientHelper().CreateSampleJavaFunctionAndJar(t) + tmpPythonFunction := testClientHelper().CreateSamplePythonFunctionAndModule(t) + + assertParametersSet := func(t *testing.T, functionParametersAssert *objectparametersassert.FunctionParametersAssert) { + t.Helper() + assertions.AssertThatObject(t, functionParametersAssert. + HasEnableConsoleOutput(true). + HasLogLevel(sdk.LogLevelWarn). + HasMetricLevel(sdk.MetricLevelAll). + HasTraceLevel(sdk.TraceLevelAlways), + ) } - t.Run("create function for Java", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) + t.Run("create function for Java - inline minimal", func(t *testing.T) { + className := "TestFunc" + funcName := "echoVarchar" + argName := "x" + dataType := testdatatypes.DataTypeVarchar_100 - definition := ` - class TestFunc { - public static String echoVarchar(String x) { - return x; - } - }` - target := fmt.Sprintf("@~/tf-%d.jar", time.Now().Unix()) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeVARCHAR) + 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) + + request := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithFunctionDefinitionWrapped(definition) + + err := client.Functions.CreateForJava(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). + HasExternalAccessIntegrations(""). + HasSecrets(""). + 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"). + HasBody(definition). + HasNullHandling(string(sdk.NullInputBehaviorCalledOnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImports(`[]`). + HasHandler(handler). + HasRuntimeVersionNil(). + HasPackages(`[]`). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) + + t.Run("create function for Java - inline full", func(t *testing.T) { + className := "TestFunc" + funcName := "echoVarchar" + argName := "x" + dataType := testdatatypes.DataTypeVarchar_100 + + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - argument := sdk.NewFunctionArgumentRequest("x", nil).WithDefaultValue("'abc'").WithArgDataTypeOld(sdk.DataTypeVARCHAR) - request := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, "TestFunc.echoVarchar"). + 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(id.SchemaObjectId(), *returns, handler). WithOrReplace(true). WithArguments([]sdk.FunctionArgumentRequest{*argument}). - WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorCalledOnNullInput)). - WithTargetPath(target). - WithFunctionDefinition(definition) + WithCopyGrants(true). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + WithRuntimeVersion("11"). + 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"), + }). + 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) require.NoError(t, err) - t.Cleanup(cleanupFunctionHandle(id)) + 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) - 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.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). + 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). + 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("JAVA"). + HasBody(definition). + HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + HasExternalAccessIntegrations(fmt.Sprintf(`[%s]`, externalAccessIntegration.FullyQualifiedName())). + // 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). + HasRuntimeVersion("11"). + 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 Javascript", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) + t.Run("create function for Java - staged minimal", func(t *testing.T) { + dataType := tmpJavaFunction.ArgType + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) - definition := ` - if (D <= 0) { - return 1; - } else { - var result = 1; - for (var i = 2; i <= D; i++) { - result = result * i; - } - return result; - }` + argName := "x" + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + handler := tmpJavaFunction.JavaHandler() + importPath := tmpJavaFunction.JarLocation() - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) + requestStaged := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithImports([]sdk.FunctionImportRequest{*sdk.NewFunctionImportRequest().WithImport(importPath)}) + + 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(sdk.DefaultFunctionComment). + HasCatalogName(fmt.Sprintf(`"%s"`, id.DatabaseName())). + HasIsTableFunction(false). + HasValidForClustering(false). + HasIsSecure(false). + HasExternalAccessIntegrations(""). + HasSecrets(""). + 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.NullInputBehaviorCalledOnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorVolatile)). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImports(fmt.Sprintf(`[%s]`, importPath)). + 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) { + 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) - argument := sdk.NewFunctionArgumentRequest("d", nil).WithArgDataTypeOld(sdk.DataTypeFloat) - request := sdk.NewCreateForJavascriptFunctionRequest(id.SchemaObjectId(), *returns, definition). + handler := tmpJavaFunction.JavaHandler() + + requestStaged := sdk.NewCreateForJavaFunctionRequest(id.SchemaObjectId(), *returns, handler). WithOrReplace(true). WithArguments([]sdk.FunctionArgumentRequest{*argument}). - WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorCalledOnNullInput)) + WithCopyGrants(true). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + WithRuntimeVersion("11"). + 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"), + }). + 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). + 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). + 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("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]`, tmpJavaFunction.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) { + dataType := testdatatypes.DataTypeFloat + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) + + argName := "d" + definition := testClientHelper().Function.SampleJavascriptDefinition(t, argName) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) + returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + + request := sdk.NewCreateForJavascriptFunctionRequestDefinitionWrapped(id.SchemaObjectId(), *returns, definition). + WithArguments([]sdk.FunctionArgumentRequest{*argument}) + 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) - require.Equal(t, id.Name(), function.Name) - require.Equal(t, "JAVASCRIPT", 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.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). + HasExternalAccessIntegrations(""). + HasSecrets(""). + HasIsExternalFunction(false). + HasLanguage("JAVASCRIPT"). + HasIsMemoizable(false). + HasIsDataMetric(false), + ) + + 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(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) }) - t.Run("create function for Python", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeNumber) + t.Run("create function for Javascript - inline full", func(t *testing.T) { + dataType := testdatatypes.DataTypeFloat + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) - definition := ` -def dump(i): - print("Hello World!")` - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeVariant) + argName := "d" + definition := testClientHelper().Function.SampleJavascriptDefinition(t, argName) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - argument := sdk.NewFunctionArgumentRequest("i", nil).WithArgDataTypeOld(sdk.DataTypeNumber) - request := sdk.NewCreateForPythonFunctionRequest(id.SchemaObjectId(), *returns, "3.8", "dump"). + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + request := sdk.NewCreateForJavascriptFunctionRequestDefinitionWrapped(id.SchemaObjectId(), *returns, definition). WithOrReplace(true). WithArguments([]sdk.FunctionArgumentRequest{*argument}). - WithFunctionDefinition(definition) - err := client.Functions.CreateForPython(ctx, request) + WithCopyGrants(true). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithComment("comment") + + 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) - require.Equal(t, id.Name(), function.Name) - require.Equal(t, "PYTHON", 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.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). + HasExternalAccessIntegrations(""). + HasSecrets(""). + HasIsExternalFunction(false). + HasLanguage("JAVASCRIPT"). + 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("JAVASCRIPT"). + HasBody(definition). + HasNullHandling(string(sdk.NullInputBehaviorReturnNullInput)). + HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImportsNil(). + HasHandlerNil(). + HasRuntimeVersionNil(). + HasPackagesNil(). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) }) - t.Run("create function for Scala", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeVARCHAR) + t.Run("create function for Python - inline minimal", func(t *testing.T) { + dataType := testdatatypes.DataTypeNumber_36_2 + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) - definition := ` - class Echo { - def echoVarchar(x : String): String = { - return x - } - }` + 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) - argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeVARCHAR) - request := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), nil, "Echo.echoVarchar"). - WithResultDataTypeOld(sdk.DataTypeVARCHAR). + 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). + HasExternalAccessIntegrations(""). + HasSecrets(""). + 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 [SNOW-1348103]: 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) { + 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}). - WithRuntimeVersion("2.12"). - WithFunctionDefinition(definition) - err := client.Functions.CreateForScala(ctx, request) + WithCopyGrants(true). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithComment("comment"). + 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"), + }). + 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(cleanupFunctionHandle(id)) + 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) + + 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). + 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). + 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 [SNOW-1348103]: 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(fmt.Sprintf(`[%s]`, tmpPythonFunction.PythonModuleLocation())). + 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 SQL", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.DataTypeFloat) + 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). + HasExternalAccessIntegrations(""). + HasSecrets(""). + 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(), " ", "")). + 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(), + ) + }) - definition := "3.141592654::FLOAT" + t.Run("create function for Python - staged full", func(t *testing.T) { + dataType := testdatatypes.DataTypeVarchar_100 + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) + argName := "i" + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeFloat) - request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition). + 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). + 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). + 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"). + 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) { + 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, "2.12"). WithArguments([]sdk.FunctionArgumentRequest{*argument}). + 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). + HasExternalAccessIntegrations(""). + HasSecrets(""). + 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, "2.12"). WithOrReplace(true). - WithComment("comment") - err := client.Functions.CreateForSQL(ctx, request) + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithCopyGrants(true). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + 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}). + WithSecrets([]sdk.SecretReference{{VariableName: "abc", Name: secretId}}). + 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(cleanupFunctionHandle(id)) + 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) - require.Equal(t, id.Name(), function.Name) - require.Equal(t, "SQL", 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.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). + 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). + 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 SQL with no arguments", func(t *testing.T) { - id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments() + t.Run("create function for Scala - staged minimal", func(t *testing.T) { + dataType := tmpJavaFunction.ArgType + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) - definition := "3.141592654::FLOAT" + argName := "x" + argument := sdk.NewFunctionArgumentRequest(argName, dataType) + handler := tmpJavaFunction.JavaHandler() + importPath := tmpJavaFunction.JarLocation() - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) - returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition). + requestStaged := sdk.NewCreateForScalaFunctionRequest(id.SchemaObjectId(), dataType, handler, "2.12"). + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + 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). + HasExternalAccessIntegrations(""). + HasSecrets(""). + 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, "2.12"). WithOrReplace(true). - WithComment("comment") + WithArguments([]sdk.FunctionArgumentRequest{*argument}). + WithCopyGrants(true). + WithNullInputBehavior(*sdk.NullInputBehaviorPointer(sdk.NullInputBehaviorReturnNullInput)). + WithReturnResultsBehavior(sdk.ReturnResultsBehaviorImmutable). + WithReturnNullValues(sdk.ReturnNullValuesNotNull). + 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). + 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), + ) + + 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) { + 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(cleanupFunctionHandle(id)) + 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) + + 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). + HasExternalAccessIntegrations(""). + HasSecrets(""). + 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(), + ) }) -} -func TestInt_OtherFunctions(t *testing.T) { - client := testClient(t) - ctx := testContext(t) + t.Run("create function for SQL - inline full", func(t *testing.T) { + argName := "x" + dataType := testdatatypes.DataTypeFloat + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments(sdk.LegacyDataTypeFrom(dataType)) - assertFunction := func(t *testing.T, id sdk.SchemaObjectIdentifierWithArguments, secure bool, withArguments bool) { - t.Helper() + 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) - 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) - } + 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). + HasExternalAccessIntegrations(""). + HasSecrets(""). + HasIsExternalFunction(false). + HasLanguage("SQL"). + HasIsMemoizable(true). + HasIsDataMetric(false), + ) - 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) - } - } + 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 [SNOW-1348103]: volatility is not returned and is present in create syntax + // HasVolatility(string(sdk.ReturnResultsBehaviorImmutable)). + HasVolatilityNil(). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(). + HasImportsNil(). + HasHandlerNil(). + HasRuntimeVersionNil(). + HasPackagesNil(). + HasTargetPathNil(). + HasInstalledPackagesNil(). + HasIsAggregateNil(), + ) - 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() - } + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) + }) - definition := "3.141592654::FLOAT" + t.Run("create function for SQL - no arguments", func(t *testing.T) { + dataType := testdatatypes.DataTypeFloat + id := testClientHelper().Ids.RandomSchemaObjectIdentifierWithArguments() - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) + definition := testClientHelper().Function.SampleSqlDefinition(t) + dt := sdk.NewFunctionReturnsResultDataTypeRequest(dataType) 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}) - } + request := sdk.NewCreateForSQLFunctionRequestDefinitionWrapped(id.SchemaObjectId(), *returns, definition) + err := client.Functions.CreateForSQL(ctx, request) require.NoError(t, err) - if cleanup { - t.Cleanup(cleanupFunctionHandle(id)) - } + t.Cleanup(testClientHelper().Function.DropFunctionFunc(t, id)) + function, err := client.Functions.ShowByID(ctx, id) require.NoError(t, err) - return function - } - t.Run("alter function: rename", func(t *testing.T) { - f := createFunctionForSQLHandle(t, false, true) + 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). + HasExternalAccessIntegrations(""). + HasSecrets(""). + 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(), + ) + }) + + 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) 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) @@ -281,89 +1381,175 @@ 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).WithSetLogLevel(string(sdk.LogLevelDebug))) - 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(sdk.DefaultFunctionComment), + ) - id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithUnsetLogLevel(true)) - require.NoError(t, err) - assertFunction(t, id, false, true) - }) + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). + HasExternalAccessIntegrationsNil(). + HasSecretsNil(), + ) + + assertions.AssertThatObject(t, objectparametersassert.FunctionParameters(t, id). + HasAllDefaults(). + HasAllDefaultsExplicit(), + ) - t.Run("alter function: set trace level", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) + 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"), + ) - id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSetTraceLevel(string(sdk.TraceLevelAlways))) + err := client.Functions.Alter(ctx, request) require.NoError(t, err) - assertFunction(t, id, false, true) - }) - t.Run("alter function: unset trace 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).WithUnsetTraceLevel(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 comment", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) + assertions.AssertThatObject(t, objectassert.Function(t, id). + HasName(id.Name()). + HasDescription(sdk.DefaultFunctionComment). + HasExternalAccessIntegrations("[]"). + HasSecrets(fmt.Sprintf(`{"abc":"\"%s\".\"%s\".%s"}`, secretId.DatabaseName(), secretId.SchemaName(), secretId.Name())), + ) - id := f.ID() - err := client.Functions.Alter(ctx, sdk.NewAlterFunctionRequest(id).WithSetComment("test comment")) + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). + HasExternalAccessIntegrationsNil(). + // TODO [SNOW-1850370]: 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) - }) - t.Run("alter function: unset comment", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, true) + assertions.AssertThatObject(t, objectassert.FunctionDetails(t, id). + HasSecretsNil(), + ) + }) + 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).WithUnsetComment(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 secure", 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).WithSetSecure(true)) + assertParametersSet(t, objectparametersassert.FunctionParameters(t, id)) + + unsetRequest := sdk.NewAlterFunctionRequest(id).WithUnset(*sdk.NewFunctionUnsetRequest(). + WithEnableConsoleOutput(true). + WithLogLevel(true). + WithMetricLevel(true). + WithTraceLevel(true). + WithComment(true), + ) + + err = client.Functions.Alter(ctx, unsetRequest) require.NoError(t, err) - assertFunction(t, id, true, 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 with no arguments", 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() + + 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) @@ -372,9 +1558,12 @@ func TestInt_OtherFunctions(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) @@ -384,81 +1573,90 @@ func TestInt_OtherFunctions(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, f.ID()) + details, err := client.Functions.Describe(ctx, id) require.NoError(t, err) - pairs := make(map[string]string) + assert.Len(t, details, 11) + + 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 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: no arguments", func(t *testing.T) { - f := createFunctionForSQLHandle(t, true, false) + 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 + 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, "(x FLOAT)", pairs["signature"]) + assert.Equal(t, "FLOAT", pairs["returns"]) + 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 := "3.141592654::FLOAT" - dt := sdk.NewFunctionReturnsResultDataTypeRequest(nil).WithResultDataTypeOld(sdk.DataTypeFloat) - returns := sdk.NewFunctionReturnsRequest().WithResultDataType(*dt) - request := sdk.NewCreateForSQLFunctionRequest(id.SchemaObjectId(), *returns, definition).WithOrReplace(true) + t.Run("describe function: for SQL - no arguments", func(t *testing.T) { + f, fCleanup := testClientHelper().Function.CreateSqlNoArgs(t) + t.Cleanup(fCleanup) + id := f.ID() - argument := sdk.NewFunctionArgumentRequest("x", nil).WithArgDataTypeOld(sdk.DataTypeFloat) - request = request.WithArguments([]sdk.FunctionArgumentRequest{*argument}) - err := client.Functions.CreateForSQL(ctx, request) + details, err := client.Functions.Describe(ctx, id) require.NoError(t, err) - t.Cleanup(cleanupFunctionHandle(id)) - } + assert.Len(t, details, 4) + + pairs := make(map[string]string) + for _, detail := range details { + pairs[detail.Property] = *detail.Value + } + 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"]) + }) 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) @@ -475,21 +1673,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) @@ -538,7 +1728,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) @@ -593,6 +1783,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{ @@ -603,10 +1794,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)), + WithFunctionDefinitionWrapped(testClientHelper().Function.PythonIdentityDefinition(t, funcName, argName)), ) require.NoError(t, err) @@ -622,7 +1813,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"]) diff --git a/pkg/sdk/testint/setup_test.go b/pkg/sdk/testint/setup_test.go index ee949d3d67..c5b60802a4 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 [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 { + 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