google-cloud-spanner 0.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +7 -0
  2. data/lib/google-cloud-spanner.rb +106 -0
  3. data/lib/google/cloud/spanner.rb +382 -0
  4. data/lib/google/cloud/spanner/admin/database/v1.rb +17 -0
  5. data/lib/google/cloud/spanner/admin/database/v1/database_admin_client.rb +703 -0
  6. data/lib/google/cloud/spanner/admin/database/v1/database_admin_client_config.json +73 -0
  7. data/lib/google/cloud/spanner/admin/database/v1/doc/google/iam/v1/policy.rb +139 -0
  8. data/lib/google/cloud/spanner/admin/database/v1/doc/google/protobuf/any.rb +114 -0
  9. data/lib/google/cloud/spanner/admin/database/v1/doc/google/rpc/status.rb +83 -0
  10. data/lib/google/cloud/spanner/admin/database/v1/doc/google/spanner/admin/database/v1/spanner_database_admin.rb +188 -0
  11. data/lib/google/cloud/spanner/admin/instance/v1.rb +17 -0
  12. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/iam/v1/policy.rb +139 -0
  13. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/any.rb +114 -0
  14. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/field_mask.rb +223 -0
  15. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/rpc/status.rb +83 -0
  16. data/lib/google/cloud/spanner/admin/instance/v1/doc/google/spanner/admin/instance/v1/spanner_instance_admin.rb +268 -0
  17. data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client.rb +868 -0
  18. data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client_config.json +78 -0
  19. data/lib/google/cloud/spanner/client.rb +1034 -0
  20. data/lib/google/cloud/spanner/commit.rb +351 -0
  21. data/lib/google/cloud/spanner/convert.rb +311 -0
  22. data/lib/google/cloud/spanner/credentials.rb +32 -0
  23. data/lib/google/cloud/spanner/data.rb +199 -0
  24. data/lib/google/cloud/spanner/database.rb +377 -0
  25. data/lib/google/cloud/spanner/database/job.rb +179 -0
  26. data/lib/google/cloud/spanner/database/list.rb +171 -0
  27. data/lib/google/cloud/spanner/errors.rb +73 -0
  28. data/lib/google/cloud/spanner/fields.rb +252 -0
  29. data/lib/google/cloud/spanner/instance.rb +472 -0
  30. data/lib/google/cloud/spanner/instance/config.rb +99 -0
  31. data/lib/google/cloud/spanner/instance/config/list.rb +171 -0
  32. data/lib/google/cloud/spanner/instance/job.rb +197 -0
  33. data/lib/google/cloud/spanner/instance/list.rb +167 -0
  34. data/lib/google/cloud/spanner/policy.rb +201 -0
  35. data/lib/google/cloud/spanner/pool.rb +279 -0
  36. data/lib/google/cloud/spanner/project.rb +480 -0
  37. data/lib/google/cloud/spanner/range.rb +99 -0
  38. data/lib/google/cloud/spanner/results.rb +280 -0
  39. data/lib/google/cloud/spanner/service.rb +458 -0
  40. data/lib/google/cloud/spanner/session.rb +565 -0
  41. data/lib/google/cloud/spanner/snapshot.rb +260 -0
  42. data/lib/google/cloud/spanner/transaction.rb +533 -0
  43. data/lib/google/cloud/spanner/v1.rb +17 -0
  44. data/lib/google/cloud/spanner/v1/doc/google/protobuf/duration.rb +77 -0
  45. data/lib/google/cloud/spanner/v1/doc/google/protobuf/struct.rb +73 -0
  46. data/lib/google/cloud/spanner/v1/doc/google/protobuf/timestamp.rb +81 -0
  47. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/keys.rb +148 -0
  48. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/mutation.rb +80 -0
  49. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/query_plan.rb +120 -0
  50. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/result_set.rb +175 -0
  51. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/spanner.rb +206 -0
  52. data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/transaction.rb +351 -0
  53. data/lib/google/cloud/spanner/v1/spanner_client.rb +850 -0
  54. data/lib/google/cloud/spanner/v1/spanner_client_config.json +78 -0
  55. data/lib/google/cloud/spanner/version.rb +22 -0
  56. data/lib/google/spanner/admin/database/v1/spanner_database_admin_pb.rb +85 -0
  57. data/lib/google/spanner/admin/database/v1/spanner_database_admin_services_pb.rb +95 -0
  58. data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_pb.rb +106 -0
  59. data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_services_pb.rb +180 -0
  60. data/lib/google/spanner/v1/keys_pb.rb +33 -0
  61. data/lib/google/spanner/v1/mutation_pb.rb +38 -0
  62. data/lib/google/spanner/v1/query_plan_pb.rb +47 -0
  63. data/lib/google/spanner/v1/result_set_pb.rb +43 -0
  64. data/lib/google/spanner/v1/spanner_pb.rb +90 -0
  65. data/lib/google/spanner/v1/spanner_services_pb.rb +131 -0
  66. data/lib/google/spanner/v1/transaction_pb.rb +51 -0
  67. data/lib/google/spanner/v1/type_pb.rb +43 -0
  68. metadata +309 -0
@@ -0,0 +1,99 @@
1
+ # Copyright 2017 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ module Google
17
+ module Cloud
18
+ module Spanner
19
+ ##
20
+ # # Range
21
+ #
22
+ # Represents a range of rows in a table or index. A range has a start key
23
+ # and an end key. These keys can be open or closed, indicating if the
24
+ # range includes rows with that key.
25
+ #
26
+ # @example
27
+ # require "google/cloud/spanner"
28
+ #
29
+ # spanner = Google::Cloud::Spanner.new
30
+ #
31
+ # db = spanner.client "my-instance", "my-database"
32
+ #
33
+ # key_range = db.range 1, 100
34
+ # results = db.read "users", [:id, :name], keys: key_range
35
+ #
36
+ # results.rows.each do |row|
37
+ # puts "User #{row[:id]} is #{row[:name]}"
38
+ # end
39
+ #
40
+ class Range
41
+ ##
42
+ # Returns the object that defines the beginning of the range.
43
+ attr_reader :begin
44
+
45
+ ##
46
+ # Returns the object that defines the end of the range.
47
+ attr_reader :end
48
+
49
+ ##
50
+ # Creates a Spanner Range. This can be used in place of a Ruby Range
51
+ # when needing to exclude the beginning value.
52
+ #
53
+ # @param [Object] beginning The object that defines the beginning of the
54
+ # range.
55
+ # @param [Object] ending The object that defines the end of the range.
56
+ # @param [Boolean] exclude_begin Determines if the range excludes its
57
+ # beginning value. Default is `false`.
58
+ # @param [Boolean] exclude_end Determines if the range excludes its
59
+ # ending value. Default is `false`.
60
+ #
61
+ # @example
62
+ # require "google/cloud/spanner"
63
+ #
64
+ # spanner = Google::Cloud::Spanner.new
65
+ #
66
+ # db = spanner.client "my-instance", "my-database"
67
+ #
68
+ # key_range = Google::Cloud::Spanner::Range.new 1, 100
69
+ # results = db.read "users", [:id, :name], keys: key_range
70
+ #
71
+ # results.rows.each do |row|
72
+ # puts "User #{row[:id]} is #{row[:name]}"
73
+ # end
74
+ #
75
+ def initialize beginning, ending, exclude_begin: false,
76
+ exclude_end: false
77
+ @begin = beginning
78
+ @end = ending
79
+ @exclude_begin = exclude_begin
80
+ @exclude_end = exclude_end
81
+ end
82
+
83
+ ##
84
+ # Returns `true` if the range excludes its beginning value.
85
+ # @return [Boolean]
86
+ def exclude_begin?
87
+ @exclude_begin
88
+ end
89
+
90
+ ##
91
+ # Returns `true` if the range excludes its end value.
92
+ # @return [Boolean]
93
+ def exclude_end?
94
+ @exclude_end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,280 @@
1
+ # Copyright 2016 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "google/cloud/spanner/errors"
17
+ require "google/cloud/spanner/data"
18
+
19
+ module Google
20
+ module Cloud
21
+ module Spanner
22
+ ##
23
+ # # Results
24
+ #
25
+ # Represents the result set from an operation returning data.
26
+ #
27
+ # See {Google::Cloud::Spanner::Client#execute} and
28
+ # {Google::Cloud::Spanner::Client#read}.
29
+ #
30
+ # @example
31
+ # require "google/cloud/spanner"
32
+ #
33
+ # spanner = Google::Cloud::Spanner.new
34
+ #
35
+ # db = spanner.client "my-instance", "my-database"
36
+ #
37
+ # results = db.execute "SELECT * FROM users"
38
+ #
39
+ # results.types.each do |name, type|
40
+ # puts "Column #{name} is type {type}"
41
+ # end
42
+ #
43
+ class Results
44
+ ##
45
+ # The read timestamp chosen for single-use snapshots (read-only
46
+ # transactions).
47
+ # @return [Time] The chosen timestamp.
48
+ def timestamp
49
+ return nil if @metadata.nil? || @metadata.transaction.nil?
50
+ Convert.timestamp_to_time @metadata.transaction.read_timestamp
51
+ end
52
+
53
+ ##
54
+ # Returns the field names and types for the rows in the returned data.
55
+ #
56
+ # @return [Fields] The fields of the returned data.
57
+ #
58
+ # @example
59
+ # require "google/cloud/spanner"
60
+ #
61
+ # spanner = Google::Cloud::Spanner.new
62
+ #
63
+ # db = spanner.client "my-instance", "my-database"
64
+ #
65
+ # results = db.execute "SELECT * FROM users"
66
+ #
67
+ # results.fields.pairs.each do |name, type|
68
+ # puts "Column #{name} is type {type}"
69
+ # end
70
+ #
71
+ def fields
72
+ @fields ||= Fields.from_grpc @metadata.row_type.fields
73
+ end
74
+
75
+ # rubocop:disable all
76
+
77
+ ##
78
+ # The values returned from the request.
79
+ #
80
+ # @yield [row] An enumerator for the rows.
81
+ # @yieldparam [Data] row object that contains the data values.
82
+ #
83
+ # @example
84
+ # require "google/cloud/spanner"
85
+ #
86
+ # spanner = Google::Cloud::Spanner.new
87
+ #
88
+ # db = spanner.client "my-instance", "my-database"
89
+ #
90
+ # results = db.execute "SELECT * FROM users"
91
+ #
92
+ # results.rows.each do |row|
93
+ # puts "User #{row[:id]} is #{row[:name]}"
94
+ # end
95
+ #
96
+ def rows
97
+ return nil if @closed
98
+
99
+ unless block_given?
100
+ return enum_for(:rows)
101
+ end
102
+
103
+ fields = @metadata.row_type.fields
104
+ values = []
105
+ buffered_responses = []
106
+ buffer_upper_bound = 10
107
+ chunked_value = nil
108
+ resume_token = nil
109
+
110
+ # Cannot call Enumerator#each because it won't return the first
111
+ # value that was already identified when calling Enumerator#peek.
112
+ # Iterate only using Enumerator#next and break on StopIteration.
113
+ loop do
114
+ begin
115
+ grpc = @enum.next
116
+ # metadata should be set before the first iteration...
117
+ @metadata ||= grpc.metadata
118
+ @stats ||= grpc.stats
119
+
120
+ buffered_responses << grpc
121
+
122
+ if (grpc.resume_token && grpc.resume_token != "") ||
123
+ buffered_responses.size >= buffer_upper_bound
124
+ # This can set the resume_token to nil
125
+ resume_token = grpc.resume_token
126
+
127
+ buffered_responses.each do |resp|
128
+ if chunked_value
129
+ resp.values.unshift merge(chunked_value, resp.values.shift)
130
+ chunked_value = nil
131
+ end
132
+ to_iterate = values + Array(resp.values)
133
+ chunked_value = to_iterate.pop if resp.chunked_value
134
+ values = to_iterate.pop(to_iterate.count % fields.count)
135
+ to_iterate.each_slice(fields.count) do |slice|
136
+ yield Data.from_grpc(slice, fields)
137
+ end
138
+ end
139
+
140
+ # Flush the buffered responses now that they are all handled
141
+ buffered_responses = []
142
+ end
143
+ rescue GRPC::Unavailable => err
144
+ if resume_token.nil? || resume_token.empty?
145
+ # Re-raise if the resume_token is not a valid value.
146
+ # This can happen if the buffer was flushed.
147
+ raise Google::Cloud::Error.from_error(err)
148
+ end
149
+
150
+ # Resume the stream from the last known resume_token
151
+ if @execute_options
152
+ @enum = @service.streaming_execute_sql \
153
+ @session_path, @sql,
154
+ @execute_options.merge(resume_token: resume_token)
155
+ else
156
+ @enum = @service.streaming_read_table \
157
+ @session_path, @table, @columns,
158
+ @read_options.merge(resume_token: resume_token)
159
+ end
160
+
161
+ # Flush the buffered responses to reset to the resume_token
162
+ buffered_responses = []
163
+ rescue GRPC::BadStatus => err
164
+ raise Google::Cloud::Error.from_error(err)
165
+ rescue StopIteration
166
+ break
167
+ end
168
+ end
169
+
170
+ # clear out any remaining values left over
171
+ buffered_responses.each do |resp|
172
+ if chunked_value
173
+ resp.values.unshift merge(chunked_value, resp.values.shift)
174
+ chunked_value = nil
175
+ end
176
+ to_iterate = values + Array(resp.values)
177
+ chunked_value = to_iterate.pop if resp.chunked_value
178
+ values = to_iterate.pop(to_iterate.count % fields.count)
179
+ to_iterate.each_slice(fields.count) do |slice|
180
+ yield Data.from_grpc(slice, fields)
181
+ end
182
+ end
183
+ values.each_slice(fields.count) do |slice|
184
+ yield Data.from_grpc(slice, fields)
185
+ end
186
+
187
+ # If we get this far then we can release the session
188
+ @closed = true
189
+ nil
190
+ end
191
+
192
+ # rubocop:enable all
193
+
194
+ # @private
195
+ def self.from_enum enum, service
196
+ grpc = enum.peek
197
+ new.tap do |results|
198
+ results.instance_variable_set :@metadata, grpc.metadata
199
+ results.instance_variable_set :@stats, grpc.stats
200
+ results.instance_variable_set :@enum, enum
201
+ results.instance_variable_set :@service, service
202
+ end
203
+ end
204
+
205
+ # @private
206
+ def self.execute service, session_path, sql, params: nil, types: nil,
207
+ transaction: nil
208
+ execute_options = { transaction: transaction, params: params,
209
+ types: types }
210
+ enum = service.streaming_execute_sql session_path, sql,
211
+ execute_options
212
+ from_enum(enum, service).tap do |results|
213
+ results.instance_variable_set :@session_path, session_path
214
+ results.instance_variable_set :@sql, sql
215
+ results.instance_variable_set :@execute_options, execute_options
216
+ end
217
+ end
218
+
219
+ # @private
220
+ def self.read service, session_path, table, columns, keys: nil,
221
+ index: nil, limit: nil, transaction: nil
222
+ read_options = { keys: keys, index: index, limit: limit,
223
+ transaction: transaction }
224
+ enum = service.streaming_read_table \
225
+ session_path, table, columns, read_options
226
+ from_enum(enum, service).tap do |results|
227
+ results.instance_variable_set :@session_path, session_path
228
+ results.instance_variable_set :@table, table
229
+ results.instance_variable_set :@columns, columns
230
+ results.instance_variable_set :@read_options, read_options
231
+ end
232
+ end
233
+
234
+ # @private
235
+ def to_s
236
+ "(#{fields.inspect} streaming)"
237
+ end
238
+
239
+ # @private
240
+ def inspect
241
+ "#<#{self.class.name} #{self}>"
242
+ end
243
+
244
+ protected
245
+
246
+ # rubocop:disable all
247
+
248
+ # @private
249
+ def merge left, right
250
+ if left.kind != right.kind
251
+ raise "Can't merge #{left.kind} and #{right.kind} values"
252
+ end
253
+ if left.kind == :string_value
254
+ left.string_value = left.string_value + right.string_value
255
+ return left
256
+ elsif left.kind == :list_value
257
+ left_val = left.list_value.values.pop
258
+ right_val = right.list_value.values.shift
259
+ if (left_val.kind == right_val.kind) &&
260
+ (left_val.kind == :list_value || left_val.kind == :string_value)
261
+ left.list_value.values << merge(left_val, right_val)
262
+ else
263
+ left.list_value.values << left_val
264
+ left.list_value.values << right_val
265
+ end
266
+ right.list_value.values.each { |val| left.list_value.values << val }
267
+ return left
268
+ elsif left.kind == :struct_value
269
+ # Don't worry about this yet since Spanner isn't return STRUCT
270
+ fail "STRUCT not implemented yet"
271
+ else
272
+ raise "Can't merge #{left.kind} values"
273
+ end
274
+ end
275
+
276
+ # rubocop:enable all
277
+ end
278
+ end
279
+ end
280
+ end
@@ -0,0 +1,458 @@
1
+ # Copyright 2016 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ require "google/cloud/spanner/errors"
17
+ require "google/cloud/spanner/credentials"
18
+ require "google/cloud/spanner/version"
19
+ require "google/cloud/spanner/v1"
20
+ require "google/cloud/spanner/admin/instance/v1"
21
+ require "google/cloud/spanner/admin/database/v1"
22
+ require "google/cloud/spanner/convert"
23
+
24
+ module Google
25
+ module Cloud
26
+ module Spanner
27
+ ##
28
+ # @private Represents the gRPC Spanner service, including all the API
29
+ # methods.
30
+ class Service
31
+ attr_accessor :project, :credentials, :host, :timeout, :client_config
32
+
33
+ ##
34
+ # Creates a new Service instance.
35
+ def initialize project, credentials, host: nil, timeout: nil,
36
+ client_config: nil
37
+ @project = project
38
+ @credentials = credentials
39
+ @host = host || V1::SpannerClient::SERVICE_ADDRESS
40
+ @timeout = timeout
41
+ @client_config = client_config || {}
42
+ end
43
+
44
+ def channel
45
+ require "grpc"
46
+ GRPC::Core::Channel.new host, nil, chan_creds
47
+ end
48
+
49
+ def chan_creds
50
+ return credentials if insecure?
51
+ require "grpc"
52
+ GRPC::Core::ChannelCredentials.new.compose \
53
+ GRPC::Core::CallCredentials.new credentials.client.updater_proc
54
+ end
55
+
56
+ def service
57
+ return mocked_service if mocked_service
58
+ @service ||= \
59
+ V1::SpannerClient.new(
60
+ service_path: host,
61
+ channel: channel,
62
+ timeout: timeout,
63
+ client_config: client_config,
64
+ lib_name: "gccl",
65
+ lib_version: Google::Cloud::Spanner::VERSION)
66
+ end
67
+ attr_accessor :mocked_service
68
+
69
+ def instances
70
+ return mocked_instances if mocked_instances
71
+ @instances ||= \
72
+ Admin::Instance::V1::InstanceAdminClient.new(
73
+ service_path: host,
74
+ channel: channel,
75
+ timeout: timeout,
76
+ client_config: client_config,
77
+ lib_name: "gccl",
78
+ lib_version: Google::Cloud::Spanner::VERSION)
79
+ end
80
+ attr_accessor :mocked_instances
81
+
82
+ def databases
83
+ return mocked_databases if mocked_databases
84
+ @databases ||= \
85
+ Admin::Database::V1::DatabaseAdminClient.new(
86
+ service_path: host,
87
+ channel: channel,
88
+ timeout: timeout,
89
+ client_config: client_config,
90
+ lib_name: "gccl",
91
+ lib_version: Google::Cloud::Spanner::VERSION)
92
+ end
93
+ attr_accessor :mocked_databases
94
+
95
+ def insecure?
96
+ credentials == :this_channel_is_insecure
97
+ end
98
+
99
+ def list_instances token: nil, max: nil
100
+ call_options = nil
101
+ call_options = Google::Gax::CallOptions.new page_token: token if token
102
+
103
+ execute do
104
+ paged_enum = instances.list_instances project_path,
105
+ page_size: max,
106
+ options: call_options
107
+
108
+ paged_enum.page.response
109
+ end
110
+ end
111
+
112
+ def get_instance name
113
+ execute do
114
+ instances.get_instance instance_path(name)
115
+ end
116
+ end
117
+
118
+ def create_instance instance_id, name: nil, config: nil, nodes: nil,
119
+ labels: nil
120
+ labels = Hash[labels.map { |k, v| [String(k), String(v)] }] if labels
121
+
122
+ create_obj = Google::Spanner::Admin::Instance::V1::Instance.new({
123
+ display_name: name, config: instance_config_path(config),
124
+ node_count: nodes, labels: labels
125
+ }.delete_if { |_, v| v.nil? })
126
+
127
+ execute do
128
+ instances.create_instance project_path, instance_id, create_obj
129
+ end
130
+ end
131
+
132
+ def update_instance instance_obj
133
+ mask = Google::Protobuf::FieldMask.new(
134
+ paths: %w(display_name node_count labels))
135
+
136
+ execute do
137
+ instances.update_instance instance_obj, mask
138
+ end
139
+ end
140
+
141
+ def delete_instance name
142
+ execute do
143
+ instances.delete_instance instance_path(name)
144
+ end
145
+ end
146
+
147
+ def get_instance_policy name
148
+ execute do
149
+ instances.get_iam_policy instance_path(name)
150
+ end
151
+ end
152
+
153
+ def set_instance_policy name, new_policy
154
+ execute do
155
+ instances.set_iam_policy instance_path(name), new_policy
156
+ end
157
+ end
158
+
159
+ def test_instance_permissions name, permissions
160
+ execute do
161
+ instances.test_iam_permissions instance_path(name), permissions
162
+ end
163
+ end
164
+
165
+ def list_instance_configs token: nil, max: nil
166
+ call_options = nil
167
+ call_options = Google::Gax::CallOptions.new page_token: token if token
168
+
169
+ execute do
170
+ paged_enum = instances.list_instance_configs project_path,
171
+ page_size: max,
172
+ options: call_options
173
+
174
+ paged_enum.page.response
175
+ end
176
+ end
177
+
178
+ def get_instance_config name
179
+ execute do
180
+ instances.get_instance_config instance_config_path(name)
181
+ end
182
+ end
183
+
184
+ def list_databases instance_id, token: nil, max: nil
185
+ call_options = nil
186
+ call_options = Google::Gax::CallOptions.new page_token: token if token
187
+
188
+ execute do
189
+ paged_enum = databases.list_databases instance_path(instance_id),
190
+ page_size: max,
191
+ options: call_options
192
+
193
+ paged_enum.page.response
194
+ end
195
+ end
196
+
197
+ def get_database instance_id, database_id
198
+ execute do
199
+ databases.get_database database_path(instance_id, database_id)
200
+ end
201
+ end
202
+
203
+ def create_database instance_id, database_id, statements: []
204
+ execute do
205
+ databases.create_database \
206
+ instance_path(instance_id),
207
+ "CREATE DATABASE `#{database_id}`",
208
+ extra_statements: Array(statements)
209
+ end
210
+ end
211
+
212
+ def drop_database instance_id, database_id
213
+ execute do
214
+ databases.drop_database database_path(instance_id, database_id)
215
+ end
216
+ end
217
+
218
+ def get_database_ddl instance_id, database_id
219
+ execute do
220
+ databases.get_database_ddl database_path(instance_id, database_id)
221
+ end
222
+ end
223
+
224
+ def update_database_ddl instance_id, database_id, statements: [],
225
+ operation_id: nil
226
+ execute do
227
+ databases.update_database_ddl \
228
+ database_path(instance_id, database_id),
229
+ Array(statements),
230
+ operation_id: operation_id
231
+ end
232
+ end
233
+
234
+ def get_database_policy instance_id, database_id
235
+ execute do
236
+ databases.get_iam_policy database_path(instance_id, database_id)
237
+ end
238
+ end
239
+
240
+ def set_database_policy instance_id, database_id, new_policy
241
+ execute do
242
+ databases.set_iam_policy \
243
+ database_path(instance_id, database_id), new_policy
244
+ end
245
+ end
246
+
247
+ def test_database_permissions instance_id, database_id, permissions
248
+ execute do
249
+ databases.test_iam_permissions \
250
+ database_path(instance_id, database_id), permissions
251
+ end
252
+ end
253
+
254
+ def get_session session_name
255
+ opts = default_options_from_session session_name
256
+ execute do
257
+ service.get_session session_name, options: opts
258
+ end
259
+ end
260
+
261
+ def create_session database_name
262
+ opts = default_options_from_session database_name
263
+ execute do
264
+ service.create_session database_name, options: opts
265
+ end
266
+ end
267
+
268
+ def delete_session session_name
269
+ opts = default_options_from_session session_name
270
+ execute do
271
+ service.delete_session session_name, options: opts
272
+ end
273
+ end
274
+
275
+ def execute_sql session_name, sql, transaction: nil, params: nil
276
+ input_params = nil
277
+ input_param_types = nil
278
+ unless params.nil?
279
+ input_param_pairs = Convert.to_query_params params
280
+ input_params = Google::Protobuf::Struct.new(
281
+ fields: Hash[input_param_pairs.map { |k, v| [k, v.first] }])
282
+ input_param_types = Hash[
283
+ input_param_pairs.map { |k, v| [k, v.last] }]
284
+ end
285
+ opts = default_options_from_session session_name
286
+ execute do
287
+ service.execute_sql \
288
+ session_name, sql, transaction: transaction, params: input_params,
289
+ param_types: input_param_types, options: opts
290
+ end
291
+ end
292
+
293
+ def streaming_execute_sql session_name, sql, transaction: nil,
294
+ params: nil, types: nil, resume_token: nil
295
+ input_params = nil
296
+ input_param_types = nil
297
+ unless params.nil?
298
+ input_param_pairs = Convert.to_query_params params, types
299
+ input_params = Google::Protobuf::Struct.new(
300
+ fields: Hash[input_param_pairs.map { |k, v| [k, v.first] }])
301
+ input_param_types = Hash[
302
+ input_param_pairs.map { |k, v| [k, v.last] }]
303
+ end
304
+ opts = default_options_from_session session_name
305
+ execute do
306
+ service.execute_streaming_sql \
307
+ session_name, sql, transaction: transaction, params: input_params,
308
+ param_types: input_param_types,
309
+ resume_token: resume_token, options: opts
310
+ end
311
+ end
312
+
313
+ def read_table session_name, table_name, columns, keys: nil, index: nil,
314
+ transaction: nil, limit: nil
315
+ columns.map!(&:to_s)
316
+ opts = default_options_from_session session_name
317
+ execute do
318
+ service.read \
319
+ session_name, table_name, columns, key_set(keys),
320
+ transaction: transaction, index: index, limit: limit,
321
+ options: opts
322
+ end
323
+ end
324
+
325
+ def streaming_read_table session_name, table_name, columns, keys: nil,
326
+ index: nil, transaction: nil, limit: nil,
327
+ resume_token: nil
328
+ columns.map!(&:to_s)
329
+ opts = default_options_from_session session_name
330
+ execute do
331
+ service.streaming_read \
332
+ session_name, table_name, columns, key_set(keys),
333
+ transaction: transaction, index: index, limit: limit,
334
+ resume_token: resume_token, options: opts
335
+ end
336
+ end
337
+
338
+ def commit session_name, mutations = [], transaction_id: nil
339
+ tx_opts = nil
340
+ if transaction_id.nil?
341
+ tx_opts = Google::Spanner::V1::TransactionOptions.new(read_write:
342
+ Google::Spanner::V1::TransactionOptions::ReadWrite.new)
343
+ end
344
+ opts = default_options_from_session session_name
345
+ execute do
346
+ service.commit \
347
+ session_name, mutations,
348
+ transaction_id: transaction_id, single_use_transaction: tx_opts,
349
+ options: opts
350
+ end
351
+ end
352
+
353
+ def rollback session_name, transaction_id
354
+ opts = default_options_from_session session_name
355
+ execute do
356
+ service.rollback session_name, transaction_id, options: opts
357
+ end
358
+ end
359
+
360
+ def begin_transaction session_name
361
+ tx_opts = Google::Spanner::V1::TransactionOptions.new(read_write:
362
+ Google::Spanner::V1::TransactionOptions::ReadWrite.new)
363
+ opts = default_options_from_session session_name
364
+ execute do
365
+ service.begin_transaction session_name, tx_opts, options: opts
366
+ end
367
+ end
368
+
369
+ def create_snapshot session_name, strong: nil, timestamp: nil,
370
+ staleness: nil
371
+ tx_opts = Google::Spanner::V1::TransactionOptions.new(read_only:
372
+ Google::Spanner::V1::TransactionOptions::ReadOnly.new({
373
+ strong: strong,
374
+ read_timestamp: Convert.time_to_timestamp(timestamp),
375
+ exact_staleness: Convert.number_to_duration(staleness),
376
+ return_read_timestamp: true
377
+ }.delete_if { |_, v| v.nil? }))
378
+ opts = default_options_from_session session_name
379
+ execute do
380
+ service.begin_transaction session_name, tx_opts, options: opts
381
+ end
382
+ end
383
+
384
+ def inspect
385
+ "#{self.class}(#{@project})"
386
+ end
387
+
388
+ protected
389
+
390
+ def key_set keys
391
+ return Google::Spanner::V1::KeySet.new(all: true) if keys.nil?
392
+ keys = [keys] unless keys.is_a? Array
393
+ return Google::Spanner::V1::KeySet.new(all: true) if keys.empty?
394
+ if keys_are_ranges? keys
395
+ keys = [keys] unless keys.is_a? Array
396
+ key_ranges = keys.map do |r|
397
+ Convert.to_key_range(r)
398
+ end
399
+ return Google::Spanner::V1::KeySet.new(ranges: key_ranges)
400
+ end
401
+ key_list = Array(keys).map do |i|
402
+ Convert.raw_to_value(Array(i)).list_value
403
+ end
404
+ Google::Spanner::V1::KeySet.new keys: key_list
405
+ end
406
+
407
+ def keys_are_ranges? keys
408
+ keys.each do |key|
409
+ return true if key.is_a? ::Range
410
+ return true if key.is_a? Google::Cloud::Spanner::Range
411
+ end
412
+ false
413
+ end
414
+
415
+ def default_options_from_session session_name
416
+ default_prefix = session_name.split("/sessions/").first
417
+ Google::Gax::CallOptions.new kwargs: \
418
+ { "google-cloud-resource-prefix" => default_prefix }
419
+ end
420
+
421
+ def project_path
422
+ Admin::Instance::V1::InstanceAdminClient.project_path project
423
+ end
424
+
425
+ def instance_path name
426
+ return name if name.to_s.include? "/"
427
+ Admin::Instance::V1::InstanceAdminClient.instance_path(
428
+ project, name)
429
+ end
430
+
431
+ def instance_config_path name
432
+ return name if name.to_s.include? "/"
433
+ Admin::Instance::V1::InstanceAdminClient.instance_config_path(
434
+ project, name.to_s)
435
+ end
436
+
437
+ def database_path instance_id, database_id
438
+ Admin::Database::V1::DatabaseAdminClient.database_path(
439
+ project, instance_id, database_id)
440
+ end
441
+
442
+ def session_path instance_id, database_id, session_id
443
+ V1::SpannerClient.session_path(
444
+ project, instance_id, database_id, session_id)
445
+ end
446
+
447
+ def execute
448
+ yield
449
+ rescue Google::Gax::GaxError => e
450
+ # GaxError wraps BadStatus, but exposes it as #cause
451
+ raise Google::Cloud::Error.from_error(e.cause)
452
+ rescue GRPC::BadStatus => e
453
+ raise Google::Cloud::Error.from_error(e)
454
+ end
455
+ end
456
+ end
457
+ end
458
+ end