activerecord-spanner-adapter 0.6.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/blunderbuss.yml +2 -0
- data/.github/sync-repo-settings.yaml +1 -1
- data/.github/workflows/acceptance-tests-on-emulator.yaml +1 -1
- data/.github/workflows/acceptance-tests-on-production.yaml +2 -2
- data/.github/workflows/ci.yaml +1 -1
- data/.github/workflows/release-please-label.yml +4 -4
- data/.github/workflows/release-please.yml +10 -9
- data/.github/workflows/rubocop.yaml +2 -2
- data/.release-please-manifest.json +3 -0
- data/.toys/release.rb +2 -2
- data/CHANGELOG.md +46 -23
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +1 -1
- data/README.md +3 -3
- data/acceptance/cases/models/query_test.rb +24 -0
- data/acceptance/cases/type/all_types_test.rb +16 -14
- data/examples/rails/README.md +9 -9
- data/examples/snippets/generated-column/db/schema.rb +3 -3
- data/examples/snippets/hints/README.md +19 -0
- data/examples/snippets/{interleaved-tables → hints}/Rakefile +2 -2
- data/examples/snippets/hints/application.rb +47 -0
- data/examples/snippets/{interleaved-tables → hints}/config/database.yml +0 -0
- data/examples/snippets/hints/db/migrate/01_create_tables.rb +23 -0
- data/examples/snippets/{interleaved-tables → hints}/db/schema.rb +10 -14
- data/examples/snippets/{interleaved-tables → hints}/db/seeds.rb +0 -11
- data/examples/snippets/hints/models/album.rb +9 -0
- data/examples/snippets/hints/models/singer.rb +9 -0
- data/examples/snippets/partitioned-dml/README.md +16 -0
- data/examples/snippets/partitioned-dml/Rakefile +13 -0
- data/examples/snippets/partitioned-dml/application.rb +48 -0
- data/examples/snippets/partitioned-dml/config/database.yml +8 -0
- data/examples/snippets/partitioned-dml/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/partitioned-dml/db/schema.rb +26 -0
- data/examples/snippets/partitioned-dml/db/seeds.rb +29 -0
- data/examples/snippets/partitioned-dml/models/album.rb +9 -0
- data/examples/snippets/partitioned-dml/models/singer.rb +9 -0
- data/lib/active_record/connection_adapters/spanner_adapter.rb +1 -1
- data/lib/active_record/tasks/spanner_database_tasks.rb +1 -1
- data/lib/active_record/type/spanner/array.rb +19 -5
- data/lib/activerecord_spanner_adapter/base.rb +31 -10
- data/lib/activerecord_spanner_adapter/connection.rb +46 -20
- data/lib/activerecord_spanner_adapter/information_schema.rb +2 -1
- data/lib/activerecord_spanner_adapter/transaction.rb +52 -21
- data/lib/activerecord_spanner_adapter/version.rb +1 -1
- data/lib/arel/visitors/spanner.rb +39 -0
- data/lib/spanner_client_ext.rb +4 -0
- data/release-please-config.json +19 -0
- metadata +24 -13
- data/examples/snippets/interleaved-tables/README.md +0 -152
- data/examples/snippets/interleaved-tables/application.rb +0 -109
- data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +0 -44
- data/examples/snippets/interleaved-tables/models/album.rb +0 -15
- data/examples/snippets/interleaved-tables/models/singer.rb +0 -20
- data/examples/snippets/interleaved-tables/models/track.rb +0 -25
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bcc2624f18893422bf8ac9c23d07fff039e79cee5f51b975054314f49820b694
|
4
|
+
data.tar.gz: 7145b48f67236eb7ee9aac20bd1dfa907268419f262abf82cd6973acba25ab64
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 49aed5257b191aee1e9271c1d04e91c2ff1663c78e9b1e70e89acda430587324991a10cb1b2aabdfd9b1e9d70ab862b3a1579fda5c032023175303fa260deeed
|
7
|
+
data.tar.gz: 1764369d2a370d4c8d35cbbcd3b9c45130514cc6e76bac67e5a9dafbf2bf020734b973be44f354b13da9acaaffdebaba78e31046da56c6e62efe0546aca30611
|
@@ -1,7 +1,7 @@
|
|
1
1
|
on:
|
2
2
|
push:
|
3
3
|
branches:
|
4
|
-
-
|
4
|
+
- main
|
5
5
|
pull_request:
|
6
6
|
name: acceptance tests on production
|
7
7
|
jobs:
|
@@ -35,7 +35,7 @@ jobs:
|
|
35
35
|
bundler-cache: true
|
36
36
|
ruby-version: ${{ matrix.ruby }}
|
37
37
|
- name: Setup GCloud
|
38
|
-
uses: google-github-actions/setup-gcloud@
|
38
|
+
uses: google-github-actions/setup-gcloud@v0
|
39
39
|
with:
|
40
40
|
project_id: ${{ secrets.GCP_PROJECT_ID }}
|
41
41
|
service_account_key: ${{ secrets.GCP_SA_KEY }}
|
data/.github/workflows/ci.yaml
CHANGED
@@ -2,21 +2,21 @@ name: release-please-label
|
|
2
2
|
on:
|
3
3
|
pull_request_target:
|
4
4
|
branches:
|
5
|
-
-
|
5
|
+
- main
|
6
6
|
types:
|
7
7
|
- opened
|
8
8
|
jobs:
|
9
9
|
release-please-label:
|
10
|
-
if: "${{ github.event.sender.login == 'yoshi-code-bot' && startsWith(github.event.pull_request.title, 'chore: release ') }}"
|
10
|
+
if: "${{ github.event.sender.login == 'yoshi-code-bot' && startsWith(github.event.pull_request.title, 'chore(main): release ') }}"
|
11
11
|
runs-on: ubuntu-latest
|
12
12
|
steps:
|
13
13
|
- name: ReleaseLabel
|
14
|
-
uses: actions/github-script@
|
14
|
+
uses: actions/github-script@v6
|
15
15
|
with:
|
16
16
|
github-token: ${{secrets.YOSHI_APPROVER_TOKEN}}
|
17
17
|
script: |
|
18
18
|
core.info('Labeling release');
|
19
|
-
await github.issues.addLabels({
|
19
|
+
await github.rest.issues.addLabels({
|
20
20
|
owner: context.repo.owner,
|
21
21
|
repo: context.repo.repo,
|
22
22
|
issue_number: context.payload.pull_request.number,
|
@@ -1,7 +1,7 @@
|
|
1
1
|
name: Release-Please
|
2
2
|
on:
|
3
3
|
schedule:
|
4
|
-
- cron: '
|
4
|
+
- cron: '57 10 * * *'
|
5
5
|
workflow_dispatch:
|
6
6
|
inputs:
|
7
7
|
gem:
|
@@ -21,19 +21,20 @@ jobs:
|
|
21
21
|
steps:
|
22
22
|
- name: Checkout repo
|
23
23
|
uses: actions/checkout@v2
|
24
|
-
- name: Install Ruby
|
24
|
+
- name: Install Ruby 3.0
|
25
25
|
uses: ruby/setup-ruby@v1
|
26
26
|
with:
|
27
|
-
ruby-version: "
|
28
|
-
- name: Install NodeJS
|
27
|
+
ruby-version: "3.0"
|
28
|
+
- name: Install NodeJS 16.x
|
29
29
|
uses: actions/setup-node@v2
|
30
30
|
with:
|
31
|
-
node-version: "
|
31
|
+
node-version: "16.x"
|
32
32
|
- name: Install tools
|
33
|
-
run: "gem install --no-document toys
|
33
|
+
run: "gem install --no-document toys"
|
34
34
|
- name: execute
|
35
35
|
run: |
|
36
|
-
toys release
|
36
|
+
toys release manifest -v \
|
37
|
+
--fork --skip-labeling \
|
37
38
|
--github-event-name=${{ github.event_name }} \
|
38
|
-
|
39
|
-
|
39
|
+
${{ github.event.inputs.args }} \
|
40
|
+
-- ${{ github.event.inputs.gem }}
|
@@ -4,7 +4,7 @@ on:
|
|
4
4
|
pull_request:
|
5
5
|
types: [opened, synchronize]
|
6
6
|
push:
|
7
|
-
branches: [
|
7
|
+
branches: [ main ]
|
8
8
|
|
9
9
|
jobs:
|
10
10
|
build:
|
@@ -18,7 +18,7 @@ jobs:
|
|
18
18
|
with:
|
19
19
|
ruby-version: '2.5'
|
20
20
|
- name: cache gems
|
21
|
-
uses: actions/cache@
|
21
|
+
uses: actions/cache@v2
|
22
22
|
with:
|
23
23
|
path: vendor/bundle
|
24
24
|
key: ${{ runner.os }}-rubocop-${{ hashFiles('**/Gemfile.lock') }}
|
data/.toys/release.rb
CHANGED
@@ -14,5 +14,5 @@
|
|
14
14
|
# See the License for the specific language governing permissions and
|
15
15
|
# limitations under the License.
|
16
16
|
|
17
|
-
load_git remote: "https://github.com/googleapis/
|
18
|
-
path: "
|
17
|
+
load_git remote: "https://github.com/googleapis/ruby-common-tools.git",
|
18
|
+
path: "toys/release"
|
data/CHANGELOG.md
CHANGED
@@ -1,35 +1,58 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
-
|
3
|
+
### 1.0.1 (2022-04-21)
|
4
4
|
|
5
|
+
#### Bug Fixes
|
5
6
|
|
6
|
-
|
7
|
+
* ActiveRecord::Type::Spanner::Array does not use element type
|
7
8
|
|
8
|
-
|
9
|
-
* support single stale reads ([#127](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/127)) ([a600628](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/a600628267355b808f478ed543bc505e73f95d4a))
|
10
|
-
* support stale reads in read-only transactions ([#126](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/126)) ([8bf7730](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/8bf77300283c01e951725dd5e457270db20e98d2))
|
9
|
+
#### Documentation
|
11
10
|
|
12
|
-
|
11
|
+
* add limitation of interleaved tables
|
12
|
+
* fix a couple of minor formatting issues
|
13
13
|
|
14
|
+
### 1.0.0 (2021-12-07)
|
14
15
|
|
15
|
-
|
16
|
+
* GA release
|
16
17
|
|
17
|
-
|
18
|
-
* Add support for ARRAY data type ([#86](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/86)) ([0c66a62](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/0c66a620cab968779de04faf48e03eec643ebea9))
|
19
|
-
* google-cloud-spanner version upgraded to 2.2 ([#55](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/55)) ([d7581d6](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/d7581d60bd9a9e7b9989565449119f73e2caa694))
|
20
|
-
* Support interleaved tables ([#83](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/83)) ([82265f9](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/82265f94ace79964639a2c65554714752be39724))
|
21
|
-
* retry session not found ([#81](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/81)) ([88fd3b7](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/88fd3b70a03a90de2b667bb0f2e86efe5dc9328b))
|
22
|
-
* support and test multiple ActiveRecord versions ([#107](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/107)) ([db9d96c](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/db9d96c44b9560f6904209df1a9aa42bf50a5844))
|
23
|
-
* support DDL batches on connection ([#72](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/72)) ([0d18cd4](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/0d18cd49641bdb567012d6ac88b1909461d42551))
|
24
|
-
* support generated columns ([#94](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/94)) ([68664eb](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/68664eb5c617abc2954dea274430f416e616a324))
|
25
|
-
* support interleaved indexes + test other index features ([#101](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/101)) ([812e0f7](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/812e0f7f60b36ec26a974f6fb48266de5d840652))
|
26
|
-
* support optimistic locking ([#92](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/92)) ([9eb71d8](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/9eb71d8a207a8df0406241bff5780593eb0afd34))
|
27
|
-
* support PDML transactions ([#106](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/106)) ([fa0599a](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/fa0599afe986a184bb6ab26340305eeaa753dafa))
|
28
|
-
* support prepared statements and query cache ([#74](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/74)) ([fed8258](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/fed825862c95e3e052410e3576de18fc3b7849b7))
|
29
|
-
* support read only transactions ([#80](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/80)) ([2d6097b](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/2d6097bd8f4530634a41dcdbcbb3a02614f482b8))
|
30
|
-
* support setting attributes to commit timestamp ([#89](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/89)) ([cdd8448](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/cdd844852da92fa4e2c43fd06eeef31310d6ff8a))
|
18
|
+
### 0.7.1 (2021-11-21)
|
31
19
|
|
20
|
+
#### Performance Improvements
|
32
21
|
|
33
|
-
|
22
|
+
* inline BeginTransaction with first statement in the transaction
|
34
23
|
|
35
|
-
|
24
|
+
### 0.7.0 (2021-10-03)
|
25
|
+
|
26
|
+
#### Features
|
27
|
+
|
28
|
+
* add support for query hints
|
29
|
+
|
30
|
+
### 0.6.0 (2021-09-09)
|
31
|
+
|
32
|
+
#### Features
|
33
|
+
|
34
|
+
* support JSON data type
|
35
|
+
* support single stale reads
|
36
|
+
* support stale reads in read-only transactions
|
37
|
+
|
38
|
+
### 0.5.0 (2021-08-31)
|
39
|
+
|
40
|
+
#### Features
|
41
|
+
|
42
|
+
* Add support for NUMERIC type
|
43
|
+
* Add support for ARRAY data type
|
44
|
+
* google-cloud-spanner version upgraded to 2.2
|
45
|
+
* retry session not found
|
46
|
+
* support and test multiple ActiveRecord versions
|
47
|
+
* support DDL batches on connection
|
48
|
+
* support generated columns
|
49
|
+
* support interleaved indexes + test other index features
|
50
|
+
* support optimistic locking
|
51
|
+
* support PDML transactions
|
52
|
+
* support prepared statements and query cache
|
53
|
+
* support read only transactions
|
54
|
+
* support setting attributes to commit timestamp
|
55
|
+
|
56
|
+
#### Performance Improvements
|
57
|
+
|
58
|
+
* add benchmarks
|
data/CONTRIBUTING.md
CHANGED
@@ -70,7 +70,7 @@ $ cd ruby-spanner-activerecord
|
|
70
70
|
$ bundle exec rubocop
|
71
71
|
```
|
72
72
|
|
73
|
-
The rubocop settings depend on [googleapis/ruby-style](https://github.com/googleapis/ruby-style/), in addition to [.rubocop.yml](https://github.com/googleapis/ruby-spanner-activerecord/blob/
|
73
|
+
The rubocop settings depend on [googleapis/ruby-style](https://github.com/googleapis/ruby-style/), in addition to [.rubocop.yml](https://github.com/googleapis/ruby-spanner-activerecord/blob/main/.rubocop.yml).
|
74
74
|
|
75
75
|
## Code of Conduct
|
76
76
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
|
5
5
|
![rubocop](https://github.com/googleapis/ruby-spanner-activerecord/workflows/rubocop/badge.svg)
|
6
6
|
|
7
|
-
This project provides a Cloud Spanner adapter for ActiveRecord. It
|
7
|
+
This project provides a Cloud Spanner adapter for ActiveRecord. It supports the following versions:
|
8
8
|
|
9
9
|
- ActiveRecord 6.0.x with Ruby 2.6 and 2.7.
|
10
10
|
- ActiveRecord 6.1.x with Ruby 2.6 and higher.
|
@@ -69,13 +69,13 @@ Some noteworthy examples in the snippets directory:
|
|
69
69
|
- [mutations](examples/snippets/mutations): Shows how you can use [mutations instead of DML](https://cloud.google.com/spanner/docs/dml-versus-mutations)
|
70
70
|
for inserting, updating and deleting data in a Cloud Spanner database. Mutations can have a significant performance
|
71
71
|
advantage compared to DML statements, but do not allow read-your-writes semantics during a transaction.
|
72
|
-
- [interleaved-tables](examples/snippets/interleaved-tables): Shows how to create and work with a hierarchy of `INTERLEAVED IN` tables.
|
73
72
|
- [array-data-type](examples/snippets/array-data-type): Shows how to work with `ARRAY` data types.
|
74
73
|
|
75
74
|
## Limitations
|
76
75
|
|
77
76
|
Limitation|Comment|Resolution
|
78
77
|
---|---|---
|
78
|
+
Interleaved tables are not supported|Cloud Spanner requires to use composite primary keys for interleaved tables, but Active Record does not support composite primary keys. More details: [#160](https://github.com/googleapis/ruby-spanner-activerecord/issues/160) |Use non-interleaved tables.
|
79
79
|
Lack of DEFAULT for columns [change_column_default](https://apidock.com/rails/v5.2.3/ActiveRecord/ConnectionAdapters/SchemaStatements/change_column_default)|Cloud Spanner does not support DEFAULT values for columns. The use of default must be enforced in your controller logic| Always set a value in your model or controller logic.
|
80
80
|
Lack of sequential and auto-assigned IDs|Cloud Spanner doesn't autogenerate IDs and this integration instead creates UUID4 to avoid [hotspotting](https://cloud.google.com/spanner/docs/schema-design#uuid_primary_key) so you SHOULD NOT rely on IDs being sorted| UUID4s are automatically generated for primary keys.
|
81
81
|
Table without Primary Key| Cloud Spanner support does not support tables without a primary key.| Always define a primary key for your table.
|
@@ -93,4 +93,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
93
93
|
|
94
94
|
## Code of Conduct
|
95
95
|
|
96
|
-
Everyone interacting in the Activerecord::Spanner project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/googleapis/ruby-spanner-activerecord/blob/
|
96
|
+
Everyone interacting in the Activerecord::Spanner project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/googleapis/ruby-spanner-activerecord/blob/main/CODE_OF_CONDUCT.md).
|
@@ -142,6 +142,30 @@ module ActiveRecord
|
|
142
142
|
|
143
143
|
assert_equal [post], posts.to_a
|
144
144
|
end
|
145
|
+
|
146
|
+
def test_statement_hint
|
147
|
+
post = Post.optimizer_hints("statement_hint: @{USE_ADDITIONAL_PARALLELISM=TRUE}")
|
148
|
+
.select(:title).to_a.first
|
149
|
+
|
150
|
+
assert_nil post.id
|
151
|
+
assert_equal "Title - 1", post.title
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_table_hint
|
155
|
+
post = Post.optimizer_hints("table_hint: posts@{FORCE_INDEX=_BASE_TABLE}")
|
156
|
+
.select(:title).to_a.first
|
157
|
+
|
158
|
+
assert_nil post.id
|
159
|
+
assert_equal "Title - 1", post.title
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_join_hint
|
163
|
+
post = Post.joins("inner join @{JOIN_TYPE=HASH_JOIN} comments on posts.id=comments.post_id")
|
164
|
+
.select(:title).to_a.first
|
165
|
+
|
166
|
+
assert_nil post.id
|
167
|
+
assert_equal "Title - 1", post.title
|
168
|
+
end
|
145
169
|
end
|
146
170
|
end
|
147
171
|
end
|
@@ -32,17 +32,18 @@ module ActiveRecord
|
|
32
32
|
col_timestamp: ::Time.new(2021, 6, 23, 17, 8, 21, "+02:00"),
|
33
33
|
col_json: ENV["SPANNER_EMULATOR_HOST"] ? "" : { kind: "user_renamed", change: %w[jack john]},
|
34
34
|
col_array_string: ["string1", nil, "string2"],
|
35
|
-
col_array_int64: [100, nil, 200],
|
36
|
-
col_array_float64: [3.14, nil, 2.0/3.0],
|
37
|
-
col_array_numeric: [6.626, nil, 3.20],
|
38
|
-
col_array_bool: [true, nil, false],
|
35
|
+
col_array_int64: [100, nil, 200, "300"],
|
36
|
+
col_array_float64: [3.14, nil, 2.0/3.0, "3.14"],
|
37
|
+
col_array_numeric: [6.626, nil, 3.20, "400"],
|
38
|
+
col_array_bool: [true, nil, false, "false"],
|
39
39
|
col_array_bytes: [StringIO.new("bytes1"), nil, StringIO.new("bytes2")],
|
40
|
-
col_array_date: [::Date.new(2021, 6, 23), nil, ::Date.new(2021, 6, 24)],
|
40
|
+
col_array_date: [::Date.new(2021, 6, 23), nil, ::Date.new(2021, 6, 24), "2021-06-25"],
|
41
41
|
col_array_timestamp: [::Time.new(2021, 6, 23, 17, 8, 21, "+02:00"), nil, \
|
42
|
-
::Time.new(2021, 6, 24, 17, 8, 21, "+02:00")],
|
42
|
+
::Time.new(2021, 6, 24, 17, 8, 21, "+02:00"), "2021-06-25 17:08:21 +02:00"],
|
43
43
|
col_array_json: ENV["SPANNER_EMULATOR_HOST"] ? [""] : \
|
44
44
|
[{ kind: "user_renamed", change: %w[jack john]}, nil, \
|
45
|
-
{ kind: "user_renamed", change: %w[alice meredith]}
|
45
|
+
{ kind: "user_renamed", change: %w[alice meredith]},
|
46
|
+
"{\"kind\":\"user_renamed\",\"change\":[\"bob\",\"carol\"]}"]
|
46
47
|
end
|
47
48
|
|
48
49
|
def test_create_record
|
@@ -69,20 +70,21 @@ module ActiveRecord
|
|
69
70
|
record.col_json unless ENV["SPANNER_EMULATOR_HOST"]
|
70
71
|
|
71
72
|
assert_equal ["string1", nil, "string2"], record.col_array_string
|
72
|
-
assert_equal [100, nil, 200], record.col_array_int64
|
73
|
-
assert_equal [3.14, nil, 2.0/3.0], record.col_array_float64
|
74
|
-
assert_equal [6.626, nil, 3.20], record.col_array_numeric
|
75
|
-
assert_equal [true, nil, false], record.col_array_bool
|
73
|
+
assert_equal [100, nil, 200, 300], record.col_array_int64
|
74
|
+
assert_equal [3.14, nil, 2.0/3.0, 3.14], record.col_array_float64
|
75
|
+
assert_equal [6.626, nil, 3.20, 400], record.col_array_numeric
|
76
|
+
assert_equal [true, nil, false, false], record.col_array_bool
|
76
77
|
assert_equal [StringIO.new("bytes1"), nil, StringIO.new("bytes2")].map { |bytes| bytes&.read },
|
77
78
|
record.col_array_bytes.map { |bytes| bytes&.read }
|
78
|
-
assert_equal [::Date.new(2021, 6, 23), nil, ::Date.new(2021, 6, 24)], record.col_array_date
|
79
|
+
assert_equal [::Date.new(2021, 6, 23), nil, ::Date.new(2021, 6, 24), ::Date.new(2021, 06, 25)], record.col_array_date
|
79
80
|
assert_equal [::Time.new(2021, 6, 23, 17, 8, 21, "+02:00"), \
|
80
81
|
nil, \
|
81
|
-
::Time.new(2021, 6, 24, 17, 8, 21, "+02:00")].map { |timestamp| timestamp&.utc },
|
82
|
+
::Time.new(2021, 6, 24, 17, 8, 21, "+02:00"), ::Time.new(2021, 6, 25, 17, 8, 21, "+02:00")].map { |timestamp| timestamp&.utc },
|
82
83
|
record.col_array_timestamp.map { |timestamp| timestamp&.utc}
|
83
84
|
assert_equal [{"kind" => "user_renamed", "change" => %w[jack john]}, \
|
84
85
|
nil, \
|
85
|
-
{"kind" => "user_renamed", "change" => %w[alice meredith]}
|
86
|
+
{"kind" => "user_renamed", "change" => %w[alice meredith]},
|
87
|
+
{"kind" => "user_renamed", "change" => %w[bob carol]}],
|
86
88
|
record.col_array_json unless ENV["SPANNER_EMULATOR_HOST"]
|
87
89
|
end
|
88
90
|
end
|
data/examples/rails/README.md
CHANGED
@@ -99,7 +99,7 @@ If you are not familiar with Active Record, you can read more about it on [Ruby
|
|
99
99
|
### Use Cloud Spanner adapter in Gemfile
|
100
100
|
1. Edit the Gemfile file of the `blog` app and add the `activerecord-spanner-adapter` gem:
|
101
101
|
```ruby
|
102
|
-
gem 'activerecord-spanner-adapter'
|
102
|
+
gem 'activerecord-spanner-adapter'
|
103
103
|
```
|
104
104
|
1. Install gems:
|
105
105
|
|
@@ -142,17 +142,17 @@ Replace `[PROJECT_ID]` with the project id you are currently using.
|
|
142
142
|
### Create database
|
143
143
|
|
144
144
|
You now can run the following command to create the database:
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
145
|
+
|
146
|
+
```shell
|
147
|
+
./bin/rails db:create
|
148
|
+
```
|
149
|
+
|
150
|
+
You should see output like the following: `Created database 'blog_dev'`
|
151
|
+
|
152
152
|
### Generate a Model and apply the migration
|
153
153
|
1. Use the model generato to define a model:
|
154
154
|
```shell
|
155
|
-
bin/rails generate model Article title:string body:text
|
155
|
+
./bin/rails generate model Article title:string body:text
|
156
156
|
```
|
157
157
|
1. Apply the migration:
|
158
158
|
```shell
|
@@ -2,8 +2,8 @@
|
|
2
2
|
# of editing this file, please use the migrations feature of Active Record to
|
3
3
|
# incrementally modify your database, and then regenerate this schema definition.
|
4
4
|
#
|
5
|
-
# This file is the source Rails uses to define your schema when running `rails
|
6
|
-
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
5
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
6
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
7
7
|
# be faster and is potentially less error prone than running all of your
|
8
8
|
# migrations from scratch. Old migrations may fail to apply correctly if those
|
9
9
|
# migrations use external dependencies or application code.
|
@@ -12,7 +12,7 @@
|
|
12
12
|
|
13
13
|
ActiveRecord::Schema.define(version: 1) do
|
14
14
|
|
15
|
-
create_table "singers", force: :cascade do |t|
|
15
|
+
create_table "singers", id: { limit: 8 }, force: :cascade do |t|
|
16
16
|
t.string "first_name", limit: 100
|
17
17
|
t.string "last_name", limit: 200, null: false
|
18
18
|
t.string "full_name", limit: 300, null: false
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# Sample - Query Hints
|
2
|
+
|
3
|
+
This example shows how to use query hints Spanner ActiveRecord adapter. Statement hints and
|
4
|
+
table hints can be specified using the optimizer_hints method. Join hints must be specified
|
5
|
+
in a join string.
|
6
|
+
|
7
|
+
See https://cloud.google.com/spanner/docs/query-syntax#sql_syntax for more information on
|
8
|
+
the supported query hints.
|
9
|
+
|
10
|
+
## Running the Sample
|
11
|
+
|
12
|
+
The sample will automatically start a Spanner Emulator in a docker container and execute the sample
|
13
|
+
against that emulator. The emulator will automatically be stopped when the application finishes.
|
14
|
+
|
15
|
+
Run the application with the command
|
16
|
+
|
17
|
+
```bash
|
18
|
+
bundle exec rake run
|
19
|
+
```
|
@@ -7,7 +7,7 @@
|
|
7
7
|
require_relative "../config/environment"
|
8
8
|
require "sinatra/activerecord/rake"
|
9
9
|
|
10
|
-
desc "Sample showing how to work with
|
10
|
+
desc "Sample showing how to work with generated columns in ActiveRecord."
|
11
11
|
task :run do
|
12
|
-
Dir.chdir("..") { sh "bundle exec rake run[
|
12
|
+
Dir.chdir("..") { sh "bundle exec rake run[hints]" }
|
13
13
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# Copyright 2021 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
|
+
|
12
|
+
class Application
|
13
|
+
def self.run
|
14
|
+
puts ""
|
15
|
+
puts "Listing all singers using additional parallelism:"
|
16
|
+
# A statement hint must be prefixed with 'statement_hint:'
|
17
|
+
Singer.optimizer_hints("statement_hint: @{USE_ADDITIONAL_PARALLELISM=TRUE}")
|
18
|
+
.order("last_name, first_name").each do |singer|
|
19
|
+
puts singer.full_name
|
20
|
+
end
|
21
|
+
|
22
|
+
puts ""
|
23
|
+
puts "Listing all singers using the index on full_name:"
|
24
|
+
# All table hints must be prefixed with 'table_hint:'.
|
25
|
+
# Queries may contain multiple table hints.
|
26
|
+
Singer.optimizer_hints("table_hint: singers@{FORCE_INDEX=index_singers_on_full_name}")
|
27
|
+
.order("full_name").each do |singer|
|
28
|
+
puts singer.full_name
|
29
|
+
end
|
30
|
+
|
31
|
+
puts ""
|
32
|
+
puts "Listing all singers with at least one album that starts with 'blue':"
|
33
|
+
# Join hints cannot be specified using an optimizer_hint. Instead, the join can
|
34
|
+
# be specified using a string that includes the join hint.
|
35
|
+
Singer.joins("INNER JOIN @{JOIN_METHOD=HASH_JOIN} albums " \
|
36
|
+
"on singers.id=albums.singer_id AND albums.title LIKE 'blue%'")
|
37
|
+
.distinct.order("last_name, first_name").each do |singer|
|
38
|
+
puts singer.full_name
|
39
|
+
end
|
40
|
+
|
41
|
+
puts ""
|
42
|
+
puts "Press any key to end the application"
|
43
|
+
STDIN.getch
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
Application.run
|
File without changes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# Copyright 2021 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
|
+
connection.ddl_batch do
|
10
|
+
create_table :singers do |t|
|
11
|
+
t.string :first_name, limit: 100
|
12
|
+
t.string :last_name, limit: 200, null: false
|
13
|
+
t.string :full_name, limit: 300, null: false, as: "COALESCE(first_name || ' ', '') || last_name", stored: true
|
14
|
+
t.index [:full_name], name: "index_singers_on_full_name"
|
15
|
+
end
|
16
|
+
|
17
|
+
create_table :albums do |t|
|
18
|
+
t.string :title
|
19
|
+
t.references :singer, index: false, foreign_key: true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -2,8 +2,8 @@
|
|
2
2
|
# of editing this file, please use the migrations feature of Active Record to
|
3
3
|
# incrementally modify your database, and then regenerate this schema definition.
|
4
4
|
#
|
5
|
-
# This file is the source Rails uses to define your schema when running `rails
|
6
|
-
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
5
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
6
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
7
7
|
# be faster and is potentially less error prone than running all of your
|
8
8
|
# migrations from scratch. Old migrations may fail to apply correctly if those
|
9
9
|
# migrations use external dependencies or application code.
|
@@ -12,21 +12,17 @@
|
|
12
12
|
|
13
13
|
ActiveRecord::Schema.define(version: 1) do
|
14
14
|
|
15
|
-
create_table "albums",
|
16
|
-
t.integer "singerid", limit: 8, null: false
|
15
|
+
create_table "albums", id: { limit: 8 }, force: :cascade do |t|
|
17
16
|
t.string "title"
|
17
|
+
t.integer "singer_id", limit: 8
|
18
18
|
end
|
19
19
|
|
20
|
-
create_table "singers",
|
21
|
-
t.string "first_name"
|
22
|
-
t.string "last_name"
|
23
|
-
|
24
|
-
|
25
|
-
create_table "tracks", primary_key: "trackid", force: :cascade do |t|
|
26
|
-
t.integer "singerid", limit: 8, null: false
|
27
|
-
t.integer "albumid", limit: 8, null: false
|
28
|
-
t.string "title"
|
29
|
-
t.decimal "duration"
|
20
|
+
create_table "singers", id: { limit: 8 }, force: :cascade do |t|
|
21
|
+
t.string "first_name", limit: 100
|
22
|
+
t.string "last_name", limit: 200, null: false
|
23
|
+
t.string "full_name", limit: 300, null: false
|
24
|
+
t.index ["full_name"], name: "index_singers_on_full_name", order: { full_name: :asc }
|
30
25
|
end
|
31
26
|
|
27
|
+
add_foreign_key "albums", "singers"
|
32
28
|
end
|
@@ -7,7 +7,6 @@
|
|
7
7
|
require_relative "../../config/environment.rb"
|
8
8
|
require_relative "../models/singer"
|
9
9
|
require_relative "../models/album"
|
10
|
-
require_relative "../models/track"
|
11
10
|
|
12
11
|
first_names = %w[Pete Alice John Ethel Trudy Naomi Wendy Ruben Thomas Elly]
|
13
12
|
last_names = %w[Wendelson Allison Peterson Johnson Henderson Ericsson Aronson Tennet Courtou]
|
@@ -15,11 +14,6 @@ last_names = %w[Wendelson Allison Peterson Johnson Henderson Ericsson Aronson Te
|
|
15
14
|
adjectives = %w[daily happy blue generous cooked bad open]
|
16
15
|
nouns = %w[windows potatoes bank street tree glass bottle]
|
17
16
|
|
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
17
|
# This ensures all the records are inserted using one read/write transaction that will use mutations instead of DML.
|
24
18
|
ActiveRecord::Base.transaction isolation: :buffered_mutations do
|
25
19
|
singers = []
|
@@ -32,9 +26,4 @@ ActiveRecord::Base.transaction isolation: :buffered_mutations do
|
|
32
26
|
singer = singers.sample
|
33
27
|
albums << Album.create(title: "#{adjectives.sample} #{nouns.sample}", singer: singer)
|
34
28
|
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
29
|
end
|