activerecord-spanner-adapter 1.0.1 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/CODEOWNERS +1 -1
- data/.github/sync-repo-settings.yaml +2 -2
- data/.github/workflows/acceptance-tests-on-emulator.yaml +10 -6
- data/.github/workflows/acceptance-tests-on-production.yaml +1 -1
- data/.github/workflows/ci.yaml +10 -8
- data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +14 -5
- data/.github/workflows/nightly-acceptance-tests-on-production.yaml +2 -2
- data/.github/workflows/nightly-unit-tests.yaml +14 -5
- data/.github/workflows/release-please.yml +2 -2
- data/.github/workflows/rubocop.yaml +3 -3
- data/.kokoro/release.sh +1 -3
- data/.release-please-manifest.json +1 -1
- data/.toys/release.rb +8 -2
- data/CHANGELOG.md +18 -0
- data/Gemfile +5 -1
- data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +12 -8
- data/acceptance/cases/models/insert_all_test.rb +150 -0
- data/acceptance/cases/transactions/optimistic_locking_test.rb +5 -0
- data/acceptance/cases/type/all_types_test.rb +10 -13
- data/acceptance/cases/type/json_test.rb +0 -2
- data/acceptance/models/album.rb +7 -2
- data/acceptance/models/singer.rb +2 -2
- data/acceptance/models/track.rb +5 -2
- data/acceptance/schema/schema.rb +2 -4
- data/acceptance/test_helper.rb +1 -1
- data/activerecord-spanner-adapter.gemspec +1 -1
- data/examples/snippets/interleaved-tables/README.md +164 -0
- data/examples/snippets/interleaved-tables/Rakefile +13 -0
- data/examples/snippets/interleaved-tables/application.rb +126 -0
- data/examples/snippets/interleaved-tables/config/database.yml +8 -0
- data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +44 -0
- data/examples/snippets/interleaved-tables/db/schema.rb +32 -0
- data/examples/snippets/interleaved-tables/db/seeds.rb +40 -0
- data/examples/snippets/interleaved-tables/models/album.rb +20 -0
- data/examples/snippets/interleaved-tables/models/singer.rb +18 -0
- data/examples/snippets/interleaved-tables/models/track.rb +28 -0
- data/lib/active_record/connection_adapters/spanner/schema_creation.rb +10 -4
- data/lib/active_record/connection_adapters/spanner_adapter.rb +64 -31
- data/lib/activerecord_spanner_adapter/base.rb +150 -17
- data/lib/activerecord_spanner_adapter/connection.rb +1 -1
- data/lib/activerecord_spanner_adapter/relation.rb +21 -0
- data/lib/activerecord_spanner_adapter/transaction.rb +4 -4
- data/lib/activerecord_spanner_adapter/version.rb +1 -1
- data/lib/arel/visitors/spanner.rb +10 -0
- metadata +25 -7
@@ -8,6 +8,7 @@ require "securerandom"
|
|
8
8
|
require "google/cloud/spanner"
|
9
9
|
require "spanner_client_ext"
|
10
10
|
require "active_record/connection_adapters/abstract_adapter"
|
11
|
+
require "active_record/connection_adapters/abstract/connection_pool"
|
11
12
|
require "active_record/connection_adapters/spanner/database_statements"
|
12
13
|
require "active_record/connection_adapters/spanner/schema_statements"
|
13
14
|
require "active_record/connection_adapters/spanner/schema_cache"
|
@@ -43,9 +44,9 @@ module ActiveRecord
|
|
43
44
|
module ConnectionAdapters
|
44
45
|
module AbstractPool
|
45
46
|
def get_schema_cache connection
|
46
|
-
|
47
|
-
|
48
|
-
|
47
|
+
self.schema_cache ||= SpannerSchemaCache.new connection
|
48
|
+
schema_cache.connection = connection
|
49
|
+
schema_cache
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
@@ -178,41 +179,73 @@ module ActiveRecord
|
|
178
179
|
Arel::Visitors::Spanner.new self
|
179
180
|
end
|
180
181
|
|
181
|
-
|
182
|
+
def build_insert_sql insert
|
183
|
+
if current_spanner_transaction&.isolation == :buffered_mutations
|
184
|
+
raise "ActiveRecordSpannerAdapter does not support insert_sql with buffered_mutations transaction."
|
185
|
+
end
|
182
186
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
m, %r{^BYTES}i, ActiveRecord::Type::Spanner::Bytes
|
187
|
-
)
|
188
|
-
m.register_type "DATE", Type::Date.new
|
189
|
-
m.register_type "FLOAT64", Type::Float.new
|
190
|
-
m.register_type "NUMERIC", Type::Decimal.new
|
191
|
-
m.register_type "INT64", Type::Integer.new(limit: 8)
|
192
|
-
register_class_with_limit m, %r{^STRING}i, Type::String
|
193
|
-
m.register_type "TIMESTAMP", ActiveRecord::Type::Spanner::Time.new
|
194
|
-
m.register_type "JSON", ActiveRecord::Type::Json.new
|
187
|
+
if insert.skip_duplicates? || insert.update_duplicates?
|
188
|
+
raise NotImplementedError, "CloudSpanner does not support skip_duplicates and update_duplicates."
|
189
|
+
end
|
195
190
|
|
196
|
-
|
191
|
+
values_list, = insert.values_list
|
192
|
+
"INSERT #{insert.into} #{values_list}"
|
197
193
|
end
|
198
194
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
m
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
195
|
+
module TypeMapBuilder
|
196
|
+
private
|
197
|
+
|
198
|
+
def initialize_type_map m = type_map
|
199
|
+
m.register_type "BOOL", Type::Boolean.new
|
200
|
+
register_class_with_limit(
|
201
|
+
m, %r{^BYTES}i, ActiveRecord::Type::Spanner::Bytes
|
202
|
+
)
|
203
|
+
m.register_type "DATE", Type::Date.new
|
204
|
+
m.register_type "FLOAT64", Type::Float.new
|
205
|
+
m.register_type "NUMERIC", Type::Decimal.new
|
206
|
+
m.register_type "INT64", Type::Integer.new(limit: 8)
|
207
|
+
register_class_with_limit m, %r{^STRING}i, Type::String
|
208
|
+
m.register_type "TIMESTAMP", ActiveRecord::Type::Spanner::Time.new
|
209
|
+
m.register_type "JSON", ActiveRecord::Type::Json.new
|
210
|
+
|
211
|
+
register_array_types m
|
212
|
+
end
|
213
|
+
|
214
|
+
def register_array_types m
|
215
|
+
m.register_type %r{^ARRAY<BOOL>}i, Type::Spanner::Array.new(Type::Boolean.new)
|
216
|
+
m.register_type %r{^ARRAY<BYTES\((MAX|d+)\)>}i,
|
217
|
+
Type::Spanner::Array.new(ActiveRecord::Type::Spanner::Bytes.new)
|
218
|
+
m.register_type %r{^ARRAY<DATE>}i, Type::Spanner::Array.new(Type::Date.new)
|
219
|
+
m.register_type %r{^ARRAY<FLOAT64>}i, Type::Spanner::Array.new(Type::Float.new)
|
220
|
+
m.register_type %r{^ARRAY<NUMERIC>}i, Type::Spanner::Array.new(Type::Decimal.new)
|
221
|
+
m.register_type %r{^ARRAY<INT64>}i, Type::Spanner::Array.new(Type::Integer.new(limit: 8))
|
222
|
+
m.register_type %r{^ARRAY<STRING\((MAX|d+)\)>}i, Type::Spanner::Array.new(Type::String.new)
|
223
|
+
m.register_type %r{^ARRAY<TIMESTAMP>}i, Type::Spanner::Array.new(ActiveRecord::Type::Spanner::Time.new)
|
224
|
+
m.register_type %r{^ARRAY<JSON>}i, Type::Spanner::Array.new(ActiveRecord::Type::Json.new)
|
225
|
+
end
|
226
|
+
|
227
|
+
def extract_limit sql_type
|
228
|
+
value = /\((.*)\)/.match sql_type
|
229
|
+
return unless value
|
230
|
+
|
231
|
+
value[1] == "MAX" ? "MAX" : value[1].to_i
|
232
|
+
end
|
209
233
|
end
|
210
234
|
|
211
|
-
|
212
|
-
|
213
|
-
|
235
|
+
if ActiveRecord::VERSION::MAJOR >= 7
|
236
|
+
class << self
|
237
|
+
include TypeMapBuilder
|
238
|
+
end
|
239
|
+
|
240
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map m }
|
241
|
+
|
242
|
+
private
|
214
243
|
|
215
|
-
|
244
|
+
def type_map
|
245
|
+
TYPE_MAP
|
246
|
+
end
|
247
|
+
else
|
248
|
+
include TypeMapBuilder
|
216
249
|
end
|
217
250
|
|
218
251
|
def translate_exception exception, message:, sql:, binds:
|
@@ -4,6 +4,8 @@
|
|
4
4
|
# license that can be found in the LICENSE file or at
|
5
5
|
# https://opensource.org/licenses/MIT.
|
6
6
|
|
7
|
+
require "activerecord_spanner_adapter/relation"
|
8
|
+
|
7
9
|
module ActiveRecord
|
8
10
|
class TableMetadata # :nodoc:
|
9
11
|
# This attr_reader is private in ActiveRecord 6.0.x and public in 6.1.x. This makes sure it is always available in
|
@@ -42,35 +44,137 @@ module ActiveRecord
|
|
42
44
|
end
|
43
45
|
|
44
46
|
def self._insert_record values
|
45
|
-
return super unless buffered_mutations?
|
47
|
+
return super unless buffered_mutations? || (primary_key && values.is_a?(Hash))
|
46
48
|
|
47
|
-
|
48
|
-
primary_key_value = nil
|
49
|
+
return _buffer_record values, :insert if buffered_mutations?
|
49
50
|
|
50
|
-
|
51
|
-
|
51
|
+
primary_key_value =
|
52
|
+
if primary_key.is_a? Array
|
53
|
+
_set_composite_primary_key_values primary_key, values
|
54
|
+
else
|
55
|
+
_set_single_primary_key_value primary_key, values
|
56
|
+
end
|
57
|
+
if ActiveRecord::VERSION::MAJOR >= 7
|
58
|
+
im = Arel::InsertManager.new arel_table
|
59
|
+
im.insert(values.transform_keys { |name| arel_table[name] })
|
60
|
+
else
|
61
|
+
im = arel_table.compile_insert _substitute_values(values)
|
62
|
+
end
|
63
|
+
connection.insert(im, "#{self} Create", primary_key || false, primary_key_value)
|
64
|
+
end
|
52
65
|
|
53
|
-
|
54
|
-
|
55
|
-
|
66
|
+
def self._upsert_record values
|
67
|
+
_buffer_record values, :insert_or_update
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.insert_all _attributes, _returning: nil, _unique_by: nil
|
71
|
+
raise NotImplementedError, "Cloud Spanner does not support skip_duplicates."
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.insert_all! attributes, returning: nil
|
75
|
+
return super unless spanner_adapter?
|
76
|
+
return super if active_transaction? && !buffered_mutations?
|
77
|
+
|
78
|
+
# This might seem inefficient, but is actually not, as it is only buffering a mutation locally.
|
79
|
+
# The mutations will be sent as one batch when the transaction is committed.
|
80
|
+
if active_transaction?
|
81
|
+
attributes.each do |record|
|
82
|
+
_insert_record record
|
83
|
+
end
|
84
|
+
else
|
85
|
+
transaction isolation: :buffered_mutations do
|
86
|
+
attributes.each do |record|
|
87
|
+
_insert_record record
|
88
|
+
end
|
56
89
|
end
|
57
90
|
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.upsert_all attributes, returning: nil, unique_by: nil
|
94
|
+
return super unless spanner_adapter?
|
95
|
+
if active_transaction? && !buffered_mutations?
|
96
|
+
raise NotImplementedError, "Cloud Spanner does not support upsert using DML. " \
|
97
|
+
"Use upsert outside a transaction block or in a transaction " \
|
98
|
+
"block with isolation: :buffered_mutations"
|
99
|
+
end
|
100
|
+
|
101
|
+
# This might seem inefficient, but is actually not, as it is only buffering a mutation locally.
|
102
|
+
# The mutations will be sent as one batch when the transaction is committed.
|
103
|
+
if active_transaction?
|
104
|
+
attributes.each do |record|
|
105
|
+
_upsert_record record
|
106
|
+
end
|
107
|
+
else
|
108
|
+
transaction isolation: :buffered_mutations do
|
109
|
+
attributes.each do |record|
|
110
|
+
_upsert_record record
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def self._buffer_record values, method
|
117
|
+
primary_key_value =
|
118
|
+
if primary_key.is_a? Array
|
119
|
+
_set_composite_primary_key_values primary_key, values
|
120
|
+
else
|
121
|
+
_set_single_primary_key_value primary_key, values
|
122
|
+
end
|
58
123
|
|
59
124
|
metadata = TableMetadata.new self, arel_table
|
60
125
|
columns, grpc_values = _create_grpc_values_for_insert metadata, values
|
61
126
|
|
127
|
+
write = Google::Cloud::Spanner::V1::Mutation::Write.new(
|
128
|
+
table: arel_table.name,
|
129
|
+
columns: columns,
|
130
|
+
values: [grpc_values.list_value]
|
131
|
+
)
|
62
132
|
mutation = Google::Cloud::Spanner::V1::Mutation.new(
|
63
|
-
|
64
|
-
table: arel_table.name,
|
65
|
-
columns: columns,
|
66
|
-
values: [grpc_values.list_value]
|
67
|
-
)
|
133
|
+
"#{method}": write
|
68
134
|
)
|
135
|
+
|
69
136
|
connection.current_spanner_transaction.buffer mutation
|
70
137
|
|
71
138
|
primary_key_value
|
72
139
|
end
|
73
140
|
|
141
|
+
def self._set_composite_primary_key_values primary_key, values
|
142
|
+
primary_key_value = []
|
143
|
+
primary_key.each do |col|
|
144
|
+
value = values[col]
|
145
|
+
|
146
|
+
if !value && prefetch_primary_key?
|
147
|
+
value =
|
148
|
+
if ActiveRecord::VERSION::MAJOR >= 7
|
149
|
+
ActiveModel::Attribute.from_database col, next_sequence_value, ActiveModel::Type::BigInteger.new
|
150
|
+
else
|
151
|
+
next_sequence_value
|
152
|
+
end
|
153
|
+
values[col] = value
|
154
|
+
end
|
155
|
+
if value.is_a? ActiveModel::Attribute
|
156
|
+
value = value.value
|
157
|
+
end
|
158
|
+
primary_key_value.append value
|
159
|
+
end
|
160
|
+
primary_key_value
|
161
|
+
end
|
162
|
+
|
163
|
+
def self._set_single_primary_key_value primary_key, values
|
164
|
+
primary_key_value = values[primary_key] || values[primary_key.to_sym]
|
165
|
+
|
166
|
+
if !primary_key_value && prefetch_primary_key?
|
167
|
+
primary_key_value = next_sequence_value
|
168
|
+
if ActiveRecord::VERSION::MAJOR >= 7
|
169
|
+
values[primary_key] = ActiveModel::Attribute.from_database primary_key, primary_key_value,
|
170
|
+
ActiveModel::Type::BigInteger.new
|
171
|
+
else
|
172
|
+
values[primary_key] = primary_key_value
|
173
|
+
end
|
174
|
+
end
|
175
|
+
primary_key_value
|
176
|
+
end
|
177
|
+
|
74
178
|
# Deletes all records of this class. This method will use mutations instead of DML if there is no active
|
75
179
|
# transaction, or if the active transaction has been created with the option isolation: :buffered_mutations.
|
76
180
|
def self.delete_all
|
@@ -87,6 +191,14 @@ module ActiveRecord
|
|
87
191
|
!(current_transaction.nil? || current_transaction.is_a?(ConnectionAdapters::NullTransaction))
|
88
192
|
end
|
89
193
|
|
194
|
+
def self.unwrap_attribute attr_or_value
|
195
|
+
if attr_or_value.is_a? ActiveModel::Attribute
|
196
|
+
attr_or_value.value
|
197
|
+
else
|
198
|
+
attr_or_value
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
90
202
|
# Updates the given attributes of the object in the database. This method will use mutations instead
|
91
203
|
# of DML if there is no active transaction, or if the active transaction has been created with the option
|
92
204
|
# isolation: :buffered_mutations.
|
@@ -117,6 +229,7 @@ module ActiveRecord
|
|
117
229
|
serialized_values = []
|
118
230
|
columns = []
|
119
231
|
values.each_pair do |k, v|
|
232
|
+
v = unwrap_attribute v
|
120
233
|
type = metadata.type k
|
121
234
|
serialized_values << (type.method(:serialize).arity < 0 ? type.serialize(v, :mutation) : type.serialize(v))
|
122
235
|
columns << metadata.arel_table[k].name
|
@@ -168,6 +281,7 @@ module ActiveRecord
|
|
168
281
|
all_values.each do |h|
|
169
282
|
h.each_pair do |k, v|
|
170
283
|
type = metadata.type k
|
284
|
+
v = self.class.unwrap_attribute v
|
171
285
|
has_serialize_options = type.method(:serialize).arity < 0
|
172
286
|
all_serialized_values << (has_serialize_options ? type.serialize(v, :mutation) : type.serialize(v))
|
173
287
|
all_columns << metadata.arel_table[k].name
|
@@ -222,15 +336,34 @@ module ActiveRecord
|
|
222
336
|
serialized_values
|
223
337
|
end
|
224
338
|
|
225
|
-
def _execute_version_check attempted_action
|
339
|
+
def _execute_version_check attempted_action # rubocop:disable Metrics/AbcSize
|
226
340
|
locking_column = self.class.locking_column
|
227
341
|
previous_lock_value = read_attribute_before_type_cast locking_column
|
228
342
|
|
343
|
+
primary_key = self.class.primary_key
|
344
|
+
if primary_key.is_a? Array
|
345
|
+
pk_sql = ""
|
346
|
+
params = {}
|
347
|
+
param_types = {}
|
348
|
+
id = id_in_database
|
349
|
+
primary_key.each_with_index do |col, idx|
|
350
|
+
pk_sql.concat "`#{col}`=@id#{idx}"
|
351
|
+
pk_sql.concat " AND " if idx < primary_key.length - 1
|
352
|
+
|
353
|
+
params["id#{idx}"] = id[idx]
|
354
|
+
param_types["id#{idx}"] = :INT64
|
355
|
+
end
|
356
|
+
params["lock_version"] = previous_lock_value
|
357
|
+
param_types["lock_version"] = :INT64
|
358
|
+
else
|
359
|
+
pk_sql = "`#{self.class.primary_key}` = @id"
|
360
|
+
params = { "id" => id_in_database, "lock_version" => previous_lock_value }
|
361
|
+
param_types = { "id" => :INT64, "lock_version" => :INT64 }
|
362
|
+
end
|
363
|
+
|
229
364
|
# We need to check the version using a SELECT query, as a mutation cannot include a WHERE clause.
|
230
365
|
sql = "SELECT 1 FROM `#{self.class.arel_table.name}` " \
|
231
|
-
"WHERE
|
232
|
-
params = { "id" => id_in_database, "lock_version" => previous_lock_value }
|
233
|
-
param_types = { "id" => :INT64, "lock_version" => :INT64 }
|
366
|
+
"WHERE #{pk_sql} AND `#{locking_column}` = @lock_version"
|
234
367
|
locked_row = self.class.connection.raw_connection.execute_query sql, params: params, types: param_types
|
235
368
|
raise ActiveRecord::StaleObjectError.new(self, attempted_action) unless locked_row.rows.any?
|
236
369
|
end
|
@@ -251,7 +251,7 @@ module ActiveRecordSpannerAdapter
|
|
251
251
|
# transaction fails, as that also means that no transaction id was returned.
|
252
252
|
def create_transaction_after_failed_first_statement original_error
|
253
253
|
transaction = current_transaction.force_begin_read_write
|
254
|
-
Google::Spanner::V1::TransactionSelector.new id: transaction.transaction_id
|
254
|
+
Google::Cloud::Spanner::V1::TransactionSelector.new id: transaction.transaction_id
|
255
255
|
rescue Google::Cloud::Error
|
256
256
|
# Raise the original error if the BeginTransaction RPC also fails.
|
257
257
|
raise original_error
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Copyright 2022 Google LLC
|
2
|
+
#
|
3
|
+
# Use of this source code is governed by an MIT-style
|
4
|
+
# license that can be found in the LICENSE file or at
|
5
|
+
# https://opensource.org/licenses/MIT.
|
6
|
+
|
7
|
+
module ActiveRecord
|
8
|
+
module CpkExtension
|
9
|
+
def cpk_subquery stmt
|
10
|
+
return super unless spanner_adapter?
|
11
|
+
# The composite_primary_key gem will by default generate WHERE clauses using an IN clause with a multi-column
|
12
|
+
# sub select, e.g.: SELECT * FROM my_table WHERE (id1, id2) IN (SELECT id1, id2 FROM my_table WHERE ...).
|
13
|
+
# This is not supported in Cloud Spanner. Instead, composite_primary_key should generate an EXISTS clause.
|
14
|
+
cpk_exists_subquery stmt
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Relation
|
19
|
+
prepend CpkExtension
|
20
|
+
end
|
21
|
+
end
|
@@ -55,9 +55,9 @@ module ActiveRecordSpannerAdapter
|
|
55
55
|
when :pdml
|
56
56
|
@grpc_transaction = @connection.session.create_pdml
|
57
57
|
else
|
58
|
-
@begin_transaction_selector = Google::Spanner::V1::TransactionSelector.new \
|
59
|
-
begin: Google::Spanner::V1::TransactionOptions.new(
|
60
|
-
read_write: Google::Spanner::V1::TransactionOptions::ReadWrite.new
|
58
|
+
@begin_transaction_selector = Google::Cloud::Spanner::V1::TransactionSelector.new \
|
59
|
+
begin: Google::Cloud::Spanner::V1::TransactionOptions.new(
|
60
|
+
read_write: Google::Cloud::Spanner::V1::TransactionOptions::ReadWrite.new
|
61
61
|
)
|
62
62
|
|
63
63
|
end
|
@@ -143,7 +143,7 @@ module ActiveRecordSpannerAdapter
|
|
143
143
|
|
144
144
|
# Use the transaction that has been started by a BeginTransaction RPC or returned by a
|
145
145
|
# statement, if present.
|
146
|
-
return Google::Spanner::V1::TransactionSelector.new id: @grpc_transaction.transaction_id \
|
146
|
+
return Google::Cloud::Spanner::V1::TransactionSelector.new id: @grpc_transaction.transaction_id \
|
147
147
|
if @grpc_transaction
|
148
148
|
|
149
149
|
# Return a transaction selector that will instruct the statement to also start a transaction
|
@@ -98,6 +98,16 @@ module Arel # :nodoc: all
|
|
98
98
|
end
|
99
99
|
end
|
100
100
|
|
101
|
+
# For ActiveRecord 7.0
|
102
|
+
def visit_ActiveModel_Attribute o, collector
|
103
|
+
# Do not generate a query parameter if the value should be set to the PENDING_COMMIT_TIMESTAMP(), as that is
|
104
|
+
# not supported as a parameter value by Cloud Spanner.
|
105
|
+
return collector << "PENDING_COMMIT_TIMESTAMP()" \
|
106
|
+
if o.type.is_a?(ActiveRecord::Type::Spanner::Time) && o.value == :commit_timestamp
|
107
|
+
collector.add_bind(o, &bind_block)
|
108
|
+
end
|
109
|
+
|
110
|
+
# For ActiveRecord 6.x
|
101
111
|
def visit_Arel_Nodes_BindParam o, collector
|
102
112
|
# Do not generate a query parameter if the value should be set to the PENDING_COMMIT_TIMESTAMP(), as that is
|
103
113
|
# not supported as a parameter value by Cloud Spanner.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-spanner-adapter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Google LLC
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-08-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: google-cloud-spanner
|
@@ -28,16 +28,22 @@ dependencies:
|
|
28
28
|
name: activerecord
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 6.0.0
|
34
|
+
- - "<"
|
32
35
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
36
|
+
version: '7.1'
|
34
37
|
type: :runtime
|
35
38
|
prerelease: false
|
36
39
|
version_requirements: !ruby/object:Gem::Requirement
|
37
40
|
requirements:
|
38
|
-
- - "
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 6.0.0
|
44
|
+
- - "<"
|
39
45
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
46
|
+
version: '7.1'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
48
|
name: autotest-suffix
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -264,6 +270,7 @@ files:
|
|
264
270
|
- acceptance/cases/migration/rename_column_test.rb
|
265
271
|
- acceptance/cases/models/calculation_query_test.rb
|
266
272
|
- acceptance/cases/models/generated_column_test.rb
|
273
|
+
- acceptance/cases/models/insert_all_test.rb
|
267
274
|
- acceptance/cases/models/mutation_test.rb
|
268
275
|
- acceptance/cases/models/query_test.rb
|
269
276
|
- acceptance/cases/sessions/session_not_found_test.rb
|
@@ -380,6 +387,16 @@ files:
|
|
380
387
|
- examples/snippets/hints/db/seeds.rb
|
381
388
|
- examples/snippets/hints/models/album.rb
|
382
389
|
- examples/snippets/hints/models/singer.rb
|
390
|
+
- examples/snippets/interleaved-tables/README.md
|
391
|
+
- examples/snippets/interleaved-tables/Rakefile
|
392
|
+
- examples/snippets/interleaved-tables/application.rb
|
393
|
+
- examples/snippets/interleaved-tables/config/database.yml
|
394
|
+
- examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb
|
395
|
+
- examples/snippets/interleaved-tables/db/schema.rb
|
396
|
+
- examples/snippets/interleaved-tables/db/seeds.rb
|
397
|
+
- examples/snippets/interleaved-tables/models/album.rb
|
398
|
+
- examples/snippets/interleaved-tables/models/singer.rb
|
399
|
+
- examples/snippets/interleaved-tables/models/track.rb
|
383
400
|
- examples/snippets/migrations/README.md
|
384
401
|
- examples/snippets/migrations/Rakefile
|
385
402
|
- examples/snippets/migrations/application.rb
|
@@ -485,6 +502,7 @@ files:
|
|
485
502
|
- lib/activerecord_spanner_adapter/index/column.rb
|
486
503
|
- lib/activerecord_spanner_adapter/information_schema.rb
|
487
504
|
- lib/activerecord_spanner_adapter/primary_key.rb
|
505
|
+
- lib/activerecord_spanner_adapter/relation.rb
|
488
506
|
- lib/activerecord_spanner_adapter/table.rb
|
489
507
|
- lib/activerecord_spanner_adapter/table/column.rb
|
490
508
|
- lib/activerecord_spanner_adapter/transaction.rb
|
@@ -512,7 +530,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
512
530
|
- !ruby/object:Gem::Version
|
513
531
|
version: '0'
|
514
532
|
requirements: []
|
515
|
-
rubygems_version: 3.3.
|
533
|
+
rubygems_version: 3.3.14
|
516
534
|
signing_key:
|
517
535
|
specification_version: 4
|
518
536
|
summary: Rails ActiveRecord connector for Google Spanner Database
|