Ejemplo n.º 1
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
        """
        api = self._instance._client.database_admin_api
        options = _options_with_prefix(self.name)
        db_name = self.database_id
        if '-' in db_name:
            db_name = '`%s`' % (db_name, )

        try:
            future = api.create_database(
                parent=self._instance.name,
                create_statement='CREATE DATABASE %s' % (db_name, ),
                extra_statements=list(self._ddl_statements),
                options=options,
            )
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.ALREADY_EXISTS:
                raise Conflict(self.name)
            elif exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                raise NotFound('Instance not found: {name}'.format(
                    name=self._instance.name, ))
            raise

        future.caller_metadata = {'request_type': 'CreateDatabase'}
        return future
Ejemplo n.º 2
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.cloud.iterator.Iterator`
        :returns:
            Iterator of :class:`~google.cloud.spanner.database.Database`
            resources within the current instance.
        """
        if page_token is None:
            page_token = INITIAL_PAGE
        options = _options_with_prefix(self.name, page_token=page_token)
        page_iter = self._client.database_admin_api.list_databases(
            self.name, page_size=page_size, options=options)
        iterator = GAXIterator(self._client, page_iter, _item_to_database)
        iterator.instance = self
        return iterator
Ejemplo n.º 3
0
    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._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
        options = _options_with_prefix(database.name)
        txn_options = TransactionOptions(
            read_write=TransactionOptions.ReadWrite())
        response = api.begin_transaction(self._session.name,
                                         txn_options,
                                         options=options)
        self._id = response.id
        return self._id
Ejemplo n.º 4
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.cloud.future.operation.Operation`
        :returns: an operation instance
        :raises NotFound: if the database does not exist
        :raises GaxError:
            for errors other than ``NOT_FOUND`` returned from the call
        """
        client = self._instance._client
        api = client.database_admin_api
        options = _options_with_prefix(self.name)

        try:
            future = api.update_database_ddl(
                self.name, ddl_statements, '', options=options)
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                raise NotFound(self.name)
            raise

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

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

        :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.cloud.iterator.Iterator`
        :returns:
            Iterator of
            :class:`~google.cloud.spanner.instance.InstanceConfig`
            resources within the client's project.
        """
        if page_token is None:
            page_token = INITIAL_PAGE
        options = _options_with_prefix(self.project_name,
                                       page_token=page_token)
        path = 'projects/%s' % (self.project,)
        page_iter = self.instance_admin_api.list_instance_configs(
            path, page_size=page_size, options=options)
        return GAXIterator(self, page_iter, _item_to_instance_config)
Ejemplo n.º 6
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.cloud.iterator.Iterator`
        :returns:
            Iterator of :class:`~google.cloud.spanner.instance.Instance`
            resources within the client's project.
        """
        if page_token is None:
            page_token = INITIAL_PAGE
        options = _options_with_prefix(self.project_name,
                                       page_token=page_token)
        path = 'projects/%s' % (self.project,)
        page_iter = self.instance_admin_api.list_instances(
            path, filter_=filter_, page_size=page_size, options=options)
        return GAXIterator(self, page_iter, _item_to_instance)
Ejemplo n.º 7
0
    def execute_sql(self, sql, params=None, param_types=None, query_mode=None,
                    resume_token=b''):
        """Perform an ``ExecuteStreamingSql`` API request for rows in a table.

        :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
        :param param_types:
            (Optional) maps explicit types for one or more param values;
            required if parameters are passed.

        :type query_mode:
            :class:`google.cloud.proto.spanner.v1.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

        :type resume_token: bytes
        :param resume_token: token for resuming previously-interrupted query

        :rtype: :class:`~google.cloud.spanner.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.")

        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
        options = _options_with_prefix(database.name)
        transaction = self._make_txn_selector()
        api = database.spanner_api
        iterator = api.execute_streaming_sql(
            self._session.name, sql,
            transaction=transaction, params=params_pb, param_types=param_types,
            query_mode=query_mode, resume_token=resume_token, options=options)

        self._read_request_count += 1

        if self._multi_use:
            return StreamedResultSet(iterator, source=self)
        else:
            return StreamedResultSet(iterator)
Ejemplo n.º 8
0
    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' 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
        options = _options_with_prefix(database.name)
        txn_selector = self._make_txn_selector()
        response = api.begin_transaction(
            self._session.name, txn_selector.begin, options=options)
        self._transaction_id = response.id
        return self._transaction_id
Ejemplo n.º 9
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.cloud.operation.Operation`
        :returns: an operation instance
        """
        client = self._instance._client
        api = client.database_admin_api
        options = _options_with_prefix(self.name)

        try:
            future = api.update_database_ddl(self.name,
                                             ddl_statements,
                                             '',
                                             options=options)
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                raise NotFound(self.name)
            raise

        future.caller_metadata = {'request_type': 'UpdateDatabaseDdl'}
        return future
Ejemplo n.º 10
0
 def rollback(self):
     """Roll back a transaction on the database."""
     self._check_state()
     database = self._session._database
     api = database.spanner_api
     options = _options_with_prefix(database.name)
     api.rollback(self._session.name, self._id, options=options)
     self._rolled_back = True
Ejemplo n.º 11
0
    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.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) maxiumn number of rows to return

        :rtype: :class:`~google.cloud.spanner.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
        options = _options_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,
                                    options=options)

        iterator = _restart_on_unavailable(restart)

        self._read_request_count += 1

        if self._multi_use:
            return StreamedResultSet(iterator, source=self)
        else:
            return StreamedResultSet(iterator)
Ejemplo n.º 12
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
        options = _options_with_prefix(self._database.name)
        session_pb = api.create_session(self._database.name, options=options)
        self._session_id = session_pb.name.split('/')[-1]
Ejemplo n.º 13
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
        options = _options_with_prefix(self.name)

        try:
            api.drop_database(self.name, options=options)
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                raise NotFound(self.name)
            raise
Ejemplo n.º 14
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
        """
        api = self._instance._client.database_admin_api
        options = _options_with_prefix(self.name)

        try:
            api.get_database_ddl(self.name, options=options)
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                return False
            raise
        return True
Ejemplo n.º 15
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
        :raises GaxError:
            for errors other than ``ALREADY_EXISTS`` returned from the call
        """
        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,
            )
        options = _options_with_prefix(self.name)

        try:
            future = api.create_instance(
                parent=self._client.project_name,
                instance_id=self.instance_id,
                instance=instance_pb,
                options=options,
            )
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.ALREADY_EXISTS:
                raise Conflict(self.name)
            raise

        return future
Ejemplo n.º 16
0
    def read(self,
             table,
             columns,
             keyset,
             index='',
             limit=0,
             resume_token=b''):
        """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.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) maxiumn number of rows to return

        :type resume_token: bytes
        :param resume_token: token for resuming previously-interrupted read

        :rtype: :class:`~google.cloud.spanner.streamed.StreamedResultSet`
        :returns: a result set instance which can be used to consume rows.
        """
        database = self._session._database
        api = database.spanner_api
        options = _options_with_prefix(database.name)
        transaction = self._make_txn_selector()

        iterator = api.streaming_read(self._session.name,
                                      table,
                                      columns,
                                      keyset.to_pb(),
                                      transaction=transaction,
                                      index=index,
                                      limit=limit,
                                      resume_token=resume_token,
                                      options=options)

        return StreamedResultSet(iterator)
Ejemplo n.º 17
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
        """
        api = self._client.instance_admin_api
        options = _options_with_prefix(self.name)

        try:
            instance_pb = api.get_instance(self.name, options=options)
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                raise NotFound(self.name)
            raise

        self._update_from_pb(instance_pb)
Ejemplo n.º 18
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
        :raises GaxError: for other errors returned from the call
        """
        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'])
        options = _options_with_prefix(self.name)

        try:
            future = api.update_instance(
                instance=instance_pb,
                field_mask=field_mask,
                options=options,
            )
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                raise NotFound(self.name)
            raise

        return future
Ejemplo n.º 19
0
    def delete(self):
        """Delete this session.

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

        :raises: :exc:`ValueError` if :attr:`session_id` is not already set.
        """
        if self._session_id is None:
            raise ValueError('Session ID not set by back-end')
        api = self._database.spanner_api
        options = _options_with_prefix(self._database.name)
        try:
            api.delete_session(self.name, options=options)
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                raise NotFound(self.name)
            raise
Ejemplo n.º 20
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
        """
        api = self._instance._client.database_admin_api
        options = _options_with_prefix(self.name)

        try:
            response = api.get_database_ddl(self.name, options=options)
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                raise NotFound(self.name)
            raise
        self._ddl_statements = tuple(response.statements)
Ejemplo n.º 21
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
        options = _options_with_prefix(database.name)
        txn_options = TransactionOptions(
            read_write=TransactionOptions.ReadWrite())
        response = api.commit(self._session.name,
                              self._mutations,
                              single_use_transaction=txn_options,
                              options=options)
        self.committed = _pb_timestamp_to_datetime(response.commit_timestamp)
        return self.committed
Ejemplo n.º 22
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
        options = _options_with_prefix(self._database.name)
        try:
            api.get_session(self.name, options=options)
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                return False
            raise
        else:
            return True
Ejemplo n.º 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
        :raises GaxError:
            for errors other than ``NOT_FOUND`` returned from the call
        """
        if self._session_id is None:
            raise ValueError('Session ID not set by back-end')
        api = self._database.spanner_api
        options = _options_with_prefix(self._database.name)
        try:
            api.delete_session(self.name, options=options)
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                raise NotFound(self.name)
            raise
Ejemplo n.º 24
0
    def commit(self):
        """Commit mutations to the database.

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

        if len(self._mutations) == 0:
            raise ValueError("No mutations to commit")

        database = self._session._database
        api = database.spanner_api
        options = _options_with_prefix(database.name)
        response = api.commit(self._session.name,
                              self._mutations,
                              transaction_id=self._id,
                              options=options)
        self.committed = _pb_timestamp_to_datetime(response.commit_timestamp)
        return self.committed
Ejemplo n.º 25
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.
        :raises GaxError:
            for errors other than ``NOT_FOUND`` returned from the call
        """
        api = self._instance._client.database_admin_api
        options = _options_with_prefix(self.name)

        try:
            api.get_database_ddl(self.name, options=options)
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                return False
            raise
        return True
Ejemplo n.º 26
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.cloud.future.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
        :raises GaxError:
            for errors other than ``ALREADY_EXISTS`` returned from the call
        """
        api = self._instance._client.database_admin_api
        options = _options_with_prefix(self.name)
        db_name = self.database_id
        if '-' in db_name:
            db_name = '`%s`' % (db_name,)

        try:
            future = api.create_database(
                parent=self._instance.name,
                create_statement='CREATE DATABASE %s' % (db_name,),
                extra_statements=list(self._ddl_statements),
                options=options,
            )
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.ALREADY_EXISTS:
                raise Conflict(self.name)
            elif exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                raise NotFound('Instance not found: {name}'.format(
                    name=self._instance.name,
                ))
            raise

        return future
Ejemplo n.º 27
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
        options = _options_with_prefix(self.name)

        try:
            api.delete_instance(self.name, options=options)
        except GaxError as exc:
            if exc_to_code(exc.cause) == StatusCode.NOT_FOUND:
                raise NotFound(self.name)
            raise
Ejemplo n.º 28
0
    def _call_fut(self, *args, **kw):
        from google.cloud.spanner._helpers import _options_with_prefix

        return _options_with_prefix(*args, **kw)