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.
Files changed (264) hide show
  1. checksums.yaml +5 -5
  2. data/.github/CODEOWNERS +7 -0
  3. data/.github/sync-repo-settings.yaml +16 -0
  4. data/.github/workflows/acceptance-tests-on-emulator.yaml +45 -0
  5. data/.github/workflows/acceptance-tests-on-production.yaml +36 -0
  6. data/.github/workflows/ci.yaml +33 -0
  7. data/.github/workflows/nightly-acceptance-tests-on-emulator.yaml +52 -0
  8. data/.github/workflows/nightly-acceptance-tests-on-production.yaml +35 -0
  9. data/.github/workflows/nightly-unit-tests.yaml +40 -0
  10. data/.github/workflows/release-please-label.yml +25 -0
  11. data/.github/workflows/release-please.yml +39 -0
  12. data/.github/workflows/rubocop.yaml +31 -0
  13. data/.gitignore +67 -5
  14. data/.kokoro/populate-secrets.sh +77 -0
  15. data/.kokoro/release.cfg +33 -0
  16. data/.kokoro/release.sh +15 -0
  17. data/.kokoro/trampoline_v2.sh +489 -0
  18. data/.rubocop.yml +46 -0
  19. data/.toys/release.rb +18 -0
  20. data/.trampolinerc +48 -0
  21. data/.yardopts +11 -0
  22. data/CHANGELOG.md +26 -0
  23. data/CODE_OF_CONDUCT.md +40 -0
  24. data/CONTRIBUTING.md +79 -0
  25. data/Gemfile +9 -4
  26. data/LICENSE +6 -6
  27. data/README.md +67 -30
  28. data/Rakefile +79 -3
  29. data/SECURITY.md +7 -0
  30. data/acceptance/cases/associations/has_many_associations_test.rb +119 -0
  31. data/acceptance/cases/associations/has_many_through_associations_test.rb +63 -0
  32. data/acceptance/cases/associations/has_one_associations_test.rb +79 -0
  33. data/acceptance/cases/associations/has_one_through_associations_test.rb +98 -0
  34. data/acceptance/cases/interleaved_associations/has_many_associations_using_interleaved_test.rb +211 -0
  35. data/acceptance/cases/migration/change_schema_test.rb +433 -0
  36. data/acceptance/cases/migration/change_table_test.rb +115 -0
  37. data/acceptance/cases/migration/column_attributes_test.rb +122 -0
  38. data/acceptance/cases/migration/column_positioning_test.rb +48 -0
  39. data/acceptance/cases/migration/columns_test.rb +201 -0
  40. data/acceptance/cases/migration/command_recorder_test.rb +406 -0
  41. data/acceptance/cases/migration/create_join_table_test.rb +216 -0
  42. data/acceptance/cases/migration/ddl_batching_test.rb +80 -0
  43. data/acceptance/cases/migration/foreign_key_test.rb +297 -0
  44. data/acceptance/cases/migration/index_test.rb +211 -0
  45. data/acceptance/cases/migration/references_foreign_key_test.rb +259 -0
  46. data/acceptance/cases/migration/references_index_test.rb +135 -0
  47. data/acceptance/cases/migration/references_statements_test.rb +166 -0
  48. data/acceptance/cases/migration/rename_column_test.rb +96 -0
  49. data/acceptance/cases/models/calculation_query_test.rb +128 -0
  50. data/acceptance/cases/models/generated_column_test.rb +126 -0
  51. data/acceptance/cases/models/mutation_test.rb +122 -0
  52. data/acceptance/cases/models/query_test.rb +147 -0
  53. data/acceptance/cases/sessions/session_not_found_test.rb +121 -0
  54. data/acceptance/cases/transactions/optimistic_locking_test.rb +141 -0
  55. data/acceptance/cases/transactions/read_only_transactions_test.rb +67 -0
  56. data/acceptance/cases/transactions/read_write_transactions_test.rb +248 -0
  57. data/acceptance/cases/type/all_types_test.rb +152 -0
  58. data/acceptance/cases/type/binary_test.rb +59 -0
  59. data/acceptance/cases/type/boolean_test.rb +31 -0
  60. data/acceptance/cases/type/date_test.rb +32 -0
  61. data/acceptance/cases/type/date_time_test.rb +30 -0
  62. data/acceptance/cases/type/float_test.rb +27 -0
  63. data/acceptance/cases/type/integer_test.rb +44 -0
  64. data/acceptance/cases/type/numeric_test.rb +27 -0
  65. data/acceptance/cases/type/string_test.rb +79 -0
  66. data/acceptance/cases/type/text_test.rb +30 -0
  67. data/acceptance/cases/type/time_test.rb +87 -0
  68. data/acceptance/models/account.rb +13 -0
  69. data/acceptance/models/address.rb +9 -0
  70. data/acceptance/models/album.rb +12 -0
  71. data/acceptance/models/all_types.rb +8 -0
  72. data/acceptance/models/author.rb +11 -0
  73. data/acceptance/models/club.rb +12 -0
  74. data/acceptance/models/comment.rb +9 -0
  75. data/acceptance/models/customer.rb +9 -0
  76. data/acceptance/models/department.rb +9 -0
  77. data/acceptance/models/firm.rb +10 -0
  78. data/acceptance/models/member.rb +13 -0
  79. data/acceptance/models/member_type.rb +9 -0
  80. data/acceptance/models/membership.rb +10 -0
  81. data/acceptance/models/organization.rb +9 -0
  82. data/acceptance/models/post.rb +10 -0
  83. data/acceptance/models/singer.rb +10 -0
  84. data/acceptance/models/track.rb +20 -0
  85. data/acceptance/models/transaction.rb +9 -0
  86. data/acceptance/schema/schema.rb +143 -0
  87. data/acceptance/test_helper.rb +260 -0
  88. data/activerecord-spanner-adapter.gemspec +32 -17
  89. data/assets/solidus-db.png +0 -0
  90. data/benchmarks/README.md +17 -0
  91. data/benchmarks/Rakefile +14 -0
  92. data/benchmarks/application.rb +308 -0
  93. data/benchmarks/config/database.yml +8 -0
  94. data/benchmarks/config/environment.rb +12 -0
  95. data/benchmarks/db/migrate/01_create_tables.rb +25 -0
  96. data/benchmarks/db/schema.rb +29 -0
  97. data/benchmarks/models/album.rb +9 -0
  98. data/benchmarks/models/singer.rb +9 -0
  99. data/bin/console +6 -7
  100. data/examples/rails/README.md +262 -0
  101. data/examples/snippets/README.md +29 -0
  102. data/examples/snippets/Rakefile +57 -0
  103. data/examples/snippets/array-data-type/README.md +45 -0
  104. data/examples/snippets/array-data-type/Rakefile +13 -0
  105. data/examples/snippets/array-data-type/application.rb +45 -0
  106. data/examples/snippets/array-data-type/config/database.yml +8 -0
  107. data/examples/snippets/array-data-type/db/migrate/01_create_tables.rb +24 -0
  108. data/examples/snippets/array-data-type/db/schema.rb +26 -0
  109. data/examples/snippets/array-data-type/db/seeds.rb +5 -0
  110. data/examples/snippets/array-data-type/models/entity_with_array_types.rb +18 -0
  111. data/examples/snippets/bin/create_emulator_instance.rb +18 -0
  112. data/examples/snippets/bulk-insert/README.md +21 -0
  113. data/examples/snippets/bulk-insert/Rakefile +13 -0
  114. data/examples/snippets/bulk-insert/application.rb +64 -0
  115. data/examples/snippets/bulk-insert/config/database.yml +8 -0
  116. data/examples/snippets/bulk-insert/db/migrate/01_create_tables.rb +21 -0
  117. data/examples/snippets/bulk-insert/db/schema.rb +26 -0
  118. data/examples/snippets/bulk-insert/db/seeds.rb +5 -0
  119. data/examples/snippets/bulk-insert/models/album.rb +9 -0
  120. data/examples/snippets/bulk-insert/models/singer.rb +9 -0
  121. data/examples/snippets/commit-timestamp/README.md +18 -0
  122. data/examples/snippets/commit-timestamp/Rakefile +13 -0
  123. data/examples/snippets/commit-timestamp/application.rb +53 -0
  124. data/examples/snippets/commit-timestamp/config/database.yml +8 -0
  125. data/examples/snippets/commit-timestamp/db/migrate/01_create_tables.rb +26 -0
  126. data/examples/snippets/commit-timestamp/db/schema.rb +29 -0
  127. data/examples/snippets/commit-timestamp/db/seeds.rb +5 -0
  128. data/examples/snippets/commit-timestamp/models/album.rb +9 -0
  129. data/examples/snippets/commit-timestamp/models/singer.rb +9 -0
  130. data/examples/snippets/config/environment.rb +21 -0
  131. data/examples/snippets/create-records/README.md +12 -0
  132. data/examples/snippets/create-records/Rakefile +13 -0
  133. data/examples/snippets/create-records/application.rb +42 -0
  134. data/examples/snippets/create-records/config/database.yml +8 -0
  135. data/examples/snippets/create-records/db/migrate/01_create_tables.rb +21 -0
  136. data/examples/snippets/create-records/db/schema.rb +26 -0
  137. data/examples/snippets/create-records/db/seeds.rb +5 -0
  138. data/examples/snippets/create-records/models/album.rb +9 -0
  139. data/examples/snippets/create-records/models/singer.rb +9 -0
  140. data/examples/snippets/date-data-type/README.md +19 -0
  141. data/examples/snippets/date-data-type/Rakefile +13 -0
  142. data/examples/snippets/date-data-type/application.rb +35 -0
  143. data/examples/snippets/date-data-type/config/database.yml +8 -0
  144. data/examples/snippets/date-data-type/db/migrate/01_create_tables.rb +20 -0
  145. data/examples/snippets/date-data-type/db/schema.rb +21 -0
  146. data/examples/snippets/date-data-type/db/seeds.rb +16 -0
  147. data/examples/snippets/date-data-type/models/singer.rb +8 -0
  148. data/examples/snippets/generated-column/README.md +41 -0
  149. data/examples/snippets/generated-column/Rakefile +13 -0
  150. data/examples/snippets/generated-column/application.rb +37 -0
  151. data/examples/snippets/generated-column/config/database.yml +8 -0
  152. data/examples/snippets/generated-column/db/migrate/01_create_tables.rb +23 -0
  153. data/examples/snippets/generated-column/db/schema.rb +21 -0
  154. data/examples/snippets/generated-column/db/seeds.rb +18 -0
  155. data/examples/snippets/generated-column/models/singer.rb +8 -0
  156. data/examples/snippets/interleaved-tables/README.md +152 -0
  157. data/examples/snippets/interleaved-tables/Rakefile +13 -0
  158. data/examples/snippets/interleaved-tables/application.rb +109 -0
  159. data/examples/snippets/interleaved-tables/config/database.yml +8 -0
  160. data/examples/snippets/interleaved-tables/db/migrate/01_create_tables.rb +44 -0
  161. data/examples/snippets/interleaved-tables/db/schema.rb +32 -0
  162. data/examples/snippets/interleaved-tables/db/seeds.rb +40 -0
  163. data/examples/snippets/interleaved-tables/models/album.rb +15 -0
  164. data/examples/snippets/interleaved-tables/models/singer.rb +20 -0
  165. data/examples/snippets/interleaved-tables/models/track.rb +25 -0
  166. data/examples/snippets/migrations/README.md +43 -0
  167. data/examples/snippets/migrations/Rakefile +13 -0
  168. data/examples/snippets/migrations/application.rb +26 -0
  169. data/examples/snippets/migrations/config/database.yml +8 -0
  170. data/examples/snippets/migrations/db/migrate/01_create_tables.rb +28 -0
  171. data/examples/snippets/migrations/db/schema.rb +33 -0
  172. data/examples/snippets/migrations/db/seeds.rb +5 -0
  173. data/examples/snippets/migrations/models/album.rb +10 -0
  174. data/examples/snippets/migrations/models/singer.rb +10 -0
  175. data/examples/snippets/migrations/models/track.rb +9 -0
  176. data/examples/snippets/mutations/README.md +34 -0
  177. data/examples/snippets/mutations/Rakefile +13 -0
  178. data/examples/snippets/mutations/application.rb +47 -0
  179. data/examples/snippets/mutations/config/database.yml +8 -0
  180. data/examples/snippets/mutations/db/migrate/01_create_tables.rb +22 -0
  181. data/examples/snippets/mutations/db/schema.rb +27 -0
  182. data/examples/snippets/mutations/db/seeds.rb +25 -0
  183. data/examples/snippets/mutations/models/album.rb +9 -0
  184. data/examples/snippets/mutations/models/singer.rb +9 -0
  185. data/examples/snippets/optimistic-locking/README.md +12 -0
  186. data/examples/snippets/optimistic-locking/Rakefile +13 -0
  187. data/examples/snippets/optimistic-locking/application.rb +48 -0
  188. data/examples/snippets/optimistic-locking/config/database.yml +8 -0
  189. data/examples/snippets/optimistic-locking/db/migrate/01_create_tables.rb +26 -0
  190. data/examples/snippets/optimistic-locking/db/schema.rb +29 -0
  191. data/examples/snippets/optimistic-locking/db/seeds.rb +25 -0
  192. data/examples/snippets/optimistic-locking/models/album.rb +9 -0
  193. data/examples/snippets/optimistic-locking/models/singer.rb +9 -0
  194. data/examples/snippets/quickstart/README.md +26 -0
  195. data/examples/snippets/quickstart/Rakefile +13 -0
  196. data/examples/snippets/quickstart/application.rb +51 -0
  197. data/examples/snippets/quickstart/config/database.yml +8 -0
  198. data/examples/snippets/quickstart/db/migrate/01_create_tables.rb +21 -0
  199. data/examples/snippets/quickstart/db/schema.rb +26 -0
  200. data/examples/snippets/quickstart/db/seeds.rb +24 -0
  201. data/examples/snippets/quickstart/models/album.rb +9 -0
  202. data/examples/snippets/quickstart/models/singer.rb +9 -0
  203. data/examples/snippets/read-only-transactions/README.md +13 -0
  204. data/examples/snippets/read-only-transactions/Rakefile +13 -0
  205. data/examples/snippets/read-only-transactions/application.rb +49 -0
  206. data/examples/snippets/read-only-transactions/config/database.yml +8 -0
  207. data/examples/snippets/read-only-transactions/db/migrate/01_create_tables.rb +21 -0
  208. data/examples/snippets/read-only-transactions/db/schema.rb +26 -0
  209. data/examples/snippets/read-only-transactions/db/seeds.rb +24 -0
  210. data/examples/snippets/read-only-transactions/models/album.rb +9 -0
  211. data/examples/snippets/read-only-transactions/models/singer.rb +9 -0
  212. data/examples/snippets/read-write-transactions/README.md +12 -0
  213. data/examples/snippets/read-write-transactions/Rakefile +13 -0
  214. data/examples/snippets/read-write-transactions/application.rb +39 -0
  215. data/examples/snippets/read-write-transactions/config/database.yml +8 -0
  216. data/examples/snippets/read-write-transactions/db/migrate/01_create_tables.rb +22 -0
  217. data/examples/snippets/read-write-transactions/db/schema.rb +27 -0
  218. data/examples/snippets/read-write-transactions/db/seeds.rb +25 -0
  219. data/examples/snippets/read-write-transactions/models/album.rb +9 -0
  220. data/examples/snippets/read-write-transactions/models/singer.rb +9 -0
  221. data/examples/snippets/timestamp-data-type/README.md +17 -0
  222. data/examples/snippets/timestamp-data-type/Rakefile +13 -0
  223. data/examples/snippets/timestamp-data-type/application.rb +42 -0
  224. data/examples/snippets/timestamp-data-type/config/database.yml +8 -0
  225. data/examples/snippets/timestamp-data-type/db/migrate/01_create_tables.rb +21 -0
  226. data/examples/snippets/timestamp-data-type/db/schema.rb +21 -0
  227. data/examples/snippets/timestamp-data-type/db/seeds.rb +6 -0
  228. data/examples/snippets/timestamp-data-type/models/meeting.rb +19 -0
  229. data/examples/solidus/README.md +172 -0
  230. data/lib/active_record/connection_adapters/spanner/database_statements.rb +224 -269
  231. data/lib/active_record/connection_adapters/spanner/quoting.rb +42 -50
  232. data/lib/active_record/connection_adapters/spanner/schema_cache.rb +43 -0
  233. data/lib/active_record/connection_adapters/spanner/schema_creation.rb +125 -9
  234. data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +122 -0
  235. data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +19 -0
  236. data/lib/active_record/connection_adapters/spanner/schema_statements.rb +553 -139
  237. data/lib/active_record/connection_adapters/spanner/type_metadata.rb +37 -0
  238. data/lib/active_record/connection_adapters/spanner_adapter.rb +182 -78
  239. data/lib/active_record/tasks/spanner_database_tasks.rb +74 -0
  240. data/lib/active_record/type/spanner/array.rb +32 -0
  241. data/lib/active_record/type/spanner/bytes.rb +26 -0
  242. data/lib/active_record/type/spanner/spanner_active_record_converter.rb +32 -0
  243. data/lib/active_record/type/spanner/time.rb +37 -0
  244. data/lib/activerecord-spanner-adapter.rb +23 -0
  245. data/lib/activerecord_spanner_adapter/base.rb +217 -0
  246. data/lib/activerecord_spanner_adapter/connection.rb +324 -0
  247. data/lib/activerecord_spanner_adapter/errors.rb +13 -0
  248. data/lib/activerecord_spanner_adapter/foreign_key.rb +29 -0
  249. data/lib/activerecord_spanner_adapter/index/column.rb +38 -0
  250. data/lib/activerecord_spanner_adapter/index.rb +80 -0
  251. data/lib/activerecord_spanner_adapter/information_schema.rb +261 -0
  252. data/lib/activerecord_spanner_adapter/primary_key.rb +31 -0
  253. data/lib/activerecord_spanner_adapter/table/column.rb +59 -0
  254. data/lib/activerecord_spanner_adapter/table.rb +61 -0
  255. data/lib/activerecord_spanner_adapter/transaction.rb +113 -0
  256. data/lib/activerecord_spanner_adapter/version.rb +9 -0
  257. data/lib/arel/visitors/spanner.rb +35 -0
  258. data/lib/spanner_client_ext.rb +82 -0
  259. data/renovate.json +5 -0
  260. metadata +387 -34
  261. data/.travis.yml +0 -5
  262. data/lib/active_record/connection_adapters/spanner/client.rb +0 -190
  263. data/lib/active_record/connection_adapters/spanner.rb +0 -10
  264. 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