Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions google/cloud/spanner/doc/spanner-main.dox
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ into your project.
- @ref spanner-auth-example
- @ref spanner-universe-domain-example
- @ref spanner-mocking
- @ref spanner-isolation-level-example
- The [Setting up your development environment] guide describes how to set up
a C++ development environment in various platforms, including the Google Cloud
C++ client libraries.
Expand Down Expand Up @@ -65,6 +66,19 @@ Follow these links to find examples for other `*Client` classes:

*/

/**
@page spanner-isolation-level-example Support Isolation Level

Cloud Spanner supports different isolation levels for read-write transactions.
You can specify a default isolation level at the client-level, or override it
at the transaction-level.

The following example shows how to set the isolation level:

@snippet samples.cc spanner_isolation_level

*/

/**
@page spanner-auth-example Override the authentication configuration

Expand Down
59 changes: 59 additions & 0 deletions google/cloud/spanner/samples/samples.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "google/cloud/spanner/backoff_policy.h"
#include "google/cloud/spanner/backup.h"
#include "google/cloud/spanner/create_instance_request_builder.h"
#include "google/cloud/spanner/options.h"
#include "google/cloud/spanner/row.h"
#include "google/cloud/spanner/testing/debug_log.h" // TODO(#4758): remove
#include "google/cloud/spanner/testing/instance_location.h"
Expand Down Expand Up @@ -3472,6 +3473,63 @@ void ReadWriteTransaction(google::cloud::spanner::Client client) {
}
//! [END spanner_read_write_transaction]

//! [START spanner_isolation_level] [spanner_isolation_level]
void IsolationLevelSetting(std::string const& project_id,
std::string const& instance_id,
std::string const& database_id) {
namespace spanner = ::google::cloud::spanner;
using ::google::cloud::Options;
using ::google::cloud::StatusOr;

auto db = spanner::Database(project_id, instance_id, database_id);

// The isolation level specified at the client-level will be applied
// to all RW transactions.
auto options = Options{}.set<spanner::TransactionIsolationLevelOption>(
spanner::Transaction::IsolationLevel::kSerializable);
auto client = spanner::Client(spanner::MakeConnection(db, options));

auto commit = client.Commit(
[&client](spanner::Transaction const& txn) -> StatusOr<spanner::Mutations> {
// Read an AlbumTitle.
auto sql = spanner::SqlStatement(
"SELECT AlbumTitle from Albums WHERE SingerId = 1 and AlbumId = 1");
Comment on lines +3495 to +3496

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For improved security and to prevent SQL injection vulnerabilities, it's a best practice to use query parameters instead of embedding literal values directly into the SQL string. This also improves readability and maintainability.

        auto sql = spanner::SqlStatement(
            "SELECT AlbumTitle from Albums WHERE SingerId = @SingerId and AlbumId = @AlbumId",
            {{"SingerId", spanner::Value(1)}, {"AlbumId", spanner::Value(1)}});

auto rows = client.ExecuteQuery(txn, std::move(sql));
for (auto const& row : spanner::StreamOf<std::tuple<std::string>>(rows)) {
if (!row) return row.status();
std::cout << "Current album title: " << std::get<0>(*row) << "\n";
}

// Update the title.
auto update_sql = spanner::SqlStatement(
"UPDATE Albums "
"SET AlbumTitle = 'New Album Title' "
"WHERE SingerId = 1 and AlbumId = 1");
Comment on lines +3504 to +3507

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the SELECT statement, it's recommended to use parameters for DML statements to prevent SQL injection and improve code clarity.

        auto update_sql = spanner::SqlStatement(
            "UPDATE Albums "
            "SET AlbumTitle = @AlbumTitle "
            "WHERE SingerId = @SingerId and AlbumId = @AlbumId",
            {{"AlbumTitle", spanner::Value("New Album Title")},
             {"SingerId", spanner::Value(1)},
             {"AlbumId", spanner::Value(1)}});

auto result = client.ExecuteDml(txn, std::move(update_sql));
if (!result) return result.status();
std::cout << result->RowsModified() << " record updated.\n";

return spanner::Mutations{};
},
// The isolation level specified at the transaction-level takes
// precedence over the isolation level configured at the client-level.
// kRepeatableRead is used here to demonstrate overriding the client-level setting.
Options{}.set<spanner::TransactionIsolationLevelOption>(
spanner::Transaction::IsolationLevel::kRepeatableRead));

if (!commit) throw std::move(commit).status();
std::cout << "Update was successful [spanner_isolation_level]\n";
}
//! [END spanner_isolation_level] [spanner_isolation_level]

void IsolationLevelSettingCommand(std::vector<std::string> argv) {
if (argv.size() != 3) {
throw std::runtime_error(
"isolation-level-setting <project-id> <instance-id> <database-id>");
}
IsolationLevelSetting(argv[0], argv[1], argv[2]);
}

//! [START spanner_get_commit_stats]
void GetCommitStatistics(google::cloud::spanner::Client client) {
namespace spanner = ::google::cloud::spanner;
Expand Down Expand Up @@ -5190,6 +5248,7 @@ int RunOneCommand(std::vector<std::string> argv) {
make_command_entry("read-data-with-storing-index",
ReadDataWithStoringIndex),
make_command_entry("read-write-transaction", ReadWriteTransaction),
{"isolation-level-setting", IsolationLevelSettingCommand},
make_command_entry("get-commit-stats", GetCommitStatistics),
make_command_entry("dml-standard-insert", DmlStandardInsert),
make_command_entry("dml-standard-update", DmlStandardUpdate),
Expand Down
2 changes: 1 addition & 1 deletion google/cloud/spanner/transaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ class Transaction {
* absence of conflicts between its updates and any concurrent updates
* that have occurred since that snapshot. Consequently, in contrast to
* `kSerializable` transactions, only write-write conflicts are detected in
* snapshot transactions.
* repeatable read transactions.
*/
kRepeatableRead,
};
Expand Down
4 changes: 2 additions & 2 deletions google/cloud/spanner/transaction_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ TEST(Transaction, IsolationLevelPrecedence) {
return 0;
});

// Case 2: Fallback to client default
// Case 2: Fallback to default options
auto opts_default = Transaction::ReadWriteOptions();
Transaction txn_default = MakeReadWriteTransaction(opts_default);
spanner_internal::Visit(
Expand All @@ -202,7 +202,7 @@ TEST(Transaction, IsolationLevelPrecedence) {
}

TEST(Transaction, IsolationLevelNotSpecified) {
// Case: Isolation not specified in transaction level or client level
// Case: Isolation not specified in transaction options or default options
auto opts = Transaction::ReadWriteOptions();
Transaction txn = MakeReadWriteTransaction(opts);
spanner_internal::Visit(
Expand Down