ridgepole 0.9.6 → 1.0.2

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.
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_record/connection_adapters/postgresql_adapter'
4
+
5
+ module Ridgepole
6
+ module Ext
7
+ module PostgreSQLAdapter
8
+ module Partitioning
9
+ def supports_partitions?
10
+ ActiveRecord::VERSION::MAJOR >= 6 && postgresql_version >= 100_000 # >= 10.0
11
+ end
12
+
13
+ def table_options(table_name)
14
+ options = partition_options(table_name)
15
+ if options
16
+ (super || {}).merge(options: "PARTITION BY #{options[:type].to_s.upcase}(#{options[:columns].join(',')})")
17
+ else
18
+ super
19
+ end
20
+ end
21
+
22
+ def partition_options(table_name)
23
+ return unless supports_partitions?
24
+
25
+ scope = quoted_scope(table_name)
26
+ result = query_value(<<~SQL, 'SCHEMA')
27
+ SELECT pg_get_partkeydef(t.oid)
28
+ FROM pg_class t
29
+ LEFT JOIN pg_namespace n ON n.oid = t.relnamespace
30
+ WHERE t.relname = #{scope[:name]}
31
+ AND n.nspname = #{scope[:schema]}
32
+ SQL
33
+ return unless result
34
+
35
+ type, *columns = result.scan(/\w+/).map { |value| value.downcase.to_sym }
36
+ { type: type, columns: columns }
37
+ end
38
+
39
+ def partition(table_name)
40
+ options = partition_options(table_name)
41
+ return unless options
42
+
43
+ scope = quoted_scope(table_name)
44
+ partition_info = query(<<~SQL, 'SCHEMA')
45
+ SELECT p.relname, pg_get_expr(p.relpartbound, p.oid, true)
46
+ FROM pg_class t
47
+ JOIN pg_inherits i on i.inhparent = t.oid
48
+ JOIN pg_class p on p.oid = i.inhrelid
49
+ WHERE t.relname = #{scope[:name]}
50
+ AND p.relnamespace::regnamespace::text = #{scope[:schema]}
51
+ ORDER BY p.relname
52
+ SQL
53
+
54
+ partition_definitions = partition_info.map do |row|
55
+ values = case options[:type]
56
+ when :list
57
+ values = row[1].match(/FOR VALUES IN \((?<csv>.+)\)$/)[:csv].split(',').map(&:strip).map { |value| cast_value(value) }
58
+ { in: Array.wrap(values) }
59
+ when :range
60
+ match = row[1].match(/FOR VALUES FROM \((?<from>.+)\) TO \((?<to>.+)\)/)
61
+ from = match[:from].split(',').map(&:strip).map { |value| cast_value(value) }
62
+ to = match[:to].split(',').map(&:strip).map { |value| cast_value(value) }
63
+ { from: from, to: to }
64
+ else
65
+ raise NotImplementedError
66
+ end
67
+ { name: row[0], values: values }
68
+ end
69
+
70
+ ActiveRecord::ConnectionAdapters::PartitionOptions.new(table_name, options[:type], options[:columns], partition_definitions: partition_definitions)
71
+ end
72
+
73
+ def cast_value(value)
74
+ Integer(value)
75
+ rescue ArgumentError
76
+ value.delete(%q("')) # "
77
+ end
78
+
79
+ def quote_value(value)
80
+ if %w[MINVALUE MAXVALUE].include?(value)
81
+ value
82
+ else
83
+ quote(value)
84
+ end
85
+ end
86
+
87
+ def partition_tables
88
+ partition_info = query(<<~SQL, 'SCHEMA')
89
+ SELECT p.relname
90
+ FROM pg_class t
91
+ JOIN pg_inherits i on i.inhparent = t.oid
92
+ JOIN pg_class p on p.oid = i.inhrelid
93
+ ORDER BY p.relname
94
+ SQL
95
+ partition_info.map { |row| row[0] }
96
+ end
97
+
98
+ # SchemaStatements
99
+ def add_partition(table_name, name:, values:)
100
+ condition = if values.key?(:in)
101
+ "FOR VALUES IN (#{values[:in].map { |v| quote_value(v) }.join(',')})"
102
+ elsif values.key?(:to)
103
+ from = values[:from].map { |v| quote_value(v) }.join(',')
104
+ to = values[:to].map { |v| quote_value(v) }.join(',')
105
+ "FOR VALUES FROM (#{from}) TO (#{to})"
106
+ else
107
+ raise NotImplementedError
108
+ end
109
+ create_table(name, id: false, options: "PARTITION OF #{table_name} #{condition}")
110
+ end
111
+
112
+ def remove_partition(_table_name, name:)
113
+ drop_table(name)
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ module ActiveRecord
121
+ module ConnectionAdapters
122
+ class PostgreSQLAdapter
123
+ prepend Ridgepole::Ext::PostgreSQLAdapter::Partitioning
124
+ end
125
+ end
126
+ end
@@ -45,6 +45,30 @@ module Ridgepole
45
45
  stream.puts add_foreign_key_statements.sort.join("\n")
46
46
  end
47
47
  end
48
+
49
+ def tables(stream)
50
+ original = ignore_tables.dup
51
+ ignore_tables.concat(@connection.partition_tables)
52
+ super
53
+ ensure
54
+ self.ignore_tables = original
55
+ end
56
+
57
+ def table(table, stream)
58
+ super
59
+ partition(table, stream)
60
+ end
61
+
62
+ def partition(table, stream)
63
+ if (partition = @connection.partition(table))
64
+ partition_definitions = partition.partition_definitions.map do |partition_definition|
65
+ "{ name: #{partition_definition.name.inspect}, values: #{partition_definition.values} }"
66
+ end.join(' ,')
67
+
68
+ stream.puts " add_partition #{partition.table.inspect}, #{partition.type.inspect}, #{partition.columns.inspect}, partition_definitions: [#{partition_definitions}]"
69
+ stream.puts
70
+ end
71
+ end
48
72
  end
49
73
  end
50
74
  end
@@ -25,20 +25,18 @@ module Ridgepole
25
25
  readable = ready[0]
26
26
 
27
27
  readable.each do |f|
28
- begin
29
- data = f.read_nonblock(1024)
30
- next if data.nil?
31
-
32
- data.chomp!
33
-
34
- if f == stderr
35
- @logger.warn("[WARNING] #{script_basename}: #{data}")
36
- else
37
- @logger.info("#{script_basename}: #{data}")
38
- end
39
- rescue EOFError
40
- files.delete f
28
+ data = f.read_nonblock(1024)
29
+ next if data.nil?
30
+
31
+ data.chomp!
32
+
33
+ if f == stderr
34
+ @logger.warn("[WARNING] #{script_basename}: #{data}")
35
+ else
36
+ @logger.info("#{script_basename}: #{data}")
41
37
  end
38
+ rescue EOFError
39
+ files.delete f
42
40
  end
43
41
  end
44
42
  rescue EOFError
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ridgepole
4
- VERSION = '0.9.6'
4
+ VERSION = '1.0.2'
5
5
  end
data/lib/ridgepole.rb CHANGED
@@ -16,6 +16,9 @@ require 'diffy'
16
16
  module Ridgepole; end
17
17
 
18
18
  require 'ridgepole/ext/abstract_adapter/disable_table_options'
19
+ require 'ridgepole/ext/abstract_adapter/partition_definition'
20
+ require 'ridgepole/ext/abstract_adapter/partition_options'
21
+ require 'ridgepole/ext/abstract_adapter/partitioning'
19
22
  require 'ridgepole/ext/pp_sort_hash'
20
23
  require 'ridgepole/ext/schema_dumper'
21
24
  require 'ridgepole/client'
data/ridgepole.gemspec CHANGED
@@ -24,12 +24,12 @@ Gem::Specification.new do |spec|
24
24
 
25
25
  spec.required_ruby_version = Gem::Requirement.new('>= 2.2.7') # rubocop:disable Gemspec/RequiredRubyVersion
26
26
 
27
- spec.add_dependency 'activerecord', '>= 5.1', '< 6.2'
27
+ spec.add_dependency 'activerecord', '>= 5.1', '< 7.1'
28
28
  spec.add_dependency 'diffy'
29
29
 
30
30
  spec.add_development_dependency 'appraisal', '>= 2.2.0'
31
31
  spec.add_development_dependency 'bundler'
32
- spec.add_development_dependency 'erbh', '>= 0.1.2'
32
+ spec.add_development_dependency 'erbh', '>= 0.2.1'
33
33
  spec.add_development_dependency 'hash_modern_inspect', '>= 0.1.1'
34
34
  spec.add_development_dependency 'hash_order_helper', '>= 0.1.6'
35
35
  spec.add_development_dependency 'mysql2'
@@ -38,9 +38,10 @@ Gem::Specification.new do |spec|
38
38
  spec.add_development_dependency 'rspec', '>= 3.0.0'
39
39
  spec.add_development_dependency 'rspec-match_fuzzy', '>= 0.1.3'
40
40
  spec.add_development_dependency 'rspec-match_ruby', '>= 0.1.3'
41
- spec.add_development_dependency 'rubocop', '1.9.1'
41
+ spec.add_development_dependency 'rubocop', '1.24.1'
42
42
  spec.add_development_dependency 'rubocop-rake', '>= 0.5.1'
43
43
  spec.add_development_dependency 'rubocop-rspec', '>= 2.1.0'
44
44
  spec.add_development_dependency 'simplecov'
45
45
  spec.add_development_dependency 'simplecov-lcov'
46
+ spec.metadata['rubygems_mfa_required'] = 'true'
46
47
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ridgepole
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.6
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Genki Sugawara
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-08-28 00:00:00.000000000 Z
11
+ date: 2022-02-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -19,7 +19,7 @@ dependencies:
19
19
  version: '5.1'
20
20
  - - "<"
21
21
  - !ruby/object:Gem::Version
22
- version: '6.2'
22
+ version: '7.1'
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -29,7 +29,7 @@ dependencies:
29
29
  version: '5.1'
30
30
  - - "<"
31
31
  - !ruby/object:Gem::Version
32
- version: '6.2'
32
+ version: '7.1'
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: diffy
35
35
  requirement: !ruby/object:Gem::Requirement
@@ -78,14 +78,14 @@ dependencies:
78
78
  requirements:
79
79
  - - ">="
80
80
  - !ruby/object:Gem::Version
81
- version: 0.1.2
81
+ version: 0.2.1
82
82
  type: :development
83
83
  prerelease: false
84
84
  version_requirements: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - ">="
87
87
  - !ruby/object:Gem::Version
88
- version: 0.1.2
88
+ version: 0.2.1
89
89
  - !ruby/object:Gem::Dependency
90
90
  name: hash_modern_inspect
91
91
  requirement: !ruby/object:Gem::Requirement
@@ -204,14 +204,14 @@ dependencies:
204
204
  requirements:
205
205
  - - '='
206
206
  - !ruby/object:Gem::Version
207
- version: 1.9.1
207
+ version: 1.24.1
208
208
  type: :development
209
209
  prerelease: false
210
210
  version_requirements: !ruby/object:Gem::Requirement
211
211
  requirements:
212
212
  - - '='
213
213
  - !ruby/object:Gem::Version
214
- version: 1.9.1
214
+ version: 1.24.1
215
215
  - !ruby/object:Gem::Dependency
216
216
  name: rubocop-rake
217
217
  requirement: !ruby/object:Gem::Requirement
@@ -281,6 +281,7 @@ files:
281
281
  - ".rubocop.yml"
282
282
  - ".simplecov"
283
283
  - Appraisals
284
+ - CHANGELOG.md
284
285
  - Gemfile
285
286
  - LICENSE.txt
286
287
  - README.md
@@ -291,6 +292,7 @@ files:
291
292
  - gemfiles/activerecord_5.2.gemfile
292
293
  - gemfiles/activerecord_6.0.gemfile
293
294
  - gemfiles/activerecord_6.1.gemfile
295
+ - gemfiles/activerecord_7.0.gemfile
294
296
  - lib/ridgepole.rb
295
297
  - lib/ridgepole/cli/config.rb
296
298
  - lib/ridgepole/client.rb
@@ -304,7 +306,13 @@ files:
304
306
  - lib/ridgepole/dumper.rb
305
307
  - lib/ridgepole/execute_expander.rb
306
308
  - lib/ridgepole/ext/abstract_adapter/disable_table_options.rb
309
+ - lib/ridgepole/ext/abstract_adapter/partition_definition.rb
310
+ - lib/ridgepole/ext/abstract_adapter/partition_options.rb
311
+ - lib/ridgepole/ext/abstract_adapter/partitioning.rb
307
312
  - lib/ridgepole/ext/abstract_mysql_adapter/dump_auto_increment.rb
313
+ - lib/ridgepole/ext/abstract_mysql_adapter/partitioning.rb
314
+ - lib/ridgepole/ext/abstract_mysql_adapter/schema_creation.rb
315
+ - lib/ridgepole/ext/postgresql_adapter/partitioning.rb
308
316
  - lib/ridgepole/ext/pp_sort_hash.rb
309
317
  - lib/ridgepole/ext/schema_dumper.rb
310
318
  - lib/ridgepole/external_sql_executer.rb
@@ -317,7 +325,8 @@ files:
317
325
  homepage: https://github.com/ridgepole/ridgepole
318
326
  licenses:
319
327
  - MIT
320
- metadata: {}
328
+ metadata:
329
+ rubygems_mfa_required: 'true'
321
330
  post_install_message:
322
331
  rdoc_options: []
323
332
  require_paths:
@@ -333,7 +342,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
333
342
  - !ruby/object:Gem::Version
334
343
  version: '0'
335
344
  requirements: []
336
- rubygems_version: 3.1.6
345
+ rubygems_version: 3.2.32
337
346
  signing_key:
338
347
  specification_version: 4
339
348
  summary: Ridgepole is a tool to manage DB schema.