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 :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
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 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
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 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._transaction_id = response.id return self._transaction_id
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. """ 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 page_iterator._GAXIterator(self, page_iter, _item_to_instance_config)
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. """ 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 page_iterator._GAXIterator(self, page_iter, _item_to_instance)
def execute_sql(self, sql, params=None, param_types=None, query_mode=None): """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.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: :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.") 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 restart = functools.partial( api.execute_streaming_sql, self._session.name, sql, transaction=transaction, params=params_pb, param_types=param_types, query_mode=query_mode, 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)
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. """ 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 = page_iterator._GAXIterator(self._client, page_iter, _item_to_database) iterator.instance = self return iterator
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 :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: raise exceptions.from_grpc_error(exc.cause) return future
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._transaction_id, options=options) self._rolled_back = True del self._session._transaction
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 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)
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]
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
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
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
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
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 :raises GaxError: for other errors returned from the call """ 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)
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
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
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() if not self._mutations: 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._transaction_id, options=options) self.committed = _pb_timestamp_to_datetime( response.commit_timestamp) del self._session._transaction return self.committed
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 :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: 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)
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. :raises GaxError: for errors other than ``NOT_FOUND`` returned from the call """ 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
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
def _call_fut(self, *args, **kw): from google.cloud.spanner_v1._helpers import _options_with_prefix return _options_with_prefix(*args, **kw)