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,58 @@
1
+ # frozen_string_literal: false
2
+
3
+ # @!method ActiveRecord::Migration#rename_view(name, **options)
4
+ # Change the name and/or schema of a view
5
+ #
6
+ # @param [#to_s] :name (nil) The qualified name of the view
7
+ # @option [#to_s] :to (nil) The new qualified name for the view
8
+ # @option [Boolean] :if_exists (false) Suppress the error when the view is absent
9
+ #
10
+ # A view can be renamed by changing both the name
11
+ # and the schema (namespace) it belongs to.
12
+ #
13
+ # rename_view "views.admin_users", to: "admins"
14
+ #
15
+ # With the `if_exists: true` option, the operation won't fail
16
+ # even when the view wasn't existed.
17
+ #
18
+ # rename_view "views.admin_users", to: "admins", if_exists: true
19
+ #
20
+ # At the same time, the option makes a view irreversible
21
+ # due to uncertainty of the previous state of the database.
22
+
23
+ module PGTrunk::Operations::Views
24
+ # @private
25
+ class RenameView < Base
26
+ validates :new_name, presence: true
27
+ validates :replace_existing, :sql_definition, :check, :force, :version,
28
+ absence: true
29
+
30
+ def to_sql(_version)
31
+ [*change_schema, *change_name].join("; ")
32
+ end
33
+
34
+ def invert
35
+ irreversible!("if_exists: true") if if_exists
36
+ self.class.new(**to_h, name: new_name, to: name)
37
+ end
38
+
39
+ private
40
+
41
+ def change_schema
42
+ return if name.schema == new_name.schema
43
+
44
+ sql = "ALTER VIEW"
45
+ sql << " IF EXISTS" if if_exists
46
+ sql << " #{name.to_sql} SET SCHEMA #{new_name.schema.inspect};"
47
+ end
48
+
49
+ def change_name
50
+ return if new_name.name == name.name
51
+
52
+ moved = name.merge(schema: new_name.schema)
53
+ sql = "ALTER VIEW"
54
+ sql << " IF EXISTS" if if_exists
55
+ sql << " #{moved.to_sql} RENAME TO #{new_name.name.inspect};"
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # nodoc
4
+ module PGTrunk::Operations
5
+ # @private
6
+ # Namespace for operations with views
7
+ module Views
8
+ require_relative "views/base"
9
+ require_relative "views/change_view"
10
+ require_relative "views/create_view"
11
+ require_relative "views/drop_view"
12
+ require_relative "views/rename_view"
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ # @private
4
+ module PGTrunk
5
+ # Namespace for creator definitions and their fetchers
6
+ module Operations
7
+ # The order of requirements is essential:
8
+ # in this order independent objects will be dumped to the schema.
9
+ require_relative "operations/enums"
10
+ require_relative "operations/composite_types"
11
+ require_relative "operations/domains"
12
+ require_relative "operations/tables"
13
+ require_relative "operations/views"
14
+ require_relative "operations/materialized_views"
15
+ require_relative "operations/functions"
16
+ require_relative "operations/indexes"
17
+ require_relative "operations/check_constraints"
18
+ require_relative "operations/foreign_keys"
19
+ require_relative "operations/procedures"
20
+ require_relative "operations/triggers"
21
+ require_relative "operations/statistics"
22
+ end
23
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PGTrunk
4
+ # @private
5
+ VERSION = "0.1.0"
6
+ end
data/lib/pg_trunk.rb ADDED
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+ require "active_record/connection_adapters/postgresql_adapter"
5
+ require "active_record/migration"
6
+ require "pg"
7
+ require "rails/railtie"
8
+
9
+ # @private
10
+ # PGTrunk adds methods to `ActiveRecord::Migration`
11
+ # to create and manage PostgreSQL objects
12
+ # in Rails applications.
13
+ module PGTrunk
14
+ require_relative "pg_trunk/version"
15
+ require_relative "pg_trunk/core"
16
+ require_relative "pg_trunk/operations"
17
+
18
+ # @private
19
+ def self.database
20
+ @database ||= Adapters::Postgres.new
21
+ end
22
+
23
+ # @private
24
+ def self.dumper
25
+ @dumper ||= database.dumper
26
+ end
27
+ end
data/pg_trunk.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path("lib", __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require "pg_trunk/version"
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "pg_trunk"
9
+ spec.version = PGTrunk::VERSION
10
+ spec.authors = ["Andrew Kozin"]
11
+ spec.email = ["andrew.kozin@gmail.com"]
12
+
13
+ spec.summary = "Empower PostgreSQL migrations in Rails app"
14
+ spec.description = <<-DESCRIPTION
15
+ Adds methods to ActiveRecord::Migration to create and manage PostgreSQL objects in Rails
16
+ DESCRIPTION
17
+ spec.homepage = "https://github.com/nepalez/pg_trunk"
18
+ spec.license = "MIT"
19
+
20
+ spec.metadata["homepage_uri"] = spec.homepage
21
+ spec.metadata["source_code_uri"] = spec.homepage
22
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGELOG.md"
23
+ spec.metadata["rubygems_mfa_required"] = "true"
24
+
25
+ spec.files = `git ls-files -z`.split("\x0")
26
+ spec.test_files = spec.files.grep(%r{^spec/})
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_dependency "activerecord", ">= 4.0.0"
30
+ spec.add_dependency "pg"
31
+ spec.add_dependency "railties", ">= 4.0.0"
32
+
33
+ spec.required_ruby_version = ">= 2.7.0"
34
+ end
@@ -0,0 +1,16 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore the default SQLite database.
11
+ /db/*.sqlite3
12
+ /db/*.sqlite3-journal
13
+
14
+ # Ignore all logfiles and tempfiles.
15
+ /log/*.log
16
+ /tmp
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
4
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
5
+
6
+ require File.expand_path("config/application", __dir__)
7
+
8
+ Rails.application.load_tasks
9
+
10
+ unless Rake::Task.task_defined?("db:environment:set")
11
+ desc "dummy task for rails versions where this task does not exist"
12
+ task "db:environment:set" do
13
+ # no op
14
+ end
15
+ end
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
5
+ # noinspection RubyResolve
6
+ load Gem.bin_path("bundler", "bundle")
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ APP_PATH = File.expand_path("../config/application", __dir__)
5
+ require_relative "../config/boot"
6
+ require "rails/commands"
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../config/boot"
5
+ require "rake"
6
+ Rake.application.run
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path("boot", __dir__)
4
+
5
+ # Pick the frameworks you want:
6
+ require "active_record/railtie"
7
+
8
+ # noinspection RubyResolve
9
+ Bundler.require(*Rails.groups)
10
+
11
+ module Dummy
12
+ # noinspection RubyResolve
13
+ class Application < Rails::Application
14
+ config.cache_classes = true
15
+ config.eager_load = false
16
+ config.active_support.deprecation = :stderr
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Set up gems listed in the Gemfile.
4
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__)
5
+
6
+ require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
7
+ $LOAD_PATH.unshift File.expand_path("../../../lib", __dir__)
@@ -0,0 +1,14 @@
1
+ development: &default
2
+ adapter: postgresql
3
+ database: pg_trunk_dummy_dev
4
+ encoding: unicode
5
+ pool: 5
6
+ <% if ENV.fetch("GITHUB_ACTIONS", false) %>
7
+ host: localhost
8
+ username: <%= ENV.fetch("POSTGRES_USER") %>
9
+ password: <%= ENV.fetch("POSTGRES_PASSWORD") %>
10
+ <% end %>
11
+
12
+ test:
13
+ <<: *default
14
+ database: pg_trunk_dummy_test
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Load the Rails application.
4
+ require File.expand_path("application", __dir__)
5
+
6
+ # Initialize the Rails application.
7
+ Rails.application.initialize!
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is used by Rack-based servers to start the application.
4
+
5
+ require ::File.expand_path("config/environment", __dir__)
6
+ run Rails.application
@@ -0,0 +1 @@
1
+ SELECT id, name FROM users WHERE admin;
File without changes
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is auto-generated from the current state of the database. Instead
4
+ # of editing this file, please use the operations feature of Active Record to
5
+ # incrementally modify your database, and then regenerate this schema definition.
6
+ #
7
+ # This file is the source Rails uses to define your schema when running `bin/rails
8
+ # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
9
+ # be faster and is potentially less error prone than running all of your
10
+ # operations from scratch. Old operations may fail to apply correctly if those
11
+ # operations use external dependencies or application code.
12
+ #
13
+ # It's strongly recommended that you check this file into your version control system.
14
+
15
+ ActiveRecord::Schema.define(version: 0) do
16
+ # These are extensions that must be enabled in order to support this database
17
+ enable_extension "plpgsql"
18
+ end
@@ -0,0 +1 @@
1
+ SELECT id, name FROM users WHERE admin;
@@ -0,0 +1 @@
1
+ SELECT NULL::bigint AS id, name FROM users WHERE admin;
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#add_check_constraint" do
4
+ before_all do
5
+ run_migration <<~RUBY
6
+ create_table "users" do |t|
7
+ t.string :name
8
+ end
9
+ RUBY
10
+ end
11
+
12
+ let(:valid_query) { "INSERT INTO users (name) VALUES ('xx');" }
13
+ # breaks the constraint 'length(name) > 1'
14
+ let(:invalid_query) { "INSERT INTO users (name) VALUES ('x');" }
15
+
16
+ context "when added separately" do
17
+ let(:migration) do
18
+ <<~RUBY.squish
19
+ add_check_constraint "users", "length((name)::text) > 1"
20
+ RUBY
21
+ end
22
+
23
+ its(:execution) { is_expected.to enable_sql_request(valid_query) }
24
+ its(:execution) { is_expected.to disable_sql_request(invalid_query) }
25
+ its(:execution) { is_expected.to insert(migration).into_schema }
26
+
27
+ its(:inversion) { is_expected.to enable_sql_request(valid_query) }
28
+ its(:inversion) { is_expected.not_to change_schema }
29
+ end
30
+
31
+ context "when added inside the table definition" do
32
+ let(:migration) do
33
+ <<~RUBY
34
+ change_table "users" do |t|
35
+ t.check_constraint "length(name) > 1"
36
+ end
37
+ RUBY
38
+ end
39
+ let(:snippet) do
40
+ <<~RUBY.squish
41
+ add_check_constraint "users", "length((name)::text) > 1"
42
+ RUBY
43
+ end
44
+
45
+ its(:execution) { is_expected.to disable_sql_request(invalid_query) }
46
+ its(:execution) { is_expected.to enable_sql_request(valid_query) }
47
+ its(:execution) { is_expected.to insert(snippet).into_schema }
48
+
49
+ its(:inversion) { is_expected.not_to change_schema }
50
+ end
51
+
52
+ context "with a comment" do
53
+ let(:migration) do
54
+ <<~RUBY.squish
55
+ add_check_constraint "users", "length((name)::text) > 1",
56
+ comment: "Name is long enough"
57
+ RUBY
58
+ end
59
+
60
+ its(:execution) { is_expected.to insert(migration).into_schema }
61
+ its(:inversion) { is_expected.not_to change_schema }
62
+ end
63
+
64
+ context "with an explicit name" do
65
+ let(:migration) do
66
+ <<~RUBY.squish
67
+ add_check_constraint "users", "length((name)::text) > 1",
68
+ name: "name_is_long_enough"
69
+ RUBY
70
+ end
71
+
72
+ its(:execution) { is_expected.to insert(migration).into_schema }
73
+ its(:inversion) { is_expected.not_to change_schema }
74
+ end
75
+
76
+ context "without an expression" do
77
+ let(:migration) do
78
+ <<~RUBY
79
+ add_check_constraint "users"
80
+ RUBY
81
+ end
82
+
83
+ it { is_expected.to fail_validation.because(/expression can't be blank/i) }
84
+ end
85
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#drop_check_constraint" do
4
+ before_all do
5
+ run_migration <<~RUBY
6
+ create_table "users" do |t|
7
+ t.string :name
8
+ end
9
+ RUBY
10
+ end
11
+
12
+ # satisfy the constraint 'length(name) > 1'
13
+ let(:valid_query) { "INSERT INTO users (name) VALUES ('xx');" }
14
+ # breaks the constraint 'length(name) > 1'
15
+ let(:invalid_query) { "INSERT INTO users (name) VALUES ('x');" }
16
+
17
+ context "when the constraint was named explicitly" do
18
+ before { run_migration(snippet) }
19
+
20
+ let(:snippet) do
21
+ <<~RUBY.squish
22
+ add_check_constraint "users", "length((name)::text) > 1",
23
+ name: "name_is_long_enough"
24
+ RUBY
25
+ end
26
+
27
+ context "with an expression" do
28
+ let(:migration) do
29
+ <<~RUBY.squish
30
+ drop_check_constraint "users", "length((name)::text) > 1",
31
+ name: "name_is_long_enough"
32
+ RUBY
33
+ end
34
+
35
+ its(:execution) { is_expected.to enable_sql_request(invalid_query) }
36
+ its(:execution) { is_expected.to remove(snippet).from_schema }
37
+
38
+ its(:inversion) { is_expected.to disable_sql_request(invalid_query) }
39
+ its(:inversion) { is_expected.to enable_sql_request(valid_query) }
40
+ its(:inversion) { is_expected.not_to change_schema }
41
+ end
42
+
43
+ context "without an expression" do
44
+ let(:migration) do
45
+ <<~RUBY
46
+ drop_check_constraint "users", name: "name_is_long_enough"
47
+ RUBY
48
+ end
49
+
50
+ its(:execution) { is_expected.to enable_sql_request(invalid_query) }
51
+ its(:execution) { is_expected.to remove(snippet).from_schema }
52
+
53
+ it { is_expected.to be_irreversible.because(/expression can't be blank/i) }
54
+ end
55
+
56
+ context "without table name" do
57
+ let(:migration) do
58
+ <<~RUBY
59
+ drop_check_constraint
60
+ RUBY
61
+ end
62
+
63
+ it { is_expected.to fail_validation.because(/table can't be blank/i) }
64
+ end
65
+ end
66
+
67
+ context "when the constraint was anonymous" do
68
+ before { run_migration(snippet) }
69
+
70
+ let(:snippet) do
71
+ <<~RUBY.squish
72
+ add_check_constraint "users", "length((name)::text) > 1"
73
+ RUBY
74
+ end
75
+ let(:migration) do
76
+ <<~RUBY
77
+ drop_check_constraint "users", "length((name)::text) > 1"
78
+ RUBY
79
+ end
80
+
81
+ its(:execution) { is_expected.to enable_sql_request(invalid_query) }
82
+ its(:execution) { is_expected.to remove(snippet).from_schema }
83
+
84
+ its(:inversion) { is_expected.to disable_sql_request(invalid_query) }
85
+ its(:inversion) { is_expected.to enable_sql_request(valid_query) }
86
+ its(:inversion) { is_expected.not_to change_schema }
87
+ end
88
+
89
+ context "when a constraint not existed" do
90
+ context "without the `it_exists: true` option" do
91
+ let(:migration) do
92
+ <<~RUBY
93
+ drop_check_constraint :users, name: "id_positive"
94
+ RUBY
95
+ end
96
+
97
+ its(:execution) { is_expected.to raise_error(StandardError) }
98
+ end
99
+
100
+ context "with the `it_exists: true` option" do
101
+ let(:migration) do
102
+ <<~RUBY
103
+ drop_check_constraint :users, "id_positive", if_exists: true
104
+ RUBY
105
+ end
106
+
107
+ its(:execution) { is_expected.not_to raise_error }
108
+ it { is_expected.to be_irreversible.because_of(/if_exists: true/i) }
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe ActiveRecord::Migration, "#rename_check_constraint" do
4
+ before_all do
5
+ run_migration <<~RUBY
6
+ create_table :users do |t|
7
+ t.string :name
8
+ end
9
+ RUBY
10
+ end
11
+ before { run_migration(old_snippet) }
12
+
13
+ context "when the constraint was anonymous" do
14
+ let(:old_snippet) do
15
+ <<~RUBY.squish
16
+ add_check_constraint "users", "length(name) > 1"
17
+ RUBY
18
+ end
19
+
20
+ context "with a new name" do
21
+ let(:migration) do
22
+ <<~RUBY.squish
23
+ rename_check_constraint "users", "length(name) > 1", to: "my_new_key"
24
+ RUBY
25
+ end
26
+ let(:new_snippet) do
27
+ <<~RUBY.squish
28
+ add_check_constraint "users", "length((name)::text) > 1",
29
+ name: "my_new_key"
30
+ RUBY
31
+ end
32
+
33
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
34
+ its(:reversion) { is_expected.not_to change_schema }
35
+ end
36
+
37
+ context "without a new name" do
38
+ let(:migration) do
39
+ <<~RUBY.squish
40
+ rename_check_constraint "users", "length((name)::text) > 1"
41
+ RUBY
42
+ end
43
+
44
+ it { is_expected.to fail_validation.because(/new name must be different/i) }
45
+ end
46
+ end
47
+
48
+ context "when the constraint was named explicitly" do
49
+ let(:old_snippet) do
50
+ <<~RUBY.squish
51
+ add_check_constraint "users", "length(name) > 1", name: "my_key"
52
+ RUBY
53
+ end
54
+
55
+ context "without new name" do
56
+ let(:migration) do
57
+ <<~RUBY
58
+ rename_check_constraint "users", "length((name)::text) > 1",
59
+ name: "my_key"
60
+ RUBY
61
+ end
62
+ let(:new_snippet) do
63
+ <<~RUBY
64
+ add_check_constraint "users", "length((name)::text) > 1"
65
+ RUBY
66
+ end
67
+
68
+ its(:execution) { is_expected.to insert(new_snippet).into_schema }
69
+ its(:reversion) { is_expected.not_to change_schema }
70
+ end
71
+
72
+ context "with a new name" do
73
+ let(:migration) do
74
+ <<~RUBY
75
+ rename_check_constraint "users", "length((name)::text) > 1",
76
+ name: "my_key",
77
+ to: "new_key"
78
+ RUBY
79
+ end
80
+ let(:snippet) do
81
+ <<~RUBY.squish
82
+ add_check_constraint "users", "length((name)::text) > 1", name: "new_key"
83
+ RUBY
84
+ end
85
+
86
+ its(:execution) { is_expected.to insert(snippet).into_schema }
87
+ its(:reversion) { is_expected.not_to change_schema }
88
+ end
89
+ end
90
+ end