activerecord-spanner-adapter 1.2.1 → 1.3.1
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 +4 -4
- data/.github/CODEOWNERS +1 -1
- data/.github/blunderbuss.yml +1 -1
- data/.release-please-manifest.json +1 -1
- data/CHANGELOG.md +29 -0
- data/README.md +7 -2
- data/acceptance/cases/migration/schema_dumper_test.rb +69 -0
- data/acceptance/cases/models/generated_column_test.rb +21 -7
- data/acceptance/cases/models/interleave_test.rb +36 -0
- data/acceptance/cases/models/logging_test.rb +53 -0
- data/acceptance/cases/models/query_test.rb +6 -1
- data/acceptance/cases/tasks/database_tasks_test.rb +407 -0
- data/acceptance/models/album_partial_disabled.rb +17 -0
- data/acceptance/schema/schema.rb +139 -134
- data/acceptance/test_helper.rb +2 -0
- data/examples/snippets/array-data-type/db/schema.rb +8 -3
- data/examples/snippets/bulk-insert/db/schema.rb +9 -4
- data/examples/snippets/commit-timestamp/db/schema.rb +11 -6
- data/examples/snippets/create-records/db/schema.rb +9 -4
- data/examples/snippets/date-data-type/db/schema.rb +8 -3
- data/examples/snippets/generated-column/db/schema.rb +6 -1
- data/examples/snippets/hints/db/schema.rb +6 -1
- data/examples/snippets/interleaved-tables/README.md +2 -2
- data/examples/snippets/interleaved-tables/db/schema.rb +5 -0
- data/examples/snippets/migrations/db/schema.rb +10 -5
- data/examples/snippets/mutations/db/schema.rb +9 -4
- data/examples/snippets/optimistic-locking/db/schema.rb +9 -4
- data/examples/snippets/partitioned-dml/db/schema.rb +5 -0
- data/examples/snippets/quickstart/db/schema.rb +9 -4
- data/examples/snippets/read-only-transactions/db/schema.rb +5 -0
- data/examples/snippets/read-write-transactions/db/schema.rb +9 -4
- data/examples/snippets/stale-reads/db/schema.rb +5 -0
- data/examples/snippets/timestamp-data-type/db/schema.rb +8 -3
- data/lib/active_record/connection_adapters/spanner/column.rb +23 -0
- data/lib/active_record/connection_adapters/spanner/database_statements.rb +7 -4
- data/lib/active_record/connection_adapters/spanner/quoting.rb +9 -0
- data/lib/active_record/connection_adapters/spanner/schema_creation.rb +17 -4
- data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +11 -2
- data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +56 -0
- data/lib/active_record/connection_adapters/spanner/schema_statements.rb +56 -9
- data/lib/active_record/connection_adapters/spanner/type_metadata.rb +19 -4
- data/lib/active_record/connection_adapters/spanner_adapter.rb +11 -0
- data/lib/active_record/tasks/spanner_database_tasks.rb +18 -4
- data/lib/active_record/type/spanner/spanner_active_record_converter.rb +10 -0
- data/lib/active_record/type/spanner/time.rb +10 -3
- data/lib/activerecord_spanner_adapter/base.rb +41 -27
- data/lib/activerecord_spanner_adapter/connection.rb +8 -3
- data/lib/activerecord_spanner_adapter/information_schema.rb +52 -3
- data/lib/activerecord_spanner_adapter/table/column.rb +7 -2
- data/lib/activerecord_spanner_adapter/version.rb +1 -1
- data/lib/arel/visitors/spanner.rb +8 -2
- metadata +8 -2
@@ -141,29 +141,41 @@ module ActiveRecord
|
|
141
141
|
def self._set_composite_primary_key_values primary_key, values
|
142
142
|
primary_key_value = []
|
143
143
|
primary_key.each do |col|
|
144
|
-
|
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
|
144
|
+
primary_key_value.append _set_composite_primary_key_value col, values
|
159
145
|
end
|
160
146
|
primary_key_value
|
161
147
|
end
|
162
148
|
|
149
|
+
def self._set_composite_primary_key_value primary_key, values
|
150
|
+
value = values[primary_key]
|
151
|
+
|
152
|
+
if value.is_a? ActiveModel::Attribute
|
153
|
+
value = value.value
|
154
|
+
end
|
155
|
+
|
156
|
+
return value unless prefetch_primary_key?
|
157
|
+
|
158
|
+
if value.nil?
|
159
|
+
value = next_sequence_value
|
160
|
+
end
|
161
|
+
|
162
|
+
values[primary_key] =
|
163
|
+
if ActiveRecord::VERSION::MAJOR >= 7
|
164
|
+
ActiveModel::Attribute.from_database primary_key, value,
|
165
|
+
ActiveModel::Type::BigInteger.new
|
166
|
+
else
|
167
|
+
value
|
168
|
+
end
|
169
|
+
|
170
|
+
value
|
171
|
+
end
|
172
|
+
|
163
173
|
def self._set_single_primary_key_value primary_key, values
|
164
174
|
primary_key_value = values[primary_key] || values[primary_key.to_sym]
|
165
175
|
|
166
|
-
|
176
|
+
return primary_key_value unless prefetch_primary_key?
|
177
|
+
|
178
|
+
if primary_key_value.nil?
|
167
179
|
primary_key_value = next_sequence_value
|
168
180
|
if ActiveRecord::VERSION::MAJOR >= 7
|
169
181
|
values[primary_key] = ActiveModel::Attribute.from_database primary_key, primary_key_value,
|
@@ -172,6 +184,7 @@ module ActiveRecord
|
|
172
184
|
values[primary_key] = primary_key_value
|
173
185
|
end
|
174
186
|
end
|
187
|
+
|
175
188
|
primary_key_value
|
176
189
|
end
|
177
190
|
|
@@ -229,9 +242,10 @@ module ActiveRecord
|
|
229
242
|
serialized_values = []
|
230
243
|
columns = []
|
231
244
|
values.each_pair do |k, v|
|
232
|
-
|
233
|
-
|
234
|
-
|
245
|
+
serialized_values << ActiveRecord::Type::Spanner::SpannerActiveRecordConverter
|
246
|
+
.serialize_with_transaction_isolation_level(metadata.type(k),
|
247
|
+
unwrap_attribute(v),
|
248
|
+
:mutation)
|
235
249
|
columns << metadata.arel_table[k].name
|
236
250
|
end
|
237
251
|
[columns, Google::Protobuf::Value.new(list_value:
|
@@ -280,10 +294,10 @@ module ActiveRecord
|
|
280
294
|
all_columns = []
|
281
295
|
all_values.each do |h|
|
282
296
|
h.each_pair do |k, v|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
297
|
+
all_serialized_values << ActiveRecord::Type::Spanner::SpannerActiveRecordConverter
|
298
|
+
.serialize_with_transaction_isolation_level(metadata.type(k),
|
299
|
+
self.class.unwrap_attribute(v),
|
300
|
+
:mutation)
|
287
301
|
all_columns << metadata.arel_table[k].name
|
288
302
|
end
|
289
303
|
end
|
@@ -328,10 +342,10 @@ module ActiveRecord
|
|
328
342
|
def serialize_keys metadata, keys
|
329
343
|
serialized_values = []
|
330
344
|
keys.each do |key|
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
345
|
+
serialized_values << ActiveRecord::Type::Spanner::SpannerActiveRecordConverter
|
346
|
+
.serialize_with_transaction_isolation_level(metadata.type(key),
|
347
|
+
attribute_in_database(key),
|
348
|
+
:mutation)
|
335
349
|
end
|
336
350
|
serialized_values
|
337
351
|
end
|
@@ -36,6 +36,13 @@ module ActiveRecordSpannerAdapter
|
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
39
|
+
# Clears the cached information about the underlying information schemas.
|
40
|
+
# Call this method if you drop and recreate a database with the same name
|
41
|
+
# to prevent the cached information to be used for the new database.
|
42
|
+
def self.reset_information_schemas!
|
43
|
+
@information_schemas = {}
|
44
|
+
end
|
45
|
+
|
39
46
|
def self.information_schema config
|
40
47
|
@information_schemas ||= {}
|
41
48
|
@information_schemas[database_path(config)] ||= \
|
@@ -89,9 +96,7 @@ module ActiveRecordSpannerAdapter
|
|
89
96
|
@database ||= begin
|
90
97
|
database = spanner.database instance_id, database_id
|
91
98
|
unless database
|
92
|
-
raise ActiveRecord::NoDatabaseError
|
93
|
-
"#{spanner.project}/#{instance_id}/#{database_id}"
|
94
|
-
)
|
99
|
+
raise ActiveRecord::NoDatabaseError, "#{spanner.project}/#{instance_id}/#{database_id}"
|
95
100
|
end
|
96
101
|
database
|
97
102
|
end
|
@@ -62,27 +62,34 @@ module ActiveRecordSpannerAdapter
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def table_columns table_name, column_name: nil
|
65
|
-
sql = +"SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE,"
|
65
|
+
sql = +"SELECT COLUMN_NAME, SPANNER_TYPE, IS_NULLABLE, GENERATION_EXPRESSION,"
|
66
66
|
sql << " CAST(COLUMN_DEFAULT AS STRING) AS COLUMN_DEFAULT, ORDINAL_POSITION"
|
67
67
|
sql << " FROM INFORMATION_SCHEMA.COLUMNS"
|
68
68
|
sql << " WHERE TABLE_NAME=%<table_name>s"
|
69
69
|
sql << " AND COLUMN_NAME=%<column_name>s" if column_name
|
70
70
|
sql << " ORDER BY ORDINAL_POSITION ASC"
|
71
71
|
|
72
|
+
column_options = column_options table_name, column_name
|
72
73
|
execute_query(
|
73
74
|
sql,
|
74
75
|
table_name: table_name,
|
75
76
|
column_name: column_name
|
76
77
|
).map do |row|
|
77
78
|
type, limit = parse_type_and_limit row["SPANNER_TYPE"]
|
79
|
+
column_name = row["COLUMN_NAME"]
|
80
|
+
options = column_options[column_name]
|
81
|
+
|
78
82
|
Table::Column.new \
|
79
83
|
table_name,
|
80
|
-
|
84
|
+
column_name,
|
81
85
|
type,
|
82
86
|
limit: limit,
|
87
|
+
allow_commit_timestamp: options["allow_commit_timestamp"],
|
83
88
|
ordinal_position: row["ORDINAL_POSITION"],
|
84
89
|
nullable: row["IS_NULLABLE"] == "YES",
|
85
|
-
default: row["COLUMN_DEFAULT"]
|
90
|
+
default: row["COLUMN_DEFAULT"],
|
91
|
+
default_function: row["GENERATION_EXPRESSION"],
|
92
|
+
generated: row["GENERATION_EXPRESSION"].present?
|
86
93
|
end
|
87
94
|
end
|
88
95
|
|
@@ -240,6 +247,27 @@ module ActiveRecordSpannerAdapter
|
|
240
247
|
end
|
241
248
|
end
|
242
249
|
|
250
|
+
def check_constraints table_name
|
251
|
+
sql = <<~SQL.squish
|
252
|
+
SELECT tc.TABLE_NAME,
|
253
|
+
tc.CONSTRAINT_NAME,
|
254
|
+
cc.CHECK_CLAUSE
|
255
|
+
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
|
256
|
+
INNER JOIN INFORMATION_SCHEMA.CHECK_CONSTRAINTS cc ON tc.CONSTRAINT_NAME = cc.CONSTRAINT_NAME
|
257
|
+
WHERE tc.TABLE_NAME = %<table_name>s
|
258
|
+
AND tc.CONSTRAINT_TYPE = 'CHECK'
|
259
|
+
AND NOT (tc.CONSTRAINT_NAME LIKE 'CK_IS_NOT_NULL_%%' AND cc.CHECK_CLAUSE LIKE '%%IS NOT NULL')
|
260
|
+
SQL
|
261
|
+
|
262
|
+
rows = execute_query sql, table_name: table_name
|
263
|
+
|
264
|
+
rows.map do |row|
|
265
|
+
ActiveRecord::ConnectionAdapters::CheckConstraintDefinition.new(
|
266
|
+
table_name, row["CHECK_CLAUSE"], name: row["CONSTRAINT_NAME"]
|
267
|
+
)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
243
271
|
def parse_type_and_limit value
|
244
272
|
matched = /^([A-Z]*)\((.*)\)/.match value
|
245
273
|
return [value] unless matched
|
@@ -252,6 +280,27 @@ module ActiveRecordSpannerAdapter
|
|
252
280
|
|
253
281
|
private
|
254
282
|
|
283
|
+
def column_options table_name, column_name
|
284
|
+
sql = +"SELECT COLUMN_NAME, OPTION_NAME, OPTION_TYPE, OPTION_VALUE"
|
285
|
+
sql << " FROM INFORMATION_SCHEMA.COLUMN_OPTIONS"
|
286
|
+
sql << " WHERE TABLE_NAME=%<table_name>s"
|
287
|
+
sql << " AND COLUMN_NAME=%<column_name>s" if column_name
|
288
|
+
|
289
|
+
column_options = Hash.new { |h, k| h[k] = {} }
|
290
|
+
execute_query(
|
291
|
+
sql,
|
292
|
+
table_name: table_name,
|
293
|
+
column_name: column_name
|
294
|
+
).each_with_object(column_options) do |row, options|
|
295
|
+
next unless row["OPTION_TYPE"] == "BOOL"
|
296
|
+
|
297
|
+
col = row["COLUMN_NAME"]
|
298
|
+
opt = row["OPTION_NAME"]
|
299
|
+
value = row["OPTION_VALUE"] == "TRUE"
|
300
|
+
options[col][opt] = value
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
255
304
|
def execute_query sql, params = {}
|
256
305
|
params = params.transform_values { |v| quote v }
|
257
306
|
sql = format sql, params
|
@@ -8,7 +8,8 @@ module ActiveRecordSpannerAdapter
|
|
8
8
|
class Table
|
9
9
|
class Column
|
10
10
|
attr_accessor :table_name, :name, :type, :limit, :ordinal_position,
|
11
|
-
:allow_commit_timestamp, :default, :
|
11
|
+
:allow_commit_timestamp, :default, :default_function, :generated,
|
12
|
+
:primary_key
|
12
13
|
attr_writer :nullable
|
13
14
|
|
14
15
|
def initialize \
|
@@ -19,7 +20,9 @@ module ActiveRecordSpannerAdapter
|
|
19
20
|
ordinal_position: nil,
|
20
21
|
nullable: true,
|
21
22
|
allow_commit_timestamp: nil,
|
22
|
-
default: nil
|
23
|
+
default: nil,
|
24
|
+
default_function: nil,
|
25
|
+
generated: nil
|
23
26
|
@table_name = table_name.to_s
|
24
27
|
@name = name.to_s
|
25
28
|
@type = type
|
@@ -28,6 +31,8 @@ module ActiveRecordSpannerAdapter
|
|
28
31
|
@ordinal_position = ordinal_position
|
29
32
|
@allow_commit_timestamp = allow_commit_timestamp
|
30
33
|
@default = default
|
34
|
+
@default_function = default_function
|
35
|
+
@generated = generated == true
|
31
36
|
@primary_key = false
|
32
37
|
end
|
33
38
|
|
@@ -30,10 +30,16 @@ module Arel # :nodoc: all
|
|
30
30
|
collector.hints = {}
|
31
31
|
collector.table_hints = {}
|
32
32
|
collector.join_hints = {}
|
33
|
+
|
33
34
|
sql, binds = accept(node, collector).value
|
34
35
|
sql = collector.hints[:statement_hint].value + sql if collector.hints[:statement_hint]
|
35
|
-
|
36
|
-
|
36
|
+
|
37
|
+
if binds
|
38
|
+
binds << collector.hints[:staleness] if collector.hints[:staleness]
|
39
|
+
[sql, binds]
|
40
|
+
else
|
41
|
+
sql
|
42
|
+
end
|
37
43
|
end
|
38
44
|
|
39
45
|
private
|
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.3.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:
|
11
|
+
date: 2023-01-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: google-cloud-spanner
|
@@ -268,12 +268,16 @@ files:
|
|
268
268
|
- acceptance/cases/migration/references_index_test.rb
|
269
269
|
- acceptance/cases/migration/references_statements_test.rb
|
270
270
|
- acceptance/cases/migration/rename_column_test.rb
|
271
|
+
- acceptance/cases/migration/schema_dumper_test.rb
|
271
272
|
- acceptance/cases/models/calculation_query_test.rb
|
272
273
|
- acceptance/cases/models/generated_column_test.rb
|
273
274
|
- acceptance/cases/models/insert_all_test.rb
|
275
|
+
- acceptance/cases/models/interleave_test.rb
|
276
|
+
- acceptance/cases/models/logging_test.rb
|
274
277
|
- acceptance/cases/models/mutation_test.rb
|
275
278
|
- acceptance/cases/models/query_test.rb
|
276
279
|
- acceptance/cases/sessions/session_not_found_test.rb
|
280
|
+
- acceptance/cases/tasks/database_tasks_test.rb
|
277
281
|
- acceptance/cases/transactions/optimistic_locking_test.rb
|
278
282
|
- acceptance/cases/transactions/read_only_transactions_test.rb
|
279
283
|
- acceptance/cases/transactions/read_write_transactions_test.rb
|
@@ -292,6 +296,7 @@ files:
|
|
292
296
|
- acceptance/models/account.rb
|
293
297
|
- acceptance/models/address.rb
|
294
298
|
- acceptance/models/album.rb
|
299
|
+
- acceptance/models/album_partial_disabled.rb
|
295
300
|
- acceptance/models/all_types.rb
|
296
301
|
- acceptance/models/author.rb
|
297
302
|
- acceptance/models/club.rb
|
@@ -479,6 +484,7 @@ files:
|
|
479
484
|
- examples/snippets/timestamp-data-type/db/seeds.rb
|
480
485
|
- examples/snippets/timestamp-data-type/models/meeting.rb
|
481
486
|
- examples/solidus/README.md
|
487
|
+
- lib/active_record/connection_adapters/spanner/column.rb
|
482
488
|
- lib/active_record/connection_adapters/spanner/database_statements.rb
|
483
489
|
- lib/active_record/connection_adapters/spanner/quoting.rb
|
484
490
|
- lib/active_record/connection_adapters/spanner/schema_cache.rb
|