diff --git a/bigquery/integration_test.go b/bigquery/integration_test.go index 94d98f1401d..093188187e0 100644 --- a/bigquery/integration_test.go +++ b/bigquery/integration_test.go @@ -2916,18 +2916,33 @@ func TestIntegration_RoutineJSUDF(t *testing.T) { // Create a scalar UDF routine via API. routineID := routineIDs.New() routine := dataset.Routine(routineID) - err := routine.Create(ctx, &RoutineMetadata{ + meta := &RoutineMetadata{ Language: "JAVASCRIPT", Type: "SCALAR_FUNCTION", - Description: "capitalizes using javascript", + Description: "capitalizes using javascript", + DeterminismLevel: Deterministic, Arguments: []*RoutineArgument{ {Name: "instr", Kind: "FIXED_TYPE", DataType: &StandardSQLDataType{TypeKind: "STRING"}}, }, ReturnType: &StandardSQLDataType{TypeKind: "STRING"}, Body: "return instr.toUpperCase();", - }) - if err != nil { + } + if err := routine.Create(ctx, meta); err != nil { t.Fatalf("Create: %v", err) } + + newMeta := &RoutineMetadataToUpdate{ + Language: meta.Language, + Body: meta.Body, + Arguments: meta.Arguments, + Description: meta.Description, + ReturnType: meta.ReturnType, + Type: meta.Type, + + DeterminismLevel: NotDeterministic, + } + if _, err := routine.Update(ctx, newMeta, ""); err != nil { + t.Fatalf("Update: %v", err) + } } func TestIntegration_RoutineComplexTypes(t *testing.T) { diff --git a/bigquery/routine.go b/bigquery/routine.go index 58f1a78b798..6f6c3a177f4 100644 --- a/bigquery/routine.go +++ b/bigquery/routine.go @@ -129,13 +129,27 @@ func (r *Routine) Delete(ctx context.Context) (err error) { return req.Do() } +// RoutineDeterminism specifies the level of determinism that javascript User Defined Functions +// exhibit. +type RoutineDeterminism string + +const ( + // Deterministic indicates that two calls with the same input to a UDF yield the same output. + Deterministic RoutineDeterminism = "DETERMINISTIC" + // NotDeterministic indicates that the output of the UDF is not guaranteed to yield the same + // output each time for a given set of inputs. + NotDeterministic RoutineDeterminism = "NOT_DETERMINISTIC" +) + // RoutineMetadata represents details of a given BigQuery Routine. type RoutineMetadata struct { ETag string // Type indicates the type of routine, such as SCALAR_FUNCTION or PROCEDURE. - Type string - CreationTime time.Time - Description string + Type string + CreationTime time.Time + Description string + // DeterminismLevel is only applicable to Javascript UDFs. + DeterminismLevel RoutineDeterminism LastModifiedTime time.Time // Language of the routine, such as SQL or JAVASCRIPT. Language string @@ -161,6 +175,7 @@ func (rm *RoutineMetadata) toBQ() (*bq.Routine, error) { return r, nil } r.Description = rm.Description + r.DeterminismLevel = string(rm.DeterminismLevel) r.Language = rm.Language r.RoutineType = rm.Type r.DefinitionBody = rm.Body @@ -280,6 +295,7 @@ func routineArgumentsToBQ(in []*RoutineArgument) ([]*bq.Argument, error) { type RoutineMetadataToUpdate struct { Arguments []*RoutineArgument Description optional.String + DeterminismLevel optional.String Type optional.String Language optional.String Body optional.String @@ -299,6 +315,21 @@ func (rm *RoutineMetadataToUpdate) toBQ() (*bq.Routine, error) { r.Description = optional.ToString(rm.Description) forceSend("Description") } + if rm.DeterminismLevel != nil { + processed := false + // Allow either string or RoutineDeterminism, a type based on string. + if x, ok := rm.DeterminismLevel.(RoutineDeterminism); ok { + r.DeterminismLevel = string(x) + processed = true + } + if x, ok := rm.DeterminismLevel.(string); ok { + r.DeterminismLevel = x + processed = true + } + if !processed { + panic(fmt.Sprintf("DeterminismLevel should be either type string or RoutineDetermism in update, got %T", rm.DeterminismLevel)) + } + } if rm.Arguments != nil { if len(rm.Arguments) == 0 { nullField("Arguments") @@ -348,6 +379,7 @@ func bqToRoutineMetadata(r *bq.Routine) (*RoutineMetadata, error) { Type: r.RoutineType, CreationTime: unixMillisToTime(r.CreationTime), Description: r.Description, + DeterminismLevel: RoutineDeterminism(r.DeterminismLevel), LastModifiedTime: unixMillisToTime(r.LastModifiedTime), Language: r.Language, ImportedLibraries: r.ImportedLibraries, diff --git a/bigquery/routine_test.go b/bigquery/routine_test.go index 00122a44e0c..f0c122ea113 100644 --- a/bigquery/routine_test.go +++ b/bigquery/routine_test.go @@ -80,6 +80,7 @@ func TestRoutineTypeConversions(t *testing.T) { DefinitionBody: "body", Description: "desc", Etag: "etag", + DeterminismLevel: "DETERMINISTIC", RoutineType: "type", Language: "lang", ReturnType: &bq.StandardSqlDataType{TypeKind: "INT64"}, @@ -88,6 +89,7 @@ func TestRoutineTypeConversions(t *testing.T) { CreationTime: aTime, LastModifiedTime: aTime, Description: "desc", + DeterminismLevel: Deterministic, Body: "body", ETag: "etag", Type: "type",