// 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.

syntax = "proto3";

package google.spanner.v1;

import public "google/spanner/v1/commit_response.proto";

import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/api/resource.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/timestamp.proto";
import "google/rpc/status.proto";
import "google/spanner/v1/keys.proto";
import "google/spanner/v1/mutation.proto";
import "google/spanner/v1/result_set.proto";
import "google/spanner/v1/transaction.proto";
import "google/spanner/v1/type.proto";

option csharp_namespace = "Google.Cloud.Spanner.V1";
option go_package = "cloud.google.com/go/spanner/apiv1/spannerpb;spannerpb";
option java_multiple_files = true;
option java_outer_classname = "SpannerProto";
option java_package = "com.google.spanner.v1";
option php_namespace = "Google\\Cloud\\Spanner\\V1";
option ruby_package = "Google::Cloud::Spanner::V1";
option (google.api.resource_definition) = {
  type: "spanner.googleapis.com/Database"
  pattern: "projects/{project}/instances/{instance}/databases/{database}"
};

// Cloud Spanner API
//
// The Cloud Spanner API can be used to manage sessions and execute
// transactions on data stored in Cloud Spanner databases.
service Spanner {
  option (google.api.default_host) = "spanner.googleapis.com";
  option (google.api.oauth_scopes) =
      "https://www.googleapis.com/auth/cloud-platform,"
      "https://www.googleapis.com/auth/spanner.data";

  // Creates a new session. A session can be used to perform
  // transactions that read and/or modify data in a Cloud Spanner database.
  // Sessions are meant to be reused for many consecutive
  // transactions.
  //
  // Sessions can only execute one transaction at a time. To execute
  // multiple concurrent read-write/write-only transactions, create
  // multiple sessions. Note that standalone reads and queries use a
  // transaction internally, and count toward the one transaction
  // limit.
  //
  // Active sessions use additional server resources, so it is a good idea to
  // delete idle and unneeded sessions.
  // Aside from explicit deletes, Cloud Spanner may delete sessions for which no
  // operations are sent for more than an hour. If a session is deleted,
  // requests to it return `NOT_FOUND`.
  //
  // Idle sessions can be kept alive by sending a trivial SQL query
  // periodically, e.g., `"SELECT 1"`.
  rpc CreateSession(CreateSessionRequest) returns (Session) {
    option (google.api.http) = {
      post: "/v1/{database=projects/*/instances/*/databases/*}/sessions"
      body: "*"
    };
    option (google.api.method_signature) = "database";
  }

  // Creates multiple new sessions.
  //
  // This API can be used to initialize a session cache on the clients.
  // See https://goo.gl/TgSFN2 for best practices on session cache management.
  rpc BatchCreateSessions(BatchCreateSessionsRequest)
      returns (BatchCreateSessionsResponse) {
    option (google.api.http) = {
      post: "/v1/{database=projects/*/instances/*/databases/*}/sessions:batchCreate"
      body: "*"
    };
    option (google.api.method_signature) = "database,session_count";
  }

  // Gets a session. Returns `NOT_FOUND` if the session does not exist.
  // This is mainly useful for determining whether a session is still
  // alive.
  rpc GetSession(GetSessionRequest) returns (Session) {
    option (google.api.http) = {
      get: "/v1/{name=projects/*/instances/*/databases/*/sessions/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Lists all sessions in a given database.
  rpc ListSessions(ListSessionsRequest) returns (ListSessionsResponse) {
    option (google.api.http) = {
      get: "/v1/{database=projects/*/instances/*/databases/*}/sessions"
    };
    option (google.api.method_signature) = "database";
  }

  // Ends a session, releasing server resources associated with it. This will
  // asynchronously trigger cancellation of any operations that are running with
  // this session.
  rpc DeleteSession(DeleteSessionRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/v1/{name=projects/*/instances/*/databases/*/sessions/*}"
    };
    option (google.api.method_signature) = "name";
  }

  // Executes an SQL statement, returning all results in a single reply. This
  // method cannot be used to return a result set larger than 10 MiB;
  // if the query yields more data than that, the query fails with
  // a `FAILED_PRECONDITION` error.
  //
  // Operations inside read-write transactions might return `ABORTED`. If
  // this occurs, the application should restart the transaction from
  // the beginning. See [Transaction][google.spanner.v1.Transaction] for more
  // details.
  //
  // Larger result sets can be fetched in streaming fashion by calling
  // [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql]
  // instead.
  rpc ExecuteSql(ExecuteSqlRequest) returns (ResultSet) {
    option (google.api.http) = {
      post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:executeSql"
      body: "*"
    };
  }

  // Like [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql], except returns the
  // result set as a stream. Unlike
  // [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql], there is no limit on
  // the size of the returned result set. However, no individual row in the
  // result set can exceed 100 MiB, and no column value can exceed 10 MiB.
  rpc ExecuteStreamingSql(ExecuteSqlRequest) returns (stream PartialResultSet) {
    option (google.api.http) = {
      post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:executeStreamingSql"
      body: "*"
    };
  }

  // Executes a batch of SQL DML statements. This method allows many statements
  // to be run with lower latency than submitting them sequentially with
  // [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql].
  //
  // Statements are executed in sequential order. A request can succeed even if
  // a statement fails. The
  // [ExecuteBatchDmlResponse.status][google.spanner.v1.ExecuteBatchDmlResponse.status]
  // field in the response provides information about the statement that failed.
  // Clients must inspect this field to determine whether an error occurred.
  //
  // Execution stops after the first failed statement; the remaining statements
  // are not executed.
  rpc ExecuteBatchDml(ExecuteBatchDmlRequest)
      returns (ExecuteBatchDmlResponse) {
    option (google.api.http) = {
      post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:executeBatchDml"
      body: "*"
    };
  }

  // Reads rows from the database using key lookups and scans, as a
  // simple key/value style alternative to
  // [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql].  This method cannot be
  // used to return a result set larger than 10 MiB; if the read matches more
  // data than that, the read fails with a `FAILED_PRECONDITION`
  // error.
  //
  // Reads inside read-write transactions might return `ABORTED`. If
  // this occurs, the application should restart the transaction from
  // the beginning. See [Transaction][google.spanner.v1.Transaction] for more
  // details.
  //
  // Larger result sets can be yielded in streaming fashion by calling
  // [StreamingRead][google.spanner.v1.Spanner.StreamingRead] instead.
  rpc Read(ReadRequest) returns (ResultSet) {
    option (google.api.http) = {
      post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:read"
      body: "*"
    };
  }

  // Like [Read][google.spanner.v1.Spanner.Read], except returns the result set
  // as a stream. Unlike [Read][google.spanner.v1.Spanner.Read], there is no
  // limit on the size of the returned result set. However, no individual row in
  // the result set can exceed 100 MiB, and no column value can exceed
  // 10 MiB.
  rpc StreamingRead(ReadRequest) returns (stream PartialResultSet) {
    option (google.api.http) = {
      post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:streamingRead"
      body: "*"
    };
  }

  // Begins a new transaction. This step can often be skipped:
  // [Read][google.spanner.v1.Spanner.Read],
  // [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] and
  // [Commit][google.spanner.v1.Spanner.Commit] can begin a new transaction as a
  // side-effect.
  rpc BeginTransaction(BeginTransactionRequest) returns (Transaction) {
    option (google.api.http) = {
      post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:beginTransaction"
      body: "*"
    };
    option (google.api.method_signature) = "session,options";
  }

  // Commits a transaction. The request includes the mutations to be
  // applied to rows in the database.
  //
  // `Commit` might return an `ABORTED` error. This can occur at any time;
  // commonly, the cause is conflicts with concurrent
  // transactions. However, it can also happen for a variety of other
  // reasons. If `Commit` returns `ABORTED`, the caller should re-attempt
  // the transaction from the beginning, re-using the same session.
  //
  // On very rare occasions, `Commit` might return `UNKNOWN`. This can happen,
  // for example, if the client job experiences a 1+ hour networking failure.
  // At that point, Cloud Spanner has lost track of the transaction outcome and
  // we recommend that you perform another read from the database to see the
  // state of things as they are now.
  rpc Commit(CommitRequest) returns (CommitResponse) {
    option (google.api.http) = {
      post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:commit"
      body: "*"
    };
    option (google.api.method_signature) = "session,transaction_id,mutations";
    option (google.api.method_signature) =
        "session,single_use_transaction,mutations";
  }

  // Rolls back a transaction, releasing any locks it holds. It is a good
  // idea to call this for any transaction that includes one or more
  // [Read][google.spanner.v1.Spanner.Read] or
  // [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] requests and ultimately
  // decides not to commit.
  //
  // `Rollback` returns `OK` if it successfully aborts the transaction, the
  // transaction was already aborted, or the transaction is not
  // found. `Rollback` never returns `ABORTED`.
  rpc Rollback(RollbackRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:rollback"
      body: "*"
    };
    option (google.api.method_signature) = "session,transaction_id";
  }

  // Creates a set of partition tokens that can be used to execute a query
  // operation in parallel.  Each of the returned partition tokens can be used
  // by [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] to
  // specify a subset of the query result to read.  The same session and
  // read-only transaction must be used by the PartitionQueryRequest used to
  // create the partition tokens and the ExecuteSqlRequests that use the
  // partition tokens.
  //
  // Partition tokens become invalid when the session used to create them
  // is deleted, is idle for too long, begins a new transaction, or becomes too
  // old.  When any of these happen, it is not possible to resume the query, and
  // the whole operation must be restarted from the beginning.
  rpc PartitionQuery(PartitionQueryRequest) returns (PartitionResponse) {
    option (google.api.http) = {
      post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:partitionQuery"
      body: "*"
    };
  }

  // Creates a set of partition tokens that can be used to execute a read
  // operation in parallel.  Each of the returned partition tokens can be used
  // by [StreamingRead][google.spanner.v1.Spanner.StreamingRead] to specify a
  // subset of the read result to read.  The same session and read-only
  // transaction must be used by the PartitionReadRequest used to create the
  // partition tokens and the ReadRequests that use the partition tokens.  There
  // are no ordering guarantees on rows returned among the returned partition
  // tokens, or even within each individual StreamingRead call issued with a
  // partition_token.
  //
  // Partition tokens become invalid when the session used to create them
  // is deleted, is idle for too long, begins a new transaction, or becomes too
  // old.  When any of these happen, it is not possible to resume the read, and
  // the whole operation must be restarted from the beginning.
  rpc PartitionRead(PartitionReadRequest) returns (PartitionResponse) {
    option (google.api.http) = {
      post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:partitionRead"
      body: "*"
    };
  }

  // Batches the supplied mutation groups in a collection of efficient
  // transactions. All mutations in a group are committed atomically. However,
  // mutations across groups can be committed non-atomically in an unspecified
  // order and thus, they must be independent of each other. Partial failure is
  // possible, i.e., some groups may have been committed successfully, while
  // some may have failed. The results of individual batches are streamed into
  // the response as the batches are applied.
  //
  // BatchWrite requests are not replay protected, meaning that each mutation
  // group may be applied more than once. Replays of non-idempotent mutations
  // may have undesirable effects. For example, replays of an insert mutation
  // may produce an already exists error or if you use generated or commit
  // timestamp-based keys, it may result in additional rows being added to the
  // mutation's table. We recommend structuring your mutation groups to be
  // idempotent to avoid this issue.
  rpc BatchWrite(BatchWriteRequest) returns (stream BatchWriteResponse) {
    option (google.api.http) = {
      post: "/v1/{session=projects/*/instances/*/databases/*/sessions/*}:batchWrite"
      body: "*"
    };
    option (google.api.method_signature) = "session,mutation_groups";
  }
}

// The request for [CreateSession][google.spanner.v1.Spanner.CreateSession].
message CreateSessionRequest {
  // Required. The database in which the new session is created.
  string database = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "spanner.googleapis.com/Database"
    }
  ];

  // Required. The session to create.
  Session session = 2 [(google.api.field_behavior) = REQUIRED];
}

// The request for
// [BatchCreateSessions][google.spanner.v1.Spanner.BatchCreateSessions].
message BatchCreateSessionsRequest {
  // Required. The database in which the new sessions are created.
  string database = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "spanner.googleapis.com/Database"
    }
  ];

  // Parameters to be applied to each created session.
  Session session_template = 2;

  // Required. The number of sessions to be created in this batch call.
  // The API may return fewer than the requested number of sessions. If a
  // specific number of sessions are desired, the client can make additional
  // calls to BatchCreateSessions (adjusting
  // [session_count][google.spanner.v1.BatchCreateSessionsRequest.session_count]
  // as necessary).
  int32 session_count = 3 [(google.api.field_behavior) = REQUIRED];
}

// The response for
// [BatchCreateSessions][google.spanner.v1.Spanner.BatchCreateSessions].
message BatchCreateSessionsResponse {
  // The freshly created sessions.
  repeated Session session = 1;
}

// A session in the Cloud Spanner API.
message Session {
  option (google.api.resource) = {
    type: "spanner.googleapis.com/Session"
    pattern: "projects/{project}/instances/{instance}/databases/{database}/sessions/{session}"
  };

  // Output only. The name of the session. This is always system-assigned.
  string name = 1 [(google.api.field_behavior) = OUTPUT_ONLY];

  // The labels for the session.
  //
  //  * Label keys must be between 1 and 63 characters long and must conform to
  //    the following regular expression: `[a-z]([-a-z0-9]*[a-z0-9])?`.
  //  * Label values must be between 0 and 63 characters long and must conform
  //    to the regular expression `([a-z]([-a-z0-9]*[a-z0-9])?)?`.
  //  * No more than 64 labels can be associated with a given session.
  //
  // See https://goo.gl/xmQnxf for more information on and examples of labels.
  map<string, string> labels = 2;

  // Output only. The timestamp when the session is created.
  google.protobuf.Timestamp create_time = 3
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // Output only. The approximate timestamp when the session is last used. It is
  // typically earlier than the actual last use time.
  google.protobuf.Timestamp approximate_last_use_time = 4
      [(google.api.field_behavior) = OUTPUT_ONLY];

  // The database role which created this session.
  string creator_role = 5;

  // Optional. If true, specifies a multiplexed session. A multiplexed session
  // may be used for multiple, concurrent read-only operations but can not be
  // used for read-write transactions, partitioned reads, or partitioned
  // queries. Multiplexed sessions can be created via
  // [CreateSession][google.spanner.v1.Spanner.CreateSession] but not via
  // [BatchCreateSessions][google.spanner.v1.Spanner.BatchCreateSessions].
  // Multiplexed sessions may not be deleted nor listed.
  bool multiplexed = 6 [(google.api.field_behavior) = OPTIONAL];
}

// The request for [GetSession][google.spanner.v1.Spanner.GetSession].
message GetSessionRequest {
  // Required. The name of the session to retrieve.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" }
  ];
}

// The request for [ListSessions][google.spanner.v1.Spanner.ListSessions].
message ListSessionsRequest {
  // Required. The database in which to list sessions.
  string database = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "spanner.googleapis.com/Database"
    }
  ];

  // Number of sessions to be returned in the response. If 0 or less, defaults
  // to the server's maximum allowed page size.
  int32 page_size = 2;

  // If non-empty, `page_token` should contain a
  // [next_page_token][google.spanner.v1.ListSessionsResponse.next_page_token]
  // from a previous
  // [ListSessionsResponse][google.spanner.v1.ListSessionsResponse].
  string page_token = 3;

  // An expression for filtering the results of the request. Filter rules are
  // case insensitive. The fields eligible for filtering are:
  //
  //   * `labels.key` where key is the name of a label
  //
  // Some examples of using filters are:
  //
  //   * `labels.env:*` --> The session has the label "env".
  //   * `labels.env:dev` --> The session has the label "env" and the value of
  //                        the label contains the string "dev".
  string filter = 4;
}

// The response for [ListSessions][google.spanner.v1.Spanner.ListSessions].
message ListSessionsResponse {
  // The list of requested sessions.
  repeated Session sessions = 1;

  // `next_page_token` can be sent in a subsequent
  // [ListSessions][google.spanner.v1.Spanner.ListSessions] call to fetch more
  // of the matching sessions.
  string next_page_token = 2;
}

// The request for [DeleteSession][google.spanner.v1.Spanner.DeleteSession].
message DeleteSessionRequest {
  // Required. The name of the session to delete.
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" }
  ];
}

// Common request options for various APIs.
message RequestOptions {
  // The relative priority for requests. Note that priority is not applicable
  // for [BeginTransaction][google.spanner.v1.Spanner.BeginTransaction].
  //
  // The priority acts as a hint to the Cloud Spanner scheduler and does not
  // guarantee priority or order of execution. For example:
  //
  // * Some parts of a write operation always execute at `PRIORITY_HIGH`,
  //   regardless of the specified priority. This may cause you to see an
  //   increase in high priority workload even when executing a low priority
  //   request. This can also potentially cause a priority inversion where a
  //   lower priority request will be fulfilled ahead of a higher priority
  //   request.
  // * If a transaction contains multiple operations with different priorities,
  //   Cloud Spanner does not guarantee to process the higher priority
  //   operations first. There may be other constraints to satisfy, such as
  //   order of operations.
  enum Priority {
    // `PRIORITY_UNSPECIFIED` is equivalent to `PRIORITY_HIGH`.
    PRIORITY_UNSPECIFIED = 0;

    // This specifies that the request is low priority.
    PRIORITY_LOW = 1;

    // This specifies that the request is medium priority.
    PRIORITY_MEDIUM = 2;

    // This specifies that the request is high priority.
    PRIORITY_HIGH = 3;
  }

  // Priority for the request.
  Priority priority = 1;

  // A per-request tag which can be applied to queries or reads, used for
  // statistics collection.
  // Both request_tag and transaction_tag can be specified for a read or query
  // that belongs to a transaction.
  // This field is ignored for requests where it's not applicable (e.g.
  // CommitRequest).
  // Legal characters for `request_tag` values are all printable characters
  // (ASCII 32 - 126) and the length of a request_tag is limited to 50
  // characters. Values that exceed this limit are truncated.
  // Any leading underscore (_) characters will be removed from the string.
  string request_tag = 2;

  // A tag used for statistics collection about this transaction.
  // Both request_tag and transaction_tag can be specified for a read or query
  // that belongs to a transaction.
  // The value of transaction_tag should be the same for all requests belonging
  // to the same transaction.
  // If this request doesn't belong to any transaction, transaction_tag will be
  // ignored.
  // Legal characters for `transaction_tag` values are all printable characters
  // (ASCII 32 - 126) and the length of a transaction_tag is limited to 50
  // characters. Values that exceed this limit are truncated.
  // Any leading underscore (_) characters will be removed from the string.
  string transaction_tag = 3;
}

// The DirectedReadOptions can be used to indicate which replicas or regions
// should be used for non-transactional reads or queries.
//
// DirectedReadOptions may only be specified for a read-only transaction,
// otherwise the API will return an `INVALID_ARGUMENT` error.
message DirectedReadOptions {
  // The directed read replica selector.
  // Callers must provide one or more of the following fields for replica
  // selection:
  //
  //   * `location` - The location must be one of the regions within the
  //      multi-region configuration of your database.
  //   * `type` - The type of the replica.
  //
  // Some examples of using replica_selectors are:
  //
  //   * `location:us-east1` --> The "us-east1" replica(s) of any available type
  //                             will be used to process the request.
  //   * `type:READ_ONLY`    --> The "READ_ONLY" type replica(s) in nearest
  //                             available location will be used to process the
  //                             request.
  //   * `location:us-east1 type:READ_ONLY` --> The "READ_ONLY" type replica(s)
  //                          in location "us-east1" will be used to process
  //                          the request.
  message ReplicaSelection {
    // Indicates the type of replica.
    enum Type {
      // Not specified.
      TYPE_UNSPECIFIED = 0;

      // Read-write replicas support both reads and writes.
      READ_WRITE = 1;

      // Read-only replicas only support reads (not writes).
      READ_ONLY = 2;
    }

    // The location or region of the serving requests, e.g. "us-east1".
    string location = 1;

    // The type of replica.
    Type type = 2;
  }

  // An IncludeReplicas contains a repeated set of ReplicaSelection which
  // indicates the order in which replicas should be considered.
  message IncludeReplicas {
    // The directed read replica selector.
    repeated ReplicaSelection replica_selections = 1;

    // If true, Spanner will not route requests to a replica outside the
    // include_replicas list when all of the specified replicas are unavailable
    // or unhealthy. Default value is `false`.
    bool auto_failover_disabled = 2;
  }

  // An ExcludeReplicas contains a repeated set of ReplicaSelection that should
  // be excluded from serving requests.
  message ExcludeReplicas {
    // The directed read replica selector.
    repeated ReplicaSelection replica_selections = 1;
  }

  // Required. At most one of either include_replicas or exclude_replicas
  // should be present in the message.
  oneof replicas {
    // Include_replicas indicates the order of replicas (as they appear in
    // this list) to process the request. If auto_failover_disabled is set to
    // true and all replicas are exhausted without finding a healthy replica,
    // Spanner will wait for a replica in the list to become available, requests
    // may fail due to `DEADLINE_EXCEEDED` errors.
    IncludeReplicas include_replicas = 1;

    // Exclude_replicas indicates that specified replicas should be excluded
    // from serving requests. Spanner will not route requests to the replicas
    // in this list.
    ExcludeReplicas exclude_replicas = 2;
  }
}

// The request for [ExecuteSql][google.spanner.v1.Spanner.ExecuteSql] and
// [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql].
message ExecuteSqlRequest {
  // Mode in which the statement must be processed.
  enum QueryMode {
    // The default mode. Only the statement results are returned.
    NORMAL = 0;

    // This mode returns only the query plan, without any results or
    // execution statistics information.
    PLAN = 1;

    // This mode returns both the query plan and the execution statistics along
    // with the results.
    PROFILE = 2;
  }

  // Query optimizer configuration.
  message QueryOptions {
    // An option to control the selection of optimizer version.
    //
    // This parameter allows individual queries to pick different query
    // optimizer versions.
    //
    // Specifying `latest` as a value instructs Cloud Spanner to use the
    // latest supported query optimizer version. If not specified, Cloud Spanner
    // uses the optimizer version set at the database level options. Any other
    // positive integer (from the list of supported optimizer versions)
    // overrides the default optimizer version for query execution.
    //
    // The list of supported optimizer versions can be queried from
    // SPANNER_SYS.SUPPORTED_OPTIMIZER_VERSIONS.
    //
    // Executing a SQL statement with an invalid optimizer version fails with
    // an `INVALID_ARGUMENT` error.
    //
    // See
    // https://cloud.google.com/spanner/docs/query-optimizer/manage-query-optimizer
    // for more information on managing the query optimizer.
    //
    // The `optimizer_version` statement hint has precedence over this setting.
    string optimizer_version = 1;

    // An option to control the selection of optimizer statistics package.
    //
    // This parameter allows individual queries to use a different query
    // optimizer statistics package.
    //
    // Specifying `latest` as a value instructs Cloud Spanner to use the latest
    // generated statistics package. If not specified, Cloud Spanner uses
    // the statistics package set at the database level options, or the latest
    // package if the database option is not set.
    //
    // The statistics package requested by the query has to be exempt from
    // garbage collection. This can be achieved with the following DDL
    // statement:
    //
    // ```
    // ALTER STATISTICS <package_name> SET OPTIONS (allow_gc=false)
    // ```
    //
    // The list of available statistics packages can be queried from
    // `INFORMATION_SCHEMA.SPANNER_STATISTICS`.
    //
    // Executing a SQL statement with an invalid optimizer statistics package
    // or with a statistics package that allows garbage collection fails with
    // an `INVALID_ARGUMENT` error.
    string optimizer_statistics_package = 2;
  }

  // Required. The session in which the SQL query should be performed.
  string session = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" }
  ];

  // The transaction to use.
  //
  // For queries, if none is provided, the default is a temporary read-only
  // transaction with strong concurrency.
  //
  // Standard DML statements require a read-write transaction. To protect
  // against replays, single-use transactions are not supported.  The caller
  // must either supply an existing transaction ID or begin a new transaction.
  //
  // Partitioned DML requires an existing Partitioned DML transaction ID.
  TransactionSelector transaction = 2;

  // Required. The SQL string.
  string sql = 3 [(google.api.field_behavior) = REQUIRED];

  // Parameter names and values that bind to placeholders in the SQL string.
  //
  // A parameter placeholder consists of the `@` character followed by the
  // parameter name (for example, `@firstName`). Parameter names must conform
  // to the naming requirements of identifiers as specified at
  // https://cloud.google.com/spanner/docs/lexical#identifiers.
  //
  // Parameters can appear anywhere that a literal value is expected.  The same
  // parameter name can be used more than once, for example:
  //
  // `"WHERE id > @msg_id AND id < @msg_id + 100"`
  //
  // It is an error to execute a SQL statement with unbound parameters.
  google.protobuf.Struct params = 4;

  // It is not always possible for Cloud Spanner to infer the right SQL type
  // from a JSON value.  For example, values of type `BYTES` and values
  // of type `STRING` both appear in
  // [params][google.spanner.v1.ExecuteSqlRequest.params] as JSON strings.
  //
  // In these cases, `param_types` can be used to specify the exact
  // SQL type for some or all of the SQL statement parameters. See the
  // definition of [Type][google.spanner.v1.Type] for more information
  // about SQL types.
  map<string, Type> param_types = 5;

  // If this request is resuming a previously interrupted SQL statement
  // execution, `resume_token` should be copied from the last
  // [PartialResultSet][google.spanner.v1.PartialResultSet] yielded before the
  // interruption. Doing this enables the new SQL statement execution to resume
  // where the last one left off. The rest of the request parameters must
  // exactly match the request that yielded this token.
  bytes resume_token = 6;

  // Used to control the amount of debugging information returned in
  // [ResultSetStats][google.spanner.v1.ResultSetStats]. If
  // [partition_token][google.spanner.v1.ExecuteSqlRequest.partition_token] is
  // set, [query_mode][google.spanner.v1.ExecuteSqlRequest.query_mode] can only
  // be set to
  // [QueryMode.NORMAL][google.spanner.v1.ExecuteSqlRequest.QueryMode.NORMAL].
  QueryMode query_mode = 7;

  // If present, results will be restricted to the specified partition
  // previously created using PartitionQuery().  There must be an exact
  // match for the values of fields common to this message and the
  // PartitionQueryRequest message used to create this partition_token.
  bytes partition_token = 8;

  // A per-transaction sequence number used to identify this request. This field
  // makes each request idempotent such that if the request is received multiple
  // times, at most one will succeed.
  //
  // The sequence number must be monotonically increasing within the
  // transaction. If a request arrives for the first time with an out-of-order
  // sequence number, the transaction may be aborted. Replays of previously
  // handled requests will yield the same response as the first execution.
  //
  // Required for DML statements. Ignored for queries.
  int64 seqno = 9;

  // Query optimizer configuration to use for the given query.
  QueryOptions query_options = 10;

  // Common options for this request.
  RequestOptions request_options = 11;

  // Directed read options for this request.
  DirectedReadOptions directed_read_options = 15;

  // If this is for a partitioned query and this field is set to `true`, the
  // request is executed with Spanner Data Boost independent compute resources.
  //
  // If the field is set to `true` but the request does not set
  // `partition_token`, the API returns an `INVALID_ARGUMENT` error.
  bool data_boost_enabled = 16;
}

// The request for [ExecuteBatchDml][google.spanner.v1.Spanner.ExecuteBatchDml].
message ExecuteBatchDmlRequest {
  // A single DML statement.
  message Statement {
    // Required. The DML string.
    string sql = 1 [(google.api.field_behavior) = REQUIRED];

    // Parameter names and values that bind to placeholders in the DML string.
    //
    // A parameter placeholder consists of the `@` character followed by the
    // parameter name (for example, `@firstName`). Parameter names can contain
    // letters, numbers, and underscores.
    //
    // Parameters can appear anywhere that a literal value is expected.  The
    // same parameter name can be used more than once, for example:
    //
    // `"WHERE id > @msg_id AND id < @msg_id + 100"`
    //
    // It is an error to execute a SQL statement with unbound parameters.
    google.protobuf.Struct params = 2;

    // It is not always possible for Cloud Spanner to infer the right SQL type
    // from a JSON value.  For example, values of type `BYTES` and values
    // of type `STRING` both appear in
    // [params][google.spanner.v1.ExecuteBatchDmlRequest.Statement.params] as
    // JSON strings.
    //
    // In these cases, `param_types` can be used to specify the exact
    // SQL type for some or all of the SQL statement parameters. See the
    // definition of [Type][google.spanner.v1.Type] for more information
    // about SQL types.
    map<string, Type> param_types = 3;
  }

  // Required. The session in which the DML statements should be performed.
  string session = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" }
  ];

  // Required. The transaction to use. Must be a read-write transaction.
  //
  // To protect against replays, single-use transactions are not supported. The
  // caller must either supply an existing transaction ID or begin a new
  // transaction.
  TransactionSelector transaction = 2 [(google.api.field_behavior) = REQUIRED];

  // Required. The list of statements to execute in this batch. Statements are
  // executed serially, such that the effects of statement `i` are visible to
  // statement `i+1`. Each statement must be a DML statement. Execution stops at
  // the first failed statement; the remaining statements are not executed.
  //
  // Callers must provide at least one statement.
  repeated Statement statements = 3 [(google.api.field_behavior) = REQUIRED];

  // Required. A per-transaction sequence number used to identify this request.
  // This field makes each request idempotent such that if the request is
  // received multiple times, at most one will succeed.
  //
  // The sequence number must be monotonically increasing within the
  // transaction. If a request arrives for the first time with an out-of-order
  // sequence number, the transaction may be aborted. Replays of previously
  // handled requests will yield the same response as the first execution.
  int64 seqno = 4 [(google.api.field_behavior) = REQUIRED];

  // Common options for this request.
  RequestOptions request_options = 5;
}

// The response for
// [ExecuteBatchDml][google.spanner.v1.Spanner.ExecuteBatchDml]. Contains a list
// of [ResultSet][google.spanner.v1.ResultSet] messages, one for each DML
// statement that has successfully executed, in the same order as the statements
// in the request. If a statement fails, the status in the response body
// identifies the cause of the failure.
//
// To check for DML statements that failed, use the following approach:
//
// 1. Check the status in the response message. The
// [google.rpc.Code][google.rpc.Code] enum
//    value `OK` indicates that all statements were executed successfully.
// 2. If the status was not `OK`, check the number of result sets in the
//    response. If the response contains `N`
//    [ResultSet][google.spanner.v1.ResultSet] messages, then statement `N+1` in
//    the request failed.
//
// Example 1:
//
// * Request: 5 DML statements, all executed successfully.
// * Response: 5 [ResultSet][google.spanner.v1.ResultSet] messages, with the
// status `OK`.
//
// Example 2:
//
// * Request: 5 DML statements. The third statement has a syntax error.
// * Response: 2 [ResultSet][google.spanner.v1.ResultSet] messages, and a syntax
// error (`INVALID_ARGUMENT`)
//   status. The number of [ResultSet][google.spanner.v1.ResultSet] messages
//   indicates that the third statement failed, and the fourth and fifth
//   statements were not executed.
message ExecuteBatchDmlResponse {
  // One [ResultSet][google.spanner.v1.ResultSet] for each statement in the
  // request that ran successfully, in the same order as the statements in the
  // request. Each [ResultSet][google.spanner.v1.ResultSet] does not contain any
  // rows. The [ResultSetStats][google.spanner.v1.ResultSetStats] in each
  // [ResultSet][google.spanner.v1.ResultSet] contain the number of rows
  // modified by the statement.
  //
  // Only the first [ResultSet][google.spanner.v1.ResultSet] in the response
  // contains valid [ResultSetMetadata][google.spanner.v1.ResultSetMetadata].
  repeated ResultSet result_sets = 1;

  // If all DML statements are executed successfully, the status is `OK`.
  // Otherwise, the error status of the first failed statement.
  google.rpc.Status status = 2;
}

// Options for a PartitionQueryRequest and
// PartitionReadRequest.
message PartitionOptions {
  // **Note:** This hint is currently ignored by PartitionQuery and
  // PartitionRead requests.
  //
  // The desired data size for each partition generated.  The default for this
  // option is currently 1 GiB.  This is only a hint. The actual size of each
  // partition may be smaller or larger than this size request.
  int64 partition_size_bytes = 1;

  // **Note:** This hint is currently ignored by PartitionQuery and
  // PartitionRead requests.
  //
  // The desired maximum number of partitions to return.  For example, this may
  // be set to the number of workers available.  The default for this option
  // is currently 10,000. The maximum value is currently 200,000.  This is only
  // a hint.  The actual number of partitions returned may be smaller or larger
  // than this maximum count request.
  int64 max_partitions = 2;
}

// The request for [PartitionQuery][google.spanner.v1.Spanner.PartitionQuery]
message PartitionQueryRequest {
  // Required. The session used to create the partitions.
  string session = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" }
  ];

  // Read only snapshot transactions are supported, read/write and single use
  // transactions are not.
  TransactionSelector transaction = 2;

  // Required. The query request to generate partitions for. The request will
  // fail if the query is not root partitionable. For a query to be root
  // partitionable, it needs to satisfy a few conditions. For example, if the
  // query execution plan contains a distributed union operator, then it must be
  // the first operator in the plan. For more information about other
  // conditions, see [Read data in
  // parallel](https://cloud.google.com/spanner/docs/reads#read_data_in_parallel).
  //
  // The query request must not contain DML commands, such as INSERT, UPDATE, or
  // DELETE. Use
  // [ExecuteStreamingSql][google.spanner.v1.Spanner.ExecuteStreamingSql] with a
  // PartitionedDml transaction for large, partition-friendly DML operations.
  string sql = 3 [(google.api.field_behavior) = REQUIRED];

  // Parameter names and values that bind to placeholders in the SQL string.
  //
  // A parameter placeholder consists of the `@` character followed by the
  // parameter name (for example, `@firstName`). Parameter names can contain
  // letters, numbers, and underscores.
  //
  // Parameters can appear anywhere that a literal value is expected.  The same
  // parameter name can be used more than once, for example:
  //
  // `"WHERE id > @msg_id AND id < @msg_id + 100"`
  //
  // It is an error to execute a SQL statement with unbound parameters.
  google.protobuf.Struct params = 4;

  // It is not always possible for Cloud Spanner to infer the right SQL type
  // from a JSON value.  For example, values of type `BYTES` and values
  // of type `STRING` both appear in
  // [params][google.spanner.v1.PartitionQueryRequest.params] as JSON strings.
  //
  // In these cases, `param_types` can be used to specify the exact
  // SQL type for some or all of the SQL query parameters. See the
  // definition of [Type][google.spanner.v1.Type] for more information
  // about SQL types.
  map<string, Type> param_types = 5;

  // Additional options that affect how many partitions are created.
  PartitionOptions partition_options = 6;
}

// The request for [PartitionRead][google.spanner.v1.Spanner.PartitionRead]
message PartitionReadRequest {
  // Required. The session used to create the partitions.
  string session = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" }
  ];

  // Read only snapshot transactions are supported, read/write and single use
  // transactions are not.
  TransactionSelector transaction = 2;

  // Required. The name of the table in the database to be read.
  string table = 3 [(google.api.field_behavior) = REQUIRED];

  // If non-empty, the name of an index on
  // [table][google.spanner.v1.PartitionReadRequest.table]. This index is used
  // instead of the table primary key when interpreting
  // [key_set][google.spanner.v1.PartitionReadRequest.key_set] and sorting
  // result rows. See [key_set][google.spanner.v1.PartitionReadRequest.key_set]
  // for further information.
  string index = 4;

  // The columns of [table][google.spanner.v1.PartitionReadRequest.table] to be
  // returned for each row matching this request.
  repeated string columns = 5;

  // Required. `key_set` identifies the rows to be yielded. `key_set` names the
  // primary keys of the rows in
  // [table][google.spanner.v1.PartitionReadRequest.table] to be yielded, unless
  // [index][google.spanner.v1.PartitionReadRequest.index] is present. If
  // [index][google.spanner.v1.PartitionReadRequest.index] is present, then
  // [key_set][google.spanner.v1.PartitionReadRequest.key_set] instead names
  // index keys in [index][google.spanner.v1.PartitionReadRequest.index].
  //
  // It is not an error for the `key_set` to name rows that do not
  // exist in the database. Read yields nothing for nonexistent rows.
  KeySet key_set = 6 [(google.api.field_behavior) = REQUIRED];

  // Additional options that affect how many partitions are created.
  PartitionOptions partition_options = 9;
}

// Information returned for each partition returned in a
// PartitionResponse.
message Partition {
  // This token can be passed to Read, StreamingRead, ExecuteSql, or
  // ExecuteStreamingSql requests to restrict the results to those identified by
  // this partition token.
  bytes partition_token = 1;
}

// The response for [PartitionQuery][google.spanner.v1.Spanner.PartitionQuery]
// or [PartitionRead][google.spanner.v1.Spanner.PartitionRead]
message PartitionResponse {
  // Partitions created by this request.
  repeated Partition partitions = 1;

  // Transaction created by this request.
  Transaction transaction = 2;
}

// The request for [Read][google.spanner.v1.Spanner.Read] and
// [StreamingRead][google.spanner.v1.Spanner.StreamingRead].
message ReadRequest {
  // An option to control the order in which rows are returned from a read.
  enum OrderBy {
    // Default value.
    //
    // ORDER_BY_UNSPECIFIED is equivalent to ORDER_BY_PRIMARY_KEY.
    ORDER_BY_UNSPECIFIED = 0;

    // Read rows are returned in primary key order.
    //
    // In the event that this option is used in conjunction with the
    // `partition_token` field, the API will return an `INVALID_ARGUMENT` error.
    ORDER_BY_PRIMARY_KEY = 1;

    // Read rows are returned in any order.
    ORDER_BY_NO_ORDER = 2;
  }

  // A lock hint mechanism for reads done within a transaction.
  enum LockHint {
    // Default value.
    //
    // LOCK_HINT_UNSPECIFIED is equivalent to LOCK_HINT_SHARED.
    LOCK_HINT_UNSPECIFIED = 0;

    // Acquire shared locks.
    //
    // By default when you perform a read as part of a read-write transaction,
    // Spanner acquires shared read locks, which allows other reads to still
    // access the data until your transaction is ready to commit. When your
    // transaction is committing and writes are being applied, the transaction
    // attempts to upgrade to an exclusive lock for any data you are writing.
    // For more information about locks, see [Lock
    // modes](https://cloud.google.com/spanner/docs/introspection/lock-statistics#explain-lock-modes).
    LOCK_HINT_SHARED = 1;

    // Acquire exclusive locks.
    //
    // Requesting exclusive locks is beneficial if you observe high write
    // contention, which means you notice that multiple transactions are
    // concurrently trying to read and write to the same data, resulting in a
    // large number of aborts. This problem occurs when two transactions
    // initially acquire shared locks and then both try to upgrade to exclusive
    // locks at the same time. In this situation both transactions are waiting
    // for the other to give up their lock, resulting in a deadlocked situation.
    // Spanner is able to detect this occurring and force one of the
    // transactions to abort. However, this is a slow and expensive operation
    // and results in lower performance. In this case it makes sense to acquire
    // exclusive locks at the start of the transaction because then when
    // multiple transactions try to act on the same data, they automatically get
    // serialized. Each transaction waits its turn to acquire the lock and
    // avoids getting into deadlock situations.
    //
    // Because the exclusive lock hint is just a hint, it should not be
    // considered equivalent to a mutex. In other words, you should not use
    // Spanner exclusive locks as a mutual exclusion mechanism for the execution
    // of code outside of Spanner.
    //
    // **Note:** Request exclusive locks judiciously because they block others
    // from reading that data for the entire transaction, rather than just when
    // the writes are being performed. Unless you observe high write contention,
    // you should use the default of shared read locks so you don't prematurely
    // block other clients from reading the data that you're writing to.
    LOCK_HINT_EXCLUSIVE = 2;
  }

  // Required. The session in which the read should be performed.
  string session = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" }
  ];

  // The transaction to use. If none is provided, the default is a
  // temporary read-only transaction with strong concurrency.
  TransactionSelector transaction = 2;

  // Required. The name of the table in the database to be read.
  string table = 3 [(google.api.field_behavior) = REQUIRED];

  // If non-empty, the name of an index on
  // [table][google.spanner.v1.ReadRequest.table]. This index is used instead of
  // the table primary key when interpreting
  // [key_set][google.spanner.v1.ReadRequest.key_set] and sorting result rows.
  // See [key_set][google.spanner.v1.ReadRequest.key_set] for further
  // information.
  string index = 4;

  // Required. The columns of [table][google.spanner.v1.ReadRequest.table] to be
  // returned for each row matching this request.
  repeated string columns = 5 [(google.api.field_behavior) = REQUIRED];

  // Required. `key_set` identifies the rows to be yielded. `key_set` names the
  // primary keys of the rows in [table][google.spanner.v1.ReadRequest.table] to
  // be yielded, unless [index][google.spanner.v1.ReadRequest.index] is present.
  // If [index][google.spanner.v1.ReadRequest.index] is present, then
  // [key_set][google.spanner.v1.ReadRequest.key_set] instead names index keys
  // in [index][google.spanner.v1.ReadRequest.index].
  //
  // If the [partition_token][google.spanner.v1.ReadRequest.partition_token]
  // field is empty, rows are yielded in table primary key order (if
  // [index][google.spanner.v1.ReadRequest.index] is empty) or index key order
  // (if [index][google.spanner.v1.ReadRequest.index] is non-empty).  If the
  // [partition_token][google.spanner.v1.ReadRequest.partition_token] field is
  // not empty, rows will be yielded in an unspecified order.
  //
  // It is not an error for the `key_set` to name rows that do not
  // exist in the database. Read yields nothing for nonexistent rows.
  KeySet key_set = 6 [(google.api.field_behavior) = REQUIRED];

  // If greater than zero, only the first `limit` rows are yielded. If `limit`
  // is zero, the default is no limit. A limit cannot be specified if
  // `partition_token` is set.
  int64 limit = 8;

  // If this request is resuming a previously interrupted read,
  // `resume_token` should be copied from the last
  // [PartialResultSet][google.spanner.v1.PartialResultSet] yielded before the
  // interruption. Doing this enables the new read to resume where the last read
  // left off. The rest of the request parameters must exactly match the request
  // that yielded this token.
  bytes resume_token = 9;

  // If present, results will be restricted to the specified partition
  // previously created using PartitionRead().    There must be an exact
  // match for the values of fields common to this message and the
  // PartitionReadRequest message used to create this partition_token.
  bytes partition_token = 10;

  // Common options for this request.
  RequestOptions request_options = 11;

  // Directed read options for this request.
  DirectedReadOptions directed_read_options = 14;

  // If this is for a partitioned read and this field is set to `true`, the
  // request is executed with Spanner Data Boost independent compute resources.
  //
  // If the field is set to `true` but the request does not set
  // `partition_token`, the API returns an `INVALID_ARGUMENT` error.
  bool data_boost_enabled = 15;

  // Optional. Order for the returned rows.
  //
  // By default, Spanner will return result rows in primary key order except for
  // PartitionRead requests. For applications that do not require rows to be
  // returned in primary key (`ORDER_BY_PRIMARY_KEY`) order, setting
  // `ORDER_BY_NO_ORDER` option allows Spanner to optimize row retrieval,
  // resulting in lower latencies in certain cases (e.g. bulk point lookups).
  OrderBy order_by = 16 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Lock Hint for the request, it can only be used with read-write
  // transactions.
  LockHint lock_hint = 17 [(google.api.field_behavior) = OPTIONAL];
}

// The request for
// [BeginTransaction][google.spanner.v1.Spanner.BeginTransaction].
message BeginTransactionRequest {
  // Required. The session in which the transaction runs.
  string session = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" }
  ];

  // Required. Options for the new transaction.
  TransactionOptions options = 2 [(google.api.field_behavior) = REQUIRED];

  // Common options for this request.
  // Priority is ignored for this request. Setting the priority in this
  // request_options struct will not do anything. To set the priority for a
  // transaction, set it on the reads and writes that are part of this
  // transaction instead.
  RequestOptions request_options = 3;
}

// The request for [Commit][google.spanner.v1.Spanner.Commit].
message CommitRequest {
  // Required. The session in which the transaction to be committed is running.
  string session = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" }
  ];

  // Required. The transaction in which to commit.
  oneof transaction {
    // Commit a previously-started transaction.
    bytes transaction_id = 2;

    // Execute mutations in a temporary transaction. Note that unlike
    // commit of a previously-started transaction, commit with a
    // temporary transaction is non-idempotent. That is, if the
    // `CommitRequest` is sent to Cloud Spanner more than once (for
    // instance, due to retries in the application, or in the
    // transport library), it is possible that the mutations are
    // executed more than once. If this is undesirable, use
    // [BeginTransaction][google.spanner.v1.Spanner.BeginTransaction] and
    // [Commit][google.spanner.v1.Spanner.Commit] instead.
    TransactionOptions single_use_transaction = 3;
  }

  // The mutations to be executed when this transaction commits. All
  // mutations are applied atomically, in the order they appear in
  // this list.
  repeated Mutation mutations = 4;

  // If `true`, then statistics related to the transaction will be included in
  // the [CommitResponse][google.spanner.v1.CommitResponse.commit_stats].
  // Default value is `false`.
  bool return_commit_stats = 5;

  // Optional. The amount of latency this request is willing to incur in order
  // to improve throughput. If this field is not set, Spanner assumes requests
  // are relatively latency sensitive and automatically determines an
  // appropriate delay time. You can specify a batching delay value between 0
  // and 500 ms.
  google.protobuf.Duration max_commit_delay = 8
      [(google.api.field_behavior) = OPTIONAL];

  // Common options for this request.
  RequestOptions request_options = 6;
}

// The request for [Rollback][google.spanner.v1.Spanner.Rollback].
message RollbackRequest {
  // Required. The session in which the transaction to roll back is running.
  string session = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" }
  ];

  // Required. The transaction to roll back.
  bytes transaction_id = 2 [(google.api.field_behavior) = REQUIRED];
}

// The request for [BatchWrite][google.spanner.v1.Spanner.BatchWrite].
message BatchWriteRequest {
  // A group of mutations to be committed together. Related mutations should be
  // placed in a group. For example, two mutations inserting rows with the same
  // primary key prefix in both parent and child tables are related.
  message MutationGroup {
    // Required. The mutations in this group.
    repeated Mutation mutations = 1 [(google.api.field_behavior) = REQUIRED];
  }

  // Required. The session in which the batch request is to be run.
  string session = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = { type: "spanner.googleapis.com/Session" }
  ];

  // Common options for this request.
  RequestOptions request_options = 3;

  // Required. The groups of mutations to be applied.
  repeated MutationGroup mutation_groups = 4
      [(google.api.field_behavior) = REQUIRED];

  // Optional. When `exclude_txn_from_change_streams` is set to `true`:
  //  * Mutations from all transactions in this batch write operation will not
  //  be recorded in change streams with DDL option `allow_txn_exclusion=true`
  //  that are tracking columns modified by these transactions.
  //  * Mutations from all transactions in this batch write operation will be
  //  recorded in change streams with DDL option `allow_txn_exclusion=false or
  //  not set` that are tracking columns modified by these transactions.
  //
  // When `exclude_txn_from_change_streams` is set to `false` or not set,
  // mutations from all transactions in this batch write operation will be
  // recorded in all change streams that are tracking columns modified by these
  // transactions.
  bool exclude_txn_from_change_streams = 5
      [(google.api.field_behavior) = OPTIONAL];
}

// The result of applying a batch of mutations.
message BatchWriteResponse {
  // The mutation groups applied in this batch. The values index into the
  // `mutation_groups` field in the corresponding `BatchWriteRequest`.
  repeated int32 indexes = 1;

  // An `OK` status indicates success. Any other status indicates a failure.
  google.rpc.Status status = 2;

  // The commit timestamp of the transaction that applied this batch.
  // Present if `status` is `OK`, absent otherwise.
  google.protobuf.Timestamp commit_timestamp = 3;
}
