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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df28341210b0da8f86475dc621e5c4113ad00a72b23fa6bc3e224a0064c7e267
|
4
|
+
data.tar.gz: 00eed36443cb2cd7c99869a6e2ff3821c16fd2a16a3d937dd831c3226743ef19
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a53ee5f7ab4d576a969631ce6125557a6171f91dd372f71c03c34c62b67b9363e0898d4b8d7ff3836d58efd73fd2a71dbf108668c5e837634864e017d129b8ca
|
7
|
+
data.tar.gz: 665f4e837ee3dee167c2d5e4160dfe03fb3008903f3be50dbae50dd07fcea8b01b7b561b683c825c460a3462d0f729c5ace143c4285d9eb05b2871776086cd94
|
@@ -18,14 +18,12 @@ jobs:
|
|
18
18
|
strategy:
|
19
19
|
max-parallel: 4
|
20
20
|
matrix:
|
21
|
-
ruby: ["
|
22
|
-
ar: ["~>
|
21
|
+
ruby: ["3.1", "3.2", "3.3"]
|
22
|
+
ar: ["~> 7.0.0", "~> 7.1.0", "~> 7.2.0", "~> 8.0.0"]
|
23
23
|
# Exclude combinations that are not supported.
|
24
24
|
exclude:
|
25
|
-
- ruby: "
|
26
|
-
ar: "~>
|
27
|
-
- ruby: "3.0"
|
28
|
-
ar: "~> 7.2.0"
|
25
|
+
- ruby: "3.1"
|
26
|
+
ar: "~> 8.0.0"
|
29
27
|
env:
|
30
28
|
AR_VERSION: ${{ matrix.ar }}
|
31
29
|
steps:
|
data/.github/workflows/ci.yaml
CHANGED
@@ -10,14 +10,12 @@ jobs:
|
|
10
10
|
strategy:
|
11
11
|
max-parallel: 4
|
12
12
|
matrix:
|
13
|
-
ruby: ["
|
14
|
-
ar: ["~>
|
13
|
+
ruby: ["3.1", "3.2", "3.3"]
|
14
|
+
ar: ["~> 7.0.0", "~> 7.1.0", "~> 7.2.0", "~> 8.0.0"]
|
15
15
|
# Exclude combinations that are not supported.
|
16
16
|
exclude:
|
17
|
-
- ruby: "
|
18
|
-
ar: "~>
|
19
|
-
- ruby: "3.0"
|
20
|
-
ar: "~> 7.2.0"
|
17
|
+
- ruby: "3.1"
|
18
|
+
ar: "~> 8.0.0"
|
21
19
|
env:
|
22
20
|
AR_VERSION: ${{ matrix.ar }}
|
23
21
|
steps:
|
@@ -18,14 +18,12 @@ jobs:
|
|
18
18
|
strategy:
|
19
19
|
max-parallel: 4
|
20
20
|
matrix:
|
21
|
-
ruby: ["
|
22
|
-
ar: ["~>
|
21
|
+
ruby: ["3.1", "3.2", "3.3"]
|
22
|
+
ar: ["~> 7.0.0", "~> 7.1.0", "~> 7.2.0", "~> 8.0.0"]
|
23
23
|
# Exclude combinations that are not supported.
|
24
24
|
exclude:
|
25
|
-
- ruby: "
|
26
|
-
ar: "~>
|
27
|
-
- ruby: "3.0"
|
28
|
-
ar: "~> 7.2.0"
|
25
|
+
- ruby: "3.1"
|
26
|
+
ar: "~> 8.0.0"
|
29
27
|
env:
|
30
28
|
AR_VERSION: ${{ matrix.ar }}
|
31
29
|
steps:
|
@@ -11,14 +11,12 @@ jobs:
|
|
11
11
|
max-parallel: 4
|
12
12
|
matrix:
|
13
13
|
# Run acceptance tests all supported combinations of Ruby and ActiveRecord.
|
14
|
-
ruby: ["
|
15
|
-
ar: ["~>
|
14
|
+
ruby: ["3.1", "3.2", "3.3"]
|
15
|
+
ar: ["~> 7.0.0", "~> 7.1.0", "~> 7.2.0", "~> 8.0.0"]
|
16
16
|
# Exclude combinations that are not supported.
|
17
17
|
exclude:
|
18
|
-
- ruby: "
|
19
|
-
ar: "~>
|
20
|
-
- ruby: "3.0"
|
21
|
-
ar: "~> 7.2.0"
|
18
|
+
- ruby: "3.1"
|
19
|
+
ar: "~> 8.0.0"
|
22
20
|
env:
|
23
21
|
AR_VERSION: ${{ matrix.ar }}
|
24
22
|
steps:
|
@@ -0,0 +1,30 @@
|
|
1
|
+
on:
|
2
|
+
pull_request:
|
3
|
+
name: samples
|
4
|
+
jobs:
|
5
|
+
test:
|
6
|
+
runs-on: ubuntu-latest
|
7
|
+
|
8
|
+
strategy:
|
9
|
+
max-parallel: 4
|
10
|
+
matrix:
|
11
|
+
ruby: ["3.1", "3.2", "3.3"]
|
12
|
+
ar: ["~> 7.0.0", "~> 7.1.0", "~> 7.2.0", "~> 8.0.0"]
|
13
|
+
# Exclude combinations that are not supported.
|
14
|
+
exclude:
|
15
|
+
- ruby: "3.1"
|
16
|
+
ar: "~> 8.0.0"
|
17
|
+
env:
|
18
|
+
AR_VERSION: ${{ matrix.ar }}
|
19
|
+
steps:
|
20
|
+
- uses: actions/checkout@v4
|
21
|
+
- name: Set up Ruby
|
22
|
+
uses: ruby/setup-ruby@v1
|
23
|
+
with:
|
24
|
+
bundler-cache: false
|
25
|
+
ruby-version: ${{ matrix.ruby }}
|
26
|
+
- name: Install dependencies
|
27
|
+
run: bundle install
|
28
|
+
- name: Run samples
|
29
|
+
run: bundle exec rake all
|
30
|
+
working-directory: examples/snippets
|
data/.kokoro/release.sh
CHANGED
@@ -8,6 +8,4 @@ export GEM_HOME=$HOME/.gem
|
|
8
8
|
export PATH=$GEM_HOME/bin:$PATH
|
9
9
|
|
10
10
|
gem install --no-document toys
|
11
|
-
toys release
|
12
|
-
python3 -m releasetool publish-reporter-script > /tmp/publisher-script; source /tmp/publisher-script
|
13
|
-
toys release perform -v --enable-docs < /dev/null
|
11
|
+
toys release perform -v --reporter-org=googleapis --enable-docs --force-republish --enable-docs --enable-rad < /dev/null
|
data/.rubocop.yml
CHANGED
@@ -16,7 +16,7 @@ AllCops:
|
|
16
16
|
|
17
17
|
Documentation:
|
18
18
|
Enabled: false
|
19
|
-
Layout/
|
19
|
+
Layout/HashAlignment:
|
20
20
|
Enabled: false
|
21
21
|
Metrics/ClassLength:
|
22
22
|
Enabled: false
|
@@ -24,7 +24,7 @@ Metrics/ModuleLength:
|
|
24
24
|
Enabled: false
|
25
25
|
Naming/RescuedExceptionsVariableName:
|
26
26
|
Enabled: false
|
27
|
-
Naming/
|
27
|
+
Naming/MethodParameterName:
|
28
28
|
Enabled: false
|
29
29
|
Style/EmptyMethod:
|
30
30
|
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
### 2.0.0 (2025-01-23)
|
4
|
+
|
5
|
+
### ⚠ BREAKING CHANGES
|
6
|
+
|
7
|
+
* drop support for Rails 6.1 ([#346](https://github.com/googleapis/ruby-spanner-activerecord/issues/346))
|
8
|
+
* deserialize BYTES to StringIO ([#343](https://github.com/googleapis/ruby-spanner-activerecord/issues/343))
|
9
|
+
|
10
|
+
#### Features
|
11
|
+
|
12
|
+
* drop support for Rails 6.1 ([#346](https://github.com/googleapis/ruby-spanner-activerecord/issues/346))
|
13
|
+
* support Rails 8.0 ([#331](https://github.com/googleapis/ruby-spanner-activerecord/issues/331))
|
14
|
+
#### Bug Fixes
|
15
|
+
|
16
|
+
* deserialize BYTES to StringIO ([#343](https://github.com/googleapis/ruby-spanner-activerecord/issues/343))
|
17
|
+
#### Documentation
|
18
|
+
|
19
|
+
* add rails dbconsole to list of limitations ([#224](https://github.com/googleapis/ruby-spanner-activerecord/issues/224))
|
20
|
+
|
3
21
|
### 1.8.0 (2024-12-12)
|
4
22
|
|
5
23
|
#### Features
|
data/Gemfile
CHANGED
@@ -3,17 +3,18 @@ source "https://rubygems.org"
|
|
3
3
|
# Specify your gem's dependencies in activerecord-spanner.gemspec
|
4
4
|
gemspec
|
5
5
|
|
6
|
-
|
6
|
+
ar_version = ENV.fetch("AR_VERSION", "~> 7.1.0")
|
7
|
+
gem "activerecord", ar_version
|
7
8
|
gem "ostruct"
|
8
9
|
gem "minitest", "~> 5.25.0"
|
9
10
|
gem "minitest-rg", "~> 5.3.0"
|
10
|
-
gem "pry", "~> 0.
|
11
|
-
gem "pry-byebug", "~> 3.
|
11
|
+
gem "pry", "~> 0.14.2"
|
12
|
+
gem "pry-byebug", "~> 3.10.1"
|
12
13
|
# Add sqlite3 for testing for compatibility with other adapters.
|
13
|
-
gem 'sqlite3'
|
14
|
+
gem 'sqlite3'
|
14
15
|
|
15
16
|
# Required for samples and testing.
|
16
|
-
install_if -> {
|
17
|
+
install_if -> { ar_version.dup.to_s.sub("~>", "").strip < "7.1.0" && !ENV["SKIP_COMPOSITE_PK"] } do
|
17
18
|
gem "composite_primary_keys"
|
18
19
|
end
|
19
20
|
|
data/README.md
CHANGED
@@ -89,15 +89,17 @@ Some noteworthy examples in the snippets directory:
|
|
89
89
|
|
90
90
|
## Limitations
|
91
91
|
|
92
|
-
| Limitation | Comment
|
93
|
-
|
94
|
-
| Interleaved tables require composite primary keys | Cloud Spanner requires composite primary keys for interleaved tables. See {file:examples/snippets/interleaved-tables/README.md this example} for an example on how to use interleaved tables with ActiveRecord
|
95
|
-
| Lack of sequential IDs | Cloud Spanner uses either using bit-reversed sequences or UUID4 to generated primary keys to avoid [hotspotting](https://cloud.google.com/spanner/docs/schema-design#uuid_primary_key) so you SHOULD NOT rely on IDs being sorted
|
96
|
-
| Table without Primary Key | Cloud Spanner support does not support tables without a primary key.
|
97
|
-
| Table names CANNOT have spaces within them whether back-ticked or not | Cloud Spanner DOES NOT support tables with spaces in them for example `Entity ID`
|
98
|
-
| Table names CANNOT have punctuation marks and MUST contain valid UTF-8 | Cloud Spanner DOES NOT support punctuation marks e.g. periods ".", question marks "?" in table names
|
99
|
-
| Index with fields length [add_index](https://apidock.com/rails/v5.2.3/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index) | Cloud Spanner does not support index with fields length
|
100
|
-
| Only GoogleSQL-dialect databases | Cloud Spanner supports both GoogleSQL- and PostgreSQL-dialect databases. This adapter only supports GoogleSQL-dialect databases.
|
92
|
+
| Limitation | Comment | Resolution |
|
93
|
+
|-----------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
|
94
|
+
| Interleaved tables require composite primary keys | Cloud Spanner requires composite primary keys for interleaved tables. See {file:examples/snippets/interleaved-tables/README.md this example} for an example on how to use interleaved tables with ActiveRecord | Use composite primary keys. |
|
95
|
+
| Lack of sequential IDs | Cloud Spanner uses either using bit-reversed sequences or UUID4 to generated primary keys to avoid [hotspotting](https://cloud.google.com/spanner/docs/schema-design#uuid_primary_key) so you SHOULD NOT rely on IDs being sorted | Use either UUID4s or bit-reversed sequences to automatically generate primary keys. |
|
96
|
+
| Table without Primary Key | Cloud Spanner support does not support tables without a primary key. | Always define a primary key for your table. |
|
97
|
+
| Table names CANNOT have spaces within them whether back-ticked or not | Cloud Spanner DOES NOT support tables with spaces in them for example `Entity ID` | Ensure that your table names don't contain spaces. |
|
98
|
+
| Table names CANNOT have punctuation marks and MUST contain valid UTF-8 | Cloud Spanner DOES NOT support punctuation marks e.g. periods ".", question marks "?" in table names | Ensure that your table names don't contain punctuation marks. |
|
99
|
+
| Index with fields length [add_index](https://apidock.com/rails/v5.2.3/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index) | Cloud Spanner does not support index with fields length | Ensure that your database definition does not include index definitions with field lengths. |
|
100
|
+
| Only GoogleSQL-dialect databases | Cloud Spanner supports both GoogleSQL- and PostgreSQL-dialect databases. This adapter only supports GoogleSQL-dialect databases. You can use the [PostgreSQL ActiveRecord provider in combination with PGAdapter](https://github.com/GoogleCloudPlatform/pgadapter/tree/postgresql-dialect/samples/ruby/activerecord) for Spanner PostgreSQL databases. | |
|
101
|
+
| `rails dbconsole` is not supported. | The `rails dbconsole` is not supported for Spanner databases. | |
|
102
|
+
|
101
103
|
|
102
104
|
## Contributing
|
103
105
|
|
@@ -13,9 +13,6 @@ module ActiveRecord
|
|
13
13
|
class CommandRecorderTest < SpannerAdapter::TestCase
|
14
14
|
include SpannerAdapter::Migration::TestHelper
|
15
15
|
|
16
|
-
VERSION_6_1_0 = Gem::Version.create "6.1.0"
|
17
|
-
VERSION_6_0_3 = Gem::Version.create "6.0.3"
|
18
|
-
|
19
16
|
def setup
|
20
17
|
skip_test_table_create!
|
21
18
|
super
|
@@ -203,20 +200,12 @@ module ActiveRecord
|
|
203
200
|
|
204
201
|
def test_invert_add_index
|
205
202
|
remove = @recorder.inverse_of :add_index, [:table, [:one, :two]]
|
206
|
-
|
207
|
-
assert_equal [:remove_index, [:table, { column: [:one, :two] }]], remove
|
208
|
-
else
|
209
|
-
assert_equal [:remove_index, [:table, [:one, :two]], nil], remove
|
210
|
-
end
|
203
|
+
assert_equal [:remove_index, [:table, [:one, :two]], nil], remove
|
211
204
|
end
|
212
205
|
|
213
206
|
def test_invert_add_index_with_name
|
214
207
|
remove = @recorder.inverse_of :add_index, [:table, [:one, :two], name: "new_index"]
|
215
|
-
|
216
|
-
assert_equal [:remove_index, [:table, { name: "new_index" }]], remove
|
217
|
-
else
|
218
|
-
assert_equal [:remove_index, [:table, [:one, :two], {:name=>"new_index"}], nil], remove
|
219
|
-
end
|
208
|
+
assert_equal [:remove_index, [:table, [:one, :two], {:name=>"new_index"}], nil], remove
|
220
209
|
end
|
221
210
|
|
222
211
|
def test_invert_remove_index
|
@@ -226,11 +215,7 @@ module ActiveRecord
|
|
226
215
|
|
227
216
|
def test_invert_remove_index_with_options
|
228
217
|
add = @recorder.inverse_of :remove_index, [:table, { column: :one }]
|
229
|
-
|
230
|
-
assert_equal [:add_index, [:table, :one, {} ]], add
|
231
|
-
else
|
232
|
-
assert_equal [:add_index, [:table, :one ]], add
|
233
|
-
end
|
218
|
+
assert_equal [:add_index, [:table, :one ]], add
|
234
219
|
end
|
235
220
|
|
236
221
|
def test_invert_remove_index_with_positional_column
|
@@ -250,11 +235,7 @@ module ActiveRecord
|
|
250
235
|
|
251
236
|
def test_invert_remove_index_with_no_special_options
|
252
237
|
add = @recorder.inverse_of :remove_index, [:table, { column: [:one, :two] }]
|
253
|
-
|
254
|
-
assert_equal [:add_index, [:table, [:one, :two], {}]], add
|
255
|
-
else
|
256
|
-
assert_equal [:add_index, [:table, [:one, :two]]], add
|
257
|
-
end
|
238
|
+
assert_equal [:add_index, [:table, [:one, :two]]], add
|
258
239
|
end
|
259
240
|
|
260
241
|
def test_invert_remove_index_with_no_column
|
@@ -315,11 +296,7 @@ module ActiveRecord
|
|
315
296
|
|
316
297
|
def test_invert_add_foreign_key
|
317
298
|
enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people]
|
318
|
-
|
319
|
-
assert_equal [:remove_foreign_key, [:dogs, :people]], enable
|
320
|
-
else
|
321
|
-
assert_equal [:remove_foreign_key, [:dogs, :people], nil], enable
|
322
|
-
end
|
299
|
+
assert_equal [:remove_foreign_key, [:dogs, :people], nil], enable
|
323
300
|
end
|
324
301
|
|
325
302
|
def test_invert_remove_foreign_key
|
@@ -329,11 +306,7 @@ module ActiveRecord
|
|
329
306
|
|
330
307
|
def test_invert_add_foreign_key_with_column
|
331
308
|
enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people, column: "owner_id"]
|
332
|
-
|
333
|
-
assert_equal [:remove_foreign_key, [:dogs, column: "owner_id"]], enable
|
334
|
-
else
|
335
|
-
assert_equal [:remove_foreign_key, [:dogs, :people, column: "owner_id"], nil], enable
|
336
|
-
end
|
309
|
+
assert_equal [:remove_foreign_key, [:dogs, :people, column: "owner_id"], nil], enable
|
337
310
|
end
|
338
311
|
|
339
312
|
def test_invert_remove_foreign_key_with_column
|
@@ -343,11 +316,7 @@ module ActiveRecord
|
|
343
316
|
|
344
317
|
def test_invert_add_foreign_key_with_column_and_name
|
345
318
|
enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"]
|
346
|
-
|
347
|
-
assert_equal [:remove_foreign_key, [:dogs, { name: "fk" }]], enable
|
348
|
-
else
|
349
|
-
assert_equal [:remove_foreign_key, [:dogs, :people, { column: "owner_id", name: "fk" }], nil], enable
|
350
|
-
end
|
319
|
+
assert_equal [:remove_foreign_key, [:dogs, :people, { column: "owner_id", name: "fk" }], nil], enable
|
351
320
|
end
|
352
321
|
|
353
322
|
def test_invert_remove_foreign_key_with_column_and_name
|
@@ -76,12 +76,7 @@ module ActiveRecord
|
|
76
76
|
t.references :foo, polymorphic: true, index: true
|
77
77
|
end
|
78
78
|
end
|
79
|
-
|
80
|
-
if ActiveRecord::gem_version < Gem::Version.create('6.1.0')
|
81
|
-
assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo_type_and_foo_id)
|
82
|
-
else
|
83
|
-
assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo)
|
84
|
-
end
|
79
|
+
assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo)
|
85
80
|
end
|
86
81
|
|
87
82
|
def test_creates_index_for_existing_table
|
@@ -124,11 +119,7 @@ module ActiveRecord
|
|
124
119
|
end
|
125
120
|
end
|
126
121
|
|
127
|
-
|
128
|
-
assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo_type_and_foo_id)
|
129
|
-
else
|
130
|
-
assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo)
|
131
|
-
end
|
122
|
+
assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo)
|
132
123
|
end
|
133
124
|
end
|
134
125
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# Copyright 2025 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
|
+
# frozen_string_literal: true
|
8
|
+
|
9
|
+
require "test_helper"
|
10
|
+
require "test_helpers/with_separate_database"
|
11
|
+
require_relative "../../models/user"
|
12
|
+
require_relative "../../models/binary_project"
|
13
|
+
|
14
|
+
module Models
|
15
|
+
class DefaultValueTest < SpannerAdapter::TestCase
|
16
|
+
include TestHelpers::WithSeparateDatabase
|
17
|
+
|
18
|
+
def setup
|
19
|
+
super
|
20
|
+
|
21
|
+
connection.create_table :users, id: :binary do |t|
|
22
|
+
t.string :email, null: false
|
23
|
+
t.string :full_name, null: false
|
24
|
+
end
|
25
|
+
connection.create_table :binary_projects, id: :binary do |t|
|
26
|
+
t.string :name, null: false
|
27
|
+
t.string :description, null: false
|
28
|
+
t.binary :owner_id, null: false
|
29
|
+
t.foreign_key :users, column: :owner_id
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_includes_works
|
34
|
+
user = User.create!(
|
35
|
+
email: "test@example.com",
|
36
|
+
full_name: "Test User"
|
37
|
+
)
|
38
|
+
3.times do |i|
|
39
|
+
Project.create!(
|
40
|
+
name: "Project #{i}",
|
41
|
+
description: "Description #{i}",
|
42
|
+
owner: user
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
# First verify the association works without includes
|
47
|
+
projects = Project.all
|
48
|
+
assert_equal 3, projects.count
|
49
|
+
|
50
|
+
# Compare the base64 content instead of the StringIO objects
|
51
|
+
first_project = projects.first
|
52
|
+
assert_equal to_base64(user.id), to_base64(first_project.owner_id)
|
53
|
+
|
54
|
+
# Now verify includes is working
|
55
|
+
query_count = count_queries do
|
56
|
+
loaded_projects = Project.includes(:owner).to_a
|
57
|
+
loaded_projects.each do |project|
|
58
|
+
# Access the owner to ensure it's preloaded
|
59
|
+
assert_equal user.full_name, project.owner.full_name
|
60
|
+
end
|
61
|
+
assert_equal 3, loaded_projects.count
|
62
|
+
end
|
63
|
+
|
64
|
+
# Spanner should execute 2 queries: one for projects and one for users
|
65
|
+
assert_equal 2, query_count
|
66
|
+
|
67
|
+
user = User.all.includes(:projects).first
|
68
|
+
assert user
|
69
|
+
project_count = 0
|
70
|
+
user.projects.each do |_|
|
71
|
+
project_count += 1
|
72
|
+
end
|
73
|
+
assert_equal 3, project_count
|
74
|
+
assert_equal 3, user.projects.count
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def to_base64 buffer
|
80
|
+
buffer.rewind
|
81
|
+
value = buffer.read
|
82
|
+
Base64.strict_encode64 value.force_encoding("ASCII-8BIT")
|
83
|
+
end
|
84
|
+
|
85
|
+
def count_queries(&block)
|
86
|
+
count = 0
|
87
|
+
counter_fn = ->(name, started, finished, unique_id, payload) {
|
88
|
+
unless %w[CACHE SCHEMA].include?(payload[:name])
|
89
|
+
count += 1
|
90
|
+
end
|
91
|
+
}
|
92
|
+
|
93
|
+
ActiveSupport::Notifications.subscribed(counter_fn, "sql.active_record", &block)
|
94
|
+
count
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Copyright 2025 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
|
+
# frozen_string_literal: true
|
8
|
+
|
9
|
+
require_relative 'string_io'
|
10
|
+
|
11
|
+
class BinaryProject < ActiveRecord::Base
|
12
|
+
belongs_to :owner, class_name: 'User'
|
13
|
+
|
14
|
+
before_create :set_uuid
|
15
|
+
private
|
16
|
+
|
17
|
+
def set_uuid
|
18
|
+
self.id ||= StringIO.new(SecureRandom.random_bytes(16))
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Copyright 2025 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
|
+
# frozen_string_literal: true
|
8
|
+
|
9
|
+
# Add equality and hash functions to StringIO to use it for sets.
|
10
|
+
class StringIO
|
11
|
+
def ==(o)
|
12
|
+
o.class == self.class && self.to_base64 == o.to_base64
|
13
|
+
end
|
14
|
+
|
15
|
+
def eql?(o)
|
16
|
+
self == o
|
17
|
+
end
|
18
|
+
|
19
|
+
def hash
|
20
|
+
to_base64.hash
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_base64
|
24
|
+
self.rewind
|
25
|
+
value = self.read
|
26
|
+
Base64.strict_encode64 value.force_encoding("ASCII-8BIT")
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Copyright 2025 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
|
+
# frozen_string_literal: true
|
8
|
+
|
9
|
+
require_relative 'string_io'
|
10
|
+
|
11
|
+
class User < ActiveRecord::Base
|
12
|
+
has_many :binary_projects, foreign_key: :owner_id
|
13
|
+
|
14
|
+
before_create :set_uuid
|
15
|
+
private
|
16
|
+
|
17
|
+
def set_uuid
|
18
|
+
self.id ||= StringIO.new(SecureRandom.random_bytes(16))
|
19
|
+
end
|
20
|
+
end
|
data/acceptance/test_helper.rb
CHANGED
@@ -22,16 +22,16 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename f }
|
23
23
|
spec.require_paths = ["lib"]
|
24
24
|
|
25
|
-
spec.required_ruby_version = ">=
|
25
|
+
spec.required_ruby_version = ">= 3.1"
|
26
26
|
|
27
27
|
spec.add_dependency "google-cloud-spanner", "~> 2.18"
|
28
28
|
# Pin gRPC to 1.64.3, as 1.65 and 1.66 cause test runs to hang randomly.
|
29
29
|
spec.add_dependency "grpc", "1.64.3"
|
30
|
-
spec.add_runtime_dependency "activerecord", [">= 6.1", "<
|
30
|
+
spec.add_runtime_dependency "activerecord", [">= 6.1", "< 9"]
|
31
31
|
|
32
32
|
spec.add_development_dependency "autotest-suffix", "~> 1.1"
|
33
33
|
spec.add_development_dependency "bundler", "~> 2.0"
|
34
|
-
spec.add_development_dependency "google-style", "~> 1.
|
34
|
+
spec.add_development_dependency "google-style", "~> 1.30.1"
|
35
35
|
spec.add_development_dependency "minitest", "~> 5.10"
|
36
36
|
spec.add_development_dependency "minitest-autotest", "~> 1.0"
|
37
37
|
spec.add_development_dependency "minitest-focus", "~> 1.1"
|
data/benchmarks/application.rb
CHANGED
@@ -63,7 +63,7 @@ class Application
|
|
63
63
|
|
64
64
|
puts ""
|
65
65
|
puts "Press any key to end the application"
|
66
|
-
|
66
|
+
$stdin.getch
|
67
67
|
end
|
68
68
|
|
69
69
|
def self.execute_individual_benchmarks singer, client
|
@@ -120,9 +120,7 @@ class Application
|
|
120
120
|
sql = "SELECT * FROM Singers WHERE id=@id"
|
121
121
|
params = { id: singer[:id] }
|
122
122
|
param_types = { id: :INT64 }
|
123
|
-
client.execute(sql, params: params, types: param_types).rows.
|
124
|
-
return row
|
125
|
-
end
|
123
|
+
client.execute(sql, params: params, types: param_types).rows.first(1)
|
126
124
|
else
|
127
125
|
Singer.find singer.id
|
128
126
|
end
|
@@ -134,9 +132,7 @@ class Application
|
|
134
132
|
sql = "SELECT * FROM Singers WHERE id=@id"
|
135
133
|
params = { id: singer[:id] }
|
136
134
|
param_types = { id: :INT64 }
|
137
|
-
client.execute(sql, params: params, types: param_types).rows.
|
138
|
-
return row
|
139
|
-
end
|
135
|
+
client.execute(sql, params: params, types: param_types).rows.first(1)
|
140
136
|
else
|
141
137
|
singer.reload
|
142
138
|
end
|