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,211 @@
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
+ require "models/album"
12
+ require "models/track"
13
+
14
+ module ActiveRecord
15
+ module Associations
16
+ class HasManyUsingInterleavedTest < SpannerAdapter::TestCase
17
+ include SpannerAdapter::Associations::TestHelper
18
+
19
+ attr_accessor :singer, :album1, :album2
20
+
21
+ def setup
22
+ super
23
+
24
+ @singer = Singer.create first_name: "FirstName1", last_name: "LastName1"
25
+
26
+ @album2 = Album.create title: "Title2", singer: singer
27
+ @album1 = Album.create title: "Title1", singer: singer
28
+
29
+ @track2_1 = Track.create title: "Title2_1", album: album2, duration: 3.6
30
+ @track2_2 = Track.create title: "Title2_2", album: album2, duration: 3.3
31
+ @track1_1 = Track.create title: "Title1_1", album: album1, duration: 4.5
32
+ @track1_2 = Track.create title: "Title1_2", album: album1
33
+ end
34
+
35
+ def teardown
36
+ Album.destroy_all
37
+ Singer.destroy_all
38
+ end
39
+
40
+ def test_has_many
41
+ assert_equal 2, singer.albums.count
42
+ assert_equal singer.albums.pluck(:title).sort, %w[Title1 Title2]
43
+
44
+ assert_equal 4, singer.tracks.count
45
+ assert_equal singer.tracks.pluck(:title).sort, %w[Title1_1 Title1_2 Title2_1 Title2_2]
46
+
47
+ assert_equal 2, album1.tracks.count
48
+ assert_equal album1.tracks.pluck(:title).sort, %w[Title1_1 Title1_2]
49
+ assert_equal 2, album2.tracks.count
50
+ assert_equal album2.tracks.pluck(:title).sort, %w[Title2_1 Title2_2]
51
+ end
52
+
53
+ def test_finding_using_associated_fields
54
+ assert_equal Album.where(singerid: singer.id).to_a, singer.albums.to_a
55
+ assert_equal Track.where(singerid: singer.id).to_a, singer.tracks.to_a
56
+ end
57
+
58
+ def test_successful_build_association
59
+ album = singer.albums.build title: "New Title"
60
+ assert album.save
61
+
62
+ singer.reload
63
+ assert_equal album, singer.albums.find(album.id)
64
+ end
65
+
66
+ def test_successful_build_nested_association
67
+ track = album1.tracks.build title: "New Title", duration: 4.45
68
+ assert track.save
69
+
70
+ album1.reload
71
+ assert_equal track, album1.tracks.find(track.id)
72
+ end
73
+
74
+ def test_create_and_destroy_associated_records
75
+ singer2 = Singer.new first_name: "First", last_name: "Last"
76
+ singer2.albums.build title: "New Title 1"
77
+ singer2.albums.build title: "New Title 2"
78
+ singer2.save!
79
+
80
+ singer2.reload
81
+
82
+ assert_equal 2, singer2.albums.count
83
+ assert_equal 4, Album.count
84
+
85
+ singer2.albums.destroy_all
86
+ singer2.reload
87
+
88
+ assert_equal 0, singer2.albums.count
89
+ assert_equal 2, Album.count
90
+ end
91
+
92
+ def test_create_and_destroy_nested_associated_records
93
+ album3 = Album.new singer: singer, title: "Title 3"
94
+ album3.tracks.build title: "Title3_1", duration: 2.5, singer: singer
95
+ album3.tracks.build title: "Title3_2", singer: singer
96
+ album3.save!
97
+
98
+ album3.reload
99
+
100
+ assert_equal 2, album3.tracks.count
101
+ assert_equal 6, singer.tracks.count
102
+ assert_equal 6, Track.count
103
+
104
+ album3.tracks.destroy_all
105
+ album3.reload
106
+
107
+ assert_equal 0, album3.tracks.count
108
+ assert_equal 4, Track.count
109
+ end
110
+
111
+ def test_create_and_delete_associated_records
112
+ singer2 = Singer.new first_name: "First", last_name: "Last"
113
+ singer2.albums.build title: "Album - 11"
114
+ singer2.albums.build title: "Album - 12"
115
+ singer2.save!
116
+
117
+ singer2.reload
118
+
119
+ assert_equal 2, singer2.albums.count
120
+ assert_equal 4, Album.count
121
+
122
+ assert_equal 2, singer2.albums.delete_all
123
+ singer2.reload
124
+
125
+ assert_equal 0, singer2.albums.count
126
+ assert_equal 2, Album.count
127
+ end
128
+
129
+ def test_create_and_delete_nested_associated_records
130
+ album3 = Album.new title: "Album 3", singer: singer
131
+ album3.tracks.build title: "Track - 31", singer: singer
132
+ album3.tracks.build title: "Track - 32", singer: singer
133
+ album3.save!
134
+
135
+ album3.reload
136
+
137
+ assert_equal 2, album3.tracks.count
138
+ assert_equal 6, Track.count
139
+
140
+ assert_equal 2, album3.tracks.delete_all
141
+ album3.reload
142
+
143
+ assert_equal 0, album3.tracks.count
144
+ assert_equal 4, Track.count
145
+ end
146
+
147
+ def test_update_associated_records
148
+ count = singer.albums.update_all title: "Title - Update"
149
+ assert_equal singer.albums.count, count
150
+
151
+ singer.reload
152
+ singer.albums.each do |album|
153
+ assert_equal "Title - Update", album.title
154
+ end
155
+ end
156
+
157
+ def test_update_nested_associated_records
158
+ count = album1.tracks.update_all title: "Title - Update", duration: 6.626
159
+ assert_equal album1.tracks.count, count
160
+
161
+ album1.reload
162
+ album1.tracks.each do |track|
163
+ assert_equal "Title - Update", track.title
164
+ assert_equal 6.626, track.duration
165
+ end
166
+ end
167
+
168
+ def test_fetch_associated_record_with_order
169
+ albums = singer.albums.order title: :desc
170
+ assert_equal %w[Title2 Title1], albums.pluck(:title)
171
+
172
+ albums = singer.albums.order title: :asc
173
+ assert_equal %w[Title1 Title2], albums.pluck(:title)
174
+ end
175
+
176
+ def test_fetch_nested_associated_record_with_order
177
+ tracks = album1.tracks.order duration: :desc
178
+ assert_equal [4.5, nil], tracks.pluck(:duration)
179
+
180
+ tracks = album1.tracks.order duration: :asc
181
+ assert_equal [nil, 4.5], tracks.pluck(:duration)
182
+ end
183
+
184
+ def test_set_counter_cache
185
+ singer.tracks.create! title: "New Title 1", album: album1
186
+ singer.tracks.create! title: "New Title 2", album: album2
187
+
188
+ singer.reload
189
+ assert_equal 6, singer.tracks_count
190
+ end
191
+
192
+ def test_cascade_destroy
193
+ assert_equal 4, singer.tracks.count
194
+
195
+ assert album1.destroy
196
+
197
+ singer.reload
198
+ assert_equal 2, singer.tracks.count
199
+ end
200
+
201
+ def test_cascade_delete
202
+ assert_equal 4, singer.tracks.count
203
+
204
+ assert album1.delete
205
+
206
+ singer.reload
207
+ assert_equal 2, singer.tracks.count
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,433 @@
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 ChangeSchemaTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ attr_reader :connection, :table_name
17
+
18
+ def setup
19
+ skip_test_table_create!
20
+ super
21
+ @table_name = :testings
22
+ end
23
+
24
+ def teardown
25
+ connection.ddl_batch do
26
+ connection.drop_table :testings rescue nil
27
+ end
28
+ ActiveRecord::Base.primary_key_prefix_type = nil
29
+ ActiveRecord::Base.clear_cache!
30
+ end
31
+
32
+ def test_add_column
33
+ testing_table_with_only_foo_attribute do
34
+ assert_equal connection.columns(:testings).size, 2
35
+ connection.add_column :testings, :name, :string
36
+ assert_equal connection.columns(:testings).size, 3
37
+ end
38
+ end
39
+
40
+ def test_create_table_adds_id
41
+ connection.ddl_batch do
42
+ connection.create_table :testings do |t|
43
+ t.column :foo, :string
44
+ end
45
+ end
46
+
47
+ assert_equal %w[id foo].sort, connection.columns(:testings).map(&:name).sort
48
+ assert_equal "id", connection.primary_key(:testings)
49
+
50
+ column = connection.columns(:testings).find { |c| c.name == "id" }
51
+
52
+ assert_equal "id", column.name
53
+ assert_equal :integer, column.type
54
+ end
55
+
56
+ def test_create_table_with_custom_primary_key
57
+ connection.ddl_batch do
58
+ connection.create_table :testings, force: true, primary_key: :email do |t|
59
+ t.string :name
60
+ end
61
+ end
62
+
63
+ assert_equal "email", connection.primary_key(:testings)
64
+ end
65
+
66
+ def test_create_table_with_custom_primary_key_using_options
67
+ connection.ddl_batch do
68
+ connection.create_table :testings, force: true, id: false do |t|
69
+ t.string :phone, primary_key: true
70
+ t.string :name
71
+ end
72
+ end
73
+
74
+ assert_equal "phone", connection.primary_key(:testings)
75
+ end
76
+
77
+ def test_create_table_with_custom_primary_key_using_type
78
+ connection.ddl_batch do
79
+ connection.create_table :testings, force: true, id: false do |t|
80
+ t.primary_key :username, limit: 255
81
+ t.string :name
82
+ end
83
+ end
84
+
85
+ assert_equal "username", connection.primary_key(:testings)
86
+
87
+ column = connection.columns(:testings).find { |c| c.name == "username" }
88
+
89
+ assert_equal "username", column.name
90
+ assert_equal :integer, column.type
91
+ end
92
+
93
+ def test_create_table_with_custom_primary_key_type
94
+ connection.ddl_batch do
95
+ connection.create_table :testings, force: true, id: false do |t|
96
+ t.primary_key :user_id, :integer
97
+ t.string :name
98
+ end
99
+ end
100
+
101
+ assert_equal "user_id", connection.primary_key(:testings)
102
+
103
+ column = connection.columns(:testings).find { |c| c.name == "user_id" }
104
+
105
+ assert_equal "user_id", column.name
106
+ assert_equal :integer, column.type
107
+ end
108
+
109
+ def test_create_table_with_not_null_column
110
+ connection.ddl_batch do
111
+ connection.create_table :testings do |t|
112
+ t.column :foo, :string, null: false
113
+ end
114
+ end
115
+
116
+ assert_raises ActiveRecord::StatementInvalid do
117
+ connection.transaction {
118
+ connection.execute "insert into testings (id, foo) values (#{generate_id}, NULL)"
119
+ }
120
+ end
121
+ end
122
+
123
+ def test_create_table_with_limits
124
+ connection.ddl_batch do
125
+ connection.create_table :testings do |t|
126
+ t.column :foo, :string, limit: 255
127
+ t.column :default_int, :integer
128
+ end
129
+ end
130
+
131
+ columns = connection.columns :testings
132
+ foo = columns.detect { |c| c.name == "foo" }
133
+ assert_equal "STRING(255)", foo.sql_type
134
+ assert_equal 255, foo.limit
135
+
136
+ default_int = columns.detect { |c| c.name == "default_int" }
137
+ assert_equal "INT64", default_int.sql_type
138
+ end
139
+
140
+ def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
141
+ ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
142
+
143
+ connection.ddl_batch do
144
+ connection.create_table :testings do |t|
145
+ t.column :foo, :string
146
+ end
147
+ end
148
+
149
+ assert_equal "testing_id", connection.primary_key(:testings)
150
+
151
+ columns = connection.columns(:testings)
152
+ assert_equal %w[testing_id foo].sort, columns.map(&:name).sort
153
+ end
154
+
155
+ def test_create_table_with_primary_key_prefix_as_table_name
156
+ ActiveRecord::Base.primary_key_prefix_type = :table_name
157
+
158
+ connection.ddl_batch do
159
+ connection.create_table :testings do |t|
160
+ t.column :foo, :string
161
+ end
162
+ end
163
+
164
+ assert_equal "testingid", connection.primary_key(:testings)
165
+
166
+ columns = connection.columns(:testings)
167
+ assert_equal %w[testingid foo].sort, columns.map(&:name).sort
168
+ end
169
+
170
+ def test_create_table_raises_when_redefining_primary_key_column
171
+ error = assert_raise ArgumentError do
172
+ connection.ddl_batch do
173
+ connection.create_table :testings do |t|
174
+ t.column :id, :string
175
+ end
176
+ end
177
+ end
178
+
179
+ assert_equal "you can't redefine the primary key column 'id'. To define a custom primary key, pass { id: false } to create_table.", error.message
180
+ end
181
+
182
+ def test_create_table_raises_when_redefining_custom_primary_key_column
183
+ error = assert_raise ArgumentError do
184
+ connection.ddl_batch do
185
+ connection.create_table :testings, primary_key: :testing_id do |t|
186
+ t.column :testing_id, :string
187
+ end
188
+ end
189
+ end
190
+
191
+ assert_equal "you can't redefine the primary key column 'testing_id'. To define a custom primary key, pass { id: false } to create_table.", error.message
192
+ end
193
+
194
+ def test_create_table_raises_when_defining_existing_column
195
+ error = assert_raise ArgumentError do
196
+ connection.ddl_batch do
197
+ connection.create_table :testings do |t|
198
+ t.column :testing_column, :string
199
+ t.column :testing_column, :integer
200
+ end
201
+ end
202
+ end
203
+
204
+ assert_equal "you can't define an already defined column 'testing_column'.", error.message
205
+ end
206
+
207
+ def test_create_table_with_timestamps_should_create_datetime_columns
208
+ connection.ddl_batch do
209
+ connection.create_table table_name do |t|
210
+ t.timestamps
211
+ end
212
+ end
213
+ created_columns = connection.columns table_name
214
+
215
+ created_at_column = created_columns.detect { |c| c.name == "created_at" }
216
+ updated_at_column = created_columns.detect { |c| c.name == "updated_at" }
217
+
218
+ assert_not created_at_column.null
219
+ assert_not updated_at_column.null
220
+ end
221
+
222
+ def test_create_table_with_timestamps_should_create_datetime_columns_with_options
223
+ connection.ddl_batch do
224
+ connection.create_table table_name do |t|
225
+ t.timestamps null: true
226
+ end
227
+ end
228
+ created_columns = connection.columns table_name
229
+
230
+ created_at_column = created_columns.detect { |c| c.name == "created_at" }
231
+ updated_at_column = created_columns.detect { |c| c.name == "updated_at" }
232
+
233
+ assert created_at_column.null
234
+ assert updated_at_column.null
235
+ end
236
+
237
+ def test_create_table_without_a_block
238
+ connection.create_table table_name
239
+ end
240
+
241
+ def test_add_column_not_null_without_default
242
+ skip "Unimplemented error: Cannot add NOT NULL column to existing table"
243
+ connection.ddl_batch do
244
+ connection.create_table :testings do |t|
245
+ t.column :foo, :string
246
+ end
247
+ connection.add_column :testings, :bar, :string, null: false
248
+ end
249
+
250
+ assert_raise ActiveRecord::NotNullViolation do
251
+ connection.transaction {
252
+ connection.execute "insert into testings (id, foo, bar) values (#{generate_id}, 'hello', NULL)"
253
+ }
254
+ end
255
+ end
256
+
257
+ def test_add_column_with_timestamp_type
258
+ connection.ddl_batch do
259
+ connection.create_table :testings do |t|
260
+ t.column :foo, :timestamp
261
+ end
262
+ end
263
+
264
+ column = connection.columns(:testings).find { |c| c.name == "foo" }
265
+
266
+ assert_equal :time, column.type
267
+ assert_equal "TIMESTAMP", column.sql_type
268
+ end
269
+
270
+ def test_change_column_quotes_column_names
271
+ connection.ddl_batch do
272
+ connection.create_table :testings do |t|
273
+ t.column :select, :string
274
+ end
275
+ end
276
+ # These two changes cannot be in one batch, as the change_column operation will check whether the column
277
+ # actually exists before executing any DDL statements.
278
+ connection.ddl_batch do
279
+ connection.change_column :testings, :select, :string, limit: 10
280
+ end
281
+
282
+ connection.transaction {
283
+ connection.execute "insert into testings (#{connection.quote_column_name 'id'},"\
284
+ "#{connection.quote_column_name 'select'}) values (#{generate_id}, '7 chars')"
285
+ }
286
+ end
287
+
288
+ def test_keeping_notnull_constraints_on_change
289
+ person_klass = Class.new ActiveRecord::Base
290
+ connection.ddl_batch do
291
+ connection.create_table :testings do |t|
292
+ t.column :title, :string
293
+ end
294
+ person_klass.table_name = "testings"
295
+ person_klass.connection.add_column "testings", "wealth", :integer, null: false
296
+ end
297
+
298
+ person_klass.reset_column_information
299
+ assert_equal false, person_klass.columns_hash["wealth"].null
300
+
301
+ assert_nothing_raised {
302
+ person_klass.connection.transaction {
303
+ person_klass.connection.execute("insert into testings (id, title, wealth) values (#{generate_id}, 'tester', 100000)")
304
+ }
305
+ }
306
+
307
+ # change column, make it nullable
308
+ connection.ddl_batch do
309
+ person_klass.connection.change_column "testings", "wealth", :integer, null: true
310
+ end
311
+ person_klass.reset_column_information
312
+ assert_nil person_klass.columns_hash["wealth"].default
313
+ assert_equal true, person_klass.columns_hash["wealth"].null
314
+ end
315
+
316
+ def test_change_column_null
317
+ testing_table_with_only_foo_attribute do
318
+ notnull_migration = Class.new(ActiveRecord::Migration::Current) do
319
+ def change
320
+ change_column_null :testings, :foo, false
321
+ end
322
+ end
323
+ notnull_migration.new.suppress_messages do
324
+ notnull_migration.migrate(:up)
325
+ assert_equal false, connection.columns(:testings).find { |c| c.name == "foo" }.null
326
+ notnull_migration.migrate(:down)
327
+ assert connection.columns(:testings).find { |c| c.name == "foo" }.null
328
+ end
329
+ end
330
+ end
331
+
332
+ def test_column_exists
333
+ connection.ddl_batch do
334
+ connection.create_table :testings do |t|
335
+ t.column :foo, :string
336
+ end
337
+ end
338
+
339
+ assert connection.column_exists?(:testings, :foo)
340
+ assert_not connection.column_exists?(:testings, :bar)
341
+ end
342
+
343
+ def test_column_exists_with_type
344
+ connection.ddl_batch do
345
+ connection.create_table :testings do |t|
346
+ t.column :foo, :string
347
+ t.column :bar, :float
348
+ end
349
+ end
350
+
351
+ assert connection.column_exists?(:testings, :foo, :string)
352
+ assert_not connection.column_exists?(:testings, :foo, :integer)
353
+
354
+ assert connection.column_exists?(:testings, :bar, :float)
355
+ assert_not connection.column_exists?(:testings, :bar, :integer)
356
+ end
357
+
358
+ def test_column_exists_with_definition
359
+ connection.ddl_batch do
360
+ connection.create_table :testings do |t|
361
+ t.column :foo, :string, limit: 100
362
+ t.column :bar, :float
363
+ t.column :taggable_id, :integer, null: false
364
+ t.column :taggable_type, :string
365
+ end
366
+ end
367
+
368
+ assert connection.column_exists?(:testings, :foo, :string, limit: 100)
369
+ assert_not connection.column_exists?(:testings, :foo, :string, limit: nil)
370
+ assert connection.column_exists?(:testings, :bar, :float)
371
+ assert connection.column_exists?(:testings, :taggable_id, :integer, null: false)
372
+ assert_not connection.column_exists?(:testings, :taggable_id, :integer, null: true)
373
+ assert connection.column_exists?(:testings, :taggable_type, :string, default: nil)
374
+ end
375
+
376
+ def test_column_exists_on_table_with_no_options_parameter_supplied
377
+ connection.ddl_batch do
378
+ connection.create_table :testings do |t|
379
+ t.string :foo
380
+ end
381
+ end
382
+ connection.change_table :testings do |t|
383
+ assert t.column_exists?(:foo)
384
+ assert_not (t.column_exists?(:bar))
385
+ end
386
+ end
387
+
388
+ def test_drop_table_if_exists
389
+ connection.ddl_batch do
390
+ connection.create_table(:testings)
391
+ end
392
+ assert connection.table_exists?(:testings)
393
+ connection.ddl_batch do
394
+ connection.drop_table(:testings, if_exists: true)
395
+ end
396
+ assert_not connection.table_exists?(:testings)
397
+ end
398
+
399
+ def test_drop_table_if_exists_nothing_raised
400
+ assert_nothing_raised { connection.drop_table(:nonexistent, if_exists: true) }
401
+ end
402
+
403
+ def test_drop_and_create_table_with_indexes
404
+ connection.ddl_batch do
405
+ connection.create_table :testings, force: true do |t|
406
+ t.string :name, index: true
407
+ end
408
+ end
409
+
410
+ assert_nothing_raised {
411
+ connection.create_table :testings, force: true do |t|
412
+ t.string :name, index: true
413
+ end
414
+ }
415
+
416
+ assert_equal true, connection.table_exists?(:testings)
417
+ assert_equal true, connection.index_exists?(:testings, :name)
418
+ end
419
+
420
+ private
421
+
422
+ def testing_table_with_only_foo_attribute
423
+ connection.ddl_batch do
424
+ connection.create_table :testings do |t|
425
+ t.column :foo, :string
426
+ end
427
+ end
428
+
429
+ yield
430
+ end
431
+ end
432
+ end
433
+ end