pg_trunk 0.1.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 (196) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +87 -0
  3. data/.gitignore +9 -0
  4. data/.rspec +4 -0
  5. data/.rubocop.yml +92 -0
  6. data/.yardopts +4 -0
  7. data/CHANGELOG.md +31 -0
  8. data/CONTRIBUTING.md +17 -0
  9. data/Gemfile +22 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +141 -0
  12. data/Rakefile +16 -0
  13. data/bin/console +8 -0
  14. data/bin/rake +19 -0
  15. data/bin/rspec +19 -0
  16. data/bin/setup +8 -0
  17. data/bin/yard +19 -0
  18. data/lib/pg_trunk/core/adapters/postgres.rb +80 -0
  19. data/lib/pg_trunk/core/dependencies_resolver.rb +101 -0
  20. data/lib/pg_trunk/core/generators.rb +140 -0
  21. data/lib/pg_trunk/core/operation/attributes.rb +78 -0
  22. data/lib/pg_trunk/core/operation/callbacks.rb +40 -0
  23. data/lib/pg_trunk/core/operation/generators.rb +51 -0
  24. data/lib/pg_trunk/core/operation/inversion.rb +70 -0
  25. data/lib/pg_trunk/core/operation/registration.rb +55 -0
  26. data/lib/pg_trunk/core/operation/ruby_builder.rb +112 -0
  27. data/lib/pg_trunk/core/operation/ruby_helpers.rb +99 -0
  28. data/lib/pg_trunk/core/operation/sql_helpers.rb +44 -0
  29. data/lib/pg_trunk/core/operation/validations.rb +21 -0
  30. data/lib/pg_trunk/core/operation.rb +78 -0
  31. data/lib/pg_trunk/core/qualified_name.rb +165 -0
  32. data/lib/pg_trunk/core/railtie/command_recorder.rb +30 -0
  33. data/lib/pg_trunk/core/railtie/custom_types.rb +37 -0
  34. data/lib/pg_trunk/core/railtie/migration.rb +50 -0
  35. data/lib/pg_trunk/core/railtie/migrator.rb +22 -0
  36. data/lib/pg_trunk/core/railtie/schema_dumper.rb +75 -0
  37. data/lib/pg_trunk/core/railtie/schema_migration.rb +22 -0
  38. data/lib/pg_trunk/core/railtie/statements.rb +21 -0
  39. data/lib/pg_trunk/core/railtie.rb +35 -0
  40. data/lib/pg_trunk/core/registry.rb +159 -0
  41. data/lib/pg_trunk/core/serializers/array_of_hashes_serializer.rb +28 -0
  42. data/lib/pg_trunk/core/serializers/array_of_strings_serializer.rb +29 -0
  43. data/lib/pg_trunk/core/serializers/array_of_symbols_serializer.rb +28 -0
  44. data/lib/pg_trunk/core/serializers/array_serializer.rb +22 -0
  45. data/lib/pg_trunk/core/serializers/lowercase_string_serializer.rb +21 -0
  46. data/lib/pg_trunk/core/serializers/multiline_text_serializer.rb +21 -0
  47. data/lib/pg_trunk/core/serializers/qualified_name_serializer.rb +27 -0
  48. data/lib/pg_trunk/core/serializers/symbol_serializer.rb +22 -0
  49. data/lib/pg_trunk/core/serializers.rb +16 -0
  50. data/lib/pg_trunk/core/validators/all_items_valid_validator.rb +15 -0
  51. data/lib/pg_trunk/core/validators/difference_validator.rb +19 -0
  52. data/lib/pg_trunk/core/validators.rb +10 -0
  53. data/lib/pg_trunk/core.rb +21 -0
  54. data/lib/pg_trunk/generators.rb +7 -0
  55. data/lib/pg_trunk/operations/check_constraints/add_check_constraint.rb +109 -0
  56. data/lib/pg_trunk/operations/check_constraints/base.rb +69 -0
  57. data/lib/pg_trunk/operations/check_constraints/drop_check_constraint.rb +60 -0
  58. data/lib/pg_trunk/operations/check_constraints/rename_check_constraint.rb +54 -0
  59. data/lib/pg_trunk/operations/check_constraints/validate_check_constraint.rb +39 -0
  60. data/lib/pg_trunk/operations/check_constraints.rb +14 -0
  61. data/lib/pg_trunk/operations/composite_types/base.rb +61 -0
  62. data/lib/pg_trunk/operations/composite_types/change_composite_type.rb +136 -0
  63. data/lib/pg_trunk/operations/composite_types/column.rb +118 -0
  64. data/lib/pg_trunk/operations/composite_types/create_composite_type.rb +99 -0
  65. data/lib/pg_trunk/operations/composite_types/drop_composite_type.rb +67 -0
  66. data/lib/pg_trunk/operations/composite_types/rename_composite_type.rb +44 -0
  67. data/lib/pg_trunk/operations/composite_types.rb +15 -0
  68. data/lib/pg_trunk/operations/domains/base.rb +46 -0
  69. data/lib/pg_trunk/operations/domains/change_domain.rb +140 -0
  70. data/lib/pg_trunk/operations/domains/constraint.rb +93 -0
  71. data/lib/pg_trunk/operations/domains/create_domain.rb +124 -0
  72. data/lib/pg_trunk/operations/domains/drop_domain.rb +65 -0
  73. data/lib/pg_trunk/operations/domains/rename_domain.rb +44 -0
  74. data/lib/pg_trunk/operations/domains.rb +15 -0
  75. data/lib/pg_trunk/operations/enums/base.rb +47 -0
  76. data/lib/pg_trunk/operations/enums/change.rb +55 -0
  77. data/lib/pg_trunk/operations/enums/change_enum.rb +119 -0
  78. data/lib/pg_trunk/operations/enums/create_enum.rb +83 -0
  79. data/lib/pg_trunk/operations/enums/drop_enum.rb +63 -0
  80. data/lib/pg_trunk/operations/enums/rename_enum.rb +44 -0
  81. data/lib/pg_trunk/operations/enums.rb +15 -0
  82. data/lib/pg_trunk/operations/foreign_keys/add_foreign_key.rb +174 -0
  83. data/lib/pg_trunk/operations/foreign_keys/base.rb +155 -0
  84. data/lib/pg_trunk/operations/foreign_keys/drop_foreign_key.rb +76 -0
  85. data/lib/pg_trunk/operations/foreign_keys/rename_foreign_key.rb +63 -0
  86. data/lib/pg_trunk/operations/foreign_keys.rb +16 -0
  87. data/lib/pg_trunk/operations/functions/base.rb +54 -0
  88. data/lib/pg_trunk/operations/functions/change_function.rb +108 -0
  89. data/lib/pg_trunk/operations/functions/create_function.rb +198 -0
  90. data/lib/pg_trunk/operations/functions/drop_function.rb +88 -0
  91. data/lib/pg_trunk/operations/functions/rename_function.rb +57 -0
  92. data/lib/pg_trunk/operations/functions.rb +14 -0
  93. data/lib/pg_trunk/operations/indexes/add_index.rb +68 -0
  94. data/lib/pg_trunk/operations/indexes.rb +10 -0
  95. data/lib/pg_trunk/operations/materialized_views/base.rb +79 -0
  96. data/lib/pg_trunk/operations/materialized_views/change_materialized_view.rb +139 -0
  97. data/lib/pg_trunk/operations/materialized_views/column.rb +94 -0
  98. data/lib/pg_trunk/operations/materialized_views/create_materialized_view.rb +170 -0
  99. data/lib/pg_trunk/operations/materialized_views/drop_materialized_view.rb +70 -0
  100. data/lib/pg_trunk/operations/materialized_views/refresh_materialized_view.rb +48 -0
  101. data/lib/pg_trunk/operations/materialized_views/rename_materialized_view.rb +61 -0
  102. data/lib/pg_trunk/operations/materialized_views.rb +17 -0
  103. data/lib/pg_trunk/operations/procedures/base.rb +42 -0
  104. data/lib/pg_trunk/operations/procedures/change_procedure.rb +107 -0
  105. data/lib/pg_trunk/operations/procedures/create_procedure.rb +146 -0
  106. data/lib/pg_trunk/operations/procedures/drop_procedure.rb +66 -0
  107. data/lib/pg_trunk/operations/procedures/rename_procedure.rb +57 -0
  108. data/lib/pg_trunk/operations/procedures.rb +14 -0
  109. data/lib/pg_trunk/operations/statistics/base.rb +94 -0
  110. data/lib/pg_trunk/operations/statistics/create_statistics.rb +181 -0
  111. data/lib/pg_trunk/operations/statistics/drop_statistics.rb +75 -0
  112. data/lib/pg_trunk/operations/statistics/rename_statistics.rb +48 -0
  113. data/lib/pg_trunk/operations/statistics.rb +13 -0
  114. data/lib/pg_trunk/operations/tables/create_table.rb +75 -0
  115. data/lib/pg_trunk/operations/tables.rb +10 -0
  116. data/lib/pg_trunk/operations/triggers/base.rb +119 -0
  117. data/lib/pg_trunk/operations/triggers/change_trigger.rb +82 -0
  118. data/lib/pg_trunk/operations/triggers/create_trigger.rb +208 -0
  119. data/lib/pg_trunk/operations/triggers/drop_trigger.rb +66 -0
  120. data/lib/pg_trunk/operations/triggers/rename_trigger.rb +71 -0
  121. data/lib/pg_trunk/operations/triggers.rb +14 -0
  122. data/lib/pg_trunk/operations/views/base.rb +38 -0
  123. data/lib/pg_trunk/operations/views/change_view.rb +90 -0
  124. data/lib/pg_trunk/operations/views/create_view.rb +115 -0
  125. data/lib/pg_trunk/operations/views/drop_view.rb +69 -0
  126. data/lib/pg_trunk/operations/views/rename_view.rb +58 -0
  127. data/lib/pg_trunk/operations/views.rb +14 -0
  128. data/lib/pg_trunk/operations.rb +23 -0
  129. data/lib/pg_trunk/version.rb +6 -0
  130. data/lib/pg_trunk.rb +27 -0
  131. data/pg_trunk.gemspec +34 -0
  132. data/spec/dummy/.gitignore +16 -0
  133. data/spec/dummy/Rakefile +15 -0
  134. data/spec/dummy/bin/bundle +6 -0
  135. data/spec/dummy/bin/rails +6 -0
  136. data/spec/dummy/bin/rake +6 -0
  137. data/spec/dummy/config/application.rb +18 -0
  138. data/spec/dummy/config/boot.rb +7 -0
  139. data/spec/dummy/config/database.yml +14 -0
  140. data/spec/dummy/config/environment.rb +7 -0
  141. data/spec/dummy/config.ru +6 -0
  142. data/spec/dummy/db/materialized_views/admin_users_v01.sql +1 -0
  143. data/spec/dummy/db/migrate/.keep +0 -0
  144. data/spec/dummy/db/schema.rb +18 -0
  145. data/spec/dummy/db/views/admin_users_v01.sql +1 -0
  146. data/spec/dummy/db/views/admin_users_v02.sql +1 -0
  147. data/spec/operations/check_constraints/add_check_constraint_spec.rb +85 -0
  148. data/spec/operations/check_constraints/drop_check_constraint_spec.rb +111 -0
  149. data/spec/operations/check_constraints/rename_check_constraint_spec.rb +90 -0
  150. data/spec/operations/composite_types/change_composite_type_spec.rb +257 -0
  151. data/spec/operations/composite_types/create_composite_type_spec.rb +55 -0
  152. data/spec/operations/composite_types/drop_composite_type_spec.rb +109 -0
  153. data/spec/operations/composite_types/rename_composite_type_spec.rb +74 -0
  154. data/spec/operations/dependency_resolver_spec.rb +177 -0
  155. data/spec/operations/domains/change_domain_spec.rb +287 -0
  156. data/spec/operations/domains/create_domain_spec.rb +69 -0
  157. data/spec/operations/domains/drop_domain_spec.rb +119 -0
  158. data/spec/operations/domains/rename_domain_spec.rb +70 -0
  159. data/spec/operations/enums/change_enum_spec.rb +157 -0
  160. data/spec/operations/enums/create_enum_spec.rb +40 -0
  161. data/spec/operations/enums/drop_enum_spec.rb +120 -0
  162. data/spec/operations/enums/rename_enum_spec.rb +72 -0
  163. data/spec/operations/foreign_keys/add_foreign_key_spec.rb +208 -0
  164. data/spec/operations/foreign_keys/drop_foreign_key_spec.rb +167 -0
  165. data/spec/operations/foreign_keys/rename_foreign_key_spec.rb +101 -0
  166. data/spec/operations/functions/change_function_spec.rb +166 -0
  167. data/spec/operations/functions/create_function_spec.rb +192 -0
  168. data/spec/operations/functions/drop_function_spec.rb +182 -0
  169. data/spec/operations/functions/rename_function_spec.rb +101 -0
  170. data/spec/operations/indexes/add_index_spec.rb +94 -0
  171. data/spec/operations/materialized_views/change_materialized_view_spec.rb +190 -0
  172. data/spec/operations/materialized_views/create_materialized_view_spec.rb +144 -0
  173. data/spec/operations/materialized_views/drop_materialized_view_spec.rb +145 -0
  174. data/spec/operations/materialized_views/refresh_materialized_view_spec.rb +79 -0
  175. data/spec/operations/materialized_views/rename_materialized_view_spec.rb +88 -0
  176. data/spec/operations/procedures/change_procedure_spec.rb +175 -0
  177. data/spec/operations/procedures/create_procedure_spec.rb +151 -0
  178. data/spec/operations/procedures/drop_procedure_spec.rb +159 -0
  179. data/spec/operations/procedures/rename_procedure_spec.rb +107 -0
  180. data/spec/operations/statistics/create_statistics_spec.rb +230 -0
  181. data/spec/operations/statistics/drop_statistics_spec.rb +106 -0
  182. data/spec/operations/statistics/rename_statistics_spec.rb +129 -0
  183. data/spec/operations/tables/create_table_spec.rb +53 -0
  184. data/spec/operations/tables/rename_table_spec.rb +37 -0
  185. data/spec/operations/triggers/change_trigger_spec.rb +195 -0
  186. data/spec/operations/triggers/create_trigger_spec.rb +104 -0
  187. data/spec/operations/triggers/drop_trigger_spec.rb +124 -0
  188. data/spec/operations/triggers/rename_trigger_spec.rb +160 -0
  189. data/spec/operations/views/change_view_spec.rb +144 -0
  190. data/spec/operations/views/create_view_spec.rb +134 -0
  191. data/spec/operations/views/drop_view_spec.rb +146 -0
  192. data/spec/operations/views/rename_view_spec.rb +85 -0
  193. data/spec/pg_trunk/dependencies_resolver_spec.rb +43 -0
  194. data/spec/spec_helper.rb +28 -0
  195. data/spec/support/migrations_helper.rb +376 -0
  196. metadata +348 -0
@@ -0,0 +1,287 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#change_domain" do
4
+ before_all { run_migration "create_schema :dict" }
5
+ before { run_migration(old_snippet) }
6
+
7
+ let(:old_snippet) do
8
+ <<~RUBY
9
+ create_domain "dict.us_postal_code", as: "text" do |d|
10
+ d.collation "en_US"
11
+ d.default_sql "'00000'::text"
12
+ d.null false
13
+ d.constraint %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text), name: "valid_code"
14
+ d.comment "US postal code"
15
+ end
16
+ RUBY
17
+ end
18
+
19
+ context "when not null constraint is changed" do
20
+ let(:migration) do
21
+ <<~RUBY
22
+ change_domain "dict.us_postal_code" do |d|
23
+ d.null true
24
+ end
25
+ RUBY
26
+ end
27
+ let(:new_snippet) do
28
+ <<~RUBY
29
+ create_domain "dict.us_postal_code", as: "text" do |d|
30
+ d.collation "en_US"
31
+ d.default_sql "'00000'::text"
32
+ d.constraint %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text), name: "valid_code"
33
+ d.comment "US postal code"
34
+ end
35
+ RUBY
36
+ end
37
+ let(:query) { "SELECT NULL::dict.us_postal_code;" }
38
+
39
+ its(:execution) { is_expected.to enable_sql_request(query) }
40
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
41
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
42
+
43
+ its(:inversion) { is_expected.to disable_sql_request(query) }
44
+ its(:inversion) { is_expected.not_to change_schema }
45
+ end
46
+
47
+ context "when new constraint is added" do
48
+ let(:migration) do
49
+ <<~RUBY
50
+ change_domain "dict.us_postal_code" do |d|
51
+ d.add_constraint %q(VALUE ~ '^\\d{5}$'::text), name: "new_check"
52
+ end
53
+ RUBY
54
+ end
55
+ let(:new_snippet) do
56
+ <<~RUBY
57
+ create_domain "dict.us_postal_code", as: "text" do |d|
58
+ d.collation "en_US"
59
+ d.default_sql "'00000'::text"
60
+ d.null false
61
+ d.constraint %q(VALUE ~ '^\\d{5}$'::text), name: "new_check"
62
+ d.constraint %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text), name: "valid_code"
63
+ d.comment "US postal code"
64
+ end
65
+ RUBY
66
+ end
67
+ let(:query) { "SELECT '00000-0000'::dict.us_postal_code;" }
68
+
69
+ its(:execution) { is_expected.to disable_sql_request(query) }
70
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
71
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
72
+
73
+ its(:inversion) { is_expected.to enable_sql_request(query) }
74
+ its(:inversion) { is_expected.not_to change_schema }
75
+ end
76
+
77
+ context "when existing constraint is dropped with :check option" do
78
+ let(:migration) do
79
+ <<~RUBY
80
+ change_domain "dict.us_postal_code" do |d|
81
+ d.drop_constraint "valid_code", check: %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text)
82
+ end
83
+ RUBY
84
+ end
85
+ let(:new_snippet) do
86
+ <<~RUBY
87
+ create_domain "dict.us_postal_code", as: "text" do |d|
88
+ d.collation "en_US"
89
+ d.default_sql "'00000'::text"
90
+ d.null false
91
+ d.comment "US postal code"
92
+ end
93
+ RUBY
94
+ end
95
+ let(:query) { "SELECT 'foobar'::dict.us_postal_code;" }
96
+
97
+ its(:execution) { is_expected.to enable_sql_request(query) }
98
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
99
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
100
+
101
+ its(:inversion) { is_expected.to disable_sql_request(query) }
102
+ its(:inversion) { is_expected.not_to change_schema }
103
+ end
104
+
105
+ context "with `force: :cascade` option" do
106
+ let(:migration) do
107
+ <<~RUBY
108
+ change_domain "dict.us_postal_code", force: :cascade do |d|
109
+ d.drop_constraint "valid_code", check: %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text)
110
+ end
111
+ RUBY
112
+ end
113
+ let(:new_snippet) do
114
+ <<~RUBY
115
+ create_domain "dict.us_postal_code", as: "text" do |d|
116
+ d.collation "en_US"
117
+ d.default_sql "'00000'::text"
118
+ d.null false
119
+ d.comment "US postal code"
120
+ end
121
+ RUBY
122
+ end
123
+ let(:query) { "SELECT '00000-0000'::dict.us_postal_code;" }
124
+
125
+ its(:execution) { is_expected.to enable_sql_request(query) }
126
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
127
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
128
+ it { is_expected.to be_irreversible.because_of(/force: :cascade/i) }
129
+ end
130
+
131
+ context "when existing constraint is dropped without :check option" do
132
+ let(:migration) do
133
+ <<~RUBY
134
+ change_domain "dict.us_postal_code" do |d|
135
+ d.drop_constraint "valid_code"
136
+ end
137
+ RUBY
138
+ end
139
+ let(:new_snippet) do
140
+ <<~RUBY
141
+ create_domain "dict.us_postal_code", as: "text" do |d|
142
+ d.collation "en_US"
143
+ d.default_sql "'00000'::text"
144
+ d.null false
145
+ d.comment "US postal code"
146
+ end
147
+ RUBY
148
+ end
149
+ let(:query) { "SELECT '00000-0000'::us_postal_code;" }
150
+
151
+ its(:execution) { is_expected.to disable_sql_request(query) }
152
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
153
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
154
+ it { is_expected.to be_irreversible.because_of(/check/i) }
155
+ end
156
+
157
+ context "when absent constraint is dropped without :if_exists option" do
158
+ let(:migration) do
159
+ <<~RUBY
160
+ change_domain "dict.us_postal_code" do |d|
161
+ d.drop_constraint "foo"
162
+ end
163
+ RUBY
164
+ end
165
+
166
+ its(:execution) { is_expected.to raise_error(StandardError) }
167
+ end
168
+
169
+ context "when absent constraint is dropped with `if_exists: true` option" do
170
+ let(:migration) do
171
+ <<~RUBY
172
+ change_domain "dict.us_postal_code" do |d|
173
+ d.drop_constraint "foo", if_exists: true
174
+ end
175
+ RUBY
176
+ end
177
+
178
+ its(:execution) { is_expected.not_to change_schema }
179
+ it { is_expected.to be_irreversible.because_of(/if_exists: true/i) }
180
+ end
181
+
182
+ context "when default_sql value is changed with :from option" do
183
+ let(:migration) do
184
+ <<~RUBY
185
+ change_domain "dict.us_postal_code" do |d|
186
+ d.default_sql "'11111-0000'", from: "'00000'"
187
+ end
188
+ RUBY
189
+ end
190
+ let(:new_snippet) do
191
+ <<~RUBY
192
+ create_domain "dict.us_postal_code", as: "text" do |d|
193
+ d.collation "en_US"
194
+ d.default_sql "'11111-0000'::text"
195
+ d.null false
196
+ d.constraint %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text), name: "valid_code"
197
+ d.comment "US postal code"
198
+ end
199
+ RUBY
200
+ end
201
+
202
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
203
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
204
+ its(:inversion) { is_expected.not_to change_schema }
205
+ end
206
+
207
+ context "when default_sql value is changed without :from option" do
208
+ let(:migration) do
209
+ <<~RUBY
210
+ change_domain "dict.us_postal_code" do |d|
211
+ d.default_sql "'11111-0000'"
212
+ end
213
+ RUBY
214
+ end
215
+ let(:new_snippet) do
216
+ <<~RUBY
217
+ create_domain "dict.us_postal_code", as: "text" do |d|
218
+ d.collation "en_US"
219
+ d.default_sql "'11111-0000'::text"
220
+ d.null false
221
+ d.constraint %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text), name: "valid_code"
222
+ d.comment "US postal code"
223
+ end
224
+ RUBY
225
+ end
226
+
227
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
228
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
229
+ it { is_expected.to be_irreversible.because_of(/default_sql/i) }
230
+ end
231
+
232
+ context "when comment is changed with :from option" do
233
+ let(:migration) do
234
+ <<~RUBY
235
+ change_domain "dict.us_postal_code" do |d|
236
+ d.comment "US postal code (zip)", from: "US postal code"
237
+ end
238
+ RUBY
239
+ end
240
+ let(:new_snippet) do
241
+ <<~RUBY
242
+ create_domain "dict.us_postal_code", as: "text" do |d|
243
+ d.collation "en_US"
244
+ d.default_sql "'00000'::text"
245
+ d.null false
246
+ d.constraint %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text), name: "valid_code"
247
+ d.comment "US postal code (zip)"
248
+ end
249
+ RUBY
250
+ end
251
+
252
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
253
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
254
+ its(:inversion) { is_expected.not_to change_schema }
255
+ end
256
+
257
+ context "when comment is changed without :from option" do
258
+ let(:migration) do
259
+ <<~RUBY
260
+ change_domain "dict.us_postal_code" do |d|
261
+ d.comment "US postal code (zip)"
262
+ end
263
+ RUBY
264
+ end
265
+ let(:new_snippet) do
266
+ <<~RUBY
267
+ create_domain "dict.us_postal_code", as: "text" do |d|
268
+ d.collation "en_US"
269
+ d.default_sql "'00000'::text"
270
+ d.null false
271
+ d.constraint %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text), name: "valid_code"
272
+ d.comment "US postal code (zip)"
273
+ end
274
+ RUBY
275
+ end
276
+
277
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
278
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
279
+ it { is_expected.to be_irreversible.because_of(/comment/i) }
280
+ end
281
+
282
+ context "without changes" do
283
+ let(:migration) { 'change_domain "dict.us_postal_code"' }
284
+
285
+ it { is_expected.to fail_validation.because(/there are no changes/i) }
286
+ end
287
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#create_domain" do
4
+ before_all { run_migration "create_schema :dict" }
5
+
6
+ context "with a full definition" do
7
+ let(:migration) do
8
+ <<~RUBY
9
+ create_domain "dict.us_postal_code", as: "text" do |d|
10
+ d.collation "en_US"
11
+ d.default_sql "'00000'::text"
12
+ d.null false
13
+ d.constraint %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text), name: "valid_code"
14
+ d.comment "US postal code"
15
+ end
16
+ RUBY
17
+ end
18
+ let(:query) { "SELECT '12345'::dict.us_postal_code;" }
19
+
20
+ its(:execution) { is_expected.to enable_sql_request(query) }
21
+ its(:execution) { is_expected.to insert(migration).into_schema }
22
+ its(:inversion) { is_expected.to disable_sql_request(query) }
23
+ its(:inversion) { is_expected.not_to change_schema }
24
+ end
25
+
26
+ context "without type" do
27
+ let(:migration) do
28
+ <<~RUBY
29
+ create_domain "dict.us_postal_code" do |d|
30
+ d.collation "en_US"
31
+ d.default_sql <<~Q
32
+ '00000'::text
33
+ Q
34
+ d.null false
35
+ d.constraint <<~Q, name: "valid_code"
36
+ VALUE ~ '^\d{5}$'::text OR VALUE ~ '^\d{5}-\d{4}$'::text
37
+ Q
38
+ d.comment <<~COMMENT
39
+ US postal code
40
+ COMMENT
41
+ end
42
+ RUBY
43
+ end
44
+
45
+ it { is_expected.to fail_validation.because(/type can't be blank/i) }
46
+ end
47
+
48
+ context "without name" do
49
+ let(:migration) do
50
+ <<~RUBY
51
+ create_domain as: "text" do |d|
52
+ d.collation "en_US"
53
+ d.default_sql <<~Q
54
+ '00000'::text
55
+ Q
56
+ d.null false
57
+ d.constraint <<~Q, name: "valid_code"
58
+ VALUE ~ '^\d{5}$'::text OR VALUE ~ '^\d{5}-\d{4}$'::text
59
+ Q
60
+ d.comment <<~COMMENT
61
+ US postal code
62
+ COMMENT
63
+ end
64
+ RUBY
65
+ end
66
+
67
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
68
+ end
69
+ end
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#drop_domain" do
4
+ before_all { run_migration "create_schema :dict" }
5
+ before { run_migration(snippet) }
6
+
7
+ let(:snippet) do
8
+ <<~RUBY
9
+ create_domain "dict.us_postal_code", as: "text" do |d|
10
+ d.collation "en_US"
11
+ d.default_sql "'00000'::text"
12
+ d.null false
13
+ d.constraint %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text), name: "valid_code"
14
+ d.comment "US postal code"
15
+ end
16
+ RUBY
17
+ end
18
+ let(:query) { "SELECT '00000-0000'::dict.us_postal_code;" }
19
+
20
+ context "with a full definition" do
21
+ let(:migration) do
22
+ <<~RUBY
23
+ drop_domain "dict.us_postal_code", as: "text" do |d|
24
+ d.collation "en_US"
25
+ d.default_sql "'00000'::text"
26
+ d.constraint %q(VALUE ~ '^\\d{5}(-\\d{4})?$'::text), name: "valid_code"
27
+ d.null false
28
+ d.comment "US postal code"
29
+ end
30
+ RUBY
31
+ end
32
+
33
+ its(:execution) { is_expected.to disable_sql_request(query) }
34
+ its(:execution) { is_expected.to remove(snippet).from_schema }
35
+ its(:inversion) { is_expected.to enable_sql_request(query) }
36
+ its(:inversion) { is_expected.not_to change_schema }
37
+ end
38
+
39
+ context "with a qualified name only" do
40
+ let(:migration) do
41
+ <<~RUBY
42
+ drop_domain "dict.us_postal_code"
43
+ RUBY
44
+ end
45
+
46
+ its(:execution) { is_expected.to disable_sql_request(query) }
47
+ its(:execution) { is_expected.to remove(snippet).from_schema }
48
+ it { is_expected.to be_irreversible.because(/type can't be blank/i) }
49
+ end
50
+
51
+ context "when domain is used" do
52
+ before do
53
+ run_migration <<~RUBY
54
+ execute <<~Q
55
+ CREATE TABLE sums (value integer, zip dict.us_postal_code);
56
+ Q
57
+ RUBY
58
+ end
59
+
60
+ context "without the `force` option" do
61
+ let(:migration) do
62
+ <<~RUBY
63
+ drop_domain "dict.us_postal_code"
64
+ RUBY
65
+ end
66
+
67
+ its(:execution) { is_expected.to raise_error(StandardError) }
68
+ end
69
+
70
+ context "with the `force: :cascade` option" do
71
+ let(:migration) do
72
+ <<~RUBY
73
+ drop_domain "dict.us_postal_code", force: :cascade
74
+ RUBY
75
+ end
76
+
77
+ its(:execution) { is_expected.to disable_sql_request(query) }
78
+ its(:execution) { is_expected.to remove(snippet).from_schema }
79
+ it { is_expected.to be_irreversible.because_of(/force: :cascade/i) }
80
+ end
81
+ end
82
+
83
+ context "when domain is absent without `if_exists` option" do
84
+ let(:migration) do
85
+ <<~RUBY
86
+ drop_domain "foo"
87
+ RUBY
88
+ end
89
+
90
+ its(:execution) { is_expected.to raise_error(StandardError) }
91
+ end
92
+
93
+ context "when domain is absent with `if_exists: true` option" do
94
+ let(:migration) do
95
+ <<~RUBY
96
+ drop_domain "foo", if_exists: true
97
+ RUBY
98
+ end
99
+
100
+ its(:execution) { is_expected.not_to change_schema }
101
+ it { is_expected.to be_irreversible.because_of(/if_exists: true/i) }
102
+ end
103
+
104
+ context "without a name" do
105
+ let(:migration) do
106
+ <<~RUBY
107
+ drop_domain as: "text" do |d|
108
+ d.collation "en_US"
109
+ d.default_sql "'00000'::text"
110
+ d.constraint %q(VALUE ~ '^\d{5}$' OR VALUE ~ '^\d{5}-\d{4}$'), name: "valid_code"
111
+ d.null false
112
+ d.comment "US postal code"
113
+ end
114
+ RUBY
115
+ end
116
+
117
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
118
+ end
119
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#rename_domain" do
4
+ before_all { run_migration "create_schema :dict" }
5
+ before { run_migration(old_snippet) }
6
+
7
+ let(:old_snippet) do
8
+ <<~RUBY
9
+ create_domain "existing_string", as: "text" do |d|
10
+ d.null false
11
+ end
12
+ RUBY
13
+ end
14
+ let(:old_query) { "SELECT 'foo'::existing_string;" }
15
+
16
+ context "with new name and schema" do
17
+ let(:migration) do
18
+ <<~RUBY
19
+ rename_domain "existing_string", to: "dict.present_string"
20
+ RUBY
21
+ end
22
+ let(:new_snippet) do
23
+ <<~RUBY
24
+ create_domain "dict.present_string", as: "text" do |d|
25
+ d.null false
26
+ end
27
+ RUBY
28
+ end
29
+ let(:new_query) { "SELECT 'USD'::dict.present_string;" }
30
+
31
+ its(:execution) { is_expected.to enable_sql_request(new_query) }
32
+ its(:execution) { is_expected.to disable_sql_request(old_query) }
33
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
34
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
35
+
36
+ its(:inversion) { is_expected.to disable_sql_request(new_query) }
37
+ its(:inversion) { is_expected.to enable_sql_request(old_query) }
38
+ its(:inversion) { is_expected.not_to change_schema }
39
+ end
40
+
41
+ context "with the same name and schema" do
42
+ let(:migration) do
43
+ <<~RUBY
44
+ rename_domain "existing_string", to: "public.existing_string"
45
+ RUBY
46
+ end
47
+
48
+ it { is_expected.to fail_validation.because(/new name must be different/i) }
49
+ end
50
+
51
+ context "without new schema/name" do
52
+ let(:migration) do
53
+ <<~RUBY
54
+ rename_domain "existing_string"
55
+ RUBY
56
+ end
57
+
58
+ it { is_expected.to fail_validation.because(/new name can't be blank/i) }
59
+ end
60
+
61
+ context "without current name" do
62
+ let(:migration) do
63
+ <<~RUBY
64
+ rename_domain to: "dict.present_string"
65
+ RUBY
66
+ end
67
+
68
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
69
+ end
70
+ end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#change_enum" do
4
+ before_all { run_migration "create_schema :finances" }
5
+ before { run_migration(old_snippet) }
6
+
7
+ let(:old_snippet) do
8
+ <<~RUBY
9
+ create_enum "currency" do |e|
10
+ e.values "eur", "usd"
11
+ e.comment "Supported currencies"
12
+ end
13
+ RUBY
14
+ end
15
+
16
+ context "when values are renamed" do
17
+ let(:migration) do
18
+ <<~RUBY
19
+ change_enum :currency do |e|
20
+ e.rename_value "usd", to: "USD"
21
+ e.rename_value "eur", to: "EUR"
22
+ end
23
+ RUBY
24
+ end
25
+ let(:new_snippet) do
26
+ <<~RUBY
27
+ create_enum "currency" do |e|
28
+ e.values "EUR", "USD"
29
+ e.comment "Supported currencies"
30
+ end
31
+ RUBY
32
+ end
33
+ let(:old_query) { "SELECT 'eur'::currency;" }
34
+ let(:new_query) { "SELECT 'EUR'::currency;" }
35
+
36
+ its(:execution) { is_expected.to disable_sql_request(old_query) }
37
+ its(:execution) { is_expected.to enable_sql_request(new_query) }
38
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
39
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
40
+
41
+ its(:inversion) { is_expected.to enable_sql_request(old_query) }
42
+ its(:inversion) { is_expected.to disable_sql_request(new_query) }
43
+ its(:inversion) { is_expected.not_to change_schema }
44
+ end
45
+
46
+ context "when values are added" do
47
+ let(:migration) do
48
+ <<~RUBY
49
+ change_enum :currency do |e|
50
+ e.add_value "jpy"
51
+ e.add_value "cfr", before: "eur"
52
+ e.add_value "gbp", after: "eur"
53
+ e.add_value "btc", before: "cfr"
54
+ end
55
+ RUBY
56
+ end
57
+ let(:new_snippet) do
58
+ <<~RUBY
59
+ create_enum "currency" do |e|
60
+ e.values "btc", "cfr", "eur", "gbp", "usd", "jpy"
61
+ e.comment "Supported currencies"
62
+ end
63
+ RUBY
64
+ end
65
+
66
+ context "in PostgreSQL v11 and below", before_version: 12 do
67
+ its(:execution) { is_expected.to raise_error(/supported in PostgreSQL v12+/i) }
68
+ end
69
+
70
+ context "in PostgreSQL v12+", since_version: 12 do
71
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
72
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
73
+ it { is_expected.to be_irreversible.because_of(/adding new values/i) }
74
+ end
75
+ end
76
+
77
+ context "when values both added and renamed", since_version: 12 do
78
+ let(:migration) do
79
+ <<~RUBY
80
+ change_enum :currency do |e|
81
+ e.rename_value "eur", to: "EUR"
82
+ e.add_value "CFR", before: "eur"
83
+ e.rename_value "usd", to: "USD"
84
+ end
85
+ RUBY
86
+ end
87
+ let(:new_snippet) do
88
+ <<~RUBY
89
+ create_enum "currency" do |e|
90
+ e.values "CFR", "EUR", "USD"
91
+ e.comment "Supported currencies"
92
+ end
93
+ RUBY
94
+ end
95
+
96
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
97
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
98
+ it { is_expected.to be_irreversible.because_of(/adding new values/i) }
99
+ end
100
+
101
+ context "when the comment is changed" do
102
+ let(:new_snippet) do
103
+ <<~RUBY
104
+ create_enum "currency" do |e|
105
+ e.values "eur", "usd"
106
+ e.comment "Supported currency values"
107
+ end
108
+ RUBY
109
+ end
110
+
111
+ context "without the `from` option" do
112
+ let(:migration) do
113
+ <<~RUBY
114
+ change_enum "currency" do |e|
115
+ e.comment "Supported currency values"
116
+ end
117
+ RUBY
118
+ end
119
+
120
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
121
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
122
+ it { is_expected.to be_irreversible.because_of(/comment/i) }
123
+ end
124
+
125
+ context "with the `from` option" do
126
+ let(:migration) do
127
+ <<~RUBY
128
+ change_enum "currency" do |e|
129
+ e.comment "Supported currency values", from: "Supported currencies"
130
+ end
131
+ RUBY
132
+ end
133
+
134
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
135
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
136
+ its(:inversion) { is_expected.not_to change_schema }
137
+ end
138
+ end
139
+
140
+ context "without any change" do
141
+ let(:migration) { "change_enum :currencies" }
142
+
143
+ it { is_expected.to fail_validation.because(/there are no changes/i) }
144
+ end
145
+
146
+ context "without a name" do
147
+ let(:migration) do
148
+ <<~RUBY
149
+ change_enum do |e|
150
+ e.rename_value "eur", to: "EUR"
151
+ end
152
+ RUBY
153
+ end
154
+
155
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
156
+ end
157
+ end