Skip to content

Commit

Permalink
docs: add transaction samples
Browse files Browse the repository at this point in the history
  • Loading branch information
olavloite committed May 17, 2024
1 parent 87f43a4 commit f1db348
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 2 deletions.
11 changes: 9 additions & 2 deletions samples/snippets/golang-snippets/query_data_with_new_column.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package golang_snippets
// [START spanner_query_data_with_new_column]
import (
"context"
"database/sql"
"fmt"

"github.com/jackc/pgx/v5"
Expand All @@ -41,12 +42,18 @@ func queryDataWithNewColumn(host string, port int, database string) error {
}
for rows.Next() {
var singerId, albumId int64
var marketingBudget string
var marketingBudget sql.NullString
err = rows.Scan(&singerId, &albumId, &marketingBudget)
if err != nil {
return err
}
fmt.Printf("%v %v %v\n", singerId, albumId, marketingBudget)
var budget string
if marketingBudget.Valid {
budget = marketingBudget.String
} else {
budget = "NULL"
}
fmt.Printf("%v %v %v\n", singerId, albumId, budget)
}

return rows.Err()
Expand Down
6 changes: 6 additions & 0 deletions samples/snippets/golang-snippets/samples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,10 @@ func TestSamples(t *testing.T) {
if err := updateDataWithCopy(host, port, db); err != nil {
t.Fatalf("update data with copy failed: %v", err)
}
if err := queryDataWithNewColumn(host, port, db); err != nil {
t.Fatalf("query data with with new column failed: %v", err)
}
if err := writeWithTransactionUsingDml(host, port, db); err != nil {
t.Fatalf("update data using a transaction failed: %v", err)
}
}
86 changes: 86 additions & 0 deletions samples/snippets/golang-snippets/update_data_with_transaction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
Copyright 2024 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 golang_snippets

// [START spanner_dml_getting_started_update]
import (
"context"
"fmt"

"github.com/jackc/pgx/v5"
)

func writeWithTransactionUsingDml(host string, port int, database string) error {
ctx := context.Background()
connString := fmt.Sprintf(
"postgres://uid:pwd@%s:%d/%s?sslmode=disable",
host, port, database)
conn, err := pgx.Connect(ctx, connString)
if err != nil {
return err
}
defer conn.Close(ctx)

// Transfer marketing budget from one album to another. We do it in a
// transaction to ensure that the transfer is atomic.
tx, err := conn.Begin(ctx)
if err != nil {
return err
}
const selectSql = "SELECT marketing_budget " +
"from albums " +
"WHERE singer_id = $1 and album_id = $2"
// Get the marketing_budget of singer 2 / album 2.
row := tx.QueryRow(ctx, selectSql, 2, 2)
var budget2 int64
if err := row.Scan(&budget2); err != nil {
tx.Rollback(ctx)
return err
}
const transfer = 20000
// The transaction will only be committed if this condition still holds
// at the time of commit. Otherwise, the transaction will be aborted.
if budget2 >= transfer {
// Get the marketing_budget of singer 1 / album 1.
row := tx.QueryRow(ctx, selectSql, 1, 1)
var budget1 int64
if err := row.Scan(&budget1); err != nil {
tx.Rollback(ctx)
return err
}
// Transfer part of the marketing budget of Album 2 to Album 1.
budget1 += transfer
budget2 -= transfer
const updateSql = "UPDATE albums " +
"SET marketing_budget = $1 " +
"WHERE singer_id = $2 and album_id = $3"
// Start a DML batch and execute it as part of the current transaction.
batch := &pgx.Batch{}
batch.Queue(updateSql, budget1, 1, 1)
batch.Queue(updateSql, budget2, 2, 2)
br := tx.SendBatch(ctx, batch)
_, err = br.Exec()
if err := br.Close(); err != nil {
tx.Rollback(ctx)
return err
}
}
// Commit the current transaction.
tx.Commit(ctx)
fmt.Println("Transferred marketing budget from Album 2 to Album 1")

return nil
}

// [END spanner_dml_getting_started_update]
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// Copyright 2024 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.

// [START spanner_dml_getting_started_update]
import { Client } from 'pg';

async function writeWithTransactionUsingDml(host: string, port: number, database: string): Promise<void> {
const connection = new Client({
host: host,
port: port,
database: database,
});
await connection.connect();

// Transfer marketing budget from one album to another. We do it in a
// transaction to ensure that the transfer is atomic. node-postgres
// requires you to explicitly start the transaction by executing 'begin'.
await connection.query("begin");
const selectMarketingBudgetSql = "SELECT marketing_budget " +
"from albums " +
"WHERE singer_id = $1 and album_id = $2";
// Get the marketing_budget of singer 2 / album 2.
const album2BudgetResult = await connection.query(selectMarketingBudgetSql, [2, 2]);
let album2Budget = album2BudgetResult.rows[0]["marketing_budget"];
const transfer = 200000;
// The transaction will only be committed if this condition still holds
// at the time of commit. Otherwise, the transaction will be aborted.
if (album2Budget >= transfer) {
// Get the marketing budget of singer 1 / album 1.
const album1BudgetResult = await connection.query(selectMarketingBudgetSql, [1, 1]);
let album1Budget = album1BudgetResult.rows[0]["marketing_budget"];
// Transfer part of the marketing budget of Album 2 to Album 1.
album1Budget += transfer;
album2Budget -= transfer;
const updateSql = "UPDATE albums " +
"SET marketing_budget = $1 " +
"WHERE singer_id = $2 and album_id = $3";
// Start a DML batch. This batch will become part of the current transaction.
await connection.query("start batch dml");
// Update the marketing budget of both albums.
await connection.query(updateSql, [album1Budget, 1, 1]);
await connection.query(updateSql, [album2Budget, 2, 2]);
await connection.query("run batch");
}
// Commit the current transaction.
await connection.query("commit");
console.log("Transferred marketing budget from Album 2 to Album 1");

// Close the connection.
await connection.end();
}
// [END spanner_dml_getting_started_update]

export = writeWithTransactionUsingDml;
5 changes: 5 additions & 0 deletions samples/snippets/nodejs-snippets/test/sample_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import addColumn from "../src/add_column";
import ddlBatch from "../src/ddl_batch";
import updateDataWithCopy from "../src/update_data_with_copy";
import queryDataWithNewColumn from "../src/query_data_with_new_column";
import writeWithTransactionUsingDml from "../src/update_data_with_transaction";

const container: TestContainer = new GenericContainer("gcr.io/cloud-spanner-pg-adapter/pgadapter-emulator")
.withExposedPorts(5432)
Expand Down Expand Up @@ -103,4 +104,8 @@ describe('running samples', () => {
expect(console.log).toHaveBeenCalledWith("2 2 500000");
expect(console.log).toHaveBeenCalledWith("2 3 null");
}, 30000);
test('update data with transaction', async () => {
await writeWithTransactionUsingDml(startedTestContainer.getHost(), startedTestContainer.getMappedPort(5432), "example-db");
expect(console.log).toHaveBeenCalledWith("Transferred marketing budget from Album 2 to Album 1");
}, 30000);
});

0 comments on commit f1db348

Please sign in to comment.