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,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#change_function" do
4
+ before do
5
+ run_migration <<~RUBY
6
+ create_function "mult(a integer, b integer) integer" do |f|
7
+ f.volatility :immutable
8
+ f.body "SELECT a * b"
9
+ end
10
+
11
+ # Overload the function to ensure a proper one is found
12
+ create_function "mult(a int, b int, c int) int",
13
+ body: "SELECT a * b * c"
14
+
15
+ # Use the function to ensure it can be chanded
16
+ # even though it has dependent objects.
17
+ create_table "sums" do |t|
18
+ t.integer :a
19
+ t.integer :b
20
+ t.index "mult(a, b)"
21
+ end
22
+ RUBY
23
+ end
24
+
25
+ let(:old_snippet) do
26
+ <<~RUBY
27
+ create_function "mult(a integer, b integer) integer" do |f|
28
+ f.volatility :immutable
29
+ f.body "SELECT a * b"
30
+ end
31
+ RUBY
32
+ end
33
+
34
+ context "with implicitly reversible changes" do
35
+ let(:migration) do
36
+ <<~RUBY
37
+ change_function "mult(integer, integer)" do |f|
38
+ f.strict true
39
+ f.leakproof true
40
+ f.security :definer
41
+ end
42
+ RUBY
43
+ end
44
+ let(:new_snippet) do
45
+ <<~RUBY
46
+ create_function "mult(a integer, b integer) integer" do |f|
47
+ f.volatility :immutable
48
+ f.leakproof true
49
+ f.strict true
50
+ f.security :definer
51
+ f.body "SELECT a * b"
52
+ end
53
+ RUBY
54
+ end
55
+
56
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
57
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
58
+ its(:inversion) { is_expected.not_to change_schema }
59
+ end
60
+
61
+ context "with explicitly reversible changes" do
62
+ let(:migration) do
63
+ <<~RUBY
64
+ change_function "mult(integer, integer)" do |f|
65
+ f.parallel :safe, from: :unsafe
66
+ f.body <<~Q, from: <<~Q
67
+ SELECT a + b
68
+ Q
69
+ SELECT a * b
70
+ Q
71
+ end
72
+ RUBY
73
+ end
74
+ let(:new_snippet) do
75
+ <<~RUBY
76
+ create_function "mult(a integer, b integer) integer" do |f|
77
+ f.volatility :immutable
78
+ f.parallel :safe
79
+ f.body "SELECT a + b"
80
+ end
81
+ RUBY
82
+ end
83
+
84
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
85
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
86
+ its(:inversion) { is_expected.not_to change_schema }
87
+ end
88
+
89
+ context "with irreversible changes" do
90
+ let(:migration) do
91
+ <<~RUBY
92
+ change_function "mult(integer, integer)" do |f|
93
+ f.strict true
94
+ f.leakproof true
95
+ f.security :definer
96
+ f.parallel :safe
97
+ f.cost 5.0
98
+ end
99
+ RUBY
100
+ end
101
+ let(:new_snippet) do
102
+ <<~RUBY
103
+ create_function "mult(a integer, b integer) integer" do |f|
104
+ f.volatility :immutable
105
+ f.leakproof true
106
+ f.strict true
107
+ f.security :definer
108
+ f.parallel :safe
109
+ f.cost 5.0
110
+ f.body "SELECT a * b"
111
+ end
112
+ RUBY
113
+ end
114
+
115
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
116
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
117
+ it { is_expected.to be_irreversible.because_of(/parallel|cost/i) }
118
+ end
119
+
120
+ context "with no changes" do
121
+ let(:migration) do
122
+ <<~RUBY
123
+ change_function "mult(integer, integer)"
124
+ RUBY
125
+ end
126
+
127
+ it { is_expected.to fail_validation.because(/changes can't be blank/i) }
128
+ end
129
+
130
+ context "when the function is absent" do
131
+ let(:migration) do
132
+ <<~RUBY
133
+ change_function "unknown(integer)" do |f|
134
+ f.comment "New comment"
135
+ end
136
+ RUBY
137
+ end
138
+
139
+ context "without the `if_exists` option" do
140
+ its(:execution) { is_expected.to raise_error(StandardError) }
141
+ end
142
+
143
+ context "with the `if_exists: true` option" do
144
+ let(:migration) do
145
+ <<~RUBY
146
+ change_function "unknown(integer)", if_exists: true do |f|
147
+ f.comment "New comment"
148
+ end
149
+ RUBY
150
+ end
151
+
152
+ its(:execution) { is_expected.not_to change_schema }
153
+ it { is_expected.to be_irreversible.because_of(/if_exists: true/i) }
154
+ end
155
+ end
156
+
157
+ context "without name" do
158
+ let(:migration) do
159
+ <<~RUBY
160
+ change_function strict: true
161
+ RUBY
162
+ end
163
+
164
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
165
+ end
166
+ end
@@ -0,0 +1,192 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#create_function" do
4
+ context "with a minimal definition" do
5
+ let(:migration) do
6
+ <<~RUBY
7
+ create_function "mult(a integer, b integer) integer", body: "SELECT a * b"
8
+ RUBY
9
+ end
10
+ let(:snippet) do
11
+ <<~RUBY
12
+ create_function "mult(a integer, b integer) integer" do |f|
13
+ f.body "SELECT a * b"
14
+ end
15
+ RUBY
16
+ end
17
+ let(:query) { "SELECT mult(1, 3);" }
18
+
19
+ its(:execution) { is_expected.to enable_sql_request(query) }
20
+ its(:execution) { is_expected.to insert(snippet).into_schema }
21
+
22
+ its(:inversion) { is_expected.to disable_sql_request(query) }
23
+ its(:inversion) { is_expected.not_to change_schema }
24
+ end
25
+
26
+ context "with options" do
27
+ let(:migration) do
28
+ <<~RUBY
29
+ create_function "mult(a integer, b integer) integer" do |f|
30
+ f.language "plpgsql"
31
+ f.volatility :immutable
32
+ f.leakproof true
33
+ f.strict true
34
+ f.parallel :safe
35
+ f.cost 1.0
36
+ f.body "BEGIN return a * b; END;"
37
+ f.comment "Multiply 2 values"
38
+ end
39
+ RUBY
40
+ end
41
+
42
+ its(:execution) { is_expected.to insert(migration).into_schema }
43
+ its(:inversion) { is_expected.not_to change_schema }
44
+ end
45
+
46
+ context "when a function existed" do
47
+ before do
48
+ run_migration <<~RUBY
49
+ create_function "mult(a int, b int) int", body: "SELECT 0"
50
+ RUBY
51
+ end
52
+
53
+ context "without replace_existing: true" do
54
+ let(:migration) do
55
+ <<~RUBY
56
+ create_function "mult(a integer, b integer) integer",
57
+ body: "SELECT a * b"
58
+ RUBY
59
+ end
60
+
61
+ its(:execution) { is_expected.to raise_error(StandardError) }
62
+ end
63
+
64
+ context "with replace_existing: true" do
65
+ let(:migration) do
66
+ <<~RUBY
67
+ create_function "mult(a integer, b integer) integer",
68
+ body: "SELECT a * b",
69
+ replace_existing: true
70
+ RUBY
71
+ end
72
+ let(:new_snippet) do
73
+ <<~RUBY
74
+ create_function "mult(a integer, b integer) integer" do |f|
75
+ f.body "SELECT a * b"
76
+ end
77
+ RUBY
78
+ end
79
+ let(:old_snippet) do
80
+ <<~RUBY
81
+ create_function "mult(a integer, b integer) integer" do |f|
82
+ f.body "SELECT 0"
83
+ end
84
+ RUBY
85
+ end
86
+
87
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
88
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
89
+ it { is_expected.to be_irreversible.because_of(/replace_existing: true/i) }
90
+ end
91
+ end
92
+
93
+ context "when a function contains SQL injection" do
94
+ # Running it would inject the SQL code going after $$
95
+ let(:migration) do
96
+ <<~RUBY
97
+ create_function "mult(a integer, b integer) integer" do |f|
98
+ f.body <<~Q
99
+ SELECT a * b$$;DROP TABLE priceless;--
100
+ Q
101
+ end
102
+ RUBY
103
+ end
104
+
105
+ it { is_expected.to fail_validation.because_of(/SQL injection/i) }
106
+ end
107
+
108
+ context "when a function has named $-quotations" do
109
+ # This code is safe because `$greeting$` doesn't closes `$$`
110
+ let(:migration) do
111
+ <<~RUBY
112
+ create_function "greet(name text) text" do |f|
113
+ f.body "SELECT $greeting$Hi $greeting$ || name"
114
+ end
115
+ RUBY
116
+ end
117
+
118
+ its(:execution) { is_expected.to insert(migration).into_schema }
119
+ end
120
+
121
+ context "without returned value" do
122
+ let(:migration) do
123
+ <<~RUBY.squish
124
+ create_function "mult(a integer, b integer, OUT c integer)",
125
+ body: "SELECT a * b"
126
+ RUBY
127
+ end
128
+ let(:snippet) do
129
+ <<~RUBY
130
+ create_function "mult(a integer, b integer, OUT c integer) integer" do |f|
131
+ f.body "SELECT a * b"
132
+ end
133
+ RUBY
134
+ end
135
+ let(:query) { "SELECT mult(1, 3);" }
136
+
137
+ its(:execution) { is_expected.to enable_sql_request(query) }
138
+ its(:execution) { is_expected.to insert(snippet).into_schema }
139
+
140
+ its(:inversion) { is_expected.to disable_sql_request(query) }
141
+ its(:inversion) { is_expected.not_to change_schema }
142
+ end
143
+
144
+ context "without arguments" do
145
+ let(:migration) do
146
+ <<~RUBY
147
+ create_function "set_foo", body: "SET foo = 42"
148
+ RUBY
149
+ end
150
+ let(:snippet) do
151
+ <<~RUBY
152
+ create_function "set_foo() void" do |f|
153
+ f.body "SET foo = 42"
154
+ end
155
+ RUBY
156
+ end
157
+
158
+ its(:execution) { is_expected.to insert(snippet).into_schema }
159
+ end
160
+
161
+ context "without name" do
162
+ let(:migration) do
163
+ <<~RUBY
164
+ create_function body: "INSERT INTO foo(bar) VALUES (1);"
165
+ RUBY
166
+ end
167
+
168
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
169
+ end
170
+
171
+ context "with unknown volatility" do
172
+ let(:migration) do
173
+ <<~RUBY
174
+ create_function "mult(a int, b int) int",
175
+ body: "SELECT a * b",
176
+ volatility: :whatever
177
+ RUBY
178
+ end
179
+
180
+ it { is_expected.to fail_validation.because(/not included in the list/i) }
181
+ end
182
+
183
+ context "without body" do
184
+ let(:migration) do
185
+ <<~RUBY
186
+ create_function "foo () void"
187
+ RUBY
188
+ end
189
+
190
+ it { is_expected.to fail_validation.because(/body can't be blank/i) }
191
+ end
192
+ end
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#drop_function" do
4
+ before { run_migration(snippet) }
5
+
6
+ let(:snippet) do
7
+ <<~RUBY
8
+ create_function "mult(a integer, b integer) integer" do |f|
9
+ f.body "SELECT a * b"
10
+ end
11
+ RUBY
12
+ end
13
+ let(:query) { "SELECT mult(6, 7);" }
14
+
15
+ context "with a function name only" do
16
+ let(:migration) do
17
+ <<~RUBY
18
+ drop_function "mult"
19
+ RUBY
20
+ end
21
+
22
+ its(:execution) { is_expected.to disable_sql_request(query) }
23
+ its(:execution) { is_expected.to remove(snippet).from_schema }
24
+ it { is_expected.to be_irreversible.because(/body can't be blank/i) }
25
+ end
26
+
27
+ context "with a function signature" do
28
+ let(:migration) do
29
+ <<~RUBY
30
+ drop_function "mult (int, int)"
31
+ RUBY
32
+ end
33
+
34
+ its(:execution) { is_expected.to disable_sql_request(query) }
35
+ its(:execution) { is_expected.to remove(snippet).from_schema }
36
+ it { is_expected.to be_irreversible.because(/body can't be blank/i) }
37
+ end
38
+
39
+ context "with a function body" do
40
+ let(:migration) do
41
+ <<~RUBY
42
+ drop_function "mult (a int, b int) int", body: "SELECT a * b"
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
+ its(:inversion) { is_expected.not_to change_schema }
49
+ end
50
+
51
+ context "with additional options" do
52
+ let(:migration) do
53
+ <<~RUBY
54
+ drop_function "mult(a integer, b integer) integer" do |f|
55
+ f.language "plpgsql"
56
+ f.volatility :immutable
57
+ f.leakproof true
58
+ f.strict true
59
+ f.parallel :safe
60
+ f.cost 5.0
61
+ f.body "BEGIN RETURN a * b; END;"
62
+ f.comment "Multiply 2 integers"
63
+ end
64
+ RUBY
65
+ end
66
+ let(:new_snippet) do
67
+ <<~RUBY
68
+ create_function "mult(a integer, b integer) integer" do |f|
69
+ f.language "plpgsql"
70
+ f.volatility :immutable
71
+ f.leakproof true
72
+ f.strict true
73
+ f.parallel :safe
74
+ f.cost 5.0
75
+ f.body "BEGIN RETURN a * b; END;"
76
+ f.comment "Multiply 2 integers"
77
+ end
78
+ RUBY
79
+ end
80
+
81
+ its(:execution) { is_expected.to disable_sql_request(query) }
82
+ its(:execution) { is_expected.to remove(snippet).from_schema }
83
+
84
+ its(:inversion) { is_expected.to enable_sql_request(query) }
85
+ its(:inversion) { is_expected.to insert(new_snippet).into_schema }
86
+ end
87
+
88
+ context "without a name" do
89
+ let(:migration) do
90
+ <<~RUBY
91
+ drop_function
92
+ RUBY
93
+ end
94
+
95
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
96
+ end
97
+
98
+ context "when several functions existed" do
99
+ before do
100
+ run_migration <<~RUBY
101
+ create_function "mult(a int, b int, c int) int",
102
+ body: "SELECT a * b * c"
103
+ RUBY
104
+ end
105
+
106
+ context "with a name only" do
107
+ let(:migration) do
108
+ <<~RUBY
109
+ drop_function "mult"
110
+ RUBY
111
+ end
112
+
113
+ its(:execution) { is_expected.to raise_exception(StandardError) }
114
+ end
115
+
116
+ context "with a signature" do
117
+ let(:migration) do
118
+ <<~RUBY
119
+ drop_function "mult(integer, integer)"
120
+ RUBY
121
+ end
122
+
123
+ its(:execution) { is_expected.to remove(snippet).from_schema }
124
+ end
125
+ end
126
+
127
+ context "when the function was used" do
128
+ before do
129
+ run_migration <<~RUBY
130
+ create_table "foo" do |t|
131
+ t.integer "a"
132
+ t.integer "b"
133
+ t.index "mult(a, b)"
134
+ end
135
+ RUBY
136
+ end
137
+
138
+ context "without the :force option" do
139
+ let(:migration) do
140
+ <<~RUBY
141
+ drop_function "mult"
142
+ RUBY
143
+ end
144
+
145
+ its(:execution) { is_expected.to raise_exception(StandardError) }
146
+ end
147
+
148
+ context "with the force: :cascade option" do
149
+ let(:migration) do
150
+ <<~RUBY
151
+ drop_function "mult", force: :cascade
152
+ RUBY
153
+ end
154
+
155
+ its(:execution) { is_expected.to remove(snippet).from_schema }
156
+ it { is_expected.to be_irreversible.because_of(/force: :cascade/i) }
157
+ end
158
+ end
159
+
160
+ context "when the function was absent" do
161
+ context "without the :if_exists option" do
162
+ let(:migration) do
163
+ <<~RUBY
164
+ drop_function "unknown"
165
+ RUBY
166
+ end
167
+
168
+ its(:execution) { is_expected.to raise_error(StandardError) }
169
+ end
170
+
171
+ context "with the if_exists: true option" do
172
+ let(:migration) do
173
+ <<~RUBY
174
+ drop_function "unknown", if_exists: true
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
+ end
182
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#rename_function" do
4
+ before do
5
+ run_migration <<~RUBY
6
+ create_schema :math
7
+ RUBY
8
+
9
+ run_migration(old_snippet)
10
+ end
11
+
12
+ let(:new_snippet) do
13
+ <<~RUBY
14
+ create_function "math.multiply(a integer, b integer) integer" do |f|
15
+ f.body "SELECT a * b"
16
+ end
17
+ RUBY
18
+ end
19
+ let(:old_snippet) do
20
+ <<~RUBY
21
+ create_function "mult(a integer, b integer) integer" do |f|
22
+ f.body "SELECT a * b"
23
+ end
24
+ RUBY
25
+ end
26
+ let(:old_query) { "SELECT mult(7, 6);" }
27
+ let(:new_query) { "SELECT math.multiply(7, 6);" }
28
+
29
+ context "with new name and schema" do
30
+ let(:migration) do
31
+ <<~RUBY
32
+ rename_function "mult", to: "math.multiply"
33
+ RUBY
34
+ end
35
+
36
+ its(:execution) { is_expected.to enable_sql_request(new_query) }
37
+ its(:execution) { is_expected.to disable_sql_request(old_query) }
38
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
39
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
40
+
41
+ its(:inversion) { is_expected.to disable_sql_request(new_query) }
42
+ its(:inversion) { is_expected.to enable_sql_request(old_query) }
43
+ its(:inversion) { is_expected.not_to change_schema }
44
+ end
45
+
46
+ context "with the same name and schema" do
47
+ let(:migration) do
48
+ <<~RUBY
49
+ rename_function "mult", to: "public.mult"
50
+ RUBY
51
+ end
52
+
53
+ it { is_expected.to fail_validation.because(/new name must be different/i) }
54
+ end
55
+
56
+ context "when several functions exist" do
57
+ before do
58
+ run_migration <<~RUBY
59
+ create_function "mult(a integer, b integer, c integer) integer",
60
+ body: "SELECT a * b * c"
61
+ RUBY
62
+ end
63
+
64
+ context "without a signature" do
65
+ let(:migration) do
66
+ <<~RUBY
67
+ rename_function "mult", to: "math.multiply"
68
+ RUBY
69
+ end
70
+
71
+ its(:execution) { is_expected.to raise_error(StandardError) }
72
+ end
73
+
74
+ context "with a signature" do
75
+ let(:migration) do
76
+ <<~RUBY
77
+ rename_function "mult(int, integer)", to: "math.multiply"
78
+ RUBY
79
+ end
80
+
81
+ its(:execution) { is_expected.to enable_sql_request(new_query) }
82
+ its(:execution) { is_expected.to disable_sql_request(old_query) }
83
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
84
+ its(:execution) { is_expected.to remove(old_snippet).from_schema }
85
+
86
+ its(:inversion) { is_expected.to disable_sql_request(new_query) }
87
+ its(:inversion) { is_expected.to enable_sql_request(old_query) }
88
+ its(:inversion) { is_expected.not_to change_schema }
89
+ end
90
+ end
91
+
92
+ context "without a name" do
93
+ let(:migration) do
94
+ <<~RUBY
95
+ rename_function to: "math.mult"
96
+ RUBY
97
+ end
98
+
99
+ it { is_expected.to fail_validation.because(/name can't be blank/i) }
100
+ end
101
+ end