Exemple #1
0
    def list_instances(self, filter_="", page_size=None, page_token=None):
        """List instances for the client's project.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.InstanceAdmin.ListInstances

        :type filter_: string
        :param filter_: (Optional) Filter to select instances listed.  See
                        the ``ListInstancesRequest`` docs above for examples.

        :type page_size: int
        :param page_size: (Optional) Maximum number of results to return.

        :type page_token: str
        :param page_token: (Optional) Token for fetching next page of results.

        :rtype: :class:`~google.api_core.page_iterator.Iterator`
        :returns:
            Iterator of :class:`~google.cloud.spanner_v1.instance.Instance`
            resources within the client's project.
        """
        metadata = _metadata_with_prefix(self.project_name)
        path = "projects/%s" % (self.project,)
        page_iter = self.instance_admin_api.list_instances(
            path, page_size=page_size, metadata=metadata
        )
        page_iter.item_to_value = self._item_to_instance
        page_iter.next_page_token = page_token
        return page_iter
Exemple #2
0
    def create(self):
        """Create this database within its instance

        Inclues any configured schema assigned to :attr:`ddl_statements`.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.CreateDatabase

        :rtype: :class:`~google.api_core.operation.Operation`
        :returns: a future used to poll the status of the create request
        :raises Conflict: if the database already exists
        :raises NotFound: if the instance owning the database does not exist
        """
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)
        db_name = self.database_id
        if "-" in db_name:
            db_name = "`%s`" % (db_name,)

        future = api.create_database(
            parent=self._instance.name,
            create_statement="CREATE DATABASE %s" % (db_name,),
            extra_statements=list(self._ddl_statements),
            metadata=metadata,
        )
        return future
    def begin(self):
        """Begin a read-only transaction on the database.

        :rtype: bytes
        :returns: the ID for the newly-begun transaction.

        :raises ValueError:
            if the transaction is already begun, committed, or rolled back.
        """
        if not self._multi_use:
            raise ValueError("Cannot call 'begin' on single-use snapshots")

        if self._transaction_id is not None:
            raise ValueError("Read-only transaction already begun")

        if self._read_request_count > 0:
            raise ValueError("Read-only transaction already pending")

        database = self._session._database
        api = database.spanner_api
        metadata = _metadata_with_prefix(database.name)
        txn_selector = self._make_txn_selector()
        response = api.begin_transaction(
            self._session.name, txn_selector.begin, metadata=metadata
        )
        self._transaction_id = response.id
        return self._transaction_id
    def begin(self):
        """Begin a transaction on the database.

        :rtype: bytes
        :returns: the ID for the newly-begun transaction.
        :raises ValueError:
            if the transaction is already begun, committed, or rolled back.
        """
        if self._transaction_id is not None:
            raise ValueError("Transaction already begun")

        if self.committed is not None:
            raise ValueError("Transaction already committed")

        if self._rolled_back:
            raise ValueError("Transaction is already rolled back")

        database = self._session._database
        api = database.spanner_api
        metadata = _metadata_with_prefix(database.name)
        txn_options = TransactionOptions(read_write=TransactionOptions.ReadWrite())
        response = api.begin_transaction(
            self._session.name, txn_options, metadata=metadata
        )
        self._transaction_id = response.id
        return self._transaction_id
Exemple #5
0
    def list_instance_configs(self, page_size=None, page_token=None):
        """List available instance configurations for the client's project.

        .. _RPC docs: https://cloud.google.com/spanner/docs/reference/rpc/\
                      google.spanner.admin.instance.v1#google.spanner.admin.\
                      instance.v1.InstanceAdmin.ListInstanceConfigs

        See `RPC docs`_.

        :type page_size: int
        :param page_size: (Optional) Maximum number of results to return.

        :type page_token: str
        :param page_token: (Optional) Token for fetching next page of results.

        :rtype: :class:`~google.api_core.page_iterator.Iterator`
        :returns:
            Iterator of
            :class:`~google.cloud.spanner_v1.instance.InstanceConfig`
            resources within the client's project.
        """
        metadata = _metadata_with_prefix(self.project_name)
        path = "projects/%s" % (self.project,)
        page_iter = self.instance_admin_api.list_instance_configs(
            path, page_size=page_size, metadata=metadata
        )
        page_iter.next_page_token = page_token
        page_iter.item_to_value = _item_to_instance_config
        return page_iter
Exemple #6
0
    def update_ddl(self, ddl_statements, operation_id=""):
        """Update DDL for this database.

        Apply any configured schema from :attr:`ddl_statements`.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabase

        :type ddl_statements: Sequence[str]
        :param ddl_statements: a list of DDL statements to use on this database
        :type operation_id: str
        :param operation_id: (optional) a string ID for the long-running operation

        :rtype: :class:`google.api_core.operation.Operation`
        :returns: an operation instance
        :raises NotFound: if the database does not exist
        """
        client = self._instance._client
        api = client.database_admin_api
        metadata = _metadata_with_prefix(self.name)

        future = api.update_database_ddl(
            self.name, ddl_statements, operation_id=operation_id, metadata=metadata
        )
        return future
    def list_databases(self, page_size=None, page_token=None):
        """List databases for the instance.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.ListDatabases

        :type page_size: int
        :param page_size:
            Optional. The maximum number of databases in each page of results
            from this request. Non-positive values are ignored. Defaults
            to a sensible value set by the API.

        :type page_token: str
        :param page_token:
            Optional. If present, return the next batch of databases, using
            the value, which must correspond to the ``nextPageToken`` value
            returned in the previous response.  Deprecated: use the ``pages``
            property of the returned iterator instead of manually passing
            the token.

        :rtype: :class:`~google.api._ore.page_iterator.Iterator`
        :returns:
            Iterator of :class:`~google.cloud.spanner_v1.database.Database`
            resources within the current instance.
        """
        metadata = _metadata_with_prefix(self.name)
        page_iter = self._client.database_admin_api.list_databases(
            self.name, page_size=page_size, metadata=metadata
        )
        page_iter.next_page_token = page_token
        page_iter.item_to_value = self._item_to_database
        return page_iter
 def rollback(self):
     """Roll back a transaction on the database."""
     self._check_state()
     database = self._session._database
     api = database.spanner_api
     metadata = _metadata_with_prefix(database.name)
     api.rollback(self._session.name, self._transaction_id, metadata=metadata)
     self._rolled_back = True
     del self._session._transaction
Exemple #9
0
    def execute_partitioned_dml(self, dml, params=None, param_types=None):
        """Execute a partitionable DML statement.

        :type dml: str
        :param dml: DML statement

        :type params: dict, {str -> column value}
        :param params: values for parameter replacement.  Keys must match
                       the names used in ``dml``.

        :type param_types: dict[str -> Union[dict, .types.Type]]
        :param param_types:
            (Optional) maps explicit types for one or more param values;
            required if parameters are passed.

        :rtype: int
        :returns: Count of rows affected by the DML statement.
        """
        if params is not None:
            if param_types is None:
                raise ValueError("Specify 'param_types' when passing 'params'.")
            params_pb = Struct(
                fields={key: _make_value_pb(value) for key, value in params.items()}
            )
        else:
            params_pb = None

        api = self.spanner_api

        txn_options = TransactionOptions(
            partitioned_dml=TransactionOptions.PartitionedDml()
        )

        metadata = _metadata_with_prefix(self.name)

        with SessionCheckout(self._pool) as session:

            txn = api.begin_transaction(session.name, txn_options, metadata=metadata)

            txn_selector = TransactionSelector(id=txn.id)

            restart = functools.partial(
                api.execute_streaming_sql,
                session.name,
                dml,
                transaction=txn_selector,
                params=params_pb,
                param_types=param_types,
                metadata=metadata,
            )

            iterator = _restart_on_unavailable(restart)

            result_set = StreamedResultSet(iterator)
            list(result_set)  # consume all partials

            return result_set.stats.row_count_lower_bound
Exemple #10
0
    def read(self, table, columns, keyset, index='', limit=0, partition=None):
        """Perform a ``StreamingRead`` API request for rows in a table.

        :type table: str
        :param table: name of the table from which to fetch data

        :type columns: list of str
        :param columns: names of columns to be retrieved

        :type keyset: :class:`~google.cloud.spanner_v1.keyset.KeySet`
        :param keyset: keys / ranges identifying rows to be retrieved

        :type index: str
        :param index: (Optional) name of index to use, rather than the
                      table's primary key

        :type limit: int
        :param limit: (Optional) maximum number of rows to return.
                      Incompatible with ``partition``.

        :type partition: bytes
        :param partition: (Optional) one of the partition tokens returned
                          from :meth:`partition_read`.  Incompatible with
                          ``limit``.

        :rtype: :class:`~google.cloud.spanner_v1.streamed.StreamedResultSet`
        :returns: a result set instance which can be used to consume rows.

        :raises ValueError:
            for reuse of single-use snapshots, or if a transaction ID is
            already pending for multiple-use snapshots.
        """
        if self._read_request_count > 0:
            if not self._multi_use:
                raise ValueError("Cannot re-use single-use snapshot.")
            if self._transaction_id is None:
                raise ValueError("Transaction ID pending.")

        database = self._session._database
        api = database.spanner_api
        metadata = _metadata_with_prefix(database.name)
        transaction = self._make_txn_selector()

        restart = functools.partial(
            api.streaming_read,
            self._session.name, table, columns, keyset._to_pb(),
            transaction=transaction, index=index, limit=limit,
            partition_token=partition, metadata=metadata)

        iterator = _restart_on_unavailable(restart)

        self._read_request_count += 1

        if self._multi_use:
            return StreamedResultSet(iterator, source=self)
        else:
            return StreamedResultSet(iterator)
Exemple #11
0
    def drop(self):
        """Drop this database.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.DropDatabase
        """
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)
        api.drop_database(self.name, metadata=metadata)
    def batch_update(self, statements):
        """Perform a batch of DML statements via an ``ExecuteBatchDml`` request.

        :type statements:
            Sequence[Union[ str, Tuple[str, Dict[str, Any], Dict[str, Union[dict, .types.Type]]]]]

        :param statements:
            List of DML statements, with optional params / param types.
            If passed, 'params' is a dict mapping names to the values
            for parameter replacement.  Keys must match the names used in the
            corresponding DML statement.  If 'params' is passed, 'param_types'
            must also be passed, as a dict mapping names to the type of
            value passed in 'params'.

        :rtype:
            Tuple(status, Sequence[int])
        :returns:
            Status code, plus counts of rows affected by each completed DML
            statement.  Note that if the staus code is not ``OK``, the
            statement triggering the error will not have an entry in the
            list, nor will any statements following that one.
        """
        parsed = []
        for statement in statements:
            if isinstance(statement, str):
                parsed.append({"sql": statement})
            else:
                dml, params, param_types = statement
                params_pb = self._make_params_pb(params, param_types)
                parsed.append(
                    {"sql": dml, "params": params_pb, "param_types": param_types}
                )

        database = self._session._database
        metadata = _metadata_with_prefix(database.name)
        transaction = self._make_txn_selector()
        api = database.spanner_api

        response = api.execute_batch_dml(
            session=self._session.name,
            transaction=transaction,
            statements=parsed,
            seqno=self._execute_sql_count,
            metadata=metadata,
        )

        self._execute_sql_count += 1
        row_counts = [
            result_set.stats.row_count_exact for result_set in response.result_sets
        ]
        return response.status, row_counts
Exemple #13
0
    def reload(self):
        """Reload the metadata for this instance.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.GetInstanceConfig

        :raises NotFound: if the instance does not exist
        """
        api = self._client.instance_admin_api
        metadata = _metadata_with_prefix(self.name)

        instance_pb = api.get_instance(self.name, metadata=metadata)

        self._update_from_pb(instance_pb)
Exemple #14
0
    def create(self):
        """Create this session, bound to its database.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.v1#google.spanner.v1.Spanner.CreateSession

        :raises: :exc:`ValueError` if :attr:`session_id` is already set.
        """
        if self._session_id is not None:
            raise ValueError('Session ID already set by back-end')
        api = self._database.spanner_api
        metadata = _metadata_with_prefix(self._database.name)
        session_pb = api.create_session(self._database.name, metadata=metadata)
        self._session_id = session_pb.name.split('/')[-1]
    def read(self, table, columns, keyset, index='', limit=0):
        """Perform a ``StreamingRead`` API request for rows in a table.

        :type table: str
        :param table: name of the table from which to fetch data

        :type columns: list of str
        :param columns: names of columns to be retrieved

        :type keyset: :class:`~google.cloud.spanner_v1.keyset.KeySet`
        :param keyset: keys / ranges identifying rows to be retrieved

        :type index: str
        :param index: (Optional) name of index to use, rather than the
                      table's primary key

        :type limit: int
        :param limit: (Optional) maximum number of rows to return

        :rtype: :class:`~google.cloud.spanner_v1.streamed.StreamedResultSet`
        :returns: a result set instance which can be used to consume rows.
        :raises ValueError:
            for reuse of single-use snapshots, or if a transaction ID is
            already pending for multiple-use snapshots.
        """
        if self._read_request_count > 0:
            if not self._multi_use:
                raise ValueError("Cannot re-use single-use snapshot.")
            if self._transaction_id is None:
                raise ValueError("Transaction ID pending.")

        database = self._session._database
        api = database.spanner_api
        metadata = _metadata_with_prefix(database.name)
        transaction = self._make_txn_selector()

        restart = functools.partial(
            api.streaming_read,
            self._session.name, table, columns, keyset.to_pb(),
            transaction=transaction, index=index, limit=limit,
            metadata=metadata)

        iterator = _restart_on_unavailable(restart)

        self._read_request_count += 1

        if self._multi_use:
            return StreamedResultSet(iterator, source=self)
        else:
            return StreamedResultSet(iterator)
Exemple #16
0
    def execute_update(self, dml, params=None, param_types=None, query_mode=None):
        """Perform an ``ExecuteSql`` API request with DML.

        :type dml: str
        :param dml: SQL DML statement

        :type params: dict, {str -> column value}
        :param params: values for parameter replacement.  Keys must match
                       the names used in ``dml``.

        :type param_types: dict[str -> Union[dict, .types.Type]]
        :param param_types:
            (Optional) maps explicit types for one or more param values;
            required if parameters are passed.

        :type query_mode:
            :class:`google.cloud.spanner_v1.proto.ExecuteSqlRequest.QueryMode`
        :param query_mode: Mode governing return of results / query plan. See
            https://cloud.google.com/spanner/reference/rpc/google.spanner.v1#google.spanner.v1.ExecuteSqlRequest.QueryMode1

        :rtype: int
        :returns: Count of rows affected by the DML statement.
        """
        if params is not None:
            if param_types is None:
                raise ValueError("Specify 'param_types' when passing 'params'.")
            params_pb = Struct(
                fields={key: _make_value_pb(value) for key, value in params.items()}
            )
        else:
            params_pb = None

        database = self._session._database
        metadata = _metadata_with_prefix(database.name)
        transaction = self._make_txn_selector()
        api = database.spanner_api

        response = api.execute_sql(
            self._session.name,
            dml,
            transaction=transaction,
            params=params_pb,
            param_types=param_types,
            query_mode=query_mode,
            seqno=self._execute_sql_count,
            metadata=metadata,
        )

        self._execute_sql_count += 1
        return response.stats.row_count_exact
Exemple #17
0
    def reload(self):
        """Reload this database.

        Refresh any configured schema into :attr:`ddl_statements`.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.GetDatabaseDDL

        :raises NotFound: if the database does not exist
        """
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)
        response = api.get_database_ddl(self.name, metadata=metadata)
        self._ddl_statements = tuple(response.statements)
Exemple #18
0
    def commit(self, return_commit_stats=False, request_options=None):
        """Commit mutations to the database.

        :type return_commit_stats: bool
        :param return_commit_stats:
          If true, the response will return commit stats which can be accessed though commit_stats.

        :type request_options:
            :class:`google.cloud.spanner_v1.types.RequestOptions`
        :param request_options:
                (Optional) Common options for this request.
                If a dict is provided, it must be of the same form as the protobuf
                message :class:`~google.cloud.spanner_v1.types.RequestOptions`.

        :rtype: datetime
        :returns: timestamp of the committed changes.
        """
        self._check_state()
        database = self._session._database
        api = database.spanner_api
        metadata = _metadata_with_prefix(database.name)
        txn_options = TransactionOptions(
            read_write=TransactionOptions.ReadWrite())
        trace_attributes = {"num_mutations": len(self._mutations)}

        if request_options is None:
            request_options = RequestOptions()
        elif type(request_options) == dict:
            request_options = RequestOptions(request_options)
        request_options.transaction_tag = self.transaction_tag

        # Request tags are not supported for commit requests.
        request_options.request_tag = None

        request = CommitRequest(
            session=self._session.name,
            mutations=self._mutations,
            single_use_transaction=txn_options,
            return_commit_stats=return_commit_stats,
            request_options=request_options,
        )
        with trace_call("CloudSpanner.Commit", self._session,
                        trace_attributes):
            response = api.commit(
                request=request,
                metadata=metadata,
            )
        self.committed = response.commit_timestamp
        self.commit_stats = response.commit_stats
        return self.committed
Exemple #19
0
    def reload(self):
        """Reload this database.

        Refresh any configured schema into :attr:`ddl_statements`.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.GetDatabaseDDL

        :raises NotFound: if the database does not exist
        """
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)
        response = api.get_database_ddl(self.name, metadata=metadata)
        self._ddl_statements = tuple(response.statements)
Exemple #20
0
    def exists(self):
        """Test whether this backup exists.

        :rtype: bool
        :returns: True if the backup exists, else False.
        """
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)

        try:
            api.get_backup(name=self.name, metadata=metadata)
        except NotFound:
            return False
        return True
    def reload(self):
        """Reload the metadata for this instance.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.GetInstanceConfig

        :raises NotFound: if the instance does not exist
        """
        api = self._client.instance_admin_api
        metadata = _metadata_with_prefix(self.name)

        instance_pb = api.get_instance(self.name, metadata=metadata)

        self._update_from_pb(instance_pb)
 def rollback(self):
     """Roll back a transaction on the database."""
     self._check_state()
     database = self._session._database
     api = database.spanner_api
     metadata = _metadata_with_prefix(database.name)
     with trace_call("CloudSpanner.Rollback", self._session):
         api.rollback(
             session=self._session.name,
             transaction_id=self._transaction_id,
             metadata=metadata,
         )
     self.rolled_back = True
     del self._session._transaction
Exemple #23
0
    def delete(self):
        """Delete this session.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.v1#google.spanner.v1.Spanner.GetSession

        :raises ValueError: if :attr:`session_id` is not already set.
        :raises NotFound: if the session does not exist
        """
        if self._session_id is None:
            raise ValueError('Session ID not set by back-end')
        api = self._database.spanner_api
        metadata = _metadata_with_prefix(self._database.name)

        api.delete_session(self.name, metadata=metadata)
Exemple #24
0
    def delete(self):
        """Delete this session.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.v1#google.spanner.v1.Spanner.GetSession

        :raises ValueError: if :attr:`session_id` is not already set.
        :raises NotFound: if the session does not exist
        """
        if self._session_id is None:
            raise ValueError('Session ID not set by back-end')
        api = self._database.spanner_api
        metadata = _metadata_with_prefix(self._database.name)

        api.delete_session(self.name, metadata=metadata)
    def update_expire_time(self, new_expire_time):
        """Update the expire time of this backup.

        :type new_expire_time: :class:`datetime.datetime`
        :param new_expire_time: the new expire time timestamp
        """
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)
        backup_update = {
            "name": self.name,
            "expire_time": _datetime_to_pb_timestamp(new_expire_time),
        }
        update_mask = {"paths": ["expire_time"]}
        api.update_backup(backup_update, update_mask, metadata=metadata)
        self._expire_time = new_expire_time
    def reload(self):
        """Reload this backup.

        Refresh the stored backup properties.

        :raises NotFound: if the backup does not exist
        """
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)
        pb = api.get_backup(self.name, metadata=metadata)
        self._database = pb.database
        self._expire_time = _pb_timestamp_to_datetime(pb.expire_time)
        self._create_time = _pb_timestamp_to_datetime(pb.create_time)
        self._size_bytes = pb.size_bytes
        self._state = enums.Backup.State(pb.state)
        self._referencing_databases = pb.referencing_databases
Exemple #27
0
    def execute_update(self,
                       dml,
                       params=None,
                       param_types=None,
                       query_mode=None):
        """Perform an ``ExecuteSql`` API request with DML.

        :type dml: str
        :param dml: SQL DML statement

        :type params: dict, {str -> column value}
        :param params: values for parameter replacement.  Keys must match
                       the names used in ``dml``.

        :type param_types: dict[str -> Union[dict, .types.Type]]
        :param param_types:
            (Optional) maps explicit types for one or more param values;
            required if parameters are passed.

        :type query_mode:
            :class:`google.cloud.spanner_v1.proto.ExecuteSqlRequest.QueryMode`
        :param query_mode: Mode governing return of results / query plan. See
            https://cloud.google.com/spanner/reference/rpc/google.spanner.v1#google.spanner.v1.ExecuteSqlRequest.QueryMode1

        :rtype: int
        :returns: Count of rows affected by the DML statement.
        """
        params_pb = self._make_params_pb(params, param_types)
        database = self._session._database
        metadata = _metadata_with_prefix(database.name)
        transaction = self._make_txn_selector()
        api = database.spanner_api

        response = api.execute_sql(
            self._session.name,
            dml,
            transaction=transaction,
            params=params_pb,
            param_types=param_types,
            query_mode=query_mode,
            seqno=self._execute_sql_count,
            metadata=metadata,
        )

        self._execute_sql_count += 1
        return response.stats.row_count_exact
Exemple #28
0
    def exists(self):
        """Test whether this database exists.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.GetDatabaseDDL

        :rtype: bool
        :returns: True if the database exists, else false.
        """
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)

        try:
            api.get_database_ddl(self.name, metadata=metadata)
        except NotFound:
            return False
        return True
Exemple #29
0
    def exists(self):
        """Test whether this database exists.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.GetDatabaseDDL

        :rtype: bool
        :returns: True if the database exists, else false.
        """
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)

        try:
            api.get_database_ddl(self.name, metadata=metadata)
        except NotFound:
            return False
        return True
Exemple #30
0
    def create(self):
        """Create this session, bound to its database.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.v1#google.spanner.v1.Spanner.CreateSession

        :raises: :exc:`ValueError` if :attr:`session_id` is already set.
        """
        if self._session_id is not None:
            raise ValueError("Session ID already set by back-end")
        api = self._database.spanner_api
        metadata = _metadata_with_prefix(self._database.name)
        kw = {}
        if self._labels:
            kw = {"session": {"labels": self._labels}}
        session_pb = api.create_session(self._database.name, metadata=metadata, **kw)
        self._session_id = session_pb.name.split("/")[-1]
Exemple #31
0
    def exists(self):
        """Test whether this instance exists.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.GetInstanceConfig

        :rtype: bool
        :returns: True if the instance exists, else false
        """
        api = self._client.instance_admin_api
        metadata = _metadata_with_prefix(self.name)

        try:
            api.get_instance(self.name, metadata=metadata)
        except NotFound:
            return False

        return True
    def exists(self):
        """Test whether this instance exists.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.GetInstanceConfig

        :rtype: bool
        :returns: True if the instance exists, else false
        """
        api = self._client.instance_admin_api
        metadata = _metadata_with_prefix(self.name)

        try:
            api.get_instance(self.name, metadata=metadata)
        except NotFound:
            return False

        return True
Exemple #33
0
    def commit(self):
        """Commit mutations to the database.

        :rtype: datetime
        :returns: timestamp of the committed changes.
        """
        self._check_state()
        database = self._session._database
        api = database.spanner_api
        metadata = _metadata_with_prefix(database.name)
        txn_options = TransactionOptions(
            read_write=TransactionOptions.ReadWrite())
        response = api.commit(self._session.name, self._mutations,
                              single_use_transaction=txn_options,
                              metadata=metadata)
        self.committed = _pb_timestamp_to_datetime(
            response.commit_timestamp)
        return self.committed
Exemple #34
0
    def commit(self):
        """Commit mutations to the database.

        :rtype: datetime
        :returns: timestamp of the committed changes.
        :raises ValueError: if there are no mutations to commit.
        """
        self._check_state()

        database = self._session._database
        api = database.spanner_api
        metadata = _metadata_with_prefix(database.name)
        response = api.commit(self._session.name,
                              self._mutations,
                              transaction_id=self._transaction_id,
                              metadata=metadata)
        self.committed = _pb_timestamp_to_datetime(response.commit_timestamp)
        del self._session._transaction
        return self.committed
Exemple #35
0
    def delete(self):
        """Mark an instance and all of its databases for permanent deletion.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.DeleteInstance

        Immediately upon completion of the request:

        * Billing will cease for all of the instance's reserved resources.

        Soon afterward:

        * The instance and all databases within the instance will be deleteed.
          All data in the databases will be permanently deleted.
        """
        api = self._client.instance_admin_api
        metadata = _metadata_with_prefix(self.name)

        api.delete_instance(self.name, metadata=metadata)
Exemple #36
0
    def exists(self):
        """Test for the existence of this session.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.v1#google.spanner.v1.Spanner.GetSession

        :rtype: bool
        :returns: True if the session exists on the back-end, else False.
        """
        if self._session_id is None:
            return False
        api = self._database.spanner_api
        metadata = _metadata_with_prefix(self._database.name)
        try:
            api.get_session(self.name, metadata=metadata)
        except NotFound:
            return False

        return True
    def delete(self):
        """Mark an instance and all of its databases for permanent deletion.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.DeleteInstance

        Immediately upon completion of the request:

        * Billing will cease for all of the instance's reserved resources.

        Soon afterward:

        * The instance and all databases within the instance will be deleteed.
          All data in the databases will be permanently deleted.
        """
        api = self._client.instance_admin_api
        metadata = _metadata_with_prefix(self.name)

        api.delete_instance(self.name, metadata=metadata)
    def bind(self, database):
        """Associate the pool with a database.

        :type database: :class:`~google.cloud.spanner_v1.database.Database`
        :param database: database used by the pool:  used to create sessions
                         when needed.
        """
        self._database = database
        api = database.spanner_api
        metadata = _metadata_with_prefix(database.name)

        while not self._sessions.full():
            resp = api.batch_create_sessions(
                database.name, self.size - self._sessions.qsize(), metadata=metadata
            )
            for session_pb in resp.session:
                session = self._new_session()
                session._session_id = session_pb.name.split("/")[-1]
                self._sessions.put(session)
Exemple #39
0
    def update(self):
        """Update this instance.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstance

        .. note::

            Updates the ``display_name``, ``node_count``, ``processing_units``
            and ``labels``. To change those values before updating, set them via

            .. code:: python

                instance.display_name = 'New display name'
                instance.node_count = 5

           before calling :meth:`update`.

        :rtype: :class:`google.api_core.operation.Operation`
        :returns: an operation instance
        :raises NotFound: if the instance does not exist
        """
        api = self._client.instance_admin_api
        instance_pb = InstancePB(
            name=self.name,
            config=self.configuration_name,
            display_name=self.display_name,
            node_count=self._node_count,
            processing_units=self._processing_units,
            labels=self.labels,
        )

        # Always update only processing_units, not nodes
        field_mask = FieldMask(
            paths=["config", "display_name", "processing_units", "labels"]
        )
        metadata = _metadata_with_prefix(self.name)

        future = api.update_instance(
            instance=instance_pb, field_mask=field_mask, metadata=metadata
        )

        return future
Exemple #40
0
    def exists(self):
        """Test for the existence of this session.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.v1#google.spanner.v1.Spanner.GetSession

        :rtype: bool
        :returns: True if the session exists on the back-end, else False.
        """
        if self._session_id is None:
            return False
        api = self._database.spanner_api
        metadata = _metadata_with_prefix(self._database.name)
        try:
            api.get_session(self.name, metadata=metadata)
        except NotFound:
            return False

        return True
Exemple #41
0
 def spanner_api(self):
     """Helper for session-related API calls."""
     if self._spanner_api is None:
         credentials = self._instance._client.credentials
         if isinstance(credentials, google.auth.credentials.Scoped):
             credentials = credentials.with_scopes((SPANNER_DATA_SCOPE, ))
         client_info = self._instance._client._client_info
         client_options = self._instance._client._client_options
         if (os.getenv("GOOGLE_CLOUD_SPANNER_ENABLE_RESOURCE_BASED_ROUTING")
                 == "true"):
             endpoint_cache = self._instance._client._endpoint_cache
             if self._instance.name in endpoint_cache:
                 client_options = ClientOptions(
                     api_endpoint=endpoint_cache[self._instance.name])
             else:
                 try:
                     api = self._instance._client.instance_admin_api
                     resp = api.get_instance(
                         self._instance.name,
                         field_mask={"paths": ["endpoint_uris"]},
                         metadata=_metadata_with_prefix(self.name),
                     )
                     endpoints = resp.endpoint_uris
                     if endpoints:
                         endpoint_cache[self._instance.name] = list(
                             endpoints)[0]
                         client_options = ClientOptions(
                             api_endpoint=endpoint_cache[
                                 self._instance.name])
                     # If there are no endpoints, use default endpoint.
                 except PermissionDenied:
                     warnings.warn(
                         _RESOURCE_ROUTING_PERMISSIONS_WARNING,
                         ResourceRoutingPermissionsWarning,
                         stacklevel=2,
                     )
         self._spanner_api = SpannerClient(
             credentials=credentials,
             client_info=client_info,
             client_options=client_options,
         )
     return self._spanner_api
Exemple #42
0
    def reload(self):
        """Reload this database.

        Refresh any configured schema into :attr:`ddl_statements`.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.GetDatabaseDDL

        :raises NotFound: if the database does not exist
        """
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)
        response = api.get_database_ddl(database=self.name, metadata=metadata)
        self._ddl_statements = tuple(response.statements)
        response = api.get_database(name=self.name, metadata=metadata)
        self._state = DatabasePB.State(response.state)
        self._create_time = response.create_time
        self._restore_info = response.restore_info
        self._version_retention_period = response.version_retention_period
        self._earliest_version_time = response.earliest_version_time
Exemple #43
0
    def reload(self):
        """Reload this backup.

        Refresh the stored backup properties.

        :raises NotFound: if the backup does not exist
        """
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)
        pb = api.get_backup(name=self.name, metadata=metadata)
        self._database = pb.database
        self._expire_time = pb.expire_time
        self._create_time = pb.create_time
        self._version_time = pb.version_time
        self._size_bytes = pb.size_bytes
        self._state = BackupPB.State(pb.state)
        self._referencing_databases = pb.referencing_databases
        self._encryption_info = pb.encryption_info
        self._max_expire_time = pb.max_expire_time
        self._referencing_backups = pb.referencing_backups
Exemple #44
0
    def create(self):
        """Create this instance.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.CreateInstance

        .. note::

           Uses the ``project`` and ``instance_id`` on the current
           :class:`Instance` in addition to the ``display_name``.
           To change them before creating, reset the values via

           .. code:: python

              instance.display_name = 'New display name'
              instance.instance_id = 'i-changed-my-mind'

           before calling :meth:`create`.

        :rtype: :class:`~google.api_core.operation.Operation`
        :returns: an operation instance
        :raises Conflict: if the instance already exists
        """
        api = self._client.instance_admin_api
        instance_pb = InstancePB(
            name=self.name,
            config=self.configuration_name,
            display_name=self.display_name,
            node_count=self.node_count,
            labels=self.labels,
        )
        metadata = _metadata_with_prefix(self.name)

        future = api.create_instance(
            parent=self._client.project_name,
            instance_id=self.instance_id,
            instance=instance_pb,
            metadata=metadata,
        )

        return future
Exemple #45
0
    def commit(self):
        """Commit mutations to the database.

        :rtype: datetime
        :returns: timestamp of the committed changes.
        """
        self._check_state()
        database = self._session._database
        api = database.spanner_api
        metadata = _metadata_with_prefix(database.name)
        txn_options = TransactionOptions(read_write=TransactionOptions.ReadWrite())
        trace_attributes = {"num_mutations": len(self._mutations)}
        with trace_call("CloudSpanner.Commit", self._session, trace_attributes):
            response = api.commit(
                session=self._session.name,
                mutations=self._mutations,
                single_use_transaction=txn_options,
                metadata=metadata,
            )
        self.committed = response.commit_timestamp
        return self.committed
    def commit(self):
        """Commit mutations to the database.

        :rtype: datetime
        :returns: timestamp of the committed changes.
        :raises ValueError: if there are no mutations to commit.
        """
        self._check_state()

        database = self._session._database
        api = database.spanner_api
        metadata = _metadata_with_prefix(database.name)
        response = api.commit(
            self._session.name,
            self._mutations,
            transaction_id=self._transaction_id,
            metadata=metadata,
        )
        self.committed = _pb_timestamp_to_datetime(response.commit_timestamp)
        del self._session._transaction
        return self.committed
Exemple #47
0
    def update_ddl(self, ddl_statements):
        """Update DDL for this database.

        Apply any configured schema from :attr:`ddl_statements`.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.UpdateDatabase

        :rtype: :class:`google.api_core.operation.Operation`
        :returns: an operation instance
        :raises NotFound: if the database does not exist
        """
        client = self._instance._client
        api = client.database_admin_api
        metadata = _metadata_with_prefix(self.name)

        future = api.update_database_ddl(self.name,
                                         ddl_statements,
                                         '',
                                         metadata=metadata)
        return future
Exemple #48
0
    def create(self):
        """Create this session, bound to its database.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.v1#google.spanner.v1.Spanner.CreateSession

        :raises ValueError: if :attr:`session_id` is already set.
        """
        if self._session_id is not None:
            raise ValueError("Session ID already set by back-end")
        api = self._database.spanner_api
        metadata = _metadata_with_prefix(self._database.name)

        request = CreateSessionRequest(database=self._database.name)

        if self._labels:
            request.session.labels = self._labels

        with trace_call("CloudSpanner.CreateSession", self, self._labels):
            session_pb = api.create_session(request=request, metadata=metadata,)
        self._session_id = session_pb.name.split("/")[-1]
Exemple #49
0
    def create(self):
        """Create this backup within its instance.

        :rtype: :class:`~google.api_core.operation.Operation`
        :returns: a future used to poll the status of the create request
        :raises Conflict: if the backup already exists
        :raises NotFound: if the instance owning the backup does not exist
        :raises BadRequest: if the database or expire_time values are invalid
                            or expire_time is not set
        """
        if not self._expire_time:
            raise ValueError("expire_time not set")
        if not self._database:
            raise ValueError("database not set")
        if (self.encryption_config and self.encryption_config.kms_key_name
                and self.encryption_config.encryption_type !=
                CreateBackupEncryptionConfig.EncryptionType.
                CUSTOMER_MANAGED_ENCRYPTION):
            raise ValueError(
                "kms_key_name only used with CUSTOMER_MANAGED_ENCRYPTION")
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)
        backup = BackupPB(
            database=self._database,
            expire_time=self.expire_time,
            version_time=self.version_time,
        )

        request = CreateBackupRequest(
            parent=self._instance.name,
            backup_id=self.backup_id,
            backup=backup,
            encryption_config=self._encryption_config,
        )

        future = api.create_backup(
            request=request,
            metadata=metadata,
        )
        return future
Exemple #50
0
    def create(self):
        """Create this instance.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.CreateInstance

        .. note::

           Uses the ``project`` and ``instance_id`` on the current
           :class:`Instance` in addition to the ``display_name``.
           To change them before creating, reset the values via

           .. code:: python

              instance.display_name = 'New display name'
              instance.instance_id = 'i-changed-my-mind'

           before calling :meth:`create`.

        :rtype: :class:`google.api_core.operation.Operation`
        :returns: an operation instance
        :raises Conflict: if the instance already exists
        """
        api = self._client.instance_admin_api
        instance_pb = admin_v1_pb2.Instance(
            name=self.name,
            config=self.configuration_name,
            display_name=self.display_name,
            node_count=self.node_count,
        )
        metadata = _metadata_with_prefix(self.name)

        future = api.create_instance(
            parent=self._client.project_name,
            instance_id=self.instance_id,
            instance=instance_pb,
            metadata=metadata,
        )

        return future
Exemple #51
0
    def list_databases(self, page_size=None):
        """List databases for the instance.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.ListDatabases

        :type page_size: int
        :param page_size:
            Optional. The maximum number of databases in each page of results
            from this request. Non-positive values are ignored. Defaults
            to a sensible value set by the API.

        :rtype: :class:`~google.api._ore.page_iterator.Iterator`
        :returns:
            Iterator of :class:`~google.cloud.spanner_v1.database.Database`
            resources within the current instance.
        """
        metadata = _metadata_with_prefix(self.name)
        request = ListDatabasesRequest(parent=self.name, page_size=page_size)
        page_iter = self._client.database_admin_api.list_databases(
            request=request, metadata=metadata)
        return page_iter
    def update(self):
        """Update this instance.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstance

        .. note::

           Updates the ``display_name`` and ``node_count``. To change those
           values before updating, set them via

           .. code:: python

              instance.display_name = 'New display name'
              instance.node_count = 5

            before calling :meth:`update`.

        :rtype: :class:`google.api_core.operation.Operation`
        :returns: an operation instance
        :raises NotFound: if the instance does not exist
        """
        api = self._client.instance_admin_api
        instance_pb = admin_v1_pb2.Instance(
            name=self.name,
            config=self.configuration_name,
            display_name=self.display_name,
            node_count=self.node_count,
            )
        field_mask = FieldMask(paths=['config', 'display_name', 'node_count'])
        metadata = _metadata_with_prefix(self.name)

        future = api.update_instance(
            instance=instance_pb,
            field_mask=field_mask,
            metadata=metadata,
        )

        return future
Exemple #53
0
    def restore(self, source):
        """Restore from a backup to this database.

        :type source: :class:`~google.cloud.spanner_v1.backup.Backup`
        :param source: the path of the source being restored from.

        :rtype: :class:`~google.api_core.operation.Operation`
        :returns: a future used to poll the status of the create request
        :raises Conflict: if the database already exists
        :raises NotFound:
            if the instance owning the database does not exist, or
            if the backup being restored from does not exist
        :raises ValueError: if backup is not set
        """
        if source is None:
            raise ValueError("Restore source not specified")
        if type(self._encryption_config) == dict:
            self._encryption_config = RestoreDatabaseEncryptionConfig(
                **self._encryption_config)
        if (self.encryption_config and self.encryption_config.kms_key_name
                and self.encryption_config.encryption_type !=
                RestoreDatabaseEncryptionConfig.EncryptionType.
                CUSTOMER_MANAGED_ENCRYPTION):
            raise ValueError(
                "kms_key_name only used with CUSTOMER_MANAGED_ENCRYPTION")
        api = self._instance._client.database_admin_api
        metadata = _metadata_with_prefix(self.name)
        request = RestoreDatabaseRequest(
            parent=self._instance.name,
            database_id=self.database_id,
            backup=source.name,
            encryption_config=self._encryption_config or None,
        )
        future = api.restore_database(
            request=request,
            metadata=metadata,
        )
        return future
    def list_instances(self, filter_="", page_size=None, page_token=None):
        """List instances for the client's project.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.InstanceAdmin.ListInstances

        :type filter_: string
        :param filter_: (Optional) Filter to select instances listed.  See
                        the ``ListInstancesRequest`` docs above for examples.

        :type page_size: int
        :param page_size:
            Optional. The maximum number of instances in each page of results
            from this request. Non-positive values are ignored. Defaults
            to a sensible value set by the API.

        :type page_token: str
        :param page_token:
            Optional. If present, return the next batch of instances, using
            the value, which must correspond to the ``nextPageToken`` value
            returned in the previous response.  Deprecated: use the ``pages``
            property of the returned iterator instead of manually passing
            the token.

        :rtype: :class:`~google.api_core.page_iterator.Iterator`
        :returns:
            Iterator of :class:`~google.cloud.spanner_v1.instance.Instance`
            resources within the client's project.
        """
        metadata = _metadata_with_prefix(self.project_name)
        path = "projects/%s" % (self.project,)
        page_iter = self.instance_admin_api.list_instances(
            path, page_size=page_size, metadata=metadata
        )
        page_iter.item_to_value = self._item_to_instance
        page_iter.next_page_token = page_token
        return page_iter
Exemple #55
0
    def update(self):
        """Update this instance.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.instance.v1#google.spanner.admin.instance.v1.InstanceAdmin.UpdateInstance

        .. note::

           Updates the ``display_name`` and ``node_count``. To change those
           values before updating, set them via

           .. code:: python

              instance.display_name = 'New display name'
              instance.node_count = 5

            before calling :meth:`update`.

        :rtype: :class:`google.api_core.operation.Operation`
        :returns: an operation instance
        :raises NotFound: if the instance does not exist
        """
        api = self._client.instance_admin_api
        instance_pb = admin_v1_pb2.Instance(
            name=self.name,
            config=self.configuration_name,
            display_name=self.display_name,
            node_count=self.node_count,
        )
        field_mask = FieldMask(paths=["config", "display_name", "node_count"])
        metadata = _metadata_with_prefix(self.name)

        future = api.update_instance(
            instance=instance_pb, field_mask=field_mask, metadata=metadata
        )

        return future
    def list_instance_configs(self, page_size=None, page_token=None):
        """List available instance configurations for the client's project.

        .. _RPC docs: https://cloud.google.com/spanner/docs/reference/rpc/\
                      google.spanner.admin.instance.v1#google.spanner.admin.\
                      instance.v1.InstanceAdmin.ListInstanceConfigs

        See `RPC docs`_.

        :type page_size: int
        :param page_size:
            Optional. The maximum number of configs in each page of results
            from this request. Non-positive values are ignored. Defaults
            to a sensible value set by the API.

        :type page_token: str
        :param page_token:
            Optional. If present, return the next batch of configs, using
            the value, which must correspond to the ``nextPageToken`` value
            returned in the previous response.  Deprecated: use the ``pages``
            property of the returned iterator instead of manually passing
            the token.

        :rtype: :class:`~google.api_core.page_iterator.Iterator`
        :returns:
            Iterator of
            :class:`~google.cloud.spanner_v1.instance.InstanceConfig`
            resources within the client's project.
        """
        metadata = _metadata_with_prefix(self.project_name)
        path = "projects/%s" % (self.project,)
        page_iter = self.instance_admin_api.list_instance_configs(
            path, page_size=page_size, metadata=metadata
        )
        page_iter.next_page_token = page_token
        page_iter.item_to_value = _item_to_instance_config
        return page_iter
Exemple #57
0
    def list_databases(self, page_size=None, page_token=None):
        """List databases for the instance.

        See
        https://cloud.google.com/spanner/reference/rpc/google.spanner.admin.database.v1#google.spanner.admin.database.v1.DatabaseAdmin.ListDatabases

        :type page_size: int
        :param page_size: (Optional) Maximum number of results to return.

        :type page_token: str
        :param page_token: (Optional) Token for fetching next page of results.

        :rtype: :class:`~google.api._ore.page_iterator.Iterator`
        :returns:
            Iterator of :class:`~google.cloud.spanner_v1.database.Database`
            resources within the current instance.
        """
        metadata = _metadata_with_prefix(self.name)
        page_iter = self._client.database_admin_api.list_databases(
            self.name, page_size=page_size, metadata=metadata
        )
        page_iter.next_page_token = page_token
        page_iter.item_to_value = self._item_to_database
        return page_iter
    def partition_query(
        self,
        sql,
        params=None,
        param_types=None,
        partition_size_bytes=None,
        max_partitions=None,
    ):
        """Perform a ``ParitionQuery`` API request.

        :type sql: str
        :param sql: SQL query statement

        :type params: dict, {str -> column value}
        :param params: values for parameter replacement.  Keys must match
                       the names used in ``sql``.

        :type param_types: dict[str -> Union[dict, .types.Type]]
        :param param_types:
            (Optional) maps explicit types for one or more param values;
            required if parameters are passed.

        :type partition_size_bytes: int
        :param partition_size_bytes:
            (Optional) desired size for each partition generated.  The service
            uses this as a hint, the actual partition size may differ.

        :type max_partitions: int
        :param max_partitions:
            (Optional) desired maximum number of partitions generated. The
            service uses this as a hint, the actual number of partitions may
            differ.

        :rtype: iterable of bytes
        :returns: a sequence of partition tokens

        :raises ValueError:
            for single-use snapshots, or if a transaction ID is
            already associtated with the snapshot.
        """
        if not self._multi_use:
            raise ValueError("Cannot use single-use snapshot.")

        if self._transaction_id is None:
            raise ValueError("Transaction not started.")

        if params is not None:
            if param_types is None:
                raise ValueError("Specify 'param_types' when passing 'params'.")
            params_pb = Struct(
                fields={key: _make_value_pb(value) for key, value in params.items()}
            )
        else:
            params_pb = None

        database = self._session._database
        api = database.spanner_api
        metadata = _metadata_with_prefix(database.name)
        transaction = self._make_txn_selector()
        partition_options = PartitionOptions(
            partition_size_bytes=partition_size_bytes, max_partitions=max_partitions
        )

        response = api.partition_query(
            session=self._session.name,
            sql=sql,
            transaction=transaction,
            params=params_pb,
            param_types=param_types,
            partition_options=partition_options,
            metadata=metadata,
        )

        return [partition.partition_token for partition in response.partitions]
    def _call_fut(self, *args, **kw):
        from google.cloud.spanner_v1._helpers import _metadata_with_prefix

        return _metadata_with_prefix(*args, **kw)