activerecord-clean-db-structure 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6ee2c62ab134b71d0ccce4e02928038c983c4403
4
- data.tar.gz: 6264e28734c7ca2066fefbd2a6ef3dc68e68c1c1
2
+ SHA256:
3
+ metadata.gz: 2e2fac86758bda878d5fea13096bc67af323ddffbb73e889c4cfc40bcb5a28c3
4
+ data.tar.gz: 7f425da0e19c813789a988326a07a69fe5d54e6e5987af9cfa1965fe09b1e27f
5
5
  SHA512:
6
- metadata.gz: bf88f74415c68337f2b9f409a358085d4d71c64be4696a942b973a2d34c420024f9bde791f3edb120c1df26357d8c3d0f5d47ed94892437b63ef72b388ae5175
7
- data.tar.gz: 1b086ae9152601942d7999ab0491c2d5d4e812e34208688f5bd8d8437530d74c868b0ca6a5639b237b2cb9cdd80c3caa1cd7871c601100b586ee748a86fe5def
6
+ metadata.gz: b87f9ddf4b6e9a8203463627c88164af61202fd054450dde7ff8316e10bf9849e05aa7077c72dac05aef27f1b0796d3f9c41538bb939f62b8958652c9664bc21
7
+ data.tar.gz: 591a5cb637d76600972fc5552e1124d9bfe6c46823622bfd03fac2e04facd3baa7f74949894f2e6649d808eb59c2912dca8a1a22d4b6bad2c429972d8b9a957c
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ * ...
6
+
7
+ ## 0.4.0 2019-08-27
8
+
9
+ * Add "indexes_after_tables" option to allow indexes to be placed following the respective tables [#13](https://github.com/lfittl/activerecord-clean-db-structure/pull/13) [Giovanni Kock Bonetti](https://github.com/giovannibonetti)
10
+ * Add "order_schema_migrations_values" option to prevent schema_migrations values causing merge conflicts [#15](https://github.com/lfittl/activerecord-clean-db-structure/pull/15) [Nicke van Oorschot](https://github.com/nvanoorschot)
11
+ * Add "order_column_definitions" option to sort table columns alphabetically [#11](https://github.com/lfittl/activerecord-clean-db-structure/pull/11) [RKushnir](https://github.com/RKushnir)
12
+ * Generalize handling of schema names to not assume public
13
+ * Rails 6 support
14
+ * Fix Rails 6 compatibility [#16](https://github.com/lfittl/activerecord-clean-db-structure/pull/16) [Giovanni Kock Bonetti](https://github.com/giovannibonetti)
15
+ * Fix handling of multiple structure.sql files
16
+ * Remove Postgres 12 specific GUCs
17
+ * Generalize handling of schema names to not assume public
18
+ * Fix whitespace issue for config settings, remove default_with_oids
19
+
20
+
3
21
  ## 0.3.0 2019-05-07
4
22
 
5
23
  * Add "ignore_ids" option to allow disabling of primary key substitution logic [#12](https://github.com/lfittl/activerecord-clean-db-structure/pull/12) [Vladimir Dementyev](https://github.com/palkan)
@@ -1,24 +1,23 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- activerecord-clean-db-structure (0.3.0)
4
+ activerecord-clean-db-structure (0.4.0)
5
5
  activerecord (>= 4.2)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activemodel (5.2.3)
11
- activesupport (= 5.2.3)
12
- activerecord (5.2.3)
13
- activemodel (= 5.2.3)
14
- activesupport (= 5.2.3)
15
- arel (>= 9.0)
16
- activesupport (5.2.3)
10
+ activemodel (6.0.0)
11
+ activesupport (= 6.0.0)
12
+ activerecord (6.0.0)
13
+ activemodel (= 6.0.0)
14
+ activesupport (= 6.0.0)
15
+ activesupport (6.0.0)
17
16
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
17
  i18n (>= 0.7, < 2)
19
18
  minitest (~> 5.1)
20
19
  tzinfo (~> 1.1)
21
- arel (9.0.0)
20
+ zeitwerk (~> 2.1, >= 2.1.8)
22
21
  concurrent-ruby (1.1.5)
23
22
  i18n (1.6.0)
24
23
  concurrent-ruby (~> 1.0)
@@ -27,6 +26,7 @@ GEM
27
26
  thread_safe (0.3.6)
28
27
  tzinfo (1.2.5)
29
28
  thread_safe (~> 0.1)
29
+ zeitwerk (2.1.9)
30
30
 
31
31
  PLATFORMS
32
32
  ruby
@@ -36,4 +36,4 @@ DEPENDENCIES
36
36
  rake (~> 0)
37
37
 
38
38
  BUNDLED WITH
39
- 1.16.1
39
+ 1.17.3
data/README.md CHANGED
@@ -38,6 +38,55 @@ Rails.application.configure do
38
38
  end
39
39
  ```
40
40
 
41
+ ## Other options
42
+
43
+ You can optionally have indexes following the respective tables setting `indexes_after_tables`:
44
+
45
+ ```ruby
46
+ Rails.application.configure do
47
+ config.activerecord_clean_db_structure.indexes_after_tables = true
48
+ end
49
+ ```
50
+
51
+ When it is enabled the structure looks like this:
52
+
53
+ ```sql
54
+ CREATE TABLE public.users (
55
+ id SERIAL PRIMARY KEY,
56
+ tenant_id integer,
57
+ email text NOT NULL
58
+ );
59
+
60
+ CREATE INDEX index_users_on_tentant_id ON public.users USING btree (tenant_id);
61
+ CREATE UNIQUE INDEX index_users_on_email ON public.users USING btree (email);
62
+ ```
63
+
64
+ To enable sorting the table column definitions alphabetically, discarding the actual order provided by `pg_dump`, set `order_column_definitions`:
65
+
66
+ ```ruby
67
+ Rails.application.configure do
68
+ config.activerecord_clean_db_structure.order_column_definitions = true
69
+ end
70
+ ```
71
+
72
+ You can have the schema_migrations values reorganized to prevent merge conflicts by setting `order_schema_migrations_values`:
73
+
74
+ ```ruby
75
+ Rails.application.configure do
76
+ config.activerecord_clean_db_structure.order_schema_migrations_values = true
77
+ end
78
+ ```
79
+
80
+ When it is enabled the values are ordered chronological and the semicolon is placed on a separate line:
81
+
82
+ ```sql
83
+ INSERT INTO "schema_migrations" (version) VALUES
84
+ ('20190503120501')
85
+ ,('20190508123941')
86
+ ,('20190508132644')
87
+ ;
88
+ ```
89
+
41
90
  ## Authors
42
91
 
43
92
  * [Lukas Fittl](https://github.com/lfittl)
@@ -15,8 +15,11 @@ module ActiveRecordCleanDbStructure
15
15
 
16
16
  # Remove version-specific output
17
17
  dump.gsub!(/^-- Dumped.*/, '')
18
- dump.gsub!(/^SET row_security = off;$/, '') # 9.5
19
- dump.gsub!(/^SET idle_in_transaction_session_timeout = 0;$/, '') # 9.6
18
+ dump.gsub!(/^SET row_security = off;\n/m, '') # 9.5
19
+ dump.gsub!(/^SET idle_in_transaction_session_timeout = 0;\n/m, '') # 9.6
20
+ dump.gsub!(/^SET default_with_oids = false;\n/m, '') # all older than 12
21
+ dump.gsub!(/^SET xmloption = content;\n/m, '') # 12
22
+ dump.gsub!(/^SET default_table_access_method = heap;\n/m, '') # 12
20
23
 
21
24
  # Remove pg_stat_statements extension (its not relevant to the code)
22
25
  dump.gsub!(/^CREATE EXTENSION IF NOT EXISTS pg_stat_statements.*/, '')
@@ -30,7 +33,7 @@ module ActiveRecordCleanDbStructure
30
33
  dump.gsub!(/^COMMENT ON EXTENSION .*/, '')
31
34
 
32
35
  # Remove useless, version-specific parts of comments
33
- dump.gsub!(/^-- (.*); Schema: (public|-); Owner: -.*/, '-- \1')
36
+ dump.gsub!(/^-- (.*); Schema: ([\w_\.]+|-); Owner: -.*/, '-- \1')
34
37
 
35
38
  # Remove useless comment lines
36
39
  dump.gsub!(/^--$/, '')
@@ -41,8 +44,8 @@ module ActiveRecordCleanDbStructure
41
44
  # This is a bit optimistic, but works as long as you don't have an id field thats not a sequence/uuid
42
45
  dump.gsub!(/^ id integer NOT NULL(,)?$/, ' id SERIAL PRIMARY KEY\1')
43
46
  dump.gsub!(/^ id bigint NOT NULL(,)?$/, ' id BIGSERIAL PRIMARY KEY\1')
44
- dump.gsub!(/^ id uuid DEFAULT (public\.)?uuid_generate_v4\(\) NOT NULL(,)?$/, ' id uuid DEFAULT \1uuid_generate_v4() PRIMARY KEY\2')
45
- dump.gsub!(/^ id uuid DEFAULT (public\.)?gen_random_uuid\(\) NOT NULL(,)?$/, ' id uuid DEFAULT \1gen_random_uuid() PRIMARY KEY\2')
47
+ dump.gsub!(/^ id uuid DEFAULT ([\w_]+\.)?uuid_generate_v4\(\) NOT NULL(,)?$/, ' id uuid DEFAULT \1uuid_generate_v4() PRIMARY KEY\2')
48
+ dump.gsub!(/^ id uuid DEFAULT ([\w_]+\.)?gen_random_uuid\(\) NOT NULL(,)?$/, ' id uuid DEFAULT \1gen_random_uuid() PRIMARY KEY\2')
46
49
  dump.gsub!(/^CREATE SEQUENCE [\w\.]+_id_seq\s+(AS integer\s+)?START WITH 1\s+INCREMENT BY 1\s+NO MINVALUE\s+NO MAXVALUE\s+CACHE 1;$/, '')
47
50
  dump.gsub!(/^ALTER SEQUENCE [\w\.]+_id_seq OWNED BY .*;$/, '')
48
51
  dump.gsub!(/^ALTER TABLE ONLY [\w\.]+ ALTER COLUMN id SET DEFAULT nextval\('[\w\.]+_id_seq'::regclass\);$/, '')
@@ -57,9 +60,9 @@ module ActiveRecordCleanDbStructure
57
60
  inherited_tables = dump.scan(inherited_tables_regexp).map(&:first)
58
61
  dump.gsub!(inherited_tables_regexp, '')
59
62
  inherited_tables.each do |inherited_table|
60
- dump.gsub!(/ALTER TABLE ONLY (public\.)?#{inherited_table}[^;]+;/, '')
63
+ dump.gsub!(/ALTER TABLE ONLY ([\w_]+\.)?#{inherited_table}[^;]+;/, '')
61
64
 
62
- index_regexp = /CREATE INDEX ([\w_]+) ON (public\.)?#{inherited_table}[^;]+;/m
65
+ index_regexp = /CREATE INDEX ([\w_]+) ON ([\w_]+\.)?#{inherited_table}[^;]+;/m
63
66
  dump.scan(index_regexp).map(&:first).each do |inherited_table_index|
64
67
  dump.gsub!("-- Name: #{inherited_table_index}; Type: INDEX", '')
65
68
  end
@@ -67,31 +70,122 @@ module ActiveRecordCleanDbStructure
67
70
  end
68
71
 
69
72
  # Remove partitioned tables
70
- partitioned_tables_regexp = /-- Name: ([\w_\.]+); Type: TABLE\n\n[^;]+?PARTITION OF [\w_\.]+\n[^;]+?;/m
71
- partitioned_tables = dump.scan(partitioned_tables_regexp).map(&:first)
72
- dump.gsub!(partitioned_tables_regexp, '')
73
+ partitioned_tables = []
74
+
75
+ # Postgres 12 pg_dump will output separate ATTACH PARTITION statements (even when run against an 11 or older server)
76
+ partitioned_tables_regexp1 = /ALTER TABLE ONLY [\w_\.]+ ATTACH PARTITION ([\w_\.]+)/
77
+ partitioned_tables += dump.scan(partitioned_tables_regexp1).map(&:last)
78
+
79
+ # Earlier versions use an inline PARTITION OF
80
+ partitioned_tables_regexp2 = /-- Name: ([\w_\.]+); Type: TABLE\n\n[^;]+?PARTITION OF [\w_\.]+\n[^;]+?;/m
81
+ partitioned_tables += dump.scan(partitioned_tables_regexp2).map(&:first)
82
+
73
83
  partitioned_tables.each do |partitioned_table|
74
- dump.gsub!(/ALTER TABLE ONLY (public\.)?#{partitioned_table}[^;]+;/, '')
84
+ partitioned_schema_name, partitioned_table_name_only = partitioned_table.split('.', 2)
85
+ dump.gsub!(/-- Name: #{partitioned_table_name_only}; Type: TABLE/, '')
86
+ dump.gsub!(/CREATE TABLE #{partitioned_table} \([^;]+;/m, '')
87
+ dump.gsub!(/ALTER TABLE ONLY ([\w_\.]+) ATTACH PARTITION #{partitioned_table}[^;]+;/m, '')
88
+
89
+ dump.gsub!(/ALTER TABLE ONLY ([\w_]+\.)?#{partitioned_table}[^;]+;/, '')
75
90
  dump.gsub!(/-- Name: #{partitioned_table} [^;]+; Type: DEFAULT/, '')
76
91
 
77
- index_regexp = /CREATE INDEX ([\w_]+) ON (public\.)?#{partitioned_table}[^;]+;/m
78
- dump.scan(index_regexp).map(&:first).each do |partitioned_table_index|
92
+ index_regexp = /CREATE (UNIQUE )?INDEX ([\w_]+) ON ([\w_]+\.)?#{partitioned_table}[^;]+;/m
93
+ dump.scan(index_regexp).each do |m|
94
+ partitioned_table_index = m[1]
79
95
  dump.gsub!("-- Name: #{partitioned_table_index}; Type: INDEX ATTACH", '')
80
96
  dump.gsub!("-- Name: #{partitioned_table_index}; Type: INDEX", '')
81
- dump.gsub!(/ALTER INDEX ([\w_\.]+) ATTACH PARTITION (public\.)?#{partitioned_table_index};/, '')
97
+ dump.gsub!(/ALTER INDEX ([\w_\.]+) ATTACH PARTITION ([\w_]+\.)?#{partitioned_table_index};/, '')
82
98
  end
83
99
  dump.gsub!(index_regexp, '')
84
100
 
85
- dump.gsub!(/-- Name: #{partitioned_table}_pkey; Type: INDEX ATTACH\n\n[^;]+?ATTACH PARTITION (public\.)?#{partitioned_table}_pkey;/, '')
101
+ dump.gsub!(/-- Name: #{partitioned_table}_pkey; Type: INDEX ATTACH\n\n[^;]+?ATTACH PARTITION ([\w_]+\.)?#{partitioned_table}_pkey;/, '')
86
102
  end
87
103
  # This is mostly done to allow restoring Postgres 11 output on Postgres 10
88
104
  dump.gsub!(/CREATE INDEX ([\w_]+) ON ONLY/, 'CREATE INDEX \\1 ON')
89
105
 
90
- # Remove whitespace between schema migration INSERTS to make editing easier
91
- dump.gsub!(/^(INSERT INTO schema_migrations .*)\n\n/, "\\1\n")
106
+ if options[:order_schema_migrations_values] == true
107
+ schema_migrations_cleanup
108
+ else
109
+ # Remove whitespace between schema migration INSERTS to make editing easier
110
+ dump.gsub!(/^(INSERT INTO schema_migrations .*)\n\n/, "\\1\n")
111
+ end
112
+
113
+ if options[:indexes_after_tables] == true
114
+ # Extract indexes, remove comments and place them just after the respective tables
115
+ indexes =
116
+ dump
117
+ .scan(/^CREATE.+INDEX.+ON.+\n/)
118
+ .group_by { |line| line.scan(/\b\w+\.\w+\b/).first }
119
+ .transform_values(&:join)
120
+
121
+ dump.gsub!(/^CREATE( UNIQUE)? INDEX \w+ ON .+\n+/, '')
122
+ dump.gsub!(/^-- Name: \w+; Type: INDEX\n+/, '')
123
+ indexes.each do |table, indexes_for_table|
124
+ dump.gsub!(/^(CREATE TABLE #{table}\b(:?[^;\n]*\n)+\);\n)/) { $1 + "\n" + indexes_for_table }
125
+ end
126
+ end
92
127
 
93
128
  # Reduce 2+ lines of whitespace to one line of whitespace
94
129
  dump.gsub!(/\n{2,}/m, "\n\n")
130
+
131
+ if options[:order_column_definitions] == true
132
+ dump.replace(order_column_definitions(dump))
133
+ end
134
+ end
135
+
136
+ def order_column_definitions(source)
137
+ result = []
138
+
139
+ parse_column_name = ->(line) { line.match(/^ "?([^" ]+)/)[1] }
140
+ with_column_separator = ->(line) { line.sub(/,?\n$/, ",\n") }
141
+ without_column_separator = ->(line) { line.sub(/,\n$/, "\n") }
142
+
143
+ inside_table = false
144
+ columns = []
145
+
146
+ source.each_line do |source_line|
147
+ if source_line.start_with?("CREATE TABLE")
148
+ inside_table = true
149
+ columns = []
150
+ result << source_line
151
+ elsif source_line.start_with?(");")
152
+ if inside_table
153
+ inside_table = false
154
+ columns.sort_by!(&:first)
155
+
156
+ columns[0..-2].each do |_, line|
157
+ result << with_column_separator[line]
158
+ end
159
+
160
+ result << without_column_separator[columns.last[1]]
161
+ end
162
+
163
+ result << source_line
164
+ elsif inside_table
165
+ columns << [parse_column_name[source_line], source_line]
166
+ else
167
+ result << source_line
168
+ end
169
+ end
170
+
171
+ result.join
172
+ end
173
+
174
+ private
175
+
176
+ # Cleanup of schema_migrations values to prevent merge conflicts:
177
+ # - sorts all values chronological
178
+ # - places the comma's in front of each value (except for the first)
179
+ # - places the semicolon on a separate last line
180
+ def schema_migrations_cleanup
181
+ # Read all schema_migrations values from the dump.
182
+ values = dump.scan(/^(\(\'\d{14}\'\))[,;]\n/).flatten.sort
183
+
184
+ # Replace the schema_migrations values.
185
+ dump.sub!(
186
+ /(?<=INSERT INTO "schema_migrations" \(version\) VALUES).+;\n*/m,
187
+ "\n #{values.join("\n,")}\n;\n\n"
188
+ )
95
189
  end
96
190
  end
97
191
  end
@@ -1,18 +1,23 @@
1
1
  require 'activerecord-clean-db-structure/clean_dump'
2
2
 
3
3
  Rake::Task['db:structure:dump'].enhance do
4
- filenames = ENV['DB_STRUCTURE']
4
+ filenames = []
5
+ filenames << ENV['DB_STRUCTURE'] if ENV.key?('DB_STRUCTURE')
5
6
 
6
7
  if ActiveRecord::VERSION::MAJOR >= 6
7
- ActiveRecord::Tasks::DatabaseTasks.for_each do |spec_name|
8
- filenames ||= Rails.application.config.paths['db'].map do |path|
9
- File.join(path, spec_name + '_structure.sql')
8
+ # Based on https://github.com/rails/rails/pull/36560/files
9
+ databases = ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
10
+ ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |spec_name|
11
+ Rails.application.config.paths['db'].each do |path|
12
+ filenames << File.join(path, spec_name + '_structure.sql')
10
13
  end
11
14
  end
12
15
  end
13
16
 
14
- filenames ||= Rails.application.config.paths['db'].map do |path|
15
- File.join(path, 'structure.sql')
17
+ unless filenames.present?
18
+ Rails.application.config.paths['db'].each do |path|
19
+ filenames << File.join(path, 'structure.sql')
20
+ end
16
21
  end
17
22
 
18
23
  filenames.each do |filename|
@@ -1,3 +1,3 @@
1
1
  module ActiveRecordCleanDbStructure
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-clean-db-structure
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lukas Fittl
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-07 00:00:00.000000000 Z
11
+ date: 2019-08-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -76,8 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
76
76
  - !ruby/object:Gem::Version
77
77
  version: '0'
78
78
  requirements: []
79
- rubyforge_project:
80
- rubygems_version: 2.6.13
79
+ rubygems_version: 3.0.3
81
80
  signing_key:
82
81
  specification_version: 4
83
82
  summary: Automatic cleanup for the Rails db/structure.sql file (ActiveRecord/PostgreSQL)