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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.github/blunderbuss.yml +2 -0
  3. data/.github/sync-repo-settings.yaml +1 -1
  4. data/.github/workflows/acceptance-tests-on-emulator.yaml +1 -1
  5. data/.github/workflows/acceptance-tests-on-production.yaml +2 -2
  6. data/.github/workflows/ci.yaml +1 -1
  7. data/.github/workflows/release-please-label.yml +4 -4
  8. data/.github/workflows/release-please.yml +10 -9
  9. data/.github/workflows/rubocop.yaml +2 -2
  10. data/.release-please-manifest.json +3 -0
  11. data/.toys/release.rb +2 -2
  12. data/CHANGELOG.md +46 -23
  13. data/CONTRIBUTING.md +1 -1
  14. data/Gemfile +1 -1
  15. data/README.md +3 -3
  16. data/acceptance/cases/models/query_test.rb +24 -0
  17. data/acceptance/cases/type/all_types_test.rb +16 -14
  18. data/examples/rails/README.md +9 -9
  19. data/examples/snippets/generated-column/db/schema.rb +3 -3
  20. data/examples/snippets/hints/README.md +19 -0
  21. data/examples/snippets/{interleaved-tables → hints}/Rakefile +2 -2
  22. data/examples/snippets/hints/application.rb +47 -0
  23. data/examples/snippets/{interleaved-tables → hints}/config/database.yml +0 -0
  24. data/examples/snippets/hints/db/migrate/01_create_tables.rb +23 -0
  25. data/examples/snippets/{interleaved-tables → hints}/db/schema.rb +10 -14
  26. data/examples/snippets/{interleaved-tables → hints}/db/seeds.rb +0 -11
  27. data/examples/snippets/hints/models/album.rb +9 -0
  28. data/examples/snippets/hints/models/singer.rb +9 -0
  29. data/examples/snippets/partitioned-dml/README.md +16 -0
  30. data/examples/snippets/partitioned-dml/Rakefile +13 -0
  31. data/examples/snippets/partitioned-dml/application.rb +48 -0
  32. data/examples/snippets/partitioned-dml/config/database.yml +8 -0
  33. data/examples/snippets/partitioned-dml/db/migrate/01_create_tables.rb +21 -0
  34. data/examples/snippets/partitioned-dml/db/schema.rb +26 -0
  35. data/examples/snippets/partitioned-dml/db/seeds.rb +29 -0
  36. data/examples/snippets/partitioned-dml/models/album.rb +9 -0
  37. data/examples/snippets/partitioned-dml/models/singer.rb +9 -0
  38. data/lib/active_record/connection_adapters/spanner_adapter.rb +1 -1
  39. data/lib/active_record/tasks/spanner_database_tasks.rb +1 -1
  40. data/lib/active_record/type/spanner/array.rb +19 -5
  41. data/lib/activerecord_spanner_adapter/base.rb +31 -10
  42. data/lib/activerecord_spanner_adapter/connection.rb +46 -20
  43. data/lib/activerecord_spanner_adapter/information_schema.rb +2 -1
  44. data/lib/activerecord_spanner_adapter/transaction.rb +52 -21
  45. data/lib/activerecord_spanner_adapter/version.rb +1 -1
  46. data/lib/arel/visitors/spanner.rb +39 -0
  47. data/lib/spanner_client_ext.rb +4 -0
  48. data/release-please-config.json +19 -0
  49. metadata +24 -13
  50. data/examples/snippets/interleaved-tables/README.md +0 -152
  51. data/examples/snippets/interleaved-tables/application.rb +0 -109
  52. data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +0 -44
  53. data/examples/snippets/interleaved-tables/models/album.rb +0 -15
  54. data/examples/snippets/interleaved-tables/models/singer.rb +0 -20
  55. 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: 55f265a14ddd7a176378a49090cbb3277aae66ce2c74913978ec9614289759fc
4
- data.tar.gz: ae48998a381c0142063ac4499c8a0c13ece58bded34a72cbcc0371541ee02231
3
+ metadata.gz: bcc2624f18893422bf8ac9c23d07fff039e79cee5f51b975054314f49820b694
4
+ data.tar.gz: 7145b48f67236eb7ee9aac20bd1dfa907268419f262abf82cd6973acba25ab64
5
5
  SHA512:
6
- metadata.gz: fa104b38f89edc91eb78d05b5f89e232fb50847953061eae4e3c9eb12bf307f9a0cf69545eb06a0682c92b2ac7bce065b8d88f8fa148570b4720b47ccccad0d5
7
- data.tar.gz: 3de12a8b087940d6234ecd902ea7b109a8319960700dd323c363da266aa299f211b003e50fb01e24ff5e4632e4372b5451e0d129168cacfa3541771263dc2703
6
+ metadata.gz: 49aed5257b191aee1e9271c1d04e91c2ff1663c78e9b1e70e89acda430587324991a10cb1b2aabdfd9b1e9d70ab862b3a1579fda5c032023175303fa260deeed
7
+ data.tar.gz: 1764369d2a370d4c8d35cbbcd3b9c45130514cc6e76bac67e5a9dafbf2bf020734b973be44f354b13da9acaaffdebaba78e31046da56c6e62efe0546aca30611
@@ -0,0 +1,2 @@
1
+ assign_issues:
2
+ - hengfengli
@@ -2,7 +2,7 @@ rebaseMergeAllowed: true
2
2
  squashMergeAllowed: true
3
3
  mergeCommitAllowed: false
4
4
  branchProtectionRules:
5
- - pattern: master
5
+ - pattern: main
6
6
  isAdminEnforced: true
7
7
  requiredStatusCheckContexts:
8
8
  - 'cla/google'
@@ -1,7 +1,7 @@
1
1
  on:
2
2
  push:
3
3
  branches:
4
- - master
4
+ - main
5
5
  pull_request:
6
6
  name: acceptance tests on emulator
7
7
  jobs:
@@ -1,7 +1,7 @@
1
1
  on:
2
2
  push:
3
3
  branches:
4
- - master
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@master
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 }}
@@ -1,7 +1,7 @@
1
1
  on:
2
2
  push:
3
3
  branches:
4
- - master
4
+ - main
5
5
  pull_request:
6
6
  name: ci
7
7
  jobs:
@@ -2,21 +2,21 @@ name: release-please-label
2
2
  on:
3
3
  pull_request_target:
4
4
  branches:
5
- - master
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@v4
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: '27 8 * * *'
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 2.7
24
+ - name: Install Ruby 3.0
25
25
  uses: ruby/setup-ruby@v1
26
26
  with:
27
- ruby-version: "2.7"
28
- - name: Install NodeJS 12.x
27
+ ruby-version: "3.0"
28
+ - name: Install NodeJS 16.x
29
29
  uses: actions/setup-node@v2
30
30
  with:
31
- node-version: "12.x"
31
+ node-version: "16.x"
32
32
  - name: Install tools
33
- run: "gem install --no-document toys && npm install release-please"
33
+ run: "gem install --no-document toys"
34
34
  - name: execute
35
35
  run: |
36
- toys release please -v --fork \
36
+ toys release manifest -v \
37
+ --fork --skip-labeling \
37
38
  --github-event-name=${{ github.event_name }} \
38
- --version-file=lib/activerecord_spanner_adapter/version.rb \
39
- ${{ github.event.inputs.args }} -- ${{ github.event.inputs.gem }}
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: [ master ]
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@v1
21
+ uses: actions/cache@v2
22
22
  with:
23
23
  path: vendor/bundle
24
24
  key: ${{ runner.os }}-rubocop-${{ hashFiles('**/Gemfile.lock') }}
@@ -0,0 +1,3 @@
1
+ {
2
+ ".": "1.0.1"
3
+ }
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/google-cloud-ruby.git",
18
- path: ".toys/release"
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
- ## [0.6.0](https://www.github.com/googleapis/ruby-spanner-activerecord/compare/activerecord-spanner-adapter/v0.5.0...activerecord-spanner-adapter/v0.6.0) (2021-09-09)
3
+ ### 1.0.1 (2022-04-21)
4
4
 
5
+ #### Bug Fixes
5
6
 
6
- ### Features
7
+ * ActiveRecord::Type::Spanner::Array does not use element type
7
8
 
8
- * support JSON data type ([#123](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/123)) ([d177ddf](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/d177ddfc7326f02189bd4054571564b94d162b02))
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
- ## 0.5.0 (2021-08-31)
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
- ### Features
16
+ * GA release
16
17
 
17
- * Add support for NUMERIC type ([#73](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/73)) ([176cf99](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/176cf99dc8c26b3fd34d9e85d82a91dbde2b15c8))
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
- ### Performance Improvements
22
+ * inline BeginTransaction with first statement in the transaction
34
23
 
35
- * add benchmarks ([#98](https://www.github.com/googleapis/ruby-spanner-activerecord/issues/98)) ([80cbadc](https://www.github.com/googleapis/ruby-spanner-activerecord/commit/80cbadc5063f2f257ca1e6e7bf563fc376967428))
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/master/.rubocop.yml).
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
@@ -3,7 +3,7 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in activerecord-spanner.gemspec
4
4
  gemspec
5
5
 
6
- gem "minitest", "~> 5.14.0"
6
+ gem "minitest", "~> 5.15.0"
7
7
  gem "pry", "~> 0.13.0"
8
8
  gem "pry-byebug", "~> 3.9.0"
9
9
 
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 has the __Preview__ release status and supports the following versions:
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/master/CODE_OF_CONDUCT.md).
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
@@ -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', git: 'https://github.com/googleapis/ruby-spanner-activerecord.git'
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
- ```shell
146
- ./bin/rails db:create
147
- ```
148
- You should see output like the following:
149
- ```
150
- Created database 'blog_dev'
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 interleaved tables in ActiveRecord."
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[interleaved-tables]" }
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
@@ -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", primary_key: "albumid", force: :cascade do |t|
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", primary_key: "singerid", force: :cascade do |t|
21
- t.string "first_name"
22
- t.string "last_name"
23
- end
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
@@ -0,0 +1,9 @@
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 Album < ActiveRecord::Base
8
+ belongs_to :singer
9
+ end
@@ -0,0 +1,9 @@
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 Singer < ActiveRecord::Base
8
+ has_many :albums
9
+ end