pg_trunk 0.1.0 → 0.2.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +4 -15
  3. data/CHANGELOG.md +21 -0
  4. data/README.md +3 -1
  5. data/lib/pg_trunk/core/operation/attributes.rb +1 -1
  6. data/lib/pg_trunk/core/railtie/custom_types.rb +5 -6
  7. data/lib/pg_trunk/operations/check_constraints/add_check_constraint.rb +42 -33
  8. data/lib/pg_trunk/operations/check_constraints/drop_check_constraint.rb +51 -40
  9. data/lib/pg_trunk/operations/check_constraints/rename_check_constraint.rb +39 -30
  10. data/lib/pg_trunk/operations/check_constraints/validate_check_constraint.rb +28 -21
  11. data/lib/pg_trunk/operations/composite_types/change_composite_type.rb +59 -50
  12. data/lib/pg_trunk/operations/composite_types/create_composite_type.rb +23 -19
  13. data/lib/pg_trunk/operations/composite_types/drop_composite_type.rb +48 -43
  14. data/lib/pg_trunk/operations/composite_types/rename_composite_type.rb +15 -12
  15. data/lib/pg_trunk/operations/domains/change_domain.rb +53 -47
  16. data/lib/pg_trunk/operations/domains/create_domain.rb +28 -25
  17. data/lib/pg_trunk/operations/domains/drop_domain.rb +50 -41
  18. data/lib/pg_trunk/operations/domains/rename_domain.rb +17 -12
  19. data/lib/pg_trunk/operations/enums/change_enum.rb +37 -32
  20. data/lib/pg_trunk/operations/enums/create_enum.rb +23 -20
  21. data/lib/pg_trunk/operations/enums/drop_enum.rb +50 -39
  22. data/lib/pg_trunk/operations/enums/rename_enum.rb +17 -12
  23. data/lib/pg_trunk/operations/foreign_keys/add_foreign_key.rb +58 -49
  24. data/lib/pg_trunk/operations/foreign_keys/drop_foreign_key.rb +57 -48
  25. data/lib/pg_trunk/operations/foreign_keys/rename_foreign_key.rb +38 -29
  26. data/lib/pg_trunk/operations/functions/change_function.rb +53 -47
  27. data/lib/pg_trunk/operations/functions/create_function.rb +75 -64
  28. data/lib/pg_trunk/operations/functions/drop_function.rb +78 -65
  29. data/lib/pg_trunk/operations/functions/rename_function.rb +29 -22
  30. data/lib/pg_trunk/operations/materialized_views/change_materialized_view.rb +65 -55
  31. data/lib/pg_trunk/operations/materialized_views/create_materialized_view.rb +82 -71
  32. data/lib/pg_trunk/operations/materialized_views/drop_materialized_view.rb +59 -46
  33. data/lib/pg_trunk/operations/materialized_views/refresh_materialized_view.rb +29 -24
  34. data/lib/pg_trunk/operations/materialized_views/rename_materialized_view.rb +29 -22
  35. data/lib/pg_trunk/operations/procedures/change_procedure.rb +53 -46
  36. data/lib/pg_trunk/operations/procedures/create_procedure.rb +63 -52
  37. data/lib/pg_trunk/operations/procedures/drop_procedure.rb +56 -45
  38. data/lib/pg_trunk/operations/procedures/rename_procedure.rb +29 -22
  39. data/lib/pg_trunk/operations/rules/base.rb +77 -0
  40. data/lib/pg_trunk/operations/rules/create_rule.rb +155 -0
  41. data/lib/pg_trunk/operations/rules/drop_rule.rb +94 -0
  42. data/lib/pg_trunk/operations/rules/rename_rule.rb +62 -0
  43. data/lib/pg_trunk/operations/rules.rb +13 -0
  44. data/lib/pg_trunk/operations/sequences/base.rb +79 -0
  45. data/lib/pg_trunk/operations/sequences/change_sequence.rb +142 -0
  46. data/lib/pg_trunk/operations/sequences/create_sequence.rb +180 -0
  47. data/lib/pg_trunk/operations/sequences/drop_sequence.rb +82 -0
  48. data/lib/pg_trunk/operations/sequences/rename_sequence.rb +64 -0
  49. data/lib/pg_trunk/operations/sequences.rb +14 -0
  50. data/lib/pg_trunk/operations/statistics/create_statistics.rb +67 -56
  51. data/lib/pg_trunk/operations/statistics/drop_statistics.rb +64 -53
  52. data/lib/pg_trunk/operations/statistics/rename_statistics.rb +18 -13
  53. data/lib/pg_trunk/operations/triggers/change_trigger.rb +23 -18
  54. data/lib/pg_trunk/operations/triggers/create_trigger.rb +63 -54
  55. data/lib/pg_trunk/operations/triggers/drop_trigger.rb +55 -46
  56. data/lib/pg_trunk/operations/triggers/rename_trigger.rb +51 -48
  57. data/lib/pg_trunk/operations/views/change_view.rb +47 -38
  58. data/lib/pg_trunk/operations/views/create_view.rb +56 -45
  59. data/lib/pg_trunk/operations/views/drop_view.rb +59 -46
  60. data/lib/pg_trunk/operations/views/rename_view.rb +27 -20
  61. data/lib/pg_trunk/operations.rb +2 -0
  62. data/lib/pg_trunk/version.rb +1 -1
  63. data/pg_trunk.gemspec +0 -1
  64. data/spec/operations/rules/create_rule_spec.rb +119 -0
  65. data/spec/operations/rules/drop_rule_spec.rb +117 -0
  66. data/spec/operations/rules/rename_rule_spec.rb +148 -0
  67. data/spec/operations/sequences/change_sequence_spec.rb +134 -0
  68. data/spec/operations/sequences/create_sequence_spec.rb +156 -0
  69. data/spec/operations/sequences/drop_sequence_spec.rb +102 -0
  70. data/spec/operations/sequences/rename_sequence_spec.rb +100 -0
  71. metadata +22 -68
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: false
2
+
3
+ # @!parse
4
+ # class ActiveRecord::Migration
5
+ # # Modify a sequence
6
+ # #
7
+ # # @param [#to_s] name (nil) The qualified name of the sequence.
8
+ # # @option options [Boolean] :if_exists (false) Suppress the error when the sequence is absent.
9
+ # # @yield [s] the block with the sequence's definition.
10
+ # # @yieldparam Object receiver of methods specifying the sequence.
11
+ # # @return [void]
12
+ # #
13
+ # # The operation enables to alter a sequence without recreating it.
14
+ # # PostgreSQL allows any setting to be modified. The comment can be
15
+ # # changed as well.
16
+ # #
17
+ # # ```ruby
18
+ # # change_sequence "my_schema.global_id" do |s|
19
+ # # s.owned_by "", "", from: %w[users gid]
20
+ # # s.type "smallint", from: "integer"
21
+ # # s.iterate_by 1, from: 2
22
+ # # s.min_value 1, from: 0
23
+ # # s.max_value 2000, from: 1999
24
+ # # s.start_with 2, from: 1
25
+ # # s.cache 1, from: 10
26
+ # # s.cycle false
27
+ # # s.comment "Identifier", from: "Global identifier"
28
+ # # end
29
+ # # ```
30
+ # #
31
+ # # As in the snippet above, to make the change invertible,
32
+ # # you have to define from option for every changed attribute,
33
+ # # except for the boolean `cycle`.
34
+ # #
35
+ # # With the `if_exists: true` option, the operation won't raise
36
+ # # when the sequence is absent.
37
+ # #
38
+ # # ```ruby
39
+ # # change_sequence "my_schema.global_id", if_exists: true do |s|
40
+ # # s.type "smallint"
41
+ # # s.iterate_by 1
42
+ # # s.min_value 1
43
+ # # s.max_value 2000
44
+ # # s.start_with 2
45
+ # # s.cache 1
46
+ # # s.cycle false
47
+ # # s.comment "Identifier"
48
+ # # end
49
+ # # ```
50
+ # #
51
+ # # This option makes a migration irreversible due to uncertainty
52
+ # # of the previous state of the database. That's why in the last
53
+ # # example no `from:` option was added (they are useless).
54
+ # def change_sequence(name, **options, &block); end
55
+ # end
56
+ module PGTrunk::Operations::Sequences
57
+ # @private
58
+ class ChangeSequence < Base
59
+ # Operation-specific validations
60
+ validate { errors.add :base, "Changes can't be blank" if changes.blank? }
61
+ validates :force, :if_not_exists, :new_name, absence: true
62
+
63
+ def owned_by(table, column, from: nil)
64
+ self.table = table
65
+ self.column = column
66
+ self.from_table, self.from_column = Array(from)
67
+ end
68
+
69
+ def to_sql(_version)
70
+ [*alter_sequence, *update_comment].join(" ")
71
+ end
72
+
73
+ def invert
74
+ irreversible!("if_exists: true") if if_exists
75
+ undefined = inversion.select { |_, v| v.nil? }.keys.join(", ").presence
76
+ raise IrreversibleMigration.new(self, nil, <<~MSG.squish) if undefined
77
+ Undefined values to revert #{undefined}.
78
+ MSG
79
+
80
+ self.class.new(name: name, **inversion) if inversion.any?
81
+ end
82
+
83
+ private
84
+
85
+ INF = (2**63) - 1
86
+
87
+ def changes
88
+ @changes ||= attributes.symbolize_keys.except(:name, :if_exists).compact
89
+ end
90
+
91
+ def inversion
92
+ @inversion ||= changes.each_with_object({}) do |(key, val), obj|
93
+ obj[key] = send(:"from_#{key}")
94
+ obj[key] = !val if [true, false].include?(val)
95
+ end
96
+ end
97
+
98
+ def alter_sequence
99
+ return if changes.except(:comment).blank?
100
+
101
+ sql = "ALTER SEQUENCE"
102
+ sql << " IF EXISTS" if if_exists
103
+ sql << " #{name.to_sql}"
104
+ sql << " AS #{type}" if type.present?
105
+ sql << " INCREMENT BY #{increment_by}" if increment_by.present?
106
+ sql << " MINVALUE #{min_value}" if min_value&.>(-INF)
107
+ sql << " NO MINVALUE" if min_value&.<=(-INF)
108
+ sql << " MAXVALUE #{max_value}" if max_value&.<(INF)
109
+ sql << " NO MAXVALUE" if max_value&.>=(INF)
110
+ sql << " START WITH #{start_with}" if start_with.present?
111
+ sql << " CACHE #{cache}" if cache.present?
112
+ sql << " OWNED BY #{table}.#{column}" if table.present? && column.present?
113
+ sql << " OWNED BY NONE" if table == "" || column == ""
114
+ sql << " CYCLE" if cycle
115
+ sql << " NO CYCLE" if cycle == false
116
+ sql << ";"
117
+ end
118
+
119
+ def update_comment
120
+ return unless comment
121
+ return <<~SQL.squish unless if_exists
122
+ COMMENT ON SEQUENCE #{name.to_sql} IS $comment$#{comment}$comment$;
123
+ SQL
124
+
125
+ # change the comment conditionally
126
+ <<~SQL.squish
127
+ DO $$
128
+ BEGIN
129
+ IF EXISTS (
130
+ SELECT FROM pg_sequence s JOIN pg_class c ON c.oid = s.seqrelid
131
+ WHERE c.relname = #{name.quoted}
132
+ AND c.relnamespace = #{name.namespace}
133
+ ) THEN
134
+ COMMENT ON SEQUENCE #{name.to_sql}
135
+ IS $comment$#{comment}$comment$;
136
+ END IF;
137
+ END
138
+ $$;
139
+ SQL
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: false
2
+
3
+ # @!parse
4
+ # class ActiveRecord::Migration
5
+ # # Create a sequence
6
+ # #
7
+ # # @param [#to_s] name (nil) The qualified name of the sequence
8
+ # # @option options [#to_s] :as ("bigint") The type of the sequence's value
9
+ # # Supported values: "bigint" (or "int8", default), "integer" (or "int4"), "smallint" ("int2").
10
+ # # @option options [Boolean] :if_not_exists (false)
11
+ # # Suppress the error when the sequence already existed.
12
+ # # @option options [Integer] :increment_by (1) Non-zero step of the sequence (either positive or negative).
13
+ # # @option options [Integer] :min_value (nil) Minimum value of the sequence.
14
+ # # @option options [Integer] :max_value (nil) Maximum value of the sequence.
15
+ # # @option options [Integer] :start_with (nil) The first value of the sequence.
16
+ # # @option options [Integer] :cache (1) The number of values to be generated and cached.
17
+ # # @option options [Boolean] :cycle (false) If the sequence should be reset to start
18
+ # # after its value reaches min/max value.
19
+ # # @option options [#to_s] :comment (nil) The comment describing the sequence.
20
+ # # @yield [s] the block with the sequence's definition
21
+ # # @yieldparam Object receiver of methods specifying the sequence
22
+ # # @return [void]
23
+ # #
24
+ # # The sequence can be created by its qualified name only
25
+ # #
26
+ # # ```ruby
27
+ # # create_sequence "my_schema.global_id"
28
+ # # ```
29
+ # #
30
+ # # we also support all PostgreSQL settings for the sequence:
31
+ # #
32
+ # # ```ruby
33
+ # # create_sequence "my_schema.global_id", as: "integer" do |s|
34
+ # # s.iterate_by 2
35
+ # # s.min_value 0
36
+ # # s.max_value 1999
37
+ # # s.start_with 1
38
+ # # s.cache 10
39
+ # # s.cycle true
40
+ # # s.comment "Global identifier"
41
+ # # end
42
+ # # ```
43
+ # #
44
+ # # Using a block method `s.owned_by` you can bind the sequence to
45
+ # # some table's column. This means the sequence is dependent from
46
+ # # the column and will be dropped along with it. Notice that the
47
+ # # name of the table is NOT qualified because the table MUST belong
48
+ # # to the same schema as the sequence itself.
49
+ # #
50
+ # # ```ruby
51
+ # # create_table "users" do |t|
52
+ # # t.bigint :gid
53
+ # # end
54
+ # #
55
+ # # create_sequence "my_schema.global_id" do |s|
56
+ # # s.owned_by "users", "gid"
57
+ # # end
58
+ # # ```
59
+ # #
60
+ # # With the `if_not_exists: true` option the operation wouldn't raise
61
+ # # an exception in case the sequence has been already created.
62
+ # #
63
+ # # ```ruby
64
+ # # create_sequence "my_schema.global_id", if_not_exists: true
65
+ # # ```
66
+ # #
67
+ # # This option makes the migration irreversible due to uncertainty
68
+ # # of the previous state of the database.
69
+ # def create_sequence(name, **options, &block); end
70
+ # end
71
+ module PGTrunk::Operations::Sequences
72
+ # @private
73
+ class CreateSequence < Base
74
+ validates :if_exists, :force, :new_name, absence: true
75
+
76
+ from_sql do |_server_version|
77
+ <<~SQL
78
+ SELECT
79
+ c.oid,
80
+ (c.relnamespace::regnamespace || '.' || c.relname) AS name,
81
+ p.refobjid::regclass AS table,
82
+ a.attname AS column,
83
+ (
84
+ CASE WHEN s.seqtypid != 'int8'::regtype THEN format_type(s.seqtypid, 0) END
85
+ ) AS type,
86
+ ( CASE WHEN s.seqincrement != 1 THEN s.seqincrement END ) AS increment_by,
87
+ (
88
+ CASE
89
+ WHEN s.seqincrement > 0 THEN
90
+ CASE WHEN s.seqmin != 1 THEN s.seqmin END
91
+ ELSE
92
+ CASE
93
+ WHEN s.seqtypid = 'int2'::regtype AND s.seqmin = -32768 THEN NULL
94
+ WHEN s.seqtypid = 'int4'::regtype AND s.seqmin = -2147483648 THEN NULL
95
+ WHEN s.seqtypid = 'int8'::regtype AND s.seqmin = -9223372036854775808 THEN NULL
96
+ ELSE s.seqmin
97
+ END
98
+ END
99
+ ) AS min_value,
100
+ (
101
+ CASE
102
+ WHEN s.seqincrement < 0 THEN
103
+ CASE WHEN s.seqmax != -1 THEN s.seqmax END
104
+ ELSE
105
+ CASE
106
+ WHEN s.seqtypid = 'int2'::regtype AND s.seqmax = 32767 THEN NULL
107
+ WHEN s.seqtypid = 'int4'::regtype AND s.seqmax = 2147483647 THEN NULL
108
+ WHEN s.seqtypid = 'int8'::regtype AND s.seqmax = 9223372036854775807 THEN NULL
109
+ ELSE s.seqmax
110
+ END
111
+ END
112
+ ) AS max_value,
113
+ (
114
+ CASE
115
+ WHEN s.seqincrement > 0 AND s.seqstart = s.seqmin THEN NULL
116
+ WHEN s.seqincrement < 0 AND s.seqstart = s.seqmax THEN NULL
117
+ ELSE s.seqstart
118
+ END
119
+ ) AS start_with,
120
+ ( CASE WHEN s.seqcache != 1 THEN s.seqcache END ) AS cache,
121
+ ( CASE WHEN s.seqcycle THEN true END ) AS cycle,
122
+ d.description AS comment
123
+ FROM pg_sequence s
124
+ JOIN pg_class c ON c.oid = s.seqrelid
125
+ JOIN pg_trunk t ON t.oid = c.oid
126
+ AND t.classid = 'pg_sequence'::regclass
127
+ LEFT JOIN pg_depend p ON p.objid = c.oid
128
+ AND p.classid = 'pg_class'::regclass
129
+ AND p.refclassid = 'pg_class'::regclass
130
+ AND p.objsubid = 0 AND p.refobjsubid !=0
131
+ LEFT JOIN pg_attribute a ON a.attrelid = p.refobjid
132
+ AND a.attnum = p.refobjsubid
133
+ LEFT JOIN pg_description d ON d.objoid = c.oid;
134
+ SQL
135
+ end
136
+
137
+ def to_sql(_server_version)
138
+ [create_sequence, *comment_sequence, register_sequence].join(" ")
139
+ end
140
+
141
+ def invert
142
+ irreversible!("if_not_exists: true") if if_not_exists
143
+ DropSequence.new(**to_h.except(:if_not_exists))
144
+ end
145
+
146
+ private
147
+
148
+ def create_sequence
149
+ sql = "CREATE SEQUENCE"
150
+ sql << " IF NOT EXISTS" if if_not_exists
151
+ sql << " #{name.to_sql}"
152
+ sql << " AS #{type}" if type.present?
153
+ sql << " INCREMENT BY #{increment_by}" if increment_by.present?
154
+ sql << " MINVALUE #{min_value}" if min_value.present?
155
+ sql << " MAXVALUE #{max_value}" if max_value.present?
156
+ sql << " START WITH #{start_with}" if start_with.present?
157
+ sql << " CACHE #{cache}" if cache.present?
158
+ sql << " OWNED BY #{table}.#{column}" if table.present? && column.present?
159
+ sql << " CYCLE" if cycle
160
+ sql << ";"
161
+ end
162
+
163
+ def comment_sequence
164
+ <<~SQL.squish if comment.present?
165
+ COMMENT ON SEQUENCE #{name.to_sql} IS $comment$#{comment}$comment$;
166
+ SQL
167
+ end
168
+
169
+ def register_sequence
170
+ <<~SQL.squish
171
+ INSERT INTO pg_trunk (oid, classid)
172
+ SELECT c.oid, 'pg_sequence'::regclass
173
+ FROM pg_sequence s JOIN pg_class c ON c.oid = s.seqrelid
174
+ WHERE c.relname = #{name.quoted}
175
+ AND c.relnamespace = #{name.namespace}
176
+ ON CONFLICT DO NOTHING;
177
+ SQL
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: false
2
+
3
+ # @!parse
4
+ # class ActiveRecord::Migration
5
+ # # Drop a sequence
6
+ # #
7
+ # # @param [#to_s] name (nil) The qualified name of the sequence
8
+ # # @option options [#to_s] :as ("bigint") The type of the sequence's value
9
+ # # Supported values: "bigint" (or "int8", default), "integer" (or "int4"), "smallint" ("int2").
10
+ # # @option options [Boolean] :if_exists (false) Suppress the error when the sequence is absent.
11
+ # # @option options [Symbol] :force (:restrict) Define how to process dependent objects
12
+ # # Supported values: :restrict (default), :cascade.
13
+ # # @option options [Integer] :increment_by (1) Non-zero step of the sequence (either positive or negative).
14
+ # # @option options [Integer] :min_value (nil) Minimum value of the sequence.
15
+ # # @option options [Integer] :max_value (nil) Maximum value of the sequence.
16
+ # # @option options [Integer] :start_with (nil) The first value of the sequence.
17
+ # # @option options [Integer] :cache (1) The number of values to be generated and cached.
18
+ # # @option options [Boolean] :cycle (false) If the sequence should be reset to start
19
+ # # after its value reaches min/max value.
20
+ # # @option options [#to_s] :comment (nil) The comment describing the sequence.
21
+ # # @yield [s] the block with the sequence's definition
22
+ # # @yieldparam Object receiver of methods specifying the sequence
23
+ # # @return [void]
24
+ # #
25
+ # # The sequence can be dropped by its qualified name only
26
+ # #
27
+ # # ```ruby
28
+ # # drop_sequence "global_number"
29
+ # # ```
30
+ # #
31
+ # # For inversion provide options for the `create_sequence` operation as well:
32
+ # #
33
+ # # ```ruby
34
+ # # drop_sequence "global_id", as: "int2" do |s|
35
+ # # s.iterate_by 2
36
+ # # s.min_value 0
37
+ # # s.max_value 1999
38
+ # # s.start_with 1
39
+ # # s.cache 10
40
+ # # s.cycle true
41
+ # # s.comment "Global identifier"
42
+ # # end
43
+ # # ```
44
+ # #
45
+ # # The operation can be called with `if_exists` option to suppress
46
+ # # the exception in case when the sequence is absent:
47
+ # #
48
+ # # ```ruby
49
+ # # drop_sequence "global_number", if_exists: true
50
+ # # ```
51
+ # #
52
+ # # With the `force: :cascade` option the operation would remove
53
+ # # all the objects that use the sequence.
54
+ # #
55
+ # # ```ruby
56
+ # # drop_sequence "global_number", force: :cascade
57
+ # # ```
58
+ # #
59
+ # # In both cases the operation becomes irreversible due to
60
+ # # uncertainty of the previous state of the database.
61
+ # def drop_sequence(name, **options, &block); end
62
+ # end
63
+ module PGTrunk::Operations::Sequences
64
+ # @private
65
+ class DropSequence < Base
66
+ validates :if_not_exists, :new_name, absence: true
67
+
68
+ def to_sql(_version)
69
+ sql = "DROP SEQUENCE"
70
+ sql << " IF EXISTS" if if_exists
71
+ sql << " #{name.to_sql}"
72
+ sql << " CASCADE" if force == :cascade
73
+ sql << ";"
74
+ end
75
+
76
+ def invert
77
+ irreversible!("if_exists: true") if if_exists
78
+ irreversible!("force: :cascade") if force == :cascade
79
+ CreateSequence.new(**to_h.except(:if_exists, :force))
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: false
2
+
3
+ # @!parse
4
+ # class ActiveRecord::Migration
5
+ # # Rename a sequence
6
+ # #
7
+ # # @param [#to_s] name (nil) The current qualified name of the sequence
8
+ # # @option options [#to_s] :to (nil) The new qualified name for the sequence
9
+ # # @option options [Boolean] :if_exists (false) Suppress the error when the sequence is absent.
10
+ # # @return [void]
11
+ # #
12
+ # # The operation allows to change both name and schema
13
+ # #
14
+ # # ```ruby
15
+ # # rename_sequence "global_num", to: "sequences.global_number"
16
+ # # ```
17
+ # #
18
+ # # With the `if_exists: true` option the operation wouldn't raise
19
+ # # an exception in case the sequence hasn't been created yet.
20
+ # #
21
+ # # ```ruby
22
+ # # create_sequence "my_schema.global_id", if_exists: true
23
+ # # ```
24
+ # #
25
+ # # This option makes the migration irreversible due to uncertainty
26
+ # # of the previous state of the database.
27
+ # def rename_sequence(name, **options, &block); end
28
+ # end
29
+ module PGTrunk::Operations::Sequences
30
+ # @private
31
+ class RenameSequence < Base
32
+ validates :new_name, presence: true
33
+ validates :if_not_exists, :force, :type, :increment_by, :min_value,
34
+ :max_value, :start_with, :cache, :cycle, :comment, absence: true
35
+
36
+ def to_sql(_version)
37
+ [*change_schema, *change_name].join(" ")
38
+ end
39
+
40
+ def invert
41
+ irreversible!("if_exists: true") if if_exists
42
+ self.class.new(**to_h, name: new_name, to: name)
43
+ end
44
+
45
+ private
46
+
47
+ def change_schema
48
+ return if name.schema == new_name.schema
49
+
50
+ sql = "ALTER SEQUENCE"
51
+ sql << " IF EXISTS" if if_exists
52
+ sql << " #{name.to_sql} SET SCHEMA #{new_name.schema.inspect};"
53
+ end
54
+
55
+ def change_name
56
+ return if new_name.name == name.name
57
+
58
+ moved = name.merge(schema: new_name.schema)
59
+ sql = "ALTER SEQUENCE"
60
+ sql << " IF EXISTS" if if_exists
61
+ sql << " #{moved.to_sql} RENAME TO #{new_name.name.inspect};"
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ # nodoc
4
+ module PGTrunk::Operations
5
+ # @private
6
+ # Namespace for operations with sequences
7
+ module Sequences
8
+ require_relative "sequences/base"
9
+ require_relative "sequences/change_sequence"
10
+ require_relative "sequences/create_sequence"
11
+ require_relative "sequences/drop_sequence"
12
+ require_relative "sequences/rename_sequence"
13
+ end
14
+ end
@@ -1,61 +1,72 @@
1
1
  # frozen_string_literal: false
2
2
 
3
- # @!method ActiveRecord::Migration#create_statistics(name, **options, &block)
4
- # Create a custom statistics
5
- #
6
- # @param [#to_s] name (nil) The qualified name of the statistics
7
- # @option [Boolean] :if_not_exists (false)
8
- # Suppress the error when the statistics is already exist
9
- # @option [#to_s] table (nil)
10
- # The qualified name of the table whose statistics will be collected
11
- # @option [Array<Symbol>] kinds ([:dependencies, :mcv, :ndistinct])
12
- # The kinds of statistics to be collected (all by default).
13
- # Supported values in the array: :dependencies, :mcv, :ndistinct
14
- # @option [#to_s] :comment The description of the statistics
15
- # @yield [Proc] the block with the statistics' definition
16
- # @yieldparam The receiver of methods specifying the statistics
17
- #
18
- # The statistics can be created with explicit name:
19
- #
20
- # create_statistics "users_stats" do |s|
21
- # s.table "users"
22
- # s.columns "family", "name"
23
- # s.kinds :dependencies, :mcv, :ndistinct
24
- # s.comment "Statistics for users' names and families"
25
- # SQL
26
- #
27
- # The name can be generated as well:
28
- #
29
- # create_statistics do |s|
30
- # s.table "users"
31
- # s.columns "family", "name"
32
- # s.kinds :dependencies, :mcv, :ndistinct
33
- # s.comment "Statistics for users' names and families"
34
- # SQL
35
- #
36
- # Since v14 PostgreSQL have supported expressions in addition to columns:
37
- #
38
- # create_statistics "users_stats" do |s|
39
- # s.table "users"
40
- # s.columns "family"
41
- # s.expression "length(name)"
42
- # s.kinds :dependencies, :mcv, :ndistinct
43
- # s.comment "Statistics for users' name lengths and families"
44
- # SQL
45
- #
46
- # as well as statistics for the sole expression (kinds must be blank)
47
- # by columns of some table.
48
- #
49
- # create_statistics "users_stats" do |s|
50
- # s.table "users"
51
- # s.expression "length(name || ' ' || family)"
52
- # s.comment "Statistics for full name lengths"
53
- # SQL
54
- #
55
- # Use `if_not_exists: true` to suppress error in case the statistics
56
- # has already been created. This option, though, makes the migration
57
- # irreversible due to uncertainty of the previous state of the database.
58
-
3
+ # @!parse
4
+ # class ActiveRecord::Migration
5
+ # # Create a custom statistics
6
+ # #
7
+ # # @param [#to_s] name (nil) The qualified name of the statistics
8
+ # # @option options [Boolean] :if_not_exists (false)
9
+ # # Suppress the error when the statistics is already exist
10
+ # # @option options [#to_s] table (nil)
11
+ # # The qualified name of the table whose statistics will be collected
12
+ # # @option options [Array<Symbol>] kinds ([:dependencies, :mcv, :ndistinct])
13
+ # # The kinds of statistics to be collected (all by default).
14
+ # # Supported values in the array: :dependencies, :mcv, :ndistinct
15
+ # # @option options [#to_s] :comment The description of the statistics
16
+ # # @yield [s] the block with the statistics' definition
17
+ # # @yieldparam Object receiver of methods specifying the statistics
18
+ # # @return [void]
19
+ # #
20
+ # # The statistics can be created with explicit name:
21
+ # #
22
+ # # ```ruby
23
+ # # create_statistics "users_stats" do |s|
24
+ # # s.table "users"
25
+ # # s.columns "family", "name"
26
+ # # s.kinds :dependencies, :mcv, :ndistinct
27
+ # # s.comment "Statistics for users' names and families"
28
+ # # SQL
29
+ # # ```
30
+ # #
31
+ # # The name can be generated as well:
32
+ # #
33
+ # # ```ruby
34
+ # # create_statistics do |s|
35
+ # # s.table "users"
36
+ # # s.columns "family", "name"
37
+ # # s.kinds :dependencies, :mcv, :ndistinct
38
+ # # s.comment "Statistics for users' names and families"
39
+ # # SQL
40
+ # # ```
41
+ # #
42
+ # # Since v14 PostgreSQL have supported expressions in addition to columns:
43
+ # #
44
+ # # ```ruby
45
+ # # create_statistics "users_stats" do |s|
46
+ # # s.table "users"
47
+ # # s.columns "family"
48
+ # # s.expression "length(name)"
49
+ # # s.kinds :dependencies, :mcv, :ndistinct
50
+ # # s.comment "Statistics for users' name lengths and families"
51
+ # # SQL
52
+ # # ```
53
+ # #
54
+ # # as well as statistics for the sole expression (kinds must be blank)
55
+ # # by columns of some table.
56
+ # #
57
+ # # ```ruby
58
+ # # create_statistics "users_stats" do |s|
59
+ # # s.table "users"
60
+ # # s.expression "length(name || ' ' || family)"
61
+ # # s.comment "Statistics for full name lengths"
62
+ # # SQL
63
+ # # ```
64
+ # #
65
+ # # Use `if_not_exists: true` to suppress error in case the statistics
66
+ # # has already been created. This option, though, makes the migration
67
+ # # irreversible due to uncertainty of the previous state of the database.
68
+ # def create_statistics(name, **options, &block); end
69
+ # end
59
70
  module PGTrunk::Operations::Statistics
60
71
  # SQL snippet to fetch statistics in v10-13
61
72
  SQL_V10 = <<~SQL.freeze