// Copyright 2025 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
//
//     https://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.
//
// ** This file is automatically generated by gapic-generator-typescript. **
// ** https://github.com/googleapis/gapic-generator-typescript **
// ** All changes to this file may be overwritten. **

/* global window */
import type * as gax from 'google-gax';
import type {
  Callback,
  CallOptions,
  Descriptors,
  ClientOptions,
  PaginationCallback,
  GaxCall,
} from 'google-gax';
import {Transform, PassThrough} from 'stream';
import * as protos from '../../protos/protos';
import jsonProtos = require('../../protos/protos.json');
import {loggingUtils as logging, decodeAnyProtosInArray} from 'google-gax';

/**
 * Client JSON configuration object, loaded from
 * `src/v1/spanner_client_config.json`.
 * This file defines retry strategy and timeouts for all API methods in this library.
 */
import * as gapicConfig from './spanner_client_config.json';
const version = require('../../../package.json').version;

/**
 *  Cloud Spanner API
 *
 *  The Cloud Spanner API can be used to manage sessions and execute
 *  transactions on data stored in Cloud Spanner databases.
 * @class
 * @memberof v1
 */
export class SpannerClient {
  private _terminated = false;
  private _opts: ClientOptions;
  private _providedCustomServicePath: boolean;
  private _gaxModule: typeof gax | typeof gax.fallback;
  private _gaxGrpc: gax.GrpcClient | gax.fallback.GrpcClient;
  private _protos: {};
  private _defaults: {[method: string]: gax.CallSettings};
  private _universeDomain: string;
  private _servicePath: string;
  private _log = logging.log('spanner');

  auth: gax.GoogleAuth;
  descriptors: Descriptors = {
    page: {},
    stream: {},
    longrunning: {},
    batching: {},
  };
  warn: (code: string, message: string, warnType?: string) => void;
  innerApiCalls: {[name: string]: Function};
  pathTemplates: {[name: string]: gax.PathTemplate};
  spannerStub?: Promise<{[name: string]: Function}>;

  /**
   * Construct an instance of SpannerClient.
   *
   * @param {object} [options] - The configuration object.
   * The options accepted by the constructor are described in detail
   * in [this document](https://github.com/googleapis/gax-nodejs/blob/main/client-libraries.md#creating-the-client-instance).
   * The common options are:
   * @param {object} [options.credentials] - Credentials object.
   * @param {string} [options.credentials.client_email]
   * @param {string} [options.credentials.private_key]
   * @param {string} [options.email] - Account email address. Required when
   *     using a .pem or .p12 keyFilename.
   * @param {string} [options.keyFilename] - Full path to the a .json, .pem, or
   *     .p12 key downloaded from the Google Developers Console. If you provide
   *     a path to a JSON file, the projectId option below is not necessary.
   *     NOTE: .pem and .p12 require you to specify options.email as well.
   * @param {number} [options.port] - The port on which to connect to
   *     the remote host.
   * @param {string} [options.projectId] - The project ID from the Google
   *     Developer's Console, e.g. 'grape-spaceship-123'. We will also check
   *     the environment variable GCLOUD_PROJECT for your project ID. If your
   *     app is running in an environment which supports
   *     {@link https://cloud.google.com/docs/authentication/application-default-credentials Application Default Credentials},
   *     your project ID will be detected automatically.
   * @param {string} [options.apiEndpoint] - The domain name of the
   *     API remote host.
   * @param {gax.ClientConfig} [options.clientConfig] - Client configuration override.
   *     Follows the structure of {@link gapicConfig}.
   * @param {boolean} [options.fallback] - Use HTTP/1.1 REST mode.
   *     For more information, please check the
   *     {@link https://github.com/googleapis/gax-nodejs/blob/main/client-libraries.md#http11-rest-api-mode documentation}.
   * @param {gax} [gaxInstance]: loaded instance of `google-gax`. Useful if you
   *     need to avoid loading the default gRPC version and want to use the fallback
   *     HTTP implementation. Load only fallback version and pass it to the constructor:
   *     ```
   *     const gax = require('google-gax/build/src/fallback'); // avoids loading google-gax with gRPC
   *     const client = new SpannerClient({fallback: true}, gax);
   *     ```
   */
  constructor(
    opts?: ClientOptions,
    gaxInstance?: typeof gax | typeof gax.fallback,
  ) {
    // Ensure that options include all the required fields.
    const staticMembers = this.constructor as typeof SpannerClient;
    if (
      opts?.universe_domain &&
      opts?.universeDomain &&
      opts?.universe_domain !== opts?.universeDomain
    ) {
      throw new Error(
        'Please set either universe_domain or universeDomain, but not both.',
      );
    }
    const universeDomainEnvVar =
      typeof process === 'object' && typeof process.env === 'object'
        ? process.env['GOOGLE_CLOUD_UNIVERSE_DOMAIN']
        : undefined;
    this._universeDomain =
      opts?.universeDomain ??
      opts?.universe_domain ??
      universeDomainEnvVar ??
      'googleapis.com';
    this._servicePath = 'spanner.' + this._universeDomain;
    const servicePath =
      opts?.servicePath || opts?.apiEndpoint || this._servicePath;
    this._providedCustomServicePath = !!(
      opts?.servicePath || opts?.apiEndpoint
    );
    const port = opts?.port || staticMembers.port;
    const clientConfig = opts?.clientConfig ?? {};
    const fallback =
      opts?.fallback ??
      (typeof window !== 'undefined' && typeof window?.fetch === 'function');
    opts = Object.assign({servicePath, port, clientConfig, fallback}, opts);

    // Request numeric enum values if REST transport is used.
    opts.numericEnums = true;

    // If scopes are unset in options and we're connecting to a non-default endpoint, set scopes just in case.
    if (servicePath !== this._servicePath && !('scopes' in opts)) {
      opts['scopes'] = staticMembers.scopes;
    }

    // Load google-gax module synchronously if needed
    if (!gaxInstance) {
      gaxInstance = require('google-gax') as typeof gax;
    }

    // Choose either gRPC or proto-over-HTTP implementation of google-gax.
    this._gaxModule = opts.fallback ? gaxInstance.fallback : gaxInstance;

    // Create a `gaxGrpc` object, with any grpc-specific options sent to the client.
    this._gaxGrpc = new this._gaxModule.GrpcClient(opts);

    // Save options to use in initialize() method.
    this._opts = opts;

    // Save the auth object to the client, for use by other methods.
    this.auth = this._gaxGrpc.auth as gax.GoogleAuth;

    // Set useJWTAccessWithScope on the auth object.
    this.auth.useJWTAccessWithScope = true;

    // Set defaultServicePath on the auth object.
    this.auth.defaultServicePath = this._servicePath;

    // Set the default scopes in auth client if needed.
    if (servicePath === this._servicePath) {
      this.auth.defaultScopes = staticMembers.scopes;
    }

    // Determine the client header string.
    const clientHeader = [`gax/${this._gaxModule.version}`, `gapic/${version}`];
    if (typeof process === 'object' && 'versions' in process) {
      clientHeader.push(`gl-node/${process.versions.node}`);
    } else {
      clientHeader.push(`gl-web/${this._gaxModule.version}`);
    }
    if (!opts.fallback) {
      clientHeader.push(`grpc/${this._gaxGrpc.grpcVersion}`);
    } else {
      clientHeader.push(`rest/${this._gaxGrpc.grpcVersion}`);
    }
    if (opts.libName && opts.libVersion) {
      clientHeader.push(`${opts.libName}/${opts.libVersion}`);
    }
    // Load the applicable protos.
    this._protos = this._gaxGrpc.loadProtoJSON(jsonProtos);

    // This API contains "path templates"; forward-slash-separated
    // identifiers to uniquely identify resources within the API.
    // Create useful helper objects for these.
    this.pathTemplates = {
      databasePathTemplate: new this._gaxModule.PathTemplate(
        'projects/{project}/instances/{instance}/databases/{database}',
      ),
      sessionPathTemplate: new this._gaxModule.PathTemplate(
        'projects/{project}/instances/{instance}/databases/{database}/sessions/{session}',
      ),
    };

    // Some of the methods on this service return "paged" results,
    // (e.g. 50 results at a time, with tokens to get subsequent
    // pages). Denote the keys used for pagination and results.
    this.descriptors.page = {
      listSessions: new this._gaxModule.PageDescriptor(
        'pageToken',
        'nextPageToken',
        'sessions',
      ),
    };

    // Some of the methods on this service provide streaming responses.
    // Provide descriptors for these.
    this.descriptors.stream = {
      executeStreamingSql: new this._gaxModule.StreamDescriptor(
        this._gaxModule.StreamType.SERVER_STREAMING,
        !!opts.fallback,
        !!opts.gaxServerStreamingRetries,
      ),
      streamingRead: new this._gaxModule.StreamDescriptor(
        this._gaxModule.StreamType.SERVER_STREAMING,
        !!opts.fallback,
        !!opts.gaxServerStreamingRetries,
      ),
      batchWrite: new this._gaxModule.StreamDescriptor(
        this._gaxModule.StreamType.SERVER_STREAMING,
        !!opts.fallback,
        !!opts.gaxServerStreamingRetries,
      ),
    };

    // Put together the default options sent with requests.
    this._defaults = this._gaxGrpc.constructSettings(
      'google.spanner.v1.Spanner',
      gapicConfig as gax.ClientConfig,
      opts.clientConfig || {},
      {'x-goog-api-client': clientHeader.join(' ')},
    );

    // Set up a dictionary of "inner API calls"; the core implementation
    // of calling the API is handled in `google-gax`, with this code
    // merely providing the destination and request information.
    this.innerApiCalls = {};

    // Add a warn function to the client constructor so it can be easily tested.
    this.warn = this._gaxModule.warn;
  }

  /**
   * Initialize the client.
   * Performs asynchronous operations (such as authentication) and prepares the client.
   * This function will be called automatically when any class method is called for the
   * first time, but if you need to initialize it before calling an actual method,
   * feel free to call initialize() directly.
   *
   * You can await on this method if you want to make sure the client is initialized.
   *
   * @returns {Promise} A promise that resolves to an authenticated service stub.
   */
  initialize() {
    // If the client stub promise is already initialized, return immediately.
    if (this.spannerStub) {
      return this.spannerStub;
    }

    // Put together the "service stub" for
    // google.spanner.v1.Spanner.
    this.spannerStub = this._gaxGrpc.createStub(
      this._opts.fallback
        ? (this._protos as protobuf.Root).lookupService(
            'google.spanner.v1.Spanner',
          )
        : // eslint-disable-next-line @typescript-eslint/no-explicit-any
          (this._protos as any).google.spanner.v1.Spanner,
      this._opts,
      this._providedCustomServicePath,
    ) as Promise<{[method: string]: Function}>;

    // Iterate over each of the methods that the service provides
    // and create an API call method for each.
    const spannerStubMethods = [
      'createSession',
      'batchCreateSessions',
      'getSession',
      'listSessions',
      'deleteSession',
      'executeSql',
      'executeStreamingSql',
      'executeBatchDml',
      'read',
      'streamingRead',
      'beginTransaction',
      'commit',
      'rollback',
      'partitionQuery',
      'partitionRead',
      'batchWrite',
    ];
    for (const methodName of spannerStubMethods) {
      const callPromise = this.spannerStub.then(
        stub =>
          (...args: Array<{}>) => {
            if (this._terminated) {
              if (methodName in this.descriptors.stream) {
                const stream = new PassThrough({objectMode: true});
                setImmediate(() => {
                  stream.emit(
                    'error',
                    new this._gaxModule.GoogleError(
                      'The client has already been closed.',
                    ),
                  );
                });
                return stream;
              }
              return Promise.reject('The client has already been closed.');
            }
            const func = stub[methodName];
            return func.apply(stub, args);
          },
        (err: Error | null | undefined) => () => {
          throw err;
        },
      );

      const descriptor =
        this.descriptors.page[methodName] ||
        this.descriptors.stream[methodName] ||
        undefined;
      const apiCall = this._gaxModule.createApiCall(
        callPromise,
        this._defaults[methodName],
        descriptor,
        this._opts.fallback,
      );

      this.innerApiCalls[methodName] = apiCall;
    }

    return this.spannerStub;
  }

  /**
   * The DNS address for this API service.
   * @deprecated Use the apiEndpoint method of the client instance.
   * @returns {string} The DNS address for this service.
   */
  static get servicePath() {
    if (
      typeof process === 'object' &&
      typeof process.emitWarning === 'function'
    ) {
      process.emitWarning(
        'Static servicePath is deprecated, please use the instance method instead.',
        'DeprecationWarning',
      );
    }
    return 'spanner.googleapis.com';
  }

  /**
   * The DNS address for this API service - same as servicePath.
   * @deprecated Use the apiEndpoint method of the client instance.
   * @returns {string} The DNS address for this service.
   */
  static get apiEndpoint() {
    if (
      typeof process === 'object' &&
      typeof process.emitWarning === 'function'
    ) {
      process.emitWarning(
        'Static apiEndpoint is deprecated, please use the instance method instead.',
        'DeprecationWarning',
      );
    }
    return 'spanner.googleapis.com';
  }

  /**
   * The DNS address for this API service.
   * @returns {string} The DNS address for this service.
   */
  get apiEndpoint() {
    return this._servicePath;
  }

  get universeDomain() {
    return this._universeDomain;
  }

  /**
   * The port for this API service.
   * @returns {number} The default port for this service.
   */
  static get port() {
    return 443;
  }

  /**
   * The scopes needed to make gRPC calls for every method defined
   * in this service.
   * @returns {string[]} List of default scopes.
   */
  static get scopes() {
    return [
      'https://www.googleapis.com/auth/cloud-platform',
      'https://www.googleapis.com/auth/spanner.data',
    ];
  }

  getProjectId(): Promise<string>;
  getProjectId(callback: Callback<string, undefined, undefined>): void;
  /**
   * Return the project ID used by this class.
   * @returns {Promise} A promise that resolves to string containing the project ID.
   */
  getProjectId(
    callback?: Callback<string, undefined, undefined>,
  ): Promise<string> | void {
    if (callback) {
      this.auth.getProjectId(callback);
      return;
    }
    return this.auth.getProjectId();
  }

  // -------------------
  // -- Service calls --
  // -------------------
  /**
   * 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's a good idea to
   * delete idle and unneeded sessions.
   * Aside from explicit deletes, Cloud Spanner can delete sessions when 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, for example, `"SELECT 1"`.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.database
   *   Required. The database in which the new session is created.
   * @param {google.spanner.v1.Session} request.session
   *   Required. The session to create.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.spanner.v1.Session|Session}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  createSession(
    request?: protos.google.spanner.v1.ICreateSessionRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.spanner.v1.ISession,
      protos.google.spanner.v1.ICreateSessionRequest | undefined,
      {} | undefined,
    ]
  >;
  createSession(
    request: protos.google.spanner.v1.ICreateSessionRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.spanner.v1.ISession,
      protos.google.spanner.v1.ICreateSessionRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  createSession(
    request: protos.google.spanner.v1.ICreateSessionRequest,
    callback: Callback<
      protos.google.spanner.v1.ISession,
      protos.google.spanner.v1.ICreateSessionRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  createSession(
    request?: protos.google.spanner.v1.ICreateSessionRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.spanner.v1.ISession,
          protos.google.spanner.v1.ICreateSessionRequest | null | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.spanner.v1.ISession,
      protos.google.spanner.v1.ICreateSessionRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.spanner.v1.ISession,
      protos.google.spanner.v1.ICreateSessionRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        database: request.database ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('createSession request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.spanner.v1.ISession,
          protos.google.spanner.v1.ICreateSessionRequest | null | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('createSession response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .createSession(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.spanner.v1.ISession,
          protos.google.spanner.v1.ICreateSessionRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('createSession response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }
  /**
   * 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.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.database
   *   Required. The database in which the new sessions are created.
   * @param {google.spanner.v1.Session} request.sessionTemplate
   *   Parameters to apply to each created session.
   * @param {number} request.sessionCount
   *   Required. The number of sessions to be created in this batch call.
   *   The API can 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
   *   {@link protos.google.spanner.v1.BatchCreateSessionsRequest.session_count|session_count}
   *   as necessary).
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.spanner.v1.BatchCreateSessionsResponse|BatchCreateSessionsResponse}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  batchCreateSessions(
    request?: protos.google.spanner.v1.IBatchCreateSessionsRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.spanner.v1.IBatchCreateSessionsResponse,
      protos.google.spanner.v1.IBatchCreateSessionsRequest | undefined,
      {} | undefined,
    ]
  >;
  batchCreateSessions(
    request: protos.google.spanner.v1.IBatchCreateSessionsRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.spanner.v1.IBatchCreateSessionsResponse,
      protos.google.spanner.v1.IBatchCreateSessionsRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  batchCreateSessions(
    request: protos.google.spanner.v1.IBatchCreateSessionsRequest,
    callback: Callback<
      protos.google.spanner.v1.IBatchCreateSessionsResponse,
      protos.google.spanner.v1.IBatchCreateSessionsRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  batchCreateSessions(
    request?: protos.google.spanner.v1.IBatchCreateSessionsRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.spanner.v1.IBatchCreateSessionsResponse,
          | protos.google.spanner.v1.IBatchCreateSessionsRequest
          | null
          | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.spanner.v1.IBatchCreateSessionsResponse,
      protos.google.spanner.v1.IBatchCreateSessionsRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.spanner.v1.IBatchCreateSessionsResponse,
      protos.google.spanner.v1.IBatchCreateSessionsRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        database: request.database ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('batchCreateSessions request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.spanner.v1.IBatchCreateSessionsResponse,
          | protos.google.spanner.v1.IBatchCreateSessionsRequest
          | null
          | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('batchCreateSessions response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .batchCreateSessions(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.spanner.v1.IBatchCreateSessionsResponse,
          protos.google.spanner.v1.IBatchCreateSessionsRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('batchCreateSessions response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }
  /**
   * Gets a session. Returns `NOT_FOUND` if the session doesn't exist.
   * This is mainly useful for determining whether a session is still
   * alive.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.name
   *   Required. The name of the session to retrieve.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.spanner.v1.Session|Session}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  getSession(
    request?: protos.google.spanner.v1.IGetSessionRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.spanner.v1.ISession,
      protos.google.spanner.v1.IGetSessionRequest | undefined,
      {} | undefined,
    ]
  >;
  getSession(
    request: protos.google.spanner.v1.IGetSessionRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.spanner.v1.ISession,
      protos.google.spanner.v1.IGetSessionRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  getSession(
    request: protos.google.spanner.v1.IGetSessionRequest,
    callback: Callback<
      protos.google.spanner.v1.ISession,
      protos.google.spanner.v1.IGetSessionRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  getSession(
    request?: protos.google.spanner.v1.IGetSessionRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.spanner.v1.ISession,
          protos.google.spanner.v1.IGetSessionRequest | null | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.spanner.v1.ISession,
      protos.google.spanner.v1.IGetSessionRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.spanner.v1.ISession,
      protos.google.spanner.v1.IGetSessionRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        name: request.name ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('getSession request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.spanner.v1.ISession,
          protos.google.spanner.v1.IGetSessionRequest | null | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('getSession response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .getSession(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.spanner.v1.ISession,
          protos.google.spanner.v1.IGetSessionRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('getSession response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }
  /**
   * Ends a session, releasing server resources associated with it. This
   * asynchronously triggers the cancellation of any operations that are running
   * with this session.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.name
   *   Required. The name of the session to delete.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.protobuf.Empty|Empty}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  deleteSession(
    request?: protos.google.spanner.v1.IDeleteSessionRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.protobuf.IEmpty,
      protos.google.spanner.v1.IDeleteSessionRequest | undefined,
      {} | undefined,
    ]
  >;
  deleteSession(
    request: protos.google.spanner.v1.IDeleteSessionRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.protobuf.IEmpty,
      protos.google.spanner.v1.IDeleteSessionRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  deleteSession(
    request: protos.google.spanner.v1.IDeleteSessionRequest,
    callback: Callback<
      protos.google.protobuf.IEmpty,
      protos.google.spanner.v1.IDeleteSessionRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  deleteSession(
    request?: protos.google.spanner.v1.IDeleteSessionRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.protobuf.IEmpty,
          protos.google.spanner.v1.IDeleteSessionRequest | null | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.protobuf.IEmpty,
      protos.google.spanner.v1.IDeleteSessionRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.protobuf.IEmpty,
      protos.google.spanner.v1.IDeleteSessionRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        name: request.name ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('deleteSession request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.protobuf.IEmpty,
          protos.google.spanner.v1.IDeleteSessionRequest | null | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('deleteSession response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .deleteSession(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.protobuf.IEmpty,
          protos.google.spanner.v1.IDeleteSessionRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('deleteSession response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }
  /**
   * Executes an SQL statement, returning all results in a single reply. This
   * method can't 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 {@link protos.google.spanner.v1.Transaction|Transaction} for more
   * details.
   *
   * Larger result sets can be fetched in streaming fashion by calling
   * {@link protos.google.spanner.v1.Spanner.ExecuteStreamingSql|ExecuteStreamingSql}
   * instead.
   *
   * The query string can be SQL or [Graph Query Language
   * (GQL)](https://cloud.google.com/spanner/docs/reference/standard-sql/graph-intro).
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.session
   *   Required. The session in which the SQL query should be performed.
   * @param {google.spanner.v1.TransactionSelector} request.transaction
   *   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.
   * @param {string} request.sql
   *   Required. The SQL string.
   * @param {google.protobuf.Struct} request.params
   *   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's an error to execute a SQL statement with unbound parameters.
   * @param {number[]} request.paramTypes
   *   It isn't 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
   *   {@link protos.google.spanner.v1.ExecuteSqlRequest.params|params} as JSON strings.
   *
   *   In these cases, you can use `param_types` to specify the exact
   *   SQL type for some or all of the SQL statement parameters. See the
   *   definition of {@link protos.google.spanner.v1.Type|Type} for more information
   *   about SQL types.
   * @param {Buffer} request.resumeToken
   *   If this request is resuming a previously interrupted SQL statement
   *   execution, `resume_token` should be copied from the last
   *   {@link protos.google.spanner.v1.PartialResultSet|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.
   * @param {google.spanner.v1.ExecuteSqlRequest.QueryMode} request.queryMode
   *   Used to control the amount of debugging information returned in
   *   {@link protos.google.spanner.v1.ResultSetStats|ResultSetStats}. If
   *   {@link protos.google.spanner.v1.ExecuteSqlRequest.partition_token|partition_token} is
   *   set, {@link protos.google.spanner.v1.ExecuteSqlRequest.query_mode|query_mode} can only
   *   be set to
   *   {@link protos.google.spanner.v1.ExecuteSqlRequest.QueryMode.NORMAL|QueryMode.NORMAL}.
   * @param {Buffer} request.partitionToken
   *   If present, results are 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`.
   * @param {number} request.seqno
   *   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 succeeds.
   *
   *   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 can be aborted. Replays of previously
   *   handled requests yield the same response as the first execution.
   *
   *   Required for DML statements. Ignored for queries.
   * @param {google.spanner.v1.ExecuteSqlRequest.QueryOptions} request.queryOptions
   *   Query optimizer configuration to use for the given query.
   * @param {google.spanner.v1.RequestOptions} request.requestOptions
   *   Common options for this request.
   * @param {google.spanner.v1.DirectedReadOptions} request.directedReadOptions
   *   Directed read options for this request.
   * @param {boolean} request.dataBoostEnabled
   *   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 doesn't set
   *   `partition_token`, the API returns an `INVALID_ARGUMENT` error.
   * @param {boolean} [request.lastStatement]
   *   Optional. If set to `true`, this statement marks the end of the
   *   transaction. After this statement executes, you must commit or abort the
   *   transaction. Attempts to execute any other requests against this
   *   transaction (including reads and queries) are rejected.
   *
   *   For DML statements, setting this option might cause some error reporting to
   *   be deferred until commit time (for example, validation of unique
   *   constraints). Given this, successful execution of a DML statement shouldn't
   *   be assumed until a subsequent `Commit` call completes successfully.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.spanner.v1.ResultSet|ResultSet}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  executeSql(
    request?: protos.google.spanner.v1.IExecuteSqlRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.spanner.v1.IResultSet,
      protos.google.spanner.v1.IExecuteSqlRequest | undefined,
      {} | undefined,
    ]
  >;
  executeSql(
    request: protos.google.spanner.v1.IExecuteSqlRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.spanner.v1.IResultSet,
      protos.google.spanner.v1.IExecuteSqlRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  executeSql(
    request: protos.google.spanner.v1.IExecuteSqlRequest,
    callback: Callback<
      protos.google.spanner.v1.IResultSet,
      protos.google.spanner.v1.IExecuteSqlRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  executeSql(
    request?: protos.google.spanner.v1.IExecuteSqlRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.spanner.v1.IResultSet,
          protos.google.spanner.v1.IExecuteSqlRequest | null | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.spanner.v1.IResultSet,
      protos.google.spanner.v1.IExecuteSqlRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.spanner.v1.IResultSet,
      protos.google.spanner.v1.IExecuteSqlRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        session: request.session ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('executeSql request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.spanner.v1.IResultSet,
          protos.google.spanner.v1.IExecuteSqlRequest | null | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('executeSql response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .executeSql(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.spanner.v1.IResultSet,
          protos.google.spanner.v1.IExecuteSqlRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('executeSql response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }
  /**
   * Executes a batch of SQL DML statements. This method allows many statements
   * to be run with lower latency than submitting them sequentially with
   * {@link protos.google.spanner.v1.Spanner.ExecuteSql|ExecuteSql}.
   *
   * Statements are executed in sequential order. A request can succeed even if
   * a statement fails. The
   * {@link protos.google.spanner.v1.ExecuteBatchDmlResponse.status|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.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.session
   *   Required. The session in which the DML statements should be performed.
   * @param {google.spanner.v1.TransactionSelector} request.transaction
   *   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.
   * @param {number[]} request.statements
   *   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.
   * @param {number} request.seqno
   *   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 succeeds.
   *
   *   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 might be aborted. Replays of previously
   *   handled requests yield the same response as the first execution.
   * @param {google.spanner.v1.RequestOptions} request.requestOptions
   *   Common options for this request.
   * @param {boolean} [request.lastStatements]
   *   Optional. If set to `true`, this request marks the end of the transaction.
   *   After these statements execute, you must commit or abort the transaction.
   *   Attempts to execute any other requests against this transaction
   *   (including reads and queries) are rejected.
   *
   *   Setting this option might cause some error reporting to be deferred until
   *   commit time (for example, validation of unique constraints). Given this,
   *   successful execution of statements shouldn't be assumed until a subsequent
   *   `Commit` call completes successfully.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.spanner.v1.ExecuteBatchDmlResponse|ExecuteBatchDmlResponse}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  executeBatchDml(
    request?: protos.google.spanner.v1.IExecuteBatchDmlRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.spanner.v1.IExecuteBatchDmlResponse,
      protos.google.spanner.v1.IExecuteBatchDmlRequest | undefined,
      {} | undefined,
    ]
  >;
  executeBatchDml(
    request: protos.google.spanner.v1.IExecuteBatchDmlRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.spanner.v1.IExecuteBatchDmlResponse,
      protos.google.spanner.v1.IExecuteBatchDmlRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  executeBatchDml(
    request: protos.google.spanner.v1.IExecuteBatchDmlRequest,
    callback: Callback<
      protos.google.spanner.v1.IExecuteBatchDmlResponse,
      protos.google.spanner.v1.IExecuteBatchDmlRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  executeBatchDml(
    request?: protos.google.spanner.v1.IExecuteBatchDmlRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.spanner.v1.IExecuteBatchDmlResponse,
          protos.google.spanner.v1.IExecuteBatchDmlRequest | null | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.spanner.v1.IExecuteBatchDmlResponse,
      protos.google.spanner.v1.IExecuteBatchDmlRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.spanner.v1.IExecuteBatchDmlResponse,
      protos.google.spanner.v1.IExecuteBatchDmlRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        session: request.session ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('executeBatchDml request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.spanner.v1.IExecuteBatchDmlResponse,
          protos.google.spanner.v1.IExecuteBatchDmlRequest | null | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('executeBatchDml response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .executeBatchDml(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.spanner.v1.IExecuteBatchDmlResponse,
          protos.google.spanner.v1.IExecuteBatchDmlRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('executeBatchDml response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }
  /**
   * Reads rows from the database using key lookups and scans, as a
   * simple key/value style alternative to
   * {@link protos.google.spanner.v1.Spanner.ExecuteSql|ExecuteSql}. This method can't 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 {@link protos.google.spanner.v1.Transaction|Transaction} for more
   * details.
   *
   * Larger result sets can be yielded in streaming fashion by calling
   * {@link protos.google.spanner.v1.Spanner.StreamingRead|StreamingRead} instead.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.session
   *   Required. The session in which the read should be performed.
   * @param {google.spanner.v1.TransactionSelector} request.transaction
   *   The transaction to use. If none is provided, the default is a
   *   temporary read-only transaction with strong concurrency.
   * @param {string} request.table
   *   Required. The name of the table in the database to be read.
   * @param {string} request.index
   *   If non-empty, the name of an index on
   *   {@link protos.google.spanner.v1.ReadRequest.table|table}. This index is used instead of
   *   the table primary key when interpreting
   *   {@link protos.google.spanner.v1.ReadRequest.key_set|key_set} and sorting result rows.
   *   See {@link protos.google.spanner.v1.ReadRequest.key_set|key_set} for further
   *   information.
   * @param {string[]} request.columns
   *   Required. The columns of {@link protos.google.spanner.v1.ReadRequest.table|table} to be
   *   returned for each row matching this request.
   * @param {google.spanner.v1.KeySet} request.keySet
   *   Required. `key_set` identifies the rows to be yielded. `key_set` names the
   *   primary keys of the rows in {@link protos.google.spanner.v1.ReadRequest.table|table} to
   *   be yielded, unless {@link protos.google.spanner.v1.ReadRequest.index|index} is present.
   *   If {@link protos.google.spanner.v1.ReadRequest.index|index} is present, then
   *   {@link protos.google.spanner.v1.ReadRequest.key_set|key_set} instead names index keys
   *   in {@link protos.google.spanner.v1.ReadRequest.index|index}.
   *
   *   If the {@link protos.google.spanner.v1.ReadRequest.partition_token|partition_token}
   *   field is empty, rows are yielded in table primary key order (if
   *   {@link protos.google.spanner.v1.ReadRequest.index|index} is empty) or index key order
   *   (if {@link protos.google.spanner.v1.ReadRequest.index|index} is non-empty). If the
   *   {@link protos.google.spanner.v1.ReadRequest.partition_token|partition_token} field
   *   isn't empty, rows are yielded in an unspecified order.
   *
   *   It isn't an error for the `key_set` to name rows that don't
   *   exist in the database. Read yields nothing for nonexistent rows.
   * @param {number} request.limit
   *   If greater than zero, only the first `limit` rows are yielded. If `limit`
   *   is zero, the default is no limit. A limit can't be specified if
   *   `partition_token` is set.
   * @param {Buffer} request.resumeToken
   *   If this request is resuming a previously interrupted read,
   *   `resume_token` should be copied from the last
   *   {@link protos.google.spanner.v1.PartialResultSet|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.
   * @param {Buffer} request.partitionToken
   *   If present, results are 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.
   * @param {google.spanner.v1.RequestOptions} request.requestOptions
   *   Common options for this request.
   * @param {google.spanner.v1.DirectedReadOptions} request.directedReadOptions
   *   Directed read options for this request.
   * @param {boolean} request.dataBoostEnabled
   *   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 doesn't set
   *   `partition_token`, the API returns an `INVALID_ARGUMENT` error.
   * @param {google.spanner.v1.ReadRequest.OrderBy} [request.orderBy]
   *   Optional. Order for the returned rows.
   *
   *   By default, Spanner returns result rows in primary key order except for
   *   PartitionRead requests. For applications that don't 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 (for example, bulk point
   *   lookups).
   * @param {google.spanner.v1.ReadRequest.LockHint} [request.lockHint]
   *   Optional. Lock Hint for the request, it can only be used with read-write
   *   transactions.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.spanner.v1.ResultSet|ResultSet}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  read(
    request?: protos.google.spanner.v1.IReadRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.spanner.v1.IResultSet,
      protos.google.spanner.v1.IReadRequest | undefined,
      {} | undefined,
    ]
  >;
  read(
    request: protos.google.spanner.v1.IReadRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.spanner.v1.IResultSet,
      protos.google.spanner.v1.IReadRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  read(
    request: protos.google.spanner.v1.IReadRequest,
    callback: Callback<
      protos.google.spanner.v1.IResultSet,
      protos.google.spanner.v1.IReadRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  read(
    request?: protos.google.spanner.v1.IReadRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.spanner.v1.IResultSet,
          protos.google.spanner.v1.IReadRequest | null | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.spanner.v1.IResultSet,
      protos.google.spanner.v1.IReadRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.spanner.v1.IResultSet,
      protos.google.spanner.v1.IReadRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        session: request.session ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('read request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.spanner.v1.IResultSet,
          protos.google.spanner.v1.IReadRequest | null | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('read response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .read(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.spanner.v1.IResultSet,
          protos.google.spanner.v1.IReadRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('read response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }
  /**
   * Begins a new transaction. This step can often be skipped:
   * {@link protos.google.spanner.v1.Spanner.Read|Read},
   * {@link protos.google.spanner.v1.Spanner.ExecuteSql|ExecuteSql} and
   * {@link protos.google.spanner.v1.Spanner.Commit|Commit} can begin a new transaction as a
   * side-effect.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.session
   *   Required. The session in which the transaction runs.
   * @param {google.spanner.v1.TransactionOptions} request.options
   *   Required. Options for the new transaction.
   * @param {google.spanner.v1.RequestOptions} request.requestOptions
   *   Common options for this request.
   *   Priority is ignored for this request. Setting the priority in this
   *   `request_options` struct doesn't do anything. To set the priority for a
   *   transaction, set it on the reads and writes that are part of this
   *   transaction instead.
   * @param {google.spanner.v1.Mutation} [request.mutationKey]
   *   Optional. Required for read-write transactions on a multiplexed session
   *   that commit mutations but don't perform any reads or queries. You must
   *   randomly select one of the mutations from the mutation set and send it as a
   *   part of this request.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.spanner.v1.Transaction|Transaction}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  beginTransaction(
    request?: protos.google.spanner.v1.IBeginTransactionRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.spanner.v1.ITransaction,
      protos.google.spanner.v1.IBeginTransactionRequest | undefined,
      {} | undefined,
    ]
  >;
  beginTransaction(
    request: protos.google.spanner.v1.IBeginTransactionRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.spanner.v1.ITransaction,
      protos.google.spanner.v1.IBeginTransactionRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  beginTransaction(
    request: protos.google.spanner.v1.IBeginTransactionRequest,
    callback: Callback<
      protos.google.spanner.v1.ITransaction,
      protos.google.spanner.v1.IBeginTransactionRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  beginTransaction(
    request?: protos.google.spanner.v1.IBeginTransactionRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.spanner.v1.ITransaction,
          protos.google.spanner.v1.IBeginTransactionRequest | null | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.spanner.v1.ITransaction,
      protos.google.spanner.v1.IBeginTransactionRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.spanner.v1.ITransaction,
      protos.google.spanner.v1.IBeginTransactionRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        session: request.session ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('beginTransaction request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.spanner.v1.ITransaction,
          protos.google.spanner.v1.IBeginTransactionRequest | null | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('beginTransaction response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .beginTransaction(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.spanner.v1.ITransaction,
          protos.google.spanner.v1.IBeginTransactionRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('beginTransaction response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }
  /**
   * 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 retry
   * the transaction from the beginning, reusing 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.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.session
   *   Required. The session in which the transaction to be committed is running.
   * @param {Buffer} request.transactionId
   *   Commit a previously-started transaction.
   * @param {google.spanner.v1.TransactionOptions} request.singleUseTransaction
   *   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's possible that the mutations are
   *   executed more than once. If this is undesirable, use
   *   {@link protos.google.spanner.v1.Spanner.BeginTransaction|BeginTransaction} and
   *   {@link protos.google.spanner.v1.Spanner.Commit|Commit} instead.
   * @param {number[]} request.mutations
   *   The mutations to be executed when this transaction commits. All
   *   mutations are applied atomically, in the order they appear in
   *   this list.
   * @param {boolean} request.returnCommitStats
   *   If `true`, then statistics related to the transaction is included in
   *   the {@link protos.google.spanner.v1.CommitResponse.commit_stats|CommitResponse}.
   *   Default value is `false`.
   * @param {google.protobuf.Duration} [request.maxCommitDelay]
   *   Optional. The amount of latency this request is configured to incur in
   *   order to improve throughput. If this field isn't set, Spanner assumes
   *   requests are relatively latency sensitive and automatically determines an
   *   appropriate delay time. You can specify a commit delay value between 0 and
   *   500 ms.
   * @param {google.spanner.v1.RequestOptions} request.requestOptions
   *   Common options for this request.
   * @param {google.spanner.v1.MultiplexedSessionPrecommitToken} [request.precommitToken]
   *   Optional. If the read-write transaction was executed on a multiplexed
   *   session, then you must include the precommit token with the highest
   *   sequence number received in this transaction attempt. Failing to do so
   *   results in a `FailedPrecondition` error.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.spanner.v1.CommitResponse|CommitResponse}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  commit(
    request?: protos.google.spanner.v1.ICommitRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.spanner.v1.ICommitResponse,
      protos.google.spanner.v1.ICommitRequest | undefined,
      {} | undefined,
    ]
  >;
  commit(
    request: protos.google.spanner.v1.ICommitRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.spanner.v1.ICommitResponse,
      protos.google.spanner.v1.ICommitRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  commit(
    request: protos.google.spanner.v1.ICommitRequest,
    callback: Callback<
      protos.google.spanner.v1.ICommitResponse,
      protos.google.spanner.v1.ICommitRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  commit(
    request?: protos.google.spanner.v1.ICommitRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.spanner.v1.ICommitResponse,
          protos.google.spanner.v1.ICommitRequest | null | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.spanner.v1.ICommitResponse,
      protos.google.spanner.v1.ICommitRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.spanner.v1.ICommitResponse,
      protos.google.spanner.v1.ICommitRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        session: request.session ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('commit request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.spanner.v1.ICommitResponse,
          protos.google.spanner.v1.ICommitRequest | null | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('commit response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .commit(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.spanner.v1.ICommitResponse,
          protos.google.spanner.v1.ICommitRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('commit response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }
  /**
   * Rolls back a transaction, releasing any locks it holds. It's a good
   * idea to call this for any transaction that includes one or more
   * {@link protos.google.spanner.v1.Spanner.Read|Read} or
   * {@link protos.google.spanner.v1.Spanner.ExecuteSql|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 isn't
   * found. `Rollback` never returns `ABORTED`.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.session
   *   Required. The session in which the transaction to roll back is running.
   * @param {Buffer} request.transactionId
   *   Required. The transaction to roll back.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.protobuf.Empty|Empty}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  rollback(
    request?: protos.google.spanner.v1.IRollbackRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.protobuf.IEmpty,
      protos.google.spanner.v1.IRollbackRequest | undefined,
      {} | undefined,
    ]
  >;
  rollback(
    request: protos.google.spanner.v1.IRollbackRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.protobuf.IEmpty,
      protos.google.spanner.v1.IRollbackRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  rollback(
    request: protos.google.spanner.v1.IRollbackRequest,
    callback: Callback<
      protos.google.protobuf.IEmpty,
      protos.google.spanner.v1.IRollbackRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  rollback(
    request?: protos.google.spanner.v1.IRollbackRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.protobuf.IEmpty,
          protos.google.spanner.v1.IRollbackRequest | null | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.protobuf.IEmpty,
      protos.google.spanner.v1.IRollbackRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.protobuf.IEmpty,
      protos.google.spanner.v1.IRollbackRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        session: request.session ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('rollback request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.protobuf.IEmpty,
          protos.google.spanner.v1.IRollbackRequest | null | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('rollback response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .rollback(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.protobuf.IEmpty,
          protos.google.spanner.v1.IRollbackRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('rollback response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }
  /**
   * 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 {@link protos.google.spanner.v1.Spanner.ExecuteStreamingSql|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 isn't possible to resume the query, and
   * the whole operation must be restarted from the beginning.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.session
   *   Required. The session used to create the partitions.
   * @param {google.spanner.v1.TransactionSelector} request.transaction
   *   Read-only snapshot transactions are supported, read and write and
   *   single-use transactions are not.
   * @param {string} request.sql
   *   Required. The query request to generate partitions for. The request fails
   *   if the query isn't 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
   *   {@link protos.google.spanner.v1.Spanner.ExecuteStreamingSql|`ExecuteStreamingSql`} with
   *   a `PartitionedDml` transaction for large, partition-friendly DML
   *   operations.
   * @param {google.protobuf.Struct} request.params
   *   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's an error to execute a SQL statement with unbound parameters.
   * @param {number[]} request.paramTypes
   *   It isn't 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
   *   {@link protos.google.spanner.v1.PartitionQueryRequest.params|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 {@link protos.google.spanner.v1.Type|Type} for more information
   *   about SQL types.
   * @param {google.spanner.v1.PartitionOptions} request.partitionOptions
   *   Additional options that affect how many partitions are created.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.spanner.v1.PartitionResponse|PartitionResponse}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  partitionQuery(
    request?: protos.google.spanner.v1.IPartitionQueryRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.spanner.v1.IPartitionResponse,
      protos.google.spanner.v1.IPartitionQueryRequest | undefined,
      {} | undefined,
    ]
  >;
  partitionQuery(
    request: protos.google.spanner.v1.IPartitionQueryRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.spanner.v1.IPartitionResponse,
      protos.google.spanner.v1.IPartitionQueryRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  partitionQuery(
    request: protos.google.spanner.v1.IPartitionQueryRequest,
    callback: Callback<
      protos.google.spanner.v1.IPartitionResponse,
      protos.google.spanner.v1.IPartitionQueryRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  partitionQuery(
    request?: protos.google.spanner.v1.IPartitionQueryRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.spanner.v1.IPartitionResponse,
          protos.google.spanner.v1.IPartitionQueryRequest | null | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.spanner.v1.IPartitionResponse,
      protos.google.spanner.v1.IPartitionQueryRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.spanner.v1.IPartitionResponse,
      protos.google.spanner.v1.IPartitionQueryRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        session: request.session ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('partitionQuery request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.spanner.v1.IPartitionResponse,
          protos.google.spanner.v1.IPartitionQueryRequest | null | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('partitionQuery response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .partitionQuery(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.spanner.v1.IPartitionResponse,
          protos.google.spanner.v1.IPartitionQueryRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('partitionQuery response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }
  /**
   * 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 {@link protos.google.spanner.v1.Spanner.StreamingRead|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 isn't possible to resume the read, and
   * the whole operation must be restarted from the beginning.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.session
   *   Required. The session used to create the partitions.
   * @param {google.spanner.v1.TransactionSelector} request.transaction
   *   Read only snapshot transactions are supported, read/write and single use
   *   transactions are not.
   * @param {string} request.table
   *   Required. The name of the table in the database to be read.
   * @param {string} request.index
   *   If non-empty, the name of an index on
   *   {@link protos.google.spanner.v1.PartitionReadRequest.table|table}. This index is used
   *   instead of the table primary key when interpreting
   *   {@link protos.google.spanner.v1.PartitionReadRequest.key_set|key_set} and sorting
   *   result rows. See {@link protos.google.spanner.v1.PartitionReadRequest.key_set|key_set}
   *   for further information.
   * @param {string[]} request.columns
   *   The columns of {@link protos.google.spanner.v1.PartitionReadRequest.table|table} to be
   *   returned for each row matching this request.
   * @param {google.spanner.v1.KeySet} request.keySet
   *   Required. `key_set` identifies the rows to be yielded. `key_set` names the
   *   primary keys of the rows in
   *   {@link protos.google.spanner.v1.PartitionReadRequest.table|table} to be yielded, unless
   *   {@link protos.google.spanner.v1.PartitionReadRequest.index|index} is present. If
   *   {@link protos.google.spanner.v1.PartitionReadRequest.index|index} is present, then
   *   {@link protos.google.spanner.v1.PartitionReadRequest.key_set|key_set} instead names
   *   index keys in {@link protos.google.spanner.v1.PartitionReadRequest.index|index}.
   *
   *   It isn't an error for the `key_set` to name rows that don't
   *   exist in the database. Read yields nothing for nonexistent rows.
   * @param {google.spanner.v1.PartitionOptions} request.partitionOptions
   *   Additional options that affect how many partitions are created.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is an object representing {@link protos.google.spanner.v1.PartitionResponse|PartitionResponse}.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods | documentation }
   *   for more details and examples.
   */
  partitionRead(
    request?: protos.google.spanner.v1.IPartitionReadRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.spanner.v1.IPartitionResponse,
      protos.google.spanner.v1.IPartitionReadRequest | undefined,
      {} | undefined,
    ]
  >;
  partitionRead(
    request: protos.google.spanner.v1.IPartitionReadRequest,
    options: CallOptions,
    callback: Callback<
      protos.google.spanner.v1.IPartitionResponse,
      protos.google.spanner.v1.IPartitionReadRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  partitionRead(
    request: protos.google.spanner.v1.IPartitionReadRequest,
    callback: Callback<
      protos.google.spanner.v1.IPartitionResponse,
      protos.google.spanner.v1.IPartitionReadRequest | null | undefined,
      {} | null | undefined
    >,
  ): void;
  partitionRead(
    request?: protos.google.spanner.v1.IPartitionReadRequest,
    optionsOrCallback?:
      | CallOptions
      | Callback<
          protos.google.spanner.v1.IPartitionResponse,
          protos.google.spanner.v1.IPartitionReadRequest | null | undefined,
          {} | null | undefined
        >,
    callback?: Callback<
      protos.google.spanner.v1.IPartitionResponse,
      protos.google.spanner.v1.IPartitionReadRequest | null | undefined,
      {} | null | undefined
    >,
  ): Promise<
    [
      protos.google.spanner.v1.IPartitionResponse,
      protos.google.spanner.v1.IPartitionReadRequest | undefined,
      {} | undefined,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        session: request.session ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('partitionRead request %j', request);
    const wrappedCallback:
      | Callback<
          protos.google.spanner.v1.IPartitionResponse,
          protos.google.spanner.v1.IPartitionReadRequest | null | undefined,
          {} | null | undefined
        >
      | undefined = callback
      ? (error, response, options, rawResponse) => {
          this._log.info('partitionRead response %j', response);
          callback!(error, response, options, rawResponse); // We verified callback above.
        }
      : undefined;
    return this.innerApiCalls
      .partitionRead(request, options, wrappedCallback)
      ?.then(
        ([response, options, rawResponse]: [
          protos.google.spanner.v1.IPartitionResponse,
          protos.google.spanner.v1.IPartitionReadRequest | undefined,
          {} | undefined,
        ]) => {
          this._log.info('partitionRead response %j', response);
          return [response, options, rawResponse];
        },
      )
      .catch((error: any) => {
        if (
          error &&
          'statusDetails' in error &&
          error.statusDetails instanceof Array
        ) {
          const protos = this._gaxModule.protobuf.Root.fromJSON(
            jsonProtos,
          ) as unknown as gax.protobuf.Type;
          error.statusDetails = decodeAnyProtosInArray(
            error.statusDetails,
            protos,
          );
        }
        throw error;
      });
  }

  /**
   * Like {@link protos.google.spanner.v1.Spanner.ExecuteSql|ExecuteSql}, except returns the
   * result set as a stream. Unlike
   * {@link protos.google.spanner.v1.Spanner.ExecuteSql|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.
   *
   * The query string can be SQL or [Graph Query Language
   * (GQL)](https://cloud.google.com/spanner/docs/reference/standard-sql/graph-intro).
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.session
   *   Required. The session in which the SQL query should be performed.
   * @param {google.spanner.v1.TransactionSelector} request.transaction
   *   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.
   * @param {string} request.sql
   *   Required. The SQL string.
   * @param {google.protobuf.Struct} request.params
   *   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's an error to execute a SQL statement with unbound parameters.
   * @param {number[]} request.paramTypes
   *   It isn't 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
   *   {@link protos.google.spanner.v1.ExecuteSqlRequest.params|params} as JSON strings.
   *
   *   In these cases, you can use `param_types` to specify the exact
   *   SQL type for some or all of the SQL statement parameters. See the
   *   definition of {@link protos.google.spanner.v1.Type|Type} for more information
   *   about SQL types.
   * @param {Buffer} request.resumeToken
   *   If this request is resuming a previously interrupted SQL statement
   *   execution, `resume_token` should be copied from the last
   *   {@link protos.google.spanner.v1.PartialResultSet|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.
   * @param {google.spanner.v1.ExecuteSqlRequest.QueryMode} request.queryMode
   *   Used to control the amount of debugging information returned in
   *   {@link protos.google.spanner.v1.ResultSetStats|ResultSetStats}. If
   *   {@link protos.google.spanner.v1.ExecuteSqlRequest.partition_token|partition_token} is
   *   set, {@link protos.google.spanner.v1.ExecuteSqlRequest.query_mode|query_mode} can only
   *   be set to
   *   {@link protos.google.spanner.v1.ExecuteSqlRequest.QueryMode.NORMAL|QueryMode.NORMAL}.
   * @param {Buffer} request.partitionToken
   *   If present, results are 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`.
   * @param {number} request.seqno
   *   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 succeeds.
   *
   *   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 can be aborted. Replays of previously
   *   handled requests yield the same response as the first execution.
   *
   *   Required for DML statements. Ignored for queries.
   * @param {google.spanner.v1.ExecuteSqlRequest.QueryOptions} request.queryOptions
   *   Query optimizer configuration to use for the given query.
   * @param {google.spanner.v1.RequestOptions} request.requestOptions
   *   Common options for this request.
   * @param {google.spanner.v1.DirectedReadOptions} request.directedReadOptions
   *   Directed read options for this request.
   * @param {boolean} request.dataBoostEnabled
   *   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 doesn't set
   *   `partition_token`, the API returns an `INVALID_ARGUMENT` error.
   * @param {boolean} [request.lastStatement]
   *   Optional. If set to `true`, this statement marks the end of the
   *   transaction. After this statement executes, you must commit or abort the
   *   transaction. Attempts to execute any other requests against this
   *   transaction (including reads and queries) are rejected.
   *
   *   For DML statements, setting this option might cause some error reporting to
   *   be deferred until commit time (for example, validation of unique
   *   constraints). Given this, successful execution of a DML statement shouldn't
   *   be assumed until a subsequent `Commit` call completes successfully.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Stream}
   *   An object stream which emits {@link protos.google.spanner.v1.PartialResultSet|PartialResultSet} on 'data' event.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#server-streaming | documentation }
   *   for more details and examples.
   */
  executeStreamingSql(
    request?: protos.google.spanner.v1.IExecuteSqlRequest,
    options?: CallOptions,
  ): gax.CancellableStream {
    request = request || {};
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        session: request.session ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('executeStreamingSql stream %j', options);
    return this.innerApiCalls.executeStreamingSql(request, options);
  }

  /**
   * Like {@link protos.google.spanner.v1.Spanner.Read|Read}, except returns the result set
   * as a stream. Unlike {@link protos.google.spanner.v1.Spanner.Read|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.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.session
   *   Required. The session in which the read should be performed.
   * @param {google.spanner.v1.TransactionSelector} request.transaction
   *   The transaction to use. If none is provided, the default is a
   *   temporary read-only transaction with strong concurrency.
   * @param {string} request.table
   *   Required. The name of the table in the database to be read.
   * @param {string} request.index
   *   If non-empty, the name of an index on
   *   {@link protos.google.spanner.v1.ReadRequest.table|table}. This index is used instead of
   *   the table primary key when interpreting
   *   {@link protos.google.spanner.v1.ReadRequest.key_set|key_set} and sorting result rows.
   *   See {@link protos.google.spanner.v1.ReadRequest.key_set|key_set} for further
   *   information.
   * @param {string[]} request.columns
   *   Required. The columns of {@link protos.google.spanner.v1.ReadRequest.table|table} to be
   *   returned for each row matching this request.
   * @param {google.spanner.v1.KeySet} request.keySet
   *   Required. `key_set` identifies the rows to be yielded. `key_set` names the
   *   primary keys of the rows in {@link protos.google.spanner.v1.ReadRequest.table|table} to
   *   be yielded, unless {@link protos.google.spanner.v1.ReadRequest.index|index} is present.
   *   If {@link protos.google.spanner.v1.ReadRequest.index|index} is present, then
   *   {@link protos.google.spanner.v1.ReadRequest.key_set|key_set} instead names index keys
   *   in {@link protos.google.spanner.v1.ReadRequest.index|index}.
   *
   *   If the {@link protos.google.spanner.v1.ReadRequest.partition_token|partition_token}
   *   field is empty, rows are yielded in table primary key order (if
   *   {@link protos.google.spanner.v1.ReadRequest.index|index} is empty) or index key order
   *   (if {@link protos.google.spanner.v1.ReadRequest.index|index} is non-empty). If the
   *   {@link protos.google.spanner.v1.ReadRequest.partition_token|partition_token} field
   *   isn't empty, rows are yielded in an unspecified order.
   *
   *   It isn't an error for the `key_set` to name rows that don't
   *   exist in the database. Read yields nothing for nonexistent rows.
   * @param {number} request.limit
   *   If greater than zero, only the first `limit` rows are yielded. If `limit`
   *   is zero, the default is no limit. A limit can't be specified if
   *   `partition_token` is set.
   * @param {Buffer} request.resumeToken
   *   If this request is resuming a previously interrupted read,
   *   `resume_token` should be copied from the last
   *   {@link protos.google.spanner.v1.PartialResultSet|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.
   * @param {Buffer} request.partitionToken
   *   If present, results are 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.
   * @param {google.spanner.v1.RequestOptions} request.requestOptions
   *   Common options for this request.
   * @param {google.spanner.v1.DirectedReadOptions} request.directedReadOptions
   *   Directed read options for this request.
   * @param {boolean} request.dataBoostEnabled
   *   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 doesn't set
   *   `partition_token`, the API returns an `INVALID_ARGUMENT` error.
   * @param {google.spanner.v1.ReadRequest.OrderBy} [request.orderBy]
   *   Optional. Order for the returned rows.
   *
   *   By default, Spanner returns result rows in primary key order except for
   *   PartitionRead requests. For applications that don't 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 (for example, bulk point
   *   lookups).
   * @param {google.spanner.v1.ReadRequest.LockHint} [request.lockHint]
   *   Optional. Lock Hint for the request, it can only be used with read-write
   *   transactions.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Stream}
   *   An object stream which emits {@link protos.google.spanner.v1.PartialResultSet|PartialResultSet} on 'data' event.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#server-streaming | documentation }
   *   for more details and examples.
   */
  streamingRead(
    request?: protos.google.spanner.v1.IReadRequest,
    options?: CallOptions,
  ): gax.CancellableStream {
    request = request || {};
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        session: request.session ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('streamingRead stream %j', options);
    return this.innerApiCalls.streamingRead(request, options);
  }

  /**
   * 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, that is, some groups might have been committed successfully,
   * while some might 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 can be applied more than once. Replays of non-idempotent mutations
   * can have undesirable effects. For example, replays of an insert mutation
   * can produce an already exists error or if you use generated or commit
   * timestamp-based keys, it can result in additional rows being added to the
   * mutation's table. We recommend structuring your mutation groups to be
   * idempotent to avoid this issue.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.session
   *   Required. The session in which the batch request is to be run.
   * @param {google.spanner.v1.RequestOptions} request.requestOptions
   *   Common options for this request.
   * @param {number[]} request.mutationGroups
   *   Required. The groups of mutations to be applied.
   * @param {boolean} [request.excludeTxnFromChangeStreams]
   *   Optional. If you don't set the `exclude_txn_from_change_streams` option or
   *   if it's set to `false`, then any change streams monitoring columns modified
   *   by transactions will capture the updates made within that transaction.
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Stream}
   *   An object stream which emits {@link protos.google.spanner.v1.BatchWriteResponse|BatchWriteResponse} on 'data' event.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#server-streaming | documentation }
   *   for more details and examples.
   */
  batchWrite(
    request?: protos.google.spanner.v1.IBatchWriteRequest,
    options?: CallOptions,
  ): gax.CancellableStream {
    request = request || {};
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        session: request.session ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('batchWrite stream %j', options);
    return this.innerApiCalls.batchWrite(request, options);
  }

  /**
   * Lists all sessions in a given database.
   *
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.database
   *   Required. The database in which to list sessions.
   * @param {number} request.pageSize
   *   Number of sessions to be returned in the response. If 0 or less, defaults
   *   to the server's maximum allowed page size.
   * @param {string} request.pageToken
   *   If non-empty, `page_token` should contain a
   *   {@link protos.google.spanner.v1.ListSessionsResponse.next_page_token|next_page_token}
   *   from a previous
   *   {@link protos.google.spanner.v1.ListSessionsResponse|ListSessionsResponse}.
   * @param {string} request.filter
   *   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".
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Promise} - The promise which resolves to an array.
   *   The first element of the array is Array of {@link protos.google.spanner.v1.Session|Session}.
   *   The client library will perform auto-pagination by default: it will call the API as many
   *   times as needed and will merge results from all the pages into this array.
   *   Note that it can affect your quota.
   *   We recommend using `listSessionsAsync()`
   *   method described below for async iteration which you can stop as needed.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#auto-pagination | documentation }
   *   for more details and examples.
   */
  listSessions(
    request?: protos.google.spanner.v1.IListSessionsRequest,
    options?: CallOptions,
  ): Promise<
    [
      protos.google.spanner.v1.ISession[],
      protos.google.spanner.v1.IListSessionsRequest | null,
      protos.google.spanner.v1.IListSessionsResponse,
    ]
  >;
  listSessions(
    request: protos.google.spanner.v1.IListSessionsRequest,
    options: CallOptions,
    callback: PaginationCallback<
      protos.google.spanner.v1.IListSessionsRequest,
      protos.google.spanner.v1.IListSessionsResponse | null | undefined,
      protos.google.spanner.v1.ISession
    >,
  ): void;
  listSessions(
    request: protos.google.spanner.v1.IListSessionsRequest,
    callback: PaginationCallback<
      protos.google.spanner.v1.IListSessionsRequest,
      protos.google.spanner.v1.IListSessionsResponse | null | undefined,
      protos.google.spanner.v1.ISession
    >,
  ): void;
  listSessions(
    request?: protos.google.spanner.v1.IListSessionsRequest,
    optionsOrCallback?:
      | CallOptions
      | PaginationCallback<
          protos.google.spanner.v1.IListSessionsRequest,
          protos.google.spanner.v1.IListSessionsResponse | null | undefined,
          protos.google.spanner.v1.ISession
        >,
    callback?: PaginationCallback<
      protos.google.spanner.v1.IListSessionsRequest,
      protos.google.spanner.v1.IListSessionsResponse | null | undefined,
      protos.google.spanner.v1.ISession
    >,
  ): Promise<
    [
      protos.google.spanner.v1.ISession[],
      protos.google.spanner.v1.IListSessionsRequest | null,
      protos.google.spanner.v1.IListSessionsResponse,
    ]
  > | void {
    request = request || {};
    let options: CallOptions;
    if (typeof optionsOrCallback === 'function' && callback === undefined) {
      callback = optionsOrCallback;
      options = {};
    } else {
      options = optionsOrCallback as CallOptions;
    }
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        database: request.database ?? '',
      });
    this.initialize().catch(err => {
      throw err;
    });
    const wrappedCallback:
      | PaginationCallback<
          protos.google.spanner.v1.IListSessionsRequest,
          protos.google.spanner.v1.IListSessionsResponse | null | undefined,
          protos.google.spanner.v1.ISession
        >
      | undefined = callback
      ? (error, values, nextPageRequest, rawResponse) => {
          this._log.info('listSessions values %j', values);
          callback!(error, values, nextPageRequest, rawResponse); // We verified callback above.
        }
      : undefined;
    this._log.info('listSessions request %j', request);
    return this.innerApiCalls
      .listSessions(request, options, wrappedCallback)
      ?.then(
        ([response, input, output]: [
          protos.google.spanner.v1.ISession[],
          protos.google.spanner.v1.IListSessionsRequest | null,
          protos.google.spanner.v1.IListSessionsResponse,
        ]) => {
          this._log.info('listSessions values %j', response);
          return [response, input, output];
        },
      );
  }

  /**
   * Equivalent to `listSessions`, but returns a NodeJS Stream object.
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.database
   *   Required. The database in which to list sessions.
   * @param {number} request.pageSize
   *   Number of sessions to be returned in the response. If 0 or less, defaults
   *   to the server's maximum allowed page size.
   * @param {string} request.pageToken
   *   If non-empty, `page_token` should contain a
   *   {@link protos.google.spanner.v1.ListSessionsResponse.next_page_token|next_page_token}
   *   from a previous
   *   {@link protos.google.spanner.v1.ListSessionsResponse|ListSessionsResponse}.
   * @param {string} request.filter
   *   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".
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Stream}
   *   An object stream which emits an object representing {@link protos.google.spanner.v1.Session|Session} on 'data' event.
   *   The client library will perform auto-pagination by default: it will call the API as many
   *   times as needed. Note that it can affect your quota.
   *   We recommend using `listSessionsAsync()`
   *   method described below for async iteration which you can stop as needed.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#auto-pagination | documentation }
   *   for more details and examples.
   */
  listSessionsStream(
    request?: protos.google.spanner.v1.IListSessionsRequest,
    options?: CallOptions,
  ): Transform {
    request = request || {};
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        database: request.database ?? '',
      });
    const defaultCallSettings = this._defaults['listSessions'];
    const callSettings = defaultCallSettings.merge(options);
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('listSessions stream %j', request);
    return this.descriptors.page.listSessions.createStream(
      this.innerApiCalls.listSessions as GaxCall,
      request,
      callSettings,
    );
  }

  /**
   * Equivalent to `listSessions`, but returns an iterable object.
   *
   * `for`-`await`-`of` syntax is used with the iterable to get response elements on-demand.
   * @param {Object} request
   *   The request object that will be sent.
   * @param {string} request.database
   *   Required. The database in which to list sessions.
   * @param {number} request.pageSize
   *   Number of sessions to be returned in the response. If 0 or less, defaults
   *   to the server's maximum allowed page size.
   * @param {string} request.pageToken
   *   If non-empty, `page_token` should contain a
   *   {@link protos.google.spanner.v1.ListSessionsResponse.next_page_token|next_page_token}
   *   from a previous
   *   {@link protos.google.spanner.v1.ListSessionsResponse|ListSessionsResponse}.
   * @param {string} request.filter
   *   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".
   * @param {object} [options]
   *   Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details.
   * @returns {Object}
   *   An iterable Object that allows {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols | async iteration }.
   *   When you iterate the returned iterable, each element will be an object representing
   *   {@link protos.google.spanner.v1.Session|Session}. The API will be called under the hood as needed, once per the page,
   *   so you can stop the iteration when you don't need more results.
   *   Please see the {@link https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#auto-pagination | documentation }
   *   for more details and examples.
   */
  listSessionsAsync(
    request?: protos.google.spanner.v1.IListSessionsRequest,
    options?: CallOptions,
  ): AsyncIterable<protos.google.spanner.v1.ISession> {
    request = request || {};
    options = options || {};
    options.otherArgs = options.otherArgs || {};
    options.otherArgs.headers = options.otherArgs.headers || {};
    options.otherArgs.headers['x-goog-request-params'] =
      this._gaxModule.routingHeader.fromParams({
        database: request.database ?? '',
      });
    const defaultCallSettings = this._defaults['listSessions'];
    const callSettings = defaultCallSettings.merge(options);
    this.initialize().catch(err => {
      throw err;
    });
    this._log.info('listSessions iterate %j', request);
    return this.descriptors.page.listSessions.asyncIterate(
      this.innerApiCalls['listSessions'] as GaxCall,
      request as {},
      callSettings,
    ) as AsyncIterable<protos.google.spanner.v1.ISession>;
  }
  // --------------------
  // -- Path templates --
  // --------------------

  /**
   * Return a fully-qualified database resource name string.
   *
   * @param {string} project
   * @param {string} instance
   * @param {string} database
   * @returns {string} Resource name string.
   */
  databasePath(project: string, instance: string, database: string) {
    return this.pathTemplates.databasePathTemplate.render({
      project: project,
      instance: instance,
      database: database,
    });
  }

  /**
   * Parse the project from Database resource.
   *
   * @param {string} databaseName
   *   A fully-qualified path representing Database resource.
   * @returns {string} A string representing the project.
   */
  matchProjectFromDatabaseName(databaseName: string) {
    return this.pathTemplates.databasePathTemplate.match(databaseName).project;
  }

  /**
   * Parse the instance from Database resource.
   *
   * @param {string} databaseName
   *   A fully-qualified path representing Database resource.
   * @returns {string} A string representing the instance.
   */
  matchInstanceFromDatabaseName(databaseName: string) {
    return this.pathTemplates.databasePathTemplate.match(databaseName).instance;
  }

  /**
   * Parse the database from Database resource.
   *
   * @param {string} databaseName
   *   A fully-qualified path representing Database resource.
   * @returns {string} A string representing the database.
   */
  matchDatabaseFromDatabaseName(databaseName: string) {
    return this.pathTemplates.databasePathTemplate.match(databaseName).database;
  }

  /**
   * Return a fully-qualified session resource name string.
   *
   * @param {string} project
   * @param {string} instance
   * @param {string} database
   * @param {string} session
   * @returns {string} Resource name string.
   */
  sessionPath(
    project: string,
    instance: string,
    database: string,
    session: string,
  ) {
    return this.pathTemplates.sessionPathTemplate.render({
      project: project,
      instance: instance,
      database: database,
      session: session,
    });
  }

  /**
   * Parse the project from Session resource.
   *
   * @param {string} sessionName
   *   A fully-qualified path representing Session resource.
   * @returns {string} A string representing the project.
   */
  matchProjectFromSessionName(sessionName: string) {
    return this.pathTemplates.sessionPathTemplate.match(sessionName).project;
  }

  /**
   * Parse the instance from Session resource.
   *
   * @param {string} sessionName
   *   A fully-qualified path representing Session resource.
   * @returns {string} A string representing the instance.
   */
  matchInstanceFromSessionName(sessionName: string) {
    return this.pathTemplates.sessionPathTemplate.match(sessionName).instance;
  }

  /**
   * Parse the database from Session resource.
   *
   * @param {string} sessionName
   *   A fully-qualified path representing Session resource.
   * @returns {string} A string representing the database.
   */
  matchDatabaseFromSessionName(sessionName: string) {
    return this.pathTemplates.sessionPathTemplate.match(sessionName).database;
  }

  /**
   * Parse the session from Session resource.
   *
   * @param {string} sessionName
   *   A fully-qualified path representing Session resource.
   * @returns {string} A string representing the session.
   */
  matchSessionFromSessionName(sessionName: string) {
    return this.pathTemplates.sessionPathTemplate.match(sessionName).session;
  }

  /**
   * Terminate the gRPC channel and close the client.
   *
   * The client will no longer be usable and all future behavior is undefined.
   * @returns {Promise} A promise that resolves when the client is closed.
   */
  close(): Promise<void> {
    if (this.spannerStub && !this._terminated) {
      return this.spannerStub.then(stub => {
        this._log.info('ending gRPC channel');
        this._terminated = true;
        stub.close();
      });
    }
    return Promise.resolve();
  }
}
