activerecord-spanner-adapter 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/CODEOWNERS +7 -0
- data/.github/sync-repo-settings.yaml +16 -0
- data/.github/workflows/acceptance-tests-on-emulator.yaml +45 -0
- data/.github/workflows/acceptance-tests-on-production.yaml +36 -0
- data/.github/workflows/ci.yaml +33 -0
- data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +52 -0
- data/.github/workflows/nightly-acceptance-tests-on-production.yaml +35 -0
- data/.github/workflows/nightly-unit-tests.yaml +40 -0
- data/.github/workflows/release-please-label.yml +25 -0
- data/.github/workflows/release-please.yml +39 -0
- data/.github/workflows/rubocop.yaml +31 -0
- data/.gitignore +67 -5
- data/.kokoro/populate-secrets.sh +77 -0
- data/.kokoro/release.cfg +33 -0
- data/.kokoro/release.sh +15 -0
- data/.kokoro/trampoline_v2.sh +489 -0
- data/.rubocop.yml +46 -0
- data/.toys/release.rb +18 -0
- data/.trampolinerc +48 -0
- data/.yardopts +11 -0
- data/CHANGELOG.md +26 -0
- data/CODE_OF_CONDUCT.md +40 -0
- data/CONTRIBUTING.md +79 -0
- data/Gemfile +9 -4
- data/LICENSE +6 -6
- data/README.md +67 -30
- data/Rakefile +79 -3
- data/SECURITY.md +7 -0
- data/acceptance/cases/associations/has_many_associations_test.rb +119 -0
- data/acceptance/cases/associations/has_many_through_associations_test.rb +63 -0
- data/acceptance/cases/associations/has_one_associations_test.rb +79 -0
- data/acceptance/cases/associations/has_one_through_associations_test.rb +98 -0
- data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +211 -0
- data/acceptance/cases/migration/change_schema_test.rb +433 -0
- data/acceptance/cases/migration/change_table_test.rb +115 -0
- data/acceptance/cases/migration/column_attributes_test.rb +122 -0
- data/acceptance/cases/migration/column_positioning_test.rb +48 -0
- data/acceptance/cases/migration/columns_test.rb +201 -0
- data/acceptance/cases/migration/command_recorder_test.rb +406 -0
- data/acceptance/cases/migration/create_join_table_test.rb +216 -0
- data/acceptance/cases/migration/ddl_batching_test.rb +80 -0
- data/acceptance/cases/migration/foreign_key_test.rb +297 -0
- data/acceptance/cases/migration/index_test.rb +211 -0
- data/acceptance/cases/migration/references_foreign_key_test.rb +259 -0
- data/acceptance/cases/migration/references_index_test.rb +135 -0
- data/acceptance/cases/migration/references_statements_test.rb +166 -0
- data/acceptance/cases/migration/rename_column_test.rb +96 -0
- data/acceptance/cases/models/calculation_query_test.rb +128 -0
- data/acceptance/cases/models/generated_column_test.rb +126 -0
- data/acceptance/cases/models/mutation_test.rb +122 -0
- data/acceptance/cases/models/query_test.rb +147 -0
- data/acceptance/cases/sessions/session_not_found_test.rb +121 -0
- data/acceptance/cases/transactions/optimistic_locking_test.rb +141 -0
- data/acceptance/cases/transactions/read_only_transactions_test.rb +67 -0
- data/acceptance/cases/transactions/read_write_transactions_test.rb +248 -0
- data/acceptance/cases/type/all_types_test.rb +152 -0
- data/acceptance/cases/type/binary_test.rb +59 -0
- data/acceptance/cases/type/boolean_test.rb +31 -0
- data/acceptance/cases/type/date_test.rb +32 -0
- data/acceptance/cases/type/date_time_test.rb +30 -0
- data/acceptance/cases/type/float_test.rb +27 -0
- data/acceptance/cases/type/integer_test.rb +44 -0
- data/acceptance/cases/type/numeric_test.rb +27 -0
- data/acceptance/cases/type/string_test.rb +79 -0
- data/acceptance/cases/type/text_test.rb +30 -0
- data/acceptance/cases/type/time_test.rb +87 -0
- data/acceptance/models/account.rb +13 -0
- data/acceptance/models/address.rb +9 -0
- data/acceptance/models/album.rb +12 -0
- data/acceptance/models/all_types.rb +8 -0
- data/acceptance/models/author.rb +11 -0
- data/acceptance/models/club.rb +12 -0
- data/acceptance/models/comment.rb +9 -0
- data/acceptance/models/customer.rb +9 -0
- data/acceptance/models/department.rb +9 -0
- data/acceptance/models/firm.rb +10 -0
- data/acceptance/models/member.rb +13 -0
- data/acceptance/models/member_type.rb +9 -0
- data/acceptance/models/membership.rb +10 -0
- data/acceptance/models/organization.rb +9 -0
- data/acceptance/models/post.rb +10 -0
- data/acceptance/models/singer.rb +10 -0
- data/acceptance/models/track.rb +20 -0
- data/acceptance/models/transaction.rb +9 -0
- data/acceptance/schema/schema.rb +143 -0
- data/acceptance/test_helper.rb +260 -0
- data/activerecord-spanner-adapter.gemspec +32 -17
- data/assets/solidus-db.png +0 -0
- data/benchmarks/README.md +17 -0
- data/benchmarks/Rakefile +14 -0
- data/benchmarks/application.rb +308 -0
- data/benchmarks/config/database.yml +8 -0
- data/benchmarks/config/environment.rb +12 -0
- data/benchmarks/db/migrate/01_create_tables.rb +25 -0
- data/benchmarks/db/schema.rb +29 -0
- data/benchmarks/models/album.rb +9 -0
- data/benchmarks/models/singer.rb +9 -0
- data/bin/console +6 -7
- data/examples/rails/README.md +262 -0
- data/examples/snippets/README.md +29 -0
- data/examples/snippets/Rakefile +57 -0
- data/examples/snippets/array-data-type/README.md +45 -0
- data/examples/snippets/array-data-type/Rakefile +13 -0
- data/examples/snippets/array-data-type/application.rb +45 -0
- data/examples/snippets/array-data-type/config/database.yml +8 -0
- data/examples/snippets/array-data-type/db/migrate/01_create_tables.rb +24 -0
- data/examples/snippets/array-data-type/db/schema.rb +26 -0
- data/examples/snippets/array-data-type/db/seeds.rb +5 -0
- data/examples/snippets/array-data-type/models/entity_with_array_types.rb +18 -0
- data/examples/snippets/bin/create_emulator_instance.rb +18 -0
- data/examples/snippets/bulk-insert/README.md +21 -0
- data/examples/snippets/bulk-insert/Rakefile +13 -0
- data/examples/snippets/bulk-insert/application.rb +64 -0
- data/examples/snippets/bulk-insert/config/database.yml +8 -0
- data/examples/snippets/bulk-insert/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/bulk-insert/db/schema.rb +26 -0
- data/examples/snippets/bulk-insert/db/seeds.rb +5 -0
- data/examples/snippets/bulk-insert/models/album.rb +9 -0
- data/examples/snippets/bulk-insert/models/singer.rb +9 -0
- data/examples/snippets/commit-timestamp/README.md +18 -0
- data/examples/snippets/commit-timestamp/Rakefile +13 -0
- data/examples/snippets/commit-timestamp/application.rb +53 -0
- data/examples/snippets/commit-timestamp/config/database.yml +8 -0
- data/examples/snippets/commit-timestamp/db/migrate/01_create_tables.rb +26 -0
- data/examples/snippets/commit-timestamp/db/schema.rb +29 -0
- data/examples/snippets/commit-timestamp/db/seeds.rb +5 -0
- data/examples/snippets/commit-timestamp/models/album.rb +9 -0
- data/examples/snippets/commit-timestamp/models/singer.rb +9 -0
- data/examples/snippets/config/environment.rb +21 -0
- data/examples/snippets/create-records/README.md +12 -0
- data/examples/snippets/create-records/Rakefile +13 -0
- data/examples/snippets/create-records/application.rb +42 -0
- data/examples/snippets/create-records/config/database.yml +8 -0
- data/examples/snippets/create-records/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/create-records/db/schema.rb +26 -0
- data/examples/snippets/create-records/db/seeds.rb +5 -0
- data/examples/snippets/create-records/models/album.rb +9 -0
- data/examples/snippets/create-records/models/singer.rb +9 -0
- data/examples/snippets/date-data-type/README.md +19 -0
- data/examples/snippets/date-data-type/Rakefile +13 -0
- data/examples/snippets/date-data-type/application.rb +35 -0
- data/examples/snippets/date-data-type/config/database.yml +8 -0
- data/examples/snippets/date-data-type/db/migrate/01_create_tables.rb +20 -0
- data/examples/snippets/date-data-type/db/schema.rb +21 -0
- data/examples/snippets/date-data-type/db/seeds.rb +16 -0
- data/examples/snippets/date-data-type/models/singer.rb +8 -0
- data/examples/snippets/generated-column/README.md +41 -0
- data/examples/snippets/generated-column/Rakefile +13 -0
- data/examples/snippets/generated-column/application.rb +37 -0
- data/examples/snippets/generated-column/config/database.yml +8 -0
- data/examples/snippets/generated-column/db/migrate/01_create_tables.rb +23 -0
- data/examples/snippets/generated-column/db/schema.rb +21 -0
- data/examples/snippets/generated-column/db/seeds.rb +18 -0
- data/examples/snippets/generated-column/models/singer.rb +8 -0
- data/examples/snippets/interleaved-tables/README.md +152 -0
- data/examples/snippets/interleaved-tables/Rakefile +13 -0
- data/examples/snippets/interleaved-tables/application.rb +109 -0
- data/examples/snippets/interleaved-tables/config/database.yml +8 -0
- data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +44 -0
- data/examples/snippets/interleaved-tables/db/schema.rb +32 -0
- data/examples/snippets/interleaved-tables/db/seeds.rb +40 -0
- data/examples/snippets/interleaved-tables/models/album.rb +15 -0
- data/examples/snippets/interleaved-tables/models/singer.rb +20 -0
- data/examples/snippets/interleaved-tables/models/track.rb +25 -0
- data/examples/snippets/migrations/README.md +43 -0
- data/examples/snippets/migrations/Rakefile +13 -0
- data/examples/snippets/migrations/application.rb +26 -0
- data/examples/snippets/migrations/config/database.yml +8 -0
- data/examples/snippets/migrations/db/migrate/01_create_tables.rb +28 -0
- data/examples/snippets/migrations/db/schema.rb +33 -0
- data/examples/snippets/migrations/db/seeds.rb +5 -0
- data/examples/snippets/migrations/models/album.rb +10 -0
- data/examples/snippets/migrations/models/singer.rb +10 -0
- data/examples/snippets/migrations/models/track.rb +9 -0
- data/examples/snippets/mutations/README.md +34 -0
- data/examples/snippets/mutations/Rakefile +13 -0
- data/examples/snippets/mutations/application.rb +47 -0
- data/examples/snippets/mutations/config/database.yml +8 -0
- data/examples/snippets/mutations/db/migrate/01_create_tables.rb +22 -0
- data/examples/snippets/mutations/db/schema.rb +27 -0
- data/examples/snippets/mutations/db/seeds.rb +25 -0
- data/examples/snippets/mutations/models/album.rb +9 -0
- data/examples/snippets/mutations/models/singer.rb +9 -0
- data/examples/snippets/optimistic-locking/README.md +12 -0
- data/examples/snippets/optimistic-locking/Rakefile +13 -0
- data/examples/snippets/optimistic-locking/application.rb +48 -0
- data/examples/snippets/optimistic-locking/config/database.yml +8 -0
- data/examples/snippets/optimistic-locking/db/migrate/01_create_tables.rb +26 -0
- data/examples/snippets/optimistic-locking/db/schema.rb +29 -0
- data/examples/snippets/optimistic-locking/db/seeds.rb +25 -0
- data/examples/snippets/optimistic-locking/models/album.rb +9 -0
- data/examples/snippets/optimistic-locking/models/singer.rb +9 -0
- data/examples/snippets/quickstart/README.md +26 -0
- data/examples/snippets/quickstart/Rakefile +13 -0
- data/examples/snippets/quickstart/application.rb +51 -0
- data/examples/snippets/quickstart/config/database.yml +8 -0
- data/examples/snippets/quickstart/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/quickstart/db/schema.rb +26 -0
- data/examples/snippets/quickstart/db/seeds.rb +24 -0
- data/examples/snippets/quickstart/models/album.rb +9 -0
- data/examples/snippets/quickstart/models/singer.rb +9 -0
- data/examples/snippets/read-only-transactions/README.md +13 -0
- data/examples/snippets/read-only-transactions/Rakefile +13 -0
- data/examples/snippets/read-only-transactions/application.rb +49 -0
- data/examples/snippets/read-only-transactions/config/database.yml +8 -0
- data/examples/snippets/read-only-transactions/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/read-only-transactions/db/schema.rb +26 -0
- data/examples/snippets/read-only-transactions/db/seeds.rb +24 -0
- data/examples/snippets/read-only-transactions/models/album.rb +9 -0
- data/examples/snippets/read-only-transactions/models/singer.rb +9 -0
- data/examples/snippets/read-write-transactions/README.md +12 -0
- data/examples/snippets/read-write-transactions/Rakefile +13 -0
- data/examples/snippets/read-write-transactions/application.rb +39 -0
- data/examples/snippets/read-write-transactions/config/database.yml +8 -0
- data/examples/snippets/read-write-transactions/db/migrate/01_create_tables.rb +22 -0
- data/examples/snippets/read-write-transactions/db/schema.rb +27 -0
- data/examples/snippets/read-write-transactions/db/seeds.rb +25 -0
- data/examples/snippets/read-write-transactions/models/album.rb +9 -0
- data/examples/snippets/read-write-transactions/models/singer.rb +9 -0
- data/examples/snippets/timestamp-data-type/README.md +17 -0
- data/examples/snippets/timestamp-data-type/Rakefile +13 -0
- data/examples/snippets/timestamp-data-type/application.rb +42 -0
- data/examples/snippets/timestamp-data-type/config/database.yml +8 -0
- data/examples/snippets/timestamp-data-type/db/migrate/01_create_tables.rb +21 -0
- data/examples/snippets/timestamp-data-type/db/schema.rb +21 -0
- data/examples/snippets/timestamp-data-type/db/seeds.rb +6 -0
- data/examples/snippets/timestamp-data-type/models/meeting.rb +19 -0
- data/examples/solidus/README.md +172 -0
- data/lib/active_record/connection_adapters/spanner/database_statements.rb +224 -269
- data/lib/active_record/connection_adapters/spanner/quoting.rb +42 -50
- data/lib/active_record/connection_adapters/spanner/schema_cache.rb +43 -0
- data/lib/active_record/connection_adapters/spanner/schema_creation.rb +125 -9
- data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +122 -0
- data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +19 -0
- data/lib/active_record/connection_adapters/spanner/schema_statements.rb +553 -139
- data/lib/active_record/connection_adapters/spanner/type_metadata.rb +37 -0
- data/lib/active_record/connection_adapters/spanner_adapter.rb +182 -78
- data/lib/active_record/tasks/spanner_database_tasks.rb +74 -0
- data/lib/active_record/type/spanner/array.rb +32 -0
- data/lib/active_record/type/spanner/bytes.rb +26 -0
- data/lib/active_record/type/spanner/spanner_active_record_converter.rb +32 -0
- data/lib/active_record/type/spanner/time.rb +37 -0
- data/lib/activerecord-spanner-adapter.rb +23 -0
- data/lib/activerecord_spanner_adapter/base.rb +217 -0
- data/lib/activerecord_spanner_adapter/connection.rb +324 -0
- data/lib/activerecord_spanner_adapter/errors.rb +13 -0
- data/lib/activerecord_spanner_adapter/foreign_key.rb +29 -0
- data/lib/activerecord_spanner_adapter/index/column.rb +38 -0
- data/lib/activerecord_spanner_adapter/index.rb +80 -0
- data/lib/activerecord_spanner_adapter/information_schema.rb +261 -0
- data/lib/activerecord_spanner_adapter/primary_key.rb +31 -0
- data/lib/activerecord_spanner_adapter/table/column.rb +59 -0
- data/lib/activerecord_spanner_adapter/table.rb +61 -0
- data/lib/activerecord_spanner_adapter/transaction.rb +113 -0
- data/lib/activerecord_spanner_adapter/version.rb +9 -0
- data/lib/arel/visitors/spanner.rb +35 -0
- data/lib/spanner_client_ext.rb +82 -0
- data/renovate.json +5 -0
- metadata +387 -34
- data/.travis.yml +0 -5
- data/lib/active_record/connection_adapters/spanner/client.rb +0 -190
- data/lib/active_record/connection_adapters/spanner.rb +0 -10
- data/lib/activerecord-spanner-adapter/version.rb +0 -3
|
@@ -0,0 +1,248 @@
|
|
|
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
|
+
# frozen_string_literal: true
|
|
8
|
+
|
|
9
|
+
require "test_helper"
|
|
10
|
+
require "models/author"
|
|
11
|
+
require "models/post"
|
|
12
|
+
require "models/comment"
|
|
13
|
+
require "models/organization"
|
|
14
|
+
|
|
15
|
+
module ActiveRecord
|
|
16
|
+
module Transactions
|
|
17
|
+
class ReadWriteTransactionsTest < SpannerAdapter::TestCase
|
|
18
|
+
include SpannerAdapter::Associations::TestHelper
|
|
19
|
+
|
|
20
|
+
attr_accessor :organization, :author, :post, :comment
|
|
21
|
+
|
|
22
|
+
def create_test_records
|
|
23
|
+
@organization = Organization.create name: "Organization 1"
|
|
24
|
+
@author = Author.create name: "David", organization: organization
|
|
25
|
+
@post = Post.create title: "Title - 1", author: author
|
|
26
|
+
@comment = Comment.create comment: "Comment - 1", post: post
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def teardown
|
|
30
|
+
super
|
|
31
|
+
|
|
32
|
+
delete_test_records
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def delete_test_records
|
|
36
|
+
Comment.destroy_all
|
|
37
|
+
Post.destroy_all
|
|
38
|
+
Author.destroy_all
|
|
39
|
+
Organization.destroy_all
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Runs the given block in a transaction with the given isolation level, or without a transaction if isolation is
|
|
43
|
+
# nil.
|
|
44
|
+
def run_in_transaction isolation
|
|
45
|
+
if isolation
|
|
46
|
+
Base.transaction isolation: isolation do
|
|
47
|
+
yield
|
|
48
|
+
end
|
|
49
|
+
else
|
|
50
|
+
yield
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_create_multiple_records
|
|
55
|
+
[nil, :serializable, :buffered_mutations].each do |isolation|
|
|
56
|
+
initial_author_count = Author.count
|
|
57
|
+
initial_posts_count = Post.count
|
|
58
|
+
initial_comment_count = Comment.count
|
|
59
|
+
|
|
60
|
+
run_in_transaction isolation do
|
|
61
|
+
author = Author.create name: "Author 1", organization: organization
|
|
62
|
+
posts = Post.create [{title: "Post 1", author: author}, {title: "Post 2", author: author}]
|
|
63
|
+
Comment.create [
|
|
64
|
+
{comment: "Comment 1", post: posts[0]},
|
|
65
|
+
{comment: "Comment 2", post: posts[1]}
|
|
66
|
+
]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Verify that all the records were created.
|
|
70
|
+
assert_equal initial_author_count + 1, Author.count
|
|
71
|
+
assert_equal initial_posts_count + 2, Post.count
|
|
72
|
+
assert_equal initial_comment_count + 2, Comment.count
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def test_update_multiple_records
|
|
77
|
+
[nil, :serializable, :buffered_mutations].each do |isolation|
|
|
78
|
+
create_test_records
|
|
79
|
+
|
|
80
|
+
run_in_transaction isolation do
|
|
81
|
+
organization.update name: "Updated name #{isolation}"
|
|
82
|
+
author.update name: "Updated name #{isolation}"
|
|
83
|
+
post.update title: "Updated title #{isolation}"
|
|
84
|
+
comment.update comment: "Updated comment #{isolation}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
assert_equal "Updated name #{isolation}", organization.reload.name
|
|
88
|
+
assert_equal "Updated name #{isolation}", author.reload.name
|
|
89
|
+
assert_equal "Updated title #{isolation}", post.reload.title
|
|
90
|
+
assert_equal "Updated comment #{isolation}", comment.reload.comment
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def test_destroy_multiple_records
|
|
95
|
+
[nil, :serializable, :buffered_mutations].each do |isolation|
|
|
96
|
+
create_test_records
|
|
97
|
+
|
|
98
|
+
run_in_transaction isolation do
|
|
99
|
+
comment.destroy
|
|
100
|
+
post.destroy
|
|
101
|
+
author.destroy
|
|
102
|
+
organization.destroy
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
assert_equal 0, Organization.count
|
|
106
|
+
assert_equal 0, Author.count
|
|
107
|
+
assert_equal 0, Post.count
|
|
108
|
+
assert_equal 0, Comment.count
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def test_delete_multiple_records
|
|
113
|
+
[nil, :serializable, :buffered_mutations].each do |isolation|
|
|
114
|
+
create_test_records
|
|
115
|
+
|
|
116
|
+
run_in_transaction isolation do
|
|
117
|
+
comment.delete
|
|
118
|
+
post.delete
|
|
119
|
+
author.delete
|
|
120
|
+
organization.delete
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
assert_equal 0, Organization.count
|
|
124
|
+
assert_equal 0, Author.count
|
|
125
|
+
assert_equal 0, Post.count
|
|
126
|
+
assert_equal 0, Comment.count
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def test_destroy_parent_record
|
|
131
|
+
[nil, :serializable, :buffered_mutations].each do |isolation|
|
|
132
|
+
create_test_records
|
|
133
|
+
|
|
134
|
+
run_in_transaction isolation do
|
|
135
|
+
# Only destroy the top-level record. This should cascade to the author records, as those are
|
|
136
|
+
# marked with `dependent: destroy`. The dependants of Author are however not marked with
|
|
137
|
+
# `dependent: destroy`, which means that those will not be deleted, but the reference to Author will
|
|
138
|
+
# be set to nil.
|
|
139
|
+
organization.destroy
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
assert_equal 0, Organization.count
|
|
143
|
+
assert_equal 0, Author.count
|
|
144
|
+
assert_equal 1, Post.count # These are not marked with `dependent: destroy`
|
|
145
|
+
assert_nil Post.find(post.id).author # The author is set to NULL instead of deleting the posts.
|
|
146
|
+
assert_equal 1, Comment.count
|
|
147
|
+
|
|
148
|
+
# Delete all remaining test records to make sure the next iteration starts clean.
|
|
149
|
+
delete_test_records
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def test_multiple_consecutive_transactions
|
|
154
|
+
isolation_levels = [nil, :serializable, :buffered_mutations]
|
|
155
|
+
isolation_levels.each do |isolation|
|
|
156
|
+
|
|
157
|
+
run_in_transaction isolation do
|
|
158
|
+
create_test_records
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
isolation_levels.each do |isolation|
|
|
162
|
+
run_in_transaction isolation do
|
|
163
|
+
create_test_records
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
transaction_count = isolation_levels.length + 1
|
|
168
|
+
assert_equal transaction_count, Organization.count
|
|
169
|
+
assert_equal transaction_count, Author.count
|
|
170
|
+
assert_equal transaction_count, Post.count
|
|
171
|
+
assert_equal transaction_count, Comment.count
|
|
172
|
+
|
|
173
|
+
delete_test_records
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def test_read_your_writes
|
|
178
|
+
[nil, :serializable, :buffered_mutations].each do |isolation|
|
|
179
|
+
initial_author_count = Author.count
|
|
180
|
+
initial_posts_count = Post.count
|
|
181
|
+
initial_comment_count = Comment.count
|
|
182
|
+
|
|
183
|
+
run_in_transaction isolation do
|
|
184
|
+
author = Author.create name: "Author 1", organization: organization
|
|
185
|
+
posts = Post.create [{title: "Post 1", author: author}, {title: "Post 2", author: author}]
|
|
186
|
+
Comment.create [
|
|
187
|
+
{comment: "Comment 1", post: posts[0]},
|
|
188
|
+
{comment: "Comment 2", post: posts[1]}
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
# Verify that the new records are visible, unless we are working with an actual transaction that
|
|
192
|
+
# uses buffered mutations. Implicit transactions (isolation = nil) will also use mutations, but each
|
|
193
|
+
# create call will automatically be committed, and the changes will be visible here.
|
|
194
|
+
unless isolation == :buffered_mutations
|
|
195
|
+
assert_equal initial_author_count + 1, Author.count
|
|
196
|
+
assert_equal initial_posts_count + 2, Post.count
|
|
197
|
+
assert_equal initial_comment_count + 2, Comment.count
|
|
198
|
+
else
|
|
199
|
+
assert_equal initial_author_count, Author.count
|
|
200
|
+
assert_equal initial_posts_count, Post.count
|
|
201
|
+
assert_equal initial_comment_count, Comment.count
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def test_create_commit_timestamp
|
|
208
|
+
[nil, :serializable, :buffered_mutations].each do |isolation|
|
|
209
|
+
current_timestamp = Organization.connection.select_all("SELECT CURRENT_TIMESTAMP() AS t").to_a[0]["t"]
|
|
210
|
+
organization = nil
|
|
211
|
+
run_in_transaction isolation do
|
|
212
|
+
organization = Organization.create name: "Org with commit timestamp", last_updated: :commit_timestamp
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
organization.reload
|
|
216
|
+
assert organization.last_updated
|
|
217
|
+
assert organization.last_updated > current_timestamp
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def test_update_commit_timestamp
|
|
222
|
+
[nil, :serializable, :buffered_mutations].each do |isolation|
|
|
223
|
+
organization = Organization.create name: "Org without commit timestamp"
|
|
224
|
+
current_timestamp = Organization.connection.select_all("SELECT CURRENT_TIMESTAMP() AS t").to_a[0]["t"]
|
|
225
|
+
|
|
226
|
+
run_in_transaction isolation do
|
|
227
|
+
organization.update last_updated: :commit_timestamp
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
organization.reload
|
|
231
|
+
assert organization.last_updated
|
|
232
|
+
assert organization.last_updated > current_timestamp
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def test_pdml
|
|
237
|
+
create_test_records
|
|
238
|
+
assert Comment.count > 0
|
|
239
|
+
|
|
240
|
+
Comment.transaction isolation: :pdml do
|
|
241
|
+
Comment.delete_all
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
assert_equal 0, Comment.count
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
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
|
+
# frozen_string_literal: true
|
|
8
|
+
|
|
9
|
+
require "test_helper"
|
|
10
|
+
require "models/all_types"
|
|
11
|
+
|
|
12
|
+
module ActiveRecord
|
|
13
|
+
module Type
|
|
14
|
+
class AllTypesTest < SpannerAdapter::TestCase
|
|
15
|
+
include SpannerAdapter::Associations::TestHelper
|
|
16
|
+
|
|
17
|
+
# Runs the given block in a transaction with the given isolation level, or without a transaction if isolation is
|
|
18
|
+
# nil.
|
|
19
|
+
def run_in_transaction isolation
|
|
20
|
+
if isolation
|
|
21
|
+
Base.transaction isolation: isolation do
|
|
22
|
+
yield
|
|
23
|
+
end
|
|
24
|
+
else
|
|
25
|
+
yield
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create_test_record
|
|
30
|
+
AllTypes.create col_string: "string", col_int64: 100, col_float64: 3.14, col_numeric: 6.626, col_bool: true,
|
|
31
|
+
col_bytes: StringIO.new("bytes"), col_date: ::Date.new(2021, 6, 23),
|
|
32
|
+
col_timestamp: ::Time.new(2021, 6, 23, 17, 8, 21, "+02:00"),
|
|
33
|
+
col_array_string: ["string1", nil, "string2"],
|
|
34
|
+
col_array_int64: [100, nil, 200],
|
|
35
|
+
col_array_float64: [3.14, nil, 2.0/3.0],
|
|
36
|
+
col_array_numeric: [6.626, nil, 3.20],
|
|
37
|
+
col_array_bool: [true, nil, false],
|
|
38
|
+
col_array_bytes: [StringIO.new("bytes1"), nil, StringIO.new("bytes2")],
|
|
39
|
+
col_array_date: [::Date.new(2021, 6, 23), nil, ::Date.new(2021, 6, 24)],
|
|
40
|
+
col_array_timestamp: [::Time.new(2021, 6, 23, 17, 8, 21, "+02:00"), nil, \
|
|
41
|
+
::Time.new(2021, 6, 24, 17, 8, 21, "+02:00")]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_create_record
|
|
45
|
+
[nil, :serializable, :buffered_mutations].each do |isolation|
|
|
46
|
+
initial_count = AllTypes.count
|
|
47
|
+
record = nil
|
|
48
|
+
run_in_transaction isolation do
|
|
49
|
+
record = create_test_record
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Verify that the record was created and that the data can be read back.
|
|
53
|
+
assert_equal initial_count + 1, AllTypes.count
|
|
54
|
+
|
|
55
|
+
record = AllTypes.find record.id
|
|
56
|
+
assert_equal "string", record.col_string
|
|
57
|
+
assert_equal 100, record.col_int64
|
|
58
|
+
assert_equal 3.14, record.col_float64
|
|
59
|
+
assert_equal 6.626, record.col_numeric
|
|
60
|
+
assert_equal true, record.col_bool
|
|
61
|
+
assert_equal StringIO.new("bytes").read, record.col_bytes.read
|
|
62
|
+
assert_equal ::Date.new(2021, 6, 23), record.col_date
|
|
63
|
+
assert_equal ::Time.new(2021, 6, 23, 17, 8, 21, "+02:00").utc, record.col_timestamp.utc
|
|
64
|
+
|
|
65
|
+
assert_equal ["string1", nil, "string2"], record.col_array_string
|
|
66
|
+
assert_equal [100, nil, 200], record.col_array_int64
|
|
67
|
+
assert_equal [3.14, nil, 2.0/3.0], record.col_array_float64
|
|
68
|
+
assert_equal [6.626, nil, 3.20], record.col_array_numeric
|
|
69
|
+
assert_equal [true, nil, false], record.col_array_bool
|
|
70
|
+
assert_equal [StringIO.new("bytes1"), nil, StringIO.new("bytes2")].map { |bytes| bytes&.read },
|
|
71
|
+
record.col_array_bytes.map { |bytes| bytes&.read }
|
|
72
|
+
assert_equal [::Date.new(2021, 6, 23), nil, ::Date.new(2021, 6, 24)], record.col_array_date
|
|
73
|
+
assert_equal [::Time.new(2021, 6, 23, 17, 8, 21, "+02:00"), \
|
|
74
|
+
nil, \
|
|
75
|
+
::Time.new(2021, 6, 24, 17, 8, 21, "+02:00")].map { |timestamp| timestamp&.utc },
|
|
76
|
+
record.col_array_timestamp.map { |timestamp| timestamp&.utc}
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def test_update_record
|
|
81
|
+
[nil, :serializable, :buffered_mutations].each do |isolation|
|
|
82
|
+
# First create a test record outside a transaction.
|
|
83
|
+
record = create_test_record
|
|
84
|
+
|
|
85
|
+
run_in_transaction isolation do
|
|
86
|
+
# Update the record in a transaction using different isolation levels.
|
|
87
|
+
record.update col_string: "new string", col_int64: 200, col_float64: 6.28, col_numeric: 10.1,
|
|
88
|
+
col_bool: false, col_bytes: StringIO.new("new bytes"),
|
|
89
|
+
col_date: ::Date.new(2021, 6, 28),
|
|
90
|
+
col_timestamp: ::Time.new(2021, 6, 28, 11, 22, 21, "+02:00"),
|
|
91
|
+
col_array_string: ["new string 1", "new string 2"],
|
|
92
|
+
col_array_int64: [300, 200, 100],
|
|
93
|
+
col_array_float64: [1.1, 2.2, 3.3],
|
|
94
|
+
col_array_numeric: [3.3, 2.2, 1.1],
|
|
95
|
+
col_array_bool: [false, true, false],
|
|
96
|
+
col_array_bytes: [StringIO.new("new bytes 1"), StringIO.new("new bytes 2")],
|
|
97
|
+
col_array_date: [::Date.new(2021, 6, 28)],
|
|
98
|
+
col_array_timestamp: [::Time.utc(2020, 12, 31, 0, 0, 0)]
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Verify that the record was updated.
|
|
102
|
+
record = AllTypes.find record.id
|
|
103
|
+
assert_equal "new string", record.col_string
|
|
104
|
+
assert_equal 200, record.col_int64
|
|
105
|
+
assert_equal 6.28, record.col_float64
|
|
106
|
+
assert_equal 10.1, record.col_numeric
|
|
107
|
+
assert_equal false, record.col_bool
|
|
108
|
+
assert_equal StringIO.new("new bytes").read, record.col_bytes.read
|
|
109
|
+
assert_equal ::Date.new(2021, 6, 28), record.col_date
|
|
110
|
+
assert_equal ::Time.new(2021, 6, 28, 11, 22, 21, "+02:00").utc, record.col_timestamp.utc
|
|
111
|
+
|
|
112
|
+
assert_equal ["new string 1", "new string 2"], record.col_array_string
|
|
113
|
+
assert_equal [300, 200, 100], record.col_array_int64
|
|
114
|
+
assert_equal [1.1, 2.2, 3.3], record.col_array_float64
|
|
115
|
+
assert_equal [3.3, 2.2, 1.1], record.col_array_numeric
|
|
116
|
+
assert_equal [false, true, false], record.col_array_bool
|
|
117
|
+
assert_equal [StringIO.new("new bytes 1"), StringIO.new("new bytes 2")].map(&:read),
|
|
118
|
+
record.col_array_bytes.map(&:read)
|
|
119
|
+
assert_equal [::Date.new(2021, 6, 28)], record.col_array_date
|
|
120
|
+
assert_equal [::Time.utc(2020, 12, 31, 0, 0, 0)], record.col_array_timestamp.map(&:utc)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def test_create_empty_arrays
|
|
125
|
+
[nil, :serializable, :buffered_mutations].each do |isolation|
|
|
126
|
+
record = nil
|
|
127
|
+
run_in_transaction isolation do
|
|
128
|
+
record = AllTypes.create \
|
|
129
|
+
col_array_string: [],
|
|
130
|
+
col_array_int64: [],
|
|
131
|
+
col_array_float64: [],
|
|
132
|
+
col_array_numeric: [],
|
|
133
|
+
col_array_bool: [],
|
|
134
|
+
col_array_bytes: [],
|
|
135
|
+
col_array_date: [],
|
|
136
|
+
col_array_timestamp: []
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
record = AllTypes.find record.id
|
|
140
|
+
assert_equal [], record.col_array_string
|
|
141
|
+
assert_equal [], record.col_array_int64
|
|
142
|
+
assert_equal [], record.col_array_float64
|
|
143
|
+
assert_equal [], record.col_array_numeric
|
|
144
|
+
assert_equal [], record.col_array_bool
|
|
145
|
+
assert_equal [], record.col_array_bytes
|
|
146
|
+
assert_equal [], record.col_array_date
|
|
147
|
+
assert_equal [], record.col_array_timestamp
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Copyright 2020 Google LLC
|
|
2
|
+
#
|
|
3
|
+
# Use of this source code is governed by an MIT-style
|
|
4
|
+
# license that can be found in the LICENSE file or at
|
|
5
|
+
# https://opensource.org/licenses/MIT.
|
|
6
|
+
|
|
7
|
+
# frozen_string_literal: true
|
|
8
|
+
|
|
9
|
+
require "test_helper"
|
|
10
|
+
|
|
11
|
+
module ActiveRecord
|
|
12
|
+
module Type
|
|
13
|
+
class BinaryTest < SpannerAdapter::TestCase
|
|
14
|
+
include SpannerAdapter::Types::TestHelper
|
|
15
|
+
|
|
16
|
+
def test_convert_to_sql_type
|
|
17
|
+
assert_equal "BYTES(MAX)", connection.type_to_sql(:binary)
|
|
18
|
+
assert_equal "BYTES(1024)", connection.type_to_sql(:binary, limit: 1024)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_set_binary_data_io_in_create
|
|
22
|
+
data = StringIO.new "hello"
|
|
23
|
+
|
|
24
|
+
record = TestTypeModel.create(data: data)
|
|
25
|
+
record.reload
|
|
26
|
+
|
|
27
|
+
assert_equal "hello", record.data.read
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def test_set_binary_data_byte_string_in_create
|
|
31
|
+
data = StringIO.new "hello1"
|
|
32
|
+
|
|
33
|
+
record = TestTypeModel.create(data: data.read)
|
|
34
|
+
record.reload
|
|
35
|
+
|
|
36
|
+
assert_equal "hello1", record.data.read
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_check_max_limit
|
|
40
|
+
str = "a" * 256
|
|
41
|
+
|
|
42
|
+
assert_raise(ActiveRecord::StatementInvalid) {
|
|
43
|
+
TestTypeModel.create(name: str)
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def test_set_binary_data_from_file
|
|
48
|
+
Tempfile.create do |f|
|
|
49
|
+
f << "hello 123"
|
|
50
|
+
|
|
51
|
+
record = TestTypeModel.create(file: f)
|
|
52
|
+
record.reload
|
|
53
|
+
|
|
54
|
+
assert_equal "hello 123", record.file.read
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|