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,216 @@
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 CreateJoinTableTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ def setup
17
+ skip_test_table_create!
18
+ super
19
+ end
20
+
21
+ def teardown
22
+ connection.ddl_batch do
23
+ %w(artists_musics musics_videos catalog).each do |table_name|
24
+ connection.drop_table table_name, if_exists: true
25
+ end
26
+ end
27
+ end
28
+
29
+ def test_create_join_table
30
+ connection.ddl_batch do
31
+ connection.create_join_table :artists, :musics
32
+ end
33
+
34
+ assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort
35
+ end
36
+
37
+ def test_create_join_table_set_not_null_by_default
38
+ connection.ddl_batch do
39
+ connection.create_join_table :artists, :musics
40
+ end
41
+
42
+ assert_equal [false, false], connection.columns(:artists_musics).map(&:null)
43
+ end
44
+
45
+ def test_create_join_table_with_strings
46
+ connection.ddl_batch do
47
+ connection.create_join_table "artists", "musics"
48
+ end
49
+
50
+ assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort
51
+ end
52
+
53
+ def test_create_join_table_with_symbol_and_string
54
+ connection.ddl_batch do
55
+ connection.create_join_table :artists, "musics"
56
+ end
57
+
58
+ assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort
59
+ end
60
+
61
+ def test_create_join_table_with_the_proper_order
62
+ connection.ddl_batch do
63
+ connection.create_join_table :videos, :musics
64
+ end
65
+
66
+ assert_equal %w(music_id video_id), connection.columns(:musics_videos).map(&:name).sort
67
+ end
68
+
69
+ def test_create_join_table_with_the_table_name
70
+ connection.ddl_batch do
71
+ connection.create_join_table :artists, :musics, table_name: :catalog
72
+ end
73
+
74
+ assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
75
+ end
76
+
77
+ def test_create_join_table_with_the_table_name_as_string
78
+ connection.ddl_batch do
79
+ connection.create_join_table :artists, :musics, table_name: "catalog"
80
+ end
81
+
82
+ assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
83
+ end
84
+
85
+ def test_create_join_table_with_column_options
86
+ connection.ddl_batch do
87
+ connection.create_join_table :artists, :musics, column_options: { null: true }
88
+ end
89
+
90
+ assert_equal [true, true], connection.columns(:artists_musics).map(&:null)
91
+ end
92
+
93
+ def test_create_join_table_without_indexes
94
+ connection.ddl_batch do
95
+ connection.create_join_table :artists, :musics
96
+ end
97
+
98
+ assert_predicate connection.indexes(:artists_musics), :blank?
99
+ end
100
+
101
+ def test_create_join_table_with_index
102
+ connection.ddl_batch do
103
+ connection.create_join_table :artists, :musics do |t|
104
+ t.index [:artist_id, :music_id]
105
+ end
106
+ end
107
+
108
+ assert_equal [%w(artist_id music_id)], connection.indexes(:artists_musics).map(&:columns)
109
+ end
110
+
111
+ def test_create_join_table_respects_reference_key_type
112
+ connection.ddl_batch do
113
+ connection.create_join_table :artists, :musics do |t|
114
+ t.references :video
115
+ end
116
+ end
117
+
118
+ artist_id, music_id, video_id = connection.columns(:artists_musics).sort_by(&:name)
119
+
120
+ assert_equal video_id.sql_type, artist_id.sql_type
121
+ assert_equal video_id.sql_type, music_id.sql_type
122
+ end
123
+
124
+ def test_drop_join_table
125
+ connection.ddl_batch do
126
+ connection.create_join_table :artists, :musics
127
+ end
128
+ connection.ddl_batch do
129
+ connection.drop_join_table :artists, :musics
130
+ end
131
+
132
+ assert_not connection.table_exists?("artists_musics")
133
+ end
134
+
135
+ def test_drop_join_table_with_strings
136
+ connection.ddl_batch do
137
+ connection.create_join_table :artists, :musics
138
+ end
139
+ connection.ddl_batch do
140
+ connection.drop_join_table "artists", "musics"
141
+ end
142
+
143
+ assert_not connection.table_exists?("artists_musics")
144
+ end
145
+
146
+ def test_drop_join_table_with_the_proper_order
147
+ connection.ddl_batch do
148
+ connection.create_join_table :videos, :musics
149
+ end
150
+ connection.ddl_batch do
151
+ connection.drop_join_table :videos, :musics
152
+ end
153
+
154
+ assert_not connection.table_exists?("musics_videos")
155
+ end
156
+
157
+ def test_drop_join_table_with_the_table_name
158
+ connection.ddl_batch do
159
+ connection.create_join_table :artists, :musics, table_name: :catalog
160
+ end
161
+ connection.ddl_batch do
162
+ connection.drop_join_table :artists, :musics, table_name: :catalog
163
+ end
164
+
165
+ assert_not connection.table_exists?("catalog")
166
+ end
167
+
168
+ def test_drop_join_table_with_the_table_name_as_string
169
+ connection.ddl_batch do
170
+ connection.create_join_table :artists, :musics, table_name: "catalog"
171
+ end
172
+ connection.ddl_batch do
173
+ connection.drop_join_table :artists, :musics, table_name: "catalog"
174
+ end
175
+
176
+ assert_not connection.table_exists?("catalog")
177
+ end
178
+
179
+ def test_drop_join_table_with_column_options
180
+ connection.ddl_batch do
181
+ connection.create_join_table :artists, :musics, column_options: { null: true }
182
+ end
183
+ connection.ddl_batch do
184
+ connection.drop_join_table :artists, :musics, column_options: { null: true }
185
+ end
186
+
187
+ assert_not connection.table_exists?("artists_musics")
188
+ end
189
+
190
+ def test_create_and_drop_join_table_with_common_prefix
191
+ with_table_cleanup do
192
+ connection.create_join_table "audio_artists", "audio_musics"
193
+ assert connection.table_exists?("audio_artists_musics")
194
+
195
+ connection.drop_join_table "audio_artists", "audio_musics"
196
+ assert_not connection.table_exists?("audio_artists_musics"), "Should have dropped join table, but didn't"
197
+ end
198
+ end
199
+
200
+ private
201
+ def with_table_cleanup
202
+ tables_before = connection.data_sources
203
+
204
+ yield
205
+ ensure
206
+ tables_after = connection.data_sources - tables_before
207
+
208
+ connection.ddl_batch do
209
+ tables_after.each do |table|
210
+ connection.drop_table table
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1,80 @@
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 DDLBatchingTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+ include ActiveSupport::Testing::Stream
16
+
17
+ class Box < ActiveRecord::Base
18
+ end
19
+
20
+ class CreateBoxMigration < ActiveRecord::Migration::Current
21
+ def change
22
+ connection.ddl_batch do
23
+ create_table("boxes") do |t|
24
+ t.string :name
25
+ end
26
+
27
+ add_column :boxes, :length, :integer
28
+ end
29
+
30
+ Box.create(name: "Box1", length: 10)
31
+ end
32
+ end
33
+
34
+ def setup
35
+ skip_test_table_create!
36
+
37
+ super
38
+ end
39
+
40
+ def teardown
41
+ super
42
+
43
+ connection.ddl_batch do
44
+ [:boxes, :ddl_batch_test].each do |name|
45
+ if connection.table_exists?(name)
46
+ connection.drop_table name
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def test_ddl_batching
53
+ information_schema = connection.send :information_schema
54
+
55
+ connection.ddl_batch do
56
+ connection.create_table("ddl_batch_test") do |t|
57
+ t.string :name
58
+ end
59
+ connection.add_column :ddl_batch_test, :created_at, :time
60
+
61
+ assert_not information_schema.table(:ddl_batch_test)
62
+ end
63
+
64
+ assert information_schema.table(:ddl_batch_test)
65
+ end
66
+
67
+ def test_ddl_batching_with_dml_statement
68
+ migration = CreateBoxMigration.new
69
+ silence_stream($stdout) { migration.migrate(:up) }
70
+
71
+ assert connection.table_exists?(:boxes)
72
+ assert connection.column_exists?(:boxes, :length, :integer)
73
+
74
+ assert_equal 1, Box.count
75
+ box = Box.first
76
+ assert_equal 10, box.length
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,297 @@
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 ForeignKeyTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+ include ActiveSupport::Testing::Stream
16
+
17
+ class Rocket < ActiveRecord::Base
18
+ end
19
+
20
+ class Astronaut < ActiveRecord::Base
21
+ end
22
+
23
+ def setup
24
+ skip_test_table_create!
25
+ super
26
+
27
+ connection.ddl_batch do
28
+ connection.create_table "rockets", force: true do |t|
29
+ t.string :name
30
+ end
31
+
32
+ connection.create_table "astronauts", force: true do |t|
33
+ t.string :name
34
+ t.references :rocket
35
+ end
36
+ end
37
+ end
38
+
39
+ def teardown
40
+ connection.ddl_batch do
41
+ connection.drop_table "astronauts", if_exists: true
42
+ connection.drop_table "rockets", if_exists: true
43
+ connection.drop_table "fk_test_has_fk", if_exists: true
44
+ connection.drop_table "fk_test_has_pk", if_exists: true
45
+ end
46
+ end
47
+
48
+ def test_foreign_keys
49
+ connection.ddl_batch do
50
+ connection.create_table :fk_test_has_pk, primary_key: "pk_id", force: :cascade do |t|
51
+ end
52
+
53
+ connection.create_table :fk_test_has_fk, force: true do |t|
54
+ t.references :fk, null: false
55
+ t.foreign_key :fk_test_has_pk, column: "fk_id", name: "fk_name", primary_key: "pk_id"
56
+ end
57
+ end
58
+
59
+ foreign_keys = connection.foreign_keys("fk_test_has_fk")
60
+ assert_equal 1, foreign_keys.size
61
+
62
+ fk = foreign_keys.first
63
+ assert_equal "fk_test_has_fk", fk.from_table
64
+ assert_equal "fk_test_has_pk", fk.to_table
65
+ assert_equal "fk_id", fk.column
66
+ assert_equal "pk_id", fk.primary_key
67
+ end
68
+
69
+ def test_add_foreign_key_inferes_column
70
+ connection.ddl_batch do
71
+ connection.add_foreign_key :astronauts, :rockets
72
+ end
73
+
74
+ foreign_keys = connection.foreign_keys("astronauts")
75
+ assert_equal 1, foreign_keys.size
76
+
77
+ fk = foreign_keys.first
78
+ assert_equal "astronauts", fk.from_table
79
+ assert_equal "rockets", fk.to_table
80
+ assert_equal "rocket_id", fk.column
81
+ assert_equal "id", fk.primary_key
82
+ end
83
+
84
+ def test_add_foreign_key_with_column
85
+ connection.ddl_batch do
86
+ connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
87
+ end
88
+
89
+ foreign_keys = connection.foreign_keys("astronauts")
90
+ assert_equal 1, foreign_keys.size
91
+
92
+ fk = foreign_keys.first
93
+ assert_equal "astronauts", fk.from_table
94
+ assert_equal "rockets", fk.to_table
95
+ assert_equal "rocket_id", fk.column
96
+ assert_equal "id", fk.primary_key
97
+ end
98
+
99
+ def test_add_foreign_key_with_non_standard_primary_key
100
+ connection.ddl_batch do
101
+ connection.create_table :space_shuttles, id: false, force: true do |t|
102
+ t.integer :pk, primary_key: true
103
+ end
104
+
105
+ connection.add_foreign_key(:astronauts, :space_shuttles,
106
+ column: "rocket_id", primary_key: "pk", name: "custom_pk")
107
+ end
108
+
109
+ foreign_keys = connection.foreign_keys("astronauts")
110
+ assert_equal 1, foreign_keys.size
111
+
112
+ fk = foreign_keys.first
113
+ assert_equal "astronauts", fk.from_table
114
+ assert_equal "space_shuttles", fk.to_table
115
+ assert_equal "pk", fk.primary_key
116
+ ensure
117
+ connection.ddl_batch do
118
+ connection.remove_foreign_key :astronauts, name: "custom_pk", to_table: "space_shuttles"
119
+ connection.drop_table :space_shuttles
120
+ end
121
+ end
122
+
123
+ def test_foreign_key_exists
124
+ connection.ddl_batch do
125
+ connection.add_foreign_key :astronauts, :rockets
126
+ end
127
+
128
+ assert connection.foreign_key_exists?(:astronauts, :rockets)
129
+ assert_not connection.foreign_key_exists?(:astronauts, :stars)
130
+ end
131
+
132
+ def test_foreign_key_exists_by_column
133
+ connection.ddl_batch do
134
+ connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
135
+ end
136
+
137
+ assert connection.foreign_key_exists?(:astronauts, column: "rocket_id")
138
+ assert_not connection.foreign_key_exists?(:astronauts, column: "star_id")
139
+ end
140
+
141
+ def test_foreign_key_exists_by_name
142
+ connection.ddl_batch do
143
+ connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk"
144
+ end
145
+
146
+ assert connection.foreign_key_exists?(:astronauts, name: "fancy_named_fk")
147
+ assert_not connection.foreign_key_exists?(:astronauts, name: "other_fancy_named_fk")
148
+ end
149
+
150
+ def test_foreign_key_exists_in_change_table
151
+ connection.change_table(:astronauts) do |t|
152
+ t.foreign_key :rockets, column: "rocket_id", name: "fancy_named_fk"
153
+
154
+ assert t.foreign_key_exists?(column: "rocket_id")
155
+ assert_not t.foreign_key_exists?(column: "star_id")
156
+ end
157
+ end
158
+
159
+ def test_remove_foreign_key_inferes_column
160
+ connection.ddl_batch do
161
+ connection.add_foreign_key :astronauts, :rockets
162
+ end
163
+
164
+ assert_equal 1, connection.foreign_keys("astronauts").size
165
+ connection.ddl_batch do
166
+ @connection.remove_foreign_key :astronauts, :rockets
167
+ end
168
+ assert_equal [], connection.foreign_keys("astronauts")
169
+ end
170
+
171
+ def test_remove_foreign_key_by_column
172
+ connection.ddl_batch do
173
+ connection.add_foreign_key :astronauts, :rockets, column: "rocket_id"
174
+ end
175
+
176
+ assert_equal 1, connection.foreign_keys("astronauts").size
177
+ connection.ddl_batch do
178
+ @connection.remove_foreign_key :astronauts, column: "rocket_id"
179
+ end
180
+ assert_equal [], connection.foreign_keys("astronauts")
181
+ end
182
+
183
+ def test_remove_foreign_key_by_symbol_column
184
+ connection.ddl_batch do
185
+ connection.add_foreign_key :astronauts, :rockets, column: :rocket_id
186
+ end
187
+
188
+ assert_equal 1, connection.foreign_keys("astronauts").size
189
+ connection.ddl_batch do
190
+ connection.remove_foreign_key :astronauts, column: :rocket_id
191
+ end
192
+ assert_equal [], connection.foreign_keys("astronauts")
193
+ end
194
+
195
+ def test_remove_foreign_key_by_name
196
+ connection.ddl_batch do
197
+ connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk"
198
+ end
199
+
200
+ assert_equal 1, connection.foreign_keys("astronauts").size
201
+ connection.ddl_batch do
202
+ connection.remove_foreign_key :astronauts, name: "fancy_named_fk"
203
+ end
204
+ assert_equal [], connection.foreign_keys("astronauts")
205
+ end
206
+
207
+ def test_remove_foreign_non_existing_foreign_key_raises
208
+ e = assert_raises ArgumentError do
209
+ connection.remove_foreign_key :astronauts, :rockets
210
+ end
211
+ assert_equal "Table 'astronauts' has no foreign key for rockets", e.message
212
+ end
213
+
214
+ def test_remove_foreign_key_by_the_select_one_on_the_same_table
215
+ connection.ddl_batch do
216
+ connection.add_foreign_key :astronauts, :rockets
217
+ connection.add_reference :astronauts, :myrocket, foreign_key: { to_table: :rockets }
218
+ end
219
+
220
+ assert_equal 2, connection.foreign_keys("astronauts").size
221
+
222
+ connection.ddl_batch do
223
+ connection.remove_foreign_key :astronauts, :rockets, column: "myrocket_id"
224
+ end
225
+
226
+ assert_equal [["astronauts", "rockets", "rocket_id"]],
227
+ connection.foreign_keys("astronauts").map { |fk| [fk.from_table, fk.to_table, fk.column] }
228
+ end
229
+
230
+ class CreateCitiesAndHousesMigration < ActiveRecord::Migration::Current
231
+ def change
232
+ connection.ddl_batch do
233
+ create_table("cities") { |t| }
234
+
235
+ create_table("houses") do |t|
236
+ t.references :city
237
+ end
238
+ add_foreign_key :houses, :cities, column: "city_id"
239
+ end
240
+
241
+ # remove and re-add to test that schema is updated and not accidentally cached
242
+ remove_foreign_key :houses, :cities
243
+ add_foreign_key :houses, :cities, column: "city_id"
244
+ end
245
+ end
246
+
247
+ def test_add_foreign_key_is_reversible
248
+ migration = CreateCitiesAndHousesMigration.new
249
+ silence_stream($stdout) { migration.migrate(:up) }
250
+ assert_equal 1, connection.foreign_keys("houses").size
251
+ ensure
252
+ silence_stream($stdout) { migration.migrate(:down) }
253
+ end
254
+
255
+ class CreateSchoolsAndClassesMigration < ActiveRecord::Migration::Current
256
+ def up
257
+ connection.ddl_batch do
258
+ create_table(:schools)
259
+
260
+ create_table(:classes) do |t|
261
+ t.references :school
262
+ end
263
+ add_foreign_key :classes, :schools
264
+ end
265
+ end
266
+
267
+ def down
268
+ connection.ddl_batch do
269
+ drop_table :classes, if_exists: true
270
+ drop_table :schools, if_exists: true
271
+ end
272
+ end
273
+ end
274
+
275
+ def test_add_foreign_key_with_prefix
276
+ ActiveRecord::Base.table_name_prefix = "p_"
277
+ migration = CreateSchoolsAndClassesMigration.new
278
+ silence_stream($stdout) { migration.migrate(:up) }
279
+ assert_equal 1, connection.foreign_keys("p_classes").size
280
+ ensure
281
+ silence_stream($stdout) { migration.migrate(:down) }
282
+ ActiveRecord::Base.table_name_prefix = nil
283
+ end
284
+
285
+ def test_add_foreign_key_with_suffix
286
+ ActiveRecord::Base.table_name_suffix = "_s"
287
+ migration = CreateSchoolsAndClassesMigration.new
288
+ silence_stream($stdout) { migration.migrate(:up) }
289
+ assert_equal 1, connection.foreign_keys("classes_s").size
290
+ ensure
291
+ silence_stream($stdout) { migration.migrate(:down) }
292
+ ActiveRecord::Base.table_name_suffix = nil
293
+ end
294
+ end
295
+ end
296
+ end
297
+