activerecord-spanner-adapter 1.5.1 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/acceptance-tests-on-emulator.yaml +1 -1
  3. data/.github/workflows/acceptance-tests-on-production.yaml +5 -3
  4. data/.github/workflows/ci.yaml +1 -1
  5. data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +1 -1
  6. data/.github/workflows/nightly-acceptance-tests-on-production.yaml +5 -3
  7. data/.github/workflows/nightly-unit-tests.yaml +1 -1
  8. data/.github/workflows/release-please-label.yml +1 -1
  9. data/.release-please-manifest.json +1 -1
  10. data/CHANGELOG.md +8 -0
  11. data/Gemfile +5 -2
  12. data/README.md +10 -10
  13. data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +6 -0
  14. data/acceptance/cases/migration/change_schema_test.rb +19 -3
  15. data/acceptance/cases/migration/schema_dumper_test.rb +10 -1
  16. data/acceptance/cases/models/interleave_test.rb +6 -0
  17. data/acceptance/cases/tasks/database_tasks_test.rb +340 -2
  18. data/acceptance/cases/transactions/optimistic_locking_test.rb +6 -0
  19. data/acceptance/cases/transactions/read_write_transactions_test.rb +24 -0
  20. data/acceptance/models/table_with_sequence.rb +10 -0
  21. data/acceptance/schema/schema.rb +65 -19
  22. data/acceptance/test_helper.rb +1 -1
  23. data/activerecord-spanner-adapter.gemspec +1 -1
  24. data/examples/snippets/bit-reversed-sequence/README.md +103 -0
  25. data/examples/snippets/bit-reversed-sequence/Rakefile +13 -0
  26. data/examples/snippets/bit-reversed-sequence/application.rb +68 -0
  27. data/examples/snippets/bit-reversed-sequence/config/database.yml +8 -0
  28. data/examples/snippets/bit-reversed-sequence/db/migrate/01_create_tables.rb +33 -0
  29. data/examples/snippets/bit-reversed-sequence/db/schema.rb +31 -0
  30. data/examples/snippets/bit-reversed-sequence/db/seeds.rb +31 -0
  31. data/examples/snippets/bit-reversed-sequence/models/album.rb +11 -0
  32. data/examples/snippets/bit-reversed-sequence/models/singer.rb +15 -0
  33. data/examples/snippets/interleaved-tables/README.md +44 -53
  34. data/examples/snippets/interleaved-tables/Rakefile +2 -2
  35. data/examples/snippets/interleaved-tables/application.rb +2 -2
  36. data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +12 -18
  37. data/examples/snippets/interleaved-tables/db/schema.rb +9 -7
  38. data/examples/snippets/interleaved-tables/db/seeds.rb +1 -1
  39. data/examples/snippets/interleaved-tables/models/album.rb +3 -7
  40. data/examples/snippets/interleaved-tables/models/singer.rb +1 -1
  41. data/examples/snippets/interleaved-tables/models/track.rb +6 -7
  42. data/examples/snippets/interleaved-tables-before-7.1/README.md +167 -0
  43. data/examples/snippets/interleaved-tables-before-7.1/Rakefile +13 -0
  44. data/examples/snippets/interleaved-tables-before-7.1/application.rb +126 -0
  45. data/examples/snippets/interleaved-tables-before-7.1/config/database.yml +8 -0
  46. data/examples/snippets/interleaved-tables-before-7.1/db/migrate/01_create_tables.rb +44 -0
  47. data/examples/snippets/interleaved-tables-before-7.1/db/schema.rb +37 -0
  48. data/examples/snippets/interleaved-tables-before-7.1/db/seeds.rb +40 -0
  49. data/examples/snippets/interleaved-tables-before-7.1/models/album.rb +20 -0
  50. data/examples/snippets/interleaved-tables-before-7.1/models/singer.rb +18 -0
  51. data/examples/snippets/interleaved-tables-before-7.1/models/track.rb +28 -0
  52. data/examples/snippets/query-logs/README.md +43 -0
  53. data/examples/snippets/query-logs/Rakefile +13 -0
  54. data/examples/snippets/query-logs/application.rb +63 -0
  55. data/examples/snippets/query-logs/config/database.yml +8 -0
  56. data/examples/snippets/query-logs/db/migrate/01_create_tables.rb +21 -0
  57. data/examples/snippets/query-logs/db/schema.rb +31 -0
  58. data/examples/snippets/query-logs/db/seeds.rb +24 -0
  59. data/examples/snippets/query-logs/models/album.rb +9 -0
  60. data/examples/snippets/query-logs/models/singer.rb +9 -0
  61. data/lib/active_record/connection_adapters/spanner/column.rb +13 -0
  62. data/lib/active_record/connection_adapters/spanner/database_statements.rb +144 -35
  63. data/lib/active_record/connection_adapters/spanner/schema_cache.rb +3 -21
  64. data/lib/active_record/connection_adapters/spanner/schema_creation.rb +11 -0
  65. data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +4 -0
  66. data/lib/active_record/connection_adapters/spanner/schema_statements.rb +3 -2
  67. data/lib/active_record/connection_adapters/spanner_adapter.rb +28 -9
  68. data/lib/activerecord_spanner_adapter/base.rb +56 -19
  69. data/lib/activerecord_spanner_adapter/information_schema.rb +33 -24
  70. data/lib/activerecord_spanner_adapter/primary_key.rb +1 -1
  71. data/lib/activerecord_spanner_adapter/table/column.rb +4 -9
  72. data/lib/activerecord_spanner_adapter/version.rb +1 -1
  73. data/lib/arel/visitors/spanner.rb +3 -1
  74. metadata +33 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 73df0f7ee319edb7461b0fa60f8f8b611b38d798de2c0caf20a9c7d6796a7a85
4
- data.tar.gz: 9dedd540069c5bc85d458008c20e48b9b550a75388a1f4d48b1b29b34bb689d4
3
+ metadata.gz: 2ad2384bb1890bb224c89a146c42f615f89c6e2892848da5e701a091d90fa95b
4
+ data.tar.gz: 0e8a9c608068ff30011bc9bca68b10ab0d22b910260a37a6d4a492365adf78b0
5
5
  SHA512:
6
- metadata.gz: faac93588f6e703f183a773caf553d61b64370439093ac27a7d1a4893f6d887c887fbf1be9082baf78bb81e2c23b24f45a1e4053672e386595ff95c0980a0c4b
7
- data.tar.gz: 4d6061c3daf7760d3015a33b52a41354f6bafa21b594a0c2b44852ae928935e1b2e23421ce417de2e65eecdc56de0890d97d0321e07fd3a3c3c104cf56856c88
6
+ metadata.gz: 1894765bbc1e3dfac270d73199a4f235ae95724ca18145d6850cce27c38ef2018b59e9035cf2fa91bc30d3f650e080b8eea58c3e91a66035db3a4d07c35ca1be
7
+ data.tar.gz: 4a9b93d7990fa78f901864bed816e75ff5d0f9da34a530c7107f475d8fd06dc71817fbf8b2c1e0262e4656bf7ab363917ecf7b7c82315b6d5fa84cce0d22489d
@@ -19,7 +19,7 @@ jobs:
19
19
  max-parallel: 4
20
20
  matrix:
21
21
  ruby: ["2.7", "3.0", "3.1", "3.2"]
22
- ar: ["~> 6.0.6", "~> 6.1.7", "~> 7.0.4"]
22
+ ar: ["~> 6.0.6", "~> 6.1.7", "~> 7.0.4", "~> 7.1.0"]
23
23
  # Exclude combinations that are not supported.
24
24
  exclude:
25
25
  - ruby: "3.0"
@@ -34,12 +34,14 @@ jobs:
34
34
  with:
35
35
  bundler-cache: true
36
36
  ruby-version: ${{ matrix.ruby }}
37
+ - name: Authenticate Google Cloud
38
+ uses: google-github-actions/auth@v2
39
+ with:
40
+ credentials_json: ${{ secrets.GCP_SA_KEY }}
37
41
  - name: Setup GCloud
38
- uses: google-github-actions/setup-gcloud@v0
42
+ uses: google-github-actions/setup-gcloud@v2
39
43
  with:
40
44
  project_id: ${{ secrets.GCP_PROJECT_ID }}
41
- service_account_key: ${{ secrets.GCP_SA_KEY }}
42
- export_default_credentials: true
43
45
  - name: Install dependencies
44
46
  run: bundle install
45
47
  - name: Run acceptance tests on production
@@ -11,7 +11,7 @@ jobs:
11
11
  max-parallel: 4
12
12
  matrix:
13
13
  ruby: ["2.7", "3.0", "3.1", "3.2"]
14
- ar: ["~> 6.0.6", "~> 6.1.7", "~> 7.0.4"]
14
+ ar: ["~> 6.0.6", "~> 6.1.7", "~> 7.0.4", "~> 7.1.0"]
15
15
  # Exclude combinations that are not supported.
16
16
  exclude:
17
17
  - ruby: "3.0"
@@ -20,7 +20,7 @@ jobs:
20
20
  matrix:
21
21
  # Run acceptance tests all supported combinations of Ruby and ActiveRecord.
22
22
  ruby: [2.7, 3.0, 3.1, 3.2]
23
- ar: [6.0.0, 6.0.1, 6.0.2.2, 6.0.3.7, 6.0.4, 6.1.3.2, 6.1.4.7, 6.1.5.1, 6.1.6.1, 7.0.2.4, 7.0.3.1, 7.0.4, 7.0.5, 7.0.6, 7.0.7]
23
+ ar: [6.0.0, 6.0.1, 6.0.2.2, 6.0.3.7, 6.0.4, 6.1.3.2, 6.1.4.7, 6.1.5.1, 6.1.6.1, 7.0.2.4, 7.0.3.1, 7.0.4, 7.0.5, 7.0.6, 7.0.7, 7.1.0, 7.1.1, 7.1.2]
24
24
  # Exclude combinations that are not supported.
25
25
  exclude:
26
26
  - ruby: 3.0
@@ -20,12 +20,14 @@ jobs:
20
20
  with:
21
21
  bundler-cache: true
22
22
  ruby-version: ${{ matrix.ruby }}
23
+ - name: Authenticate Google Cloud
24
+ uses: google-github-actions/auth@v2
25
+ with:
26
+ credentials_json: ${{ secrets.GCP_SA_KEY }}
23
27
  - name: Setup GCloud
24
- uses: google-github-actions/setup-gcloud@v0
28
+ uses: google-github-actions/setup-gcloud@v2
25
29
  with:
26
30
  project_id: ${{ secrets.GCP_PROJECT_ID }}
27
- service_account_key: ${{ secrets.GCP_SA_KEY }}
28
- export_default_credentials: true
29
31
  - name: Install dependencies
30
32
  run: bundle install
31
33
  - name: Run acceptance tests on production
@@ -12,7 +12,7 @@ jobs:
12
12
  matrix:
13
13
  # Run unit tests all supported combinations of Ruby and ActiveRecord.
14
14
  ruby: [2.7, 3.0, 3.1, 3.2]
15
- ar: [6.0.0, 6.0.1, 6.0.2.2, 6.0.3.7, 6.0.4, 6.1.3.2, 6.1.4.7, 6.1.5.1, 6.1.6.1, 7.0.2.4, 7.0.3.1, 7.0.4, 7.0.5]
15
+ ar: [6.0.0, 6.0.1, 6.0.2.2, 6.0.3.7, 6.0.4, 6.1.3.2, 6.1.4.7, 6.1.5.1, 6.1.6.1, 7.0.2.4, 7.0.3.1, 7.0.4, 7.0.5, 7.1.0, 7.1.1, 7.1.2]
16
16
  # Exclude combinations that are not supported.
17
17
  exclude:
18
18
  - ruby: 3.0
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
13
  - name: ReleaseLabel
14
- uses: actions/github-script@v6
14
+ uses: actions/github-script@v7
15
15
  with:
16
16
  github-token: ${{secrets.YOSHI_APPROVER_TOKEN}}
17
17
  script: |
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "1.5.1"
2
+ ".": "1.6.0"
3
3
  }
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ### 1.6.0 (2023-12-20)
4
+
5
+ #### Features
6
+
7
+ * interleaved tables with built-in composite pk ([#282](https://github.com/googleapis/ruby-spanner-activerecord/issues/282))
8
+ * support Query Logs ([#291](https://github.com/googleapis/ruby-spanner-activerecord/issues/291))
9
+ * support Rails 7.1 ([#278](https://github.com/googleapis/ruby-spanner-activerecord/issues/278))
10
+
3
11
  ### 1.5.1 (2023-12-12)
4
12
 
5
13
  #### Bug Fixes
data/Gemfile CHANGED
@@ -4,12 +4,15 @@ source "https://rubygems.org"
4
4
  gemspec
5
5
 
6
6
  gem "activerecord", ENV.fetch("AR_VERSION", "~> 6.1.6.1")
7
- gem "minitest", "~> 5.19.0"
7
+ gem "minitest", "~> 5.20.0"
8
+ gem "minitest-rg", "~> 5.3.0"
8
9
  gem "pry", "~> 0.13.0"
9
10
  gem "pry-byebug", "~> 3.9.0"
10
11
 
11
12
  # Required for samples and testing.
12
- gem "composite_primary_keys"
13
+ install_if -> { ENV.fetch("AR_VERSION", "~> 6.1.6.1").dup.to_s.sub!("~>", "").strip < "7.1.0" && !ENV["SKIP_COMPOSITE_PK"] } do
14
+ gem "composite_primary_keys"
15
+ end
13
16
 
14
17
  # Required for samples
15
18
  gem "docker-api"
data/README.md CHANGED
@@ -84,19 +84,19 @@ Some noteworthy examples in the snippets directory:
84
84
  for inserting, updating and deleting data in a Cloud Spanner database. Mutations can have a significant performance
85
85
  advantage compared to DML statements, but do not allow read-your-writes semantics during a transaction.
86
86
  - [array-data-type](examples/snippets/array-data-type): Shows how to work with `ARRAY` data types.
87
- - [interleaved-tables](examples/snippets/interleaved-tables): Shows how to work with [Interleaved Tables](https://cloud.google.com/spanner/docs/schema-and-data-model#create-interleaved-tables).
87
+ - [interleaved-tables](examples/snippets/interleaved-tables-before-7.1): Shows how to work with [Interleaved Tables](https://cloud.google.com/spanner/docs/schema-and-data-model#create-interleaved-tables).
88
88
 
89
89
  ## Limitations
90
90
 
91
- Limitation|Comment|Resolution
92
- ---|---|---
93
- 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.
94
- 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.
95
- Table without Primary Key| Cloud Spanner support does not support tables without a primary key.| Always define a primary key for your table.
96
- 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.
97
- 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.
98
- 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.
99
- Only GoogleSQL-dialect databases| Cloud Spanner supports both GoogleSQL- and PostgreSQL-dialect databases. This adapter only supports GoogleSQL-dialect databases. | Ensure that your database uses the GoogleSQL dialect.
91
+ | Limitation | Comment | Resolution |
92
+ |-----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------|
93
+ | 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. |
94
+ | 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. |
95
+ | Table without Primary Key | Cloud Spanner support does not support tables without a primary key. | Always define a primary key for your table. |
96
+ | 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. |
97
+ | 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. |
98
+ | 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. |
99
+ | Only GoogleSQL-dialect databases | Cloud Spanner supports both GoogleSQL- and PostgreSQL-dialect databases. This adapter only supports GoogleSQL-dialect databases. | Ensure that your database uses the GoogleSQL dialect. |
100
100
 
101
101
  ## Contributing
102
102
 
@@ -6,6 +6,12 @@
6
6
 
7
7
  # frozen_string_literal: true
8
8
 
9
+ # ActiveRecord 7.1 introduced native support for composite primary keys.
10
+ # This deprecates the https://github.com/composite-primary-keys/composite_primary_keys gem that was previously used in
11
+ # this library to support composite primary keys, which again are needed for interleaved tables. These tests use the
12
+ # third-party composite primary key gem and are therefore not executed for Rails 7.1 and higher.
13
+ return if ActiveRecord::gem_version >= Gem::Version.create('7.1.0')
14
+
9
15
  require "test_helper"
10
16
  require "models/singer"
11
17
  require "models/album"
@@ -196,8 +196,14 @@ module ActiveRecord
196
196
  end
197
197
  end
198
198
  end
199
+ expected = if ActiveRecord::gem_version < Gem::Version.create('7.1.0')
200
+ "you can't redefine the primary key column 'id'. To define a custom primary key, pass { id: false } to create_table."
201
+ else
202
+ "you can't redefine the primary key column 'id' on 'testings'. To define a custom primary key, pass { id: false } to create_table."
203
+ end
204
+
199
205
 
200
- assert_equal "you can't redefine the primary key column 'id'. To define a custom primary key, pass { id: false } to create_table.", error.message
206
+ assert_equal expected, error.message
201
207
  end
202
208
 
203
209
  def test_create_table_raises_when_redefining_custom_primary_key_column
@@ -209,7 +215,12 @@ module ActiveRecord
209
215
  end
210
216
  end
211
217
 
212
- assert_equal "you can't redefine the primary key column 'testing_id'. To define a custom primary key, pass { id: false } to create_table.", error.message
218
+ expected = if ActiveRecord::gem_version < Gem::Version.create('7.1.0')
219
+ "you can't redefine the primary key column 'testing_id'. To define a custom primary key, pass { id: false } to create_table."
220
+ else
221
+ "you can't redefine the primary key column 'testing_id' on 'testings'. To define a custom primary key, pass { id: false } to create_table."
222
+ end
223
+ assert_equal expected, error.message
213
224
  end
214
225
 
215
226
  def test_create_table_raises_when_defining_existing_column
@@ -222,7 +233,12 @@ module ActiveRecord
222
233
  end
223
234
  end
224
235
 
225
- assert_equal "you can't define an already defined column 'testing_column'.", error.message
236
+ expected = if ActiveRecord::gem_version < Gem::Version.create('7.1.0')
237
+ "you can't define an already defined column 'testing_column'."
238
+ else
239
+ "you can't define an already defined column 'testing_column' on 'testings'."
240
+ end
241
+ assert_equal expected, error.message
226
242
  end
227
243
 
228
244
  def test_create_table_with_timestamps_should_create_datetime_columns
@@ -13,6 +13,10 @@ module ActiveRecord
13
13
  class IndexTest < SpannerAdapter::TestCase
14
14
  include SpannerAdapter::Migration::TestHelper
15
15
 
16
+ def is_7_1_or_higher?
17
+ ActiveRecord::gem_version >= Gem::Version.create('7.1.0')
18
+ end
19
+
16
20
  def test_dump_schema_contains_start_batch_ddl
17
21
  connection = ActiveRecord::Base.connection
18
22
  schema = StringIO.new
@@ -34,7 +38,12 @@ module ActiveRecord
34
38
  connection = ActiveRecord::Base.connection
35
39
  schema = StringIO.new
36
40
  ActiveRecord::SchemaDumper.dump connection, schema
37
- assert schema.string.include?("create_table \"albums\", primary_key: \"albumid\"")
41
+ sql = schema.string
42
+ if is_7_1_or_higher?
43
+ assert schema.string.include?("create_table \"albums\", primary_key: [\"singerid\", \"albumid\"]"), sql
44
+ else
45
+ assert schema.string.include?("create_table \"albums\", primary_key: \"albumid\""), sql
46
+ end
38
47
  end
39
48
 
40
49
  def test_dump_schema_contains_interleaved_index
@@ -6,6 +6,12 @@
6
6
 
7
7
  # frozen_string_literal: true
8
8
 
9
+ # ActiveRecord 7.1 introduced native support for composite primary keys.
10
+ # This deprecates the https://github.com/composite-primary-keys/composite_primary_keys gem that was previously used in
11
+ # this library to support composite primary keys, which again are needed for interleaved tables. These tests use the
12
+ # third-party composite primary key gem and are therefore not executed for Rails 7.1 and higher.
13
+ return if ActiveRecord::gem_version >= Gem::Version.create('7.1.0')
14
+
9
15
  require "test_helper"
10
16
  require "test_helpers/with_separate_database"
11
17
 
@@ -14,6 +14,10 @@ module ActiveRecord
14
14
  class DatabaseTasksTest < SpannerAdapter::TestCase
15
15
  attr_reader :connector_config, :connection
16
16
 
17
+ def is_7_1_or_higher?
18
+ ActiveRecord::gem_version >= Gem::Version.create('7.1.0')
19
+ end
20
+
17
21
  def setup
18
22
  @database_id = "ar-tasks-test-#{SecureRandom.hex 4}"
19
23
  @connector_config = {
@@ -87,8 +91,12 @@ module ActiveRecord
87
91
  end
88
92
  ActiveRecord::Tasks::DatabaseTasks.dump_schema db_config, :sql
89
93
  sql = File.read(filename)
90
- if ENV["SPANNER_EMULATOR_HOST"]
94
+ if ENV["SPANNER_EMULATOR_HOST"] && is_7_1_or_higher?
95
+ assert_equal expected_schema_sql_on_emulator_7_1, sql, msg = sql
96
+ elsif ENV["SPANNER_EMULATOR_HOST"]
91
97
  assert_equal expected_schema_sql_on_emulator, sql, msg = sql
98
+ elsif is_7_1_or_higher?
99
+ assert_equal expected_schema_sql_on_production_7_1, sql, msg = sql
92
100
  else
93
101
  assert_equal expected_schema_sql_on_production, sql, msg = sql
94
102
  end
@@ -235,6 +243,168 @@ CREATE TABLE tracks (
235
243
  ) PRIMARY KEY(singerid, albumid, trackid),
236
244
  INTERLEAVE IN PARENT albums ON DELETE CASCADE;
237
245
  CREATE NULL_FILTERED INDEX index_tracks_on_singerid_and_albumid_and_title ON tracks(singerid, albumid, title), INTERLEAVE IN albums;
246
+ CREATE TABLE table_with_sequence (
247
+ id INT64 NOT NULL DEFAULT (FARM_FINGERPRINT(GENERATE_UUID())),
248
+ name STRING(MAX) NOT NULL,
249
+ age INT64 NOT NULL,
250
+ ) PRIMARY KEY(id);
251
+ CREATE TABLE schema_migrations (
252
+ version STRING(MAX) NOT NULL,
253
+ ) PRIMARY KEY(version);
254
+ CREATE TABLE ar_internal_metadata (
255
+ key STRING(MAX) NOT NULL,
256
+ value STRING(MAX),
257
+ created_at TIMESTAMP NOT NULL,
258
+ updated_at TIMESTAMP NOT NULL,
259
+ ) PRIMARY KEY(key);
260
+ INSERT INTO `schema_migrations` (version) VALUES
261
+ ('1');
262
+
263
+ "
264
+ end
265
+
266
+ def expected_schema_sql_on_emulator_7_1
267
+ "CREATE TABLE all_types (
268
+ id INT64 NOT NULL,
269
+ col_string STRING(MAX),
270
+ col_int64 INT64,
271
+ col_float64 FLOAT64,
272
+ col_numeric NUMERIC,
273
+ col_bool BOOL,
274
+ col_bytes BYTES(MAX),
275
+ col_date DATE,
276
+ col_timestamp TIMESTAMP,
277
+ col_json JSON,
278
+ col_array_string ARRAY<STRING(MAX)>,
279
+ col_array_int64 ARRAY<INT64>,
280
+ col_array_float64 ARRAY<FLOAT64>,
281
+ col_array_numeric ARRAY<NUMERIC>,
282
+ col_array_bool ARRAY<BOOL>,
283
+ col_array_bytes ARRAY<BYTES(MAX)>,
284
+ col_array_date ARRAY<DATE>,
285
+ col_array_timestamp ARRAY<TIMESTAMP>,
286
+ col_array_json ARRAY<JSON>,
287
+ ) PRIMARY KEY(id);
288
+ CREATE TABLE firms (
289
+ id INT64 NOT NULL,
290
+ name STRING(MAX),
291
+ rating INT64,
292
+ description STRING(MAX),
293
+ account_id INT64,
294
+ ) PRIMARY KEY(id);
295
+ CREATE INDEX index_firms_on_account_id ON firms(account_id);
296
+ CREATE TABLE customers (
297
+ id INT64 NOT NULL,
298
+ name STRING(MAX),
299
+ ) PRIMARY KEY(id);
300
+ CREATE TABLE accounts (
301
+ id INT64 NOT NULL,
302
+ customer_id INT64,
303
+ firm_id INT64,
304
+ name STRING(MAX),
305
+ credit_limit INT64,
306
+ transactions_count INT64,
307
+ ) PRIMARY KEY(id);
308
+ CREATE TABLE transactions (
309
+ id INT64 NOT NULL,
310
+ amount FLOAT64,
311
+ account_id INT64,
312
+ ) PRIMARY KEY(id);
313
+ CREATE TABLE departments (
314
+ id INT64 NOT NULL,
315
+ name STRING(MAX),
316
+ resource_type STRING(255),
317
+ resource_id INT64,
318
+ ) PRIMARY KEY(id);
319
+ CREATE INDEX index_departments_on_resource ON departments(resource_type, resource_id);
320
+ CREATE TABLE member_types (
321
+ id INT64 NOT NULL,
322
+ name STRING(MAX),
323
+ ) PRIMARY KEY(id);
324
+ CREATE TABLE members (
325
+ id INT64 NOT NULL,
326
+ name STRING(MAX),
327
+ member_type_id INT64,
328
+ admittable_type STRING(255),
329
+ admittable_id INT64,
330
+ ) PRIMARY KEY(id);
331
+ CREATE TABLE memberships (
332
+ id INT64 NOT NULL,
333
+ joined_on TIMESTAMP,
334
+ club_id INT64,
335
+ member_id INT64,
336
+ favourite BOOL,
337
+ ) PRIMARY KEY(id);
338
+ CREATE TABLE clubs (
339
+ id INT64 NOT NULL,
340
+ name STRING(MAX),
341
+ ) PRIMARY KEY(id);
342
+ CREATE TABLE authors (
343
+ id INT64 NOT NULL,
344
+ name STRING(MAX) NOT NULL,
345
+ registered_date DATE,
346
+ organization_id INT64,
347
+ ) PRIMARY KEY(id);
348
+ CREATE TABLE posts (
349
+ id INT64 NOT NULL,
350
+ title STRING(MAX),
351
+ content STRING(MAX),
352
+ author_id INT64,
353
+ comments_count INT64,
354
+ post_date DATE,
355
+ published_time TIMESTAMP,
356
+ ) PRIMARY KEY(id);
357
+ CREATE INDEX index_posts_on_author_id ON posts(author_id);
358
+ CREATE TABLE comments (
359
+ id INT64 NOT NULL,
360
+ comment STRING(MAX),
361
+ post_id INT64,
362
+ CONSTRAINT fk_rails_2fd19c0db7 FOREIGN KEY(post_id) REFERENCES posts(id),
363
+ ) PRIMARY KEY(id);
364
+ CREATE TABLE addresses (
365
+ id INT64 NOT NULL,
366
+ line1 STRING(MAX),
367
+ postal_code STRING(MAX),
368
+ city STRING(MAX),
369
+ author_id INT64,
370
+ ) PRIMARY KEY(id);
371
+ CREATE TABLE organizations (
372
+ id INT64 NOT NULL,
373
+ name STRING(MAX),
374
+ last_updated TIMESTAMP OPTIONS (
375
+ allow_commit_timestamp = true
376
+ ),
377
+ ) PRIMARY KEY(id);
378
+ CREATE TABLE singers (
379
+ singerid INT64 NOT NULL,
380
+ first_name STRING(200),
381
+ last_name STRING(MAX),
382
+ tracks_count INT64,
383
+ lock_version INT64,
384
+ full_name STRING(MAX) AS (COALESCE(first_name || ' ', '') || last_name) STORED,
385
+ ) PRIMARY KEY(singerid);
386
+ CREATE TABLE albums (
387
+ singerid INT64 NOT NULL,
388
+ albumid INT64 NOT NULL,
389
+ title STRING(MAX),
390
+ lock_version INT64,
391
+ ) PRIMARY KEY(singerid, albumid),
392
+ INTERLEAVE IN PARENT singers ON DELETE NO ACTION;
393
+ CREATE TABLE tracks (
394
+ singerid INT64 NOT NULL,
395
+ albumid INT64 NOT NULL,
396
+ trackid INT64 NOT NULL,
397
+ title STRING(MAX),
398
+ duration NUMERIC,
399
+ lock_version INT64,
400
+ ) PRIMARY KEY(singerid, albumid, trackid),
401
+ INTERLEAVE IN PARENT albums ON DELETE CASCADE;
402
+ CREATE NULL_FILTERED INDEX index_tracks_on_singerid_and_albumid_and_title ON tracks(singerid, albumid, title), INTERLEAVE IN albums;
403
+ CREATE TABLE table_with_sequence (
404
+ id INT64 NOT NULL DEFAULT (FARM_FINGERPRINT(GENERATE_UUID())),
405
+ name STRING(MAX) NOT NULL,
406
+ age INT64 NOT NULL,
407
+ ) PRIMARY KEY(id);
238
408
  CREATE TABLE schema_migrations (
239
409
  version STRING(MAX) NOT NULL,
240
410
  ) PRIMARY KEY(version);
@@ -251,7 +421,10 @@ INSERT INTO `schema_migrations` (version) VALUES
251
421
  end
252
422
 
253
423
  def expected_schema_sql_on_production
254
- "CREATE TABLE accounts (
424
+ "CREATE SEQUENCE test_sequence OPTIONS (
425
+ sequence_kind = 'bit_reversed_positive'
426
+ );
427
+ CREATE TABLE accounts (
255
428
  id INT64 NOT NULL,
256
429
  customer_id INT64,
257
430
  firm_id INT64,
@@ -391,6 +564,171 @@ CREATE TABLE tracks (
391
564
  ) PRIMARY KEY(singerid, albumid, trackid),
392
565
  INTERLEAVE IN PARENT albums ON DELETE CASCADE;
393
566
  CREATE NULL_FILTERED INDEX index_tracks_on_singerid_and_albumid_and_title ON tracks(singerid, albumid, title), INTERLEAVE IN albums;
567
+ CREATE TABLE table_with_sequence (
568
+ id INT64 NOT NULL DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE test_sequence)),
569
+ name STRING(MAX) NOT NULL,
570
+ age INT64 NOT NULL,
571
+ ) PRIMARY KEY(id);
572
+ CREATE TABLE transactions (
573
+ id INT64 NOT NULL,
574
+ amount FLOAT64,
575
+ account_id INT64,
576
+ ) PRIMARY KEY(id);
577
+ INSERT INTO `schema_migrations` (version) VALUES
578
+ ('1');
579
+
580
+ "
581
+ end
582
+
583
+ def expected_schema_sql_on_production_7_1
584
+ "CREATE SEQUENCE test_sequence OPTIONS (
585
+ sequence_kind = 'bit_reversed_positive'
586
+ );
587
+ CREATE TABLE accounts (
588
+ id INT64 NOT NULL,
589
+ customer_id INT64,
590
+ firm_id INT64,
591
+ name STRING(MAX),
592
+ credit_limit INT64,
593
+ transactions_count INT64,
594
+ ) PRIMARY KEY(id);
595
+ CREATE TABLE addresses (
596
+ id INT64 NOT NULL,
597
+ line1 STRING(MAX),
598
+ postal_code STRING(MAX),
599
+ city STRING(MAX),
600
+ author_id INT64,
601
+ ) PRIMARY KEY(id);
602
+ CREATE TABLE all_types (
603
+ id INT64 NOT NULL,
604
+ col_string STRING(MAX),
605
+ col_int64 INT64,
606
+ col_float64 FLOAT64,
607
+ col_numeric NUMERIC,
608
+ col_bool BOOL,
609
+ col_bytes BYTES(MAX),
610
+ col_date DATE,
611
+ col_timestamp TIMESTAMP,
612
+ col_json JSON,
613
+ col_array_string ARRAY<STRING(MAX)>,
614
+ col_array_int64 ARRAY<INT64>,
615
+ col_array_float64 ARRAY<FLOAT64>,
616
+ col_array_numeric ARRAY<NUMERIC>,
617
+ col_array_bool ARRAY<BOOL>,
618
+ col_array_bytes ARRAY<BYTES(MAX)>,
619
+ col_array_date ARRAY<DATE>,
620
+ col_array_timestamp ARRAY<TIMESTAMP>,
621
+ col_array_json ARRAY<JSON>,
622
+ ) PRIMARY KEY(id);
623
+ CREATE TABLE ar_internal_metadata (
624
+ key STRING(MAX) NOT NULL,
625
+ value STRING(MAX),
626
+ created_at TIMESTAMP NOT NULL,
627
+ updated_at TIMESTAMP NOT NULL,
628
+ ) PRIMARY KEY(key);
629
+ CREATE TABLE authors (
630
+ id INT64 NOT NULL,
631
+ name STRING(MAX) NOT NULL,
632
+ registered_date DATE,
633
+ organization_id INT64,
634
+ ) PRIMARY KEY(id);
635
+ CREATE TABLE clubs (
636
+ id INT64 NOT NULL,
637
+ name STRING(MAX),
638
+ ) PRIMARY KEY(id);
639
+ CREATE TABLE comments (
640
+ id INT64 NOT NULL,
641
+ comment STRING(MAX),
642
+ post_id INT64,
643
+ ) PRIMARY KEY(id);
644
+ CREATE TABLE customers (
645
+ id INT64 NOT NULL,
646
+ name STRING(MAX),
647
+ ) PRIMARY KEY(id);
648
+ CREATE TABLE departments (
649
+ id INT64 NOT NULL,
650
+ name STRING(MAX),
651
+ resource_type STRING(255),
652
+ resource_id INT64,
653
+ ) PRIMARY KEY(id);
654
+ CREATE INDEX index_departments_on_resource ON departments(resource_type, resource_id);
655
+ CREATE TABLE firms (
656
+ id INT64 NOT NULL,
657
+ name STRING(MAX),
658
+ rating INT64,
659
+ description STRING(MAX),
660
+ account_id INT64,
661
+ ) PRIMARY KEY(id);
662
+ CREATE INDEX index_firms_on_account_id ON firms(account_id);
663
+ CREATE TABLE member_types (
664
+ id INT64 NOT NULL,
665
+ name STRING(MAX),
666
+ ) PRIMARY KEY(id);
667
+ CREATE TABLE members (
668
+ id INT64 NOT NULL,
669
+ name STRING(MAX),
670
+ member_type_id INT64,
671
+ admittable_type STRING(255),
672
+ admittable_id INT64,
673
+ ) PRIMARY KEY(id);
674
+ CREATE TABLE memberships (
675
+ id INT64 NOT NULL,
676
+ joined_on TIMESTAMP,
677
+ club_id INT64,
678
+ member_id INT64,
679
+ favourite BOOL,
680
+ ) PRIMARY KEY(id);
681
+ CREATE TABLE organizations (
682
+ id INT64 NOT NULL,
683
+ name STRING(MAX),
684
+ last_updated TIMESTAMP OPTIONS (
685
+ allow_commit_timestamp = true
686
+ ),
687
+ ) PRIMARY KEY(id);
688
+ CREATE TABLE posts (
689
+ id INT64 NOT NULL,
690
+ title STRING(MAX),
691
+ content STRING(MAX),
692
+ author_id INT64,
693
+ comments_count INT64,
694
+ post_date DATE,
695
+ published_time TIMESTAMP,
696
+ ) PRIMARY KEY(id);
697
+ ALTER TABLE comments ADD CONSTRAINT fk_rails_2fd19c0db7 FOREIGN KEY(post_id) REFERENCES posts(id);
698
+ CREATE INDEX index_posts_on_author_id ON posts(author_id);
699
+ CREATE TABLE schema_migrations (
700
+ version STRING(MAX) NOT NULL,
701
+ ) PRIMARY KEY(version);
702
+ CREATE TABLE singers (
703
+ singerid INT64 NOT NULL,
704
+ first_name STRING(200),
705
+ last_name STRING(MAX),
706
+ tracks_count INT64,
707
+ lock_version INT64,
708
+ full_name STRING(MAX) AS (COALESCE(first_name || ' ', '') || last_name) STORED,
709
+ ) PRIMARY KEY(singerid);
710
+ CREATE TABLE albums (
711
+ singerid INT64 NOT NULL,
712
+ albumid INT64 NOT NULL,
713
+ title STRING(MAX),
714
+ lock_version INT64,
715
+ ) PRIMARY KEY(singerid, albumid),
716
+ INTERLEAVE IN PARENT singers ON DELETE NO ACTION;
717
+ CREATE TABLE tracks (
718
+ singerid INT64 NOT NULL,
719
+ albumid INT64 NOT NULL,
720
+ trackid INT64 NOT NULL,
721
+ title STRING(MAX),
722
+ duration NUMERIC,
723
+ lock_version INT64,
724
+ ) PRIMARY KEY(singerid, albumid, trackid),
725
+ INTERLEAVE IN PARENT albums ON DELETE CASCADE;
726
+ CREATE NULL_FILTERED INDEX index_tracks_on_singerid_and_albumid_and_title ON tracks(singerid, albumid, title), INTERLEAVE IN albums;
727
+ CREATE TABLE table_with_sequence (
728
+ id INT64 NOT NULL DEFAULT (GET_NEXT_SEQUENCE_VALUE(SEQUENCE test_sequence)),
729
+ name STRING(MAX) NOT NULL,
730
+ age INT64 NOT NULL,
731
+ ) PRIMARY KEY(id);
394
732
  CREATE TABLE transactions (
395
733
  id INT64 NOT NULL,
396
734
  amount FLOAT64,
@@ -6,6 +6,12 @@
6
6
 
7
7
  # frozen_string_literal: true
8
8
 
9
+ # ActiveRecord 7.1 introduced native support for composite primary keys.
10
+ # This deprecates the https://github.com/composite-primary-keys/composite_primary_keys gem that was previously used in
11
+ # this library to support composite primary keys, which again are needed for interleaved tables. These tests use the
12
+ # third-party composite primary key gem and are therefore not executed for Rails 7.1 and higher.
13
+ return if ActiveRecord::gem_version >= Gem::Version.create('7.1.0')
14
+
9
15
  require "test_helper"
10
16
  require "models/singer"
11
17
  require "models/album"