activerecord-spanner-adapter 1.8.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/acceptance-tests-on-emulator.yaml +4 -6
- data/.github/workflows/ci.yaml +4 -6
- data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +4 -6
- data/.github/workflows/nightly-unit-tests.yaml +4 -6
- data/.github/workflows/rubocop.yaml +1 -1
- data/.github/workflows/samples.yaml +30 -0
- data/.kokoro/release.sh +1 -3
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +2 -2
- data/CHANGELOG.md +18 -0
- data/Gemfile +6 -5
- data/README.md +11 -9
- data/acceptance/cases/migration/command_recorder_test.rb +7 -38
- data/acceptance/cases/migration/references_index_test.rb +2 -11
- data/acceptance/cases/models/binary_identifiers.rb +97 -0
- data/acceptance/models/binary_project.rb +20 -0
- data/acceptance/models/string_io.rb +28 -0
- data/acceptance/models/user.rb +20 -0
- data/acceptance/test_helper.rb +1 -0
- data/activerecord-spanner-adapter.gemspec +3 -3
- data/benchmarks/application.rb +3 -7
- data/examples/snippets/Rakefile +27 -5
- data/examples/snippets/array-data-type/application.rb +1 -5
- data/examples/snippets/array-data-type/config/database.yml +1 -0
- data/examples/snippets/bit-reversed-sequence/application.rb +0 -4
- data/examples/snippets/bit-reversed-sequence/config/database.yml +1 -0
- data/examples/snippets/bit-reversed-sequence/db/seeds.rb +2 -2
- data/examples/snippets/bulk-insert/application.rb +1 -5
- data/examples/snippets/bulk-insert/config/database.yml +1 -0
- data/examples/snippets/commit-timestamp/application.rb +0 -4
- data/examples/snippets/commit-timestamp/config/database.yml +1 -0
- data/examples/snippets/config/environment.rb +5 -0
- data/examples/snippets/create-records/application.rb +1 -5
- data/examples/snippets/create-records/config/database.yml +1 -0
- data/examples/snippets/date-data-type/application.rb +1 -5
- data/examples/snippets/date-data-type/config/database.yml +1 -0
- data/examples/snippets/date-data-type/db/seeds.rb +1 -1
- data/examples/snippets/generated-column/application.rb +0 -4
- data/examples/snippets/generated-column/config/database.yml +1 -0
- data/examples/snippets/generated-column/db/seeds.rb +1 -1
- data/examples/snippets/hints/application.rb +0 -4
- data/examples/snippets/hints/config/database.yml +1 -0
- data/examples/snippets/hints/db/seeds.rb +1 -1
- data/examples/snippets/interleaved-tables/application.rb +1 -5
- data/examples/snippets/interleaved-tables/config/database.yml +1 -0
- data/examples/snippets/interleaved-tables/db/seeds.rb +1 -1
- data/examples/snippets/interleaved-tables/models/album.rb +6 -2
- data/examples/snippets/interleaved-tables/models/track.rb +5 -1
- data/examples/snippets/interleaved-tables-before-7.1/application.rb +1 -5
- data/examples/snippets/interleaved-tables-before-7.1/config/database.yml +1 -0
- data/examples/snippets/interleaved-tables-before-7.1/db/seeds.rb +1 -1
- data/examples/snippets/migrations/application.rb +0 -4
- data/examples/snippets/migrations/config/database.yml +1 -0
- data/examples/snippets/mutations/application.rb +1 -5
- data/examples/snippets/mutations/config/database.yml +1 -0
- data/examples/snippets/mutations/db/seeds.rb +1 -1
- data/examples/snippets/optimistic-locking/application.rb +0 -4
- data/examples/snippets/optimistic-locking/config/database.yml +1 -0
- data/examples/snippets/optimistic-locking/db/seeds.rb +1 -1
- data/examples/snippets/partitioned-dml/application.rb +0 -4
- data/examples/snippets/partitioned-dml/config/database.yml +1 -0
- data/examples/snippets/partitioned-dml/db/seeds.rb +1 -1
- data/examples/snippets/query-logs/application.rb +15 -13
- data/examples/snippets/query-logs/config/database.yml +1 -0
- data/examples/snippets/query-logs/db/seeds.rb +1 -1
- data/examples/snippets/quickstart/application.rb +0 -4
- data/examples/snippets/quickstart/config/database.yml +1 -0
- data/examples/snippets/quickstart/db/seeds.rb +1 -1
- data/examples/snippets/read-only-transactions/application.rb +0 -4
- data/examples/snippets/read-only-transactions/config/database.yml +1 -0
- data/examples/snippets/read-only-transactions/db/seeds.rb +1 -1
- data/examples/snippets/read-write-transactions/application.rb +2 -6
- data/examples/snippets/read-write-transactions/config/database.yml +1 -0
- data/examples/snippets/read-write-transactions/db/seeds.rb +1 -1
- data/examples/snippets/stale-reads/application.rb +0 -4
- data/examples/snippets/stale-reads/config/database.yml +1 -0
- data/examples/snippets/stale-reads/db/seeds.rb +1 -1
- data/examples/snippets/tags/application.rb +0 -4
- data/examples/snippets/tags/config/database.yml +1 -0
- data/examples/snippets/tags/db/seeds.rb +1 -1
- data/examples/snippets/timestamp-data-type/application.rb +0 -4
- data/examples/snippets/timestamp-data-type/config/database.yml +1 -0
- data/lib/active_record/connection_adapters/spanner/column.rb +3 -3
- data/lib/active_record/connection_adapters/spanner/database_statements.rb +34 -22
- data/lib/active_record/connection_adapters/spanner/quoting.rb +2 -1
- data/lib/active_record/connection_adapters/spanner/schema_creation.rb +7 -9
- data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +12 -2
- data/lib/active_record/connection_adapters/spanner/schema_statements.rb +17 -46
- data/lib/active_record/connection_adapters/spanner/type_metadata.rb +4 -6
- data/lib/active_record/connection_adapters/spanner_adapter.rb +20 -7
- data/lib/active_record/tasks/spanner_database_tasks.rb +4 -4
- data/lib/active_record/type/spanner/array.rb +4 -0
- data/lib/active_record/type/spanner/bytes.rb +10 -0
- data/lib/activerecord_spanner_adapter/base.rb +12 -18
- data/lib/activerecord_spanner_adapter/connection.rb +9 -5
- data/lib/activerecord_spanner_adapter/foreign_key.rb +9 -2
- data/lib/activerecord_spanner_adapter/index/column.rb +6 -1
- data/lib/activerecord_spanner_adapter/index.rb +10 -2
- data/lib/activerecord_spanner_adapter/information_schema.rb +1 -1
- data/lib/activerecord_spanner_adapter/primary_key.rb +2 -2
- data/lib/activerecord_spanner_adapter/table/column.rb +12 -3
- data/lib/activerecord_spanner_adapter/table.rb +8 -2
- data/lib/activerecord_spanner_adapter/transaction.rb +1 -1
- data/lib/activerecord_spanner_adapter/version.rb +1 -1
- data/lib/arel/visitors/spanner.rb +16 -11
- data/lib/spanner_client_ext.rb +4 -3
- metadata +13 -32
- data/examples/snippets/array-data-type/db/schema.rb +0 -31
- data/examples/snippets/bit-reversed-sequence/db/schema.rb +0 -31
- data/examples/snippets/bulk-insert/db/schema.rb +0 -31
- data/examples/snippets/commit-timestamp/db/schema.rb +0 -34
- data/examples/snippets/create-records/db/schema.rb +0 -31
- data/examples/snippets/date-data-type/db/schema.rb +0 -26
- data/examples/snippets/generated-column/db/schema.rb +0 -26
- data/examples/snippets/hints/db/schema.rb +0 -33
- data/examples/snippets/interleaved-tables/db/schema.rb +0 -39
- data/examples/snippets/interleaved-tables-before-7.1/db/schema.rb +0 -37
- data/examples/snippets/migrations/db/schema.rb +0 -38
- data/examples/snippets/mutations/db/schema.rb +0 -32
- data/examples/snippets/optimistic-locking/db/schema.rb +0 -34
- data/examples/snippets/partitioned-dml/db/schema.rb +0 -31
- data/examples/snippets/query-logs/db/schema.rb +0 -31
- data/examples/snippets/quickstart/db/schema.rb +0 -31
- data/examples/snippets/read-only-transactions/db/schema.rb +0 -31
- data/examples/snippets/read-write-transactions/db/schema.rb +0 -32
- data/examples/snippets/stale-reads/db/schema.rb +0 -31
- data/examples/snippets/tags/db/schema.rb +0 -31
- data/examples/snippets/timestamp-data-type/db/schema.rb +0 -26
@@ -10,7 +10,7 @@ require_relative "models/singer"
|
|
10
10
|
require_relative "models/album"
|
11
11
|
|
12
12
|
class Application
|
13
|
-
def self.run
|
13
|
+
def self.run
|
14
14
|
from_album = nil
|
15
15
|
to_album = nil
|
16
16
|
# Use a read/write transaction to execute multiple statements as an atomic unit.
|
@@ -20,7 +20,7 @@ class Application
|
|
20
20
|
to_album = Album.where.not(id: from_album.id).sample
|
21
21
|
|
22
22
|
puts ""
|
23
|
-
puts "Transferring 10,000 marketing budget from #{from_album.title} (#{from_album.marketing_budget}) "\
|
23
|
+
puts "Transferring 10,000 marketing budget from #{from_album.title} (#{from_album.marketing_budget}) " \
|
24
24
|
"to #{to_album.title} (#{to_album.marketing_budget})"
|
25
25
|
from_album.update marketing_budget: from_album.marketing_budget - 10000
|
26
26
|
to_album.update marketing_budget: to_album.marketing_budget + 10000
|
@@ -29,10 +29,6 @@ class Application
|
|
29
29
|
puts "Budgets after update:"
|
30
30
|
puts "Marketing budget #{from_album.title}: #{from_album.reload.marketing_budget}"
|
31
31
|
puts "Marketing budget #{to_album.title}: #{to_album.reload.marketing_budget}"
|
32
|
-
|
33
|
-
puts ""
|
34
|
-
puts "Press any key to end the application"
|
35
|
-
STDIN.getch
|
36
32
|
end
|
37
33
|
end
|
38
34
|
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# license that can be found in the LICENSE file or at
|
5
5
|
# https://opensource.org/licenses/MIT.
|
6
6
|
|
7
|
-
require_relative "../../config/environment
|
7
|
+
require_relative "../../config/environment"
|
8
8
|
require_relative "../models/singer"
|
9
9
|
require_relative "../models/album"
|
10
10
|
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# license that can be found in the LICENSE file or at
|
5
5
|
# https://opensource.org/licenses/MIT.
|
6
6
|
|
7
|
-
require_relative "../../config/environment
|
7
|
+
require_relative "../../config/environment"
|
8
8
|
require_relative "../models/singer"
|
9
9
|
require_relative "../models/album"
|
10
10
|
|
@@ -22,10 +22,6 @@ class Application
|
|
22
22
|
albums = Album.annotate("request_tag: query-all-albums", "transaction_tag: sample-transaction").all
|
23
23
|
puts "Queried #{albums.length} albums using a request and a transaction tag"
|
24
24
|
end
|
25
|
-
|
26
|
-
puts ""
|
27
|
-
puts "Press any key to end the application"
|
28
|
-
STDIN.getch
|
29
25
|
end
|
30
26
|
end
|
31
27
|
|
@@ -4,7 +4,7 @@
|
|
4
4
|
# license that can be found in the LICENSE file or at
|
5
5
|
# https://opensource.org/licenses/MIT.
|
6
6
|
|
7
|
-
require_relative "../../config/environment
|
7
|
+
require_relative "../../config/environment"
|
8
8
|
require_relative "../models/singer"
|
9
9
|
require_relative "../models/album"
|
10
10
|
|
@@ -32,10 +32,6 @@ class Application
|
|
32
32
|
# Simulate that the application is now running in the timezone America/Los_Angeles.
|
33
33
|
Time.zone = "America/Los_Angeles"
|
34
34
|
puts "#{'Meeting time in the local timezone (America/Los_Angeles):'.ljust 60} #{meeting.local_meeting_time}"
|
35
|
-
|
36
|
-
puts ""
|
37
|
-
puts "Press any key to end the application"
|
38
|
-
STDIN.getch
|
39
35
|
end
|
40
36
|
end
|
41
37
|
|
@@ -10,16 +10,16 @@ module ActiveRecord
|
|
10
10
|
module ConnectionAdapters
|
11
11
|
module Spanner
|
12
12
|
class Column < ConnectionAdapters::Column
|
13
|
-
# rubocop:disable Style/
|
13
|
+
# rubocop:disable Style/OptionalBooleanParameter
|
14
14
|
def initialize(name, default, sql_type_metadata = nil, null = true,
|
15
15
|
default_function = nil, collation: nil, comment: nil,
|
16
16
|
primary_key: false, **)
|
17
|
-
# rubocop:enable Style/
|
17
|
+
# rubocop:enable Style/OptionalBooleanParameter
|
18
18
|
super
|
19
19
|
@primary_key = primary_key
|
20
20
|
end
|
21
21
|
|
22
|
-
def has_default?
|
22
|
+
def has_default?
|
23
23
|
super && !virtual?
|
24
24
|
end
|
25
25
|
|
@@ -29,7 +29,7 @@ module ActiveRecord
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def internal_execute sql, name = "SQL", binds = [],
|
32
|
-
prepare: false, async: false, allow_retry: false # rubocop:disable Lint/UnusedMethodArgument,
|
32
|
+
prepare: false, async: false, allow_retry: false # rubocop:disable Lint/UnusedMethodArgument, /
|
33
33
|
statement_type = sql_statement_type sql
|
34
34
|
# Call `transform` to invoke any query transformers that might have been registered.
|
35
35
|
sql = transform sql
|
@@ -65,7 +65,7 @@ module ActiveRecord
|
|
65
65
|
end
|
66
66
|
|
67
67
|
log_args = [sql, name]
|
68
|
-
log_args.
|
68
|
+
log_args.push binds, type_casted_binds(binds) if log_statement_binds
|
69
69
|
|
70
70
|
log(*log_args) do
|
71
71
|
types, params = to_types_and_params binds
|
@@ -83,12 +83,16 @@ module ActiveRecord
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def append_request_tag_from_query_logs sql, binds
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
86
|
+
possible_prefixes = [
|
87
|
+
"/*request_tag:true,",
|
88
|
+
"/*_request_tag='true',",
|
89
|
+
"/*_request_tag:true,",
|
90
|
+
"/*_request_tag='true',"
|
91
|
+
]
|
92
|
+
possible_prefixes.each do |prefix|
|
93
|
+
if sql.start_with? prefix
|
94
|
+
append_request_tag_from_query_logs_with_format sql, binds, prefix
|
95
|
+
end
|
92
96
|
end
|
93
97
|
end
|
94
98
|
|
@@ -101,7 +105,7 @@ module ActiveRecord
|
|
101
105
|
if options.request_tag == ""
|
102
106
|
options.request_tag = request_tag
|
103
107
|
else
|
104
|
-
options.request_tag += "
|
108
|
+
options.request_tag += ",#{request_tag}"
|
105
109
|
end
|
106
110
|
|
107
111
|
binds.append options
|
@@ -192,7 +196,7 @@ module ActiveRecord
|
|
192
196
|
# and this RPC can return multiple partial result sets for DML as well. Only the last partial
|
193
197
|
# result set will contain the statistics. Although there will never be any rows, this makes
|
194
198
|
# sure that the stream is fully consumed.
|
195
|
-
result.rows.each { |_| }
|
199
|
+
result.rows.each { |_| } # rubocop:disable Lint/EmptyBlock
|
196
200
|
return result.row_count if result.row_count
|
197
201
|
|
198
202
|
raise ActiveRecord::StatementInvalid.new(
|
@@ -282,12 +286,12 @@ module ActiveRecord
|
|
282
286
|
#
|
283
287
|
def begin_isolated_db_transaction isolation
|
284
288
|
if isolation.is_a? Hash
|
285
|
-
raise "Unsupported isolation level: #{isolation}" unless
|
289
|
+
raise "Unsupported isolation level: #{isolation}" unless
|
286
290
|
isolation[:timestamp] || isolation[:staleness] || isolation[:strong]
|
287
291
|
raise "Only one option is supported. It must be one of `timestamp`, `staleness` or `strong`." \
|
288
292
|
if isolation.count != 1
|
289
293
|
else
|
290
|
-
raise "Unsupported isolation level: #{isolation}" unless
|
294
|
+
raise "Unsupported isolation level: #{isolation}" unless
|
291
295
|
[:serializable, :read_only, :buffered_mutations, :pdml].include? isolation
|
292
296
|
end
|
293
297
|
|
@@ -312,26 +316,35 @@ module ActiveRecord
|
|
312
316
|
|
313
317
|
# Translates binds to Spanner types and params.
|
314
318
|
def to_types_and_params binds
|
315
|
-
types = binds
|
319
|
+
types = to_types binds
|
320
|
+
params = to_params binds
|
321
|
+
[types, params]
|
322
|
+
end
|
323
|
+
|
324
|
+
def to_types binds
|
325
|
+
binds.enum_for(:each_with_index).to_h do |bind, i|
|
316
326
|
type = :INT64
|
317
327
|
if bind.respond_to? :type
|
318
328
|
type = ActiveRecord::Type::Spanner::SpannerActiveRecordConverter
|
319
329
|
.convert_active_model_type_to_spanner(bind.type)
|
320
|
-
elsif bind.
|
330
|
+
elsif bind.instance_of? Symbol
|
321
331
|
# This ensures that for example :environment is sent as the string 'environment' to Cloud Spanner.
|
322
332
|
type = :STRING
|
323
|
-
elsif bind.
|
333
|
+
elsif bind.instance_of?(TrueClass) || bind.instance_of?(FalseClass)
|
324
334
|
type = :BOOL
|
325
335
|
end
|
326
336
|
[
|
327
337
|
# Generates binds for named parameters in the format `@p1, @p2, ...`
|
328
338
|
"p#{i + 1}", type
|
329
339
|
]
|
330
|
-
end
|
331
|
-
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def to_params binds
|
344
|
+
binds.enum_for(:each_with_index).to_h do |bind, i|
|
332
345
|
type = if bind.respond_to? :type
|
333
346
|
bind.type
|
334
|
-
elsif bind.
|
347
|
+
elsif bind.instance_of? Symbol
|
335
348
|
# This ensures that for example :environment is sent as the string 'environment' to Cloud Spanner.
|
336
349
|
:STRING
|
337
350
|
else
|
@@ -343,8 +356,7 @@ module ActiveRecord
|
|
343
356
|
.serialize_with_transaction_isolation_level(type, bind_value, :dml)
|
344
357
|
|
345
358
|
["p#{i + 1}", value]
|
346
|
-
end
|
347
|
-
[types, params]
|
359
|
+
end
|
348
360
|
end
|
349
361
|
|
350
362
|
# An insert/update/delete statement could use mutations in some specific circumstances.
|
@@ -353,7 +365,7 @@ module ActiveRecord
|
|
353
365
|
def should_use_mutation arel
|
354
366
|
!@connection.current_transaction.nil? \
|
355
367
|
&& @connection.current_transaction.isolation == :buffered_mutations \
|
356
|
-
&& can_use_mutation(arel)
|
368
|
+
&& can_use_mutation(arel)
|
357
369
|
end
|
358
370
|
|
359
371
|
def can_use_mutation arel
|
@@ -383,7 +395,7 @@ module ActiveRecord
|
|
383
395
|
)
|
384
396
|
end
|
385
397
|
|
386
|
-
COMMENT_REGEX = %r{(?:--.*\n)*|/\*(?:[^*]|\*[^/])*\*/}m
|
398
|
+
COMMENT_REGEX = %r{(?:--.*\n)*|/\*(?:[^*]|\*[^/])*\*/}m \
|
387
399
|
unless defined? ActiveRecord::ConnectionAdapters::AbstractAdapter::COMMENT_REGEX
|
388
400
|
COMMENT_REGEX = ActiveRecord::ConnectionAdapters::AbstractAdapter::COMMENT_REGEX \
|
389
401
|
if defined? ActiveRecord::ConnectionAdapters::AbstractAdapter::COMMENT_REGEX
|
@@ -45,6 +45,7 @@ module ActiveRecord
|
|
45
45
|
end
|
46
46
|
end
|
47
47
|
end
|
48
|
+
|
48
49
|
module Spanner
|
49
50
|
module Quoting
|
50
51
|
def quote_column_name name
|
@@ -55,7 +56,7 @@ module ActiveRecord
|
|
55
56
|
QUOTED_TABLE_NAMES[name] ||= "`#{name.to_s.gsub '.', '`.`'}`".freeze
|
56
57
|
end
|
57
58
|
|
58
|
-
STR_ESCAPE_REGX = /[\n\r'\\]
|
59
|
+
STR_ESCAPE_REGX = /[\n\r'\\]/
|
59
60
|
STR_ESCAPE_VALUES = {
|
60
61
|
"\n" => "\\n", "\r" => "\\r", "'" => "\\'", "\\" => "\\\\"
|
61
62
|
}.freeze
|
@@ -44,10 +44,7 @@ module ActiveRecord
|
|
44
44
|
else
|
45
45
|
[o.options[:primary_key]]
|
46
46
|
end
|
47
|
-
pk_names =
|
48
|
-
columns.each do |c|
|
49
|
-
pk_names.append c.to_s
|
50
|
-
end
|
47
|
+
pk_names = columns.map(&:to_s)
|
51
48
|
PrimaryKeyDefinition.new pk_names
|
52
49
|
else
|
53
50
|
pk_names = o.columns.each_with_object [] do |c, r|
|
@@ -93,8 +90,8 @@ module ActiveRecord
|
|
93
90
|
end
|
94
91
|
|
95
92
|
def visit_DropColumnDefinition o
|
96
|
-
"ALTER TABLE #{quote_table_name o.table_name} DROP" \
|
97
|
-
|
93
|
+
"ALTER TABLE #{quote_table_name o.table_name} DROP " \
|
94
|
+
"COLUMN #{quote_column_name o.name}"
|
98
95
|
end
|
99
96
|
|
100
97
|
def visit_ChangeColumnDefinition o
|
@@ -143,7 +140,7 @@ module ActiveRecord
|
|
143
140
|
|
144
141
|
if !options[:allow_commit_timestamp].nil? &&
|
145
142
|
options[:column].sql_type == "TIMESTAMP"
|
146
|
-
sql << " OPTIONS (allow_commit_timestamp = "\
|
143
|
+
sql << " OPTIONS (allow_commit_timestamp = " \
|
147
144
|
"#{options[:allow_commit_timestamp]})"
|
148
145
|
end
|
149
146
|
|
@@ -153,8 +150,9 @@ module ActiveRecord
|
|
153
150
|
sql << " STORED" if options[:stored]
|
154
151
|
unless options[:stored]
|
155
152
|
raise ArgumentError, "" \
|
156
|
-
|
157
|
-
|
153
|
+
"Cloud Spanner currently does not support generated columns" \
|
154
|
+
"without the STORED option." \
|
155
|
+
"Specify 'stored: true' option for `#{options[:column].name}`"
|
158
156
|
end
|
159
157
|
end
|
160
158
|
|
@@ -5,7 +5,7 @@
|
|
5
5
|
# https://opensource.org/licenses/MIT.
|
6
6
|
|
7
7
|
module ActiveRecord
|
8
|
-
module ConnectionAdapters
|
8
|
+
module ConnectionAdapters # :nodoc:
|
9
9
|
module Spanner
|
10
10
|
class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
|
11
11
|
attr_reader :interleave_in_parent
|
@@ -62,6 +62,9 @@ module ActiveRecord
|
|
62
62
|
DropIndexDefinition = Struct.new :name
|
63
63
|
|
64
64
|
class ReferenceDefinition < ActiveRecord::ConnectionAdapters::ReferenceDefinition
|
65
|
+
# This constructor intentionally does not call super to prevent ActiveRecord
|
66
|
+
# from creating an additional secondary index for the foreign key.
|
67
|
+
# rubocop:disable Lint/MissingSuper
|
65
68
|
def initialize \
|
66
69
|
name,
|
67
70
|
polymorphic: false,
|
@@ -81,6 +84,7 @@ module ActiveRecord
|
|
81
84
|
return unless polymorphic && foreign_key
|
82
85
|
raise ArgumentError, "Cannot add a foreign key to a polymorphic relation"
|
83
86
|
end
|
87
|
+
# rubocop:enable Lint/MissingSuper
|
84
88
|
|
85
89
|
private
|
86
90
|
|
@@ -96,8 +100,13 @@ module ActiveRecord
|
|
96
100
|
end
|
97
101
|
|
98
102
|
class IndexDefinition < ActiveRecord::ConnectionAdapters::IndexDefinition
|
99
|
-
attr_reader :null_filtered
|
103
|
+
attr_reader :null_filtered
|
104
|
+
attr_reader :interleave_in
|
105
|
+
attr_reader :storing
|
106
|
+
attr_reader :orders
|
100
107
|
|
108
|
+
# This constructor intentionally does not call super.
|
109
|
+
# rubocop:disable Lint/MissingSuper
|
101
110
|
def initialize \
|
102
111
|
table_name,
|
103
112
|
name,
|
@@ -123,6 +132,7 @@ module ActiveRecord
|
|
123
132
|
|
124
133
|
@orders = @orders.symbolize_keys
|
125
134
|
end
|
135
|
+
# rubocop:enable Lint/MissingSuper
|
126
136
|
|
127
137
|
def columns_with_order
|
128
138
|
columns.each_with_object({}) do |c, result|
|
@@ -109,8 +109,7 @@ module ActiveRecord
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def rename_table _table_name, _new_name
|
112
|
-
raise ActiveRecordSpannerAdapter::NotSupportedError,
|
113
|
-
"rename_table is not implemented"
|
112
|
+
raise ActiveRecordSpannerAdapter::NotSupportedError, "rename_table is not implemented"
|
114
113
|
end
|
115
114
|
|
116
115
|
# Column
|
@@ -168,20 +167,14 @@ module ActiveRecord
|
|
168
167
|
execute_schema_statements statements
|
169
168
|
end
|
170
169
|
|
171
|
-
|
172
|
-
|
173
|
-
_remove_columns table_name, *column_names
|
174
|
-
end
|
175
|
-
else
|
176
|
-
def remove_columns table_name, *column_names, _type: nil, **_options
|
177
|
-
_remove_columns table_name, *column_names
|
178
|
-
end
|
170
|
+
def remove_columns table_name, *column_names, _type: nil, **_options
|
171
|
+
_remove_columns table_name, *column_names
|
179
172
|
end
|
180
173
|
|
181
174
|
def _remove_columns table_name, *column_names
|
182
175
|
if column_names.empty?
|
183
|
-
raise ArgumentError, "You must specify at least one column name. "\
|
184
|
-
|
176
|
+
raise ArgumentError, "You must specify at least one column name. " \
|
177
|
+
"Example: remove_columns(:people, :first_name)"
|
185
178
|
end
|
186
179
|
|
187
180
|
statements = []
|
@@ -193,14 +186,8 @@ module ActiveRecord
|
|
193
186
|
execute_schema_statements statements
|
194
187
|
end
|
195
188
|
|
196
|
-
|
197
|
-
|
198
|
-
_change_column table_name, column_name, type, **options
|
199
|
-
end
|
200
|
-
else
|
201
|
-
def change_column table_name, column_name, type, **options
|
202
|
-
_change_column table_name, column_name, type, **options
|
203
|
-
end
|
189
|
+
def change_column table_name, column_name, type, **options
|
190
|
+
_change_column table_name, column_name, type, **options
|
204
191
|
end
|
205
192
|
|
206
193
|
def change_column_null table_name, column_name, null, _default = nil
|
@@ -208,14 +195,12 @@ module ActiveRecord
|
|
208
195
|
end
|
209
196
|
|
210
197
|
def change_column_default _table_name, _column_name, _default_or_changes
|
211
|
-
raise ActiveRecordSpannerAdapter::NotSupportedError,
|
212
|
-
"change column with default value not supported."
|
198
|
+
raise ActiveRecordSpannerAdapter::NotSupportedError, "change column with default value not supported."
|
213
199
|
end
|
214
200
|
|
215
201
|
def rename_column table_name, column_name, new_column_name
|
216
202
|
if ActiveRecord::Base.connection.ddl_batch?
|
217
|
-
raise ActiveRecordSpannerAdapter::NotSupportedError,
|
218
|
-
"rename_column in a DDL Batch is not supported."
|
203
|
+
raise ActiveRecordSpannerAdapter::NotSupportedError, "rename_column in a DDL Batch is not supported."
|
219
204
|
end
|
220
205
|
column = information_schema do |i|
|
221
206
|
i.table_column table_name, column_name
|
@@ -280,16 +265,9 @@ module ActiveRecord
|
|
280
265
|
execute_schema_statements schema_creation.accept(id)
|
281
266
|
end
|
282
267
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
execute "DROP INDEX #{quote_table_name index_name}"
|
287
|
-
end
|
288
|
-
else
|
289
|
-
def remove_index table_name, column_name = nil, **options
|
290
|
-
index_name = index_name_for_remove table_name, column_name, options
|
291
|
-
execute "DROP INDEX #{quote_table_name index_name}"
|
292
|
-
end
|
268
|
+
def remove_index table_name, column_name = nil, **options
|
269
|
+
index_name = index_name_for_remove table_name, column_name, options
|
270
|
+
execute "DROP INDEX #{quote_table_name index_name}"
|
293
271
|
end
|
294
272
|
|
295
273
|
def rename_index table_name, old_name, new_name
|
@@ -358,14 +336,8 @@ module ActiveRecord
|
|
358
336
|
end
|
359
337
|
end
|
360
338
|
|
361
|
-
|
362
|
-
|
363
|
-
_add_foreign_key from_table, to_table, **options
|
364
|
-
end
|
365
|
-
else
|
366
|
-
def add_foreign_key from_table, to_table, **options
|
367
|
-
_add_foreign_key from_table, to_table, **options
|
368
|
-
end
|
339
|
+
def add_foreign_key from_table, to_table, **options
|
340
|
+
_add_foreign_key from_table, to_table, **options
|
369
341
|
end
|
370
342
|
|
371
343
|
def _add_foreign_key from_table, to_table, **options
|
@@ -508,8 +480,8 @@ module ActiveRecord
|
|
508
480
|
type ||= column.type
|
509
481
|
options[:null] = column.null unless options.key? :null
|
510
482
|
|
511
|
-
if ["STRING", "BYTES"].include?
|
512
|
-
options[:limit] = column.limit
|
483
|
+
if ["STRING", "BYTES"].include?(type) && !options.key?(:limit)
|
484
|
+
options[:limit] = column.limit
|
513
485
|
end
|
514
486
|
|
515
487
|
# Only timestamp type can set commit timestamp
|
@@ -642,8 +614,7 @@ module ActiveRecord
|
|
642
614
|
end
|
643
615
|
|
644
616
|
def information_schema
|
645
|
-
info_schema =
|
646
|
-
ActiveRecordSpannerAdapter::Connection.information_schema @config
|
617
|
+
info_schema = ActiveRecordSpannerAdapter::Connection.information_schema @config
|
647
618
|
|
648
619
|
return info_schema unless block_given?
|
649
620
|
|
@@ -14,7 +14,9 @@ module ActiveRecord
|
|
14
14
|
|
15
15
|
include Deduplicable if defined?(Deduplicable)
|
16
16
|
|
17
|
-
attr_reader :ordinal_position
|
17
|
+
attr_reader :ordinal_position
|
18
|
+
attr_reader :allow_commit_timestamp
|
19
|
+
attr_reader :generated
|
18
20
|
|
19
21
|
def initialize type_metadata, ordinal_position: nil, allow_commit_timestamp: nil, generated: nil
|
20
22
|
super type_metadata
|
@@ -33,11 +35,7 @@ module ActiveRecord
|
|
33
35
|
alias eql? ==
|
34
36
|
|
35
37
|
def hash
|
36
|
-
TypeMetadata.hash
|
37
|
-
__getobj__.hash ^
|
38
|
-
ordinal_position.hash ^
|
39
|
-
allow_commit_timestamp.hash ^
|
40
|
-
generated.hash
|
38
|
+
[TypeMetadata.name, __getobj__, ordinal_position, allow_commit_timestamp, generated].hash
|
41
39
|
end
|
42
40
|
|
43
41
|
private
|
@@ -276,7 +276,9 @@ module ActiveRecord
|
|
276
276
|
end
|
277
277
|
|
278
278
|
def transform sql
|
279
|
-
if ActiveRecord::VERSION::MAJOR >=
|
279
|
+
if ActiveRecord::VERSION::MAJOR >= 8
|
280
|
+
preprocess_query sql
|
281
|
+
elsif ActiveRecord::VERSION::MAJOR == 7
|
280
282
|
transform_query sql
|
281
283
|
else
|
282
284
|
sql
|
@@ -284,12 +286,23 @@ module ActiveRecord
|
|
284
286
|
end
|
285
287
|
|
286
288
|
# Overwrite the standard log method to be able to translate exceptions.
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
289
|
+
# sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block
|
290
|
+
if ActiveRecord::VERSION::MAJOR >= 8
|
291
|
+
def log sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block
|
292
|
+
super
|
293
|
+
rescue ActiveRecord::StatementInvalid
|
294
|
+
raise
|
295
|
+
rescue StandardError => e
|
296
|
+
raise translate_exception_class(e, sql, binds)
|
297
|
+
end
|
298
|
+
else
|
299
|
+
def log sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, *args
|
300
|
+
super
|
301
|
+
rescue ActiveRecord::StatementInvalid
|
302
|
+
raise
|
303
|
+
rescue StandardError => e
|
304
|
+
raise translate_exception_class(e, sql, binds)
|
305
|
+
end
|
293
306
|
end
|
294
307
|
|
295
308
|
def translate_exception exception, message:, sql:, binds:
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
def purge
|
32
32
|
begin
|
33
33
|
drop
|
34
|
-
rescue ActiveRecord::NoDatabaseError
|
34
|
+
rescue ActiveRecord::NoDatabaseError
|
35
35
|
# ignored; create the database
|
36
36
|
end
|
37
37
|
|
@@ -51,8 +51,8 @@ module ActiveRecord
|
|
51
51
|
ignore_tables = ActiveRecord::SchemaDumper.ignore_tables
|
52
52
|
|
53
53
|
if ignore_tables.any?
|
54
|
-
index_regx = /^CREATE(.*)INDEX(.*)ON (#{ignore_tables.join
|
55
|
-
table_regx = /^CREATE TABLE (#{ignore_tables.join
|
54
|
+
index_regx = /^CREATE(.*)INDEX(.*)ON (#{ignore_tables.join '|'})\(/
|
55
|
+
table_regx = /^CREATE TABLE (#{ignore_tables.join '|'})/
|
56
56
|
end
|
57
57
|
|
58
58
|
@connection.database.ddl(force: true).each do |statement|
|
@@ -66,7 +66,7 @@ module ActiveRecord
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def structure_load filename, _extra_flags
|
69
|
-
statements = File.read(filename).split(
|
69
|
+
statements = File.read(filename).split(";").map(&:strip).reject(&:empty?)
|
70
70
|
ddls = statements.select { |s| s =~ /^(CREATE|ALTER|DROP|GRANT|REVOKE|ANALYZE)/ }
|
71
71
|
@connection.execute_ddl ddls
|
72
72
|
|
@@ -9,11 +9,15 @@ module ActiveRecord
|
|
9
9
|
module Spanner
|
10
10
|
class Array < Type::Value
|
11
11
|
attr_reader :element_type
|
12
|
+
|
12
13
|
delegate :type, :user_input_in_time_zone, :limit, :precision, :scale, to: :element_type
|
13
14
|
|
15
|
+
# This constructor intentionally does not call super.
|
16
|
+
# rubocop:disable Lint/MissingSuper
|
14
17
|
def initialize element_type
|
15
18
|
@element_type = element_type
|
16
19
|
end
|
20
|
+
# rubocop:enable Lint/MissingSuper
|
17
21
|
|
18
22
|
def cast value
|
19
23
|
return super if value.nil?
|
@@ -10,6 +10,16 @@ module ActiveRecord
|
|
10
10
|
module Type
|
11
11
|
module Spanner
|
12
12
|
class Bytes < ActiveRecord::Type::Binary
|
13
|
+
def deserialize value
|
14
|
+
# Set this environment variable to disable de-serializing BYTES
|
15
|
+
# to a StringIO instance.
|
16
|
+
return super if ENV["SPANNER_BYTES_DESERIALIZE_DISABLED"]
|
17
|
+
|
18
|
+
return super value if value.nil?
|
19
|
+
return StringIO.new Base64.strict_decode64(value) if value.is_a? ::String
|
20
|
+
value
|
21
|
+
end
|
22
|
+
|
13
23
|
def serialize value
|
14
24
|
return super value if value.nil?
|
15
25
|
|