1/*
2Copyright 2017 Google LLC
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17package spanner_test
18
19import (
20	"context"
21	"errors"
22	"fmt"
23	"sync"
24	"time"
25
26	"cloud.google.com/go/spanner"
27	"google.golang.org/api/iterator"
28	sppb "google.golang.org/genproto/googleapis/spanner/v1"
29	"google.golang.org/grpc/codes"
30	"google.golang.org/grpc/status"
31)
32
33func ExampleNewClient() {
34	ctx := context.Background()
35	const myDB = "projects/my-project/instances/my-instance/database/my-db"
36	client, err := spanner.NewClient(ctx, myDB)
37	if err != nil {
38		// TODO: Handle error.
39	}
40	_ = client // TODO: Use client.
41}
42
43const myDB = "projects/my-project/instances/my-instance/database/my-db"
44
45func ExampleNewClientWithConfig() {
46	ctx := context.Background()
47	const myDB = "projects/my-project/instances/my-instance/database/my-db"
48	client, err := spanner.NewClientWithConfig(ctx, myDB, spanner.ClientConfig{
49		NumChannels: 10,
50	})
51	if err != nil {
52		// TODO: Handle error.
53	}
54	_ = client     // TODO: Use client.
55	client.Close() // Close client when done.
56}
57
58func ExampleClient_Single() {
59	ctx := context.Background()
60	client, err := spanner.NewClient(ctx, myDB)
61	if err != nil {
62		// TODO: Handle error.
63	}
64	iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers"))
65	_ = iter // TODO: iterate using Next or Do.
66}
67
68func ExampleClient_ReadOnlyTransaction() {
69	ctx := context.Background()
70	client, err := spanner.NewClient(ctx, myDB)
71	if err != nil {
72		// TODO: Handle error.
73	}
74	t := client.ReadOnlyTransaction()
75	defer t.Close()
76	// TODO: Read with t using Read, ReadRow, ReadUsingIndex, or Query.
77}
78
79func ExampleClient_ReadWriteTransaction() {
80	ctx := context.Background()
81	client, err := spanner.NewClient(ctx, myDB)
82	if err != nil {
83		// TODO: Handle error.
84	}
85	_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
86		var balance int64
87		row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
88		if err != nil {
89			// This function will be called again if this is an IsAborted error.
90			return err
91		}
92		if err := row.Column(0, &balance); err != nil {
93			return err
94		}
95
96		if balance <= 10 {
97			return errors.New("insufficient funds in account")
98		}
99		balance -= 10
100		m := spanner.Update("Accounts", []string{"user", "balance"}, []interface{}{"alice", balance})
101		return txn.BufferWrite([]*spanner.Mutation{m})
102		// The buffered mutation will be committed. If the commit fails with an
103		// IsAborted error, this function will be called again.
104	})
105	if err != nil {
106		// TODO: Handle error.
107	}
108}
109
110func ExampleUpdate() {
111	ctx := context.Background()
112	client, err := spanner.NewClient(ctx, myDB)
113	if err != nil {
114		// TODO: Handle error.
115	}
116	_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
117		row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
118		if err != nil {
119			return err
120		}
121		var balance int64
122		if err := row.Column(0, &balance); err != nil {
123			return err
124		}
125		return txn.BufferWrite([]*spanner.Mutation{
126			spanner.Update("Accounts", []string{"user", "balance"}, []interface{}{"alice", balance + 10}),
127		})
128	})
129	if err != nil {
130		// TODO: Handle error.
131	}
132}
133
134// This example is the same as the one for Update, except for the use of UpdateMap.
135func ExampleUpdateMap() {
136	ctx := context.Background()
137	client, err := spanner.NewClient(ctx, myDB)
138	if err != nil {
139		// TODO: Handle error.
140	}
141	_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
142		row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
143		if err != nil {
144			return err
145		}
146		var balance int64
147		if err := row.Column(0, &balance); err != nil {
148			return err
149		}
150		return txn.BufferWrite([]*spanner.Mutation{
151			spanner.UpdateMap("Accounts", map[string]interface{}{
152				"user":    "alice",
153				"balance": balance + 10,
154			}),
155		})
156	})
157	if err != nil {
158		// TODO: Handle error.
159	}
160}
161
162// This example is the same as the one for Update, except for the use of
163// UpdateStruct.
164func ExampleUpdateStruct() {
165	ctx := context.Background()
166	client, err := spanner.NewClient(ctx, myDB)
167	if err != nil {
168		// TODO: Handle error.
169	}
170	type account struct {
171		User    string `spanner:"user"`
172		Balance int64  `spanner:"balance"`
173	}
174	_, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error {
175		row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
176		if err != nil {
177			return err
178		}
179		var balance int64
180		if err := row.Column(0, &balance); err != nil {
181			return err
182		}
183		m, err := spanner.UpdateStruct("Accounts", account{
184			User:    "alice",
185			Balance: balance + 10,
186		})
187		if err != nil {
188			return err
189		}
190		return txn.BufferWrite([]*spanner.Mutation{m})
191	})
192	if err != nil {
193		// TODO: Handle error.
194	}
195}
196
197func ExampleClient_Apply() {
198	ctx := context.Background()
199	client, err := spanner.NewClient(ctx, myDB)
200	if err != nil {
201		// TODO: Handle error.
202	}
203	m := spanner.Update("Users", []string{"name", "email"}, []interface{}{"alice", "a@example.com"})
204	_, err = client.Apply(ctx, []*spanner.Mutation{m})
205	if err != nil {
206		// TODO: Handle error.
207	}
208}
209
210func ExampleInsert() {
211	m := spanner.Insert("Users", []string{"name", "email"}, []interface{}{"alice", "a@example.com"})
212	_ = m // TODO: use with Client.Apply or in a ReadWriteTransaction.
213}
214
215func ExampleInsertMap() {
216	m := spanner.InsertMap("Users", map[string]interface{}{
217		"name":  "alice",
218		"email": "a@example.com",
219	})
220	_ = m // TODO: use with Client.Apply or in a ReadWriteTransaction.
221}
222
223func ExampleInsertStruct() {
224	type User struct {
225		Name, Email string
226	}
227	u := User{Name: "alice", Email: "a@example.com"}
228	m, err := spanner.InsertStruct("Users", u)
229	if err != nil {
230		// TODO: Handle error.
231	}
232	_ = m // TODO: use with Client.Apply or in a ReadWriteTransaction.
233}
234
235func ExampleDelete() {
236	m := spanner.Delete("Users", spanner.Key{"alice"})
237	_ = m // TODO: use with Client.Apply or in a ReadWriteTransaction.
238}
239
240func ExampleDelete_keyRange() {
241	m := spanner.Delete("Users", spanner.KeyRange{
242		Start: spanner.Key{"alice"},
243		End:   spanner.Key{"bob"},
244		Kind:  spanner.ClosedClosed,
245	})
246	_ = m // TODO: use with Client.Apply or in a ReadWriteTransaction.
247}
248
249func ExampleRowIterator_Next() {
250	ctx := context.Background()
251	client, err := spanner.NewClient(ctx, myDB)
252	if err != nil {
253		// TODO: Handle error.
254	}
255	iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers"))
256	defer iter.Stop()
257	for {
258		row, err := iter.Next()
259		if err == iterator.Done {
260			break
261		}
262		if err != nil {
263			// TODO: Handle error.
264		}
265		var firstName string
266		if err := row.Column(0, &firstName); err != nil {
267			// TODO: Handle error.
268		}
269		fmt.Println(firstName)
270	}
271}
272
273func ExampleRowIterator_Do() {
274	ctx := context.Background()
275	client, err := spanner.NewClient(ctx, myDB)
276	if err != nil {
277		// TODO: Handle error.
278	}
279	iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers"))
280	err = iter.Do(func(r *spanner.Row) error {
281		var firstName string
282		if err := r.Column(0, &firstName); err != nil {
283			return err
284		}
285		fmt.Println(firstName)
286		return nil
287	})
288	if err != nil {
289		// TODO: Handle error.
290	}
291}
292
293func ExampleRow_Size() {
294	ctx := context.Background()
295	client, err := spanner.NewClient(ctx, myDB)
296	if err != nil {
297		// TODO: Handle error.
298	}
299	row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
300	if err != nil {
301		// TODO: Handle error.
302	}
303	fmt.Println(row.Size()) // 2
304}
305
306func ExampleRow_ColumnName() {
307	ctx := context.Background()
308	client, err := spanner.NewClient(ctx, myDB)
309	if err != nil {
310		// TODO: Handle error.
311	}
312	row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
313	if err != nil {
314		// TODO: Handle error.
315	}
316	fmt.Println(row.ColumnName(1)) // "balance"
317}
318
319func ExampleRow_ColumnIndex() {
320	ctx := context.Background()
321	client, err := spanner.NewClient(ctx, myDB)
322	if err != nil {
323		// TODO: Handle error.
324	}
325	row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
326	if err != nil {
327		// TODO: Handle error.
328	}
329	index, err := row.ColumnIndex("balance")
330	if err != nil {
331		// TODO: Handle error.
332	}
333	fmt.Println(index)
334}
335
336func ExampleRow_ColumnNames() {
337	ctx := context.Background()
338	client, err := spanner.NewClient(ctx, myDB)
339	if err != nil {
340		// TODO: Handle error.
341	}
342	row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
343	if err != nil {
344		// TODO: Handle error.
345	}
346	fmt.Println(row.ColumnNames())
347}
348
349func ExampleRow_ColumnByName() {
350	ctx := context.Background()
351	client, err := spanner.NewClient(ctx, myDB)
352	if err != nil {
353		// TODO: Handle error.
354	}
355	row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
356	if err != nil {
357		// TODO: Handle error.
358	}
359	var balance int64
360	if err := row.ColumnByName("balance", &balance); err != nil {
361		// TODO: Handle error.
362	}
363	fmt.Println(balance)
364}
365
366func ExampleRow_Columns() {
367	ctx := context.Background()
368	client, err := spanner.NewClient(ctx, myDB)
369	if err != nil {
370		// TODO: Handle error.
371	}
372	row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
373	if err != nil {
374		// TODO: Handle error.
375	}
376	var name string
377	var balance int64
378	if err := row.Columns(&name, &balance); err != nil {
379		// TODO: Handle error.
380	}
381	fmt.Println(name, balance)
382}
383
384func ExampleRow_ToStruct() {
385	ctx := context.Background()
386	client, err := spanner.NewClient(ctx, myDB)
387	if err != nil {
388		// TODO: Handle error.
389	}
390	row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"})
391	if err != nil {
392		// TODO: Handle error.
393	}
394
395	type Account struct {
396		Name    string
397		Balance int64
398	}
399
400	var acct Account
401	if err := row.ToStruct(&acct); err != nil {
402		// TODO: Handle error.
403	}
404	fmt.Println(acct)
405}
406
407func ExampleReadOnlyTransaction_Read() {
408	ctx := context.Background()
409	client, err := spanner.NewClient(ctx, myDB)
410	if err != nil {
411		// TODO: Handle error.
412	}
413	iter := client.Single().Read(ctx, "Users",
414		spanner.KeySets(spanner.Key{"alice"}, spanner.Key{"bob"}),
415		[]string{"name", "email"})
416	_ = iter // TODO: iterate using Next or Do.
417}
418
419func ExampleReadOnlyTransaction_ReadUsingIndex() {
420	ctx := context.Background()
421	client, err := spanner.NewClient(ctx, myDB)
422	if err != nil {
423		// TODO: Handle error.
424	}
425	iter := client.Single().ReadUsingIndex(ctx, "Users",
426		"UsersByEmail",
427		spanner.KeySets(spanner.Key{"a@example.com"}, spanner.Key{"b@example.com"}),
428		[]string{"name", "email"})
429	_ = iter // TODO: iterate using Next or Do.
430}
431
432func ExampleReadOnlyTransaction_ReadWithOptions() {
433	ctx := context.Background()
434	client, err := spanner.NewClient(ctx, myDB)
435	if err != nil {
436		// TODO: Handle error.
437	}
438	// Use an index, and limit to 100 rows at most.
439	iter := client.Single().ReadWithOptions(ctx, "Users",
440		spanner.KeySets(spanner.Key{"a@example.com"}, spanner.Key{"b@example.com"}),
441		[]string{"name", "email"}, &spanner.ReadOptions{
442			Index: "UsersByEmail",
443			Limit: 100,
444		})
445	_ = iter // TODO: iterate using Next or Do.
446}
447
448func ExampleReadOnlyTransaction_ReadRow() {
449	ctx := context.Background()
450	client, err := spanner.NewClient(ctx, myDB)
451	if err != nil {
452		// TODO: Handle error.
453	}
454	row, err := client.Single().ReadRow(ctx, "Users", spanner.Key{"alice"},
455		[]string{"name", "email"})
456	if err != nil {
457		// TODO: Handle error.
458	}
459	_ = row // TODO: use row
460}
461
462func ExampleReadOnlyTransaction_Query() {
463	ctx := context.Background()
464	client, err := spanner.NewClient(ctx, myDB)
465	if err != nil {
466		// TODO: Handle error.
467	}
468	iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers"))
469	_ = iter // TODO: iterate using Next or Do.
470}
471
472func ExampleNewStatement() {
473	stmt := spanner.NewStatement("SELECT FirstName, LastName FROM SINGERS WHERE LastName >= @start")
474	stmt.Params["start"] = "Dylan"
475	// TODO: Use stmt in Query.
476}
477
478func ExampleNewStatement_structLiteral() {
479	stmt := spanner.Statement{
480		SQL: `SELECT FirstName, LastName FROM SINGERS WHERE LastName = ("Lea", "Martin")`,
481	}
482	_ = stmt // TODO: Use stmt in Query.
483}
484
485func ExampleStructParam() {
486	stmt := spanner.Statement{
487		SQL: "SELECT * FROM SINGERS WHERE (FirstName, LastName) = @singerinfo",
488		Params: map[string]interface{}{
489			"singerinfo": struct {
490				FirstName string
491				LastName  string
492			}{"Bob", "Dylan"},
493		},
494	}
495	_ = stmt // TODO: Use stmt in Query.
496}
497
498func ExampleArrayOfStructParam() {
499	stmt := spanner.Statement{
500		SQL: "SELECT * FROM SINGERS WHERE (FirstName, LastName) IN UNNEST(@singerinfo)",
501		Params: map[string]interface{}{
502			"singerinfo": []struct {
503				FirstName string
504				LastName  string
505			}{
506				{"Ringo", "Starr"},
507				{"John", "Lennon"},
508			},
509		},
510	}
511	_ = stmt // TODO: Use stmt in Query.
512}
513
514func ExampleReadOnlyTransaction_Timestamp() {
515	ctx := context.Background()
516	client, err := spanner.NewClient(ctx, myDB)
517	if err != nil {
518		// TODO: Handle error.
519	}
520	txn := client.Single()
521	row, err := txn.ReadRow(ctx, "Users", spanner.Key{"alice"},
522		[]string{"name", "email"})
523	if err != nil {
524		// TODO: Handle error.
525	}
526	readTimestamp, err := txn.Timestamp()
527	if err != nil {
528		// TODO: Handle error.
529	}
530	fmt.Println("read happened at", readTimestamp)
531	_ = row // TODO: use row
532}
533
534func ExampleReadOnlyTransaction_WithTimestampBound() {
535	ctx := context.Background()
536	client, err := spanner.NewClient(ctx, myDB)
537	if err != nil {
538		// TODO: Handle error.
539	}
540	txn := client.Single().WithTimestampBound(spanner.MaxStaleness(30 * time.Second))
541	row, err := txn.ReadRow(ctx, "Users", spanner.Key{"alice"}, []string{"name", "email"})
542	if err != nil {
543		// TODO: Handle error.
544	}
545	_ = row // TODO: use row
546	readTimestamp, err := txn.Timestamp()
547	if err != nil {
548		// TODO: Handle error.
549	}
550	fmt.Println("read happened at", readTimestamp)
551}
552
553func ExampleGenericColumnValue_Decode() {
554	// In real applications, rows can be retrieved by methods like client.Single().ReadRow().
555	row, err := spanner.NewRow([]string{"intCol", "strCol"}, []interface{}{42, "my-text"})
556	if err != nil {
557		// TODO: Handle error.
558	}
559	for i := 0; i < row.Size(); i++ {
560		var col spanner.GenericColumnValue
561		if err := row.Column(i, &col); err != nil {
562			// TODO: Handle error.
563		}
564		switch col.Type.Code {
565		case sppb.TypeCode_INT64:
566			var v int64
567			if err := col.Decode(&v); err != nil {
568				// TODO: Handle error.
569			}
570			fmt.Println("int", v)
571		case sppb.TypeCode_STRING:
572			var v string
573			if err := col.Decode(&v); err != nil {
574				// TODO: Handle error.
575			}
576			fmt.Println("string", v)
577		}
578	}
579	// Output:
580	// int 42
581	// string my-text
582}
583
584func ExampleClient_BatchReadOnlyTransaction() {
585	ctx := context.Background()
586	var (
587		client *spanner.Client
588		txn    *spanner.BatchReadOnlyTransaction
589		err    error
590	)
591	if client, err = spanner.NewClient(ctx, myDB); err != nil {
592		// TODO: Handle error.
593	}
594	defer client.Close()
595	if txn, err = client.BatchReadOnlyTransaction(ctx, spanner.StrongRead()); err != nil {
596		// TODO: Handle error.
597	}
598	defer txn.Close()
599
600	// Singer represents the elements in a row from the Singers table.
601	type Singer struct {
602		SingerID   int64
603		FirstName  string
604		LastName   string
605		SingerInfo []byte
606	}
607	stmt := spanner.Statement{SQL: "SELECT * FROM Singers;"}
608	partitions, err := txn.PartitionQuery(ctx, stmt, spanner.PartitionOptions{})
609	if err != nil {
610		// TODO: Handle error.
611	}
612	// Note: here we use multiple goroutines, but you should use separate
613	// processes/machines.
614	wg := sync.WaitGroup{}
615	for i, p := range partitions {
616		wg.Add(1)
617		go func(i int, p *spanner.Partition) {
618			defer wg.Done()
619			iter := txn.Execute(ctx, p)
620			defer iter.Stop()
621			for {
622				row, err := iter.Next()
623				if err == iterator.Done {
624					break
625				} else if err != nil {
626					// TODO: Handle error.
627				}
628				var s Singer
629				if err := row.ToStruct(&s); err != nil {
630					// TODO: Handle error.
631				}
632				_ = s // TODO: Process the row.
633			}
634		}(i, p)
635	}
636	wg.Wait()
637}
638
639func ExampleCommitTimestamp() {
640	ctx := context.Background()
641	client, err := spanner.NewClient(ctx, myDB)
642	if err != nil {
643		// TODO: Handle error.
644	}
645
646	type account struct {
647		User     string
648		Creation spanner.NullTime // time.Time can also be used if column isNOT NULL
649	}
650
651	a := account{User: "Joe", Creation: spanner.NullTime{spanner.CommitTimestamp, true}}
652	m, err := spanner.InsertStruct("Accounts", a)
653	if err != nil {
654		// TODO: Handle error.
655	}
656	_, err = client.Apply(ctx, []*spanner.Mutation{m}, spanner.ApplyAtLeastOnce())
657	if err != nil {
658		// TODO: Handle error.
659	}
660
661	if r, e := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"Joe"}, []string{"User", "Creation"}); e != nil {
662		// TODO: Handle error.
663	} else {
664		var got account
665		if err := r.ToStruct(&got); err != nil {
666			// TODO: Handle error.
667		}
668		_ = got // TODO: Process row.
669	}
670}
671
672func ExampleStatement_regexpContains() {
673	// Search for accounts with valid emails using regexp as per:
674	//   https://cloud.google.com/spanner/docs/functions-and-operators#regexp_contains
675	stmt := spanner.Statement{
676		SQL: `SELECT * FROM users WHERE REGEXP_CONTAINS(email, @valid_email)`,
677		Params: map[string]interface{}{
678			"valid_email": `\Q@\E`,
679		},
680	}
681	_ = stmt // TODO: Use stmt in a query.
682}
683
684func ExampleKeySets() {
685	ctx := context.Background()
686	client, err := spanner.NewClient(ctx, myDB)
687	if err != nil {
688		// TODO: Handle error.
689	}
690	// Get some rows from the Accounts table using a secondary index. In this case we get all users who are in Georgia.
691	iter := client.Single().ReadUsingIndex(context.Background(), "Accounts", "idx_state", spanner.Key{"GA"}, []string{"state"})
692
693	// Create a empty KeySet by calling the KeySets function with no parameters.
694	ks := spanner.KeySets()
695
696	// Loop the results of a previous query iterator.
697	for {
698		row, err := iter.Next()
699		if err == iterator.Done {
700			break
701		} else if err != nil {
702			// TODO: Handle error.
703		}
704		var id string
705		err = row.ColumnByName("User", &id)
706		if err != nil {
707			// TODO: Handle error.
708		}
709		ks = spanner.KeySets(spanner.KeySets(spanner.Key{id}, ks))
710	}
711
712	_ = ks //TODO: Go use the KeySet in another query.
713
714}
715
716func ExampleNewReadWriteStmtBasedTransaction() {
717	ctx := context.Background()
718	client, err := spanner.NewClient(ctx, myDB)
719	if err != nil {
720		// TODO: Handle error.
721	}
722	defer client.Close()
723
724	f := func(tx *spanner.ReadWriteStmtBasedTransaction) error {
725		var balance int64
726		row, err := tx.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"})
727		if err != nil {
728			return err
729		}
730		if err := row.Column(0, &balance); err != nil {
731			return err
732		}
733
734		if balance <= 10 {
735			return errors.New("insufficient funds in account")
736		}
737		balance -= 10
738		m := spanner.Update("Accounts", []string{"user", "balance"}, []interface{}{"alice", balance})
739		return tx.BufferWrite([]*spanner.Mutation{m})
740	}
741
742	for {
743		tx, err := spanner.NewReadWriteStmtBasedTransaction(ctx, client)
744		if err != nil {
745			// TODO: Handle error.
746			break
747		}
748		err = f(tx)
749		if err != nil && status.Code(err) != codes.Aborted {
750			tx.Rollback(ctx)
751			// TODO: Handle error.
752			break
753		} else if err == nil {
754			_, err = tx.Commit(ctx)
755			if err == nil {
756				break
757			} else if status.Code(err) != codes.Aborted {
758				// TODO: Handle error.
759				break
760			}
761		}
762		// Set a default sleep time if the server delay is absent.
763		delay := 10 * time.Millisecond
764		if serverDelay, hasServerDelay := spanner.ExtractRetryDelay(err); hasServerDelay {
765			delay = serverDelay
766		}
767		time.Sleep(delay)
768	}
769}
770