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,48 @@
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 ColumnPositioningTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ def setup
17
+ skip_test_table_create!
18
+ super
19
+
20
+ connection.ddl_batch do
21
+ connection.create_table :testing_columns_position, id: false, force: true do |t|
22
+ t.column :first, :integer
23
+ t.column :second, :integer
24
+ t.column :third, :integer
25
+ end
26
+ end
27
+ end
28
+
29
+ def teardown
30
+ connection.ddl_batch do
31
+ connection.drop_table :testing_columns_position
32
+ end rescue nil
33
+ ActiveRecord::Base.primary_key_prefix_type = nil
34
+ end
35
+
36
+ def test_column_positioning
37
+ assert_equal %w(first second third), connection.columns(:testing_columns_position).map(&:name)
38
+ end
39
+
40
+ def test_add_column_with_positioning
41
+ connection.ddl_batch do
42
+ connection.add_column :testing_columns_position, :fourth, :integer
43
+ end
44
+ assert_equal %w(first second third fourth), connection.columns(:testing_columns_position).map(&:name)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,201 @@
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 ColumnsTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ def test_rename_column
17
+ connection.ddl_batch do
18
+ add_column "test_models", :hat_name, :string
19
+ end
20
+ assert_column TestModel, :hat_name
21
+
22
+ rename_column "test_models", :hat_name, :cap_name
23
+ assert_column TestModel, :cap_name
24
+ end
25
+
26
+ def test_change_column_default_value
27
+ error = assert_raise ActiveRecordSpannerAdapter::NotSupportedError do
28
+ change_column_default "test_models", :hat_name, "hat"
29
+ end
30
+
31
+ assert_equal "change column with default value not supported.", error.message
32
+ end
33
+
34
+ def test_remove_column_with_index
35
+ connection.ddl_batch do
36
+ add_column "test_models", :hat_name, :string
37
+ add_index :test_models, :hat_name
38
+ end
39
+
40
+ assert_equal 1, connection.indexes("test_models").size
41
+ connection.ddl_batch do
42
+ remove_column "test_models", "hat_name"
43
+ end
44
+ assert_equal 0, connection.indexes("test_models").size
45
+ end
46
+
47
+ def test_remove_column_with_multi_column_index
48
+ connection.ddl_batch do
49
+ add_column "test_models", :hat_size, :integer
50
+ add_column "test_models", :hat_style, :string, limit: 100
51
+ add_index "test_models", ["hat_style", "hat_size"], unique: true
52
+ end
53
+
54
+ assert_equal 1, connection.indexes("test_models").size
55
+ connection.ddl_batch do
56
+ remove_column "test_models", "hat_size"
57
+ end
58
+
59
+ assert_equal [], connection.indexes("test_models").map(&:name)
60
+ end
61
+
62
+ def test_change_type_of_not_null_column
63
+ connection.ddl_batch do
64
+ change_column "test_models", "updated_at", :datetime, null: false
65
+ change_column "test_models", "updated_at", :datetime, null: false
66
+ end
67
+
68
+ TestModel.reset_column_information
69
+ assert_equal false, TestModel.columns_hash["updated_at"].null
70
+ ensure
71
+ connection.ddl_batch do
72
+ change_column "test_models", "updated_at", :datetime, null: true
73
+ end
74
+ end
75
+
76
+ def test_change_column_nullability
77
+ connection.ddl_batch do
78
+ add_column "test_models", "funny", :boolean
79
+ end
80
+ assert TestModel.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
81
+
82
+ connection.ddl_batch do
83
+ change_column "test_models", "funny", :boolean, null: false
84
+ end
85
+
86
+ TestModel.reset_column_information
87
+ assert_not TestModel.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
88
+
89
+ connection.ddl_batch do
90
+ change_column "test_models", "funny", :boolean, null: true
91
+ end
92
+ TestModel.reset_column_information
93
+ assert TestModel.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
94
+ end
95
+
96
+ def test_change_column
97
+ # Only string and binary allows to change types
98
+ connection.ddl_batch do
99
+ add_column "test_models", "name", :string
100
+ end
101
+
102
+ old_columns = connection.columns TestModel.table_name
103
+
104
+ assert old_columns.find { |c| c.name == "name" && c.type == :string }
105
+
106
+ connection.ddl_batch do
107
+ change_column "test_models", "name", :binary
108
+ end
109
+
110
+ new_columns = connection.columns TestModel.table_name
111
+
112
+ assert_not new_columns.find { |c| c.name == "name" && c.type == :string }
113
+ assert new_columns.find { |c| c.name == "name" && c.type == :binary }
114
+ end
115
+
116
+ def test_change_column_with_custom_index_name
117
+ connection.ddl_batch do
118
+ add_column :test_models, :category, :string
119
+ add_index :test_models, :category, name: "test_models_categories_idx", order: { category: :desc}
120
+ end
121
+
122
+ assert_equal ["test_models_categories_idx"], connection.indexes("test_models").map(&:name)
123
+ connection.ddl_batch do
124
+ change_column "test_models", "category", :string, null: false
125
+ end
126
+
127
+ assert column_exists?(:test_models, :category, :string, null: false)
128
+ indexes = connection.indexes("test_models")
129
+ assert_equal ["test_models_categories_idx"], indexes.map(&:name)
130
+ assert_equal({ category: :desc }, indexes.first.orders)
131
+ end
132
+
133
+ def test_change_column_with_long_index_name
134
+ table_name_prefix = "test_models_"
135
+ long_index_name = table_name_prefix + ("x" * (connection.index_name_length - table_name_prefix.length))
136
+ connection.ddl_batch do
137
+ add_column "test_models", "category", :string
138
+ add_index :test_models, :category, name: long_index_name
139
+ end
140
+
141
+ connection.ddl_batch do
142
+ change_column "test_models", "category", :string, null: false
143
+ end
144
+
145
+ assert_equal [long_index_name], connection.indexes("test_models").map(&:name)
146
+ end
147
+
148
+ def test_remove_column_no_second_parameter_raises_exception
149
+ assert_raise(ArgumentError) { connection.remove_column("funny") }
150
+ end
151
+
152
+ def test_removing_column_preserves_custom_primary_key
153
+ connection.ddl_batch do
154
+ connection.create_table "my_table", primary_key: "my_table_id", force: true do |t|
155
+ t.integer "col_one"
156
+ t.string "col_two", limit: 128, null: false
157
+ end
158
+ end
159
+
160
+ connection.ddl_batch do
161
+ remove_column "my_table", "col_two"
162
+ end
163
+
164
+ assert_equal "my_table_id", connection.primary_key("my_table")
165
+
166
+ columns = connection.columns "my_table"
167
+ my_table_id = columns.detect { |c| c.name == "my_table_id" }
168
+ assert_equal "INT64", my_table_id.sql_type
169
+ ensure
170
+ connection.ddl_batch do
171
+ connection.drop_table :my_table rescue nil
172
+ end
173
+ end
174
+
175
+ def test_column_with_index
176
+ connection.ddl_batch do
177
+ connection.create_table "my_table", force: true do |t|
178
+ t.string :item_number, index: true
179
+ end
180
+ end
181
+
182
+ assert connection.index_exists?("my_table", :item_number, name: :index_my_table_on_item_number)
183
+ ensure
184
+ connection.ddl_batch do
185
+ connection.drop_table :my_table rescue nil
186
+ end
187
+ end
188
+
189
+ def test_add_column_without_column_name
190
+ e = assert_raise ArgumentError do
191
+ connection.create_table "my_table", force: true do |t|
192
+ t.timestamp
193
+ end
194
+ end
195
+ assert_equal "Missing column name(s) for timestamp", e.message
196
+ ensure
197
+ connection.drop_table :my_table, if_exists: true
198
+ end
199
+ end
200
+ end
201
+ end
@@ -0,0 +1,406 @@
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 CommandRecorderTest < SpannerAdapter::TestCase
14
+ include SpannerAdapter::Migration::TestHelper
15
+
16
+ VERSION_6_1_0 = Gem::Version.create "6.1.0"
17
+ VERSION_6_0_3 = Gem::Version.create "6.0.3"
18
+
19
+ def setup
20
+ skip_test_table_create!
21
+ super
22
+ @recorder = CommandRecorder.new(connection)
23
+ end
24
+
25
+ def test_respond_to_delegates
26
+ recorder = CommandRecorder.new(Class.new {
27
+ def america; end
28
+ }.new)
29
+ assert_respond_to recorder, :america
30
+ end
31
+
32
+ def test_send_calls_super
33
+ assert_raises(NoMethodError) do
34
+ @recorder.send(:non_existing_method, :horses)
35
+ end
36
+ end
37
+
38
+ def test_send_delegates_to_record
39
+ recorder = CommandRecorder.new(Class.new {
40
+ def create_table(name); end
41
+ }.new)
42
+ assert_respond_to recorder, :create_table
43
+ recorder.send(:create_table, :horses)
44
+ assert_equal [[:create_table, [:horses], nil]], recorder.commands
45
+ end
46
+
47
+ def test_unknown_commands_delegate
48
+ recorder = Class.new do
49
+ def foo(kw:)
50
+ kw
51
+ end
52
+ end
53
+ recorder = CommandRecorder.new(recorder.new)
54
+ assert_equal "bar", recorder.foo(kw: "bar")
55
+ end
56
+
57
+ def test_inverse_of_raise_exception_on_unknown_commands
58
+ assert_raises(ActiveRecord::IrreversibleMigration) do
59
+ @recorder.inverse_of :execute, ["some sql"]
60
+ end
61
+ end
62
+
63
+ def test_irreversible_commands_raise_exception
64
+ assert_raises(ActiveRecord::IrreversibleMigration) do
65
+ @recorder.revert { @recorder.execute "some sql" }
66
+ end
67
+ end
68
+
69
+ def test_record
70
+ @recorder.record :create_table, [:system_settings]
71
+ assert_equal 1, @recorder.commands.length
72
+ end
73
+
74
+ def test_inverted_commands_are_reversed
75
+ @recorder.revert do
76
+ @recorder.record :create_table, [:hello]
77
+ @recorder.record :create_table, [:world]
78
+ end
79
+ tables = @recorder.commands.map { |_cmd, args, _block| args }
80
+ assert_equal [[:world], [:hello]], tables
81
+ end
82
+
83
+ def test_revert_order
84
+ block = Proc.new { |t| t.string :name }
85
+ @recorder.instance_eval do
86
+ create_table("apples", &block)
87
+ revert do
88
+ create_table("bananas", &block)
89
+ revert do
90
+ create_table("clementines", &block)
91
+ create_table("dates")
92
+ end
93
+ create_table("elderberries")
94
+ end
95
+ revert do
96
+ create_table("figs", &block)
97
+ create_table("grapes")
98
+ end
99
+ end
100
+ assert_equal [[:create_table, ["apples"], block], [:drop_table, ["elderberries"], nil],
101
+ [:create_table, ["clementines"], block], [:create_table, ["dates"], nil],
102
+ [:drop_table, ["bananas"], block], [:drop_table, ["grapes"], nil],
103
+ [:drop_table, ["figs"], block]], @recorder.commands
104
+ end
105
+
106
+ def test_invert_change_table
107
+ @recorder.revert do
108
+ @recorder.change_table :fruits do |t|
109
+ t.string :name
110
+ t.rename :kind, :cultivar
111
+ end
112
+ end
113
+
114
+ if RUBY_VERSION >= "2.8"
115
+ assert_equal [
116
+ [:rename_column, [:fruits, :cultivar, :kind]],
117
+ [:remove_column, [:fruits, :name, :string], nil],
118
+ ], @recorder.commands
119
+ else
120
+ assert_equal [
121
+ [:rename_column, [:fruits, :cultivar, :kind]],
122
+ [:remove_column, [:fruits, :name, :string, {}], nil],
123
+ ], @recorder.commands
124
+ end
125
+
126
+ assert_raises(ActiveRecord::IrreversibleMigration) do
127
+ @recorder.revert do
128
+ @recorder.change_table :fruits do |t|
129
+ t.remove :kind
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ def test_invert_create_table
136
+ @recorder.revert do
137
+ @recorder.record :create_table, [:system_settings]
138
+ end
139
+ drop_table = @recorder.commands.first
140
+ assert_equal [:drop_table, [:system_settings], nil], drop_table
141
+ end
142
+
143
+ def test_invert_create_table_with_options_and_block
144
+ block = Proc.new { }
145
+ drop_table = @recorder.inverse_of :create_table, [:people_reminders, id: false], &block
146
+ assert_equal [:drop_table, [:people_reminders, id: false], block], drop_table
147
+ end
148
+
149
+ def test_invert_drop_table
150
+ block = Proc.new { }
151
+ create_table = @recorder.inverse_of :drop_table, [:people_reminders, id: false], &block
152
+ assert_equal [:create_table, [:people_reminders, id: false], block], create_table
153
+ end
154
+
155
+ def test_invert_drop_table_without_a_block_nor_option
156
+ assert_raises(ActiveRecord::IrreversibleMigration) do
157
+ @recorder.inverse_of :drop_table, [:people_reminders]
158
+ end
159
+ end
160
+
161
+ def test_invert_create_join_table
162
+ drop_join_table = @recorder.inverse_of :create_join_table, [:musics, :artists]
163
+ assert_equal [:drop_join_table, [:musics, :artists], nil], drop_join_table
164
+ end
165
+
166
+ def test_invert_create_join_table_with_table_name
167
+ drop_join_table = @recorder.inverse_of :create_join_table, [:musics, :artists, table_name: :catalog]
168
+ assert_equal [:drop_join_table, [:musics, :artists, table_name: :catalog], nil], drop_join_table
169
+ end
170
+
171
+ def test_invert_drop_join_table
172
+ block = Proc.new { }
173
+ create_join_table = @recorder.inverse_of :drop_join_table, [:musics, :artists, table_name: :catalog], &block
174
+ assert_equal [:create_join_table, [:musics, :artists, table_name: :catalog], block], create_join_table
175
+ end
176
+
177
+ def test_invert_rename_table
178
+ rename = @recorder.inverse_of :rename_table, [:old, :new]
179
+ assert_equal [:rename_table, [:new, :old]], rename
180
+ end
181
+
182
+ def test_invert_add_column
183
+ remove = @recorder.inverse_of :add_column, [:table, :column, :type, {}]
184
+ assert_equal [:remove_column, [:table, :column, :type, {}], nil], remove
185
+ end
186
+
187
+ def test_invert_change_column
188
+ assert_raises(ActiveRecord::IrreversibleMigration) do
189
+ @recorder.inverse_of :change_column, [:table, :column, :type, {}]
190
+ end
191
+ end
192
+
193
+ def test_invert_remove_column
194
+ add = @recorder.inverse_of :remove_column, [:table, :column, :type, {}]
195
+ assert_equal [:add_column, [:table, :column, :type, {}], nil], add
196
+ end
197
+
198
+ def test_invert_remove_column_without_type
199
+ assert_raises(ActiveRecord::IrreversibleMigration) do
200
+ @recorder.inverse_of :remove_column, [:table, :column]
201
+ end
202
+ end
203
+
204
+ def test_invert_add_index
205
+ remove = @recorder.inverse_of :add_index, [:table, [:one, :two]]
206
+ if ActiveRecord::gem_version < Gem::Version.create('6.1.0')
207
+ assert_equal [:remove_index, [:table, { column: [:one, :two] }]], remove
208
+ else
209
+ assert_equal [:remove_index, [:table, [:one, :two]], nil], remove
210
+ end
211
+ end
212
+
213
+ def test_invert_add_index_with_name
214
+ remove = @recorder.inverse_of :add_index, [:table, [:one, :two], name: "new_index"]
215
+ if ActiveRecord::gem_version < Gem::Version.create('6.1.0')
216
+ assert_equal [:remove_index, [:table, { name: "new_index" }]], remove
217
+ else
218
+ assert_equal [:remove_index, [:table, [:one, :two], {:name=>"new_index"}], nil], remove
219
+ end
220
+ end
221
+
222
+ def test_invert_remove_index
223
+ add = @recorder.inverse_of :remove_index, [:table, :one]
224
+ assert_equal [:add_index, [:table, :one]], add
225
+ end
226
+
227
+ def test_invert_remove_index_with_options
228
+ add = @recorder.inverse_of :remove_index, [:table, { column: :one }]
229
+ if ActiveRecord::gem_version < Gem::Version.create('6.1.0')
230
+ assert_equal [:add_index, [:table, :one, {} ]], add
231
+ else
232
+ assert_equal [:add_index, [:table, :one ]], add
233
+ end
234
+ end
235
+
236
+ def test_invert_remove_index_with_positional_column
237
+ add = @recorder.inverse_of :remove_index, [:table, { column: [:one, :two], options: true }]
238
+ assert_equal [:add_index, [:table, [:one, :two], options: true]], add
239
+ end
240
+
241
+ def test_invert_remove_index_with_column
242
+ add = @recorder.inverse_of :remove_index, [:table, { column: [:one, :two], options: true }]
243
+ assert_equal [:add_index, [:table, [:one, :two], options: true]], add
244
+ end
245
+
246
+ def test_invert_remove_index_with_name
247
+ add = @recorder.inverse_of :remove_index, [:table, { column: [:one, :two], name: "new_index" }]
248
+ assert_equal [:add_index, [:table, [:one, :two], name: "new_index"]], add
249
+ end
250
+
251
+ def test_invert_remove_index_with_no_special_options
252
+ add = @recorder.inverse_of :remove_index, [:table, { column: [:one, :two] }]
253
+ if ActiveRecord::gem_version < Gem::Version.create('6.1.0')
254
+ assert_equal [:add_index, [:table, [:one, :two], {}]], add
255
+ else
256
+ assert_equal [:add_index, [:table, [:one, :two]]], add
257
+ end
258
+ end
259
+
260
+ def test_invert_remove_index_with_no_column
261
+ assert_raises(ActiveRecord::IrreversibleMigration) do
262
+ @recorder.inverse_of :remove_index, [:table, name: "new_index"]
263
+ end
264
+ end
265
+
266
+ def test_invert_rename_index
267
+ rename = @recorder.inverse_of :rename_index, [:table, :old, :new]
268
+ assert_equal [:rename_index, [:table, :new, :old]], rename
269
+ end
270
+
271
+ def test_invert_add_timestamps
272
+ remove = @recorder.inverse_of :add_timestamps, [:table]
273
+ assert_equal [:remove_timestamps, [:table], nil], remove
274
+ end
275
+
276
+ def test_invert_remove_timestamps
277
+ add = @recorder.inverse_of :remove_timestamps, [:table, { null: true }]
278
+ assert_equal [:add_timestamps, [:table, { null: true }], nil], add
279
+ end
280
+
281
+ def test_invert_add_reference
282
+ remove = @recorder.inverse_of :add_reference, [:table, :taggable, { polymorphic: true }]
283
+ assert_equal [:remove_reference, [:table, :taggable, { polymorphic: true }], nil], remove
284
+ end
285
+
286
+ def test_invert_add_belongs_to_alias
287
+ remove = @recorder.inverse_of :add_belongs_to, [:table, :user]
288
+ assert_equal [:remove_reference, [:table, :user], nil], remove
289
+ end
290
+
291
+ def test_invert_remove_reference
292
+ add = @recorder.inverse_of :remove_reference, [:table, :taggable, { polymorphic: true }]
293
+ assert_equal [:add_reference, [:table, :taggable, { polymorphic: true }], nil], add
294
+ end
295
+
296
+ def test_invert_remove_reference_with_index_and_foreign_key
297
+ add = @recorder.inverse_of :remove_reference, [:table, :taggable, { index: true, foreign_key: true }]
298
+ assert_equal [:add_reference, [:table, :taggable, { index: true, foreign_key: true }], nil], add
299
+ end
300
+
301
+ def test_invert_remove_belongs_to_alias
302
+ add = @recorder.inverse_of :remove_belongs_to, [:table, :user]
303
+ assert_equal [:add_reference, [:table, :user], nil], add
304
+ end
305
+
306
+ def test_invert_enable_extension
307
+ disable = @recorder.inverse_of :enable_extension, ["uuid-ossp"]
308
+ assert_equal [:disable_extension, ["uuid-ossp"], nil], disable
309
+ end
310
+
311
+ def test_invert_disable_extension
312
+ enable = @recorder.inverse_of :disable_extension, ["uuid-ossp"]
313
+ assert_equal [:enable_extension, ["uuid-ossp"], nil], enable
314
+ end
315
+
316
+ def test_invert_add_foreign_key
317
+ enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people]
318
+ if ActiveRecord.gem_version < VERSION_6_0_3
319
+ assert_equal [:remove_foreign_key, [:dogs, :people]], enable
320
+ else
321
+ assert_equal [:remove_foreign_key, [:dogs, :people], nil], enable
322
+ end
323
+ end
324
+
325
+ def test_invert_remove_foreign_key
326
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people]
327
+ assert_equal [:add_foreign_key, [:dogs, :people]], enable
328
+ end
329
+
330
+ def test_invert_add_foreign_key_with_column
331
+ enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people, column: "owner_id"]
332
+ if ActiveRecord.gem_version < VERSION_6_0_3
333
+ assert_equal [:remove_foreign_key, [:dogs, column: "owner_id"]], enable
334
+ else
335
+ assert_equal [:remove_foreign_key, [:dogs, :people, column: "owner_id"], nil], enable
336
+ end
337
+ end
338
+
339
+ def test_invert_remove_foreign_key_with_column
340
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, column: "owner_id"]
341
+ assert_equal [:add_foreign_key, [:dogs, :people, column: "owner_id"]], enable
342
+ end
343
+
344
+ def test_invert_add_foreign_key_with_column_and_name
345
+ enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"]
346
+ if ActiveRecord.gem_version < VERSION_6_0_3
347
+ assert_equal [:remove_foreign_key, [:dogs, { name: "fk" }]], enable
348
+ else
349
+ assert_equal [:remove_foreign_key, [:dogs, :people, { column: "owner_id", name: "fk" }], nil], enable
350
+ end
351
+ end
352
+
353
+ def test_invert_remove_foreign_key_with_column_and_name
354
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"]
355
+ assert_equal [:add_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"]], enable
356
+ end
357
+
358
+ def test_invert_remove_foreign_key_with_primary_key
359
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, primary_key: "person_id"]
360
+ assert_equal [:add_foreign_key, [:dogs, :people, primary_key: "person_id"]], enable
361
+ end
362
+
363
+ def test_invert_remove_foreign_key_with_primary_key_and_to_table_in_options
364
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, to_table: :people, primary_key: "uuid"]
365
+ assert_equal [:add_foreign_key, [:dogs, :people, primary_key: "uuid"]], enable
366
+ end
367
+
368
+ def test_invert_remove_foreign_key_with_on_delete_on_update
369
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, on_delete: :nullify, on_update: :cascade]
370
+ assert_equal [:add_foreign_key, [:dogs, :people, on_delete: :nullify, on_update: :cascade]], enable
371
+ end
372
+
373
+ def test_invert_remove_foreign_key_with_to_table_in_options
374
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, to_table: :people]
375
+ assert_equal [:add_foreign_key, [:dogs, :people]], enable
376
+
377
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, to_table: :people, column: :owner_id]
378
+ assert_equal [:add_foreign_key, [:dogs, :people, column: :owner_id]], enable
379
+ end
380
+
381
+ def test_invert_remove_foreign_key_is_irreversible_without_to_table
382
+ assert_raises ActiveRecord::IrreversibleMigration do
383
+ @recorder.inverse_of :remove_foreign_key, [:dogs, column: "owner_id"]
384
+ end
385
+
386
+ assert_raises ActiveRecord::IrreversibleMigration do
387
+ @recorder.inverse_of :remove_foreign_key, [:dogs, name: "fk"]
388
+ end
389
+
390
+ assert_raises ActiveRecord::IrreversibleMigration do
391
+ @recorder.inverse_of :remove_foreign_key, [:dogs]
392
+ end
393
+ end
394
+
395
+ def test_invert_transaction_with_irreversible_inside_is_irreversible
396
+ assert_raises(ActiveRecord::IrreversibleMigration) do
397
+ @recorder.revert do
398
+ @recorder.transaction do
399
+ @recorder.execute "some sql"
400
+ end
401
+ end
402
+ end
403
+ end
404
+ end
405
+ end
406
+ end