ridgepole 1.0.0 → 1.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd3d82e112d8ef525a7a909538e864b1dc6b4d1b6eaabad978e2ec552962fd63
4
- data.tar.gz: 76dcd540c7ae97d1c226077f2b09979fa16e9937ec8a8d8d9f81b001bcdb2ee2
3
+ metadata.gz: e83bd61a718b5b97d6be9214742e452fce8c54e9bff48e2b604344753138c0bb
4
+ data.tar.gz: f2ed01a46f18174e3173f07715002fc222467a341d9e93682910ef2c2497d613
5
5
  SHA512:
6
- metadata.gz: f641654487c60639c6de8633cba4ee8d3dc55a52b200ee43857f670a818c6699ba4271de56616152b8d378aac2c0e1eee118c17aa362c2e5e451542e5be540c3
7
- data.tar.gz: b502738eb29e65a73e03dc0d64cec03cb250227c319888d65c7869bb123166fbd6354a9accfb25072484ed50e4bbbccd98e674b89dad51618341284bc8c1aeed
6
+ metadata.gz: 62a7e740ec77f0124b3b9776a3c78fc8186a9f00d0f97917cae79342664f1a77e1f2ce3992e7e6e463c8fad76e2fba92af0f9a6031a70f7d545768dae2942ba1
7
+ data.tar.gz: 2f43e6f67be8820450c9fd47ddde4dacabe69778bbc1d3837b9ce8988e6b7393f80df3ed5a400a142a0a20a06c58344aa08490ea81cc25d4fdb10b56de824aec
data/.rubocop.yml CHANGED
@@ -3,7 +3,7 @@ AllCops:
3
3
  - "gemfiles/**/*"
4
4
  - "omnibus-ridgepole/**/*"
5
5
  - "vendor/bundle/**/*"
6
- TargetRubyVersion: 2.4
6
+ TargetRubyVersion: 2.5
7
7
  NewCops: enable
8
8
  Bundler/OrderedGems:
9
9
  Include:
@@ -24,10 +24,18 @@ Layout/LineLength:
24
24
  Enabled: false
25
25
  Metrics/MethodLength:
26
26
  Enabled: false
27
+ Metrics/ModuleLength:
28
+ Max: 106
27
29
  Metrics/ParameterLists:
28
30
  Enabled: false
29
31
  Metrics/PerceivedComplexity:
30
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"
31
39
  Style/Documentation:
32
40
  Enabled: false
33
41
  Style/GuardClause:
data/.simplecov CHANGED
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- SimpleCov.start do
3
+ SimpleCov.configure do
4
4
  # exclude directories and files
5
5
  add_filter '/spec/'
6
6
  end
data/CHANGELOG.md CHANGED
@@ -1,99 +1,116 @@
1
1
  # Changelog
2
2
 
3
- ## 1.0 (2021/12/19)
3
+ ## 1.0
4
+
5
+ ### 1.0.3 (2022/02/12)
6
+
7
+ * Support Rails 7.0.2 [pull#380](https://github.com/ridgepole/ridgepole/pull/380)
8
+
9
+ ### 1.0.2 (2022/02/06)
10
+
11
+ * Add support for partitioning ([pull#374](https://github.com/ridgepole/ridgepole/pull/374))
12
+ * Suppress warning of table option differences ([pull#378](https://github.com/ridgepole/ridgepole/pull/378))
13
+
14
+ ### 1.0.1 (2022/01/15)
15
+
16
+ * Fix code for RuboCop 1.24.1
17
+ * Fix PostgreSQL spec for Rails 7.0
18
+ * Update ERBh gem (for development)
19
+
20
+ ### 1.0.0 (2021/12/19)
4
21
 
5
22
  * Support Rails 7.0
6
- * `--skip-drop-table` by default ([pull#363](https://github.com/winebarrel/ridgepole/pull/363))
23
+ * `--skip-drop-table` by default ([pull#363](https://github.com/ridgepole/ridgepole/pull/363))
7
24
 
8
25
  ## 0.9
9
26
 
10
27
  ### 0.9.6
11
28
 
12
- * Fix malformed error ([pull#362](https://github.com/winebarrel/ridgepole/pull/362))
29
+ * Fix malformed error ([pull#362](https://github.com/ridgepole/ridgepole/pull/362))
13
30
 
14
31
  ### 0.9.5
15
32
 
16
- * Call `super` in `disable_table_options.rb` ([pull#357](https://github.com/winebarrel/ridgepole/pull/357))
33
+ * Call `super` in `disable_table_options.rb` ([pull#357](https://github.com/ridgepole/ridgepole/pull/357))
17
34
 
18
35
  ### 0.9.4
19
36
 
20
- * Fix `--alter-extra` option for unique index ([pull#356](https://github.com/winebarrel/ridgepole/pull/356))
37
+ * Fix `--alter-extra` option for unique index ([pull#356](https://github.com/ridgepole/ridgepole/pull/356))
21
38
 
22
39
  ### 0.9.3
23
40
 
24
- * Fix `limit` option for `t.integer` ([pull#354](https://github.com/winebarrel/ridgepole/pull/354))
41
+ * Fix `limit` option for `t.integer` ([pull#354](https://github.com/ridgepole/ridgepole/pull/354))
25
42
 
26
43
  ### 0.9.2
27
44
 
28
- * Support `t.column index option` ([pull#353](https://github.com/winebarrel/ridgepole/pull/353))
45
+ * Support `t.column index option` ([pull#353](https://github.com/ridgepole/ridgepole/pull/353))
29
46
 
30
47
  ### 0.9.1
31
48
 
32
- * Support `t.foreign_key` ([pull#348](https://github.com/winebarrel/ridgepole/pull/348))
49
+ * Support `t.foreign_key` ([pull#348](https://github.com/ridgepole/ridgepole/pull/348))
33
50
 
34
51
  ### 0.9.0
35
52
 
36
- * Remove `--mysql-use-alter` option ([pull#330](https://github.com/winebarrel/ridgepole/pull/330))
37
- * Add `--table-hash-options` option ([pull#331](https://github.com/winebarrel/ridgepole/pull/331))
38
- * Support Rails 6.1 ([pull#323](https://github.com/winebarrel/ridgepole/pull/323))
39
- * Disable Rails 5.0 support ([pull#335](https://github.com/winebarrel/ridgepole/pull/335))
40
- * Fix PK AUTO_INCREMENT change bug ([pull#334](https://github.com/winebarrel/ridgepole/pull/334))
53
+ * Remove `--mysql-use-alter` option ([pull#330](https://github.com/ridgepole/ridgepole/pull/330))
54
+ * Add `--table-hash-options` option ([pull#331](https://github.com/ridgepole/ridgepole/pull/331))
55
+ * Support Rails 6.1 ([pull#323](https://github.com/ridgepole/ridgepole/pull/323))
56
+ * Disable Rails 5.0 support ([pull#335](https://github.com/ridgepole/ridgepole/pull/335))
57
+ * Fix PK AUTO_INCREMENT change bug ([pull#334](https://github.com/ridgepole/ridgepole/pull/334))
41
58
 
42
59
  ## 0.8
43
60
 
44
61
  ### 0.8.13
45
62
 
46
- * Support `serial` and `bigserial` column types ([pull#321](https://github.com/winebarrel/ridgepole/pull/321))
63
+ * Support `serial` and `bigserial` column types ([pull#321](https://github.com/ridgepole/ridgepole/pull/321))
47
64
 
48
65
  ### 0.8.12
49
66
 
50
- * Pluralize column specified by `references` ([pull#317](https://github.com/winebarrel/ridgepole/pull/317))
67
+ * Pluralize column specified by `references` ([pull#317](https://github.com/ridgepole/ridgepole/pull/317))
51
68
 
52
69
  ### 0.8.11
53
70
 
54
- * Fix FK index check support multiple PK ([pull#315](https://github.com/winebarrel/ridgepole/pull/315))
55
- * Support t.reference() foreign_key option ([pull#316](https://github.com/winebarrel/ridgepole/pull/316))
71
+ * Fix FK index check support multiple PK ([pull#315](https://github.com/ridgepole/ridgepole/pull/315))
72
+ * Support t.reference() foreign_key option ([pull#316](https://github.com/ridgepole/ridgepole/pull/316))
56
73
 
57
74
  ### 0.8.10
58
75
 
59
- * Raise an error if an InnoDB column has a foreign key but no index ([pull#310](https://github.com/winebarrel/ridgepole/pull/310))
76
+ * Raise an error if an InnoDB column has a foreign key but no index ([pull#310](https://github.com/ridgepole/ridgepole/pull/310))
60
77
 
61
78
  ### 0.8.9
62
79
 
63
- * Fix unexpected differences on text types and blob types on Rails 6 ([pull#306](https://github.com/winebarrel/ridgepole/pull/306))
64
- * Fix unexpected warning when a foreign key is added on the primary key ([pull#307](https://github.com/winebarrel/ridgepole/pull/307))
80
+ * Fix unexpected differences on text types and blob types on Rails 6 ([pull#306](https://github.com/ridgepole/ridgepole/pull/306))
81
+ * Fix unexpected warning when a foreign key is added on the primary key ([pull#307](https://github.com/ridgepole/ridgepole/pull/307))
65
82
 
66
83
  ### 0.8.8
67
84
 
68
- * Fix keyword arguments warnings in Ruby 2.7 ([pull#303](https://github.com/winebarrel/ridgepole/pull/303))
85
+ * Fix keyword arguments warnings in Ruby 2.7 ([pull#303](https://github.com/ridgepole/ridgepole/pull/303))
69
86
 
70
87
  ### 0.8.7
71
88
 
72
- * Support `require_relative` ([pull#298](https://github.com/winebarrel/ridgepole/pull/298))
89
+ * Support `require_relative` ([pull#298](https://github.com/ridgepole/ridgepole/pull/298))
73
90
 
74
91
  ### 0.8.6
75
92
 
76
- * Support multiple databases feature ([pull#297](https://github.com/winebarrel/ridgepole/pull/297))
93
+ * Support multiple databases feature ([pull#297](https://github.com/ridgepole/ridgepole/pull/297))
77
94
 
78
95
  ### 0.8.5
79
96
 
80
- * Improve warning message on table options ([pull#291](https://github.com/winebarrel/ridgepole/pull/291))
97
+ * Improve warning message on table options ([pull#291](https://github.com/ridgepole/ridgepole/pull/291))
81
98
 
82
99
  ### 0.8.4
83
100
 
84
- * Display a warning if an InnoDB table doesn't have any indexes on a column where it has a foreign key ([pull#290](https://github.com/winebarrel/ridgepole/pull/290))
101
+ * Display a warning if an InnoDB table doesn't have any indexes on a column where it has a foreign key ([pull#290](https://github.com/ridgepole/ridgepole/pull/290))
85
102
 
86
103
  ### 0.8.3
87
104
 
88
- * Fix "topological sort failed" error ([pull#287](https://github.com/winebarrel/ridgepole/pull/287))
105
+ * Fix "topological sort failed" error ([pull#287](https://github.com/ridgepole/ridgepole/pull/287))
89
106
 
90
107
  ### 0.8.2
91
108
 
92
- * Support `postgres://` schema ([pull#285](https://github.com/winebarrel/ridgepole/pull/285))
109
+ * Support `postgres://` schema ([pull#285](https://github.com/ridgepole/ridgepole/pull/285))
93
110
 
94
111
  ### 0.8.1
95
112
 
96
- * Drop tables in an order considering foreign key constraints ([pull#284](https://github.com/winebarrel/ridgepole/pull/284))
113
+ * Drop tables in an order considering foreign key constraints ([pull#284](https://github.com/ridgepole/ridgepole/pull/284))
97
114
 
98
115
  ### 0.8.0
99
116
 
@@ -103,32 +120,32 @@
103
120
 
104
121
  ### 0.7.8
105
122
 
106
- * Fix for `add_foreign_key(..., column: ,,,)` ([pull#278](https://github.com/winebarrel/ridgepole/pull/278))
123
+ * Fix for `add_foreign_key(..., column: ,,,)` ([pull#278](https://github.com/ridgepole/ridgepole/pull/278))
107
124
 
108
125
  ### 0.7.7
109
126
 
110
- * Support URI query string ([pull#273](https://github.com/winebarrel/ridgepole/pull/273))
127
+ * Support URI query string ([pull#273](https://github.com/ridgepole/ridgepole/pull/273))
111
128
 
112
129
  ### 0.7.6
113
130
 
114
- * Fix database url check ([pull#266](https://github.com/winebarrel/ridgepole/pull/266))
115
- * Add ignore option ([pull#267](https://github.com/winebarrel/ridgepole/pull/267))
131
+ * Fix database url check ([pull#266](https://github.com/ridgepole/ridgepole/pull/266))
132
+ * Add ignore option ([pull#267](https://github.com/ridgepole/ridgepole/pull/267))
116
133
 
117
134
  ### 0.7.5
118
135
 
119
- * Fix polymorphic options ([pull#263](https://github.com/winebarrel/ridgepole/pull/263))
120
- * Fix `--mysql-use-alter` option ([pull#246](https://github.com/winebarrel/ridgepole/pull/264))
121
- * Fix Database URI parsing ([pull#265](https://github.com/winebarrel/ridgepole/pull/265))
136
+ * Fix polymorphic options ([pull#263](https://github.com/ridgepole/ridgepole/pull/263))
137
+ * Fix `--mysql-use-alter` option ([pull#246](https://github.com/ridgepole/ridgepole/pull/264))
138
+ * Fix Database URI parsing ([pull#265](https://github.com/ridgepole/ridgepole/pull/265))
122
139
 
123
140
  ### 0.7.4
124
141
 
125
- * Fix `add_foreign_key` options ([issue#250](https://github.com/winebarrel/ridgepole/issues/250))
142
+ * Fix `add_foreign_key` options ([issue#250](https://github.com/ridgepole/ridgepole/issues/250))
126
143
 
127
144
  ### 0.7.3
128
145
 
129
- * Add `--mysql-change-table-comment option` ([pull#166](https://github.com/winebarrel/ridgepole/pull/166))
146
+ * Add `--mysql-change-table-comment option` ([pull#166](https://github.com/ridgepole/ridgepole/pull/166))
130
147
  * Refactoring with RuboCop
131
- * Support primary key adding/dropping ([issue#246](https://github.com/winebarrel/ridgepole/issues/246))
148
+ * Support primary key adding/dropping ([issue#246](https://github.com/ridgepole/ridgepole/issues/246))
132
149
 
133
150
  ### 0.7.2
134
151
 
@@ -161,33 +178,33 @@
161
178
 
162
179
  ### 0.6.6
163
180
 
164
- * Use `t.column` for migration ([pull#114](https://github.com/winebarrel/ridgepole/pull/114))
165
- * Support DATABASE_URL format ([pull#118](https://github.com/winebarrel/ridgepole/pull/118))
166
- * Add Ruby2.4 CI ([pull#119](https://github.com/winebarrel/ridgepole/pull/119))
181
+ * Use `t.column` for migration ([pull#114](https://github.com/ridgepole/ridgepole/pull/114))
182
+ * Support DATABASE_URL format ([pull#118](https://github.com/ridgepole/ridgepole/pull/118))
183
+ * Add Ruby2.4 CI ([pull#119](https://github.com/ridgepole/ridgepole/pull/119))
167
184
 
168
185
  ### 0.6.5
169
186
 
170
187
  * Fix rails version `'>= 4.2', '< 6'`
171
- * Support new types ([pull#84](https://github.com/winebarrel/ridgepole/pull/84))
172
- * Support `default: -> { ... }` ([pull#85](https://github.com/winebarrel/ridgepole/pull/85))
188
+ * Support new types ([pull#84](https://github.com/ridgepole/ridgepole/pull/84))
189
+ * Support `default: -> { ... }` ([pull#85](https://github.com/ridgepole/ridgepole/pull/85))
173
190
  * Support DDL Comment (Rails5 only)
174
191
  * Output schema diff when pass `--verbose`
175
- * Support composite primary key (Rails5 only / [pull#97](https://github.com/winebarrel/ridgepole/pull/97))
192
+ * Support composite primary key (Rails5 only / [pull#97](https://github.com/ridgepole/ridgepole/pull/97))
176
193
 
177
194
  ### 0.6.4
178
195
 
179
- * Execute sql using external script ([pull#56](https://github.com/winebarrel/ridgepole/pull/56))
196
+ * Execute sql using external script ([pull#56](https://github.com/ridgepole/ridgepole/pull/56))
180
197
  * Add `--mysql-use-alter` option
181
198
  * Add `--alter-extra` option
182
199
  * Add `--dump-with-default-fk-name` option
183
- * Support `t.index` ([pull#64](https://github.com/winebarrel/ridgepole/pull/64))
200
+ * Support `t.index` ([pull#64](https://github.com/ridgepole/ridgepole/pull/64))
184
201
  * Remove migration_comments
185
202
  * Fix foreign key apply order
186
203
 
187
204
  ### 0.6.3
188
205
 
189
- * Fix `default` option ([pull#48](https://github.com/winebarrel/ridgepole/pull/48))
190
- * Add `--enable-migration-comments` option ([pull#50](https://github.com/winebarrel/ridgepole/pull/50))
206
+ * Fix `default` option ([pull#48](https://github.com/ridgepole/ridgepole/pull/48))
207
+ * Add `--enable-migration-comments` option ([pull#50](https://github.com/ridgepole/ridgepole/pull/50))
191
208
  * Disable `rename_table_indexes`
192
209
 
193
210
  ### 0.6.1
data/README.md CHANGED
@@ -6,14 +6,16 @@ It defines DB schema using [Rails DSL](http://guides.rubyonrails.org/migrations.
6
6
  (like Chef/Puppet)
7
7
 
8
8
  [![Gem Version](https://badge.fury.io/rb/ridgepole.svg)](http://badge.fury.io/rb/ridgepole)
9
- [![Build Status](https://github.com/winebarrel/ridgepole/workflows/test/badge.svg?branch=1.0)](https://github.com/winebarrel/ridgepole/actions)
10
- [![Coverage Status](https://coveralls.io/repos/github/winebarrel/ridgepole/badge.svg?branch=1.0)](https://coveralls.io/github/winebarrel/ridgepole?branch=1.0)
9
+ [![Build Status](https://github.com/ridgepole/ridgepole/workflows/test/badge.svg?branch=1.0)](https://github.com/ridgepole/ridgepole/actions)
10
+ [![Coverage Status](https://coveralls.io/repos/github/ridgepole/ridgepole/badge.svg?branch=1.0)](https://coveralls.io/github/ridgepole/ridgepole?branch=1.0)
11
11
 
12
12
  **Notice**
13
13
 
14
+ For ActiveRecord 7.x series, please use AcriveRecord 7.0.2 or higher / Ridgepole 1.0.3 or higher.
15
+ * cf. https://github.com/ridgepole/ridgepole/pull/380
14
16
  * ActiveRecord 6.1 is supported in ridgepole v0.9, but the ActiveRecord dump has been changed, so there is a difference between ActiveRecord 5.x/6.0 format.
15
17
  * **If you use ActiveRecord 6.1, please modify Schemafile format**.
16
- * cf. https://github.com/winebarrel/ridgepole/pull/323
18
+ * cf. https://github.com/ridgepole/ridgepole/pull/323
17
19
  * `DROP TABLE` is skipped by default in v1.0 and later versions.
18
20
  * If you want to `DROP TABLE`, please pass `--drop-table`.
19
21
  * cf. https://github.com/ridgepole/ridgepole/pull/363
@@ -301,18 +303,44 @@ Apply `Schemafile`
301
303
  ...
302
304
  ```
303
305
 
306
+ ## Partitioning
307
+
308
+ **Notice:** PostgreSQL `PARTITION BY` must be specified with the create_table option.
309
+
310
+ ### List Partitioning
311
+
312
+ ```ruby
313
+ create_table "articles", force: :cascade, options: "PARTITION BY LIST(id)" do |t|
314
+ end
315
+
316
+ add_partition("articles", :list, :id, partition_definitions: [{ name: 'p0', values: { in: [0,1,2] } }, { name: 'p1', values: { in: [3,4,5] } }])
317
+ ```
318
+
319
+ ### Range Partitioning
320
+
321
+ ```ruby
322
+ create_table "articles", force: :cascade, options: "PARTITION BY RANGE(id)" do |t|
323
+ end
324
+
325
+ # postgresql
326
+ add_partition("articles", :range, :id, partition_definitions: [{ name: 'p0', values: { from: 'MINVALUE', to: 5 }}, { name: 'p1', values: { from: 5, to: 10 } }])
327
+ # mysql
328
+ add_partition("articles", :range, :id, partition_definitions: [{ name: 'p0', values: { to: 5 }}, { name: 'p1', values: { to: 10 } }])
329
+ ```
330
+
304
331
  ## Run tests
305
332
 
333
+
306
334
  ```sh
307
335
  docker-compose up -d
308
336
  bundle install
309
337
  bundle exec appraisal install
310
- bundle exec appraisal activerecord-5.1 rake
311
- # POSTGRESQL=1 bundle exec appraisal activerecord-5.1 rake
312
- # MYSQL57=1 bundle exec appraisal activerecord-5.1 rake
338
+ bundle exec appraisal activerecord-7.0 rake
339
+ # POSTGRESQL=1 bundle exec appraisal activerecord-7.0 rake
340
+ # MYSQL57=1 bundle exec appraisal activerecord-7.0 rake
313
341
  ```
314
342
 
315
- **Notice:** mysql-client/postgresql-client is required.
343
+ **Notice:** Ruby 2.6 or above/mysql-client/postgresql-client is required.
316
344
 
317
345
  ## Demo
318
346
 
@@ -321,6 +349,6 @@ bundle exec appraisal activerecord-5.1 rake
321
349
 
322
350
  ## Example project
323
351
 
324
- * https://github.com/winebarrel/ridgepole-example
325
- * https://github.com/winebarrel/ridgepole-example/pull/1
326
- * https://github.com/winebarrel/ridgepole-example/pull/2
352
+ * https://github.com/ridgepole/ridgepole-example
353
+ * https://github.com/ridgepole/ridgepole-example/pull/1
354
+ * https://github.com/ridgepole/ridgepole-example/pull/2
data/bin/ridgepole CHANGED
@@ -74,106 +74,104 @@ def noop_migrate(delta, options)
74
74
  end
75
75
 
76
76
  ARGV.options do |opt|
77
- begin
78
- opt.on('-c', '--config CONF_OR_FILE') { |v| config = v }
79
- opt.on('-E', '--env ENVIRONMENT') { |v| env = v }
80
- opt.on('-s', '--spec-name SPEC_NAME') { |v| spec_name = v }
81
- opt.on('-a', '--apply') { set_mode[:apply] }
82
- opt.on('-m', '--merge') do
83
- set_mode[:apply]
84
- options[:merge] = true
77
+ opt.on('-c', '--config CONF_OR_FILE') { |v| config = v }
78
+ opt.on('-E', '--env ENVIRONMENT') { |v| env = v }
79
+ opt.on('-s', '--spec-name SPEC_NAME') { |v| spec_name = v }
80
+ opt.on('-a', '--apply') { set_mode[:apply] }
81
+ opt.on('-m', '--merge') do
82
+ set_mode[:apply]
83
+ options[:merge] = true
84
+ end
85
+ opt.on('-f', '--file SCHEMAFILE') { |v| file = v }
86
+ opt.on('', '--dry-run') { options[:dry_run] = true }
87
+ opt.on('', '--table-options OPTIONS') { |v| options[:table_options] = v }
88
+ opt.on('', '--table-hash-options OPTIONS') do |v|
89
+ # NOTE: Ruby2.4 doesn't support `symbolize_names: true`
90
+ hash = YAML.safe_load(v).deep_symbolize_keys
91
+
92
+ case hash[:id]
93
+ when String
94
+ hash[:id] = hash[:id].to_sym
95
+ when Hash
96
+ hash[:id][:type] = hash[:id][:type].to_sym if hash[:id][:type]
85
97
  end
86
- opt.on('-f', '--file SCHEMAFILE') { |v| file = v }
87
- opt.on('', '--dry-run') { options[:dry_run] = true }
88
- opt.on('', '--table-options OPTIONS') { |v| options[:table_options] = v }
89
- opt.on('', '--table-hash-options OPTIONS') do |v|
90
- # NOTE: Ruby2.4 doesn't support `symbolize_names: true`
91
- hash = YAML.safe_load(v).deep_symbolize_keys
92
-
93
- case hash[:id]
94
- when String
95
- hash[:id] = hash[:id].to_sym
96
- when Hash
97
- hash[:id][:type] = hash[:id][:type].to_sym if hash[:id][:type]
98
- end
99
98
 
100
- options[:table_hash_options] = hash
101
- end
102
- opt.on('', '--alter-extra ALTER_SPEC') { |v| options[:alter_extra] = v }
103
- opt.on('', '--external-script SCRIPT') { |v| options[:external_script] = v }
104
- opt.on('', '--bulk-change') do
105
- raise OptionParser::InvalidOption, 'Cannot use `bulk-change` in `merge`' if options[:merge]
99
+ options[:table_hash_options] = hash
100
+ end
101
+ opt.on('', '--alter-extra ALTER_SPEC') { |v| options[:alter_extra] = v }
102
+ opt.on('', '--external-script SCRIPT') { |v| options[:external_script] = v }
103
+ opt.on('', '--bulk-change') do
104
+ raise OptionParser::InvalidOption, 'Cannot use `bulk-change` in `merge`' if options[:merge]
106
105
 
107
- options[:bulk_change] = true
108
- end
106
+ options[:bulk_change] = true
107
+ end
109
108
 
110
- COLUMN_TYPES.each do |column_type, column_type_alias|
111
- opt.on('', "--default-#{column_type_alias}-limit LIMIT", Integer) do |v|
112
- options[:"default_#{column_type}_limit"] = v
113
- end
109
+ COLUMN_TYPES.each do |column_type, column_type_alias|
110
+ opt.on('', "--default-#{column_type_alias}-limit LIMIT", Integer) do |v|
111
+ options[:"default_#{column_type}_limit"] = v
114
112
  end
113
+ end
115
114
 
116
- opt.on('', '--pre-query QUERY') { |v| options[:pre_query] = v }
117
- opt.on('', '--post-query QUERY') { |v| options[:post_query] = v }
118
- opt.on('-e', '--export') { set_mode[:export] }
119
- opt.on('', '--split') { split = true }
120
- opt.on('', '--split-with-dir') { split = :with_dir }
121
- opt.on('-d', '--diff DSL1 DSL2') do |diff_arg1|
122
- set_mode[:diff]
123
- diff_arg2 = ARGV.first
124
-
125
- if [diff_arg1, diff_arg2].any? { |i| i.nil? || i.start_with?('-') }
126
- puts opt.help
127
- exit 1
128
- end
115
+ opt.on('', '--pre-query QUERY') { |v| options[:pre_query] = v }
116
+ opt.on('', '--post-query QUERY') { |v| options[:post_query] = v }
117
+ opt.on('-e', '--export') { set_mode[:export] }
118
+ opt.on('', '--split') { split = true }
119
+ opt.on('', '--split-with-dir') { split = :with_dir }
120
+ opt.on('-d', '--diff DSL1 DSL2') do |diff_arg1|
121
+ set_mode[:diff]
122
+ diff_arg2 = ARGV.first
129
123
 
130
- ARGV.shift
131
- diff_files = [diff_arg1, diff_arg2]
132
- end
133
- opt.on('', '--with-apply') { diff_with_apply = true }
134
- opt.on('-o', '--output SCHEMAFILE') { |v| output_file = v }
135
- opt.on('-t', '--tables TABLES', Array) { |v| options[:tables] = v }
136
- opt.on('', '--ignore-tables REGEX_LIST', Array) { |v| options[:ignore_tables] = v.map { |i| Regexp.new(i) } }
137
- opt.on('', '--dump-without-table-options') { options[:dump_without_table_options] = true }
138
- opt.on('', '--dump-with-default-fk-name') { options[:dump_with_default_fk_name] = true }
139
- opt.on('', '--index-removed-drop-column') { options[:index_removed_drop_column] = true }
140
- opt.on('', '--drop-table') { options[:force_drop_table] = true }
141
- opt.on('', '--mysql-change-table-options') { options[:mysql_change_table_options] = true }
142
- opt.on('', '--mysql-change-table-comment') { options[:mysql_change_table_comment] = true }
143
- opt.on('', '--check-relation-type DEF_PK') { |v| options[:check_relation_type] = v }
144
- opt.on('', '--ignore-table-comment') { options[:ignore_table_comment] = true }
145
- opt.on('', '--skip-column-comment-change') { options[:skip_column_comment_change] = true }
146
- opt.on('', '--allow-pk-change') { options[:allow_pk_change] = true }
147
- opt.on('', '--create-table-with-index') { options[:create_table_with_index] = true }
148
-
149
- opt.on('', '--mysql-dump-auto-increment') do
150
- options[:mysql_dump_auto_increment] = true
124
+ if [diff_arg1, diff_arg2].any? { |i| i.nil? || i.start_with?('-') }
125
+ puts opt.help
126
+ exit 1
151
127
  end
152
128
 
153
- opt.on('-r', '--require LIBS', Array) { |v| v.each { |i| require i } }
154
- opt.on('', '--log-file LOG_FILE') { |v| options[:log_file] = v }
155
- opt.on('', '--verbose') { Ridgepole::Logger.verbose = true }
156
- opt.on('', '--debug') { options[:debug] = true }
157
- opt.on('', '--[no-]color') { |v| options[:color] = v }
158
-
159
- opt.on('-v', '--version') do
160
- puts opt.ver
161
- exit
162
- end
129
+ ARGV.shift
130
+ diff_files = [diff_arg1, diff_arg2]
131
+ end
132
+ opt.on('', '--with-apply') { diff_with_apply = true }
133
+ opt.on('-o', '--output SCHEMAFILE') { |v| output_file = v }
134
+ opt.on('-t', '--tables TABLES', Array) { |v| options[:tables] = v }
135
+ opt.on('', '--ignore-tables REGEX_LIST', Array) { |v| options[:ignore_tables] = v.map { |i| Regexp.new(i) } }
136
+ opt.on('', '--dump-without-table-options') { options[:dump_without_table_options] = true }
137
+ opt.on('', '--dump-with-default-fk-name') { options[:dump_with_default_fk_name] = true }
138
+ opt.on('', '--index-removed-drop-column') { options[:index_removed_drop_column] = true }
139
+ opt.on('', '--drop-table') { options[:force_drop_table] = true }
140
+ opt.on('', '--mysql-change-table-options') { options[:mysql_change_table_options] = true }
141
+ opt.on('', '--mysql-change-table-comment') { options[:mysql_change_table_comment] = true }
142
+ opt.on('', '--check-relation-type DEF_PK') { |v| options[:check_relation_type] = v }
143
+ opt.on('', '--ignore-table-comment') { options[:ignore_table_comment] = true }
144
+ opt.on('', '--skip-column-comment-change') { options[:skip_column_comment_change] = true }
145
+ opt.on('', '--allow-pk-change') { options[:allow_pk_change] = true }
146
+ opt.on('', '--create-table-with-index') { options[:create_table_with_index] = true }
147
+
148
+ opt.on('', '--mysql-dump-auto-increment') do
149
+ options[:mysql_dump_auto_increment] = true
150
+ end
163
151
 
164
- opt.parse!
152
+ opt.on('-r', '--require LIBS', Array) { |v| v.each { |i| require i } }
153
+ opt.on('', '--log-file LOG_FILE') { |v| options[:log_file] = v }
154
+ opt.on('', '--verbose') { Ridgepole::Logger.verbose = true }
155
+ opt.on('', '--debug') { options[:debug] = true }
156
+ opt.on('', '--[no-]color') { |v| options[:color] = v }
165
157
 
166
- if !mode || (%i[apply export].include?(mode) && !config) || (options[:with_apply] && !config)
167
- puts opt.help
168
- exit 1
169
- end
170
- rescue StandardError => e
171
- warn("[ERROR] #{e.message}")
158
+ opt.on('-v', '--version') do
159
+ puts opt.ver
160
+ exit
161
+ end
172
162
 
173
- puts "\t" + e.backtrace.join("\n\t") unless e.is_a?(OptionParser::ParseError)
163
+ opt.parse!
174
164
 
165
+ if !mode || (%i[apply export].include?(mode) && !config) || (options[:with_apply] && !config)
166
+ puts opt.help
175
167
  exit 1
176
168
  end
169
+ rescue StandardError => e
170
+ warn("[ERROR] #{e.message}")
171
+
172
+ puts "\t" + e.backtrace.join("\n\t") unless e.is_a?(OptionParser::ParseError)
173
+
174
+ exit 1
177
175
  end
178
176
 
179
177
  begin
@@ -15,7 +15,12 @@ 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
18
22
  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?
19
24
  end
20
25
 
21
26
  def dump(&block)
@@ -310,11 +310,29 @@ 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
+
313
329
  def append_change(table_name, attrs, buf, pre_buf_for_fk, post_buf_for_fk)
314
330
  definition = attrs[:definition] || {}
315
331
  primary_key_definition = attrs[:primary_key_definition] || {}
316
332
  indices = attrs[:indices] || {}
317
333
  foreign_keys = attrs[:foreign_keys] || {}
334
+ partition = attrs[:partition] || {}
335
+ partition_definitions = attrs[:partition_definitions] || {}
318
336
  table_options = attrs[:table_options]
319
337
  table_charset = attrs[:table_charset]
320
338
  table_collation = attrs[:table_collation]
@@ -335,6 +353,10 @@ execute "ALTER TABLE #{ActiveRecord::Base.connection.quote_table_name(table_name
335
353
 
336
354
  append_change_table_comment(table_name, table_comment, buf) if table_comment
337
355
 
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
+
338
360
  buf.puts
339
361
  pre_buf_for_fk.puts
340
362
  post_buf_for_fk.puts
@@ -37,6 +37,9 @@ 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])
40
43
  end
41
44
 
42
45
  scan_relation_info(relation_info)
@@ -182,8 +185,8 @@ module Ridgepole
182
185
  to = to.except(*PRIMARY_KEY_OPTIONS)
183
186
 
184
187
  unless from == to
185
- @logger.warn(<<-MSG)
186
- [WARNING] Table option changes are ignored on `#{table_name}`.
188
+ @logger.verbose_info(<<-MSG)
189
+ # Table option changes are ignored on `#{table_name}`.
187
190
  from: #{from}
188
191
  to: #{to}
189
192
  MSG
@@ -419,9 +422,6 @@ module Ridgepole
419
422
  opts[:limit] = 4_294_967_295
420
423
  end
421
424
  end
422
-
423
- # Workaround for Active Record 7.0
424
- opts.delete(:precision) if attrs[:type] == :datetime && opts[:precision].nil?
425
425
  end
426
426
  end
427
427
 
@@ -612,6 +612,54 @@ module Ridgepole
612
612
  end
613
613
  end
614
614
 
615
+ def scan_partition_change(table_name, from, to, table_delta)
616
+ from = (from || {}).dup
617
+ to = (to || {}).dup
618
+ partition_delta = {}
619
+
620
+ return if to.empty? && from.empty?
621
+
622
+ if from.empty? && Ridgepole::ConnectionAdapters.mysql?
623
+ partition_delta[:add] ||= {}
624
+ partition_delta[:add][table_name] = to
625
+ else
626
+ if from.present? && (to[:type] != from[:type] || to[:columns] != from[:columns])
627
+ @logger.warn(<<-MSG)
628
+ "[WARNING] '#{table_name}' partition is skipped because of the different partition type.
629
+ to: #{to[:type]} #{to[:columns]}
630
+ from: #{from[:type]}" #{from[:columns]}
631
+ MSG
632
+ return
633
+ end
634
+
635
+ 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?
636
+
637
+ scan_partition_definition_chanage(from, to, table_delta)
638
+ end
639
+
640
+ table_delta[:partition] = partition_delta unless partition_delta.empty?
641
+ end
642
+
643
+ def scan_partition_definition_chanage(from, to, table_delta)
644
+ partition_definitions_delta = {}
645
+ attrs = { type: from[:type] || to[:type] }
646
+
647
+ from_partitions = (from[:partition_definitions] || []).index_by { |partition| partition[:name] }
648
+ to_partitions = (to[:partition_definitions] || []).index_by { |partition| partition[:name] }
649
+
650
+ (from_partitions.keys - to_partitions.keys).each do |name|
651
+ partition_definitions_delta[:delete] ||= {}
652
+ partition_definitions_delta[:delete][name] = attrs.merge(valuve: from_partitions[name][:values])
653
+ end
654
+
655
+ (to_partitions.keys - from_partitions.keys).each do |name|
656
+ partition_definitions_delta[:add] ||= {}
657
+ partition_definitions_delta[:add][name] = attrs.merge(values: to_partitions[name][:values])
658
+ end
659
+
660
+ table_delta[:partition_definitions] = partition_definitions_delta unless partition_definitions_delta.empty?
661
+ end
662
+
615
663
  def check_table_existence(definition)
616
664
  return unless @options[:tables]
617
665
 
@@ -91,6 +91,22 @@ 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
+
94
110
  def require(file)
95
111
  schemafile = %r{\A/}.match?(file) ? file : File.join(@__working_dir, file)
96
112
 
@@ -39,7 +39,7 @@ module Ridgepole
39
39
 
40
40
  dsl = stream.string.lines.select do |line|
41
41
  line !~ /\A#/ &&
42
- line !~ /\AActiveRecord::Schema\.define/ &&
42
+ line !~ /\AActiveRecord::Schema(\[\d\.\d\])?\.define/ &&
43
43
  line !~ /\Aend/
44
44
  end
45
45
 
@@ -0,0 +1,19 @@
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
@@ -0,0 +1,34 @@
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
@@ -0,0 +1,40 @@
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
@@ -0,0 +1,71 @@
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
@@ -0,0 +1,46 @@
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
@@ -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 = '1.0.0'
4
+ VERSION = '1.0.3'
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
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
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: 1.0.0
4
+ version: 1.0.3
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-12-18 00:00:00.000000000 Z
11
+ date: 2022-02-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -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
@@ -306,7 +306,13 @@ 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
309
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
310
316
  - lib/ridgepole/ext/pp_sort_hash.rb
311
317
  - lib/ridgepole/ext/schema_dumper.rb
312
318
  - lib/ridgepole/external_sql_executer.rb
@@ -319,7 +325,8 @@ files:
319
325
  homepage: https://github.com/ridgepole/ridgepole
320
326
  licenses:
321
327
  - MIT
322
- metadata: {}
328
+ metadata:
329
+ rubygems_mfa_required: 'true'
323
330
  post_install_message:
324
331
  rdoc_options: []
325
332
  require_paths: