google-cloud-spanner 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/google-cloud-spanner.rb +106 -0
- data/lib/google/cloud/spanner.rb +382 -0
- data/lib/google/cloud/spanner/admin/database/v1.rb +17 -0
- data/lib/google/cloud/spanner/admin/database/v1/database_admin_client.rb +703 -0
- data/lib/google/cloud/spanner/admin/database/v1/database_admin_client_config.json +73 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/iam/v1/policy.rb +139 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/protobuf/any.rb +114 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/rpc/status.rb +83 -0
- data/lib/google/cloud/spanner/admin/database/v1/doc/google/spanner/admin/database/v1/spanner_database_admin.rb +188 -0
- data/lib/google/cloud/spanner/admin/instance/v1.rb +17 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/iam/v1/policy.rb +139 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/any.rb +114 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/protobuf/field_mask.rb +223 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/rpc/status.rb +83 -0
- data/lib/google/cloud/spanner/admin/instance/v1/doc/google/spanner/admin/instance/v1/spanner_instance_admin.rb +268 -0
- data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client.rb +868 -0
- data/lib/google/cloud/spanner/admin/instance/v1/instance_admin_client_config.json +78 -0
- data/lib/google/cloud/spanner/client.rb +1034 -0
- data/lib/google/cloud/spanner/commit.rb +351 -0
- data/lib/google/cloud/spanner/convert.rb +311 -0
- data/lib/google/cloud/spanner/credentials.rb +32 -0
- data/lib/google/cloud/spanner/data.rb +199 -0
- data/lib/google/cloud/spanner/database.rb +377 -0
- data/lib/google/cloud/spanner/database/job.rb +179 -0
- data/lib/google/cloud/spanner/database/list.rb +171 -0
- data/lib/google/cloud/spanner/errors.rb +73 -0
- data/lib/google/cloud/spanner/fields.rb +252 -0
- data/lib/google/cloud/spanner/instance.rb +472 -0
- data/lib/google/cloud/spanner/instance/config.rb +99 -0
- data/lib/google/cloud/spanner/instance/config/list.rb +171 -0
- data/lib/google/cloud/spanner/instance/job.rb +197 -0
- data/lib/google/cloud/spanner/instance/list.rb +167 -0
- data/lib/google/cloud/spanner/policy.rb +201 -0
- data/lib/google/cloud/spanner/pool.rb +279 -0
- data/lib/google/cloud/spanner/project.rb +480 -0
- data/lib/google/cloud/spanner/range.rb +99 -0
- data/lib/google/cloud/spanner/results.rb +280 -0
- data/lib/google/cloud/spanner/service.rb +458 -0
- data/lib/google/cloud/spanner/session.rb +565 -0
- data/lib/google/cloud/spanner/snapshot.rb +260 -0
- data/lib/google/cloud/spanner/transaction.rb +533 -0
- data/lib/google/cloud/spanner/v1.rb +17 -0
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/duration.rb +77 -0
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/struct.rb +73 -0
- data/lib/google/cloud/spanner/v1/doc/google/protobuf/timestamp.rb +81 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/keys.rb +148 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/mutation.rb +80 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/query_plan.rb +120 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/result_set.rb +175 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/spanner.rb +206 -0
- data/lib/google/cloud/spanner/v1/doc/google/spanner/v1/transaction.rb +351 -0
- data/lib/google/cloud/spanner/v1/spanner_client.rb +850 -0
- data/lib/google/cloud/spanner/v1/spanner_client_config.json +78 -0
- data/lib/google/cloud/spanner/version.rb +22 -0
- data/lib/google/spanner/admin/database/v1/spanner_database_admin_pb.rb +85 -0
- data/lib/google/spanner/admin/database/v1/spanner_database_admin_services_pb.rb +95 -0
- data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_pb.rb +106 -0
- data/lib/google/spanner/admin/instance/v1/spanner_instance_admin_services_pb.rb +180 -0
- data/lib/google/spanner/v1/keys_pb.rb +33 -0
- data/lib/google/spanner/v1/mutation_pb.rb +38 -0
- data/lib/google/spanner/v1/query_plan_pb.rb +47 -0
- data/lib/google/spanner/v1/result_set_pb.rb +43 -0
- data/lib/google/spanner/v1/spanner_pb.rb +90 -0
- data/lib/google/spanner/v1/spanner_services_pb.rb +131 -0
- data/lib/google/spanner/v1/transaction_pb.rb +51 -0
- data/lib/google/spanner/v1/type_pb.rb +43 -0
- 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
|