# Copyright 2016 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require "google/cloud/spanner/database/job" require "google/cloud/spanner/database/list" require "google/cloud/spanner/policy" module Google module Cloud module Spanner ## # # Database # # Represents a Cloud Spanner database. To use Cloud Spanner's read and # write operations, you must first create a database. A database belongs # to a {Instance} and contains tables and indexes. You may create multiple # databases in an {Instance}. # # See {Google::Cloud::Spanner::Instance#databases}, # {Google::Cloud::Spanner::Instance#database}, and # {Google::Cloud::Spanner::Instance#create_database}. # # To read and/or modify data in a Cloud Spanner database, use an instance # of {Google::Cloud::Spanner::Client}. See # {Google::Cloud::Spanner::Project#client}. # # @example # require "google/cloud" # # spanner = Google::Cloud::Spanner.new # instance = spanner.instance "my-instance" # # job = instance.create_database "my-new-database" # # job.done? #=> false # job.reload! # API call # job.done? #=> true # # if job.error? # status = job.error # else # database = job.database # end # class Database ## # @private The gRPC Service object. attr_accessor :service # @private Creates a new Database instance. def initialize grpc, service @grpc = grpc @service = service end # The unique identifier for the project. # @return [String] def project_id @grpc.name.split("/")[1] end # The unique identifier for the instance. # @return [String] def instance_id @grpc.name.split("/")[3] end # The unique identifier for the database. # @return [String] def database_id @grpc.name.split("/")[5] end ## # The full path for the database resource. Values are of the form # `projects//instances//databases/`. # @return [String] def path @grpc.name end ## # The current database state. Possible values are `:CREATING` and # `:READY`. # @return [Symbol] def state @grpc.state end ## # The database is still being created. Operations on the database may # raise with `FAILED_PRECONDITION` in this state. # @return [Boolean] def creating? state == :CREATING end ## # The database is fully created and ready for use. # @return [Boolean] def ready? state == :READY end ## # Retrieve the Data Definition Language (DDL) statements that define # database structures. DDL statements are used to create, update, # and delete tables and indexes. # # @see https://cloud.google.com/spanner/docs/data-definition-language # Data Definition Language # # @param [Boolean] force Force the latest DDL statements to be retrieved # from the Spanner service when `true`. Otherwise the DDL statements # will be memoized to reduce the number of API calls made to the # Spanner service. The default is `false`. # # @return [Array] The DDL statements. # # @example statements are memoized to reduce the number of API calls: # require "google/cloud/spanner" # # spanner = Google::Cloud::Spanner.new # database = spanner.database "my-instance", "my-database" # # statements = database.ddl # API call # statements_2 = database.ddl # No API call # # @example Use `force` to retrieve the statements from the service: # require "google/cloud/spanner" # # spanner = Google::Cloud::Spanner.new # database = spanner.database "my-instance", "my-database" # # statements = database.ddl force: true # API call # statements_2 = database.ddl force: true # API call # def ddl force: nil return @ddl if @ddl && !force ensure_service! ddl_grpc = service.get_database_ddl instance_id, database_id @ddl = ddl_grpc.statements end ## # Updates the database schema by adding Data Definition Language (DDL) # statements to create, update, and delete tables and indexes. # # @see https://cloud.google.com/spanner/docs/data-definition-language # Data Definition Language # # @param [Array] statements The DDL statements to be applied to # the database. # @param [String, nil] operation_id The operation ID used to perform the # update. When `nil`, the update request is assigned an # automatically-generated operation ID. Specifying an explicit value # simplifies determining whether the statements were executed in the # event that the update is replayed, or the return value is otherwise # lost. This value should be unique within the database, and must be a # valid identifier: `[a-z][a-z0-9_]*`. Will raise # {Google::Cloud::AlreadyExistsError} if the named operation already # exists. Optional. # # @return [Database::Job] The job representing the long-running, # asynchronous processing of a database schema update operation. # # @example # require "google/cloud/spanner" # # spanner = Google::Cloud::Spanner.new # database = spanner.database "my-instance", "my-database" # # add_users_table_sql = %q( # CREATE TABLE users ( # id INT64 NOT NULL, # username STRING(25) NOT NULL, # name STRING(45) NOT NULL, # email STRING(128), # ) PRIMARY KEY(id) # ) # # database.update statements: [add_users_table_sql] # def update statements: [], operation_id: nil ensure_service! grpc = service.update_database_ddl instance_id, database_id, statements: statements, operation_id: operation_id Database::Job.from_grpc grpc, service end ## # Drops (deletes) the Cloud Spanner database. # # @return [Boolean] Returns `true` if the database was deleted. # # @example # require "google/cloud/spanner" # # spanner = Google::Cloud::Spanner.new # database = spanner.database "my-instance", "my-database" # # database.drop # def drop ensure_service! service.drop_database instance_id, database_id true end ## # Gets the [Cloud IAM](https://cloud.google.com/iam/) access control # policy for this database. # # @see https://cloud.google.com/spanner/reference/rpc/google.iam.v1#google.iam.v1.Policy # google.iam.v1.IAMPolicy # # @yield [policy] A block for updating the policy. The latest policy # will be read from the Spanner service and passed to the block. After # the block completes, the modified policy will be written to the # service. # @yieldparam [Policy] policy the current Cloud IAM Policy for this # database # # @return [Policy] The current Cloud IAM Policy for this database. # # @example # require "google/cloud/spanner" # # spanner = Google::Cloud::Spanner.new # database = spanner.database "my-instance", "my-database" # # policy = database.policy # # @example Update the policy by passing a block: # require "google/cloud/spanner" # # spanner = Google::Cloud::Spanner.new # database = spanner.database "my-instance", "my-database" # # database.policy do |p| # p.add "roles/owner", "user:owner@example.com" # end # 2 API calls # def policy ensure_service! grpc = service.get_database_policy instance_id, database_id policy = Policy.from_grpc grpc return policy unless block_given? yield policy update_policy policy end ## # Updates the [Cloud IAM](https://cloud.google.com/iam/) access control # policy for this database. The policy should be read from {#policy}. # See {Google::Cloud::Spanner::Policy} for an explanation of the policy # `etag` property and how to modify policies. # # You can also update the policy by passing a block to {#policy}, which # will call this method internally after the block completes. # # @see https://cloud.google.com/spanner/reference/rpc/google.iam.v1#google.iam.v1.Policy # google.iam.v1.IAMPolicy # # @param [Policy] new_policy a new or modified Cloud IAM Policy for this # database # # @return [Policy] The policy returned by the API update operation. # # @example # require "google/cloud/spanner" # # spanner = Google::Cloud::Spanner.new # database = spanner.database "my-instance", "my-database" # # policy = database.policy # API call # # policy.add "roles/owner", "user:owner@example.com" # # database.update_policy policy # API call # def update_policy new_policy ensure_service! grpc = service.set_database_policy \ instance_id, database_id, new_policy.to_grpc Policy.from_grpc grpc end alias policy= update_policy ## # Tests the specified permissions against the [Cloud # IAM](https://cloud.google.com/iam/) access control policy. # # @see https://cloud.google.com/iam/docs/managing-policies Managing # Policies # # @param [String, Array] permissions The set of permissions to # check access for. Permissions with wildcards (such as `*` or # `storage.*`) are not allowed. # # The permissions that can be checked on a database are: # # * pubsub.databases.create # * pubsub.databases.list # * pubsub.databases.update # * pubsub.databases.updateDdl # * pubsub.databases.get # * pubsub.databases.getDdl # * pubsub.databases.getIamPolicy # * pubsub.databases.setIamPolicy # * pubsub.databases.beginReadOnlyTransaction # * pubsub.databases.beginOrRollbackReadWriteTransaction # * pubsub.databases.read # * pubsub.databases.select # * pubsub.databases.write # * pubsub.databases.drop # # @return [Array] The permissions that have access. # # @example # require "google/cloud/spanner" # # spanner = Google::Cloud::Spanner.new # database = spanner.database "my-instance", "my-database" # perms = database.test_permissions "spanner.databases.get", # "spanner.databases.update" # perms.include? "spanner.databases.get" #=> true # perms.include? "spanner.databases.update" #=> false # def test_permissions *permissions permissions = Array(permissions).flatten permissions = Array(permissions).flatten ensure_service! grpc = service.test_database_permissions \ instance_id, database_id, permissions grpc.permissions end ## # @private Creates a new Database instance from a # Google::Spanner::Admin::Database::V1::Database. def self.from_grpc grpc, service new grpc, service end protected ## # @private Raise an error unless an active connection to the service is # available. def ensure_service! raise "Must have active connection to service" unless service end def session_path instance_id, database_id, session_id V1::SpannerClient.session_path( project_id, instance_id, database_id, session_id ) end end end end end