activerecord-spanner-adapter 1.6.2 → 1.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/acceptance-tests-on-emulator.yaml +5 -7
  3. data/.github/workflows/acceptance-tests-on-production.yaml +1 -1
  4. data/.github/workflows/ci.yaml +5 -7
  5. data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +6 -33
  6. data/.github/workflows/nightly-acceptance-tests-on-production.yaml +1 -1
  7. data/.github/workflows/nightly-unit-tests.yaml +7 -33
  8. data/.kokoro/populate-secrets.sh +5 -1
  9. data/.kokoro/release.cfg +22 -12
  10. data/.kokoro/trampoline_v2.sh +19 -11
  11. data/.release-please-manifest.json +1 -1
  12. data/.trampolinerc +6 -1
  13. data/CHANGELOG.md +28 -0
  14. data/Gemfile +5 -4
  15. data/README.md +1 -0
  16. data/Rakefile +2 -2
  17. data/acceptance/cases/migration/schema_dumper_test.rb +33 -7
  18. data/acceptance/cases/models/insert_all_test.rb +22 -7
  19. data/acceptance/cases/sessions/session_not_found_test.rb +2 -0
  20. data/acceptance/cases/tasks/database_tasks_test.rb +5 -0
  21. data/acceptance/schema/schema.rb +1 -0
  22. data/acceptance/test_helper.rb +5 -1
  23. data/activerecord-spanner-adapter.gemspec +3 -1
  24. data/examples/snippets/bit-reversed-sequence/README.md +6 -0
  25. data/examples/snippets/bit-reversed-sequence/db/migrate/01_create_tables.rb +3 -6
  26. data/examples/snippets/bit-reversed-sequence/db/schema.rb +1 -1
  27. data/lib/active_record/connection_adapters/spanner/database_statements.rb +5 -3
  28. data/lib/active_record/connection_adapters/spanner/quoting.rb +17 -5
  29. data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +3 -1
  30. data/lib/active_record/connection_adapters/spanner/schema_statements.rb +11 -0
  31. data/lib/active_record/connection_adapters/spanner_adapter.rb +34 -20
  32. data/lib/activerecord-spanner-adapter.rb +5 -1
  33. data/lib/activerecord_spanner_adapter/base.rb +46 -12
  34. data/lib/activerecord_spanner_adapter/version.rb +1 -1
  35. metadata +21 -9
  36. data/.github/workflows/release-please-label.yml +0 -25
  37. data/.github/workflows/release-please.yml +0 -38
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2daeef21393399532473625549d008d1a4cfcf791be1a347c98909c3e3973dde
4
- data.tar.gz: 16caacb8cde7a9dfd2d2e22a8500d9a25b21f22fe392bb3c2631af892d35168a
3
+ metadata.gz: e521009bd7ee056c90e4147edd3e6dfe012e7808abf8dcf2b9a0145f380d669d
4
+ data.tar.gz: 9be31f9f418a9ca5f80531924bc299a75c64826d09989c7318aff88eaccf8a41
5
5
  SHA512:
6
- metadata.gz: da334e1c10da6bbd60c6ececee046889b33e045df31838b8a0939f56cf93240b44c20002aa2b61bbcb6b961907b1f1ed73aa0fc9de8b1283faef427db84fce94
7
- data.tar.gz: 52c05cfca494e311aa7049591a2258acf93880434d548834667742440ba9a7ddc283183dedb16227fc5b469fbc2b1cd50ed333c30f86e6f2fda0bf3ae1473e72
6
+ metadata.gz: a529d5691fdcecc54e6a090a807c5b5ff561aa2a355d268a605c91b54705a9a093544129da76df2bb5341953a55dfac792be77079aa1b63121260eb8ed9559c3
7
+ data.tar.gz: 0f6be5f7de6113d16ce2f5e0474924ee4af6bdf3599c84050382f5c47a64f24876b656522e57ebb1e35742e6154221174b0a1e7487fa82034a5a4a5acfa527c2
@@ -18,16 +18,14 @@ jobs:
18
18
  strategy:
19
19
  max-parallel: 4
20
20
  matrix:
21
- ruby: ["2.7", "3.0", "3.1", "3.2"]
22
- ar: ["~> 6.0.6", "~> 6.1.7", "~> 7.0.4", "~> 7.1.0"]
21
+ ruby: ["2.7", "3.0", "3.1", "3.2", "3.3"]
22
+ ar: ["~> 6.1.0", "~> 7.0.0", "~> 7.1.0", "~> 7.2.0"]
23
23
  # Exclude combinations that are not supported.
24
24
  exclude:
25
+ - ruby: "2.7"
26
+ ar: "~> 7.2.0"
25
27
  - ruby: "3.0"
26
- ar: "~> 6.0.6"
27
- - ruby: "3.1"
28
- ar: "~> 6.0.6"
29
- - ruby: "3.2"
30
- ar: "~> 6.0.6"
28
+ ar: "~> 7.2.0"
31
29
  env:
32
30
  AR_VERSION: ${{ matrix.ar }}
33
31
  steps:
@@ -24,7 +24,7 @@ jobs:
24
24
  strategy:
25
25
  max-parallel: 4
26
26
  matrix:
27
- ruby: [3.0]
27
+ ruby: [3.3]
28
28
  steps:
29
29
  - uses: actions/checkout@v4
30
30
  - name: Set up Ruby
@@ -10,16 +10,14 @@ jobs:
10
10
  strategy:
11
11
  max-parallel: 4
12
12
  matrix:
13
- ruby: ["2.7", "3.0", "3.1", "3.2"]
14
- ar: ["~> 6.0.6", "~> 6.1.7", "~> 7.0.4", "~> 7.1.0"]
13
+ ruby: ["2.7", "3.0", "3.1", "3.2", "3.3"]
14
+ ar: ["~> 6.1.0", "~> 7.0.0", "~> 7.1.0", "~> 7.2.0"]
15
15
  # Exclude combinations that are not supported.
16
16
  exclude:
17
+ - ruby: "2.7"
18
+ ar: "~> 7.2.0"
17
19
  - ruby: "3.0"
18
- ar: "~> 6.0.6"
19
- - ruby: "3.1"
20
- ar: "~> 6.0.6"
21
- - ruby: "3.2"
22
- ar: "~> 6.0.6"
20
+ ar: "~> 7.2.0"
23
21
  env:
24
22
  AR_VERSION: ${{ matrix.ar }}
25
23
  steps:
@@ -18,41 +18,14 @@ jobs:
18
18
  strategy:
19
19
  max-parallel: 4
20
20
  matrix:
21
- # Run acceptance tests all supported combinations of Ruby and ActiveRecord.
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, 7.1.0, 7.1.1, 7.1.2]
21
+ ruby: ["2.7", "3.0", "3.1", "3.2", "3.3"]
22
+ ar: ["~> 6.1.0", "~> 7.0.0", "~> 7.1.0", "~> 7.2.0"]
24
23
  # Exclude combinations that are not supported.
25
24
  exclude:
26
- - ruby: 3.0
27
- ar: 6.0.0
28
- - ruby: 3.0
29
- ar: 6.0.1
30
- - ruby: 3.0
31
- ar: 6.0.2.2
32
- - ruby: 3.0
33
- ar: 6.0.3.7
34
- - ruby: 3.0
35
- ar: 6.0.4
36
- - ruby: 3.1
37
- ar: 6.0.0
38
- - ruby: 3.1
39
- ar: 6.0.1
40
- - ruby: 3.1
41
- ar: 6.0.2.2
42
- - ruby: 3.1
43
- ar: 6.0.3.7
44
- - ruby: 3.1
45
- ar: 6.0.4
46
- - ruby: 3.2
47
- ar: 6.0.0
48
- - ruby: 3.2
49
- ar: 6.0.1
50
- - ruby: 3.2
51
- ar: 6.0.2.2
52
- - ruby: 3.2
53
- ar: 6.0.3.7
54
- - ruby: 3.2
55
- ar: 6.0.4
25
+ - ruby: "2.7"
26
+ ar: "~> 7.2.0"
27
+ - ruby: "3.0"
28
+ ar: "~> 7.2.0"
56
29
  env:
57
30
  AR_VERSION: ${{ matrix.ar }}
58
31
  steps:
@@ -10,7 +10,7 @@ jobs:
10
10
  strategy:
11
11
  max-parallel: 4
12
12
  matrix:
13
- ruby: [3.0]
13
+ ruby: [3.3]
14
14
  steps:
15
15
  - uses: actions/checkout@v4
16
16
  - name: Set up Ruby
@@ -10,41 +10,15 @@ jobs:
10
10
  strategy:
11
11
  max-parallel: 4
12
12
  matrix:
13
- # Run unit tests all supported combinations of Ruby and ActiveRecord.
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, 7.1.0, 7.1.1, 7.1.2]
13
+ # Run acceptance tests all supported combinations of Ruby and ActiveRecord.
14
+ ruby: ["2.7", "3.0", "3.1", "3.2", "3.3"]
15
+ ar: ["~> 6.1.0", "~> 7.0.0", "~> 7.1.0", "~> 7.2.0"]
16
16
  # Exclude combinations that are not supported.
17
17
  exclude:
18
- - ruby: 3.0
19
- ar: 6.0.0
20
- - ruby: 3.0
21
- ar: 6.0.1
22
- - ruby: 3.0
23
- ar: 6.0.2.2
24
- - ruby: 3.0
25
- ar: 6.0.3.7
26
- - ruby: 3.0
27
- ar: 6.0.4
28
- - ruby: 3.1
29
- ar: 6.0.0
30
- - ruby: 3.1
31
- ar: 6.0.1
32
- - ruby: 3.1
33
- ar: 6.0.2.2
34
- - ruby: 3.1
35
- ar: 6.0.3.7
36
- - ruby: 3.1
37
- ar: 6.0.4
38
- - ruby: 3.2
39
- ar: 6.0.0
40
- - ruby: 3.2
41
- ar: 6.0.1
42
- - ruby: 3.2
43
- ar: 6.0.2.2
44
- - ruby: 3.2
45
- ar: 6.0.3.7
46
- - ruby: 3.2
47
- ar: 6.0.4
18
+ - ruby: "2.7"
19
+ ar: "~> 7.2.0"
20
+ - ruby: "3.0"
21
+ ar: "~> 7.2.0"
48
22
  env:
49
23
  AR_VERSION: ${{ matrix.ar }}
50
24
  steps:
@@ -24,6 +24,10 @@ function msg { println "$*" >&2 ;}
24
24
  function println { printf '%s\n' "$(now) $*" ;}
25
25
 
26
26
  # Populates requested secrets set in SECRET_MANAGER_KEYS
27
+ if [[ -z "${SECRET_MANAGER_PROJECT_ID-}" ]]; then
28
+ msg "SECRET_MANAGER_PROJECT_ID is not set in environment variables, using default"
29
+ SECRET_MANAGER_PROJECT_ID="cloud-devrel-kokoro-resources"
30
+ fi
27
31
 
28
32
  # In Kokoro CI builds, we use the service account attached to the
29
33
  # Kokoro VM. This means we need to setup auth on other CI systems.
@@ -65,7 +69,7 @@ do
65
69
  msg "Retrieving secret ${key}"
66
70
  "${GCLOUD_COMMANDS[@]}" \
67
71
  secrets versions access latest \
68
- --project cloud-devrel-kokoro-resources \
72
+ --project "${SECRET_MANAGER_PROJECT_ID}" \
69
73
  --secret $key > \
70
74
  "$SECRET_LOCATION/$key"
71
75
  if [[ $? == 0 ]]; then
data/.kokoro/release.cfg CHANGED
@@ -7,19 +7,13 @@ action {
7
7
  }
8
8
  }
9
9
 
10
- # Download resources for system tests (service account key, etc.)
11
- gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/google-cloud-ruby"
12
-
13
- # Download trampoline resources.
14
- gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline"
15
-
16
10
  # Use the trampoline script to run in docker.
17
11
  build_file: "ruby-spanner-activerecord/.kokoro/trampoline_v2.sh"
18
12
 
19
13
  # Configure the docker image for kokoro-trampoline.
20
14
  env_vars: {
21
15
  key: "TRAMPOLINE_IMAGE"
22
- value: "gcr.io/cloud-devrel-kokoro-resources/yoshi-ruby/release"
16
+ value: "us-central1-docker.pkg.dev/cloud-sdk-release-custom-pool/release-images/ruby-multi"
23
17
  }
24
18
 
25
19
  env_vars: {
@@ -27,16 +21,32 @@ env_vars: {
27
21
  value: ".kokoro/release.sh"
28
22
  }
29
23
 
24
+ env_vars: {
25
+ key: "SECRET_MANAGER_PROJECT_ID"
26
+ value: "cloud-sdk-release-custom-pool"
27
+ }
28
+
30
29
  env_vars: {
31
30
  key: "SECRET_MANAGER_KEYS"
32
31
  value: "releasetool-publish-reporter-app,releasetool-publish-reporter-googleapis-installation,releasetool-publish-reporter-pem,docuploader_service_account"
33
32
  }
34
33
 
34
+ # Pick up Rubygems key from internal keystore
35
+ before_action {
36
+ fetch_keystore {
37
+ keystore_resource {
38
+ keystore_config_id: 73713
39
+ keyname: "rubygems-publish-key"
40
+ backend: "blade:keystore-fastconfigpush"
41
+ }
42
+ }
43
+ }
44
+
35
45
  # Store the packages uploaded to rubygems.org, which
36
46
  # we can later use to generate SBOMs and attestations.
37
47
  action {
38
- define_artifacts {
39
- regex: "github/ruby-spanner-activerecord/pkg/*.gem"
40
- strip_prefix: "github"
41
- }
42
- }
48
+ define_artifacts {
49
+ regex: "github/ruby-spanner-activerecord/pkg/*.gem"
50
+ strip_prefix: "github"
51
+ }
52
+ }
@@ -138,18 +138,26 @@ if [[ -n "${KOKORO_BUILD_ID:-}" ]]; then
138
138
  RUNNING_IN_CI="true"
139
139
  TRAMPOLINE_CI="kokoro"
140
140
  if [[ "${TRAMPOLINE_USE_LEGACY_SERVICE_ACCOUNT:-}" == "true" ]]; then
141
- if [[ ! -f "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" ]]; then
142
- log_red "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json does not exist. Did you forget to mount cloud-devrel-kokoro-resources/trampoline? Aborting."
143
- exit 1
144
- fi
145
- # This service account will be activated later.
146
- TRAMPOLINE_SERVICE_ACCOUNT="${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json"
141
+ if [[ ! -f "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" ]]; then
142
+ log_red "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json does not exist. Did you forget to mount cloud-devrel-kokoro-resources/trampoline? Aborting."
143
+ exit 1
144
+ fi
145
+ # This service account will be activated later.
146
+ TRAMPOLINE_SERVICE_ACCOUNT="${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json"
147
147
  else
148
- if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then
149
- gcloud auth list
150
- fi
151
- log_yellow "Configuring Container Registry access"
152
- gcloud auth configure-docker --quiet
148
+ if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then
149
+ gcloud auth list
150
+ fi
151
+ log_yellow "Configuring Container Registry access"
152
+ TRAMPOLINE_HOST=$(echo "${TRAMPOLINE_IMAGE}" | cut -d/ -f1)
153
+ if [[ ! "${TRAMPOLINE_HOST}" =~ "gcr.io" ]]; then
154
+ # If you need to specificy a host other than gcr.io, you have to run on an update version of gcloud.
155
+ echo "TRAMPOLINE_HOST: ${TRAMPOLINE_HOST}"
156
+ gcloud components update
157
+ gcloud auth configure-docker "${TRAMPOLINE_HOST}"
158
+ else
159
+ gcloud auth configure-docker --quiet
160
+ fi
153
161
  fi
154
162
  pass_down_envvars+=(
155
163
  # KOKORO dynamic variables.
@@ -1,3 +1,3 @@
1
1
  {
2
- ".": "1.6.2"
2
+ ".": "1.8.0"
3
3
  }
data/.trampolinerc CHANGED
@@ -18,7 +18,12 @@ required_envvars+=(
18
18
 
19
19
  # Add env vars which are passed down into the container here.
20
20
  pass_down_envvars+=(
21
- "AUTORELEASE_PR" "RELEASE_DRY_RUN" "RELEASE_PACKAGE" "KOKORO_GIT_COMMIT" "RUBY_VERSIONS" "EXTRA_CI_ARGS"
21
+ "AUTORELEASE_PR"
22
+ "EXTRA_CI_ARGS"
23
+ "KOKORO_GIT_COMMIT"
24
+ "RELEASE_DRY_RUN"
25
+ "RELEASE_PACKAGE"
26
+ "RUBY_VERSIONS"
22
27
  )
23
28
 
24
29
  # Prevent unintentional override on the default image.
data/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ### 1.8.0 (2024-12-12)
4
+
5
+ #### Features
6
+
7
+ * INSERT OR [IGNORE|UPDATE] ([#332](https://github.com/googleapis/ruby-spanner-activerecord/issues/332))
8
+ #### Bug Fixes
9
+
10
+ * Fixed incorrect argument handling. ([#333](https://github.com/googleapis/ruby-spanner-activerecord/issues/333))
11
+
12
+ ### 1.7.0 (2024-12-11)
13
+
14
+ #### Features
15
+
16
+ * support Rails 7.2.0 ([#328](https://github.com/googleapis/ruby-spanner-activerecord/issues/328))
17
+ #### Bug Fixes
18
+
19
+ * `SpannerAdapter` requires prepared statements to be enabled ([#323](https://github.com/googleapis/ruby-spanner-activerecord/issues/323))
20
+ * local emulator test ([#320](https://github.com/googleapis/ruby-spanner-activerecord/issues/320))
21
+
22
+ ### 1.6.3 (2024-08-31)
23
+
24
+ #### Bug Fixes
25
+
26
+ * a few Ruby DSL schema dump bug fixes ([#308](https://github.com/googleapis/ruby-spanner-activerecord/issues/308))
27
+ #### Documentation
28
+
29
+ * update bit-reversed sequence sample ([#303](https://github.com/googleapis/ruby-spanner-activerecord/issues/303))
30
+
3
31
  ### 1.6.2 (2024-02-19)
4
32
 
5
33
  #### Bug Fixes
data/Gemfile CHANGED
@@ -3,16 +3,17 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in activerecord-spanner.gemspec
4
4
  gemspec
5
5
 
6
- gem "activerecord", ENV.fetch("AR_VERSION", "~> 6.1.6.1")
7
- gem "minitest", "~> 5.20.0"
6
+ gem "activerecord", ENV.fetch("AR_VERSION", "~> 7.1.0")
7
+ gem "ostruct"
8
+ gem "minitest", "~> 5.25.0"
8
9
  gem "minitest-rg", "~> 5.3.0"
9
10
  gem "pry", "~> 0.13.0"
10
11
  gem "pry-byebug", "~> 3.9.0"
11
12
  # Add sqlite3 for testing for compatibility with other adapters.
12
- gem "sqlite3"
13
+ gem 'sqlite3', '~> 1.4'
13
14
 
14
15
  # Required for samples and testing.
15
- install_if -> { ENV.fetch("AR_VERSION", "~> 6.1.6.1").dup.to_s.sub("~>", "").strip < "7.1.0" && !ENV["SKIP_COMPOSITE_PK"] } do
16
+ install_if -> { ENV.fetch("AR_VERSION", "~> 7.1.0").dup.to_s.sub("~>", "").strip < "7.1.0" && !ENV["SKIP_COMPOSITE_PK"] } do
16
17
  gem "composite_primary_keys"
17
18
  end
18
19
 
data/README.md CHANGED
@@ -77,6 +77,7 @@ __NOTE__: You do need to have [Docker](https://docs.docker.com/get-docker/) inst
77
77
  Some noteworthy examples in the snippets directory:
78
78
  - [quickstart](examples/snippets/quickstart): A simple application that shows how to create and query a simple database containing two tables.
79
79
  - [migrations](examples/snippets/migrations): Shows a best-practice for executing migrations on Cloud Spanner.
80
+ - [bit-reversed-sequences](examples/snippets/bit-reversed-sequence): Shows how to use bit-reversed sequences for primary keys.
80
81
  - [read-write-transactions](examples/snippets/read-write-transactions): Shows how to execute transactions on Cloud Spanner.
81
82
  - [read-only-transactions](examples/snippets/read-only-transactions): Shows how to execute read-only transactions on Cloud Spanner.
82
83
  - [bulk-insert](examples/snippets/bulk-insert): Shows the best way to insert a large number of new records.
data/Rakefile CHANGED
@@ -56,8 +56,8 @@ task :acceptance, [:project, :keyfile, :instance, :tests] do |t, args|
56
56
  tests ||= "**"
57
57
 
58
58
  # always overwrite when running tests
59
- ENV["SPANNER_PROJECT"] = project
60
- ENV["SPANNER_KEYFILE_JSON"] = keyfile
59
+ ENV["SPANNER_TEST_PROJECT"] = project
60
+ ENV["SPANNER_TEST_KEYFILE_JSON"] = keyfile
61
61
  ENV["SPANNER_TEST_INSTANCE"] = instance
62
62
  ENV["SPANNER_EMULATOR_HOST"] = emulator_host
63
63
 
@@ -17,15 +17,27 @@ module ActiveRecord
17
17
  ActiveRecord::gem_version >= Gem::Version.create('7.1.0')
18
18
  end
19
19
 
20
+ def is_7_2_or_higher?
21
+ ActiveRecord::gem_version >= Gem::Version.create('7.2.0')
22
+ end
23
+
24
+ def pool_or_connection
25
+ if is_7_2_or_higher?
26
+ ActiveRecord::Base.connection_pool
27
+ else
28
+ ActiveRecord::Base.connection
29
+ end
30
+ end
31
+
20
32
  def test_dump_schema_contains_start_batch_ddl
21
- connection = ActiveRecord::Base.connection
33
+ connection = pool_or_connection
22
34
  schema = StringIO.new
23
35
  ActiveRecord::SchemaDumper.dump connection, schema
24
36
  assert schema.string.include?("connection.start_batch_ddl")
25
37
  end
26
38
 
27
39
  def test_dump_schema_contains_run_batch
28
- connection = ActiveRecord::Base.connection
40
+ connection = pool_or_connection
29
41
  schema = StringIO.new
30
42
  ActiveRecord::SchemaDumper.dump connection, schema
31
43
  assert schema.string.include?(" connection.run_batch\n"\
@@ -35,7 +47,7 @@ module ActiveRecord
35
47
  end
36
48
 
37
49
  def test_dump_schema_contains_albums_table
38
- connection = ActiveRecord::Base.connection
50
+ connection = pool_or_connection
39
51
  schema = StringIO.new
40
52
  ActiveRecord::SchemaDumper.dump connection, schema
41
53
  sql = schema.string
@@ -47,32 +59,46 @@ module ActiveRecord
47
59
  end
48
60
 
49
61
  def test_dump_schema_contains_interleaved_index
50
- connection = ActiveRecord::Base.connection
62
+ connection = pool_or_connection
51
63
  schema = StringIO.new
52
64
  ActiveRecord::SchemaDumper.dump connection, schema
53
65
  assert schema.string.include?("t.index [\"singerid\", \"albumid\", \"title\"], name: \"index_tracks_on_singerid_and_albumid_and_title\", order: { singerid: :asc, albumid: :asc, title: :asc }, null_filtered: true, interleave_in: \"albums\""), schema.string
54
66
  end
55
67
 
56
68
  def test_dump_schema_should_not_contain_id_limit
57
- connection = ActiveRecord::Base.connection
69
+ connection = pool_or_connection
58
70
  schema = StringIO.new
59
71
  ActiveRecord::SchemaDumper.dump connection, schema
60
72
  assert !schema.string.include?("id: { limit: 8 }")
61
73
  end
62
74
 
63
75
  def test_dump_schema_contains_commit_timestamp
64
- connection = ActiveRecord::Base.connection
76
+ connection = pool_or_connection
65
77
  schema = StringIO.new
66
78
  ActiveRecord::SchemaDumper.dump connection, schema
67
79
  assert schema.string.include?("t.time \"last_updated\", allow_commit_timestamp: true"), schema.string
68
80
  end
69
81
 
70
82
  def test_dump_schema_contains_virtual_column
71
- connection = ActiveRecord::Base.connection
83
+ connection = pool_or_connection
72
84
  schema = StringIO.new
73
85
  ActiveRecord::SchemaDumper.dump connection, schema
74
86
  assert schema.string.include?("t.virtual \"full_name\", type: :string, as: \"COALESCE(first_name || ' ', '') || last_name\", stored: true"), schema.string
75
87
  end
88
+
89
+ def test_dump_schema_contains_string_array
90
+ connection = pool_or_connection
91
+ schema = StringIO.new
92
+ ActiveRecord::SchemaDumper.dump connection, schema
93
+ assert schema.string.include?("t.string \"col_array_string\", array: true"), schema.string
94
+ end
95
+
96
+ def test_dump_schema_index_storing
97
+ connection = pool_or_connection
98
+ schema = StringIO.new
99
+ ActiveRecord::SchemaDumper.dump connection, schema
100
+ assert schema.string.include?("t.index [\"last_name\"], name: \"index_singers_on_last_name\", order: { last_name: :asc }, storing: [\"first_name\", \"tracks_count\"]"), schema.string
101
+ end
76
102
  end
77
103
  end
78
104
  end
@@ -30,13 +30,24 @@ module ActiveRecord
30
30
  { id: Author.next_sequence_value, name: "Carol" },
31
31
  ]
32
32
 
33
- assert_raise(NotImplementedError) { Author.insert_all(values) }
33
+ Author.insert_all(values)
34
+
35
+ authors = Author.all.order(:name)
36
+
37
+ assert_equal "Alice", authors[0].name
38
+ assert_equal "Bob", authors[1].name
39
+ assert_equal "Carol", authors[2].name
34
40
  end
35
41
 
36
42
  def test_insert
37
43
  value = { id: Author.next_sequence_value, name: "Alice" }
38
44
 
39
- assert_raise(NotImplementedError) { Author.insert(value) }
45
+ Author.insert(value)
46
+
47
+ authors = Author.all.order(:name)
48
+
49
+ assert_equal 1, authors.length
50
+ assert_equal "Alice", authors[0].name
40
51
  end
41
52
 
42
53
  def test_insert_all!
@@ -136,12 +147,16 @@ module ActiveRecord
136
147
  { id: Author.next_sequence_value, name: "Carol" },
137
148
  ]
138
149
 
139
- err = assert_raise(NotImplementedError) do
140
- ActiveRecord::Base.transaction do
141
- Author.upsert_all(values)
142
- end
150
+ ActiveRecord::Base.transaction do
151
+ Author.upsert_all(values)
143
152
  end
144
- assert_match "Use upsert outside a transaction block", err.message
153
+
154
+ authors = Author.all.order(:name)
155
+
156
+ assert_equal 3, authors.length
157
+ assert_equal "Alice", authors[0].name
158
+ assert_equal "Bob", authors[1].name
159
+ assert_equal "Carol", authors[2].name
145
160
  end
146
161
 
147
162
  def test_upsert_all_with_buffered_mutation_transaction
@@ -51,6 +51,8 @@ module ActiveRecord
51
51
  sessions.each do |session|
52
52
  client.delete_session Google::Cloud::Spanner::V1::DeleteSessionRequest.new name: session.name
53
53
  end
54
+ # Wait a bit to ensure that the sessions are really deleted.
55
+ sleep 5 unless ENV["SPANNER_EMULATOR_HOST"]
54
56
  end
55
57
 
56
58
  def test_single_read
@@ -48,6 +48,7 @@ module ActiveRecord
48
48
  end
49
49
 
50
50
  def teardown
51
+ drop_database
51
52
  ActiveRecord::Base.connection_pool.disconnect!
52
53
  FileUtils.rm_rf ActiveRecord::Tasks::DatabaseTasks.db_dir
53
54
  ActiveRecord::Tasks::DatabaseTasks.db_dir = @original_db_dir
@@ -226,6 +227,7 @@ CREATE TABLE singers (
226
227
  lock_version INT64,
227
228
  full_name STRING(MAX) AS (COALESCE(first_name || ' ', '') || last_name) STORED,
228
229
  ) PRIMARY KEY(singerid);
230
+ CREATE INDEX index_singers_on_last_name ON singers(last_name) STORING (tracks_count, first_name);
229
231
  CREATE TABLE albums (
230
232
  albumid INT64 NOT NULL,
231
233
  singerid INT64 NOT NULL,
@@ -383,6 +385,7 @@ CREATE TABLE singers (
383
385
  lock_version INT64,
384
386
  full_name STRING(MAX) AS (COALESCE(first_name || ' ', '') || last_name) STORED,
385
387
  ) PRIMARY KEY(singerid);
388
+ CREATE INDEX index_singers_on_last_name ON singers(last_name) STORING (tracks_count, first_name);
386
389
  CREATE TABLE albums (
387
390
  singerid INT64 NOT NULL,
388
391
  albumid INT64 NOT NULL,
@@ -547,6 +550,7 @@ CREATE TABLE singers (
547
550
  lock_version INT64,
548
551
  full_name STRING(MAX) AS (COALESCE(first_name || ' ', '') || last_name) STORED,
549
552
  ) PRIMARY KEY(singerid);
553
+ CREATE INDEX index_singers_on_last_name ON singers(last_name) STORING (tracks_count, first_name);
550
554
  CREATE TABLE albums (
551
555
  albumid INT64 NOT NULL,
552
556
  singerid INT64 NOT NULL,
@@ -707,6 +711,7 @@ CREATE TABLE singers (
707
711
  lock_version INT64,
708
712
  full_name STRING(MAX) AS (COALESCE(first_name || ' ', '') || last_name) STORED,
709
713
  ) PRIMARY KEY(singerid);
714
+ CREATE INDEX index_singers_on_last_name ON singers(last_name) STORING (tracks_count, first_name);
710
715
  CREATE TABLE albums (
711
716
  singerid INT64 NOT NULL,
712
717
  albumid INT64 NOT NULL,
@@ -126,6 +126,7 @@ def create_tables_in_test_schema
126
126
  t.integer :lock_version
127
127
  t.virtual :full_name, type: :string, as: "COALESCE(first_name || ' ', '') || last_name", stored: true
128
128
  end
129
+ add_index :singers, :last_name, storing: %i[tracks_count first_name]
129
130
 
130
131
  if is_7_1_or_higher?
131
132
  create_table :albums, primary_key: [:singerid, :albumid] do |t|
@@ -8,6 +8,7 @@ gem "minitest"
8
8
  require "minitest/autorun"
9
9
  require "minitest/focus"
10
10
  require "minitest/rg"
11
+ require "ostruct"
11
12
  require "active_support"
12
13
  require "google/cloud/spanner"
13
14
  require "active_record"
@@ -18,7 +19,10 @@ require "securerandom"
18
19
  require "composite_primary_keys" if ActiveRecord::gem_version < Gem::Version.create('7.1.0')
19
20
 
20
21
  # rubocop:disable Style/GlobalVars
21
-
22
+ #
23
+ if ActiveRecord.gem_version >= Gem::Version.create("7.2.0")
24
+ ActiveRecord::ConnectionAdapters.register("spanner", "ActiveRecord::ConnectionAdapters::SpannerAdapter")
25
+ end
22
26
  $spanner_test_database = "ar-test-#{SecureRandom.hex 4}"
23
27
 
24
28
  def connector_config
@@ -25,7 +25,9 @@ Gem::Specification.new do |spec|
25
25
  spec.required_ruby_version = ">= 2.7"
26
26
 
27
27
  spec.add_dependency "google-cloud-spanner", "~> 2.18"
28
- spec.add_runtime_dependency "activerecord", [">= 6.0.0", "< 7.2"]
28
+ # Pin gRPC to 1.64.3, as 1.65 and 1.66 cause test runs to hang randomly.
29
+ spec.add_dependency "grpc", "1.64.3"
30
+ spec.add_runtime_dependency "activerecord", [">= 6.1", "< 7.3"]
29
31
 
30
32
  spec.add_development_dependency "autotest-suffix", "~> 1.1"
31
33
  spec.add_development_dependency "bundler", "~> 2.0"
@@ -5,6 +5,12 @@ This example shows how to use a bit-reversed sequence to generate the primary ke
5
5
  See https://cloud.google.com/spanner/docs/primary-key-default-value#bit-reversed-sequence for more information
6
6
  about bit-reversed sequences in Cloud Spanner.
7
7
 
8
+ ## Requirements
9
+ Using bit-reversed sequences for generating primary key values in ActiveRecord has the following requirements:
10
+ 1. You must use __ActiveRecord version 7.1 or higher__.
11
+ 2. Your models must include a sequence name like this: `self.sequence_name = :singer_sequence`
12
+ 3. You must create the bit-reversed sequence using a SQL statement in your migrations.
13
+
8
14
  ## Creating Tables with Bit-Reversed Sequences in ActiveRecord
9
15
  You can create bit-reversed sequences using migrations in ActiveRecord by executing a SQL statement using the underlying
10
16
  connection.
@@ -8,15 +8,12 @@ class CreateTables < ActiveRecord::Migration[7.1]
8
8
  def change
9
9
  # Execute the entire migration as one DDL batch.
10
10
  connection.ddl_batch do
11
- # TODO: Uncomment when bit-reversed sequences are supported in the emulator.
12
- # connection.execute "create sequence singer_sequence OPTIONS (sequence_kind = 'bit_reversed_positive')"
11
+ connection.execute "create sequence singer_sequence OPTIONS (sequence_kind = 'bit_reversed_positive')"
13
12
 
14
13
  # Explicitly define the primary key.
15
14
  create_table :singers, id: false, primary_key: :singerid do |t|
16
- # TODO: Uncomment when bit-reversed sequences are supported in the emulator.
17
- # t.integer :singerid, primary_key: true, null: false,
18
- # default: -> { "GET_NEXT_SEQUENCE_VALUE(SEQUENCE singer_sequence)" }
19
- t.integer :singerid, primary_key: true, null: false, default: -> { "FARM_FINGERPRINT(GENERATE_UUID())" }
15
+ t.integer :singerid, primary_key: true, null: false,
16
+ default: -> { "GET_NEXT_SEQUENCE_VALUE(SEQUENCE singer_sequence)" }
20
17
  t.string :first_name
21
18
  t.string :last_name
22
19
  end
@@ -19,7 +19,7 @@ ActiveRecord::Schema[7.1].define(version: 1) do
19
19
  t.string "title"
20
20
  end
21
21
 
22
- create_table "singers", primary_key: "singerid", default: -> { "FARM_FINGERPRINT(GENERATE_UUID())" }, force: :cascade do |t|
22
+ create_table "singers", primary_key: "singerid", default: -> { "GET_NEXT_SEQUENCE_VALUE(SEQUENCE singer_sequence)" }, force: :cascade do |t|
23
23
  t.string "first_name"
24
24
  t.string "last_name"
25
25
  end
@@ -21,15 +21,15 @@ module ActiveRecord
21
21
  internal_execute sql, name, binds
22
22
  end
23
23
 
24
- def internal_exec_query sql, name = "SQL", binds = [], prepare: false, async: false
25
- result = internal_execute sql, name, binds, prepare: prepare, async: async
24
+ def internal_exec_query sql, name = "SQL", binds = [], prepare: false, async: false, allow_retry: false
25
+ result = internal_execute sql, name, binds, prepare: prepare, async: async, allow_retry: allow_retry
26
26
  ActiveRecord::Result.new(
27
27
  result.fields.keys.map(&:to_s), result.rows.map(&:values)
28
28
  )
29
29
  end
30
30
 
31
31
  def internal_execute sql, name = "SQL", binds = [],
32
- prepare: false, async: false # rubocop:disable Lint/UnusedMethodArgument
32
+ prepare: false, async: false, allow_retry: false # rubocop:disable Lint/UnusedMethodArgument, Metrics/LineLength
33
33
  statement_type = sql_statement_type sql
34
34
  # Call `transform` to invoke any query transformers that might have been registered.
35
35
  sql = transform sql
@@ -320,6 +320,8 @@ module ActiveRecord
320
320
  elsif bind.class == Symbol
321
321
  # This ensures that for example :environment is sent as the string 'environment' to Cloud Spanner.
322
322
  type = :STRING
323
+ elsif bind.class == TrueClass || bind.class == FalseClass
324
+ type = :BOOL
323
325
  end
324
326
  [
325
327
  # Generates binds for named parameters in the format `@p1, @p2, ...`
@@ -30,17 +30,29 @@
30
30
 
31
31
  module ActiveRecord
32
32
  module ConnectionAdapters
33
+ QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
34
+ QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
35
+
36
+ module Quoting
37
+ module ClassMethods
38
+ # This is used for ActiveRecord v8 and higher.
39
+ def quote_column_name name
40
+ QUOTED_COLUMN_NAMES[name] ||= "`#{name.to_s.gsub '`', '``'}`".freeze
41
+ end
42
+
43
+ def quote_table_name name
44
+ QUOTED_TABLE_NAMES[name] ||= "`#{name.to_s.gsub '.', '`.`'}`".freeze
45
+ end
46
+ end
47
+ end
33
48
  module Spanner
34
49
  module Quoting
35
- QUOTED_COLUMN_NAMES = Concurrent::Map.new # :nodoc:
36
- QUOTED_TABLE_NAMES = Concurrent::Map.new # :nodoc:
37
-
38
50
  def quote_column_name name
39
- QUOTED_COLUMN_NAMES[name] ||= "`#{super.gsub '`', '``'}`".freeze
51
+ QUOTED_COLUMN_NAMES[name] ||= "`#{name.to_s.gsub '`', '``'}`".freeze
40
52
  end
41
53
 
42
54
  def quote_table_name name
43
- QUOTED_TABLE_NAMES[name] ||= super.gsub(".", "`.`").freeze
55
+ QUOTED_TABLE_NAMES[name] ||= "`#{name.to_s.gsub '.', '`.`'}`".freeze
44
56
  end
45
57
 
46
58
  STR_ESCAPE_REGX = /[\n\r'\\]/.freeze
@@ -27,6 +27,8 @@ module ActiveRecord
27
27
  spec = { type: schema_type(column).inspect }.merge! spec
28
28
  end
29
29
 
30
+ spec[:array] = true if column.sql_type.start_with? "ARRAY<"
31
+
30
32
  spec
31
33
  end
32
34
 
@@ -54,7 +56,7 @@ module ActiveRecord
54
56
  index_parts = super
55
57
  index_parts << "null_filtered: #{index.null_filtered.inspect}" if index.null_filtered
56
58
  index_parts << "interleave_in: #{index.interleave_in.inspect}" if index.interleave_in
57
- index_parts << "storing: #{format_index_parts index.storing}" if index.storing.present?
59
+ index_parts << "storing: #{format_index_parts index.storing.sort}" if index.storing.present?
58
60
  index_parts
59
61
  end
60
62
 
@@ -23,6 +23,7 @@ module ActiveRecord
23
23
  module SchemaStatements
24
24
  VERSION_6_1_0 = Gem::Version.create "6.1.0"
25
25
  VERSION_6_0_3 = Gem::Version.create "6.0.3"
26
+ VERSION_7_2 = Gem::Version.create "7.2.0"
26
27
 
27
28
  def current_database
28
29
  @connection.database_id
@@ -401,6 +402,16 @@ module ActiveRecord
401
402
  information_schema { |i| i.check_constraints table_name }
402
403
  end
403
404
 
405
+ if ActiveRecord.gem_version >= VERSION_7_2
406
+ def migration_context
407
+ pool.migration_context
408
+ end
409
+
410
+ def schema_migration
411
+ pool.schema_migration
412
+ end
413
+ end
414
+
404
415
  def assume_migrated_upto_version version
405
416
  version = version.to_i
406
417
  sm_table = quote_table_name schema_migration.table_name
@@ -28,16 +28,18 @@ require "activerecord_spanner_adapter/primary_key"
28
28
  require "activerecord_spanner_adapter/transaction"
29
29
 
30
30
  module ActiveRecord
31
- module ConnectionHandling # :nodoc:
32
- def spanner_connection config
33
- connection = ActiveRecordSpannerAdapter::Connection.new config
34
- connection.connect!
35
- ConnectionAdapters::SpannerAdapter.new connection, logger, nil, config
36
- rescue Google::Cloud::Error => error
37
- if error.instance_of? Google::Cloud::NotFoundError
38
- raise ActiveRecord::NoDatabaseError
39
- end
40
- raise error
31
+ if ActiveRecord.version < Gem::Version.new("7.2")
32
+ module ConnectionHandling # :nodoc:
33
+ def spanner_connection config
34
+ connection = ActiveRecordSpannerAdapter::Connection.new config
35
+ connection.connect!
36
+ ConnectionAdapters::SpannerAdapter.new connection, logger, nil, config
37
+ rescue Google::Cloud::Error => error
38
+ if error.instance_of? Google::Cloud::NotFoundError
39
+ raise ActiveRecord::NoDatabaseError
40
+ end
41
+ raise error
42
+ end
41
43
  end
42
44
  end
43
45
 
@@ -69,11 +71,21 @@ module ActiveRecord
69
71
  # Determines whether or not to log query binds when executing statements
70
72
  class_attribute :log_statement_binds, instance_writer: false, default: false
71
73
 
72
- def initialize connection, logger, connection_options, config
73
- @connection = connection
74
- @connection_options = connection_options
75
- super connection, logger, config
76
- @raw_connection ||= connection
74
+ def initialize config_or_deprecated_connection, deprecated_logger = nil,
75
+ deprecated_connection_options = nil, deprecated_config = nil
76
+ if config_or_deprecated_connection.is_a? Hash
77
+ @connection = ActiveRecordSpannerAdapter::Connection.new config_or_deprecated_connection
78
+ @connection.connect!
79
+ super config_or_deprecated_connection
80
+ @raw_connection ||= @connection
81
+ else
82
+ @connection = config_or_deprecated_connection
83
+ @connection_options = deprecated_connection_options
84
+ super config_or_deprecated_connection, deprecated_logger, deprecated_config
85
+ @raw_connection ||= config_or_deprecated_connection
86
+ end
87
+ # Spanner does not support unprepared statements
88
+ @prepared_statements = true
77
89
  end
78
90
 
79
91
  def max_identifier_length
@@ -197,12 +209,14 @@ module ActiveRecord
197
209
  raise "ActiveRecordSpannerAdapter does not support insert_sql with buffered_mutations transaction."
198
210
  end
199
211
 
200
- if insert.skip_duplicates? || insert.update_duplicates?
201
- raise NotImplementedError, "CloudSpanner does not support skip_duplicates and update_duplicates."
202
- end
203
-
204
212
  values_list, = insert.values_list
205
- "INSERT #{insert.into} #{values_list}"
213
+ prefix = "INSERT"
214
+ if insert.update_duplicates?
215
+ prefix += " OR UPDATE"
216
+ elsif insert.skip_duplicates?
217
+ prefix += " OR IGNORE"
218
+ end
219
+ "#{prefix} #{insert.into} #{values_list}"
206
220
  end
207
221
 
208
222
  module TypeMapBuilder
@@ -15,7 +15,11 @@ if defined?(Rails)
15
15
  end
16
16
 
17
17
  ActiveSupport.on_load :active_record do
18
- require "active_record/connection_adapters/spanner_adapter"
18
+ if Rails.version >= "7.2.0"
19
+ ActiveRecord::ConnectionAdapters.register("spanner", "ActiveRecord::ConnectionAdapters::SpannerAdapter")
20
+ else
21
+ require "active_record/connection_adapters/spanner_adapter"
22
+ end
19
23
  end
20
24
  end
21
25
  end
@@ -16,6 +16,7 @@ module ActiveRecord
16
16
 
17
17
  class Base
18
18
  VERSION_7_1 = Gem::Version.create "7.1.0"
19
+ VERSION_7_2 = Gem::Version.create "7.2.0"
19
20
 
20
21
  # Creates an object (or multiple objects) and saves it to the database. This method will use mutations instead
21
22
  # of DML if there is no active transaction, or if the active transaction has been created with the option
@@ -48,8 +49,26 @@ module ActiveRecord
48
49
  spanner_adapter? && connection&.current_spanner_transaction&.isolation == :buffered_mutations
49
50
  end
50
51
 
51
- def self._insert_record values, returning = []
52
- if !(buffered_mutations? || (primary_key && values.is_a?(Hash))) || !spanner_adapter?
52
+ def self._should_use_standard_insert_record? values
53
+ !(buffered_mutations? || (primary_key && values.is_a?(Hash))) || !spanner_adapter?
54
+ end
55
+
56
+ def self._internal_insert_record values
57
+ if ActiveRecord.gem_version < VERSION_7_2
58
+ _insert_record values
59
+ else
60
+ _insert_record nil, values
61
+ end
62
+ end
63
+
64
+ def self._insert_record *args
65
+ if ActiveRecord.gem_version < VERSION_7_2
66
+ values, returning = args
67
+ else
68
+ _connection, values, returning = args
69
+ end
70
+
71
+ if _should_use_standard_insert_record? values
53
72
  return super values if ActiveRecord.gem_version < VERSION_7_1
54
73
  return super
55
74
  end
@@ -111,8 +130,20 @@ module ActiveRecord
111
130
  _buffer_record values, :insert_or_update, returning
112
131
  end
113
132
 
114
- def self.insert_all _attributes, **_kwargs
115
- raise NotImplementedError, "Cloud Spanner does not support skip_duplicates. Use insert! or upsert instead."
133
+ def self.insert_all attributes, returning: nil, **_kwargs
134
+ if active_transaction? && buffered_mutations?
135
+ raise NotImplementedError,
136
+ "Spanner does not support skip_duplicates for mutations. " \
137
+ "Use a transaction that uses DML, or use insert! or upsert instead."
138
+ end
139
+ super
140
+ end
141
+
142
+ def self.insert! attributes, returning: nil, **kwargs
143
+ return super unless spanner_adapter?
144
+ return super if active_transaction? && !buffered_mutations?
145
+
146
+ insert_all! [attributes], returning: returning, **kwargs
116
147
  end
117
148
 
118
149
  def self.insert_all! attributes, returning: nil, **_kwargs
@@ -123,24 +154,27 @@ module ActiveRecord
123
154
  # The mutations will be sent as one batch when the transaction is committed.
124
155
  if active_transaction?
125
156
  attributes.each do |record|
126
- _insert_record record
157
+ _internal_insert_record record
127
158
  end
128
159
  else
129
160
  transaction isolation: :buffered_mutations do
130
161
  attributes.each do |record|
131
- _insert_record record
162
+ _internal_insert_record record
132
163
  end
133
164
  end
134
165
  end
135
166
  end
136
167
 
137
- def self.upsert_all attributes, returning: nil, unique_by: nil, **_kwargs
168
+ def self.upsert attributes, returning: nil, **kwargs
138
169
  return super unless spanner_adapter?
139
- if active_transaction? && !buffered_mutations?
140
- raise NotImplementedError, "Cloud Spanner does not support upsert using DML. " \
141
- "Use upsert outside a transaction block or in a transaction " \
142
- "block with isolation: :buffered_mutations"
143
- end
170
+ return super if active_transaction? && !buffered_mutations?
171
+
172
+ upsert_all [attributes], returning: returning, **kwargs
173
+ end
174
+
175
+ def self.upsert_all attributes, returning: nil, unique_by: nil, **kwargs
176
+ return super unless spanner_adapter?
177
+ return super if active_transaction? && !buffered_mutations?
144
178
 
145
179
  # This might seem inefficient, but is actually not, as it is only buffering a mutation locally.
146
180
  # The mutations will be sent as one batch when the transaction is committed.
@@ -5,5 +5,5 @@
5
5
  # https://opensource.org/licenses/MIT.
6
6
 
7
7
  module ActiveRecordSpannerAdapter
8
- VERSION = "1.6.2".freeze
8
+ VERSION = "1.8.0".freeze
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-spanner-adapter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.2
4
+ version: 1.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Google LLC
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-02-19 00:00:00.000000000 Z
11
+ date: 2024-12-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: google-cloud-spanner
@@ -24,26 +24,40 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.18'
27
+ - !ruby/object:Gem::Dependency
28
+ name: grpc
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.64.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.64.3
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: activerecord
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - ">="
32
46
  - !ruby/object:Gem::Version
33
- version: 6.0.0
47
+ version: '6.1'
34
48
  - - "<"
35
49
  - !ruby/object:Gem::Version
36
- version: '7.2'
50
+ version: '7.3'
37
51
  type: :runtime
38
52
  prerelease: false
39
53
  version_requirements: !ruby/object:Gem::Requirement
40
54
  requirements:
41
55
  - - ">="
42
56
  - !ruby/object:Gem::Version
43
- version: 6.0.0
57
+ version: '6.1'
44
58
  - - "<"
45
59
  - !ruby/object:Gem::Version
46
- version: '7.2'
60
+ version: '7.3'
47
61
  - !ruby/object:Gem::Dependency
48
62
  name: autotest-suffix
49
63
  requirement: !ruby/object:Gem::Requirement
@@ -230,8 +244,6 @@ files:
230
244
  - ".github/workflows/nightly-acceptance-tests-on-emulator.yaml"
231
245
  - ".github/workflows/nightly-acceptance-tests-on-production.yaml"
232
246
  - ".github/workflows/nightly-unit-tests.yaml"
233
- - ".github/workflows/release-please-label.yml"
234
- - ".github/workflows/release-please.yml"
235
247
  - ".github/workflows/rubocop.yaml"
236
248
  - ".gitignore"
237
249
  - ".kokoro/populate-secrets.sh"
@@ -577,7 +589,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
577
589
  - !ruby/object:Gem::Version
578
590
  version: '0'
579
591
  requirements: []
580
- rubygems_version: 3.5.3
592
+ rubygems_version: 3.5.23
581
593
  signing_key:
582
594
  specification_version: 4
583
595
  summary: Rails ActiveRecord connector for Google Spanner Database
@@ -1,25 +0,0 @@
1
- name: release-please-label
2
- on:
3
- pull_request_target:
4
- branches:
5
- - main
6
- types:
7
- - opened
8
- jobs:
9
- release-please-label:
10
- if: "${{ github.event.sender.login == 'yoshi-code-bot' && startsWith(github.event.pull_request.title, 'chore(main): release ') }}"
11
- runs-on: ubuntu-latest
12
- steps:
13
- - name: ReleaseLabel
14
- uses: actions/github-script@v7
15
- with:
16
- github-token: ${{secrets.YOSHI_APPROVER_TOKEN}}
17
- script: |
18
- core.info('Labeling release');
19
- await github.rest.issues.addLabels({
20
- owner: context.repo.owner,
21
- repo: context.repo.repo,
22
- issue_number: context.payload.pull_request.number,
23
- labels: ['autorelease: pending']
24
- });
25
- core.info('Labeled');
@@ -1,38 +0,0 @@
1
- name: Release-Please
2
- on:
3
- workflow_dispatch:
4
- inputs:
5
- gem:
6
- description: "Name of single gem to release. Leave blank to check all gems."
7
- required: false
8
- args:
9
- description: "Extra command line arguments."
10
- required: false
11
-
12
- jobs:
13
- release-please:
14
- if: ${{ github.repository == 'googleapis/ruby-spanner-activerecord' }}
15
- runs-on: ubuntu-latest
16
- env:
17
- GITHUB_TOKEN: ${{ secrets.YOSHI_CODE_BOT_TOKEN }}
18
- RELEASE_PLEASE_DISABLE: ${{ secrets.RELEASE_PLEASE_DISABLE }}
19
- steps:
20
- - name: Checkout repo
21
- uses: actions/checkout@v4
22
- - name: Install Ruby 3.0
23
- uses: ruby/setup-ruby@v1
24
- with:
25
- ruby-version: "3.0"
26
- - name: Install NodeJS 16.x
27
- uses: actions/setup-node@v4
28
- with:
29
- node-version: "16.x"
30
- - name: Install tools
31
- run: "gem install --no-document toys"
32
- - name: execute
33
- run: |
34
- toys release manifest -v \
35
- --fork --skip-labeling \
36
- --github-event-name=${{ github.event_name }} \
37
- ${{ github.event.inputs.args }} \
38
- -- ${{ github.event.inputs.gem }}