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.
- checksums.yaml +7 -0
- data/.github/workflows/ci.yml +87 -0
- data/.gitignore +9 -0
- data/.rspec +4 -0
- data/.rubocop.yml +92 -0
- data/.yardopts +4 -0
- data/CHANGELOG.md +31 -0
- data/CONTRIBUTING.md +17 -0
- data/Gemfile +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +141 -0
- data/Rakefile +16 -0
- data/bin/console +8 -0
- data/bin/rake +19 -0
- data/bin/rspec +19 -0
- data/bin/setup +8 -0
- data/bin/yard +19 -0
- data/lib/pg_trunk/core/adapters/postgres.rb +80 -0
- data/lib/pg_trunk/core/dependencies_resolver.rb +101 -0
- data/lib/pg_trunk/core/generators.rb +140 -0
- data/lib/pg_trunk/core/operation/attributes.rb +78 -0
- data/lib/pg_trunk/core/operation/callbacks.rb +40 -0
- data/lib/pg_trunk/core/operation/generators.rb +51 -0
- data/lib/pg_trunk/core/operation/inversion.rb +70 -0
- data/lib/pg_trunk/core/operation/registration.rb +55 -0
- data/lib/pg_trunk/core/operation/ruby_builder.rb +112 -0
- data/lib/pg_trunk/core/operation/ruby_helpers.rb +99 -0
- data/lib/pg_trunk/core/operation/sql_helpers.rb +44 -0
- data/lib/pg_trunk/core/operation/validations.rb +21 -0
- data/lib/pg_trunk/core/operation.rb +78 -0
- data/lib/pg_trunk/core/qualified_name.rb +165 -0
- data/lib/pg_trunk/core/railtie/command_recorder.rb +30 -0
- data/lib/pg_trunk/core/railtie/custom_types.rb +37 -0
- data/lib/pg_trunk/core/railtie/migration.rb +50 -0
- data/lib/pg_trunk/core/railtie/migrator.rb +22 -0
- data/lib/pg_trunk/core/railtie/schema_dumper.rb +75 -0
- data/lib/pg_trunk/core/railtie/schema_migration.rb +22 -0
- data/lib/pg_trunk/core/railtie/statements.rb +21 -0
- data/lib/pg_trunk/core/railtie.rb +35 -0
- data/lib/pg_trunk/core/registry.rb +159 -0
- data/lib/pg_trunk/core/serializers/array_of_hashes_serializer.rb +28 -0
- data/lib/pg_trunk/core/serializers/array_of_strings_serializer.rb +29 -0
- data/lib/pg_trunk/core/serializers/array_of_symbols_serializer.rb +28 -0
- data/lib/pg_trunk/core/serializers/array_serializer.rb +22 -0
- data/lib/pg_trunk/core/serializers/lowercase_string_serializer.rb +21 -0
- data/lib/pg_trunk/core/serializers/multiline_text_serializer.rb +21 -0
- data/lib/pg_trunk/core/serializers/qualified_name_serializer.rb +27 -0
- data/lib/pg_trunk/core/serializers/symbol_serializer.rb +22 -0
- data/lib/pg_trunk/core/serializers.rb +16 -0
- data/lib/pg_trunk/core/validators/all_items_valid_validator.rb +15 -0
- data/lib/pg_trunk/core/validators/difference_validator.rb +19 -0
- data/lib/pg_trunk/core/validators.rb +10 -0
- data/lib/pg_trunk/core.rb +21 -0
- data/lib/pg_trunk/generators.rb +7 -0
- data/lib/pg_trunk/operations/check_constraints/add_check_constraint.rb +109 -0
- data/lib/pg_trunk/operations/check_constraints/base.rb +69 -0
- data/lib/pg_trunk/operations/check_constraints/drop_check_constraint.rb +60 -0
- data/lib/pg_trunk/operations/check_constraints/rename_check_constraint.rb +54 -0
- data/lib/pg_trunk/operations/check_constraints/validate_check_constraint.rb +39 -0
- data/lib/pg_trunk/operations/check_constraints.rb +14 -0
- data/lib/pg_trunk/operations/composite_types/base.rb +61 -0
- data/lib/pg_trunk/operations/composite_types/change_composite_type.rb +136 -0
- data/lib/pg_trunk/operations/composite_types/column.rb +118 -0
- data/lib/pg_trunk/operations/composite_types/create_composite_type.rb +99 -0
- data/lib/pg_trunk/operations/composite_types/drop_composite_type.rb +67 -0
- data/lib/pg_trunk/operations/composite_types/rename_composite_type.rb +44 -0
- data/lib/pg_trunk/operations/composite_types.rb +15 -0
- data/lib/pg_trunk/operations/domains/base.rb +46 -0
- data/lib/pg_trunk/operations/domains/change_domain.rb +140 -0
- data/lib/pg_trunk/operations/domains/constraint.rb +93 -0
- data/lib/pg_trunk/operations/domains/create_domain.rb +124 -0
- data/lib/pg_trunk/operations/domains/drop_domain.rb +65 -0
- data/lib/pg_trunk/operations/domains/rename_domain.rb +44 -0
- data/lib/pg_trunk/operations/domains.rb +15 -0
- data/lib/pg_trunk/operations/enums/base.rb +47 -0
- data/lib/pg_trunk/operations/enums/change.rb +55 -0
- data/lib/pg_trunk/operations/enums/change_enum.rb +119 -0
- data/lib/pg_trunk/operations/enums/create_enum.rb +83 -0
- data/lib/pg_trunk/operations/enums/drop_enum.rb +63 -0
- data/lib/pg_trunk/operations/enums/rename_enum.rb +44 -0
- data/lib/pg_trunk/operations/enums.rb +15 -0
- data/lib/pg_trunk/operations/foreign_keys/add_foreign_key.rb +174 -0
- data/lib/pg_trunk/operations/foreign_keys/base.rb +155 -0
- data/lib/pg_trunk/operations/foreign_keys/drop_foreign_key.rb +76 -0
- data/lib/pg_trunk/operations/foreign_keys/rename_foreign_key.rb +63 -0
- data/lib/pg_trunk/operations/foreign_keys.rb +16 -0
- data/lib/pg_trunk/operations/functions/base.rb +54 -0
- data/lib/pg_trunk/operations/functions/change_function.rb +108 -0
- data/lib/pg_trunk/operations/functions/create_function.rb +198 -0
- data/lib/pg_trunk/operations/functions/drop_function.rb +88 -0
- data/lib/pg_trunk/operations/functions/rename_function.rb +57 -0
- data/lib/pg_trunk/operations/functions.rb +14 -0
- data/lib/pg_trunk/operations/indexes/add_index.rb +68 -0
- data/lib/pg_trunk/operations/indexes.rb +10 -0
- data/lib/pg_trunk/operations/materialized_views/base.rb +79 -0
- data/lib/pg_trunk/operations/materialized_views/change_materialized_view.rb +139 -0
- data/lib/pg_trunk/operations/materialized_views/column.rb +94 -0
- data/lib/pg_trunk/operations/materialized_views/create_materialized_view.rb +170 -0
- data/lib/pg_trunk/operations/materialized_views/drop_materialized_view.rb +70 -0
- data/lib/pg_trunk/operations/materialized_views/refresh_materialized_view.rb +48 -0
- data/lib/pg_trunk/operations/materialized_views/rename_materialized_view.rb +61 -0
- data/lib/pg_trunk/operations/materialized_views.rb +17 -0
- data/lib/pg_trunk/operations/procedures/base.rb +42 -0
- data/lib/pg_trunk/operations/procedures/change_procedure.rb +107 -0
- data/lib/pg_trunk/operations/procedures/create_procedure.rb +146 -0
- data/lib/pg_trunk/operations/procedures/drop_procedure.rb +66 -0
- data/lib/pg_trunk/operations/procedures/rename_procedure.rb +57 -0
- data/lib/pg_trunk/operations/procedures.rb +14 -0
- data/lib/pg_trunk/operations/statistics/base.rb +94 -0
- data/lib/pg_trunk/operations/statistics/create_statistics.rb +181 -0
- data/lib/pg_trunk/operations/statistics/drop_statistics.rb +75 -0
- data/lib/pg_trunk/operations/statistics/rename_statistics.rb +48 -0
- data/lib/pg_trunk/operations/statistics.rb +13 -0
- data/lib/pg_trunk/operations/tables/create_table.rb +75 -0
- data/lib/pg_trunk/operations/tables.rb +10 -0
- data/lib/pg_trunk/operations/triggers/base.rb +119 -0
- data/lib/pg_trunk/operations/triggers/change_trigger.rb +82 -0
- data/lib/pg_trunk/operations/triggers/create_trigger.rb +208 -0
- data/lib/pg_trunk/operations/triggers/drop_trigger.rb +66 -0
- data/lib/pg_trunk/operations/triggers/rename_trigger.rb +71 -0
- data/lib/pg_trunk/operations/triggers.rb +14 -0
- data/lib/pg_trunk/operations/views/base.rb +38 -0
- data/lib/pg_trunk/operations/views/change_view.rb +90 -0
- data/lib/pg_trunk/operations/views/create_view.rb +115 -0
- data/lib/pg_trunk/operations/views/drop_view.rb +69 -0
- data/lib/pg_trunk/operations/views/rename_view.rb +58 -0
- data/lib/pg_trunk/operations/views.rb +14 -0
- data/lib/pg_trunk/operations.rb +23 -0
- data/lib/pg_trunk/version.rb +6 -0
- data/lib/pg_trunk.rb +27 -0
- data/pg_trunk.gemspec +34 -0
- data/spec/dummy/.gitignore +16 -0
- data/spec/dummy/Rakefile +15 -0
- data/spec/dummy/bin/bundle +6 -0
- data/spec/dummy/bin/rails +6 -0
- data/spec/dummy/bin/rake +6 -0
- data/spec/dummy/config/application.rb +18 -0
- data/spec/dummy/config/boot.rb +7 -0
- data/spec/dummy/config/database.yml +14 -0
- data/spec/dummy/config/environment.rb +7 -0
- data/spec/dummy/config.ru +6 -0
- data/spec/dummy/db/materialized_views/admin_users_v01.sql +1 -0
- data/spec/dummy/db/migrate/.keep +0 -0
- data/spec/dummy/db/schema.rb +18 -0
- data/spec/dummy/db/views/admin_users_v01.sql +1 -0
- data/spec/dummy/db/views/admin_users_v02.sql +1 -0
- data/spec/operations/check_constraints/add_check_constraint_spec.rb +85 -0
- data/spec/operations/check_constraints/drop_check_constraint_spec.rb +111 -0
- data/spec/operations/check_constraints/rename_check_constraint_spec.rb +90 -0
- data/spec/operations/composite_types/change_composite_type_spec.rb +257 -0
- data/spec/operations/composite_types/create_composite_type_spec.rb +55 -0
- data/spec/operations/composite_types/drop_composite_type_spec.rb +109 -0
- data/spec/operations/composite_types/rename_composite_type_spec.rb +74 -0
- data/spec/operations/dependency_resolver_spec.rb +177 -0
- data/spec/operations/domains/change_domain_spec.rb +287 -0
- data/spec/operations/domains/create_domain_spec.rb +69 -0
- data/spec/operations/domains/drop_domain_spec.rb +119 -0
- data/spec/operations/domains/rename_domain_spec.rb +70 -0
- data/spec/operations/enums/change_enum_spec.rb +157 -0
- data/spec/operations/enums/create_enum_spec.rb +40 -0
- data/spec/operations/enums/drop_enum_spec.rb +120 -0
- data/spec/operations/enums/rename_enum_spec.rb +72 -0
- data/spec/operations/foreign_keys/add_foreign_key_spec.rb +208 -0
- data/spec/operations/foreign_keys/drop_foreign_key_spec.rb +167 -0
- data/spec/operations/foreign_keys/rename_foreign_key_spec.rb +101 -0
- data/spec/operations/functions/change_function_spec.rb +166 -0
- data/spec/operations/functions/create_function_spec.rb +192 -0
- data/spec/operations/functions/drop_function_spec.rb +182 -0
- data/spec/operations/functions/rename_function_spec.rb +101 -0
- data/spec/operations/indexes/add_index_spec.rb +94 -0
- data/spec/operations/materialized_views/change_materialized_view_spec.rb +190 -0
- data/spec/operations/materialized_views/create_materialized_view_spec.rb +144 -0
- data/spec/operations/materialized_views/drop_materialized_view_spec.rb +145 -0
- data/spec/operations/materialized_views/refresh_materialized_view_spec.rb +79 -0
- data/spec/operations/materialized_views/rename_materialized_view_spec.rb +88 -0
- data/spec/operations/procedures/change_procedure_spec.rb +175 -0
- data/spec/operations/procedures/create_procedure_spec.rb +151 -0
- data/spec/operations/procedures/drop_procedure_spec.rb +159 -0
- data/spec/operations/procedures/rename_procedure_spec.rb +107 -0
- data/spec/operations/statistics/create_statistics_spec.rb +230 -0
- data/spec/operations/statistics/drop_statistics_spec.rb +106 -0
- data/spec/operations/statistics/rename_statistics_spec.rb +129 -0
- data/spec/operations/tables/create_table_spec.rb +53 -0
- data/spec/operations/tables/rename_table_spec.rb +37 -0
- data/spec/operations/triggers/change_trigger_spec.rb +195 -0
- data/spec/operations/triggers/create_trigger_spec.rb +104 -0
- data/spec/operations/triggers/drop_trigger_spec.rb +124 -0
- data/spec/operations/triggers/rename_trigger_spec.rb +160 -0
- data/spec/operations/views/change_view_spec.rb +144 -0
- data/spec/operations/views/create_view_spec.rb +134 -0
- data/spec/operations/views/drop_view_spec.rb +146 -0
- data/spec/operations/views/rename_view_spec.rb +85 -0
- data/spec/pg_trunk/dependencies_resolver_spec.rb +43 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/migrations_helper.rb +376 -0
- metadata +348 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: false
|
|
2
|
+
|
|
3
|
+
# @!method ActiveRecord::Migration#drop_foreign_key(table, reference, **options, &block)
|
|
4
|
+
# Drops a foreign key constraint
|
|
5
|
+
#
|
|
6
|
+
# @param [#to_s] table (nil) The qualified name of the table
|
|
7
|
+
# @param [#to_s] reference (nil) The qualified name of the reference table
|
|
8
|
+
# @option [#to_s] :name (nil) The current name of the foreign key
|
|
9
|
+
# @option [Boolean] :if_exists (false) Suppress the error when the constraint is absent
|
|
10
|
+
# @option [#to_s] :to (nil) The new name for the foreign key
|
|
11
|
+
# @option [Array<#to_s>] :columns ([]) The list of columns of the table
|
|
12
|
+
# @option [#to_s] :column (nil) An alias for :columns for the case of single-column keys
|
|
13
|
+
# @option [Array<#to_s>] :primary_key ([]) The list of columns of the reference table
|
|
14
|
+
# @option [Symbol] :match (:full) Define how to match rows
|
|
15
|
+
# Supported values: :full (default), :partial, :simple
|
|
16
|
+
# @option [Symbol] :on_delete (:restrict)
|
|
17
|
+
# Define how to handle the deletion of the referred row.
|
|
18
|
+
# Supported values: :restrict (default), :cascade, :nullify, :reset
|
|
19
|
+
# @option [Symbol] :on_update (:restrict)
|
|
20
|
+
# Define how to handle the update of the referred row.
|
|
21
|
+
# Supported values: :restrict (default), :cascade, :nullify, :reset
|
|
22
|
+
# @yield [Proc] the block with the key's definition
|
|
23
|
+
# @yieldparam The receiver of methods specifying the foreign key
|
|
24
|
+
#
|
|
25
|
+
# The key can be identified by table/name (not invertible):
|
|
26
|
+
#
|
|
27
|
+
# drop_foreign_key "users", name: "user_roles_fk"
|
|
28
|
+
#
|
|
29
|
+
# To make it invertible use the same options like
|
|
30
|
+
# in the `add_foreign_key` operation.
|
|
31
|
+
#
|
|
32
|
+
# drop_foreign_key do |c|
|
|
33
|
+
# c.table "users"
|
|
34
|
+
# c.reference "roles"
|
|
35
|
+
# c.column "role_id"
|
|
36
|
+
# c.primary_key "id"
|
|
37
|
+
# c.on_update :cascade
|
|
38
|
+
# c.on_delete :cascade
|
|
39
|
+
# c.comment "Phone is 10+ chars long"
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# Notice that the name can be skipped, in this case we would
|
|
43
|
+
# find it in the database.
|
|
44
|
+
#
|
|
45
|
+
# The operation can be called with `if_exists` option.
|
|
46
|
+
#
|
|
47
|
+
# drop_foreign_key "users", name: "user_roles_fk", if_exists: true
|
|
48
|
+
#
|
|
49
|
+
# In this case the operation is always irreversible due to
|
|
50
|
+
# uncertainty of the previous state of the database.
|
|
51
|
+
|
|
52
|
+
module PGTrunk::Operations::ForeignKeys
|
|
53
|
+
# @private
|
|
54
|
+
class DropForeignKey < Base
|
|
55
|
+
# The name can be looked for in the database.
|
|
56
|
+
# This is necessary because the name generated by `rails` inside the table
|
|
57
|
+
# is different from the name generated by the `add_foreign_key`.
|
|
58
|
+
after_initialize { self.name = current_name if name.blank? }
|
|
59
|
+
# This prevents a validation error in case there's no fk found in the db
|
|
60
|
+
# by reference, columns and primary key.
|
|
61
|
+
after_initialize { self.name = generated_name if if_exists }
|
|
62
|
+
|
|
63
|
+
validates :if_not_exists, absence: true
|
|
64
|
+
|
|
65
|
+
def to_sql(_version)
|
|
66
|
+
sql = "ALTER TABLE #{table.to_sql} DROP CONSTRAINT"
|
|
67
|
+
sql << " IF EXISTS" if if_exists
|
|
68
|
+
sql << " #{name.lean.inspect};"
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def invert
|
|
72
|
+
irreversible!("if_exists: true") if if_exists
|
|
73
|
+
AddForeignKey.new(**to_h)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: false
|
|
2
|
+
|
|
3
|
+
# @!method ActiveRecord::Migration#rename_foreign_key(table, reference, **options, &block)
|
|
4
|
+
# Rename a foreign key
|
|
5
|
+
#
|
|
6
|
+
# @param [#to_s] table (nil) The qualified name of the table
|
|
7
|
+
# @param [#to_s] reference (nil) The qualified name of the reference table
|
|
8
|
+
# @option [#to_s] :name (nil) The current name of the foreign key
|
|
9
|
+
# @option [#to_s] :to (nil) The new name for the foreign key
|
|
10
|
+
# @option [Array<#to_s>] :columns ([]) The list of columns of the table
|
|
11
|
+
# @option [#to_s] :column (nil) An alias for :columns for the case of single-column keys
|
|
12
|
+
# @option [Array<#to_s>] :primary_key ([]) The list of columns of the reference table
|
|
13
|
+
# @yield [Proc] the block with the key's definition
|
|
14
|
+
# @yieldparam The receiver of methods specifying the foreign key
|
|
15
|
+
#
|
|
16
|
+
# You can rename the foreign key constraint identified by its explicit name:
|
|
17
|
+
#
|
|
18
|
+
# rename_foreign_key :users,
|
|
19
|
+
# name: "user_roles_fk",
|
|
20
|
+
# to: "constraints.users_by_roles_fk"
|
|
21
|
+
#
|
|
22
|
+
# The key can also be found in the database by table/reference/columns/pk
|
|
23
|
+
#
|
|
24
|
+
# rename_foreign_key :users, :roles, primary_key: "name", to: "user_roles"
|
|
25
|
+
#
|
|
26
|
+
# If a new name is missed, then the name will be reset to the auto-generated one:
|
|
27
|
+
#
|
|
28
|
+
# rename_foreign_key :users, "user_roles_fk"
|
|
29
|
+
#
|
|
30
|
+
# The operation is always reversible.
|
|
31
|
+
|
|
32
|
+
module PGTrunk::Operations::ForeignKeys
|
|
33
|
+
#
|
|
34
|
+
# Definition for the `rename_foreign_key` operation
|
|
35
|
+
#
|
|
36
|
+
class RenameForeignKey < Base
|
|
37
|
+
# The name can be looked for in the database.
|
|
38
|
+
# This is necessary because the name generated by `rails` inside the table
|
|
39
|
+
# is different from the name generated by the `add_foreign_key`.
|
|
40
|
+
after_initialize { self.name = current_name if name.blank? }
|
|
41
|
+
# Reset the name to default when `to:` option is missed or set to `nil`
|
|
42
|
+
after_initialize { self.new_name = generated_name if new_name.blank? }
|
|
43
|
+
|
|
44
|
+
validates :new_name, presence: true
|
|
45
|
+
validates :if_exists, :if_not_exists, :match, absence: true
|
|
46
|
+
|
|
47
|
+
def to_sql(_version)
|
|
48
|
+
<<~SQL.squish
|
|
49
|
+
ALTER TABLE #{table.to_sql}
|
|
50
|
+
RENAME CONSTRAINT #{name.name.inspect}
|
|
51
|
+
TO #{new_name.name.inspect};
|
|
52
|
+
SQL
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def invert
|
|
56
|
+
self.class.new(
|
|
57
|
+
**to_h,
|
|
58
|
+
name: (new_name if custom_name?(new_name)),
|
|
59
|
+
to: (name if name != current_name),
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# nodoc
|
|
4
|
+
module PGTrunk::Operations
|
|
5
|
+
# @private
|
|
6
|
+
# Definitions for foreign keys
|
|
7
|
+
#
|
|
8
|
+
# We overload only add/drop operations to support features
|
|
9
|
+
# like composite keys along with anonymous key deletion.
|
|
10
|
+
module ForeignKeys
|
|
11
|
+
require_relative "foreign_keys/base"
|
|
12
|
+
require_relative "foreign_keys/add_foreign_key"
|
|
13
|
+
require_relative "foreign_keys/drop_foreign_key"
|
|
14
|
+
require_relative "foreign_keys/rename_foreign_key"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: false
|
|
2
|
+
|
|
3
|
+
module PGTrunk::Operations::Functions
|
|
4
|
+
# @abstract
|
|
5
|
+
# @private
|
|
6
|
+
# Base class for operations with functions
|
|
7
|
+
class Base < PGTrunk::Operation
|
|
8
|
+
# All attributes that can be used by function-related commands
|
|
9
|
+
attribute :body, :pg_trunk_multiline_text
|
|
10
|
+
attribute :cost, :float
|
|
11
|
+
attribute :language, :pg_trunk_lowercase_string
|
|
12
|
+
attribute :leakproof, :boolean
|
|
13
|
+
attribute :parallel, :pg_trunk_symbol
|
|
14
|
+
attribute :replace_existing, :boolean
|
|
15
|
+
attribute :rows, :integer
|
|
16
|
+
attribute :security, :pg_trunk_symbol
|
|
17
|
+
attribute :strict, :boolean
|
|
18
|
+
attribute :volatility, :pg_trunk_symbol
|
|
19
|
+
attribute :version, :pg_trunk_multiline_text
|
|
20
|
+
|
|
21
|
+
# Ensure correctness of present values
|
|
22
|
+
validates :if_not_exists, absence: true
|
|
23
|
+
validates :volatility, inclusion: { in: %i[volatile stable immutable] }, allow_nil: true
|
|
24
|
+
validates :security, inclusion: { in: %i[invoker definer] }, allow_nil: true
|
|
25
|
+
validates :parallel, inclusion: { in: %i[safe unsafe] }, allow_nil: true
|
|
26
|
+
validates :cost, numericality: { greater_than: 0 }, allow_nil: true
|
|
27
|
+
validates :rows, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
|
|
28
|
+
validate do
|
|
29
|
+
errors.add :body, "can't contain SQL injection with $$" if body&.include?("$$")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Use comparison by name from pg_trunk operations base class (default)
|
|
33
|
+
# Support name as the only positional argument (default)
|
|
34
|
+
|
|
35
|
+
ruby_snippet do |s|
|
|
36
|
+
s.ruby_param(name.lean) if name.present?
|
|
37
|
+
s.ruby_param(to: new_name.lean) if new_name.present?
|
|
38
|
+
s.ruby_param(if_exists: true) if if_exists
|
|
39
|
+
s.ruby_param(force: :cascade) if force == :cascade
|
|
40
|
+
s.ruby_param(replace_existing: true) if replace_existing
|
|
41
|
+
|
|
42
|
+
s.ruby_line(:language, language) if language&.!= "sql"
|
|
43
|
+
s.ruby_line(:volatility, volatility, from: from_volatility) if volatility.present?
|
|
44
|
+
s.ruby_line(:leakproof, true) if leakproof
|
|
45
|
+
s.ruby_line(:strict, true) if strict
|
|
46
|
+
s.ruby_line(:security, security) if security.present?
|
|
47
|
+
s.ruby_line(:parallel, parallel, from: from_parallel) unless parallel.nil?
|
|
48
|
+
s.ruby_line(:cost, cost, from: from_cost) if cost.present?
|
|
49
|
+
s.ruby_line(:rows, rows, from: from_rows) if rows.present?
|
|
50
|
+
s.ruby_line(:body, body, from: from_body) if body.present?
|
|
51
|
+
s.ruby_line(:comment, comment, from: from_comment) if comment
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: false
|
|
2
|
+
|
|
3
|
+
# @!method ActiveRecord::Migration#change_function(name, **options, &block)
|
|
4
|
+
# Modify a function
|
|
5
|
+
#
|
|
6
|
+
# @param [#to_s] name (nil) The qualified name of the function
|
|
7
|
+
# @option [Boolean] :if_exists (false) Suppress the error when the function is absent
|
|
8
|
+
# @yield [Proc] the block with the function's definition
|
|
9
|
+
# @yieldparam The receiver of methods specifying the function
|
|
10
|
+
#
|
|
11
|
+
# The operation changes the function without dropping it
|
|
12
|
+
# (which can be necessary when there are other objects
|
|
13
|
+
# using the function and you don't want to change them all).
|
|
14
|
+
#
|
|
15
|
+
# You can change any property except for the name
|
|
16
|
+
# (use `rename_function` instead) and `language`.
|
|
17
|
+
#
|
|
18
|
+
# change_function "math.mult(int, int)" do |f|
|
|
19
|
+
# f.volatility :immutable, from: :stable
|
|
20
|
+
# f.parallel :safe, from: :restricted
|
|
21
|
+
# f.security :invoker
|
|
22
|
+
# f.leakproof true
|
|
23
|
+
# f.strict true
|
|
24
|
+
# f.cost 5.0
|
|
25
|
+
# # f.rows 1 (supported for functions returning sets of rows)
|
|
26
|
+
# SQL
|
|
27
|
+
#
|
|
28
|
+
# The example above is not invertible because of uncertainty
|
|
29
|
+
# about the previous volatility, parallelism, and cost.
|
|
30
|
+
# To define them, use a from options (available in a block syntax only):
|
|
31
|
+
#
|
|
32
|
+
# change_function "math.mult(a int, b int)" do |f|
|
|
33
|
+
# f.body <<~SQL, from: <<~SQL
|
|
34
|
+
# SELECT a * b;
|
|
35
|
+
# SQL
|
|
36
|
+
# SELECT min(a * b, 1);
|
|
37
|
+
# SQL
|
|
38
|
+
# f.volatility :immutable, from: :volatile
|
|
39
|
+
# f.parallel :safe, from: :unsafe
|
|
40
|
+
# f.leakproof true
|
|
41
|
+
# f.strict true
|
|
42
|
+
# f.cost 5.0, from: 100.0
|
|
43
|
+
# # f.rows 1, from: 0
|
|
44
|
+
# SQL
|
|
45
|
+
#
|
|
46
|
+
# Like in the other operations, the function can be
|
|
47
|
+
# identified by a qualified name (with types of arguments).
|
|
48
|
+
# If it has no overloaded implementations, the plain name is supported as well.
|
|
49
|
+
|
|
50
|
+
module PGTrunk::Operations::Functions
|
|
51
|
+
# @private
|
|
52
|
+
class ChangeFunction < Base
|
|
53
|
+
validates :force, :new_name, :language, :replace_existing, absence: true
|
|
54
|
+
validate { errors.add :base, "Changes can't be blank" if changes.blank? }
|
|
55
|
+
validate do
|
|
56
|
+
next if if_exists || name.blank? || create_function.present?
|
|
57
|
+
|
|
58
|
+
errors.add :base, "Function #{name.lean} can't be found"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def to_sql(server_version)
|
|
62
|
+
# Use `CREATE OR REPLACE FUNCTION` to make changes
|
|
63
|
+
create_function&.to_sql(server_version)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def invert
|
|
67
|
+
irreversible!("if_exists: true") if if_exists
|
|
68
|
+
undefined = inversion.select { |_, v| v.nil? }.keys.join(", ").presence
|
|
69
|
+
raise IrreversibleMigration.new(self, nil, <<~MSG.squish) if undefined
|
|
70
|
+
Undefined values to revert #{undefined}.
|
|
71
|
+
MSG
|
|
72
|
+
|
|
73
|
+
self.class.new(**inversion, name: name)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
private
|
|
77
|
+
|
|
78
|
+
def create_function
|
|
79
|
+
return if name.blank?
|
|
80
|
+
|
|
81
|
+
@create_function ||= begin
|
|
82
|
+
list = CreateFunction.select { |obj| name.maybe_eq?(obj.name) }
|
|
83
|
+
list.select! { |obj| name == obj.name } if list.size > 1 && name.args
|
|
84
|
+
list.first&.tap do |op|
|
|
85
|
+
op.attributes = { **changes, replace_existing: true }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def changes
|
|
91
|
+
@changes ||= to_h.except(:name).reject { |_, v| v.nil? || v == "" }
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def inversion
|
|
95
|
+
@inversion ||= {
|
|
96
|
+
body: [body, from_body],
|
|
97
|
+
volatility: [volatility, from_volatility],
|
|
98
|
+
parallel: [parallel, from_parallel],
|
|
99
|
+
cost: [cost, from_cost],
|
|
100
|
+
rows: [rows, from_rows],
|
|
101
|
+
comment: [comment, from_comment],
|
|
102
|
+
security: [security, (security == :invoker ? :definer : :invoker)],
|
|
103
|
+
leakproof: [leakproof, !leakproof],
|
|
104
|
+
strict: [strict, !strict],
|
|
105
|
+
}.slice(*changes.keys).transform_values(&:last)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# frozen_string_literal: false
|
|
2
|
+
|
|
3
|
+
# @!method ActiveRecord::Migration#create_function(name, **options, &block)
|
|
4
|
+
# Create a function
|
|
5
|
+
#
|
|
6
|
+
# @param [#to_s] name (nil)
|
|
7
|
+
# The qualified name of the function with arguments and returned value type
|
|
8
|
+
# @option [Boolean] :replace_existing (false) If the function should overwrite an existing one
|
|
9
|
+
# @option [#to_s] :language ("sql") The language (like "sql" or "plpgsql")
|
|
10
|
+
# @option [#to_s] :body (nil) The body of the function
|
|
11
|
+
# @option [Symbol] :volatility (:volatile) The volatility of the function.
|
|
12
|
+
# Supported values: :volatile (default), :stable, :immutable
|
|
13
|
+
# @option [Symbol] :parallel (:unsafe) The safety of parallel execution.
|
|
14
|
+
# Supported values: :unsafe (default), :restricted, :safe
|
|
15
|
+
# @option [Symbol] :security (:invoker) Define the role under which the function is invoked
|
|
16
|
+
# Supported values: :invoker (default), :definer
|
|
17
|
+
# @option [Boolean] :leakproof (false) If the function is leakproof
|
|
18
|
+
# @option [Boolean] :strict (false) If the function is strict
|
|
19
|
+
# @option [Float] :cost (nil) The cost estimation for the function
|
|
20
|
+
# @option [Integer] :rows (nil) The number of rows returned by a function
|
|
21
|
+
# @option [#to_s] :comment The description of the function
|
|
22
|
+
# @yield [Proc] the block with the function's definition
|
|
23
|
+
# @yieldparam The receiver of methods specifying the function
|
|
24
|
+
#
|
|
25
|
+
# The function can be created either using inline syntax
|
|
26
|
+
#
|
|
27
|
+
# create_function "math.mult(a int, b int) int",
|
|
28
|
+
# language: :sql,
|
|
29
|
+
# body: "SELECT a * b",
|
|
30
|
+
# volatility: :immutable,
|
|
31
|
+
# leakproof: true,
|
|
32
|
+
# comment: "Multiplies 2 integers"
|
|
33
|
+
#
|
|
34
|
+
# or using a block:
|
|
35
|
+
#
|
|
36
|
+
# create_function "math.mult(a int, b int) int" do |f|
|
|
37
|
+
# f.language "sql" # (default)
|
|
38
|
+
# f.body <<~SQL
|
|
39
|
+
# SELECT a * b;
|
|
40
|
+
# SQL
|
|
41
|
+
# f.volatility :immutable # :stable, :volatile (default)
|
|
42
|
+
# f.parallel :safe # :restricted, :unsafe (default)
|
|
43
|
+
# f.security :invoker # (default), also :definer
|
|
44
|
+
# f.leakproof true
|
|
45
|
+
# f.strict true
|
|
46
|
+
# f.cost 5.0
|
|
47
|
+
# # f.rows 1 (supported for functions returning sets of rows)
|
|
48
|
+
# f.comment "Multiplies 2 integers"
|
|
49
|
+
# SQL
|
|
50
|
+
#
|
|
51
|
+
# With a `replace_existing: true` option,
|
|
52
|
+
# it will be created using the `CREATE OR REPLACE` clause.
|
|
53
|
+
# In this case the migration is irreversible because we
|
|
54
|
+
# don't know if and how to restore its previous definition.
|
|
55
|
+
#
|
|
56
|
+
# create_function "math.mult(a int, b int) int",
|
|
57
|
+
# body: "SELECT a * b",
|
|
58
|
+
# replace_existing: true
|
|
59
|
+
#
|
|
60
|
+
# We presume a function without arguments should have
|
|
61
|
+
# no arguments and return `void` like
|
|
62
|
+
#
|
|
63
|
+
# # the same as "do_something() void"
|
|
64
|
+
# create_function "do_something" do |f|
|
|
65
|
+
# # ...
|
|
66
|
+
# end
|
|
67
|
+
|
|
68
|
+
module PGTrunk::Operations::Functions
|
|
69
|
+
# @private
|
|
70
|
+
class CreateFunction < Base
|
|
71
|
+
# The definition must be either set explicitly
|
|
72
|
+
# or by reading the versioned snippet.
|
|
73
|
+
validate { errors.add :body, :blank if body.blank? && version.blank? }
|
|
74
|
+
validates :if_exists, :force, :new_name, absence: true
|
|
75
|
+
|
|
76
|
+
from_sql do |server_version|
|
|
77
|
+
plain_function = "NOT p.proisagg AND NOT p.proiswindow"
|
|
78
|
+
plain_function = "p.prokind = 'f'" if server_version >= "11"
|
|
79
|
+
|
|
80
|
+
<<~SQL.squish
|
|
81
|
+
SELECT
|
|
82
|
+
p.oid,
|
|
83
|
+
(
|
|
84
|
+
p.pronamespace::regnamespace || '.' || p.proname || '(' || (
|
|
85
|
+
regexp_replace(
|
|
86
|
+
regexp_replace(
|
|
87
|
+
pg_get_function_arguments(p.oid), '^\s*IN\s+', '', 'g'
|
|
88
|
+
), '[,]\s*IN\s+', ',', 'g'
|
|
89
|
+
)
|
|
90
|
+
) || ')' || (
|
|
91
|
+
CASE
|
|
92
|
+
WHEN p.prorettype IS NULL THEN ''
|
|
93
|
+
ELSE ' ' || pg_get_function_result(p.oid)
|
|
94
|
+
END
|
|
95
|
+
)
|
|
96
|
+
) AS name,
|
|
97
|
+
p.prosrc AS body,
|
|
98
|
+
l.lanname AS language,
|
|
99
|
+
(
|
|
100
|
+
CASE
|
|
101
|
+
WHEN p.provolatile = 'i' THEN 'immutable'
|
|
102
|
+
WHEN p.provolatile = 's' THEN 'stable'
|
|
103
|
+
END
|
|
104
|
+
) AS volatility,
|
|
105
|
+
( CASE WHEN p.proleakproof THEN true END ) AS leakproof,
|
|
106
|
+
( CASE WHEN p.proisstrict THEN true END ) AS strict,
|
|
107
|
+
(
|
|
108
|
+
CASE
|
|
109
|
+
WHEN p.proparallel = 's' THEN 'safe'
|
|
110
|
+
WHEN p.proparallel = 'r' THEN 'restricted'
|
|
111
|
+
END
|
|
112
|
+
) AS parallel,
|
|
113
|
+
( CASE WHEN p.prosecdef THEN 'definer' END ) AS security,
|
|
114
|
+
( CASE WHEN p.procost != 100 THEN p.procost END ) AS cost,
|
|
115
|
+
( CASE WHEN p.prorows != 0 THEN p.prorows END ) AS rows,
|
|
116
|
+
d.description AS comment
|
|
117
|
+
FROM pg_proc p
|
|
118
|
+
JOIN pg_trunk e ON e.oid = p.oid
|
|
119
|
+
JOIN pg_language l ON l.oid = p.prolang
|
|
120
|
+
LEFT JOIN pg_description d ON d.objoid = p.oid
|
|
121
|
+
WHERE e.classid = 'pg_proc'::regclass
|
|
122
|
+
AND #{plain_function};
|
|
123
|
+
SQL
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def to_sql(version)
|
|
127
|
+
[
|
|
128
|
+
create_function,
|
|
129
|
+
*comment_function,
|
|
130
|
+
register_function(version),
|
|
131
|
+
].join(" ")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def invert
|
|
135
|
+
irreversible!("replace_existing: true") if replace_existing
|
|
136
|
+
DropFunction.new(**to_h)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
def create_function
|
|
142
|
+
sql = "CREATE"
|
|
143
|
+
sql << " OR REPLACE" if replace_existing
|
|
144
|
+
sql << " FUNCTION #{name.to_sql(true)}"
|
|
145
|
+
sql << " RETURNS #{name.returns}" if name.returns
|
|
146
|
+
sql << " RETURNS void" if name.returns.blank? && name.args.blank?
|
|
147
|
+
sql << " LANGUAGE #{language || 'sql'}"
|
|
148
|
+
sql << " IMMUTABLE" if volatility == :immutable
|
|
149
|
+
sql << " STABLE" if volatility == :stable
|
|
150
|
+
sql << " VOLATILE" if volatility.blank? || volatility == :volatile
|
|
151
|
+
sql << " LEAKPROOF" if leakproof
|
|
152
|
+
sql << " NOT LEAKPROOF" unless leakproof
|
|
153
|
+
sql << " STRICT" if strict
|
|
154
|
+
sql << " CALLED ON NULL INPUT" if strict == false
|
|
155
|
+
sql << " SECURITY DEFINER" if security == :definer
|
|
156
|
+
sql << " SECURITY INVOKER" if security == :invoker
|
|
157
|
+
sql << " PARALLEL SAFE" if parallel == :safe
|
|
158
|
+
sql << " PARALLEL RESTRICTED" if parallel == :restricted
|
|
159
|
+
sql << " PARALLEL UNSAFE" if parallel.blank? || parallel == :unsafe
|
|
160
|
+
sql << " COST #{cost}" if cost
|
|
161
|
+
sql << " ROWS #{rows}" if rows
|
|
162
|
+
sql << " AS $$#{body}$$;"
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def comment_function
|
|
166
|
+
<<~SQL
|
|
167
|
+
COMMENT ON FUNCTION #{name.to_sql(true)}
|
|
168
|
+
IS $comment$#{comment}$comment$;
|
|
169
|
+
SQL
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Register the most recent `oid` of functions with this schema/name
|
|
173
|
+
# There can be several overloaded definitions, but we're interested
|
|
174
|
+
# in that one we created just now so we can skip checking its args.
|
|
175
|
+
def register_function(version)
|
|
176
|
+
function_only = "NOT proisagg AND NOT proiswindow"
|
|
177
|
+
function_only = "prokind = 'f'" if version >= "11"
|
|
178
|
+
|
|
179
|
+
<<~SQL.squish
|
|
180
|
+
WITH latest AS (
|
|
181
|
+
SELECT
|
|
182
|
+
oid,
|
|
183
|
+
(
|
|
184
|
+
proname = #{name.quoted} AND pronamespace = #{name.namespace}
|
|
185
|
+
) AS ok
|
|
186
|
+
FROM pg_proc
|
|
187
|
+
WHERE #{function_only}
|
|
188
|
+
ORDER BY oid DESC LIMIT 1
|
|
189
|
+
)
|
|
190
|
+
INSERT INTO pg_trunk (oid, classid)
|
|
191
|
+
SELECT oid, 'pg_proc'::regclass
|
|
192
|
+
FROM latest
|
|
193
|
+
WHERE ok
|
|
194
|
+
ON CONFLICT DO NOTHING;
|
|
195
|
+
SQL
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: false
|
|
2
|
+
|
|
3
|
+
# @!method ActiveRecord::Migration#drop_function(name, **options, &block)
|
|
4
|
+
# Drop a function
|
|
5
|
+
#
|
|
6
|
+
# @param [#to_s] name (nil)
|
|
7
|
+
# The qualified name of the function with arguments and returned value type
|
|
8
|
+
# @option [Boolean] :if_exists (false) Suppress the error when the function is absent
|
|
9
|
+
# @option [Symbol] :force (:restrict) How to process dependent objects
|
|
10
|
+
# Supported values: :restrict (default), :cascade
|
|
11
|
+
# @option [#to_s] :language ("sql") The language (like "sql" or "plpgsql")
|
|
12
|
+
# @option [#to_s] :body (nil) The body of the function
|
|
13
|
+
# @option [Symbol] :volatility (:volatile) The volatility of the function.
|
|
14
|
+
# Supported values: :volatile (default), :stable, :immutable
|
|
15
|
+
# @option [Symbol] :parallel (:unsafe) The safety of parallel execution.
|
|
16
|
+
# Supported values: :unsafe (default), :restricted, :safe
|
|
17
|
+
# @option [Symbol] :security (:invoker) Define the role under which the function is invoked
|
|
18
|
+
# Supported values: :invoker (default), :definer
|
|
19
|
+
# @option [Boolean] :leakproof (false) If the function is leakproof
|
|
20
|
+
# @option [Boolean] :strict (false) If the function is strict
|
|
21
|
+
# @option [Float] :cost (nil) The cost estimation for the function
|
|
22
|
+
# @option [Integer] :rows (nil) The number of rows returned by a function
|
|
23
|
+
# @option [#to_s] :comment The description of the function
|
|
24
|
+
# @yield [Proc] the block with the function's definition
|
|
25
|
+
# @yieldparam The receiver of methods specifying the function
|
|
26
|
+
#
|
|
27
|
+
# A function can be dropped by a plain name:
|
|
28
|
+
#
|
|
29
|
+
# drop_function "multiply"
|
|
30
|
+
#
|
|
31
|
+
# If several overloaded functions have the name,
|
|
32
|
+
# then you must specify the signature having
|
|
33
|
+
# types of attributes at least:
|
|
34
|
+
#
|
|
35
|
+
# drop_function "multiply(int, int)"
|
|
36
|
+
#
|
|
37
|
+
# In both cases above the operation is irreversible. To make it
|
|
38
|
+
# inverted you have to provide a full signature along with
|
|
39
|
+
# the body definition. The other options are supported as well:
|
|
40
|
+
#
|
|
41
|
+
# drop_function "math.mult(a int, b int) int" do |f|
|
|
42
|
+
# f.language "sql" # (default)
|
|
43
|
+
# f.body <<~SQL
|
|
44
|
+
# SELECT a * b;
|
|
45
|
+
# SQL
|
|
46
|
+
# f.volatility :immutable # :stable, :volatile (default)
|
|
47
|
+
# f.parallel :safe # :restricted, :unsafe (default)
|
|
48
|
+
# f.security :invoker # (default), also :definer
|
|
49
|
+
# f.leakproof true
|
|
50
|
+
# f.strict true
|
|
51
|
+
# f.cost 5.0
|
|
52
|
+
# # f.rows 1 (supported for functions returning sets of rows)
|
|
53
|
+
# f.comment "Multiplies 2 integers"
|
|
54
|
+
# end
|
|
55
|
+
#
|
|
56
|
+
# The operation can be called with `if_exists` option. In this case
|
|
57
|
+
# it would do nothing when no function existed.
|
|
58
|
+
#
|
|
59
|
+
# drop_function "math.multiply(integer, integer)", if_exists: true
|
|
60
|
+
#
|
|
61
|
+
# Another operation-specific option `force: :cascade` enables
|
|
62
|
+
# to drop silently any object depending on the function.
|
|
63
|
+
#
|
|
64
|
+
# drop_function "math.multiply(integer, integer)", force: :cascade
|
|
65
|
+
#
|
|
66
|
+
# Both options make the operation irreversible because of
|
|
67
|
+
# uncertainty about the previous state of the database.
|
|
68
|
+
|
|
69
|
+
module PGTrunk::Operations::Functions
|
|
70
|
+
# @private
|
|
71
|
+
class DropFunction < Base
|
|
72
|
+
validates :replace_existing, :new_name, absence: true
|
|
73
|
+
|
|
74
|
+
def to_sql(_version)
|
|
75
|
+
sql = "DROP FUNCTION"
|
|
76
|
+
sql << " IF EXISTS" if if_exists
|
|
77
|
+
sql << " #{name.to_sql}"
|
|
78
|
+
sql << " CASCADE" if force == :cascade
|
|
79
|
+
sql << ";"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def invert
|
|
83
|
+
irreversible!("if_exists: true") if if_exists
|
|
84
|
+
irreversible!("force: :cascade") if force == :cascade
|
|
85
|
+
CreateFunction.new(**to_h.except(:force))
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: false
|
|
2
|
+
|
|
3
|
+
# @!method ActiveRecord::Migration#rename_function(name, to:)
|
|
4
|
+
# Change the name and/or schema of a function
|
|
5
|
+
#
|
|
6
|
+
# @param [#to_s] :name (nil) The qualified name of the function
|
|
7
|
+
# @option [#to_s] :to (nil) The new qualified name for the function
|
|
8
|
+
#
|
|
9
|
+
# A function can be renamed by changing both the name
|
|
10
|
+
# and the schema (namespace) it belongs to.
|
|
11
|
+
#
|
|
12
|
+
# If there are no overloaded functions, then you can use a plain name:
|
|
13
|
+
#
|
|
14
|
+
# rename_function "math.multiply", to: "public.product"
|
|
15
|
+
#
|
|
16
|
+
# otherwise the types of attributes must be explicitly specified.
|
|
17
|
+
#
|
|
18
|
+
# rename_function "math.multiply(int, int)", to: "public.product"
|
|
19
|
+
#
|
|
20
|
+
# Any specification of attributes or returned values in `to:` option
|
|
21
|
+
# is ignored because they cannot be changed anyway.
|
|
22
|
+
#
|
|
23
|
+
# The operation is always reversible.
|
|
24
|
+
|
|
25
|
+
module PGTrunk::Operations::Functions
|
|
26
|
+
# @private
|
|
27
|
+
class RenameFunction < Base
|
|
28
|
+
validates :new_name, presence: true
|
|
29
|
+
validates :body, :cost, :force, :if_exists, :language, :leakproof,
|
|
30
|
+
:parallel, :replace_existing, :rows, :security, :strict,
|
|
31
|
+
:volatility, absence: true
|
|
32
|
+
|
|
33
|
+
def to_sql(_version)
|
|
34
|
+
[*change_schema, *change_name].join(" ")
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def invert
|
|
38
|
+
q_new_name = "#{new_name.schema}.#{new_name.routine}(#{name.args}) #{name.returns}"
|
|
39
|
+
self.class.new(**to_h, name: q_new_name.strip, to: name)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def change_schema
|
|
45
|
+
return if name.schema == new_name.schema
|
|
46
|
+
|
|
47
|
+
"ALTER FUNCTION #{name.to_sql} SET SCHEMA #{new_name.schema.inspect};"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def change_name
|
|
51
|
+
return if new_name.routine == name.routine
|
|
52
|
+
|
|
53
|
+
changed_name = name.merge(schema: new_name.schema).to_sql
|
|
54
|
+
"ALTER FUNCTION #{changed_name} RENAME TO #{new_name.routine.inspect};"
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# nodoc
|
|
4
|
+
module PGTrunk::Operations
|
|
5
|
+
# @private
|
|
6
|
+
# Namespace for operations with functions
|
|
7
|
+
module Functions
|
|
8
|
+
require_relative "functions/base"
|
|
9
|
+
require_relative "functions/create_function"
|
|
10
|
+
require_relative "functions/change_function"
|
|
11
|
+
require_relative "functions/drop_function"
|
|
12
|
+
require_relative "functions/rename_function"
|
|
13
|
+
end
|
|
14
|
+
end
|