Skip to content

Commit da9ab8f

Browse files
authored
docs(bigquery): add routine preview samples (#5356)
* docs(bigquery): add routine preview samples This PR also includes minor docstring fixes for update table
1 parent 7149e3c commit da9ab8f

File tree

6 files changed

+395
-0
lines changed

6 files changed

+395
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package routine
16+
17+
// [START bigquery_create_routine_preview]
18+
import (
19+
"context"
20+
"fmt"
21+
"io"
22+
23+
"cloud.google.com/go/bigquery/v2/apiv2/bigquerypb"
24+
"cloud.google.com/go/bigquery/v2/apiv2_client"
25+
26+
"github.com/googleapis/gax-go/v2/apierror"
27+
28+
"google.golang.org/grpc/codes"
29+
"google.golang.org/protobuf/encoding/protojson"
30+
)
31+
32+
// createRoutine demonstrates creation of a new routine that represents a SQL User Defined Function (UDF).
33+
func createRoutine(client *apiv2_client.Client, w io.Writer, projectID, datasetID, routineID string) error {
34+
// client can be instantiated per-RPC service, or use cloud.google.com/go/bigquery/v2/apiv2_client to create
35+
// an aggregate client.
36+
//
37+
// projectID := "my-project-id"
38+
// datasetID := "mydataset"
39+
// routineID := "myroutine"
40+
ctx := context.Background()
41+
42+
// Construct a request, populating some of the available configuration
43+
// settings.
44+
req := &bigquerypb.InsertRoutineRequest{
45+
ProjectId: projectID,
46+
DatasetId: datasetID,
47+
Routine: &bigquerypb.Routine{
48+
RoutineReference: &bigquerypb.RoutineReference{
49+
ProjectId: projectID,
50+
DatasetId: datasetID,
51+
RoutineId: routineID,
52+
},
53+
RoutineType: bigquerypb.Routine_SCALAR_FUNCTION,
54+
Arguments: []*bigquerypb.Routine_Argument{
55+
{
56+
Name: "x",
57+
DataType: &bigquerypb.StandardSqlDataType{
58+
TypeKind: bigquerypb.StandardSqlDataType_INT64,
59+
},
60+
},
61+
},
62+
ReturnType: &bigquerypb.StandardSqlDataType{
63+
TypeKind: bigquerypb.StandardSqlDataType_INT64,
64+
},
65+
DefinitionBody: "x * 3",
66+
},
67+
}
68+
resp, err := client.InsertRoutine(ctx, req)
69+
if err != nil {
70+
// Examine the error structure more deeply.
71+
if apierr, ok := apierror.FromError(err); ok {
72+
if status := apierr.GRPCStatus(); status.Code() == codes.AlreadyExists {
73+
// The error was due to the routine already existing. For this sample
74+
// we don't consider that a failure, so return nil.
75+
return nil
76+
}
77+
}
78+
return fmt.Errorf("InsertRoutine: %w", err)
79+
}
80+
// Print the JSON representation of the response to the provided writer.
81+
fmt.Fprintf(w, "Response from insert: %s", protojson.Format(resp))
82+
return nil
83+
}
84+
85+
// [END bigquery_create_routine_preview]
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package routine
16+
17+
// [START bigquery_delete_routine_preview]
18+
import (
19+
"context"
20+
"fmt"
21+
22+
"cloud.google.com/go/bigquery/v2/apiv2/bigquerypb"
23+
"cloud.google.com/go/bigquery/v2/apiv2_client"
24+
"github.com/googleapis/gax-go/v2/apierror"
25+
26+
"google.golang.org/grpc/codes"
27+
)
28+
29+
// deleteRoutine demonstrates deleting a routine from BigQuery.
30+
func deleteRoutine(client *apiv2_client.Client, projectID, datasetID, routineID string) error {
31+
// client can be instantiated per-RPC service, or use cloud.google.com/go/bigquery/v2/apiv2_client to create
32+
// an aggregate client.
33+
//
34+
// projectID := "my-project-id"
35+
// datasetID := "mydataset"
36+
// routineID := "myroutine"
37+
ctx := context.Background()
38+
39+
req := &bigquerypb.DeleteRoutineRequest{
40+
ProjectId: projectID,
41+
DatasetId: datasetID,
42+
RoutineId: routineID,
43+
}
44+
45+
// Deleting a routine doesn't return information, but it may produce an error.
46+
if err := client.DeleteRoutine(ctx, req); err != nil {
47+
if apierr, ok := apierror.FromError(err); ok {
48+
if status := apierr.GRPCStatus(); status.Code() == codes.NotFound {
49+
// The error indicates the routine isn't present. Possibly another process removed
50+
// the routine, or perhaps there was a partial failure and this was handled via automatic retry.
51+
// In any case, treat this as a success.
52+
return nil
53+
}
54+
}
55+
return fmt.Errorf("DeleteRoutine: %w", err)
56+
}
57+
return nil
58+
}
59+
60+
// [END bigquery_delete_routine_preview]
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package routine
16+
17+
// [START bigquery_list_routines_preview]
18+
import (
19+
"context"
20+
"fmt"
21+
"io"
22+
23+
"cloud.google.com/go/bigquery/v2/apiv2/bigquerypb"
24+
"cloud.google.com/go/bigquery/v2/apiv2_client"
25+
26+
"google.golang.org/api/iterator"
27+
"google.golang.org/protobuf/types/known/wrapperspb"
28+
)
29+
30+
// listRoutines demonstrates iterating through the routines within a specified dataset.
31+
func listRoutines(client *apiv2_client.Client, w io.Writer, projectID, datasetID string) error {
32+
// client can be instantiated per-RPC service, or use cloud.google.com/go/bigquery/v2/apiv2_client to create
33+
// an aggregate client.
34+
//
35+
// projectID := "my-project-id"
36+
// datasetID := "mydataset"
37+
ctx := context.Background()
38+
39+
req := &bigquerypb.ListRoutinesRequest{
40+
ProjectId: projectID,
41+
DatasetId: datasetID,
42+
// MaxResults is the per-page threshold (aka page size). Generally you should only
43+
// worry about setting this if you're executing code in a memory constrained environment
44+
// and don't want to process large pages of results. BigQuery will select a reasonable
45+
// page size automatically.
46+
MaxResults: &wrapperspb.UInt32Value{Value: 100},
47+
}
48+
49+
// ListRoutines returns an iterator so users don't have to manage pagination when processing
50+
// the results.
51+
it := client.ListRoutines(ctx, req)
52+
53+
// Process data from the iterator one result at a time. The internal implementation of the iterator
54+
// is fetching pages at a time.
55+
for {
56+
routine, err := it.Next()
57+
if err == iterator.Done {
58+
// We're reached the end of the iteration, break the loop.
59+
break
60+
}
61+
if err != nil {
62+
return fmt.Errorf("iterator errored: %w", err)
63+
}
64+
// Print basic information to the provided writer.
65+
fmt.Fprintf(w, "routine %q reports type %q\n", routine.GetRoutineReference().GetRoutineId(), routine.GetRoutineType().String())
66+
}
67+
return nil
68+
}
69+
70+
// [END bigquery_list_routines_preview]
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package routine
16+
17+
// [START bigquery_update_routine_preview]
18+
import (
19+
"context"
20+
"fmt"
21+
"io"
22+
23+
"cloud.google.com/go/bigquery/v2/apiv2/bigquerypb"
24+
"cloud.google.com/go/bigquery/v2/apiv2_client"
25+
"github.com/googleapis/gax-go/v2/apierror"
26+
"github.com/googleapis/gax-go/v2/callctx"
27+
28+
"google.golang.org/grpc/codes"
29+
)
30+
31+
// updateRoutine demonstrates making partial updates to an existing routine's metadata.
32+
func updateRoutine(client *apiv2_client.Client, w io.Writer, projectID, datasetID, routineID string) error {
33+
// client can be instantiated per-RPC service, or use cloud.google.com/go/bigquery/v2/apiv2_client to create
34+
// an aggregate client.
35+
//
36+
// projectID := "my-project-id"
37+
// datasetID := "mydataset"
38+
// routineID := "myroutine"
39+
ctx := context.Background()
40+
41+
// Fetch the existing routine metadata prior to making any modifications.
42+
// This allows us to use optimistic concurrency controls to avoid overwriting
43+
// other changes.
44+
meta, err := client.GetRoutine(ctx, &bigquerypb.GetRoutineRequest{
45+
ProjectId: projectID,
46+
DatasetId: datasetID,
47+
RoutineId: routineID,
48+
})
49+
if err != nil {
50+
return fmt.Errorf("GetRoutine: %w", err)
51+
}
52+
53+
// Construct an update request, copying the current state of the
54+
// request since we'll be doing a full update.
55+
req := &bigquerypb.UpdateRoutineRequest{
56+
ProjectId: projectID,
57+
DatasetId: datasetID,
58+
RoutineId: routineID,
59+
Routine: meta,
60+
}
61+
// Modify the request to change the description.
62+
req.GetRoutine().Description = "Updated description."
63+
64+
// Now, use the ETag from the original metadata to guard against conflicting writes.
65+
// The callctx package let's us inject headers in a transport agnostic fashion (gRPC or HTTP).
66+
patchCtx := callctx.SetHeaders(ctx, "if-match", meta.GetEtag())
67+
resp, err := client.UpdateRoutine(patchCtx, req)
68+
if err != nil {
69+
if apierr, ok := apierror.FromError(err); ok {
70+
if status := apierr.GRPCStatus(); status.Code() == codes.FailedPrecondition {
71+
// The error was due to precondition failing (the If-Match constraint).
72+
// For this example we're not doing anything overly stateful with the dataset
73+
// so we simply return a more readable outer error.
74+
return fmt.Errorf("routine etag changed between Get and Update: %w", err)
75+
}
76+
}
77+
return fmt.Errorf("UpdateRoutine: %w", err)
78+
}
79+
// Print the values we expected to be modified.
80+
fmt.Fprintf(w, "Description: %s\n", resp.GetDescription())
81+
return nil
82+
}
83+
84+
// [END bigquery_update_routine_preview]
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Package routine provides some basic snippet examples for working with routines using
16+
// the preview BigQuery Cloud Client Library.
17+
package routine
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"io"
23+
"testing"
24+
"time"
25+
26+
"cloud.google.com/go/bigquery/v2/apiv2/bigquerypb"
27+
"cloud.google.com/go/bigquery/v2/apiv2_client"
28+
"github.com/GoogleCloudPlatform/golang-samples/internal/testutil"
29+
)
30+
31+
const testTimeout = 30 * time.Second
32+
const testLocation = "us-west1"
33+
34+
func TestRoutineSnippet(t *testing.T) {
35+
tc := testutil.SystemTest(t)
36+
names := []string{"gRPC", "REST"}
37+
38+
for _, name := range names {
39+
t.Run(name, func(t *testing.T) {
40+
t.Parallel()
41+
ctx, cancel := context.WithTimeout(context.Background(), testTimeout)
42+
defer cancel()
43+
// Setup client.
44+
var client *apiv2_client.Client
45+
var err error
46+
if name == "gRPC" {
47+
client, err = apiv2_client.NewClient(ctx)
48+
} else {
49+
client, err = apiv2_client.NewRESTClient(ctx)
50+
}
51+
if err != nil {
52+
t.Fatalf("client creation failed: %v", err)
53+
}
54+
defer client.Close()
55+
56+
// Create a test dataset.
57+
projID := tc.ProjectID
58+
dsID := fmt.Sprintf("snippettesting_routines_%s_%d", name, time.Now().UnixNano())
59+
60+
_, err = client.InsertDataset(ctx, &bigquerypb.InsertDatasetRequest{
61+
ProjectId: projID,
62+
Dataset: &bigquerypb.Dataset{
63+
DatasetReference: &bigquerypb.DatasetReference{
64+
ProjectId: projID,
65+
DatasetId: dsID,
66+
},
67+
Location: testLocation,
68+
},
69+
})
70+
if err != nil {
71+
t.Fatalf("couldn't create test dataset: %v", err)
72+
}
73+
defer client.DeleteDataset(ctx, &bigquerypb.DeleteDatasetRequest{
74+
ProjectId: projID,
75+
DatasetId: dsID,
76+
DeleteContents: true,
77+
})
78+
79+
routineID := fmt.Sprintf("routinesnippet_%s_%d", name, time.Now().UnixNano())
80+
81+
if err := createRoutine(client, io.Discard, projID, dsID, routineID); err != nil {
82+
t.Fatalf("createRoutine(%q,%q,%q): %v", projID, dsID, routineID, err)
83+
}
84+
if err := updateRoutine(client, io.Discard, projID, dsID, routineID); err != nil {
85+
t.Fatalf("updateRoutine(%q,%q,%q): %v", projID, dsID, routineID, err)
86+
}
87+
if err := listRoutines(client, io.Discard, projID, dsID); err != nil {
88+
t.Fatalf("listRoutines(%q,%q): %v", projID, dsID, err)
89+
}
90+
if err := deleteRoutine(client, projID, dsID, routineID); err != nil {
91+
t.Fatalf("deleteRoutine(%q,%q,%q): %v", projID, dsID, routineID, err)
92+
}
93+
})
94+
}
95+
}

0 commit comments

Comments
 (0)