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,257 @@
1
+ # frozen_string_literal: true
2
+
3
+ # frozen_text_literal: true
4
+
5
+ describe ActiveRecord::Migration, "#change_composite_type" do
6
+ before_all { run_migration "create_schema :paint" }
7
+ before { run_migration(old_snippet) }
8
+
9
+ let(:old_snippet) do
10
+ <<~RUBY
11
+ create_composite_type "paint.colored_point" do |t|
12
+ t.column "x", "integer"
13
+ t.column "y", "integer"
14
+ t.column "color", "text", collation: "en_US"
15
+ t.comment "2D point with a color"
16
+ end
17
+ RUBY
18
+ end
19
+ let(:old_query) { "SELECT (10, -1, 'blue')::paint.colored_point" }
20
+
21
+ context "when new column is added" do
22
+ let(:migration) do
23
+ <<~RUBY
24
+ change_composite_type "paint.colored_point" do |t|
25
+ t.add_column "z", "integer"
26
+ end
27
+ RUBY
28
+ end
29
+ let(:new_snippet) do
30
+ <<~RUBY
31
+ create_composite_type "paint.colored_point" do |t|
32
+ t.column "x", "integer"
33
+ t.column "y", "integer"
34
+ t.column "color", "text", collation: "en_US"
35
+ t.column "z", "integer"
36
+ t.comment "2D point with a color"
37
+ end
38
+ RUBY
39
+ end
40
+ let(:new_query) { "SELECT (10, -1, 'blue', 6)::paint.colored_point" }
41
+
42
+ its(:execution) { is_expected.to enable_sql_request(new_query) }
43
+ its(:execution) { is_expected.to disable_sql_request(old_query) }
44
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
45
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
46
+
47
+ its(:inversion) { is_expected.to disable_sql_request(new_query) }
48
+ its(:inversion) { is_expected.to enable_sql_request(old_query) }
49
+ its(:inversion) { is_expected.not_to change_schema }
50
+ end
51
+
52
+ context "when the column is dropped" do
53
+ let(:migration) do
54
+ <<~RUBY
55
+ change_composite_type "paint.colored_point" do |t|
56
+ t.drop_column "color", "text", collation: "en_US"
57
+ end
58
+ RUBY
59
+ end
60
+ let(:new_snippet) do
61
+ <<~RUBY
62
+ create_composite_type "paint.colored_point" do |t|
63
+ t.column "x", "integer"
64
+ t.column "y", "integer"
65
+ t.comment "2D point with a color"
66
+ end
67
+ RUBY
68
+ end
69
+ let(:new_query) { "SELECT (10, 11)::paint.colored_point" }
70
+
71
+ its(:execution) { is_expected.to enable_sql_request(new_query) }
72
+ its(:execution) { is_expected.to disable_sql_request(old_query) }
73
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
74
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
75
+
76
+ it { is_expected.to be_irreversible.because_of(/y/i) }
77
+ end
78
+
79
+ context "when an absent column is dropped without :if_exists option" do
80
+ let(:migration) do
81
+ <<~RUBY
82
+ change_composite_type "paint.colored_point" do |t|
83
+ t.drop_column "f"
84
+ end
85
+ RUBY
86
+ end
87
+
88
+ its(:execution) { is_expected.to raise_error(StandardError) }
89
+ end
90
+
91
+ context "when an absent column is dropped with `if_exists: true` option" do
92
+ let(:migration) do
93
+ <<~RUBY
94
+ change_composite_type "paint.colored_point" do |t|
95
+ t.drop_column "f", if_exists: true
96
+ end
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 "when columns are renamed" do
105
+ let(:migration) do
106
+ <<~RUBY
107
+ change_composite_type "paint.colored_point" do |t|
108
+ t.rename_column "y", to: "z"
109
+ end
110
+ RUBY
111
+ end
112
+ let(:new_snippet) do
113
+ <<~RUBY
114
+ create_composite_type "paint.colored_point" do |t|
115
+ t.column "x", "integer"
116
+ t.column "z", "integer"
117
+ t.column "color", "text", collation: "en_US"
118
+ t.comment "2D point with a color"
119
+ end
120
+ RUBY
121
+ end
122
+
123
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
124
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
125
+ its(:inversion) { is_expected.not_to change_schema }
126
+ end
127
+
128
+ context "when columns are changed with :from options" do
129
+ let(:migration) do
130
+ <<~RUBY
131
+ change_composite_type "paint.colored_point" do |t|
132
+ t.change_column "x", "bigint", from_type: "integer"
133
+ t.change_column "y", "bigint", from_type: "integer"
134
+ t.change_column "color", "text", collation: "POSIX", from_type: "text", from_collation: "en_US"
135
+ end
136
+ RUBY
137
+ end
138
+ let(:new_snippet) do
139
+ <<~RUBY
140
+ create_composite_type "paint.colored_point" do |t|
141
+ t.column "x", "bigint"
142
+ t.column "y", "bigint"
143
+ t.column "color", "text", collation: "POSIX"
144
+ t.comment "2D point with a color"
145
+ end
146
+ RUBY
147
+ end
148
+
149
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
150
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
151
+ its(:inversion) { is_expected.not_to change_schema }
152
+ end
153
+
154
+ context "when columns are changed without :from options" do
155
+ let(:migration) do
156
+ <<~RUBY
157
+ change_composite_type "paint.colored_point" do |t|
158
+ t.change_column "x", "bigint"
159
+ t.change_column "y", "bigint"
160
+ t.change_column "color", "text", collation: "POSIX"
161
+ end
162
+ RUBY
163
+ end
164
+ let(:new_snippet) do
165
+ <<~RUBY
166
+ create_composite_type "paint.colored_point" do |t|
167
+ t.column "x", "bigint"
168
+ t.column "y", "bigint"
169
+ t.column "color", "text", collation: "POSIX"
170
+ t.comment "2D point with a color"
171
+ end
172
+ RUBY
173
+ end
174
+
175
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
176
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
177
+ it { is_expected.to be_irreversible }
178
+ end
179
+
180
+ context "when column is changed with `force: :cascade` option" do
181
+ let(:migration) do
182
+ <<~RUBY
183
+ change_composite_type "paint.colored_point", force: :cascade do |t|
184
+ t.change_column "x", "bigint", from_type: "integer"
185
+ end
186
+ RUBY
187
+ end
188
+ let(:new_snippet) do
189
+ <<~RUBY
190
+ create_composite_type "paint.colored_point" do |t|
191
+ t.column "x", "bigint"
192
+ t.column "y", "integer"
193
+ t.column "color", "text", collation: "en_US"
194
+ t.comment "2D point with a color"
195
+ end
196
+ RUBY
197
+ end
198
+
199
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
200
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
201
+ it { is_expected.to be_irreversible.because_of(/force: :cascade/) }
202
+ end
203
+
204
+ context "when comment is changed with :from option" do
205
+ let(:migration) do
206
+ <<~RUBY
207
+ change_composite_type "paint.colored_point" do |d|
208
+ d.comment "Colored 2D point", from: "2D point with a color"
209
+ end
210
+ RUBY
211
+ end
212
+ let(:new_snippet) do
213
+ <<~RUBY
214
+ create_composite_type "paint.colored_point" do |t|
215
+ t.column "x", "integer"
216
+ t.column "y", "integer"
217
+ t.column "color", "text", collation: "en_US"
218
+ t.comment "Colored 2D point"
219
+ end
220
+ RUBY
221
+ end
222
+
223
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
224
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
225
+ its(:inversion) { is_expected.not_to change_schema }
226
+ end
227
+
228
+ context "when comment is changed without :from option" do
229
+ let(:migration) do
230
+ <<~RUBY
231
+ change_composite_type "paint.colored_point" do |d|
232
+ d.comment "Colored 2D point"
233
+ end
234
+ RUBY
235
+ end
236
+ let(:new_snippet) do
237
+ <<~RUBY
238
+ create_composite_type "paint.colored_point" do |t|
239
+ t.column "x", "integer"
240
+ t.column "y", "integer"
241
+ t.column "color", "text", collation: "en_US"
242
+ t.comment "Colored 2D point"
243
+ end
244
+ RUBY
245
+ end
246
+
247
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
248
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
249
+ it { is_expected.to be_irreversible.because_of(/comment/i) }
250
+ end
251
+
252
+ context "without changes" do
253
+ let(:migration) { 'change_composite_type "dict.us_postal_code"' }
254
+
255
+ it { is_expected.to fail_validation.because(/there are no changes/i) }
256
+ end
257
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#create_composite_type" do
4
+ before_all { run_migration "create_schema :paint" }
5
+
6
+ context "with a full definition" do
7
+ let(:migration) do
8
+ <<~RUBY
9
+ create_composite_type "paint.colored_point" do |t|
10
+ t.column "x", "integer"
11
+ t.column "y", "integer"
12
+ t.column "color", "text", collation: "en_US"
13
+ t.comment "2D point with a color"
14
+ end
15
+ RUBY
16
+ end
17
+ let(:query) { "SELECT (10, -1, 'blue')::paint.colored_point" }
18
+
19
+ its(:execution) { is_expected.to enable_sql_request(query) }
20
+ its(:execution) { is_expected.to insert(migration).into_schema }
21
+ its(:inversion) { is_expected.to disable_sql_request(query) }
22
+ its(:inversion) { is_expected.not_to change_schema }
23
+ end
24
+
25
+ context "without columns" do
26
+ let(:migration) do
27
+ <<~RUBY
28
+ create_composite_type "paint.nothing" do |t|
29
+ t.comment "2D point with a color"
30
+ end
31
+ RUBY
32
+ end
33
+ let(:query) { "SELECT NULL::paint.nothing" }
34
+
35
+ its(:execution) { is_expected.to enable_sql_request(query) }
36
+ its(:execution) { is_expected.to insert(migration).into_schema }
37
+ its(:inversion) { is_expected.to disable_sql_request(query) }
38
+ its(:inversion) { is_expected.not_to change_schema }
39
+ end
40
+
41
+ context "without name" do
42
+ let(:migration) do
43
+ <<~RUBY
44
+ create_composite_type do |t|
45
+ t.column "x", "integer"
46
+ t.column "y", "integer"
47
+ t.column "color", "text", collation: "en_US"
48
+ t.comment "2D point with a color"
49
+ end
50
+ RUBY
51
+ end
52
+
53
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
54
+ end
55
+ end
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#drop_composite_type" do
4
+ before_all { run_migration "create_schema :paint" }
5
+ before { run_migration(snippet) }
6
+
7
+ let(:snippet) do
8
+ <<~RUBY
9
+ create_composite_type "paint.colored_point" do |t|
10
+ t.column "x", "integer"
11
+ t.column "y", "integer"
12
+ t.column "color", "text", collation: "en_US"
13
+ t.comment "2D point with a color"
14
+ end
15
+ RUBY
16
+ end
17
+ let(:query) { "SELECT (10, -1, 'blue')::paint.colored_point" }
18
+
19
+ context "with a full definition" do
20
+ let(:migration) do
21
+ <<~RUBY
22
+ drop_composite_type "paint.colored_point" do |t|
23
+ t.column "x", "integer"
24
+ t.column "y", "integer"
25
+ t.column "color", "text", collation: "en_US"
26
+ t.comment "2D point with a color"
27
+ end
28
+ RUBY
29
+ end
30
+
31
+ its(:execution) { is_expected.to disable_sql_request(query) }
32
+ its(:execution) { is_expected.to remove(snippet).from_schema }
33
+
34
+ its(:inversion) { is_expected.to enable_sql_request(query) }
35
+ its(:inversion) { is_expected.not_to change_schema }
36
+ end
37
+
38
+ context "with a qualified name only" do
39
+ let(:migration) do
40
+ <<~RUBY
41
+ drop_composite_type "paint.colored_point"
42
+ RUBY
43
+ end
44
+ let(:new_snippet) do
45
+ <<~RUBY
46
+ create_composite_type "paint.colored_point"
47
+ RUBY
48
+ end
49
+
50
+ its(:execution) { is_expected.to disable_sql_request(query) }
51
+ its(:execution) { is_expected.to remove(snippet).from_schema }
52
+
53
+ its(:inversion) { is_expected.to remove(snippet).from_schema }
54
+ its(:inversion) { is_expected.to insert(new_snippet).into_schema }
55
+ end
56
+
57
+ context "when the type is used" do
58
+ before do
59
+ run_migration <<~RUBY
60
+ # The function depends on the composite type
61
+ create_function "make_red(p paint.colored_point) paint.colored_point",
62
+ body: "SELECT (p.x, p.y, 'red')::paint.colored_point"
63
+ RUBY
64
+ end
65
+
66
+ context "without the `force` option" do
67
+ let(:migration) do
68
+ <<~RUBY
69
+ drop_composite_type "paint.colored_point"
70
+ RUBY
71
+ end
72
+
73
+ its(:execution) { is_expected.to raise_error(StandardError) }
74
+ end
75
+
76
+ context "with the `force: :cascade` option" do
77
+ let(:migration) do
78
+ <<~RUBY
79
+ drop_composite_type "paint.colored_point", force: :cascade
80
+ RUBY
81
+ end
82
+
83
+ its(:execution) { is_expected.to disable_sql_request(query) }
84
+ its(:execution) { is_expected.to remove(snippet).from_schema }
85
+ it { is_expected.to be_irreversible.because_of(/force: :cascade/i) }
86
+ end
87
+ end
88
+
89
+ context "when the type is absent without the `if_exists` option" do
90
+ let(:migration) do
91
+ <<~RUBY
92
+ drop_composite_type "foo"
93
+ RUBY
94
+ end
95
+
96
+ its(:execution) { is_expected.to raise_error(StandardError) }
97
+ end
98
+
99
+ context "when the type is absent with the `if_exists: true` option" do
100
+ let(:migration) do
101
+ <<~RUBY
102
+ drop_composite_type "foo", if_exists: true
103
+ RUBY
104
+ end
105
+
106
+ its(:execution) { is_expected.not_to change_schema }
107
+ it { is_expected.to be_irreversible.because_of(/if_exists: true/i) }
108
+ end
109
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#rename_composite_type" do
4
+ before_all { run_migration "create_schema :paint" }
5
+ before { run_migration(old_snippet) }
6
+
7
+ let(:old_snippet) do
8
+ <<~RUBY
9
+ create_composite_type "paint.colored_point" do |t|
10
+ t.column "x", "integer"
11
+ t.column "y", "integer"
12
+ t.column "color", "text", collation: "en_US"
13
+ end
14
+ RUBY
15
+ end
16
+ let(:old_query) { "SELECT (1, 1, 'black')::paint.colored_point;" }
17
+
18
+ context "with new name and schema" do
19
+ let(:migration) do
20
+ <<~RUBY
21
+ rename_enum "paint.colored_point", to: "cpoint"
22
+ RUBY
23
+ end
24
+ let(:new_snippet) do
25
+ <<~RUBY
26
+ create_composite_type "cpoint" do |t|
27
+ t.column "x", "integer"
28
+ t.column "y", "integer"
29
+ t.column "color", "text", collation: "en_US"
30
+ end
31
+ RUBY
32
+ end
33
+ let(:new_query) { "SELECT (1, 1, 'black')::cpoint;" }
34
+
35
+ its(:execution) { is_expected.to enable_sql_request(new_query) }
36
+ its(:execution) { is_expected.to disable_sql_request(old_query) }
37
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
38
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
39
+
40
+ its(:inversion) { is_expected.to disable_sql_request(new_query) }
41
+ its(:inversion) { is_expected.to enable_sql_request(old_query) }
42
+ its(:inversion) { is_expected.not_to change_schema }
43
+ end
44
+
45
+ context "with the same name and schema" do
46
+ let(:migration) do
47
+ <<~RUBY
48
+ rename_enum "paint.colored_point", to: "paint.colored_point"
49
+ RUBY
50
+ end
51
+
52
+ it { is_expected.to fail_validation.because(/new name must be different/i) }
53
+ end
54
+
55
+ context "without new schema/name" do
56
+ let(:migration) do
57
+ <<~RUBY
58
+ rename_composite_type "paint.colored_point"
59
+ RUBY
60
+ end
61
+
62
+ it { is_expected.to fail_validation.because(/new name can't be blank/i) }
63
+ end
64
+
65
+ context "without name" do
66
+ let(:migration) do
67
+ <<~RUBY
68
+ rename_composite_type to: "cpoint"
69
+ RUBY
70
+ end
71
+
72
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
73
+ end
74
+ end
@@ -0,0 +1,177 @@
1
+ # frozen_string_literal: true
2
+
3
+ # There is a couple tests just to check the order of objects creation
4
+ # in the resulting database schema.
5
+ describe ActiveRecord::Migration do
6
+ context "with a table -> function -> check_constraint dependency" do
7
+ let(:migration) do
8
+ <<~RUBY
9
+ create_table "users", force: :cascade do |t|
10
+ t.string "first_name"
11
+ t.string "last_name"
12
+ end
13
+
14
+ create_function "full_name(u users) text" do |f|
15
+ f.volatility :immutable
16
+ f.strict true
17
+ f.parallel :safe
18
+ f.body <<~Q.chomp
19
+ SELECT (
20
+ CASE WHEN u.first_name IS NULL THEN '' ELSE u.first_name || ' ' END
21
+ ) || COALESCE(u.last_name, '')
22
+ Q
23
+ end
24
+
25
+ add_check_constraint "users", "length(users.full_name) > 0"
26
+
27
+ # no dependencies here, that's why the table must be moved up
28
+ # before users (in alphabetical order)
29
+ create_table "colors" do |t|
30
+ t.string "name"
31
+ t.string "code"
32
+ end
33
+ RUBY
34
+ end
35
+ let(:snippet) do
36
+ <<~RUBY
37
+ create_table "colors", force: :cascade do |t|
38
+ t.string "name"
39
+ t.string "code"
40
+ end
41
+
42
+ create_table "users", force: :cascade do |t|
43
+ t.string "first_name"
44
+ t.string "last_name"
45
+ end
46
+
47
+ create_function "full_name(u users) text" do |f|
48
+ f.volatility :immutable
49
+ f.strict true
50
+ f.parallel :safe
51
+ f.body <<~Q.chomp
52
+ SELECT (
53
+ CASE WHEN u.first_name IS NULL THEN '' ELSE u.first_name || ' ' END
54
+ ) || COALESCE(u.last_name, '')
55
+ Q
56
+ end
57
+
58
+ add_check_constraint "users", "length(full_name(users.*)) > 0"
59
+ RUBY
60
+ end
61
+
62
+ its(:execution) { is_expected.to insert(snippet).into_schema }
63
+ end
64
+
65
+ context "with a table -> function -> index dependency" do
66
+ let(:migration) do
67
+ <<~RUBY
68
+ create_table "users", force: :cascade do |t|
69
+ t.string "first_name"
70
+ t.string "last_name"
71
+ end
72
+
73
+ create_function "full_name(u users) text" do |f|
74
+ f.volatility :immutable
75
+ f.strict true
76
+ f.parallel :safe
77
+ f.body <<~Q.chomp
78
+ SELECT (
79
+ CASE WHEN u.first_name IS NULL THEN '' ELSE u.first_name || ' ' END
80
+ ) || COALESCE(u.last_name, '')
81
+ Q
82
+ end
83
+
84
+ add_index "users", "full_name(users.*)", name: "users_full_name_idx"
85
+ RUBY
86
+ end
87
+
88
+ its(:execution) { is_expected.to insert(migration).into_schema }
89
+ end
90
+
91
+ context "with a composite -> domain -> function dependency", since_version: 12 do
92
+ let(:migration) do
93
+ <<~RUBY
94
+ create_composite_type "color_point" do |t|
95
+ t.column "x", "integer"
96
+ t.column "y", "integer"
97
+ t.column "color", "text"
98
+ end
99
+
100
+ create_domain "rb_point", as: "color_point" do |d|
101
+ d.constraint <<~Q.chomp, name: "valid_color"
102
+ (VALUE).color = 'red'::text OR (VALUE).color = 'blue'::text
103
+ Q
104
+ d.constraint "(VALUE).x IS NOT NULL", name: "valid_x"
105
+ d.constraint "(VALUE).y IS NOT NULL", name: "valid_y"
106
+ end
107
+
108
+ create_function "distance(a rb_point, b rb_point) double precision" do |f|
109
+ f.body <<~Q.chomp
110
+ SELECT |/ (((b.x) - (a.x)) ^ 2 + ((b.y) - (a.y)) ^ 2 +
111
+ (CASE WHEN b.color != a.color THEN 1 ELSE 0 END))
112
+ Q
113
+ f.comment <<~Q.chomp
114
+ Apply the Pythagorean theorem adding 1 as a distance between colors
115
+ Q
116
+ end
117
+ RUBY
118
+ end
119
+
120
+ its(:execution) { is_expected.to insert(migration).into_schema }
121
+ end
122
+
123
+ context "with a enum -> table dependency", since_version: 12 do
124
+ let(:migration) do
125
+ <<~RUBY
126
+ create_enum "currency" do |e|
127
+ e.values "CFR", "EUR", "JPY", "USD"
128
+ end
129
+
130
+ create_table "transactions", force: :cascade do |t|
131
+ t.text "sender"
132
+ t.text "receiver"
133
+ t.column "currency", "currency"
134
+ end
135
+ RUBY
136
+ end
137
+
138
+ its(:execution) { is_expected.to insert(migration).into_schema }
139
+ end
140
+
141
+ context "with a domain -> table dependency", since_version: 12 do
142
+ let(:migration) do
143
+ <<~RUBY
144
+ create_domain "currency", as: "text" do |d|
145
+ d.constraint "VALUE ~ '^[A-Z]{3}$'::text", name: "currency_check"
146
+ end
147
+
148
+ create_table "transactions", force: :cascade do |t|
149
+ t.text "sender"
150
+ t.text "receiver"
151
+ t.column "currency", "currency"
152
+ end
153
+ RUBY
154
+ end
155
+
156
+ its(:execution) { is_expected.to insert(migration).into_schema }
157
+ end
158
+
159
+ context "with a composite_type -> table dependency", since_version: 12 do
160
+ let(:migration) do
161
+ <<~RUBY
162
+ create_composite_type "sum" do |t|
163
+ t.column "currency", "text"
164
+ t.column "value", "integer"
165
+ end
166
+
167
+ create_table "transactions", force: :cascade do |t|
168
+ t.text "sender"
169
+ t.text "receiver"
170
+ t.column "summa", "sum"
171
+ end
172
+ RUBY
173
+ end
174
+
175
+ its(:execution) { is_expected.to insert(migration).into_schema }
176
+ end
177
+ end