ridgepole 1.0.5 → 1.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 +4 -4
- data/.rubocop.yml +1 -7
- data/CHANGELOG.md +14 -0
- data/README.md +2 -26
- data/lib/ridgepole/client.rb +0 -5
- data/lib/ridgepole/delta.rb +4 -23
- data/lib/ridgepole/diff.rb +0 -51
- data/lib/ridgepole/dsl_parser/context.rb +0 -16
- data/lib/ridgepole/dsl_parser.rb +3 -1
- data/lib/ridgepole/ext/schema_dumper.rb +0 -24
- data/lib/ridgepole/version.rb +1 -1
- data/lib/ridgepole.rb +0 -3
- metadata +2 -8
- data/lib/ridgepole/ext/abstract_adapter/partition_definition.rb +0 -19
- data/lib/ridgepole/ext/abstract_adapter/partition_options.rb +0 -34
- data/lib/ridgepole/ext/abstract_adapter/partitioning.rb +0 -40
- data/lib/ridgepole/ext/abstract_mysql_adapter/partitioning.rb +0 -71
- data/lib/ridgepole/ext/abstract_mysql_adapter/schema_creation.rb +0 -46
- data/lib/ridgepole/ext/postgresql_adapter/partitioning.rb +0 -132
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5906641e6a784f8548801ef9b94acedf8580ce9f5fcf2d8c23640b29968d9a65
|
4
|
+
data.tar.gz: fcbbf42e5eba4bfa9d16f8ba813d5c2374c357265a96bbf0322a66b84c17b99b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8eab1b038598d76ad94806aeeeda51af292527fa9ca84cecbc9be8b28b8351225c80782069824c7f7ca8619cdbacb7f85a9c29ef647e783a5d850957273d5f25
|
7
|
+
data.tar.gz: 834667f47a46455e1969574fb2341d620742e119c8882e70dc7d30c29c5780bf2671698f9982dbfed7a99cfc61f29344e7bffc0ec654c6f161d37af8189e4531
|
data/.rubocop.yml
CHANGED
@@ -21,7 +21,7 @@ Metrics/ClassLength:
|
|
21
21
|
Metrics/CyclomaticComplexity:
|
22
22
|
Enabled: false
|
23
23
|
Layout/LineLength:
|
24
|
-
|
24
|
+
Max: 200
|
25
25
|
Metrics/MethodLength:
|
26
26
|
Enabled: false
|
27
27
|
Metrics/ModuleLength:
|
@@ -30,12 +30,6 @@ Metrics/ParameterLists:
|
|
30
30
|
Enabled: false
|
31
31
|
Metrics/PerceivedComplexity:
|
32
32
|
Enabled: false
|
33
|
-
Naming/MethodName:
|
34
|
-
Exclude:
|
35
|
-
- "lib/ridgepole/ext/abstract_mysql_adapter/schema_creation.rb"
|
36
|
-
Naming/MethodParameterName:
|
37
|
-
Exclude:
|
38
|
-
- "lib/ridgepole/ext/abstract_mysql_adapter/schema_creation.rb"
|
39
33
|
Style/Documentation:
|
40
34
|
Enabled: false
|
41
35
|
Style/GuardClause:
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,20 @@
|
|
2
2
|
|
3
3
|
## 1.0
|
4
4
|
|
5
|
+
### 1.1.0 (2022/06/18)
|
6
|
+
|
7
|
+
* Revert partitioning support [pull#392](https://github.com/ridgepole/ridgepole/pull/392)
|
8
|
+
|
9
|
+
## 1.0
|
10
|
+
|
11
|
+
### 1.0.7 (2022/06/09)
|
12
|
+
|
13
|
+
* Normalize list partition values for PostgreSQL [pull#389](https://github.com/ridgepole/ridgepole/pull/389)
|
14
|
+
|
15
|
+
### 1.0.6 (2022/06/06)
|
16
|
+
|
17
|
+
* Support Hash partition for PostgreSQL [pull#387](https://github.com/ridgepole/ridgepole/pull/387)
|
18
|
+
|
5
19
|
### 1.0.5 (2022/06/05)
|
6
20
|
|
7
21
|
* Support DEFAULT partition for PostgreSQL [pull#386](https://github.com/ridgepole/ridgepole/pull/386)
|
data/README.md
CHANGED
@@ -11,6 +11,7 @@ It defines DB schema using [Rails DSL](http://guides.rubyonrails.org/migrations.
|
|
11
11
|
|
12
12
|
**Notice**
|
13
13
|
|
14
|
+
* Partitioning is no longer supported in ridgepole v1.1.0.
|
14
15
|
* ActiveRecord 7.x has some incompatible changes. If you get unintended differences in `datetime`, add `precision`.
|
15
16
|
* cf. https://github.com/ridgepole/ridgepole/issues/381
|
16
17
|
* For ActiveRecord 7.x series, please use AcriveRecord 7.0.2 or higher / Ridgepole 1.0.3 or higher.
|
@@ -216,7 +217,7 @@ See `mysql> show character set;` to find charset / collation pair for your syste
|
|
216
217
|
|
217
218
|
## Generated Column (MySQL)
|
218
219
|
|
219
|
-
There should be NO extra white spaces in the expression (such as after comma).
|
220
|
+
There should be NO extra white spaces in the expression (such as after comma).
|
220
221
|
Quotes in expression may cause the operations failure with MySQL 8.0.
|
221
222
|
|
222
223
|
```ruby
|
@@ -318,31 +319,6 @@ Apply `Schemafile`
|
|
318
319
|
...
|
319
320
|
```
|
320
321
|
|
321
|
-
## Partitioning
|
322
|
-
|
323
|
-
**Notice:** PostgreSQL `PARTITION BY` must be specified with the create_table option.
|
324
|
-
|
325
|
-
### List Partitioning
|
326
|
-
|
327
|
-
```ruby
|
328
|
-
create_table "articles", force: :cascade, options: "PARTITION BY LIST(id)" do |t|
|
329
|
-
end
|
330
|
-
|
331
|
-
add_partition("articles", :list, :id, partition_definitions: [{ name: 'p0', values: { in: [0,1,2] } }, { name: 'p1', values: { in: [3,4,5] } }])
|
332
|
-
```
|
333
|
-
|
334
|
-
### Range Partitioning
|
335
|
-
|
336
|
-
```ruby
|
337
|
-
create_table "articles", force: :cascade, options: "PARTITION BY RANGE(id)" do |t|
|
338
|
-
end
|
339
|
-
|
340
|
-
# postgresql
|
341
|
-
add_partition("articles", :range, :id, partition_definitions: [{ name: 'p0', values: { from: 'MINVALUE', to: 5 }}, { name: 'p1', values: { from: 5, to: 10 } }])
|
342
|
-
# mysql
|
343
|
-
add_partition("articles", :range, :id, partition_definitions: [{ name: 'p0', values: { to: 5 }}, { name: 'p1', values: { to: 10 } }])
|
344
|
-
```
|
345
|
-
|
346
322
|
## Run tests
|
347
323
|
|
348
324
|
|
data/lib/ridgepole/client.rb
CHANGED
@@ -15,12 +15,7 @@ module Ridgepole
|
|
15
15
|
@parser = Ridgepole::DSLParser.new(@options)
|
16
16
|
@diff = Ridgepole::Diff.new(@options)
|
17
17
|
|
18
|
-
if Ridgepole::ConnectionAdapters.mysql?
|
19
|
-
require 'ridgepole/ext/abstract_mysql_adapter/partitioning'
|
20
|
-
require 'ridgepole/ext/abstract_mysql_adapter/schema_creation'
|
21
|
-
end
|
22
18
|
require 'ridgepole/ext/abstract_mysql_adapter/dump_auto_increment' if @options[:mysql_dump_auto_increment]
|
23
|
-
require 'ridgepole/ext/postgresql_adapter/partitioning' if Ridgepole::ConnectionAdapters.postgresql?
|
24
19
|
end
|
25
20
|
|
26
21
|
def dump(&block)
|
data/lib/ridgepole/delta.rb
CHANGED
@@ -310,29 +310,11 @@ execute "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name
|
|
310
310
|
append_change_table_options(table_name, comment_literal, buf)
|
311
311
|
end
|
312
312
|
|
313
|
-
def append_change_partition(table_name, delta, buf)
|
314
|
-
(delta[:add] || {}).each do |_, attrs|
|
315
|
-
buf.puts "create_partition #{table_name.inspect}, **#{attrs.inspect}"
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
def append_change_partition_definitions(table_name, partition_definitions, buf, _post_buf_for_fk)
|
320
|
-
(partition_definitions[:add] || []).each do |partition_name, attrs|
|
321
|
-
buf.puts "add_partition #{table_name.inspect}, name: #{partition_name.inspect}, values: #{attrs[:values].inspect}"
|
322
|
-
end
|
323
|
-
|
324
|
-
(partition_definitions[:delete] || []).each do |partition_name, _attrs|
|
325
|
-
buf.puts "remove_partition #{table_name.inspect}, name: #{partition_name.inspect}"
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
313
|
def append_change(table_name, attrs, buf, pre_buf_for_fk, post_buf_for_fk)
|
330
314
|
definition = attrs[:definition] || {}
|
331
315
|
primary_key_definition = attrs[:primary_key_definition] || {}
|
332
316
|
indices = attrs[:indices] || {}
|
333
317
|
foreign_keys = attrs[:foreign_keys] || {}
|
334
|
-
partition = attrs[:partition] || {}
|
335
|
-
partition_definitions = attrs[:partition_definitions] || {}
|
336
318
|
table_options = attrs[:table_options]
|
337
319
|
table_charset = attrs[:table_charset]
|
338
320
|
table_collation = attrs[:table_collation]
|
@@ -349,14 +331,13 @@ execute "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name
|
|
349
331
|
|
350
332
|
append_change_foreign_keys(table_name, foreign_keys, pre_buf_for_fk, post_buf_for_fk, @options) unless foreign_keys.empty?
|
351
333
|
|
352
|
-
|
334
|
+
if table_options || table_charset || table_collation
|
335
|
+
append_change_table_raw_options(table_name, table_options, table_charset, table_collation,
|
336
|
+
buf)
|
337
|
+
end
|
353
338
|
|
354
339
|
append_change_table_comment(table_name, table_comment, buf) if table_comment
|
355
340
|
|
356
|
-
append_change_partition(table_name, partition, buf) unless partition.empty?
|
357
|
-
|
358
|
-
append_change_partition_definitions(table_name, partition_definitions, buf, post_buf_for_fk) unless partition_definitions.empty?
|
359
|
-
|
360
341
|
buf.puts
|
361
342
|
pre_buf_for_fk.puts
|
362
343
|
post_buf_for_fk.puts
|
data/lib/ridgepole/diff.rb
CHANGED
@@ -37,9 +37,6 @@ module Ridgepole
|
|
37
37
|
delta[:add] ||= {}
|
38
38
|
delta[:add][table_name] = to_attrs
|
39
39
|
end
|
40
|
-
delta[:change] ||= {}
|
41
|
-
delta[:change][table_name] ||= {}
|
42
|
-
scan_partition_change(table_name, from_attrs&.fetch(:partition, nil), to_attrs&.fetch(:partition, nil), delta[:change][table_name])
|
43
40
|
end
|
44
41
|
|
45
42
|
scan_relation_info(relation_info)
|
@@ -620,54 +617,6 @@ module Ridgepole
|
|
620
617
|
end
|
621
618
|
end
|
622
619
|
|
623
|
-
def scan_partition_change(table_name, from, to, table_delta)
|
624
|
-
from = (from || {}).dup
|
625
|
-
to = (to || {}).dup
|
626
|
-
partition_delta = {}
|
627
|
-
|
628
|
-
return if to.empty? && from.empty?
|
629
|
-
|
630
|
-
if from.empty? && Ridgepole::ConnectionAdapters.mysql?
|
631
|
-
partition_delta[:add] ||= {}
|
632
|
-
partition_delta[:add][table_name] = to
|
633
|
-
else
|
634
|
-
if from.present? && (to[:type] != from[:type] || to[:columns] != from[:columns])
|
635
|
-
@logger.warn(<<-MSG)
|
636
|
-
"[WARNING] '#{table_name}' partition is skipped because of the different partition type.
|
637
|
-
to: #{to[:type]} #{to[:columns]}
|
638
|
-
from: #{from[:type]}" #{from[:columns]}
|
639
|
-
MSG
|
640
|
-
return
|
641
|
-
end
|
642
|
-
|
643
|
-
raise "All partition is different. please check partition settings.to: #{to}, from: #{from}" if from[:partition_definitions].present? && (to[:partition_definitions] & from[:partition_definitions]).empty?
|
644
|
-
|
645
|
-
scan_partition_definition_chanage(from, to, table_delta)
|
646
|
-
end
|
647
|
-
|
648
|
-
table_delta[:partition] = partition_delta unless partition_delta.empty?
|
649
|
-
end
|
650
|
-
|
651
|
-
def scan_partition_definition_chanage(from, to, table_delta)
|
652
|
-
partition_definitions_delta = {}
|
653
|
-
attrs = { type: from[:type] || to[:type] }
|
654
|
-
|
655
|
-
from_partitions = (from[:partition_definitions] || []).index_by { |partition| partition[:name] }
|
656
|
-
to_partitions = (to[:partition_definitions] || []).index_by { |partition| partition[:name] }
|
657
|
-
|
658
|
-
(from_partitions.keys - to_partitions.keys).each do |name|
|
659
|
-
partition_definitions_delta[:delete] ||= {}
|
660
|
-
partition_definitions_delta[:delete][name] = attrs.merge(values: from_partitions[name][:values])
|
661
|
-
end
|
662
|
-
|
663
|
-
(to_partitions.keys - from_partitions.keys).each do |name|
|
664
|
-
partition_definitions_delta[:add] ||= {}
|
665
|
-
partition_definitions_delta[:add][name] = attrs.merge(values: to_partitions[name][:values])
|
666
|
-
end
|
667
|
-
|
668
|
-
table_delta[:partition_definitions] = partition_definitions_delta unless partition_definitions_delta.empty?
|
669
|
-
end
|
670
|
-
|
671
620
|
def check_table_existence(definition)
|
672
621
|
return unless @options[:tables]
|
673
622
|
|
@@ -91,22 +91,6 @@ module Ridgepole
|
|
91
91
|
}
|
92
92
|
end
|
93
93
|
|
94
|
-
def add_partition(table_name, type, columns, partition_definitions: [])
|
95
|
-
partition_definitions.each do |partition_definition|
|
96
|
-
values = partition_definition.fetch(:values)
|
97
|
-
raise ArgumentError unless values.is_a?(Hash)
|
98
|
-
|
99
|
-
values[:in] = Array.wrap(values[:in]) if values.key?(:in)
|
100
|
-
values[:to] = Array.wrap(values[:to]) if values.key?(:to)
|
101
|
-
values[:from] = Array.wrap(values[:from]) if values.key?(:from)
|
102
|
-
end
|
103
|
-
@__definition[table_name][:partition] = {
|
104
|
-
type: type,
|
105
|
-
columns: Array.wrap(columns),
|
106
|
-
partition_definitions: partition_definitions,
|
107
|
-
}
|
108
|
-
end
|
109
|
-
|
110
94
|
def require(file)
|
111
95
|
schemafile = %r{\A/}.match?(file) ? file : File.join(@__working_dir, file)
|
112
96
|
|
data/lib/ridgepole/dsl_parser.rb
CHANGED
@@ -40,7 +40,9 @@ module Ridgepole
|
|
40
40
|
# NOTE: For composite primary keys, the first column of the primary key is used as the foreign key index
|
41
41
|
next if Array(attrs[:options][:primary_key]).first == fk_index
|
42
42
|
|
43
|
-
raise
|
43
|
+
raise("The column `#{fk_index}` of the table `#{table_name}` has a foreign key but no index." \
|
44
|
+
' Although InnoDB creates an index automatically,' \
|
45
|
+
' please add one explicitly in order for ridgepole to manage it.')
|
44
46
|
end
|
45
47
|
end
|
46
48
|
end
|
@@ -45,30 +45,6 @@ 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
|
72
48
|
end
|
73
49
|
end
|
74
50
|
end
|
data/lib/ridgepole/version.rb
CHANGED
data/lib/ridgepole.rb
CHANGED
@@ -16,9 +16,6 @@ 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'
|
22
19
|
require 'ridgepole/ext/pp_sort_hash'
|
23
20
|
require 'ridgepole/ext/schema_dumper'
|
24
21
|
require 'ridgepole/client'
|
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: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Genki Sugawara
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-06-
|
11
|
+
date: 2022-06-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -306,13 +306,7 @@ files:
|
|
306
306
|
- lib/ridgepole/dumper.rb
|
307
307
|
- lib/ridgepole/execute_expander.rb
|
308
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
|
312
309
|
- 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
|
316
310
|
- lib/ridgepole/ext/pp_sort_hash.rb
|
317
311
|
- lib/ridgepole/ext/schema_dumper.rb
|
318
312
|
- lib/ridgepole/external_sql_executer.rb
|
@@ -1,19 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_record/connection_adapters/abstract_adapter'
|
4
|
-
|
5
|
-
module ActiveRecord
|
6
|
-
module ConnectionAdapters
|
7
|
-
class PartitionDefinition
|
8
|
-
attr_reader :name, :values
|
9
|
-
|
10
|
-
def initialize(
|
11
|
-
name,
|
12
|
-
values
|
13
|
-
)
|
14
|
-
@name = name
|
15
|
-
@values = values
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_record/connection_adapters/abstract_adapter'
|
4
|
-
|
5
|
-
module ActiveRecord
|
6
|
-
module ConnectionAdapters
|
7
|
-
class PartitionOptions
|
8
|
-
attr_reader :table, :type, :columns, :partition_definitions
|
9
|
-
|
10
|
-
TYPES = %i[range list].freeze
|
11
|
-
|
12
|
-
def initialize(
|
13
|
-
table, type,
|
14
|
-
columns,
|
15
|
-
partition_definitions: []
|
16
|
-
)
|
17
|
-
@table = table
|
18
|
-
@type = type
|
19
|
-
@columns = Array.wrap(columns)
|
20
|
-
@partition_definitions = build_definitions(partition_definitions)
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def build_definitions(definitions)
|
26
|
-
definitions.map do |definition|
|
27
|
-
next if definition.is_a?(PartitionDefinition)
|
28
|
-
|
29
|
-
PartitionDefinition.new(definition.fetch(:name), definition.fetch(:values))
|
30
|
-
end.compact
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_record/connection_adapters/abstract_adapter'
|
4
|
-
|
5
|
-
module Ridgepole
|
6
|
-
module Ext
|
7
|
-
module AbstractAdapter
|
8
|
-
module Partitioning
|
9
|
-
def partition(*)
|
10
|
-
nil
|
11
|
-
end
|
12
|
-
|
13
|
-
def partition_tables
|
14
|
-
[]
|
15
|
-
end
|
16
|
-
|
17
|
-
# SchemaStatements
|
18
|
-
def create_partition(*)
|
19
|
-
raise NotImplementedError
|
20
|
-
end
|
21
|
-
|
22
|
-
def add_partition(*)
|
23
|
-
raise NotImplementedError
|
24
|
-
end
|
25
|
-
|
26
|
-
def remove_partition(*)
|
27
|
-
raise NotImplementedError
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
module ActiveRecord
|
35
|
-
module ConnectionAdapters
|
36
|
-
class AbstractAdapter
|
37
|
-
prepend Ridgepole::Ext::AbstractAdapter::Partitioning
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_record/connection_adapters/abstract_mysql_adapter'
|
4
|
-
|
5
|
-
module Ridgepole
|
6
|
-
module Ext
|
7
|
-
module AbstractMysqlAdapter
|
8
|
-
module Partitioning
|
9
|
-
def partition(table_name)
|
10
|
-
scope = quoted_scope(table_name)
|
11
|
-
|
12
|
-
partition_info = exec_query(<<~SQL, 'SCHEMA')
|
13
|
-
SELECT PARTITION_NAME, PARTITION_DESCRIPTION, PARTITION_METHOD, PARTITION_EXPRESSION
|
14
|
-
FROM information_schema.partitions
|
15
|
-
WHERE partition_name IS NOT NULL
|
16
|
-
AND table_schema = #{scope[:schema]}
|
17
|
-
AND table_name = #{scope[:name]}
|
18
|
-
SQL
|
19
|
-
return if partition_info.count == 0
|
20
|
-
|
21
|
-
type = case partition_info.first['PARTITION_METHOD']
|
22
|
-
when 'LIST COLUMNS'
|
23
|
-
:list
|
24
|
-
when 'RANGE COLUMNS'
|
25
|
-
:range
|
26
|
-
else
|
27
|
-
raise NotImplementedError, partition_info.first['PARTITION_METHOD'].to_s
|
28
|
-
end
|
29
|
-
columns = partition_info.first['PARTITION_EXPRESSION'].delete('`').split(',').map(&:to_sym)
|
30
|
-
|
31
|
-
partition_definitions = partition_info.map do |row|
|
32
|
-
values = case type
|
33
|
-
when :list
|
34
|
-
{ in: instance_eval("[#{row['PARTITION_DESCRIPTION'].gsub(/\(/, '[').gsub(/\)/, ']')}] # [1,2]", __FILE__, __LINE__) }
|
35
|
-
when :range
|
36
|
-
{ to: instance_eval("[#{row['PARTITION_DESCRIPTION']}] # [1,2]", __FILE__, __LINE__) }
|
37
|
-
else
|
38
|
-
raise NotImplementedError
|
39
|
-
end
|
40
|
-
|
41
|
-
{ name: row['PARTITION_NAME'], values: values }
|
42
|
-
end
|
43
|
-
|
44
|
-
ActiveRecord::ConnectionAdapters::PartitionOptions.new(table_name, type, columns, partition_definitions: partition_definitions)
|
45
|
-
end
|
46
|
-
|
47
|
-
# SchemaStatements
|
48
|
-
def create_partition(table_name, type:, columns:, partition_definitions:)
|
49
|
-
execute schema_creation.accept(ActiveRecord::ConnectionAdapters::PartitionOptions.new(table_name, type, columns, partition_definitions: partition_definitions))
|
50
|
-
end
|
51
|
-
|
52
|
-
def add_partition(table_name, name:, values:)
|
53
|
-
pd = ActiveRecord::ConnectionAdapters::PartitionDefinition.new(name, values)
|
54
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} ADD PARTITION (#{schema_creation.accept(pd)})"
|
55
|
-
end
|
56
|
-
|
57
|
-
def remove_partition(table_name, name:)
|
58
|
-
execute "ALTER TABLE #{quote_table_name(table_name)} DROP PARTITION #{name}"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
module ActiveRecord
|
66
|
-
module ConnectionAdapters
|
67
|
-
class AbstractMysqlAdapter < AbstractAdapter
|
68
|
-
prepend Ridgepole::Ext::AbstractMysqlAdapter::Partitioning
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'active_record/connection_adapters/mysql/schema_creation'
|
4
|
-
|
5
|
-
module Ridgepole
|
6
|
-
module Ext
|
7
|
-
module AbstractMysqlAdapter
|
8
|
-
module SchemaCreation
|
9
|
-
def visit_PartitionOptions(o)
|
10
|
-
sqls = o.partition_definitions.map { |partition_definition| accept partition_definition }
|
11
|
-
function = case o.type
|
12
|
-
when :list
|
13
|
-
"LIST COLUMNS(#{o.columns.map { |column| quote_column_name(column) }.join(',')})"
|
14
|
-
when :range
|
15
|
-
"RANGE COLUMNS(#{o.columns.map { |column| quote_column_name(column) }.join(',')})"
|
16
|
-
else
|
17
|
-
raise NotImplementedError
|
18
|
-
end
|
19
|
-
"ALTER TABLE #{quote_table_name(o.table)} PARTITION BY #{function} (#{sqls.join(',')})"
|
20
|
-
end
|
21
|
-
|
22
|
-
def visit_PartitionDefinition(o)
|
23
|
-
if o.values.key?(:in)
|
24
|
-
"PARTITION #{o.name} VALUES IN (#{o.values[:in].map do |value|
|
25
|
-
value.is_a?(Array) ? "(#{value.map(&:inspect).join(',')})" : value.inspect
|
26
|
-
end.join(',')})"
|
27
|
-
elsif o.values.key?(:to)
|
28
|
-
"PARTITION #{o.name} VALUES LESS THAN (#{o.values[:to].map(&:inspect).join(',')})"
|
29
|
-
else
|
30
|
-
raise NotImplementedError
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
module ActiveRecord
|
39
|
-
module ConnectionAdapters
|
40
|
-
module MySQL
|
41
|
-
class SchemaCreation
|
42
|
-
prepend Ridgepole::Ext::AbstractMysqlAdapter::SchemaCreation
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,132 +0,0 @@
|
|
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 |name, val_str|
|
55
|
-
values = if val_str == 'DEFAULT'
|
56
|
-
{ default: true }
|
57
|
-
else
|
58
|
-
case options[:type]
|
59
|
-
when :list
|
60
|
-
values = val_str.match(/FOR VALUES IN \((?<csv>.+)\)$/)[:csv].split(',').map(&:strip).map { |value| cast_value(value) }
|
61
|
-
{ in: Array.wrap(values) }
|
62
|
-
when :range
|
63
|
-
match = val_str.match(/FOR VALUES FROM \((?<from>.+)\) TO \((?<to>.+)\)/)
|
64
|
-
from = match[:from].split(',').map(&:strip).map { |value| cast_value(value) }
|
65
|
-
to = match[:to].split(',').map(&:strip).map { |value| cast_value(value) }
|
66
|
-
{ from: from, to: to }
|
67
|
-
else
|
68
|
-
raise NotImplementedError
|
69
|
-
end
|
70
|
-
end
|
71
|
-
{ name: name, values: values }
|
72
|
-
end
|
73
|
-
|
74
|
-
ActiveRecord::ConnectionAdapters::PartitionOptions.new(table_name, options[:type], options[:columns], partition_definitions: partition_definitions)
|
75
|
-
end
|
76
|
-
|
77
|
-
def cast_value(value)
|
78
|
-
Integer(value)
|
79
|
-
rescue ArgumentError
|
80
|
-
value.delete(%q("')) # "
|
81
|
-
end
|
82
|
-
|
83
|
-
def quote_value(value)
|
84
|
-
if %w[MINVALUE MAXVALUE].include?(value)
|
85
|
-
value
|
86
|
-
else
|
87
|
-
quote(value)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def partition_tables
|
92
|
-
partition_info = query(<<~SQL, 'SCHEMA')
|
93
|
-
SELECT p.relname
|
94
|
-
FROM pg_class t
|
95
|
-
JOIN pg_inherits i on i.inhparent = t.oid
|
96
|
-
JOIN pg_class p on p.oid = i.inhrelid
|
97
|
-
ORDER BY p.relname
|
98
|
-
SQL
|
99
|
-
partition_info.map { |row| row[0] }
|
100
|
-
end
|
101
|
-
|
102
|
-
# SchemaStatements
|
103
|
-
def add_partition(table_name, name:, values:)
|
104
|
-
condition = if values.key?(:default)
|
105
|
-
'DEFAULT'
|
106
|
-
elsif values.key?(:in)
|
107
|
-
"FOR VALUES IN (#{values[:in].map { |v| quote_value(v) }.join(',')})"
|
108
|
-
elsif values.key?(:to)
|
109
|
-
from = values[:from].map { |v| quote_value(v) }.join(',')
|
110
|
-
to = values[:to].map { |v| quote_value(v) }.join(',')
|
111
|
-
"FOR VALUES FROM (#{from}) TO (#{to})"
|
112
|
-
else
|
113
|
-
raise NotImplementedError
|
114
|
-
end
|
115
|
-
create_table(name, id: false, options: "PARTITION OF #{table_name} #{condition}")
|
116
|
-
end
|
117
|
-
|
118
|
-
def remove_partition(_table_name, name:)
|
119
|
-
drop_table(name)
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
module ActiveRecord
|
127
|
-
module ConnectionAdapters
|
128
|
-
class PostgreSQLAdapter
|
129
|
-
prepend Ridgepole::Ext::PostgreSQLAdapter::Partitioning
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|