activerecord-spanner-adapter 0.3.0 → 1.0.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 (282) 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 +49 -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 +55 -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 +66 -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 +171 -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 +130 -0
  56. data/acceptance/cases/transactions/read_write_transactions_test.rb +248 -0
  57. data/acceptance/cases/type/all_types_test.rb +172 -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/json_test.rb +34 -0
  65. data/acceptance/cases/type/numeric_test.rb +27 -0
  66. data/acceptance/cases/type/string_test.rb +79 -0
  67. data/acceptance/cases/type/text_test.rb +30 -0
  68. data/acceptance/cases/type/time_test.rb +87 -0
  69. data/acceptance/models/account.rb +13 -0
  70. data/acceptance/models/address.rb +9 -0
  71. data/acceptance/models/album.rb +12 -0
  72. data/acceptance/models/all_types.rb +8 -0
  73. data/acceptance/models/author.rb +11 -0
  74. data/acceptance/models/club.rb +12 -0
  75. data/acceptance/models/comment.rb +9 -0
  76. data/acceptance/models/customer.rb +9 -0
  77. data/acceptance/models/department.rb +9 -0
  78. data/acceptance/models/firm.rb +10 -0
  79. data/acceptance/models/member.rb +13 -0
  80. data/acceptance/models/member_type.rb +9 -0
  81. data/acceptance/models/membership.rb +10 -0
  82. data/acceptance/models/organization.rb +9 -0
  83. data/acceptance/models/post.rb +10 -0
  84. data/acceptance/models/singer.rb +10 -0
  85. data/acceptance/models/track.rb +20 -0
  86. data/acceptance/models/transaction.rb +9 -0
  87. data/acceptance/schema/schema.rb +147 -0
  88. data/acceptance/test_helper.rb +261 -0
  89. data/activerecord-spanner-adapter.gemspec +32 -17
  90. data/assets/solidus-db.png +0 -0
  91. data/benchmarks/README.md +17 -0
  92. data/benchmarks/Rakefile +14 -0
  93. data/benchmarks/application.rb +308 -0
  94. data/benchmarks/config/database.yml +8 -0
  95. data/benchmarks/config/environment.rb +12 -0
  96. data/benchmarks/db/migrate/01_create_tables.rb +25 -0
  97. data/benchmarks/db/schema.rb +29 -0
  98. data/benchmarks/models/album.rb +9 -0
  99. data/benchmarks/models/singer.rb +9 -0
  100. data/bin/console +6 -7
  101. data/examples/rails/README.md +262 -0
  102. data/examples/snippets/README.md +29 -0
  103. data/examples/snippets/Rakefile +57 -0
  104. data/examples/snippets/array-data-type/README.md +45 -0
  105. data/examples/snippets/array-data-type/Rakefile +13 -0
  106. data/examples/snippets/array-data-type/application.rb +45 -0
  107. data/examples/snippets/array-data-type/config/database.yml +8 -0
  108. data/examples/snippets/array-data-type/db/migrate/01_create_tables.rb +24 -0
  109. data/examples/snippets/array-data-type/db/schema.rb +26 -0
  110. data/examples/snippets/array-data-type/db/seeds.rb +5 -0
  111. data/examples/snippets/array-data-type/models/entity_with_array_types.rb +18 -0
  112. data/examples/snippets/bin/create_emulator_instance.rb +18 -0
  113. data/examples/snippets/bulk-insert/README.md +21 -0
  114. data/examples/snippets/bulk-insert/Rakefile +13 -0
  115. data/examples/snippets/bulk-insert/application.rb +64 -0
  116. data/examples/snippets/bulk-insert/config/database.yml +8 -0
  117. data/examples/snippets/bulk-insert/db/migrate/01_create_tables.rb +21 -0
  118. data/examples/snippets/bulk-insert/db/schema.rb +26 -0
  119. data/examples/snippets/bulk-insert/db/seeds.rb +5 -0
  120. data/examples/snippets/bulk-insert/models/album.rb +9 -0
  121. data/examples/snippets/bulk-insert/models/singer.rb +9 -0
  122. data/examples/snippets/commit-timestamp/README.md +18 -0
  123. data/examples/snippets/commit-timestamp/Rakefile +13 -0
  124. data/examples/snippets/commit-timestamp/application.rb +53 -0
  125. data/examples/snippets/commit-timestamp/config/database.yml +8 -0
  126. data/examples/snippets/commit-timestamp/db/migrate/01_create_tables.rb +26 -0
  127. data/examples/snippets/commit-timestamp/db/schema.rb +29 -0
  128. data/examples/snippets/commit-timestamp/db/seeds.rb +5 -0
  129. data/examples/snippets/commit-timestamp/models/album.rb +9 -0
  130. data/examples/snippets/commit-timestamp/models/singer.rb +9 -0
  131. data/examples/snippets/config/environment.rb +21 -0
  132. data/examples/snippets/create-records/README.md +12 -0
  133. data/examples/snippets/create-records/Rakefile +13 -0
  134. data/examples/snippets/create-records/application.rb +42 -0
  135. data/examples/snippets/create-records/config/database.yml +8 -0
  136. data/examples/snippets/create-records/db/migrate/01_create_tables.rb +21 -0
  137. data/examples/snippets/create-records/db/schema.rb +26 -0
  138. data/examples/snippets/create-records/db/seeds.rb +5 -0
  139. data/examples/snippets/create-records/models/album.rb +9 -0
  140. data/examples/snippets/create-records/models/singer.rb +9 -0
  141. data/examples/snippets/date-data-type/README.md +19 -0
  142. data/examples/snippets/date-data-type/Rakefile +13 -0
  143. data/examples/snippets/date-data-type/application.rb +35 -0
  144. data/examples/snippets/date-data-type/config/database.yml +8 -0
  145. data/examples/snippets/date-data-type/db/migrate/01_create_tables.rb +20 -0
  146. data/examples/snippets/date-data-type/db/schema.rb +21 -0
  147. data/examples/snippets/date-data-type/db/seeds.rb +16 -0
  148. data/examples/snippets/date-data-type/models/singer.rb +8 -0
  149. data/examples/snippets/generated-column/README.md +41 -0
  150. data/examples/snippets/generated-column/Rakefile +13 -0
  151. data/examples/snippets/generated-column/application.rb +37 -0
  152. data/examples/snippets/generated-column/config/database.yml +8 -0
  153. data/examples/snippets/generated-column/db/migrate/01_create_tables.rb +23 -0
  154. data/examples/snippets/generated-column/db/schema.rb +21 -0
  155. data/examples/snippets/generated-column/db/seeds.rb +18 -0
  156. data/examples/snippets/generated-column/models/singer.rb +8 -0
  157. data/examples/snippets/hints/README.md +19 -0
  158. data/examples/snippets/hints/Rakefile +13 -0
  159. data/examples/snippets/hints/application.rb +47 -0
  160. data/examples/snippets/hints/config/database.yml +8 -0
  161. data/examples/snippets/hints/db/migrate/01_create_tables.rb +23 -0
  162. data/examples/snippets/hints/db/schema.rb +28 -0
  163. data/examples/snippets/hints/db/seeds.rb +29 -0
  164. data/examples/snippets/hints/models/album.rb +9 -0
  165. data/examples/snippets/hints/models/singer.rb +9 -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/partitioned-dml/README.md +16 -0
  195. data/examples/snippets/partitioned-dml/Rakefile +13 -0
  196. data/examples/snippets/partitioned-dml/application.rb +48 -0
  197. data/examples/snippets/partitioned-dml/config/database.yml +8 -0
  198. data/examples/snippets/partitioned-dml/db/migrate/01_create_tables.rb +21 -0
  199. data/examples/snippets/partitioned-dml/db/schema.rb +26 -0
  200. data/examples/snippets/partitioned-dml/db/seeds.rb +29 -0
  201. data/examples/snippets/partitioned-dml/models/album.rb +9 -0
  202. data/examples/snippets/partitioned-dml/models/singer.rb +9 -0
  203. data/examples/snippets/quickstart/README.md +26 -0
  204. data/examples/snippets/quickstart/Rakefile +13 -0
  205. data/examples/snippets/quickstart/application.rb +51 -0
  206. data/examples/snippets/quickstart/config/database.yml +8 -0
  207. data/examples/snippets/quickstart/db/migrate/01_create_tables.rb +21 -0
  208. data/examples/snippets/quickstart/db/schema.rb +26 -0
  209. data/examples/snippets/quickstart/db/seeds.rb +24 -0
  210. data/examples/snippets/quickstart/models/album.rb +9 -0
  211. data/examples/snippets/quickstart/models/singer.rb +9 -0
  212. data/examples/snippets/read-only-transactions/README.md +13 -0
  213. data/examples/snippets/read-only-transactions/Rakefile +13 -0
  214. data/examples/snippets/read-only-transactions/application.rb +77 -0
  215. data/examples/snippets/read-only-transactions/config/database.yml +8 -0
  216. data/examples/snippets/read-only-transactions/db/migrate/01_create_tables.rb +21 -0
  217. data/examples/snippets/read-only-transactions/db/schema.rb +26 -0
  218. data/examples/snippets/read-only-transactions/db/seeds.rb +24 -0
  219. data/examples/snippets/read-only-transactions/models/album.rb +9 -0
  220. data/examples/snippets/read-only-transactions/models/singer.rb +9 -0
  221. data/examples/snippets/read-write-transactions/README.md +12 -0
  222. data/examples/snippets/read-write-transactions/Rakefile +13 -0
  223. data/examples/snippets/read-write-transactions/application.rb +39 -0
  224. data/examples/snippets/read-write-transactions/config/database.yml +8 -0
  225. data/examples/snippets/read-write-transactions/db/migrate/01_create_tables.rb +22 -0
  226. data/examples/snippets/read-write-transactions/db/schema.rb +27 -0
  227. data/examples/snippets/read-write-transactions/db/seeds.rb +25 -0
  228. data/examples/snippets/read-write-transactions/models/album.rb +9 -0
  229. data/examples/snippets/read-write-transactions/models/singer.rb +9 -0
  230. data/examples/snippets/stale-reads/README.md +27 -0
  231. data/examples/snippets/stale-reads/Rakefile +13 -0
  232. data/examples/snippets/stale-reads/application.rb +63 -0
  233. data/examples/snippets/stale-reads/config/database.yml +8 -0
  234. data/examples/snippets/stale-reads/db/migrate/01_create_tables.rb +21 -0
  235. data/examples/snippets/stale-reads/db/schema.rb +26 -0
  236. data/examples/snippets/stale-reads/db/seeds.rb +24 -0
  237. data/examples/snippets/stale-reads/models/album.rb +9 -0
  238. data/examples/snippets/stale-reads/models/singer.rb +9 -0
  239. data/examples/snippets/timestamp-data-type/README.md +17 -0
  240. data/examples/snippets/timestamp-data-type/Rakefile +13 -0
  241. data/examples/snippets/timestamp-data-type/application.rb +42 -0
  242. data/examples/snippets/timestamp-data-type/config/database.yml +8 -0
  243. data/examples/snippets/timestamp-data-type/db/migrate/01_create_tables.rb +21 -0
  244. data/examples/snippets/timestamp-data-type/db/schema.rb +21 -0
  245. data/examples/snippets/timestamp-data-type/db/seeds.rb +6 -0
  246. data/examples/snippets/timestamp-data-type/models/meeting.rb +19 -0
  247. data/examples/solidus/README.md +172 -0
  248. data/lib/active_record/connection_adapters/spanner/database_statements.rb +244 -266
  249. data/lib/active_record/connection_adapters/spanner/quoting.rb +42 -50
  250. data/lib/active_record/connection_adapters/spanner/schema_cache.rb +43 -0
  251. data/lib/active_record/connection_adapters/spanner/schema_creation.rb +125 -9
  252. data/lib/active_record/connection_adapters/spanner/schema_definitions.rb +122 -0
  253. data/lib/active_record/connection_adapters/spanner/schema_dumper.rb +19 -0
  254. data/lib/active_record/connection_adapters/spanner/schema_statements.rb +553 -139
  255. data/lib/active_record/connection_adapters/spanner/type_metadata.rb +37 -0
  256. data/lib/active_record/connection_adapters/spanner_adapter.rb +185 -78
  257. data/lib/active_record/tasks/spanner_database_tasks.rb +74 -0
  258. data/lib/active_record/type/spanner/array.rb +32 -0
  259. data/lib/active_record/type/spanner/bytes.rb +26 -0
  260. data/lib/active_record/type/spanner/spanner_active_record_converter.rb +33 -0
  261. data/lib/active_record/type/spanner/time.rb +37 -0
  262. data/lib/activerecord-spanner-adapter.rb +23 -0
  263. data/lib/activerecord_spanner_adapter/base.rb +238 -0
  264. data/lib/activerecord_spanner_adapter/connection.rb +350 -0
  265. data/lib/activerecord_spanner_adapter/errors.rb +13 -0
  266. data/lib/activerecord_spanner_adapter/foreign_key.rb +29 -0
  267. data/lib/activerecord_spanner_adapter/index/column.rb +38 -0
  268. data/lib/activerecord_spanner_adapter/index.rb +80 -0
  269. data/lib/activerecord_spanner_adapter/information_schema.rb +262 -0
  270. data/lib/activerecord_spanner_adapter/primary_key.rb +31 -0
  271. data/lib/activerecord_spanner_adapter/table/column.rb +59 -0
  272. data/lib/activerecord_spanner_adapter/table.rb +61 -0
  273. data/lib/activerecord_spanner_adapter/transaction.rb +154 -0
  274. data/lib/activerecord_spanner_adapter/version.rb +9 -0
  275. data/lib/arel/visitors/spanner.rb +111 -0
  276. data/lib/spanner_client_ext.rb +107 -0
  277. data/renovate.json +5 -0
  278. metadata +405 -34
  279. data/.travis.yml +0 -5
  280. data/lib/active_record/connection_adapters/spanner/client.rb +0 -190
  281. data/lib/active_record/connection_adapters/spanner.rb +0 -10
  282. data/lib/activerecord-spanner-adapter/version.rb +0 -3
@@ -0,0 +1,166 @@
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 ReferencesStatementsTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ attr_reader :table_name
17
+
18
+ def setup
19
+ super
20
+ @table_name = :test_models
21
+
22
+ connection.ddl_batch do
23
+ add_column table_name, :supplier_id, :integer, index: true
24
+ end
25
+ end
26
+
27
+ def test_creates_reference_id_column
28
+ connection.ddl_batch do
29
+ add_reference table_name, :user
30
+ end
31
+ assert column_exists?(table_name, :user_id, :integer)
32
+ end
33
+
34
+ def test_does_not_create_reference_type_column
35
+ connection.ddl_batch do
36
+ add_reference table_name, :taggable
37
+ end
38
+ assert_not column_exists?(table_name, :taggable_type, :string)
39
+ end
40
+
41
+ def test_creates_reference_type_column
42
+ connection.ddl_batch do
43
+ add_reference table_name, :taggable, polymorphic: true
44
+ end
45
+ assert column_exists?(table_name, :taggable_id, :integer)
46
+ assert column_exists?(table_name, :taggable_type, :string)
47
+ end
48
+
49
+ def test_does_not_create_reference_id_index_if_index_is_false
50
+ connection.ddl_batch do
51
+ add_reference table_name, :user, index: false
52
+ end
53
+ assert_not index_exists?(table_name, :user_id)
54
+ end
55
+
56
+ def test_create_reference_id_index_even_if_index_option_is_not_passed
57
+ connection.ddl_batch do
58
+ add_reference table_name, :user
59
+ end
60
+ assert index_exists?(table_name, :user_id)
61
+ end
62
+
63
+ def test_creates_polymorphic_index
64
+ connection.ddl_batch do
65
+ add_reference table_name, :taggable, polymorphic: true, index: true
66
+ end
67
+ assert index_exists?(table_name, [:taggable_type, :taggable_id])
68
+ end
69
+
70
+ def test_creates_reference_type_column_with_not_null
71
+ connection.ddl_batch do
72
+ connection.create_table table_name, force: true do |t|
73
+ t.references :taggable, null: false, polymorphic: true
74
+ end
75
+ end
76
+ assert column_exists?(table_name, :taggable_id, :integer, null: false)
77
+ assert column_exists?(table_name, :taggable_type, :string, null: false)
78
+ end
79
+
80
+ def test_creates_named_index
81
+ connection.ddl_batch do
82
+ add_reference table_name, :tag, index: { name: "index_taggings_on_tag_id" }
83
+ end
84
+ assert index_exists?(table_name, :tag_id, name: "index_taggings_on_tag_id")
85
+ end
86
+
87
+ def test_creates_named_unique_index
88
+ connection.ddl_batch do
89
+ add_reference table_name, :tag, index: { name: "index_taggings_on_tag_id", unique: true }
90
+ end
91
+ assert index_exists?(table_name, :tag_id, name: "index_taggings_on_tag_id", unique: true)
92
+ end
93
+
94
+ def test_creates_reference_id_with_specified_type
95
+ connection.ddl_batch do
96
+ add_reference table_name, :user, type: :string
97
+ end
98
+ assert column_exists?(table_name, :user_id, :string)
99
+ end
100
+
101
+ def test_deletes_reference_id_column
102
+ connection.ddl_batch do
103
+ remove_reference table_name, :supplier
104
+ end
105
+ assert_not column_exists?(table_name, :supplier_id, :string)
106
+ end
107
+
108
+ def test_deletes_reference_id_index
109
+ connection.ddl_batch do
110
+ remove_reference table_name, :supplier
111
+ end
112
+ assert_not index_exists?(table_name, :supplier_id)
113
+ end
114
+
115
+ def test_does_not_delete_reference_type_column
116
+ with_polymorphic_column do
117
+ connection.ddl_batch do
118
+ remove_reference table_name, :supplier
119
+ end
120
+
121
+ assert_not column_exists?(table_name, :supplier_id, :string)
122
+ assert column_exists?(table_name, :supplier_type, :string)
123
+ end
124
+ end
125
+
126
+ def test_deletes_reference_type_column
127
+ with_polymorphic_column do
128
+ remove_reference table_name, :supplier, polymorphic: true
129
+ assert_not column_exists?(table_name, :supplier_type, :string)
130
+ end
131
+ end
132
+
133
+ def test_deletes_polymorphic_index
134
+ with_polymorphic_column do
135
+ remove_reference table_name, :supplier, polymorphic: true
136
+ assert_not index_exists?(table_name, [:supplier_id, :supplier_type])
137
+ end
138
+ end
139
+
140
+ def test_add_belongs_to_alias
141
+ connection.ddl_batch do
142
+ add_belongs_to table_name, :user
143
+ end
144
+ assert column_exists?(table_name, :user_id, :integer)
145
+ end
146
+
147
+ def test_remove_belongs_to_alias
148
+ connection.ddl_batch do
149
+ remove_belongs_to table_name, :supplier
150
+ end
151
+ assert_not column_exists?(table_name, :supplier_id, :integer)
152
+ end
153
+
154
+ private
155
+
156
+ def with_polymorphic_column
157
+ connection.ddl_batch do
158
+ add_column table_name, :supplier_type, :string
159
+ add_index table_name, [:supplier_id, :supplier_type]
160
+ end
161
+
162
+ yield
163
+ end
164
+ end
165
+ end
166
+ end
@@ -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