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,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#change_view" do
4
+ before_all do
5
+ run_migration <<~RUBY
6
+ create_table "users", force: :cascade do |t|
7
+ t.string "name"
8
+ t.boolean "admin"
9
+ end
10
+ RUBY
11
+ end
12
+ before { run_migration(old_snippet) }
13
+
14
+ let(:old_snippet) do
15
+ <<~RUBY
16
+ create_view "admin_users" do |v|
17
+ v.sql_definition <<~Q.chomp
18
+ SELECT users.id, users.name
19
+ FROM users
20
+ WHERE users.admin
21
+ Q
22
+ v.check :local
23
+ v.comment "Old comment"
24
+ end
25
+ RUBY
26
+ end
27
+ let(:new_snippet) do
28
+ <<~RUBY
29
+ create_view "admin_users" do |v|
30
+ v.sql_definition <<~Q.chomp
31
+ SELECT NULL::bigint AS id, users.name
32
+ FROM users
33
+ WHERE users.admin
34
+ Q
35
+ v.check :cascaded
36
+ v.comment "New comment"
37
+ end
38
+ RUBY
39
+ end
40
+
41
+ context "with explicitly reversible inline changes" do
42
+ let(:migration) do
43
+ <<~RUBY
44
+ change_view "admin_users" do |v|
45
+ v.sql_definition <<~Q.chomp, from: <<~Q.chomp
46
+ -- the column can be nullified but neither deleted nor retyped
47
+ SELECT NULL::bigint AS id, users.name
48
+ FROM users
49
+ WHERE users.admin
50
+ Q
51
+ SELECT users.id, users.name
52
+ FROM users
53
+ WHERE users.admin
54
+ Q
55
+ v.check :cascaded, from: :local
56
+ v.comment "New comment", from: "Old comment"
57
+ end
58
+ RUBY
59
+ end
60
+
61
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
62
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
63
+ its(:inversion) { is_expected.not_to change_schema }
64
+ end
65
+
66
+ context "with explicitly reversible version changes" do
67
+ let(:migration) do
68
+ <<~RUBY
69
+ change_view "admin_users" do |v|
70
+ v.version 2, from: 1
71
+ v.check :cascaded, from: :local
72
+ v.comment "New comment", from: "Old comment"
73
+ end
74
+ RUBY
75
+ end
76
+
77
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
78
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
79
+ its(:inversion) { is_expected.not_to change_schema }
80
+ end
81
+
82
+ context "with irreversible changes" do
83
+ let(:migration) do
84
+ <<~RUBY
85
+ change_view "admin_users" do |v|
86
+ v.version 2
87
+ v.check :cascaded
88
+ v.comment "New comment"
89
+ end
90
+ RUBY
91
+ end
92
+
93
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
94
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
95
+ it { is_expected.to be_irreversible.because_of(/version|check|comment/i) }
96
+ end
97
+
98
+ context "with no changes" do
99
+ let(:migration) do
100
+ <<~RUBY
101
+ change_view "admin_users"
102
+ RUBY
103
+ end
104
+
105
+ it { is_expected.to fail_validation.because(/changes can't be blank/i) }
106
+ end
107
+
108
+ context "when the view is absent" do
109
+ let(:migration) do
110
+ <<~RUBY
111
+ change_view "unknown" do |v|
112
+ v.comment "New comment"
113
+ end
114
+ RUBY
115
+ end
116
+
117
+ context "without the `if_exists` option" do
118
+ its(:execution) { is_expected.to raise_error(StandardError) }
119
+ end
120
+
121
+ context "with the `if_exists: true` option" do
122
+ let(:migration) do
123
+ <<~RUBY
124
+ change_view "unknown(integer)", if_exists: true do |v|
125
+ v.comment "New comment"
126
+ end
127
+ RUBY
128
+ end
129
+
130
+ its(:execution) { is_expected.not_to change_schema }
131
+ it { is_expected.to be_irreversible.because_of(/if_exists: true/i) }
132
+ end
133
+ end
134
+
135
+ context "without view name" do
136
+ let(:migration) do
137
+ <<~RUBY
138
+ change_view check: :cascaded
139
+ RUBY
140
+ end
141
+
142
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
143
+ end
144
+ end
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#create_view" do
4
+ before_all do
5
+ run_migration <<~RUBY
6
+ create_table "users", force: :cascade do |t|
7
+ t.string "name"
8
+ t.boolean "admin"
9
+ end
10
+ RUBY
11
+ end
12
+
13
+ # from /spec/dummy/db/views/admin_users_v01.sql
14
+ let(:snippet) do
15
+ <<~RUBY
16
+ create_view "admin_users" do |v|
17
+ v.sql_definition <<~Q.chomp
18
+ SELECT users.id, users.name
19
+ FROM users
20
+ WHERE users.admin
21
+ Q
22
+ end
23
+ RUBY
24
+ end
25
+
26
+ context "when a view was absent" do
27
+ let(:migration) do
28
+ <<~RUBY
29
+ create_view "admin_users" do |v|
30
+ v.sql_definition <<~Q.chomp
31
+ SELECT users.id, users.name
32
+ FROM users
33
+ WHERE users.admin
34
+ Q
35
+ v.check :local
36
+ v.comment "Admin users only"
37
+ end
38
+ RUBY
39
+ end
40
+ let(:query) { "SELECT * FROM admin_users;" }
41
+
42
+ its(:execution) { is_expected.to enable_sql_request(query) }
43
+ its(:execution) { is_expected.to insert(migration).into_schema }
44
+
45
+ its(:inversion) { is_expected.to disable_sql_request(query) }
46
+ its(:inversion) { is_expected.not_to change_schema }
47
+ end
48
+
49
+ context "when a view was present" do
50
+ before do
51
+ run_migration <<~RUBY
52
+ create_view "admin_users", sql_definition: "SELECT id, name FROM users"
53
+ RUBY
54
+ end
55
+
56
+ context "without the `replace_existing` option" do
57
+ let(:migration) do
58
+ <<~RUBY
59
+ create_view "admin_users" do |v|
60
+ v.sql_definition <<~Q.chomp
61
+ SELECT users.id, users.name
62
+ FROM users
63
+ WHERE users.admin
64
+ Q
65
+ v.check :local
66
+ v.comment "Admin users only"
67
+ end
68
+ RUBY
69
+ end
70
+
71
+ its(:execution) { is_expected.to raise_error(StandardError) }
72
+ end
73
+
74
+ context "with the `replace_existing: true` option" do
75
+ let(:migration) do
76
+ <<~RUBY
77
+ create_view "admin_users", replace_existing: true do |v|
78
+ v.sql_definition <<~Q.chomp
79
+ SELECT users.id, users.name
80
+ FROM users
81
+ WHERE users.admin
82
+ Q
83
+ end
84
+ RUBY
85
+ end
86
+
87
+ its(:execution) { is_expected.to insert(snippet).into_schema }
88
+ it { is_expected.to be_irreversible.because_of(/replace_existing: true/i) }
89
+ end
90
+ end
91
+
92
+ context "without sql definition" do
93
+ context "with an existing version" do
94
+ let(:migration) do
95
+ <<~RUBY
96
+ create_view "admin_users", version: 1
97
+ RUBY
98
+ end
99
+
100
+ its(:execution) { is_expected.to insert(snippet).into_schema }
101
+ its(:inversion) { is_expected.not_to change_schema }
102
+ end
103
+
104
+ context "with an absent version" do
105
+ let(:migration) do
106
+ <<~RUBY
107
+ create_view "admin_users", version: 99
108
+ RUBY
109
+ end
110
+
111
+ its(:execution) { is_expected.to raise_error(StandardError) }
112
+ end
113
+
114
+ context "without version" do
115
+ let(:migration) do
116
+ <<~RUBY
117
+ create_view "admin_users"
118
+ RUBY
119
+ end
120
+
121
+ it { is_expected.to fail_validation.because(/sql definition can't be blank/i) }
122
+ end
123
+ end
124
+
125
+ context "without a name" do
126
+ let(:migration) do
127
+ <<~RUBY
128
+ create_view sql_definition: "SELECT * FROM users"
129
+ RUBY
130
+ end
131
+
132
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
133
+ end
134
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#drop_view" do
4
+ before_all do
5
+ run_migration <<~RUBY
6
+ create_table "users", force: :cascade do |t|
7
+ t.string "name"
8
+ t.boolean "admin"
9
+ end
10
+
11
+ create_table "weird"
12
+ RUBY
13
+ end
14
+ before { run_migration(snippet) }
15
+
16
+ let(:snippet) do
17
+ <<~RUBY
18
+ create_view "admin_users" do |v|
19
+ v.sql_definition <<~Q.chomp
20
+ SELECT users.id, users.name
21
+ FROM users
22
+ WHERE users.admin
23
+ Q
24
+ v.check :local
25
+ v.comment "Admin users only"
26
+ end
27
+ RUBY
28
+ end
29
+
30
+ context "with a full definition" do
31
+ let(:migration) do
32
+ <<~RUBY
33
+ drop_view "admin_users" do |v|
34
+ v.sql_definition <<~Q.chomp
35
+ SELECT users.id, users.name
36
+ FROM users
37
+ WHERE users.admin
38
+ Q
39
+ v.check :local
40
+ v.comment "Admin users only"
41
+ end
42
+ RUBY
43
+ end
44
+ let(:query) { "SELECT * FROM admin_users;" }
45
+
46
+ its(:execution) { is_expected.to disable_sql_request(query) }
47
+ its(:execution) { is_expected.to remove(snippet).from_schema }
48
+
49
+ its(:inversion) { is_expected.to enable_sql_request(query) }
50
+ its(:inversion) { is_expected.not_to change_schema }
51
+ end
52
+
53
+ context "with a version-based definition" do
54
+ let(:migration) do
55
+ <<~RUBY
56
+ drop_view "admin_users", revert_to_version: 1 do |v|
57
+ v.check :local
58
+ v.comment "Admin users only"
59
+ end
60
+ RUBY
61
+ end
62
+
63
+ its(:execution) { is_expected.to remove(snippet).from_schema }
64
+ its(:inversion) { is_expected.not_to change_schema }
65
+ end
66
+
67
+ context "with a name only" do
68
+ let(:migration) do
69
+ <<~RUBY
70
+ drop_view "admin_users"
71
+ RUBY
72
+ end
73
+
74
+ its(:execution) { is_expected.to remove(snippet).from_schema }
75
+ it { is_expected.to be_irreversible.because(/sql_definition can't be blank/i) }
76
+ end
77
+
78
+ context "when a view was absent" do
79
+ before do
80
+ run_migration <<~RUBY
81
+ drop_view "admin_users"
82
+ RUBY
83
+ end
84
+
85
+ context "without the `if_exists` option" do
86
+ let(:migration) do
87
+ <<~RUBY
88
+ drop_view "admin_users"
89
+ RUBY
90
+ end
91
+
92
+ its(:execution) { is_expected.to raise_error(StandardError) }
93
+ end
94
+
95
+ context "with the `replace_existing: true` option" do
96
+ let(:migration) do
97
+ <<~RUBY
98
+ drop_view "admin_users", if_exists: true
99
+ RUBY
100
+ end
101
+
102
+ its(:execution) { is_expected.not_to change_schema }
103
+ it { is_expected.to be_irreversible.because_of(/if_exists: true/i) }
104
+ end
105
+ end
106
+
107
+ context "when a view was used" do
108
+ before do
109
+ run_migration <<~RUBY
110
+ create_function "do_nothing() admin_users",
111
+ body: "SELECT * FROM admin_users;"
112
+ RUBY
113
+ end
114
+
115
+ context "without the `:force` option" do
116
+ let(:migration) do
117
+ <<~RUBY
118
+ drop_view "admin_users"
119
+ RUBY
120
+ end
121
+
122
+ its(:execution) { is_expected.to raise_error(StandardError) }
123
+ end
124
+
125
+ context "with the `force: :cascade` option" do
126
+ let(:migration) do
127
+ <<~RUBY
128
+ drop_view "admin_users", force: :cascade
129
+ RUBY
130
+ end
131
+
132
+ its(:execution) { is_expected.to remove(snippet).from_schema }
133
+ it { is_expected.to be_irreversible.because_of(/force: :cascade/i) }
134
+ end
135
+ end
136
+
137
+ context "without a name" do
138
+ let(:migration) do
139
+ <<~RUBY
140
+ drop_view sql_definition: "SELECT * FROM users"
141
+ RUBY
142
+ end
143
+
144
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
145
+ end
146
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#rename_view" do
4
+ before_all do
5
+ run_migration <<~RUBY
6
+ create_table :users do |t|
7
+ t.string :name
8
+ t.boolean :admin
9
+ end
10
+ RUBY
11
+ end
12
+ before { run_migration(old_snippet) }
13
+
14
+ let(:old_snippet) do
15
+ <<~RUBY
16
+ create_view "admins" do |v|
17
+ v.sql_definition <<~Q.chomp
18
+ SELECT users.id, users.name
19
+ FROM users
20
+ WHERE users.admin
21
+ Q
22
+ end
23
+ RUBY
24
+ end
25
+ let(:new_snippet) do
26
+ <<~RUBY
27
+ create_view "admin_users" do |v|
28
+ v.sql_definition <<~Q.chomp
29
+ SELECT users.id, users.name
30
+ FROM users
31
+ WHERE users.admin
32
+ Q
33
+ end
34
+ RUBY
35
+ end
36
+ let(:query) { "SELECT * FROM admin_users;" }
37
+
38
+ context "with a new name" do
39
+ let(:migration) do
40
+ <<~RUBY
41
+ rename_view :admins, to: :admin_users
42
+ RUBY
43
+ end
44
+
45
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
46
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
47
+ its(:execution) { is_expected.to enable_sql_request(query) }
48
+
49
+ its(:inversion) { is_expected.to disable_sql_request(query) }
50
+ its(:inversion) { is_expected.not_to change_schema }
51
+ end
52
+
53
+ context "with the same name" do
54
+ let(:migration) do
55
+ <<~RUBY
56
+ rename_view :admin_users, to: "public.admin_users"
57
+ RUBY
58
+ end
59
+
60
+ it { is_expected.to fail_validation.because(/new name must be different/i) }
61
+ end
62
+
63
+ context "when a view was absent" do
64
+ context "without the `if_exists` option" do
65
+ let(:migration) do
66
+ <<~RUBY
67
+ rename_view :administrators, to: :admins
68
+ RUBY
69
+ end
70
+
71
+ its(:execution) { is_expected.to raise_error(StandardError) }
72
+ end
73
+
74
+ context "with the `if_exists: true` option" do
75
+ let(:migration) do
76
+ <<~RUBY
77
+ rename_view :administrators, to: :admins, if_exists: true
78
+ RUBY
79
+ end
80
+
81
+ its(:execution) { is_expected.not_to raise_error }
82
+ it { is_expected.to be_irreversible.because_of(/if_exists: true/i) }
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # noinspection RubyResolve
4
+ require "spec_helper"
5
+
6
+ RSpec.describe PGTrunk::DependenciesResolver do
7
+ subject { described_class.resolve(input).map(&:oid) }
8
+
9
+ let(:input) { (1..10).map { |oid| object.new(oid) }.freeze }
10
+ # For simplicity we presume objects are sorted by oid
11
+ let(:object) do
12
+ Struct.new(:oid) do
13
+ include Comparable
14
+
15
+ def <=>(other)
16
+ oid <=> other.oid
17
+ end
18
+ end
19
+ end
20
+
21
+ before do
22
+ # We mock the method here to avoid polluting the `pg_depend` catalog.
23
+ allow(described_class).to receive(:dependencies).and_return(dependencies)
24
+ end
25
+
26
+ describe "List of independent objects" do
27
+ let(:dependencies) { {} }
28
+ let(:output) { (1..10).to_a }
29
+
30
+ it "restores the natural order" do
31
+ expect(subject).to eq(output)
32
+ end
33
+ end
34
+
35
+ describe "List of dependent objects" do
36
+ let(:dependencies) { { 2 => [7], 3 => [5], 5 => [8] } }
37
+ let(:output) { [1, 7, 2, 8, 5, 3, 4, 6, 9, 10] }
38
+
39
+ it "is sorted so that dependencies are resolved" do
40
+ expect(subject).to eq(output)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ ENV["RAILS_ENV"] = "test"
4
+
5
+ require "pry"
6
+ require "pry-byebug"
7
+ require "database_cleaner/active_record"
8
+ require "pg_trunk"
9
+ require "rspec/its"
10
+ require "test_prof/recipes/rspec/before_all"
11
+
12
+ require File.expand_path("dummy/config/environment", __dir__)
13
+
14
+ # noinspection RubyResolve
15
+ Dir["spec/support/**/*.rb"].sort.each { |file| load file }
16
+
17
+ RSpec.configure do |config|
18
+ config.example_status_persistence_file_path = ".rspec_status"
19
+ config.order = "random"
20
+ config.expect_with :rspec do |c|
21
+ c.syntax = :expect
22
+ end
23
+
24
+ # Filter examples by server version
25
+ server_version = PGTrunk.database.server_version.first(2).to_i
26
+ config.filter_run_excluding(before_version: ->(v) { v <= server_version })
27
+ config.filter_run_excluding(since_version: ->(v) { v > server_version })
28
+ end