activerecord-spanner-adapter 2.5.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/acceptance-tests-on-emulator.yaml +19 -15
- data/.github/workflows/acceptance-tests-on-production.yaml +13 -6
- data/.github/workflows/ci.yaml +18 -14
- data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +19 -15
- data/.github/workflows/nightly-acceptance-tests-on-production.yaml +13 -6
- data/.github/workflows/nightly-unit-tests.yaml +18 -14
- data/.github/workflows/rubocop.yaml +9 -4
- data/.github/workflows/samples.yaml +18 -12
- data/.gitignore +0 -1
- data/.release-please-manifest.json +1 -1
- data/.rubocop.yml +2 -0
- data/Appraisals +15 -0
- data/CHANGELOG.md +19 -0
- data/Gemfile +12 -20
- data/Gemfile.lock +479 -0
- data/README.md +3 -3
- data/acceptance/cases/migration/change_table_test.rb +4 -14
- data/acceptance/cases/models/generated_column_test.rb +8 -26
- data/acceptance/test_helper.rb +10 -22
- data/activerecord-spanner-adapter.gemspec +4 -3
- data/benchmarks/db/migrate/01_create_tables.rb +1 -1
- data/examples/rails/README.md +1 -1
- data/examples/snippets/Rakefile +0 -7
- data/examples/snippets/array-data-type/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/batch-dml/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/bulk-insert/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/commit-timestamp/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/create-records/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/date-data-type/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/generated-column/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/hints/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/interleaved-tables/README.md +2 -5
- data/examples/snippets/isolation-level/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/migrations/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/mutations/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/optimistic-locking/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/partitioned-dml/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/query-logs/application.rb +3 -3
- data/examples/snippets/query-logs/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/quickstart/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/read-only-transactions/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/read-write-transactions/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/stale-reads/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/tags/db/migrate/01_create_tables.rb +1 -1
- data/examples/snippets/timestamp-data-type/db/migrate/01_create_tables.rb +1 -1
- data/gemfiles/7.1.gemfile +18 -0
- data/gemfiles/7.1.gemfile.lock +389 -0
- data/gemfiles/7.2.gemfile +18 -0
- data/gemfiles/7.2.gemfile.lock +479 -0
- data/gemfiles/8.0.gemfile +18 -0
- data/gemfiles/8.0.gemfile.lock +480 -0
- data/gemfiles/8.1.gemfile +18 -0
- data/gemfiles/8.1.gemfile.lock +478 -0
- data/lib/active_record/connection_adapters/spanner/database_statements.rb +62 -95
- data/lib/active_record/connection_adapters/spanner/quoting.rb +0 -22
- data/lib/active_record/connection_adapters/spanner/schema_creation.rb +3 -15
- data/lib/active_record/connection_adapters/spanner/schema_statements.rb +0 -4
- data/lib/active_record/connection_adapters/spanner_adapter.rb +12 -14
- data/lib/active_record/type/spanner/spanner_active_record_converter.rb +33 -2
- data/lib/activerecord_spanner_adapter/base.rb +5 -27
- data/lib/activerecord_spanner_adapter/connection.rb +0 -20
- data/lib/activerecord_spanner_adapter/information_schema.rb +1 -4
- data/lib/activerecord_spanner_adapter/table.rb +0 -22
- data/lib/activerecord_spanner_adapter/version.rb +1 -1
- data/lib/arel/visitors/spanner.rb +12 -15
- data/lib/spanner_client_ext.rb +6 -2
- metadata +30 -20
- data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +0 -221
- data/acceptance/cases/models/interleave_test.rb +0 -113
- data/acceptance/cases/transactions/optimistic_locking_test.rb +0 -152
- data/acceptance/models/album.rb +0 -15
- data/acceptance/models/track.rb +0 -23
- data/examples/snippets/interleaved-tables-before-7.1/README.md +0 -167
- data/examples/snippets/interleaved-tables-before-7.1/Rakefile +0 -13
- data/examples/snippets/interleaved-tables-before-7.1/application.rb +0 -122
- data/examples/snippets/interleaved-tables-before-7.1/config/database.yml +0 -9
- data/examples/snippets/interleaved-tables-before-7.1/db/migrate/01_create_tables.rb +0 -44
- data/examples/snippets/interleaved-tables-before-7.1/db/seeds.rb +0 -40
- data/examples/snippets/interleaved-tables-before-7.1/models/album.rb +0 -20
- data/examples/snippets/interleaved-tables-before-7.1/models/singer.rb +0 -18
- data/examples/snippets/interleaved-tables-before-7.1/models/track.rb +0 -28
|
@@ -1,13 +0,0 @@
|
|
|
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
|
-
require_relative "../config/environment"
|
|
8
|
-
require "sinatra/activerecord/rake"
|
|
9
|
-
|
|
10
|
-
desc "Sample showing how to work with interleaved tables in ActiveRecord."
|
|
11
|
-
task :run do
|
|
12
|
-
Dir.chdir("..") { sh "bundle exec rake run[interleaved-tables-before-7.1]" }
|
|
13
|
-
end
|
|
@@ -1,122 +0,0 @@
|
|
|
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
|
-
require "io/console"
|
|
8
|
-
require_relative "../config/environment"
|
|
9
|
-
require_relative "models/singer"
|
|
10
|
-
require_relative "models/album"
|
|
11
|
-
require_relative "models/track"
|
|
12
|
-
|
|
13
|
-
class Application
|
|
14
|
-
def self.run
|
|
15
|
-
# List all singers, albums and tracks.
|
|
16
|
-
list_singers_albums
|
|
17
|
-
|
|
18
|
-
# Create a new album with some tracks.
|
|
19
|
-
create_new_album
|
|
20
|
-
|
|
21
|
-
# Try to update the singer of an album. This is not possible as albums are interleaved in singers.
|
|
22
|
-
update_singer_of_album
|
|
23
|
-
|
|
24
|
-
# Try to delete a singer that has at least one album. This is NOT possible as albums is NOT marked with
|
|
25
|
-
# ON DELETE CASCADE.
|
|
26
|
-
delete_singer_with_albums
|
|
27
|
-
|
|
28
|
-
# Try to delete an album that has at least one track. This IS possible as tracks IS marked with
|
|
29
|
-
# ON DELETE CASCADE.
|
|
30
|
-
delete_album_with_tracks
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def self.find_singer
|
|
34
|
-
singerid = Singer.all.sample.singerid
|
|
35
|
-
|
|
36
|
-
singer = Singer.find singerid
|
|
37
|
-
puts "Found singer: #{singer.first_name} #{singer.last_name}"
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def self.find_album
|
|
41
|
-
singer = Singer.all.sample
|
|
42
|
-
albumid = singer.albums.sample.albumid
|
|
43
|
-
|
|
44
|
-
album = Album.find [singer.singerid, albumid]
|
|
45
|
-
puts "Found album: #{album.title}"
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def self.list_singers_albums
|
|
49
|
-
puts ""
|
|
50
|
-
puts "Listing all singers with corresponding albums and tracks"
|
|
51
|
-
Singer.all.order("last_name, first_name").each do |singer|
|
|
52
|
-
puts "#{singer.first_name} #{singer.last_name} has #{singer.albums.count} albums:"
|
|
53
|
-
singer.albums.order("title").each do |album|
|
|
54
|
-
puts " #{album.title} has #{album.tracks.count} tracks:"
|
|
55
|
-
album.tracks.each do |track|
|
|
56
|
-
puts " #{track.title}"
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def self.create_new_album
|
|
63
|
-
# Create a new album with some tracks.
|
|
64
|
-
puts ""
|
|
65
|
-
singer = Singer.all.sample
|
|
66
|
-
puts "Creating a new album for #{singer.first_name} #{singer.last_name}"
|
|
67
|
-
album = singer.albums.build title: "New Title"
|
|
68
|
-
# NOTE: When adding multiple elements to a collection, you *MUST* set the primary key value (i.e. trackid).
|
|
69
|
-
# Otherwise, ActiveRecord thinks that you are adding the same record multiple times and will only add one.
|
|
70
|
-
album.tracks.build title: "Track 1", duration: 3.5, singer: singer, trackid: Track.next_sequence_value
|
|
71
|
-
album.tracks.build title: "Track 2", duration: 3.6, singer: singer, trackid: Track.next_sequence_value
|
|
72
|
-
# This will save the album and corresponding tracks in one transaction.
|
|
73
|
-
album.save!
|
|
74
|
-
|
|
75
|
-
album.reload
|
|
76
|
-
puts "Album #{album.title} has #{album.tracks.count} tracks:"
|
|
77
|
-
album.tracks.order("title").each do |track|
|
|
78
|
-
puts " #{track.title} with duration #{track.duration}"
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def self.update_singer_of_album
|
|
83
|
-
# It is not possible to change the singer of an album or the album of a track. This is because the associations
|
|
84
|
-
# between these are not traditional foreign keys, but an immutable parent-child relationship.
|
|
85
|
-
album = Album.all.sample
|
|
86
|
-
new_singer = Singer.all.where.not(singerid: album.singer).sample
|
|
87
|
-
# This will fail as we cannot assign a new singer to an album as it is an INTERLEAVE IN PARENT relationship.
|
|
88
|
-
begin
|
|
89
|
-
album.update! singer: new_singer
|
|
90
|
-
raise StandardError, "Unexpected error: Updating the singer of an album should not be possible."
|
|
91
|
-
rescue ActiveRecord::StatementInvalid
|
|
92
|
-
puts ""
|
|
93
|
-
puts "Failed to update the singer of an album. This is expected."
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def self.delete_singer_with_albums
|
|
98
|
-
# Deleting a singer that has albums is not possible, as the INTERLEAVE IN PARENT of albums is not marked with
|
|
99
|
-
# ON DELETE CASCADE.
|
|
100
|
-
singer = Album.all.sample.singer
|
|
101
|
-
begin
|
|
102
|
-
singer.delete
|
|
103
|
-
raise StandardError, "Unexpected error: Updating the singer of an album should not be possible."
|
|
104
|
-
rescue ActiveRecord::StatementInvalid
|
|
105
|
-
puts ""
|
|
106
|
-
puts "Failed to delete a singer that has #{singer.albums.count} albums. This is expected."
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def self.delete_album_with_tracks
|
|
111
|
-
# Deleting an album with tracks is supported, as the INTERLEAVE IN PARENT relationship between tracks and albums is
|
|
112
|
-
# marked with ON DELETE CASCADE.
|
|
113
|
-
puts ""
|
|
114
|
-
puts "Total track count: #{Track.count}"
|
|
115
|
-
album = Track.all.sample.album
|
|
116
|
-
puts "Deleting album #{album.title} with #{album.tracks.count} tracks"
|
|
117
|
-
album.delete
|
|
118
|
-
puts "Total track count after deletion: #{Track.count}"
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
Application.run
|
|
@@ -1,44 +0,0 @@
|
|
|
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
|
-
class CreateTables < ActiveRecord::Migration[6.0]
|
|
8
|
-
def change
|
|
9
|
-
# Execute the entire migration as one DDL batch.
|
|
10
|
-
connection.ddl_batch do
|
|
11
|
-
create_table :singers, id: false do |t|
|
|
12
|
-
# Explicitly define the primary key with a custom name to prevent all primary key columns from being named `id`.
|
|
13
|
-
t.primary_key :singerid
|
|
14
|
-
t.string :first_name
|
|
15
|
-
t.string :last_name
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
create_table :albums, id: false do |t|
|
|
19
|
-
# Interleave the `albums` table in the parent table `singers`.
|
|
20
|
-
t.interleave_in :singers
|
|
21
|
-
t.primary_key :albumid
|
|
22
|
-
# `singerid` is defined as a `parent_key` which makes it a part of the primary key in the table definition, but
|
|
23
|
-
# it is not presented to ActiveRecord as part of the primary key, to prevent ActiveRecord from considering this
|
|
24
|
-
# to be an entity with a composite primary key (which is not supported by ActiveRecord).
|
|
25
|
-
t.parent_key :singerid
|
|
26
|
-
t.string :title
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
create_table :tracks, id: false do |t|
|
|
30
|
-
# Interleave the `tracks` table in the parent table `albums` and cascade delete all tracks that belong to an
|
|
31
|
-
# album when an album is deleted.
|
|
32
|
-
t.interleave_in :albums, :cascade
|
|
33
|
-
# `trackid` is considered the only primary key column by ActiveRecord.
|
|
34
|
-
t.primary_key :trackid
|
|
35
|
-
# `singerid` and `albumid` form the parent key of `tracks`. These are part of the primary key definition in the
|
|
36
|
-
# database, but are presented as parent keys to ActiveRecord.
|
|
37
|
-
t.parent_key :singerid
|
|
38
|
-
t.parent_key :albumid
|
|
39
|
-
t.string :title
|
|
40
|
-
t.numeric :duration
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
@@ -1,40 +0,0 @@
|
|
|
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
|
-
require_relative "../../config/environment"
|
|
8
|
-
require_relative "../models/singer"
|
|
9
|
-
require_relative "../models/album"
|
|
10
|
-
require_relative "../models/track"
|
|
11
|
-
|
|
12
|
-
first_names = %w[Pete Alice John Ethel Trudy Naomi Wendy Ruben Thomas Elly]
|
|
13
|
-
last_names = %w[Wendelson Allison Peterson Johnson Henderson Ericsson Aronson Tennet Courtou]
|
|
14
|
-
|
|
15
|
-
adjectives = %w[daily happy blue generous cooked bad open]
|
|
16
|
-
nouns = %w[windows potatoes bank street tree glass bottle]
|
|
17
|
-
|
|
18
|
-
verbs = %w[operate waste package chew yield express polish stress slip want cough campaign cultivate report park refer]
|
|
19
|
-
adverbs = %w[directly right hopefully personally economically privately supposedly consequently fully later urgently]
|
|
20
|
-
|
|
21
|
-
durations = [3.14, 5.4, 3.3, 4.1, 5.0, 3.2, 3.0, 3.5, 4.0, 4.5, 5.5, 6.0]
|
|
22
|
-
|
|
23
|
-
# This ensures all the records are inserted using one read/write transaction that will use mutations instead of DML.
|
|
24
|
-
ActiveRecord::Base.transaction isolation: :buffered_mutations do
|
|
25
|
-
singers = []
|
|
26
|
-
5.times do
|
|
27
|
-
singers << Singer.create(first_name: first_names.sample, last_name: last_names.sample)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
albums = []
|
|
31
|
-
20.times do
|
|
32
|
-
singer = singers.sample
|
|
33
|
-
albums << Album.create(title: "#{adjectives.sample} #{nouns.sample}", singer: singer)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
200.times do
|
|
37
|
-
album = albums.sample
|
|
38
|
-
Track.create title: "#{verbs.sample} #{adverbs.sample}", duration: durations.sample, album: album
|
|
39
|
-
end
|
|
40
|
-
end
|
|
@@ -1,20 +0,0 @@
|
|
|
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
|
-
require "composite_primary_keys"
|
|
8
|
-
|
|
9
|
-
class Album < ActiveRecord::Base
|
|
10
|
-
# Use the `composite_primary_key` gem to create a composite primary key definition for the model.
|
|
11
|
-
self.primary_keys = :singerid, :albumid
|
|
12
|
-
|
|
13
|
-
# `albums` is defined as INTERLEAVE IN PARENT `singers`.
|
|
14
|
-
# The primary key of `singers` is `singerid`.
|
|
15
|
-
belongs_to :singer, foreign_key: :singerid
|
|
16
|
-
|
|
17
|
-
# `tracks` is defined as INTERLEAVE IN PARENT `albums`.
|
|
18
|
-
# The primary key of `albums` is (`singerid`, `albumid`).
|
|
19
|
-
has_many :tracks, foreign_key: [:singerid, :albumid]
|
|
20
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
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
|
-
class Singer < ActiveRecord::Base
|
|
8
|
-
# `albums` is defined as INTERLEAVE IN PARENT `singers`.
|
|
9
|
-
# The primary key of `albums` is (`singerid`, `albumid`).
|
|
10
|
-
has_many :albums, foreign_key: :singerid
|
|
11
|
-
|
|
12
|
-
# `tracks` is defined as INTERLEAVE IN PARENT `albums`.
|
|
13
|
-
# The primary key of `tracks` is [`singerid`, `albumid`, `trackid`].
|
|
14
|
-
# The `singerid` column can be used to associate tracks with a singer without the need to go through albums.
|
|
15
|
-
# Note also that the inclusion of `singerid` as a column in `tracks` is required in order to make `tracks` a child
|
|
16
|
-
# table of `albums` which has primary key (`singerid`, `albumid`).
|
|
17
|
-
has_many :tracks, foreign_key: :singerid
|
|
18
|
-
end
|
|
@@ -1,28 +0,0 @@
|
|
|
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
|
-
class Track < ActiveRecord::Base
|
|
8
|
-
# Use the `composite_primary_key` gem to create a composite primary key definition for the model.
|
|
9
|
-
self.primary_keys = :singerid, :albumid, :trackid
|
|
10
|
-
|
|
11
|
-
# `tracks` is defined as INTERLEAVE IN PARENT `albums`. The primary key of `albums` is ()`singerid`, `albumid`).
|
|
12
|
-
belongs_to :album, foreign_key: [:singerid, :albumid]
|
|
13
|
-
|
|
14
|
-
# `tracks` also has a `singerid` column should be used to associate a Track with a Singer.
|
|
15
|
-
belongs_to :singer, foreign_key: :singerid
|
|
16
|
-
|
|
17
|
-
# Override the default initialize method to automatically set the singer attribute when an album is given.
|
|
18
|
-
def initialize attributes = nil
|
|
19
|
-
super
|
|
20
|
-
self.singer ||= album&.singer
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def album=value
|
|
24
|
-
super
|
|
25
|
-
# Ensure the singer of this track is equal to the singer of the album that is set.
|
|
26
|
-
self.singer = value&.singer
|
|
27
|
-
end
|
|
28
|
-
end
|