activerecord-spanner-adapter 1.8.0 → 2.0.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.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/acceptance-tests-on-emulator.yaml +4 -6
  3. data/.github/workflows/ci.yaml +4 -6
  4. data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +4 -6
  5. data/.github/workflows/nightly-unit-tests.yaml +4 -6
  6. data/.github/workflows/rubocop.yaml +1 -1
  7. data/.github/workflows/samples.yaml +30 -0
  8. data/.kokoro/release.sh +1 -3
  9. data/.release-please-manifest.json +1 -1
  10. data/.rubocop.yml +2 -2
  11. data/CHANGELOG.md +18 -0
  12. data/Gemfile +6 -5
  13. data/README.md +11 -9
  14. data/acceptance/cases/migration/command_recorder_test.rb +7 -38
  15. data/acceptance/cases/migration/references_index_test.rb +2 -11
  16. data/acceptance/cases/models/binary_identifiers.rb +97 -0
  17. data/acceptance/models/binary_project.rb +20 -0
  18. data/acceptance/models/string_io.rb +28 -0
  19. data/acceptance/models/user.rb +20 -0
  20. data/acceptance/test_helper.rb +1 -0
  21. data/activerecord-spanner-adapter.gemspec +3 -3
  22. data/benchmarks/application.rb +3 -7
  23. data/examples/snippets/Rakefile +27 -5
  24. data/examples/snippets/array-data-type/application.rb +1 -5
  25. data/examples/snippets/array-data-type/config/database.yml +1 -0
  26. data/examples/snippets/bit-reversed-sequence/application.rb +0 -4
  27. data/examples/snippets/bit-reversed-sequence/config/database.yml +1 -0
  28. data/examples/snippets/bit-reversed-sequence/db/seeds.rb +2 -2
  29. data/examples/snippets/bulk-insert/application.rb +1 -5
  30. data/examples/snippets/bulk-insert/config/database.yml +1 -0
  31. data/examples/snippets/commit-timestamp/application.rb +0 -4
  32. data/examples/snippets/commit-timestamp/config/database.yml +1 -0
  33. data/examples/snippets/config/environment.rb +5 -0
  34. data/examples/snippets/create-records/application.rb +1 -5
  35. data/examples/snippets/create-records/config/database.yml +1 -0
  36. data/examples/snippets/date-data-type/application.rb +1 -5
  37. data/examples/snippets/date-data-type/config/database.yml +1 -0
  38. data/examples/snippets/date-data-type/db/seeds.rb +1 -1
  39. data/examples/snippets/generated-column/application.rb +0 -4
  40. data/examples/snippets/generated-column/config/database.yml +1 -0
  41. data/examples/snippets/generated-column/db/seeds.rb +1 -1
  42. data/examples/snippets/hints/application.rb +0 -4
  43. data/examples/snippets/hints/config/database.yml +1 -0
  44. data/examples/snippets/hints/db/seeds.rb +1 -1
  45. data/examples/snippets/interleaved-tables/application.rb +1 -5
  46. data/examples/snippets/interleaved-tables/config/database.yml +1 -0
  47. data/examples/snippets/interleaved-tables/db/seeds.rb +1 -1
  48. data/examples/snippets/interleaved-tables/models/album.rb +6 -2
  49. data/examples/snippets/interleaved-tables/models/track.rb +5 -1
  50. data/examples/snippets/interleaved-tables-before-7.1/application.rb +1 -5
  51. data/examples/snippets/interleaved-tables-before-7.1/config/database.yml +1 -0
  52. data/examples/snippets/interleaved-tables-before-7.1/db/seeds.rb +1 -1
  53. data/examples/snippets/migrations/application.rb +0 -4
  54. data/examples/snippets/migrations/config/database.yml +1 -0
  55. data/examples/snippets/mutations/application.rb +1 -5
  56. data/examples/snippets/mutations/config/database.yml +1 -0
  57. data/examples/snippets/mutations/db/seeds.rb +1 -1
  58. data/examples/snippets/optimistic-locking/application.rb +0 -4
  59. data/examples/snippets/optimistic-locking/config/database.yml +1 -0
  60. data/examples/snippets/optimistic-locking/db/seeds.rb +1 -1
  61. data/examples/snippets/partitioned-dml/application.rb +0 -4
  62. data/examples/snippets/partitioned-dml/config/database.yml +1 -0
  63. data/examples/snippets/partitioned-dml/db/seeds.rb +1 -1
  64. data/examples/snippets/query-logs/application.rb +15 -13
  65. data/examples/snippets/query-logs/config/database.yml +1 -0
  66. data/examples/snippets/query-logs/db/seeds.rb +1 -1
  67. data/examples/snippets/quickstart/application.rb +0 -4
  68. data/examples/snippets/quickstart/config/database.yml +1 -0
  69. data/examples/snippets/quickstart/db/seeds.rb +1 -1
  70. data/examples/snippets/read-only-transactions/application.rb +0 -4
  71. data/examples/snippets/read-only-transactions/config/database.yml +1 -0
  72. data/examples/snippets/read-only-transactions/db/seeds.rb +1 -1
  73. data/examples/snippets/read-write-transactions/application.rb +2 -6
  74. data/examples/snippets/read-write-transactions/config/database.yml +1 -0
  75. data/examples/snippets/read-write-transactions/db/seeds.rb +1 -1
  76. data/examples/snippets/stale-reads/application.rb +0 -4
  77. data/examples/snippets/stale-reads/config/database.yml +1 -0
  78. data/examples/snippets/stale-reads/db/seeds.rb +1 -1
  79. data/examples/snippets/tags/application.rb +0 -4
  80. data/examples/snippets/tags/config/database.yml +1 -0
  81. data/examples/snippets/tags/db/seeds.rb +1 -1
  82. data/examples/snippets/timestamp-data-type/application.rb +0 -4
  83. data/examples/snippets/timestamp-data-type/config/database.yml +1 -0
  84. data/lib/active_record/connection_adapters/spanner/column.rb +3 -3
  85. data/lib/active_record/connection_adapters/spanner/database_statements.rb +34 -22
  86. data/lib/active_record/connection_adapters/spanner/quoting.rb +2 -1
  87. data/lib/active_record/connection_adapters/spanner/schema_creation.rb +7 -9
  88. data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +12 -2
  89. data/lib/active_record/connection_adapters/spanner/schema_statements.rb +17 -46
  90. data/lib/active_record/connection_adapters/spanner/type_metadata.rb +4 -6
  91. data/lib/active_record/connection_adapters/spanner_adapter.rb +20 -7
  92. data/lib/active_record/tasks/spanner_database_tasks.rb +4 -4
  93. data/lib/active_record/type/spanner/array.rb +4 -0
  94. data/lib/active_record/type/spanner/bytes.rb +10 -0
  95. data/lib/activerecord_spanner_adapter/base.rb +12 -18
  96. data/lib/activerecord_spanner_adapter/connection.rb +9 -5
  97. data/lib/activerecord_spanner_adapter/foreign_key.rb +9 -2
  98. data/lib/activerecord_spanner_adapter/index/column.rb +6 -1
  99. data/lib/activerecord_spanner_adapter/index.rb +10 -2
  100. data/lib/activerecord_spanner_adapter/information_schema.rb +1 -1
  101. data/lib/activerecord_spanner_adapter/primary_key.rb +2 -2
  102. data/lib/activerecord_spanner_adapter/table/column.rb +12 -3
  103. data/lib/activerecord_spanner_adapter/table.rb +8 -2
  104. data/lib/activerecord_spanner_adapter/transaction.rb +1 -1
  105. data/lib/activerecord_spanner_adapter/version.rb +1 -1
  106. data/lib/arel/visitors/spanner.rb +16 -11
  107. data/lib/spanner_client_ext.rb +4 -3
  108. metadata +13 -32
  109. data/examples/snippets/array-data-type/db/schema.rb +0 -31
  110. data/examples/snippets/bit-reversed-sequence/db/schema.rb +0 -31
  111. data/examples/snippets/bulk-insert/db/schema.rb +0 -31
  112. data/examples/snippets/commit-timestamp/db/schema.rb +0 -34
  113. data/examples/snippets/create-records/db/schema.rb +0 -31
  114. data/examples/snippets/date-data-type/db/schema.rb +0 -26
  115. data/examples/snippets/generated-column/db/schema.rb +0 -26
  116. data/examples/snippets/hints/db/schema.rb +0 -33
  117. data/examples/snippets/interleaved-tables/db/schema.rb +0 -39
  118. data/examples/snippets/interleaved-tables-before-7.1/db/schema.rb +0 -37
  119. data/examples/snippets/migrations/db/schema.rb +0 -38
  120. data/examples/snippets/mutations/db/schema.rb +0 -32
  121. data/examples/snippets/optimistic-locking/db/schema.rb +0 -34
  122. data/examples/snippets/partitioned-dml/db/schema.rb +0 -31
  123. data/examples/snippets/query-logs/db/schema.rb +0 -31
  124. data/examples/snippets/quickstart/db/schema.rb +0 -31
  125. data/examples/snippets/read-only-transactions/db/schema.rb +0 -31
  126. data/examples/snippets/read-write-transactions/db/schema.rb +0 -32
  127. data/examples/snippets/stale-reads/db/schema.rb +0 -31
  128. data/examples/snippets/tags/db/schema.rb +0 -31
  129. 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 # rubocop:disable Metrics/AbcSize
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
 
@@ -6,3 +6,4 @@ development:
6
6
  database: testdb
7
7
  pool: 5
8
8
  timeout: 5000
9
+ schema_dump: false
@@ -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.rb"
7
+ require_relative "../../config/environment"
8
8
  require_relative "../models/singer"
9
9
  require_relative "../models/album"
10
10
 
@@ -53,10 +53,6 @@ class Application
53
53
  puts ""
54
54
  puts "Updated album title: #{album.title}"
55
55
  puts "Title somewhere during the last 10 seconds: #{album_max_staleness&.title}"
56
-
57
- puts ""
58
- puts "Press any key to end the application"
59
- STDIN.getch
60
56
  end
61
57
  end
62
58
 
@@ -6,3 +6,4 @@ development:
6
6
  database: testdb
7
7
  pool: 5
8
8
  timeout: 5000
9
+ schema_dump: false
@@ -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.rb"
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
 
@@ -6,3 +6,4 @@ development:
6
6
  database: testdb
7
7
  pool: 5
8
8
  timeout: 5000
9
+ schema_dump: false
@@ -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.rb"
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
 
@@ -6,3 +6,4 @@ development:
6
6
  database: testdb
7
7
  pool: 5
8
8
  timeout: 5000
9
+ schema_dump: false
@@ -10,16 +10,16 @@ module ActiveRecord
10
10
  module ConnectionAdapters
11
11
  module Spanner
12
12
  class Column < ConnectionAdapters::Column
13
- # rubocop:disable Style/MethodDefParentheses
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/MethodDefParentheses
17
+ # rubocop:enable Style/OptionalBooleanParameter
18
18
  super
19
19
  @primary_key = primary_key
20
20
  end
21
21
 
22
- def has_default? # rubocop:disable Naming/PredicateName
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, Metrics/LineLength
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.concat [binds, type_casted_binds(binds)] if log_statement_binds
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
- legacy_formatter_prefix = "/*request_tag:true,"
87
- sql_commenter_prefix = "/*request_tag='true',"
88
- if sql.start_with? legacy_formatter_prefix
89
- append_request_tag_from_query_logs_with_format sql, binds, legacy_formatter_prefix
90
- elsif sql.start_with? sql_commenter_prefix
91
- append_request_tag_from_query_logs_with_format sql, binds, sql_commenter_prefix
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 += "," + 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.enum_for(:each_with_index).map do |bind, i|
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.class == Symbol
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.class == TrueClass || bind.class == FalseClass
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.to_h
331
- params = binds.enum_for(:each_with_index).map do |bind, i|
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.class == Symbol
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.to_h
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.freeze \
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'\\]/.freeze
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
- " COLUMN #{quote_column_name o.name}"
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
- "Cloud Spanner currently does not support generated columns without the STORED option." \
157
- "Specify 'stored: true' option for `#{options[:column].name}`"
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 #:nodoc:
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, :interleave_in, :storing, :orders
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
- if ActiveRecord.gem_version < VERSION_6_1_0
172
- def remove_columns table_name, *column_names
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
- "Example: remove_columns(:people, :first_name)"
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
- if ActiveRecord.gem_version < VERSION_6_1_0
197
- def change_column table_name, column_name, type, options = {}
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
- if ActiveRecord.gem_version < VERSION_6_1_0
284
- def remove_index table_name, options = {}
285
- index_name = index_name_for_remove table_name, options
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
- if ActiveRecord.gem_version < VERSION_6_0_3
362
- def add_foreign_key from_table, to_table, options = {}
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? type
512
- options[:limit] = column.limit unless options.key? :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, :allow_commit_timestamp, :generated
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 >= 7
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
- def log sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, *args
288
- super
289
- rescue ActiveRecord::StatementInvalid
290
- raise
291
- rescue StandardError => e
292
- raise translate_exception_class(e, sql, binds)
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 # rubocop:disable Lint/HandleExceptions
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(/;/).map(&:strip).reject(&:empty?)
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