pg_party 0.7.3 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +194 -64
- data/lib/pg_party.rb +15 -7
- data/lib/pg_party/adapter/abstract_methods.rb +13 -7
- data/lib/pg_party/adapter/postgresql_methods.rb +6 -0
- data/lib/pg_party/adapter_decorator.rb +81 -65
- data/lib/pg_party/cache.rb +2 -0
- data/lib/pg_party/hacks/schema_cache.rb +13 -0
- data/lib/pg_party/model/list_methods.rb +2 -0
- data/lib/pg_party/model/methods.rb +6 -4
- data/lib/pg_party/model/range_methods.rb +2 -0
- data/lib/pg_party/model/shared_methods.rb +2 -0
- data/lib/pg_party/model_decorator.rb +53 -20
- data/lib/pg_party/model_injector.rb +10 -7
- data/lib/pg_party/version.rb +3 -1
- metadata +50 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: b3eb42aaee8d1f94ffd962760c6f4596330cbdc899a314738848861bfc01e29c
|
4
|
+
data.tar.gz: '049172b0bb51a0d8664ad54b4599a588c448b8558861c3f50688fb938aaf7680'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e00484e8d7142cdff81b64301f5cf2207318e07dd9f918730472ece7840e2025e71e15a827987042c3ede9c66ad41633af88cc4ec7e0f83d7a4e28c57e822a8
|
7
|
+
data.tar.gz: c3fa8f87087bd47e87910fcb2707cd58d83c7e6bf91207dfde299b550c9aace44cc905a7781d9ddc5b4281a8de654bf096813420aa8937442bf09a053f13f549
|
data/README.md
CHANGED
@@ -2,26 +2,28 @@
|
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/pg_party.svg)][rubygems]
|
4
4
|
[![Build Status](https://circleci.com/gh/rkrage/pg_party.svg?&style=shield)][circle]
|
5
|
-
[![Dependency Status](https://gemnasium.com/badges/github.com/rkrage/pg_party.svg)][gemnasium]
|
6
5
|
[![Maintainability](https://api.codeclimate.com/v1/badges/c409453d2283dd440227/maintainability)][cc_maintainability]
|
7
6
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/c409453d2283dd440227/test_coverage)][cc_coverage]
|
8
7
|
|
9
8
|
[rubygems]: https://rubygems.org/gems/pg_party
|
10
9
|
[circle]: https://circleci.com/gh/rkrage/pg_party/tree/master
|
11
|
-
[gemnasium]: https://gemnasium.com/github.com/rkrage/pg_party
|
12
10
|
[cc_maintainability]: https://codeclimate.com/github/rkrage/pg_party/maintainability
|
13
11
|
[cc_coverage]: https://codeclimate.com/github/rkrage/pg_party/test_coverage
|
14
12
|
|
15
|
-
[ActiveRecord](http://guides.rubyonrails.org/active_record_basics.html) migrations and model helpers for creating and managing [PostgreSQL 10 partitions](https://www.postgresql.org/docs/10/static/ddl-partitioning.html)!
|
13
|
+
[ActiveRecord](http://guides.rubyonrails.org/active_record_basics.html) migrations and model helpers for creating and managing [PostgreSQL 10+ partitions](https://www.postgresql.org/docs/10/static/ddl-partitioning.html)!
|
16
14
|
|
17
|
-
Features
|
18
|
-
- Migration methods for partition specific database operations
|
19
|
-
- Model methods for querying partitioned data
|
20
|
-
- Model methods for creating adhoc partitions
|
15
|
+
## Features
|
21
16
|
|
22
|
-
|
23
|
-
|
24
|
-
|
17
|
+
- Migration methods for partition specific database operations
|
18
|
+
- Model methods for querying partitioned data, creating adhoc partitions, and retreiving partition metadata
|
19
|
+
|
20
|
+
## Limitations
|
21
|
+
|
22
|
+
- Partition tables are not represented correctly in `db/schema.rb` — please use the `:sql` schema format
|
23
|
+
|
24
|
+
## Future Work
|
25
|
+
|
26
|
+
- Automatic partition creation (via cron or some other means)
|
25
27
|
|
26
28
|
## Installation
|
27
29
|
|
@@ -33,89 +35,152 @@ gem 'pg_party'
|
|
33
35
|
|
34
36
|
And then execute:
|
35
37
|
|
36
|
-
|
38
|
+
```
|
39
|
+
$ bundle
|
40
|
+
```
|
37
41
|
|
38
42
|
Or install it yourself as:
|
39
43
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
Full API documentation is in progress.
|
44
|
+
```
|
45
|
+
$ gem install pg_party
|
46
|
+
```
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
- https://github.com/rkrage/pg_party/tree/master/spec/integration
|
48
|
+
Note that the gemspec does not require `pg`, as some model methods _may_ work for other databases.
|
49
|
+
Migration methods will be unavailable unless `pg` is installed.
|
49
50
|
|
50
|
-
|
51
|
+
## Usage
|
51
52
|
|
52
|
-
|
53
|
+
### Migrations
|
54
|
+
|
55
|
+
#### Methods
|
56
|
+
|
57
|
+
These methods are available in migrations as well as `ActiveRecord::Base#connection` objects.
|
58
|
+
|
59
|
+
- `create_range_partition`
|
60
|
+
- Create partitioned table using the _range_ partitioning method
|
61
|
+
- Required args: `table_name`, `partitition_key:`
|
62
|
+
- `create_list_partition`
|
63
|
+
- Create partitioned table using the _list_ partitioning method
|
64
|
+
- Required args: `table_name`, `partition_key:`
|
65
|
+
- `create_range_partition_of`
|
66
|
+
- Create partition in _range_ partitioned table with partition key between _range_ of values
|
67
|
+
- Required args: `table_name`, `start_range:`, `end_range:`
|
68
|
+
- `create_list_partition_of`
|
69
|
+
- Create partition in _list_ partitioned table with partition key in _list_ of values
|
70
|
+
- Required args: `table_name`, `values:`
|
71
|
+
- `attach_range_partition`
|
72
|
+
- Attach existing table to _range_ partitioned table with partition key between _range_ of values
|
73
|
+
- Required args: `parent_table_name`, `child_table_name`, `start_range:`, `end_range:`
|
74
|
+
- `attach_list_partition`
|
75
|
+
- Attach existing table to _list_ partitioned table with partition key in _list_ of values
|
76
|
+
- Required args: `parent_table_name`, `child_table_name`, `values:`
|
77
|
+
- `detach_partition`
|
78
|
+
- Detach partition from both _range and list_ partitioned tables
|
79
|
+
- Required args: `parent_table_name`, `child_table_name`
|
80
|
+
- `create_table_like`
|
81
|
+
- Clone _any_ existing table
|
82
|
+
- Required args: `table_name`, `new_table_name`
|
83
|
+
|
84
|
+
#### Examples
|
85
|
+
|
86
|
+
Create _range_ partitioned table on `created_at::date` with two partitions:
|
53
87
|
|
54
88
|
```ruby
|
55
89
|
class CreateSomeRangeRecord < ActiveRecord::Migration[5.1]
|
56
90
|
def up
|
57
|
-
|
58
|
-
|
59
|
-
create_range_partition :some_range_records, partition_key: "created_at::date" do |t|
|
91
|
+
# proc is used for partition keys containing expressions
|
92
|
+
create_range_partition :some_range_records, partition_key: ->{ "(created_at::date)" } do |t|
|
60
93
|
t.text :some_value
|
61
94
|
t.timestamps
|
62
95
|
end
|
63
96
|
|
97
|
+
# optional name argument is used to specify child table name
|
64
98
|
create_range_partition_of \
|
65
99
|
:some_range_records,
|
66
|
-
|
67
|
-
start_range:
|
68
|
-
end_range:
|
100
|
+
name: :some_range_records_a,
|
101
|
+
start_range: "2019-06-07",
|
102
|
+
end_range: "2019-06-08"
|
69
103
|
|
104
|
+
# optional name argument is used to specify child table name
|
70
105
|
create_range_partition_of \
|
71
106
|
:some_range_records,
|
72
|
-
|
73
|
-
start_range:
|
74
|
-
end_range:
|
107
|
+
name: :some_range_records_b,
|
108
|
+
start_range: "2019-06-08",
|
109
|
+
end_range: "2019-06-09"
|
75
110
|
end
|
76
111
|
end
|
77
112
|
```
|
78
113
|
|
79
|
-
Create
|
114
|
+
Create _list_ partitioned table on `id` with two partitions:
|
80
115
|
|
81
116
|
```ruby
|
82
117
|
class CreateSomeListRecord < ActiveRecord::Migration[5.1]
|
83
118
|
def up
|
119
|
+
# symbol is used for partition keys referring to individual columns
|
84
120
|
create_list_partition :some_list_records, partition_key: :id do |t|
|
85
121
|
t.text :some_value
|
86
122
|
t.timestamps
|
87
123
|
end
|
88
124
|
|
125
|
+
# without name argument, child partition created as "some_list_records_<hash>"
|
89
126
|
create_list_partition_of \
|
90
127
|
:some_list_records,
|
91
|
-
|
92
|
-
values: (1..100).to_a
|
128
|
+
values: 1..100
|
93
129
|
|
130
|
+
# without name argument, child partition created as "some_list_records_<hash>"
|
94
131
|
create_list_partition_of \
|
95
132
|
:some_list_records,
|
96
|
-
|
97
|
-
values: (100..200).to_a
|
133
|
+
values: 101..200
|
98
134
|
end
|
99
135
|
end
|
100
136
|
```
|
101
137
|
|
102
|
-
|
138
|
+
Unfortunately, PostgreSQL 10 doesn't support indexes on partitioned tables.
|
139
|
+
However, individual _partitions_ can have indexes.
|
140
|
+
To avoid explicit index creation for _every_ new partition, we've introduced the idea of template tables.
|
141
|
+
For every call to `create_list_partition` and `create_range_partition`, a clone `<table_name>_template` is created.
|
142
|
+
Indexes, constraints, etc. created on the template table will propagate to new partitions in calls to `create_list_partition_of` and `create_range_partition_of`:
|
103
143
|
|
104
144
|
```ruby
|
105
|
-
class
|
145
|
+
class CreateSomeListRecord < ActiveRecord::Migration[5.1]
|
106
146
|
def up
|
107
|
-
|
147
|
+
# template table creation is enabled by default - use "template: false" to opt-out
|
148
|
+
create_list_partition :some_list_records, partition_key: :id do |t|
|
149
|
+
t.integer :some_foreign_id
|
150
|
+
t.text :some_value
|
151
|
+
t.timestamps
|
152
|
+
end
|
153
|
+
|
154
|
+
# create index on the template table
|
155
|
+
add_index :some_list_records_template, :some_foreign_id
|
156
|
+
|
157
|
+
# create partition with an index on "some_foreign_id"
|
158
|
+
create_list_partition_of \
|
159
|
+
:some_list_records,
|
160
|
+
values: 1..100
|
161
|
+
|
162
|
+
# create partition with an index on "some_foreign_id"
|
163
|
+
create_list_partition_of \
|
164
|
+
:some_list_records,
|
165
|
+
values: 101..200
|
166
|
+
end
|
167
|
+
end
|
168
|
+
```
|
169
|
+
|
170
|
+
Attach an existing table to a _range_ partitioned table:
|
108
171
|
|
172
|
+
```ruby
|
173
|
+
def up
|
109
174
|
attach_range_partition \
|
110
175
|
:some_range_records,
|
111
176
|
:some_existing_table,
|
112
|
-
start_range:
|
113
|
-
end_range:
|
177
|
+
start_range: "2019-06-09",
|
178
|
+
end_range: "2019-06-10"
|
114
179
|
end
|
115
180
|
end
|
116
181
|
```
|
117
182
|
|
118
|
-
Attach an existing table to a
|
183
|
+
Attach an existing table to a _list_ partitioned table:
|
119
184
|
|
120
185
|
```ruby
|
121
186
|
class AttachListPartition < ActiveRecord::Migration[5.1]
|
@@ -123,12 +188,12 @@ class AttachListPartition < ActiveRecord::Migration[5.1]
|
|
123
188
|
attach_list_partition \
|
124
189
|
:some_list_records,
|
125
190
|
:some_existing_table,
|
126
|
-
values:
|
191
|
+
values: 200..300
|
127
192
|
end
|
128
193
|
end
|
129
194
|
```
|
130
195
|
|
131
|
-
Detach a
|
196
|
+
Detach a partition from any partitioned table:
|
132
197
|
|
133
198
|
```ruby
|
134
199
|
class DetachPartition < ActiveRecord::Migration[5.1]
|
@@ -138,51 +203,104 @@ class DetachPartition < ActiveRecord::Migration[5.1]
|
|
138
203
|
end
|
139
204
|
```
|
140
205
|
|
141
|
-
|
206
|
+
For more examples, take a look at the Combustion schema definition and integration specs:
|
207
|
+
|
208
|
+
- https://github.com/rkrage/pg_party/blob/master/spec/dummy/db/schema.rb
|
209
|
+
- https://github.com/rkrage/pg_party/blob/master/spec/integration/migration_spec.rb
|
210
|
+
|
211
|
+
### Models
|
212
|
+
|
213
|
+
#### Methods
|
214
|
+
|
215
|
+
Class methods available to _all_ ActiveRecord models:
|
216
|
+
|
217
|
+
- `partitioned?`
|
218
|
+
- Check if a model is backed by either a _list or range_ partitioned table
|
219
|
+
- No arguments
|
220
|
+
- `range_partition_by`
|
221
|
+
- Configure a model backed by a _range_ partitioned table
|
222
|
+
- Required arg: `key` (partition key column) or block returning partition key expression
|
223
|
+
- `list_partition_by`
|
224
|
+
- Configure a model backed by a _list_ partitioned table
|
225
|
+
- Required arg: `key` (partition key column) or block returning partition key expression
|
226
|
+
|
227
|
+
Class methods available to both _range and list_ partitioned models:
|
228
|
+
|
229
|
+
- `partitions`
|
230
|
+
- Retrieve a list of currently attached partitions
|
231
|
+
- No arguments
|
232
|
+
- `in_partition`
|
233
|
+
- Retrieve an ActiveRecord model scoped to an individual partition
|
234
|
+
- Required arg: `child_table_name`
|
235
|
+
- `partition_key_eq`
|
236
|
+
- Query for records where partition key matches a value
|
237
|
+
- Required arg: `value`
|
238
|
+
|
239
|
+
Class methods available to _range_ partitioned models:
|
240
|
+
|
241
|
+
- `create_partition`
|
242
|
+
- Dynamically create new partition with partition key in _range_ of values
|
243
|
+
- Required args: `start_range:`, `end_range:`
|
244
|
+
- `partition_key_in`
|
245
|
+
- Query for records where partition key in _range_ of values
|
246
|
+
- Required args: `start_range`, `end_range`
|
247
|
+
|
248
|
+
Class methods available to _list_ partitioned models:
|
142
249
|
|
143
|
-
|
250
|
+
- `create_partition`
|
251
|
+
- Dynamically create new partition with partition key in _list_ of values
|
252
|
+
- Required arg: `values:`
|
253
|
+
- `partition_key_in`
|
254
|
+
- Query for records where partition key in _list_ of values
|
255
|
+
- Required arg: list of `values`
|
256
|
+
|
257
|
+
#### Examples
|
258
|
+
|
259
|
+
Configure model backed by a _range_ partitioned table to get access to the methods described above:
|
144
260
|
|
145
261
|
```ruby
|
146
262
|
class SomeRangeRecord < ApplicationRecord
|
147
|
-
|
263
|
+
# block is used for partition keys containing expressions
|
264
|
+
range_partition_by { "(created_at::date)" }
|
148
265
|
end
|
149
266
|
```
|
150
267
|
|
151
|
-
|
268
|
+
Configure model backed by a _list_ partitioned table to get access to the methods described above:
|
152
269
|
|
153
270
|
```ruby
|
154
271
|
class SomeListRecord < ApplicationRecord
|
272
|
+
# symbol is used for partition keys referring to individual columns
|
155
273
|
list_partition_by :id
|
156
274
|
end
|
157
275
|
```
|
158
276
|
|
159
|
-
|
277
|
+
Dynamically create new partition from _range_ partitioned model:
|
160
278
|
|
161
279
|
```ruby
|
162
|
-
|
163
|
-
|
164
|
-
SomeRangeRecord.create_partition(start_range: current_date + 1.day, end_range: current_date + 2.days)
|
280
|
+
# additional options include: "name:" and "primary_key:"
|
281
|
+
SomeRangeRecord.create_partition(start_range: "2019-06-09", end_range: "2019-06-10")
|
165
282
|
```
|
166
283
|
|
167
|
-
|
284
|
+
Dynamically create new partition from _list_ partitioned model:
|
168
285
|
|
169
286
|
```ruby
|
170
|
-
|
287
|
+
# additional options include: "name:" and "primary_key:"
|
288
|
+
SomeListRecord.create_partition(values: 200..300)
|
171
289
|
```
|
172
290
|
|
173
|
-
|
291
|
+
For _range_ partitioned model, query for records where partition key in _range_ of values:
|
174
292
|
|
175
293
|
```ruby
|
176
|
-
SomeRangeRecord.partition_key_in("
|
294
|
+
SomeRangeRecord.partition_key_in("2019-06-08", "2019-06-10")
|
177
295
|
```
|
178
296
|
|
179
|
-
|
297
|
+
For _list_ partitioned model, query for records where partition key in _list_ of values:
|
180
298
|
|
181
299
|
```ruby
|
182
300
|
SomeListRecord.partition_key_in(1, 2, 3, 4)
|
183
301
|
```
|
184
302
|
|
185
|
-
|
303
|
+
For both _range and list_ partitioned models, query for records matching partition key:
|
186
304
|
|
187
305
|
```ruby
|
188
306
|
SomeRangeRecord.partition_key_eq(Date.current)
|
@@ -190,7 +308,7 @@ SomeRangeRecord.partition_key_eq(Date.current)
|
|
190
308
|
SomeListRecord.partition_key_eq(100)
|
191
309
|
```
|
192
310
|
|
193
|
-
|
311
|
+
For both _range and list_ partitioned models, retrieve currently attached partitions:
|
194
312
|
|
195
313
|
```ruby
|
196
314
|
SomeRangeRecord.partitions
|
@@ -198,7 +316,7 @@ SomeRangeRecord.partitions
|
|
198
316
|
SomeListRecord.partitions
|
199
317
|
```
|
200
318
|
|
201
|
-
|
319
|
+
For both _range and list_ partitioned models, retrieve ActiveRecord model scoped to individual partition:
|
202
320
|
|
203
321
|
```ruby
|
204
322
|
SomeRangeRecord.in_partition(:some_range_records_partition_name)
|
@@ -206,26 +324,38 @@ SomeRangeRecord.in_partition(:some_range_records_partition_name)
|
|
206
324
|
SomeListRecord.in_partition(:some_list_records_partition_name)
|
207
325
|
```
|
208
326
|
|
327
|
+
For more examples, take a look at the model integration specs:
|
328
|
+
|
329
|
+
- https://github.com/rkrage/pg_party/tree/documentation/spec/integration/model
|
330
|
+
|
209
331
|
## Development
|
210
332
|
|
211
333
|
The development / test environment relies heavily on [Docker](https://docs.docker.com).
|
212
334
|
|
213
335
|
Start the containers in the background:
|
214
336
|
|
215
|
-
|
337
|
+
```
|
338
|
+
$ docker-compose up -d
|
339
|
+
```
|
216
340
|
|
217
341
|
Install dependencies:
|
218
342
|
|
219
|
-
|
220
|
-
|
343
|
+
```
|
344
|
+
$ bin/de bundle
|
345
|
+
$ bin/de appraisal
|
346
|
+
```
|
221
347
|
|
222
348
|
Run the tests:
|
223
349
|
|
224
|
-
|
350
|
+
```
|
351
|
+
$ bin/de appraisal rake
|
352
|
+
```
|
225
353
|
|
226
354
|
Open a Pry console to play around with the sample Rails app:
|
227
355
|
|
228
|
-
|
356
|
+
```
|
357
|
+
$ bin/de console
|
358
|
+
```
|
229
359
|
|
230
360
|
## Contributing
|
231
361
|
|
data/lib/pg_party.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pg_party/version"
|
2
4
|
require "active_support"
|
3
5
|
|
@@ -8,17 +10,23 @@ ActiveSupport.on_load(:active_record) do
|
|
8
10
|
|
9
11
|
require "pg_party/adapter/abstract_methods"
|
10
12
|
|
11
|
-
ActiveRecord::ConnectionAdapters::AbstractAdapter.
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
ActiveRecord::ConnectionAdapters::AbstractAdapter.include(
|
14
|
+
PgParty::Adapter::AbstractMethods
|
15
|
+
)
|
16
|
+
|
17
|
+
require "pg_party/hacks/schema_cache"
|
18
|
+
|
19
|
+
ActiveRecord::ConnectionAdapters::SchemaCache.include(
|
20
|
+
PgParty::Hacks::SchemaCache
|
21
|
+
)
|
22
|
+
|
15
23
|
begin
|
16
24
|
require "active_record/connection_adapters/postgresql_adapter"
|
17
25
|
require "pg_party/adapter/postgresql_methods"
|
18
26
|
|
19
|
-
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.
|
20
|
-
|
21
|
-
|
27
|
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.include(
|
28
|
+
PgParty::Adapter::PostgreSQLMethods
|
29
|
+
)
|
22
30
|
rescue LoadError
|
23
31
|
# migration methods will not be available
|
24
32
|
end
|
@@ -1,32 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PgParty
|
2
4
|
module Adapter
|
3
5
|
module AbstractMethods
|
4
6
|
def create_range_partition(*)
|
5
|
-
raise
|
7
|
+
raise "#create_range_partition is not implemented"
|
6
8
|
end
|
7
9
|
|
8
10
|
def create_list_partition(*)
|
9
|
-
raise
|
11
|
+
raise "#create_list_partition is not implemented"
|
10
12
|
end
|
11
13
|
|
12
14
|
def create_range_partition_of(*)
|
13
|
-
raise
|
15
|
+
raise "#create_range_partition_of is not implemented"
|
14
16
|
end
|
15
17
|
|
16
18
|
def create_list_partition_of(*)
|
17
|
-
raise
|
19
|
+
raise "#create_list_partition_of is not implemented"
|
20
|
+
end
|
21
|
+
|
22
|
+
def create_table_like(*)
|
23
|
+
raise "#create_table_like is not implemented"
|
18
24
|
end
|
19
25
|
|
20
26
|
def attach_range_partition(*)
|
21
|
-
raise
|
27
|
+
raise "#attach_range_partition is not implemented"
|
22
28
|
end
|
23
29
|
|
24
30
|
def attach_list_partition(*)
|
25
|
-
raise
|
31
|
+
raise "#attach_list_partition is not implemented"
|
26
32
|
end
|
27
33
|
|
28
34
|
def detach_partition(*)
|
29
|
-
raise
|
35
|
+
raise "#detach_partition is not implemented"
|
30
36
|
end
|
31
37
|
end
|
32
38
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pg_party/adapter_decorator"
|
2
4
|
|
3
5
|
module PgParty
|
@@ -19,6 +21,10 @@ module PgParty
|
|
19
21
|
PgParty::AdapterDecorator.new(self).create_list_partition_of(*args)
|
20
22
|
end
|
21
23
|
|
24
|
+
def create_table_like(*args)
|
25
|
+
PgParty::AdapterDecorator.new(self).create_table_like(*args)
|
26
|
+
end
|
27
|
+
|
22
28
|
def attach_range_partition(*args)
|
23
29
|
PgParty::AdapterDecorator.new(self).attach_range_partition(*args)
|
24
30
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "digest"
|
2
4
|
require "pg_party/cache"
|
3
5
|
|
@@ -18,47 +20,39 @@ module PgParty
|
|
18
20
|
end
|
19
21
|
|
20
22
|
def create_range_partition_of(table_name, start_range:, end_range:, **options)
|
21
|
-
|
22
|
-
child_table_name = options[:name]
|
23
|
-
else
|
24
|
-
child_table_name = hashed_table_name(table_name, "#{start_range}#{end_range}")
|
25
|
-
end
|
26
|
-
|
27
|
-
constraint_clause = "FROM (#{quote(start_range)}) TO (#{quote(end_range)})"
|
28
|
-
|
29
|
-
create_partition_of(table_name, child_table_name, constraint_clause, **options)
|
23
|
+
create_partition_of(table_name, range_constraint_clause(start_range, end_range), **options)
|
30
24
|
end
|
31
25
|
|
32
26
|
def create_list_partition_of(table_name, values:, **options)
|
33
|
-
|
34
|
-
|
35
|
-
else
|
36
|
-
child_table_name = hashed_table_name(table_name, values.to_s)
|
37
|
-
end
|
27
|
+
create_partition_of(table_name, list_constraint_clause(values), **options)
|
28
|
+
end
|
38
29
|
|
39
|
-
|
30
|
+
def create_table_like(table_name, new_table_name, **options)
|
31
|
+
primary_key = options.fetch(:primary_key) { calculate_primary_key(table_name) }
|
40
32
|
|
41
|
-
|
42
|
-
end
|
33
|
+
validate_primary_key(primary_key)
|
43
34
|
|
44
|
-
def attach_range_partition(parent_table_name, child_table_name, start_range:, end_range:)
|
45
35
|
execute(<<-SQL)
|
46
|
-
|
47
|
-
|
48
|
-
|
36
|
+
CREATE TABLE #{quote_table_name(new_table_name)} (
|
37
|
+
LIKE #{quote_table_name(table_name)} INCLUDING ALL
|
38
|
+
)
|
49
39
|
SQL
|
50
40
|
|
51
|
-
|
52
|
-
|
41
|
+
return if !primary_key
|
42
|
+
return if has_primary_key?(new_table_name)
|
53
43
|
|
54
|
-
def attach_list_partition(parent_table_name, child_table_name, values:)
|
55
44
|
execute(<<-SQL)
|
56
|
-
ALTER TABLE #{quote_table_name(
|
57
|
-
|
58
|
-
FOR VALUES IN (#{Array.wrap(values).map(&method(:quote)).join(",")})
|
45
|
+
ALTER TABLE #{quote_table_name(new_table_name)}
|
46
|
+
ADD PRIMARY KEY (#{quote_column_name(primary_key)})
|
59
47
|
SQL
|
48
|
+
end
|
60
49
|
|
61
|
-
|
50
|
+
def attach_range_partition(parent_table_name, child_table_name, start_range:, end_range:)
|
51
|
+
attach_partition(parent_table_name, child_table_name, range_constraint_clause(start_range, end_range))
|
52
|
+
end
|
53
|
+
|
54
|
+
def attach_list_partition(parent_table_name, child_table_name, values:)
|
55
|
+
attach_partition(parent_table_name, child_table_name, list_constraint_clause(values))
|
62
56
|
end
|
63
57
|
|
64
58
|
def detach_partition(parent_table_name, child_table_name)
|
@@ -73,16 +67,17 @@ module PgParty
|
|
73
67
|
private
|
74
68
|
|
75
69
|
def create_partition(table_name, type, partition_key, **options)
|
76
|
-
modified_options = options.except(:id, :primary_key)
|
70
|
+
modified_options = options.except(:id, :primary_key, :template)
|
71
|
+
template = options.fetch(:template, true)
|
77
72
|
id = options.fetch(:id, :bigserial)
|
78
|
-
primary_key = options.fetch(:primary_key
|
73
|
+
primary_key = options.fetch(:primary_key) { calculate_primary_key(table_name) }
|
79
74
|
|
80
|
-
|
75
|
+
validate_primary_key(primary_key)
|
81
76
|
|
82
77
|
modified_options[:id] = false
|
83
|
-
modified_options[:options] = "PARTITION BY #{type.to_s.upcase} (
|
78
|
+
modified_options[:options] = "PARTITION BY #{type.to_s.upcase} (#{quote_partition_key(partition_key)})"
|
84
79
|
|
85
|
-
|
80
|
+
create_table(table_name, modified_options) do |td|
|
86
81
|
if id == :uuid
|
87
82
|
td.column(primary_key, id, null: false, default: uuid_function)
|
88
83
|
elsif id
|
@@ -95,42 +90,35 @@ module PgParty
|
|
95
90
|
# Rails 4 has a bug where uuid columns are always nullable
|
96
91
|
change_column_null(table_name, primary_key, false) if id == :uuid
|
97
92
|
|
98
|
-
|
93
|
+
return unless template
|
94
|
+
|
95
|
+
create_table_like(table_name, template_table_name(table_name), primary_key: id && primary_key)
|
99
96
|
end
|
100
97
|
|
101
|
-
def create_partition_of(table_name,
|
102
|
-
|
103
|
-
|
104
|
-
|
98
|
+
def create_partition_of(table_name, constraint_clause, **options)
|
99
|
+
child_table_name = options.fetch(:name) { hashed_table_name(table_name, constraint_clause) }
|
100
|
+
primary_key = options.fetch(:primary_key) { calculate_primary_key(table_name) }
|
101
|
+
template_table_name = template_table_name(table_name)
|
105
102
|
|
106
|
-
|
103
|
+
if schema_cache.data_source_exists?(template_table_name)
|
104
|
+
create_table_like(template_table_name, child_table_name, primary_key: false)
|
105
|
+
else
|
106
|
+
create_table_like(table_name, child_table_name, primary_key: primary_key)
|
107
|
+
end
|
107
108
|
|
109
|
+
attach_partition(table_name, child_table_name, constraint_clause)
|
110
|
+
|
111
|
+
child_table_name
|
112
|
+
end
|
113
|
+
|
114
|
+
def attach_partition(parent_table_name, child_table_name, constraint_clause)
|
108
115
|
execute(<<-SQL)
|
109
|
-
|
110
|
-
PARTITION
|
116
|
+
ALTER TABLE #{quote_table_name(parent_table_name)}
|
117
|
+
ATTACH PARTITION #{quote_table_name(child_table_name)}
|
111
118
|
FOR VALUES #{constraint_clause}
|
112
119
|
SQL
|
113
120
|
|
114
|
-
if primary_key
|
115
|
-
execute(<<-SQL)
|
116
|
-
ALTER TABLE #{quote_table_name(child_table_name)}
|
117
|
-
ADD PRIMARY KEY (#{quote_column_name(primary_key)})
|
118
|
-
SQL
|
119
|
-
end
|
120
|
-
|
121
|
-
if index && partition_key && primary_key != partition_key
|
122
|
-
index_name = index_name(child_table_name, partition_key)
|
123
|
-
|
124
|
-
execute(<<-SQL)
|
125
|
-
CREATE INDEX #{quote_table_name(index_name)}
|
126
|
-
ON #{quote_table_name(child_table_name)}
|
127
|
-
USING btree ((#{quote_partition_key(partition_key)}))
|
128
|
-
SQL
|
129
|
-
end
|
130
|
-
|
131
121
|
PgParty::Cache.clear!
|
132
|
-
|
133
|
-
child_table_name
|
134
122
|
end
|
135
123
|
|
136
124
|
# Rails 5.2 now returns boolean literals
|
@@ -145,8 +133,40 @@ module PgParty
|
|
145
133
|
end
|
146
134
|
end
|
147
135
|
|
136
|
+
def has_primary_key?(table_name)
|
137
|
+
primary_key(table_name).present?
|
138
|
+
end
|
139
|
+
|
140
|
+
def calculate_primary_key(table_name)
|
141
|
+
ActiveRecord::Base.get_primary_key(table_name.to_s.singularize).to_sym
|
142
|
+
end
|
143
|
+
|
144
|
+
def validate_primary_key(key)
|
145
|
+
raise ArgumentError, "composite primary key not supported" if key.is_a?(Array)
|
146
|
+
end
|
147
|
+
|
148
148
|
def quote_partition_key(key)
|
149
|
-
key.
|
149
|
+
if key.is_a?(Proc)
|
150
|
+
key.call.to_s # very difficult to determine how to sanitize a complex expression
|
151
|
+
else
|
152
|
+
quote_column_name(key)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def quote_collection(values)
|
157
|
+
Array.wrap(values).map(&method(:quote)).join(",")
|
158
|
+
end
|
159
|
+
|
160
|
+
def template_table_name(table_name)
|
161
|
+
"#{table_name}_template"
|
162
|
+
end
|
163
|
+
|
164
|
+
def range_constraint_clause(start_range, end_range)
|
165
|
+
"FROM (#{quote_collection(start_range)}) TO (#{quote_collection(end_range)})"
|
166
|
+
end
|
167
|
+
|
168
|
+
def list_constraint_clause(values)
|
169
|
+
"IN (#{quote_collection(values.try(:to_a) || values)})"
|
150
170
|
end
|
151
171
|
|
152
172
|
def uuid_function
|
@@ -160,9 +180,5 @@ module PgParty
|
|
160
180
|
def supports_partitions?
|
161
181
|
__getobj__.send(:postgresql_version) >= 100000
|
162
182
|
end
|
163
|
-
|
164
|
-
def index_name(table_name, key)
|
165
|
-
"index_#{table_name}_on_#{key.to_s.split("::").join("_")}"
|
166
|
-
end
|
167
183
|
end
|
168
184
|
end
|
data/lib/pg_party/cache.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgParty
|
4
|
+
module Hacks
|
5
|
+
module SchemaCache
|
6
|
+
def self.included(base)
|
7
|
+
return if base.method_defined?(:data_source_exists?)
|
8
|
+
|
9
|
+
base.send(:alias_method, :data_source_exists?, :table_exists?)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,14 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pg_party/model_injector"
|
2
4
|
|
3
5
|
module PgParty
|
4
6
|
module Model
|
5
7
|
module Methods
|
6
|
-
def range_partition_by(key)
|
7
|
-
PgParty::ModelInjector.new(self, key).inject_range_methods
|
8
|
+
def range_partition_by(key=nil, &blk)
|
9
|
+
PgParty::ModelInjector.new(self, key || blk).inject_range_methods
|
8
10
|
end
|
9
11
|
|
10
|
-
def list_partition_by(key)
|
11
|
-
PgParty::ModelInjector.new(self, key).inject_list_methods
|
12
|
+
def list_partition_by(key=nil, &blk)
|
13
|
+
PgParty::ModelInjector.new(self, key || blk).inject_list_methods
|
12
14
|
end
|
13
15
|
|
14
16
|
def partitioned?
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "pg_party/cache"
|
2
4
|
|
3
5
|
module PgParty
|
@@ -15,7 +17,7 @@ module PgParty
|
|
15
17
|
def partition_table_exists?
|
16
18
|
target_table = partitions.first || table_name
|
17
19
|
|
18
|
-
connection.schema_cache.
|
20
|
+
connection.schema_cache.data_source_exists?(target_table)
|
19
21
|
end
|
20
22
|
|
21
23
|
def in_partition(child_table_name)
|
@@ -44,17 +46,33 @@ module PgParty
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def partition_key_eq(value)
|
47
|
-
|
49
|
+
if complex_partition_key
|
50
|
+
complex_partition_key_query("(#{partition_key}) = (?)", value)
|
51
|
+
else
|
52
|
+
where(current_arel_table[partition_key].eq(value))
|
53
|
+
end
|
48
54
|
end
|
49
55
|
|
50
56
|
def range_partition_key_in(start_range, end_range)
|
51
|
-
|
57
|
+
if complex_partition_key
|
58
|
+
complex_partition_key_query(
|
59
|
+
"(#{partition_key}) >= (?) AND (#{partition_key}) < (?)",
|
60
|
+
start_range,
|
61
|
+
end_range
|
62
|
+
)
|
63
|
+
else
|
64
|
+
node = current_arel_table[partition_key]
|
52
65
|
|
53
|
-
|
66
|
+
where(node.gteq(start_range).and(node.lt(end_range)))
|
67
|
+
end
|
54
68
|
end
|
55
69
|
|
56
70
|
def list_partition_key_in(*values)
|
57
|
-
|
71
|
+
if complex_partition_key
|
72
|
+
complex_partition_key_query("(#{partition_key}) IN (?)", values.flatten)
|
73
|
+
else
|
74
|
+
where(current_arel_table[partition_key].in(values.flatten))
|
75
|
+
end
|
58
76
|
end
|
59
77
|
|
60
78
|
def partitions
|
@@ -67,6 +85,8 @@ module PgParty
|
|
67
85
|
WHERE pg_tables.tablename = #{connection.quote(table_name)}
|
68
86
|
SQL
|
69
87
|
end
|
88
|
+
rescue
|
89
|
+
[]
|
70
90
|
end
|
71
91
|
|
72
92
|
def create_range_partition(start_range:, end_range:, **options)
|
@@ -74,44 +94,57 @@ module PgParty
|
|
74
94
|
start_range: start_range,
|
75
95
|
end_range: end_range,
|
76
96
|
primary_key: primary_key,
|
77
|
-
partition_key: partition_key
|
78
97
|
)
|
79
98
|
|
80
|
-
|
99
|
+
create_partition(:create_range_partition_of, table_name, **modified_options)
|
81
100
|
end
|
82
101
|
|
83
102
|
def create_list_partition(values:, **options)
|
84
103
|
modified_options = options.merge(
|
85
104
|
values: values,
|
86
105
|
primary_key: primary_key,
|
87
|
-
partition_key: partition_key
|
88
106
|
)
|
89
107
|
|
90
|
-
|
108
|
+
create_partition(:create_list_partition_of, table_name, **modified_options)
|
91
109
|
end
|
92
110
|
|
93
111
|
private
|
94
112
|
|
113
|
+
def create_partition(migration_method, table_name, **options)
|
114
|
+
transaction { connection.send(migration_method, table_name, **options) }
|
115
|
+
end
|
116
|
+
|
95
117
|
def cache_key
|
96
118
|
__getobj__.object_id
|
97
119
|
end
|
98
120
|
|
99
|
-
|
100
|
-
|
101
|
-
|
121
|
+
# https://stackoverflow.com/questions/28685149/activerecord-query-with-aliasd-table-names
|
122
|
+
def current_arel_table
|
123
|
+
none.arel.source.left.tap do |node|
|
124
|
+
if [Arel::Table, Arel::Nodes::TableAlias].exclude?(node.class)
|
125
|
+
raise "could not find arel table in current scope"
|
126
|
+
end
|
102
127
|
end
|
103
128
|
end
|
104
129
|
|
105
|
-
def
|
106
|
-
|
130
|
+
def current_alias
|
131
|
+
arel_node = current_arel_table
|
107
132
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
arel_column
|
133
|
+
case arel_node
|
134
|
+
when Arel::Table
|
135
|
+
arel_node.name
|
136
|
+
when Arel::Nodes::TableAlias
|
137
|
+
arel_node.right
|
114
138
|
end
|
115
139
|
end
|
140
|
+
|
141
|
+
def complex_partition_key_query(clause, *interpolated_values)
|
142
|
+
subquery = base_class
|
143
|
+
.unscoped
|
144
|
+
.select("*")
|
145
|
+
.where(clause, *interpolated_values)
|
146
|
+
|
147
|
+
from(subquery, current_alias)
|
148
|
+
end
|
116
149
|
end
|
117
150
|
end
|
@@ -1,10 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PgParty
|
2
4
|
class ModelInjector
|
3
5
|
def initialize(model, key)
|
4
6
|
@model = model
|
5
7
|
@key = key
|
6
|
-
|
7
|
-
@column, @cast = key.to_s.split("::")
|
8
8
|
end
|
9
9
|
|
10
10
|
def inject_range_methods
|
@@ -33,15 +33,18 @@ module PgParty
|
|
33
33
|
def create_class_attributes
|
34
34
|
@model.class_attribute(
|
35
35
|
:partition_key,
|
36
|
-
:
|
37
|
-
:partition_cast,
|
36
|
+
:complex_partition_key,
|
38
37
|
instance_accessor: false,
|
39
38
|
instance_predicate: false
|
40
39
|
)
|
41
40
|
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
if @key.is_a?(Proc)
|
42
|
+
@model.partition_key = @key.call
|
43
|
+
@model.complex_partition_key = true
|
44
|
+
else
|
45
|
+
@model.partition_key = @key
|
46
|
+
@model.complex_partition_key = false
|
47
|
+
end
|
45
48
|
end
|
46
49
|
end
|
47
50
|
end
|
data/lib/pg_party/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_party
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Krage
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-06-07 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: '4.2'
|
20
20
|
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version: '6'
|
22
|
+
version: '6.1'
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,21 +29,21 @@ dependencies:
|
|
29
29
|
version: '4.2'
|
30
30
|
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: '6'
|
32
|
+
version: '6.1'
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
34
|
+
name: appraisal
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: '
|
39
|
+
version: '2.2'
|
40
40
|
type: :development
|
41
41
|
prerelease: false
|
42
42
|
version_requirements: !ruby/object:Gem::Requirement
|
43
43
|
requirements:
|
44
44
|
- - "~>"
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: '
|
46
|
+
version: '2.2'
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: bundler
|
49
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -59,131 +59,131 @@ dependencies:
|
|
59
59
|
- !ruby/object:Gem::Version
|
60
60
|
version: '1.15'
|
61
61
|
- !ruby/object:Gem::Dependency
|
62
|
-
name:
|
62
|
+
name: byebug
|
63
63
|
requirement: !ruby/object:Gem::Requirement
|
64
64
|
requirements:
|
65
65
|
- - "~>"
|
66
66
|
- !ruby/object:Gem::Version
|
67
|
-
version: '
|
67
|
+
version: '10.0'
|
68
68
|
type: :development
|
69
69
|
prerelease: false
|
70
70
|
version_requirements: !ruby/object:Gem::Requirement
|
71
71
|
requirements:
|
72
72
|
- - "~>"
|
73
73
|
- !ruby/object:Gem::Version
|
74
|
-
version: '
|
74
|
+
version: '10.0'
|
75
75
|
- !ruby/object:Gem::Dependency
|
76
|
-
name:
|
76
|
+
name: combustion
|
77
77
|
requirement: !ruby/object:Gem::Requirement
|
78
78
|
requirements:
|
79
79
|
- - "~>"
|
80
80
|
- !ruby/object:Gem::Version
|
81
|
-
version: '
|
81
|
+
version: '1.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: '
|
88
|
+
version: '1.1'
|
89
89
|
- !ruby/object:Gem::Dependency
|
90
|
-
name:
|
90
|
+
name: database_cleaner
|
91
91
|
requirement: !ruby/object:Gem::Requirement
|
92
92
|
requirements:
|
93
93
|
- - "~>"
|
94
94
|
- !ruby/object:Gem::Version
|
95
|
-
version: '1.
|
95
|
+
version: '1.6'
|
96
96
|
type: :development
|
97
97
|
prerelease: false
|
98
98
|
version_requirements: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|
100
100
|
- - "~>"
|
101
101
|
- !ruby/object:Gem::Version
|
102
|
-
version: '1.
|
102
|
+
version: '1.6'
|
103
103
|
- !ruby/object:Gem::Dependency
|
104
|
-
name:
|
104
|
+
name: nokogiri
|
105
105
|
requirement: !ruby/object:Gem::Requirement
|
106
106
|
requirements:
|
107
107
|
- - "~>"
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version:
|
109
|
+
version: 1.9.1
|
110
110
|
type: :development
|
111
111
|
prerelease: false
|
112
112
|
version_requirements: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
|
-
version:
|
116
|
+
version: 1.9.1
|
117
117
|
- !ruby/object:Gem::Dependency
|
118
|
-
name:
|
118
|
+
name: pry-byebug
|
119
119
|
requirement: !ruby/object:Gem::Requirement
|
120
120
|
requirements:
|
121
121
|
- - "~>"
|
122
122
|
- !ruby/object:Gem::Version
|
123
|
-
version: '
|
123
|
+
version: '3.4'
|
124
124
|
type: :development
|
125
125
|
prerelease: false
|
126
126
|
version_requirements: !ruby/object:Gem::Requirement
|
127
127
|
requirements:
|
128
128
|
- - "~>"
|
129
129
|
- !ruby/object:Gem::Version
|
130
|
-
version: '
|
130
|
+
version: '3.4'
|
131
131
|
- !ruby/object:Gem::Dependency
|
132
|
-
name:
|
132
|
+
name: rake
|
133
133
|
requirement: !ruby/object:Gem::Requirement
|
134
134
|
requirements:
|
135
135
|
- - "~>"
|
136
136
|
- !ruby/object:Gem::Version
|
137
|
-
version: '
|
137
|
+
version: '12.0'
|
138
138
|
type: :development
|
139
139
|
prerelease: false
|
140
140
|
version_requirements: !ruby/object:Gem::Requirement
|
141
141
|
requirements:
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: '
|
144
|
+
version: '12.0'
|
145
145
|
- !ruby/object:Gem::Dependency
|
146
|
-
name:
|
146
|
+
name: rspec-its
|
147
147
|
requirement: !ruby/object:Gem::Requirement
|
148
148
|
requirements:
|
149
149
|
- - "~>"
|
150
150
|
- !ruby/object:Gem::Version
|
151
|
-
version: '
|
151
|
+
version: '1.2'
|
152
152
|
type: :development
|
153
153
|
prerelease: false
|
154
154
|
version_requirements: !ruby/object:Gem::Requirement
|
155
155
|
requirements:
|
156
156
|
- - "~>"
|
157
157
|
- !ruby/object:Gem::Version
|
158
|
-
version: '
|
158
|
+
version: '1.2'
|
159
159
|
- !ruby/object:Gem::Dependency
|
160
|
-
name:
|
160
|
+
name: rspec-rails
|
161
161
|
requirement: !ruby/object:Gem::Requirement
|
162
162
|
requirements:
|
163
163
|
- - "~>"
|
164
164
|
- !ruby/object:Gem::Version
|
165
|
-
version: '
|
165
|
+
version: '3.6'
|
166
166
|
type: :development
|
167
167
|
prerelease: false
|
168
168
|
version_requirements: !ruby/object:Gem::Requirement
|
169
169
|
requirements:
|
170
170
|
- - "~>"
|
171
171
|
- !ruby/object:Gem::Version
|
172
|
-
version: '
|
172
|
+
version: '3.6'
|
173
173
|
- !ruby/object:Gem::Dependency
|
174
|
-
name:
|
174
|
+
name: rspec_junit_formatter
|
175
175
|
requirement: !ruby/object:Gem::Requirement
|
176
176
|
requirements:
|
177
177
|
- - "~>"
|
178
178
|
- !ruby/object:Gem::Version
|
179
|
-
version: '0.
|
179
|
+
version: '0.3'
|
180
180
|
type: :development
|
181
181
|
prerelease: false
|
182
182
|
version_requirements: !ruby/object:Gem::Requirement
|
183
183
|
requirements:
|
184
184
|
- - "~>"
|
185
185
|
- !ruby/object:Gem::Version
|
186
|
-
version: '0.
|
186
|
+
version: '0.3'
|
187
187
|
- !ruby/object:Gem::Dependency
|
188
188
|
name: simplecov
|
189
189
|
requirement: !ruby/object:Gem::Requirement
|
@@ -198,6 +198,20 @@ dependencies:
|
|
198
198
|
- - "~>"
|
199
199
|
- !ruby/object:Gem::Version
|
200
200
|
version: '0.15'
|
201
|
+
- !ruby/object:Gem::Dependency
|
202
|
+
name: timecop
|
203
|
+
requirement: !ruby/object:Gem::Requirement
|
204
|
+
requirements:
|
205
|
+
- - "~>"
|
206
|
+
- !ruby/object:Gem::Version
|
207
|
+
version: '0.9'
|
208
|
+
type: :development
|
209
|
+
prerelease: false
|
210
|
+
version_requirements: !ruby/object:Gem::Requirement
|
211
|
+
requirements:
|
212
|
+
- - "~>"
|
213
|
+
- !ruby/object:Gem::Version
|
214
|
+
version: '0.9'
|
201
215
|
description: Migrations and model helpers for creating and managing PostgreSQL 10
|
202
216
|
partitions
|
203
217
|
email:
|
@@ -213,6 +227,7 @@ files:
|
|
213
227
|
- lib/pg_party/adapter/postgresql_methods.rb
|
214
228
|
- lib/pg_party/adapter_decorator.rb
|
215
229
|
- lib/pg_party/cache.rb
|
230
|
+
- lib/pg_party/hacks/schema_cache.rb
|
216
231
|
- lib/pg_party/model/list_methods.rb
|
217
232
|
- lib/pg_party/model/methods.rb
|
218
233
|
- lib/pg_party/model/range_methods.rb
|
@@ -239,8 +254,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
239
254
|
- !ruby/object:Gem::Version
|
240
255
|
version: 1.8.11
|
241
256
|
requirements: []
|
242
|
-
|
243
|
-
rubygems_version: 2.6.11
|
257
|
+
rubygems_version: 3.0.3
|
244
258
|
signing_key:
|
245
259
|
specification_version: 4
|
246
260
|
summary: ActiveRecord PostgreSQL Partitioning
|