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,96 @@
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
+ class Migration
13
+ class RenameColumnsTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ class RenameColumnPost < ActiveRecord::Base
17
+ end
18
+
19
+ class RenameColumnComment < ActiveRecord::Base
20
+ end
21
+
22
+ def setup
23
+ skip_test_table_create!
24
+ super
25
+
26
+ connection.drop_table :rename_column_comments, if_exists: true
27
+ connection.drop_table :rename_column_posts, if_exists: true
28
+
29
+ connection.create_table(:rename_column_posts) do |t|
30
+ t.string :name, limit: 128
31
+ t.integer :comment_count
32
+ end
33
+
34
+ connection.create_table(:rename_column_comments) do |t|
35
+ t.string :comment
36
+ # The Spanner ActiveRecord adapter does not support creating an index on a column that also has a foreign key,
37
+ # as Cloud Spanner automatically creates a managed index for the foreign key. The index is therefore created
38
+ # separately.
39
+ t.references :rename_column_post, foreign_key: true
40
+ t.index :rename_column_post_id
41
+ end
42
+
43
+ RenameColumnPost.reset_column_information
44
+ RenameColumnComment.reset_column_information
45
+ end
46
+
47
+ def teardown
48
+ super
49
+ connection.drop_table :rename_column_comments, if_exists: true
50
+ connection.drop_table :rename_column_posts, if_exists: true
51
+ end
52
+
53
+ def test_rename_column
54
+ assert connection.column_exists?(:rename_column_posts, :name, :string, limit: 128)
55
+
56
+ RenameColumnPost.create!(name: "Post1", comment_count: 1)
57
+ RenameColumnPost.create!(name: "Post2", comment_count: 2)
58
+
59
+ connection.rename_column :rename_column_posts, :name, :title
60
+
61
+ RenameColumnPost.reset_column_information
62
+
63
+ assert connection.column_exists?(:rename_column_posts, :title, :string, limit: 128)
64
+
65
+ assert_equal ["Post1", "Post2"].sort, RenameColumnPost.pluck(:title).sort
66
+ assert_equal [1, 2], RenameColumnPost.pluck(:comment_count).sort
67
+ end
68
+
69
+ def test_rename_column_with_index_and_foreign_key
70
+ assert connection.column_exists?(:rename_column_comments, :rename_column_post_id, :integer)
71
+ assert connection.index_exists?(:rename_column_comments, :rename_column_post_id)
72
+ assert connection.foreign_key_exists?(:rename_column_comments, :rename_column_posts)
73
+
74
+ post = RenameColumnPost.create!(name: "Post1")
75
+ RenameColumnComment.create!(comment: "Comment1", rename_column_post_id: post.id)
76
+ RenameColumnComment.create!(comment: "Comment2", rename_column_post_id: post.id)
77
+
78
+ connection.rename_column :rename_column_comments, :rename_column_post_id, :post_id
79
+
80
+ RenameColumnComment.reset_column_information
81
+
82
+ assert connection.column_exists?(:rename_column_comments, :post_id, :integer)
83
+ assert connection.index_exists?(:rename_column_comments, :post_id)
84
+
85
+ foreign_keys = connection.foreign_keys("rename_column_comments")
86
+ assert_equal 1, foreign_keys.length
87
+
88
+ fk = foreign_keys.first
89
+ assert_equal "rename_column_comments", fk.from_table
90
+ assert_equal "post_id", fk.column
91
+
92
+ assert_equal [post.id, post.id].sort, RenameColumnComment.pluck(:post_id)
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,128 @@
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
+ require "models/account"
11
+ require "models/customer"
12
+ require "models/firm"
13
+
14
+ module ActiveRecord
15
+ module Model
16
+ class CalculationQueryTest < SpannerAdapter::TestCase
17
+ include SpannerAdapter::Associations::TestHelper
18
+
19
+ attr_accessor :customer, :accounts
20
+
21
+ def setup
22
+ super
23
+
24
+ @customer = Customer.new name: "Customer - 1"
25
+ @accounts = []
26
+
27
+ [10, 21, 1007, 3].each_with_index do |credit_limit, i|
28
+ @accounts << Account.create!(
29
+ name: "Account#{i}", customer: customer, credit_limit: credit_limit
30
+ )
31
+ end
32
+ end
33
+
34
+ def teardown
35
+ super
36
+
37
+ Account.delete_all
38
+ Customer.delete_all
39
+ end
40
+
41
+ def test_pluck_single_field
42
+ expected_ids = @accounts.map(&:id).sort
43
+ ids = Account.pluck(:id).sort
44
+ assert_equal expected_ids, ids
45
+ end
46
+
47
+ def test_pluck_multi_fields
48
+ expected_values = @accounts.sort_by{|a| a.id }.map{|a| [a.id, a.name]}
49
+
50
+ values = Account.pluck(:id, :name).sort_by{|v| v.first }
51
+ assert_equal expected_values, values
52
+ end
53
+
54
+ def test_get_ids
55
+ expected_ids = @accounts.map(&:id).sort
56
+
57
+ assert_equal expected_ids, Account.ids.sort
58
+ end
59
+
60
+ def test_pick_one_field
61
+ assert_equal "Account0", Account.order(:name).pick(:name)
62
+ end
63
+
64
+ def test_pick_two_field
65
+ assert_equal ["Account0", 10], Account.order(:name).pick(:name, :credit_limit)
66
+ end
67
+
68
+ def test_count_all
69
+ assert_equal 4, Account.count(:all)
70
+ assert_equal 4, Account.count(:all)
71
+ end
72
+
73
+ def test_distinct_count
74
+ Account.create name: "Account-101", credit_limit: 3
75
+ assert_equal 4, Account.distinct.count(:credit_limit)
76
+ end
77
+
78
+ def test_group_count
79
+ Account.create name: "Account-101", credit_limit: 3
80
+
81
+ expected_values = { 3 => 2, 10 => 1, 21 => 1, 1007 => 1}
82
+ assert_equal expected_values, Account.group(:credit_limit).count
83
+ end
84
+
85
+ def test_sum_field
86
+ assert_equal 1041, Account.sum(:credit_limit)
87
+ end
88
+
89
+ def test_resolve_aliased_attributes
90
+ assert_equal 1041, Account.sum(:available_credit)
91
+ end
92
+
93
+ def test_average_field
94
+ assert_equal 260.25, Account.average(:credit_limit)
95
+ end
96
+
97
+ def test_get_maximum_of_field
98
+ assert_equal 1007, Account.maximum(:credit_limit)
99
+ end
100
+
101
+ def test_get_maximum_of_field_with_include
102
+ customer2 = Customer.create! name: "Customer - 2"
103
+ firm = Firm.create name: "Firm - 1"
104
+ Account.create!(customer: customer2, credit_limit: 103, firm: firm)
105
+ Account.create!(customer: customer2, credit_limit: 24, firm: firm)
106
+
107
+ assert_equal 103, Account.where("customer_id = ?", customer2.id).includes(:firm).maximum(:credit_limit)
108
+ end
109
+
110
+ def test_get_minimum_of_field
111
+ assert_equal 3, Account.minimum(:credit_limit)
112
+ end
113
+
114
+ def test_calculate_count
115
+ assert_equal 4, Account.calculate(:count, "*")
116
+ assert_equal 4, Account.calculate(:count, :all)
117
+ end
118
+
119
+ def test_caclulate_sum
120
+ assert_equal 1041, Account.calculate(:sum, :credit_limit)
121
+ end
122
+
123
+ def test_caclulate_average
124
+ assert_equal 260.25, Account.calculate(:average, :credit_limit)
125
+ end
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,126 @@
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/singer"
11
+
12
+ module ActiveRecord
13
+ module Model
14
+ class GeneratedColumnTest < 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 test_create_non_null
30
+ [nil, :serializable, :buffered_mutations].each do |isolation|
31
+ singer = nil
32
+ run_in_transaction isolation do
33
+ singer = Singer.create first_name: "Pete", last_name: "Allison"
34
+ end
35
+
36
+ singer.reload
37
+ assert_equal "Pete Allison", singer.full_name
38
+ end
39
+ end
40
+
41
+ def test_create_null
42
+ [nil, :serializable, :buffered_mutations].each do |isolation|
43
+ singer = nil
44
+ run_in_transaction isolation do
45
+ singer = Singer.create last_name: "Allison"
46
+ end
47
+
48
+ singer.reload
49
+ assert_equal "Allison", singer.full_name
50
+ end
51
+ end
52
+
53
+ def test_update_non_null
54
+ [nil, :serializable, :buffered_mutations].each do |isolation|
55
+ singer = Singer.create first_name: "Pete", last_name: "Allison"
56
+ singer.reload # reload to ensure the full_name attribute is populated.
57
+ run_in_transaction isolation do
58
+ singer.update first_name: "Alice"
59
+ end
60
+
61
+ singer.reload
62
+ assert_equal "Alice Allison", singer.full_name
63
+ end
64
+ end
65
+
66
+ def test_update_null
67
+ [nil, :serializable, :buffered_mutations].each do |isolation|
68
+ singer = Singer.create first_name: "Pete", last_name: "Allison"
69
+ singer.reload # reload to ensure the full_name attribute is populated.
70
+ run_in_transaction isolation do
71
+ singer.update first_name: nil
72
+ end
73
+
74
+ singer.reload
75
+ assert_equal "Allison", singer.full_name
76
+ end
77
+ end
78
+
79
+ def test_create_with_value_for_generated_column
80
+ # Note: The statement itself will not fail for an explicit transaction that uses buffered transactions.
81
+ # Instead, the commit will fail. That is tested in a separate test case.
82
+ [nil, :serializable].each do |isolation|
83
+ run_in_transaction isolation do
84
+ assert_raises ActiveRecord::StatementInvalid do
85
+ Singer.create first_name: "Pete", last_name: "Allison", full_name: "Alice Peterson"
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ def test_create_with_value_for_generated_column_buffered_mutations
92
+ # The transaction itself will raise an error, as the failure occurs during the commit.
93
+ assert_raises ActiveRecord::StatementInvalid do
94
+ Singer.transaction isolation: :buffered_mutations do
95
+ Singer.create first_name: "Pete", last_name: "Allison", full_name: "Alice Peterson"
96
+ end
97
+ end
98
+ end
99
+
100
+ def test_update_with_value_for_generated_column
101
+ # Note: The statement itself will not fail for an explicit transaction that uses buffered transactions.
102
+ # Instead, the commit will fail. That is tested in a separate test case.
103
+ [nil, :serializable].each do |isolation|
104
+ singer = Singer.create first_name: "Pete", last_name: "Allison"
105
+ singer.reload # reload to ensure the full_name attribute is populated.
106
+ run_in_transaction isolation do
107
+ assert_raises ActiveRecord::StatementInvalid do
108
+ Singer.update full_name: "Alice Peterson"
109
+ end
110
+ end
111
+ end
112
+ end
113
+
114
+ def test_update_with_value_for_generated_column_buffered_mutations
115
+ singer = Singer.create first_name: "Pete", last_name: "Allison"
116
+ singer.reload # reload to ensure the full_name attribute is populated.
117
+ # The transaction itself will raise an error, as the failure occurs during the commit.
118
+ assert_raises ActiveRecord::StatementInvalid do
119
+ Singer.transaction isolation: :buffered_mutations do
120
+ Singer.update full_name: "Alice Peterson"
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,122 @@
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
+ require "models/author"
11
+ require "models/post"
12
+ require "models/comment"
13
+ require "models/address"
14
+ require "models/organization"
15
+
16
+ module ActiveRecord
17
+ module Model
18
+ class MutationTest < SpannerAdapter::TestCase
19
+ include SpannerAdapter::Associations::TestHelper
20
+
21
+ attr_accessor :author, :post, :comment
22
+
23
+ def setup
24
+ super
25
+
26
+ @author = Author.create name: "David"
27
+ @post = Post.create title: "Title - 1", author: author
28
+ @comment = Comment.create comment: "Comment - 1", post: post
29
+ end
30
+
31
+ def teardown
32
+ super
33
+ Comment.destroy_all
34
+ Post.destroy_all
35
+ Author.destroy_all
36
+ Organization.destroy_all
37
+ end
38
+
39
+ def test_update_all
40
+ organization = Organization.create name: "Org - 1"
41
+ organization.authors << author
42
+ organization.authors << Author.new(name: "John")
43
+
44
+ organization.reload
45
+
46
+ assert_equal 2, organization.authors.where(registered_date: nil).count
47
+
48
+ date = Date.new(2020, 01, 31)
49
+ organization.authors.update_all(registered_date: date)
50
+
51
+ assert_equal 0, organization.authors.where(registered_date: nil).count
52
+ assert_equal 2, organization.authors.where(registered_date: date).count
53
+ end
54
+
55
+ def test_offset_and_limit_update
56
+ post = Post.create title: "Title - 1", author: author
57
+ 5.times.each do |i|
58
+ Comment.create comment: "Comment - #{i+1}", post: post
59
+ end
60
+
61
+ assert_equal 5, post.comments.count
62
+ assert_equal 3, post.comments.offset(1).limit(3).update_all(comment: "New Comment")
63
+
64
+ post.reload
65
+ assert_equal 3, post.comments.where(comment: "New Comment").count
66
+ end
67
+
68
+ def test_update_all_with_joins
69
+ post = Post.create title: "Title - 1", author: author
70
+ post.comments << Comment.new(comment: "Comment - 1")
71
+
72
+ posts = Post.joins(:comments).where(comments: { comment: "Comment - 1" })
73
+
74
+ assert_equal true, posts.exists?
75
+ assert_equal posts.count, posts.update_all(title: "Title - 1 Update")
76
+ end
77
+
78
+ def test_update_counters_with_joins
79
+ post = Post.create title: "Title - 1", author: author
80
+ assert_nil post.comments_count
81
+
82
+ post.comments << Comment.new(comment: "Comment - 201")
83
+ assert_equal 1, post.reload.comments_count
84
+
85
+ Post.joins(:comments).where(comments: { comment: "Comment - 201" }).update_counters(comments_count: 10)
86
+
87
+ assert_equal 11, post.reload.comments_count
88
+ end
89
+
90
+ def test_destroy_all
91
+ authors = Author.where name: "David"
92
+
93
+ assert_equal [author], authors.to_a
94
+
95
+ authors.destroy_all
96
+ assert_equal 0, Author.count
97
+ end
98
+
99
+ def test_dependent_destroy
100
+ organization = Organization.create name: "Org - 1"
101
+ organization.authors << author
102
+ organization.authors << Author.new(name: "John")
103
+
104
+ organization.reload
105
+ assert_equal 2, organization.authors.count
106
+
107
+ organization.destroy
108
+
109
+ assert_nil Organization.find_by(id: organization.id)
110
+ assert_equal 0, Author.count
111
+ end
112
+
113
+ def test_delete_all
114
+ assert_equal 1, Author.count
115
+
116
+ Author.delete_all
117
+
118
+ assert_equal 0, Author.count
119
+ end
120
+ end
121
+ end
122
+ end