| /* |
| Copyright 2017 Google LLC |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| package spanner_test |
| |
| import ( |
| "context" |
| "errors" |
| "fmt" |
| "sync" |
| "time" |
| |
| "cloud.google.com/go/spanner" |
| "google.golang.org/api/iterator" |
| sppb "google.golang.org/genproto/googleapis/spanner/v1" |
| ) |
| |
| func ExampleNewClient() { |
| ctx := context.Background() |
| const myDB = "projects/my-project/instances/my-instance/database/my-db" |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| _ = client // TODO: Use client. |
| } |
| |
| const myDB = "projects/my-project/instances/my-instance/database/my-db" |
| |
| func ExampleNewClientWithConfig() { |
| ctx := context.Background() |
| const myDB = "projects/my-project/instances/my-instance/database/my-db" |
| client, err := spanner.NewClientWithConfig(ctx, myDB, spanner.ClientConfig{ |
| NumChannels: 10, |
| }) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| _ = client // TODO: Use client. |
| client.Close() // Close client when done. |
| } |
| |
| func ExampleClient_Single() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers")) |
| _ = iter // TODO: iterate using Next or Do. |
| } |
| |
| func ExampleClient_ReadOnlyTransaction() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| t := client.ReadOnlyTransaction() |
| defer t.Close() |
| // TODO: Read with t using Read, ReadRow, ReadUsingIndex, or Query. |
| } |
| |
| func ExampleClient_ReadWriteTransaction() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { |
| var balance int64 |
| row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"}) |
| if err != nil { |
| // This function will be called again if this is an IsAborted error. |
| return err |
| } |
| if err := row.Column(0, &balance); err != nil { |
| return err |
| } |
| |
| if balance <= 10 { |
| return errors.New("insufficient funds in account") |
| } |
| balance -= 10 |
| m := spanner.Update("Accounts", []string{"user", "balance"}, []interface{}{"alice", balance}) |
| return txn.BufferWrite([]*spanner.Mutation{m}) |
| // The buffered mutation will be committed. If the commit fails with an |
| // IsAborted error, this function will be called again. |
| }) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| } |
| |
| func ExampleUpdate() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { |
| row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"}) |
| if err != nil { |
| return err |
| } |
| var balance int64 |
| if err := row.Column(0, &balance); err != nil { |
| return err |
| } |
| return txn.BufferWrite([]*spanner.Mutation{ |
| spanner.Update("Accounts", []string{"user", "balance"}, []interface{}{"alice", balance + 10}), |
| }) |
| }) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| } |
| |
| // This example is the same as the one for Update, except for the use of UpdateMap. |
| func ExampleUpdateMap() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { |
| row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"}) |
| if err != nil { |
| return err |
| } |
| var balance int64 |
| if err := row.Column(0, &balance); err != nil { |
| return err |
| } |
| return txn.BufferWrite([]*spanner.Mutation{ |
| spanner.UpdateMap("Accounts", map[string]interface{}{ |
| "user": "alice", |
| "balance": balance + 10, |
| }), |
| }) |
| }) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| } |
| |
| // This example is the same as the one for Update, except for the use of |
| // UpdateStruct. |
| func ExampleUpdateStruct() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| type account struct { |
| User string `spanner:"user"` |
| Balance int64 `spanner:"balance"` |
| } |
| _, err = client.ReadWriteTransaction(ctx, func(ctx context.Context, txn *spanner.ReadWriteTransaction) error { |
| row, err := txn.ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"balance"}) |
| if err != nil { |
| return err |
| } |
| var balance int64 |
| if err := row.Column(0, &balance); err != nil { |
| return err |
| } |
| m, err := spanner.UpdateStruct("Accounts", account{ |
| User: "alice", |
| Balance: balance + 10, |
| }) |
| if err != nil { |
| return err |
| } |
| return txn.BufferWrite([]*spanner.Mutation{m}) |
| }) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| } |
| |
| func ExampleClient_Apply() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| m := spanner.Update("Users", []string{"name", "email"}, []interface{}{"alice", "a@example.com"}) |
| _, err = client.Apply(ctx, []*spanner.Mutation{m}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| } |
| |
| func ExampleInsert() { |
| m := spanner.Insert("Users", []string{"name", "email"}, []interface{}{"alice", "a@example.com"}) |
| _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction. |
| } |
| |
| func ExampleInsertMap() { |
| m := spanner.InsertMap("Users", map[string]interface{}{ |
| "name": "alice", |
| "email": "a@example.com", |
| }) |
| _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction. |
| } |
| |
| func ExampleInsertStruct() { |
| type User struct { |
| Name, Email string |
| } |
| u := User{Name: "alice", Email: "a@example.com"} |
| m, err := spanner.InsertStruct("Users", u) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction. |
| } |
| |
| func ExampleDelete() { |
| m := spanner.Delete("Users", spanner.Key{"alice"}) |
| _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction. |
| } |
| |
| func ExampleDelete_keyRange() { |
| m := spanner.Delete("Users", spanner.KeyRange{ |
| Start: spanner.Key{"alice"}, |
| End: spanner.Key{"bob"}, |
| Kind: spanner.ClosedClosed, |
| }) |
| _ = m // TODO: use with Client.Apply or in a ReadWriteTransaction. |
| } |
| |
| func ExampleRowIterator_Next() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers")) |
| defer iter.Stop() |
| for { |
| row, err := iter.Next() |
| if err == iterator.Done { |
| break |
| } |
| if err != nil { |
| // TODO: Handle error. |
| } |
| var firstName string |
| if err := row.Column(0, &firstName); err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println(firstName) |
| } |
| } |
| |
| func ExampleRowIterator_Do() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers")) |
| err = iter.Do(func(r *spanner.Row) error { |
| var firstName string |
| if err := r.Column(0, &firstName); err != nil { |
| return err |
| } |
| fmt.Println(firstName) |
| return nil |
| }) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| } |
| |
| func ExampleRow_Size() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println(row.Size()) // 2 |
| } |
| |
| func ExampleRow_ColumnName() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println(row.ColumnName(1)) // "balance" |
| } |
| |
| func ExampleRow_ColumnIndex() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| index, err := row.ColumnIndex("balance") |
| if err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println(index) |
| } |
| |
| func ExampleRow_ColumnNames() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println(row.ColumnNames()) |
| } |
| |
| func ExampleRow_ColumnByName() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| var balance int64 |
| if err := row.ColumnByName("balance", &balance); err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println(balance) |
| } |
| |
| func ExampleRow_Columns() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| var name string |
| var balance int64 |
| if err := row.Columns(&name, &balance); err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println(name, balance) |
| } |
| |
| func ExampleRow_ToStruct() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| row, err := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"alice"}, []string{"name", "balance"}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| |
| type Account struct { |
| Name string |
| Balance int64 |
| } |
| |
| var acct Account |
| if err := row.ToStruct(&acct); err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println(acct) |
| } |
| |
| func ExampleReadOnlyTransaction_Read() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| iter := client.Single().Read(ctx, "Users", |
| spanner.KeySets(spanner.Key{"alice"}, spanner.Key{"bob"}), |
| []string{"name", "email"}) |
| _ = iter // TODO: iterate using Next or Do. |
| } |
| |
| func ExampleReadOnlyTransaction_ReadUsingIndex() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| iter := client.Single().ReadUsingIndex(ctx, "Users", |
| "UsersByEmail", |
| spanner.KeySets(spanner.Key{"a@example.com"}, spanner.Key{"b@example.com"}), |
| []string{"name", "email"}) |
| _ = iter // TODO: iterate using Next or Do. |
| } |
| |
| func ExampleReadOnlyTransaction_ReadWithOptions() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| // Use an index, and limit to 100 rows at most. |
| iter := client.Single().ReadWithOptions(ctx, "Users", |
| spanner.KeySets(spanner.Key{"a@example.com"}, spanner.Key{"b@example.com"}), |
| []string{"name", "email"}, &spanner.ReadOptions{ |
| Index: "UsersByEmail", |
| Limit: 100, |
| }) |
| _ = iter // TODO: iterate using Next or Do. |
| } |
| |
| func ExampleReadOnlyTransaction_ReadRow() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| row, err := client.Single().ReadRow(ctx, "Users", spanner.Key{"alice"}, |
| []string{"name", "email"}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| _ = row // TODO: use row |
| } |
| |
| func ExampleReadOnlyTransaction_Query() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| iter := client.Single().Query(ctx, spanner.NewStatement("SELECT FirstName FROM Singers")) |
| _ = iter // TODO: iterate using Next or Do. |
| } |
| |
| func ExampleNewStatement() { |
| stmt := spanner.NewStatement("SELECT FirstName, LastName FROM SINGERS WHERE LastName >= @start") |
| stmt.Params["start"] = "Dylan" |
| // TODO: Use stmt in Query. |
| } |
| |
| func ExampleNewStatement_structLiteral() { |
| stmt := spanner.Statement{ |
| SQL: `SELECT FirstName, LastName FROM SINGERS WHERE LastName = ("Lea", "Martin")`, |
| } |
| _ = stmt // TODO: Use stmt in Query. |
| } |
| |
| func ExampleStructParam() { |
| stmt := spanner.Statement{ |
| SQL: "SELECT * FROM SINGERS WHERE (FirstName, LastName) = @singerinfo", |
| Params: map[string]interface{}{ |
| "singerinfo": struct { |
| FirstName string |
| LastName string |
| }{"Bob", "Dylan"}, |
| }, |
| } |
| _ = stmt // TODO: Use stmt in Query. |
| } |
| |
| func ExampleArrayOfStructParam() { |
| stmt := spanner.Statement{ |
| SQL: "SELECT * FROM SINGERS WHERE (FirstName, LastName) IN UNNEST(@singerinfo)", |
| Params: map[string]interface{}{ |
| "singerinfo": []struct { |
| FirstName string |
| LastName string |
| }{ |
| {"Ringo", "Starr"}, |
| {"John", "Lennon"}, |
| }, |
| }, |
| } |
| _ = stmt // TODO: Use stmt in Query. |
| } |
| |
| func ExampleReadOnlyTransaction_Timestamp() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| txn := client.Single() |
| row, err := txn.ReadRow(ctx, "Users", spanner.Key{"alice"}, |
| []string{"name", "email"}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| readTimestamp, err := txn.Timestamp() |
| if err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println("read happened at", readTimestamp) |
| _ = row // TODO: use row |
| } |
| |
| func ExampleReadOnlyTransaction_WithTimestampBound() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| txn := client.Single().WithTimestampBound(spanner.MaxStaleness(30 * time.Second)) |
| row, err := txn.ReadRow(ctx, "Users", spanner.Key{"alice"}, []string{"name", "email"}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| _ = row // TODO: use row |
| readTimestamp, err := txn.Timestamp() |
| if err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println("read happened at", readTimestamp) |
| } |
| |
| func ExampleGenericColumnValue_Decode() { |
| // In real applications, rows can be retrieved by methods like client.Single().ReadRow(). |
| row, err := spanner.NewRow([]string{"intCol", "strCol"}, []interface{}{42, "my-text"}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| for i := 0; i < row.Size(); i++ { |
| var col spanner.GenericColumnValue |
| if err := row.Column(i, &col); err != nil { |
| // TODO: Handle error. |
| } |
| switch col.Type.Code { |
| case sppb.TypeCode_INT64: |
| var v int64 |
| if err := col.Decode(&v); err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println("int", v) |
| case sppb.TypeCode_STRING: |
| var v string |
| if err := col.Decode(&v); err != nil { |
| // TODO: Handle error. |
| } |
| fmt.Println("string", v) |
| } |
| } |
| // Output: |
| // int 42 |
| // string my-text |
| } |
| |
| func ExampleClient_BatchReadOnlyTransaction() { |
| ctx := context.Background() |
| var ( |
| client *spanner.Client |
| txn *spanner.BatchReadOnlyTransaction |
| err error |
| ) |
| if client, err = spanner.NewClient(ctx, myDB); err != nil { |
| // TODO: Handle error. |
| } |
| defer client.Close() |
| if txn, err = client.BatchReadOnlyTransaction(ctx, spanner.StrongRead()); err != nil { |
| // TODO: Handle error. |
| } |
| defer txn.Close() |
| |
| // Singer represents the elements in a row from the Singers table. |
| type Singer struct { |
| SingerID int64 |
| FirstName string |
| LastName string |
| SingerInfo []byte |
| } |
| stmt := spanner.Statement{SQL: "SELECT * FROM Singers;"} |
| partitions, err := txn.PartitionQuery(ctx, stmt, spanner.PartitionOptions{}) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| // Note: here we use multiple goroutines, but you should use separate |
| // processes/machines. |
| wg := sync.WaitGroup{} |
| for i, p := range partitions { |
| wg.Add(1) |
| go func(i int, p *spanner.Partition) { |
| defer wg.Done() |
| iter := txn.Execute(ctx, p) |
| defer iter.Stop() |
| for { |
| row, err := iter.Next() |
| if err == iterator.Done { |
| break |
| } else if err != nil { |
| // TODO: Handle error. |
| } |
| var s Singer |
| if err := row.ToStruct(&s); err != nil { |
| // TODO: Handle error. |
| } |
| _ = s // TODO: Process the row. |
| } |
| }(i, p) |
| } |
| wg.Wait() |
| } |
| |
| func ExampleCommitTimestamp() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| |
| type account struct { |
| User string |
| Creation spanner.NullTime // time.Time can also be used if column isNOT NULL |
| } |
| |
| a := account{User: "Joe", Creation: spanner.NullTime{spanner.CommitTimestamp, true}} |
| m, err := spanner.InsertStruct("Accounts", a) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| _, err = client.Apply(ctx, []*spanner.Mutation{m}, spanner.ApplyAtLeastOnce()) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| |
| if r, e := client.Single().ReadRow(ctx, "Accounts", spanner.Key{"Joe"}, []string{"User", "Creation"}); e != nil { |
| // TODO: Handle error. |
| } else { |
| var got account |
| if err := r.ToStruct(&got); err != nil { |
| // TODO: Handle error. |
| } |
| _ = got // TODO: Process row. |
| } |
| } |
| |
| func ExampleStatement_regexpContains() { |
| // Search for accounts with valid emails using regexp as per: |
| // https://cloud.google.com/spanner/docs/functions-and-operators#regexp_contains |
| stmt := spanner.Statement{ |
| SQL: `SELECT * FROM users WHERE REGEXP_CONTAINS(email, @valid_email)`, |
| Params: map[string]interface{}{ |
| "valid_email": `\Q@\E`, |
| }, |
| } |
| _ = stmt // TODO: Use stmt in a query. |
| } |
| |
| func ExampleKeySets() { |
| ctx := context.Background() |
| client, err := spanner.NewClient(ctx, myDB) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| // Get some rows from the Accounts table using a secondary index. In this case we get all users who are in Georgia. |
| iter := client.Single().ReadUsingIndex(context.Background(), "Accounts", "idx_state", spanner.Key{"GA"}, []string{"state"}) |
| |
| // Create a empty KeySet by calling the KeySets function with no parameters. |
| ks := spanner.KeySets() |
| |
| // Loop the results of a previous query iterator. |
| for { |
| row, err := iter.Next() |
| if err == iterator.Done { |
| break |
| } else if err != nil { |
| // TODO: Handle error. |
| } |
| var id string |
| err = row.ColumnByName("User", &id) |
| if err != nil { |
| // TODO: Handle error. |
| } |
| ks = spanner.KeySets(spanner.KeySets(spanner.Key{id}, ks)) |
| } |
| |
| _ = ks //TODO: Go use the KeySet in another query. |
| |
| } |