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.
- 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
|