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,211 @@
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 IndexTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ attr_reader :table_name
17
+
18
+ def setup
19
+ skip_test_table_create!
20
+ super
21
+
22
+ @table_name = :testings
23
+
24
+ connection.ddl_batch do
25
+ connection.create_table table_name do |t|
26
+ t.column :foo, :string, limit: 100
27
+ t.column :bar, :string, limit: 100
28
+
29
+ t.string :first_name
30
+ t.string :last_name, limit: 100
31
+ t.string :key, limit: 100
32
+ t.boolean :administrator
33
+ end
34
+ end
35
+ end
36
+
37
+ def teardown
38
+ connection.ddl_batch do
39
+ connection.drop_table :testings
40
+ end rescue nil
41
+ ActiveRecord::Base.primary_key_prefix_type = nil
42
+ end
43
+
44
+ def test_rename_index
45
+ connection.add_index(table_name, [:foo], name: "old_idx")
46
+ connection.ddl_batch do
47
+ connection.rename_index(table_name, "old_idx", "new_idx")
48
+ end
49
+
50
+ assert_not connection.index_name_exists?(table_name, "old_idx")
51
+ assert connection.index_name_exists?(table_name, "new_idx")
52
+ end
53
+
54
+ def test_rename_index_too_long
55
+ too_long_index_name = good_index_name + "x"
56
+ connection.add_index(table_name, [:foo], name: "old_idx")
57
+ e = assert_raises(ArgumentError) {
58
+ connection.rename_index(table_name, "old_idx", too_long_index_name)
59
+ }
60
+ assert_match(/too long; the limit is #{connection.index_name_length} characters/, e.message)
61
+
62
+ assert connection.index_name_exists?(table_name, "old_idx")
63
+ end
64
+
65
+ def test_remove_nonexistent_index
66
+ assert_raise(ArgumentError) { connection.remove_index(table_name, "no_such_index") }
67
+ end
68
+
69
+ def test_add_index_works_with_long_index_names
70
+ connection.add_index(table_name, "foo", name: good_index_name)
71
+
72
+ assert connection.index_name_exists?(table_name, good_index_name)
73
+ connection.remove_index(table_name, name: good_index_name)
74
+ end
75
+
76
+ def test_add_index_does_not_accept_too_long_index_names
77
+ too_long_index_name = good_index_name + "x"
78
+
79
+ e = assert_raises(ArgumentError) {
80
+ connection.add_index(table_name, "foo", name: too_long_index_name)
81
+ }
82
+ assert_match(/too long; the limit is #{connection.index_name_length} characters/, e.message)
83
+
84
+ assert_not connection.index_name_exists?(table_name, too_long_index_name)
85
+ connection.add_index(table_name, "foo", name: good_index_name)
86
+ end
87
+
88
+ def test_internal_index_with_name_matching_database_limit
89
+ good_index_name = "x" * connection.index_name_length
90
+ connection.add_index(table_name, "foo", name: good_index_name)
91
+
92
+ assert connection.index_name_exists?(table_name, good_index_name)
93
+ connection.remove_index(table_name, name: good_index_name)
94
+ end
95
+
96
+ def test_index_symbol_names
97
+ connection.add_index table_name, :foo, name: :symbol_index_name
98
+ assert connection.index_exists?(table_name, :foo, name: :symbol_index_name)
99
+
100
+ connection.remove_index table_name, name: :symbol_index_name
101
+ assert_not connection.index_exists?(table_name, :foo, name: :symbol_index_name)
102
+ end
103
+
104
+ def test_index_exists
105
+ connection.add_index :testings, :foo
106
+
107
+ assert connection.index_exists?(:testings, :foo)
108
+ assert_not connection.index_exists?(:testings, :bar)
109
+ end
110
+
111
+ def test_index_exists_on_multiple_columns
112
+ connection.add_index :testings, [:foo, :bar]
113
+
114
+ assert connection.index_exists?(:testings, [:foo, :bar])
115
+ end
116
+
117
+ def test_index_exists_with_custom_name_checks_columns
118
+ connection.add_index :testings, [:foo, :bar], name: "my_index"
119
+ assert connection.index_exists?(:testings, [:foo, :bar], name: "my_index")
120
+ assert_not connection.index_exists?(:testings, [:foo], name: "my_index")
121
+ end
122
+
123
+ def test_valid_index_options
124
+ assert_raise ArgumentError do
125
+ connection.add_index :testings, :foo, unqiue: true
126
+ end
127
+ end
128
+
129
+ def test_unique_index_exists
130
+ connection.add_index :testings, :foo, unique: true
131
+
132
+ assert connection.index_exists?(:testings, :foo, unique: true)
133
+ end
134
+
135
+ def test_order_index_exists
136
+ connection.add_index :testings, :foo, order: { foo: :desc }
137
+
138
+ assert connection.index_exists?(:testings, :foo)
139
+
140
+ index = connection.indexes(:testings).first
141
+ assert_equal({ foo: :desc }, index.orders)
142
+ end
143
+
144
+ def test_named_index_exists
145
+ connection.add_index :testings, :foo, name: "custom_index_name"
146
+
147
+ assert connection.index_exists?(:testings, :foo)
148
+ assert connection.index_exists?(:testings, :foo, name: "custom_index_name")
149
+ assert_not connection.index_exists?(:testings, :foo, name: "other_index_name")
150
+ end
151
+
152
+ def test_remove_named_index
153
+ connection.add_index :testings, :foo, name: "index_testings_on_custom_index_name"
154
+
155
+ assert connection.index_exists?(:testings, :foo)
156
+
157
+ assert_raise(ArgumentError) { connection.remove_index(:testings, "custom_index_name") }
158
+
159
+ connection.remove_index :testings, :foo
160
+ assert_not connection.index_exists?(:testings, :foo)
161
+ end
162
+
163
+ def test_add_index
164
+ connection.add_index("testings", "last_name")
165
+ connection.remove_index("testings", "last_name")
166
+
167
+ connection.add_index("testings", ["last_name", "first_name"])
168
+ connection.remove_index("testings", column: ["last_name", "first_name"])
169
+
170
+ connection.add_index("testings", ["last_name", "first_name"])
171
+ connection.remove_index("testings", name: :index_testings_on_last_name_and_first_name)
172
+ connection.add_index("testings", ["last_name", "first_name"])
173
+ connection.remove_index("testings", "last_name_and_first_name")
174
+
175
+ connection.add_index("testings", ["last_name", "first_name"])
176
+ connection.remove_index("testings", ["last_name", "first_name"])
177
+
178
+ connection.add_index("testings", "key", unique: true)
179
+ connection.remove_index("testings", "key")
180
+
181
+ connection.add_index("testings", ["key"], name: "key_idx", unique: true)
182
+ connection.remove_index("testings", name: "key_idx")
183
+
184
+ connection.add_index("testings", %w(last_name first_name administrator), name: "named_admin")
185
+ connection.remove_index("testings", name: "named_admin")
186
+
187
+ connection.add_index("testings", ["last_name"], order: { last_name: :desc })
188
+ connection.remove_index("testings", ["last_name"])
189
+ connection.add_index("testings", ["last_name", "first_name"], order: { last_name: :desc })
190
+ connection.remove_index("testings", ["last_name", "first_name"])
191
+ connection.add_index("testings", ["last_name", "first_name"], order: { last_name: :desc, first_name: :asc })
192
+ connection.remove_index("testings", ["last_name", "first_name"])
193
+ connection.add_index("testings", ["last_name", "first_name"], order: :desc)
194
+ connection.remove_index("testings", ["last_name", "first_name"])
195
+ end
196
+
197
+ def test_add_interleaved_index
198
+ connection.add_index "albums", ["singerid", "title"], interleave_in: :singers
199
+ index = connection.indexes(:albums).first
200
+
201
+ assert index, "Could not find interleaved index"
202
+ assert_equal "singers", index.interleave_in
203
+ end
204
+
205
+ private
206
+ def good_index_name
207
+ "x" * connection.index_name_length
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,259 @@
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 ReferencesForeignKeyInCreateTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ def setup
17
+ skip_test_table_create!
18
+ super
19
+ connection.ddl_batch do
20
+ @connection.create_table(:testing_parents, force: true)
21
+ end
22
+ end
23
+
24
+ def teardown
25
+ @connection.ddl_batch do
26
+ @connection.drop_table "testings", if_exists: true
27
+ @connection.drop_table "testing_parents", if_exists: true
28
+ end
29
+ end
30
+
31
+ def test_create_foreign_key_with_table_create
32
+ connection.ddl_batch do
33
+ connection.create_table :testings do |t|
34
+ t.references :testing_parent, foreign_key: true
35
+ end
36
+ end
37
+
38
+ fk = connection.foreign_keys("testings").first
39
+ assert_equal "testings", fk.from_table
40
+ assert_equal "testing_parents", fk.to_table
41
+ end
42
+
43
+ def test_create_foreign_key_created_by_default
44
+ connection.ddl_batch do
45
+ connection.create_table :testings do |t|
46
+ t.references :testing_parent
47
+ end
48
+ end
49
+
50
+ assert_equal [], @connection.foreign_keys("testings")
51
+ end
52
+
53
+ def test_options_can_be_passed
54
+ connection.ddl_batch do
55
+ connection.change_table :testing_parents do |t|
56
+ t.references :other, index: { unique: true }
57
+ end
58
+ connection.create_table :testings do |t|
59
+ t.references :testing_parent, foreign_key: { primary_key: :other_id }
60
+ end
61
+ end
62
+
63
+ fk = @connection.foreign_keys("testings").find { |k| k.to_table == "testing_parents" }
64
+ assert_equal "other_id", fk.primary_key
65
+ end
66
+
67
+ def test_to_table_options_can_be_passed
68
+ connection.ddl_batch do
69
+ @connection.create_table :testings do |t|
70
+ t.references :parent, foreign_key: { to_table: :testing_parents }
71
+ end
72
+ end
73
+ fks = @connection.foreign_keys("testings")
74
+ assert_equal([["testings", "testing_parents", "parent_id"]],
75
+ fks.map { |fk| [fk.from_table, fk.to_table, fk.column] })
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ module ActiveRecord
82
+ class Migration
83
+ class ReferencesForeignKeyTest < SpannerAdapter::TestCase
84
+ include SpannerAdapter::Migration::TestHelper
85
+ include ActiveSupport::Testing::Stream
86
+
87
+ def setup
88
+ skip_test_table_create!
89
+ super
90
+ connection = ActiveRecord::Base.connection
91
+ connection.ddl_batch do
92
+ connection.create_table(:testing_parents, force: true)
93
+ end
94
+ end
95
+
96
+ def teardown
97
+ connection.ddl_batch do
98
+ connection.drop_table "testings", if_exists: true
99
+ connection.drop_table "testing_parents", if_exists: true
100
+ end
101
+ end
102
+
103
+ def test_create_foreign_key_with_changing_table
104
+ connection.ddl_batch do
105
+ connection.create_table :testings
106
+ connection.change_table :testings do |t|
107
+ t.references :testing_parent, foreign_key: true
108
+ end
109
+ end
110
+
111
+ fk = connection.foreign_keys("testings").first
112
+ assert_equal "testings", fk.from_table
113
+ assert_equal "testing_parents", fk.to_table
114
+ end
115
+
116
+ def test_foreign_key_not_added_by_default_on_table_change
117
+ connection.create_table :testings
118
+ connection.change_table :testings do |t|
119
+ t.references :testing_parent
120
+ end
121
+
122
+ assert_equal [], connection.foreign_keys("testings")
123
+ end
124
+
125
+ def test_foreign_key_accept_option_on_table_change
126
+ connection.ddl_batch do
127
+ connection.change_table :testing_parents do |t|
128
+ t.references :other, index: { unique: true }
129
+ end
130
+ connection.create_table :testings
131
+ end
132
+ connection.change_table :testings do |t|
133
+ t.references :testing_parent, foreign_key: { primary_key: :other_id }
134
+ end
135
+
136
+ fk = connection.foreign_keys("testings").find { |k| k.to_table == "testing_parents" }
137
+ assert_equal "other_id", fk.primary_key
138
+ end
139
+
140
+ def test_foreign_key_column_can_be_removed
141
+ connection.ddl_batch do
142
+ connection.create_table :testings do |t|
143
+ t.references :testing_parent, index: true, foreign_key: true
144
+ end
145
+ end
146
+
147
+ connection.remove_reference :testings, :testing_parent, foreign_key: true
148
+ assert_equal 0, connection.foreign_keys('testings').size
149
+ end
150
+
151
+ def test_remove_column_removes_foreign_key
152
+ connection.ddl_batch do
153
+ connection.create_table :testings do |t|
154
+ t.references :testing_parent, index: true, foreign_key: true
155
+ end
156
+ end
157
+
158
+ connection.ddl_batch do
159
+ connection.remove_column :testings, :testing_parent_id
160
+ end
161
+ assert_equal 0, connection.foreign_keys('testings').size
162
+ end
163
+
164
+ def test_foreign_key_respect_pluralize_table_names
165
+ original_pluralize_table_names = ActiveRecord::Base.pluralize_table_names
166
+ ActiveRecord::Base.pluralize_table_names = false
167
+ connection.create_table :testing
168
+ connection.ddl_batch do
169
+ connection.change_table :testing_parents do |t|
170
+ t.references :testing, foreign_key: true
171
+ end
172
+ end
173
+
174
+ fk = connection.foreign_keys("testing_parents").first
175
+ assert_equal "testing_parents", fk.from_table
176
+ assert_equal "testing", fk.to_table
177
+
178
+ assert_difference "@connection.foreign_keys('testing_parents').size", -1 do
179
+ connection.remove_reference :testing_parents, :testing, foreign_key: true
180
+ end
181
+ ensure
182
+ ActiveRecord::Base.pluralize_table_names = original_pluralize_table_names
183
+ connection.ddl_batch do
184
+ connection.drop_table "testing", if_exists: true
185
+ end
186
+ end
187
+
188
+ class CreateDogsMigration < ActiveRecord::Migration::Current
189
+ def up
190
+ connection.ddl_batch do
191
+ create_table :dog_owners
192
+
193
+ create_table :dogs do |t|
194
+ t.references :dog_owner, foreign_key: true
195
+ end
196
+ end
197
+ end
198
+
199
+ def down
200
+ connection.ddl_batch do
201
+ drop_table :dogs, if_exists: true
202
+ drop_table :dog_owners, if_exists: true
203
+ end
204
+ end
205
+ end
206
+
207
+ def test_references_foreign_key_with_prefix
208
+ ActiveRecord::Base.table_name_prefix = "p_"
209
+ migration = CreateDogsMigration.new
210
+ silence_stream($stdout) { migration.migrate(:up) }
211
+ assert_equal 1, @connection.foreign_keys("p_dogs").size
212
+ ensure
213
+ silence_stream($stdout) { migration.migrate(:down) }
214
+ ActiveRecord::Base.table_name_prefix = nil
215
+ end
216
+
217
+ def test_references_foreign_key_with_suffix
218
+ ActiveRecord::Base.table_name_suffix = "_s"
219
+ migration = CreateDogsMigration.new
220
+ silence_stream($stdout) { migration.migrate(:up) }
221
+ assert_equal 1, @connection.foreign_keys("dogs_s").size
222
+ ensure
223
+ silence_stream($stdout) { migration.migrate(:down) }
224
+ ActiveRecord::Base.table_name_suffix = nil
225
+ end
226
+
227
+ def test_multiple_foreign_keys_can_added_to_same_table
228
+ connection.ddl_batch do
229
+ connection.create_table :testings do |t|
230
+ t.references :parent1, foreign_key: { to_table: :testing_parents }
231
+ t.references :parent2, foreign_key: { to_table: :testing_parents }
232
+ end
233
+ end
234
+
235
+ fks = connection.foreign_keys("testings").sort_by(&:column)
236
+
237
+ fk_definitions = fks.map { |fk| [fk.from_table, fk.to_table, fk.column] }
238
+ assert_equal([["testings", "testing_parents", "parent1_id"],
239
+ ["testings", "testing_parents", "parent2_id"]], fk_definitions)
240
+ end
241
+
242
+ def test_multiple_foreign_keys_removed
243
+ connection.create_table :testings do |t|
244
+ t.references :parent1, foreign_key: { to_table: :testing_parents }
245
+ t.references :parent2, foreign_key: { to_table: :testing_parents }
246
+ end
247
+
248
+ connection.remove_reference :testings, :parent1, foreign_key: { to_table: :testing_parents }
249
+
250
+ assert_equal 1, connection.foreign_keys('testings').size
251
+
252
+ fks = connection.foreign_keys("testings").sort_by(&:column)
253
+
254
+ fk_definitions = fks.map { |fk| [fk.from_table, fk.to_table, fk.column] }
255
+ assert_equal([["testings", "testing_parents", "parent2_id"]], fk_definitions)
256
+ end
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,135 @@
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 ReferencesIndexTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ attr_reader :table_name
17
+
18
+ def setup
19
+ skip_test_table_create!
20
+ super
21
+
22
+ @table_name = :testings
23
+ end
24
+
25
+ def teardown
26
+ connection.ddl_batch do
27
+ connection.drop_table :testings
28
+ end rescue nil
29
+ end
30
+
31
+ def test_creates_index
32
+ connection.ddl_batch do
33
+ connection.create_table table_name do |t|
34
+ t.references :foo, index: true
35
+ end
36
+ end
37
+
38
+ assert connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo_id)
39
+ end
40
+
41
+ def test_creates_index_by_default_even_if_index_option_is_not_passed
42
+ connection.ddl_batch do
43
+ connection.create_table table_name do |t|
44
+ t.references :foo
45
+ end
46
+ end
47
+
48
+ assert connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo_id)
49
+ end
50
+
51
+ def test_does_not_create_index_explicit
52
+ connection.ddl_batch do
53
+ connection.create_table table_name do |t|
54
+ t.references :foo, index: false
55
+ end
56
+ end
57
+
58
+ assert_not connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo_id)
59
+ end
60
+
61
+ def test_creates_index_with_options
62
+ connection.ddl_batch do
63
+ connection.create_table table_name do |t|
64
+ t.references :foo, index: { name: :index_testings_on_yo_momma }
65
+ t.references :bar, index: { unique: true }
66
+ end
67
+ end
68
+
69
+ assert connection.index_exists?(table_name, :foo_id, name: :index_testings_on_yo_momma)
70
+ assert connection.index_exists?(table_name, :bar_id, name: :index_testings_on_bar_id, unique: true)
71
+ end
72
+
73
+ def test_creates_polymorphic_index
74
+ connection.ddl_batch do
75
+ connection.create_table table_name do |t|
76
+ t.references :foo, polymorphic: true, index: true
77
+ end
78
+ end
79
+
80
+ if ActiveRecord::gem_version < Gem::Version.create('6.1.0')
81
+ assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo_type_and_foo_id)
82
+ else
83
+ assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo)
84
+ end
85
+ end
86
+
87
+ def test_creates_index_for_existing_table
88
+ connection.ddl_batch do
89
+ connection.create_table table_name
90
+ connection.change_table table_name do |t|
91
+ t.references :foo, index: true
92
+ end
93
+ end
94
+
95
+ assert connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo_id)
96
+ end
97
+
98
+ def test_creates_index_for_existing_table_even_if_index_option_is_not_passed
99
+ connection.ddl_batch do
100
+ connection.create_table table_name
101
+ connection.change_table table_name do |t|
102
+ t.references :foo
103
+ end
104
+ end
105
+
106
+ assert connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo_id)
107
+ end
108
+
109
+ def test_does_not_create_index_for_existing_table_explicit
110
+ connection.ddl_batch do
111
+ connection.create_table table_name
112
+ connection.change_table table_name do |t|
113
+ t.references :foo, index: false
114
+ end
115
+ end
116
+ assert_not connection.index_exists?(table_name, :foo_id, name: :index_testings_on_foo)
117
+ end
118
+
119
+ def test_creates_polymorphic_index_for_existing_table
120
+ connection.ddl_batch do
121
+ connection.create_table table_name
122
+ connection.change_table table_name do |t|
123
+ t.references :foo, polymorphic: true, index: true
124
+ end
125
+ end
126
+
127
+ if ActiveRecord::gem_version < Gem::Version.create('6.1.0')
128
+ assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo_type_and_foo_id)
129
+ else
130
+ assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo)
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end