activerecord-pg-format-db-structure 0.1.3 → 0.1.5
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +55 -1
- data/lib/activerecord-pg-format-db-structure/transforms/sort_schema_migrations.rb +23 -0
- data/lib/activerecord-pg-format-db-structure/transforms/sort_table_columns.rb +220 -0
- data/lib/activerecord-pg-format-db-structure/version.rb +1 -1
- data/lib/activerecord-pg-format-db-structure.rb +5 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 231e1ce74a64d5b68e27b122465855bf382cb24c5f573da3983b2ab1e11b3c23
|
4
|
+
data.tar.gz: 3341fb271ea14d6762cb879f28b6b526d51a76c37c83163fe26ce2955d9b1b83
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f06ac5c8140c6766f4dd77909ba62f60fb2b86b2453d4fad3eb190221de590025eb079e6c2e74999ff3e39fcf90dcac8b1751d52fa33bf9a33ec26ea9c05b91
|
7
|
+
data.tar.gz: 790f40bbc56b3b0062e72b7793a9c1645ea286f9a3f557f180aea1fc261dc52401ef721ee2320901cf2ec30d1dc6b763eb0662d30906ab6e351e0b4ab33b1377
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# activerecord-pg-format-db-structure
|
2
|
+
[](https://rubygems.org/gems/activerecord-pg-format-db-structure)
|
3
|
+

|
4
|
+
|
2
5
|
|
3
6
|
Automatically cleans up your `structure.sql` file after each rails migration.
|
4
7
|
|
@@ -9,6 +12,8 @@ By default, it will:
|
|
9
12
|
* Inline table constraints
|
10
13
|
* Move index creation below their corresponding tables
|
11
14
|
* Group `ALTER TABLE` statements into a single statement per table
|
15
|
+
* Sorts table column declarations (primary key / foreign keys / data / timestamp / constraints)
|
16
|
+
* Sorts `schema_migrations` inserts
|
12
17
|
* Removes unnecessary whitespace
|
13
18
|
|
14
19
|
The task will transform this raw `structure.sql`:
|
@@ -259,12 +264,14 @@ Rails.application.configure do
|
|
259
264
|
|
260
265
|
config.activerecord_pg_format_db_structure.transforms = [
|
261
266
|
ActiveRecordPgFormatDbStructure::Transforms::RemoveCommentsOnExtensions,
|
267
|
+
ActiveRecordPgFormatDbStructure::Transforms::SortSchemaMigrations,
|
262
268
|
ActiveRecordPgFormatDbStructure::Transforms::InlinePrimaryKeys,
|
263
269
|
# ActiveRecordPgFormatDbStructure::Transforms::InlineForeignKeys,
|
264
270
|
ActiveRecordPgFormatDbStructure::Transforms::InlineSerials,
|
265
271
|
ActiveRecordPgFormatDbStructure::Transforms::InlineConstraints,
|
266
272
|
ActiveRecordPgFormatDbStructure::Transforms::MoveIndicesAfterCreateTable,
|
267
|
-
ActiveRecordPgFormatDbStructure::Transforms::GroupAlterTableStatements
|
273
|
+
ActiveRecordPgFormatDbStructure::Transforms::GroupAlterTableStatements,
|
274
|
+
ActiveRecordPgFormatDbStructure::Transforms::SortTableColumns,
|
268
275
|
]
|
269
276
|
|
270
277
|
config.activerecord_pg_format_db_structure.deparser = ActiveRecordPgFormatDbStructure::Deparser
|
@@ -293,6 +300,10 @@ Remove unnecessary comment, whitespase and empty lines.
|
|
293
300
|
|
294
301
|
Remove COMMENT statement applied to extensions
|
295
302
|
|
303
|
+
### SortSchemaMigrations
|
304
|
+
|
305
|
+
Sort schema_migrations inserts to be in chronological order, helps with reducing merge conflicts.
|
306
|
+
|
296
307
|
### InlinePrimaryKeys
|
297
308
|
|
298
309
|
Inlines primary keys with the table declaration
|
@@ -334,6 +345,49 @@ table.
|
|
334
345
|
|
335
346
|
Should be run after other operations that inline alter statements.
|
336
347
|
|
348
|
+
### SortTableColumns
|
349
|
+
|
350
|
+
Sort table columns, by order of priority and alphabetically:
|
351
|
+
|
352
|
+
1. primary key
|
353
|
+
2. foreign keys
|
354
|
+
3. generic columns
|
355
|
+
4. timestamps
|
356
|
+
5. constraints
|
357
|
+
|
358
|
+
Note that you can define your own ordering by replacing the default `priority_mapping`:
|
359
|
+
|
360
|
+
```ruby
|
361
|
+
ActiveRecordPgFormatDbStructure::Transforms::SortTableColumns.priority_mapping = lambda do |sortable_entry|
|
362
|
+
case sortable_entry
|
363
|
+
in is_column: true, is_primary_key: true, name:
|
364
|
+
[0, name]
|
365
|
+
in is_column: true, is_foreign_key: true, name:
|
366
|
+
[1, name]
|
367
|
+
in is_column: true, is_timestamp: false, name:
|
368
|
+
[2, name]
|
369
|
+
in is_column: true, is_timestamp: true, name:
|
370
|
+
[3, name]
|
371
|
+
in is_constraint: true, name:
|
372
|
+
[5, name]
|
373
|
+
end
|
374
|
+
end
|
375
|
+
```
|
376
|
+
|
377
|
+
where `sortable_entry` is an instance of:
|
378
|
+
|
379
|
+
```ruby
|
380
|
+
SORTABLE_ENTRY = Data.define(
|
381
|
+
:name,
|
382
|
+
:is_column,
|
383
|
+
:is_constraint,
|
384
|
+
:is_primary_key,
|
385
|
+
:is_foreign_key,
|
386
|
+
:is_timestamp,
|
387
|
+
:raw_entry
|
388
|
+
)
|
389
|
+
```
|
390
|
+
|
337
391
|
## Deparser
|
338
392
|
|
339
393
|
Returns an SQL string from raw PgQuery statements.
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
module ActiveRecordPgFormatDbStructure
|
6
|
+
module Transforms
|
7
|
+
# Sort schema migration inserts to reduce merge conflicts
|
8
|
+
class SortSchemaMigrations < Base
|
9
|
+
def transform!
|
10
|
+
raw_statements.each do |raw_statement|
|
11
|
+
next unless raw_statement.stmt.to_h in insert_stmt: {
|
12
|
+
relation: { relname: "schema_migrations" },
|
13
|
+
select_stmt: { select_stmt: { values_lists: _ } }
|
14
|
+
}
|
15
|
+
|
16
|
+
raw_statement.stmt.insert_stmt.select_stmt.select_stmt.values_lists.sort_by! do |list|
|
17
|
+
list.list.items.first.a_const.sval.sval
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
module ActiveRecordPgFormatDbStructure
|
6
|
+
module Transforms
|
7
|
+
# Sort table columns
|
8
|
+
class SortTableColumns < Base
|
9
|
+
SORTABLE_ENTRY = Data.define(
|
10
|
+
:name,
|
11
|
+
:is_column,
|
12
|
+
:is_constraint,
|
13
|
+
:is_primary_key,
|
14
|
+
:is_foreign_key,
|
15
|
+
:is_timestamp,
|
16
|
+
:raw_entry
|
17
|
+
)
|
18
|
+
|
19
|
+
class << self
|
20
|
+
attr_accessor :priority_mapping
|
21
|
+
end
|
22
|
+
|
23
|
+
self.priority_mapping = lambda do |sortable_entry|
|
24
|
+
case sortable_entry
|
25
|
+
in is_column: true, is_primary_key: true, name:
|
26
|
+
[0, name]
|
27
|
+
in is_column: true, is_foreign_key: true, name:
|
28
|
+
[1, name]
|
29
|
+
in is_column: true, is_timestamp: false, name:
|
30
|
+
[2, name]
|
31
|
+
in is_column: true, is_timestamp: true, name:
|
32
|
+
[3, name]
|
33
|
+
in is_constraint: true, name:
|
34
|
+
[5, name]
|
35
|
+
# :nocov:
|
36
|
+
# non-reachable else
|
37
|
+
# :nocov:
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def transform!
|
42
|
+
foreign_keys = extract_foreign_keys
|
43
|
+
primary_keys = extract_primary_keys
|
44
|
+
raw_statements.each do |raw_statement|
|
45
|
+
next unless raw_statement.stmt.to_h in create_stmt: { relation: { schemaname:, relname: } }
|
46
|
+
|
47
|
+
raw_statement.stmt.create_stmt.table_elts.sort_by! do |table_elt|
|
48
|
+
elt_priority(
|
49
|
+
table_elt:,
|
50
|
+
primary_keys: primary_keys.fetch({ schemaname:, relname: }, Set.new),
|
51
|
+
foreign_keys: foreign_keys.fetch({ schemaname:, relname: }, Set.new)
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def extract_primary_keys
|
60
|
+
primary_keys = {}
|
61
|
+
raw_statements.each do |raw_statement|
|
62
|
+
case raw_statement.stmt.to_h
|
63
|
+
in alter_table_stmt: {
|
64
|
+
objtype: :OBJECT_TABLE,
|
65
|
+
relation: {
|
66
|
+
schemaname:,
|
67
|
+
relname:
|
68
|
+
}
|
69
|
+
}
|
70
|
+
primary_keys[{ schemaname:, relname: }] ||= Set.new
|
71
|
+
raw_statement.stmt.alter_table_stmt.cmds.each do |cmd|
|
72
|
+
extract_primary_keys_from_alter_table_cmd(cmd:).each do |key|
|
73
|
+
primary_keys[{ schemaname:, relname: }] << key
|
74
|
+
end
|
75
|
+
end
|
76
|
+
in create_stmt: { relation: { schemaname:, relname: } }
|
77
|
+
primary_keys[{ schemaname:, relname: }] ||= Set.new
|
78
|
+
raw_statement.stmt.create_stmt.table_elts.each do |table_elt|
|
79
|
+
extract_primary_keys_from_table_elt(table_elt:).each do |key|
|
80
|
+
primary_keys[{ schemaname:, relname: }] << key
|
81
|
+
end
|
82
|
+
end
|
83
|
+
else
|
84
|
+
end
|
85
|
+
end
|
86
|
+
primary_keys
|
87
|
+
end
|
88
|
+
|
89
|
+
def extract_primary_keys_from_alter_table_cmd(cmd:)
|
90
|
+
if cmd.to_h in {
|
91
|
+
alter_table_cmd: {
|
92
|
+
subtype: :AT_AddConstraint,
|
93
|
+
def: {
|
94
|
+
constraint: {
|
95
|
+
contype: :CONSTR_PRIMARY
|
96
|
+
}
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
cmd.alter_table_cmd.def.constraint.keys.map do |key|
|
101
|
+
key.string.sval
|
102
|
+
end
|
103
|
+
else
|
104
|
+
[]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def extract_primary_keys_from_table_elt(table_elt:)
|
109
|
+
case table_elt.to_h
|
110
|
+
in column_def: { constraints: [*, {constraint: {contype: :CONSTR_PRIMARY}}, *] }
|
111
|
+
[table_elt.column_def.colname]
|
112
|
+
in constraint: { contype: :CONSTR_PRIMARY }
|
113
|
+
table_elt.constraint.keys.map do |key|
|
114
|
+
key.string.sval
|
115
|
+
end
|
116
|
+
else
|
117
|
+
[]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def extract_foreign_keys
|
122
|
+
foreign_keys = {}
|
123
|
+
raw_statements.each do |raw_statement|
|
124
|
+
case raw_statement.stmt.to_h
|
125
|
+
in alter_table_stmt: {
|
126
|
+
objtype: :OBJECT_TABLE,
|
127
|
+
relation: {
|
128
|
+
schemaname:,
|
129
|
+
relname:
|
130
|
+
}
|
131
|
+
}
|
132
|
+
foreign_keys[{ schemaname:, relname: }] ||= Set.new
|
133
|
+
raw_statement.stmt.alter_table_stmt.cmds.each do |cmd|
|
134
|
+
extract_foreign_keys_from_alter_table_cmd(cmd:).each do |key|
|
135
|
+
foreign_keys[{ schemaname:, relname: }] << key
|
136
|
+
end
|
137
|
+
end
|
138
|
+
in create_stmt: { relation: { schemaname:, relname: } }
|
139
|
+
foreign_keys[{ schemaname:, relname: }] ||= Set.new
|
140
|
+
raw_statement.stmt.create_stmt.table_elts.each do |table_elt|
|
141
|
+
extract_foreign_keys_from_table_elt(table_elt:).each do |key|
|
142
|
+
foreign_keys[{ schemaname:, relname: }] << key
|
143
|
+
end
|
144
|
+
end
|
145
|
+
else
|
146
|
+
end
|
147
|
+
end
|
148
|
+
foreign_keys
|
149
|
+
end
|
150
|
+
|
151
|
+
def extract_foreign_keys_from_alter_table_cmd(cmd:)
|
152
|
+
if cmd.to_h in {
|
153
|
+
alter_table_cmd: {
|
154
|
+
subtype: :AT_AddConstraint,
|
155
|
+
def: {
|
156
|
+
constraint: {
|
157
|
+
contype: :CONSTR_FOREIGN
|
158
|
+
}
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
cmd.alter_table_cmd.def.constraint.fk_attrs.map do |fk_attr|
|
163
|
+
fk_attr.string.sval
|
164
|
+
end
|
165
|
+
else
|
166
|
+
[]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def extract_foreign_keys_from_table_elt(table_elt:)
|
171
|
+
case table_elt.to_h
|
172
|
+
in column_def: { constraints: [*, {constraint: {contype: :CONSTR_FOREIGN}}, *] }
|
173
|
+
[table_elt.column_def.colname]
|
174
|
+
in constraint: { contype: :CONSTR_FOREIGN }
|
175
|
+
table_elt.constraint.fk_attrs.map do |fk_attr|
|
176
|
+
fk_attr.string.sval
|
177
|
+
end
|
178
|
+
else
|
179
|
+
[]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def elt_priority(table_elt:, primary_keys:, foreign_keys:)
|
184
|
+
case table_elt.to_h
|
185
|
+
in column_def: _
|
186
|
+
self.class.priority_mapping[
|
187
|
+
SORTABLE_ENTRY.new(
|
188
|
+
name: table_elt.column_def.colname,
|
189
|
+
is_column: true,
|
190
|
+
is_constraint: false,
|
191
|
+
is_primary_key: primary_keys.include?(table_elt.column_def.colname),
|
192
|
+
is_timestamp: timestamp?(table_elt.column_def),
|
193
|
+
is_foreign_key: foreign_keys.include?(table_elt.column_def.colname),
|
194
|
+
raw_entry: table_elt
|
195
|
+
)
|
196
|
+
]
|
197
|
+
in constraint: _
|
198
|
+
self.class.priority_mapping[
|
199
|
+
SORTABLE_ENTRY.new(
|
200
|
+
name: table_elt.constraint.conname,
|
201
|
+
is_column: false,
|
202
|
+
is_constraint: true,
|
203
|
+
is_primary_key: false,
|
204
|
+
is_timestamp: false,
|
205
|
+
is_foreign_key: false,
|
206
|
+
raw_entry: table_elt
|
207
|
+
)
|
208
|
+
]
|
209
|
+
# :nocov:
|
210
|
+
# non-reachable else
|
211
|
+
# :nocov:
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def timestamp?(table_elt)
|
216
|
+
table_elt.type_name.names.any? { |name| name.to_h in string: { sval: "timestamp" } }
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -11,6 +11,8 @@ require_relative "activerecord-pg-format-db-structure/transforms/inline_foreign_
|
|
11
11
|
require_relative "activerecord-pg-format-db-structure/transforms/move_indices_after_create_table"
|
12
12
|
require_relative "activerecord-pg-format-db-structure/transforms/inline_constraints"
|
13
13
|
require_relative "activerecord-pg-format-db-structure/transforms/group_alter_table_statements"
|
14
|
+
require_relative "activerecord-pg-format-db-structure/transforms/sort_schema_migrations"
|
15
|
+
require_relative "activerecord-pg-format-db-structure/transforms/sort_table_columns"
|
14
16
|
|
15
17
|
module ActiveRecordPgFormatDbStructure
|
16
18
|
DEFAULT_PREPROCESSORS = [
|
@@ -19,12 +21,14 @@ module ActiveRecordPgFormatDbStructure
|
|
19
21
|
|
20
22
|
DEFAULT_TRANSFORMS = [
|
21
23
|
Transforms::RemoveCommentsOnExtensions,
|
24
|
+
Transforms::SortSchemaMigrations,
|
22
25
|
Transforms::InlinePrimaryKeys,
|
23
26
|
# Transforms::InlineForeignKeys,
|
24
27
|
Transforms::InlineSerials,
|
25
28
|
Transforms::InlineConstraints,
|
26
29
|
Transforms::MoveIndicesAfterCreateTable,
|
27
|
-
Transforms::GroupAlterTableStatements
|
30
|
+
Transforms::GroupAlterTableStatements,
|
31
|
+
Transforms::SortTableColumns
|
28
32
|
].freeze
|
29
33
|
|
30
34
|
DEFAULT_DEPARSER = Deparser
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-pg-format-db-structure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jell
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-02-
|
10
|
+
date: 2025-02-08 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: pg_query
|
@@ -53,6 +53,8 @@ files:
|
|
53
53
|
- lib/activerecord-pg-format-db-structure/transforms/inline_serials.rb
|
54
54
|
- lib/activerecord-pg-format-db-structure/transforms/move_indices_after_create_table.rb
|
55
55
|
- lib/activerecord-pg-format-db-structure/transforms/remove_comments_on_extensions.rb
|
56
|
+
- lib/activerecord-pg-format-db-structure/transforms/sort_schema_migrations.rb
|
57
|
+
- lib/activerecord-pg-format-db-structure/transforms/sort_table_columns.rb
|
56
58
|
- lib/activerecord-pg-format-db-structure/version.rb
|
57
59
|
homepage: https://github.com/ReifyAB/activerecord-pg-format-db-structure
|
58
60
|
licenses:
|